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