]> git.basschouten.com Git - openhab-addons.git/blob
a2fd6f3b7bc9355e8909c525af16892410243635
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.denonmarantz.internal.connector.telnet;
14
15 import java.io.BufferedReader;
16 import java.io.IOException;
17 import java.io.InputStreamReader;
18 import java.io.OutputStreamWriter;
19 import java.net.InetSocketAddress;
20 import java.net.Socket;
21 import java.net.SocketTimeoutException;
22
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.denonmarantz.internal.config.DenonMarantzConfiguration;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * Manage telnet connection to the Denon/Marantz Receiver
30  *
31  * @author Jeroen Idserda - Initial contribution (1.x Binding)
32  * @author Jan-Willem Veldhuis - Refactored for 2.x
33  */
34 public class DenonMarantzTelnetClientThread extends Thread {
35
36     private Logger logger = LoggerFactory.getLogger(DenonMarantzTelnetClientThread.class);
37
38     private static final Integer RECONNECT_DELAY = 60000; // 1 minute
39
40     private static final Integer TIMEOUT = 60000; // 1 minute
41
42     private DenonMarantzConfiguration config;
43
44     private DenonMarantzTelnetListener listener;
45
46     private boolean connected = false;
47
48     private Socket socket;
49
50     private OutputStreamWriter out;
51
52     private @Nullable BufferedReader in;
53
54     public DenonMarantzTelnetClientThread(DenonMarantzConfiguration config, DenonMarantzTelnetListener listener) {
55         logger.debug("Denon listener created");
56         this.config = config;
57         this.listener = listener;
58     }
59
60     @Override
61     public void run() {
62         while (!isInterrupted()) {
63             if (!connected) {
64                 connectTelnetSocket();
65             }
66
67             do {
68                 try {
69                     String line = null;
70                     BufferedReader in = this.in;
71                     if (in != null) {
72                         line = in.readLine();
73                     }
74                     if (line == null) {
75                         logger.debug("No more data read from client. Disconnecting..");
76                         listener.telnetClientConnected(false);
77                         disconnect();
78                         break;
79                     }
80                     logger.trace("Received from {}: {}", config.getHost(), line);
81                     if (!line.isBlank()) {
82                         listener.receivedLine(line);
83                     }
84                 } catch (SocketTimeoutException e) {
85                     logger.trace("Socket timeout");
86                     // Disconnects are not always detected unless you write to the socket.
87                     try {
88                         out.write('\r');
89                         out.flush();
90                     } catch (IOException e2) {
91                         logger.debug("Error writing to socket");
92                         connected = false;
93                     }
94                 } catch (IOException e) {
95                     if (!isInterrupted()) {
96                         // only log if we don't stop this on purpose causing a SocketClosed
97                         logger.debug("Error in telnet connection ", e);
98                     }
99                     connected = false;
100                     listener.telnetClientConnected(false);
101                 }
102             } while (!isInterrupted() && connected);
103         }
104         disconnect();
105         logger.debug("Stopped client thread");
106     }
107
108     public void sendCommand(String command) {
109         if (out != null) {
110             try {
111                 out.write(command + '\r');
112                 out.flush();
113             } catch (IOException e) {
114                 logger.debug("Error sending command", e);
115             }
116         } else {
117             logger.debug("Cannot send command, no telnet connection");
118         }
119     }
120
121     public void shutdown() {
122         disconnect();
123     }
124
125     private void connectTelnetSocket() {
126         disconnect();
127         int delay = 0;
128
129         while (!isInterrupted() && (socket == null || !socket.isConnected())) {
130             try {
131                 Thread.sleep(delay);
132                 logger.debug("Connecting to {}", config.getHost());
133
134                 // Use raw socket instead of TelnetClient here because TelnetClient sends an
135                 // extra newline char after each write which causes the connection to become
136                 // unresponsive.
137                 socket = new Socket();
138                 socket.connect(new InetSocketAddress(config.getHost(), config.getTelnetPort()), TIMEOUT);
139                 socket.setKeepAlive(true);
140                 socket.setSoTimeout(TIMEOUT);
141
142                 in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
143                 out = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
144
145                 connected = true;
146                 listener.telnetClientConnected(true);
147                 logger.debug("Denon telnet client connected to {}", config.getHost());
148             } catch (IOException e) {
149                 logger.debug("Cannot connect to {}", config.getHost(), e);
150                 listener.telnetClientConnected(false);
151             } catch (InterruptedException e) {
152                 logger.debug("Interrupted while connecting to {}", config.getHost(), e);
153                 Thread.currentThread().interrupt();
154             }
155             delay = RECONNECT_DELAY;
156         }
157     }
158
159     public boolean isConnected() {
160         return connected;
161     }
162
163     private void disconnect() {
164         if (socket != null) {
165             logger.debug("Disconnecting socket");
166             try {
167                 socket.close();
168             } catch (IOException e) {
169                 logger.debug("Error while disconnecting telnet client", e);
170             } finally {
171                 socket = null;
172                 out = null;
173                 in = null;
174                 listener.telnetClientConnected(false);
175             }
176         }
177     }
178 }