hmsniff: more compact output by default
[hmcfgusb] / hmsniff.c
1 /* HM-sniffer for HM-CFG-USB
2 *
3 * Copyright (c) 2013-15 Michael Gernoth <michael@gernoth.net>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <poll.h>
31 #include <errno.h>
32 #include <time.h>
33 #include <sys/time.h>
34 #include <libusb-1.0/libusb.h>
35
36 #include "version.h"
37 #include "hexdump.h"
38 #include "hmcfgusb.h"
39
40 static int verbose = 0;
41
42 /* See HMConfig.pm */
43 char *hm_message_types(uint8_t type, uint8_t subtype)
44 {
45 switch(type) {
46 case 0x00:
47 return "Device Info";
48 break;
49 case 0x01:
50 return "Configuration";
51 break;
52 case 0x02:
53 if (subtype >= 0x80 && subtype <= 0x8f) {
54 return "NACK";
55 } else if (subtype == 0x01) {
56 return "ACKinfo";
57 } else if (subtype == 0x04) {
58 return "AESrequest";
59 }
60 return "ACK";
61 break;
62 case 0x03:
63 return "AESreply";
64 break;
65 case 0x04:
66 return "AESkey";
67 break;
68 case 0x10:
69 return "Information";
70 break;
71 case 0x11:
72 return "SET";
73 break;
74 case 0x12:
75 return "HAVE_DATA";
76 break;
77 case 0x3e:
78 return "Switch";
79 break;
80 case 0x3f:
81 return "Timestamp";
82 break;
83 case 0x40:
84 return "Remote";
85 break;
86 case 0x41:
87 return "Sensor";
88 break;
89 case 0x53:
90 return "Water sensor";
91 break;
92 case 0x58:
93 return "Climate event";
94 break;
95 case 0x5a:
96 return "Thermal control";
97 break;
98 case 0x70:
99 return "Weather event";
100 break;
101 default:
102 return "?";
103 break;
104 }
105 }
106
107 static void dissect_hm(uint8_t *buf, int len)
108 {
109 struct timeval tv;
110 struct tm *tmp;
111 char ts[32];
112 static int count = 0;
113 int i;
114
115 gettimeofday(&tv, NULL);
116 tmp = localtime(&tv.tv_sec);
117 memset(ts, 0, sizeof(ts));
118 strftime(ts, sizeof(ts)-1, "%Y-%m-%d %H:%M:%S", tmp);
119
120 if (verbose) {
121 printf("%s.%06ld: ", ts, tv.tv_usec);
122
123 for (i = 0; i < len; i++) {
124 printf("%02X", buf[i]);
125 }
126 printf("\n");
127 printf("Packet information:\n");
128 printf("\tLength: %u\n", buf[0]);
129 printf("\tMessage ID: %u\n", buf[1]);
130 printf("\tSender: %02x%02x%02x\n", buf[4], buf[5], buf[6]);
131 printf("\tReceiver: %02x%02x%02x\n", buf[7], buf[8], buf[9]);
132 printf("\tControl Byte: 0x%02x\n", buf[2]);
133 printf("\t\tFlags: ");
134 if (buf[2] & (1 << 0)) printf("WAKEUP ");
135 if (buf[2] & (1 << 1)) printf("WAKEMEUP ");
136 if (buf[2] & (1 << 2)) printf("CFG ");
137 if (buf[2] & (1 << 3)) printf("? ");
138 if (buf[2] & (1 << 4)) printf("BURST ");
139 if (buf[2] & (1 << 5)) printf("BIDI ");
140 if (buf[2] & (1 << 6)) printf("RPTED ");
141 if (buf[2] & (1 << 7)) printf("RPTEN ");
142 printf("\n");
143 printf("\tMessage type: %s (0x%02x 0x%02x)\n", hm_message_types(buf[3], buf[10]), buf[3], buf[10]);
144 printf("\tMessage: ");
145 for (i = 10; i < len; i++) {
146 printf("%02X", buf[i]);
147 }
148 printf("\n");
149
150 printf("\n");
151 } else {
152 if (!(count++ % 20))
153 printf(" LL NR FL CM sender recvr payload\n");
154 printf("%s.%03ld: %02X %02X %02X %02X %02X%02X%02X %02X%02X%02X ",
155 ts, tv.tv_usec/1000,
156 buf[0], buf[1], buf[2], buf[3],
157 buf[4], buf[5], buf[6],
158 buf[7], buf[8], buf[9]);
159
160 for (i = 10; i < len; i++) {
161 printf("%02X", buf[i]);
162 }
163 printf("%s(%s)\n", (i>10)?" ":"", hm_message_types(buf[3], buf[10]));
164 }
165 }
166
167 struct recv_data {
168 int wrong_hmid;
169 };
170
171 static int parse_hmcfgusb(uint8_t *buf, int buf_len, void *data)
172 {
173 struct recv_data *rdata = data;
174
175 if (buf_len < 1)
176 return 1;
177
178 switch(buf[0]) {
179 case 'E':
180 dissect_hm(buf + 13, buf[13] + 1);
181 break;
182 case 'H':
183 if ((buf[27] != 0x00) ||
184 (buf[28] != 0x00) ||
185 (buf[29] != 0x00)) {
186 printf("hmId is currently set to: %02x%02x%02x\n", buf[27], buf[28], buf[29]);
187 rdata->wrong_hmid = 1;
188 }
189 break;
190 case 'R':
191 case 'I':
192 break;
193 default:
194 hexdump(buf, buf_len, "Unknown> ");
195 break;
196 }
197
198 return 1;
199 }
200
201 void hmsniff_syntax(char *prog)
202 {
203 fprintf(stderr, "Syntax: %s options\n\n", prog);
204 fprintf(stderr, "Possible options:\n");
205 fprintf(stderr, "\t-v\t\tverbose mode\n");
206 fprintf(stderr, "\t-V\t\tshow version (" VERSION ")\n");
207
208 }
209
210 int main(int argc, char **argv)
211 {
212 struct hmcfgusb_dev *dev;
213 struct recv_data rdata;
214 int quit = 0;
215 int opt;
216
217 while((opt = getopt(argc, argv, "vV")) != -1) {
218 switch (opt) {
219 case 'v':
220 verbose = 1;
221 break;
222 case 'V':
223 printf("hmsniff " VERSION "\n");
224 printf("Copyright (c) 2013-15 Michael Gernoth\n\n");
225 exit(EXIT_SUCCESS);
226 case 'h':
227 case ':':
228 case '?':
229 default:
230 hmsniff_syntax(argv[0]);
231 exit(EXIT_FAILURE);
232 break;
233 }
234 }
235
236 hmcfgusb_set_debug(0);
237
238 do {
239 memset(&rdata, 0, sizeof(rdata));
240 rdata.wrong_hmid = 0;
241
242 dev = hmcfgusb_init(parse_hmcfgusb, &rdata);
243 if (!dev) {
244 fprintf(stderr, "Can't initialize HM-CFG-USB, retrying in 1s...\n");
245 sleep(1);
246 continue;
247 }
248 printf("HM-CFG-USB opened!\n");
249
250 hmcfgusb_send_null_frame(dev, 1);
251 hmcfgusb_send(dev, (unsigned char*)"K", 1, 1);
252
253 while(!quit) {
254 int fd;
255
256 if (rdata.wrong_hmid) {
257 printf("changing hmId to 000000, this might reboot the device!\n");
258 hmcfgusb_send(dev, (unsigned char*)"A\00\00\00", 4, 1);
259 rdata.wrong_hmid = 0;
260 hmcfgusb_send(dev, (unsigned char*)"K", 1, 1);
261 }
262 fd = hmcfgusb_poll(dev, 1000);
263 if (fd >= 0) {
264 fprintf(stderr, "activity on unknown fd %d!\n", fd);
265 continue;
266 } else if (fd == -1) {
267 if (errno) {
268 if (errno != ETIMEDOUT) {
269 perror("hmcfgusb_poll");
270 break;
271 } else {
272 /* periodically wakeup the device */
273 hmcfgusb_send_null_frame(dev, 1);
274 }
275 }
276 }
277 }
278
279 hmcfgusb_close(dev);
280 } while (!quit);
281
282 hmcfgusb_exit();
283
284 return EXIT_SUCCESS;
285 }
Impressum, Datenschutz