app_alarmreceiver.c File Reference

Central Station Alarm receiver for Ademco Contact ID. More...

#include "asterisk.h"
#include <math.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/app.h"
#include "asterisk/dsp.h"
#include "asterisk/config.h"
#include "asterisk/localtime.h"
#include "asterisk/callerid.h"
#include "asterisk/astdb.h"
#include "asterisk/utils.h"
#include "asterisk/indications.h"
#include "asterisk/format_cache.h"

Include dependency graph for app_alarmreceiver.c:

Go to the source code of this file.

Data Structures

struct  event_node

Defines

#define ADEMCO_AUDIO_CALL_NEXT   "606"
#define ADEMCO_CONTACT_ID   "ADEMCO_CONTACT_ID"
#define ADEMCO_EXPRESS_4_1   "ADEMCO_EXPRESS_4_1"
#define ADEMCO_EXPRESS_4_2   "ADEMCO_EXPRESS_4_2"
#define ADEMCO_HIGH_SPEED   "ADEMCO_HIGH_SPEED"
#define ADEMCO_MSG_TYPE_1   "18"
#define ADEMCO_MSG_TYPE_2   "98"
#define ADEMCO_MSG_TYPE_3   "17"
#define ADEMCO_MSG_TYPE_4   "27"
#define ADEMCO_MSG_TYPE_5   "55"
#define ADEMCO_MSG_TYPE_6   "56"
#define ADEMCO_SUPER_FAST   "ADEMCO_SUPER_FAST"
#define ALMRCV_CONFIG   "alarmreceiver.conf"
#define UNKNOWN_FORMAT   "UNKNOWN_FORMAT"

Typedefs

typedef struct event_node event_node_t

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int ademco_check_valid (char *signalling_type, char *event)
 Check if the message is in known and valid Ademco format.
static int ademco_detect_format (char *signalling_type, char *event, int *no_checksum)
 Detect the message format of an event.
static int ademco_verify_checksum (char *event, int expected)
 Verify Ademco checksum.
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.
static void database_increment (char *key)
 Attempt to access a database variable and increment it.
static int load_config (int reload)
 Load the configuration from the configuration file.
static int load_module (void)
 Load the module.
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.
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.
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.
static int reload (void)
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 int unload_module (void)
 Unregister Alarm Receiver App.
static int write_event (FILE *logfile, event_node_t *event)
 Log a single event.
static int write_metadata (FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum)
 Write metadata to log file.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Alarm Receiver for Asterisk" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, }
