2 * Copyright (c) 2010-2023 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.List;
16 import java.util.concurrent.Future;
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.bluetooth.AbstractBluetoothBridgeHandler;
23 import org.openhab.binding.bluetooth.BluetoothAddress;
24 import org.openhab.binding.bluetooth.bluez.internal.events.AdapterDiscoveringChangedEvent;
25 import org.openhab.binding.bluetooth.bluez.internal.events.AdapterPoweredChangedEvent;
26 import org.openhab.binding.bluetooth.bluez.internal.events.BlueZEvent;
27 import org.openhab.binding.bluetooth.bluez.internal.events.BlueZEventListener;
28 import org.openhab.core.thing.Bridge;
29 import org.openhab.core.thing.ThingStatus;
30 import org.openhab.core.thing.ThingStatusDetail;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 import com.github.hypfvieh.bluetooth.wrapper.BluetoothAdapter;
35 import com.github.hypfvieh.bluetooth.wrapper.BluetoothDevice;
38 * The {@link BlueZBridgeHandler} is responsible for talking to the BlueZ stack, using DBus Unix Socket.
39 * This Binding does not use any JNI.
40 * It provides a private interface for {@link BlueZBluetoothDevice}s to access the stack and provides top
41 * level adaptor functionality for scanning and arbitration.
43 * @author Kai Kreuzer - Initial contribution and API
44 * @author Hilbrand Bouwkamp - Simplified calling scan and better handling manual scanning
45 * @author Connor Petty - Simplified device scan logic
46 * @author Benjamin Lafois - Replaced tinyB with bluezDbus
50 public class BlueZBridgeHandler extends AbstractBluetoothBridgeHandler<BlueZBluetoothDevice>
51 implements BlueZEventListener {
53 private final Logger logger = LoggerFactory.getLogger(BlueZBridgeHandler.class);
55 // ADAPTER from BlueZ-DBus Library
56 private @Nullable BluetoothAdapter adapter;
59 private @Nullable BluetoothAddress adapterAddress;
61 private @Nullable ScheduledFuture<?> discoveryJob;
63 private final DeviceManagerFactory deviceManagerFactory;
68 * @param bridge the bridge definition for this handler
70 public BlueZBridgeHandler(Bridge bridge, DeviceManagerFactory deviceManagerFactory) {
72 this.deviceManagerFactory = deviceManagerFactory;
76 public void initialize() {
80 final BlueZAdapterConfiguration configuration = getConfigAs(BlueZAdapterConfiguration.class);
81 String addr = configuration.address;
83 this.adapterAddress = new BluetoothAddress(addr.toUpperCase());
85 // If configuration does not contain adapter address to use, exit with error.
86 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "address not set");
90 logger.debug("Creating BlueZ adapter with address '{}'", adapterAddress);
91 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Initializing");
92 deviceManagerFactory.getPropertiesChangedHandler().addListener(this);
93 discoveryJob = scheduler.scheduleWithFixedDelay(this::initializeAndRefreshDevices, 5, 10, TimeUnit.SECONDS);
97 public void dispose() {
98 deviceManagerFactory.getPropertiesChangedHandler().removeListener(this);
99 logger.debug("Termination of DBus BlueZ handler");
101 Future<?> job = discoveryJob;
107 BluetoothAdapter localAdatper = this.adapter;
108 if (localAdatper != null) {
109 localAdatper.stopDiscovery();
116 private @Nullable BluetoothAdapter prepareAdapter(DeviceManagerWrapper deviceManager) {
117 // next lets check if we can find our adapter in the manager.
118 BluetoothAdapter localAdapter = adapter;
119 if (localAdapter == null) {
120 BluetoothAddress localAddress = adapterAddress;
121 if (localAddress != null) {
122 localAdapter = adapter = deviceManager.getAdapter(localAddress);
124 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No adapter address provided");
128 if (localAdapter == null) {
129 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
130 "Native adapter could not be found for address '" + adapterAddress + "'");
133 // now lets confirm that the adapter is powered
134 if (!localAdapter.isPowered()) {
135 localAdapter.setPowered(true);
136 // give the device some time to power on
137 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
138 "Adapter is not powered, attempting to turn on...");
142 // now lets make sure that discovery is turned on
143 if (!localAdapter.startDiscovery()) {
144 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Trying to start discovery");
150 private void initializeAndRefreshDevices() {
151 logger.debug("initializeAndRefreshDevice()");
154 // first check if the device manager is ready
155 DeviceManagerWrapper deviceManager = deviceManagerFactory.getDeviceManager();
156 if (deviceManager == null) {
157 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
158 "Bluez DeviceManager not available yet.");
162 BluetoothAdapter adapter = prepareAdapter(deviceManager);
163 if (adapter == null) {
164 // adapter isn't prepared yet
168 // now lets refresh devices
169 List<BluetoothDevice> bluezDevices = deviceManager.getDevices(adapter);
170 logger.debug("Found {} Bluetooth devices.", bluezDevices.size());
171 for (BluetoothDevice bluezDevice : bluezDevices) {
172 if (bluezDevice.getAddress() == null) {
173 // For some reasons, sometimes the address is null..
176 BlueZBluetoothDevice device = getDevice(new BluetoothAddress(bluezDevice.getAddress()));
177 device.updateBlueZDevice(bluezDevice);
178 deviceDiscovered(device);
180 updateStatus(ThingStatus.ONLINE);
181 } catch (Exception ex) {
182 // don't know what kind of exception the bluez library might throw at us so lets catch them here so our
183 // scheduler loop doesn't get terminated
184 logger.warn("Unknown exception", ex);
185 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
190 public @Nullable BluetoothAddress getAddress() {
191 return adapterAddress;
195 protected BlueZBluetoothDevice createDevice(BluetoothAddress address) {
196 logger.debug("createDevice {}", address);
197 BlueZBluetoothDevice device = new BlueZBluetoothDevice(this, address);
202 public void onDBusBlueZEvent(BlueZEvent event) {
203 BluetoothAdapter localAdapter = this.adapter;
204 String adapterName = event.getAdapterName();
205 if (adapterName == null || localAdapter == null) {
206 // We cannot be sure that this event concerns this adapter.. So ignore message
209 String localName = localAdapter.getDeviceName();
211 if (!adapterName.equals(localName)) {
212 // does not concern this adapter
216 BluetoothAddress address = event.getDevice();
218 if (address != null) {
219 // now lets forward the event to the corresponding bluetooth device
220 BlueZBluetoothDevice device = getDevice(address);
221 event.dispatch(device);
226 public void onDiscoveringChanged(AdapterDiscoveringChangedEvent event) {
227 // do nothing for now
231 public void onPoweredChange(AdapterPoweredChangedEvent event) {
232 // do nothing for now