Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
150 <version>1.0.0</version>
151 </since>
152 <synopsis>
153 Provide support for receiving alarm reports from a burglar or fire alarm panel.
154 </synopsis>
155 <syntax />
156 <description>
157 <para>This application should be called whenever there is an alarm panel calling in to dump its events.
158 The application will handshake with the alarm panel, and receive events, validate them, handshake them,
159 and store them until the panel hangs up. Once the panel hangs up, the application will run the system
160 command specified by the eventcmd setting in <filename>alarmreceiver.conf</filename> and pipe the
161 events to the standard input of the application.
162 The configuration file also contains settings for DTMF timing, and for the loudness of the
163 acknowledgement tones.</para>
164 <note><para>Few Ademco DTMF signalling formats are detected automatically: Contact ID, Express 4+1,
165 Express 4+2, High Speed and Super Fast.</para></note>
166 <para>The application is affected by the following variables:</para>
167 <variablelist>
168 <variable name="ALARMRECEIVER_CALL_LIMIT">
169 <para>Maximum call time, in milliseconds.</para>
170 <para>If set, this variable causes application to exit after the specified time.</para>
171 </variable>
172 <variable name="ALARMRECEIVER_RETRIES_LIMIT">
173 <para>Maximum number of retries per call.</para>
174 <para>If set, this variable causes application to exit after the specified number of messages.</para>
175 </variable>
176 </variablelist>
177 </description>
178 <see-also>
179 <ref type="filename">alarmreceiver.conf</ref>
180 </see-also>
181 </application>
182 ***/
183
184/* Config Variables */
185static int fdtimeout = 2000;
186static int sdtimeout = 200;
187static int answait = 1250;
188static int toneloudness = 4096;
190static int no_group_meta = 0;
191static char event_spool_dir[128] = {'\0'};
192static char event_app[128] = {'\0'};
193static char db_family[128] = {'\0'};
194static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
195
196/* Misc variables */
197static char event_file[14] = "/event-XXXXXX";
198
199/*!
200 * \brief Attempt to access a database variable and increment it
201 *
202 * \note Only if the user defined db-family in alarmreceiver.conf
203 *
204 * The alarmreceiver app will write statistics to a few variables
205 * in this family if it is defined. If the new key doesn't exist in the
206 * family, then create it and set its value to 1.
207 *
208 * \param key A database key to increment
209 */
210static void database_increment(char *key)
211{
212 unsigned v;
213 char value[16];
214
216 return; /* If not defined, don't do anything */
217 }
218
219 if (ast_db_get(db_family, key, value, sizeof(value) - 1)) {
220 ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
221 /* Guess we have to create it */
222 ast_db_put(db_family, key, "1");
223 return;
224 }
225
226 sscanf(value, "%30u", &v);
227 v++;
228
229 ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
230 snprintf(value, sizeof(value), "%u", v);
231
232 if (ast_db_put(db_family, key, value)) {
233 ast_verb(4, "AlarmReceiver: database_increment write error\n");
234 }
235
236 return;
237}
238
239/*!
240 * \brief Receive a fixed length DTMF string.
241 *
242 * \note Doesn't give preferential treatment to any digit,
243 * \note allow different timeout values for the first and all subsequent digits
244 *
245 * \param chan Asterisk Channel
246 * \param digit_string Digits String
247 * \param buf_size The size of the Digits String buffer
248 * \param expected Digits expected for this message type
249 * \param received Pointer to number of digits received so far
250 *
251 * \retval 0 if all digits were successfully received
252 * \retval 1 if a timeout occurred
253 * \retval -1 if the caller hung up or on channel errors
254 */
255static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int expected, int *received)
256{
257 int rtn = 0;
258 int r;
259 struct ast_frame *f;
260 struct timeval lastdigittime;
261
262 lastdigittime = ast_tvnow();
263 while (*received < expected && *received < buf_size - 1) {
264 /* If timed out, leave */
265 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((*received > 0) ? sdtimeout : fdtimeout)) {
266 ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", ast_channel_name(chan));
267 ast_debug(1, "AlarmReceiver: DTMF timeout on chan %s\n", ast_channel_name(chan));
268 rtn = 1;
269 break;
270 }
271
272 if ((r = ast_waitfor(chan, -1)) < 0) {
273 ast_debug(1, "Waitfor returned %d\n", r);
274 continue;
275 }
276
277 if ((f = ast_read(chan)) == NULL) {
278 rtn = -1;
279 break;
280 }
281
282 /* If they hung up, leave */
283 if ((f->frametype == AST_FRAME_CONTROL)
285 if (f->data.uint32) {
287 }
288 ast_frfree(f);
289 rtn = -1;
290 break;
291 }
292
293 /* If not DTMF, just do it again */
294 if (f->frametype != AST_FRAME_DTMF) {
295 ast_frfree(f);
296 continue;
297 }
298
299 /* Save digit */
300 digit_string[(*received)++] = f->subclass.integer;
301 ast_frfree(f);
302
303 lastdigittime = ast_tvnow();
304 }
305
306 /* Null terminate the end of the digit_string */
307 digit_string[*received] = '\0';
308
309 return rtn;
310}
311
312/*!
313 * \brief Write metadata to log file
314 *
315 * \param logfile Log File Pointer
316 * \param signalling_type Signaling Type
317 * \param chan Asterisk Channel
318 * \param no_checksum Expecting messages without checksum
319 *
320 * \retval 0 success
321 * \retval -1 failure
322 */
323static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
324{
325 struct timeval t;
326 struct ast_tm now;
327 char *cl;
328 char *cn;
329 char workstring[80];
330 char timestamp[80];
331
332 /* Extract the caller ID location */
333 ast_copy_string(workstring,
334 S_COR(ast_channel_caller(chan)->id.number.valid,
335 ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring));
336 ast_shrink_phone_number(workstring);
337 if (ast_strlen_zero(workstring)) {
338 cl = "<unknown>";
339 } else {
340 cl = workstring;
341 }
342 cn = S_COR(ast_channel_caller(chan)->id.name.valid,
343 ast_channel_caller(chan)->id.name.str, "<unknown>");
344
345 /* Get the current time */
346 t = ast_tvnow();
347 ast_localtime(&t, &now, NULL);
348
349 /* Format the time */
350 ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
351
352 if (no_group_meta && fprintf(logfile, "PROTOCOL=%s\n"
353 "CHECKSUM=%s\n"
354 "CALLINGFROM=%s\n"
355 "CALLERNAME=%s\n"
356 "TIMESTAMP=%s\n\n",
357 signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
358 return 0;
359 } else if (fprintf(logfile, "\n\n[metadata]\n\n"
360 "PROTOCOL=%s\n"
361 "CHECKSUM=%s\n"
362 "CALLINGFROM=%s\n"
363 "CALLERNAME=%s\n"
364 "TIMESTAMP=%s\n\n"
365 "[events]\n\n",
366 signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
367 return 0;
368 }
369
370 ast_verb(3, "AlarmReceiver: can't write metadata\n");
371 ast_debug(1, "AlarmReceiver: can't write metadata\n");
372 return -1;
373}
374
375/*!
376 * \brief Log a single event
377 *
378 * \param logfile Log File Pointer
379 * \param event Event Structure
380 *
381 * \retval 0 success
382 * \retval -1 failure
383 */
384static int write_event(FILE *logfile, event_node_t *event)
385{
386 if (fprintf(logfile, "%s%s\n", no_group_meta ? "event=" : "", event->data) < 0) {
387 return -1;
388 }
389
390 return 0;
391}
392
393/*!
394 * \brief Log events if configuration key logindividualevents is enabled or on exit
395 *
396 * \param chan Asterisk Channel
397 * \param signalling_type Signaling Type
398 * \param event Event Structure
399 * \param no_checksum Expecting messages without checksum
400 *
401 * \retval 0 success
402 * \retval -1 failure
403 */
404static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event, int no_checksum)
405{
406 char workstring[sizeof(event_spool_dir) + sizeof(event_file)] = "";
407 int fd;
408 FILE *logfile;
409 event_node_t *elp = event;
410
412
413 /* Make a template */
414 ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
415 strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
416
417 /* Make the temporary file */
418 fd = mkstemp(workstring);
419
420 if (fd == -1) {
421 ast_verb(3, "AlarmReceiver: can't make temporary file\n");
422 ast_debug(1, "AlarmReceiver: can't make temporary file\n");
423 return -1;
424 }
425
426 if ((logfile = fdopen(fd, "w")) == NULL) {
427 return -1;
428 }
429
430 /* Write the file */
431 if (write_metadata(logfile, signalling_type, chan, no_checksum)) {
432 fflush(logfile);
433 fclose(logfile);
434 return -1;
435 }
436
437 while ((elp != NULL) && (write_event(logfile, elp) == 0)) {
438 elp = elp->next;
439 }
440
441 fflush(logfile);
442 fclose(logfile);
443 }
444
445 return 0;
446}
447
448/*!
449 * \brief Verify Ademco checksum
450 * \since 11.0
451 *
452 * \param event Received DTMF String
453 * \param expected Number of Digits expected
454 *
455 * \retval 0 success
456 * \retval -1 failure
457 */
458static int ademco_verify_checksum(char *event, int expected)
459{
460 int checksum = 0;
461 int i, j;
462
463 for (j = 0; j < expected; j++) {
464 for (i = 0; i < ARRAY_LEN(digits_mapping); i++) {
465 if (digits_mapping[i].digit == event[j]) {
466 break;
467 }
468 }
469
470 if (i >= ARRAY_LEN(digits_mapping)) {
471 ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
472 return -1;
473 }
474
475 checksum += digits_mapping[i].weight;
476 }
477
478 /* Checksum is mod(15) of the total */
479 if (!(checksum % 15)) {
480 return 0;
481 }
482
483 return -1;
484}
485
486/*!
487 * \brief Send a single tone burst for a specified duration and frequency.
488 * \since 11.0
489 *
490 * \param chan Asterisk Channel
491 * \param tone_freq Frequency of the tone to send
492 * \param tone_duration Tone duration in ms
493 * \param delay Delay before sending the tone
494 *
495 * \retval 0 success
496 * \retval -1 failure
497 */
498static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int delay)
499{
500 if (delay && ast_safe_sleep(chan, delay)) {
501 return -1;
502 }
503
504 if (ast_playtones_start(chan, toneloudness, tone_freq, 0)) {
505 return -1;
506 }
507
508 if (ast_safe_sleep(chan, tone_duration)) {
509 return -1;
510 }
511
512 ast_playtones_stop(chan);
513 return 0;
514}
515
516/*!
517 * \brief Check if the message is in known and valid Ademco format
518 *
519 * \param signalling_type Expected signalling type for the message
520 * \param event event received
521 *
522 * \retval 0 The event is valid
523 * \retval -1 The event is not valid
524 */
525static int ademco_check_valid(char *signalling_type, char *event)
526{
527 if (!strcmp(signalling_type, UNKNOWN_FORMAT)) {
528 return 1;
529 }
530
531 if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
532 && strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
533 && strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
534 return -1;
535 }
536
537 if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_1) && strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
538 return -1;
539 }
540
541 if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) && strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
542 return -1;
543 }
544
545 if (!strcmp(signalling_type, ADEMCO_HIGH_SPEED) && strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
546 return -1;
547 }
548
549 if (!strcmp(signalling_type, ADEMCO_SUPER_FAST) && strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
550 return -1;
551 }
552
553 return 0;
554}
555
556/*!
557 * \brief Detect the message format of an event
558 *
559 * \param signalling_type Expected signalling type for the message
560 * \param event event received
561 * \param no_checksum Should we calculate checksum for the message
562 *
563 * \returns The expected digits for the detected event type
564 */
565static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum)
566{
567 int res = 16;
568
569 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
570 || !strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
571 sprintf(signalling_type, "%s", ADEMCO_CONTACT_ID);
572 }
573
574 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
575 sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_1);
576 res = 8;
577 }
578
579 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
580 sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_2);
581 res = 9;
582 }
583
584 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
585 sprintf(signalling_type, "%s", ADEMCO_HIGH_SPEED);
586 }
587
588 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
589 sprintf(signalling_type, "%s", ADEMCO_SUPER_FAST);
590 *no_checksum = 1;
591 res = 15;
592 }
593
594 if (strcmp(signalling_type, UNKNOWN_FORMAT)) {
595 ast_verb(4, "AlarmMonitoring: Detected format %s.\n", signalling_type);
596 ast_debug(1, "AlarmMonitoring: Autodetected format %s.\n", signalling_type);
597 }
598
599 return res;
600}
601
602/*!
603 * \brief Receive Ademco ContactID or other format Data String
604 *
605 * \param chan Asterisk Channel
606 * \param ehead Pointer to events list
607 * \param signalling_type Expected signalling type for the message
608 * \param no_checksum Should we calculate checksum for the message
609 *
610 * \retval 0 success
611 * \retval -1 failure
612 */
613static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum)
614{
615 int res = 0;
616 const char *limit;
617 char event[17];
618 event_node_t *enew, *elp;
619 int got_some_digits = 0;
620 int events_received = 0;
621 int ack_retries = 0;
622 int limit_retries = 0;
623 int expected_length = sizeof(event) - 1;
624
625 database_increment("calls-received");
626
627 /* Wait for first event */
628 ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n");
629
630 while (res >= 0) {
631 int digits_received = 0;
632
633 res = 0;
634
636 sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
637 expected_length = 16;
638 *no_checksum = 0;
639 }
640
641 if (got_some_digits == 0) {
642 /* Send ACK tone sequence */
643 ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
644 res = send_tone_burst(chan, "1400", 100, 0);
645 if (!res) {
646 ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
647 res = send_tone_burst(chan, "2300", 100, 100);
648 }
649 }
650 if (res) {
651 return -1;
652 }
653
654 res = receive_dtmf_digits(chan, event, sizeof(event), expected_length, &digits_received);
655 if (res < 0) {
656 if (events_received == 0) {
657 /* Hangup with no events received should be logged in the DB */
658 database_increment("no-events-received");
659 ast_verb(4, "AlarmReceiver: No events received!\n");
660 } else {
661 if (ack_retries) {
662 database_increment("ack-retries");
663 ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
664 }
665 }
666 ast_verb(4, "AlarmReceiver: App exiting...\n");
667 break;
668 }
669
670 if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) {
671 expected_length = ademco_detect_format(signalling_type, event, no_checksum);
672
673 if (res > 0) {
674 if (digits_received == expected_length) {
675 res = limit_retries = 0;
676 } else if (digits_received == expected_length - 1
677 && (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2)
678 || !strcmp(signalling_type, ADEMCO_EXPRESS_4_1))) {
679 /* ADEMCO EXPRESS without checksum */
680 res = limit_retries = 0;
681 expected_length--;
682 *no_checksum = 1;
683 ast_verb(4, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
684 ast_debug(1, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
685 }
686 }
687 }
688
689 ast_channel_lock(chan);
690 limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_CALL_LIMIT");
691 if (!ast_strlen_zero(limit)) {
692 if (ast_tvdiff_ms(ast_tvnow(), call_start_time) > atoi(limit)) {
693 ast_channel_unlock(chan);
694 return -1;
695 }
696 }
697 limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_RETRIES_LIMIT");
698 ast_channel_unlock(chan);
699 if (!ast_strlen_zero(limit)) {
700 if (limit_retries + 1 >= atoi(limit)) {
701 return -1;
702 }
703 }
704
705 if (res) {
706 /* Didn't get all of the digits */
707 ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
708 limit_retries++;
709
710 if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT))
711 {
712 sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
713 expected_length = sizeof(event) - 1;
714 }
715
716 if (!got_some_digits) {
717 got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
718 ack_retries++;
719 }
720 continue;
721 }
722
723 got_some_digits = 1;
724
725 ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
726 ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
727
728 /* Calculate checksum */
729 if (!(*no_checksum) && ademco_verify_checksum(event, expected_length)) {
730 database_increment("checksum-errors");
731 ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
732 ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
733 continue;
734 }
735
736 /* Check the message type for correctness */
737 if (ademco_check_valid(signalling_type, event)) {
738 database_increment("format-errors");
739 ast_verb(2, "AlarmReceiver: Wrong message type\n");
740 ast_debug(1, "AlarmReceiver: Wrong message type\n");
741 continue;
742 }
743
744 events_received++;
745
746 /* Queue the Event */
747 if (!(enew = ast_calloc(1, sizeof(*enew)))) {
748 return -1;
749 }
750
751 enew->next = NULL;
752 ast_copy_string(enew->data, event, sizeof(enew->data));
753
754 /* Insert event onto end of list */
755 if (*ehead == NULL) {
756 *ehead = enew;
757 } else {
758 for (elp = *ehead; elp->next != NULL; elp = elp->next) {
759 ;
760 }
761 elp->next = enew;
762 }
763
764 /* Let the user have the option of logging the single event before sending the kissoff tone */
765 if (log_individual_events && log_events(chan, signalling_type, enew, *no_checksum)) {
766 return -1;
767 }
768
769 /* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */
770 if (send_tone_burst(chan, "1400", 900, 200)) {
771 return -1;
772 }
773
774 /* If audio call follows, exit alarm receiver app */
775 if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
776 && !strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) {
777 ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n");
778 return 0;
779 }
780 }
781
782 return res;
783}
784
785/*!
786 * \brief This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
787 *
788 * \param chan Asterisk Channel
789 * \param data Application data
790 *
791 * \retval 0 success
792 * \retval -1 failure
793 */
794static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
795{
796 int res = 0;
797 int no_checksum = 0;
798 event_node_t *elp, *efree;
799 char signalling_type[64] = "";
800 event_node_t *event_head = NULL;
801
804 ast_verb(4, "AlarmReceiver: Setting write format to Mu-law\n");
806 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan));
807 return -1;
808 }
809 }
810
813 ast_verb(4, "AlarmReceiver: Setting read format to Mu-law\n");
815 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan));
816 return -1;
817 }
818 }
819
820 /* Set default values for this invocation of the application */
821 ast_copy_string(signalling_type, UNKNOWN_FORMAT, sizeof(signalling_type));
823
824 /* Answer the channel if it is not already */
825 if (ast_channel_state(chan) != AST_STATE_UP) {
826 ast_verb(4, "AlarmReceiver: Answering channel\n");
827 if (ast_answer(chan)) {
828 return -1;
829 }
830 }
831
832 /* Wait for the connection to settle post-answer */
833 ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
834 if (ast_safe_sleep(chan, answait)) {
835 return -1;
836 }
837
838 /* Attempt to receive the events */
839 receive_ademco_event(chan, &event_head, signalling_type, &no_checksum);
840
841 /* Events queued by receiver, write them all out here if so configured */
843 res = log_events(chan, signalling_type, event_head, no_checksum);
844 }
845
846 /* Do we exec a command line at the end? */
847 if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
848 ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
850 }
851
852 /* Free up the data allocated in our linked list */
853 for (elp = event_head; (elp != NULL);) {
854 efree = elp;
855 elp = elp->next;
856 ast_free(efree);
857 }
858
859 return 0;
860}
861
862/*!
863 * \brief Load the configuration from the configuration file
864 *
865 * \param reload True on reload
866 *
867 * \retval 1 success
868 * \retval 0 failure
869 */
870static int load_config(int reload)
871{
872 struct ast_config *cfg;
873 const char *value;
874 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
875
876 /* Read in the config file */
877 cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
878
879 if (!cfg) {
880 ast_verb(4, "AlarmReceiver: No config file\n");
881 return 0;
882 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
883 return 1;
884 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
885 ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n",
887 return 0;
888 }
889
890 if ((value = ast_variable_retrieve(cfg, "general", "eventcmd")) != NULL) {
892 }
893
894 if ((value = ast_variable_retrieve(cfg, "general", "loudness")) != NULL) {
895 toneloudness = atoi(value);
896 if (toneloudness < 100) {
897 toneloudness = 100;
898 } else if (toneloudness > 8192) {
899 toneloudness = 8192;
900 }
901 }
902
903 if ((value = ast_variable_retrieve(cfg, "general", "fdtimeout")) != NULL) {
904 fdtimeout = atoi(value);
905 if (fdtimeout < 1000) {
906 fdtimeout = 1000;
907 } else if (fdtimeout > 10000) {
908 fdtimeout = 10000;
909 }
910 }
911
912 if ((value = ast_variable_retrieve(cfg, "general", "sdtimeout")) != NULL) {
913 sdtimeout = atoi(value);
914 if (sdtimeout < 110) {
915 sdtimeout = 110;
916 } else if (sdtimeout > 4000) {
917 sdtimeout = 4000;
918 }
919 }
920
921 if ((value = ast_variable_retrieve(cfg, "general", "answait")) != NULL) {
922 answait = atoi(value);
923 if (answait < 500) {
924 answait = 500;
925 } else if (answait > 10000) {
926 answait = 10000;
927 }
928 }
929
930 if ((value = ast_variable_retrieve(cfg, "general", "no_group_meta")) != NULL) {
932 }
933
934 if ((value = ast_variable_retrieve(cfg, "general", "logindividualevents")) != NULL) {
936 }
937
938 if ((value = ast_variable_retrieve(cfg, "general", "eventspooldir")) != NULL) {
940 }
941
942 if ((value = ast_variable_retrieve(cfg, "general", "timestampformat")) != NULL) {
944 }
945
946 if ((value = ast_variable_retrieve(cfg, "general", "db-family")) != NULL) {
948 }
949
951
952 return 1;
953}
954
955/*!
956 * \brief Unregister Alarm Receiver App
957 *
958 * \retval 0 success
959 * \retval -1 failure
960 */
961static int unload_module(void)
962{
964}
965
966/*!
967 * \brief Load the module
968 *
969 * Module loading including tests for configuration or dependencies.
970 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
971 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
972 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
973 * configuration file or other non-critical problem return
974 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
975 */
976static int load_module(void)
977{
978 if (load_config(0)) {
981 }
983 }
984
986}
987
988static int reload(void)
989{
990 if (load_config(1)) {
992 }
993
995}
996
997AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Alarm Receiver for Asterisk",
998 .support_level = AST_MODULE_SUPPORT_EXTENDED,
999 .load = load_module,
1000 .unload = unload_module,
1001 .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: db.c:335
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: db.c:421
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:2970
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3190
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4274
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5779
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:5820
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:2834
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1601
#define ast_channel_unlock(chan)
Definition: channel.h:2971
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.
@ CONFIG_FLAG_FILEUNCHANGED
#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:869
#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:329
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ 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:640
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.
union ast_frame::@228 data
struct ast_frame_subclass subclass
enum ast_frame_type frametype
struct event_node * next
Definition: astman.c:222
Number structure.
Definition: app_followme.c:157
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