]> git.basschouten.com Git - openhab-addons.git/blob
6e8fd3d578145808c9f1013e88ec9a5384abc2f7
[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.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 = 15;
54     private static final int CONNECT_TIMEOUT_MS = CONNECT_TIMEOUT_SEC * 1000;
55     private static final int WEBSOCKET_RECONNECT_INTERVAL_SEC = CONNECT_TIMEOUT_SEC * 2;
56     private static final int WEBSOCKET_IDLE_TIMEOUT_MS = CONNECT_TIMEOUT_SEC * 10 * 1000;
57     private final Logger logger = LoggerFactory.getLogger(TouchWandWebSockets.class);
58     private static final String WS_ENDPOINT_TOUCHWAND = "/async";
59
60     private WebSocketClient client;
61     private String controllerAddress;
62     private TouchWandSocket touchWandSocket;
63     private boolean isShutDown = false;
64     private CopyOnWriteArraySet<TouchWandUnitStatusUpdateListener> listeners = new CopyOnWriteArraySet<>();
65     private @Nullable ScheduledFuture<?> socketReconnect;
66     private @Nullable URI uri;
67
68     private ScheduledExecutorService scheduler;
69
70     public TouchWandWebSockets(String ipAddress, ScheduledExecutorService scheduler) {
71         client = new WebSocketClient();
72         touchWandSocket = new TouchWandSocket();
73         this.controllerAddress = ipAddress;
74         this.scheduler = scheduler;
75         socketReconnect = null;
76     }
77
78     public void connect() {
79         try {
80             uri = new URI("ws://" + controllerAddress + WS_ENDPOINT_TOUCHWAND);
81         } catch (URISyntaxException e) {
82             logger.warn("URI not valid {} message {}", uri, e.getMessage());
83             return;
84         }
85
86         client.setConnectTimeout(CONNECT_TIMEOUT_MS);
87         ClientUpgradeRequest request = new ClientUpgradeRequest();
88         request.setSubProtocols("relay_protocol");
89
90         try {
91             client.start();
92             client.connect(touchWandSocket, uri, request);
93         } catch (Exception e) {
94             logger.warn("Could not connect webSocket URI {} message {}", uri, e.getMessage());
95             return;
96         }
97     }
98
99     public void dispose() {
100         isShutDown = true;
101         try {
102             client.stop();
103             ScheduledFuture<?> mySocketReconnect = socketReconnect;
104             if (mySocketReconnect != null) {
105                 mySocketReconnect.cancel(true);
106             }
107         } catch (Exception e) {
108             logger.warn("Could not stop webSocketClient,  message {}", e.getMessage());
109         }
110     }
111
112     public void registerListener(TouchWandUnitStatusUpdateListener listener) {
113         if (!listeners.contains(listener)) {
114             logger.debug("Adding TouchWandWebSocket listener {}", listener);
115             listeners.add(listener);
116         }
117     }
118
119     public void unregisterListener(TouchWandUnitStatusUpdateListener listener) {
120         logger.debug("Removing TouchWandWebSocket listener {}", listener);
121         listeners.remove(listener);
122     }
123
124     @WebSocket(maxIdleTime = WEBSOCKET_IDLE_TIMEOUT_MS)
125     public class TouchWandSocket {
126
127         @OnWebSocketClose
128         public void onClose(int statusCode, String reason) {
129             logger.debug("Connection closed: {} - {}", statusCode, reason);
130             if (!isShutDown) {
131                 logger.debug("weSocket Closed - reconnecting");
132                 asyncWeb();
133             }
134         }
135
136         @OnWebSocketConnect
137         public void onConnect(Session session) {
138             logger.debug("TouchWandWebSockets connected to {}", session.getRemoteAddress().toString());
139             try {
140                 long timestamp = System.currentTimeMillis(); // need unique id
141                 String controllerIdStr = String.format("{\"contId\": \"openhab%d\"}", timestamp);
142                 session.getRemote().sendString(controllerIdStr);
143             } catch (IOException e) {
144                 logger.warn("sendString : {}", e.getMessage());
145             }
146         }
147
148         @OnWebSocketMessage
149         public void onMessage(String msg) {
150             TouchWandUnitData touchWandUnit;
151             try {
152                 JsonObject unitObj = JsonParser.parseString(msg).getAsJsonObject();
153                 boolean eventUnitChanged = unitObj.get("type").getAsString().equals("UNIT_CHANGED");
154                 if (!eventUnitChanged) {
155                     return;
156                 }
157                 touchWandUnit = TouchWandUnitFromJson.parseResponse(unitObj.get("unit").getAsJsonObject());
158                 if (!touchWandUnit.getStatus().equals("ALIVE")) {
159                     return;
160                 }
161                 boolean supportedUnitType = Arrays.asList(SUPPORTED_TOUCHWAND_TYPES).contains(touchWandUnit.getType());
162                 if (!supportedUnitType) {
163                     logger.debug("UNIT_CHANGED for unsupported unit type {}", touchWandUnit.getType());
164                     return;
165                 }
166                 logger.debug("UNIT_CHANGED: name {} id {} status {}", touchWandUnit.getName(), touchWandUnit.getId(),
167                         touchWandUnit.getCurrStatus());
168
169                 for (TouchWandUnitStatusUpdateListener listener : listeners) {
170                     listener.onDataReceived(touchWandUnit);
171
172                 }
173             } catch (JsonSyntaxException e) {
174                 logger.warn("jsonParser.parse {} ", e.getMessage());
175             }
176         }
177
178         @OnWebSocketError
179         public void onError(Throwable cause) {
180             logger.warn("WebSocket Error: {}", cause.getMessage());
181             if (!isShutDown) {
182                 logger.debug("WebSocket onError - reconnecting");
183                 asyncWeb();
184             }
185         }
186
187         private void asyncWeb() {
188             ScheduledFuture<?> mySocketReconnect = socketReconnect;
189             if (mySocketReconnect == null || mySocketReconnect.isDone()) {
190                 socketReconnect = scheduler.schedule(TouchWandWebSockets.this::connect,
191                         WEBSOCKET_RECONNECT_INTERVAL_SEC, TimeUnit.SECONDS);
192             }
193         }
194     }
195 }