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