static int answait = 1250
static const char app [] = "AlarmReceiver"
static struct ast_module_infoast_module_info = &__mod_info
struct timeval call_start_time
static char db_family [128] = {'\0'}
struct {
   char   digit
   char   weight
digits_mapping []
static char event_app [128] = {'\0'}
static char event_file [14] = "/event-XXXXXX"
static char event_spool_dir [128] = {'\0'}
static int fdtimeout = 2000
static int log_individual_events = 0
static int no_group_meta = 0
static int sdtimeout = 200
static char time_stamp_format [128] = {"%a %b %d, %Y @ %H:%M:%S %Z"}
static int toneloudness = 4096


Detailed Description

Central Station Alarm receiver for Ademco Contact ID.

Author:
Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
*** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***

Use at your own risk. Please consult the GNU GPL license document included with Asterisk. *

*** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***

Definition in file app_alarmreceiver.c.


Define Documentation

#define ADEMCO_AUDIO_CALL_NEXT   "606"

Definition at line 130 of file app_alarmreceiver.c.

Referenced by receive_ademco_event().

#define ADEMCO_CONTACT_ID   "ADEMCO_CONTACT_ID"

#define ADEMCO_EXPRESS_4_1   "ADEMCO_EXPRESS_4_1"

#define ADEMCO_EXPRESS_4_2   "ADEMCO_EXPRESS_4_2"

#define ADEMCO_HIGH_SPEED   "ADEMCO_HIGH_SPEED"

Definition at line 96 of file app_alarmreceiver.c.

Referenced by ademco_check_valid(), and ademco_detect_format().

#define ADEMCO_MSG_TYPE_1   "18"

Definition at line 123 of file app_alarmreceiver.c.

Referenced by ademco_check_valid(), and ademco_detect_format().

#define ADEMCO_MSG_TYPE_2   "98"

Definition at line 124 of file app_alarmreceiver.c.

Referenced by ademco_check_valid(), and ademco_detect_format().

#define ADEMCO_MSG_TYPE_3   "17"

Definition at line 125 of file app_alarmreceiver.c.

Referenced by ademco_check_valid(), and ademco_detect_format().

#define ADEMCO_MSG_TYPE_4   "27"

Definition at line 126 of file app_alarmreceiver.c.

Referenced by ademco_check_valid(), and ademco_detect_format().

#define ADEMCO_MSG_TYPE_5   "55"

Definition at line 127 of file app_alarmreceiver.c.

Referenced by ademco_check_valid(), and ademco_detect_format().

#define ADEMCO_MSG_TYPE_6   "56"

Definition at line 128 of file app_alarmreceiver.c.

Referenced by ademco_check_valid(), and ademco_detect_format().

#define ADEMCO_SUPER_FAST   "ADEMCO_SUPER_FAST"

Definition at line 117 of file app_alarmreceiver.c.

Referenced by ademco_check_valid(), and ademco_detect_format().

#define ALMRCV_CONFIG   "alarmreceiver.conf"

Definition at line 70 of file app_alarmreceiver.c.

Referenced by load_config().

#define UNKNOWN_FORMAT   "UNKNOWN_FORMAT"


Typedef Documentation

typedef struct event_node event_node_t

Definition at line 144 of file app_alarmreceiver.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1002 of file app_alarmreceiver.c.

static void __unreg_module ( void   )  [static]

Definition at line 1002 of file app_alarmreceiver.c.

static int ademco_check_valid ( char *  signalling_type,
char *  event 
) [static]

Check if the message is in known and valid Ademco format.

Parameters:
signalling_type Expected signalling type for the message
event event received
Return values:
0 The event is valid
-1 The event is not valid

Definition at line 525 of file app_alarmreceiver.c.

References ADEMCO_CONTACT_ID, ADEMCO_EXPRESS_4_1, ADEMCO_EXPRESS_4_2, ADEMCO_HIGH_SPEED, ADEMCO_MSG_TYPE_1, ADEMCO_MSG_TYPE_2, ADEMCO_MSG_TYPE_3, ADEMCO_MSG_TYPE_4, ADEMCO_MSG_TYPE_5, ADEMCO_MSG_TYPE_6, ADEMCO_SUPER_FAST, and UNKNOWN_FORMAT.

Referenced by receive_ademco_event().

00526 {
00527    if (!strcmp(signalling_type, UNKNOWN_FORMAT)) {
00528       return 1;
00529    }
00530 
00531    if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
00532       && strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
00533       && strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
00534       return -1;
00535    }
00536 
00537    if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_1) && strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
00538       return -1;
00539    }
00540 
00541    if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) && strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
00542       return -1;
00543    }
00544 
00545    if (!strcmp(signalling_type, ADEMCO_HIGH_SPEED) && strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
00546       return -1;
00547    }
00548 
00549    if (!strcmp(signalling_type, ADEMCO_SUPER_FAST) && strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
00550       return -1;
00551    }
00552 
00553    return 0;
00554 }

static int ademco_detect_format ( char *  signalling_type,
char *  event,
int *  no_checksum 
) [static]

Detect the message format of an event.

Parameters:
signalling_type Expected signalling type for the message
event event received
no_checksum Should we calculate checksum for the message
Returns:
The expected digits for the detected event type

Definition at line 565 of file app_alarmreceiver.c.

References ADEMCO_CONTACT_ID, ADEMCO_EXPRESS_4_1, ADEMCO_EXPRESS_4_2, ADEMCO_HIGH_SPEED, ADEMCO_MSG_TYPE_1, ADEMCO_MSG_TYPE_2, ADEMCO_MSG_TYPE_3, ADEMCO_MSG_TYPE_4, ADEMCO_MSG_TYPE_5, ADEMCO_MSG_TYPE_6, ADEMCO_SUPER_FAST, ast_debug, ast_verb, and UNKNOWN_FORMAT.

Referenced by receive_ademco_event().

00566 {
00567    int res = 16;
00568 
00569    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2)
00570       || !strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) {
00571       sprintf(signalling_type, "%s", ADEMCO_CONTACT_ID);
00572    }
00573 
00574    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) {
00575       sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_1);
00576       res = 8;
00577    }
00578 
00579    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) {
00580       sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_2);
00581       res = 9;
00582    }
00583 
00584    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) {
00585       sprintf(signalling_type, "%s", ADEMCO_HIGH_SPEED);
00586    }
00587 
00588    if (!strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) {
00589       sprintf(signalling_type, "%s", ADEMCO_SUPER_FAST);
00590       *no_checksum = 1;
00591       res = 15;
00592    }
00593 
00594    if (strcmp(signalling_type, UNKNOWN_FORMAT)) {
00595       ast_verb(4, "AlarmMonitoring: Detected format %s.\n", signalling_type);
00596       ast_debug(1, "AlarmMonitoring: Autodetected format %s.\n", signalling_type);
00597    }
00598 
00599    return res;
00600 }

