2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.nobohub.internal.connection;
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;
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;
40 * Connection to the Nobø Hub (Socket wrapper).
42 * @author Jørgen Austvik - Initial contribution
43 * @author Espen Fossen - Initial contribution
46 public class HubConnection {
48 private final Logger logger = LoggerFactory.getLogger(HubConnection.class);
50 private final String hostName;
51 private final NoboHubBridgeHandler hubHandler;
52 private final String serialNumber;
54 private @Nullable InetAddress host;
55 private @Nullable Socket hubConnection;
56 private @Nullable PrintWriter out;
57 private @Nullable BufferedReader in;
59 public HubConnection(String hostName, String serialNumber, NoboHubBridgeHandler hubHandler)
60 throws NoboCommunicationException {
61 this.hostName = hostName;
62 this.serialNumber = serialNumber;
63 this.hubHandler = hubHandler;
66 public void connect() throws NoboCommunicationException {
69 String hello = String.format("HELLO %s %s %s\r", NoboHubBindingConstants.API_VERSION, serialNumber,
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])));
80 throw new NoboCommunicationException("Hub rejects us with unknown reason");
86 String handshakeRes = readLine();
87 if (null == handshakeRes || !handshakeRes.startsWith("HANDSHAKE")) {
88 throw new NoboCommunicationException("Hub rejects handshake");
91 refreshAllNoReconnect();
94 public void handshake() throws NoboCommunicationException {
102 public void setOverride(Hub hub, OverrideMode nextMode) throws NoboDataException, NoboCommunicationException {
103 if (!isConnected()) {
107 OverridePlan overridePlan = OverridePlan.fromMode(nextMode, LocalDateTime.now());
108 sendCommand(overridePlan.generateCommandString("A03"));
111 while (line != null && !line.startsWith("B03")) {
113 hubHandler.receivedData(line);
118 OverridePlan newOverridePlan = OverridePlan.fromH04(l);
119 hub.setActiveOverrideId(newOverridePlan.getId());
120 sendCommand(hub.generateCommandString("U03"));
124 public void refreshAll() throws NoboCommunicationException {
125 if (!isConnected()) {
128 refreshAllNoReconnect();
132 private void refreshAllNoReconnect() throws NoboCommunicationException {
136 while (line != null && !line.startsWith("H05")) {
138 hubHandler.receivedData(line);
142 public boolean isConnected() {
143 Socket conn = this.hubConnection;
145 return conn.isConnected();
151 public boolean hasData() throws NoboCommunicationException {
152 BufferedReader i = this.in;
156 } catch (IOException ioex) {
157 throw new NoboCommunicationException("Failed detecting if buffer has any data", ioex);
164 public void processReads(Duration timeout) throws NoboCommunicationException {
166 Socket conn = this.hubConnection;
168 throw new NoboCommunicationException("No connection to Hub");
171 logger.trace("Reading from Hub, waiting maximum {}", Helpers.formatDuration(timeout));
172 conn.setSoTimeout((int) timeout.toMillis());
175 String line = readLine();
176 if (line != null && line.startsWith("HANDSHAKE")) {
180 hubHandler.receivedData(line);
181 } catch (NoboCommunicationException nce) {
182 if (!(nce.getCause() instanceof SocketTimeoutException)) {
186 } catch (SocketException se) {
187 throw new NoboCommunicationException("Failed setting read timeout", se);
191 private @Nullable String readLine() throws NoboCommunicationException {
192 BufferedReader reader = this.in;
194 if (null != reader) {
195 String line = reader.readLine();
197 logger.trace("Reading raw data string from Nobø Hub: {}", line);
201 } catch (IOException ioex) {
202 throw new NoboCommunicationException("Failed reading from Nobø Hub", ioex);
208 public void sendCommand(String command) {
212 private void write(String s) {
214 PrintWriter o = this.out;
216 logger.trace("Sending '{}'", s);
222 private void connectSocket() throws NoboCommunicationException {
225 host = InetAddress.getByName(hostName);
226 } catch (IOException ioex) {
227 throw new NoboCommunicationException(String.format("Failed to resolve IP address of %s", hostName),
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);
241 public void disconnect() throws NoboCommunicationException {
243 PrintWriter o = this.out;
248 BufferedReader i = this.in;
253 Socket conn = this.hubConnection;
257 } catch (IOException ioex) {
258 throw new NoboCommunicationException("Error disconnecting from Hub", ioex);
262 public void hardReconnect() throws NoboCommunicationException {
267 private String getDateString() {
268 return LocalDateTime.now().format(NoboHubBindingConstants.DATE_FORMAT_SECONDS);