Skip to content

Commit 648b803

Browse files
committed
res_alarmsystem: Add support for non-alarming sensors.
* If disarm_delay is set to 0, this will now cause activation of that sensor to not alarm at all, which is useful for sensors that are only informational, such as certain window sensors. This case will also not trigger an autodial of the keypard, since disarming is not needed. * To make it easier to access sensor state from the dialplan, a new function, ALARMSYSTEM_SENSOR_TRIGGERED, now allows checking whether a sensor is currently triggered. * Remove unused lock from client structure. * Some of the logic for detecting IP loss client-side has been improved a little to be slightly less hypersensitive. PHREAKSCRIPT-52 #close
1 parent cf26500 commit 648b803

File tree

2 files changed

+120
-11
lines changed

2 files changed

+120
-11
lines changed

configs/samples/res_alarmsystem.conf.sample

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@
7373
;disarm_delay = 45 ; Number of seconds grace period permitted to disarm an active alarm after this sensor triggers before it is considered a breach.
7474
; Default is 60.
7575

76+
;[window] ; Section defining a window sensor, which does not trigger alarms (reporting only)
77+
;type = sensor
78+
;sensor_id = 2
79+
;client = myclient
80+
;device = DAHDI/24
81+
;disarm_delay = 0 ; Do not require the system to be disarmed when this sensor is triggered (reporting is informational only)
82+
7683
;[keypad] ; Section defining alarm keypad settings. An alarm keypad can be instantiated by using AlarmKeypad()
7784
;type = keypad
7885
;client = myclient ; Client associated with these keypad settings

res/res_alarmsystem.c

Lines changed: 113 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@
269269
<synopsis>Disarm delay</synopsis>
270270
<description>
271271
<para>Number of seconds grace period permitted to disarm an active alarm after this sensor triggers before considering it a breach.</para>
272+
<para>If set to 0, activation of this sensor will never trigger an alarm. This can be useful for certain sensors, like window sensors on windows that are not a breach threat. Events will still be generated for these sensors, allowing automation actions to be taken if desired.</para>
273+
<para>Consequently, to have a sensor that always triggers a breach alarm immediately, set this option to 1 second.</para>
272274
</description>
273275
</configOption>
274276
</configObject>
@@ -389,6 +391,22 @@
389391
<ref type="application">AlarmReceiver</ref>
390392
</see-also>
391393
</application>
394+
<function name="ALARMSYSTEM_SENSOR_TRIGGERED" language="en_US">
395+
<synopsis>
396+
Returns whether an alarm sensor is currently triggered
397+
</synopsis>
398+
<syntax>
399+
<parameter name="client" required="true">
400+
<para>Client name as configured in <literal>res_alarmsystem.conf</literal></para>
401+
</parameter>
402+
<parameter name="sensor" required="true">
403+
<para>Sensor name as configured in <literal>res_alarmsystem.conf</literal></para>
404+
</parameter>
405+
</syntax>
406+
<description>
407+
<para>Returns whether an alarm system sensor is currently triggered.</para>
408+
</description>
409+
</function>
392410
***/
393411

394412
#define MODULE_NAME "res_alarmsystem"
@@ -538,7 +556,6 @@ struct alarm_client {
538556
enum alarm_state state; /* Internal aggregate alarm state */
539557
unsigned int ip_connected:1; /* IP connectivity good or lost? */
540558
pthread_t thread;
541-
ast_mutex_t lock; /*! \todo not used, remove */
542559
int alertpipe[2];
543560
char client_id[AST_MAX_EXTENSION];
544561
char client_pin[AST_MAX_EXTENSION];
@@ -552,6 +569,7 @@ struct alarm_client {
552569
time_t autoservice_start;
553570
time_t breach_time;
554571
time_t last_arm;
572+
time_t ip_lost_time; /* Time that IP connectivity was last lost */
555573
int egress_delay;
556574
char *contexts[NUM_ALARM_EVENTS];
557575
struct alarm_sensors sensors;
@@ -681,7 +699,6 @@ static void cleanup_client(struct alarm_client *c)
681699
ast_free(c->cid_name);
682700
}
683701
ast_alertpipe_close(c->alertpipe);
684-
ast_mutex_destroy(&c->lock);
685702
ast_free(c);
686703
}
687704

