Skip to content

Commit

Permalink
[androiddebugbridge] Added DynamicCommandOptionsProvider to populate …
Browse files Browse the repository at this point in the history
…'start-package' Channel (openhab#12491)

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
  • Loading branch information
cweitkamp authored and psmedley committed Feb 23, 2023
1 parent 5e732a1 commit 45fee2e
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 25 deletions.
41 changes: 20 additions & 21 deletions bundles/org.openhab.binding.androiddebugbridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ The available modes are:

The configuration depends on the application, device and version used.

This is a sample of the mediaStateJSONConfig thing configuration:
This is a sample of the mediaStateJSONConfig thing configuration - the `label` is optional:

`[{"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]}]`
`[{"name": "com.amazon.tv.launcher", "mode": "idle"}, {"name": "org.jellyfin.androidtv", "mode": "wake_lock", "wakeLockPlayStates": [2,3]}, {"name": "com.amazon.firetv.youtube", "label":"YouTube", "mode": "wake_lock", "wakeLockPlayStates": [2]}]`

## Record/Send input events

Expand All @@ -80,27 +80,26 @@ An example of what you can do:

Please note that events could fail if the input method is removed, for example it could fail if you clone the events of a bluetooth controller and the remote goes offline. This is happening for me when recording the Fire TV remote events but not for my Xiaomi TV which also has a bt remote controller.


## Channels

| channel | type | description |
|----------|--------|------------------------------|
| key-event | String | Send key event to android device. Possible values listed below |
| text | String | Send text to android device |
| tap | String | Send tap event to android device (format x,y) |
| url | String | Open url in browser |
| media-volume | Dimmer | Set or get media volume level on android device |
| media-control | Player | Control media on android device |
| start-package | String | Run application by package name |
| stop-package | String | Stop application by package name |
| stop-current-package | String | Stop current application |
| current-package | String | Package name of the top application in screen |
| record-input | String | Capture events, generate the equivalent command and store it under the provided name |
| recorded-input | String | Emulates previously captured input events by name |
| shutdown | String | Power off/reboot device (allowed values POWER_OFF, REBOOT) |
| awake-state | OnOff | Awake state value. |
| wake-lock | Number | Power wake lock value |
| screen-state | Switch | Screen power state |
| channel | type | description |
|----------------------|--------|-------------------------------------------------------------------------------------------------------------------------------|
| key-event | String | Send key event to android device. Possible values listed below |
| text | String | Send text to android device |
| tap | String | Send tap event to android device (format x,y) |
| url | String | Open url in browser |
| media-volume | Dimmer | Set or get media volume level on android device |
| media-control | Player | Control media on android device |
| start-package | String | Run application by package name. The commands for this Channel are populated dynamically based on the `mediaStateJSONConfig`. |
| stop-package | String | Stop application by package name |
| stop-current-package | String | Stop current application |
| current-package | String | Package name of the top application in screen |
| record-input | String | Capture events, generate the equivalent command and store it under the provided name |
| recorded-input | String | Emulates previously captured input events by name |
| shutdown | String | Power off/reboot device (allowed values POWER_OFF, REBOOT) |
| awake-state | OnOff | Awake state value. |
| wake-lock | Number | Power wake lock value |
| screen-state | Switch | Screen power state |

#### Available key-event values:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* 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 org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.binding.BaseDynamicCommandDescriptionProvider;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.DynamicCommandDescriptionProvider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
* Dynamic provider of command options.
*
* @author Christoph Weitkamp - Initial contribution
*/
@Component(service = { DynamicCommandDescriptionProvider.class,
AndroidDebugBridgeDynamicCommandDescriptionProvider.class })
@NonNullByDefault
public class AndroidDebugBridgeDynamicCommandDescriptionProvider extends BaseDynamicCommandDescriptionProvider {
@Activate
public AndroidDebugBridgeDynamicCommandDescriptionProvider(final @Reference EventPublisher eventPublisher, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.eventPublisher = eventPublisher;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
Expand All @@ -38,6 +39,7 @@
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.CommandOption;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -65,15 +67,19 @@ public class AndroidDebugBridgeHandler extends BaseThingHandler {
private static final Gson GSON = new Gson();
private static final Pattern RECORD_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9_]*$");
private final Logger logger = LoggerFactory.getLogger(AndroidDebugBridgeHandler.class);

private final AndroidDebugBridgeDynamicCommandDescriptionProvider commandDescriptionProvider;
private final AndroidDebugBridgeDevice adbConnection;
private int maxMediaVolume = 0;
private AndroidDebugBridgeConfiguration config = new AndroidDebugBridgeConfiguration();
private @Nullable ScheduledFuture<?> connectionCheckerSchedule;
private AndroidDebugBridgeMediaStatePackageConfig @Nullable [] packageConfigs = null;
private boolean deviceAwake = false;

public AndroidDebugBridgeHandler(Thing thing) {
public AndroidDebugBridgeHandler(Thing thing,
AndroidDebugBridgeDynamicCommandDescriptionProvider commandDescriptionProvider) {
super(thing);
this.commandDescriptionProvider = commandDescriptionProvider;
this.adbConnection = new AndroidDebugBridgeDevice(scheduler);
}

Expand Down Expand Up @@ -317,12 +323,18 @@ public void initialize() {
}

private void loadMediaStateConfig(String mediaStateJSONConfig) {
List<CommandOption> commandOptions;
try {
this.packageConfigs = GSON.fromJson(mediaStateJSONConfig,
AndroidDebugBridgeMediaStatePackageConfig[].class);
packageConfigs = GSON.fromJson(mediaStateJSONConfig, AndroidDebugBridgeMediaStatePackageConfig[].class);
commandOptions = Arrays.stream(packageConfigs)
.map(AndroidDebugBridgeMediaStatePackageConfig::toCommandOption)
.collect(Collectors.toUnmodifiableList());
} catch (JsonSyntaxException e) {
logger.warn("unable to parse media state config: {}", e.getMessage());
commandOptions = List.of();
}
commandDescriptionProvider.setCommandOptions(new ChannelUID(getThing().getUID(), START_PACKAGE_CHANNEL),
commandOptions);
}

@Override
Expand Down Expand Up @@ -449,7 +461,12 @@ private void refreshStatus() throws InterruptedException, AndroidDebugBridgeDevi

static class AndroidDebugBridgeMediaStatePackageConfig {
public String name = "";
public @Nullable String label;
public String mode = "";
public List<Integer> wakeLockPlayStates = List.of();

public CommandOption toCommandOption() {
return new CommandOption(name, label == null ? name : label);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
* The {@link AndroidDebugBridgeHandlerFactory} is responsible for creating things and thing
Expand All @@ -33,6 +35,14 @@
@Component(configurationPid = BINDING_CONFIGURATION_PID, service = ThingHandlerFactory.class)
public class AndroidDebugBridgeHandlerFactory extends BaseThingHandlerFactory {

private final AndroidDebugBridgeDynamicCommandDescriptionProvider commandDescriptionProvider;

@Activate
public AndroidDebugBridgeHandlerFactory(
final @Reference AndroidDebugBridgeDynamicCommandDescriptionProvider commandDescriptionProvider) {
this.commandDescriptionProvider = commandDescriptionProvider;
}

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES.contains(thingTypeUID);
Expand All @@ -42,7 +52,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_ANDROID_DEVICE.equals(thingTypeUID)) {
return new AndroidDebugBridgeHandler(thing);
return new AndroidDebugBridgeHandler(thing, commandDescriptionProvider);
}
return null;
}
Expand Down

0 comments on commit 45fee2e

Please sign in to comment.