]> git.basschouten.com Git - openhab-addons.git/blob
356c057e7d9698ce0ec8e46449040ef60f1e155d
[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.tplinkrouter.internal;
14
15 import java.io.BufferedReader;
16 import java.io.IOException;
17 import java.io.InputStreamReader;
18 import java.io.InterruptedIOException;
19 import java.io.OutputStreamWriter;
20 import java.net.InetSocketAddress;
21 import java.net.Socket;
22 import java.net.SocketTimeoutException;
23 import java.util.concurrent.TimeUnit;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * The {@link TpLinkRouterTelnetConnector} is responsible for the telnet connection.
32  *
33  * @author Olivier Marceau - Initial contribution
34  */
35 @NonNullByDefault
36 public class TpLinkRouterTelnetConnector {
37
38     private static final int TIMEOUT_MS = (int) TimeUnit.MINUTES.toMillis(1);
39
40     private final Logger logger = LoggerFactory.getLogger(TpLinkRouterTelnetConnector.class);
41
42     private @Nullable Thread telnetClientThread;
43     private @Nullable Socket socket; // use raw socket since commons net usage seems discouraged
44     private @Nullable OutputStreamWriter out;
45
46     public void connect(TpLinkRouterTelenetListener listener, TpLinkRouterConfiguration config, String thingUID)
47             throws IOException {
48         logger.debug("Connecting to {}", config.hostname);
49
50         Socket socketLocal = new Socket();
51         socketLocal.connect(new InetSocketAddress(config.hostname, config.port));
52         socketLocal.setSoTimeout(TIMEOUT_MS);
53         socketLocal.setKeepAlive(true);
54
55         InputStreamReader inputStream = new InputStreamReader(socketLocal.getInputStream());
56         this.out = new OutputStreamWriter(socketLocal.getOutputStream());
57         this.socket = socketLocal;
58         loginAttempt(listener, inputStream, config);
59         Thread clientThread = new Thread(() -> listenInputStream(listener, inputStream, config));
60         clientThread.setName("OH-binding-" + thingUID);
61         this.telnetClientThread = clientThread;
62         clientThread.start();
63         logger.debug("TP-Link router telnet client connected to {}", config.hostname);
64     }
65
66     public void dispose() {
67         logger.debug("disposing connector");
68         Thread clientThread = telnetClientThread;
69         if (clientThread != null) {
70             clientThread.interrupt();
71             telnetClientThread = null;
72         }
73         Socket socketLocal = socket;
74         if (socketLocal != null) {
75             try {
76                 socketLocal.close();
77             } catch (IOException e) {
78                 logger.debug("Error while disconnecting telnet client", e);
79             }
80             socket = null;
81         }
82     }
83
84     public void sendCommand(String command) {
85         logger.debug("sending command: {}", command);
86         OutputStreamWriter output = out;
87         if (output != null) {
88             try {
89                 output.write(command + '\n');
90                 output.flush();
91             } catch (IOException e) {
92                 logger.warn("Error sending command", e);
93             }
94         } else {
95             logger.debug("Cannot send command, no telnet connection");
96         }
97     }
98
99     private void listenInputStream(TpLinkRouterTelenetListener listener, InputStreamReader inputStream,
100             TpLinkRouterConfiguration config) {
101         listener.onReaderThreadStarted();
102         BufferedReader in = new BufferedReader(inputStream);
103         try {
104             while (!Thread.currentThread().isInterrupted()) {
105                 try {
106                     String line = in.readLine();
107                     if (line != null && !line.isBlank()) {
108                         listener.receivedLine(line);
109                         if ("CLI exited after timing out".equals(line)) {
110                             OutputStreamWriter output = out;
111                             if (output != null) {
112                                 output.write("\n"); // trigger a "username:" prompt
113                                 output.flush();
114                                 loginAttempt(listener, inputStream, config);
115                             }
116                         }
117                     }
118                 } catch (SocketTimeoutException e) {
119                     logger.trace("Socket timeout");
120                 }
121             }
122         } catch (InterruptedIOException e) {
123             logger.debug("Error in telnet connection ", e);
124         } catch (IOException e) {
125             if (!Thread.currentThread().isInterrupted()) {
126                 logger.debug("Error in telnet connection ", e);
127                 listener.onReaderThreadStopped();
128             }
129         }
130         if (Thread.currentThread().isInterrupted()) {
131             logger.debug("Interrupted client thread");
132             listener.onReaderThreadInterrupted();
133         }
134     }
135
136     private void loginAttempt(TpLinkRouterTelenetListener listener, InputStreamReader inputStreamReader,
137             TpLinkRouterConfiguration config) throws IOException {
138         int charInt;
139         StringBuilder word = new StringBuilder();
140         OutputStreamWriter output = out;
141         if (output != null) {
142             try {
143                 while ((charInt = inputStreamReader.read()) != -1) {
144                     word.append((char) charInt);
145                     logger.trace("received char: {}", (char) charInt);
146                     if (word.toString().contains("username:")) {
147                         logger.debug("Sending username");
148                         output.write(config.username + '\n');
149                         output.flush();
150                         word = new StringBuilder();
151                     }
152                     if (word.toString().contains("password:")) {
153                         logger.debug("Sending password");
154                         output.write(config.password + '\n');
155                         output.flush();
156                         break;
157                     }
158                 }
159             } catch (SocketTimeoutException e) {
160                 listener.onCommunicationUnavailable();
161             }
162         }
163     }
164 }