2 * Copyright (c) 2010-2022 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.openwebnet.internal.discovery;
15 import java.util.Collections;
16 import java.util.HashMap;
18 import java.util.concurrent.CountDownLatch;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21 import java.util.stream.Stream;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
26 import org.openhab.core.config.discovery.AbstractDiscoveryService;
27 import org.openhab.core.config.discovery.DiscoveryResult;
28 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
29 import org.openhab.core.config.discovery.DiscoveryService;
30 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
31 import org.openhab.core.io.transport.serial.SerialPortManager;
32 import org.openhab.core.thing.ThingUID;
33 import org.openwebnet4j.GatewayListener;
34 import org.openwebnet4j.OpenDeviceType;
35 import org.openwebnet4j.USBGateway;
36 import org.openwebnet4j.communication.OWNException;
37 import org.openwebnet4j.message.BaseOpenMessage;
38 import org.openwebnet4j.message.OpenMessage;
39 import org.openwebnet4j.message.Where;
40 import org.osgi.service.component.annotations.Activate;
41 import org.osgi.service.component.annotations.Component;
42 import org.osgi.service.component.annotations.Reference;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * The {@link UsbGatewayDiscoveryService} extends {@link AbstractDiscoveryService} to detect Zigbee USB gateways
48 * connected via serial port. The service will iterate over the available serial ports and open each one to test if a
49 * OpenWebNet Zigbee USB gateway is connected. On successful connection, a new DiscoveryResult is created.
51 * @author Massimo Valla - Initial contribution
54 @Component(service = DiscoveryService.class, configurationPid = "discovery.openwebnet")
56 public class UsbGatewayDiscoveryService extends AbstractDiscoveryService implements GatewayListener {
58 private final Logger logger = LoggerFactory.getLogger(UsbGatewayDiscoveryService.class);
60 private static final int DISCOVERY_TIMEOUT_SECONDS = 30;
61 private static final int PORT_CHECK_TIMEOUT_MSEC = 1500;
63 private CountDownLatch portCheckLatch = new CountDownLatch(1);
64 private @Nullable ScheduledFuture<?> connectTimeout;
66 private final SerialPortManager serialPortManager;
67 private @Nullable USBGateway zbGateway;
69 private String currentScannedPortName = "";
72 * Keeps a boolean during time discovery process in progress.
74 private boolean scanning;
77 * Constructs a new UsbGatewayDiscoveryService with the specified Zigbee USB Bridge ThingTypeUID
80 public UsbGatewayDiscoveryService(final @Reference SerialPortManager spm) {
81 super(Collections.singleton(OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY), DISCOVERY_TIMEOUT_SECONDS,
83 // Obtain the serial port manager service using an OSGi reference
84 serialPortManager = spm;
88 * Starts a new discovery scan. All available Serial Ports are scanned.
91 protected void startScan() {
92 logger.debug("Started OpenWebNet Zigbee USB Gateway discovery scan");
93 removeOlderResults(getTimestampOfLastScan());
95 Stream<SerialPortIdentifier> portEnum = serialPortManager.getIdentifiers();
96 // Check each available serial port
98 for (SerialPortIdentifier portIdentifier : portEnum.toArray(SerialPortIdentifier[]::new)) {
100 currentScannedPortName = portIdentifier.getName();
101 logger.debug("[{}] == checking serial port", currentScannedPortName);
102 if (portIdentifier.isCurrentlyOwned()) {
103 logger.debug("[{}] serial port is owned by: {}", currentScannedPortName,
104 portIdentifier.getCurrentOwner());
106 logger.debug("[{}] trying to connect to a Zigbee USB Gateway...", currentScannedPortName);
107 USBGateway gw = new USBGateway(currentScannedPortName);
110 portCheckLatch = new CountDownLatch(1);
111 connectTimeout = scheduler.schedule(() -> {
112 logger.debug("[{}] timeout expired", currentScannedPortName);
114 portCheckLatch.countDown();
115 }, PORT_CHECK_TIMEOUT_MSEC, TimeUnit.MILLISECONDS);
118 portCheckLatch.await();
119 } catch (OWNException e) {
120 logger.debug("[{}] OWNException while trying to connect to a Zigbee USB Gateway: {}",
121 currentScannedPortName, e.getMessage());
122 cancelConnectTimeout();
126 logger.debug("[{}] == finished checking port", currentScannedPortName);
129 logger.debug("Finished checking all serial ports");
130 } catch (InterruptedException ie) {
131 logger.warn("[{}] interrupted: {}", currentScannedPortName, ie.getMessage());
133 logger.debug("Interrupted while checking serial ports");
138 protected synchronized void stopScan() {
140 cancelConnectTimeout();
142 portCheckLatch.countDown();
144 logger.debug("Stopped OpenWebNet Zigbee USB Gateway discovery scan");
148 * Ends connection to the gateway
150 private void endGwConnection() {
151 USBGateway gw = zbGateway;
153 gw.closeConnection();
155 logger.debug("[{}] connection to gateway closed", currentScannedPortName);
159 private void cancelConnectTimeout() {
160 ScheduledFuture<?> ct = connectTimeout;
161 if (ct != null && !ct.isDone()) {
164 logger.debug("[{}] timeout cancelled", currentScannedPortName);
169 * Create and notify a new Zigbee USB Gateway thing has been discovered
171 private void bridgeDiscovered() {
172 USBGateway gw = zbGateway;
174 int gatewayZigBeeId = gw.getZigBeeIdAsDecimal();
175 ThingUID gatewayUID = new ThingUID(OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY,
176 Integer.toString(gatewayZigBeeId));
177 Map<String, Object> gwProperties = new HashMap<>(3);
178 gwProperties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_SERIAL_PORT, gw.getSerialPortName());
179 gwProperties.put(OpenWebNetBindingConstants.PROPERTY_FIRMWARE_VERSION, gw.getFirmwareVersion());
180 gwProperties.put(OpenWebNetBindingConstants.PROPERTY_ZIGBEEID, String.valueOf(gatewayZigBeeId));
182 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(gatewayUID).withProperties(gwProperties)
183 .withLabel(OpenWebNetBindingConstants.THING_LABEL_ZB_GATEWAY + " (" + gw.getSerialPortName() + ")")
184 .withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_ZIGBEEID).build();
185 logger.debug("--- Zigbee USB Gateway thing discovered: {} fw: {}", discoveryResult.getLabel(),
186 gw.getFirmwareVersion());
187 thingDiscovered(discoveryResult);
192 public void onConnected() {
193 logger.debug("[{}] found Zigbee USB Gateway", currentScannedPortName);
194 cancelConnectTimeout();
197 portCheckLatch.countDown();
201 public void onConnectionError(@Nullable OWNException error) {
202 OWNException e = error;
203 String msg = (e != null ? e.getMessage() : "");
204 logger.debug("[{}] onConnectionError(): {}", currentScannedPortName, msg);
208 public void onConnectionClosed() {
209 logger.debug("UsbGatewayDiscoveryService received onConnectionClosed()");
213 public void onDisconnected(@Nullable OWNException error) {
214 logger.debug("UsbGatewayDiscoveryService received onDisconnected()");
218 public void onReconnected() {
219 logger.debug("UsbGatewayDiscoveryService received onReconnected()");
223 public void onEventMessage(@Nullable OpenMessage msg) {
224 logger.debug("UsbGatewayDiscoveryService received onEventMessage(): {}", msg);
228 public void onNewDevice(@Nullable Where where, @Nullable OpenDeviceType deviceType,
229 @Nullable BaseOpenMessage message) {
230 logger.debug("UsbGatewayDiscoveryService received onNewDevice()");
234 public void onDiscoveryCompleted() {
235 logger.debug("UsbGatewayDiscoveryService received onDiscoveryCompleted()");