]> git.basschouten.com Git - openhab-addons.git/blob
2493f4de4f2cffdc5c77ac4a22b8f876c0ec815a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.samsungtv.internal.protocol;
14
15 import java.io.IOException;
16 import java.net.URI;
17 import java.nio.ByteBuffer;
18 import java.util.Optional;
19 import java.util.concurrent.Future;
20 import java.util.concurrent.TimeUnit;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.eclipse.jetty.websocket.api.Session;
25 import org.eclipse.jetty.websocket.api.WebSocketAdapter;
26 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * Websocket base class
32  *
33  * @author Arjan Mels - Initial contribution
34  * @author Nick Waterton - refactoring
35  */
36 @NonNullByDefault
37 class WebSocketBase extends WebSocketAdapter {
38     private final Logger logger = LoggerFactory.getLogger(WebSocketBase.class);
39     final RemoteControllerWebSocket remoteControllerWebSocket;
40     final int bufferSize = 1048576; // 1 Mb
41
42     private Optional<Future<?>> sessionFuture = Optional.empty();
43
44     private String host = "";
45     private String className = "";
46     private Optional<URI> uri = Optional.empty();
47     private int count = 0;
48
49     /**
50      * @param remoteControllerWebSocket
51      */
52     WebSocketBase(RemoteControllerWebSocket remoteControllerWebSocket) {
53         this.remoteControllerWebSocket = remoteControllerWebSocket;
54         this.host = remoteControllerWebSocket.host;
55         this.className = this.getClass().getSimpleName();
56     }
57
58     @Override
59     public void onWebSocketClose(int statusCode, @Nullable String reason) {
60         logger.debug("{}: {} connection closed: {} - {}", host, className, statusCode, reason);
61         super.onWebSocketClose(statusCode, reason);
62         if (statusCode == 1001) {
63             // timeout
64             reconnect();
65         }
66         if (statusCode == 1006) {
67             // Disconnected
68             reconnect();
69         }
70     }
71
72     @Override
73     public void onWebSocketError(@Nullable Throwable error) {
74         logger.debug("{}: {} connection error {}", host, className, error != null ? error.getMessage() : "");
75         super.onWebSocketError(error);
76     }
77
78     void reconnect() {
79         if (!isConnected()) {
80             if (sessionFuture.isPresent() && count++ < 4) {
81                 uri.ifPresent(u -> {
82                     try {
83                         logger.debug("{}: Reconnecting : {} try: {}", host, className, count);
84                         remoteControllerWebSocket.callback.handler.getScheduler().schedule(() -> {
85                             reconnect();
86                         }, 2000, TimeUnit.MILLISECONDS);
87                         connect(u);
88                     } catch (RemoteControllerException e) {
89                         logger.warn("{} Reconnect Failed {} : {}", host, className, e.getMessage());
90                     }
91                 });
92             } else {
93                 count = 0;
94             }
95         }
96     }
97
98     void connect(URI uri) throws RemoteControllerException {
99         count = 0;
100         if (isConnected() || sessionFuture.map(sf -> !sf.isDone()).orElse(false)) {
101             logger.trace("{}: {} already connecting or connected", host, className);
102             return;
103         }
104         logger.debug("{}: {} connecting to: {}", host, className, uri);
105         this.uri = Optional.of(uri);
106         try {
107             sessionFuture = Optional.of(remoteControllerWebSocket.client.connect(this, uri));
108         } catch (IOException | IllegalStateException e) {
109             throw new RemoteControllerException(e);
110         }
111     }
112
113     @Override
114     public void onWebSocketConnect(@Nullable Session session) {
115         logger.debug("{}: {} connection established: {}", host, className,
116                 session != null ? session.getRemoteAddress().getHostString() : "");
117         if (session != null) {
118             final WebSocketPolicy currentPolicy = session.getPolicy();
119             currentPolicy.setInputBufferSize(bufferSize);
120             currentPolicy.setMaxTextMessageSize(bufferSize);
121             currentPolicy.setMaxBinaryMessageSize(bufferSize);
122             logger.trace("{}: {} Buffer Size set to {} Mb", host, className,
123                     Math.round((bufferSize / 1048576.0) * 100.0) / 100.0);
124             // avoid 5 minute idle timeout
125             remoteControllerWebSocket.callback.handler.getScheduler().scheduleWithFixedDelay(() -> {
126                 try {
127                     String data = "Ping";
128                     ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
129                     session.getRemote().sendPing(payload);
130                 } catch (IOException e) {
131                     logger.warn("{} problem starting periodic Ping {} : {}", host, className, e.getMessage());
132                 }
133             }, 4, 4, TimeUnit.MINUTES);
134         }
135         super.onWebSocketConnect(session);
136         count = 0;
137     }
138
139     void close() {
140         this.sessionFuture.ifPresent(sf -> {
141             if (!sf.isDone()) {
142                 logger.trace("{}: Cancelling session Future: {}", host, sf);
143                 sf.cancel(true);
144             }
145         });
146         sessionFuture = Optional.empty();
147         Optional.ofNullable(getSession()).ifPresent(s -> {
148             logger.debug("{}: {} Connection close requested", host, className);
149             s.close();
150         });
151     }
152
153     void sendCommand(String cmd) {
154         try {
155             if (isConnected()) {
156                 getRemote().sendString(cmd);
157                 logger.trace("{}: {}: sendCommand: {}", host, className, cmd);
158             } else {
159                 logger.warn("{}: {} not connected: {}", host, className, cmd);
160             }
161         } catch (IOException e) {
162             logger.warn("{}: {}: cannot send command: {}", host, className, e.getMessage());
163         }
164     }
165
166     @Override
167     public void onWebSocketText(@Nullable String str) {
168         logger.trace("{}: {}: onWebSocketText: {}", host, className, str);
169     }
170
171     @Override
172     public void onWebSocketBinary(byte @Nullable [] arr, int pos, int len) {
173         logger.trace("{}: {}: onWebSocketBinary: offset: {}, len: {}", host, className, pos, len);
174     }
175 }