]> git.basschouten.com Git - openhab-addons.git/blob
565c2714ed19b084feeef97fdfb7b3c9d0e300c3
[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.danfossairunit.internal;
14
15 import static org.openhab.binding.danfossairunit.internal.Commands.*;
16
17 import java.io.IOException;
18 import java.math.BigDecimal;
19 import java.math.RoundingMode;
20 import java.nio.charset.StandardCharsets;
21 import java.time.DateTimeException;
22 import java.time.ZoneId;
23 import java.time.ZonedDateTime;
24
25 import javax.measure.quantity.Dimensionless;
26 import javax.measure.quantity.Temperature;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.openhab.core.library.types.DateTimeType;
30 import org.openhab.core.library.types.DecimalType;
31 import org.openhab.core.library.types.OnOffType;
32 import org.openhab.core.library.types.PercentType;
33 import org.openhab.core.library.types.QuantityType;
34 import org.openhab.core.library.types.StringType;
35 import org.openhab.core.library.unit.SIUnits;
36 import org.openhab.core.library.unit.Units;
37 import org.openhab.core.types.Command;
38
39 /**
40  * The {@link DanfossAirUnit} class represents the air unit device and build the commands to be sent by
41  * {@link DanfossAirUnitCommunicationController}
42  * 
43  * @author Ralf Duckstein - Initial contribution
44  * @author Robert Bach - heavy refactorings
45  * @author Jacob Laursen - Refactoring, bugfixes and enhancements
46  */
47
48 @NonNullByDefault
49 public class DanfossAirUnit {
50
51     private final CommunicationController communicationController;
52
53     public DanfossAirUnit(CommunicationController communicationController) {
54         this.communicationController = communicationController;
55     }
56
57     private boolean getBoolean(byte[] operation, byte[] register) throws IOException {
58         return communicationController.sendRobustRequest(operation, register)[0] != 0;
59     }
60
61     private short getWord(byte[] operation, byte[] register) throws IOException {
62         byte[] resultBytes = communicationController.sendRobustRequest(operation, register);
63         return (short) ((resultBytes[0] << 8) | (resultBytes[1] & 0xFF));
64     }
65
66     private byte getByte(byte[] operation, byte[] register) throws IOException {
67         return communicationController.sendRobustRequest(operation, register)[0];
68     }
69
70     private String getString(byte[] operation, byte[] register) throws IOException {
71         // length of the string is stored in the first byte
72         byte[] result = communicationController.sendRobustRequest(operation, register);
73         return new String(result, 1, result[0], StandardCharsets.US_ASCII);
74     }
75
76     private void set(byte[] operation, byte[] register, byte value) throws IOException {
77         byte[] valueArray = { value };
78         communicationController.sendRobustRequest(operation, register, valueArray);
79     }
80
81     private short getShort(byte[] operation, byte[] register) throws IOException {
82         byte[] result = communicationController.sendRobustRequest(operation, register);
83         return (short) ((result[0] << 8) + (result[1] & 0xff));
84     }
85
86     private float getTemperature(byte[] operation, byte[] register)
87             throws IOException, UnexpectedResponseValueException {
88         short shortTemp = getShort(operation, register);
89         float temp = ((float) shortTemp) / 100;
90         if (temp <= -274 || temp > 100) {
91             throw new UnexpectedResponseValueException(String.format("Invalid temperature: %s", temp));
92         }
93         return temp;
94     }
95
96     private ZonedDateTime getTimestamp(byte[] operation, byte[] register)
97             throws IOException, UnexpectedResponseValueException {
98         byte[] result = communicationController.sendRobustRequest(operation, register);
99         return asZonedDateTime(result);
100     }
101
102     private ZonedDateTime asZonedDateTime(byte[] data) throws UnexpectedResponseValueException {
103         int second = data[0];
104         int minute = data[1];
105         int hour = data[2] & 0x1f;
106         int day = data[3] & 0x1f;
107         int month = data[4];
108         int year = data[5] + 2000;
109         try {
110             return ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.systemDefault());
111         } catch (DateTimeException e) {
112             String msg = String.format("Ignoring invalid timestamp %s.%s.%s %s:%s:%s", day, month, year, hour, minute,
113                     second);
114             throw new UnexpectedResponseValueException(msg, e);
115         }
116     }
117
118     private static int asUnsignedByte(byte b) {
119         return b & 0xFF;
120     }
121
122     private static float asPercentByte(byte b) {
123         float f = asUnsignedByte(b);
124         return f * 100 / 255;
125     }
126
127     public String getUnitName() throws IOException {
128         return getString(REGISTER_1_READ, UNIT_NAME);
129     }
130
131     public String getUnitSerialNumber() throws IOException {
132         return String.valueOf(getShort(REGISTER_4_READ, UNIT_SERIAL));
133     }
134
135     public StringType getMode() throws IOException {
136         return new StringType(Mode.values()[getByte(REGISTER_1_READ, MODE)].name());
137     }
138
139     public PercentType getManualFanStep() throws IOException, UnexpectedResponseValueException {
140         byte value = getByte(REGISTER_1_READ, MANUAL_FAN_SPEED_STEP);
141         if (value < 0 || value > 10) {
142             throw new UnexpectedResponseValueException(String.format("Invalid fan step: %d", value));
143         }
144         return new PercentType(BigDecimal.valueOf(value * 10));
145     }
146
147     public DecimalType getSupplyFanSpeed() throws IOException {
148         return new DecimalType(BigDecimal.valueOf(getWord(REGISTER_4_READ, SUPPLY_FAN_SPEED)));
149     }
150
151     public DecimalType getExtractFanSpeed() throws IOException {
152         return new DecimalType(BigDecimal.valueOf(getWord(REGISTER_4_READ, EXTRACT_FAN_SPEED)));
153     }
154
155     public PercentType getSupplyFanStep() throws IOException {
156         return new PercentType(BigDecimal.valueOf(getByte(REGISTER_4_READ, SUPPLY_FAN_STEP)));
157     }
158
159     public PercentType getExtractFanStep() throws IOException {
160         return new PercentType(BigDecimal.valueOf(getByte(REGISTER_4_READ, EXTRACT_FAN_STEP)));
161     }
162
163     public OnOffType getBoost() throws IOException {
164         return getBoolean(REGISTER_1_READ, BOOST) ? OnOffType.ON : OnOffType.OFF;
165     }
166
167     public OnOffType getNightCooling() throws IOException {
168         return getBoolean(REGISTER_1_READ, NIGHT_COOLING) ? OnOffType.ON : OnOffType.OFF;
169     }
170
171     public OnOffType getBypass() throws IOException {
172         return getBoolean(REGISTER_1_READ, BYPASS) ? OnOffType.ON : OnOffType.OFF;
173     }
174
175     public QuantityType<Dimensionless> getHumidity() throws IOException {
176         BigDecimal value = BigDecimal.valueOf(asPercentByte(getByte(REGISTER_1_READ, HUMIDITY)));
177         return new QuantityType<>(value.setScale(1, RoundingMode.HALF_UP), Units.PERCENT);
178     }
179
180     public QuantityType<Temperature> getRoomTemperature() throws IOException, UnexpectedResponseValueException {
181         return getTemperatureAsDecimalType(REGISTER_1_READ, ROOM_TEMPERATURE);
182     }
183
184     public QuantityType<Temperature> getRoomTemperatureCalculated()
185             throws IOException, UnexpectedResponseValueException {
186         return getTemperatureAsDecimalType(REGISTER_0_READ, ROOM_TEMPERATURE_CALCULATED);
187     }
188
189     public QuantityType<Temperature> getOutdoorTemperature() throws IOException, UnexpectedResponseValueException {
190         return getTemperatureAsDecimalType(REGISTER_1_READ, OUTDOOR_TEMPERATURE);
191     }
192
193     public QuantityType<Temperature> getSupplyTemperature() throws IOException, UnexpectedResponseValueException {
194         return getTemperatureAsDecimalType(REGISTER_4_READ, SUPPLY_TEMPERATURE);
195     }
196
197     public QuantityType<Temperature> getExtractTemperature() throws IOException, UnexpectedResponseValueException {
198         return getTemperatureAsDecimalType(REGISTER_4_READ, EXTRACT_TEMPERATURE);
199     }
200
201     public QuantityType<Temperature> getExhaustTemperature() throws IOException, UnexpectedResponseValueException {
202         return getTemperatureAsDecimalType(REGISTER_4_READ, EXHAUST_TEMPERATURE);
203     }
204
205     private QuantityType<Temperature> getTemperatureAsDecimalType(byte[] operation, byte[] register)
206             throws IOException, UnexpectedResponseValueException {
207         BigDecimal value = BigDecimal.valueOf(getTemperature(operation, register));
208         return new QuantityType<>(value.setScale(1, RoundingMode.HALF_UP), SIUnits.CELSIUS);
209     }
210
211     public DecimalType getBatteryLife() throws IOException {
212         return new DecimalType(BigDecimal.valueOf(asUnsignedByte(getByte(REGISTER_1_READ, BATTERY_LIFE))));
213     }
214
215     public DecimalType getFilterLife() throws IOException {
216         BigDecimal value = BigDecimal.valueOf(asPercentByte(getByte(REGISTER_1_READ, FILTER_LIFE)));
217         return new DecimalType(value.setScale(1, RoundingMode.HALF_UP));
218     }
219
220     public DecimalType getFilterPeriod() throws IOException {
221         return new DecimalType(BigDecimal.valueOf(getByte(REGISTER_1_READ, FILTER_PERIOD)));
222     }
223
224     public DecimalType setFilterPeriod(Command cmd) throws IOException {
225         return setNumberTypeRegister(cmd, FILTER_PERIOD);
226     }
227
228     public DateTimeType getCurrentTime() throws IOException, UnexpectedResponseValueException {
229         ZonedDateTime timestamp = getTimestamp(REGISTER_1_READ, CURRENT_TIME);
230         return new DateTimeType(timestamp);
231     }
232
233     public PercentType setManualFanStep(Command cmd) throws IOException {
234         return setPercentTypeRegister(cmd, MANUAL_FAN_SPEED_STEP);
235     }
236
237     private DecimalType setNumberTypeRegister(Command cmd, byte[] register) throws IOException {
238         if (cmd instanceof DecimalType) {
239             byte value = (byte) ((DecimalType) cmd).intValue();
240             set(REGISTER_1_WRITE, register, value);
241         }
242         return new DecimalType(BigDecimal.valueOf(getByte(REGISTER_1_READ, register)));
243     }
244
245     private PercentType setPercentTypeRegister(Command cmd, byte[] register) throws IOException {
246         if (cmd instanceof PercentType) {
247             byte value = (byte) ((((PercentType) cmd).intValue() + 5) / 10);
248             set(REGISTER_1_WRITE, register, value);
249         }
250         return new PercentType(BigDecimal.valueOf(getByte(REGISTER_1_READ, register) * 10));
251     }
252
253     private OnOffType setOnOffTypeRegister(Command cmd, byte[] register) throws IOException {
254         if (cmd instanceof OnOffType) {
255             set(REGISTER_1_WRITE, register, OnOffType.ON.equals(cmd) ? (byte) 1 : (byte) 0);
256         }
257         return getBoolean(REGISTER_1_READ, register) ? OnOffType.ON : OnOffType.OFF;
258     }
259
260     private StringType setStringTypeRegister(Command cmd, byte[] register) throws IOException {
261         if (cmd instanceof StringType) {
262             byte value = (byte) (Mode.valueOf(cmd.toString()).ordinal());
263             set(REGISTER_1_WRITE, register, value);
264         }
265
266         return new StringType(Mode.values()[getByte(REGISTER_1_READ, register)].name());
267     }
268
269     public StringType setMode(Command cmd) throws IOException {
270         return setStringTypeRegister(cmd, MODE);
271     }
272
273     public OnOffType setBoost(Command cmd) throws IOException {
274         return setOnOffTypeRegister(cmd, BOOST);
275     }
276
277     public OnOffType setNightCooling(Command cmd) throws IOException {
278         return setOnOffTypeRegister(cmd, NIGHT_COOLING);
279     }
280
281     public OnOffType setBypass(Command cmd) throws IOException {
282         return setOnOffTypeRegister(cmd, BYPASS);
283     }
284 }