2 * Copyright (c) 2010-2021 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.omnilink.internal.discovery;
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.*;
19 import java.math.BigInteger;
20 import java.util.HashMap;
21 import java.util.LinkedList;
22 import java.util.List;
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;
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;
54 * The {@link OmnilinkDiscoveryService} creates things based on the configured bridge.
56 * @author Craig Hamilton - Initial contribution
57 * @author Ethan Dye - openHAB3 rewrite
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;
69 * Creates an OmnilinkDiscoveryService.
71 public OmnilinkDiscoveryService() {
72 super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, false);
76 public void setThingHandler(@Nullable ThingHandler handler) {
77 if (handler instanceof OmnilinkBridgeHandler) {
78 bridgeHandler = (OmnilinkBridgeHandler) handler;
83 public @Nullable ThingHandler getThingHandler() {
88 public void activate() {
92 public void deactivate() {
96 protected synchronized void startScan() {
97 final OmnilinkBridgeHandler handler = bridgeHandler;
98 if (handler != null) {
99 logger.debug("Starting scan");
101 SystemInformation systemInformation = handler.reqSystemInformation();
102 this.systemType = SystemType.getType(systemInformation.getModel());
103 this.areas = discoverAreas();
107 discoverThermostats();
108 discoverAudioZones();
109 discoverAudioSources();
110 discoverTempSensors();
111 discoverHumiditySensors();
113 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
114 logger.debug("Received error during discovery: {}", e.getMessage());
120 protected synchronized void stopScan() {
122 removeOlderResults(getTimestampOfLastScan());
126 * Calculate the area filter the a supplied area
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.
131 private static int bitFilterForArea(AreaProperties areaProperties) {
132 return BigInteger.ZERO.setBit(areaProperties.getNumber() - 1).intValue();
136 * Discovers OmniLink buttons
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;
145 for (AreaProperties areaProperties : areas) {
146 int areaFilter = bitFilterForArea(areaProperties);
148 ObjectPropertyRequest<ButtonProperties> objectPropertyRequest = ObjectPropertyRequest
149 .builder(handler, ObjectPropertyRequests.BUTTONS, 0, 1).selectNamed().areaFilter(areaFilter)
152 for (ButtonProperties buttonProperties : objectPropertyRequest) {
153 String thingName = buttonProperties.getName();
154 String thingID = Integer.toString(buttonProperties.getNumber());
156 Map<String, Object> properties = new HashMap<>();
157 properties.put(THING_PROPERTIES_NAME, thingName);
158 properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
160 ThingUID thingUID = new ThingUID(THING_TYPE_BUTTON, bridgeUID, thingID);
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);
174 * Discovers OmniLink locks
176 private void discoverLocks() {
177 final OmnilinkBridgeHandler handler = bridgeHandler;
178 if (handler != null) {
179 final ThingUID bridgeUID = handler.getThing().getUID();
181 ObjectPropertyRequest<AccessControlReaderProperties> objectPropertyRequest = ObjectPropertyRequest
182 .builder(handler, ObjectPropertyRequests.LOCK, 0, 1).selectNamed().build();
184 for (AccessControlReaderProperties lockProperties : objectPropertyRequest) {
185 String thingName = lockProperties.getName();
186 String thingID = Integer.toString(lockProperties.getNumber());
188 Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
190 ThingUID thingUID = new ThingUID(THING_TYPE_LOCK, bridgeUID, thingID);
192 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
193 .withProperty(THING_PROPERTIES_NUMBER, thingID)
194 .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
196 thingDiscovered(discoveryResult);
202 * Discovers OmniLink audio zones
204 private void discoverAudioZones() {
205 final OmnilinkBridgeHandler handler = bridgeHandler;
206 if (handler != null) {
207 final ThingUID bridgeUID = handler.getThing().getUID();
209 ObjectPropertyRequest<AudioZoneProperties> objectPropertyRequest = ObjectPropertyRequest
210 .builder(handler, ObjectPropertyRequests.AUDIO_ZONE, 0, 1).selectNamed().build();
212 for (AudioZoneProperties audioZoneProperties : objectPropertyRequest) {
213 String thingName = audioZoneProperties.getName();
214 String thingID = Integer.toString(audioZoneProperties.getNumber());
216 Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
218 ThingUID thingUID = new ThingUID(THING_TYPE_AUDIO_ZONE, bridgeUID, thingID);
220 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
221 .withProperty(THING_PROPERTIES_NUMBER, thingID)
222 .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
224 thingDiscovered(discoveryResult);
230 * Discovers OmniLink audio sources
232 private void discoverAudioSources() {
233 final OmnilinkBridgeHandler handler = bridgeHandler;
234 if (handler != null) {
235 final ThingUID bridgeUID = handler.getThing().getUID();
237 ObjectPropertyRequest<AudioSourceProperties> objectPropertyRequest = ObjectPropertyRequest
238 .builder(handler, ObjectPropertyRequests.AUDIO_SOURCE, 0, 1).selectNamed().build();
240 for (AudioSourceProperties audioSourceProperties : objectPropertyRequest) {
241 String thingName = audioSourceProperties.getName();
242 String thingID = Integer.toString(audioSourceProperties.getNumber());
244 Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
246 ThingUID thingUID = new ThingUID(THING_TYPE_AUDIO_SOURCE, bridgeUID, thingID);
248 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
249 .withProperty(THING_PROPERTIES_NUMBER, thingID)
250 .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(thingName)
252 thingDiscovered(discoveryResult);
258 * Discovers OmniLink temperature sensors
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;
267 for (AreaProperties areaProperties : areas) {
268 int areaFilter = bitFilterForArea(areaProperties);
270 ObjectPropertyRequest<AuxSensorProperties> objectPropertyRequest = ObjectPropertyRequest
271 .builder(handler, ObjectPropertyRequests.AUX_SENSORS, 0, 1).selectNamed()
272 .areaFilter(areaFilter).build();
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());
280 Map<String, Object> properties = new HashMap<>();
281 properties.put(THING_PROPERTIES_NAME, thingName);
282 properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
284 ThingUID thingUID = new ThingUID(THING_TYPE_TEMP_SENSOR, bridgeUID, thingID);
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);
299 * Discovers OmniLink humidity sensors
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;
308 for (AreaProperties areaProperties : areas) {
309 int areaFilter = bitFilterForArea(areaProperties);
311 ObjectPropertyRequest<AuxSensorProperties> objectPropertyRequest = ObjectPropertyRequest
312 .builder(handler, ObjectPropertyRequests.AUX_SENSORS, 0, 1).selectNamed()
313 .areaFilter(areaFilter).build();
315 for (AuxSensorProperties auxSensorProperties : objectPropertyRequest) {
316 if (auxSensorProperties.getSensorType() == SENSOR_TYPE_HUMIDITY) {
317 String thingName = auxSensorProperties.getName();
318 String thingID = Integer.toString(auxSensorProperties.getNumber());
320 Map<String, Object> properties = new HashMap<>();
321 properties.put(THING_PROPERTIES_NAME, thingName);
322 properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
324 ThingUID thingUID = new ThingUID(THING_TYPE_HUMIDITY_SENSOR, bridgeUID, thingID);
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);
339 * Discovers OmniLink thermostats
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;
348 for (AreaProperties areaProperties : areas) {
349 int areaFilter = bitFilterForArea(areaProperties);
351 ObjectPropertyRequest<ThermostatProperties> objectPropertyRequest = ObjectPropertyRequest
352 .builder(handler, ObjectPropertyRequests.THERMOSTAT, 0, 1).selectNamed()
353 .areaFilter(areaFilter).build();
355 for (ThermostatProperties thermostatProperties : objectPropertyRequest) {
356 String thingName = thermostatProperties.getName();
357 String thingID = Integer.toString(thermostatProperties.getNumber());
359 ThingUID thingUID = new ThingUID(THING_TYPE_THERMOSTAT, bridgeUID, thingID);
361 Map<String, Object> properties = new HashMap<>();
362 properties.put(THING_PROPERTIES_NAME, thingName);
363 properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
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);
377 * Discovers OmniLink areas
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<>();
385 ObjectPropertyRequest<AreaProperties> objectPropertyRequest = ObjectPropertyRequest
386 .builder(handler, ObjectPropertyRequests.AREA, 0, 1).build();
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;
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.
400 if (thingNumber == 1 && "".equals(thingName)) {
401 thingName = "Main Area";
402 } else if ("".equals(thingName)) {
406 Map<String, Object> properties = Map.of(THING_PROPERTIES_NAME, thingName);
408 final SystemType systemType = this.systemType;
409 if (systemType != null) {
410 switch (systemType) {
412 thingUID = new ThingUID(THING_TYPE_LUMINA_AREA, bridgeUID, thingID);
415 thingUID = new ThingUID(THING_TYPE_OMNI_AREA, bridgeUID, thingID);
418 throw new IllegalStateException("Unknown System Type");
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);
430 areas.add(areaProperties);
439 * Discovers OmniLink supported units
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;
448 for (AreaProperties areaProperties : areas) {
449 int areaFilter = bitFilterForArea(areaProperties);
451 ObjectPropertyRequest<UnitProperties> objectPropertyRequest = ObjectPropertyRequest
452 .builder(handler, ObjectPropertyRequests.UNIT, 0, 1).selectNamed().areaFilter(areaFilter)
453 .selectAnyLoad().build();
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;
461 Map<String, Object> properties = new HashMap<>();
462 properties.put(THING_PROPERTIES_NAME, thingName);
463 properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
466 case UNIT_TYPE_HLC_ROOM:
467 case UNIT_TYPE_VIZIARF_ROOM:
468 thingUID = new ThingUID(THING_TYPE_ROOM, bridgeUID, thingID);
471 thingUID = new ThingUID(THING_TYPE_FLAG, bridgeUID, thingID);
473 case UNIT_TYPE_OUTPUT:
474 thingUID = new ThingUID(THING_TYPE_OUTPUT, bridgeUID, thingID);
477 case UNIT_TYPE_HLC_LOAD:
478 thingUID = new ThingUID(THING_TYPE_UNIT_UPB, bridgeUID, thingID);
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);
487 thingUID = new ThingUID(THING_TYPE_UNIT, bridgeUID, thingID);
488 logger.debug("Generic unit type: {}", thingType);
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);
504 * Generates zone items
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;
513 for (AreaProperties areaProperties : areas) {
514 int areaFilter = bitFilterForArea(areaProperties);
516 ObjectPropertyRequest<ZoneProperties> objectPropertyRequest = ObjectPropertyRequest
517 .builder(handler, ObjectPropertyRequests.ZONE, 0, 1).selectNamed().areaFilter(areaFilter)
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());
525 Map<String, Object> properties = new HashMap<>();
526 properties.put(THING_PROPERTIES_NAME, thingName);
527 properties.put(THING_PROPERTIES_AREA, areaProperties.getNumber());
529 ThingUID thingUID = new ThingUID(THING_TYPE_ZONE, bridgeUID, thingID);
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);