diff --git a/bundles/org.openhab.binding.roku/README.md b/bundles/org.openhab.binding.roku/README.md index 65bf9720cba89..512d94ace171c 100644 --- a/bundles/org.openhab.binding.roku/README.md +++ b/bundles/org.openhab.binding.roku/README.md @@ -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. diff --git a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuLimitedModeException.java b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuLimitedModeException.java new file mode 100644 index 0000000000000..1d73d94796b8e --- /dev/null +++ b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuLimitedModeException.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2010-2024 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link RokuLimitedModeException} extends RokuHttpException + * + * @author Michael Lobstein - Initial contribution + */ +@NonNullByDefault +public class RokuLimitedModeException extends RokuHttpException { + private static final long serialVersionUID = 1L; + + public RokuLimitedModeException(String errorMessage, Throwable t) { + super(errorMessage, t); + } + + public RokuLimitedModeException(String errorMessage) { + super(errorMessage); + } +} diff --git a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/communication/RokuCommunicator.java b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/communication/RokuCommunicator.java index c9fa33ecda44b..1c2ab0edeba65 100644 --- a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/communication/RokuCommunicator.java +++ b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/communication/RokuCommunicator.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpMethod; import org.openhab.binding.roku.internal.RokuHttpException; +import org.openhab.binding.roku.internal.RokuLimitedModeException; import org.openhab.binding.roku.internal.dto.ActiveApp; import org.openhab.binding.roku.internal.dto.Apps; import org.openhab.binding.roku.internal.dto.Apps.App; @@ -47,6 +48,7 @@ @NonNullByDefault public class RokuCommunicator { private static final int REQUEST_TIMEOUT = 5000; + private static final String LIMITED_MODE_RESPONSE = "ECP command not allowed"; private final Logger logger = LoggerFactory.getLogger(RokuCommunicator.class); private final HttpClient httpClient; @@ -283,8 +285,12 @@ public List 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 RokuLimitedModeException(url + ": " + response); + } + return response != null ? response : ""; } catch (TimeoutException | ExecutionException e) { throw new RokuHttpException("Error executing GET command for URL: " + url, e); } catch (InterruptedException e) { 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 9df1b33ea06c3..dc5099dd8faa9 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 @@ -26,6 +26,7 @@ import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.roku.internal.RokuConfiguration; import org.openhab.binding.roku.internal.RokuHttpException; +import org.openhab.binding.roku.internal.RokuLimitedModeException; import org.openhab.binding.roku.internal.RokuStateDescriptionOptionProvider; import org.openhab.binding.roku.internal.communication.RokuCommunicator; import org.openhab.binding.roku.internal.dto.Apps.App; @@ -73,6 +74,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 appMap = new HashMap<>(); private Object sequenceLock = new Object(); @@ -175,18 +177,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); @@ -208,9 +212,13 @@ private void refreshPlayerState() { } } catch (NumberFormatException e) { logger.debug("Unable to parse playerInfo integer value. Exception: {}", e.getMessage()); + } catch (RokuLimitedModeException e) { + logger.debug("RokuLimitedModeException: {}", e.getMessage()); + limitedMode = 1; } catch (RokuHttpException e) { 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); @@ -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, @@ -229,10 +238,21 @@ private void refreshPlayerState() { 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 (RokuLimitedModeException e) { + logger.debug("RokuLimitedModeException: {}", e.getMessage()); + limitedMode = 1; } catch (RokuHttpException e) { logger.debug("Unable to retrieve Roku tv-active-channel info. 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"); + } } } diff --git a/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/i18n/roku.properties b/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/i18n/roku.properties index dd6c31dbe3782..cc4c681250a97 100644 --- a/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/i18n/roku.properties +++ b/bundles/org.openhab.binding.roku/src/main/resources/OH-INF/i18n/roku.properties @@ -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