2 * Copyright (c) 2010-2023 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.zway.internal.discovery;
15 import java.util.List;
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
20 import org.openhab.binding.zway.internal.ZWayBindingConstants;
21 import org.openhab.binding.zway.internal.handler.ZWayBridgeHandler;
22 import org.openhab.core.config.discovery.AbstractDiscoveryService;
23 import org.openhab.core.config.discovery.DiscoveryResult;
24 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
25 import org.openhab.core.thing.Thing;
26 import org.openhab.core.thing.ThingStatus;
27 import org.openhab.core.thing.ThingUID;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
31 import de.fh_zwickau.informatik.sensor.model.devices.Device;
32 import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
33 import de.fh_zwickau.informatik.sensor.model.devices.types.Camera;
34 import de.fh_zwickau.informatik.sensor.model.devices.types.SensorMultiline;
35 import de.fh_zwickau.informatik.sensor.model.devices.types.Text;
36 import de.fh_zwickau.informatik.sensor.model.locations.LocationList;
37 import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
40 * The {@link ZWayDeviceDiscoveryService} is responsible for device discovery.
42 * @author Patrick Hecker - Initial contribution
44 public class ZWayDeviceDiscoveryService extends AbstractDiscoveryService {
46 private final Logger logger = LoggerFactory.getLogger(getClass());
48 private static final int SEARCH_TIME = 60;
49 private static final int INITIAL_DELAY = 15;
50 private static final int SCAN_INTERVAL = 240;
52 private ZWayBridgeHandler mBridgeHandler;
53 private ZWayDeviceScan mZWayDeviceScanningRunnable;
54 private ScheduledFuture<?> mZWayDeviceScanningJob;
56 public ZWayDeviceDiscoveryService(ZWayBridgeHandler bridgeHandler) {
57 super(ZWayBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS, SEARCH_TIME);
58 logger.debug("Initializing ZWayBridgeDiscoveryService");
59 mBridgeHandler = bridgeHandler;
60 mZWayDeviceScanningRunnable = new ZWayDeviceScan();
65 logger.debug("Starting scan on Z-Way Server {}", mBridgeHandler.getThing().getUID());
67 // Z-Way bridge have to be ONLINE because configuration is needed
68 if (mBridgeHandler == null || !mBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
69 logger.debug("Z-Way bridge handler not found or not ONLINE.");
73 LocationList locationList = mBridgeHandler.getZWayApi().getLocations();
75 DeviceList deviceList = mBridgeHandler.getZWayApi().getDevices();
76 if (deviceList != null) {
77 Map<Integer, List<Device>> physicalDevices = deviceList.getDevicesGroupByNodeId();
78 for (Map.Entry<Integer, List<Device>> entry : physicalDevices.entrySet()) {
79 final Integer nodeId = entry.getKey();
80 List<Device> devices = entry.getValue();
82 final ThingUID bridgeUID = mBridgeHandler.getThing().getUID();
86 String deviceTypes = "";
88 for (Device device : devices) {
89 if (index != 0 && index != devices.size()) {
92 deviceTypes += device.getDeviceType();
95 // Add location, assuming that each (virtual) device is assigned to the same room
96 if (locationList != null) {
97 // Add only the location if this differs from globalRoom (with id 0)
98 if (device.getLocation() != -1 && device.getLocation() != 0) {
100 location = locationList.getLocationById(device.getLocation()).getTitle();
101 } catch (NullPointerException npe) {
107 logger.debug("Z-Way device found with {} virtual devices - device types: {}", devices.size(),
110 ZWaveDevice zwaveDevice = mBridgeHandler.getZWayApi().getZWaveDevice(nodeId);
111 if (zwaveDevice != null) {
112 String givenName = "Device " + nodeId;
113 if (!zwaveDevice.getData().getGivenName().getValue().equals("")) {
114 givenName = zwaveDevice.getData().getGivenName().getValue();
115 } else if (!zwaveDevice.getData().getDeviceTypeString().getValue().equals("")) {
116 givenName += " - " + zwaveDevice.getData().getDeviceTypeString().getValue();
118 // Add additional information as properties
119 String vendorString = zwaveDevice.getData().getVendorString().getValue();
120 if (!zwaveDevice.getData().getVendorString().getValue().equals("")) {
121 givenName += " (" + vendorString + ")";
123 String manufacturerId = zwaveDevice.getData().getManufacturerId().getValue();
124 String deviceType = zwaveDevice.getData().getDeviceTypeString().getValue();
125 String zddxmlfile = zwaveDevice.getData().getZDDXMLFile().getValue();
126 String sdk = zwaveDevice.getData().getSDK().getValue();
128 ThingUID thingUID = new ThingUID(ZWayBindingConstants.THING_TYPE_DEVICE,
129 mBridgeHandler.getThing().getUID(), nodeId.toString());
133 * - Configuration: DEVICE_CONFIG_NODE_ID
134 * - System properties:
135 * --- PROPERTY_VENDOR
136 * --- other default properties not available
137 * - Custom properties:
138 * --- DEVICE_LOCATION
139 * --- DEVICE_MANUFACTURER_ID
140 * --- DEVICE_DEVICE_TYPE
141 * --- DEVICE_ZDDXMLFILE
144 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(givenName)
145 .withBridge(bridgeUID).withProperty(ZWayBindingConstants.DEVICE_CONFIG_NODE_ID, nodeId)
146 .withProperty(Thing.PROPERTY_VENDOR, vendorString)
147 .withProperty(ZWayBindingConstants.DEVICE_PROP_LOCATION, location)
148 .withProperty(ZWayBindingConstants.DEVICE_PROP_MANUFACTURER_ID, manufacturerId)
149 .withProperty(ZWayBindingConstants.DEVICE_PROP_DEVICE_TYPE, deviceType)
150 .withProperty(ZWayBindingConstants.DEVICE_PROP_ZDDXMLFILE, zddxmlfile)
151 .withProperty(ZWayBindingConstants.DEVICE_PROP_SDK, sdk).build();
152 thingDiscovered(discoveryResult);
154 logger.warn("Z-Wave device not loaded");
158 for (Device device : deviceList.getDevices()) {
159 if (device.getVisibility() && !device.getPermanentlyHidden()) {
160 if (ZWayBindingConstants.DISCOVERY_IGNORED_DEVICES.contains(device.getDeviceId().split("_")[0])) {
161 logger.debug("Skip device: {}", device.getMetrics().getTitle());
165 if (device instanceof SensorMultiline || device instanceof Camera || device instanceof Text) {
166 logger.debug("Skip device because the device type is not supported: {}",
167 device.getMetrics().getTitle());
171 ThingUID bridgeUID = mBridgeHandler.getThing().getUID();
173 String location = "";
174 // Add location, assuming that each (virtual) device is assigned to the same room
175 if (locationList != null) {
176 // Add only the location if this differs from globalRoom (with id 0)
177 if (device.getLocation() != -1 && device.getLocation() != 0) {
179 location = locationList.getLocationById(device.getLocation()).getTitle();
180 } catch (NullPointerException npe) {
186 logger.debug("Z-Way virtual device found with device type: {} - {} - {}", device.getDeviceType(),
187 device.getMetrics().getProbeTitle(), device.getNodeId());
189 ThingUID thingUID = new ThingUID(ZWayBindingConstants.THING_TYPE_VIRTUAL_DEVICE,
190 mBridgeHandler.getThing().getUID(), device.getDeviceId());
191 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
192 .withLabel(device.getMetrics().getTitle()).withBridge(bridgeUID)
193 .withProperty(ZWayBindingConstants.DEVICE_CONFIG_VIRTUAL_DEVICE_ID, device.getDeviceId())
194 .withProperty(ZWayBindingConstants.DEVICE_PROP_LOCATION, location).build();
195 thingDiscovered(discoveryResult);
199 logger.warn("Devices not loaded");
204 protected void startScan() {
209 protected synchronized void stopScan() {
211 removeOlderResults(getTimestampOfLastScan());
215 protected void startBackgroundDiscovery() {
216 if (mZWayDeviceScanningJob == null || mZWayDeviceScanningJob.isCancelled()) {
217 logger.debug("Starting background scanning job");
218 mZWayDeviceScanningJob = scheduler.scheduleWithFixedDelay(mZWayDeviceScanningRunnable, INITIAL_DELAY,
219 SCAN_INTERVAL, TimeUnit.SECONDS);
221 logger.debug("Scanning job is allready active");
226 protected void stopBackgroundDiscovery() {
227 if (mZWayDeviceScanningJob != null && !mZWayDeviceScanningJob.isCancelled()) {
228 mZWayDeviceScanningJob.cancel(false);
229 mZWayDeviceScanningJob = null;
233 public class ZWayDeviceScan implements Runnable {