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.luxtronikheatpump.internal;
15 import java.io.DataInputStream;
16 import java.io.DataOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.OutputStream;
20 import java.net.Socket;
21 import java.net.UnknownHostException;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
28 * The {@link HeatpumpConnector} reads / writes internal states of a Heat pump with Luxtronik control.
30 * Based on HeatpumpConnector class of novelanheatpump binding
32 * @author Stefan Giehl - Initial contribution
35 public class HeatpumpConnector {
37 private static final int SOCKET_PARAM_WRITE_PARAMS = 3002;
38 private static final int SOCKET_PARAM_READ_PARAMS = 3003;
39 private static final int SOCKET_PARAM_READ_VALUES = 3004;
40 private static final int SOCKET_PARAM_READ_VISIBILITIES = 3005;
42 private final Logger logger = LoggerFactory.getLogger(HeatpumpConnector.class);
44 private String serverIp;
45 private int serverPort;
46 private Integer[] heatpumpValues = new Integer[0];
47 private Integer[] heatpumpParams = new Integer[0];
48 private Integer[] heatpumpVisibilities = new Integer[0];
50 public HeatpumpConnector(String serverIp, int serverPort) {
51 this.serverIp = serverIp;
52 this.serverPort = serverPort;
56 * reads all values from the heatpump via network
58 * @throws UnknownHostException indicate that the IP address of a host could not be determined.
59 * @throws IOException indicate that no data can be read from the heat pump
61 public void read() throws UnknownHostException, IOException {
62 try (Socket sock = new Socket(serverIp, serverPort)) {
63 InputStream in = sock.getInputStream();
64 OutputStream out = sock.getOutputStream();
65 DataInputStream datain = new DataInputStream(in);
66 DataOutputStream dataout = new DataOutputStream(out);
68 heatpumpValues = readInt(datain, dataout, SOCKET_PARAM_READ_VALUES);
70 // workaround for thermal energies
71 // the thermal energies can be unreasonably high in some cases, probably due to a sign bug in the firmware
72 // trying to correct this issue here
73 for (int i = 151; i <= 154; i++) {
74 if (heatpumpValues.length > i && heatpumpValues[i] >= 214748364) {
75 heatpumpValues[i] -= 214748364;
79 heatpumpParams = readInt(datain, dataout, SOCKET_PARAM_READ_PARAMS);
80 heatpumpVisibilities = readInt(datain, dataout, SOCKET_PARAM_READ_VISIBILITIES);
88 * read the parameters of the heat pump
90 public Integer[] getParams() {
91 return heatpumpParams;
95 * set a parameter of the heat pump
99 * @throws IOException indicate that no data can be sent to the heat pump
101 public boolean setParam(int param, int value) throws IOException {
102 try (Socket sock = new Socket(serverIp, serverPort)) {
103 InputStream in = sock.getInputStream();
104 OutputStream out = sock.getOutputStream();
105 DataInputStream datain = new DataInputStream(in);
106 DataOutputStream dataout = new DataOutputStream(out);
108 while (datain.available() > 0) {
112 // write command, param and value to heatpump socket
113 dataout.writeInt(SOCKET_PARAM_WRITE_PARAMS);
114 dataout.writeInt(param);
115 dataout.writeInt(value);
118 // first integer on socket output should represent the command
119 int cmd = datain.readInt();
125 if (cmd != SOCKET_PARAM_WRITE_PARAMS) {
126 logger.debug("Couldn't write parameter {} with value {} to heat pump.", param, value);
129 logger.debug("Parameter {} with value {} successfully written to heat pump.", param, value);
136 * Returns the internal states of the heat pump
138 * @return an array with all internal data of the heat pump
140 public Integer[] getValues() {
141 return heatpumpValues;
145 * Returns the internal visibilities of the heat pump
147 * @return an array with all internal visibilities of the heat pump
149 public Integer[] getVisibilities() {
150 return heatpumpVisibilities;
154 * Reads all available values for the given parameter from socket
156 * @param datain data input stream of socket connection
157 * @param dataout data output stream of socket connection
158 * @param parameter int command to read from socket
159 * @return an array with all values returned from heat pump socket
160 * @throws IOException indicate that no data can be read from the heat pump
162 private Integer[] readInt(DataInputStream datain, DataOutputStream dataout, int parameter) throws IOException {
163 Integer[] result = null;
164 while (datain.available() > 0) {
168 // to receive values we first need to write the command followed by four 0 byte values to the socket
169 dataout.writeInt(parameter);
173 // the first integer received from socket should match the written command
174 if (datain.readInt() != parameter) {
175 return new Integer[0];
178 if (parameter == SOCKET_PARAM_READ_VALUES) {
179 // when reading values the next integer represents some kind of status
183 // the next integer value should define the number of values that are following
184 // Currently the list or parameters is the longest list and contains around 1050 values
185 // To avoid possible (memory) problems in case the returned number would be unexpected high we limit it to 2000
186 int arraylength = Integer.min(datain.readInt(), 2000);
188 logger.debug("Found {} values for {}", arraylength, parameter);
190 result = new Integer[arraylength];
192 // Note: the visibility params are returned as single byte values
193 // probably as the are used as boolean values (0/1) only
194 if (parameter == SOCKET_PARAM_READ_VISIBILITIES) {
195 byte[] data = datain.readNBytes(arraylength);
196 for (int i = 0; i < data.length; i++) {
197 result[i] = (int) data[i];
202 for (int i = 0; i < arraylength; i++) {
203 result[i] = datain.readInt();