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