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.unifiedremote.internal;
15 import static org.openhab.binding.unifiedremote.internal.UnifiedRemoteBindingConstants.*;
17 import java.net.ConnectException;
18 import java.net.NoRouteToHostException;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.client.HttpClient;
27 import org.eclipse.jetty.client.api.ContentResponse;
28 import org.openhab.core.thing.ChannelUID;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.thing.binding.BaseThingHandler;
33 import org.openhab.core.types.Command;
36 * The {@link UnifiedRemoteHandler} is responsible for handling commands, which are
37 * sent to one of the channels.
39 * @author Miguel Alvarez - Initial contribution
42 public class UnifiedRemoteHandler extends BaseThingHandler {
44 private @Nullable UnifiedRemoteConnection connection;
45 private @Nullable ScheduledFuture<?> connectionCheckerSchedule;
46 private HttpClient httpClient;
48 public UnifiedRemoteHandler(Thing thing, HttpClient httpClient) {
50 this.httpClient = httpClient;
54 public void handleCommand(ChannelUID channelUID, Command command) {
55 String channelId = channelUID.getId();
56 if (!isLinked(channelId)) {
59 String stringCommand = command.toFullString();
60 UnifiedRemoteConnection urConnection = connection;
62 if (urConnection != null) {
63 ContentResponse response;
66 response = urConnection.mouseMove(stringCommand);
68 case SEND_KEY_CHANNEL:
69 response = urConnection.sendKey(stringCommand);
74 if (isErrorResponse(response)) {
75 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Session expired");
76 urConnection.authenticate();
77 updateStatus(ThingStatus.ONLINE);
80 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection not initialized");
82 } catch (InterruptedException | ExecutionException | TimeoutException e) {
83 if (isThingOfflineException(e)) {
84 // we assume thing is offline
85 updateStatus(ThingStatus.OFFLINE);
87 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
88 "Unexpected exception: " + e.getMessage());
94 public void initialize() {
95 updateStatus(ThingStatus.UNKNOWN);
96 connection = getNewConnection();
97 initConnectionChecker();
100 private UnifiedRemoteConnection getNewConnection() {
101 UnifiedRemoteConfiguration currentConfiguration = getConfigAs(UnifiedRemoteConfiguration.class);
102 return new UnifiedRemoteConnection(this.httpClient, currentConfiguration.host);
105 private void initConnectionChecker() {
106 stopConnectionChecker();
107 connectionCheckerSchedule = scheduler.scheduleWithFixedDelay(() -> {
109 UnifiedRemoteConnection urConnection = connection;
110 if (urConnection == null) {
113 ThingStatus status = thing.getStatus();
114 if ((status == ThingStatus.OFFLINE || status == ThingStatus.UNKNOWN) && connection != null) {
115 urConnection.authenticate();
116 updateStatus(ThingStatus.ONLINE);
117 } else if (status == ThingStatus.ONLINE) {
118 if (isErrorResponse(urConnection.keepAlive())) {
119 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Keep alive failed");
122 } catch (InterruptedException | ExecutionException | TimeoutException e) {
123 if (isThingOfflineException(e)) {
124 // we assume thing is offline
125 updateStatus(ThingStatus.OFFLINE);
127 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
128 "Unexpected exception: " + e.getMessage());
131 }, 0, 40, TimeUnit.SECONDS);
134 private boolean isThingOfflineException(Exception e) {
135 return e instanceof TimeoutException || e.getCause() instanceof ConnectException
136 || e.getCause() instanceof NoRouteToHostException;
139 private void stopConnectionChecker() {
140 var schedule = connectionCheckerSchedule;
141 if (schedule != null) {
142 schedule.cancel(true);
143 connectionCheckerSchedule = null;
148 public void dispose() {
149 stopConnectionChecker();
153 private boolean isErrorResponse(ContentResponse response) {
154 return response.getStatus() != 200 || response.getContentAsString().contains("Not a valid connection");