1 /** @file
2     Honeywell ActivLink, wireless door bell, PIR Motion sensor.
3 
4     Copyright (C) 2018 Benjamin Larsson
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 */
11 
12 /**
13 Honeywell ActivLink, wireless door bell, PIR Motion sensor.
14 
15 Frame documentation courtesy of https://github.com/klohner/honeywell-wireless-doorbell
16 
17 Frame bits used in Honeywell RCWL300A, RCWL330A, Series 3, 5, 9 and all Decor Series:
18 
19 Wireless Chimes
20 
21     0000 0000 1111 1111 2222 2222 3333 3333 4444 4444 5555 5555
22     7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210
23     XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX.. XXX. .... KEY DATA (any change and receiver doesn't seem to
24                                                                           recognize signal)
25     XXXX XXXX XXXX XXXX XXXX .... .... .... .... .... .... .... KEY ID (different for each transmitter)
26     .... .... .... .... .... 0000 00.. 0000 0000 00.. 000. .... KEY UNKNOWN 0 (always 0 in devices I've tested)
27     .... .... .... .... .... .... ..XX .... .... .... .... .... DEVICE TYPE (10 = doorbell, 01 = PIR Motion sensor)
28     .... .... .... .... .... .... .... .... .... ..XX ...X XXX. FLAG DATA (may be modified for possible effects on
29                                                                            receiver)
30     .... .... .... .... .... .... .... .... .... ..XX .... .... ALERT (00 = normal, 01 or 10 = right-left halo light
31                                                                        pattern, 11 = full volume alarm)
32     .... .... .... .... .... .... .... .... .... .... ...X .... SECRET KNOCK (0 = default, 1 if doorbell is pressed 3x
33                                                                               rapidly)
34     .... .... .... .... .... .... .... .... .... .... .... X... RELAY (1 if signal is a retransmission of a received
35                                                                        transmission, only some models)
36     .... .... .... .... .... .... .... .... .... .... .... .X.. FLAG UNKNOWN (0 = default, but 1 is accepted and I don't
37                                                                               oberserve any effects)
38     .... .... .... .... .... .... .... .... .... .... .... ..X. LOWBAT (1 if battery is low, receiver gives low battery
39                                                                         alert)
40     .... .... .... .... .... .... .... .... .... .... .... ...X PARITY (LSB of count of set bits in previous 47 bits)
41 */
42 
43 #include "decoder.h"
44 
honeywell_wdb_callback(r_device * decoder,bitbuffer_t * bitbuffer)45 static int honeywell_wdb_callback(r_device *decoder, bitbuffer_t *bitbuffer)
46 {
47     int row, secret_knock, relay, battery, parity;
48     uint8_t *bytes;
49     data_t *data;
50     unsigned int device, tmp;
51     char *class, *alert;
52 
53     // The device transmits many rows, check for 4 matching rows.
54     row = bitbuffer_find_repeated_row(bitbuffer, 4, 48);
55     if (row < 0) {
56         return DECODE_ABORT_EARLY;
57     }
58     bytes = bitbuffer->bb[row];
59 
60     if (bitbuffer->bits_per_row[row] != 48)
61         return DECODE_ABORT_LENGTH;
62 
63     bitbuffer_invert(bitbuffer);
64 
65     /* Parity check (must be EVEN) */
66     parity = parity_bytes(bytes, 6);
67 
68     // No need to decode/extract values for simple test
69     if ((!bytes[0] && !bytes[2] && !bytes[4] && !bytes[5])
70        || (bytes[0] == 0xff && bytes[2] == 0xff && bytes[4] == 0xff && bytes[5] == 0xff)) {
71         if (decoder->verbose > 1) {
72             fprintf(stderr, "%s: DECODE_FAIL_SANITY data all 0x00 or 0xFF\n", __func__);
73         }
74         return DECODE_FAIL_SANITY;
75     }
76 
77     if (parity) { // ODD parity detected
78         if (decoder->verbose > 1) {
79             bitbuffer_print(bitbuffer);
80             fprintf(stderr, "honeywell_wdb: Parity check on row %d failed (%d)\n", row, parity);
81         }
82         return DECODE_FAIL_MIC;
83     }
84 
85     device = bytes[0] << 12 | bytes[1] << 4 | (bytes[2]&0xF);
86     tmp = (bytes[3]&0x30) >> 4;
87     switch (tmp) {
88         case 0x1: class = "PIR-Motion"; break;
89         case 0x2: class = "Doorbell"; break;
90         default:  class = "Unknown"; break;
91     }
92     tmp = bytes[4]&0x3;
93     switch (tmp) {
94         case 0x0: alert = "Normal"; break;
95         case 0x1:
96         case 0x2: alert = "High"; break;
97         case 0x3: alert = "Full"; break;
98         default:  alert = "Unknown"; break;
99     }
100     secret_knock = (bytes[5]&0x10) >> 4;
101     relay = (bytes[5]&0x8) >> 3;
102     battery = (bytes[5]&0x2) >> 1;
103 
104     /* clang-format off */
105     data = data_make(
106             "model",         "",            DATA_STRING, "Honeywell-ActivLink",
107             "subtype",       "Class",       DATA_FORMAT, "%s",   DATA_STRING, class,
108             "id",            "Id",          DATA_FORMAT, "%x",   DATA_INT,    device,
109             "battery_ok",    "Battery",     DATA_INT,    !battery,
110             "alert",         "Alert",       DATA_FORMAT, "%s",   DATA_STRING, alert,
111             "secret_knock",  "Secret Knock",DATA_FORMAT, "%d",   DATA_INT,    secret_knock,
112             "relay",         "Relay",       DATA_FORMAT, "%d",   DATA_INT,    relay,
113             "mic",           "Integrity",   DATA_STRING, "PARITY",
114             NULL);
115     /* clang-format on */
116 
117     decoder_output_data(decoder, data);
118     return 1;
119 }
120 
121 static char *output_fields[] = {
122         "model",
123         "subtype",
124         "id",
125         "battery_ok",
126         "alert",
127         "secret_knock",
128         "relay",
129         "mic",
130         NULL,
131 };
132 
133 r_device honeywell_wdb = {
134         .name        = "Honeywell ActivLink, Wireless Doorbell",
135         .modulation  = OOK_PULSE_PWM,
136         .short_width = 175,
137         .long_width  = 340,
138         .gap_limit   = 0,
139         .reset_limit = 5000,
140         .sync_width  = 500,
141         .decode_fn   = &honeywell_wdb_callback,
142         .disabled    = 0,
143         .fields      = output_fields,
144 };
145 
146 r_device honeywell_wdb_fsk = {
147         .name        = "Honeywell ActivLink, Wireless Doorbell (FSK)",
148         .modulation  = FSK_PULSE_PWM,
149         .short_width = 160,
150         .long_width  = 320,
151         .gap_limit   = 0,
152         .reset_limit = 560,
153         .sync_width  = 500,
154         .decode_fn   = &honeywell_wdb_callback,
155         .disabled    = 0,
156         .fields      = output_fields,
157 };
158