]> git.basschouten.com Git - openhab-addons.git/blob
af84f6195d83372548dc79862bfde9cabf389db6
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13
14 package org.openhab.binding.lutron.internal.discovery;
15
16 import static org.openhab.binding.lutron.internal.LutronBindingConstants.*;
17
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.lutron.internal.LutronHandlerFactory;
25 import org.openhab.binding.lutron.internal.handler.LeapBridgeHandler;
26 import org.openhab.binding.lutron.internal.protocol.leap.dto.Area;
27 import org.openhab.binding.lutron.internal.protocol.leap.dto.Device;
28 import org.openhab.binding.lutron.internal.protocol.leap.dto.OccupancyGroup;
29 import org.openhab.core.config.discovery.AbstractDiscoveryService;
30 import org.openhab.core.config.discovery.DiscoveryResult;
31 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
32 import org.openhab.core.config.discovery.DiscoveryService;
33 import org.openhab.core.thing.ThingTypeUID;
34 import org.openhab.core.thing.ThingUID;
35 import org.openhab.core.thing.binding.ThingHandler;
36 import org.openhab.core.thing.binding.ThingHandlerService;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The {@link LeapDeviceDiscoveryService} discovers devices paired with Lutron bridges using the LEAP protocol.
42  *
43  * @author Bob Adair - Initial contribution
44  */
45 @NonNullByDefault
46 public class LeapDeviceDiscoveryService extends AbstractDiscoveryService
47         implements DiscoveryService, ThingHandlerService {
48
49     private static final int DISCOVERY_SERVICE_TIMEOUT = 0; // seconds
50
51     private final Logger logger = LoggerFactory.getLogger(LeapDeviceDiscoveryService.class);
52
53     /** Area number to name map **/
54     private @Nullable Map<Integer, String> areaMap;
55     private @Nullable List<OccupancyGroup> oGroupList;
56
57     private @NonNullByDefault({}) LeapBridgeHandler bridgeHandler;
58
59     public LeapDeviceDiscoveryService() {
60         super(LutronHandlerFactory.DISCOVERABLE_DEVICE_TYPES_UIDS, DISCOVERY_SERVICE_TIMEOUT);
61     }
62
63     @Override
64     public void setThingHandler(ThingHandler handler) {
65         if (handler instanceof LeapBridgeHandler) {
66             bridgeHandler = (LeapBridgeHandler) handler;
67             bridgeHandler.setDiscoveryService(this);
68         }
69     }
70
71     @Override
72     public @Nullable ThingHandler getThingHandler() {
73         return bridgeHandler;
74     }
75
76     @Override
77     protected void startScan() {
78         logger.debug("Active discovery scan started");
79         bridgeHandler.queryDiscoveryData();
80     }
81
82     public void processDeviceDefinitions(List<Device> deviceList) {
83         for (Device device : deviceList) {
84             // Integer zoneid = device.getZone();
85             Integer deviceId = device.getDevice();
86             String label = device.getFullyQualifiedName();
87             if (deviceId > 0) {
88                 logger.debug("Discovered device: {} type: {} id: {}", label, device.deviceType, deviceId);
89                 if (device.deviceType != null) {
90                     switch (device.deviceType) {
91                         case "SmartBridge":
92                         case "RA2SelectMainRepeater":
93                             notifyDiscovery(THING_TYPE_VIRTUALKEYPAD, deviceId, label, "model", "Caseta");
94                             break;
95                         case "WallDimmer":
96                         case "PlugInDimmer":
97                             notifyDiscovery(THING_TYPE_DIMMER, deviceId, label);
98                             break;
99                         case "WallSwitch":
100                         case "PlugInSwitch":
101                             notifyDiscovery(THING_TYPE_SWITCH, deviceId, label);
102                             break;
103                         case "CasetaFanSpeedController":
104                         case "MaestroFanSpeedController":
105                             notifyDiscovery(THING_TYPE_FAN, deviceId, label);
106                             break;
107                         case "Pico2Button":
108                             notifyDiscovery(THING_TYPE_PICO, deviceId, label, "model", "2B");
109                             break;
110                         case "Pico2ButtonRaiseLower":
111                             notifyDiscovery(THING_TYPE_PICO, deviceId, label, "model", "2BRL");
112                             break;
113                         case "Pico3ButtonRaiseLower":
114                             notifyDiscovery(THING_TYPE_PICO, deviceId, label, "model", "3BRL");
115                             break;
116                         case "SerenaRollerShade":
117                         case "SerenaHoneycombShade":
118                         case "TriathlonRollerShade":
119                         case "TriathlonHoneycombShade":
120                         case "QsWirelessShade":
121                             notifyDiscovery(THING_TYPE_SHADE, deviceId, label);
122                             break;
123                         case "RPSOccupancySensor":
124                             // Don't discover sensors. Using occupancy groups instead.
125                             break;
126                         default:
127                             logger.info("Unrecognized device type: {}", device.deviceType);
128                             break;
129                     }
130                 }
131             }
132         }
133     }
134
135     private void processOccupancyGroups() {
136         Map<Integer, String> areaMap = this.areaMap;
137         List<OccupancyGroup> oGroupList = this.oGroupList;
138
139         if (areaMap != null && oGroupList != null) {
140             logger.trace("Processing occupancy groups");
141             for (OccupancyGroup oGroup : oGroupList) {
142                 logger.trace("Processing OccupancyGroup: {}", oGroup.href);
143                 int groupNum = oGroup.getOccupancyGroup();
144                 // Only process occupancy groups with associated occupancy sensors
145                 if (groupNum > 0 && oGroup.associatedSensors != null) {
146                     String areaName;
147                     if (oGroup.associatedAreas.length > 0) {
148                         // If multiple associated areas are listed, use only the first
149                         areaName = areaMap.get(oGroup.associatedAreas[0].getAreaNumber());
150                     } else {
151                         areaName = "Occupancy Group";
152                     }
153                     if (areaName != null) {
154                         logger.debug("Discovered occupancy group: {} areas: {} area name: {}", groupNum,
155                                 oGroup.associatedAreas.length, areaName);
156                         notifyDiscovery(THING_TYPE_OGROUP, groupNum, areaName);
157                     }
158                 }
159             }
160             this.areaMap = null;
161             this.oGroupList = null;
162         }
163     }
164
165     public void setOccupancyGroups(List<OccupancyGroup> oGroupList) {
166         logger.trace("Setting occupancy groups list");
167         this.oGroupList = oGroupList;
168
169         if (areaMap != null) {
170             processOccupancyGroups();
171         }
172     }
173
174     public void setAreas(List<Area> areaList) {
175         Map<Integer, String> areaMap = new HashMap<>();
176
177         logger.trace("Setting areas map");
178         for (Area area : areaList) {
179             int areaNum = area.getArea();
180             logger.trace("Inserting area into map - num: {} name: {}", areaNum, area.name);
181             if (areaNum > 0) {
182                 areaMap.put(areaNum, area.name);
183             } else {
184                 logger.debug("Ignoring area with unparsable href {}", area.href);
185             }
186         }
187         this.areaMap = areaMap;
188
189         if (oGroupList != null) {
190             processOccupancyGroups();
191         }
192     }
193
194     private void notifyDiscovery(ThingTypeUID thingTypeUID, @Nullable Integer integrationId, String label,
195             @Nullable String propName, @Nullable Object propValue) {
196         if (integrationId == null) {
197             logger.debug("Discovered {} with no integration ID", label);
198             return;
199         }
200         ThingUID bridgeUID = this.bridgeHandler.getThing().getUID();
201         ThingUID uid = new ThingUID(thingTypeUID, bridgeUID, integrationId.toString());
202
203         Map<String, Object> properties = new HashMap<>();
204
205         properties.put(INTEGRATION_ID, integrationId);
206         if (propName != null && propValue != null) {
207             properties.put(propName, propValue);
208         }
209
210         DiscoveryResult result = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID).withLabel(label)
211                 .withProperties(properties).withRepresentationProperty(INTEGRATION_ID).build();
212         thingDiscovered(result);
213         logger.trace("Discovered {}", uid);
214     }
215
216     private void notifyDiscovery(ThingTypeUID thingTypeUID, Integer integrationId, String label) {
217         notifyDiscovery(thingTypeUID, integrationId, label, null, null);
218     }
219
220     @Override
221     public void deactivate() {
222         super.deactivate();
223     }
224 }