static int ademco_verify_checksum ( char *  event,
int  expected 
) [static]

Verify Ademco checksum.

Since:
11.0
Parameters:
event Received DTMF String
expected Number of Digits expected
Return values:
0 success
-1 failure

Definition at line 458 of file app_alarmreceiver.c.

References ARRAY_LEN, ast_verb, digit, and digits_mapping.

Referenced by receive_ademco_event().

00459 {
00460    int checksum = 0;
00461    int i, j;
00462 
00463    for (j = 0; j < expected; j++) {
00464       for (i = 0; i < ARRAY_LEN(digits_mapping); i++) {
00465          if (digits_mapping[i].digit == event[j]) {
00466             break;
00467          }
00468       }
00469 
00470       if (i >= ARRAY_LEN(digits_mapping)) {
00471          ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
00472          return -1;
00473       }
00474 
00475       checksum += digits_mapping[i].weight;
00476    }
00477 
00478    /* Checksum is mod(15) of the total */
00479    if (!(checksum % 15)) {
00480       return 0;
00481    }
00482 
00483    return -1;
00484 }

static int alarmreceiver_exec ( struct ast_channel chan,
const char *  data 
) [static]

This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.

Parameters:
chan Asterisk Channel
data Application data
Return values:
0 success
-1 failure

Definition at line 794 of file app_alarmreceiver.c.

References ast_answer(), ast_channel_name(), ast_channel_readformat(), ast_channel_writeformat(), ast_copy_string(), ast_debug, ast_format_alaw, ast_format_cmp(), AST_FORMAT_CMP_NOT_EQUAL, ast_format_ulaw, ast_free, ast_log, ast_safe_sleep(), ast_safe_system(), ast_set_read_format(), ast_set_write_format(), AST_STATE_UP, ast_strlen_zero, ast_tvnow(), ast_verb, call_start_time, log_events(), LOG_WARNING, event_node::next, NULL, receive_ademco_event(), and UNKNOWN_FORMAT.

Referenced by load_module().

00795 {
00796    int res = 0;
00797    int no_checksum = 0;
00798    event_node_t *elp, *efree;
00799    char signalling_type[64] = "";
00800    event_node_t *event_head = NULL;
00801 
00802    if ((ast_format_cmp(ast_channel_writeformat(chan), ast_format_ulaw) == AST_FORMAT_CMP_NOT_EQUAL) &&
00803       (ast_format_cmp(ast_channel_writeformat(chan), ast_format_alaw) == AST_FORMAT_CMP_NOT_EQUAL)) {
00804       ast_verb(4, "AlarmReceiver: Setting write format to Mu-law\n");
00805       if (ast_set_write_format(chan, ast_format_ulaw)) {
00806          ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan));
00807          return -1;
00808       }
00809    }
00810 
00811    if ((ast_format_cmp(ast_channel_readformat(chan), ast_format_ulaw) == AST_FORMAT_CMP_NOT_EQUAL) &&
00812       (ast_format_cmp(ast_channel_readformat(chan), ast_format_alaw) == AST_FORMAT_CMP_NOT_EQUAL)) {
00813       ast_verb(4, "AlarmReceiver: Setting read format to Mu-law\n");
00814       if (ast_set_read_format(chan, ast_format_ulaw)) {
00815          ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan));
00816          return -1;
00817       }
00818    }
00819 
00820    /* Set default values for this invocation of the application */
00821    ast_copy_string(signalling_type, UNKNOWN_FORMAT, sizeof(signalling_type));
00822    call_start_time = ast_tvnow();
00823 
00824    /* Answer the channel if it is not already */
00825    if (ast_channel_state(chan) != AST_STATE_UP) {
00826       ast_verb(4, "AlarmReceiver: Answering channel\n");
00827       if (ast_answer(chan)) {
00828          return -1;
00829       }
00830    }
00831 
00832    /* Wait for the connection to settle post-answer */
00833    ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
00834    if (ast_safe_sleep(chan, answait)) {
00835       return -1;
00836    }
00837 
00838    /* Attempt to receive the events */
00839    receive_ademco_event(chan, &event_head, signalling_type, &no_checksum);
00840 
00841    /* Events queued by receiver, write them all out here if so configured */
00842    if (!log_individual_events) {
00843       res = log_events(chan, signalling_type, event_head, no_checksum);
00844    }
00845 
00846    /* Do we exec a command line at the end? */
00847    if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
00848       ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
00849       ast_safe_system(event_app);
00850    }
00851 
00852    /* Free up the data allocated in our linked list */
00853    for (elp = event_head; (elp != NULL);) {
00854       efree = elp;
00855       elp = elp->next;
00856       ast_free(efree);
00857    }
00858 
00859    return 0;
00860 }

