2 * Copyright (c) 2010-2022 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.livisismarthome.internal;
15 import java.io.IOException;
17 import java.util.concurrent.ExecutionException;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.eclipse.jetty.client.HttpClient;
22 import org.eclipse.jetty.websocket.api.Session;
23 import org.eclipse.jetty.websocket.api.StatusCode;
24 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
25 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
26 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
27 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
28 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
29 import org.eclipse.jetty.websocket.client.WebSocketClient;
30 import org.openhab.binding.livisismarthome.internal.client.exception.WebSocketConnectException;
31 import org.openhab.binding.livisismarthome.internal.listener.EventListener;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 * The {@link LivisiWebSocket} implements the websocket for receiving constant updates
37 * from the LIVISI SmartHome web service.
39 * @author Oliver Kuhl - Initial contribution
40 * @author Sven Strohschein - Renamed from Innogy to Livisi
44 public class LivisiWebSocket {
46 private final Logger logger = LoggerFactory.getLogger(LivisiWebSocket.class);
48 private final HttpClient httpClient;
49 private final EventListener eventListener;
50 private final URI webSocketURI;
51 private final int maxIdleTimeout;
53 private WebSocketClient client;
54 private @Nullable Session session;
55 private boolean closing;
58 * Constructs the {@link LivisiWebSocket}.
60 * @param eventListener the responsible
61 * {@link org.openhab.binding.livisismarthome.internal.handler.LivisiBridgeHandler}
62 * @param webSocketURI the {@link URI} of the websocket endpoint
63 * @param maxIdleTimeout max idle timeout
65 public LivisiWebSocket(HttpClient httpClient, EventListener eventListener, URI webSocketURI, int maxIdleTimeout) {
66 this.httpClient = httpClient;
67 this.eventListener = eventListener;
68 this.webSocketURI = webSocketURI;
69 this.maxIdleTimeout = maxIdleTimeout;
70 this.client = createWebSocketClient();
74 * Starts the {@link LivisiWebSocket}.
76 public synchronized void start() throws WebSocketConnectException {
77 if (client.isStopped()) {
78 client = createWebSocketClient();
79 startWebSocketClient(client);
82 session = connectWebSocket(session);
85 private Session connectWebSocket(@Nullable Session session) throws WebSocketConnectException {
86 closeSession(session);
89 logger.debug("Connecting to LIVISI SmartHome WebSocket...");
91 return client.connect(this, webSocketURI).get();
92 } catch (IOException | InterruptedException | ExecutionException e) {
93 throw new WebSocketConnectException("The WebSocket couldn't get connected!", e);
98 * Stops the {@link LivisiWebSocket}.
100 public synchronized void stop() {
103 closeSession(session);
105 logger.trace("Stopping websocket ignored - was not running.");
108 stopWebSocketClient(client);
109 client = createWebSocketClient();
112 private void closeSession(@Nullable Session session) {
113 if (session != null) {
114 logger.debug("Closing session...");
120 * Return true, if the websocket is running.
122 * @return true if the websocket is running, otherwise false
124 public synchronized boolean isRunning() {
125 return isRunning(session);
128 private boolean isRunning(@Nullable Session session) {
129 return session != null && session.isOpen();
133 public void onConnect(Session session) {
134 this.closing = false;
135 logger.debug("Connected to LIVISI SmartHome webservice.");
136 logger.trace("LIVISI SmartHome websocket session: {}", session);
140 public void onClose(int statusCode, String reason) {
141 if (statusCode == StatusCode.NORMAL) {
142 logger.debug("Connection to LIVISI SmartHome webservice was closed normally.");
143 } else if (!closing) {
144 // An additional reconnect attempt is only required when the close/stop wasn't executed by the binding.
145 logger.debug("Connection to LIVISI SmartHome webservice was closed abnormally (code: {}). Reason: {}",
147 eventListener.connectionClosed();
152 public void onError(Throwable cause) {
153 logger.debug("LIVISI SmartHome websocket onError() - {}", cause.getMessage());
154 eventListener.onError(cause);
158 public void onMessage(String msg) {
159 logger.debug("LIVISI SmartHome websocket onMessage() - {}", msg);
161 logger.debug("LIVISI SmartHome websocket onMessage() - ignored, WebSocket is closing...");
163 eventListener.onEvent(msg);
167 WebSocketClient createWebSocketClient() {
168 WebSocketClient client = new WebSocketClient(httpClient);
169 client.setMaxIdleTimeout(maxIdleTimeout);
173 void startWebSocketClient(WebSocketClient client) throws WebSocketConnectException {
176 } catch (Exception e) {
177 throw new WebSocketConnectException("Starting WebSocket failed!", e);
181 void stopWebSocketClient(WebSocketClient client) {
185 } catch (Exception e) {
186 logger.debug("Stopping WebSocket failed", e);