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.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.zway.internal.ZWayBindingConstants;
23 import org.openhab.binding.zway.internal.handler.ZWayBridgeHandler;
24 import org.openhab.core.config.discovery.AbstractDiscoveryService;
25 import org.openhab.core.config.discovery.DiscoveryResult;
26 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
27 import org.openhab.core.thing.Thing;
28 import org.openhab.core.thing.ThingStatus;
29 import org.openhab.core.thing.ThingUID;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import de.fh_zwickau.informatik.sensor.model.devices.Device;
34 import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
35 import de.fh_zwickau.informatik.sensor.model.devices.types.Camera;
36 import de.fh_zwickau.informatik.sensor.model.devices.types.SensorMultiline;
37 import de.fh_zwickau.informatik.sensor.model.devices.types.Text;
38 import de.fh_zwickau.informatik.sensor.model.locations.LocationList;
39 import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
42 * The {@link ZWayDeviceDiscoveryService} is responsible for device discovery.
44 * @author Patrick Hecker - Initial contribution
47 public class ZWayDeviceDiscoveryService extends AbstractDiscoveryService {
49 private final Logger logger = LoggerFactory.getLogger(getClass());
51 private static final int SEARCH_TIME = 60;
52 private static final int INITIAL_DELAY = 15;
53 private static final int SCAN_INTERVAL = 240;
55 private ZWayBridgeHandler mBridgeHandler;
56 private ZWayDeviceScan mZWayDeviceScanningRunnable;
57 private @Nullable ScheduledFuture<?> mZWayDeviceScanningJob;
59 public ZWayDeviceDiscoveryService(ZWayBridgeHandler bridgeHandler) {
60 super(ZWayBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS, SEARCH_TIME);
61 logger.debug("Initializing ZWayBridgeDiscoveryService");
62 mBridgeHandler = bridgeHandler;
63 mZWayDeviceScanningRunnable = new ZWayDeviceScan();
68 logger.debug("Starting scan on Z-Way Server {}", mBridgeHandler.getThing().getUID());
70 // Z-Way bridge have to be ONLINE because configuration is needed
71 if (!mBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
72 logger.debug("Z-Way bridge handler not found or not ONLINE.");
76 LocationList locationList = mBridgeHandler.getZWayApi().getLocations();
78 DeviceList deviceList = mBridgeHandler.getZWayApi().getDevices();
79 if (deviceList != null) {
80 Map<Integer, List<Device>> physicalDevices = deviceList.getDevicesGroupByNodeId();
81 for (Map.Entry<Integer, List<Device>> entry : physicalDevices.entrySet()) {
82 final Integer nodeId = entry.getKey();
83 List<Device> devices = entry.getValue();
85 final ThingUID bridgeUID = mBridgeHandler.getThing().getUID();
89 String deviceTypes = "";
91 for (Device device : devices) {
92 if (index != 0 && index != devices.size()) {
95 deviceTypes += device.getDeviceType();
98 // Add location, assuming that each (virtual) device is assigned to the same room
99 if (locationList != null) {
100 // Add only the location if this differs from globalRoom (with id 0)
101 if (device.getLocation() != -1 && device.getLocation() != 0) {
103 location = locationList.getLocationById(device.getLocation()).getTitle();
104 } catch (NullPointerException npe) {
110 logger.debug("Z-Way device found with {} virtual devices - device types: {}", devices.size(),
113 ZWaveDevice zwaveDevice = mBridgeHandler.getZWayApi().getZWaveDevice(nodeId);
114 if (zwaveDevice != null) {
115 String givenName = "Device " + nodeId;
116 if (!"".equals(zwaveDevice.getData().getGivenName().getValue())) {
117 givenName = zwaveDevice.getData().getGivenName().getValue();
118 } else if (!"".equals(zwaveDevice.getData().getDeviceTypeString().getValue())) {
119 givenName += " - " + zwaveDevice.getData().getDeviceTypeString().getValue();
121 // Add additional information as properties
122 String vendorString = zwaveDevice.getData().getVendorString().getValue();
123 if (!"".equals(zwaveDevice.getData().getVendorString().getValue())) {
124 givenName += " (" + vendorString + ")";
126 String manufacturerId = zwaveDevice.getData().getManufacturerId().getValue();
127 String deviceType = zwaveDevice.getData().getDeviceTypeString().getValue();
128 String zddxmlfile = zwaveDevice.getData().getZDDXMLFile().getValue();
129 String sdk = zwaveDevice.getData().getSDK().getValue();
131 ThingUID thingUID = new ThingUID(ZWayBindingConstants.THING_TYPE_DEVICE,
132 mBridgeHandler.getThing().getUID(), nodeId.toString());
136 * - Configuration: DEVICE_CONFIG_NODE_ID
137 * - System properties:
138 * --- PROPERTY_VENDOR
139 * --- other default properties not available
140 * - Custom properties:
141 * --- DEVICE_LOCATION
142 * --- DEVICE_MANUFACTURER_ID
143 * --- DEVICE_DEVICE_TYPE
144 * --- DEVICE_ZDDXMLFILE
147 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(givenName)
148 .withBridge(bridgeUID).withProperty(ZWayBindingConstants.DEVICE_CONFIG_NODE_ID, nodeId)
149 .withProperty(Thing.PROPERTY_VENDOR, vendorString)
150 .withProperty(ZWayBindingConstants.DEVICE_PROP_LOCATION, location)
151 .withProperty(ZWayBindingConstants.DEVICE_PROP_MANUFACTURER_ID, manufacturerId)
152 .withProperty(ZWayBindingConstants.DEVICE_PROP_DEVICE_TYPE, deviceType)
153 .withProperty(ZWayBindingConstants.DEVICE_PROP_ZDDXMLFILE, zddxmlfile)
154 .withProperty(ZWayBindingConstants.DEVICE_PROP_SDK, sdk).build();
155 thingDiscovered(discoveryResult);
157 logger.warn("Z-Wave device not loaded");
161 for (Device device : deviceList.getDevices()) {
162 if (device.getVisibility() && !device.getPermanentlyHidden()) {
163 if (ZWayBindingConstants.DISCOVERY_IGNORED_DEVICES.contains(device.getDeviceId().split("_")[0])) {
164 logger.debug("Skip device: {}", device.getMetrics().getTitle());
168 if (device instanceof SensorMultiline || device instanceof Camera || device instanceof Text) {
169 logger.debug("Skip device because the device type is not supported: {}",
170 device.getMetrics().getTitle());
174 ThingUID bridgeUID = mBridgeHandler.getThing().getUID();
176 String location = "";
177 // Add location, assuming that each (virtual) device is assigned to the same room
178 if (locationList != null) {
179 // Add only the location if this differs from globalRoom (with id 0)
180 if (device.getLocation() != -1 && device.getLocation() != 0) {
182 location = locationList.getLocationById(device.getLocation()).getTitle();
183 } catch (NullPointerException npe) {
189 logger.debug("Z-Way virtual device found with device type: {} - {} - {}", device.getDeviceType(),
190 device.getMetrics().getProbeTitle(), device.getNodeId());
192 ThingUID thingUID = new ThingUID(ZWayBindingConstants.THING_TYPE_VIRTUAL_DEVICE,
193 mBridgeHandler.getThing().getUID(), device.getDeviceId());
194 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
195 .withLabel(device.getMetrics().getTitle()).withBridge(bridgeUID)
196 .withProperty(ZWayBindingConstants.DEVICE_CONFIG_VIRTUAL_DEVICE_ID, device.getDeviceId())
197 .withProperty(ZWayBindingConstants.DEVICE_PROP_LOCATION, location).build();
198 thingDiscovered(discoveryResult);
202 logger.warn("Devices not loaded");
207 protected void startScan() {
212 protected synchronized void stopScan() {
214 removeOlderResults(getTimestampOfLastScan());
218 protected void startBackgroundDiscovery() {
219 ScheduledFuture<?> mZWayDeviceScanningJobLocal = mZWayDeviceScanningJob;
220 if (mZWayDeviceScanningJobLocal == null || mZWayDeviceScanningJobLocal.isCancelled()) {
221 logger.debug("Starting background scanning job");
222 mZWayDeviceScanningJob = scheduler.scheduleWithFixedDelay(mZWayDeviceScanningRunnable, INITIAL_DELAY,
223 SCAN_INTERVAL, TimeUnit.SECONDS);
225 logger.debug("Scanning job is allready active");
230 protected void stopBackgroundDiscovery() {
231 ScheduledFuture<?> mZWayDeviceScanningJobLocal = mZWayDeviceScanningJob;
232 if (mZWayDeviceScanningJobLocal != null && !mZWayDeviceScanningJobLocal.isCancelled()) {
233 mZWayDeviceScanningJobLocal.cancel(false);
234 mZWayDeviceScanningJob = null;
238 public class ZWayDeviceScan implements Runnable {