static void database_increment ( char *  key  )  [static]

Attempt to access a database variable and increment it.

Note:
Only if the user defined db-family in alarmreceiver.conf
The alarmreceiver app will write statistics to a few variables in this family if it is defined. If the new key doesn't exist in the family, then create it and set its value to 1.

Parameters:
key A database key to increment
Returns:
Nothing

Definition at line 210 of file app_alarmreceiver.c.

References ast_db_get(), ast_db_put(), ast_strlen_zero, ast_verb, and value.

Referenced by receive_ademco_event().

00211 {
00212    unsigned v;
00213    char value[16];
00214 
00215    if (ast_strlen_zero(db_family)) {
00216       return;  /* If not defined, don't do anything */
00217    }
00218 
00219    if (ast_db_get(db_family, key, value, sizeof(value) - 1)) {
00220       ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
00221       /* Guess we have to create it */
00222       ast_db_put(db_family, key, "1");
00223       return;
00224    }
00225 
00226    sscanf(value, "%30u", &v);
00227    v++;
00228 
00229    ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
00230    snprintf(value, sizeof(value), "%u", v);
00231 
00232    if (ast_db_put(db_family, key, value)) {
00233       ast_verb(4, "AlarmReceiver: database_increment write error\n");
00234    }
00235 
00236    return;
00237 }

static int load_config ( int  reload  )  [static]

Load the configuration from the configuration file.

Parameters:
reload True on reload
Return values:
1 success
0 failure

Definition at line 870 of file app_alarmreceiver.c.

References ALMRCV_CONFIG, ast_config_destroy(), ast_config_load, ast_copy_string(), ast_log, ast_true(), ast_variable_retrieve(), ast_verb, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, LOG_ERROR, NULL, and value.

00871 {
00872    struct ast_config *cfg;
00873    const char *value;
00874    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00875 
00876    /* Read in the config file */
00877    cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
00878 
00879    if (!cfg) {
00880       ast_verb(4, "AlarmReceiver: No config file\n");
00881       return 0;
00882    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00883       return 1;
00884    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00885       ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n",
00886          ALMRCV_CONFIG);
00887       return 0;
00888    }
00889 
00890    if ((value = ast_variable_retrieve(cfg, "general", "eventcmd")) != NULL) {
00891       ast_copy_string(event_app, value, sizeof(event_app));
00892    }
00893 
00894    if ((value = ast_variable_retrieve(cfg, "general", "loudness")) != NULL) {
00895       toneloudness = atoi(value);
00896       if (toneloudness < 100) {
00897          toneloudness = 100;
00898       } else if (toneloudness > 8192) {
00899          toneloudness = 8192;
00900       }
00901    }
00902 
00903    if ((value = ast_variable_retrieve(cfg, "general", "fdtimeout")) != NULL) {
00904       fdtimeout = atoi(value);
00905       if (fdtimeout < 1000) {
00906          fdtimeout = 1000;
00907       } else if (fdtimeout > 10000) {
00908          fdtimeout = 10000;
00909       }
00910    }
00911 
00912    if ((value = ast_variable_retrieve(cfg, "general", "sdtimeout")) != NULL) {
00913       sdtimeout = atoi(value);
00914       if (sdtimeout < 110) {
00915          sdtimeout = 110;
00916       } else if (sdtimeout > 4000) {
00917          sdtimeout = 4000;
00918       }
00919    }
00920 
00921    if ((value = ast_variable_retrieve(cfg, "general", "answait")) != NULL) {
00922       answait = atoi(value);
00923       if (answait < 500) {
00924          answait = 500;
00925       } else if (answait > 10000) {
00926          answait = 10000;
00927       }
00928    }
00929 
00930    if ((value = ast_variable_retrieve(cfg, "general", "no_group_meta")) != NULL) {
00931       no_group_meta = ast_true(value);
00932    }
00933 
00934    if ((value = ast_variable_retrieve(cfg, "general", "logindividualevents")) != NULL) {
00935       log_individual_events = ast_true(value);
00936    }
00937 
00938    if ((value = ast_variable_retrieve(cfg, "general", "eventspooldir")) != NULL) {
00939       ast_copy_string(event_spool_dir, value, sizeof(event_spool_dir));
00940    }
00941 
00942    if ((value = ast_variable_retrieve(cfg, "general", "timestampformat")) != NULL) {
00943       ast_copy_string(time_stamp_format, value, sizeof(time_stamp_format));
00944    }
00945 
00946    if ((value = ast_variable_retrieve(cfg, "general", "db-family")) != NULL) {
00947       ast_copy_string(db_family, value, sizeof(db_family));
00948    }
00949 
00950    ast_config_destroy(cfg);
00951 
00952    return 1;
00953 }

