Code:
#!/usr/bin/perl -w
use strict;
my @zone = (qw/null front-door lounge laundry-door dining garage-door store
rumpus rumpus-sliding/);
my @out = (qw/siren soft-siren soft-monitor siren-fire strobe reset
sonalert keypad display enable/);
#
# Status request descriptions
#
my @req = (
"Zone Input Unsealed",
"Zone Radio Unsealed",
"Zone CBus Unsealed",
"Zone in Delay",
"Zone in Double Trigger",
"Zone in Alarm",
"Zone Excluded",
"Zone Auto Excluded",
"Zone Supervision Fail Pending",
"Zone Supervision Fail",
"Zone Doors Open",
"Zone Detector Low Battery",
"Zone Detector Tamper",
"Miscellaneous Alarms",
"Arming",
"Outputs",
"View State"
);
#
# Functions to display status request details
#
my @form = (
\&f4, \&f4, \&f4, \&f4,
\&f4, \&f4, \&f4, \&f4,
\&f4, \&f4, \&f4, \&f4,
\&f4, \&f20, \&f21, \&f22,
\&f23
);
#
# F4 -- zone events
#
sub
f4
{
my (@d) = @_;
my ($v, $s, $m);
$v = hex $d[0];
$s = "Z";
$m = 1;
for (1..8)
{
$s .= ($v & $m) ? $_ : ' ';
$m <<= 1;
}
$s;
}
#
# F20 -- miscellaneous alarms
#
my (@f20) = (
"Duress",
"Panic",
"Medical",
"Fire",
"Install End",
"Ext. Tamper",
"Panel Tamper",
"Keypad Tamper",
"Pendant Panic",
"Panel Battery Low",
"Panel Battery Low",
"Mains Fail",
"CBus Fail"
);
sub
f20
{
my (@d) = @_;
my ($v, @s, $m);
local $" = "','";
$v = hex($d[0]) + 256 * hex($d[1]);
@s = ();
$m = 1;
for (0..12)
{
if ($v & $m)
{
push @s, $f20[$_];
}
$m <<= 1;
}
"'@s'";
}
#
# F21 -- arming status
#
my (@f21) = (
"AREA 1 ARMED",
"AREA 2 ARMED",
"AREA 1 FULLY ARMED",
"AREA 2 FULLY ARMED",
"MONITOR ARMED",
"Day Mode Armed",
"Entry Delay 1 ON",
"Entry Delay 2 ON",
"Manual Exclude mode",
"Memory mode",
"Day Zone Select",
);
sub
f21
{
my (@d) = @_;
my ($v, @s, $m);
local $" = "','";
$v = hex($d[0]) + 256 * hex($d[1]);
@s = ();
$m = 1;
for (0..10)
{
if ($v & $m)
{
push @s, $f21[$_];
}
$m <<= 1;
}
"'@s'";
}
#
# F22 -- Output states
#
my (@f22) = (
"Siren Loud",
"Siren Soft",
"Siren Soft Monitor",
"Siren Fire",
"Strobe",
"Reset",
"Sonalert",
"Keypad Display Enable",
"Aux1",
"Aux2",
"Aux3",
"Aux4",
"Monitor Out",
"Power Fail",
"Panel Batt Fail",
"Tamper Xpand"
);
sub
f22
{
my (@d) = @_;
my ($v, @s, $m);
local $" = "','";
$v = hex($d[0]) + 256 * hex($d[1]);
@s = ();
$m = 1;
for (0..15)
{
if ($v & $m)
{
push @s, $f22[$_];
}
$m <<= 1;
}
"'@s'";
}
#
# F23 -- View states. Hmm.
#
my (%f23) = (
F000 => "NORMAL",
E000 => "BRIEF DAY (CHIME)",
D000 => "HOME",
C000 => "MEMORY",
B000 => "BRIEF DAY ZONE SELECT",
A000 => "EXCLUDE SELECT",
9000 => "USER PROGRAM",
8000 => "INSTALLER PROGRAM"
);
sub
f23
{
my (@d) = @_;
my ($v, $s, $m);
local $" = "','";
$v = hex($d[0]) * 256 + hex($d[1]);
$s = sprintf "%04X", $v;
exists $f23{$s} ? $f23{$s} : "state $s ????";
}
sub
printtime
{
my ($t) = @_;
$t =~ /(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
printf("%s/%s/%s %s:%s:%s", $3, $2, $1, $4, $5, $6);
}
sub
showevent
{
my ($t, $d) = @_;
printtime $t;
$d =~ /^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/;
my ($ev, $id, $area) = ($1, $2, $3);
if ($ev eq '00')
{
$ev = "Unsealed: $zone[$id] $area";
}
elsif ($ev eq '01')
{
$ev = "Sealed: $zone[$id] $area";
}
elsif ($ev eq '02' or $ev eq '03')
{
$ev = $ev eq '02' ? "Alarm " : "Alarm-restore ";
if ($id =~ /^0[1-8]$/)
{
$ev .= $zone[$id];
}
else
{
$ev .= $id;
}
$ev .= " $area";
}
elsif ($ev eq '10')
{
$ev = "Power-fail"
}
elsif ($ev eq '11')
{
$ev = "Power-restore";
}
elsif ($ev eq '12')
{
$ev = "Battery=fail"
}
elsif ($ev eq '13')
{
$ev = "Battery-restore";
}
elsif ($ev eq '14')
{
$ev = "Report-fail"
}
elsif ($ev eq '15')
{
$ev = "Report-ok"
}
elsif ($ev eq '20' or $ev eq '21')
{
$ev = "Entry-delay-" . ($ev eq '20' ? "start" : "end");
$ev .= " $zone[$id] $area"
}
elsif ($ev eq '22' or $ev eq '23')
{
$ev = "Exit-delay-" . ($ev eq '20' ? "start" : "end");
$ev .= " $zone[$id] $area"
}
elsif ($ev eq '24' or $ev eq '25')
{
$ev = "Armed-" . ($ev eq '24' ? "away" : "home");
$ev .= " user-$id $area";
}
elsif ($ev eq '26')
{
$ev = "Armed-day";
}
elsif ($ev eq '2f')
{
$ev = "Disarmed user-$id $area";
}
elsif ($ev eq '31' or $ev eq '32')
{
$ev = $ev eq '31' ? 'on' : 'off';
if ($id =~ /^0[1-9]$/ or $id eq '10')
{
$ev = "Aux-$id-$ev";
}
elsif ($id =~ /^9([0-9])$/)
{
$ev = "$out[$1]-$ev";
}
else
{
$ev = "Unknown-$ev";
}
}
else
{
$ev .= " $id $area";
}
print " $ev\n";
}
sub
showstatus
{
my ($req, @d) = @_;
print "Status:", $req[$req],":", &{$form[$req]}(@d),"\n";
}
#
# Turn ASCII alarm output into something a bit more human friendly
#
while (<>)
{
chomp;
next if /^$/;
if (/^8700([\da-f]{2})61([\da-f]{6})(\d{12})([\da-f]{2})$/)
{
my ($len, $data, $time, $cksum) = ($1,$2,$3,$4,$5);
showevent($time, $data);
}
elsif (/^82000360([\da-f]{2})([\da-f]{2})([\da-f]{2})[\da-f]{2}$/)
{
my ($req,$d1,$d2) = ($1,$2,$3);
showstatus($req, $d1, $d2);
}
else
{
warn "Unknown '$_'\n";
}
}
The following C program causes a status report to be sent to that file (I'm assuming you're reading the Ness ASCII Protocol document so you know that "status 0" would report which zones were unsealed, "status 14" would report which zones were armed, etc.).
Code:
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
/*
* Open the serial port for writing with appropriate comms.
*/
static int
open_serial(const char *dev)
{
int fd;
struct termios tio;
if ((fd = open(dev, O_WRONLY)) < 0)
{
return(-1);
}
if (tcgetattr(fd, &tio) < 0)
{
close(fd);
return(-1);
}
cfsetspeed(&tio, B9600);
tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONOCR|ONLRET);
tio.c_cflag |= (CLOCAL | CREAD | CS8);
tio.c_cflag &= ~(CRTSCTS | CSTOPB | PARENB);
if (tcsetattr(fd, 0, &tio) < 0)
{
close(fd);
return(-1);
}
return(fd);
}
/*
* Append the appropriate checksum termination to a string.
*/
static void
cksum(char *s, int cont)
{
unsigned int sum;
sum = 0;
while (*s)
{
sum += *s++;
}
sum = -sum & 0xff;
sprintf(s, (cont ? "%02X?\r\n" : "%02X\r\n"), sum);
}
int
main(int argc, char **argv)
{
int fd;
int i;
char cmdbuf[80];
int sum;
int req;
if (argc != 2)
{
fprintf(stderr, "Usage:%s <status#>\n", argv[0]);
return(1);
}
req = atoi(argv[1]);
if (req < 0 || req > 16)
{
fprintf(stderr, "request must bin in [0:16]\n");
return(1);
}
if ((fd = open_serial("/dev/ttyS0")) < 0)
{
perror("/dev/ttyS0");
return(1);
}
sprintf(cmdbuf, "8300360S%02d", req);
cksum(cmdbuf, 0);
#if 0
puts(cmdbuf);
#else
write(fd, cmdbuf, strlen(cmdbuf));
#endif
return(0);
}
I'll not bother with my code that was trying to set the time. It's similar to the status code above, but follows the keypad strings protocol rather than status requests.
Bookmarks