]> git.basschouten.com Git - openhab-addons.git/blob
5bcfcd8b7aea926cb9c309de682a16b1f62023d5
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.fineoffsetweatherstation.internal.service;
14
15 import static org.openhab.binding.fineoffsetweatherstation.internal.Utils.toUInt16;
16 import static org.openhab.binding.fineoffsetweatherstation.internal.Utils.toUInt32;
17
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;
23 import java.util.Map;
24 import java.util.function.Supplier;
25
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;
38
39 /**
40  * Class to Convert the protocol data
41  *
42  * @author Andreas Berger - Initial contribution
43  */
44 @NonNullByDefault
45 public class FineOffsetDataParser {
46     private final Logger logger = LoggerFactory.getLogger(FineOffsetDataParser.class);
47
48     public @Nullable String getFirmwareVersion(byte[] data) {
49         if (data.length > 0) {
50             return new String(data, 5, data[4]);
51         }
52         return null;
53     }
54
55     public Map<SensorGatewayBinding, SensorDevice> getRegisteredSensors(byte[] data,
56             Supplier<@Nullable Boolean> isUseWh24) {
57         /*
58          * Pos | Length | Description
59          * -------------------------------------------------
60          * 0 | 2 | fixed header (0xffff)
61          * 2 | 1 | command (0x3c)
62          * 3 | 2 | size
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
70          */
71
72         Map<SensorGatewayBinding, SensorDevice> result = new HashMap<>();
73         var len = toUInt16(data, 3);
74         int entry = 0;
75         int entrySize = 7;
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]);
82                 continue;
83             }
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;
90             }
91             if (sensorGatewayBinding == null) {
92                 logger.debug("too many sensor candidates for (id={}) and index {}: {}", id, data[idx],
93                         sensorCandidates);
94                 continue;
95             }
96             switch (id) {
97                 case 0xFFFFFFFE:
98                     logger.trace("sensor {} = disabled", sensorGatewayBinding);
99                     continue;
100                 case 0xFFFFFFFF:
101                     logger.trace("sensor {} = registering", sensorGatewayBinding);
102                     continue;
103             }
104
105             BatteryStatus batteryStatus = sensorGatewayBinding.getBatteryStatus(data[idx + 5]);
106             int signal = Utils.toUInt8(data[idx + 6]);
107
108             result.put(sensorGatewayBinding, new SensorDevice(id, sensorGatewayBinding, batteryStatus, signal));
109         }
110         return result;
111     }
112
113     public @Nullable SystemInfo fetchSystemInfo(byte[] data) {
114         // expected response
115         // 0 - 0xff - header
116         // 1 - 0xff - header
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
121         // 6-9 - UTC time
122         // 10 - time zone index (?)
123         // 11 - DST 0-1 - false/true
124         // 12 - 0x?? - checksum
125         Integer frequency = null;
126         switch (data[4]) {
127             case 0:
128                 frequency = 433;
129                 break;
130             case 1:
131                 frequency = 868;
132                 break;
133             case 2:
134                 frequency = 915;
135                 break;
136             case 3:
137                 frequency = 920;
138                 break;
139
140         }
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);
146     }
147
148     List<MeasuredValue> getLiveData(byte[] data, ConversionContext context) {
149         /*
150          * Pos| Length | Description
151          * -------------------------------------------------
152          * 0 | 2 | fixed header (0xffff)
153          * 2 | 1 | command (0x27)
154          * 3 | 2 | size
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          * -------------------------------------------------
162          * ...
163          * -------------------------------------------------
164          *
165          * | 1 | checksum
166          */
167         var idx = 5;
168         var size = toUInt16(data, 3);
169         List<MeasuredValue> result = new ArrayList<>();
170         while (idx < size) {
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));
175                 return result;
176             }
177             idx += measurand.extractMeasuredValues(data, idx, context, result);
178         }
179         return result;
180     }
181 }