* 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>
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
| 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. |
| 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:
InputHDMI4
InputAV1
PowerOff
-PowerOn
+POWERON _(NOTE: POWERON needs to be completely capitalized due to a bug with older Roku devices)_
## Full Example
// 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" }
// 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" }
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" }
```
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"
Text item=Player_ProgramTitle
Text item=Player_ProgramDescription
Text item=Player_ProgramRating
+ Switch item=Player_Power
+ Text item=Player_PowerState
}
}
```
// 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";
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 = "";
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";
}
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;
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;
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();
return;
}
- if (config.refresh >= 10) {
+ if (config.refresh >= 1) {
refreshInterval = config.refresh;
}
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
}
updateState(ACTIVE_APP, new StringType(activeAppId));
+ updateState(ACTIVE_APPNAME, new StringType(appMap.get(activeAppId)));
+
if (TV_APP.equals(activeAppId)) {
tvActive = true;
} else {
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);
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);
}
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);
}
<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>
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
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
<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"/>
<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>
</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"/>
<channel id="programTitle" typeId="programTitle"/>
<channel id="programDescription" typeId="programDescription"/>
<channel id="programRating" typeId="programRating"/>
+ <channel id="powerState" typeId="powerState"/>
</channels>
<properties>
<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>
<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>
<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>
--- /dev/null
+<?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>