added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.bluetooth.roaming-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-bluetooth-roaming" description="Bluetooth Binding Roaming" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth/${project.version}</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.roaming/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bluetooth.roaming.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.BluetoothBindingConstants;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link RoamingBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RoamingBindingConstants {
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_ROAMING = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID,
|
||||
"roaming");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_ROAMING);
|
||||
|
||||
public static final String CONFIGURATION_GROUP_ADAPTER_UIDS = "groupUIDs";
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bluetooth.roaming.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.BluetoothAdapter;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
/**
|
||||
* The {@link RoamingBluetoothAdapter} adds additional functionality to {@link BluetoothAdapter}
|
||||
* but more importantly serves as a tagging interface to expose it as an OSGI service.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface RoamingBluetoothAdapter extends BluetoothAdapter {
|
||||
|
||||
void addBluetoothAdapter(BluetoothAdapter adapter);
|
||||
|
||||
void removeBluetoothAdapter(BluetoothAdapter adapter);
|
||||
|
||||
boolean isDiscoveryEnabled();
|
||||
|
||||
boolean isRoamingMember(ThingUID adapterUID);
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bluetooth.roaming.internal;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.BluetoothAdapter;
|
||||
import org.openhab.binding.bluetooth.BluetoothAddress;
|
||||
import org.openhab.binding.bluetooth.BluetoothCharacteristic;
|
||||
import org.openhab.binding.bluetooth.BluetoothCompletionStatus;
|
||||
import org.openhab.binding.bluetooth.BluetoothDescriptor;
|
||||
import org.openhab.binding.bluetooth.BluetoothDevice;
|
||||
import org.openhab.binding.bluetooth.BluetoothDeviceListener;
|
||||
import org.openhab.binding.bluetooth.DelegateBluetoothDevice;
|
||||
import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification;
|
||||
import org.openhab.binding.bluetooth.notification.BluetoothScanNotification;
|
||||
|
||||
/**
|
||||
* The {@link RoamingBluetoothDevice} acts as a roaming device by delegating
|
||||
* its operations to actual adapters.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RoamingBluetoothDevice extends DelegateBluetoothDevice {
|
||||
|
||||
private final Map<BluetoothDevice, Listener> devices = new ConcurrentHashMap<>();
|
||||
|
||||
private final List<BluetoothDeviceListener> eventListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
private final AtomicReference<@Nullable BluetoothDevice> currentDelegateRef = new AtomicReference<>();
|
||||
|
||||
protected RoamingBluetoothDevice(RoamingBridgeHandler roamingAdapter, BluetoothAddress address) {
|
||||
super(roamingAdapter, address);
|
||||
}
|
||||
|
||||
public void addBluetoothDevice(BluetoothDevice device) {
|
||||
device.addListener(devices.computeIfAbsent(device, Listener::new));
|
||||
}
|
||||
|
||||
public void removeBluetoothDevice(BluetoothDevice device) {
|
||||
device.removeListener(devices.remove(device));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<BluetoothDeviceListener> getListeners() {
|
||||
return eventListeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable BluetoothDevice getDelegate() {
|
||||
BluetoothDevice newDelegate = null;
|
||||
int newRssi = Integer.MIN_VALUE;
|
||||
for (BluetoothDevice device : devices.keySet()) {
|
||||
ConnectionState state = device.getConnectionState();
|
||||
if (state == ConnectionState.CONNECTING || state == ConnectionState.CONNECTED) {
|
||||
newDelegate = device;
|
||||
break;
|
||||
}
|
||||
Integer rssi = device.getRssi();
|
||||
if (rssi != null && (newDelegate == null || rssi > newRssi)) {
|
||||
newRssi = rssi;
|
||||
newDelegate = device;
|
||||
}
|
||||
}
|
||||
BluetoothDevice oldDelegate = currentDelegateRef.getAndSet(newDelegate);
|
||||
if (oldDelegate != newDelegate) { // using reference comparison is valid in this case
|
||||
notifyListeners(BluetoothEventType.ADAPTER_CHANGED, getAdapter(newDelegate));
|
||||
}
|
||||
return newDelegate;
|
||||
}
|
||||
|
||||
private BluetoothAdapter getAdapter(@Nullable BluetoothDevice delegate) {
|
||||
if (delegate != null) {
|
||||
return delegate.getAdapter();
|
||||
}
|
||||
// as a last resort we return our "actual" adapter
|
||||
return super.getAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BluetoothAdapter getAdapter() {
|
||||
return getAdapter(currentDelegateRef.get());
|
||||
}
|
||||
|
||||
private class Listener implements BluetoothDeviceListener {
|
||||
|
||||
private BluetoothDevice device;
|
||||
|
||||
public Listener(BluetoothDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScanRecordReceived(BluetoothScanNotification scanNotification) {
|
||||
if (device == getDelegate()) {
|
||||
notifyListeners(BluetoothEventType.SCAN_RECORD, scanNotification);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionStateChange(BluetoothConnectionStatusNotification connectionNotification) {
|
||||
if (device == getDelegate()) {
|
||||
notifyListeners(BluetoothEventType.CONNECTION_STATE, connectionNotification);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered() {
|
||||
device.getServices().forEach(RoamingBluetoothDevice.this::addService);
|
||||
if (device == getDelegate()) {
|
||||
notifyListeners(BluetoothEventType.SERVICES_DISCOVERED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicReadComplete(BluetoothCharacteristic characteristic,
|
||||
BluetoothCompletionStatus status) {
|
||||
if (device == getDelegate()) {
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic, status);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWriteComplete(BluetoothCharacteristic characteristic,
|
||||
BluetoothCompletionStatus status) {
|
||||
if (device == getDelegate()) {
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_WRITE_COMPLETE, characteristic);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicUpdate(BluetoothCharacteristic characteristic) {
|
||||
if (device == getDelegate()) {
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_UPDATED, characteristic);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorUpdate(BluetoothDescriptor bluetoothDescriptor) {
|
||||
if (device == getDelegate()) {
|
||||
notifyListeners(BluetoothEventType.DESCRIPTOR_UPDATED, bluetoothDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdapterChanged(BluetoothAdapter adapter) {
|
||||
// do nothing since we are the ones that are supposed to trigger this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bluetooth.roaming.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.BluetoothAdapter;
|
||||
import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryDevice;
|
||||
import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryParticipant;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.component.annotations.ReferenceCardinality;
|
||||
import org.osgi.service.component.annotations.ReferencePolicy;
|
||||
|
||||
/**
|
||||
* The {@link RoamingBluetoothDiscoveryParticipant} acts as the roaming adapter's gateway
|
||||
* to the osgi layer where it can find other adapter instances to use as delegates.
|
||||
* This class also serves to generate the default roaming adapter discovery result for the user.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(immediate = true, service = { BluetoothDiscoveryParticipant.class })
|
||||
public class RoamingBluetoothDiscoveryParticipant implements BluetoothDiscoveryParticipant {
|
||||
|
||||
private final Set<BluetoothAdapter> adapters = new CopyOnWriteArraySet<>();
|
||||
private final Set<RoamingBluetoothAdapter> roamingAdapters = new CopyOnWriteArraySet<>();
|
||||
|
||||
// private Optional<RoamingBluetoothAdapter> roamingAdapter = Optional.empty();
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
|
||||
protected void setRoamingBluetoothAdapter(RoamingBluetoothAdapter roamingAdapter) {
|
||||
roamingAdapters.add(roamingAdapter);
|
||||
adapters.forEach(roamingAdapter::addBluetoothAdapter);
|
||||
}
|
||||
|
||||
protected void unsetRoamingBluetoothAdapter(RoamingBluetoothAdapter roamingAdapter) {
|
||||
roamingAdapters.remove(roamingAdapter);
|
||||
adapters.forEach(roamingAdapter::removeBluetoothAdapter);
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
|
||||
protected void addBluetoothAdapter(BluetoothAdapter adapter) {
|
||||
this.adapters.add(adapter);
|
||||
|
||||
roamingAdapters.forEach(ra -> {
|
||||
ra.addBluetoothAdapter(adapter);
|
||||
});
|
||||
}
|
||||
|
||||
protected void removeBluetoothAdapter(BluetoothAdapter adapter) {
|
||||
this.adapters.remove(adapter);
|
||||
|
||||
roamingAdapters.forEach(ra -> {
|
||||
ra.removeBluetoothAdapter(adapter);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishAdditionalResults(DiscoveryResult result,
|
||||
BiConsumer<BluetoothAdapter, DiscoveryResult> publisher) {
|
||||
// we create a roaming version of every discoveryResult.
|
||||
roamingAdapters.forEach(roamingAdapter -> {
|
||||
ThingUID adapterUID = result.getBridgeUID();
|
||||
if (adapterUID != null && roamingAdapter.isDiscoveryEnabled()
|
||||
&& roamingAdapter.isRoamingMember(adapterUID)) {
|
||||
publisher.accept(roamingAdapter, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bluetooth.roaming.internal;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.BluetoothAdapter;
|
||||
import org.openhab.binding.bluetooth.BluetoothAddress;
|
||||
import org.openhab.binding.bluetooth.BluetoothBindingConstants;
|
||||
import org.openhab.binding.bluetooth.BluetoothDiscoveryListener;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link RoamingBridgeHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RoamingBridgeHandler extends BaseBridgeHandler implements RoamingBluetoothAdapter {
|
||||
|
||||
private final Set<BluetoothAdapter> adapters = new CopyOnWriteArraySet<>();
|
||||
|
||||
/*
|
||||
* Note: this will only populate from handlers calling getDevice(BluetoothAddress), so we don't need
|
||||
* to do periodic cleanup.
|
||||
*/
|
||||
private Map<BluetoothAddress, RoamingBluetoothDevice> devices = new HashMap<>();
|
||||
private ThingUID[] groupUIDs = new ThingUID[0];
|
||||
|
||||
public RoamingBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
Object value = getConfig().get(RoamingBindingConstants.CONFIGURATION_GROUP_ADAPTER_UIDS);
|
||||
if (value == null || !(value instanceof String) || "".equals(value)) {
|
||||
groupUIDs = new ThingUID[0];
|
||||
} else {
|
||||
String groupIds = (String) value;
|
||||
groupUIDs = Stream.of(groupIds.split(",")).map(ThingUID::new).toArray(ThingUID[]::new);
|
||||
}
|
||||
|
||||
if (adapters.stream().map(BluetoothAdapter::getUID).anyMatch(this::isGroupMember)) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"No Physical Bluetooth adapters found");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStatus() {
|
||||
if (adapters.stream().anyMatch(this::isRoamingMember)) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"No Physical Bluetooth adapters found");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// nothing that needs to be done here.
|
||||
// Listener cleanup will be performed by the discovery participant anyway.
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThingUID getUID() {
|
||||
return getThing().getUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getLocation() {
|
||||
return getThing().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getLabel() {
|
||||
return getThing().getLabel();
|
||||
}
|
||||
|
||||
private boolean isRoamingMember(BluetoothAdapter adapter) {
|
||||
return isRoamingMember(adapter.getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRoamingMember(ThingUID adapterUID) {
|
||||
if (!isInitialized()) {
|
||||
// an unitialized roaming adapter has no members
|
||||
return false;
|
||||
}
|
||||
return isGroupMember(adapterUID);
|
||||
}
|
||||
|
||||
private boolean isGroupMember(ThingUID adapterUID) {
|
||||
if (groupUIDs.length == 0) {
|
||||
// if there are no members of the group then it is treated as all adapters are members.
|
||||
return true;
|
||||
}
|
||||
for (ThingUID uid : groupUIDs) {
|
||||
if (adapterUID.equals(uid)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDiscoveryEnabled() {
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
return false;
|
||||
}
|
||||
Object discovery = getConfig().get(BluetoothBindingConstants.CONFIGURATION_DISCOVERY);
|
||||
if (discovery != null && discovery.toString().equalsIgnoreCase("false")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBluetoothAdapter(BluetoothAdapter adapter) {
|
||||
if (adapter == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.adapters.add(adapter);
|
||||
|
||||
if (isRoamingMember(adapter)) {
|
||||
synchronized (devices) {
|
||||
for (RoamingBluetoothDevice roamingDevice : devices.values()) {
|
||||
roamingDevice.addBluetoothDevice(adapter.getDevice(roamingDevice.getAddress()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBluetoothAdapter(BluetoothAdapter adapter) {
|
||||
if (adapter == this) {
|
||||
return;
|
||||
}
|
||||
this.adapters.remove(adapter);
|
||||
|
||||
if (isRoamingMember(adapter)) {
|
||||
synchronized (devices) {
|
||||
for (RoamingBluetoothDevice roamingDevice : devices.values()) {
|
||||
roamingDevice.removeBluetoothDevice(adapter.getDevice(roamingDevice.getAddress()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDiscoveryListener(BluetoothDiscoveryListener listener) {
|
||||
// we don't use this
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDiscoveryListener(@Nullable BluetoothDiscoveryListener listener) {
|
||||
// we don't use this
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanStart() {
|
||||
// does nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanStop() {
|
||||
// does nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BluetoothAddress getAddress() {
|
||||
// roaming adapters don't have bluetooth addresses
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoamingBluetoothDevice getDevice(BluetoothAddress address) {
|
||||
// this will only get called by a bluetooth device handler
|
||||
synchronized (devices) {
|
||||
RoamingBluetoothDevice roamingDevice = devices.computeIfAbsent(address,
|
||||
addr -> new RoamingBluetoothDevice(this, addr));
|
||||
|
||||
adapters.stream().filter(this::isRoamingMember)
|
||||
.forEach(adapter -> roamingDevice.addBluetoothDevice(adapter.getDevice(address)));
|
||||
|
||||
return roamingDevice;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasHandlerForDevice(BluetoothAddress address) {
|
||||
String addrStr = address.toString();
|
||||
/*
|
||||
* This type of search is inefficient and won't scale as the number of bluetooth Thing children increases on
|
||||
* this bridge. But implementing a more efficient search would require a bit more overhead.
|
||||
* Luckily though, it is reasonable to assume that the number of Thing children will remain small.
|
||||
*/
|
||||
for (Thing childThing : getThing().getThings()) {
|
||||
Object childAddr = childThing.getConfiguration().get(BluetoothBindingConstants.CONFIGURATION_ADDRESS);
|
||||
if (addrStr.equals(childAddr)) {
|
||||
return childThing.getHandler() != null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.bluetooth.roaming.internal;
|
||||
|
||||
import static org.openhab.binding.bluetooth.roaming.internal.RoamingBindingConstants.THING_TYPE_ROAMING;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.BluetoothAdapter;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.UID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link RoamingHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.roaming", service = ThingHandlerFactory.class)
|
||||
public class RoamingHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final Map<ThingUID, @Nullable ServiceRegistration<?>> serviceRegs = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return RoamingBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_ROAMING.equals(thingTypeUID)) {
|
||||
RoamingBridgeHandler handler = new RoamingBridgeHandler((Bridge) thing);
|
||||
registerRoamingBluetoothAdapter(handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized void registerRoamingBluetoothAdapter(RoamingBluetoothAdapter adapter) {
|
||||
this.serviceRegs.put(adapter.getUID(),
|
||||
bundleContext.registerService(RoamingBluetoothAdapter.class.getName(), adapter, new Hashtable<>()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof RoamingBluetoothAdapter) {
|
||||
UID uid = ((BluetoothAdapter) thingHandler).getUID();
|
||||
ServiceRegistration<?> serviceReg = this.serviceRegs.remove(uid);
|
||||
if (serviceReg != null) {
|
||||
serviceReg.unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="bluetooth"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="roaming">
|
||||
<label>Roaming Bluetooth Controller</label>
|
||||
<description>A virtual Bluetooth adapter that handles roaming between other adapters</description>
|
||||
<config-description>
|
||||
<parameter name="groupUIDs" type="text" multiple="true">
|
||||
<label>Adapter UIDs</label>
|
||||
<description>
|
||||
<![CDATA[ Specifies which Bluetooth adapters that roaming devices can interact through.
|
||||
<br>
|
||||
Should be formatted as a comma separated list of thing UIDs.
|
||||
<br>
|
||||
If not specified, roaming devices can interact through any other Bluetooth adapter thing.
|
||||
]]>
|
||||
</description>
|
||||
<context>thing</context>
|
||||
<advanced>true</advanced>
|
||||
<default></default>
|
||||
</parameter>
|
||||
<parameter name="backgroundDiscovery" type="boolean">
|
||||
<label>Device Discovery</label>
|
||||
<description>Whether this adapter participates in Bluetooth device discovery</description>
|
||||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user