68 #define ALMRCV_CONFIG "alarmreceiver.conf"
69 #define UNKNOWN_FORMAT "UNKNOWN_FORMAT"
71 #define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
80 #define ADEMCO_EXPRESS_4_1 "ADEMCO_EXPRESS_4_1"
87 #define ADEMCO_EXPRESS_4_2 "ADEMCO_EXPRESS_4_2"
94 #define ADEMCO_HIGH_SPEED "ADEMCO_HIGH_SPEED"
115 #define ADEMCO_SUPER_FAST "ADEMCO_SUPER_FAST"
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"
128 #define ADEMCO_AUDIO_CALL_NEXT "606"
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} };
144 struct timeval call_start_time;
146 static const char app[] =
"AlarmReceiver";
182 static int fdtimeout = 2000;
183 static int sdtimeout = 200;
184 static int answait = 1250;
185 static int toneloudness = 4096;
186 static int log_individual_events = 0;
187 static int no_group_meta = 0;
188 static char event_spool_dir[128] = {
'\0'};
189 static char event_app[128] = {
'\0'};
190 static char db_family[128] = {
'\0'};
191 static char time_stamp_format[128] = {
"%a %b %d, %Y @ %H:%M:%S %Z"};
194 static char event_file[14] =
"/event-XXXXXX";
212 if (ast_strlen_zero(db_family)) {
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);
223 sscanf(value,
"%30u", &v);
226 ast_verb(4,
"AlarmReceiver: New value for %s: %u\n", key, v);
227 snprintf(value,
sizeof(value),
"%u", v);
230 ast_verb(4,
"AlarmReceiver: database_increment write error\n");
257 struct timeval lastdigittime;
260 while (*received < expected && *received < buf_size - 1) {
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));
270 ast_debug(1,
"Waitfor returned %d\n", r);
282 if (f->
data.uint32) {
283 ast_channel_hangupcause_set(chan, f->
data.uint32);
304 digit_string[*received] =
'\0';
332 ast_channel_caller(chan)->
id.
number.str,
""),
sizeof(workstring));
334 if (ast_strlen_zero(workstring)) {
339 cn =
S_COR(ast_channel_caller(chan)->
id.name.valid,
340 ast_channel_caller(chan)->
id.name.str,
"<unknown>");
347 ast_strftime(timestamp,
sizeof(timestamp), time_stamp_format, &now);
349 if (no_group_meta && fprintf(logfile,
"PROTOCOL=%s\n"
354 signalling_type, (!no_checksum) ?
"yes" :
"no", cl, cn, timestamp) > -1) {
356 }
else if (fprintf(logfile,
"\n\n[metadata]\n\n"
363 signalling_type, (!no_checksum) ?
"yes" :
"no", cl, cn, timestamp) > -1) {
367 ast_verb(3,
"AlarmReceiver: can't write metadata\n");
368 ast_debug(1,
"AlarmReceiver: can't write metadata\n");
383 if (fprintf(logfile,
"%s%s\n", no_group_meta ?
"event=" :
"", event->data) < 0) {
403 char workstring[
sizeof(event_spool_dir) +
sizeof(event_file)] =
"";
408 if (!ast_strlen_zero(event_spool_dir)) {
412 strncat(workstring, event_file,
sizeof(workstring) - strlen(workstring) - 1);
415 fd = mkstemp(workstring);
418 ast_verb(3,
"AlarmReceiver: can't make temporary file\n");
419 ast_debug(1,
"AlarmReceiver: can't make temporary file\n");
423 if ((logfile = fdopen(fd,
"w")) == NULL) {
428 if (
write_metadata(logfile, signalling_type, chan, no_checksum)) {
434 while ((elp != NULL) && (
write_event(logfile, elp) == 0)) {
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]) {
467 if (i >= ARRAY_LEN(digits_mapping)) {
468 ast_verb(2,
"AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
472 checksum += digits_mapping[i].weight;
476 if (!(checksum % 15)) {
524 if (!strcmp(signalling_type, UNKNOWN_FORMAT)) {
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)) {
534 if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_1) && strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
538 if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) && strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
542 if (!strcmp(signalling_type, ADEMCO_HIGH_SPEED) && strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
546 if (!strcmp(signalling_type, ADEMCO_SUPER_FAST) && strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
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);
571 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
572 sprintf(signalling_type,
"%s", ADEMCO_EXPRESS_4_1);
576 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
577 sprintf(signalling_type,
"%s", ADEMCO_EXPRESS_4_2);
581 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
582 sprintf(signalling_type,
"%s", ADEMCO_HIGH_SPEED);
585 if (!strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
586 sprintf(signalling_type,
"%s", ADEMCO_SUPER_FAST);
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);
616 int got_some_digits = 0;
617 int events_received = 0;
619 int limit_retries = 0;
620 int expected_length =
sizeof(
event) - 1;
625 ast_verb(4,
"AlarmReceiver: Waiting for first event from panel...\n");
628 int digits_received = 0;
632 if (log_individual_events) {
633 sprintf(signalling_type,
"%s", UNKNOWN_FORMAT);
634 expected_length = 16;
638 if (got_some_digits == 0) {
640 ast_verb(4,
"AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
643 ast_verb(4,
"AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
653 if (events_received == 0) {
656 ast_verb(4,
"AlarmReceiver: No events received!\n");
660 ast_verb(4,
"AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
663 ast_verb(4,
"AlarmReceiver: App exiting...\n");
667 if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) {
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))) {
677 res = limit_retries = 0;
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);
686 ast_channel_lock(chan);
688 if (!ast_strlen_zero(limit)) {
690 ast_channel_unlock(chan);
695 ast_channel_unlock(chan);
696 if (!ast_strlen_zero(limit)) {
697 if (limit_retries + 1 >= atoi(limit)) {
704 ast_verb(2,
"AlarmReceiver: Incomplete string: %s, trying again...\n",
event);
707 if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT))
709 sprintf(signalling_type,
"%s", UNKNOWN_FORMAT);
710 expected_length =
sizeof(
event) - 1;
713 if (!got_some_digits) {
714 got_some_digits = (!ast_strlen_zero(
event)) ? 1 : 0;
722 ast_verb(2,
"AlarmReceiver: Received Event %s\n",
event);
728 ast_verb(2,
"AlarmReceiver: Nonzero checksum\n");
729 ast_debug(1,
"AlarmReceiver: Nonzero checksum\n");
736 ast_verb(2,
"AlarmReceiver: Wrong message type\n");
737 ast_debug(1,
"AlarmReceiver: Wrong message type\n");
752 if (*ehead == NULL) {
755 for (elp = *ehead; elp->next != NULL; elp = elp->next) {
762 if (log_individual_events &&
log_events(chan, signalling_type, enew, *no_checksum)) {
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");
796 char signalling_type[64] =
"";
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));
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));
818 ast_copy_string(signalling_type, UNKNOWN_FORMAT,
sizeof(signalling_type));
823 ast_verb(4,
"AlarmReceiver: Answering channel\n");
830 ast_verb(4,
"AlarmReceiver: Waiting for connection to stabilize\n");
839 if (!log_individual_events) {
840 res =
log_events(chan, signalling_type, event_head, no_checksum);
844 if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
845 ast_debug(1,
"Alarmreceiver: executing: %s\n", event_app);
850 for (elp = event_head; (elp != NULL);) {
877 ast_verb(4,
"AlarmReceiver: No config file\n");
879 }
else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
881 }
else if (cfg == CONFIG_STATUS_FILEINVALID) {
882 ast_log(LOG_ERROR,
"Config file %s is in an invalid format. Aborting.\n",
887 if ((value = ast_variable_retrieve(cfg,
"general",
"eventcmd")) != NULL) {
891 if ((value = ast_variable_retrieve(cfg,
"general",
"loudness")) != NULL) {
892 toneloudness = atoi(value);
893 if (toneloudness < 100) {
895 }
else if (toneloudness > 8192) {
900 if ((value = ast_variable_retrieve(cfg,
"general",
"fdtimeout")) != NULL) {
901 fdtimeout = atoi(value);
902 if (fdtimeout < 1000) {
904 }
else if (fdtimeout > 10000) {
909 if ((value = ast_variable_retrieve(cfg,
"general",
"sdtimeout")) != NULL) {
910 sdtimeout = atoi(value);
911 if (sdtimeout < 110) {
913 }
else if (sdtimeout > 4000) {
918 if ((value = ast_variable_retrieve(cfg,
"general",
"answait")) != NULL) {
919 answait = atoi(value);
922 }
else if (answait > 10000) {
927 if ((value = ast_variable_retrieve(cfg,
"general",
"no_group_meta")) != NULL) {
931 if ((value = ast_variable_retrieve(cfg,
"general",
"logindividualevents")) != NULL) {
932 log_individual_events =
ast_true(value);
935 if ((value = ast_variable_retrieve(cfg,
"general",
"eventspooldir")) != NULL) {
939 if ((value = ast_variable_retrieve(cfg,
"general",
"timestampformat")) != NULL) {
943 if ((value = ast_variable_retrieve(cfg,
"general",
"db-family")) != NULL) {
985 static int reload(
void)
994 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
"Alarm Receiver for Asterisk",
995 .support_level = AST_MODULE_SUPPORT_EXTENDED,
static int load_config(int reload)
Load the configuration from the configuration file.
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
Support for translation of data formats. translate.c.
Convenient Signal Processing routines.
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.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
ast_channel_state
ast_channel states
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
static void database_increment(char *key)
Attempt to access a database variable and increment it.
void ast_playtones_stop(struct ast_channel *chan)
Stop playing tones on a channel.
static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
Write metadata to log file.
int ast_unregister_application(const char *app)
Unregister an application.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_frame_subclass subclass
Custom localtime functions for multiple timezones.
Configuration File Parser.
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...
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
#define ast_debug(level,...)
Log a DEBUG message.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
static int ademco_verify_checksum(char *event, int expected)
Verify Ademco checksum.
Core PBX routines and definitions.
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.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
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.
union ast_frame::@224 data
#define ast_calloc(num, len)
A wrapper for calloc()
Module has failed to load, may be in an inconsistent state.
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...
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Structure used to handle boolean flags.
static int load_module(void)
Load the module.
static int write_event(FILE *logfile, event_node_t *event)
Log a single event.
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
int ast_answer(struct ast_channel *chan)
Answer a channel.
Data structure associated with a single frame of data.
int ast_playtones_start(struct ast_channel *chan, int vol, const char *tonelist, int interruptible)
Start playing a list of tones on a channel.
static int ademco_check_valid(char *signalling_type, char *event)
Check if the message is in known and valid Ademco format.
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.
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
enum ast_frame_type frametype
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
void ast_shrink_phone_number(char *n)
Shrink a phone number in place to just digits (more accurately it just removes ()'s, .'s, and -'s...
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
Persistent data storage (akin to *doze registry)
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum)
Detect the message format of an event.
static int unload_module(void)
Unregister Alarm Receiver App.