]> git.basschouten.com Git - openhab-addons.git/blob
e0c59ba3721c087a27b914554a185ae252685601
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.touchwand.internal;
14
15 import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.SUPPORTED_TOUCHWAND_TYPES;
16
17 import java.io.IOException;
18 import java.net.URI;
19 import java.net.URISyntaxException;
20 import java.util.Arrays;
21 import java.util.concurrent.CopyOnWriteArraySet;
22 import java.util.concurrent.ScheduledExecutorService;
23 import java.util.concurrent.ScheduledFuture;
24 import java.util.concurrent.TimeUnit;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.jetty.websocket.api.Session;
29 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
30 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
31 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
32 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
33 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
34 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
35 import org.eclipse.jetty.websocket.client.WebSocketClient;
36 import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
37 import org.openhab.binding.touchwand.internal.dto.TouchWandUnitFromJson;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 import com.google.gson.JsonObject;
42 import com.google.gson.JsonParser;
43 import com.google.gson.JsonSyntaxException;
44
45 /**
46  * The {@link TouchWandWebSockets} class implements WebSockets API to TouchWand controller
47  *
48  * @author Roie Geron - Initial contribution
49  */
50 @NonNullByDefault
51 public class TouchWandWebSockets {
52
53     private static final int CONNECT_TIMEOUT_SEC = 10;
54     private static final int WEBSOCKET_RECONNECT_INTERVAL_SEC = CONNECT_TIMEOUT_SEC * 2;
55     private static final int WEBSOCKET_IDLE_TIMEOUT_MS = CONNECT_TIMEOUT_SEC * 10 * 1000;
56     private final Logger logger = LoggerFactory.getLogger(TouchWandWebSockets.class);
57     private static final String WS_ENDPOINT_TOUCHWAND = "/async";
58
59     private WebSocketClient client;
60     private String controllerAddress;
61     private TouchWandSocket touchWandSocket;
62     private boolean isShutDown = false;
63     private CopyOnWriteArraySet<TouchWandUnitStatusUpdateListener> listeners = new CopyOnWriteArraySet<>();
64     private @Nullable ScheduledFuture<?> socketReconnect;
65     private @Nullable URI uri;
66
67     private ScheduledExecutorService scheduler;
68
69     public TouchWandWebSockets(String ipAddress, ScheduledExecutorService scheduler) {
70         client = new WebSocketClient();
71         touchWandSocket = new TouchWandSocket();
72         this.controllerAddress = ipAddress;
73         this.scheduler = scheduler;
74         socketReconnect = null;
75     }
76
77     public void connect() {
78         try {
79             uri = new URI("ws://" + controllerAddress + WS_ENDPOINT_TOUCHWAND);
80         } catch (URISyntaxException e) {
81             logger.warn("URI not valid {} message {}", uri, e.getMessage());
82             return;
83         }
84
85         client.setConnectTimeout(CONNECT_TIMEOUT_SEC);
86         ClientUpgradeRequest request = new ClientUpgradeRequest();
87         request.setSubProtocols("relay_protocol");
88
89         try {
90             client.start();
91             client.connect(touchWandSocket, uri, request);
92         } catch (Exception e) {
93             logger.warn("Could not connect webSocket URI {} message {}", uri, e.getMessage());
94             return;
95         }
96     }
97
98     public void dispose() {
99         isShutDown = true;
100         try {
101             client.stop();
102             ScheduledFuture<?> mySocketReconnect = socketReconnect;
103             if (mySocketReconnect != null) {
104                 mySocketReconnect.cancel(true);
105             }
106         } catch (Exception e) {
107             logger.warn("Could not stop webSocketClient,  message {}", e.getMessage());
108         }
109     }
110
111     public void registerListener(TouchWandUnitStatusUpdateListener listener) {
112         if (!listeners.contains(listener)) {
113             logger.debug("Adding TouchWandWebSocket listener {}", listener);
114             listeners.add(listener);
115         }
116     }
117
118     public void unregisterListener(TouchWandUnitStatusUpdateListener listener) {
119         logger.debug("Removing TouchWandWebSocket listener {}", listener);
120         listeners.remove(listener);
121     }
122
123     @WebSocket(maxIdleTime = WEBSOCKET_IDLE_TIMEOUT_MS)
124     public class TouchWandSocket {
125
126         @OnWebSocketClose
127         public void onClose(int statusCode, String reason) {
128             logger.debug("Connection closed: {} - {}", statusCode, reason);
129             if (!isShutDown) {
130                 logger.debug("weSocket Closed - reconnecting");
131                 asyncWeb();
132             }
133         }
134
135         @OnWebSocketConnect
136         public void onConnect(Session session) {
137             logger.debug("TouchWandWebSockets connected to {}", session.getRemoteAddress().toString());
138             try {
139                 session.getRemote().sendString("{\"myopenhab\": \"myopenhab\"}");
140             } catch (IOException e) {
141                 logger.warn("sendString : {}", e.getMessage());
142             }
143         }
144
145         @OnWebSocketMessage
146         public void onMessage(String msg) {
147             TouchWandUnitData touchWandUnit;
148             JsonParser jsonParser = new JsonParser();
149             try {
150                 JsonObject unitObj = jsonParser.parse(msg).getAsJsonObject();
151                 boolean eventUnitChanged = unitObj.get("type").getAsString().equals("UNIT_CHANGED");
152                 if (!eventUnitChanged) {
153                     return;
154                 }
155                 touchWandUnit = TouchWandUnitFromJson.parseResponse(unitObj.get("unit").getAsJsonObject());
156                 if (!touchWandUnit.getStatus().equals("ALIVE")) {
157                     return;
158                 }
159                 boolean supportedUnitType = Arrays.asList(SUPPORTED_TOUCHWAND_TYPES).contains(touchWandUnit.getType());
160                 if (!supportedUnitType) {
161                     logger.debug("UNIT_CHANGED for unsupported unit type {}", touchWandUnit.getType());
162                     return;
163                 }
164                 logger.debug("UNIT_CHANGED: name {} id {} status {}", touchWandUnit.getName(), touchWandUnit.getId(),
165                         touchWandUnit.getCurrStatus());
166
167                 for (TouchWandUnitStatusUpdateListener listener : listeners) {
168                     listener.onDataReceived(touchWandUnit);
169
170                 }
171             } catch (JsonSyntaxException e) {
172                 logger.warn("jsonParser.parse {} ", e.getMessage());
173             }
174         }
175
176         @OnWebSocketError
177         public void onError(Throwable cause) {
178             logger.warn("WebSocket Error: {}", cause.getMessage());
179             if (!isShutDown) {
180                 logger.debug("WebSocket onError - reconnecting");
181                 asyncWeb();
182             }
183         }
184
185         private void asyncWeb() {
186             ScheduledFuture<?> mySocketReconnect = socketReconnect;
187             if (mySocketReconnect == null || mySocketReconnect.isDone()) {
188                 socketReconnect = scheduler.schedule(TouchWandWebSockets.this::connect,
189                         WEBSOCKET_RECONNECT_INTERVAL_SEC, TimeUnit.SECONDS);
190             }
191         }
192     }
193 }