Add Roku TV channels (#11087)
Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>pull/10277/head^2
parent
a93b56f2d3
commit
1321049973
|
@ -7,7 +7,7 @@ The Roku device must support the Roku ECP protocol REST API.
|
||||||
|
|
||||||
There are two supported thing types, which represent either a standalone Roku device or a Roku TV.
|
There are two supported thing types, which represent either a standalone Roku device or a Roku TV.
|
||||||
A supported Roku streaming media player or streaming stick uses the `roku_player` id and a supported Roku TV uses the `roku_tv` id.
|
A supported Roku streaming media player or streaming stick uses the `roku_player` id and a supported Roku TV uses the `roku_tv` id.
|
||||||
The binding functionality is the same for both types, but the Roku TV type adds additional button commands to the button channel dropdown.
|
The Roku TV type adds additional channels and button commands to the button channel dropdown for TV specific functionality.
|
||||||
Multiple Things can be added if more than one Roku is to be controlled.
|
Multiple Things can be added if more than one Roku is to be controlled.
|
||||||
|
|
||||||
## Discovery
|
## Discovery
|
||||||
|
@ -33,17 +33,24 @@ The thing has a few configuration parameters:
|
||||||
|
|
||||||
The following channels are available:
|
The following channels are available:
|
||||||
|
|
||||||
| Channel ID | Item Type | Description |
|
| 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. |
|
| 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. |
|
||||||
| button | String | Sends a remote control command the Roku. See list of available commands below. |
|
| button | String | Sends a remote control command the Roku. See list of available commands below. |
|
||||||
| playMode | String | The current playback mode ie: stop, play, pause (ReadOnly). |
|
| 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). |
|
| 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. |
|
| timeTotal | Number:Time | The total length of the current playing title in seconds (ReadOnly). This data is not provided by all streaming apps. |
|
||||||
|
| 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). |
|
||||||
|
| channelName | String | The name of the channel currently selected (ReadOnly). |
|
||||||
|
| 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). |
|
||||||
|
|
||||||
Some Notes:
|
Some Notes:
|
||||||
|
|
||||||
* The values for `activeApp`, `playMode`, `timeElapsed` & `timeTotal` refresh automatically per the configured `refresh` interval (10 seconds minimum).
|
* The values for `activeApp`, `playMode`, `timeElapsed`, `timeTotal`, `activeChannel`, `signalMode`, `signalQuality`, `channelName`, `programTitle`, `programDescription` & `programRating` refresh automatically per the configured `refresh` interval (10 seconds minimum).
|
||||||
|
|
||||||
**List of available button commands for Roku streaming devices:**
|
**List of available button commands for Roku streaming devices:**
|
||||||
Home
|
Home
|
||||||
|
@ -81,24 +88,46 @@ PowerOff
|
||||||
|
|
||||||
roku.things:
|
roku.things:
|
||||||
|
|
||||||
```java
|
```
|
||||||
|
// Roku streaming media player
|
||||||
roku:roku_player:myplayer1 "My Roku" [ hostName="192.168.10.1", refresh=10 ]
|
roku:roku_player:myplayer1 "My Roku" [ hostName="192.168.10.1", refresh=10 ]
|
||||||
roku:roku_tv:myplayer1 "My Roku TV" [ hostName="192.168.10.1", refresh=10 ]
|
|
||||||
|
// Roku TV
|
||||||
|
roku:roku_tv:mytv1 "My Roku TV" [ hostName="192.168.10.1", refresh=10 ]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
roku.items:
|
roku.items:
|
||||||
|
|
||||||
```java
|
```
|
||||||
String Player_ActiveApp "Current App: [%s]" { channel="roku:roku_player:myplayer1:activeApp" }
|
// Roku streaming media player items:
|
||||||
String Player_Button "Send Command to Roku" { channel="roku:roku_player:myplayer1:button" }
|
|
||||||
|
String Player_ActiveApp "Current App: [%s]" { channel="roku:roku_player:myplayer1:activeApp" }
|
||||||
|
String Player_Button "Send Command to Roku" { channel="roku:roku_player:myplayer1:button" }
|
||||||
String Player_PlayMode "Status: [%s]" { channel="roku:roku_player:myplayer1:playMode" }
|
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_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" }
|
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_Button "Send Command to Roku" { channel="roku:roku_tv:mytv1:button" }
|
||||||
|
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" }
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
roku.sitemap:
|
roku.sitemap:
|
||||||
|
|
||||||
```perl
|
```
|
||||||
sitemap roku label="Roku" {
|
sitemap roku label="Roku" {
|
||||||
Frame label="My Roku" {
|
Frame label="My Roku" {
|
||||||
Selection item=Player_ActiveApp icon="screen"
|
Selection item=Player_ActiveApp icon="screen"
|
||||||
|
@ -106,6 +135,14 @@ sitemap roku label="Roku" {
|
||||||
Text item=Player_PlayMode
|
Text item=Player_PlayMode
|
||||||
Text item=Player_TimeElapsed icon="time"
|
Text item=Player_TimeElapsed icon="time"
|
||||||
Text item=Player_TimeTotal icon="time"
|
Text item=Player_TimeTotal icon="time"
|
||||||
|
// The following items apply to Roku TVs only
|
||||||
|
Selection item=Player_ActiveChannel icon="screen"
|
||||||
|
Text item=Player_SignalMode
|
||||||
|
Text item=Player_SignalQuality
|
||||||
|
Text item=Player_ChannelName
|
||||||
|
Text item=Player_ProgramTitle
|
||||||
|
Text item=Player_ProgramDescription
|
||||||
|
Text item=Player_ProgramRating
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -15,6 +15,7 @@ package org.openhab.binding.roku.internal;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.measure.Unit;
|
import javax.measure.Unit;
|
||||||
|
import javax.measure.quantity.Dimensionless;
|
||||||
import javax.measure.quantity.Time;
|
import javax.measure.quantity.Time;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
@ -52,9 +53,17 @@ public class RokuBindingConstants {
|
||||||
public static final String PLAY_MODE = "playMode";
|
public static final String PLAY_MODE = "playMode";
|
||||||
public static final String TIME_ELAPSED = "timeElapsed";
|
public static final String TIME_ELAPSED = "timeElapsed";
|
||||||
public static final String TIME_TOTAL = "timeTotal";
|
public static final String TIME_TOTAL = "timeTotal";
|
||||||
|
public static final String ACTIVE_CHANNEL = "activeChannel";
|
||||||
|
public static final String SIGNAL_MODE = "signalMode";
|
||||||
|
public static final String SIGNAL_QUALITY = "signalQuality";
|
||||||
|
public static final String CHANNEL_NAME = "channelName";
|
||||||
|
public static final String PROGRAM_TITLE = "programTitle";
|
||||||
|
public static final String PROGRAM_DESCRIPTION = "programDescription";
|
||||||
|
public static final String PROGRAM_RATING = "programRating";
|
||||||
|
|
||||||
// Units of measurement of the data delivered by the API
|
// Units of measurement of the data delivered by the API
|
||||||
public static final Unit<Time> API_SECONDS_UNIT = Units.SECOND;
|
public static final Unit<Time> API_SECONDS_UNIT = Units.SECOND;
|
||||||
|
public static final Unit<Dimensionless> API_PERCENT_UNIT = Units.PERCENT;
|
||||||
|
|
||||||
public static final String STOP = "stop";
|
public static final String STOP = "stop";
|
||||||
public static final String CLOSE = "close";
|
public static final String CLOSE = "close";
|
||||||
|
@ -63,4 +72,6 @@ public class RokuBindingConstants {
|
||||||
public static final String ROKU_HOME_ID = "-1";
|
public static final String ROKU_HOME_ID = "-1";
|
||||||
public static final String ROKU_HOME_BUTTON = "Home";
|
public static final String ROKU_HOME_BUTTON = "Home";
|
||||||
public static final String NON_DIGIT_PATTERN = "[^\\d]";
|
public static final String NON_DIGIT_PATTERN = "[^\\d]";
|
||||||
|
public static final String TV_APP = "tvinput.dtv";
|
||||||
|
public static final String TV_INPUT = "tvinput";
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import org.openhab.binding.roku.internal.dto.ActiveApp;
|
||||||
import org.openhab.binding.roku.internal.dto.Apps;
|
import org.openhab.binding.roku.internal.dto.Apps;
|
||||||
import org.openhab.binding.roku.internal.dto.DeviceInfo;
|
import org.openhab.binding.roku.internal.dto.DeviceInfo;
|
||||||
import org.openhab.binding.roku.internal.dto.Player;
|
import org.openhab.binding.roku.internal.dto.Player;
|
||||||
|
import org.openhab.binding.roku.internal.dto.TvChannel;
|
||||||
|
import org.openhab.binding.roku.internal.dto.TvChannels;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -39,6 +41,8 @@ public class JAXBUtils {
|
||||||
public static final @Nullable JAXBContext JAXBCONTEXT_APPS = initJAXBContextApps();
|
public static final @Nullable JAXBContext JAXBCONTEXT_APPS = initJAXBContextApps();
|
||||||
public static final @Nullable JAXBContext JAXBCONTEXT_DEVICE_INFO = initJAXBContextDeviceInfo();
|
public static final @Nullable JAXBContext JAXBCONTEXT_DEVICE_INFO = initJAXBContextDeviceInfo();
|
||||||
public static final @Nullable JAXBContext JAXBCONTEXT_PLAYER = initJAXBContextPlayer();
|
public static final @Nullable JAXBContext JAXBCONTEXT_PLAYER = initJAXBContextPlayer();
|
||||||
|
public static final @Nullable JAXBContext JAXBCONTEXT_TVCHANNEL = initJAXBContextTvChannel();
|
||||||
|
public static final @Nullable JAXBContext JAXBCONTEXT_TVCHANNELS = initJAXBContextTvChannels();
|
||||||
public static final XMLInputFactory XMLINPUTFACTORY = initXMLInputFactory();
|
public static final XMLInputFactory XMLINPUTFACTORY = initXMLInputFactory();
|
||||||
|
|
||||||
private static @Nullable JAXBContext initJAXBContextActiveApp() {
|
private static @Nullable JAXBContext initJAXBContextActiveApp() {
|
||||||
|
@ -77,6 +81,24 @@ public class JAXBUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @Nullable JAXBContext initJAXBContextTvChannel() {
|
||||||
|
try {
|
||||||
|
return JAXBContext.newInstance(TvChannel.class);
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
LOGGER.error("Exception creating JAXBContext for TvChannel info: {}", e.getLocalizedMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable JAXBContext initJAXBContextTvChannels() {
|
||||||
|
try {
|
||||||
|
return JAXBContext.newInstance(TvChannels.class);
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
LOGGER.error("Exception creating JAXBContext for TvChannels info: {}", e.getLocalizedMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static XMLInputFactory initXMLInputFactory() {
|
private static XMLInputFactory initXMLInputFactory() {
|
||||||
XMLInputFactory xif = XMLInputFactory.newInstance();
|
XMLInputFactory xif = XMLInputFactory.newInstance();
|
||||||
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
|
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
|
||||||
|
|
|
@ -32,6 +32,9 @@ import org.openhab.binding.roku.internal.dto.Apps;
|
||||||
import org.openhab.binding.roku.internal.dto.Apps.App;
|
import org.openhab.binding.roku.internal.dto.Apps.App;
|
||||||
import org.openhab.binding.roku.internal.dto.DeviceInfo;
|
import org.openhab.binding.roku.internal.dto.DeviceInfo;
|
||||||
import org.openhab.binding.roku.internal.dto.Player;
|
import org.openhab.binding.roku.internal.dto.Player;
|
||||||
|
import org.openhab.binding.roku.internal.dto.TvChannel;
|
||||||
|
import org.openhab.binding.roku.internal.dto.TvChannels;
|
||||||
|
import org.openhab.binding.roku.internal.dto.TvChannels.Channel;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -47,10 +50,13 @@ public class RokuCommunicator {
|
||||||
|
|
||||||
private final String urlKeyPress;
|
private final String urlKeyPress;
|
||||||
private final String urlLaunchApp;
|
private final String urlLaunchApp;
|
||||||
|
private final String urlLaunchTvChannel;
|
||||||
private final String urlQryDevice;
|
private final String urlQryDevice;
|
||||||
private final String urlQryActiveApp;
|
private final String urlQryActiveApp;
|
||||||
private final String urlQryApps;
|
private final String urlQryApps;
|
||||||
private final String urlQryPlayer;
|
private final String urlQryPlayer;
|
||||||
|
private final String urlQryActiveTvChannel;
|
||||||
|
private final String urlQryTvChannels;
|
||||||
|
|
||||||
public RokuCommunicator(HttpClient httpClient, String host, int port) {
|
public RokuCommunicator(HttpClient httpClient, String host, int port) {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
|
@ -58,10 +64,13 @@ public class RokuCommunicator {
|
||||||
final String baseUrl = "http://" + host + ":" + port;
|
final String baseUrl = "http://" + host + ":" + port;
|
||||||
urlKeyPress = baseUrl + "/keypress/";
|
urlKeyPress = baseUrl + "/keypress/";
|
||||||
urlLaunchApp = baseUrl + "/launch/";
|
urlLaunchApp = baseUrl + "/launch/";
|
||||||
|
urlLaunchTvChannel = baseUrl + "/launch/tvinput.dtv?ch=";
|
||||||
urlQryDevice = baseUrl + "/query/device-info";
|
urlQryDevice = baseUrl + "/query/device-info";
|
||||||
urlQryActiveApp = baseUrl + "/query/active-app";
|
urlQryActiveApp = baseUrl + "/query/active-app";
|
||||||
urlQryApps = baseUrl + "/query/apps";
|
urlQryApps = baseUrl + "/query/apps";
|
||||||
urlQryPlayer = baseUrl + "/query/media-player";
|
urlQryPlayer = baseUrl + "/query/media-player";
|
||||||
|
urlQryActiveTvChannel = baseUrl + "/query/tv-active-channel";
|
||||||
|
urlQryTvChannels = baseUrl + "/query/tv-channels";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +93,16 @@ public class RokuCommunicator {
|
||||||
postCommand(urlLaunchApp + appId);
|
postCommand(urlLaunchApp + appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a TV channel change command to the Roku TV
|
||||||
|
*
|
||||||
|
* @param channelNumber The channel number of the channel to tune into, ie: 2.1
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void launchTvChannel(String channelNumber) throws RokuHttpException {
|
||||||
|
postCommand(urlLaunchTvChannel + channelNumber);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a command to get device-info from the Roku and return a DeviceInfo object
|
* Send a command to get device-info from the Roku and return a DeviceInfo object
|
||||||
*
|
*
|
||||||
|
@ -188,6 +207,58 @@ public class RokuCommunicator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a command to get tv-active-channel from the Roku TV and return a TvChannel object
|
||||||
|
*
|
||||||
|
* @return A TvChannel object populated with information about the current active TV Channel
|
||||||
|
* @throws RokuHttpException
|
||||||
|
*/
|
||||||
|
public TvChannel getActiveTvChannel() throws RokuHttpException {
|
||||||
|
try {
|
||||||
|
JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNEL;
|
||||||
|
if (ctx != null) {
|
||||||
|
Unmarshaller unmarshaller = ctx.createUnmarshaller();
|
||||||
|
if (unmarshaller != null) {
|
||||||
|
XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY
|
||||||
|
.createXMLStreamReader(new StringReader(getCommand(urlQryActiveTvChannel)));
|
||||||
|
TvChannel tvChannelInfo = (TvChannel) unmarshaller.unmarshal(xsr);
|
||||||
|
if (tvChannelInfo != null) {
|
||||||
|
return tvChannelInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RokuHttpException("No TvChannel info model in response");
|
||||||
|
} catch (JAXBException | XMLStreamException e) {
|
||||||
|
throw new RokuHttpException("Exception creating TvChannel info Unmarshaller: " + e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a command to get tv-channels from the Roku TV and return a list of Channel objects
|
||||||
|
*
|
||||||
|
* @return A List of Channel objects for all TV channels currently available on the Roku TV
|
||||||
|
* @throws RokuHttpException
|
||||||
|
*/
|
||||||
|
public List<Channel> getTvChannelList() throws RokuHttpException {
|
||||||
|
try {
|
||||||
|
JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNELS;
|
||||||
|
if (ctx != null) {
|
||||||
|
Unmarshaller unmarshaller = ctx.createUnmarshaller();
|
||||||
|
if (unmarshaller != null) {
|
||||||
|
XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY
|
||||||
|
.createXMLStreamReader(new StringReader(getCommand(urlQryTvChannels)));
|
||||||
|
TvChannels tvChannels = (TvChannels) unmarshaller.unmarshal(xsr);
|
||||||
|
if (tvChannels != null) {
|
||||||
|
return tvChannels.getChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RokuHttpException("No TvChannels info model in response");
|
||||||
|
} catch (JAXBException | XMLStreamException e) {
|
||||||
|
throw new RokuHttpException("Exception creating TvChannel info Unmarshaller: " + e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a GET command to the Roku
|
* Sends a GET command to the Roku
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,309 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 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.roku.internal.dto;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the XML response from the Roku HTTP endpoint '/query/tv-active-channel' (Active TV channel information)
|
||||||
|
*
|
||||||
|
* @author Michael Lobstein - Initial contribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NonNullByDefault
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlRootElement(name = "tv-channel")
|
||||||
|
public class TvChannel {
|
||||||
|
@XmlElement
|
||||||
|
private TvChannel.Channel channel = new Channel();
|
||||||
|
|
||||||
|
public TvChannel.Channel getChannel() {
|
||||||
|
return this.channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChannel(TvChannel.Channel value) {
|
||||||
|
this.channel = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public static class Channel {
|
||||||
|
@XmlElement(name = "number")
|
||||||
|
private String number = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "name")
|
||||||
|
private String name = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "type")
|
||||||
|
private String type = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "user-hidden")
|
||||||
|
private boolean userHidden = false;
|
||||||
|
|
||||||
|
@XmlElement(name = "user-favorite")
|
||||||
|
private boolean userFavorite = false;
|
||||||
|
|
||||||
|
@XmlElement(name = "physical-channel")
|
||||||
|
private int physicalChannel = 0;
|
||||||
|
|
||||||
|
@XmlElement(name = "physical-frequency")
|
||||||
|
private int physicalFrequency = 0;
|
||||||
|
|
||||||
|
@XmlElement(name = "active-input")
|
||||||
|
private boolean activeInput = false;
|
||||||
|
|
||||||
|
@XmlElement(name = "signal-state")
|
||||||
|
private String signalState = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "signal-mode")
|
||||||
|
private String signalMode = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "signal-quality")
|
||||||
|
private int signalQuality = 0;
|
||||||
|
|
||||||
|
@XmlElement(name = "signal-strength")
|
||||||
|
private int signalStrength = 0;
|
||||||
|
|
||||||
|
@XmlElement(name = "signal-stalled-pts-cnt")
|
||||||
|
private int signalStalledPtsCnt = 0;
|
||||||
|
|
||||||
|
@XmlElement(name = "program-title")
|
||||||
|
private String programTitle = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-description")
|
||||||
|
private String programDescription = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-ratings")
|
||||||
|
private String programRatings = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-is-blocked")
|
||||||
|
private boolean programIsBlocked = false;
|
||||||
|
|
||||||
|
@XmlElement(name = "program-analog-audio")
|
||||||
|
private String programAnalogAudio = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-digital-audio")
|
||||||
|
private String programDigitalAudio = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-audio-languages")
|
||||||
|
private String programAudioLanguages = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-audio-formats")
|
||||||
|
private String programAudioFormats = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-audio-language")
|
||||||
|
private String programAudioLanguage = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-audio-format")
|
||||||
|
private String programAudioFormat = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "program-has-cc")
|
||||||
|
private boolean programHasCc = false;
|
||||||
|
|
||||||
|
public String getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber(String value) {
|
||||||
|
this.number = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String value) {
|
||||||
|
this.name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String value) {
|
||||||
|
this.type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserHidden() {
|
||||||
|
return userHidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserHidden(boolean value) {
|
||||||
|
this.userHidden = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserFavorite() {
|
||||||
|
return userFavorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserFavorite(boolean value) {
|
||||||
|
this.userFavorite = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPhysicalChannel() {
|
||||||
|
return physicalChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhysicalChannel(int value) {
|
||||||
|
this.physicalChannel = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPhysicalFrequency() {
|
||||||
|
return physicalFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhysicalFrequency(int value) {
|
||||||
|
this.physicalFrequency = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActiveInput() {
|
||||||
|
return activeInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveInput(boolean value) {
|
||||||
|
this.activeInput = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignalState() {
|
||||||
|
return signalState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignalState(String value) {
|
||||||
|
this.signalState = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignalMode() {
|
||||||
|
return signalMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignalMode(String value) {
|
||||||
|
this.signalMode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSignalQuality() {
|
||||||
|
return signalQuality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignalQuality(int value) {
|
||||||
|
this.signalQuality = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSignalStrength() {
|
||||||
|
return signalStrength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignalStrength(int value) {
|
||||||
|
this.signalStrength = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSignalStalledPtsCnt() {
|
||||||
|
return signalStalledPtsCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignalStalledPtsCnt(int value) {
|
||||||
|
this.signalStalledPtsCnt = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramTitle() {
|
||||||
|
return programTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramTitle(String value) {
|
||||||
|
this.programTitle = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramDescription() {
|
||||||
|
return programDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramDescription(String value) {
|
||||||
|
this.programDescription = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramRatings() {
|
||||||
|
return programRatings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramRatings(String value) {
|
||||||
|
this.programRatings = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProgramIsBlocked() {
|
||||||
|
return programIsBlocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramIsBlocked(boolean value) {
|
||||||
|
this.programIsBlocked = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramAnalogAudio() {
|
||||||
|
return programAnalogAudio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramAnalogAudio(String value) {
|
||||||
|
this.programAnalogAudio = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramDigitalAudio() {
|
||||||
|
return programDigitalAudio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramDigitalAudio(String value) {
|
||||||
|
this.programDigitalAudio = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramAudioLanguages() {
|
||||||
|
return programAudioLanguages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramAudioLanguages(String value) {
|
||||||
|
this.programAudioLanguages = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramAudioFormats() {
|
||||||
|
return programAudioFormats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramAudioFormats(String value) {
|
||||||
|
this.programAudioFormats = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramAudioLanguage() {
|
||||||
|
return programAudioLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramAudioLanguage(String value) {
|
||||||
|
this.programAudioLanguage = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgramAudioFormat() {
|
||||||
|
return programAudioFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramAudioFormat(String value) {
|
||||||
|
this.programAudioFormat = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProgramHasCc() {
|
||||||
|
return programHasCc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgramHasCc(boolean value) {
|
||||||
|
this.programHasCc = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 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.roku.internal.dto;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the XML response from the Roku HTTP endpoint '/query/tv-channels' (List of available TV channels)
|
||||||
|
*
|
||||||
|
* @author Michael Lobstein - Initial contribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NonNullByDefault
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlRootElement(name = "tv-channels")
|
||||||
|
public class TvChannels {
|
||||||
|
@XmlElement
|
||||||
|
private List<TvChannels.Channel> channel = new ArrayList<TvChannels.Channel>();
|
||||||
|
|
||||||
|
public List<TvChannels.Channel> getChannel() {
|
||||||
|
return this.channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public static class Channel {
|
||||||
|
@XmlElement(name = "number")
|
||||||
|
private String number = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "name")
|
||||||
|
private String name = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "type")
|
||||||
|
private String type = "";
|
||||||
|
|
||||||
|
@XmlElement(name = "user-hidden")
|
||||||
|
private boolean userHidden = false;
|
||||||
|
|
||||||
|
@XmlElement(name = "user-favorite")
|
||||||
|
private boolean userFavorite = false;
|
||||||
|
|
||||||
|
@XmlElement(name = "physical-channel")
|
||||||
|
private int physicalChannel = 0;
|
||||||
|
|
||||||
|
@XmlElement(name = "physical-frequency")
|
||||||
|
private int physicalFrequency = 0;
|
||||||
|
|
||||||
|
public String getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber(String value) {
|
||||||
|
this.number = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String value) {
|
||||||
|
this.name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String value) {
|
||||||
|
this.type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserHidden() {
|
||||||
|
return userHidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserHidden(boolean value) {
|
||||||
|
this.userHidden = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserFavorite() {
|
||||||
|
return userFavorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserFavorite(boolean value) {
|
||||||
|
this.userFavorite = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPhysicalChannel() {
|
||||||
|
return physicalChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhysicalChannel(int value) {
|
||||||
|
this.physicalChannel = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPhysicalFrequency() {
|
||||||
|
return physicalFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhysicalFrequency(int value) {
|
||||||
|
this.physicalFrequency = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,16 +26,18 @@ import org.openhab.binding.roku.internal.RokuConfiguration;
|
||||||
import org.openhab.binding.roku.internal.RokuHttpException;
|
import org.openhab.binding.roku.internal.RokuHttpException;
|
||||||
import org.openhab.binding.roku.internal.RokuStateDescriptionOptionProvider;
|
import org.openhab.binding.roku.internal.RokuStateDescriptionOptionProvider;
|
||||||
import org.openhab.binding.roku.internal.communication.RokuCommunicator;
|
import org.openhab.binding.roku.internal.communication.RokuCommunicator;
|
||||||
import org.openhab.binding.roku.internal.dto.ActiveApp;
|
|
||||||
import org.openhab.binding.roku.internal.dto.Apps.App;
|
import org.openhab.binding.roku.internal.dto.Apps.App;
|
||||||
import org.openhab.binding.roku.internal.dto.DeviceInfo;
|
import org.openhab.binding.roku.internal.dto.DeviceInfo;
|
||||||
import org.openhab.binding.roku.internal.dto.Player;
|
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.QuantityType;
|
import org.openhab.core.library.types.QuantityType;
|
||||||
import org.openhab.core.library.types.StringType;
|
import org.openhab.core.library.types.StringType;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
import org.openhab.core.thing.ThingStatusDetail;
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
import org.openhab.core.types.RefreshType;
|
import org.openhab.core.types.RefreshType;
|
||||||
|
@ -61,9 +63,11 @@ public class RokuHandler extends BaseThingHandler {
|
||||||
private @Nullable ScheduledFuture<?> refreshJob;
|
private @Nullable ScheduledFuture<?> refreshJob;
|
||||||
private @Nullable ScheduledFuture<?> appListJob;
|
private @Nullable ScheduledFuture<?> appListJob;
|
||||||
|
|
||||||
|
private ThingTypeUID thingTypeUID = THING_TYPE_ROKU_PLAYER;
|
||||||
private RokuCommunicator communicator;
|
private RokuCommunicator communicator;
|
||||||
private DeviceInfo deviceInfo = new DeviceInfo();
|
private DeviceInfo deviceInfo = new DeviceInfo();
|
||||||
private int refreshInterval = DEFAULT_REFRESH_PERIOD_SEC;
|
private int refreshInterval = DEFAULT_REFRESH_PERIOD_SEC;
|
||||||
|
private boolean tvActive = false;
|
||||||
|
|
||||||
private Object sequenceLock = new Object();
|
private Object sequenceLock = new Object();
|
||||||
|
|
||||||
|
@ -79,6 +83,7 @@ public class RokuHandler extends BaseThingHandler {
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
logger.debug("Initializing Roku handler");
|
logger.debug("Initializing Roku handler");
|
||||||
RokuConfiguration config = getConfigAs(RokuConfiguration.class);
|
RokuConfiguration config = getConfigAs(RokuConfiguration.class);
|
||||||
|
this.thingTypeUID = this.getThing().getThingTypeUID();
|
||||||
|
|
||||||
final @Nullable String host = config.hostName;
|
final @Nullable String host = config.hostName;
|
||||||
|
|
||||||
|
@ -127,37 +132,76 @@ public class RokuHandler extends BaseThingHandler {
|
||||||
*/
|
*/
|
||||||
private void refreshPlayerState() {
|
private void refreshPlayerState() {
|
||||||
synchronized (sequenceLock) {
|
synchronized (sequenceLock) {
|
||||||
|
String activeAppId = ROKU_HOME_ID;
|
||||||
try {
|
try {
|
||||||
ActiveApp activeApp = communicator.getActiveApp();
|
activeAppId = communicator.getActiveApp().getApp().getId();
|
||||||
updateState(ACTIVE_APP, new StringType(activeApp.getApp().getId()));
|
updateState(ACTIVE_APP, new StringType(activeAppId));
|
||||||
|
if (TV_APP.equals(activeAppId)) {
|
||||||
|
tvActive = true;
|
||||||
|
} else {
|
||||||
|
if (tvActive) {
|
||||||
|
updateState(SIGNAL_MODE, UnDefType.UNDEF);
|
||||||
|
updateState(SIGNAL_QUALITY, UnDefType.UNDEF);
|
||||||
|
updateState(CHANNEL_NAME, UnDefType.UNDEF);
|
||||||
|
updateState(PROGRAM_TITLE, UnDefType.UNDEF);
|
||||||
|
updateState(PROGRAM_DESCRIPTION, UnDefType.UNDEF);
|
||||||
|
updateState(PROGRAM_RATING, UnDefType.UNDEF);
|
||||||
|
}
|
||||||
|
tvActive = false;
|
||||||
|
}
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
} catch (RokuHttpException e) {
|
} catch (RokuHttpException e) {
|
||||||
logger.debug("Unable to retrieve Roku active-app info. Exception: {}", e.getMessage(), e);
|
logger.debug("Unable to retrieve Roku active-app info. Exception: {}", e.getMessage(), e);
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// On the home app and when using the TV or TV inputs, do not update the play mode or time channels
|
||||||
Player playerInfo = communicator.getPlayerInfo();
|
if (!ROKU_HOME_ID.equals(activeAppId) && !activeAppId.contains(TV_INPUT)) {
|
||||||
// When nothing playing, 'close' is reported, replace with 'stop'
|
try {
|
||||||
updateState(PLAY_MODE, new StringType(playerInfo.getState().replaceAll(CLOSE, STOP)));
|
Player playerInfo = communicator.getPlayerInfo();
|
||||||
|
// When nothing playing, 'close' is reported, replace with 'stop'
|
||||||
|
updateState(PLAY_MODE, new StringType(playerInfo.getState().replaceAll(CLOSE, STOP)));
|
||||||
|
|
||||||
// Remove non-numeric from string, ie: ' ms'
|
// Remove non-numeric from string, ie: ' ms'
|
||||||
String position = playerInfo.getPosition().replaceAll(NON_DIGIT_PATTERN, EMPTY);
|
String position = playerInfo.getPosition().replaceAll(NON_DIGIT_PATTERN, EMPTY);
|
||||||
if (!EMPTY.equals(position)) {
|
if (!EMPTY.equals(position)) {
|
||||||
updateState(TIME_ELAPSED, new QuantityType<>(Integer.parseInt(position) / 1000, API_SECONDS_UNIT));
|
updateState(TIME_ELAPSED,
|
||||||
} else {
|
new QuantityType<>(Integer.parseInt(position) / 1000, API_SECONDS_UNIT));
|
||||||
updateState(TIME_ELAPSED, UnDefType.UNDEF);
|
} else {
|
||||||
}
|
updateState(TIME_ELAPSED, UnDefType.UNDEF);
|
||||||
|
}
|
||||||
|
|
||||||
String duration = playerInfo.getDuration().replaceAll(NON_DIGIT_PATTERN, EMPTY);
|
String duration = playerInfo.getDuration().replaceAll(NON_DIGIT_PATTERN, EMPTY);
|
||||||
if (!EMPTY.equals(duration)) {
|
if (!EMPTY.equals(duration)) {
|
||||||
updateState(TIME_TOTAL, new QuantityType<>(Integer.parseInt(duration) / 1000, API_SECONDS_UNIT));
|
updateState(TIME_TOTAL,
|
||||||
} else {
|
new QuantityType<>(Integer.parseInt(duration) / 1000, API_SECONDS_UNIT));
|
||||||
updateState(TIME_TOTAL, UnDefType.UNDEF);
|
} else {
|
||||||
|
updateState(TIME_TOTAL, UnDefType.UNDEF);
|
||||||
|
}
|
||||||
|
} catch (RokuHttpException e) {
|
||||||
|
logger.debug("Unable to retrieve Roku media-player info. Exception: {}", e.getMessage(), e);
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateState(PLAY_MODE, UnDefType.UNDEF);
|
||||||
|
updateState(TIME_ELAPSED, UnDefType.UNDEF);
|
||||||
|
updateState(TIME_TOTAL, UnDefType.UNDEF);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thingTypeUID.equals(THING_TYPE_ROKU_TV) && tvActive) {
|
||||||
|
try {
|
||||||
|
TvChannel tvChannel = communicator.getActiveTvChannel();
|
||||||
|
updateState(ACTIVE_CHANNEL, new StringType(tvChannel.getChannel().getNumber()));
|
||||||
|
updateState(SIGNAL_MODE, new StringType(tvChannel.getChannel().getSignalMode()));
|
||||||
|
updateState(SIGNAL_QUALITY,
|
||||||
|
new QuantityType<>(tvChannel.getChannel().getSignalQuality(), API_PERCENT_UNIT));
|
||||||
|
updateState(CHANNEL_NAME, new StringType(tvChannel.getChannel().getName()));
|
||||||
|
updateState(PROGRAM_TITLE, new StringType(tvChannel.getChannel().getProgramTitle()));
|
||||||
|
updateState(PROGRAM_DESCRIPTION, new StringType(tvChannel.getChannel().getProgramDescription()));
|
||||||
|
updateState(PROGRAM_RATING, new StringType(tvChannel.getChannel().getProgramRatings()));
|
||||||
|
} catch (RokuHttpException e) {
|
||||||
|
logger.debug("Unable to retrieve Roku tv-active-channel info. Exception: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
} catch (RokuHttpException e) {
|
|
||||||
logger.debug("Unable to retrieve Roku media-player info. Exception: {}", e.getMessage(), e);
|
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,6 +238,26 @@ public class RokuHandler extends BaseThingHandler {
|
||||||
} catch (RokuHttpException e) {
|
} catch (RokuHttpException e) {
|
||||||
logger.debug("Unable to retrieve Roku installed app-list. Exception: {}", e.getMessage(), e);
|
logger.debug("Unable to retrieve Roku installed app-list. Exception: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thingTypeUID.equals(THING_TYPE_ROKU_TV)) {
|
||||||
|
try {
|
||||||
|
List<Channel> channelsList = communicator.getTvChannelList();
|
||||||
|
|
||||||
|
List<StateOption> channelListOptions = new ArrayList<>();
|
||||||
|
channelsList.forEach(channel -> {
|
||||||
|
if (!channel.isUserHidden()) {
|
||||||
|
channelListOptions.add(new StateOption(channel.getNumber(),
|
||||||
|
channel.getNumber() + " - " + channel.getName()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), ACTIVE_CHANNEL),
|
||||||
|
channelListOptions);
|
||||||
|
|
||||||
|
} catch (RokuHttpException e) {
|
||||||
|
logger.debug("Unable to retrieve Roku tv-channels. Exception: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +304,16 @@ public class RokuHandler extends BaseThingHandler {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (channelUID.getId().equals(ACTIVE_CHANNEL)) {
|
||||||
|
synchronized (sequenceLock) {
|
||||||
|
try {
|
||||||
|
communicator.launchTvChannel(command.toString());
|
||||||
|
} catch (RokuHttpException e) {
|
||||||
|
logger.debug("Unable to change channel on Roku TV, channelNumber: {}, Exception: {}", command,
|
||||||
|
e.getMessage());
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Unsupported command: {}", command);
|
logger.debug("Unsupported command: {}", command);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,13 @@
|
||||||
<channel id="playMode" typeId="playMode"/>
|
<channel id="playMode" typeId="playMode"/>
|
||||||
<channel id="timeElapsed" typeId="timeElapsed"/>
|
<channel id="timeElapsed" typeId="timeElapsed"/>
|
||||||
<channel id="timeTotal" typeId="timeTotal"/>
|
<channel id="timeTotal" typeId="timeTotal"/>
|
||||||
|
<channel id="activeChannel" typeId="activeChannel"/>
|
||||||
|
<channel id="signalMode" typeId="signalMode"/>
|
||||||
|
<channel id="signalQuality" typeId="signalQuality"/>
|
||||||
|
<channel id="channelName" typeId="channelName"/>
|
||||||
|
<channel id="programTitle" typeId="programTitle"/>
|
||||||
|
<channel id="programDescription" typeId="programDescription"/>
|
||||||
|
<channel id="programRating" typeId="programRating"/>
|
||||||
</channels>
|
</channels>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -153,4 +160,52 @@
|
||||||
<state readOnly="true" pattern="%d %unit%"/>
|
<state readOnly="true" pattern="%d %unit%"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="activeChannel">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Active Channel</label>
|
||||||
|
<description>The TV Channel Currently Selected on the Roku TV</description>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="signalMode">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Signal Mode</label>
|
||||||
|
<description>The Signal Type of the Current TV Channel, ie: 1080i</description>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="signalQuality">
|
||||||
|
<item-type>Number:Dimensionless</item-type>
|
||||||
|
<label>Signal Quality</label>
|
||||||
|
<description>The Signal Quality of the Current TV Channel</description>
|
||||||
|
<state readOnly="true" pattern="%d %unit%"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="channelName">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Channel Name</label>
|
||||||
|
<description>The Name of the Channel Currently Selected</description>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="programTitle">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Program Title</label>
|
||||||
|
<description>The Name of the Current TV Program</description>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="programDescription">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Program Description</label>
|
||||||
|
<description>The Description of the Current TV Program</description>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="programRating">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Program Rating</label>
|
||||||
|
<description>The TV Parental Guideline Rating of the Current TV Program</description>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
</thing:thing-descriptions>
|
</thing:thing-descriptions>
|
||||||
|
|
Loading…
Reference in New Issue