2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.androidtv.internal;
15 import static org.openhab.binding.androidtv.internal.AndroidTVBindingConstants.*;
17 import java.util.ArrayList;
18 import java.util.List;
20 import java.util.concurrent.ScheduledExecutorService;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.androidtv.internal.protocol.googletv.GoogleTVConfiguration;
27 import org.openhab.binding.androidtv.internal.protocol.googletv.GoogleTVConnectionManager;
28 import org.openhab.binding.androidtv.internal.protocol.shieldtv.ShieldTVConfiguration;
29 import org.openhab.binding.androidtv.internal.protocol.shieldtv.ShieldTVConnectionManager;
30 import org.openhab.core.library.types.StringType;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35 import org.openhab.core.thing.ThingTypeUID;
36 import org.openhab.core.thing.binding.BaseThingHandler;
37 import org.openhab.core.types.Command;
38 import org.openhab.core.types.CommandOption;
39 import org.openhab.core.types.State;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 * The {@link AndroidTVHandler} is responsible for handling commands, which are
45 * sent to one of the channels.
47 * Significant portions reused from Lutron binding with permission from Bob A.
49 * @author Ben Rosenblum - Initial contribution
52 public class AndroidTVHandler extends BaseThingHandler {
54 private final Logger logger = LoggerFactory.getLogger(AndroidTVHandler.class);
56 private @Nullable ShieldTVConnectionManager shieldtvConnectionManager;
57 private @Nullable GoogleTVConnectionManager googletvConnectionManager;
59 private @Nullable ScheduledFuture<?> monitorThingStatusJob;
60 private final Object monitorThingStatusJobLock = new Object();
61 private static final int THING_STATUS_FREQUENCY = 250;
63 private final AndroidTVDynamicCommandDescriptionProvider commandDescriptionProvider;
64 private final AndroidTVTranslationProvider translationProvider;
65 private final ThingTypeUID thingTypeUID;
66 private final String thingID;
68 private String currentThingStatus = "";
69 private boolean currentThingFailed = false;
71 public AndroidTVHandler(Thing thing, AndroidTVDynamicCommandDescriptionProvider commandDescriptionProvider,
72 AndroidTVTranslationProvider translationProvider, ThingTypeUID thingTypeUID) {
74 this.commandDescriptionProvider = commandDescriptionProvider;
75 this.translationProvider = translationProvider;
76 this.thingTypeUID = thingTypeUID;
77 this.thingID = this.getThing().getUID().getId();
80 public void setThingProperty(String property, String value) {
81 thing.setProperty(property, value);
84 public AndroidTVTranslationProvider getTranslationProvider() {
85 return translationProvider;
88 public String getThingID() {
92 public void updateChannelState(String channel, State state) {
93 updateState(channel, state);
96 public ScheduledExecutorService getScheduler() {
100 public void updateCDP(String channelName, Map<String, String> cdpMap) {
101 logger.trace("{} - Updating CDP for {}", this.thingID, channelName);
102 List<CommandOption> commandOptions = new ArrayList<CommandOption>();
103 cdpMap.forEach((key, value) -> commandOptions.add(new CommandOption(key, value)));
104 logger.trace("{} - CDP List: {}", this.thingID, commandOptions);
105 commandDescriptionProvider.setCommandOptions(new ChannelUID(getThing().getUID(), channelName), commandOptions);
108 private void monitorThingStatus() {
109 synchronized (monitorThingStatusJobLock) {
111 monitorThingStatusJob = scheduler.schedule(this::monitorThingStatus, THING_STATUS_FREQUENCY,
112 TimeUnit.MILLISECONDS);
116 public void checkThingStatus() {
117 String currentThingStatus = this.currentThingStatus;
118 boolean currentThingFailed = this.currentThingFailed;
120 String statusMessage = "";
121 boolean failed = false;
123 GoogleTVConnectionManager googletvConnectionManager = this.googletvConnectionManager;
124 ShieldTVConnectionManager shieldtvConnectionManager = this.shieldtvConnectionManager;
126 if (googletvConnectionManager != null) {
127 if (!googletvConnectionManager.getLoggedIn()) {
130 statusMessage = "GoogleTV: " + googletvConnectionManager.getStatusMessage();
133 if (THING_TYPE_SHIELDTV.equals(thingTypeUID)) {
134 if (shieldtvConnectionManager != null) {
135 if (!shieldtvConnectionManager.getLoggedIn()) {
138 statusMessage = statusMessage + " | ShieldTV: " + shieldtvConnectionManager.getStatusMessage();
142 if (!currentThingStatus.equals(statusMessage) || (currentThingFailed != failed)) {
144 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, statusMessage);
146 updateStatus(ThingStatus.ONLINE);
150 this.currentThingStatus = statusMessage;
151 this.currentThingFailed = failed;
155 public void initialize() {
156 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/offline.protocols-starting");
158 GoogleTVConfiguration googletvConfig = getConfigAs(GoogleTVConfiguration.class);
159 String ipAddress = googletvConfig.ipAddress;
160 boolean gtvEnabled = googletvConfig.gtvEnabled;
162 if (ipAddress.isBlank()) {
163 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
164 "@text/offline.googletv-address-not-specified");
168 if (THING_TYPE_GOOGLETV.equals(thingTypeUID) || gtvEnabled) {
169 googletvConnectionManager = new GoogleTVConnectionManager(this, googletvConfig);
172 if (THING_TYPE_SHIELDTV.equals(thingTypeUID)) {
173 ShieldTVConfiguration shieldtvConfig = getConfigAs(ShieldTVConfiguration.class);
174 ipAddress = shieldtvConfig.ipAddress;
176 if (ipAddress.isBlank()) {
177 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
178 "@text/offline.shieldtv-address-not-specified");
182 shieldtvConnectionManager = new ShieldTVConnectionManager(this, shieldtvConfig);
185 monitorThingStatusJob = scheduler.schedule(this::monitorThingStatus, THING_STATUS_FREQUENCY,
186 TimeUnit.MILLISECONDS);
190 public void handleCommand(ChannelUID channelUID, Command command) {
191 logger.trace("{} - Command received at handler: {} {}", this.thingID, channelUID.getId(), command);
193 if (command.toString().equals("REFRESH")) {
194 // REFRESH causes issues on some channels. Block for now until implemented.
198 GoogleTVConnectionManager googletvConnectionManager = this.googletvConnectionManager;
199 ShieldTVConnectionManager shieldtvConnectionManager = this.shieldtvConnectionManager;
201 if (CHANNEL_DEBUG.equals(channelUID.getId())) {
202 if (command instanceof StringType) {
203 if (command.toString().equals("GOOGLETV_HALT") && (googletvConnectionManager != null)) {
204 googletvConnectionManager.dispose();
205 googletvConnectionManager = null;
206 } else if (command.toString().equals("GOOGLETV_START")) {
207 GoogleTVConfiguration googletvConfig = getConfigAs(GoogleTVConfiguration.class);
208 googletvConnectionManager = new GoogleTVConnectionManager(this, googletvConfig);
209 } else if (command.toString().equals("GOOGLETV_SHIM") && (googletvConnectionManager == null)) {
210 GoogleTVConfiguration googletvConfig = getConfigAs(GoogleTVConfiguration.class);
211 googletvConfig.shim = true;
212 googletvConnectionManager = new GoogleTVConnectionManager(this, googletvConfig);
213 } else if (command.toString().equals("SHIELDTV_HALT") && (shieldtvConnectionManager != null)) {
214 shieldtvConnectionManager.dispose();
215 shieldtvConnectionManager = null;
216 } else if (command.toString().equals("SHIELDTV_START")) {
217 ShieldTVConfiguration shieldtvConfig = getConfigAs(ShieldTVConfiguration.class);
218 shieldtvConnectionManager = new ShieldTVConnectionManager(this, shieldtvConfig);
219 } else if (command.toString().equals("SHIELDTV_SHIM") && (shieldtvConnectionManager == null)) {
220 ShieldTVConfiguration shieldtvConfig = getConfigAs(ShieldTVConfiguration.class);
221 shieldtvConfig.shim = true;
222 shieldtvConnectionManager = new ShieldTVConnectionManager(this, shieldtvConfig);
223 } else if (command.toString().startsWith("GOOGLETV") && (googletvConnectionManager != null)) {
224 googletvConnectionManager.handleCommand(channelUID, command);
225 } else if (command.toString().startsWith("SHIELDTV") && (shieldtvConnectionManager != null)) {
226 shieldtvConnectionManager.handleCommand(channelUID, command);
232 if (THING_TYPE_SHIELDTV.equals(thingTypeUID) && (shieldtvConnectionManager != null)) {
233 if (CHANNEL_PINCODE.equals(channelUID.getId())) {
234 if (command instanceof StringType) {
235 if (!shieldtvConnectionManager.getLoggedIn()) {
236 shieldtvConnectionManager.handleCommand(channelUID, command);
240 } else if (CHANNEL_APP.equals(channelUID.getId())) {
241 if (command instanceof StringType) {
242 shieldtvConnectionManager.handleCommand(channelUID, command);
245 } else if (googletvConnectionManager == null) {
246 shieldtvConnectionManager.handleCommand(channelUID, command);
251 if (googletvConnectionManager != null) {
252 googletvConnectionManager.handleCommand(channelUID, command);
256 logger.warn("{} - Commands All Failed. Please report this as a bug. {} {}", thingID, channelUID.getId(),
261 public void dispose() {
262 synchronized (monitorThingStatusJobLock) {
263 ScheduledFuture<?> monitorThingStatusJob = this.monitorThingStatusJob;
264 if (monitorThingStatusJob != null) {
265 monitorThingStatusJob.cancel(true);
269 GoogleTVConnectionManager googletvConnectionManager = this.googletvConnectionManager;
270 ShieldTVConnectionManager shieldtvConnectionManager = this.shieldtvConnectionManager;
272 if (shieldtvConnectionManager != null) {
273 shieldtvConnectionManager.dispose();
276 if (googletvConnectionManager != null) {
277 googletvConnectionManager.dispose();