From e72e7d59d74640d3499857dd08bf8e31d4f5eefe Mon Sep 17 00:00:00 2001
From: Matthias Prinke <83612361+matthias-bs@users.noreply.github.com>
Date: Sun, 22 Oct 2023 09:48:38 +0100
Subject: [PATCH] Add support for Bresser water leakage sensor PN: 7009975
 (#2590)

---
 README.md                     |   1 +
 conf/rtl_433.example.conf     |   1 +
 include/rtl_433_devices.h     |   1 +
 src/CMakeLists.txt            |   1 +
 src/devices/bresser_leakage.c | 165 ++++++++++++++++++++++++++++++++++
 5 files changed, 169 insertions(+)
 create mode 100644 src/devices/bresser_leakage.c

diff --git a/README.md b/README.md
index dcd661ee..42c5a414 100644
--- a/README.md
+++ b/README.md
@@ -332,6 +332,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
     [244]  Fine Offset Electronics WS90 weather station
     [245]* ThermoPro TX-2C Thermometer and Humidity sensor
     [246]  TFA 30.3151 Weather Station
+    [247]  Bresser water leakage
 
 * Disabled by default, use -R n or a conf file to enable
 
diff --git a/conf/rtl_433.example.conf b/conf/rtl_433.example.conf
index 36e99d2e..dae99aa4 100644
--- a/conf/rtl_433.example.conf
+++ b/conf/rtl_433.example.conf
@@ -473,6 +473,7 @@ convert si
   protocol 244 # Fine Offset Electronics WS90 weather station
 # protocol 245 # ThermoPro TX-2C Thermometer and Humidity sensor
   protocol 246 # TFA 30.3151 Weather Station
+  protocol 247 # Bresser water leakage
 
 ## Flex devices (command line option "-X")
 
diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h
index 1d18eebb..4415fa60 100644
--- a/include/rtl_433_devices.h
+++ b/include/rtl_433_devices.h
@@ -254,6 +254,7 @@
     DECL(fineoffset_ws90) \
     DECL(thermopro_tx2c) \
     DECL(tfa_303151) \
+    DECL(bresser_leakage) \
 
     /* Add new decoders here. */
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index adfafa8e..dedb969a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -68,6 +68,7 @@ add_library(r_433 STATIC
     devices/bresser_5in1.c
     devices/bresser_6in1.c
     devices/bresser_7in1.c
+    devices/bresser_leakage.c
     devices/bt_rain.c
     devices/burnhardbbq.c
     devices/calibeur.c
diff --git a/src/devices/bresser_leakage.c b/src/devices/bresser_leakage.c
new file mode 100644
index 00000000..c24bff80
--- /dev/null
+++ b/src/devices/bresser_leakage.c
@@ -0,0 +1,165 @@
+/** @file
+    Bresser Water Leakage Sensor.
+
+    Copyright (C) 2023 Matthias Prinke <m.prinke@arcor.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+*/
+
+#define SENSOR_TYPE_LEAKAGE 5
+
+/**
+Decoder for Bresser Water Leakage outdoor sensor, PN 7009975
+
+see https://github.com/merbanan/rtl_433/issues/2576
+
+Based on bresser_6in1.c
+
+Preamble: aa aa 2d d4
+
+Data layout:
+    CCCCCCCC CCCCCCCC IIIIIIII IIIIIIII IIIIIIII IIIIIIII SSSSQHHH ANBBFFFF
+
+- C: 16-bit, crc16/xmodem, polynomial: 0x1021, init: 0x0000, range: byte 2...6
+- I: 24-bit little-endian id; changes on power-up/reset
+- S: 4 bit sensor type
+- Q: 1 bit startup; changes from 0 to 1 approx. one hour after power-on/reset
+- H: 3 bit channel; set via switch on the device, latched at power-on/reset
+- A: 1 bit alarm
+- N: 1 bit no_alarm; inverse of alarm
+- B: 2 bit battery state; 0b11 if battery is o.k.
+- F: 4 bit flags (always 0b0000)
+
+
+Examples:
+---------
+[Bresser Water Leakage Sensor, PN 7009975]
+
+[00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25]
+
+ C7 70 35 97 04 08 57 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF FF FF FF FF [CH7]
+ DF 7D 36 49 27 09 56 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF FF FF FF FF [CH6]
+ 9E 30 79 84 33 06 55 70 00 00 00 00 00 00 00 00 03 FF FD DF FF BF FF DF FF FF [CH5]
+ E2 C8 68 27 91 24 54 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF FF FF FF FF [CH4]
+ B3 DA 55 57 17 40 53 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF FF FF FF FB [CH3]
+ 37 FA 84 73 03 02 52 70 00 00 00 00 00 00 00 00 03 FF FF FF DF FF FF FF FF FF [CH2]
+ 27 F3 80 02 52 88 51 70 00 00 00 00 00 00 00 00 03 FF FF FF FF FF DF FF FF FF [CH1]
+ A6 FB 80 02 52 88 59 70 00 00 00 00 00 00 00 00 03 FD F7 FF FF BF FF FF FF FF [CH1+NSTARTUP]
+ A6 FB 80 02 52 88 59 B0 00 00 00 00 00 00 00 00 03 FF FF FF FD FF F7 FF FF FF [CH1+NSTARTUP+ALARM]
+ A6 FB 80 02 52 88 59 70 00 00 00 00 00 00 00 00 03 FF FF BF F7 F7 FD 7F FF FF [CH1+NSTARTUP]
+ [Reset]
+ C0 10 36 79 37 09 51 70 00 00 00 00 00 00 00 00 01 1E FD FD FF FF FF DF FF FF [CH1]
+ C0 10 36 79 37 09 51 B0 00 00 00 00 00 00 00 00 03 FE FD FF AF FF FF FF FF FD [CH1+ALARM]
+ [Reset]
+ 71 9C 54 81 72 09 51 40 00 00 00 00 00 00 00 00 0F FF FF FF FF FF FF DF FF FE [CH1+BATT_LO]
+ 71 9C 54 81 72 09 51 40 00 00 00 00 00 00 00 00 0F FE FF FF FF FF FB FF FF FF
+ 71 9C 54 81 72 09 51 40 00 00 00 00 00 00 00 00 07 FD F7 FF DF FF FF DF FF FF
+ 71 9C 54 81 72 09 51 80 00 00 00 00 00 00 00 00 1F FF FF F7 FF FF FF FF FF FF [CH1+BATT_LO+ALARM]
+ F0 94 54 81 72 09 59 40 00 00 00 00 00 00 00 00 0F FF DF FF FF FF FF BF FD F7 [CH1+BATT_LO+NSTARTUP]
+ F0 94 54 81 72 09 59 80 00 00 00 00 00 00 00 00 03 FF B7 FF ED FF FF FF DF FF [CH1+BATT_LO+NSTARTUP+ALARM]
+
+ */
+
+#include "decoder.h"
+
+static int bresser_leakage_decode(r_device *decoder, bitbuffer_t *bitbuffer)
+{
+    uint8_t const preamble_pattern[] = {0xaa, 0xaa, 0x2d, 0xd4};
+    data_t *data;
+    uint8_t msg[18];
+    uint32_t sensor_id;
+    uint8_t s_type;
+    uint8_t chan;
+    uint8_t startup;
+    uint8_t battery_ok;
+    int alarm;
+    int no_alarm;
+    int decode_ok;
+
+    if (bitbuffer->num_rows != 1
+            || bitbuffer->bits_per_row[0] < 160
+            || bitbuffer->bits_per_row[0] > 440) {
+        decoder_logf(decoder, 2, __func__, "bit_per_row %u out of range", bitbuffer->bits_per_row[0]);
+        return DECODE_ABORT_EARLY; // Unrecognized data
+    }
+
+    unsigned start_pos = bitbuffer_search(bitbuffer, 0, 0,
+            preamble_pattern, sizeof (preamble_pattern) * 8);
+
+    if (start_pos >= bitbuffer->bits_per_row[0]) {
+        return DECODE_ABORT_LENGTH;
+    }
+    start_pos += sizeof (preamble_pattern) * 8;
+
+    unsigned len = bitbuffer->bits_per_row[0] - start_pos;
+    if (len < sizeof(msg) * 8) {
+        decoder_logf(decoder, 2, __func__, "%u too short", len);
+        return DECODE_ABORT_LENGTH; // message too short
+    }
+
+    bitbuffer_extract_bytes(bitbuffer, 0, start_pos, msg, sizeof(msg) * 8);
+
+    decoder_log_bitrow(decoder, 2, __func__, msg, sizeof(msg) * 8, "");
+
+    // CRC check
+    uint16_t crc_calculated = crc16(&msg[2], 5, 0x1021, 0x0000);
+    uint16_t crc_received = msg[0] << 8 | msg[1];
+    decoder_logf(decoder, 2, __func__, "CRC 0x%04X = 0x%04X", crc_calculated, crc_received);
+    if (crc_received != crc_calculated) {
+        decoder_logf(decoder, 1, __func__, "CRC check failed (0x%04X != 0x%04X)", crc_calculated, crc_received);
+        return DECODE_FAIL_MIC;
+    }
+
+    sensor_id     = ((uint32_t)msg[2] << 24) | (msg[3] << 16) | (msg[4] << 8) | (msg[5]);
+    s_type        = msg[6] >> 4;
+    chan          = (msg[6] & 0x7);
+    battery_ok    = ((msg[7] & 0x30) != 0x00) ? 1 : 0;
+    startup       = (msg[6] >> 3) & 1;
+    alarm         = ((msg[7] & 0x80) == 0x80) ? 1 : 0;
+    no_alarm      = ((msg[7] & 0x40) == 0x40) ? 1 : 0;
+
+    // Sanity checks
+    decode_ok = (s_type == SENSOR_TYPE_LEAKAGE) &&
+            (alarm != no_alarm) &&
+            (chan != 0);
+
+   if (!decode_ok)
+       return 0;
+
+    /* clang-format off */
+    data = data_make(
+            "model",            "",             DATA_STRING, "Bresser-Leakage",
+            "id",               "",             DATA_FORMAT, "%08x",   DATA_INT,    sensor_id,
+            "channel",          "",             DATA_INT,    chan,
+            "battery_ok",       "Battery",      DATA_INT,    battery_ok,
+            "alarm",            "Alarm",        DATA_INT,    alarm,
+            "startup",          "Startup",      DATA_COND,   startup,  DATA_INT,    startup,
+            NULL);
+    /* clang-format on */
+
+    decoder_output_data(decoder, data);
+    return 1;
+}
+
+static char const *const output_fields[] = {
+        "model",
+        "id",
+        "channel",
+        "battery_ok",
+        "startup",
+        "alarm",
+        NULL,
+};
+
+r_device const bresser_leakage = {
+        .name        = "Bresser water leakage",
+        .modulation  = FSK_PULSE_PCM,
+        .short_width = 124,
+        .long_width  = 124,
+        .reset_limit = 25000,
+        .decode_fn   = &bresser_leakage_decode,
+        .fields      = output_fields,
+};