2 * Copyright (c) 2010-2023 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.fineoffsetweatherstation.internal.service;
15 import static org.openhab.binding.fineoffsetweatherstation.internal.Utils.toUInt16;
16 import static org.openhab.binding.fineoffsetweatherstation.internal.Utils.toUInt32;
18 import java.time.LocalDateTime;
19 import java.time.ZoneOffset;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
24 import java.util.function.Supplier;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.fineoffsetweatherstation.internal.Utils;
29 import org.openhab.binding.fineoffsetweatherstation.internal.domain.ConversionContext;
30 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Measurand;
31 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Protocol;
32 import org.openhab.binding.fineoffsetweatherstation.internal.domain.SensorGatewayBinding;
33 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.BatteryStatus;
34 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.MeasuredValue;
35 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.SensorDevice;
36 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.SystemInfo;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * Class to Convert the protocol data
43 * @author Andreas Berger - Initial contribution
46 public class FineOffsetDataParser {
47 private final Logger logger = LoggerFactory.getLogger(FineOffsetDataParser.class);
48 private final Protocol protocol;
50 public FineOffsetDataParser(Protocol protocol) {
51 this.protocol = protocol;
54 public @Nullable String getFirmwareVersion(byte[] data) {
55 if (data.length > 0) {
56 return new String(data, 5, data[4]);
61 public Map<SensorGatewayBinding, SensorDevice> getRegisteredSensors(byte[] data,
62 Supplier<@Nullable Boolean> isUseWh24) {
64 * Pos | Length | Description
65 * -------------------------------------------------
66 * 0 | 2 | fixed header (0xffff)
67 * 2 | 1 | command (0x3c)
69 * -------------------------------------------------
70 * (n * 7) + 5 | 1 | index of sensor n
71 * (n * 7) + 6 | 4 | id of sensor n
72 * (n * 7) + 10 | 1 | battery status of sensor n
73 * (n * 7) + 11 | 1 | signal of sensor n
74 * -------------------------------------------------
75 * (n * 7) + 12 | 1 | checksum
78 Map<SensorGatewayBinding, SensorDevice> result = new HashMap<>();
79 var len = toUInt16(data, 3);
82 while (entry * entrySize + 11 <= len) {
83 int idx = entry++ * entrySize + 5;
84 int id = toUInt32(data, idx + 1);
85 List<SensorGatewayBinding> sensorCandidates = SensorGatewayBinding.forIndex(data[idx]);
86 if (sensorCandidates == null || sensorCandidates.isEmpty()) {
87 logger.debug("unknown sensor (id={}) for index {}", id, data[idx]);
90 SensorGatewayBinding sensorGatewayBinding = null;
91 if (sensorCandidates.size() == 1) {
92 sensorGatewayBinding = sensorCandidates.get(0);
93 } else if (sensorCandidates.size() == 2 && data[idx] == 0) {
94 sensorGatewayBinding = Boolean.TRUE.equals(isUseWh24.get()) ? SensorGatewayBinding.WH24
95 : SensorGatewayBinding.WH65;
97 if (sensorGatewayBinding == null) {
98 logger.debug("too many sensor candidates for (id={}) and index {}: {}", id, data[idx],
104 logger.trace("sensor {} = disabled", sensorGatewayBinding);
107 logger.trace("sensor {} = registering", sensorGatewayBinding);
111 BatteryStatus batteryStatus = sensorGatewayBinding.getBatteryStatus(data[idx + 5]);
112 int signal = Utils.toUInt8(data[idx + 6]);
114 result.put(sensorGatewayBinding, new SensorDevice(id, sensorGatewayBinding, batteryStatus, signal));
119 public @Nullable SystemInfo fetchSystemInfo(byte[] data) {
123 // 2 - 0x30 - system info
124 // 3 - 0x?? - size of response
125 // 4 - frequency - 0=433, 1=868MHz, 2=915MHz, 3=920MHz
126 // 5 - sensor type - 0=WH24, 1=WH65
128 // 10 - time zone index (?)
129 // 11 - DST 0-1 - false/true
130 // 12 - 0x?? - checksum
131 Integer frequency = null;
147 boolean useWh24 = data[5] == 0;
148 var unix = toUInt32(data, 6);
149 var date = LocalDateTime.ofEpochSecond(unix, 0, ZoneOffset.UTC);
150 var dst = data[11] != 0;
151 return new SystemInfo(frequency, date, dst, useWh24);
154 List<MeasuredValue> getMeasuredValues(byte[] data, ConversionContext context) {
156 * Pos| Length | Description
157 * -------------------------------------------------
158 * 0 | 2 | fixed header (0xffff)
159 * 2 | 1 | command (0x27)
161 * -------------------------------------------------
162 * 5 | 1 | code of item (item defines n)
163 * 6 | n | value of item
164 * -------------------------------------------------
165 * 6 + n | 1 | code of item (item defines m)
166 * 7 + n | m | value of item
167 * -------------------------------------------------
169 * -------------------------------------------------
174 if (protocol == Protocol.ELV) {
175 idx++; // at index 5 there is an additional Byte being set to 0x04
177 return readMeasuredValues(data, idx, context, protocol.getParserCustomizationType());
180 List<MeasuredValue> getRainData(byte[] data, ConversionContext context) {
181 return readMeasuredValues(data, 5, context, Measurand.ParserCustomizationType.RAIN_READING);
184 private List<MeasuredValue> readMeasuredValues(byte[] data, int idx, ConversionContext context,
185 Measurand.@Nullable ParserCustomizationType protocol) {
186 var size = toUInt16(data, 3);
187 List<MeasuredValue> result = new ArrayList<>();
189 byte code = data[idx++];
190 Measurand.SingleChannelMeasurand measurand = Measurand.getByCode(code);
191 if (measurand == null) {
192 logger.warn("failed to get measurand 0x{}", Integer.toHexString(code));
195 idx += measurand.extractMeasuredValues(data, idx, context, protocol, result);