Skip to content

Commit

Permalink
Check for ECP Limited Mode
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Lobstein <[email protected]>
  • Loading branch information
mlobstein committed Dec 18, 2024
1 parent fd092a2 commit 1bb95af
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 8 deletions.
4 changes: 4 additions & 0 deletions bundles/org.openhab.binding.roku/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
This binding connects Roku streaming media players and Roku TVs to openHAB.
The Roku device must support the Roku ECP protocol REST API.

In order for the binding to control the Roku, the following setting:
**Settings-> System-> Advanced system settings-> Control by mobile apps**
must be configured as `Enabled` or `Permissive`.

## Supported Things

There are two supported thing types, which represent either a standalone Roku device or a Roku TV.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,6 @@ public class RokuBindingConstants {
public static final String TV_APP = "tvinput.dtv";
public static final String TV_INPUT = "tvinput";
public static final String POWER_ON = "POWERON";

public static final String LIMITED_MODE_RESPONSE = "ECP command not allowed";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package org.openhab.binding.roku.internal.communication;

import static org.openhab.binding.roku.internal.RokuBindingConstants.*;

import java.io.StringReader;
import java.util.List;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -283,8 +285,12 @@ public List<Channel> getTvChannelList() throws RokuHttpException {
*/
private String getCommand(String url) throws RokuHttpException {
try {
return httpClient.newRequest(url).method(HttpMethod.GET).timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS)
.send().getContentAsString();
final String response = httpClient.newRequest(url).method(HttpMethod.GET)
.timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS).send().getContentAsString();
if (response != null && response.contains(LIMITED_MODE_RESPONSE)) {
throw new RokuHttpException(LIMITED_MODE_RESPONSE);
}
return response != null ? response : EMPTY;
} catch (TimeoutException | ExecutionException e) {
throw new RokuHttpException("Error executing GET command for URL: " + url, e);
} catch (InterruptedException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,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 int limitedMode = -1;
private Map<String, String> appMap = new HashMap<>();

private Object sequenceLock = new Object();
Expand Down Expand Up @@ -175,18 +176,20 @@ private void refreshPlayerState() {
}
tvActive = false;
}
updateStatus(ThingStatus.ONLINE);
} catch (RokuHttpException e) {
logger.debug("Unable to retrieve Roku active-app info. Exception: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}

// On the home app and when using the TV or TV inputs, do not update the play mode or time channels
if (!ROKU_HOME_ID.equals(activeAppId) && !activeAppId.contains(TV_INPUT)) {
// if in limitedMode, keep checking getPlayerInfo to see if the error goes away
if ((!ROKU_HOME_ID.equals(activeAppId) && !activeAppId.contains(TV_INPUT)) || limitedMode != 0) {
try {
Player playerInfo = communicator.getPlayerInfo();
limitedMode = 0;
// When nothing playing, 'close' is reported, replace with 'stop'
updateState(PLAY_MODE, new StringType(playerInfo.getState().replaceAll(CLOSE, STOP)));
updateState(PLAY_MODE, new StringType(playerInfo.getState().replace(CLOSE, STOP)));
updateState(CONTROL,
PLAY.equalsIgnoreCase(playerInfo.getState()) ? PlayPauseType.PLAY : PlayPauseType.PAUSE);

Expand All @@ -209,8 +212,13 @@ private void refreshPlayerState() {
} catch (NumberFormatException e) {
logger.debug("Unable to parse playerInfo integer value. Exception: {}", e.getMessage());
} catch (RokuHttpException e) {
logger.debug("Unable to retrieve Roku media-player info. Exception: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
if (isLimitedModeResponse(e)) {
limitedMode = 1;
} else {
logger.debug("Unable to retrieve Roku media-player info. Exception: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
}
} else {
updateState(PLAY_MODE, UnDefType.UNDEF);
Expand All @@ -221,6 +229,7 @@ private void refreshPlayerState() {
if (thingTypeUID.equals(THING_TYPE_ROKU_TV) && tvActive) {
try {
TvChannel tvChannel = communicator.getActiveTvChannel();
limitedMode = 0;
updateState(ACTIVE_CHANNEL, new StringType(tvChannel.getChannel().getNumber()));
updateState(SIGNAL_MODE, new StringType(tvChannel.getChannel().getSignalMode()));
updateState(SIGNAL_QUALITY,
Expand All @@ -230,12 +239,35 @@ private void refreshPlayerState() {
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);
if (isLimitedModeResponse(e)) {
limitedMode = 1;
} else {
logger.debug("Unable to retrieve Roku tv-active-channel. Exception: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
}
}

if (limitedMode < 1) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.limited");
}
}
}

/**
* Determines if the Roku is configured for Limited mode by examining the exception message
*
* @param ex the RokuHttpException
* @return boolean indicating if the Roku is configured for Limited Mode
*/
private boolean isLimitedModeResponse(RokuHttpException ex) {
final String message = ex.getMessage();
return message != null && message.contains(LIMITED_MODE_RESPONSE);
}

/**
* Start the job to periodically update list of apps installed on the the Roku
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,7 @@ channel-type.roku.timeElapsed.label = Playback Time
channel-type.roku.timeElapsed.description = The Current Playback Time Elapsed
channel-type.roku.timeTotal.label = Total Time
channel-type.roku.timeTotal.description = The Total Length of the Current Title

# error status descriptions

error.limited = Roku device is configured incorrectly - see README

0 comments on commit 1bb95af

Please sign in to comment.