]> git.basschouten.com Git - openhab-addons.git/blob
48d14cadedc21be0b52b5adb636cb0defe78e881
[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.nobohub.internal.connection;
14
15 import java.io.BufferedReader;
16 import java.io.IOException;
17 import java.io.InputStreamReader;
18 import java.io.PrintWriter;
19 import java.net.InetAddress;
20 import java.net.Socket;
21 import java.net.SocketException;
22 import java.net.SocketTimeoutException;
23 import java.time.Duration;
24 import java.time.LocalDateTime;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.nobohub.internal.Helpers;
29 import org.openhab.binding.nobohub.internal.NoboHubBindingConstants;
30 import org.openhab.binding.nobohub.internal.NoboHubBridgeHandler;
31 import org.openhab.binding.nobohub.internal.model.Hub;
32 import org.openhab.binding.nobohub.internal.model.NoboCommunicationException;
33 import org.openhab.binding.nobohub.internal.model.NoboDataException;
34 import org.openhab.binding.nobohub.internal.model.OverrideMode;
35 import org.openhab.binding.nobohub.internal.model.OverridePlan;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * Connection to the Nobø Hub (Socket wrapper).
41  *
42  * @author Jørgen Austvik - Initial contribution
43  * @author Espen Fossen - Initial contribution
44  */
45 @NonNullByDefault
46 public class HubConnection {
47
48     private final Logger logger = LoggerFactory.getLogger(HubConnection.class);
49
50     private final String hostName;
51     private final NoboHubBridgeHandler hubHandler;
52     private final String serialNumber;
53
54     private @Nullable InetAddress host;
55     private @Nullable Socket hubConnection;
56     private @Nullable PrintWriter out;
57     private @Nullable BufferedReader in;
58
59     public HubConnection(String hostName, String serialNumber, NoboHubBridgeHandler hubHandler)
60             throws NoboCommunicationException {
61         this.hostName = hostName;
62         this.serialNumber = serialNumber;
63         this.hubHandler = hubHandler;
64     }
65
66     public void connect() throws NoboCommunicationException {
67         connectSocket();
68
69         String hello = String.format("HELLO %s %s %s\r", NoboHubBindingConstants.API_VERSION, serialNumber,
70                 getDateString());
71         write(hello);
72
73         String helloRes = readLine();
74         if (null == helloRes || !helloRes.startsWith("HELLO")) {
75             if (helloRes != null && helloRes.startsWith("REJECT")) {
76                 String[] reject = helloRes.split(" ", 2);
77                 throw new NoboCommunicationException(String.format("Hub rejects us with reason %s: %s", reject[1],
78                         NoboHubBindingConstants.REJECT_REASONS.get(reject[1])));
79             } else {
80                 throw new NoboCommunicationException("Hub rejects us with unknown reason");
81             }
82         }
83
84         write("HANDSHAKE\r");
85
86         String handshakeRes = readLine();
87         if (null == handshakeRes || !handshakeRes.startsWith("HANDSHAKE")) {
88             throw new NoboCommunicationException("Hub rejects handshake");
89         }
90
91         refreshAllNoReconnect();
92     }
93
94     public void handshake() throws NoboCommunicationException {
95         if (!isConnected()) {
96             connect();
97         } else {
98             write("HANDSHAKE\r");
99         }
100     }
101
102     public void setOverride(Hub hub, OverrideMode nextMode) throws NoboDataException, NoboCommunicationException {
103         if (!isConnected()) {
104             connect();
105         }
106
107         OverridePlan overridePlan = OverridePlan.fromMode(nextMode, LocalDateTime.now());
108         sendCommand(overridePlan.generateCommandString("A03"));
109
110         String line = "";
111         while (line != null && !line.startsWith("B03")) {
112             line = readLine();
113             hubHandler.receivedData(line);
114         }
115
116         String l = line;
117         if (null != l) {
118             OverridePlan newOverridePlan = OverridePlan.fromH04(l);
119             hub.setActiveOverrideId(newOverridePlan.getId());
120             sendCommand(hub.generateCommandString("U03"));
121         }
122     }
123
124     public void refreshAll() throws NoboCommunicationException {
125         if (!isConnected()) {
126             connect();
127         } else {
128             refreshAllNoReconnect();
129         }
130     }
131
132     private void refreshAllNoReconnect() throws NoboCommunicationException {
133         write("G00\r");
134
135         String line = "";
136         while (line != null && !line.startsWith("H05")) {
137             line = readLine();
138             hubHandler.receivedData(line);
139         }
140     }
141
142     public boolean isConnected() {
143         Socket conn = this.hubConnection;
144         if (null != conn) {
145             return conn.isConnected();
146         }
147
148         return false;
149     }
150
151     public boolean hasData() throws NoboCommunicationException {
152         BufferedReader i = this.in;
153         if (null != i) {
154             try {
155                 return i.ready();
156             } catch (IOException ioex) {
157                 throw new NoboCommunicationException("Failed detecting if buffer has any data", ioex);
158             }
159         }
160
161         return false;
162     }
163
164     public void processReads(Duration timeout) throws NoboCommunicationException {
165         try {
166             Socket conn = this.hubConnection;
167             if (null == conn) {
168                 throw new NoboCommunicationException("No connection to Hub");
169             }
170
171             logger.trace("Reading from Hub, waiting maximum {}", Helpers.formatDuration(timeout));
172             conn.setSoTimeout((int) timeout.toMillis());
173
174             try {
175                 String line = readLine();
176                 if (line != null && line.startsWith("HANDSHAKE")) {
177                     line = readLine();
178                 }
179
180                 hubHandler.receivedData(line);
181             } catch (NoboCommunicationException nce) {
182                 if (!(nce.getCause() instanceof SocketTimeoutException)) {
183                     connectSocket();
184                 }
185             }
186         } catch (SocketException se) {
187             throw new NoboCommunicationException("Failed setting read timeout", se);
188         }
189     }
190
191     private @Nullable String readLine() throws NoboCommunicationException {
192         BufferedReader reader = this.in;
193         try {
194             if (null != reader) {
195                 String line = reader.readLine();
196                 if (line != null) {
197                     logger.trace("Reading raw data string from Nobø Hub: {}", line);
198                 }
199                 return line;
200             }
201         } catch (IOException ioex) {
202             throw new NoboCommunicationException("Failed reading from Nobø Hub", ioex);
203         }
204
205         return null;
206     }
207
208     public void sendCommand(String command) {
209         write(command);
210     }
211
212     private void write(String s) {
213         @Nullable
214         PrintWriter o = this.out;
215         if (null != o) {
216             logger.trace("Sending '{}'", s);
217             o.write(s);
218             o.flush();
219         }
220     }
221
222     private void connectSocket() throws NoboCommunicationException {
223         if (null == host) {
224             try {
225                 host = InetAddress.getByName(hostName);
226             } catch (IOException ioex) {
227                 throw new NoboCommunicationException(String.format("Failed to resolve IP address of %s", hostName),
228                         ioex);
229             }
230         }
231         try {
232             Socket conn = new Socket(host, NoboHubBindingConstants.NOBO_HUB_TCP_PORT);
233             out = new PrintWriter(conn.getOutputStream(), true);
234             in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
235             hubConnection = conn;
236         } catch (IOException ioex) {
237             throw new NoboCommunicationException(String.format("Failed connecting to Nobø Hub at %s", hostName), ioex);
238         }
239     }
240
241     public void disconnect() throws NoboCommunicationException {
242         try {
243             PrintWriter o = this.out;
244             if (o != null) {
245                 o.close();
246             }
247
248             BufferedReader i = this.in;
249             if (i != null) {
250                 i.close();
251             }
252
253             Socket conn = this.hubConnection;
254             if (conn != null) {
255                 conn.close();
256             }
257         } catch (IOException ioex) {
258             throw new NoboCommunicationException("Error disconnecting from Hub", ioex);
259         }
260     }
261
262     public void hardReconnect() throws NoboCommunicationException {
263         disconnect();
264         connect();
265     }
266
267     private String getDateString() {
268         return LocalDateTime.now().format(NoboHubBindingConstants.DATE_FORMAT_SECONDS);
269     }
270 }