mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[roku] Add power, powerState, player, and activeAppName (#15542)
* Adds power and powerState. Updates refresh to allow 1 second refresh. * Add activeAppName channel * Add Player channel for transport control Signed-off-by: Ben Rosenblum <rosenblumb@gmail.com>
This commit is contained in:
parent
74a9de313a
commit
729a19a8da
@ -23,11 +23,11 @@ The binding has no configuration options, all configuration is done at Thing lev
|
||||
|
||||
The thing has a few configuration parameters:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|------------------------------------------------------------------------------------------------------------|
|
||||
| hostName | The host name or IP address of the Roku device. Mandatory. |
|
||||
| port | The port on the Roku that listens for http connections. Default 8060 |
|
||||
| refresh | Overrides the refresh interval for player status updates. Optional, the default and minimum is 10 seconds. |
|
||||
| Parameter | Description |
|
||||
|-----------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| hostName | The host name or IP address of the Roku device. Mandatory. |
|
||||
| port | The port on the Roku that listens for http connections. Default 8060 |
|
||||
| refresh | Overrides the refresh interval for player status updates. Optional, the default is 10 seconds and minimum is 1 second. |
|
||||
|
||||
## Channels
|
||||
|
||||
@ -36,7 +36,9 @@ The following channels are available:
|
||||
| Channel ID | Item Type | Description |
|
||||
|--------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| activeApp | String | A dropdown containing a list of all apps installed on the Roku. The app currently running is automatically selected. The list updates every 10 minutes. |
|
||||
| activeAppName | String | The name of the current app (ReadOnly). |
|
||||
| button | String | Sends a remote control command the Roku. See list of available commands below. |
|
||||
| control | Player | Control Playback e.g. play/pause/next/previous |
|
||||
| playMode | String | The current playback mode ie: stop, play, pause (ReadOnly). |
|
||||
| timeElapsed | Number:Time | The total number of seconds of playback time elapsed for the current playing title (ReadOnly). |
|
||||
| timeTotal | Number:Time | The total length of the current playing title in seconds (ReadOnly). This data is not provided by all streaming apps. |
|
||||
@ -47,6 +49,8 @@ The following channels are available:
|
||||
| programTitle | String | The name of the current TV program (ReadOnly). |
|
||||
| programDescription | String | The description of the current TV program (ReadOnly). |
|
||||
| programRating | String | The TV parental guideline rating of the current TV program (ReadOnly). |
|
||||
| power | Switch | Controls the power for the TV. |
|
||||
| powerState | String | The current power state for the TV. (ReadOnly - ie PowerOn, DisplayOff, Ready, etc.) |
|
||||
|
||||
Some Notes:
|
||||
|
||||
@ -83,7 +87,7 @@ InputHDMI3
|
||||
InputHDMI4
|
||||
InputAV1
|
||||
PowerOff
|
||||
PowerOn
|
||||
POWERON _(NOTE: POWERON needs to be completely capitalized due to a bug with older Roku devices)_
|
||||
|
||||
## Full Example
|
||||
|
||||
@ -104,7 +108,9 @@ roku:roku_tv:mytv1 "My Roku TV" [ hostName="192.168.10.1", refresh=10 ]
|
||||
// Roku streaming media player items:
|
||||
|
||||
String Player_ActiveApp "Current App: [%s]" { channel="roku:roku_player:myplayer1:activeApp" }
|
||||
String Player_ActiveAppName "Current App Name: [%s]" { channel="roku:roku_player:myplayer1:activeAppName" }
|
||||
String Player_Button "Send Command to Roku" { channel="roku:roku_player:myplayer1:button" }
|
||||
Player Player_Control "Control" { channel="roku:roku_player:myplayer1:control" }
|
||||
String Player_PlayMode "Status: [%s]" { channel="roku:roku_player:myplayer1:playMode" }
|
||||
Number:Time Player_TimeElapsed "Elapsed Time: [%d %unit%]" { channel="roku:roku_player:myplayer1:timeElapsed" }
|
||||
Number:Time Player_TimeTotal "Total Time: [%d %unit%]" { channel="roku:roku_player:myplayer1:timeTotal" }
|
||||
@ -112,7 +118,9 @@ Number:Time Player_TimeTotal "Total Time: [%d %unit%]" { channel="roku:roku_
|
||||
// Roku TV items:
|
||||
|
||||
String Player_ActiveApp "Current App: [%s]" { channel="roku:roku_tv:mytv1:activeApp" }
|
||||
String Player_ActiveAppName "Current App Name: [%s]" { channel="roku:roku_tv:mytv1:activeAppName" }
|
||||
String Player_Button "Send Command to Roku" { channel="roku:roku_tv:mytv1:button" }
|
||||
Player Player_Control "Control" { channel="roku:roku_tv:mytv1:control" }
|
||||
String Player_PlayMode "Status: [%s]" { channel="roku:roku_tv:mytv1:playMode" }
|
||||
Number:Time Player_TimeElapsed "Elapsed Time: [%d %unit%]" { channel="roku:roku_tv:mytv1:timeElapsed" }
|
||||
Number:Time Player_TimeTotal "Total Time: [%d %unit%]" { channel="roku:roku_tv:mytv1:timeTotal" }
|
||||
@ -123,6 +131,8 @@ String Player_ChannelName "Channel Name: [%s]" { channel="roku:rok
|
||||
String Player_ProgramTitle "Program Title: [%s]" { channel="roku:roku_tv:mytv1:programTitle" }
|
||||
String Player_ProgramDescription "Program Description: [%s]" { channel="roku:roku_tv:mytv1:programDescription" }
|
||||
String Player_ProgramRating "Program Rating: [%s]" { channel="roku:roku_tv:mytv1:programRating" }
|
||||
Switch Player_Power "Power: [%s]" { channel="roku:roku_tv:mytv1:power" }
|
||||
String Player_PowerState "Power State: [%s] { channel="roku:roku_tv:mytv1:powerState" }
|
||||
|
||||
```
|
||||
|
||||
@ -132,7 +142,9 @@ String Player_ProgramRating "Program Rating: [%s]" { channel="roku:rok
|
||||
sitemap roku label="Roku" {
|
||||
Frame label="My Roku" {
|
||||
Selection item=Player_ActiveApp icon="screen"
|
||||
Text item=Player_ActiveAppName
|
||||
Selection item=Player_Button icon="screen"
|
||||
Default item=Player_Control
|
||||
Text item=Player_PlayMode
|
||||
Text item=Player_TimeElapsed icon="time"
|
||||
Text item=Player_TimeTotal icon="time"
|
||||
@ -144,6 +156,8 @@ sitemap roku label="Roku" {
|
||||
Text item=Player_ProgramTitle
|
||||
Text item=Player_ProgramDescription
|
||||
Text item=Player_ProgramRating
|
||||
Switch item=Player_Power
|
||||
Text item=Player_PowerState
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -49,7 +49,9 @@ public class RokuBindingConstants {
|
||||
|
||||
// List of all Channel id's
|
||||
public static final String ACTIVE_APP = "activeApp";
|
||||
public static final String ACTIVE_APPNAME = "activeAppName";
|
||||
public static final String BUTTON = "button";
|
||||
public static final String CONTROL = "control";
|
||||
public static final String PLAY_MODE = "playMode";
|
||||
public static final String TIME_ELAPSED = "timeElapsed";
|
||||
public static final String TIME_TOTAL = "timeTotal";
|
||||
@ -60,11 +62,14 @@ public class RokuBindingConstants {
|
||||
public static final String PROGRAM_TITLE = "programTitle";
|
||||
public static final String PROGRAM_DESCRIPTION = "programDescription";
|
||||
public static final String PROGRAM_RATING = "programRating";
|
||||
public static final String POWER = "power";
|
||||
public static final String POWER_STATE = "powerState";
|
||||
|
||||
// Units of measurement of the data delivered by the API
|
||||
public static final Unit<Time> API_SECONDS_UNIT = Units.SECOND;
|
||||
public static final Unit<Dimensionless> API_PERCENT_UNIT = Units.PERCENT;
|
||||
|
||||
public static final String PLAY = "play";
|
||||
public static final String STOP = "stop";
|
||||
public static final String CLOSE = "close";
|
||||
public static final String EMPTY = "";
|
||||
@ -72,7 +77,11 @@ public class RokuBindingConstants {
|
||||
public static final String ROKU_HOME_ID = "-1";
|
||||
public static final String ROKU_HOME_ID_562859 = "562859";
|
||||
public static final String ROKU_HOME_BUTTON = "Home";
|
||||
public static final String ROKU_PLAY_BUTTON = "Play";
|
||||
public static final String ROKU_NEXT_BUTTON = "Fwd";
|
||||
public static final String ROKU_PREV_BUTTON = "Rev";
|
||||
public static final String NON_DIGIT_PATTERN = "[^\\d]";
|
||||
public static final String TV_APP = "tvinput.dtv";
|
||||
public static final String TV_INPUT = "tvinput";
|
||||
public static final String POWER_ON = "POWERON";
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ package org.openhab.binding.roku.internal.handler;
|
||||
import static org.openhab.binding.roku.internal.RokuBindingConstants.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -31,6 +33,9 @@ import org.openhab.binding.roku.internal.dto.DeviceInfo;
|
||||
import org.openhab.binding.roku.internal.dto.Player;
|
||||
import org.openhab.binding.roku.internal.dto.TvChannel;
|
||||
import org.openhab.binding.roku.internal.dto.TvChannels.Channel;
|
||||
import org.openhab.core.library.types.NextPreviousType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PlayPauseType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
@ -68,6 +73,7 @@ public class RokuHandler extends BaseThingHandler {
|
||||
private DeviceInfo deviceInfo = new DeviceInfo();
|
||||
private int refreshInterval = DEFAULT_REFRESH_PERIOD_SEC;
|
||||
private boolean tvActive = false;
|
||||
private Map<String, String> appMap = new HashMap<>();
|
||||
|
||||
private Object sequenceLock = new Object();
|
||||
|
||||
@ -94,7 +100,7 @@ public class RokuHandler extends BaseThingHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.refresh >= 10) {
|
||||
if (config.refresh >= 1) {
|
||||
refreshInterval = config.refresh;
|
||||
}
|
||||
|
||||
@ -134,6 +140,17 @@ public class RokuHandler extends BaseThingHandler {
|
||||
synchronized (sequenceLock) {
|
||||
String activeAppId = ROKU_HOME_ID;
|
||||
try {
|
||||
if (thingTypeUID.equals(THING_TYPE_ROKU_TV)) {
|
||||
try {
|
||||
deviceInfo = communicator.getDeviceInfo();
|
||||
String powerMode = deviceInfo.getPowerMode();
|
||||
updateState(POWER_STATE, new StringType(powerMode));
|
||||
updateState(POWER, OnOffType.from(POWER_ON.equalsIgnoreCase(powerMode)));
|
||||
} catch (RokuHttpException e) {
|
||||
logger.debug("Unable to retrieve Roku device-info.", e);
|
||||
}
|
||||
}
|
||||
|
||||
activeAppId = communicator.getActiveApp().getApp().getId();
|
||||
|
||||
// 562859 is now reported when on the home screen, reset to -1
|
||||
@ -142,6 +159,8 @@ public class RokuHandler extends BaseThingHandler {
|
||||
}
|
||||
|
||||
updateState(ACTIVE_APP, new StringType(activeAppId));
|
||||
updateState(ACTIVE_APPNAME, new StringType(appMap.get(activeAppId)));
|
||||
|
||||
if (TV_APP.equals(activeAppId)) {
|
||||
tvActive = true;
|
||||
} else {
|
||||
@ -167,6 +186,8 @@ public class RokuHandler extends BaseThingHandler {
|
||||
Player playerInfo = communicator.getPlayerInfo();
|
||||
// When nothing playing, 'close' is reported, replace with 'stop'
|
||||
updateState(PLAY_MODE, new StringType(playerInfo.getState().replaceAll(CLOSE, STOP)));
|
||||
updateState(CONTROL,
|
||||
PLAY.equalsIgnoreCase(playerInfo.getState()) ? PlayPauseType.PLAY : PlayPauseType.PAUSE);
|
||||
|
||||
// Remove non-numeric from string, ie: ' ms'
|
||||
String position = playerInfo.getPosition().replaceAll(NON_DIGIT_PATTERN, EMPTY);
|
||||
@ -229,18 +250,22 @@ public class RokuHandler extends BaseThingHandler {
|
||||
synchronized (sequenceLock) {
|
||||
try {
|
||||
List<App> appList = communicator.getAppList();
|
||||
Map<String, String> appMap = new HashMap<>();
|
||||
|
||||
List<StateOption> appListOptions = new ArrayList<>();
|
||||
// Roku Home will be selected in the drop-down any time an app is not running.
|
||||
appListOptions.add(new StateOption(ROKU_HOME_ID, ROKU_HOME));
|
||||
appMap.put(ROKU_HOME_ID, ROKU_HOME);
|
||||
|
||||
appList.forEach(app -> {
|
||||
appListOptions.add(new StateOption(app.getId(), app.getValue()));
|
||||
appMap.put(app.getId(), app.getValue());
|
||||
});
|
||||
|
||||
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), ACTIVE_APP),
|
||||
appListOptions);
|
||||
|
||||
this.appMap = appMap;
|
||||
} catch (RokuHttpException e) {
|
||||
logger.debug("Unable to retrieve Roku installed app-list. Exception: {}", e.getMessage(), e);
|
||||
}
|
||||
@ -320,6 +345,41 @@ public class RokuHandler extends BaseThingHandler {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
}
|
||||
}
|
||||
} else if (POWER.equals(channelUID.getId())) {
|
||||
synchronized (sequenceLock) {
|
||||
if (command instanceof OnOffType) {
|
||||
try {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
communicator.keyPress(POWER_ON);
|
||||
} else {
|
||||
communicator.keyPress("PowerOff");
|
||||
}
|
||||
} catch (RokuHttpException e) {
|
||||
logger.debug("Unable to send keypress to Roku, key: {}, Exception: {}", command,
|
||||
e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (channelUID.getId().equals(CONTROL)) {
|
||||
synchronized (sequenceLock) {
|
||||
try {
|
||||
if (command instanceof PlayPauseType) {
|
||||
communicator.keyPress(ROKU_PLAY_BUTTON);
|
||||
} else if (command instanceof NextPreviousType) {
|
||||
if (command == NextPreviousType.NEXT) {
|
||||
communicator.keyPress(ROKU_NEXT_BUTTON);
|
||||
} else if (command == NextPreviousType.PREVIOUS) {
|
||||
communicator.keyPress(ROKU_PREV_BUTTON);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Unknown control command: {}", command);
|
||||
}
|
||||
} catch (RokuHttpException e) {
|
||||
logger.debug("Unable to send control cmd to Roku, cmd: {}, Exception: {}", command, e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Unsupported command: {}", command);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
<default>8060</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" min="10" required="false" unit="s">
|
||||
<parameter name="refresh" type="integer" min="1" required="false" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Specifies the Refresh Interval in Seconds</description>
|
||||
<default>10</default>
|
||||
|
@ -23,6 +23,8 @@ thing-type.config.roku.rokuconfig.refresh.description = Specifies the Refresh In
|
||||
|
||||
channel-type.roku.activeApp.label = Active App
|
||||
channel-type.roku.activeApp.description = The Currently Running App on the Roku
|
||||
channel-type.roku.activeAppName.label = Active App Name
|
||||
channel-type.roku.activeAppName.description = The Currently Running App on the Roku
|
||||
channel-type.roku.activeChannel.label = Active Channel
|
||||
channel-type.roku.activeChannel.description = The TV Channel Currently Selected on the Roku TV
|
||||
channel-type.roku.button.label = Remote Button
|
||||
@ -73,11 +75,18 @@ channel-type.roku.buttonTv.state.option.InputHDMI3 = Input HDMI3
|
||||
channel-type.roku.buttonTv.state.option.InputHDMI4 = Input HDMI4
|
||||
channel-type.roku.buttonTv.state.option.InputAV1 = Input AV1
|
||||
channel-type.roku.buttonTv.state.option.PowerOff = Power Off
|
||||
channel-type.roku.buttonTv.state.option.PowerOn = Power On
|
||||
channel-type.roku.buttonTv.state.option.POWERON = Power On
|
||||
channel-type.roku.channelName.label = Channel Name
|
||||
channel-type.roku.channelName.description = The Name of the Channel Currently Selected
|
||||
channel-type.roku.control.label = Control
|
||||
channel-type.roku.control.description = Control playback e.g. Play/Pause/Next/Previous
|
||||
channel-type.roku.playMode.label = Play Mode
|
||||
channel-type.roku.playMode.description = The Current Playback Mode
|
||||
channel-type.roku.powerState.label = Power State
|
||||
channel-type.roku.powerState.description = Power State of the TV
|
||||
channel-type.roku.powerState.state.option.PowerOn = Power On
|
||||
channel-type.roku.powerState.state.option.DisplayOff = Display Off
|
||||
channel-type.roku.powerState.state.option.Ready = Ready
|
||||
channel-type.roku.programDescription.label = Program Description
|
||||
channel-type.roku.programDescription.description = The Description of the Current TV Program
|
||||
channel-type.roku.programRating.label = Program Rating
|
||||
|
@ -13,7 +13,9 @@
|
||||
|
||||
<channels>
|
||||
<channel id="activeApp" typeId="activeApp"/>
|
||||
<channel id="activeAppName" typeId="activeAppName"/>
|
||||
<channel id="button" typeId="button"/>
|
||||
<channel id="control" typeId="control"/>
|
||||
<channel id="playMode" typeId="playMode"/>
|
||||
<channel id="timeElapsed" typeId="timeElapsed"/>
|
||||
<channel id="timeTotal" typeId="timeTotal"/>
|
||||
@ -26,6 +28,7 @@
|
||||
<property name="Serial Number">unknown</property>
|
||||
<property name="Device Id">unknown</property>
|
||||
<property name="Software Version">unknown</property>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>uuid</representation-property>
|
||||
@ -41,8 +44,11 @@
|
||||
</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="system.power"/>
|
||||
<channel id="activeApp" typeId="activeApp"/>
|
||||
<channel id="activeAppName" typeId="activeAppName"/>
|
||||
<channel id="button" typeId="buttonTv"/>
|
||||
<channel id="control" typeId="control"/>
|
||||
<channel id="playMode" typeId="playMode"/>
|
||||
<channel id="timeElapsed" typeId="timeElapsed"/>
|
||||
<channel id="timeTotal" typeId="timeTotal"/>
|
||||
@ -53,6 +59,7 @@
|
||||
<channel id="programTitle" typeId="programTitle"/>
|
||||
<channel id="programDescription" typeId="programDescription"/>
|
||||
<channel id="programRating" typeId="programRating"/>
|
||||
<channel id="powerState" typeId="powerState"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
@ -62,6 +69,7 @@
|
||||
<property name="Serial Number">unknown</property>
|
||||
<property name="Device Id">unknown</property>
|
||||
<property name="Software Version">unknown</property>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>uuid</representation-property>
|
||||
@ -129,17 +137,31 @@
|
||||
<option value="InputHDMI4">Input HDMI4</option>
|
||||
<option value="InputAV1">Input AV1</option>
|
||||
<option value="PowerOff">Power Off</option>
|
||||
<option value="PowerOn">Power On</option>
|
||||
<option value="POWERON">Power On</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="control">
|
||||
<item-type>Player</item-type>
|
||||
<label>Control</label>
|
||||
<description>Control playback e.g. Play/Pause/Next/Previous</description>
|
||||
<category>Player</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="activeApp">
|
||||
<item-type>String</item-type>
|
||||
<label>Active App</label>
|
||||
<description>The Currently Running App on the Roku</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="activeAppName">
|
||||
<item-type>String</item-type>
|
||||
<label>Active App Name</label>
|
||||
<description>The Currently Running App on the Roku</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="playMode">
|
||||
<item-type>String</item-type>
|
||||
<label>Play Mode</label>
|
||||
@ -209,4 +231,16 @@
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="powerState" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Power State</label>
|
||||
<description>Power State of the TV</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="PowerOn">Power On</option>
|
||||
<option value="DisplayOff">Display Off</option>
|
||||
<option value="Ready">Ready</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
|
||||
|
||||
<thing-type uid="roku:roku_player">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="activeAppName">
|
||||
<type>roku:activeAppName</type>
|
||||
</add-channel>
|
||||
<add-channel id="control">
|
||||
<type>roku:control</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
<thing-type uid="roku:roku_tv">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="activeAppName">
|
||||
<type>roku:activeAppName</type>
|
||||
</add-channel>
|
||||
<add-channel id="power">
|
||||
<type>system:power</type>
|
||||
</add-channel>
|
||||
<add-channel id="powerState">
|
||||
<type>roku:powerState</type>
|
||||
</add-channel>
|
||||
<add-channel id="control">
|
||||
<type>roku:control</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
</update:update-descriptions>
|
Loading…
Reference in New Issue
Block a user