diff --git a/bundles/org.openhab.binding.roku/README.md b/bundles/org.openhab.binding.roku/README.md
index 512d94ace17..9f5d7683e29 100644
--- a/bundles/org.openhab.binding.roku/README.md
+++ b/bundles/org.openhab.binding.roku/README.md
@@ -46,6 +46,8 @@ The following channels are available:
| 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. |
+| endTime | DateTime | The date/time when the currently playing media will end (ReadOnly). N/A if timeTotal is not provided by the current streaming app. |
+| progress | Dimmer | The current progress [0-100%] of playing media (ReadOnly). N/A if timeTotal is not provided by the current streaming app. |
| activeChannel | String | A dropdown containing a list of available TV channels on the Roku TV. The channel currently tuned is automatically selected. The list updates every 10 minutes. |
| signalMode | String | The signal type of the current TV channel, ie: 1080i (ReadOnly). |
| signalQuality | Number:Dimensionless | The signal quality of the current TV channel, 0-100% (ReadOnly). |
@@ -59,6 +61,7 @@ The following channels are available:
Some Notes:
- The values for `activeApp`, `activeAppName`, `playMode`, `timeElapsed`, `timeTotal`, `activeChannel`, `signalMode`, `signalQuality`, `channelName`, `programTitle`, `programDescription`, `programRating`, `power` & `powerState` refresh automatically per the configured `refresh` interval.
+- The `endTime` and `progress` channels may not be accurate for some streaming apps especially 'live' streams where the `timeTotal` value constantly increases.
**List of available button commands for Roku streaming devices:**
@@ -113,32 +116,36 @@ roku:roku_tv:mytv1 "My Roku TV" [ hostName="192.168.10.1", refresh=10 ]
```java
// 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" }
+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" }
+DateTime Player_EndTime "End Time: [%1$tl:%1$tM %1$tp]" { channel="roku:roku_player:myplayer1:endTime" }
+Dimmer Player_Progress "Progress [%.0f%%]" { channel="roku:roku_player:myplayer1:progress" }
// Roku TV items:
-Switch Player_Power "Power: [%s]" { channel="roku:roku_tv:mytv1:power" }
-String Player_PowerState "Power State: [%s] { channel="roku:roku_tv:mytv1:powerState" }
-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_ActiveChannel "Current Channel: [%s]" { channel="roku:roku_tv:mytv1:activeChannel" }
-String Player_SignalMode "Signal Mode: [%s]" { channel="roku:roku_tv:mytv1:signalMode" }
-Number Player_SignalQuality "Signal Quality: [%d %%]" { channel="roku:roku_tv:mytv1:signalQuality" }
-String Player_ChannelName "Channel Name: [%s]" { channel="roku:roku_tv:mytv1:channelName" }
-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" }
+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" }
+DateTime Player_EndTime "End Time: [%1$tl:%1$tM %1$tp]" { channel="roku:roku_tv:mytv1:endTime" }
+Dimmer Player_Progress "Progress [%.0f%%]" { channel="roku:roku_tv:mytv1:progress" }
+String Player_ActiveChannel "Current Channel: [%s]" { channel="roku:roku_tv:mytv1:activeChannel" }
+String Player_SignalMode "Signal Mode: [%s]" { channel="roku:roku_tv:mytv1:signalMode" }
+Number Player_SignalQuality "Signal Quality: [%d %%]" { channel="roku:roku_tv:mytv1:signalQuality" }
+String Player_ChannelName "Channel Name: [%s]" { channel="roku:roku_tv:mytv1:channelName" }
+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" }
```
### `roku.sitemap` Example
@@ -154,6 +161,8 @@ sitemap roku label="Roku" {
Text item=Player_PlayMode
Text item=Player_TimeElapsed icon="time"
Text item=Player_TimeTotal icon="time"
+ Text item=Player_EndTime icon="time"
+ Slider item=Player_Progress icon="time"
// The following items apply to Roku TVs only
Switch item=Player_Power
Text item=Player_PowerState
diff --git a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuBindingConstants.java b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuBindingConstants.java
index 514c468d8f5..8b2beb2d62c 100644
--- a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuBindingConstants.java
+++ b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuBindingConstants.java
@@ -55,6 +55,8 @@ public class RokuBindingConstants {
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 END_TIME = "endTime";
+ public static final String PROGRESS = "progress";
public static final String ACTIVE_CHANNEL = "activeChannel";
public static final String SIGNAL_MODE = "signalMode";
public static final String SIGNAL_QUALITY = "signalQuality";
diff --git a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/handler/RokuHandler.java b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/handler/RokuHandler.java
index 6e21ba49869..3b9ca1156b6 100644
--- a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/handler/RokuHandler.java
+++ b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/handler/RokuHandler.java
@@ -14,6 +14,8 @@ package org.openhab.binding.roku.internal.handler;
import static org.openhab.binding.roku.internal.RokuBindingConstants.*;
+import java.math.BigDecimal;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -34,8 +36,10 @@ 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.DateTimeType;
import org.openhab.core.library.types.NextPreviousType;
import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.PlayPauseType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
@@ -195,21 +199,32 @@ public class RokuHandler extends BaseThingHandler {
PLAY.equalsIgnoreCase(playerInfo.getState()) ? PlayPauseType.PLAY : PlayPauseType.PAUSE);
// Remove non-numeric from string, ie: ' ms'
- String position = playerInfo.getPosition().replaceAll(NON_DIGIT_PATTERN, EMPTY);
- if (!EMPTY.equals(position)) {
- updateState(TIME_ELAPSED,
- new QuantityType<>(Integer.parseInt(position) / 1000, API_SECONDS_UNIT));
+ final String positionStr = playerInfo.getPosition().replaceAll(NON_DIGIT_PATTERN, EMPTY);
+ int position = -1;
+ if (!EMPTY.equals(positionStr)) {
+ position = Integer.parseInt(positionStr) / 1000;
+ updateState(TIME_ELAPSED, new QuantityType<>(position, API_SECONDS_UNIT));
} else {
updateState(TIME_ELAPSED, UnDefType.UNDEF);
}
- String duration = playerInfo.getDuration().replaceAll(NON_DIGIT_PATTERN, EMPTY);
- if (!EMPTY.equals(duration)) {
- updateState(TIME_TOTAL,
- new QuantityType<>(Integer.parseInt(duration) / 1000, API_SECONDS_UNIT));
+ final String durationStr = playerInfo.getDuration().replaceAll(NON_DIGIT_PATTERN, EMPTY);
+ int duration = -1;
+ if (!EMPTY.equals(durationStr)) {
+ duration = Integer.parseInt(durationStr) / 1000;
+ updateState(TIME_TOTAL, new QuantityType<>(duration, API_SECONDS_UNIT));
} else {
updateState(TIME_TOTAL, UnDefType.UNDEF);
}
+
+ if (position >= 0 && duration > 0) {
+ updateState(END_TIME, new DateTimeType(ZonedDateTime.now().plusSeconds(duration - position)));
+ updateState(PROGRESS,
+ new PercentType(BigDecimal.valueOf(Math.round(position / (double) duration * 100.0))));
+ } else {
+ updateState(END_TIME, UnDefType.UNDEF);
+ updateState(PROGRESS, UnDefType.UNDEF);
+ }
} catch (NumberFormatException e) {
logger.debug("Unable to parse playerInfo integer value. Exception: {}", e.getMessage());
} catch (RokuLimitedModeException e) {
@@ -224,6 +239,8 @@ public class RokuHandler extends BaseThingHandler {
updateState(PLAY_MODE, UnDefType.UNDEF);
updateState(TIME_ELAPSED, UnDefType.UNDEF);
updateState(TIME_TOTAL, UnDefType.UNDEF);
+ updateState(END_TIME, UnDefType.UNDEF);
+ updateState(PROGRESS, UnDefType.UNDEF);
}
if (thingTypeUID.equals(THING_TYPE_ROKU_TV) && tvActive) {
diff --git a/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/thing/roku.xml b/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/thing/roku.xml
index 78e995536d5..284560cbfe1 100644
--- a/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/thing/roku.xml
+++ b/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/thing/roku.xml
@@ -19,6 +19,8 @@
+
+
@@ -28,7 +30,7 @@
unknown
unknown
unknown
- 1
+ 2
uuid
@@ -52,6 +54,8 @@
+
+
@@ -69,7 +73,7 @@
unknown
unknown
unknown
- 1
+ 2
uuid
@@ -185,6 +189,24 @@
+
+ DateTime
+
+ The date/time when the currently playing media will end
+ Time
+
+ Status
+ Timestamp
+
+
+
+
+
+ Dimmer
+
+ The current progress of playing media
+
+
String
diff --git a/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/update/update.xml b/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/update/update.xml
index 8979f15e63c..81dac71325d 100644
--- a/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/update/update.xml
+++ b/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/update/update.xml
@@ -12,6 +12,15 @@
roku:control
+
+
+
+ roku:endTime
+
+
+ roku:progress
+
+
@@ -29,6 +38,15 @@
roku:control
+
+
+
+ roku:endTime
+
+
+ roku:progress
+
+