2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.bluetooth.bluez.internal;
15 import java.util.HashMap;
18 import java.util.concurrent.CopyOnWriteArraySet;
19 import java.util.concurrent.ScheduledExecutorService;
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;
44 * This is the PropertiesChangedHandler subclass used by the binding to handle/dispatch property change events
47 * @author Benjamin Lafois - Initial contribution and API
48 * @author Connor Petty - Code cleanup
51 public class BlueZPropertiesChangedHandler extends AbstractPropertiesChangedHandler {
53 private final Logger logger = LoggerFactory.getLogger(BlueZPropertiesChangedHandler.class);
55 private final Set<BlueZEventListener> listeners = new CopyOnWriteArraySet<>();
57 private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("bluetooth");
59 public void addListener(BlueZEventListener listener) {
60 this.listeners.add(listener);
63 public void removeListener(BlueZEventListener listener) {
64 this.listeners.remove(listener);
67 private void notifyListeners(BlueZEvent event) {
68 for (BlueZEventListener listener : this.listeners) {
69 event.dispatch(listener);
74 public void handle(@Nullable PropertiesChanged properties) {
75 if (properties == null || properties.getPropertiesChanged() == null) {
76 logger.debug("Null properties. Skipping.");
79 Map<@Nullable String, @Nullable Variant<?>> changedProperties = properties.getPropertiesChanged();
80 if (changedProperties == null) {
81 logger.debug("Null properties changed. Skipping.");
85 // do this asynchronously so that we don't slow things down for the dbus event dispatcher
86 scheduler.execute(() -> {
88 String dbusPath = properties.getPath();
89 changedProperties.forEach((key, variant) -> {
90 if (key == null || variant == null) {
93 switch (key.toLowerCase()) {
96 onRSSIUpdate(dbusPath, variant);
100 onTXPowerUpdate(dbusPath, variant);
103 // Characteristc value updated
104 onValueUpdate(dbusPath, variant);
107 onConnectedUpdate(dbusPath, variant);
110 onNameUpdate(dbusPath, variant);
115 case "manufacturerdata":
116 onManufacturerDataUpdate(dbusPath, variant);
119 onPoweredUpdate(dbusPath, variant);
122 onDiscoveringUpdate(dbusPath, variant);
124 case "servicesresolved":
125 onServicesResolved(dbusPath, variant);
130 logger.debug("PropertiesPath: {}", dbusPath);
131 logger.debug("PropertiesChanged: {}", changedProperties);
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));
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));
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));
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));
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));
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));
177 private void onManufacturerDataUpdate(String dbusPath, Variant<?> variant) {
178 Map<Short, byte[]> eventData = new HashMap<>();
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));
194 if (!eventData.isEmpty()) {
195 notifyListeners(new ManufacturerDataEvent(dbusPath, eventData));
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));
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));