2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.lutron.internal.discovery;
15 import static org.openhab.binding.lutron.internal.LutronBindingConstants.*;
17 import java.util.HashMap;
18 import java.util.List;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.lutron.internal.LutronHandlerFactory;
24 import org.openhab.binding.lutron.internal.handler.LeapBridgeHandler;
25 import org.openhab.binding.lutron.internal.protocol.leap.dto.Area;
26 import org.openhab.binding.lutron.internal.protocol.leap.dto.Device;
27 import org.openhab.binding.lutron.internal.protocol.leap.dto.OccupancyGroup;
28 import org.openhab.core.config.discovery.AbstractDiscoveryService;
29 import org.openhab.core.config.discovery.DiscoveryResult;
30 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
31 import org.openhab.core.config.discovery.DiscoveryService;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.openhab.core.thing.ThingUID;
34 import org.openhab.core.thing.binding.ThingHandler;
35 import org.openhab.core.thing.binding.ThingHandlerService;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * The {@link LeapDeviceDiscoveryService} discovers devices paired with Lutron bridges using the LEAP protocol.
42 * @author Bob Adair - Initial contribution
45 public class LeapDeviceDiscoveryService extends AbstractDiscoveryService
46 implements DiscoveryService, ThingHandlerService {
48 private static final int DISCOVERY_SERVICE_TIMEOUT = 0; // seconds
50 private final Logger logger = LoggerFactory.getLogger(LeapDeviceDiscoveryService.class);
52 /** Area number to name map **/
53 private @Nullable Map<Integer, String> areaMap;
54 private @Nullable List<OccupancyGroup> oGroupList;
56 private @NonNullByDefault({}) LeapBridgeHandler bridgeHandler;
58 public LeapDeviceDiscoveryService() {
59 super(LutronHandlerFactory.DISCOVERABLE_DEVICE_TYPES_UIDS, DISCOVERY_SERVICE_TIMEOUT);
63 public void setThingHandler(ThingHandler handler) {
64 if (handler instanceof LeapBridgeHandler leapBridgeHandler) {
65 bridgeHandler = leapBridgeHandler;
66 bridgeHandler.setDiscoveryService(this);
71 public @Nullable ThingHandler getThingHandler() {
76 protected void startScan() {
77 logger.debug("Active discovery scan started");
78 bridgeHandler.queryDiscoveryData();
81 public void processDeviceDefinitions(List<Device> deviceList) {
82 for (Device device : deviceList) {
83 // Integer zoneid = device.getZone();
84 Integer deviceId = device.getDevice();
85 String label = device.getFullyQualifiedName();
87 logger.debug("Discovered device: {} type: {} id: {}", label, device.deviceType, deviceId);
88 if (device.deviceType != null) {
89 switch (device.deviceType) {
91 case "RA2SelectMainRepeater":
92 notifyDiscovery(THING_TYPE_VIRTUALKEYPAD, deviceId, label, "model", "Caseta");
94 case "RadioRa3Processor":
95 notifyDiscovery(THING_TYPE_VIRTUALKEYPAD, deviceId, label, "model", "RadioRA 3");
101 notifyDiscovery(THING_TYPE_DIMMER, deviceId, label);
105 notifyDiscovery(THING_TYPE_SWITCH, deviceId, label);
107 case "CasetaFanSpeedController":
108 case "MaestroFanSpeedController":
109 notifyDiscovery(THING_TYPE_FAN, deviceId, label);
112 notifyDiscovery(THING_TYPE_PICO, deviceId, label, "model", "2B");
114 case "Pico2ButtonRaiseLower":
115 notifyDiscovery(THING_TYPE_PICO, deviceId, label, "model", "2BRL");
117 case "Pico3ButtonRaiseLower":
118 notifyDiscovery(THING_TYPE_PICO, deviceId, label, "model", "3BRL");
120 case "SerenaRollerShade":
121 case "SerenaHoneycombShade":
122 case "TriathlonRollerShade":
123 case "TriathlonHoneycombShade":
124 case "QsWirelessShade":
125 notifyDiscovery(THING_TYPE_SHADE, deviceId, label);
127 case "RPSOccupancySensor":
128 // Don't discover sensors. Using occupancy groups instead.
131 logger.info("Unrecognized device type: {}", device.deviceType);
139 private void processOccupancyGroups() {
140 Map<Integer, String> areaMap = this.areaMap;
141 List<OccupancyGroup> oGroupList = this.oGroupList;
143 if (areaMap != null && oGroupList != null) {
144 logger.trace("Processing occupancy groups");
145 for (OccupancyGroup oGroup : oGroupList) {
146 logger.trace("Processing OccupancyGroup: {}", oGroup.href);
147 int groupNum = oGroup.getOccupancyGroup();
148 // Only process occupancy groups with associated occupancy sensors
149 if (groupNum > 0 && oGroup.associatedSensors != null) {
151 if (oGroup.associatedAreas.length > 0) {
152 // If multiple associated areas are listed, use only the first
153 areaName = areaMap.get(oGroup.associatedAreas[0].getAreaNumber());
155 areaName = "Occupancy Group";
157 if (areaName != null) {
158 logger.debug("Discovered occupancy group: {} areas: {} area name: {}", groupNum,
159 oGroup.associatedAreas.length, areaName);
160 notifyDiscovery(THING_TYPE_OGROUP, groupNum, areaName);
165 this.oGroupList = null;
169 public void setOccupancyGroups(List<OccupancyGroup> oGroupList) {
170 logger.trace("Setting occupancy groups list");
171 this.oGroupList = oGroupList;
173 if (areaMap != null) {
174 processOccupancyGroups();
178 public void setAreas(List<Area> areaList) {
179 Map<Integer, String> areaMap = new HashMap<>();
181 logger.trace("Setting areas map");
182 for (Area area : areaList) {
183 int areaNum = area.getArea();
184 logger.trace("Inserting area into map - num: {} name: {}", areaNum, area.name);
186 areaMap.put(areaNum, area.name);
188 logger.debug("Ignoring area with unparsable href {}", area.href);
191 this.areaMap = areaMap;
193 if (oGroupList != null) {
194 processOccupancyGroups();
198 private void notifyDiscovery(ThingTypeUID thingTypeUID, @Nullable Integer integrationId, String label,
199 @Nullable String propName, @Nullable Object propValue) {
200 if (integrationId == null) {
201 logger.debug("Discovered {} with no integration ID", label);
204 ThingUID bridgeUID = this.bridgeHandler.getThing().getUID();
205 ThingUID uid = new ThingUID(thingTypeUID, bridgeUID, integrationId.toString());
207 Map<String, Object> properties = new HashMap<>();
209 properties.put(INTEGRATION_ID, integrationId);
210 if (propName != null && propValue != null) {
211 properties.put(propName, propValue);
214 DiscoveryResult result = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID).withLabel(label)
215 .withProperties(properties).withRepresentationProperty(INTEGRATION_ID).build();
216 thingDiscovered(result);
217 logger.trace("Discovered {}", uid);
220 private void notifyDiscovery(ThingTypeUID thingTypeUID, Integer integrationId, String label) {
221 notifyDiscovery(thingTypeUID, integrationId, label, null, null);
225 public void deactivate() {