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 public AndroidTVHandler(Thing thing, AndroidTVDynamicCommandDescriptionProvider commandDescriptionProvider,
69 AndroidTVTranslationProvider translationProvider, ThingTypeUID thingTypeUID) {
71 this.commandDescriptionProvider = commandDescriptionProvider;
72 this.translationProvider = translationProvider;
73 this.thingTypeUID = thingTypeUID;
74 this.thingID = this.getThing().getUID().getId();
77 public void setThingProperty(String property, String value) {
78 thing.setProperty(property, value);
81 public AndroidTVTranslationProvider getTranslationProvider() {
82 return translationProvider;
85 public String getThingID() {
89 public void updateChannelState(String channel, State state) {
90 updateState(channel, state);
93 public ScheduledExecutorService getScheduler() {
97 public void updateCDP(String channelName, Map<String, String> cdpMap) {
98 logger.trace("{} - Updating CDP for {}", this.thingID, channelName);
99 List<CommandOption> commandOptions = new ArrayList<CommandOption>();
100 cdpMap.forEach((key, value) -> commandOptions.add(new CommandOption(key, value)));
101 logger.trace("{} - CDP List: {}", this.thingID, commandOptions);
102 commandDescriptionProvider.setCommandOptions(new ChannelUID(getThing().getUID(), channelName), commandOptions);
105 private void monitorThingStatus() {
106 synchronized (monitorThingStatusJobLock) {
108 monitorThingStatusJob = scheduler.schedule(this::monitorThingStatus, THING_STATUS_FREQUENCY,
109 TimeUnit.MILLISECONDS);
113 public void checkThingStatus() {
114 String statusMessage = "";
115 boolean failed = false;
117 GoogleTVConnectionManager googletvConnectionManager = this.googletvConnectionManager;
118 ShieldTVConnectionManager shieldtvConnectionManager = this.shieldtvConnectionManager;
120 if (googletvConnectionManager != null) {
121 if (!googletvConnectionManager.getLoggedIn()) {
124 statusMessage = "GoogleTV: " + googletvConnectionManager.getStatusMessage();
127 if (THING_TYPE_SHIELDTV.equals(thingTypeUID)) {
128 if (shieldtvConnectionManager != null) {
129 if (!shieldtvConnectionManager.getLoggedIn()) {
132 statusMessage = statusMessage + " | ShieldTV: " + shieldtvConnectionManager.getStatusMessage();
137 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, statusMessage);
139 updateStatus(ThingStatus.ONLINE);
144 public void initialize() {
145 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/offline.protocols-starting");
147 GoogleTVConfiguration googletvConfig = getConfigAs(GoogleTVConfiguration.class);
148 String ipAddress = googletvConfig.ipAddress;
150 if (ipAddress.isBlank()) {
151 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
152 "@text/offline.googletv-address-not-specified");
156 googletvConnectionManager = new GoogleTVConnectionManager(this, googletvConfig);
158 if (THING_TYPE_SHIELDTV.equals(thingTypeUID)) {
159 ShieldTVConfiguration shieldtvConfig = getConfigAs(ShieldTVConfiguration.class);
160 ipAddress = shieldtvConfig.ipAddress;
162 if (ipAddress.isBlank()) {
163 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
164 "@text/offline.shieldtv-address-not-specified");
168 shieldtvConnectionManager = new ShieldTVConnectionManager(this, shieldtvConfig);
171 monitorThingStatusJob = scheduler.schedule(this::monitorThingStatus, THING_STATUS_FREQUENCY,
172 TimeUnit.MILLISECONDS);
176 public void handleCommand(ChannelUID channelUID, Command command) {
177 logger.trace("{} - Command received at handler: {} {}", this.thingID, channelUID.getId(), command);
179 if (command.toString().equals("REFRESH")) {
180 // REFRESH causes issues on some channels. Block for now until implemented.
184 GoogleTVConnectionManager googletvConnectionManager = this.googletvConnectionManager;
185 ShieldTVConnectionManager shieldtvConnectionManager = this.shieldtvConnectionManager;
187 if (CHANNEL_DEBUG.equals(channelUID.getId())) {
188 if (command instanceof StringType) {
189 if (command.toString().equals("GOOGLETV_HALT") && (googletvConnectionManager != null)) {
190 googletvConnectionManager.dispose();
191 googletvConnectionManager = null;
192 } else if (command.toString().equals("GOOGLETV_START")) {
193 GoogleTVConfiguration googletvConfig = getConfigAs(GoogleTVConfiguration.class);
194 googletvConnectionManager = new GoogleTVConnectionManager(this, googletvConfig);
195 } else if (command.toString().equals("GOOGLETV_SHIM") && (googletvConnectionManager == null)) {
196 GoogleTVConfiguration googletvConfig = getConfigAs(GoogleTVConfiguration.class);
197 googletvConfig.shim = true;
198 googletvConnectionManager = new GoogleTVConnectionManager(this, googletvConfig);
199 } else if (command.toString().equals("SHIELDTV_HALT") && (shieldtvConnectionManager != null)) {
200 shieldtvConnectionManager.dispose();
201 shieldtvConnectionManager = null;
202 } else if (command.toString().equals("SHIELDTV_START")) {
203 ShieldTVConfiguration shieldtvConfig = getConfigAs(ShieldTVConfiguration.class);
204 shieldtvConnectionManager = new ShieldTVConnectionManager(this, shieldtvConfig);
205 } else if (command.toString().equals("SHIELDTV_SHIM") && (shieldtvConnectionManager == null)) {
206 ShieldTVConfiguration shieldtvConfig = getConfigAs(ShieldTVConfiguration.class);
207 shieldtvConfig.shim = true;
208 shieldtvConnectionManager = new ShieldTVConnectionManager(this, shieldtvConfig);
209 } else if (command.toString().startsWith("GOOGLETV") && (googletvConnectionManager != null)) {
210 googletvConnectionManager.handleCommand(channelUID, command);
211 } else if (command.toString().startsWith("SHIELDTV") && (shieldtvConnectionManager != null)) {
212 shieldtvConnectionManager.handleCommand(channelUID, command);
218 if (THING_TYPE_SHIELDTV.equals(thingTypeUID) && (shieldtvConnectionManager != null)) {
219 if (CHANNEL_PINCODE.equals(channelUID.getId())) {
220 if (command instanceof StringType) {
221 if (!shieldtvConnectionManager.getLoggedIn()) {
222 shieldtvConnectionManager.handleCommand(channelUID, command);
226 } else if (CHANNEL_APP.equals(channelUID.getId())) {
227 if (command instanceof StringType) {
228 shieldtvConnectionManager.handleCommand(channelUID, command);
234 if (googletvConnectionManager != null) {
235 googletvConnectionManager.handleCommand(channelUID, command);
239 logger.warn("{} - Commands All Failed. Please report this as a bug. {} {}", thingID, channelUID.getId(),
244 public void dispose() {
245 synchronized (monitorThingStatusJobLock) {
246 ScheduledFuture<?> monitorThingStatusJob = this.monitorThingStatusJob;
247 if (monitorThingStatusJob != null) {
248 monitorThingStatusJob.cancel(true);
252 GoogleTVConnectionManager googletvConnectionManager = this.googletvConnectionManager;
253 ShieldTVConnectionManager shieldtvConnectionManager = this.shieldtvConnectionManager;
255 if (shieldtvConnectionManager != null) {
256 shieldtvConnectionManager.dispose();
259 if (googletvConnectionManager != null) {
260 googletvConnectionManager.dispose();