2 * Copyright (c) 2010-2020 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.bluegiga.handler;
15 import java.io.BufferedInputStream;
16 import java.io.BufferedOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.OutputStream;
21 import java.util.Optional;
22 import java.util.UUID;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.Future;
25 import java.util.concurrent.ScheduledExecutorService;
26 import java.util.concurrent.ScheduledFuture;
27 import java.util.concurrent.TimeUnit;
29 import org.apache.commons.io.IOUtils;
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.openhab.binding.bluetooth.AbstractBluetoothBridgeHandler;
33 import org.openhab.binding.bluetooth.BluetoothAddress;
34 import org.openhab.binding.bluetooth.BluetoothBindingConstants;
35 import org.openhab.binding.bluetooth.bluegiga.BlueGigaAdapterConstants;
36 import org.openhab.binding.bluetooth.bluegiga.BlueGigaBluetoothDevice;
37 import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaCommand;
38 import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaConfiguration;
39 import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaEventListener;
40 import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaException;
41 import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaHandlerListener;
42 import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaResponse;
43 import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaSerialHandler;
44 import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaTransactionManager;
45 import org.openhab.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaAttributeWriteCommand;
46 import org.openhab.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaAttributeWriteResponse;
47 import org.openhab.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaFindInformationCommand;
48 import org.openhab.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaFindInformationResponse;
49 import org.openhab.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByGroupTypeCommand;
50 import org.openhab.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByGroupTypeResponse;
51 import org.openhab.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByHandleCommand;
52 import org.openhab.binding.bluetooth.bluegiga.internal.command.attributeclient.BlueGigaReadByHandleResponse;
53 import org.openhab.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaConnectionStatusEvent;
54 import org.openhab.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectCommand;
55 import org.openhab.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectResponse;
56 import org.openhab.binding.bluetooth.bluegiga.internal.command.connection.BlueGigaDisconnectedEvent;
57 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaConnectDirectCommand;
58 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaConnectDirectResponse;
59 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaDiscoverCommand;
60 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaDiscoverResponse;
61 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaEndProcedureCommand;
62 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaEndProcedureResponse;
63 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaScanResponseEvent;
64 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetModeCommand;
65 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetModeResponse;
66 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetScanParametersCommand;
67 import org.openhab.binding.bluetooth.bluegiga.internal.command.gap.BlueGigaSetScanParametersResponse;
68 import org.openhab.binding.bluetooth.bluegiga.internal.command.system.BlueGigaAddressGetCommand;
69 import org.openhab.binding.bluetooth.bluegiga.internal.command.system.BlueGigaAddressGetResponse;
70 import org.openhab.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetConnectionsCommand;
71 import org.openhab.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetConnectionsResponse;
72 import org.openhab.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetInfoCommand;
73 import org.openhab.binding.bluetooth.bluegiga.internal.command.system.BlueGigaGetInfoResponse;
74 import org.openhab.binding.bluetooth.bluegiga.internal.enumeration.BgApiResponse;
75 import org.openhab.binding.bluetooth.bluegiga.internal.enumeration.BluetoothAddressType;
76 import org.openhab.binding.bluetooth.bluegiga.internal.enumeration.GapConnectableMode;
77 import org.openhab.binding.bluetooth.bluegiga.internal.enumeration.GapDiscoverMode;
78 import org.openhab.binding.bluetooth.bluegiga.internal.enumeration.GapDiscoverableMode;
79 import org.openhab.core.common.ThreadPoolManager;
80 import org.openhab.core.io.transport.serial.PortInUseException;
81 import org.openhab.core.io.transport.serial.SerialPort;
82 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
83 import org.openhab.core.io.transport.serial.SerialPortManager;
84 import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
85 import org.openhab.core.thing.Bridge;
86 import org.openhab.core.thing.Thing;
87 import org.openhab.core.thing.ThingStatus;
88 import org.openhab.core.thing.ThingStatusDetail;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
93 * The {@link BlueGigaBridgeHandler} is responsible for interfacing to the BlueGiga Bluetooth adapter.
94 * It provides a private interface for {@link BlueGigaBluetoothDevice}s to access the dongle and provides top
95 * level adaptor functionality for scanning and arbitration.
97 * The handler provides the serial interface to the dongle via the BlueGiga BG-API library.
99 * In the BlueGiga dongle, we leave scanning enabled most of the time. Normally, it's just passive scanning, and active
100 * scanning is enabled when we want to include new devices. Passive scanning is enough for us to receive beacons etc
101 * that are transmitted periodically, and active scanning will get more information which may be useful when we are
102 * including new devices.
104 * @author Chris Jackson - Initial contribution
105 * @author Kai Kreuzer - Made handler implement BlueGigaHandlerListener
106 * @author Pauli Anttila - Many improvements
109 public class BlueGigaBridgeHandler extends AbstractBluetoothBridgeHandler<BlueGigaBluetoothDevice>
110 implements BlueGigaEventListener, BlueGigaHandlerListener {
112 private final Logger logger = LoggerFactory.getLogger(BlueGigaBridgeHandler.class);
114 private final int COMMAND_TIMEOUT_MS = 5000;
115 private final int INITIALIZATION_INTERVAL_SEC = 60;
117 private final SerialPortManager serialPortManager;
119 private final ScheduledExecutorService executor = ThreadPoolManager.getScheduledPool("BlueGiga");
122 private Optional<SerialPort> serialPort = Optional.empty();
124 private BlueGigaConfiguration configuration = new BlueGigaConfiguration();
126 // The serial port input stream.
127 private Optional<InputStream> inputStream = Optional.empty();
129 // The serial port output stream.
130 private Optional<OutputStream> outputStream = Optional.empty();
132 // The BlueGiga API handler
133 private Optional<BlueGigaSerialHandler> serialHandler = Optional.empty();
135 // The BlueGiga transaction manager
136 private Optional<BlueGigaTransactionManager> transactionManager = Optional.empty();
138 // The maximum number of connections this interface supports
139 private int maxConnections = 0;
142 private @Nullable BluetoothAddress address;
144 // Map of Bluetooth devices known to this bridge.
145 // This is all devices we have heard on the network - not just things bound to the bridge
146 private final Map<BluetoothAddress, BlueGigaBluetoothDevice> devices = new ConcurrentHashMap<>();
148 // Map of open connections
149 private final Map<Integer, BluetoothAddress> connections = new ConcurrentHashMap<>();
151 private volatile boolean initComplete = false;
153 private @Nullable ScheduledFuture<?> initTask;
154 private @Nullable ScheduledFuture<?> removeInactiveDevicesTask;
155 private @Nullable ScheduledFuture<?> discoveryTask;
157 private @Nullable Future<?> passiveScanIdleTimer;
159 public BlueGigaBridgeHandler(Bridge bridge, SerialPortManager serialPortManager) {
161 this.serialPortManager = serialPortManager;
165 public void initialize() {
167 Optional<BlueGigaConfiguration> cfg = Optional.of(getConfigAs(BlueGigaConfiguration.class));
168 if (cfg.isPresent()) {
169 configuration = cfg.get();
170 initTask = executor.scheduleWithFixedDelay(this::start, 0, INITIALIZATION_INTERVAL_SEC, TimeUnit.SECONDS);
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR);
177 public void dispose() {
179 stopScheduledTasks();
180 if (initTask != null) {
181 initTask.cancel(true);
186 private void start() {
189 logger.debug("Initialize BlueGiga");
190 logger.debug("Using configuration: {}", configuration);
192 if (openSerialPort(configuration.port, 115200)) {
193 serialHandler = Optional.of(new BlueGigaSerialHandler(inputStream.get(), outputStream.get()));
194 transactionManager = Optional.of(new BlueGigaTransactionManager(serialHandler.get(), executor));
195 serialHandler.get().addHandlerListener(this);
196 transactionManager.get().addEventListener(this);
197 updateStatus(ThingStatus.UNKNOWN);
200 // Stop any procedures that are running
203 // Set mode to non-discoverable etc.
206 // Get maximum parallel connections
207 maxConnections = readMaxConnections().getMaxconn();
209 // Close all connections so we start from a known position
210 for (int connection = 0; connection < maxConnections; connection++) {
211 sendCommandWithoutChecks(
212 new BlueGigaDisconnectCommand.CommandBuilder().withConnection(connection).build(),
213 BlueGigaDisconnectResponse.class);
216 // Get our Bluetooth address
217 address = new BluetoothAddress(readAddress().getAddress());
219 updateThingProperties();
222 updateStatus(ThingStatus.ONLINE);
223 startScheduledTasks();
224 } catch (BlueGigaException e) {
225 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
226 "Initialization of BlueGiga controller failed");
230 } catch (RuntimeException e) {
231 // Avoid scheduled task to shutdown
232 // e.g. when BlueGiga module is detached
233 logger.debug("Start failed", e);
237 private void stop() {
238 if (transactionManager.isPresent()) {
239 transactionManager.get().removeEventListener(this);
240 transactionManager.get().close();
241 transactionManager = Optional.empty();
243 if (serialHandler.isPresent()) {
244 serialHandler.get().removeHandlerListener(this);
245 serialHandler.get().close();
246 serialHandler = Optional.empty();
249 initComplete = false;
254 private void schedulePassiveScan() {
255 cancelScheduledPassiveScan();
256 passiveScanIdleTimer = executor.schedule(() -> {
257 if (!activeScanEnabled) {
258 logger.debug("Activate passive scan");
260 bgStartScanning(false, configuration.passiveScanInterval, configuration.passiveScanWindow);
262 logger.debug("Ignore passive scan activation as active scan is active");
264 }, configuration.passiveScanIdleTime, TimeUnit.MILLISECONDS);
267 private void cancelScheduledPassiveScan() {
268 if (passiveScanIdleTimer != null) {
269 passiveScanIdleTimer.cancel(true);
273 private void startScheduledTasks() {
274 schedulePassiveScan();
275 logger.debug("Start scheduled task to remove inactive devices");
276 discoveryTask = scheduler.scheduleWithFixedDelay(this::refreshDiscoveredDevices, 0, 10, TimeUnit.SECONDS);
279 private void stopScheduledTasks() {
280 cancelScheduledPassiveScan();
281 if (removeInactiveDevicesTask != null) {
282 removeInactiveDevicesTask.cancel(true);
283 removeInactiveDevicesTask = null;
285 if (discoveryTask != null) {
286 discoveryTask.cancel(true);
287 discoveryTask = null;
291 private BlueGigaGetConnectionsResponse readMaxConnections() throws BlueGigaException {
292 return sendCommandWithoutChecks(new BlueGigaGetConnectionsCommand(), BlueGigaGetConnectionsResponse.class);
295 private BlueGigaAddressGetResponse readAddress() throws BlueGigaException {
296 return sendCommandWithoutChecks(new BlueGigaAddressGetCommand(), BlueGigaAddressGetResponse.class);
299 private BlueGigaGetInfoResponse readInfo() throws BlueGigaException {
300 return sendCommandWithoutChecks(new BlueGigaGetInfoCommand(), BlueGigaGetInfoResponse.class);
303 private void updateThingProperties() throws BlueGigaException {
304 BlueGigaGetInfoResponse infoResponse = readInfo();
306 Map<String, String> properties = editProperties();
307 properties.put(BluetoothBindingConstants.PROPERTY_MAXCONNECTIONS, Integer.toString(maxConnections));
308 properties.put(Thing.PROPERTY_FIRMWARE_VERSION,
309 String.format("%d.%d", infoResponse.getMajor(), infoResponse.getMinor()));
310 properties.put(Thing.PROPERTY_HARDWARE_VERSION, Integer.toString(infoResponse.getHardware()));
311 properties.put(BlueGigaAdapterConstants.PROPERTY_PROTOCOL, Integer.toString(infoResponse.getProtocolVersion()));
312 properties.put(BlueGigaAdapterConstants.PROPERTY_LINKLAYER, Integer.toString(infoResponse.getLlVersion()));
313 updateProperties(properties);
316 private boolean openSerialPort(final String serialPortName, int baudRate) {
317 logger.debug("Connecting to serial port '{}'", serialPortName);
319 SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(serialPortName);
320 if (portIdentifier == null) {
321 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Port does not exist");
324 SerialPort sp = portIdentifier.open("org.openhab.binding.bluetooth.bluegiga", 2000);
325 sp.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
327 sp.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_OUT);
328 sp.enableReceiveThreshold(1);
329 sp.enableReceiveTimeout(2000);
331 // RXTX serial port library causes high CPU load
332 // Start event listener, which will just sleep and slow down event loop
333 sp.notifyOnDataAvailable(true);
335 logger.info("Connected to serial port '{}'.", serialPortName);
338 inputStream = Optional.of(new BufferedInputStream(sp.getInputStream()));
339 outputStream = Optional.of(new BufferedOutputStream(sp.getOutputStream()));
340 } catch (IOException e) {
341 logger.error("Error getting serial streams", e);
344 serialPort = Optional.of(sp);
346 } catch (PortInUseException e) {
347 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
348 "Serial Error: Port in use");
350 } catch (UnsupportedCommOperationException e) {
351 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
352 "Serial Error: Unsupported operation");
357 private void closeSerialPort() {
358 serialPort.ifPresent(sp -> {
359 sp.removeEventListener();
361 sp.disableReceiveTimeout();
362 } catch (Exception e) {
363 // Ignore all as RXTX seems to send arbitrary exceptions when BlueGiga module is detached
365 outputStream.ifPresent(output -> {
366 IOUtils.closeQuietly(output);
368 inputStream.ifPresent(input -> {
369 IOUtils.closeQuietly(input);
372 logger.debug("Closed serial port.");
373 serialPort = Optional.empty();
374 inputStream = Optional.empty();
375 outputStream = Optional.empty();
381 public void scanStart() {
383 logger.debug("Start active scan");
384 // Stop the passive scan
385 cancelScheduledPassiveScan();
388 // Start a active scan
389 bgStartScanning(true, configuration.activeScanInterval, configuration.activeScanWindow);
393 public void scanStop() {
395 logger.debug("Stop active scan");
397 // Stop the active scan
400 // Start a passive scan after idle delay
401 schedulePassiveScan();
405 public @Nullable BluetoothAddress getAddress() {
406 BluetoothAddress addr = address;
410 throw new IllegalStateException("Adapter has not been initialized yet!");
415 protected BlueGigaBluetoothDevice createDevice(BluetoothAddress address) {
416 return new BlueGigaBluetoothDevice(this, address, BluetoothAddressType.UNKNOWN);
420 * Connects to a device.
422 * If the device is already connected, or the attempt to connect failed, then we return false. If we have reached
423 * the maximum number of connections supported by this dongle, then we return false.
425 * @param address the device {@link BluetoothAddress} to connect to
426 * @param addressType the {@link BluetoothAddressType} of the device
427 * @return true if the connection was started
429 public boolean bgConnect(BluetoothAddress address, BluetoothAddressType addressType) {
430 // Check the connection to make sure we're not already connected to this device
431 if (connections.containsValue(address)) {
435 // FIXME: When getting here, I always found all connections to be already taken and thus the code never
436 // proceeded. Relaxing this condition did not do any obvious harm, but now guaranteed that the services are
437 // queried from the device.
438 if (connections.size() == maxConnections + 1) {
439 logger.debug("BlueGiga: Attempt to connect to {} but no connections available.", address);
443 logger.debug("BlueGiga Connect: address {}.", address);
446 BlueGigaConnectDirectCommand command = new BlueGigaConnectDirectCommand.CommandBuilder()
447 .withAddress(address.toString())
448 .withAddrType(addressType)
449 .withConnIntervalMin(configuration.connIntervalMin)
450 .withConnIntervalMax(configuration.connIntervalMax)
451 .withLatency(configuration.connLatency)
452 .withTimeout(configuration.connTimeout)
456 return sendCommand(command, BlueGigaConnectDirectResponse.class, true).getResult() == BgApiResponse.SUCCESS;
457 } catch (BlueGigaException e) {
458 logger.debug("Error occured when sending connect command to device {}, reason: {}.", address,
465 * Close a connection using {@link BlueGigaDisconnectCommand}
467 * @param connectionHandle
470 public boolean bgDisconnect(int connectionHandle) {
471 logger.debug("BlueGiga Disconnect: connection {}", connectionHandle);
472 BlueGigaDisconnectCommand command = new BlueGigaDisconnectCommand.CommandBuilder()
473 .withConnection(connectionHandle).build();
476 return sendCommand(command, BlueGigaDisconnectResponse.class, true).getResult() == BgApiResponse.SUCCESS;
477 } catch (BlueGigaException e) {
478 logger.debug("Error occured when sending disconnect command to device {}, reason: {}.", address,
485 * Start a read of all primary services using {@link BlueGigaReadByGroupTypeCommand}
487 * @param connectionHandle
488 * @return true if successful
490 public boolean bgFindPrimaryServices(int connectionHandle) {
491 logger.debug("BlueGiga FindPrimary: connection {}", connectionHandle);
493 BlueGigaReadByGroupTypeCommand command = new BlueGigaReadByGroupTypeCommand.CommandBuilder()
494 .withConnection(connectionHandle)
497 .withUuid(UUID.fromString("00002800-0000-1000-8000-00805F9B34FB"))
501 return sendCommand(command, BlueGigaReadByGroupTypeResponse.class, true)
502 .getResult() == BgApiResponse.SUCCESS;
503 } catch (BlueGigaException e) {
504 logger.debug("Error occured when sending read primary services command to device {}, reason: {}.", address,
511 * Start a read of all characteristics using {@link BlueGigaFindInformationCommand}
513 * @param connectionHandle
514 * @return true if successful
516 public boolean bgFindCharacteristics(int connectionHandle) {
517 logger.debug("BlueGiga Find: connection {}", connectionHandle);
519 BlueGigaFindInformationCommand command = new BlueGigaFindInformationCommand.CommandBuilder()
520 .withConnection(connectionHandle)
526 return sendCommand(command, BlueGigaFindInformationResponse.class, true)
527 .getResult() == BgApiResponse.SUCCESS;
528 } catch (BlueGigaException e) {
529 logger.debug("Error occured when sending read characteristics command to device {}, reason: {}.", address,
536 * Read a characteristic using {@link BlueGigaReadByHandleCommand}
538 * @param connectionHandle
540 * @return true if successful
542 public boolean bgReadCharacteristic(int connectionHandle, int handle) {
543 logger.debug("BlueGiga Read: connection {}, handle {}", connectionHandle, handle);
545 BlueGigaReadByHandleCommand command = new BlueGigaReadByHandleCommand.CommandBuilder()
546 .withConnection(connectionHandle)
547 .withChrHandle(handle)
551 return sendCommand(command, BlueGigaReadByHandleResponse.class, true).getResult() == BgApiResponse.SUCCESS;
552 } catch (BlueGigaException e) {
553 logger.debug("Error occured when sending read characteristics command to device {}, reason: {}.", address,
560 * Write a characteristic using {@link BlueGigaAttributeWriteCommand}
562 * @param connectionHandle
565 * @return true if successful
567 public boolean bgWriteCharacteristic(int connectionHandle, int handle, int[] value) {
568 logger.debug("BlueGiga Write: connection {}, handle {}", connectionHandle, handle);
570 BlueGigaAttributeWriteCommand command = new BlueGigaAttributeWriteCommand.CommandBuilder()
571 .withConnection(connectionHandle)
572 .withAttHandle(handle)
577 return sendCommand(command, BlueGigaAttributeWriteResponse.class, true)
578 .getResult() == BgApiResponse.SUCCESS;
579 } catch (BlueGigaException e) {
580 logger.debug("Error occured when sending write characteristics command to device {}, reason: {}.", address,
587 * The following methods are private methods for handling the BlueGiga protocol
589 private boolean bgEndProcedure() {
591 return sendCommandWithoutChecks(new BlueGigaEndProcedureCommand(), BlueGigaEndProcedureResponse.class)
592 .getResult() == BgApiResponse.SUCCESS;
593 } catch (BlueGigaException e) {
594 logger.debug("Error occured when sending end procedure command.");
599 private boolean bgSetMode() {
602 BlueGigaSetModeCommand command = new BlueGigaSetModeCommand.CommandBuilder()
603 .withConnect(GapConnectableMode.GAP_NON_CONNECTABLE)
604 .withDiscover(GapDiscoverableMode.GAP_NON_DISCOVERABLE)
607 return sendCommandWithoutChecks(command, BlueGigaSetModeResponse.class)
608 .getResult() == BgApiResponse.SUCCESS;
609 } catch (BlueGigaException e) {
610 logger.debug("Error occured when sending set mode command, reason: {}", e.getMessage());
616 * Starts scanning on the dongle
618 * @param active true for active scanning
620 private boolean bgStartScanning(boolean active, int interval, int window) {
623 BlueGigaSetScanParametersCommand scanCommand = new BlueGigaSetScanParametersCommand.CommandBuilder()
624 .withActiveScanning(active)
625 .withScanInterval(interval)
626 .withScanWindow(window)
629 if (sendCommand(scanCommand, BlueGigaSetScanParametersResponse.class, false)
630 .getResult() == BgApiResponse.SUCCESS) {
631 BlueGigaDiscoverCommand discoverCommand = new BlueGigaDiscoverCommand.CommandBuilder()
632 .withMode(GapDiscoverMode.GAP_DISCOVER_OBSERVATION).build();
633 if (sendCommand(discoverCommand, BlueGigaDiscoverResponse.class, false)
634 .getResult() == BgApiResponse.SUCCESS) {
635 logger.debug("{} scanning succesfully started.", active ? "Active" : "Passive");
639 } catch (BlueGigaException e) {
640 logger.debug("Error occured when sending start scan command, reason: {}", e.getMessage());
642 logger.debug("Scan start failed.");
647 * Send command only if initialization phase is successfully done
649 private <T extends BlueGigaResponse> T sendCommand(BlueGigaCommand command, Class<T> expectedResponse,
650 boolean schedulePassiveScan) throws BlueGigaException {
652 throw new BlueGigaException("BlueGiga not initialized");
655 if (schedulePassiveScan) {
656 cancelScheduledPassiveScan();
659 return sendCommandWithoutChecks(command, expectedResponse);
661 if (schedulePassiveScan) {
662 schedulePassiveScan();
668 * Forcefully send command without any checks
670 private <T extends BlueGigaResponse> T sendCommandWithoutChecks(BlueGigaCommand command, Class<T> expectedResponse)
671 throws BlueGigaException {
672 if (transactionManager.isPresent()) {
673 return transactionManager.get().sendTransaction(command, expectedResponse, COMMAND_TIMEOUT_MS);
675 throw new BlueGigaException("Transaction manager missing");
680 * Add an event listener for the BlueGiga events
682 * @param listener the {@link BlueGigaEventListener} to add
684 public void addEventListener(BlueGigaEventListener listener) {
685 transactionManager.ifPresent(manager -> {
686 manager.addEventListener(listener);
691 * Remove an event listener for the BlueGiga events
693 * @param listener the {@link BlueGigaEventListener} to remove
695 public void removeEventListener(BlueGigaEventListener listener) {
696 transactionManager.ifPresent(manager -> {
697 manager.removeEventListener(listener);
702 public void bluegigaEventReceived(@Nullable BlueGigaResponse event) {
703 if (event instanceof BlueGigaScanResponseEvent) {
705 BlueGigaScanResponseEvent scanEvent = (BlueGigaScanResponseEvent) event;
707 // We use the scan event to add any devices we hear to the devices list
708 // The device gets created, and then manages itself for discovery etc.
709 BluetoothAddress sender = new BluetoothAddress(scanEvent.getSender());
710 if (!devices.containsKey(sender)) {
711 BlueGigaBluetoothDevice device;
712 logger.debug("BlueGiga adding new device to adaptor {}: {}", address, sender);
713 device = new BlueGigaBluetoothDevice(this, new BluetoothAddress(scanEvent.getSender()),
714 scanEvent.getAddressType());
715 devices.put(sender, device);
716 deviceDiscovered(device);
719 logger.trace("Ignore BlueGigaScanResponseEvent as initialization is not complete");
724 if (event instanceof BlueGigaConnectionStatusEvent) {
725 BlueGigaConnectionStatusEvent connectionEvent = (BlueGigaConnectionStatusEvent) event;
726 connections.put(connectionEvent.getConnection(), new BluetoothAddress(connectionEvent.getAddress()));
729 if (event instanceof BlueGigaDisconnectedEvent) {
730 BlueGigaDisconnectedEvent disconnectedEvent = (BlueGigaDisconnectedEvent) event;
731 connections.remove(disconnectedEvent.getConnection());
736 public void bluegigaClosed(Exception reason) {
737 logger.debug("BlueGiga connection closed, request reinitialization");
738 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, reason.getMessage());
739 initComplete = false;