2 * Copyright (c) 2010-2021 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.rfxcom.internal.messages;
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;
19 import java.math.BigDecimal;
21 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
22 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException;
23 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException;
24 import org.openhab.binding.rfxcom.internal.handler.DeviceState;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.types.State;
27 import org.openhab.core.types.Type;
28 import org.openhab.core.types.UnDefType;
29 import org.openhab.core.util.HexUtils;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
34 * Add support for the rfx-sensor
36 * @author Martin van Wingerden - Initial contribution
38 public class RFXComRFXSensorMessage extends RFXComDeviceMessageImpl<RFXComRFXSensorMessage.SubType> {
39 private final Logger logger = LoggerFactory.getLogger(RFXComRFXSensorMessage.class);
41 private static final BigDecimal PRESSURE_ADDITION = new BigDecimal("0.095");
42 private static final BigDecimal PRESSURE_DIVIDER = new BigDecimal("0.0009");
44 private static final BigDecimal HUMIDITY_VOLTAGE_SUBTRACTION = new BigDecimal("0.16");
45 private static final BigDecimal HUMIDITY_VOLTAGE_DIVIDER = new BigDecimal("0.0062");
46 private static final BigDecimal HUMIDITY_TEMPERATURE_CORRECTION = new BigDecimal("1.0546");
47 private static final BigDecimal HUMIDITY_TEMPERATURE_MULTIPLIER = new BigDecimal("0.00216");
49 private static final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100);
51 public enum SubType implements ByteEnumWrapper {
57 private final int subType;
59 SubType(int subType) {
60 this.subType = subType;
64 public byte toByte() {
65 return (byte) subType;
69 public SubType subType;
72 private Double temperature;
73 private BigDecimal miliVoltageTimesTen;
74 public byte signalLevel;
76 public RFXComRFXSensorMessage() {
77 super(PacketType.RFXSENSOR);
80 public RFXComRFXSensorMessage(byte[] data) throws RFXComException {
85 public String toString() {
86 String str = super.toString();
88 str += ", Sub type = " + subType;
89 str += ", Device Id = " + getDeviceId();
90 str += ", Temperature = " + temperature;
91 str += ", Voltage = " + getVoltage();
92 str += ", Signal level = " + signalLevel;
98 public void encodeMessage(byte[] data) throws RFXComException {
99 super.encodeMessage(data);
101 subType = fromByte(SubType.class, super.subType);
103 sensorId = (data[4] & 0xFF);
110 encodeTemperatureMessage(msg1, msg2);
114 encodeVoltageMessage(msg1, msg2);
117 encodeStatusMessage(msg2);
121 signalLevel = (byte) ((data[7] & 0xF0) >> 4);
124 private void encodeTemperatureMessage(byte msg1, byte msg2) {
125 temperature = (short) ((msg1 & 0x7F) << 8 | (msg2 & 0xFF)) * 0.01;
126 if ((msg1 & 0x80) != 0) {
127 temperature = -temperature;
131 private void encodeVoltageMessage(byte msg1, byte msg2) {
132 miliVoltageTimesTen = BigDecimal.valueOf((short) ((msg1 & 0xFF) << 8 | (msg2 & 0xFF)));
135 private void encodeStatusMessage(byte msg2) {
140 public byte[] decodeMessage() {
141 byte[] data = new byte[8];
144 data[1] = PacketType.RFXSENSOR.toByte();
145 data[2] = subType.toByte();
147 data[4] = (byte) (sensorId & 0x00FF);
149 if (subType == SubType.TEMPERATURE) {
150 decodeTemperatureMessage(data);
151 } else if (subType == SubType.A_D) {
152 decodeVoltageMessage(data);
153 } else if (subType == SubType.VOLTAGE) {
154 decodeVoltageMessage(data);
155 } else if (subType == SubType.MESSAGE) {
156 decodeStatusMessage(data);
159 data[7] = (byte) ((signalLevel & 0x0F) << 4);
165 public void convertFromState(String channelId, Type type) {
166 throw new UnsupportedOperationException();
169 private void decodeTemperatureMessage(byte[] data) {
170 short temp = (short) Math.abs(temperature * 100);
171 data[5] = (byte) ((temp >> 8) & 0xFF);
172 data[6] = (byte) (temp & 0xFF);
173 if (temperature < 0) {
178 private void decodeVoltageMessage(byte[] data) {
179 short miliVoltageTimesTenShort = this.miliVoltageTimesTen.shortValueExact();
180 data[5] = (byte) ((miliVoltageTimesTenShort >> 8) & 0xFF);
181 data[6] = (byte) (miliVoltageTimesTenShort & 0xFF);
184 private void decodeStatusMessage(byte[] data) {
185 logger.info("A status message was received {}", HexUtils.bytesToHex(data));
189 public String getDeviceId() {
190 return String.valueOf(sensorId);
194 public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
195 return ByteEnumUtil.convertSubType(SubType.class, subType);
199 public void setSubType(SubType subType) {
200 throw new UnsupportedOperationException();
204 public State convertToState(String channelId, DeviceState deviceState) throws RFXComUnsupportedChannelException {
206 case CHANNEL_TEMPERATURE:
207 return subType == SubType.TEMPERATURE ? getTemperature() : null;
209 case CHANNEL_VOLTAGE:
210 return subType == SubType.A_D ? handleVoltage() : null;
212 case CHANNEL_REFERENCE_VOLTAGE:
213 return subType == SubType.VOLTAGE ? handleVoltage() : null;
215 case CHANNEL_HUMIDITY:
216 return subType == SubType.A_D ? handleHumidity(deviceState) : null;
218 case CHANNEL_PRESSURE:
219 return subType == SubType.A_D ? handlePressure(deviceState) : null;
222 return super.convertToState(channelId, deviceState);
226 private State getTemperature() {
227 if (temperature != null) {
228 return new DecimalType(temperature);
230 return UnDefType.UNDEF;
234 private State handleVoltage() {
235 if (miliVoltageTimesTen != null) {
236 return new DecimalType(getVoltage());
238 return UnDefType.UNDEF;
242 private State handleHumidity(DeviceState deviceState) {
243 DecimalType temperatureState = (DecimalType) deviceState.getLastState(CHANNEL_TEMPERATURE);
244 Type referenceVoltageState = deviceState.getLastState(CHANNEL_REFERENCE_VOLTAGE);
245 BigDecimal adVoltage = getVoltage();
247 if (temperatureState == null || referenceVoltageState == null || adVoltage == null) {
251 if (!(referenceVoltageState instanceof DecimalType)) {
252 return UnDefType.UNDEF;
255 BigDecimal temperature = temperatureState.toBigDecimal();
256 BigDecimal supplyVoltage = ((DecimalType) referenceVoltageState).toBigDecimal();
258 // RH = (((A/D voltage / supply voltage) - 0.16) / 0.0062) / (1.0546 - 0.00216 * temperature)
259 BigDecimal belowTheDivider = adVoltage.divide(supplyVoltage, 4, HALF_DOWN)
260 .subtract(HUMIDITY_VOLTAGE_SUBTRACTION).divide(HUMIDITY_VOLTAGE_DIVIDER, 4, HALF_DOWN);
261 BigDecimal underTheDivider = HUMIDITY_TEMPERATURE_CORRECTION
262 .subtract(HUMIDITY_TEMPERATURE_MULTIPLIER.multiply(temperature));
264 return new DecimalType(belowTheDivider.divide(underTheDivider, 4, HALF_DOWN));
267 private State handlePressure(DeviceState deviceState) {
268 DecimalType referenceVoltageState = (DecimalType) deviceState.getLastState(CHANNEL_REFERENCE_VOLTAGE);
269 BigDecimal adVoltage = getVoltage();
271 if (referenceVoltageState == null || adVoltage == null) {
275 BigDecimal supplyVoltage = referenceVoltageState.toBigDecimal();
277 // hPa = ((A/D voltage / supply voltage) + 0.095) / 0.0009
278 return new DecimalType((adVoltage.divide(supplyVoltage, 4, HALF_DOWN).add(PRESSURE_ADDITION))
279 .divide(PRESSURE_DIVIDER, 4, HALF_DOWN));
282 private BigDecimal getVoltage() {
283 if (miliVoltageTimesTen == null) {
286 return miliVoltageTimesTen.divide(ONE_HUNDRED, 100, CEILING);
290 public void setDeviceId(String deviceId) throws RFXComException {
291 throw new RFXComException("Not supported");