From 4084ea623e01a6ac114e7de6b2ee224dcf3f28c0 Mon Sep 17 00:00:00 2001
From: "Christian W. Zuckschwerdt" <christian@zuckschwerdt.org>
Date: Fri, 7 Jan 2022 19:40:54 +0100
Subject: [PATCH] Add support for Yale HSA (#1929)

---
 README.md                    |   1 +
 conf/rtl_433.example.conf    |   1 +
 include/rtl_433_devices.h    |   1 +
 src/CMakeLists.txt           |   1 +
 src/devices/yale_hsa.c       | 131 +++++++++++++++++++++++++++++++++++
 vs15/rtl_433.vcxproj         |   1 +
 vs15/rtl_433.vcxproj.filters |   3 +
 7 files changed, 139 insertions(+)
 create mode 100644 src/devices/yale_hsa.c

diff --git a/README.md b/README.md
index 8c7ceb0a..32e826cc 100644
--- a/README.md
+++ b/README.md
@@ -293,6 +293,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
     [207]  SmartFire Proflame 2 remote control
     [208]  AVE TPMS
     [209]  SimpliSafe Gen 3 Home Security System
+    [210]  Yale HSA (Home Security Alarm), YES-Alarmkit
 
 * Disabled by default, use -R n or -G
 
diff --git a/conf/rtl_433.example.conf b/conf/rtl_433.example.conf
index e9305fec..2c177c6b 100644
--- a/conf/rtl_433.example.conf
+++ b/conf/rtl_433.example.conf
@@ -430,6 +430,7 @@ stop_after_successful_events false
   protocol 207 # SmartFire Proflame 2 remote control
   protocol 208 # AVE TPMS
   protocol 209 # SimpliSafe Gen 3 Home Security System
+  protocol 210 # Yale HSA (Home Security Alarm), YES-Alarmkit
 
 ## Flex devices (command line option "-X")
 
diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h
index 226d0f2a..63a9de75 100644
--- a/include/rtl_433_devices.h
+++ b/include/rtl_433_devices.h
@@ -217,6 +217,7 @@
     DECL(proflame2) \
     DECL(tpms_ave) \
     DECL(simplisafe_gen3) \
+    DECL(yale_hsa) \
 
     /* Add new decoders here. */
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a65f8365..6f37bf86 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -220,6 +220,7 @@ add_library(r_433 STATIC
     devices/wt450.c
     devices/x10_rf.c
     devices/x10_sec.c
+    devices/yale_hsa.c
 )
 
 if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" MATCHES "Clang")
diff --git a/src/devices/yale_hsa.c b/src/devices/yale_hsa.c
new file mode 100644
index 00000000..ad459288
--- /dev/null
+++ b/src/devices/yale_hsa.c
@@ -0,0 +1,131 @@
+/** @file
+    Yale HSA (Home Security Alarm) protocol.
+
+    Copyright (C) 2022 Christian W. Zuckschwerdt <zany@triq.net>
+
+    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.
+*/
+
+#include "decoder.h"
+
+/**
+Yale HSA (Home Security Alarm) protocol.
+
+Yale HSA Alarms, YES-Alarmkit:
+- Yale HSA6010 Door/Window Contact
+- Yale HSA6080 Keypad
+- Yale HSA6020 Motion PIR
+- Yale HSA6060 Remote Keyfob
+
+A message is made up of 6 packets and then repeats.
+Packets are 13 bits, start with 0x5 and a end-of-message flag, then 8 bit data.
+
+Actually data should be in the gaps, which are tighter timings of 368 / 978 us.
+
+The 6 packets combined decode as
+
+Data Layout:
+
+    ID:16h TYPE:8h STATE:8b EVENT:8h CHK:8h
+
+Or perhaps?
+
+    ID:16h TYPE:12h STATE:8b EVENT:4h CHK:8h
+
+The checksum is just remainder of adding the 5 messages bytes, i.e. adding 6 bytes checks to zero.
+
+Guessed data so far:
+- Sensor types: ac1, ad1: window sensor, 153: PIR
+- Events 1: trigger, 3: binding, 4: tamper
+- State: Could be battery?
+
+Get Raw data with:
+
+    rtl_433 -R 0 -X 'n=name,m=OOK_PWM,s=850,l=1460,y=5380,r=1500' ~/Desktop/Yale-6010.ook
+
+*/
+
+static int yale_hsa_decode(r_device *decoder, bitbuffer_t *bitbuffer)
+{
+    // Require at least 6 rows
+    if (bitbuffer->num_rows < 6)
+        return DECODE_ABORT_EARLY;
+
+    uint8_t msg[6] = {0};
+    for (int row = 0; row < bitbuffer->num_rows; ++row) {
+        // Find one full message
+        int ok = 0;
+        for (int i = 0; i < 6; ++i, ++row) {
+            if (bitbuffer->bits_per_row[row] != 13)
+                break; // wrong length
+            uint8_t *b = bitbuffer->bb[row];
+            if ((b[0] & 0xf0) != 0x50)
+                break; // wrong sync
+            int eom = (b[0] & 0x08);
+            if ((i < 5 && eom) || (i == 5 && !eom))
+                break; // wrong end-of-message
+            bitbuffer_extract_bytes(bitbuffer, row, 5, &msg[i], 8);
+            if (i == 5)
+                ok = 1;
+        }
+        // Skip to end-of-message on error
+        if (!ok) {
+            for (; row < bitbuffer->num_rows; ++row) {
+                uint8_t *b = bitbuffer->bb[row];
+                int eom = (b[0] & 0x08);
+                if (eom)
+                    break; // end-of-message
+            }
+            continue;
+        }
+        // Message found
+        int chk = add_bytes(msg, 6);
+        if (chk & 0xff)
+            continue; // bad checksum
+
+        // Get the data
+        int id = (msg[0] << 8) | (msg[1]);
+        int stype = (msg[2]);
+        int state = (msg[3]);
+        int event = (msg[4]);
+
+        /* clang-format off */
+        data_t *data = data_make(
+                "model",        "",             DATA_STRING, "Yale-HSA",
+                "id",           "",             DATA_FORMAT, "%04x", DATA_INT, id,
+                "stype",        "Sensor type",  DATA_FORMAT, "%02x", DATA_INT, stype,
+                "state",        "State",        DATA_FORMAT, "%02x", DATA_INT, state,
+                "event",        "Event",        DATA_FORMAT, "%02x", DATA_INT, event,
+                "mic",          "Integrity",    DATA_STRING, "CHECKSUM",
+                NULL);
+        /* clang-format on */
+
+        decoder_output_data(decoder, data);
+        return 1;
+    }
+    return 0;
+}
+
+static char *output_fields[] = {
+        "model",
+        "id",
+        "stype",
+        "state",
+        "event",
+        "mic",
+        NULL,
+};
+
+r_device yale_hsa = {
+        .name        = "Yale HSA (Home Security Alarm), YES-Alarmkit",
+        .modulation  = OOK_PULSE_PWM,
+        .short_width = 850,
+        .long_width  = 1460,
+        .sync_width  = 5380,
+        .reset_limit = 1500,
+        .decode_fn   = &yale_hsa_decode,
+        .fields      = output_fields,
+};
diff --git a/vs15/rtl_433.vcxproj b/vs15/rtl_433.vcxproj
index ffabc5c0..168ce239 100644
--- a/vs15/rtl_433.vcxproj
+++ b/vs15/rtl_433.vcxproj
@@ -351,6 +351,7 @@ COPY ..\..\libusb\MS64\dll\libusb*.dll $(TargetDir)</Command>
     <ClCompile Include="..\src\devices\wt450.c" />
     <ClCompile Include="..\src\devices\x10_rf.c" />
     <ClCompile Include="..\src\devices\x10_sec.c" />
+    <ClCompile Include="..\src\devices\yale_hsa.c" />
     <ClCompile Include="..\src\getopt\getopt.c" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/vs15/rtl_433.vcxproj.filters b/vs15/rtl_433.vcxproj.filters
index bb9d6a7f..ea8fbae7 100644
--- a/vs15/rtl_433.vcxproj.filters
+++ b/vs15/rtl_433.vcxproj.filters
@@ -790,5 +790,8 @@
     <ClCompile Include="..\src\devices\x10_sec.c">
       <Filter>Source Files\devices</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\devices\yale_hsa.c">
+      <Filter>Source Files\devices</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>