]> git.basschouten.com Git - openhab-addons.git/commitdiff
[fineoffsetweatherstation] Fix wrong handling temperature reading for of WH34 (#15853)
authorAndreas Berger <Andy2003@users.noreply.github.com>
Fri, 10 Nov 2023 12:29:25 +0000 (13:29 +0100)
committerGitHub <noreply@github.com>
Fri, 10 Nov 2023 12:29:25 +0000 (13:29 +0100)
* [fineoffsetweatherstation] Improve tracing
* [fineoffsetweatherstation] Fix wrong handling temperature reading for of WH34

Signed-off-by: Andreas Berger <andreas@berger-freelancer.com>
bundles/org.openhab.binding.fineoffsetweatherstation/README.md
bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/domain/Command.java
bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/domain/DebugDetails.java [new file with mode: 0644]
bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/domain/Measurand.java
bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/service/ELVGatewayQueryService.java
bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/service/FineOffsetDataParser.java
bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/service/FineOffsetGatewayQueryService.java
bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/service/GatewayQueryService.java
bundles/org.openhab.binding.fineoffsetweatherstation/src/test/java/org/openhab/binding/fineoffsetweatherstation/internal/service/FineOffsetDataParserTest.java

index fcfed15c69c71ccef8341905b69e7428ceb64d8d..fa4bb744e67e38dc88bc538494e50ae486fb54b3 100644 (file)
@@ -19,7 +19,7 @@ Here is a product picture of how this Weather Station looks like:
 
 ![WH2650](doc/WH2650.png)
 
-This binding works offline by [implementing the wire protocol](https://osswww.ecowitt.net/uploads/20210716/WN1900%20GW1000,1100%20WH2680,2650%20telenet%20v1.6.0%20.pdf) of the WiFi gateway device.
+This binding works offline by [implementing the wire protocol](https://osswww.ecowitt.net/uploads/20220407/WN1900%20GW1000,1100%20WH2680,2650%20telenet%20v1.6.4.pdf) of the WiFi gateway device.
 
 ## Discussion
 
index eb2872865154d7e50b2439bb9a157887b0c6a375..dd81779c990eeb687f3719efbfaa9f9f27f57fdc 100644 (file)
@@ -275,4 +275,8 @@ public enum Command {
     public boolean isResponseValid(byte[] data) {
         return isHeaderValid(data) && Utils.validateChecksum(data, sizeBytes);
     }
+
+    public int getSizeBytes() {
+        return sizeBytes;
+    }
 }
diff --git a/bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/domain/DebugDetails.java b/bundles/org.openhab.binding.fineoffsetweatherstation/src/main/java/org/openhab/binding/fineoffsetweatherstation/internal/domain/DebugDetails.java
new file mode 100644 (file)
index 0000000..7d2eb17
--- /dev/null
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.fineoffsetweatherstation.internal.domain;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openhab.binding.fineoffsetweatherstation.internal.Utils;
+
+/**
+ * Class to collect debug details
+ *
+ * @author Andreas Berger - Initial contribution
+ */
+public class DebugDetails {
+    final byte[] data;
+
+    private final Map<Integer, DebugSegment> segments = new TreeMap<>();
+
+    public DebugDetails(byte[] data, Command command, Protocol protocol) {
+        this.data = data;
+        addDebugDetails(0, 2, "header");
+        addDebugDetails(2, 1, "command: " + command.name());
+        addDebugDetails(3, command.getSizeBytes(), "size");
+        if (protocol == Protocol.ELV) {
+            addDebugDetails(data.length - 2, 1, "ELV checksum");
+        }
+        addDebugDetails(data.length - 1, 1, "checksum");
+    }
+
+    public void addDebugDetails(int start, int length, String description) {
+        segments.put(start, new DebugSegment(start, length, description));
+    }
+
+    @Override
+    public String toString() {
+        int padding = segments.values().stream().mapToInt(value -> value.length).max().orElse(0) * 2;
+        return "0x" + Utils.toHexString(data, data.length, "") + "\n" + segments.values().stream()
+                .map(debugSegment -> debugSegment.toDebugString(padding)).collect(Collectors.joining("\n"));
+    }
+
+    private class DebugSegment {
+        final int start;
+        final int length;
+        final String description;
+
+        DebugSegment(int start, int length, String description) {
+            this.start = start;
+            this.length = length;
+            this.description = description;
+        }
+
+        @Override
+        public String toString() {
+            return toDebugString(0);
+        }
+
+        private String toDebugString(int padding) {
+            String result = "0x";
+            String hexString = Utils.toHexString(Arrays.copyOfRange(data, start, start + length), length, "");
+            result += StringUtils.rightPad(hexString, padding, " ");
+            result += ": " + description;
+            return result;
+        }
+    }
+}
index 1693b734459635b2842464d3b1635c01faddea78..1dbe4935ba83be9a749c5582d49eaa180a8ad3a2 100644 (file)
@@ -138,8 +138,10 @@ public enum Measurand {
     // `LIGHTNING_POWER` is the name in the spec, so we keep it here as it
     LIGHTNING_POWER("lightning-counter", 0x62, "lightning counter for the day", MeasureType.LIGHTNING_COUNTER),
 
-    TF_USRX("temperature-external-channel", new int[] { 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A },
-            "Soil or Water temperature", MeasureType.TEMPERATURE),
+    TF_USRX(new int[] { 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A },
+            new MeasurandParser("temperature-external-channel", "Soil or Water temperature", MeasureType.TEMPERATURE),
+            // skip battery-level, since it is read via Command.CMD_READ_SENSOR_ID_NEW
+            new Skip(1)),
 
     ITEM_SENSOR_CO2(0x70,
             new MeasurandParser("sensor-co2-temperature", "Temperature (CO₂-Sensor)", MeasureType.TEMPERATURE),
@@ -240,18 +242,20 @@ public enum Measurand {
     }
 
     private int extractMeasuredValues(byte[] data, int offset, @Nullable Integer channel, ConversionContext context,
-            @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result) {
+            @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result,
+            DebugDetails debugDetails) {
         int subOffset = 0;
         for (Parser parser : parsers) {
             subOffset += parser.extractMeasuredValues(data, offset + subOffset, channel, context, customizationType,
-                    result);
+                    result, debugDetails);
         }
         return subOffset;
     }
 
     private interface Parser {
         int extractMeasuredValues(byte[] data, int offset, @Nullable Integer channel, ConversionContext context,
-                @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result);
+                @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result,
+                DebugDetails debugDetails);
     }
 
     private static class Skip implements Parser {
@@ -263,7 +267,9 @@ public enum Measurand {
 
         @Override
         public int extractMeasuredValues(byte[] data, int offset, @Nullable Integer channel, ConversionContext context,
-                @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result) {
+                @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result,
+                DebugDetails debugDetails) {
+            debugDetails.addDebugDetails(offset, skip, "skipped");
             return skip;
         }
     }
@@ -302,8 +308,14 @@ public enum Measurand {
         }
 
         public int extractMeasuredValues(byte[] data, int offset, ConversionContext context,
-                @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result) {
-            return measurand.extractMeasuredValues(data, offset, channel, context, customizationType, result);
+                @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result,
+                DebugDetails debugDetails) {
+            return measurand.extractMeasuredValues(data, offset, channel, context, customizationType, result,
+                    debugDetails);
+        }
+
+        public String getDebugString() {
+            return measurand.name() + (channel == null ? "" : " channel " + channel);
         }
     }
 
@@ -337,12 +349,17 @@ public enum Measurand {
 
         @Override
         public int extractMeasuredValues(byte[] data, int offset, @Nullable Integer channel, ConversionContext context,
-                @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result) {
+                @Nullable ParserCustomizationType customizationType, List<MeasuredValue> result,
+                DebugDetails debugDetails) {
             MeasureType measureType = getMeasureType(customizationType);
             State state = measureType.toState(data, offset, context);
             if (state != null) {
+                debugDetails.addDebugDetails(offset, measureType.getByteSize(),
+                        measureType.name() + ": " + state.toFullString());
                 ChannelTypeUID channelType = channelTypeUID == null ? measureType.getChannelTypeId() : channelTypeUID;
                 result.add(new MeasuredValue(measureType, channelPrefix, channel, channelType, state, name));
+            } else {
+                debugDetails.addDebugDetails(offset, measureType.getByteSize(), measureType.name() + ": null");
             }
             return measureType.getByteSize();
         }
index 2e60c9f2ff27ed85473ffd5674caf3a62191e739..02e076e78706b54b6420c12f322d218ab72b7e47 100644 (file)
@@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.fineoffsetweatherstation.internal.FineOffsetGatewayConfiguration;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Command;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.ConversionContext;
+import org.openhab.binding.fineoffsetweatherstation.internal.domain.DebugDetails;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Protocol;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.SensorGatewayBinding;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.MeasuredValue;
@@ -79,6 +80,10 @@ public class ELVGatewayQueryService extends GatewayQueryService {
         if (data == null) {
             return Collections.emptyList();
         }
-        return fineOffsetDataParser.getMeasuredValues(data, conversionContext);
+        DebugDetails debugDetails = new DebugDetails(data, Command.CMD_WS980_LIVEDATA, Protocol.ELV);
+        List<MeasuredValue> measuredValues = fineOffsetDataParser.getMeasuredValues(data, conversionContext,
+                debugDetails);
+        logger.trace("{}", debugDetails);
+        return measuredValues;
     }
 }
index fc8922db53cda2ccb9ee23dd40fbb206a063c31c..c1ce0fd76ef9f654371a143a9e410e1fff867b9c 100644 (file)
@@ -27,6 +27,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.fineoffsetweatherstation.internal.Utils;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.ConversionContext;
+import org.openhab.binding.fineoffsetweatherstation.internal.domain.DebugDetails;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Measurand;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Protocol;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.SensorGatewayBinding;
@@ -151,7 +152,7 @@ public class FineOffsetDataParser {
         return new SystemInfo(frequency, date, dst, useWh24);
     }
 
-    List<MeasuredValue> getMeasuredValues(byte[] data, ConversionContext context) {
+    List<MeasuredValue> getMeasuredValues(byte[] data, ConversionContext context, DebugDetails debugDetails) {
         /*
          * Pos| Length | Description
          * -------------------------------------------------
@@ -173,26 +174,31 @@ public class FineOffsetDataParser {
         var idx = 5;
         if (protocol == Protocol.ELV) {
             idx++; // at index 5 there is an additional Byte being set to 0x04
+            debugDetails.addDebugDetails(5, 1, "ELV extra byte");
         }
-        return readMeasuredValues(data, idx, context, protocol.getParserCustomizationType());
+        return readMeasuredValues(data, idx, context, protocol.getParserCustomizationType(), debugDetails);
     }
 
-    List<MeasuredValue> getRainData(byte[] data, ConversionContext context) {
-        return readMeasuredValues(data, 5, context, Measurand.ParserCustomizationType.RAIN_READING);
+    List<MeasuredValue> getRainData(byte[] data, ConversionContext context, DebugDetails debugDetails) {
+        return readMeasuredValues(data, 5, context, Measurand.ParserCustomizationType.RAIN_READING, debugDetails);
     }
 
     private List<MeasuredValue> readMeasuredValues(byte[] data, int idx, ConversionContext context,
-            Measurand.@Nullable ParserCustomizationType protocol) {
+            Measurand.@Nullable ParserCustomizationType protocol, DebugDetails debugDetails) {
         var size = toUInt16(data, 3);
+
         List<MeasuredValue> result = new ArrayList<>();
         while (idx < size) {
             byte code = data[idx++];
             Measurand.SingleChannelMeasurand measurand = Measurand.getByCode(code);
             if (measurand == null) {
                 logger.warn("failed to get measurand 0x{}", Integer.toHexString(code));
+                debugDetails.addDebugDetails(idx - 1, 1, "unknown measurand");
                 return result;
+            } else {
+                debugDetails.addDebugDetails(idx - 1, 1, "measurand " + measurand.getDebugString());
             }
-            idx += measurand.extractMeasuredValues(data, idx, context, protocol, result);
+            idx += measurand.extractMeasuredValues(data, idx, context, protocol, result, debugDetails);
         }
         return result;
     }
index a5921976a5eeebc0c0ec57c39319fd93cd237f39..fd049dea1dbfe559dc8ec19f196b3925d910ed0d 100644 (file)
@@ -22,6 +22,7 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.fineoffsetweatherstation.internal.FineOffsetGatewayConfiguration;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Command;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.ConversionContext;
+import org.openhab.binding.fineoffsetweatherstation.internal.domain.DebugDetails;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Protocol;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.SensorGatewayBinding;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.MeasuredValue;
@@ -38,6 +39,7 @@ import org.slf4j.LoggerFactory;
  */
 @NonNullByDefault
 public class FineOffsetGatewayQueryService extends GatewayQueryService {
+    private static final Protocol PROTOCOL = Protocol.DEFAULT;
     private final Logger logger = LoggerFactory.getLogger(FineOffsetGatewayQueryService.class);
 
     private final FineOffsetDataParser fineOffsetDataParser;
@@ -47,7 +49,7 @@ public class FineOffsetGatewayQueryService extends GatewayQueryService {
     public FineOffsetGatewayQueryService(FineOffsetGatewayConfiguration config,
             @Nullable ThingStatusListener thingStatusListener, ConversionContext conversionContext) {
         super(config, thingStatusListener);
-        this.fineOffsetDataParser = new FineOffsetDataParser(Protocol.DEFAULT);
+        this.fineOffsetDataParser = new FineOffsetDataParser(PROTOCOL);
         this.conversionContext = conversionContext;
     }
 
@@ -92,18 +94,24 @@ public class FineOffsetGatewayQueryService extends GatewayQueryService {
 
         byte[] data = executeCommand(Command.CMD_GW1000_LIVEDATA);
         if (data != null) {
-            List<MeasuredValue> measuredValues = fineOffsetDataParser.getMeasuredValues(data, conversionContext);
+            DebugDetails debugDetails = new DebugDetails(data, Command.CMD_GW1000_LIVEDATA, PROTOCOL);
+            List<MeasuredValue> measuredValues = fineOffsetDataParser.getMeasuredValues(data, conversionContext,
+                    debugDetails);
             for (MeasuredValue measuredValue : measuredValues) {
                 valuePerChannel.put(measuredValue.getChannelId(), measuredValue);
             }
+            logger.trace("{}", debugDetails);
         }
 
         data = executeCommand(Command.CMD_READ_RAIN);
         if (data != null) {
-            List<MeasuredValue> measuredRainValues = fineOffsetDataParser.getRainData(data, conversionContext);
+            DebugDetails debugDetails = new DebugDetails(data, Command.CMD_READ_RAIN, PROTOCOL);
+            List<MeasuredValue> measuredRainValues = fineOffsetDataParser.getRainData(data, conversionContext,
+                    debugDetails);
             for (MeasuredValue measuredValue : measuredRainValues) {
                 valuePerChannel.put(measuredValue.getChannelId(), measuredValue);
             }
+            logger.trace("{}", debugDetails);
         }
 
         return valuePerChannel.values();
index 4edd46dc1526e16ddc5a9273cf249248480b6a18..e4edd15cbcf29ddb7b5f8b1a2d9996acd47cf3ec 100644 (file)
@@ -44,7 +44,7 @@ import org.slf4j.LoggerFactory;
  */
 @NonNullByDefault
 public abstract class GatewayQueryService implements AutoCloseable {
-    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
 
     private static final Lock REQUEST_LOCK = new ReentrantLock();
 
index 4a5e2082de9c360899e8a73dc4f46a61e0c899c5..1625d7fb44f819783eb66f9ecdcfbfd380b7983c 100644 (file)
@@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.junit.jupiter.api.Test;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Command;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.ConversionContext;
+import org.openhab.binding.fineoffsetweatherstation.internal.domain.DebugDetails;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.Protocol;
 import org.openhab.binding.fineoffsetweatherstation.internal.domain.response.MeasuredValue;
 import org.openhab.core.util.HexUtils;
@@ -32,9 +33,11 @@ import org.openhab.core.util.HexUtils;
 class FineOffsetDataParserTest {
     @Test
     void testLiveDataWH45() {
-        List<MeasuredValue> data = new FineOffsetDataParser(Protocol.DEFAULT).getMeasuredValues(HexUtils.hexToBytes(
-                "FFFF2700510100D306280827EF0927EF020045074F0A00150B00000C0000150000000016000117001900000E0000100000110021120000002113000005850D00007000D12E0060005A005B005502AE028F0633"),
-                new ConversionContext(ZoneOffset.UTC));
+        byte[] bytes = HexUtils.hexToBytes(
+                "FFFF2700510100D306280827EF0927EF020045074F0A00150B00000C0000150000000016000117001900000E0000100000110021120000002113000005850D00007000D12E0060005A005B005502AE028F0633");
+        DebugDetails debugDetails = new DebugDetails(bytes, Command.CMD_GW1000_LIVEDATA, Protocol.DEFAULT);
+        List<MeasuredValue> data = new FineOffsetDataParser(Protocol.DEFAULT).getMeasuredValues(bytes,
+                new ConversionContext(ZoneOffset.UTC), debugDetails);
         Assertions.assertThat(data)
                 .extracting(MeasuredValue::getChannelId, measuredValue -> measuredValue.getState().toString())
                 .containsExactly(new Tuple("temperature-indoor", "21.1 °C"), new Tuple("humidity-indoor", "40 %"),
@@ -54,12 +57,43 @@ class FineOffsetDataParserTest {
                         new Tuple("sensor-co2-co2", "686 ppm"), new Tuple("sensor-co2-co2-24-hour-average", "655 ppm"));
     }
 
+    @Test
+    void testLiveDataWH34AndWh45() {
+        byte[] bytes = HexUtils.hexToBytes(
+                "FFFF2700540100CA063E0826EC0926EC02007A074C0A002F0B001F0C0023150000032016000017001A0086225558005A00620000000661654A5AF1601B1900266300884B7000CE3F001D00240016001E041A037B0695");
+        DebugDetails debugDetails = new DebugDetails(bytes, Command.CMD_GW1000_LIVEDATA, Protocol.DEFAULT);
+        List<MeasuredValue> data = new FineOffsetDataParser(Protocol.DEFAULT).getMeasuredValues(bytes,
+                new ConversionContext(ZoneOffset.UTC), debugDetails);
+        Assertions.assertThat(data)
+                .extracting(MeasuredValue::getChannelId, measuredValue -> measuredValue.getState().toString())
+                .containsExactly(new Tuple("temperature-indoor", "20.2 °C"), new Tuple("humidity-indoor", "62 %"),
+                        new Tuple("pressure-absolute", "996.4 hPa"), new Tuple("pressure-relative", "996.4 hPa"),
+                        new Tuple("temperature-outdoor", "12.2 °C"), new Tuple("humidity-outdoor", "76 %"),
+                        new Tuple("direction-wind", "47 °"), new Tuple("speed-wind", "3.1 m/s"),
+                        new Tuple("speed-gust", "3.5 m/s"), new Tuple("illumination", "80 lx"),
+                        new Tuple("irradiation-uv", "0 mW/m²"), new Tuple("uv-index", "0"),
+                        new Tuple("temperature-channel-1", "13.4 °C"), new Tuple("humidity-channel-1", "85 %"),
+                        new Tuple("water-leak-channel-1", "OFF"), new Tuple("water-leak-channel-3", "OFF"),
+                        new Tuple("lightning-counter", "6"),
+                        new Tuple("lightning-time", "2023-11-07T15:42:41.000+0000"),
+                        new Tuple("lightning-distance", "27 km"), new Tuple("wind-max-day", "3.8 m/s"),
+                        new Tuple("temperature-external-channel-1", "13.6 °C"),
+                        new Tuple("sensor-co2-temperature", "20.6 °C"), new Tuple("sensor-co2-humidity", "63 %"),
+                        new Tuple("sensor-co2-pm10", "2.9 µg/m³"),
+                        new Tuple("sensor-co2-pm10-24-hour-average", "3.6 µg/m³"),
+                        new Tuple("sensor-co2-pm25", "2.2 µg/m³"),
+                        new Tuple("sensor-co2-pm25-24-hour-average", "3 µg/m³"),
+                        new Tuple("sensor-co2-co2", "1050 ppm"),
+                        new Tuple("sensor-co2-co2-24-hour-average", "891 ppm"));
+    }
+
     @Test
     void testLiveDataELV() {
         byte[] data = HexUtils.hexToBytes(
                 "FFFF0B00500401010B0201120300620401120501120629072108254B09254B0A01480B00040C000A0E000000001000000021110000002E120000014F130000100714000012FD15000B4BB816086917056D35");
+        DebugDetails debugDetails = new DebugDetails(data, Command.CMD_WS980_LIVEDATA, Protocol.ELV);
         List<MeasuredValue> measuredValues = new FineOffsetDataParser(Protocol.ELV).getMeasuredValues(data,
-                new ConversionContext(ZoneOffset.UTC));
+                new ConversionContext(ZoneOffset.UTC), debugDetails);
         Assertions.assertThat(measuredValues)
                 .extracting(MeasuredValue::getChannelId, measuredValue -> measuredValue.getState().toString())
                 .containsExactly(new Tuple("temperature-indoor", "26.7 °C"),
@@ -79,8 +113,9 @@ class FineOffsetDataParserTest {
     void testRainData() {
         byte[] data = HexUtils
                 .hexToBytes("FFFF5700290E000010000000001100000024120000003113000005030D00000F0064880000017A017B0030");
+        DebugDetails debugDetails = new DebugDetails(data, Command.CMD_READ_RAIN, Protocol.DEFAULT);
         List<MeasuredValue> measuredValues = new FineOffsetDataParser(Protocol.DEFAULT).getRainData(data,
-                new ConversionContext(ZoneOffset.UTC));
+                new ConversionContext(ZoneOffset.UTC), debugDetails);
         Assertions.assertThat(measuredValues)
                 .extracting(MeasuredValue::getChannelId, measuredValue -> measuredValue.getState().toString())
                 .containsExactly(new Tuple("rain-rate", "0 mm/h"), new Tuple("rain-day", "0 mm"),
@@ -94,8 +129,9 @@ class FineOffsetDataParserTest {
         byte[] data = HexUtils.hexToBytes(
                 "FFFF5700398000008300000009840000000985000000C786000000C7810000870064006400640064006400640064006400640064880900007A02BF");
         Assertions.assertThat(Command.CMD_READ_RAIN.isResponseValid(data)).isTrue();
+        DebugDetails debugDetails = new DebugDetails(data, Command.CMD_READ_RAIN, Protocol.DEFAULT);
         List<MeasuredValue> measuredValues = new FineOffsetDataParser(Protocol.DEFAULT).getRainData(data,
-                new ConversionContext(ZoneOffset.UTC));
+                new ConversionContext(ZoneOffset.UTC), debugDetails);
         Assertions.assertThat(measuredValues)
                 .extracting(MeasuredValue::getChannelId, measuredValue -> measuredValue.getState().toString())
                 .containsExactly(new Tuple("piezo-rain-rate", "0 mm/h"), new Tuple("piezo-rain-day", "0.9 mm"),