]> git.basschouten.com Git - openhab-addons.git/blob
0744d04f0a82ecffbf8a720b4462a7bc18a631b3
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.Collections;
16 import java.util.HashMap;
17 import java.util.Map;
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(Collections.singleton(OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY), DISCOVERY_TIMEOUT_SECONDS,
82                 false);
83         // Obtain the serial port manager service using an OSGi reference
84         serialPortManager = spm;
85     }
86
87     /**
88      * Starts a new discovery scan. All available Serial Ports are scanned.
89      */
90     @Override
91     protected void startScan() {
92         logger.debug("Started OpenWebNet Zigbee USB Gateway discovery scan");
93         removeOlderResults(getTimestampOfLastScan());
94         scanning = true;
95         Stream<SerialPortIdentifier> portEnum = serialPortManager.getIdentifiers();
96         // Check each available serial port
97         try {
98             for (SerialPortIdentifier portIdentifier : portEnum.toArray(SerialPortIdentifier[]::new)) {
99                 if (scanning) {
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());
105                     } else {
106                         logger.debug("[{}] trying to connect to a Zigbee USB Gateway...", currentScannedPortName);
107                         USBGateway gw = new USBGateway(currentScannedPortName);
108                         zbGateway = gw;
109                         gw.subscribe(this);
110                         portCheckLatch = new CountDownLatch(1);
111                         connectTimeout = scheduler.schedule(() -> {
112                             logger.debug("[{}] timeout expired", currentScannedPortName);
113                             endGwConnection();
114                             portCheckLatch.countDown();
115                         }, PORT_CHECK_TIMEOUT_MSEC, TimeUnit.MILLISECONDS);
116                         try {
117                             gw.connect();
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();
123                             endGwConnection();
124                         }
125                     }
126                     logger.debug("[{}] == finished checking port", currentScannedPortName);
127                 }
128             }
129             logger.debug("Finished checking all serial ports");
130         } catch (InterruptedException ie) {
131             logger.warn("[{}] interrupted: {}", currentScannedPortName, ie.getMessage());
132             endGwConnection();
133             logger.debug("Interrupted while checking serial ports");
134         }
135     }
136
137     @Override
138     protected synchronized void stopScan() {
139         scanning = false;
140         cancelConnectTimeout();
141         endGwConnection();
142         portCheckLatch.countDown();
143         super.stopScan();
144         logger.debug("Stopped OpenWebNet Zigbee USB Gateway discovery scan");
145     }
146
147     /**
148      * Ends connection to the gateway
149      */
150     private void endGwConnection() {
151         USBGateway gw = zbGateway;
152         if (gw != null) {
153             gw.closeConnection();
154             zbGateway = null;
155             logger.debug("[{}] connection to gateway closed", currentScannedPortName);
156         }
157     }
158
159     private void cancelConnectTimeout() {
160         ScheduledFuture<?> ct = connectTimeout;
161         if (ct != null && !ct.isDone()) {
162             ct.cancel(false);
163             ct = null;
164             logger.debug("[{}] timeout cancelled", currentScannedPortName);
165         }
166     }
167
168     /**
169      * Create and notify a new Zigbee USB Gateway thing has been discovered
170      */
171     private void bridgeDiscovered() {
172         USBGateway gw = zbGateway;
173         if (gw != null) {
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));
181
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);
188         }
189     }
190
191     @Override
192     public void onConnected() {
193         logger.debug("[{}] found Zigbee USB Gateway", currentScannedPortName);
194         cancelConnectTimeout();
195         bridgeDiscovered();
196         endGwConnection();
197         portCheckLatch.countDown();
198     }
199
200     @Override
201     public void onConnectionError(@Nullable OWNException error) {
202         OWNException e = error;
203         String msg = (e != null ? e.getMessage() : "");
204         logger.debug("[{}] onConnectionError(): {}", currentScannedPortName, msg);
205     }
206
207     @Override
208     public void onConnectionClosed() {
209         logger.debug("UsbGatewayDiscoveryService received onConnectionClosed()");
210     }
211
212     @Override
213     public void onDisconnected(@Nullable OWNException error) {
214         logger.debug("UsbGatewayDiscoveryService received onDisconnected()");
215     }
216
217     @Override
218     public void onReconnected() {
219         logger.debug("UsbGatewayDiscoveryService received onReconnected()");
220     }
221
222     @Override
223     public void onEventMessage(@Nullable OpenMessage msg) {
224         logger.debug("UsbGatewayDiscoveryService received onEventMessage(): {}", msg);
225     }
226
227     @Override
228     public void onNewDevice(@Nullable Where where, @Nullable OpenDeviceType deviceType,
229             @Nullable BaseOpenMessage message) {
230         logger.debug("UsbGatewayDiscoveryService received onNewDevice()");
231     }
232
233     @Override
234     public void onDiscoveryCompleted() {
235         logger.debug("UsbGatewayDiscoveryService received onDiscoveryCompleted()");
236     }
237 }