]> git.basschouten.com Git - openhab-addons.git/blob
86ceadf7c6986170372e76c6339ae2a6cfed3bd6
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.omnilink.internal.discovery;
14
15 import static com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties.*;
16 import static com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties.*;
17 import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
18
19 import java.math.BigInteger;
20 import java.util.HashMap;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.omnilink.internal.SystemType;
28 import org.openhab.binding.omnilink.internal.handler.BridgeOfflineException;
29 import org.openhab.binding.omnilink.internal.handler.OmnilinkBridgeHandler;
30 import org.openhab.core.config.discovery.AbstractDiscoveryService;
31 import org.openhab.core.config.discovery.DiscoveryResult;
32 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
33 import org.openhab.core.config.discovery.DiscoveryService;
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 import com.digitaldan.jomnilinkII.MessageTypes.SystemInformation;
41 import com.digitaldan.jomnilinkII.MessageTypes.properties.AccessControlReaderProperties;
42 import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
43 import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioSourceProperties;
44 import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioZoneProperties;
45 import com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties;
46 import com.digitaldan.jomnilinkII.MessageTypes.properties.ButtonProperties;
47 import com.digitaldan.jomnilinkII.MessageTypes.properties.ThermostatProperties;
48 import com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties;
49 import com.digitaldan.jomnilinkII.MessageTypes.properties.ZoneProperties;
50 import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
51 import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
52
53 /**
54  * The {@link OmnilinkDiscoveryService} creates things based on the configured bridge.
55  *
56  * @author Craig Hamilton - Initial contribution
57  * @author Ethan Dye - openHAB3 rewrite
58  */
59 @NonNullByDefault
60 public class OmnilinkDiscoveryService extends AbstractDiscoveryService
61         implements DiscoveryService, ThingHandlerService {
62     private final Logger logger = LoggerFactory.getLogger(OmnilinkDiscoveryService.class);
63     private static final int DISCOVER_TIMEOUT_SECONDS = 30;
64     private @Nullable OmnilinkBridgeHandler bridgeHandler;
65     private @Nullable SystemType systemType;
66     private @Nullable List<AreaProperties> areas;
67
68     /**
69      * Creates an OmnilinkDiscoveryService.
70      */
71     public OmnilinkDiscoveryService() {
72         super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, false);
73     }
74
75     @Override
76     public void setThingHandler(@Nullable ThingHandler handler) {
77         if (handler instanceof OmnilinkBridgeHandler) {
78             bridgeHandler = (OmnilinkBridgeHandler) handler;
79         }
80     }
81
82     @Override
83     public @Nullable ThingHandler getThingHandler() {
84         return bridgeHandler;
85     }
86
87     @Override
88     public void activate() {
89     }
90
91     @Override
92     public void deactivate() {
93     }
94
95     @Override
96     protected synchronized void startScan() {
97         final OmnilinkBridgeHandler handler = bridgeHandler;
98         if (handler != null) {
99             logger.debug("Starting scan");
100             try {
101                 SystemInformation systemInformation = handler.reqSystemInformation();
102                 this.systemType = SystemType.getType(systemInformation.getModel());
103                 this.areas = discoverAreas();
104                 discoverUnits();
105                 discoverZones();
106                 discoverButtons();
107                 discoverThermostats();
108                 discoverAudioZones();
109                 discoverAudioSources();
110                 discoverTempSensors();
111                 discoverHumiditySensors();
112                 discoverLocks();
113             } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
114                 logger.debug("Received error during discovery: {}", e.getMessage());
115             }
116         }
117     }
118
119     @Override
120     protected synchronized void stopScan() {
121         super.stopScan();
122         removeOlderResults(getTimestampOfLastScan());
123     }
124
125     /**
126      * Calculate the area filter the a supplied area
127      *
128      * @param area Area to calculate filter for.
129      * @return Calculated Bit Filter for the supplied area. Bit 0 is area 1, bit 2 is area 2 and so on.
130      */
131     private static int bitFilterForArea(AreaProperties areaProperties) {
132         return BigInteger.ZERO.setBit(areaProperties.getNumber() - 1).intValue();
133     }
134
135     /**
136      * Discovers OmniLink buttons
137      */
138     private void discoverButtons() {
139         final OmnilinkBridgeHandler handler = bridgeHandler;
140         if (handler != null) {
141             final ThingUID bridgeUID = handler.getThing().getUID();
142             final List<AreaProperties> areas = this.areas;
143
144             if (areas != null) {
145                 for (AreaProperties areaProperties : areas) {
146                     int areaFilter = bitFilterForArea(areaProperties);
147
148                     ObjectPropertyRequest<ButtonProperties> objectPropertyRequest = ObjectPropertyRequest
149                             .builder(handler, ObjectPropertyRequests.BUTTONS, 0, 1).selectNamed().areaFilter(areaFilter)
150                             .build();
151
152                     for (ButtonProperties buttonProperties : objectPropertyRequest) {
153                         String thingName = buttonProperties.getName();
154                         String thingID = Integer.toString(buttonProperties.getNumber());
155
156                         Map<String, Object> properties = new HashMap<>();
157                         properties.put(THING_PROPERTIES_NAME, thingName);
158                         properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
159
160                         ThingUID thingUID = new ThingUID(THING_TYPE_BUTTON, bridgeUID, thingID);
161
162                         DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
163                                 .withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
164                                 .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
165                                 .withLabel(thingName).build();
166                         thingDiscovered(discoveryResult);
167                     }
168                 }
169             }
170         }
171     }
172
173     /**
174      * Discovers OmniLink locks
175      */
176     private void discoverLocks() {
177         final OmnilinkBridgeHandler handler = bridgeHandler;
178         if (handler != null) {
179             final ThingUID bridgeUID = handler.getThing().getUID();
180
181             ObjectPropertyRequest<AccessControlReaderProperties> objectPropertyRequest = ObjectPropertyRequest
182                     .builder(handler, ObjectPropertyRequests.LOCK, 0, 1).selectNamed().build();
183
184             for (AccessControlReaderProperties lockProperties : objectPropertyRequest) {
185                 String thingName = lockProperties.getName();
186                 String thingID = Integer.toString(lockProperties.getNumber());
187
188                 Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
189
190                 ThingUID thingUID = new ThingUID(THING_TYPE_LOCK, bridgeUID, thingID);
191
192                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
193                         .withProperty(THING_PROPERTIES_NUMBER, thingID)
194                         .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
195                         .build();
196                 thingDiscovered(discoveryResult);
197             }
198         }
199     }
200
201     /**
202      * Discovers OmniLink audio zones
203      */
204     private void discoverAudioZones() {
205         final OmnilinkBridgeHandler handler = bridgeHandler;
206         if (handler != null) {
207             final ThingUID bridgeUID = handler.getThing().getUID();
208
209             ObjectPropertyRequest<AudioZoneProperties> objectPropertyRequest = ObjectPropertyRequest
210                     .builder(handler, ObjectPropertyRequests.AUDIO_ZONE, 0, 1).selectNamed().build();
211
212             for (AudioZoneProperties audioZoneProperties : objectPropertyRequest) {
213                 String thingName = audioZoneProperties.getName();
214                 String thingID = Integer.toString(audioZoneProperties.getNumber());
215
216                 Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
217
218                 ThingUID thingUID = new ThingUID(THING_TYPE_AUDIO_ZONE, bridgeUID, thingID);
219
220                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
221                         .withProperty(THING_PROPERTIES_NUMBER, thingID)
222                         .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
223                         .build();
224                 thingDiscovered(discoveryResult);
225             }
226         }
227     }
228
229     /**
230      * Discovers OmniLink audio sources
231      */
232     private void discoverAudioSources() {
233         final OmnilinkBridgeHandler handler = bridgeHandler;
234         if (handler != null) {
235             final ThingUID bridgeUID = handler.getThing().getUID();
236
237             ObjectPropertyRequest<AudioSourceProperties> objectPropertyRequest = ObjectPropertyRequest
238                     .builder(handler, ObjectPropertyRequests.AUDIO_SOURCE, 0, 1).selectNamed().build();
239
240             for (AudioSourceProperties audioSourceProperties : objectPropertyRequest) {
241                 String thingName = audioSourceProperties.getName();
242                 String thingID = Integer.toString(audioSourceProperties.getNumber());
243
244                 Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
245
246                 ThingUID thingUID = new ThingUID(THING_TYPE_AUDIO_SOURCE, bridgeUID, thingID);
247
248                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
249                         .withProperty(THING_PROPERTIES_NUMBER, thingID)
250                         .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
251                         .build();
252                 thingDiscovered(discoveryResult);
253             }
254         }
255     }
256
257     /**
258      * Discovers OmniLink temperature sensors
259      */
260     private void discoverTempSensors() {
261         final OmnilinkBridgeHandler handler = bridgeHandler;
262         if (handler != null) {
263             final ThingUID bridgeUID = handler.getThing().getUID();
264             final List<AreaProperties> areas = this.areas;
265
266             if (areas != null) {
267                 for (AreaProperties areaProperties : areas) {
268                     int areaFilter = bitFilterForArea(areaProperties);
269
270                     ObjectPropertyRequest<AuxSensorProperties> objectPropertyRequest = ObjectPropertyRequest
271                             .builder(handler, ObjectPropertyRequests.AUX_SENSORS, 0, 1).selectNamed()
272                             .areaFilter(areaFilter).build();
273
274                     for (AuxSensorProperties auxSensorProperties : objectPropertyRequest) {
275                         if (auxSensorProperties.getSensorType() != SENSOR_TYPE_PROGRAMMABLE_ENERGY_SAVER_MODULE
276                                 && auxSensorProperties.getSensorType() != SENSOR_TYPE_HUMIDITY) {
277                             String thingName = auxSensorProperties.getName();
278                             String thingID = Integer.toString(auxSensorProperties.getNumber());
279
280                             Map<String, Object> properties = new HashMap<>();
281                             properties.put(THING_PROPERTIES_NAME, thingName);
282                             properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
283
284                             ThingUID thingUID = new ThingUID(THING_TYPE_TEMP_SENSOR, bridgeUID, thingID);
285
286                             DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
287                                     .withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
288                                     .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
289                                     .withLabel(thingName).build();
290                             thingDiscovered(discoveryResult);
291                         }
292                     }
293                 }
294             }
295         }
296     }
297
298     /**
299      * Discovers OmniLink humidity sensors
300      */
301     private void discoverHumiditySensors() {
302         final OmnilinkBridgeHandler handler = bridgeHandler;
303         if (handler != null) {
304             final ThingUID bridgeUID = handler.getThing().getUID();
305             final List<AreaProperties> areas = this.areas;
306
307             if (areas != null) {
308                 for (AreaProperties areaProperties : areas) {
309                     int areaFilter = bitFilterForArea(areaProperties);
310
311                     ObjectPropertyRequest<AuxSensorProperties> objectPropertyRequest = ObjectPropertyRequest
312                             .builder(handler, ObjectPropertyRequests.AUX_SENSORS, 0, 1).selectNamed()
313                             .areaFilter(areaFilter).build();
314
315                     for (AuxSensorProperties auxSensorProperties : objectPropertyRequest) {
316                         if (auxSensorProperties.getSensorType() == SENSOR_TYPE_HUMIDITY) {
317                             String thingName = auxSensorProperties.getName();
318                             String thingID = Integer.toString(auxSensorProperties.getNumber());
319
320                             Map<String, Object> properties = new HashMap<>();
321                             properties.put(THING_PROPERTIES_NAME, thingName);
322                             properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
323
324                             ThingUID thingUID = new ThingUID(THING_TYPE_HUMIDITY_SENSOR, bridgeUID, thingID);
325
326                             DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
327                                     .withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
328                                     .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
329                                     .withLabel(thingName).build();
330                             thingDiscovered(discoveryResult);
331                         }
332                     }
333                 }
334             }
335         }
336     }
337
338     /**
339      * Discovers OmniLink thermostats
340      */
341     private void discoverThermostats() {
342         final OmnilinkBridgeHandler handler = bridgeHandler;
343         if (handler != null) {
344             final ThingUID bridgeUID = handler.getThing().getUID();
345             final List<AreaProperties> areas = this.areas;
346
347             if (areas != null) {
348                 for (AreaProperties areaProperties : areas) {
349                     int areaFilter = bitFilterForArea(areaProperties);
350
351                     ObjectPropertyRequest<ThermostatProperties> objectPropertyRequest = ObjectPropertyRequest
352                             .builder(handler, ObjectPropertyRequests.THERMOSTAT, 0, 1).selectNamed()
353                             .areaFilter(areaFilter).build();
354
355                     for (ThermostatProperties thermostatProperties : objectPropertyRequest) {
356                         String thingName = thermostatProperties.getName();
357                         String thingID = Integer.toString(thermostatProperties.getNumber());
358
359                         ThingUID thingUID = new ThingUID(THING_TYPE_THERMOSTAT, bridgeUID, thingID);
360
361                         Map<String, Object> properties = new HashMap<>();
362                         properties.put(THING_PROPERTIES_NAME, thingName);
363                         properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
364
365                         DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
366                                 .withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
367                                 .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
368                                 .withLabel(thingName).build();
369                         thingDiscovered(discoveryResult);
370                     }
371                 }
372             }
373         }
374     }
375
376     /**
377      * Discovers OmniLink areas
378      */
379     private @Nullable List<AreaProperties> discoverAreas() {
380         final OmnilinkBridgeHandler handler = bridgeHandler;
381         if (handler != null) {
382             final ThingUID bridgeUID = handler.getThing().getUID();
383             List<AreaProperties> areas = new LinkedList<>();
384
385             ObjectPropertyRequest<AreaProperties> objectPropertyRequest = ObjectPropertyRequest
386                     .builder(handler, ObjectPropertyRequests.AREA, 0, 1).build();
387
388             for (AreaProperties areaProperties : objectPropertyRequest) {
389                 int thingNumber = areaProperties.getNumber();
390                 String thingName = areaProperties.getName();
391                 String thingID = Integer.toString(thingNumber);
392                 ThingUID thingUID = null;
393
394                 /*
395                  * It seems that for simple OmniLink Controller configurations there
396                  * is only 1 area, without a name. So if there is no name for the
397                  * first area, we will call that Main Area. If other area's name is
398                  * blank, we will not create a thing.
399                  */
400                 if (thingNumber == 1 && "".equals(thingName)) {
401                     thingName = "Main Area";
402                 } else if ("".equals(thingName)) {
403                     break;
404                 }
405
406                 Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
407
408                 final SystemType systemType = this.systemType;
409                 if (systemType != null) {
410                     switch (systemType) {
411                         case LUMINA:
412                             thingUID = new ThingUID(THING_TYPE_LUMINA_AREA, bridgeUID, thingID);
413                             break;
414                         case OMNI:
415                             thingUID = new ThingUID(THING_TYPE_OMNI_AREA, bridgeUID, thingID);
416                             break;
417                         default:
418                             throw new IllegalStateException("Unknown System Type");
419                     }
420                 }
421
422                 if (thingUID != null) {
423                     DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
424                             .withProperty(THING_PROPERTIES_NUMBER, thingID)
425                             .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
426                             .withLabel(thingName).build();
427                     thingDiscovered(discoveryResult);
428                 }
429
430                 areas.add(areaProperties);
431             }
432             return areas;
433         } else {
434             return null;
435         }
436     }
437
438     /**
439      * Discovers OmniLink supported units
440      */
441     private void discoverUnits() {
442         final OmnilinkBridgeHandler handler = bridgeHandler;
443         if (handler != null) {
444             final ThingUID bridgeUID = handler.getThing().getUID();
445             final List<AreaProperties> areas = this.areas;
446
447             if (areas != null) {
448                 for (AreaProperties areaProperties : areas) {
449                     int areaFilter = bitFilterForArea(areaProperties);
450
451                     ObjectPropertyRequest<UnitProperties> objectPropertyRequest = ObjectPropertyRequest
452                             .builder(handler, ObjectPropertyRequests.UNIT, 0, 1).selectNamed().areaFilter(areaFilter)
453                             .selectAnyLoad().build();
454
455                     for (UnitProperties unitProperties : objectPropertyRequest) {
456                         int thingType = unitProperties.getUnitType();
457                         String thingName = unitProperties.getName();
458                         String thingID = Integer.toString(unitProperties.getNumber());
459                         ThingUID thingUID = null;
460
461                         Map<String, Object> properties = new HashMap<>();
462                         properties.put(THING_PROPERTIES_NAME, thingName);
463                         properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
464
465                         switch (thingType) {
466                             case UNIT_TYPE_HLC_ROOM:
467                             case UNIT_TYPE_VIZIARF_ROOM:
468                                 thingUID = new ThingUID(THING_TYPE_ROOM, bridgeUID, thingID);
469                                 break;
470                             case UNIT_TYPE_FLAG:
471                                 thingUID = new ThingUID(THING_TYPE_FLAG, bridgeUID, thingID);
472                                 break;
473                             case UNIT_TYPE_OUTPUT:
474                                 thingUID = new ThingUID(THING_TYPE_OUTPUT, bridgeUID, thingID);
475                                 break;
476                             case UNIT_TYPE_UPB:
477                             case UNIT_TYPE_HLC_LOAD:
478                                 thingUID = new ThingUID(THING_TYPE_UNIT_UPB, bridgeUID, thingID);
479                                 break;
480                             case UNIT_TYPE_CENTRALITE:
481                             case UNIT_TYPE_RADIORA:
482                             case UNIT_TYPE_VIZIARF_LOAD:
483                             case UNIT_TYPE_COMPOSE:
484                                 thingUID = new ThingUID(THING_TYPE_DIMMABLE, bridgeUID, thingID);
485                                 break;
486                             default:
487                                 thingUID = new ThingUID(THING_TYPE_UNIT, bridgeUID, thingID);
488                                 logger.debug("Generic unit type: {}", thingType);
489                                 break;
490                         }
491
492                         DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
493                                 .withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
494                                 .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
495                                 .withLabel(thingName).build();
496                         thingDiscovered(discoveryResult);
497                     }
498                 }
499             }
500         }
501     }
502
503     /**
504      * Generates zone items
505      */
506     private void discoverZones() {
507         final OmnilinkBridgeHandler handler = bridgeHandler;
508         if (handler != null) {
509             final ThingUID bridgeUID = handler.getThing().getUID();
510             final List<AreaProperties> areas = this.areas;
511
512             if (areas != null) {
513                 for (AreaProperties areaProperties : areas) {
514                     int areaFilter = bitFilterForArea(areaProperties);
515
516                     ObjectPropertyRequest<ZoneProperties> objectPropertyRequest = ObjectPropertyRequest
517                             .builder(handler, ObjectPropertyRequests.ZONE, 0, 1).selectNamed().areaFilter(areaFilter)
518                             .build();
519
520                     for (ZoneProperties zoneProperties : objectPropertyRequest) {
521                         if (zoneProperties.getZoneType() <= SENSOR_TYPE_PROGRAMMABLE_ENERGY_SAVER_MODULE) {
522                             String thingName = zoneProperties.getName();
523                             String thingID = Integer.toString(zoneProperties.getNumber());
524
525                             Map<String, Object> properties = new HashMap<>();
526                             properties.put(THING_PROPERTIES_NAME, thingName);
527                             properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
528
529                             ThingUID thingUID = new ThingUID(THING_TYPE_ZONE, bridgeUID, thingID);
530
531                             DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
532                                     .withProperties(properties).withProperty(THING_PROPERTIES_NUMBER, thingID)
533                                     .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID)
534                                     .withLabel(thingName).build();
535                             thingDiscovered(discoveryResult);
536                         }
537                     }
538                 }
539             }
540         }
541     }
542 }