]> git.basschouten.com Git - openhab-addons.git/blob
c7dc04aa0afdaf5388f185c400dc33e4658ec510
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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 package org.openhab.binding.openwebnet.internal.discovery;
14
15 import java.util.HashMap;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
22 import org.openhab.binding.openwebnet.internal.handler.OpenWebNetBridgeHandler;
23 import org.openhab.core.config.discovery.AbstractDiscoveryService;
24 import org.openhab.core.config.discovery.DiscoveryResult;
25 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
26 import org.openhab.core.config.discovery.DiscoveryService;
27 import org.openhab.core.thing.ThingTypeUID;
28 import org.openhab.core.thing.ThingUID;
29 import org.openhab.core.thing.binding.ThingHandler;
30 import org.openhab.core.thing.binding.ThingHandlerService;
31 import org.openwebnet4j.OpenDeviceType;
32 import org.openwebnet4j.message.BaseOpenMessage;
33 import org.openwebnet4j.message.Where;
34 import org.openwebnet4j.message.WhereAlarm;
35 import org.openwebnet4j.message.WhereThermo;
36 import org.openwebnet4j.message.WhereZigBee;
37 import org.openwebnet4j.message.Who;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * The {@link OpenWebNetDeviceDiscoveryService} is responsible for discovering
43  * OpenWebNet devices connected to a bridge/gateway
44  *
45  * @author Massimo Valla - Initial contribution
46  * @author Andrea Conte - Energy management, Thermoregulation
47  * @author Gilberto Cocchi - Thermoregulation
48  * @author Giovanni Fabiani - Aux support
49  */
50 @NonNullByDefault
51 public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
52         implements DiscoveryService, ThingHandlerService {
53
54     private final Logger logger = LoggerFactory.getLogger(OpenWebNetDeviceDiscoveryService.class);
55
56     private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.DEVICE_SUPPORTED_THING_TYPES;
57     private static final int SEARCH_TIME_SEC = 60;
58
59     private @NonNullByDefault({}) OpenWebNetBridgeHandler bridgeHandler;
60     private @NonNullByDefault({}) ThingUID bridgeUID;
61
62     private boolean cuFound = false;
63
64     public OpenWebNetDeviceDiscoveryService() {
65         super(SUPPORTED_THING_TYPES, SEARCH_TIME_SEC);
66     }
67
68     @Override
69     public Set<ThingTypeUID> getSupportedThingTypes() {
70         return SUPPORTED_THING_TYPES;
71     }
72
73     @Override
74     protected void startScan() {
75         logger.info("------ SEARCHING for DEVICES on bridge '{}' ({}) ...", bridgeHandler.getThing().getLabel(),
76                 bridgeUID);
77         cuFound = false;
78         bridgeHandler.searchDevices();
79     }
80
81     @Override
82     protected void stopScan() {
83         logger.debug("------ stopScan() on bridge '{}'", bridgeUID);
84         bridgeHandler.scanStopped();
85     }
86
87     @Override
88     public void abortScan() {
89         logger.debug("------ abortScan() on bridge '{}'", bridgeUID);
90         bridgeHandler.scanStopped();
91     }
92
93     /**
94      * Create and notify to Inbox a new DiscoveryResult based on WHERE,
95      * OpenDeviceType and BaseOpenMessage
96      *
97      * @param where the discovered device's address (WHERE)
98      * @param deviceType {@link OpenDeviceType} of the discovered device
99      * @param message the OWN message received that identified the device
100      *            (optional)
101      */
102     public void newDiscoveryResult(@Nullable Where where, OpenDeviceType deviceType,
103             @Nullable BaseOpenMessage baseMsg) {
104         logger.debug("newDiscoveryResult() WHERE={}, deviceType={}", where, deviceType);
105         ThingTypeUID thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE; // generic device
106         String thingLabel = OpenWebNetBindingConstants.THING_LABEL_GENERIC_DEVICE;
107         Who deviceWho = Who.UNKNOWN;
108         switch (deviceType) {
109             case ZIGBEE_ON_OFF_SWITCH:
110                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH;
111                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_ON_OFF_SWITCH;
112                 deviceWho = Who.LIGHTING;
113                 break;
114             case ZIGBEE_DIMMER_SWITCH:
115                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_DIMMER;
116                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_DIMMER;
117                 deviceWho = Who.LIGHTING;
118                 break;
119             case SCS_ON_OFF_SWITCH:
120                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH;
121                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_ON_OFF_SWITCH;
122                 deviceWho = Who.LIGHTING;
123                 break;
124             case SCS_DIMMER_SWITCH:
125                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_DIMMER;
126                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_DIMMER;
127                 deviceWho = Who.LIGHTING;
128                 break;
129             case SCS_SHUTTER_SWITCH:
130             case SCS_SHUTTER_CONTROL: {
131                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_AUTOMATION;
132                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_AUTOMATION;
133                 deviceWho = Who.AUTOMATION;
134                 break;
135             }
136             case ZIGBEE_SHUTTER_SWITCH:
137             case ZIGBEE_SHUTTER_CONTROL: {
138                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_AUTOMATION;
139                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_AUTOMATION;
140                 deviceWho = Who.AUTOMATION;
141                 break;
142             }
143             case SCS_THERMO_SENSOR: {
144                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_SENSOR;
145                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_THERMO_SENSOR;
146                 deviceWho = Who.THERMOREGULATION;
147                 break;
148             }
149             case SCS_THERMO_ZONE: {
150                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_ZONE;
151                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_THERMO_ZONE;
152                 deviceWho = Who.THERMOREGULATION;
153                 break;
154             }
155             case SCS_THERMO_CENTRAL_UNIT: {
156                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_CU;
157                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_THERMO_CU;
158                 deviceWho = Who.THERMOREGULATION;
159                 break;
160             }
161             case SCS_ENERGY_METER: {
162                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_ENERGY_METER;
163                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_ENERGY_METER;
164                 deviceWho = Who.ENERGY_MANAGEMENT;
165                 break;
166             }
167             case BASIC_SCENARIO: {
168                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_SCENARIO;
169                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_SCENARIO;
170                 deviceWho = Who.SCENARIO;
171                 break;
172             }
173             case SCENARIO_CONTROL: {
174                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_CEN_SCENARIO_CONTROL;
175                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_CEN_SCENARIO_CONTROL;
176                 deviceWho = Who.CEN_SCENARIO_SCHEDULER;
177                 break;
178             }
179             case SCS_DRY_CONTACT_IR: {
180                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_DRY_CONTACT_IR;
181                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_DRY_CONTACT_IR;
182                 deviceWho = Who.CEN_PLUS_SCENARIO_SCHEDULER;
183                 break;
184             }
185             case MULTIFUNCTION_SCENARIO_CONTROL: {
186                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL;
187                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_CENPLUS_SCENARIO_CONTROL;
188                 deviceWho = Who.CEN_PLUS_SCENARIO_SCHEDULER;
189                 break;
190             }
191             case SCS_AUXILIARY_TOGGLE_CONTROL: {
192                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_AUX;
193                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_AUX;
194                 deviceWho = Who.AUX;
195                 break;
196             }
197             case SCS_ALARM_CENTRAL_UNIT: {
198                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_ALARM_SYSTEM;
199                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_ALARM_SYSTEM;
200                 deviceWho = Who.BURGLAR_ALARM;
201                 break;
202             }
203             case SCS_ALARM_ZONE: {
204                 thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_ALARM_ZONE;
205                 thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_ALARM_ZONE;
206                 deviceWho = Who.BURGLAR_ALARM;
207                 break;
208             }
209             default:
210                 logger.warn("Device type {} is not supported, default to GENERIC device (WHERE={})", deviceType, where);
211                 if (where instanceof WhereZigBee) {
212                     thingLabel = "Zigbee " + thingLabel;
213                 }
214                 if (baseMsg != null) {
215                     deviceWho = baseMsg.getWho();
216                 }
217         }
218         Where w;
219         if (where != null) {
220             w = where;
221         } else if (OpenWebNetBindingConstants.THING_TYPE_BUS_ALARM_SYSTEM.equals(thingTypeUID)) {
222             w = new WhereAlarm("0");
223         } else {
224             logger.debug("ignoring newDiscoveryResult with null where: {}", baseMsg);
225             return;
226         }
227
228         String ownId = bridgeHandler.ownIdFromWhoWhere(deviceWho, w);
229         if (OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH.equals(thingTypeUID)) {
230             if (bridgeHandler.getRegisteredDevice(ownId) != null) {
231                 logger.debug("dimmer/switch with WHERE={} already registered, skipping this discovery result", w);
232                 return;
233             }
234         }
235
236         String tId = bridgeHandler.thingIdFromWhere(w);
237         ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, tId);
238
239         DiscoveryResult discoveryResult = null;
240
241         String whereConfig = w.value();
242
243         // remove # from discovered alarm zone
244         if (OpenWebNetBindingConstants.THING_TYPE_BUS_ALARM_ZONE.equals(thingTypeUID)) {
245             whereConfig = "" + ((WhereAlarm) w).getZone();
246         }
247
248         Map<String, Object> properties = new HashMap<>(2);
249
250         // detect Thermo CU type
251         if (OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_CU.equals(thingTypeUID)) {
252             cuFound = true;
253             logger.debug("CU found: {}", w);
254             if (w.value().charAt(0) == '#') { // 99-zone CU
255                 thingLabel += " 99-zone";
256                 logger.debug("@@@@@ THERMO CU found 99-zone: where={}, ownId={}, whereConfig={}", w, ownId,
257                         whereConfig);
258             } else {
259                 thingLabel += " 4-zone";
260                 whereConfig = "#" + w.value();
261                 logger.debug("@@@@ THERMO CU found 4-zone: where={}, ownId={}, whereConfig={}", w, ownId, whereConfig);
262             }
263         } else if (OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_ZONE.equals(thingTypeUID)) {
264             if (cuFound) {
265                 // set param standalone = false for thermo zone
266                 properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_STANDALONE, false);
267             }
268             whereConfig = "" + ((WhereThermo) w).getZone();
269             logger.debug("@@@@@ THERMO ZONE found: where={}, ownId={}, whereConfig={}, standalone={}", w, ownId,
270                     whereConfig, properties.get(OpenWebNetBindingConstants.CONFIG_PROPERTY_STANDALONE));
271         }
272
273         if (w instanceof WhereZigBee whereZigBee && WhereZigBee.UNIT_02.equals(whereZigBee.getUnit())) {
274             logger.debug("UNIT=02 found (WHERE={}) -> will remove previous result if exists", w);
275             thingRemoved(thingUID); // remove previously discovered thing
276             // re-create thingUID with new type
277             thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS;
278             thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_ON_OFF_SWITCH_2UNITS;
279             thingUID = new ThingUID(thingTypeUID, bridgeUID, tId);
280             whereConfig = whereZigBee.valueWithUnit(WhereZigBee.UNIT_ALL); // replace unit '02' with '00'
281             logger.debug("UNIT=02, switching type from {} to {}",
282                     OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH,
283                     OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS);
284         }
285         properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_WHERE, whereConfig);
286         properties.put(OpenWebNetBindingConstants.PROPERTY_OWNID, ownId);
287         if (OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE.equals(thingTypeUID)) {
288             thingLabel = thingLabel + " (WHO=" + deviceWho + ", WHERE=" + whereConfig + ")";
289         } else {
290             thingLabel = thingLabel + " (WHERE=" + whereConfig + ")";
291         }
292         discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID).withProperties(properties)
293                 .withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_OWNID).withBridge(bridgeUID)
294                 .withLabel(thingLabel).build();
295         thingDiscovered(discoveryResult);
296     }
297
298     @Override
299     public void deactivate() {
300         super.deactivate();
301     }
302
303     @Override
304     public void setThingHandler(@Nullable ThingHandler handler) {
305         if (handler instanceof OpenWebNetBridgeHandler openWebNetBridgeHandler) {
306             logger.debug("attaching {} to handler {} ", this, handler);
307             bridgeHandler = openWebNetBridgeHandler;
308             bridgeHandler.deviceDiscoveryService = this;
309             bridgeUID = bridgeHandler.getThing().getUID();
310         }
311     }
312
313     @Override
314     public @Nullable ThingHandler getThingHandler() {
315         return bridgeHandler;
316     }
317 }