2 * Copyright (c) 2010-2024 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.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(Set.of(OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY), DISCOVERY_TIMEOUT_SECONDS, false);
82 // Obtain the serial port manager service using an OSGi reference
83 serialPortManager = spm;
87 * Starts a new discovery scan. All available Serial Ports are scanned.
90 protected void startScan() {
91 logger.debug("Started OpenWebNet Zigbee USB Gateway discovery scan");
92 removeOlderResults(getTimestampOfLastScan());
94 Stream<SerialPortIdentifier> portEnum = serialPortManager.getIdentifiers();
95 // Check each available serial port
97 for (SerialPortIdentifier portIdentifier : portEnum.toArray(SerialPortIdentifier[]::new)) {
99 currentScannedPortName = portIdentifier.getName();
100 logger.debug("[{}] == checking serial port", currentScannedPortName);
101 if (portIdentifier.isCurrentlyOwned()) {
102 logger.debug("[{}] serial port is owned by: {}", currentScannedPortName,
103 portIdentifier.getCurrentOwner());
105 logger.debug("[{}] trying to connect to a Zigbee USB Gateway...", currentScannedPortName);
106 USBGateway gw = new USBGateway(currentScannedPortName);
109 portCheckLatch = new CountDownLatch(1);
110 connectTimeout = scheduler.schedule(() -> {
111 logger.debug("[{}] timeout expired", currentScannedPortName);
113 portCheckLatch.countDown();
114 }, PORT_CHECK_TIMEOUT_MSEC, TimeUnit.MILLISECONDS);
117 portCheckLatch.await();
118 } catch (OWNException e) {
119 logger.debug("[{}] OWNException while trying to connect to a Zigbee USB Gateway: {}",
120 currentScannedPortName, e.getMessage());
121 cancelConnectTimeout();
125 logger.debug("[{}] == finished checking port", currentScannedPortName);
128 logger.debug("Finished checking all serial ports");
129 } catch (InterruptedException ie) {
130 logger.warn("[{}] interrupted: {}", currentScannedPortName, ie.getMessage());
132 logger.debug("Interrupted while checking serial ports");
137 protected synchronized void stopScan() {
139 cancelConnectTimeout();
141 portCheckLatch.countDown();
143 logger.debug("Stopped OpenWebNet Zigbee USB Gateway discovery scan");
147 * Ends connection to the gateway
149 private void endGwConnection() {
150 USBGateway gw = zbGateway;
152 gw.closeConnection();
154 logger.debug("[{}] connection to gateway closed", currentScannedPortName);
158 private void cancelConnectTimeout() {
159 ScheduledFuture<?> ct = connectTimeout;
160 if (ct != null && !ct.isDone()) {
163 logger.debug("[{}] timeout cancelled", currentScannedPortName);
168 * Create and notify a new Zigbee USB Gateway thing has been discovered
170 private void bridgeDiscovered() {
171 USBGateway gw = zbGateway;
173 int gatewayZigBeeId = gw.getZigBeeIdAsDecimal();
174 ThingUID gatewayUID = new ThingUID(OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY,
175 Integer.toString(gatewayZigBeeId));
176 Map<String, Object> gwProperties = new HashMap<>(3);
177 gwProperties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_SERIAL_PORT, gw.getSerialPortName());
178 gwProperties.put(OpenWebNetBindingConstants.PROPERTY_FIRMWARE_VERSION, gw.getFirmwareVersion());
179 gwProperties.put(OpenWebNetBindingConstants.PROPERTY_ZIGBEEID, String.valueOf(gatewayZigBeeId));
181 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(gatewayUID).withProperties(gwProperties)
182 .withLabel(OpenWebNetBindingConstants.THING_LABEL_ZB_GATEWAY + " (" + gw.getSerialPortName() + ")")
183 .withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_ZIGBEEID).build();
184 logger.debug("--- Zigbee USB Gateway thing discovered: {} fw: {}", discoveryResult.getLabel(),
185 gw.getFirmwareVersion());
186 thingDiscovered(discoveryResult);
191 public void onConnected() {
192 logger.debug("[{}] found Zigbee USB Gateway", currentScannedPortName);
193 cancelConnectTimeout();
196 portCheckLatch.countDown();
200 public void onConnectionError(@Nullable OWNException error) {
201 OWNException e = error;
202 String msg = (e != null ? e.getMessage() : "");
203 logger.debug("[{}] onConnectionError(): {}", currentScannedPortName, msg);
207 public void onConnectionClosed() {
208 logger.debug("UsbGatewayDiscoveryService received onConnectionClosed()");
212 public void onDisconnected(@Nullable OWNException error) {
213 logger.debug("UsbGatewayDiscoveryService received onDisconnected()");
217 public void onReconnected() {
218 logger.debug("UsbGatewayDiscoveryService received onReconnected()");
222 public void onEventMessage(@Nullable OpenMessage msg) {
223 logger.debug("UsbGatewayDiscoveryService received onEventMessage(): {}", msg);
227 public void onNewDevice(@Nullable Where where, @Nullable OpenDeviceType deviceType,
228 @Nullable BaseOpenMessage message) {
229 logger.debug("UsbGatewayDiscoveryService received onNewDevice()");
233 public void onDiscoveryCompleted() {
234 logger.debug("UsbGatewayDiscoveryService received onDiscoveryCompleted()");