]> git.basschouten.com Git - openhab-addons.git/blob
b59d3dd425d89c99da977eb783b5288cad302c95
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.bluetooth;
14
15 import java.util.Set;
16 import java.util.concurrent.CopyOnWriteArraySet;
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
19
20 import org.eclipse.jdt.annotation.DefaultLocation;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState;
23 import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification;
24 import org.openhab.core.thing.ChannelUID;
25 import org.openhab.core.thing.Thing;
26 import org.openhab.core.thing.ThingStatus;
27 import org.openhab.core.thing.ThingStatusDetail;
28 import org.openhab.core.types.Command;
29 import org.openhab.core.types.RefreshType;
30 import org.openhab.core.util.HexUtils;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * This is a base implementation for more specific thing handlers that require constant connection to bluetooth devices.
36  *
37  * @author Kai Kreuzer - Initial contribution and API
38  */
39 @NonNullByDefault({ DefaultLocation.PARAMETER, DefaultLocation.RETURN_TYPE, DefaultLocation.ARRAY_CONTENTS,
40         DefaultLocation.TYPE_ARGUMENT, DefaultLocation.TYPE_BOUND, DefaultLocation.TYPE_PARAMETER })
41 public class ConnectedBluetoothHandler extends BeaconBluetoothHandler {
42
43     private final Logger logger = LoggerFactory.getLogger(ConnectedBluetoothHandler.class);
44     private ScheduledFuture<?> connectionJob;
45
46     // internal flag for the service resolution status
47     protected volatile boolean resolved = false;
48
49     protected final Set<BluetoothCharacteristic> deviceCharacteristics = new CopyOnWriteArraySet<>();
50
51     public ConnectedBluetoothHandler(Thing thing) {
52         super(thing);
53     }
54
55     @Override
56     public void initialize() {
57         super.initialize();
58
59         connectionJob = scheduler.scheduleWithFixedDelay(() -> {
60             try {
61                 if (device.getConnectionState() != ConnectionState.CONNECTED) {
62                     device.connect();
63                     // we do not set the Thing status here, because we will anyhow receive a call to
64                     // onConnectionStateChange
65                 } else {
66                     // just in case it was already connected to begin with
67                     updateStatus(ThingStatus.ONLINE);
68                     if (!resolved && !device.discoverServices()) {
69                         logger.debug("Error while discovering services");
70                     }
71                 }
72             } catch (RuntimeException ex) {
73                 logger.warn("Unexpected error occurred", ex);
74             }
75         }, 0, 30, TimeUnit.SECONDS);
76     }
77
78     @Override
79     public void dispose() {
80         if (connectionJob != null) {
81             connectionJob.cancel(true);
82             connectionJob = null;
83         }
84         super.dispose();
85     }
86
87     @Override
88     public void handleCommand(ChannelUID channelUID, Command command) {
89         super.handleCommand(channelUID, command);
90
91         // Handle REFRESH
92         if (command == RefreshType.REFRESH) {
93             for (BluetoothCharacteristic characteristic : deviceCharacteristics) {
94                 if (characteristic.getGattCharacteristic() != null
95                         && channelUID.getId().equals(characteristic.getGattCharacteristic().name())) {
96                     device.readCharacteristic(characteristic);
97                     break;
98                 }
99             }
100         }
101     }
102
103     @Override
104     public void channelLinked(ChannelUID channelUID) {
105         super.channelLinked(channelUID);
106     }
107
108     @Override
109     protected void updateStatusBasedOnRssi(boolean receivedSignal) {
110         // if there is no signal, we can be sure we are OFFLINE, but if there is a signal, we also have to check whether
111         // we are connected.
112         if (receivedSignal) {
113             if (device.getConnectionState() == ConnectionState.CONNECTED) {
114                 updateStatus(ThingStatus.ONLINE);
115             } else {
116                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Device is not connected.");
117             }
118         } else {
119             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
120         }
121     }
122
123     @Override
124     public void onConnectionStateChange(BluetoothConnectionStatusNotification connectionNotification) {
125         super.onConnectionStateChange(connectionNotification);
126         switch (connectionNotification.getConnectionState()) {
127             case DISCOVERED:
128                 // The device is now known on the Bluetooth network, so we can do something...
129                 scheduler.submit(() -> {
130                     if (device.getConnectionState() != ConnectionState.CONNECTED) {
131                         if (!device.connect()) {
132                             logger.debug("Error connecting to device after discovery.");
133                         }
134                     }
135                 });
136                 break;
137             case CONNECTED:
138                 updateStatus(ThingStatus.ONLINE);
139                 scheduler.submit(() -> {
140                     if (!resolved && !device.discoverServices()) {
141                         logger.debug("Error while discovering services");
142                     }
143                 });
144                 break;
145             case DISCONNECTED:
146                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
147                 break;
148             default:
149                 break;
150         }
151     }
152
153     @Override
154     public void onServicesDiscovered() {
155         super.onServicesDiscovered();
156         if (!resolved) {
157             resolved = true;
158             logger.debug("Service discovery completed for '{}'", address);
159         }
160     }
161
162     @Override
163     public void onCharacteristicReadComplete(BluetoothCharacteristic characteristic, BluetoothCompletionStatus status) {
164         super.onCharacteristicReadComplete(characteristic, status);
165         if (status == BluetoothCompletionStatus.SUCCESS) {
166             if (logger.isDebugEnabled()) {
167                 logger.debug("Characteristic {} from {} has been read - value {}", characteristic.getUuid(), address,
168                         HexUtils.bytesToHex(characteristic.getByteValue()));
169             }
170         } else {
171             logger.debug("Characteristic {} from {} has been read - ERROR", characteristic.getUuid(), address);
172         }
173     }
174
175     @Override
176     public void onCharacteristicWriteComplete(BluetoothCharacteristic characteristic,
177             BluetoothCompletionStatus status) {
178         super.onCharacteristicWriteComplete(characteristic, status);
179         if (logger.isDebugEnabled()) {
180             logger.debug("Wrote {} to characteristic {} of device {}: {}",
181                     HexUtils.bytesToHex(characteristic.getByteValue()), characteristic.getUuid(), address, status);
182         }
183     }
184
185     @Override
186     public void onCharacteristicUpdate(BluetoothCharacteristic characteristic) {
187         super.onCharacteristicUpdate(characteristic);
188         if (logger.isDebugEnabled()) {
189             logger.debug("Recieved update {} to characteristic {} of device {}",
190                     HexUtils.bytesToHex(characteristic.getByteValue()), characteristic.getUuid(), address);
191         }
192     }
193
194     @Override
195     public void onDescriptorUpdate(BluetoothDescriptor descriptor) {
196         super.onDescriptorUpdate(descriptor);
197         if (logger.isDebugEnabled()) {
198             logger.debug("Received update {} to descriptor {} of device {}", HexUtils.bytesToHex(descriptor.getValue()),
199                     descriptor.getUuid(), address);
200         }
201     }
202 }