Asterisk - The Open Source Telephony Project GIT-master-7e7a603
app_alarmreceiver.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2004 - 2005 Steve Rodgers
5 *
6 * Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief Central Station Alarm receiver for Ademco Contact ID
22 * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
23 *
24 * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
25 *
26 * Use at your own risk. Please consult the GNU GPL license document included with Asterisk. *
27 *
28 * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
29 *
30 * \ingroup applications
31 */
32
33/*! \li \ref app_alarmreceiver.c uses the configuration file \ref alarmreceiver.conf
34 * \addtogroup configuration_file Configuration Files
35 */
36
37/*!
38 * \page alarmreceiver.conf alarmreceiver.conf
39 * \verbinclude alarmreceiver.conf.sample
40 */
41
42/*** MODULEINFO
43 <support_level>extended</support_level>
44 ***/
45
46#include "asterisk.h"
47
48#include <math.h>
49#include <sys/wait.h>
50#include <sys/time.h>
51
52#include "asterisk/lock.h"
53#include "asterisk/file.h"
54#include "asterisk/channel.h"
55#include "asterisk/pbx.h"
56#include "asterisk/module.h"
57#include "asterisk/translate.h"
58#include "asterisk/app.h"
59#include "asterisk/dsp.h"
60#include "asterisk/config.h"
61#include "asterisk/localtime.h"
62#include "asterisk/callerid.h"
63#include "asterisk/astdb.h"
64#include "asterisk/utils.h"
67
68#define ALMRCV_CONFIG "alarmreceiver.conf"
69#define UNKNOWN_FORMAT "UNKNOWN_FORMAT"
70
71#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
72/*
73 AAAA _ID_ P CCC XX ZZZ S
74
75where AAAA is the account number, _ID_ is 18 or 98, P is the pin status (alarm or restore), CCC
76is the alarm code which is pre-defined by Ademco (but you may be able to reprogram it in the panel), XX
77is the dialer group, partition or area number, ZZZ is the zone or user number and S is the checksum
78*/
79
80#define ADEMCO_EXPRESS_4_1 "ADEMCO_EXPRESS_4_1"
81/*
82 AAAA _ID_ C S
83
84where AAAA is the account number, _ID_ is 17, C is the alarm code and S is the checksum.
85*/
86
87#define ADEMCO_EXPRESS_4_2 "ADEMCO_EXPRESS_4_2"
88/*
89 AAAA _ID_ C Z S
90
91where AAAA is the account number, _ID_ is 27, C is the alarm code, Z is the zone or user number and S is the checksum.
92*/
93
94#define ADEMCO_HIGH_SPEED "ADEMCO_HIGH_SPEED"
95/*
96 AAAA _ID_ PPPP PPPP X S
97
98where AAAA is the account number, _ID_ is 55, PPPP PPPP is the status of each zone, X
99is a special digit which describes the type of information in the PPPP PPPP fields and S is checksum.
100Each P field contains one of the following values:
101 1 new alarm 3 new restore 5 normal
102 2 new opening 4 new closing 6 outstanding
103The X field contains one of the following values:
104 0 AlarmNet messages
105 1 ambush or duress
106 2 opening by user (the first P field contains the user number)
107 3 bypass (the P fields indicate which zones are bypassed)
108 4 closing by user (the first P field contain the user number)
109 5 trouble (the P fields contain which zones are in trouble)
110 6 system trouble
111 7 normal message (the P fields indicate zone status)
112 8 low battery (the P fields indicate zone status)
113 9 test (the P fields indicate zone status)
114*/
115#define ADEMCO_SUPER_FAST "ADEMCO_SUPER_FAST"
116/*
117 AAAA _ID_ PPPP PPPP X
118where AAA is the account number, _ID_ is 56
119*/
120
121#define ADEMCO_MSG_TYPE_1 "18"
122#define ADEMCO_MSG_TYPE_2 "98"
123#define ADEMCO_MSG_TYPE_3 "17"
124#define ADEMCO_MSG_TYPE_4 "27"
125#define ADEMCO_MSG_TYPE_5 "55"
126#define ADEMCO_MSG_TYPE_6 "56"
127
128#define ADEMCO_AUDIO_CALL_NEXT "606"
129
130struct {
131 char digit;
132 char weight;
133} digits_mapping[] = { {'0', 10}, {'1', 1} , {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5},
134 {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'*', 11}, {'#', 12},
135 {'A', 13}, {'B', 14}, {'C', 15} };
136
138 char data[17];
140};
141
143
144struct timeval call_start_time;
145
146static const char app[] = "AlarmReceiver";
147/*** DOCUMENTATION
148 <application name="AlarmReceiver" language="en_US">
149 <synopsis>
150 Provide support for receiving alarm reports from a burglar or fire alarm panel.
151 </synopsis>
152 <syntax />
153 <description>
154 <para>This application should be called whenever there is an alarm panel calling in to dump its events.
155 The application will handshake with the alarm panel, and receive events, validate them, handshake them,
156 and store them until the panel hangs up. Once the panel hangs up, the application will run the system
157 command specified by the eventcmd setting in <filename>alarmreceiver.conf</filename> and pipe the
158 events to the standard input of the application.
159 The configuration file also contains settings for DTMF timing, and for the loudness of the
160 acknowledgement tones.</para>
161 <note><para>Few Ademco DTMF signalling formats are detected automatically: Contact ID, Express 4+1,
162 Express 4+2, High Speed and Super Fast.</para></note>
163 <para>The application is affected by the following variables:</para>
164 <variablelist>
165 <variable name="ALARMRECEIVER_CALL_LIMIT">
166 <para>Maximum call time, in milliseconds.</para>
167 <para>If set, this variable causes application to exit after the specified time.</para>
168 </variable>
169 <variable name="ALARMRECEIVER_RETRIES_LIMIT">
170 <para>Maximum number of retries per call.</para>
171 <para>If set, this variable causes application to exit after the specified number of messages.</para>
172 </variable>
173 </variablelist>
174 </description>
175 <see-also>
176 <ref type="filename">alarmreceiver.conf</ref>
177 </see-also>
178 </application>
179 ***/
180
181/* Config Variables */
182static int fdtimeout = 2000;
183static int sdtimeout = 200;
184static int answait = 1250;
185static int toneloudness = 4096;
187static int no_group_meta = 0;
188static char event_spool_dir[128] = {'\0'};
189static char event_app[128] = {'\0'};
190static char db_family[128] = {'\0'};
191static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
192
193/* Misc variables */
194static char event_file[14] = "/event-XXXXXX";
195
196/*!
197 * \brief Attempt to access a database variable and increment it
198 *
199 * \note Only if the user defined db-family in alarmreceiver.conf
200 *
201 * The alarmreceiver app will write statistics to a few variables
202 * in this family if it is defined. If the new key doesn't exist in the
203 * family, then create it and set its value to 1.
204 *
205 * \param key A database key to increment
206 */
207static void database_increment(char *key)
208{
209 unsigned v;
210 char value[16];
211
213 return; /* If not defined, don't do anything */
214 }
215
216 if (ast_db_get(db_family, key, value, sizeof(value) - 1)) {
217 ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
218 /* Guess we have to create it */
219 ast_db_put(db_family, key, "1");
220 return;
221 }
222
223 sscanf(value, "%30u", &v);
224 v++;
225
226 ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
227 snprintf(value, sizeof(value), "%u", v);
228
229 if (ast_db_put(db_family, key, value)) {
230 ast_verb(4, "AlarmReceiver: database_increment write error\n");
231 }
232
233 return;
234}
235
236/*!
237 * \brief Receive a fixed length DTMF string.
238 *
239 * \note Doesn't give preferential treatment to any digit,
240 * \note allow different timeout values for the first and all subsequent digits
241 *
242 * \param chan Asterisk Channel
243 * \param digit_string Digits String
244 * \param buf_size The size of the Digits String buffer
245 * \param expected Digits expected for this message type
246 * \param received Pointer to number of digits received so far
247 *
248 * \retval 0 if all digits were successfully received
249 * \retval 1 if a timeout occurred
250 * \retval -1 if the caller hung up or on channel errors
251 */
252static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int expected, int *received)
253{
254 int rtn = 0;
255 int r;
256 struct ast_frame *f;
257 struct timeval lastdigittime;
258
259 lastdigittime = ast_tvnow();
260 while (*received < expected && *received < buf_size - 1) {
261 /* If timed out, leave */
262 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((*received > 0) ? sdtimeout : fdtimeout)) {
263 ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", ast_channel_name(chan));
264 ast_debug(1, "AlarmReceiver: DTMF timeout on chan %s\n", ast_channel_name(chan));
265 rtn = 1;
266 break;
267 }
268
269 if ((r = ast_waitfor(chan, -1)) < 0) {
270 ast_debug(1, "Waitfor returned %d\n", r);
271 continue;
272 }
273
274 if ((f = ast_read(chan)) == NULL) {
275 rtn = -1;
276 break;
277 }
278
279 /* If they hung up, leave */
280 if ((f->frametype == AST_FRAME_CONTROL)
282 if (f->data.uint32) {
284 }
285 ast_frfree(f);
286 rtn = -1;
287 break;
288 }
289
290 /* If not DTMF, just do it again */
291 if (f->frametype != AST_FRAME_DTMF) {
292 ast_frfree(f);
293 continue;
294 }
295
296 /* Save digit */
297 digit_string[(*received)++] = f->subclass.integer;
298 ast_frfree(f);
299
300 lastdigittime = ast_tvnow();
301 }
302
303 /* Null terminate the end of the digit_string */
304 digit_string[*received] = '\0';
305
306 return rtn;
307}
308
309/*!
310 * \brief Write metadata to log file
311 *
312 * \param logfile Log File Pointer
313 * \param signalling_type Signaling Type
314 * \param chan Asterisk Channel
315 * \param no_checksum Expecting messages without checksum
316 *
317 * \retval 0 success
318 * \retval -1 failure
319 */
320static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
321{
322 struct timeval t;
323 struct ast_tm now;
324 char *cl;
325 char *cn;
326 char workstring[80];
327 char timestamp[80];
328
329 /* Extract the caller ID location */
330 ast_copy_string(workstring,
331 S_COR(ast_channel_caller(chan)->id.number.valid,
332 ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring));
333 ast_shrink_phone_number(workstring);
334 if (ast_strlen_zero(workstring)) {
335 cl = "<unknown>";
336 } else {
337 cl = workstring;
338 }
339 cn = S_COR(ast_channel_caller(chan)->id.name.valid,
340 ast_channel_caller(chan)->id.name.str, "<unknown>");
341
342 /* Get the current time */
343 t = ast_tvnow();
344 ast_localtime(&t, &now, NULL);
345
346 /* Format the time */
347 ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
348
349 if (no_group_meta && fprintf(logfile, "PROTOCOL=%s\n"
350 "CHECKSUM=%s\n"
351 "CALLINGFROM=%s\n"
352 "CALLERNAME=%s\n"
353 "TIMESTAMP=%s\n\n",
354 signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
355 return 0;
356 } else if (fprintf(logfile, "\n\n[metadata]\n\n"
357 "PROTOCOL=%s\n"
358 "CHECKSUM=%s\n"
359 "CALLINGFROM=%s\n"
360 "CALLERNAME=%s\n"
361 "TIMESTAMP=%s\n\n"
362 "[events]\n\n",
363 signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
364 return 0;
365 }
366
367 ast_verb(3, "AlarmReceiver: can't write metadata\n");
368 ast_debug(1, "AlarmReceiver: can't write metadata\n");
369 return -1;
370}
371
372/*!
373 * \brief Log a single event
374 *
375 * \param logfile Log File Pointer
376 * \param event Event Structure
377 *
378 * \retval 0 success
379 * \retval -1 failure
380 */
381static int write_event(FILE *logfile, event_node_t *event)
382{
383 if (fprintf(logfile, "%s%s\n", no_group_meta ? "event=" : "", event->data) < 0) {
384 return -1;
385 }
386
387 return 0;
388}
389
390/*!
391 * \brief Log events if configuration key logindividualevents is enabled or on exit
392 *
393 * \param chan Asterisk Channel
394 * \param signalling_type Signaling Type
395 * \param event Event Structure
396 * \param no_checksum Expecting messages without checksum
397 *
398 * \retval 0 success
399 * \retval -1 failure
400 */
401static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event, int no_checksum)
402{
403 char workstring[sizeof(event_spool_dir) + sizeof(event_file)] = "";
404 int fd;
405 FILE *logfile;
406 event_node_t *elp = event;
407
409
410 /* Make a template */
411 ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
412 strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
413
414 /* Make the temporary file */
415 fd = mkstemp(workstring);
416
417 if (fd == -1) {
418 ast_verb(3, "AlarmReceiver: can't make temporary file\n");
419 ast_debug(1, "AlarmReceiver: can't make temporary file\n");
420 return -1;
421 }
422
423 if ((logfile = fdopen(fd, "w")) == NULL) {
424 return -1;
425 }
426
427 /* Write the file */
428 if (write_metadata(logfile, signalling_type, chan, no_checksum)) {
429 fflush(logfile);
430 fclose(logfile);
431 return -1;
432 }
433
434 while ((elp != NULL) && (write_event(logfile, elp) == 0)) {
435 elp = elp->next;
436 }
437
438 fflush(logfile);
439 fclose(logfile);
440 }
441
442 return 0;
443}
444
445/*!
446 * \brief Verify Ademco checksum
447 * \since 11.0
448 *
449 * \param event Received DTMF String
450 * \param expected Number of Digits expected
451 *
452 * \retval 0 success
453 * \retval -1 failure
454 */
455static int ademco_verify_checksum(char *event, int expected)
456{
457 int checksum = 0;
458 int i, j;
459
460 for (j = 0; j < expected; j++) {
461 for (i = 0; i < ARRAY_LEN(digits_mapping); i++) {
462 if (digits_mapping[i].digit == event[j]) {
463 break;
464 }
465 }
466
467 if (i >= ARRAY_LEN(digits_mapping)) {
468 ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
469 return -1;
470 }
471
472 checksum += digits_mapping[i].weight;
473 }
474
475 /* Checksum is mod(15) of the total */
476 if (!(checksum % 15)) {
477 return 0;
478 }
479
480 return -1;
481}
482
483/*!
484 * \brief Send a single tone burst for a specified duration and frequency.
485 * \since 11.0
486 *
487 * \param chan Asterisk Channel
488 * \param tone_freq Frequency of the tone to send
489 * \param tone_duration Tone duration in ms
490 * \param delay Delay before sending the tone
491 *
492 * \retval 0 success
493 * \retval -1 failure
494 */
495static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int delay)
496{
497 if (delay && ast_safe_sleep(chan, delay)) {
498 return -1;
499 }
500
501 if (ast_playtones_start(chan, toneloudness, tone_freq, 0)) {
502 return -1;
503 }
504
505 if (ast_safe_sleep(chan, tone_duration)) {
506 return -1;
507 }
508
509 ast_playtones_stop(chan);
510 return 0;
511}
512
513/*!
514 * \brief Check if the message is in known and valid Ademco format
515 *
516 * \param signalling_type Expected signalling type for the message
517 * \param event event received
518 *
519 * \retval 0 The event is valid
520 * \retval -1 The event is not valid
521 */
522static int ademco_check_valid(char *signalling_type, char *event)
523{
524 if (!strcmp(signalling_type, UNKNOWN_FORMAT)) {
525 return 1;
526 }
527
528 if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
529 && strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
530 && strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
531 return -1;
532 }
533
534 if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_1) && strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
535 return -1;
536 }
537
538 if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) && strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
539 return -1;
540 }
541
542 if (!strcmp(signalling_type, ADEMCO_HIGH_SPEED) && strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
543 return -1;
544 }
545
546 if (!strcmp(signalling_type, ADEMCO_SUPER_FAST) && strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
547 return -1;
548 }
549
550 return 0;
551}
552
553/*!
554 * \brief Detect the message format of an event
555 *
556 * \param signalling_type Expected signalling type for the message
557 * \param event event received
558 * \param no_checksum Should we calculate checksum for the message
559 *
560 * \returns The expected digits for the detected event type
561 */
562static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum)
563{
564 int res = 16;
565
566 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
567 || !strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
568 sprintf(signalling_type, "%s", ADEMCO_CONTACT_ID);
569 }
570
571 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
572 sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_1);
573 res = 8;
574 }
575
576 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
577 sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_2);
578 res = 9;
579 }
580
581 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
582 sprintf(signalling_type, "%s", ADEMCO_HIGH_SPEED);
583 }
584
585 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
586 sprintf(signalling_type, "%s", ADEMCO_SUPER_FAST);
587 *no_checksum = 1;
588 res = 15;
589 }
590
591 if (strcmp(signalling_type, UNKNOWN_FORMAT)) {
592 ast_verb(4, "AlarmMonitoring: Detected format %s.\n", signalling_type);
593 ast_debug(1, "AlarmMonitoring: Autodetected format %s.\n", signalling_type);
594 }
595
596 return res;
597}
598
599/*!
600 * \brief Receive Ademco ContactID or other format Data String
601 *
602 * \param chan Asterisk Channel
603 * \param ehead Pointer to events list
604 * \param signalling_type Expected signalling type for the message
605 * \param no_checksum Should we calculate checksum for the message
606 *
607 * \retval 0 success
608 * \retval -1 failure
609 */
610static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum)
611{
612 int res = 0;
613 const char *limit;
614 char event[17];
615 event_node_t *enew, *elp;
616 int got_some_digits = 0;
617 int events_received = 0;
618 int ack_retries = 0;
619 int limit_retries = 0;
620 int expected_length = sizeof(event) - 1;
621
622 database_increment("calls-received");
623
624 /* Wait for first event */
625 ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n");
626
627 while (res >= 0) {
628 int digits_received = 0;
629
630 res = 0;
631
633 sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
634 expected_length = 16;
635 *no_checksum = 0;
636 }
637
638 if (got_some_digits == 0) {
639 /* Send ACK tone sequence */
640 ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
641 res = send_tone_burst(chan, "1400", 100, 0);
642 if (!res) {
643 ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
644 res = send_tone_burst(chan, "2300", 100, 100);
645 }
646 }
647 if (res) {
648 return -1;
649 }
650
651 res = receive_dtmf_digits(chan, event, sizeof(event), expected_length, &digits_received);
652 if (res < 0) {
653 if (events_received == 0) {
654 /* Hangup with no events received should be logged in the DB */
655 database_increment("no-events-received");
656 ast_verb(4, "AlarmReceiver: No events received!\n");
657 } else {
658 if (ack_retries) {
659 database_increment("ack-retries");
660 ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
661 }
662 }
663 ast_verb(4, "AlarmReceiver: App exiting...\n");
664 break;
665 }
666
667 if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) {
668 expected_length = ademco_detect_format(signalling_type, event, no_checksum);
669
670 if (res > 0) {
671 if (digits_received == expected_length) {
672 res = limit_retries = 0;
673 } else if (digits_received == expected_length - 1
674 && (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2)
675 || !strcmp(signalling_type, ADEMCO_EXPRESS_4_1))) {
676 /* ADEMCO EXPRESS without checksum */
677 res = limit_retries = 0;
678 expected_length--;
679 *no_checksum = 1;
680 ast_verb(4, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
681 ast_debug(1, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
682 }
683 }
684 }
685
686 ast_channel_lock(chan);
687 limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_CALL_LIMIT");
688 if (!ast_strlen_zero(limit)) {
689 if (ast_tvdiff_ms(ast_tvnow(), call_start_time) > atoi(limit)) {
690 ast_channel_unlock(chan);
691 return -1;
692 }
693 }
694 limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_RETRIES_LIMIT");
695 ast_channel_unlock(chan);
696 if (!ast_strlen_zero(limit)) {
697 if (limit_retries + 1 >= atoi(limit)) {
698 return -1;
699 }
700 }
701
702 if (res) {
703 /* Didn't get all of the digits */
704 ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
705 limit_retries++;
706
707 if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT))
708 {
709 sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
710 expected_length = sizeof(event) - 1;
711 }
712
713 if (!got_some_digits) {
714 got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
715 ack_retries++;
716 }
717 continue;
718 }
719
720 got_some_digits = 1;
721
722 ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
723 ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
724
725 /* Calculate checksum */
726 if (!(*no_checksum) && ademco_verify_checksum(event, expected_length)) {
727 database_increment("checksum-errors");
728 ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
729 ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
730 continue;
731 }
732
733 /* Check the message type for correctness */
734 if (ademco_check_valid(signalling_type, event)) {
735 database_increment("format-errors");
736 ast_verb(2, "AlarmReceiver: Wrong message type\n");
737 ast_debug(1, "AlarmReceiver: Wrong message type\n");
738 continue;
739 }
740
741 events_received++;
742
743 /* Queue the Event */
744 if (!(enew = ast_calloc(1, sizeof(*enew)))) {
745 return -1;
746 }
747
748 enew->next = NULL;
749 ast_copy_string(enew->data, event, sizeof(enew->data));
750
751 /* Insert event onto end of list */
752 if (*ehead == NULL) {
753 *ehead = enew;
754 } else {
755 for (elp = *ehead; elp->next != NULL; elp = elp->next) {
756 ;
757 }
758 elp->next = enew;
759 }
760
761 /* Let the user have the option of logging the single event before sending the kissoff tone */
762 if (log_individual_events && log_events(chan, signalling_type, enew, *no_checksum)) {
763 return -1;
764 }
765
766 /* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */
767 if (send_tone_burst(chan, "1400", 900, 200)) {
768 return -1;
769 }
770
771 /* If audio call follows, exit alarm receiver app */
772 if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
773 && !strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) {
774 ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n");
775 return 0;
776 }
777 }
778
779 return res;
780}
781
782/*!
783 * \brief This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
784 *
785 * \param chan Asterisk Channel
786 * \param data Application data
787 *
788 * \retval 0 success
789 * \retval -1 failure
790 */
791static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
792{
793 int res = 0;
794 int no_checksum = 0;
795 event_node_t *elp, *efree;
796 char signalling_type[64] = "";
797 event_node_t *event_head = NULL;
798
801 ast_verb(4, "AlarmReceiver: Setting write format to Mu-law\n");
803 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan));
804 return -1;
805 }
806 }
807
810 ast_verb(4, "AlarmReceiver: Setting read format to Mu-law\n");
812 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan));
813 return -1;
814 }
815 }
816
817 /* Set default values for this invocation of the application */
818 ast_copy_string(signalling_type, UNKNOWN_FORMAT, sizeof(signalling_type));
820
821 /* Answer the channel if it is not already */
822 if (ast_channel_state(chan) != AST_STATE_UP) {
823 ast_verb(4, "AlarmReceiver: Answering channel\n");
824 if (ast_answer(chan)) {
825 return -1;
826 }
827 }
828
829 /* Wait for the connection to settle post-answer */
830 ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
831 if (ast_safe_sleep(chan, answait)) {
832 return -1;
833 }
834
835 /* Attempt to receive the events */
836 receive_ademco_event(chan, &event_head, signalling_type, &no_checksum);
837
838 /* Events queued by receiver, write them all out here if so configured */
840 res = log_events(chan, signalling_type, event_head, no_checksum);
841 }
842
843 /* Do we exec a command line at the end? */
844 if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
845 ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
847 }
848
849 /* Free up the data allocated in our linked list */
850 for (elp = event_head; (elp != NULL);) {
851 efree = elp;
852 elp = elp->next;
853 ast_free(efree);
854 }
855
856 return 0;
857}
858
859/*!
860 * \brief Load the configuration from the configuration file
861 *
862 * \param reload True on reload
863 *
864 * \retval 1 success
865 * \retval 0 failure
866 */
867static int load_config(int reload)
868{
869 struct ast_config *cfg;
870 const char *value;
871 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
872
873 /* Read in the config file */
874 cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
875
876 if (!cfg) {
877 ast_verb(4, "AlarmReceiver: No config file\n");
878 return 0;
879 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
880 return 1;
881 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
882 ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n",
884 return 0;
885 }
886
887 if ((value = ast_variable_retrieve(cfg, "general", "eventcmd")) != NULL) {
889 }
890
891 if ((value = ast_variable_retrieve(cfg, "general", "loudness")) != NULL) {
892 toneloudness = atoi(value);
893 if (toneloudness < 100) {
894 toneloudness = 100;
895 } else if (toneloudness > 8192) {
896 toneloudness = 8192;
897 }
898 }
899
900 if ((value = ast_variable_retrieve(cfg, "general", "fdtimeout")) != NULL) {
901 fdtimeout = atoi(value);
902 if (fdtimeout < 1000) {
903 fdtimeout = 1000;
904 } else if (fdtimeout > 10000) {
905 fdtimeout = 10000;
906 }
907 }
908
909 if ((value = ast_variable_retrieve(cfg, "general", "sdtimeout")) != NULL) {
910 sdtimeout = atoi(value);
911 if (sdtimeout < 110) {
912 sdtimeout = 110;
913 } else if (sdtimeout > 4000) {
914 sdtimeout = 4000;
915 }
916 }
917
918 if ((value = ast_variable_retrieve(cfg, "general", "answait")) != NULL) {
919 answait = atoi(value);
920 if (answait < 500) {
921 answait = 500;
922 } else if (answait > 10000) {
923 answait = 10000;
924 }
925 }
926
927 if ((value = ast_variable_retrieve(cfg, "general", "no_group_meta")) != NULL) {
929 }
930
931 if ((value = ast_variable_retrieve(cfg, "general", "logindividualevents")) != NULL) {
933 }
934
935 if ((value = ast_variable_retrieve(cfg, "general", "eventspooldir")) != NULL) {
937 }
938
939 if ((value = ast_variable_retrieve(cfg, "general", "timestampformat")) != NULL) {
941 }
942
943 if ((value = ast_variable_retrieve(cfg, "general", "db-family")) != NULL) {
945 }
946
948
949 return 1;
950}
951
952/*!
953 * \brief Unregister Alarm Receiver App
954 *
955 * \retval 0 success
956 * \retval -1 failure
957 */
958static int unload_module(void)
959{
961}
962
963/*!
964 * \brief Load the module
965 *
966 * Module loading including tests for configuration or dependencies.
967 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
968 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
969 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
970 * configuration file or other non-critical problem return
971 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
972 */
973static int load_module(void)
974{
975 if (load_config(0)) {
978 }
980 }
981
983}
984
985static int reload(void)
986{
987 if (load_config(1)) {
989 }
990
992}
993
994AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Alarm Receiver for Asterisk",
995 .support_level = AST_MODULE_SUPPORT_EXTENDED,
996 .load = load_module,
997 .unload = unload_module,
998 .reload = reload,
#define ADEMCO_MSG_TYPE_4
static int ademco_verify_checksum(char *event, int expected)
Verify Ademco checksum.
static char time_stamp_format[128]
#define ALMRCV_CONFIG
static int fdtimeout
static const char app[]
static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int expected, int *received)
Receive a fixed length DTMF string.
#define ADEMCO_EXPRESS_4_2
struct timeval call_start_time
#define ADEMCO_MSG_TYPE_2
#define ADEMCO_CONTACT_ID
static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event, int no_checksum)
Log events if configuration key logindividualevents is enabled or on exit.
#define ADEMCO_HIGH_SPEED
static char db_family[128]
#define ADEMCO_AUDIO_CALL_NEXT
struct @6 digits_mapping[]
static int toneloudness
#define ADEMCO_MSG_TYPE_3
static int sdtimeout
static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
Write metadata to log file.
#define ADEMCO_EXPRESS_4_1
static char event_spool_dir[128]
#define ADEMCO_MSG_TYPE_6
static int log_individual_events
static char event_file[14]
static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum)
Receive Ademco ContactID or other format Data String.
#define ADEMCO_MSG_TYPE_5
static char event_app[128]
#define UNKNOWN_FORMAT
char weight
#define ADEMCO_SUPER_FAST
static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
char digit
static int load_module(void)
Load the module.
static int no_group_meta
static int answait
static int unload_module(void)
Unregister Alarm Receiver App.
static int load_config(int reload)
Load the configuration from the configuration file.
static int reload(void)
static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum)
Detect the message format of an event.
#define ADEMCO_MSG_TYPE_1
static int ademco_check_valid(char *signalling_type, char *event)
Check if the message is in known and valid Ademco format.
static int write_event(FILE *logfile, event_node_t *event)
Log a single event.
static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int delay)
Send a single tone burst for a specified duration and frequency.
static void database_increment(char *key)
Attempt to access a database variable and increment it.
Persistent data storage (akin to *doze registry)
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:342
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:427
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
void ast_shrink_phone_number(char *n)
Shrink a phone number in place to just digits (more accurately it just removes ()'s,...
Definition: callerid.c:1101
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition: channel.h:2922
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5762
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5803
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1574
#define ast_channel_unlock(chan)
Definition: channel.h:2923
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
Convenient Signal Processing routines.
Generic File Format Support. Should be included by clients of the file handling routines....
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition: format.c:201
@ AST_FORMAT_CMP_NOT_EQUAL
Definition: format.h:38
Media Format Cache API.
struct ast_format * ast_format_ulaw
Built-in cached ulaw format.
Definition: format_cache.c:86
struct ast_format * ast_format_alaw
Built-in cached alaw format.
Definition: format_cache.c:91
static const char name[]
Definition: format_mp3.c:68
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:783
@ CONFIG_FLAG_FILEUNCHANGED
#define AST_FRAME_DTMF
#define ast_frfree(fr)
@ AST_FRAME_CONTROL
@ AST_CONTROL_HANGUP
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
Tone Indication Support.
int ast_playtones_start(struct ast_channel *chan, int vol, const char *tonelist, int interruptible)
Start playing a list of tones on a channel.
Definition: indications.c:302
void ast_playtones_stop(struct ast_channel *chan)
Stop playing tones on a channel.
Definition: indications.c:393
Custom localtime functions for multiple timezones.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
Asterisk locking-related definitions:
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:315
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define NULL
Definition: resample.c:96
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Main Channel structure associated with a channel.
Structure used to handle boolean flags.
Definition: utils.h:199
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
union ast_frame::@226 data
enum ast_frame_type frametype
struct event_node * next
Definition: astman.c:222
Number structure.
Definition: app_followme.c:154
int value
Definition: syslog.c:37
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Support for translation of data formats. translate.c.
Utility functions.
#define ARRAY_LEN(a)
Definition: utils.h:666