]> git.basschouten.com Git - openhab-addons.git/blob
d4cad30a16ba8bd12d948093dae0ae3bfc1fb009
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.livisismarthome.internal;
14
15 import java.io.IOException;
16 import java.net.URI;
17 import java.util.concurrent.ExecutionException;
18
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;
34
35 /**
36  * The {@link LivisiWebSocket} implements the websocket for receiving constant updates
37  * from the LIVISI SmartHome web service.
38  *
39  * @author Oliver Kuhl - Initial contribution
40  * @author Sven Strohschein - Renamed from Innogy to Livisi
41  */
42 @NonNullByDefault
43 @WebSocket
44 public class LivisiWebSocket {
45
46     private final Logger logger = LoggerFactory.getLogger(LivisiWebSocket.class);
47
48     private final HttpClient httpClient;
49     private final EventListener eventListener;
50     private final URI webSocketURI;
51     private final int maxIdleTimeout;
52
53     private WebSocketClient client;
54     private @Nullable Session session;
55     private boolean closing;
56
57     /**
58      * Constructs the {@link LivisiWebSocket}.
59      *
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
64      */
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();
71     }
72
73     /**
74      * Starts the {@link LivisiWebSocket}.
75      */
76     public synchronized void start() throws WebSocketConnectException {
77         if (client.isStopped()) {
78             client = createWebSocketClient();
79             startWebSocketClient(client);
80         }
81
82         session = connectWebSocket(session);
83     }
84
85     private Session connectWebSocket(@Nullable Session session) throws WebSocketConnectException {
86         closeSession(session);
87         this.session = null;
88
89         logger.debug("Connecting to LIVISI SmartHome WebSocket...");
90         try {
91             return client.connect(this, webSocketURI).get();
92         } catch (IOException | InterruptedException | ExecutionException e) {
93             throw new WebSocketConnectException("The WebSocket couldn't get connected!", e);
94         }
95     }
96
97     /**
98      * Stops the {@link LivisiWebSocket}.
99      */
100     public synchronized void stop() {
101         this.closing = true;
102         if (isRunning()) {
103             closeSession(session);
104         } else {
105             logger.trace("Stopping websocket ignored - was not running.");
106         }
107         session = null;
108         stopWebSocketClient(client);
109         client = createWebSocketClient();
110     }
111
112     private void closeSession(@Nullable Session session) {
113         if (session != null) {
114             logger.debug("Closing session...");
115             session.close();
116         }
117     }
118
119     /**
120      * Return true, if the websocket is running.
121      *
122      * @return true if the websocket is running, otherwise false
123      */
124     public synchronized boolean isRunning() {
125         return isRunning(session);
126     }
127
128     private boolean isRunning(@Nullable Session session) {
129         return session != null && session.isOpen();
130     }
131
132     @OnWebSocketConnect
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);
137     }
138
139     @OnWebSocketClose
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: {}",
146                     statusCode, reason);
147             eventListener.connectionClosed();
148         }
149     }
150
151     @OnWebSocketError
152     public void onError(Throwable cause) {
153         logger.debug("LIVISI SmartHome websocket onError() - {}", cause.getMessage());
154         eventListener.onError(cause);
155     }
156
157     @OnWebSocketMessage
158     public void onMessage(String msg) {
159         logger.debug("LIVISI SmartHome websocket onMessage() - {}", msg);
160         if (closing) {
161             logger.debug("LIVISI SmartHome websocket onMessage() - ignored, WebSocket is closing...");
162         } else {
163             eventListener.onEvent(msg);
164         }
165     }
166
167     WebSocketClient createWebSocketClient() {
168         WebSocketClient client = new WebSocketClient(httpClient);
169         client.setMaxIdleTimeout(maxIdleTimeout);
170         return client;
171     }
172
173     void startWebSocketClient(WebSocketClient client) throws WebSocketConnectException {
174         try {
175             client.start();
176         } catch (Exception e) {
177             throw new WebSocketConnectException("Starting WebSocket failed!", e);
178         }
179     }
180
181     void stopWebSocketClient(WebSocketClient client) {
182         try {
183             client.stop();
184             client.destroy();
185         } catch (Exception e) {
186             logger.debug("Stopping WebSocket failed", e);
187         }
188     }
189 }