]> git.basschouten.com Git - openhab-addons.git/blob
75e62114ae6639e37ba7f064eac580f1662fc6da
[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.roaming.internal;
14
15 import java.util.HashMap;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.concurrent.CopyOnWriteArraySet;
19 import java.util.stream.Stream;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.bluetooth.BluetoothAdapter;
24 import org.openhab.binding.bluetooth.BluetoothAddress;
25 import org.openhab.binding.bluetooth.BluetoothBindingConstants;
26 import org.openhab.binding.bluetooth.BluetoothDiscoveryListener;
27 import org.openhab.core.thing.Bridge;
28 import org.openhab.core.thing.ChannelUID;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.thing.ThingUID;
33 import org.openhab.core.thing.binding.BaseBridgeHandler;
34 import org.openhab.core.types.Command;
35
36 /**
37  * The {@link RoamingBridgeHandler} is responsible for handling commands, which are
38  * sent to one of the channels.
39  *
40  * @author Connor Petty - Initial contribution
41  */
42 @NonNullByDefault
43 public class RoamingBridgeHandler extends BaseBridgeHandler implements RoamingBluetoothAdapter {
44
45     private final Set<BluetoothAdapter> adapters = new CopyOnWriteArraySet<>();
46
47     /*
48      * Note: this will only populate from handlers calling getDevice(BluetoothAddress), so we don't need
49      * to do periodic cleanup.
50      */
51     private Map<BluetoothAddress, RoamingBluetoothDevice> devices = new HashMap<>();
52     private ThingUID[] groupUIDs = new ThingUID[0];
53
54     public RoamingBridgeHandler(Bridge bridge) {
55         super(bridge);
56     }
57
58     @Override
59     public void initialize() {
60         Object value = getConfig().get(RoamingBindingConstants.CONFIGURATION_GROUP_ADAPTER_UIDS);
61         if (value == null || !(value instanceof String) || "".equals(value)) {
62             groupUIDs = new ThingUID[0];
63         } else {
64             String groupIds = (String) value;
65             groupUIDs = Stream.of(groupIds.split(",")).map(ThingUID::new).toArray(ThingUID[]::new);
66         }
67
68         if (adapters.stream().map(BluetoothAdapter::getUID).anyMatch(this::isGroupMember)) {
69             updateStatus(ThingStatus.ONLINE);
70         } else {
71             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
72                     "No Physical Bluetooth adapters found");
73         }
74     }
75
76     private void updateStatus() {
77         if (adapters.stream().anyMatch(this::isRoamingMember)) {
78             updateStatus(ThingStatus.ONLINE);
79         } else {
80             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
81                     "No Physical Bluetooth adapters found");
82         }
83     }
84
85     @Override
86     public void dispose() {
87         // nothing that needs to be done here.
88         // Listener cleanup will be performed by the discovery participant anyway.
89     }
90
91     @Override
92     public ThingUID getUID() {
93         return getThing().getUID();
94     }
95
96     @Override
97     public @Nullable String getLocation() {
98         return getThing().getLocation();
99     }
100
101     @Override
102     public @Nullable String getLabel() {
103         return getThing().getLabel();
104     }
105
106     private boolean isRoamingMember(BluetoothAdapter adapter) {
107         return isRoamingMember(adapter.getUID());
108     }
109
110     @Override
111     public boolean isRoamingMember(ThingUID adapterUID) {
112         if (!isInitialized()) {
113             // an unitialized roaming adapter has no members
114             return false;
115         }
116         return isGroupMember(adapterUID);
117     }
118
119     private boolean isGroupMember(ThingUID adapterUID) {
120         if (groupUIDs.length == 0) {
121             // if there are no members of the group then it is treated as all adapters are members.
122             return true;
123         }
124         for (ThingUID uid : groupUIDs) {
125             if (adapterUID.equals(uid)) {
126                 return true;
127             }
128         }
129         return false;
130     }
131
132     @Override
133     public boolean isDiscoveryEnabled() {
134         if (getThing().getStatus() != ThingStatus.ONLINE) {
135             return false;
136         }
137         Object discovery = getConfig().get(BluetoothBindingConstants.CONFIGURATION_DISCOVERY);
138         if (discovery != null && discovery.toString().equalsIgnoreCase("false")) {
139             return false;
140         }
141         return true;
142     }
143
144     @Override
145     public void addBluetoothAdapter(BluetoothAdapter adapter) {
146         if (adapter == this) {
147             return;
148         }
149
150         this.adapters.add(adapter);
151
152         if (isRoamingMember(adapter)) {
153             synchronized (devices) {
154                 for (RoamingBluetoothDevice roamingDevice : devices.values()) {
155                     roamingDevice.addBluetoothDevice(adapter.getDevice(roamingDevice.getAddress()));
156                 }
157             }
158         }
159
160         if (getThing().getStatus() == ThingStatus.OFFLINE) {
161             updateStatus();
162         }
163     }
164
165     @Override
166     public void removeBluetoothAdapter(BluetoothAdapter adapter) {
167         if (adapter == this) {
168             return;
169         }
170         this.adapters.remove(adapter);
171
172         if (isRoamingMember(adapter)) {
173             synchronized (devices) {
174                 for (RoamingBluetoothDevice roamingDevice : devices.values()) {
175                     roamingDevice.removeBluetoothDevice(adapter.getDevice(roamingDevice.getAddress()));
176                 }
177             }
178         }
179
180         if (getThing().getStatus() == ThingStatus.ONLINE) {
181             updateStatus();
182         }
183     }
184
185     @Override
186     public void handleCommand(ChannelUID channelUID, Command command) {
187     }
188
189     @Override
190     public void addDiscoveryListener(BluetoothDiscoveryListener listener) {
191         // we don't use this
192     }
193
194     @Override
195     public void removeDiscoveryListener(@Nullable BluetoothDiscoveryListener listener) {
196         // we don't use this
197     }
198
199     @Override
200     public void scanStart() {
201         // does nothing
202     }
203
204     @Override
205     public void scanStop() {
206         // does nothing
207     }
208
209     @Override
210     public @Nullable BluetoothAddress getAddress() {
211         // roaming adapters don't have bluetooth addresses
212         return null;
213     }
214
215     @Override
216     public RoamingBluetoothDevice getDevice(BluetoothAddress address) {
217         // this will only get called by a bluetooth device handler
218         synchronized (devices) {
219             RoamingBluetoothDevice roamingDevice = devices.computeIfAbsent(address,
220                     addr -> new RoamingBluetoothDevice(this, addr));
221
222             adapters.stream().filter(this::isRoamingMember)
223                     .forEach(adapter -> roamingDevice.addBluetoothDevice(adapter.getDevice(address)));
224
225             return roamingDevice;
226         }
227     }
228
229     @Override
230     public boolean hasHandlerForDevice(BluetoothAddress address) {
231         String addrStr = address.toString();
232         /*
233          * This type of search is inefficient and won't scale as the number of bluetooth Thing children increases on
234          * this bridge. But implementing a more efficient search would require a bit more overhead.
235          * Luckily though, it is reasonable to assume that the number of Thing children will remain small.
236          */
237         for (Thing childThing : getThing().getThings()) {
238             Object childAddr = childThing.getConfiguration().get(BluetoothBindingConstants.CONFIGURATION_ADDRESS);
239             if (addrStr.equals(childAddr)) {
240                 return childThing.getHandler() != null;
241             }
242         }
243         return false;
244     }
245 }