]> git.basschouten.com Git - openhab-addons.git/blob
9cf4031efb9eacf4224c648a74ec901a98a2d3c0
[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.rfxcom.internal.messages;
14
15 import static java.math.RoundingMode.*;
16 import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.*;
17 import static org.openhab.binding.rfxcom.internal.messages.ByteEnumUtil.fromByte;
18
19 import java.math.BigDecimal;
20
21 import org.openhab.binding.rfxcom.internal.config.RFXComDeviceConfiguration;
22 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
23 import org.openhab.binding.rfxcom.internal.exceptions.RFXComInvalidStateException;
24 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException;
25 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException;
26 import org.openhab.binding.rfxcom.internal.handler.DeviceState;
27 import org.openhab.core.library.types.DecimalType;
28 import org.openhab.core.types.State;
29 import org.openhab.core.types.Type;
30 import org.openhab.core.types.UnDefType;
31 import org.openhab.core.util.HexUtils;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * Add support for the rfx-sensor
37  *
38  * @author Martin van Wingerden - Initial contribution
39  */
40 public class RFXComRFXSensorMessage extends RFXComDeviceMessageImpl<RFXComRFXSensorMessage.SubType> {
41     private final Logger logger = LoggerFactory.getLogger(RFXComRFXSensorMessage.class);
42
43     private static final BigDecimal PRESSURE_ADDITION = new BigDecimal("0.095");
44     private static final BigDecimal PRESSURE_DIVIDER = new BigDecimal("0.0009");
45
46     private static final BigDecimal HUMIDITY_VOLTAGE_SUBTRACTION = new BigDecimal("0.16");
47     private static final BigDecimal HUMIDITY_VOLTAGE_DIVIDER = new BigDecimal("0.0062");
48     private static final BigDecimal HUMIDITY_TEMPERATURE_CORRECTION = new BigDecimal("1.0546");
49     private static final BigDecimal HUMIDITY_TEMPERATURE_MULTIPLIER = new BigDecimal("0.00216");
50
51     private static final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100);
52
53     public enum SubType implements ByteEnumWrapper {
54         TEMPERATURE(0),
55         A_D(1),
56         VOLTAGE(2),
57         MESSAGE(3);
58
59         private final int subType;
60
61         SubType(int subType) {
62             this.subType = subType;
63         }
64
65         @Override
66         public byte toByte() {
67             return (byte) subType;
68         }
69     }
70
71     public SubType subType;
72
73     public int sensorId;
74     private Double temperature;
75     private BigDecimal miliVoltageTimesTen;
76     public byte signalLevel;
77
78     public RFXComRFXSensorMessage() {
79         super(PacketType.RFXSENSOR);
80     }
81
82     public RFXComRFXSensorMessage(byte[] data) throws RFXComException {
83         encodeMessage(data);
84     }
85
86     @Override
87     public String toString() {
88         String str = super.toString();
89
90         str += ", Sub type = " + subType;
91         str += ", Device Id = " + getDeviceId();
92         str += ", Temperature = " + temperature;
93         str += ", Voltage = " + getVoltage();
94         str += ", Signal level = " + signalLevel;
95
96         return str;
97     }
98
99     @Override
100     public void encodeMessage(byte[] data) throws RFXComException {
101         super.encodeMessage(data);
102
103         subType = fromByte(SubType.class, super.subType);
104
105         sensorId = (data[4] & 0xFF);
106
107         byte msg1 = data[5];
108         byte msg2 = data[6];
109
110         switch (subType) {
111             case TEMPERATURE:
112                 encodeTemperatureMessage(msg1, msg2);
113                 break;
114             case A_D:
115             case VOLTAGE:
116                 encodeVoltageMessage(msg1, msg2);
117                 break;
118             case MESSAGE:
119                 encodeStatusMessage(msg2);
120                 break;
121         }
122
123         signalLevel = (byte) ((data[7] & 0xF0) >> 4);
124     }
125
126     private void encodeTemperatureMessage(byte msg1, byte msg2) {
127         temperature = (short) ((msg1 & 0x7F) << 8 | (msg2 & 0xFF)) * 0.01;
128         if ((msg1 & 0x80) != 0) {
129             temperature = -temperature;
130         }
131     }
132
133     private void encodeVoltageMessage(byte msg1, byte msg2) {
134         miliVoltageTimesTen = BigDecimal.valueOf((short) ((msg1 & 0xFF) << 8 | (msg2 & 0xFF)));
135     }
136
137     private void encodeStatusMessage(byte msg2) {
138         // noop
139     }
140
141     @Override
142     public byte[] decodeMessage() {
143         byte[] data = new byte[8];
144
145         data[0] = 0x07;
146         data[1] = PacketType.RFXSENSOR.toByte();
147         data[2] = subType.toByte();
148         data[3] = seqNbr;
149         data[4] = (byte) (sensorId & 0x00FF);
150
151         if (subType == SubType.TEMPERATURE) {
152             decodeTemperatureMessage(data);
153         } else if (subType == SubType.A_D) {
154             decodeVoltageMessage(data);
155         } else if (subType == SubType.VOLTAGE) {
156             decodeVoltageMessage(data);
157         } else if (subType == SubType.MESSAGE) {
158             decodeStatusMessage(data);
159         }
160
161         data[7] = (byte) ((signalLevel & 0x0F) << 4);
162
163         return data;
164     }
165
166     @Override
167     public void convertFromState(String channelId, Type type) {
168         throw new UnsupportedOperationException();
169     }
170
171     private void decodeTemperatureMessage(byte[] data) {
172         short temp = (short) Math.abs(temperature * 100);
173         data[5] = (byte) ((temp >> 8) & 0xFF);
174         data[6] = (byte) (temp & 0xFF);
175         if (temperature < 0) {
176             data[5] |= 0x80;
177         }
178     }
179
180     private void decodeVoltageMessage(byte[] data) {
181         short miliVoltageTimesTenShort = this.miliVoltageTimesTen.shortValueExact();
182         data[5] = (byte) ((miliVoltageTimesTenShort >> 8) & 0xFF);
183         data[6] = (byte) (miliVoltageTimesTenShort & 0xFF);
184     }
185
186     private void decodeStatusMessage(byte[] data) {
187         logger.info("A status message was received {}", HexUtils.bytesToHex(data));
188     }
189
190     @Override
191     public String getDeviceId() {
192         return String.valueOf(sensorId);
193     }
194
195     @Override
196     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
197         return ByteEnumUtil.convertSubType(SubType.class, subType);
198     }
199
200     @Override
201     public void setSubType(SubType subType) {
202         throw new UnsupportedOperationException();
203     }
204
205     @Override
206     public State convertToState(String channelId, RFXComDeviceConfiguration config, DeviceState deviceState)
207             throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
208         switch (channelId) {
209             case CHANNEL_TEMPERATURE:
210                 return subType == SubType.TEMPERATURE ? getTemperature() : null;
211
212             case CHANNEL_VOLTAGE:
213                 return subType == SubType.A_D ? handleVoltage() : null;
214
215             case CHANNEL_REFERENCE_VOLTAGE:
216                 return subType == SubType.VOLTAGE ? handleVoltage() : null;
217
218             case CHANNEL_HUMIDITY:
219                 return subType == SubType.A_D ? handleHumidity(deviceState) : null;
220
221             case CHANNEL_PRESSURE:
222                 return subType == SubType.A_D ? handlePressure(deviceState) : null;
223
224             default:
225                 return super.convertToState(channelId, config, deviceState);
226         }
227     }
228
229     private State getTemperature() {
230         if (temperature != null) {
231             return new DecimalType(temperature);
232         } else {
233             return UnDefType.UNDEF;
234         }
235     }
236
237     private State handleVoltage() {
238         if (miliVoltageTimesTen != null) {
239             return new DecimalType(getVoltage());
240         } else {
241             return UnDefType.UNDEF;
242         }
243     }
244
245     private State handleHumidity(DeviceState deviceState) {
246         DecimalType temperatureState = (DecimalType) deviceState.getLastState(CHANNEL_TEMPERATURE);
247         Type referenceVoltageState = deviceState.getLastState(CHANNEL_REFERENCE_VOLTAGE);
248         BigDecimal adVoltage = getVoltage();
249
250         if (temperatureState == null || referenceVoltageState == null || adVoltage == null) {
251             return null;
252         }
253
254         if (!(referenceVoltageState instanceof DecimalType)) {
255             return UnDefType.UNDEF;
256         }
257
258         BigDecimal temperature = temperatureState.toBigDecimal();
259         BigDecimal supplyVoltage = ((DecimalType) referenceVoltageState).toBigDecimal();
260
261         // RH = (((A/D voltage / supply voltage) - 0.16) / 0.0062) / (1.0546 - 0.00216 * temperature)
262         BigDecimal belowTheDivider = adVoltage.divide(supplyVoltage, 4, HALF_DOWN)
263                 .subtract(HUMIDITY_VOLTAGE_SUBTRACTION).divide(HUMIDITY_VOLTAGE_DIVIDER, 4, HALF_DOWN);
264         BigDecimal underTheDivider = HUMIDITY_TEMPERATURE_CORRECTION
265                 .subtract(HUMIDITY_TEMPERATURE_MULTIPLIER.multiply(temperature));
266
267         return new DecimalType(belowTheDivider.divide(underTheDivider, 4, HALF_DOWN));
268     }
269
270     private State handlePressure(DeviceState deviceState) {
271         DecimalType referenceVoltageState = (DecimalType) deviceState.getLastState(CHANNEL_REFERENCE_VOLTAGE);
272         BigDecimal adVoltage = getVoltage();
273
274         if (referenceVoltageState == null || adVoltage == null) {
275             return null;
276         }
277
278         BigDecimal supplyVoltage = referenceVoltageState.toBigDecimal();
279
280         // hPa = ((A/D voltage / supply voltage) + 0.095) / 0.0009
281         return new DecimalType((adVoltage.divide(supplyVoltage, 4, HALF_DOWN).add(PRESSURE_ADDITION))
282                 .divide(PRESSURE_DIVIDER, 4, HALF_DOWN));
283     }
284
285     private BigDecimal getVoltage() {
286         if (miliVoltageTimesTen == null) {
287             return null;
288         }
289         return miliVoltageTimesTen.divide(ONE_HUNDRED, 100, CEILING);
290     }
291
292     @Override
293     public void setDeviceId(String deviceId) throws RFXComException {
294         throw new RFXComException("Not supported");
295     }
296 }