`[{"name": "com.amazon.tv.launcher", "mode": "idle"},{"name": "org.jellyfin.androidtv", "mode": "wake_lock", "wakeLockPlayStates": [2,3]},{"name": "com.amazon.firetv.youtube", "mode": "wake_lock", "wakeLockPlayStates": [2]}]`
## Record/Send input events
+
As the execution of key events takes a while, you can use input events as an alternative way to control your device.
They are pretty device specific, so you should use the record-input and recorded-input channels to store/send those events.
An example of what you can do:
+
* You can send the command `UP` to the `record-input` channel the binding will then capture the events you send through your remote for the defined recordDuration config for the thing, so press once the UP key on your remote and wait a while.
* Now that you have recorded your input, you can send the `UP` command to the `recorded-input` event and it will send the recorded event to the android device.
<feature name="openhab-binding-androiddebugbridge" description="Android Debug Bridge Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
+ <feature>openhab-transport-mdns</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.androiddebugbridge/${project.version}</bundle>
</feature>
</features>
*/
package org.openhab.binding.androiddebugbridge.internal;
-import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_ANDROID_DEVICE = new ThingTypeUID(BINDING_ID, "android");
- public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_ANDROID_DEVICE);
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_ANDROID_DEVICE);
// List of all Channel ids
public static final String KEY_EVENT_CHANNEL = "key-event";
public static final String TEXT_CHANNEL = "text";
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
-import java.util.concurrent.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
private @Nullable AdbConnection connection;
private @Nullable Future<String> commandFuture;
- AndroidDebugBridgeDevice(ScheduledExecutorService scheduler) {
+ public AndroidDebugBridgeDevice(ScheduledExecutorService scheduler) {
this.scheduler = scheduler;
}
try {
return Integer.parseInt(lockResp.replace("\n", "").split("=")[1].trim());
} catch (NumberFormatException e) {
- logger.debug("Unable to parse device wake lock: {}", e.getMessage());
+ String message = String.format("Unable to parse device wake-lock '%s'", lockResp);
+ logger.debug("{}: {}", message, e.getMessage());
+ throw new AndroidDebugBridgeDeviceReadException(message);
}
}
- throw new AndroidDebugBridgeDeviceReadException("Unable to read wake lock");
+ throw new AndroidDebugBridgeDeviceReadException(String.format("Unable to read wake-lock '%s'", lockResp));
}
private void setVolume(int stream, int volume)
return getDeviceProp("ro.serialno");
}
+ public String getMacAddress() throws AndroidDebugBridgeDeviceException, InterruptedException,
+ AndroidDebugBridgeDeviceReadException, TimeoutException, ExecutionException {
+ return getDeviceProp("ro.boot.wifimacaddr").toLowerCase();
+ }
+
private String getDeviceProp(String name) throws AndroidDebugBridgeDeviceException, InterruptedException,
AndroidDebugBridgeDeviceReadException, TimeoutException, ExecutionException {
var propValue = runAdbShell("getprop", name, "&&", "sleep", "0.3").replace("\n", "").replace("\r", "");
if (propValue.length() == 0) {
- throw new AndroidDebugBridgeDeviceReadException("Unable to get device property");
+ throw new AndroidDebugBridgeDeviceReadException(String.format("Unable to get device property '%s'", name));
}
return propValue;
}
sock.connect(new InetSocketAddress(ip, port), (int) TimeUnit.SECONDS.toMillis(15));
} catch (IOException e) {
logger.debug("Error connecting to {}: [{}] {}", ip, e.getClass().getName(), e.getMessage());
- if (e.getMessage().equals("Socket closed")) {
+ if ("Socket closed".equals(e.getMessage())) {
// Connection aborted by us
throw new InterruptedException();
}
var byteArrayOutputStream = new ByteArrayOutputStream();
String cmd = String.join(" ", args);
logger.debug("{} - shell:{}", ip, cmd);
- try {
- AdbStream stream = adb.open("shell:" + cmd);
+ try (AdbStream stream = adb.open("shell:" + cmd)) {
do {
byteArrayOutputStream.writeBytes(stream.read());
} while (!stream.isClosed());
} catch (IOException e) {
- String message = e.getMessage();
- if (message != null && !message.equals("Stream closed")) {
+ if (!"Stream closed".equals(e.getMessage())) {
throw e;
}
}
package org.openhab.binding.androiddebugbridge.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.androiddebugbridge.internal.discovery.AndroidDebugBridgeDiscoveryService;
/**
* The {@link AndroidDebugBridgeDiscoveryService} discover Android ADB Instances in the network.
package org.openhab.binding.androiddebugbridge.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.androiddebugbridge.internal.discovery.AndroidDebugBridgeDiscoveryService;
/**
* The {@link AndroidDebugBridgeDiscoveryService} discover Android ADB Instances in the network.
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 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.androiddebugbridge.internal;
-
-import static org.openhab.binding.androiddebugbridge.internal.AndroidDebugBridgeBindingConstants.*;
-
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.config.discovery.AbstractDiscoveryService;
-import org.openhab.core.config.discovery.DiscoveryResultBuilder;
-import org.openhab.core.config.discovery.DiscoveryService;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingUID;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link AndroidDebugBridgeDiscoveryService} discover Android ADB Instances in the network.
- *
- * @author Miguel Alvarez - Initial contribution
- */
-@NonNullByDefault
-@Component(service = DiscoveryService.class, configurationPid = "discovery.androiddebugbridge")
-public class AndroidDebugBridgeDiscoveryService extends AbstractDiscoveryService {
- static final int TIMEOUT_MS = 60000;
- private static final long DISCOVERY_RESULT_TTL_SEC = 300;
- public static final String LOCAL_INTERFACE_IP = "127.0.0.1";
- public static final int MAX_RETRIES = 2;
- private final Logger logger = LoggerFactory.getLogger(AndroidDebugBridgeDiscoveryService.class);
- private final ConfigurationAdmin admin;
- private boolean discoveryRunning = false;
-
- @Activate
- public AndroidDebugBridgeDiscoveryService(@Reference ConfigurationAdmin admin) {
- super(SUPPORTED_THING_TYPES, TIMEOUT_MS, false);
- this.admin = admin;
- }
-
- @Override
- protected void startScan() {
- logger.debug("scan started: searching android devices");
- discoveryRunning = true;
- Enumeration<NetworkInterface> nets;
- AndroidDebugBridgeBindingConfiguration configuration = getConfig();
- if (configuration == null) {
- return;
- }
- try {
- nets = NetworkInterface.getNetworkInterfaces();
- for (NetworkInterface netint : Collections.list(nets)) {
- Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();
- for (InetAddress inetAddress : Collections.list(inetAddresses)) {
- if (!discoveryRunning) {
- break;
- }
- if (!(inetAddress instanceof Inet4Address)
- || inetAddress.getHostAddress().equals(LOCAL_INTERFACE_IP)) {
- continue;
- }
- String[] ipParts = inetAddress.getHostAddress().split("\\.");
- for (int i = configuration.discoveryIpRangeMin; i <= configuration.discoveryIpRangeMax; i++) {
- if (!discoveryRunning) {
- break;
- }
- ipParts[3] = Integer.toString(i);
- String currentIp = String.join(".", ipParts);
- try {
- var currentAddress = InetAddress.getByName(currentIp);
- logger.debug("address: {}", currentIp);
- if (currentAddress.isReachable(configuration.discoveryReachableMs)) {
- logger.debug("Reachable ip: {}", currentIp);
- int retries = 0;
- while (retries < MAX_RETRIES) {
- try {
- discoverWithADB(currentIp, configuration.discoveryPort);
- } catch (AndroidDebugBridgeDeviceReadException | TimeoutException e) {
- retries++;
- if (retries < MAX_RETRIES) {
- logger.debug("retrying - pending {}", MAX_RETRIES - retries);
- continue;
- }
- throw e;
- }
- break;
- }
- }
- } catch (IOException | AndroidDebugBridgeDeviceException | AndroidDebugBridgeDeviceReadException
- | TimeoutException | ExecutionException e) {
- logger.debug("Error connecting to device at {}: {}", currentIp, e.getMessage());
- }
- }
- }
- }
- } catch (SocketException | InterruptedException e) {
- logger.warn("Error while discovering: {}", e.getMessage());
- }
- }
-
- private void discoverWithADB(String ip, int port) throws InterruptedException, AndroidDebugBridgeDeviceException,
- AndroidDebugBridgeDeviceReadException, TimeoutException, ExecutionException {
- var device = new AndroidDebugBridgeDevice(scheduler);
- device.configure(ip, port, 10, 0);
- try {
- device.connect();
- logger.debug("connected adb at {}:{}", ip, port);
- String serialNo = device.getSerialNo();
- String model = device.getModel();
- String androidVersion = device.getAndroidVersion();
- String brand = device.getBrand();
- logger.debug("discovered: {} - {} - {} - {}", model, serialNo, androidVersion, brand);
- onDiscoverResult(serialNo, ip, port, model, androidVersion, brand);
- } finally {
- device.disconnect();
- }
- }
-
- @Override
- protected void stopScan() {
- super.stopScan();
- discoveryRunning = false;
- logger.debug("scan stopped");
- }
-
- private void onDiscoverResult(String serialNo, String ip, int port, String model, String androidVersion,
- String brand) {
- Map<String, Object> properties = new HashMap<>();
- properties.put(Thing.PROPERTY_SERIAL_NUMBER, serialNo);
- properties.put(PARAMETER_IP, ip);
- properties.put(PARAMETER_PORT, port);
- properties.put(Thing.PROPERTY_MODEL_ID, model);
- properties.put(Thing.PROPERTY_VENDOR, brand);
- properties.put(Thing.PROPERTY_FIRMWARE_VERSION, androidVersion);
- thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_ANDROID_DEVICE, serialNo))
- .withTTL(DISCOVERY_RESULT_TTL_SEC).withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER)
- .withProperties(properties).withLabel(String.format("%s (%s)", model, serialNo)).build());
- }
-
- private @Nullable AndroidDebugBridgeBindingConfiguration getConfig() {
- try {
- Configuration configOnline = admin.getConfiguration(BINDING_CONFIGURATION_PID, null);
- if (configOnline != null) {
- Dictionary<String, Object> props = configOnline.getProperties();
- if (props != null) {
- Map<String, Object> propMap = Collections.list(props.keys()).stream()
- .collect(Collectors.toMap(Function.identity(), props::get));
- return new org.openhab.core.config.core.Configuration(propMap)
- .as(AndroidDebugBridgeBindingConfiguration.class);
- }
- }
- } catch (IOException e) {
- logger.warn("Unable to read configuration: {}", e.getMessage());
- }
- return null;
- }
-}
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
- var currentConfig = config;
- if (currentConfig == null) {
- return;
- }
+ AndroidDebugBridgeConfiguration currentConfig = config;
try {
if (!adbConnection.isConnected()) {
// try reconnect
@Override
public void initialize() {
- var currentConfig = getConfigAs(AndroidDebugBridgeConfiguration.class);
+ AndroidDebugBridgeConfiguration currentConfig = getConfigAs(AndroidDebugBridgeConfiguration.class);
config = currentConfig;
var mediaStateJSONConfig = currentConfig.mediaStateJSONConfig;
if (mediaStateJSONConfig != null && !mediaStateJSONConfig.isEmpty()) {
}
public void checkConnection() {
- var currentConfig = config;
- if (currentConfig == null) {
- return;
- }
+ AndroidDebugBridgeConfiguration currentConfig = config;
try {
logger.debug("Refresh device {} status", currentConfig.ip);
if (adbConnection.isConnected()) {
updateStatus(ThingStatus.ONLINE);
+ refreshProperties();
refreshStatus();
} else {
try {
}
if (adbConnection.isConnected()) {
updateStatus(ThingStatus.ONLINE);
+ refreshProperties();
refreshStatus();
}
}
} catch (InterruptedException ignored) {
- } catch (AndroidDebugBridgeDeviceException | ExecutionException e) {
+ } catch (AndroidDebugBridgeDeviceException | AndroidDebugBridgeDeviceReadException | ExecutionException e) {
logger.debug("Connection checker error: {}", e.getMessage());
adbConnection.disconnect();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
+ private void refreshProperties() throws InterruptedException, AndroidDebugBridgeDeviceException,
+ AndroidDebugBridgeDeviceReadException, ExecutionException {
+ // Add some information about the device
+ try {
+ Map<String, String> editProperties = editProperties();
+ editProperties.put(Thing.PROPERTY_SERIAL_NUMBER, adbConnection.getSerialNo());
+ editProperties.put(Thing.PROPERTY_MODEL_ID, adbConnection.getModel());
+ editProperties.put(Thing.PROPERTY_FIRMWARE_VERSION, adbConnection.getAndroidVersion());
+ editProperties.put(Thing.PROPERTY_VENDOR, adbConnection.getBrand());
+ try {
+ editProperties.put(Thing.PROPERTY_MAC_ADDRESS, adbConnection.getMacAddress());
+ } catch (AndroidDebugBridgeDeviceReadException e) {
+ logger.debug("Refresh properties error: {}", e.getMessage());
+ }
+ updateProperties(editProperties);
+ } catch (TimeoutException e) {
+ logger.debug("Refresh properties error: Timeout");
+ return;
+ }
+ }
+
private void refreshStatus() throws InterruptedException, AndroidDebugBridgeDeviceException, ExecutionException {
boolean awakeState;
boolean prevDeviceAwake = deviceAwake;
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 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.androiddebugbridge.internal.discovery;
+
+import static org.openhab.binding.androiddebugbridge.internal.AndroidDebugBridgeBindingConstants.*;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.androiddebugbridge.internal.AndroidDebugBridgeBindingConfiguration;
+import org.openhab.binding.androiddebugbridge.internal.AndroidDebugBridgeDevice;
+import org.openhab.binding.androiddebugbridge.internal.AndroidDebugBridgeDeviceException;
+import org.openhab.binding.androiddebugbridge.internal.AndroidDebugBridgeDeviceReadException;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link AndroidDebugBridgeDiscoveryService} discover Android ADB Instances in the network.
+ *
+ * @author Miguel Alvarez - Initial contribution
+ */
+@NonNullByDefault
+@Component(service = DiscoveryService.class, configurationPid = "discovery.androiddebugbridge")
+public class AndroidDebugBridgeDiscoveryService extends AbstractDiscoveryService {
+
+ static final int TIMEOUT_MS = 60000;
+ private static final long DISCOVERY_RESULT_TTL_SEC = 300;
+ public static final String LOCAL_INTERFACE_IP = "127.0.0.1";
+ public static final int MAX_RETRIES = 2;
+ private final Logger logger = LoggerFactory.getLogger(AndroidDebugBridgeDiscoveryService.class);
+ private final ConfigurationAdmin admin;
+ private boolean discoveryRunning = false;
+
+ @Activate
+ public AndroidDebugBridgeDiscoveryService(final @Reference ConfigurationAdmin admin) {
+ super(SUPPORTED_THING_TYPES, TIMEOUT_MS, false);
+ this.admin = admin;
+ }
+
+ @Override
+ protected void startScan() {
+ logger.debug("scan started: searching android devices");
+ discoveryRunning = true;
+ Enumeration<NetworkInterface> nets;
+ AndroidDebugBridgeBindingConfiguration configuration = getConfig();
+ if (configuration == null) {
+ return;
+ }
+ try {
+ nets = NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface netint : Collections.list(nets)) {
+ Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();
+ for (InetAddress inetAddress : Collections.list(inetAddresses)) {
+ if (!discoveryRunning) {
+ break;
+ }
+ if (!(inetAddress instanceof Inet4Address)
+ || inetAddress.getHostAddress().equals(LOCAL_INTERFACE_IP)) {
+ continue;
+ }
+ String[] ipParts = inetAddress.getHostAddress().split("\\.");
+ for (int i = configuration.discoveryIpRangeMin; i <= configuration.discoveryIpRangeMax; i++) {
+ if (!discoveryRunning) {
+ break;
+ }
+ ipParts[3] = Integer.toString(i);
+ String currentIp = String.join(".", ipParts);
+ try {
+ var currentAddress = InetAddress.getByName(currentIp);
+ logger.debug("address: {}", currentIp);
+ if (currentAddress.isReachable(configuration.discoveryReachableMs)) {
+ logger.debug("Reachable ip: {}", currentIp);
+ int retries = 0;
+ while (retries < MAX_RETRIES) {
+ try {
+ discoverWithADB(currentIp, configuration.discoveryPort,
+ new String(netint.getHardwareAddress()).toLowerCase());
+ } catch (AndroidDebugBridgeDeviceReadException | TimeoutException e) {
+ retries++;
+ if (retries < MAX_RETRIES) {
+ logger.debug("retrying - pending {}", MAX_RETRIES - retries);
+ continue;
+ }
+ throw e;
+ }
+ break;
+ }
+ }
+ } catch (IOException | AndroidDebugBridgeDeviceException | AndroidDebugBridgeDeviceReadException
+ | TimeoutException | ExecutionException e) {
+ logger.debug("Error connecting to device at {}: {}", currentIp, e.getMessage());
+ }
+ }
+ }
+ }
+ } catch (SocketException | InterruptedException e) {
+ logger.warn("Error while discovering: {}", e.getMessage());
+ }
+ }
+
+ private void discoverWithADB(String ip, int port, String macAddress)
+ throws InterruptedException, AndroidDebugBridgeDeviceException, AndroidDebugBridgeDeviceReadException,
+ TimeoutException, ExecutionException {
+ var device = new AndroidDebugBridgeDevice(scheduler);
+ device.configure(ip, port, 10, 0);
+ try {
+ device.connect();
+ logger.debug("connected adb at {}:{}", ip, port);
+ String serialNo = device.getSerialNo();
+ String model = device.getModel();
+ String androidVersion = device.getAndroidVersion();
+ String brand = device.getBrand();
+ logger.debug("discovered: {} - {} - {} - {} - {}", model, serialNo, androidVersion, brand, macAddress);
+ onDiscoverResult(serialNo, ip, port, model, androidVersion, brand, macAddress);
+ } finally {
+ device.disconnect();
+ }
+ }
+
+ @Override
+ protected void stopScan() {
+ super.stopScan();
+ discoveryRunning = false;
+ logger.debug("scan stopped");
+ }
+
+ private void onDiscoverResult(String serialNo, String ip, int port, String model, String androidVersion,
+ String brand, String macAddress) {
+ String friendlyName = String.format("%s (%s)", model, ip);
+ thingDiscovered(
+ DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_ANDROID_DEVICE, macAddress.replaceAll(":", ""))) //
+ .withProperties(Map.of( //
+ PARAMETER_IP, ip, //
+ PARAMETER_PORT, port, //
+ Thing.PROPERTY_MAC_ADDRESS, macAddress, //
+ Thing.PROPERTY_SERIAL_NUMBER, serialNo, //
+ Thing.PROPERTY_MODEL_ID, model, //
+ Thing.PROPERTY_VENDOR, brand, //
+ Thing.PROPERTY_FIRMWARE_VERSION, androidVersion //
+ )) //
+ .withLabel(friendlyName) //
+ .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS) //
+ .withTTL(DISCOVERY_RESULT_TTL_SEC) //
+ .build());
+ }
+
+ private @Nullable AndroidDebugBridgeBindingConfiguration getConfig() {
+ try {
+ Configuration configOnline = admin.getConfiguration(BINDING_CONFIGURATION_PID, null);
+ if (configOnline != null) {
+ Dictionary<String, Object> props = configOnline.getProperties();
+ if (props != null) {
+ Map<String, Object> propMap = Collections.list(props.keys()).stream()
+ .collect(Collectors.toMap(Function.identity(), props::get));
+ return new org.openhab.core.config.core.Configuration(propMap)
+ .as(AndroidDebugBridgeBindingConfiguration.class);
+ }
+ }
+ } catch (IOException e) {
+ logger.warn("Unable to read configuration: {}", e.getMessage());
+ }
+ return null;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 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.androiddebugbridge.internal.discovery;
+
+import static org.openhab.binding.androiddebugbridge.internal.AndroidDebugBridgeBindingConstants.*;
+
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jmdns.ServiceInfo;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
+import org.openhab.core.config.discovery.mdns.internal.MDNSDiscoveryService;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+
+/**
+ * The {@link FireTVStickMDNSDiscoveryParticipant} is responsible for discovering new and removed Fire TV Stick. It uses
+ * the central {@link MDNSDiscoveryService}.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@Component(configurationPid = "discovery.androiddebugbridge")
+@NonNullByDefault
+public class FireTVStickMDNSDiscoveryParticipant implements MDNSDiscoveryParticipant {
+
+ private static final String SERVICE_TYPE = "_amzn-wplay._tcp.local.";
+ private static final String MDNS_PROPERTY_NAME = "n";
+ private static final String MDNS_PROPERTY_MAC_ADDRESS = "c";
+
+ private boolean isAutoDiscoveryEnabled = true;
+
+ @Activate
+ protected void activate(ComponentContext componentContext) {
+ activateOrModifyService(componentContext);
+ }
+
+ @Modified
+ protected void modified(ComponentContext componentContext) {
+ activateOrModifyService(componentContext);
+ }
+
+ private void activateOrModifyService(ComponentContext componentContext) {
+ Dictionary<String, @Nullable Object> properties = componentContext.getProperties();
+ String autoDiscoveryPropertyValue = (String) properties
+ .get(DiscoveryService.CONFIG_PROPERTY_BACKGROUND_DISCOVERY);
+ if (autoDiscoveryPropertyValue != null && !autoDiscoveryPropertyValue.isBlank()) {
+ isAutoDiscoveryEnabled = Boolean.valueOf(autoDiscoveryPropertyValue);
+ }
+ }
+
+ @Override
+ public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
+ return SUPPORTED_THING_TYPES;
+ }
+
+ @Override
+ public String getServiceType() {
+ return SERVICE_TYPE;
+ }
+
+ @Override
+ public @Nullable DiscoveryResult createResult(ServiceInfo service) {
+ if (isAutoDiscoveryEnabled) {
+ ThingUID uid = getThingUID(service);
+ if (uid != null) {
+ String ip = service.getHostAddresses()[0];
+ String macAddress = service.getPropertyString(MDNS_PROPERTY_MAC_ADDRESS);
+ String friendlyName = String.format("%s (%s)", service.getPropertyString(MDNS_PROPERTY_NAME), ip);
+ return DiscoveryResultBuilder.create(uid) //
+ .withProperties(Map.of( //
+ PARAMETER_IP, ip, //
+ Thing.PROPERTY_MAC_ADDRESS, macAddress.toLowerCase())) //
+ .withLabel(friendlyName) //
+ .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS) //
+ .build();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public @Nullable ThingUID getThingUID(ServiceInfo service) {
+ String macAddress = service.getPropertyString(MDNS_PROPERTY_MAC_ADDRESS);
+ if (macAddress != null && !macAddress.isBlank()) {
+ return new ThingUID(THING_TYPE_ANDROID_DEVICE, macAddress.replaceAll(":", "").toLowerCase());
+ }
+ return null;
+ }
+}
<channel id="shutdown" typeId="shutdown-channel"/>
<channel id="awake-state" typeId="awake-state-channel"/>
</channels>
- <representation-property>serial</representation-property>
+ <representation-property>macAddress</representation-property>
<config-description>
<parameter name="ip" type="text" required="true">
<context>network-address</context>