2 * Copyright (c) 2010-2021 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.MOUSE_CHANNEL;
16 import static org.openhab.binding.unifiedremote.internal.UnifiedRemoteBindingConstants.SEND_KEY_CHANNEL;
18 import java.net.ConnectException;
19 import java.net.NoRouteToHostException;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.TimeoutException;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.eclipse.jetty.client.HttpClient;
28 import org.eclipse.jetty.client.api.ContentResponse;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.binding.BaseThingHandler;
34 import org.openhab.core.types.Command;
37 * The {@link UnifiedRemoteHandler} is responsible for handling commands, which are
38 * sent to one of the channels.
40 * @author Miguel Alvarez - Initial contribution
43 public class UnifiedRemoteHandler extends BaseThingHandler {
45 private @Nullable UnifiedRemoteConnection connection;
46 private @Nullable ScheduledFuture<?> connectionCheckerSchedule;
47 private HttpClient httpClient;
49 public UnifiedRemoteHandler(Thing thing, HttpClient httpClient) {
51 this.httpClient = httpClient;
55 public void handleCommand(ChannelUID channelUID, Command command) {
56 String channelId = channelUID.getId();
57 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)
112 ThingStatus status = thing.getStatus();
113 if ((status == ThingStatus.OFFLINE || status == ThingStatus.UNKNOWN) && connection != null) {
114 urConnection.authenticate();
115 updateStatus(ThingStatus.ONLINE);
116 } else if (status == ThingStatus.ONLINE) {
117 if (isErrorResponse(urConnection.keepAlive())) {
118 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Keep alive failed");
121 } catch (InterruptedException | ExecutionException | TimeoutException e) {
122 if (isThingOfflineException(e)) {
123 // we assume thing is offline
124 updateStatus(ThingStatus.OFFLINE);
126 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
127 "Unexpected exception: " + e.getMessage());
130 }, 0, 40, TimeUnit.SECONDS);
133 private boolean isThingOfflineException(Exception e) {
134 return e instanceof TimeoutException || e.getCause() instanceof ConnectException
135 || e.getCause() instanceof NoRouteToHostException;
138 private void stopConnectionChecker() {
139 var schedule = connectionCheckerSchedule;
140 if (schedule != null) {
141 schedule.cancel(true);
142 connectionCheckerSchedule = null;
147 public void dispose() {
148 stopConnectionChecker();
152 private boolean isErrorResponse(ContentResponse response) {
153 return response.getStatus() != 200 || response.getContentAsString().contains("Not a valid connection");