]> git.basschouten.com Git - openhab-addons.git/blob
01a89cdf884811e510305320df38dc68f6b95494
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.unifiedremote.internal;
14
15 import static org.openhab.binding.unifiedremote.internal.UnifiedRemoteBindingConstants.*;
16
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;
23
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;
34
35 /**
36  * The {@link UnifiedRemoteHandler} is responsible for handling commands, which are
37  * sent to one of the channels.
38  *
39  * @author Miguel Alvarez - Initial contribution
40  */
41 @NonNullByDefault
42 public class UnifiedRemoteHandler extends BaseThingHandler {
43
44     private @Nullable UnifiedRemoteConnection connection;
45     private @Nullable ScheduledFuture<?> connectionCheckerSchedule;
46     private HttpClient httpClient;
47
48     public UnifiedRemoteHandler(Thing thing, HttpClient httpClient) {
49         super(thing);
50         this.httpClient = httpClient;
51     }
52
53     @Override
54     public void handleCommand(ChannelUID channelUID, Command command) {
55         String channelId = channelUID.getId();
56         if (!isLinked(channelId)) {
57             return;
58         }
59         String stringCommand = command.toFullString();
60         UnifiedRemoteConnection urConnection = connection;
61         try {
62             if (urConnection != null) {
63                 ContentResponse response;
64                 switch (channelId) {
65                     case MOUSE_CHANNEL:
66                         response = urConnection.mouseMove(stringCommand);
67                         break;
68                     case SEND_KEY_CHANNEL:
69                         response = urConnection.sendKey(stringCommand);
70                         break;
71                     default:
72                         return;
73                 }
74                 if (isErrorResponse(response)) {
75                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Session expired");
76                     urConnection.authenticate();
77                     updateStatus(ThingStatus.ONLINE);
78                 }
79             } else {
80                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection not initialized");
81             }
82         } catch (InterruptedException | ExecutionException | TimeoutException e) {
83             if (isThingOfflineException(e)) {
84                 // we assume thing is offline
85                 updateStatus(ThingStatus.OFFLINE);
86             } else {
87                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
88                         "Unexpected exception: " + e.getMessage());
89             }
90         }
91     }
92
93     @Override
94     public void initialize() {
95         updateStatus(ThingStatus.UNKNOWN);
96         connection = getNewConnection();
97         initConnectionChecker();
98     }
99
100     private UnifiedRemoteConnection getNewConnection() {
101         UnifiedRemoteConfiguration currentConfiguration = getConfigAs(UnifiedRemoteConfiguration.class);
102         return new UnifiedRemoteConnection(this.httpClient, currentConfiguration.host);
103     }
104
105     private void initConnectionChecker() {
106         stopConnectionChecker();
107         connectionCheckerSchedule = scheduler.scheduleWithFixedDelay(() -> {
108             try {
109                 UnifiedRemoteConnection urConnection = connection;
110                 if (urConnection == null) {
111                     return;
112                 }
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");
120                     }
121                 }
122             } catch (InterruptedException | ExecutionException | TimeoutException e) {
123                 if (isThingOfflineException(e)) {
124                     // we assume thing is offline
125                     updateStatus(ThingStatus.OFFLINE);
126                 } else {
127                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
128                             "Unexpected exception: " + e.getMessage());
129                 }
130             }
131         }, 0, 40, TimeUnit.SECONDS);
132     }
133
134     private boolean isThingOfflineException(Exception e) {
135         return e instanceof TimeoutException || e.getCause() instanceof ConnectException
136                 || e.getCause() instanceof NoRouteToHostException;
137     }
138
139     private void stopConnectionChecker() {
140         var schedule = connectionCheckerSchedule;
141         if (schedule != null) {
142             schedule.cancel(true);
143             connectionCheckerSchedule = null;
144         }
145     }
146
147     @Override
148     public void dispose() {
149         stopConnectionChecker();
150         super.dispose();
151     }
152
153     private boolean isErrorResponse(ContentResponse response) {
154         return response.getStatus() != 200 || response.getContentAsString().contains("Not a valid connection");
155     }
156 }