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