static int load_module ( void   )  [static]

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 976 of file app_alarmreceiver.c.

References alarmreceiver_exec(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_register_application_xml, and load_config().

00977 {
00978    if (load_config(0)) {
00979       if (ast_register_application_xml(app, alarmreceiver_exec)) {
00980          return AST_MODULE_LOAD_FAILURE;
00981       }
00982       return AST_MODULE_LOAD_SUCCESS;
00983    }
00984 
00985    return AST_MODULE_LOAD_DECLINE;
00986 }

static int log_events ( struct ast_channel chan,
char *  signalling_type,
event_node_t event,
int  no_checksum 
) [static]

Log events if configuration key logindividualevents is enabled or on exit.

Parameters:
chan Asterisk Channel
signalling_type Signaling Type
event Event Structure
no_checksum Expecting messages without checksum
Return values:
0 success
-1 failure

Definition at line 404 of file app_alarmreceiver.c.

References ast_copy_string(), ast_debug, ast_strlen_zero, ast_verb, event_node::next, NULL, write_event(), and write_metadata().

Referenced by alarmreceiver_exec(), and receive_ademco_event().

00405 {
00406    char workstring[sizeof(event_spool_dir) + sizeof(event_file)] = "";
00407    int fd;
00408    FILE *logfile;
00409    event_node_t *elp = event;
00410 
00411    if (!ast_strlen_zero(event_spool_dir)) {
00412 
00413       /* Make a template */
00414       ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
00415       strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
00416 
00417       /* Make the temporary file */
00418       fd = mkstemp(workstring);
00419 
00420       if (fd == -1) {
00421          ast_verb(3, "AlarmReceiver: can't make temporary file\n");
00422          ast_debug(1, "AlarmReceiver: can't make temporary file\n");
00423          return -1;
00424       }
00425 
00426       if ((logfile = fdopen(fd, "w")) == NULL) {
00427          return -1;
00428       }
00429 
00430       /* Write the file */
00431       if (write_metadata(logfile, signalling_type, chan, no_checksum)) {
00432          fflush(logfile);
00433          fclose(logfile);
00434          return -1;
00435       }
00436 
00437       while ((elp != NULL) && (write_event(logfile, elp) == 0)) {
00438          elp = elp->next;
00439       }
00440 
00441       fflush(logfile);
00442       fclose(logfile);
00443    }
00444 
00445    return 0;
00446 }

static int receive_ademco_event ( struct ast_channel chan,
event_node_t **  ehead,
char *  signalling_type,
int *  no_checksum 
) [static]

Receive Ademco ContactID or other format Data String.

Parameters:
chan Asterisk Channel
ehead Pointer to events list
signalling_type Expected signalling type for the message
no_checksum Should we calculate checksum for the message
Return values:
0 success
-1 failure

Definition at line 613 of file app_alarmreceiver.c.

References ADEMCO_AUDIO_CALL_NEXT, ademco_check_valid(), ADEMCO_CONTACT_ID, ademco_detect_format(), ADEMCO_EXPRESS_4_1, ADEMCO_EXPRESS_4_2, ademco_verify_checksum(), ast_calloc, ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, ast_strlen_zero, ast_tvdiff_ms(), ast_tvnow(), ast_verb, call_start_time, event_node::data, database_increment(), log_events(), event_node::next, NULL, pbx_builtin_getvar_helper(), receive_dtmf_digits(), send_tone_burst(), and UNKNOWN_FORMAT.

Referenced by alarmreceiver_exec().

00614 {
00615    int res = 0;
00616    const char *limit;
00617    char event[17];
00618    event_node_t *enew, *elp;
00619    int got_some_digits = 0;
00620    int events_received = 0;
00621    int ack_retries = 0;
00622    int limit_retries = 0;
00623    int expected_length = sizeof(event) - 1;
00624 
00625    database_increment("calls-received");
00626 
00627    /* Wait for first event */
00628    ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n");
00629 
00630    while (res >= 0) {
00631       int digits_received = 0;
00632 
00633       res = 0;
00634 
00635       if (log_individual_events) {
00636          sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
00637          expected_length = 16;
00638          *no_checksum = 0;
00639       }
00640 
00641       if (got_some_digits == 0) {
00642          /* Send ACK tone sequence */
00643          ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
00644          res = send_tone_burst(chan, "1400", 100, 0);
00645          if (!res) {
00646             ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
00647             res = send_tone_burst(chan, "2300", 100, 100);
00648          }
00649       }
00650       if (res) {
00651          return -1;
00652       }
00653 
00654       res = receive_dtmf_digits(chan, event, sizeof(event), expected_length, &digits_received);
00655       if (res < 0) {
00656          if (events_received == 0) {
00657             /* Hangup with no events received should be logged in the DB */
00658             database_increment("no-events-received");
00659             ast_verb(4, "AlarmReceiver: No events received!\n");
00660          } else {
00661             if (ack_retries) {
00662                database_increment("ack-retries");
00663                ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
00664             }
00665          }
00666          ast_verb(4, "AlarmReceiver: App exiting...\n");
00667          break;
00668       }
00669 
00670       if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) {
00671          expected_length = ademco_detect_format(signalling_type, event, no_checksum);
00672 
00673          if (res > 0) {
00674             if (digits_received == expected_length) {
00675                res = limit_retries = 0;
00676             } else if (digits_received == expected_length - 1
00677                && (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2)
00678                || !strcmp(signalling_type, ADEMCO_EXPRESS_4_1))) {
00679                /* ADEMCO EXPRESS without checksum */
00680                res = limit_retries = 0;
00681                expected_length--;
00682                *no_checksum = 1;
00683                ast_verb(4, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
00684                ast_debug(1, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type);
00685             }
00686          }
00687       }
00688 
00689       ast_channel_lock(chan);
00690       limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_CALL_LIMIT");
00691       if (!ast_strlen_zero(limit)) {
00692          if (ast_tvdiff_ms(ast_tvnow(), call_start_time) > atoi(limit)) {
00693             ast_channel_unlock(chan);
00694             return -1;
00695          }
00696       }
00697       limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_RETRIES_LIMIT");
00698       ast_channel_unlock(chan);
00699       if (!ast_strlen_zero(limit)) {
00700          if (limit_retries + 1 >= atoi(limit)) {
00701             return -1;
00702          }
00703       }
00704 
00705       if (res) {
00706          /* Didn't get all of the digits */
00707          ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
00708          limit_retries++;
00709 
00710          if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT))
00711          {
00712             sprintf(signalling_type, "%s", UNKNOWN_FORMAT);
00713             expected_length = sizeof(event) - 1;
00714          }
00715 
00716          if (!got_some_digits) {
00717             got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
00718             ack_retries++;
00719          }
00720          continue;
00721       }
00722 
00723       got_some_digits = 1;
00724 
00725       ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
00726       ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
00727 
00728       /* Calculate checksum */
00729       if (!(*no_checksum) && ademco_verify_checksum(event, expected_length)) {
00730          database_increment("checksum-errors");
00731          ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
00732          ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
00733          continue;
00734       }
00735 
00736       /* Check the message type for correctness */
00737       if (ademco_check_valid(signalling_type, event)) {
00738          database_increment("format-errors");
00739          ast_verb(2, "AlarmReceiver: Wrong message type\n");
00740          ast_debug(1, "AlarmReceiver: Wrong message type\n");
00741          continue;
00742       }
00743 
00744       events_received++;
00745 
00746       /* Queue the Event */
00747       if (!(enew = ast_calloc(1, sizeof(*enew)))) {
00748          return -1;
00749       }
00750 
00751       enew->next = NULL;
00752       ast_copy_string(enew->data, event, sizeof(enew->data));
00753 
00754       /* Insert event onto end of list */
00755       if (*ehead == NULL) {
00756          *ehead = enew;
00757       } else {
00758          for (elp = *ehead; elp->next != NULL; elp = elp->next) {
00759             ;
00760          }
00761          elp->next = enew;
00762       }
00763 
00764       /* Let the user have the option of logging the single event before sending the kissoff tone */
00765       if (log_individual_events && log_events(chan, signalling_type, enew, *no_checksum)) {
00766          return -1;
00767       }
00768 
00769       /* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */
00770       if (send_tone_burst(chan, "1400", 900, 200)) {
00771          return -1;
00772       }
00773 
00774       /* If audio call follows, exit alarm receiver app */
00775       if (!strcmp(signalling_type, ADEMCO_CONTACT_ID)
00776          && !strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) {
00777          ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n");
00778          return 0;
00779       }
00780    }
00781 
00782    return res;
00783 }

