]> git.basschouten.com Git - openhab-addons.git/blob
377bd196d5c424ab8f58c93d830e41f0eb194703
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.MOUSE_CHANNEL;
16 import static org.openhab.binding.unifiedremote.internal.UnifiedRemoteBindingConstants.SEND_KEY_CHANNEL;
17
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;
24
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;
35
36 /**
37  * The {@link UnifiedRemoteHandler} is responsible for handling commands, which are
38  * sent to one of the channels.
39  *
40  * @author Miguel Alvarez - Initial contribution
41  */
42 @NonNullByDefault
43 public class UnifiedRemoteHandler extends BaseThingHandler {
44
45     private @Nullable UnifiedRemoteConnection connection;
46     private @Nullable ScheduledFuture<?> connectionCheckerSchedule;
47     private HttpClient httpClient;
48
49     public UnifiedRemoteHandler(Thing thing, HttpClient httpClient) {
50         super(thing);
51         this.httpClient = httpClient;
52     }
53
54     @Override
55     public void handleCommand(ChannelUID channelUID, Command command) {
56         String channelId = channelUID.getId();
57         if (!isLinked(channelId))
58             return;
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                 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");
119                     }
120                 }
121             } catch (InterruptedException | ExecutionException | TimeoutException e) {
122                 if (isThingOfflineException(e)) {
123                     // we assume thing is offline
124                     updateStatus(ThingStatus.OFFLINE);
125                 } else {
126                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
127                             "Unexpected exception: " + e.getMessage());
128                 }
129             }
130         }, 0, 40, TimeUnit.SECONDS);
131     }
132
133     private boolean isThingOfflineException(Exception e) {
134         return e instanceof TimeoutException || e.getCause() instanceof ConnectException
135                 || e.getCause() instanceof NoRouteToHostException;
136     }
137
138     private void stopConnectionChecker() {
139         var schedule = connectionCheckerSchedule;
140         if (schedule != null) {
141             schedule.cancel(true);
142             connectionCheckerSchedule = null;
143         }
144     }
145
146     @Override
147     public void dispose() {
148         stopConnectionChecker();
149         super.dispose();
150     }
151
152     private boolean isErrorResponse(ContentResponse response) {
153         return response.getStatus() != 200 || response.getContentAsString().contains("Not a valid connection");
154     }
155 }