]> git.basschouten.com Git - openhab-addons.git/blob
6e6662879f4bf1c0d7dbce8fe464f49915185dac
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.openwebnet.internal.discovery;
14
15 import java.util.HashMap;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.concurrent.CountDownLatch;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21 import java.util.stream.Stream;
22
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;
45
46 /**
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.
50  *
51  * @author Massimo Valla - Initial contribution
52  */
53 @NonNullByDefault
54 @Component(service = DiscoveryService.class, configurationPid = "discovery.openwebnet")
55
56 public class UsbGatewayDiscoveryService extends AbstractDiscoveryService implements GatewayListener {
57
58     private final Logger logger = LoggerFactory.getLogger(UsbGatewayDiscoveryService.class);
59
60     private static final int DISCOVERY_TIMEOUT_SECONDS = 30;
61     private static final int PORT_CHECK_TIMEOUT_MSEC = 1500;
62
63     private CountDownLatch portCheckLatch = new CountDownLatch(1);
64     private @Nullable ScheduledFuture<?> connectTimeout;
65
66     private final SerialPortManager serialPortManager;
67     private @Nullable USBGateway zbGateway;
68
69     private String currentScannedPortName = "";
70
71     /**
72      * Keeps a boolean during time discovery process in progress.
73      */
74     private boolean scanning;
75
76     /**
77      * Constructs a new UsbGatewayDiscoveryService with the specified Zigbee USB Bridge ThingTypeUID
78      */
79     @Activate
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;
84     }
85
86     /**
87      * Starts a new discovery scan. All available Serial Ports are scanned.
88      */
89     @Override
90     protected void startScan() {
91         logger.debug("Started OpenWebNet Zigbee USB Gateway discovery scan");
92         removeOlderResults(getTimestampOfLastScan());
93         scanning = true;
94         Stream<SerialPortIdentifier> portEnum = serialPortManager.getIdentifiers();
95         // Check each available serial port
96         try {
97             for (SerialPortIdentifier portIdentifier : portEnum.toArray(SerialPortIdentifier[]::new)) {
98                 if (scanning) {
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());
104                     } else {
105                         logger.debug("[{}] trying to connect to a Zigbee USB Gateway...", currentScannedPortName);
106                         USBGateway gw = new USBGateway(currentScannedPortName);
107                         zbGateway = gw;
108                         gw.subscribe(this);
109                         portCheckLatch = new CountDownLatch(1);
110                         connectTimeout = scheduler.schedule(() -> {
111                             logger.debug("[{}] timeout expired", currentScannedPortName);
112                             endGwConnection();
113                             portCheckLatch.countDown();
114                         }, PORT_CHECK_TIMEOUT_MSEC, TimeUnit.MILLISECONDS);
115                         try {
116                             gw.connect();
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();
122                             endGwConnection();
123                         }
124                     }
125                     logger.debug("[{}] == finished checking port", currentScannedPortName);
126                 }
127             }
128             logger.debug("Finished checking all serial ports");
129         } catch (InterruptedException ie) {
130             logger.warn("[{}] interrupted: {}", currentScannedPortName, ie.getMessage());
131             endGwConnection();
132             logger.debug("Interrupted while checking serial ports");
133         }
134     }
135
136     @Override
137     protected synchronized void stopScan() {
138         scanning = false;
139         cancelConnectTimeout();
140         endGwConnection();
141         portCheckLatch.countDown();
142         super.stopScan();
143         logger.debug("Stopped OpenWebNet Zigbee USB Gateway discovery scan");
144     }
145
146     /**
147      * Ends connection to the gateway
148      */
149     private void endGwConnection() {
150         USBGateway gw = zbGateway;
151         if (gw != null) {
152             gw.closeConnection();
153             zbGateway = null;
154             logger.debug("[{}] connection to gateway closed", currentScannedPortName);
155         }
156     }
157
158     private void cancelConnectTimeout() {
159         ScheduledFuture<?> ct = connectTimeout;
160         if (ct != null && !ct.isDone()) {
161             ct.cancel(false);
162             ct = null;
163             logger.debug("[{}] timeout cancelled", currentScannedPortName);
164         }
165     }
166
167     /**
168      * Create and notify a new Zigbee USB Gateway thing has been discovered
169      */
170     private void bridgeDiscovered() {
171         USBGateway gw = zbGateway;
172         if (gw != null) {
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));
180
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);
187         }
188     }
189
190     @Override
191     public void onConnected() {
192         logger.debug("[{}] found Zigbee USB Gateway", currentScannedPortName);
193         cancelConnectTimeout();
194         bridgeDiscovered();
195         endGwConnection();
196         portCheckLatch.countDown();
197     }
198
199     @Override
200     public void onConnectionError(@Nullable OWNException error) {
201         OWNException e = error;
202         String msg = (e != null ? e.getMessage() : "");
203         logger.debug("[{}] onConnectionError(): {}", currentScannedPortName, msg);
204     }
205
206     @Override
207     public void onConnectionClosed() {
208         logger.debug("UsbGatewayDiscoveryService received onConnectionClosed()");
209     }
210
211     @Override
212     public void onDisconnected(@Nullable OWNException error) {
213         logger.debug("UsbGatewayDiscoveryService received onDisconnected()");
214     }
215
216     @Override
217     public void onReconnected() {
218         logger.debug("UsbGatewayDiscoveryService received onReconnected()");
219     }
220
221     @Override
222     public void onEventMessage(@Nullable OpenMessage msg) {
223         logger.debug("UsbGatewayDiscoveryService received onEventMessage(): {}", msg);
224     }
225
226     @Override
227     public void onNewDevice(@Nullable Where where, @Nullable OpenDeviceType deviceType,
228             @Nullable BaseOpenMessage message) {
229         logger.debug("UsbGatewayDiscoveryService received onNewDevice()");
230     }
231
232     @Override
233     public void onDiscoveryCompleted() {
234         logger.debug("UsbGatewayDiscoveryService received onDiscoveryCompleted()");
235     }
236 }