static int receive_dtmf_digits ( struct ast_channel chan,
char *  digit_string,
int  buf_size,
int  expected,
int *  received 
) [static]

Receive a fixed length DTMF string.

Note:
Doesn't give preferential treatment to any digit,

allow different timeout values for the first and all subsequent digits

Parameters:
chan Asterisk Channel
digit_string Digits String
buf_size The size of the Digits String buffer
expected Digits expected for this message type
received Pointer to number of digits received so far
Return values:
0 if all digits were successfully received
1 if a timeout occurred
-1 if the caller hung up or on channel errors

Definition at line 255 of file app_alarmreceiver.c.

References ast_channel_hangupcause_set(), ast_channel_name(), AST_CONTROL_HANGUP, ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_read(), ast_tvdiff_ms(), ast_tvnow(), ast_verb, ast_waitfor(), ast_frame::data, f, ast_frame::frametype, ast_frame_subclass::integer, NULL, ast_frame::subclass, and ast_frame::uint32.

Referenced by receive_ademco_event().

00256 {
00257    int rtn = 0;
00258    int r;
00259    struct ast_frame *f;
00260    struct timeval lastdigittime;
00261 
00262    lastdigittime = ast_tvnow();
00263    while (*received < expected && *received < buf_size - 1) {
00264       /* If timed out, leave */
00265       if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((*received > 0) ? sdtimeout : fdtimeout)) {
00266          ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", ast_channel_name(chan));
00267          ast_debug(1, "AlarmReceiver: DTMF timeout on chan %s\n", ast_channel_name(chan));
00268          rtn = 1;
00269          break;
00270       }
00271 
00272       if ((r = ast_waitfor(chan, -1)) < 0) {
00273          ast_debug(1, "Waitfor returned %d\n", r);
00274          continue;
00275       }
00276 
00277       if ((f = ast_read(chan)) == NULL) {
00278          rtn = -1;
00279          break;
00280       }
00281 
00282       /* If they hung up, leave */
00283       if ((f->frametype == AST_FRAME_CONTROL)
00284          && (f->subclass.integer == AST_CONTROL_HANGUP)) {
00285          if (f->data.uint32) {
00286             ast_channel_hangupcause_set(chan, f->data.uint32);
00287          }
00288          ast_frfree(f);
00289          rtn = -1;
00290          break;
00291       }
00292 
00293       /* If not DTMF, just do it again */
00294       if (f->frametype != AST_FRAME_DTMF) {
00295          ast_frfree(f);
00296          continue;
00297       }
00298 
00299       /* Save digit */
00300       digit_string[(*received)++] = f->subclass.integer;
00301       ast_frfree(f);
00302 
00303       lastdigittime = ast_tvnow();
00304    }
00305 
00306    /* Null terminate the end of the digit_string */
00307    digit_string[*received] = '\0';
00308 
00309    return rtn;
00310 }

