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