2 * Copyright (c) 2010-2020 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.touchwand.internal;
15 import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.SUPPORTED_TOUCHWAND_TYPES;
17 import java.io.IOException;
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;
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;
41 import com.google.gson.JsonObject;
42 import com.google.gson.JsonParser;
43 import com.google.gson.JsonSyntaxException;
46 * The {@link TouchWandWebSockets} class implements WebSockets API to TouchWand controller
48 * @author Roie Geron - Initial contribution
51 public class TouchWandWebSockets {
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";
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;
67 private ScheduledExecutorService scheduler;
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;
77 public void connect() {
79 uri = new URI("ws://" + controllerAddress + WS_ENDPOINT_TOUCHWAND);
80 } catch (URISyntaxException e) {
81 logger.warn("URI not valid {} message {}", uri, e.getMessage());
85 client.setConnectTimeout(CONNECT_TIMEOUT_SEC);
86 ClientUpgradeRequest request = new ClientUpgradeRequest();
87 request.setSubProtocols("relay_protocol");
91 client.connect(touchWandSocket, uri, request);
92 } catch (Exception e) {
93 logger.warn("Could not connect webSocket URI {} message {}", uri, e.getMessage());
98 public void dispose() {
102 ScheduledFuture<?> mySocketReconnect = socketReconnect;
103 if (mySocketReconnect != null) {
104 mySocketReconnect.cancel(true);
106 } catch (Exception e) {
107 logger.warn("Could not stop webSocketClient, message {}", e.getMessage());
111 public void registerListener(TouchWandUnitStatusUpdateListener listener) {
112 if (!listeners.contains(listener)) {
113 logger.debug("Adding TouchWandWebSocket listener {}", listener);
114 listeners.add(listener);
118 public void unregisterListener(TouchWandUnitStatusUpdateListener listener) {
119 logger.debug("Removing TouchWandWebSocket listener {}", listener);
120 listeners.remove(listener);
123 @WebSocket(maxIdleTime = WEBSOCKET_IDLE_TIMEOUT_MS)
124 public class TouchWandSocket {
127 public void onClose(int statusCode, String reason) {
128 logger.debug("Connection closed: {} - {}", statusCode, reason);
130 logger.debug("weSocket Closed - reconnecting");
136 public void onConnect(Session session) {
137 logger.debug("TouchWandWebSockets connected to {}", session.getRemoteAddress().toString());
139 session.getRemote().sendString("{\"myopenhab\": \"myopenhab\"}");
140 } catch (IOException e) {
141 logger.warn("sendString : {}", e.getMessage());
146 public void onMessage(String msg) {
147 TouchWandUnitData touchWandUnit;
148 JsonParser jsonParser = new JsonParser();
150 JsonObject unitObj = jsonParser.parse(msg).getAsJsonObject();
151 boolean eventUnitChanged = unitObj.get("type").getAsString().equals("UNIT_CHANGED");
152 if (!eventUnitChanged) {
155 touchWandUnit = TouchWandUnitFromJson.parseResponse(unitObj.get("unit").getAsJsonObject());
156 if (!touchWandUnit.getStatus().equals("ALIVE")) {
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());
164 logger.debug("UNIT_CHANGED: name {} id {} status {}", touchWandUnit.getName(), touchWandUnit.getId(),
165 touchWandUnit.getCurrStatus());
167 for (TouchWandUnitStatusUpdateListener listener : listeners) {
168 listener.onDataReceived(touchWandUnit);
171 } catch (JsonSyntaxException e) {
172 logger.warn("jsonParser.parse {} ", e.getMessage());
177 public void onError(Throwable cause) {
178 logger.warn("WebSocket Error: {}", cause.getMessage());
180 logger.debug("WebSocket onError - reconnecting");
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);