static int reload ( void   )  [static]

Definition at line 988 of file app_alarmreceiver.c.

References AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, and load_config().

00989 {
00990    if (load_config(1)) {
00991       return AST_MODULE_LOAD_SUCCESS;
00992    }
00993 
00994    return AST_MODULE_LOAD_DECLINE;
00995 }

static int send_tone_burst ( struct ast_channel chan,
const char *  tone_freq,
int  tone_duration,
int  delay 
) [static]

Send a single tone burst for a specified duration and frequency.

Since:
11.0
Parameters:
chan Asterisk Channel
tone_freq Frequency of the tone to send
tone_duration Tone duration in ms
delay Delay before sending the tone
Return values:
0 success
-1 failure

Definition at line 498 of file app_alarmreceiver.c.

References ast_playtones_start(), ast_playtones_stop(), and ast_safe_sleep().

Referenced by receive_ademco_event().

00499 {
00500    if (delay && ast_safe_sleep(chan, delay)) {
00501       return -1;
00502    }
00503 
00504    if (ast_playtones_start(chan, toneloudness, tone_freq, 0)) {
00505       return -1;
00506    }
00507 
00508    if (ast_safe_sleep(chan, tone_duration)) {
00509       return -1;
00510    }
00511 
00512    ast_playtones_stop(chan);
00513    return 0;
00514 }

static int unload_module ( void   )  [static]

Unregister Alarm Receiver App.

Return values:
0 success
-1 failure

Definition at line 961 of file app_alarmreceiver.c.

References ast_unregister_application().

00962 {
00963    return ast_unregister_application(app);
00964 }

static int write_event ( FILE *  logfile,
event_node_t event 
) [static]

Log a single event.

