static void notify_db_increment(ble_service_t *svc, uint16_t src_conn_idx, uint8_t user_id, uint32_t increment) { uint8_t num_conn; uint16_t *conn_idx; ble_gap_get_connected(&num_conn, &conn_idx); while (num_conn--) { if (src_conn_idx != conn_idx[num_conn]) { database_increment(svc, conn_idx[num_conn], user_id, increment); } } if (conn_idx) { OS_FREE(conn_idx); } }
/*! * \brief Receive Ademco ContactID or other format Data String * * \param chan Asterisk Channel * \param ehead Pointer to events list * \param signalling_type Expected signalling type for the message * \param no_checksum Should we calculate checksum for the message * * \retval 0 success * \retval -1 failure */ static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum) { int res = 0; const char *limit; char event[17]; event_node_t *enew, *elp; int got_some_digits = 0; int events_received = 0; int ack_retries = 0; int limit_retries = 0; int expected_length = sizeof(event) - 1; database_increment("calls-received"); /* Wait for first event */ ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n"); while (res >= 0) { int digits_received = 0; res = 0; if (log_individual_events) { sprintf(signalling_type, "%s", UNKNOWN_FORMAT); expected_length = 16; *no_checksum = 0; } if (got_some_digits == 0) { /* Send ACK tone sequence */ ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n"); res = send_tone_burst(chan, "1400", 100, 0); if (!res) { ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n"); res = send_tone_burst(chan, "2300", 100, 100); } } if (res) { return -1; } res = receive_dtmf_digits(chan, event, sizeof(event), expected_length, &digits_received); if (res < 0) { if (events_received == 0) { /* Hangup with no events received should be logged in the DB */ database_increment("no-events-received"); ast_verb(4, "AlarmReceiver: No events received!\n"); } else { if (ack_retries) { database_increment("ack-retries"); ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries); } } ast_verb(4, "AlarmReceiver: App exiting...\n"); break; } if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) { expected_length = ademco_detect_format(signalling_type, event, no_checksum); if (res > 0) { if (digits_received == expected_length) { res = limit_retries = 0; } else if (digits_received == expected_length - 1 && (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) || !strcmp(signalling_type, ADEMCO_EXPRESS_4_1))) { /* ADEMCO EXPRESS without checksum */ res = limit_retries = 0; expected_length--; *no_checksum = 1; ast_verb(4, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type); ast_debug(1, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type); } } } ast_channel_lock(chan); limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_CALL_LIMIT"); if (!ast_strlen_zero(limit)) { if (ast_tvdiff_ms(ast_tvnow(), call_start_time) > atoi(limit)) { ast_channel_unlock(chan); return -1; } } limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_RETRIES_LIMIT"); ast_channel_unlock(chan); if (!ast_strlen_zero(limit)) { if (limit_retries + 1 >= atoi(limit)) { return -1; } } if (res) { /* Didn't get all of the digits */ ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event); limit_retries++; if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT)) { sprintf(signalling_type, "%s", UNKNOWN_FORMAT); expected_length = sizeof(event) - 1; } if (!got_some_digits) { got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0; ack_retries++; } continue; } got_some_digits = 1; ast_verb(2, "AlarmReceiver: Received Event %s\n", event); ast_debug(1, "AlarmReceiver: Received event: %s\n", event); /* Calculate checksum */ if (!(*no_checksum) && ademco_verify_checksum(event, expected_length)) { database_increment("checksum-errors"); ast_verb(2, "AlarmReceiver: Nonzero checksum\n"); ast_debug(1, "AlarmReceiver: Nonzero checksum\n"); continue; } /* Check the message type for correctness */ if (ademco_check_valid(signalling_type, event)) { database_increment("format-errors"); ast_verb(2, "AlarmReceiver: Wrong message type\n"); ast_debug(1, "AlarmReceiver: Wrong message type\n"); continue; } events_received++; /* Queue the Event */ if (!(enew = ast_calloc(1, sizeof(*enew)))) { return -1; } enew->next = NULL; ast_copy_string(enew->data, event, sizeof(enew->data)); /* Insert event onto end of list */ if (*ehead == NULL) { *ehead = enew; } else { for (elp = *ehead; elp->next != NULL; elp = elp->next) { ; } elp->next = enew; } /* Let the user have the option of logging the single event before sending the kissoff tone */ if (log_individual_events && log_events(chan, signalling_type, enew, *no_checksum)) { return -1; } /* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */ if (send_tone_burst(chan, "1400", 900, 200)) { return -1; } /* If audio call follows, exit alarm receiver app */ if (!strcmp(signalling_type, ADEMCO_CONTACT_ID) && !strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) { ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n"); return 0; } } return res; }
/* * This function implements the logic to receive the Ademco contact ID format. * * The function will return 0 when the caller hangs up, else a -1 if there was a problem. */ static int receive_ademco_contact_id(struct ast_channel *chan, const void *data, int fdto, int sdto, int tldn, event_node_t **ehead) { int i, j; int res = 0; int checksum; char event[17]; event_node_t *enew, *elp; int got_some_digits = 0; int events_received = 0; int ack_retries = 0; static char digit_map[15] = "0123456789*#ABC"; static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15}; database_increment("calls-received"); /* Wait for first event */ ast_verb(4, "AlarmReceiver: Waiting for first event from panel\n"); while (res >= 0) { if (got_some_digits == 0) { /* Send ACK tone sequence */ ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n"); res = send_tone_burst(chan, 1400.0, 100, tldn); if (!res) res = ast_safe_sleep(chan, 100); if (!res) { ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n"); res = send_tone_burst(chan, 2300.0, 100, tldn); } } if ( res >= 0) res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto); if (res < 0) { if (events_received == 0) { /* Hangup with no events received should be logged in the DB */ database_increment("no-events-received"); } else { if (ack_retries) { ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries); database_increment("ack-retries"); } } ast_verb(4, "AlarmReceiver: App exiting...\n"); res = -1; break; } if (res != 0) { /* Didn't get all of the digits */ ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event); if (!got_some_digits) { got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0; ack_retries++; } continue; } got_some_digits = 1; ast_verb(2, "AlarmReceiver: Received Event %s\n", event); ast_debug(1, "AlarmReceiver: Received event: %s\n", event); /* Calculate checksum */ for (j = 0, checksum = 0; j < 16; j++) { for (i = 0; i < sizeof(digit_map); i++) { if (digit_map[i] == event[j]) break; } if (i == 16) break; checksum += digit_weights[i]; } if (i == 16) { ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]); continue; /* Bad character */ } /* Checksum is mod(15) of the total */ checksum = checksum % 15; if (checksum) { database_increment("checksum-errors"); ast_verb(2, "AlarmReceiver: Nonzero checksum\n"); ast_debug(1, "AlarmReceiver: Nonzero checksum\n"); continue; } /* Check the message type for correctness */ if (strncmp(event + 4, "18", 2)) { if (strncmp(event + 4, "98", 2)) { database_increment("format-errors"); ast_verb(2, "AlarmReceiver: Wrong message type\n"); ast_debug(1, "AlarmReceiver: Wrong message type\n"); continue; } } events_received++; /* Queue the Event */ if (!(enew = ast_calloc(1, sizeof(*enew)))) { res = -1; break; } enew->next = NULL; ast_copy_string(enew->data, event, sizeof(enew->data)); /* * Insert event onto end of list */ if (*ehead == NULL) *ehead = enew; else { for(elp = *ehead; elp->next != NULL; elp = elp->next) ; elp->next = enew; } if (res > 0) res = 0; /* Let the user have the option of logging the single event before sending the kissoff tone */ if ((res == 0) && (log_individual_events)) res = log_events(chan, ADEMCO_CONTACT_ID, enew); /* Wait 200 msec before sending kissoff */ if (res == 0) res = ast_safe_sleep(chan, 200); /* Send the kissoff tone */ if (res == 0) res = send_tone_burst(chan, 1400.0, 900, tldn); } return res; }