]> git.basschouten.com Git - openhab-addons.git/blob
1d6bb47f3cd3308537b9386b038753332aad191b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.bluez.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.concurrent.ScheduledExecutorService;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.freedesktop.dbus.DBusMap;
24 import org.freedesktop.dbus.handlers.AbstractPropertiesChangedHandler;
25 import org.freedesktop.dbus.interfaces.Properties.PropertiesChanged;
26 import org.freedesktop.dbus.types.UInt16;
27 import org.freedesktop.dbus.types.Variant;
28 import org.openhab.binding.bluetooth.bluez.internal.events.AdapterDiscoveringChangedEvent;
29 import org.openhab.binding.bluetooth.bluez.internal.events.AdapterPoweredChangedEvent;
30 import org.openhab.binding.bluetooth.bluez.internal.events.BlueZEvent;
31 import org.openhab.binding.bluetooth.bluez.internal.events.BlueZEventListener;
32 import org.openhab.binding.bluetooth.bluez.internal.events.CharacteristicUpdateEvent;
33 import org.openhab.binding.bluetooth.bluez.internal.events.ConnectedEvent;
34 import org.openhab.binding.bluetooth.bluez.internal.events.ManufacturerDataEvent;
35 import org.openhab.binding.bluetooth.bluez.internal.events.NameEvent;
36 import org.openhab.binding.bluetooth.bluez.internal.events.RssiEvent;
37 import org.openhab.binding.bluetooth.bluez.internal.events.ServiceDataEvent;
38 import org.openhab.binding.bluetooth.bluez.internal.events.ServicesResolvedEvent;
39 import org.openhab.binding.bluetooth.bluez.internal.events.TXPowerEvent;
40 import org.openhab.core.common.ThreadPoolManager;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * This is the PropertiesChangedHandler subclass used by the binding to handle/dispatch property change events
46  * from bluez.
47  *
48  * @author Benjamin Lafois - Initial contribution and API
49  * @author Connor Petty - Code cleanup
50  * @author Peter Rosenberg - Add support for ServiceData
51  */
52 @NonNullByDefault
53 public class BlueZPropertiesChangedHandler extends AbstractPropertiesChangedHandler {
54
55     private final Logger logger = LoggerFactory.getLogger(BlueZPropertiesChangedHandler.class);
56
57     private final Set<BlueZEventListener> listeners = new CopyOnWriteArraySet<>();
58
59     private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("bluetooth");
60
61     public void addListener(BlueZEventListener listener) {
62         this.listeners.add(listener);
63     }
64
65     public void removeListener(BlueZEventListener listener) {
66         this.listeners.remove(listener);
67     }
68
69     private void notifyListeners(BlueZEvent event) {
70         for (BlueZEventListener listener : this.listeners) {
71             event.dispatch(listener);
72         }
73     }
74
75     @Override
76     public void handle(@Nullable PropertiesChanged properties) {
77         if (properties == null || properties.getPropertiesChanged() == null) {
78             logger.debug("Null properties. Skipping.");
79             return;
80         }
81         Map<@Nullable String, @Nullable Variant<?>> changedProperties = properties.getPropertiesChanged();
82         if (changedProperties == null) {
83             logger.debug("Null properties changed. Skipping.");
84             return;
85         }
86
87         // do this asynchronously so that we don't slow things down for the dbus event dispatcher
88         scheduler.execute(() -> {
89
90             String dbusPath = properties.getPath();
91             changedProperties.forEach((key, variant) -> {
92                 if (key == null || variant == null) {
93                     return;
94                 }
95                 switch (key.toLowerCase()) {
96                     case "rssi":
97                         // Signal Update
98                         onRSSIUpdate(dbusPath, variant);
99                         break;
100                     case "txpower":
101                         // TxPower
102                         onTXPowerUpdate(dbusPath, variant);
103                         break;
104                     case "value":
105                         // Characteristc value updated
106                         onValueUpdate(dbusPath, variant);
107                         break;
108                     case "connected":
109                         onConnectedUpdate(dbusPath, variant);
110                         break;
111                     case "name":
112                         onNameUpdate(dbusPath, variant);
113                         break;
114                     case "alias":
115                         // TODO
116                         break;
117                     case "manufacturerdata":
118                         onManufacturerDataUpdate(dbusPath, variant);
119                         break;
120                     case "servicedata":
121                         onServiceDataUpdate(dbusPath, variant);
122                         break;
123                     case "powered":
124                         onPoweredUpdate(dbusPath, variant);
125                         break;
126                     case "discovering":
127                         onDiscoveringUpdate(dbusPath, variant);
128                         break;
129                     case "servicesresolved":
130                         onServicesResolved(dbusPath, variant);
131                         break;
132                 }
133             });
134
135             logger.debug("PropertiesPath: {}", dbusPath);
136             logger.debug("PropertiesChanged: {}", changedProperties);
137         });
138     }
139
140     private void onDiscoveringUpdate(String dbusPath, Variant<?> variant) {
141         Object discovered = variant.getValue();
142         if (discovered instanceof Boolean) {
143             notifyListeners(new AdapterDiscoveringChangedEvent(dbusPath, (boolean) discovered));
144         }
145     }
146
147     private void onPoweredUpdate(String dbusPath, Variant<?> variant) {
148         Object powered = variant.getValue();
149         if (powered instanceof Boolean) {
150             notifyListeners(new AdapterPoweredChangedEvent(dbusPath, (boolean) powered));
151         }
152     }
153
154     private void onServicesResolved(String dbusPath, Variant<?> variant) {
155         Object resolved = variant.getValue();
156         if (resolved instanceof Boolean) {
157             notifyListeners(new ServicesResolvedEvent(dbusPath, (boolean) resolved));
158         }
159     }
160
161     private void onNameUpdate(String dbusPath, Variant<?> variant) {
162         Object name = variant.getValue();
163         if (name instanceof String) {
164             notifyListeners(new NameEvent(dbusPath, (String) name));
165         }
166     }
167
168     private void onTXPowerUpdate(String dbusPath, Variant<?> variant) {
169         Object txPower = variant.getValue();
170         if (txPower instanceof Short) {
171             notifyListeners(new TXPowerEvent(dbusPath, (short) txPower));
172         }
173     }
174
175     private void onConnectedUpdate(String dbusPath, Variant<?> variant) {
176         Object connected = variant.getValue();
177         if (connected instanceof Boolean) {
178             notifyListeners(new ConnectedEvent(dbusPath, (boolean) connected));
179         }
180     }
181
182     private void onManufacturerDataUpdate(String dbusPath, Variant<?> variant) {
183         Map<Short, byte[]> eventData = new HashMap<>();
184
185         Object map = variant.getValue();
186         if (map instanceof DBusMap) {
187             DBusMap<?, ?> dbm = (DBusMap<?, ?>) map;
188             for (Map.Entry<?, ?> entry : dbm.entrySet()) {
189                 Object key = entry.getKey();
190                 Object value = entry.getValue();
191                 if (key instanceof UInt16 && value instanceof Variant<?>) {
192                     value = ((Variant<?>) value).getValue();
193                     if (value instanceof byte[]) {
194                         eventData.put(((UInt16) key).shortValue(), ((byte[]) value));
195                     }
196                 }
197             }
198         }
199         if (!eventData.isEmpty()) {
200             notifyListeners(new ManufacturerDataEvent(dbusPath, eventData));
201         }
202     }
203
204     private void onServiceDataUpdate(String dbusPath, Variant<?> variant) {
205         Map<String, byte[]> serviceData = new HashMap<>();
206
207         Object map = variant.getValue();
208         if (map instanceof DBusMap) {
209             DBusMap<?, ?> dbm = (DBusMap<?, ?>) map;
210             for (Map.Entry<?, ?> entry : dbm.entrySet()) {
211                 Object key = entry.getKey();
212                 Object value = entry.getValue();
213                 if (key instanceof String && value instanceof Variant<?>) {
214                     value = ((Variant<?>) value).getValue();
215                     if (value instanceof byte[]) {
216                         serviceData.put(((String) key), ((byte[]) value));
217                     }
218                 }
219             }
220         }
221         if (!serviceData.isEmpty()) {
222             notifyListeners(new ServiceDataEvent(dbusPath, serviceData));
223         }
224     }
225
226     private void onValueUpdate(String dbusPath, Variant<?> variant) {
227         Object value = variant.getValue();
228         if (value instanceof byte[]) {
229             notifyListeners(new CharacteristicUpdateEvent(dbusPath, (byte[]) value));
230         }
231     }
232
233     private void onRSSIUpdate(String dbusPath, Variant<?> variant) {
234         Object rssi = variant.getValue();
235         if (rssi instanceof Short) {
236             notifyListeners(new RssiEvent(dbusPath, (short) rssi));
237         }
238     }
239 }