Parameters:
logfile Log File Pointer
event Event Structure
Return values:
0 success
-1 failure

Definition at line 384 of file app_alarmreceiver.c.

References event_node::data.

Referenced by log_events().

00385 {
00386    if (fprintf(logfile, "%s%s\n", no_group_meta ? "event=" : "", event->data) < 0) {
00387       return -1;
00388    }
00389 
00390    return 0;
00391 }

static int write_metadata ( FILE *  logfile,
char *  signalling_type,
struct ast_channel chan,
int  no_checksum 
) [static]

Write metadata to log file.

Parameters:
logfile Log File Pointer
signalling_type Signaling Type
chan Asterisk Channel
no_checksum Expecting messages without checksum
Return values:
0 success
-1 failure

Definition at line 323 of file app_alarmreceiver.c.

References ast_channel_caller(), ast_copy_string(), ast_debug, ast_localtime(), ast_shrink_phone_number(), ast_strftime(), ast_strlen_zero, ast_tvnow(), ast_verb, name, NULL, and S_COR.

Referenced by log_events().

00324 {
00325    struct timeval t;
00326    struct ast_tm now;
00327    char *cl;
00328    char *cn;
00329    char workstring[80];
00330    char timestamp[80];
00331 
00332    /* Extract the caller ID location */
00333    ast_copy_string(workstring,
00334       S_COR(ast_channel_caller(chan)->id.number.valid,
00335       ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring));
00336    ast_shrink_phone_number(workstring);
00337    if (ast_strlen_zero(workstring)) {
00338       cl = "<unknown>";
00339    } else {
00340       cl = workstring;
00341    }
00342    cn = S_COR(ast_channel_caller(chan)->id.name.valid,
00343       ast_channel_caller(chan)->id.name.str, "<unknown>");
00344 
00345    /* Get the current time */
00346    t = ast_tvnow();
00347    ast_localtime(&t, &now, NULL);
00348 
00349    /* Format the time */
00350    ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
00351 
00352    if (no_group_meta && fprintf(logfile, "PROTOCOL=%s\n"
00353          "CHECKSUM=%s\n"
00354          "CALLINGFROM=%s\n"
00355          "CALLERNAME=%s\n"
00356          "TIMESTAMP=%s\n\n",
00357          signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
00358       return 0;
00359    } else if (fprintf(logfile, "\n\n[metadata]\n\n"
00360          "PROTOCOL=%s\n"
00361          "CHECKSUM=%s\n"
00362          "CALLINGFROM=%s\n"
00363          "CALLERNAME=%s\n"
00364          "TIMESTAMP=%s\n\n"
00365          "[events]\n\n",
00366          signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) {
00367       return 0;
00368    }
00369 
00370    ast_verb(3, "AlarmReceiver: can't write metadata\n");
00371    ast_debug(1, "AlarmReceiver: can't write metadata\n");
00372    return -1;
00373 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Alarm Receiver for Asterisk" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1002 of file app_alarmreceiver.c.

int answait = 1250 [static]

Definition at line 186 of file app_alarmreceiver.c.

const char app[] = "AlarmReceiver" [static]

Definition at line 148 of file app_alarmreceiver.c.

Definition at line 1002 of file app_alarmreceiver.c.

struct timeval call_start_time

Definition at line 146 of file app_alarmreceiver.c.

Referenced by alarmreceiver_exec(), and receive_ademco_event().

char db_family[128] = {'\0'} [static]

Definition at line 192 of file app_alarmreceiver.c.

char digit

struct { ... } digits_mapping[]

Referenced by ademco_verify_checksum().

char event_app[128] = {'\0'} [static]

Definition at line 191 of file app_alarmreceiver.c.

char event_file[14] = "/event-XXXXXX" [static]

Definition at line 196 of file app_alarmreceiver.c.

char event_spool_dir[128] = {'\0'} [static]

Definition at line 190 of file app_alarmreceiver.c.

int fdtimeout = 2000 [static]

Definition at line 184 of file app_alarmreceiver.c.

Referenced by acf_faxopt_write().

int log_individual_events = 0 [static]

Definition at line 188 of file app_alarmreceiver.c.

int no_group_meta = 0 [static]

Definition at line 189 of file app_alarmreceiver.c.

int sdtimeout = 200 [static]

Definition at line 185 of file app_alarmreceiver.c.

char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"} [static]

Definition at line 193 of file app_alarmreceiver.c.

int toneloudness = 4096 [static]

Definition at line 187 of file app_alarmreceiver.c.

char weight


Generated on Thu Apr 16 06:28:17 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6