@@ -1250,9 +1267,19 @@ static int generate_event(struct alarm_client *c, enum alarm_event_type event, s
12501267
static void set_ip_connected(struct alarm_client *c, int connected)
12511268
{
12521269
if (connected != c->ip_connected) {
1270+
time_t now;
12531271
ast_log(LOG_NOTICE, "Client '%s' is now %s\n", c->name, connected ? "ONLINE" : "OFFLINE");
12541272
c->ip_connected = connected;
12551273
generate_event(c, connected ? EVENT_INTERNET_RESTORED : EVENT_INTERNET_LOST, NULL, NULL);
1274+
now = time(NULL);
1275+
if (connected) {
1276+
if (c->ip_lost_time >= now - 1) {
1277+
/* Possible, and likely just highly coincidental. */
1278+
ast_debug(1, "Interesting! IP connectivity restored immediately after it was lost!\n");
1279+
}
1280+
} else {
1281+
c->ip_lost_time = now;
1282+
}
12561283
}
12571284
}
12581285

@@ -1710,14 +1737,30 @@ static void *client_thread(void *arg)
17101737
* if we get stuck in the POLLERR case above.
17111738
* This ensure that this will always get executed periodically,
17121739
* such as to report events by phone if needed. */
1713-
if (res == 0 || (c->ip_connected == 0)) { /* res == 0 */
1740+
if (res == 0 || c->ip_connected == 0) { /* res == 0 */
17141741
time_t now = time(NULL);
17151742
/* No need for pings if IP isn't enabled */
1716-
if (now >= c->last_ip_ack + c->ping_interval * 2 + 1) {
1717-
/* Haven't gotten any ACKs over IP from the server in a while.
1718-
* Set client as offline. */
1719-
ast_debug(1, "Time is %lu, but haven't gotten an ACK since %lu\n", now, c->last_ip_ack);
1720-
set_ip_connected(c, 0);
1743+
if (c->ip_connected) {
1744+
/* Handling to determine if we think the client is still online when it's really offline now */
1745+
if (now >= c->last_ip_ack + c->ping_interval * 3 + 1) {
1746+
/* Haven't gotten any ACKs over IP from the server in a while.
1747+
* Set client as offline. */
1748+
ast_debug(1, "Confirmed connectivity loss: time is %lu, but haven't gotten an ACK since %lu\n", now, c->last_ip_ack);
1749+
set_ip_connected(c, 0);
1750+
/* It is possible at this point that we will get a reply to the next ping we send,
1751+
* later on in this function. The bizarre effect of this is that it is possible
1752+
* to have an INTERNET_LOST event immediately followed by INTERNET_RESTORED.
1753+
* It would require just the right number of pings to be lost followed by one that is not,
1754+
* immediately after we determine that connectivity has been lost. */
1755+
} else if (now >= c->last_ip_ack + c->ping_interval * 2 + 1) {
1756+
/* Haven't gotten any ACKs over IP from the server in a while.
1757+
* Don't immediately mark client as offline though. */
1758+
ast_debug(1, "Likely connectivity loss: time is %lu, but haven't gotten an ACK since %lu\n", now, c->last_ip_ack);
1759+
ast_log(LOG_NOTICE, "Significant packet loss encountered, possible connectivity loss\n");
1760+
/* Don't set connected to 0 yet... allow one more ping, and send an extra one just to be sure. */
1761+
generate_event(c, EVENT_PING, NULL, NULL);
1762+
usleep(50000); /* Wait briefly before sending the next packet, since if this one is dropped, the next one probably will be too */
1763+
}
17211764
}
17221765
/* There might still be some outstanding events that need to be delivered.
17231766
* For example, a few seconds ago, we were woken up to send events to server by IP,
@@ -1899,7 +1942,6 @@ static int load_config(void)
18991942
}
19001943
strcpy(c->data, cat); /* Safe */
19011944
c->name = c->data;
1902-
ast_mutex_init(&c->lock);
19031945
c->alertpipe[0] = c->alertpipe[1] = -1;
19041946
if (ast_alertpipe_init(c->alertpipe)) {
19051947
ast_log(LOG_ERROR, "Failed to initialize alertpipe\n");
@@ -2209,6 +2251,7 @@ static int alarmsensor_exec(struct ast_channel *chan, const char *data)
22092251

22102252
/* Okay, now we can start.
22112253
* Since the sensor just took the sensor loop off hook, it has been triggered. */
2254+
s->triggered = 1;
22122255

22132256
/* Update state from OK to TRIGGERED.
22142257
* From here, it can return to ALARM_STATE_OK if disarmed within s->disarm_delay time.
@@ -2217,6 +2260,9 @@ static int alarmsensor_exec(struct ast_channel *chan, const char *data)
22172260
if (is_egress) {
22182261
ast_debug(1, "Egress is currently permitted, not triggering alarm\n");
22192262
breach_time = 0;
2263+
} else if (!s->disarm_delay) {
2264+
ast_debug(1, "Sensor does not trigger alarms, no breach timer required\n");
2265+
breach_time = 0;
22202266
} else {
22212267
time_t now = time(NULL);
22222268
c->state = ALARM_STATE_TRIGGERED;
@@ -2243,14 +2289,15 @@ static int alarmsensor_exec(struct ast_channel *chan, const char *data)
22432289
}
22442290

22452291
/* If we have a keypad device to autodial, kick that off */
2246-
if (!is_egress && !ast_strlen_zero(c->keypad_device)) {
2292+
if (breach_time && !is_egress && !ast_strlen_zero(c->keypad_device)) {
22472293
orig_app_device(c->keypad_device, NULL, "AlarmKeypad", c->name, c->cid_num, c->cid_name);
22482294
}
22492295

22502296
/* Now, wait for the sensor to be restored. This could be soon, it could not be. */
2251-
while (ast_safe_sleep(chan, 500) != -1);
2297+
while (ast_safe_sleep(chan, 60000) != -1);
22522298

22532299
ast_debug(3, "Sensor '%s' appears to have been restored\n", s->name);
2300+
s->triggered = 0;
22542301
generate_event(c, EVENT_ALARM_SENSOR_RESTORED, s, NULL);
22552302

22562303
/* The only time we get a WRLOCK on clients is when cleaning them up at module unload.
@@ -2469,6 +2516,59 @@ static int alarmkeypad_exec(struct ast_channel *chan, const char *data)
24692516
return 0;
24702517
}
24712518

2519+
static int sensor_triggered_read(struct ast_channel *chan, const char *cmd, char *parse, char *buffer, size_t buflen)
2520+
{
2521+
char *argcopy;
2522+
struct alarm_client *c;
2523+
struct alarm_sensor *s;
2524+
AST_DECLARE_APP_ARGS(args,
2525+
AST_APP_ARG(client);
2526+
AST_APP_ARG(sensor);
2527+
);
2528+
2529+
if (ast_strlen_zero(parse)) {
2530+
ast_log(LOG_ERROR, "Must specify client-name,sensor-name\n");
2531+
return -1;
2532+
}
2533+
2534+
argcopy = ast_strdupa(parse);
2535+
AST_STANDARD_APP_ARGS(args, argcopy);
2536+
2537+
if (ast_strlen_zero(args.client)) {
2538+
ast_log(LOG_ERROR, "Must specify client name\n");
2539+
return -1;
2540+
}
2541+
2542+
AST_RWLIST_RDLOCK(&clients);
2543+
c = find_client_locked(args.client);
2544+
if (!c) {
2545+
ast_log(LOG_ERROR, "Client '%s' not found in configuration\n", args.client);
2546+
AST_RWLIST_UNLOCK(&clients);
2547+
return -1;
2548+
}
2549+
if (!ast_strlen_zero(args.sensor)) {
2550+
s = find_sensor(c, args.sensor);
2551+
if (!s) {
2552+
AST_RWLIST_UNLOCK(&clients);
2553+
ast_log(LOG_ERROR, "No such sensor '%s'\n", args.sensor);
2554+
return -1;
2555+
}
2556+
} else {
2557+
AST_RWLIST_UNLOCK(&clients);
2558+
ast_log(LOG_ERROR, "Must specify sensor name\n");
2559+
return -1;
2560+
}
2561+
2562+
ast_copy_string(buffer, s->triggered ? "1" : "0", buflen);
2563+
AST_RWLIST_UNLOCK(&clients);
2564+
return 0;
2565+
}
2566+
2567+
static struct ast_custom_function acf_sensortriggered = {
2568+
.name = "ALARMSYSTEM_SENSOR_TRIGGERED",
2569+
.read = sensor_triggered_read,
2570+
};
2571+
24722572
static char *handle_show_sensors(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
24732573
{
24742574
#define FORMAT "%-12s %-20s %s\n"
@@ -2625,6 +2725,7 @@ static int unload_module(void)
26252725
module_shutting_down = 1;
26262726

26272727
ast_cli_unregister_multiple(alarmsystem_cli, ARRAY_LEN(alarmsystem_cli));
2728+
ast_custom_function_unregister(&acf_sensortriggered);
26282729
ast_unregister_application("AlarmSensor");
26292730
ast_unregister_application("AlarmEventReceiver");
26302731
ast_unregister_application("AlarmKeypad");
@@ -2678,6 +2779,7 @@ static int load_module(void)
26782779
return AST_MODULE_LOAD_DECLINE;
26792780
}
26802781

2782+
ast_custom_function_register(&acf_sensortriggered);
26812783
ast_cli_register_multiple(alarmsystem_cli, ARRAY_LEN(alarmsystem_cli));
26822784
return 0;
26832785
}

0 commit comments

Comments
 (0)