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.deconz.internal.netutils;
17 import java.util.concurrent.ConcurrentHashMap;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jetty.websocket.api.Session;
21 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
22 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
23 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
24 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
25 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
26 import org.eclipse.jetty.websocket.client.WebSocketClient;
27 import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
28 import org.openhab.binding.deconz.internal.dto.GroupMessage;
29 import org.openhab.binding.deconz.internal.dto.LightMessage;
30 import org.openhab.binding.deconz.internal.dto.SensorMessage;
31 import org.openhab.binding.deconz.internal.types.ResourceType;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
35 import com.google.gson.Gson;
38 * Establishes and keeps a websocket connection to the deCONZ software.
40 * The connection is closed by deCONZ now and then and needs to be re-established.
42 * @author David Graeff - Initial contribution
46 public class WebSocketConnection {
47 private static final Map<ResourceType, Class<? extends DeconzBaseMessage>> EXPECTED_MESSAGE_TYPES = Map.of(
48 ResourceType.GROUPS, GroupMessage.class, ResourceType.LIGHTS, LightMessage.class, ResourceType.SENSORS,
51 private final Logger logger = LoggerFactory.getLogger(WebSocketConnection.class);
53 private final WebSocketClient client;
54 private final WebSocketConnectionListener connectionListener;
55 private final Map<Map.Entry<ResourceType, String>, WebSocketMessageListener> listeners = new ConcurrentHashMap<>();
57 private final Gson gson;
58 private boolean connected = false;
60 public WebSocketConnection(WebSocketConnectionListener listener, WebSocketClient client, Gson gson) {
61 this.connectionListener = listener;
63 this.client.setMaxIdleTimeout(0);
67 public void start(String ip) {
72 URI destUri = URI.create("ws://" + ip);
76 logger.debug("Connecting to: {}", destUri);
77 client.connect(this, destUri).get();
78 } catch (Exception e) {
79 connectionListener.connectionError(e);
87 } catch (Exception e) {
88 logger.debug("Error while closing connection", e);
93 public void registerListener(ResourceType resourceType, String sensorID, WebSocketMessageListener listener) {
94 listeners.put(Map.entry(resourceType, sensorID), listener);
97 public void unregisterListener(ResourceType resourceType, String sensorID) {
98 listeners.remove(Map.entry(resourceType, sensorID));
102 public void onConnect(Session session) {
104 logger.debug("Connect: {}", session.getRemoteAddress().getAddress());
105 connectionListener.connectionEstablished();
108 @SuppressWarnings("null")
110 public void onMessage(String message) {
111 logger.trace("Raw data received by websocket: {}", message);
113 DeconzBaseMessage changedMessage = gson.fromJson(message, DeconzBaseMessage.class);
114 if (changedMessage.r == ResourceType.UNKNOWN) {
115 logger.trace("Received message has unknown resource type. Skipping message.");
119 WebSocketMessageListener listener = listeners.get(Map.entry(changedMessage.r, changedMessage.id));
120 if (listener == null) {
122 "Couldn't find listener for id {} with resource type {}. Either no thing for this id has been defined or this is a bug.",
123 changedMessage.id, changedMessage.r);
127 Class<? extends DeconzBaseMessage> expectedMessageType = EXPECTED_MESSAGE_TYPES.get(changedMessage.r);
128 if (expectedMessageType == null) {
129 logger.warn("BUG! Could not get expected message type for resource type {}. Please report this incident.",
134 listener.messageReceived(changedMessage.id, gson.fromJson(message, expectedMessageType));
138 public void onError(Throwable cause) {
140 connectionListener.connectionError(cause);
144 public void onClose(int statusCode, String reason) {
146 connectionListener.connectionLost(reason);
149 public boolean isConnected() {