2 * Copyright (c) 2010-2022 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.SensorGatewayBinding;
32 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.BatteryStatus;
33 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.MeasuredValue;
34 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.SensorDevice;
35 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.SystemInfo;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * Class to Convert the protocol data
42 * @author Andreas Berger - Initial contribution
45 public class FineOffsetDataParser {
46 private final Logger logger = LoggerFactory.getLogger(FineOffsetDataParser.class);
48 public @Nullable String getFirmwareVersion(byte[] data) {
49 if (data.length > 0) {
50 return new String(data, 5, data[4]);
55 public Map<SensorGatewayBinding, SensorDevice> getRegisteredSensors(byte[] data,
56 Supplier<@Nullable Boolean> isUseWh24) {
58 * Pos | Length | Description
59 * -------------------------------------------------
60 * 0 | 2 | fixed header (0xffff)
61 * 2 | 1 | command (0x3c)
63 * -------------------------------------------------
64 * (n * 7) + 5 | 1 | index of sensor n
65 * (n * 7) + 6 | 4 | id of sensor n
66 * (n * 7) + 10 | 1 | battery status of sensor n
67 * (n * 7) + 11 | 1 | signal of sensor n
68 * -------------------------------------------------
69 * (n * 7) + 12 | 1 | checksum
72 Map<SensorGatewayBinding, SensorDevice> result = new HashMap<>();
73 var len = toUInt16(data, 3);
76 while (entry * entrySize + 11 <= len) {
77 int idx = entry++ * entrySize + 5;
78 int id = toUInt32(data, idx + 1);
79 List<SensorGatewayBinding> sensorCandidates = SensorGatewayBinding.forIndex(data[idx]);
80 if (sensorCandidates == null || sensorCandidates.isEmpty()) {
81 logger.debug("unknown sensor (id={}) for index {}", id, data[idx]);
84 SensorGatewayBinding sensorGatewayBinding = null;
85 if (sensorCandidates.size() == 1) {
86 sensorGatewayBinding = sensorCandidates.get(0);
87 } else if (sensorCandidates.size() == 2 && data[idx] == 0) {
88 sensorGatewayBinding = Boolean.TRUE.equals(isUseWh24.get()) ? SensorGatewayBinding.WH24
89 : SensorGatewayBinding.WH65;
91 if (sensorGatewayBinding == null) {
92 logger.debug("too many sensor candidates for (id={}) and index {}: {}", id, data[idx],
98 logger.trace("sensor {} = disabled", sensorGatewayBinding);
101 logger.trace("sensor {} = registering", sensorGatewayBinding);
105 BatteryStatus batteryStatus = sensorGatewayBinding.getBatteryStatus(data[idx + 5]);
106 int signal = Utils.toUInt8(data[idx + 6]);
108 result.put(sensorGatewayBinding, new SensorDevice(id, sensorGatewayBinding, batteryStatus, signal));
113 public @Nullable SystemInfo fetchSystemInfo(byte[] data) {
117 // 2 - 0x30 - system info
118 // 3 - 0x?? - size of response
119 // 4 - frequency - 0=433, 1=868MHz, 2=915MHz, 3=920MHz
120 // 5 - sensor type - 0=WH24, 1=WH65
122 // 10 - time zone index (?)
123 // 11 - DST 0-1 - false/true
124 // 12 - 0x?? - checksum
125 Integer frequency = null;
141 boolean useWh24 = data[5] == 0;
142 var unix = toUInt32(data, 6);
143 var date = LocalDateTime.ofEpochSecond(unix, 0, ZoneOffset.UTC);
144 var dst = data[11] != 0;
145 return new SystemInfo(frequency, date, dst, useWh24);
148 List<MeasuredValue> getLiveData(byte[] data, ConversionContext context) {
150 * Pos| Length | Description
151 * -------------------------------------------------
152 * 0 | 2 | fixed header (0xffff)
153 * 2 | 1 | command (0x27)
155 * -------------------------------------------------
156 * 5 | 1 | code of item (item defines n)
157 * 6 | n | value of item
158 * -------------------------------------------------
159 * 6 + n | 1 | code of item (item defines m)
160 * 7 + n | m | value of item
161 * -------------------------------------------------
163 * -------------------------------------------------
168 var size = toUInt16(data, 3);
169 List<MeasuredValue> result = new ArrayList<>();
171 byte code = data[idx++];
172 Measurand measurand = Measurand.getByCode(code);
173 if (measurand == null) {
174 logger.warn("failed to get measurand 0x{}", Integer.toHexString(code));
177 idx += measurand.extractMeasuredValues(data, idx, context, result);