mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-09-23 02:55:44 -04:00
The scripts run under -T, so they cannot trust the caller's PATH and set their own. Sixteen of them hardcoded /bin:/usr/bin:/usr/local/bin, which assumes everything they shell out to lives under /usr or /usr/local. ZoneMinder::General::findDbCommand looks for a database client on that PATH, and zmupdate.pl and zmcamtool.pl run what it finds. So an install whose client sits anywhere else cannot apply schema changes: sh: mysql: command not found Command 'mysql -u'zmuser' ... ' exited with status: 127 even with the client on the caller's PATH. Homebrew on Apple Silicon is the case that surfaced it - the client is in /opt/homebrew/bin - but a --prefix=/opt install on Linux has the same shape, as does anything that keeps its database client outside the FHS locations. Replaced the literal with @ZM_SCRIPT_PATH@, defaulting to the same three directories plus wherever cmake actually found a client, and overridable for packagers who want to pin it. Warn at configure time when no client is found at all, since that failure otherwise appears much later and says something unrelated. Nothing changes for an install whose client is already under /usr/bin: the directory is only appended when it is not in the list, so the default stays exactly as it was. Memory.pm is deliberately left alone. It has a narrower PATH of /bin:/usr/bin, and the only command it runs is uname through an absolute path from ZM_PATH_UNAME, so it does not need widening. Verified on macOS across the three cases: with the client in /opt/homebrew/bin the directory is appended; -DZM_SCRIPT_PATH= is respected verbatim; and pointing detection at /usr/bin/mariadb leaves the default untouched. Build clean, suite 146 cases. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B5KL9Xbi7K5aGsauLtd8tG
673 lines
19 KiB
Plaintext
673 lines
19 KiB
Plaintext
#!@PERL_EXECUTABLE@ -wT
|
|
#
|
|
# ==========================================================================
|
|
#
|
|
# ZoneMinder X10 Control Script, $Date$, $Revision$
|
|
# Copyright (C) 2001-2008 Philip Coombes
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
# ==========================================================================
|
|
|
|
=head1 NAME
|
|
|
|
zmx10.pl - ZoneMinder X10 Control Script
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
zmx10.pl -c <command>,--command=<command> [-u <unit code>,--unit-code=<unit code>]
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This script controls the monitoring of the X10 interface and the consequent
|
|
management of the ZM daemons based on the receipt of X10 signals.
|
|
|
|
=head1 OPTIONS
|
|
|
|
-c <command>, --command=<command> - Command to issue, one of 'on','off','dim','bright','start', 'status','shutdown'
|
|
-u <unit code>, --unit-code=<unit code> - Unit code to act on required for all commands
|
|
except 'status' (optional) and 'shutdown'
|
|
-v, --verison - Pirnts the currently installed version of ZoneMinder
|
|
|
|
=cut
|
|
use strict;
|
|
use bytes;
|
|
|
|
# ==========================================================================
|
|
#
|
|
# These are the elements you can edit to suit your installation
|
|
#
|
|
# ==========================================================================
|
|
|
|
use constant CAUSE_STRING => 'X10'; # What gets written as the cause of any events
|
|
|
|
# ==========================================================================
|
|
#
|
|
# Don't change anything below here
|
|
#
|
|
# ==========================================================================
|
|
|
|
@EXTRA_PERL_LIB@
|
|
use ZoneMinder;
|
|
use POSIX;
|
|
use Socket;
|
|
use Getopt::Long;
|
|
use autouse 'Pod::Usage'=>qw(pod2usage);
|
|
use autouse 'Data::Dumper'=>qw(Dumper);
|
|
|
|
use constant SOCK_FILE => $Config{ZM_PATH_SOCKS}.'/zmx10.sock';
|
|
|
|
$| = 1;
|
|
|
|
$ENV{PATH} = '@ZM_SCRIPT_PATH@';
|
|
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
|
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
|
|
|
my $command;
|
|
my $unit_code;
|
|
my $version;
|
|
|
|
GetOptions(
|
|
'command=s' =>\$command,
|
|
'unit-code=i' =>\$unit_code,
|
|
'version' =>\$version
|
|
) or pod2usage(-exitstatus => -1);
|
|
|
|
if ( $version ) {
|
|
print ZoneMinder::Base::ZM_VERSION;
|
|
exit(0);
|
|
}
|
|
|
|
die 'No command given' unless $command;
|
|
die 'No unit code given'
|
|
unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) );
|
|
|
|
if ( $command eq 'start' ) {
|
|
X10Server::runServer();
|
|
exit();
|
|
}
|
|
|
|
logInit();
|
|
logSetSignal();
|
|
my $zm_terminate = 0;
|
|
sub TermHandler {
|
|
Info('Received TERM, exiting');
|
|
$zm_terminate = 1;
|
|
}
|
|
$SIG{TERM} = \&TermHandler;
|
|
$SIG{INT} = \&TermHandler;
|
|
|
|
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0)
|
|
or Fatal("Can't open socket: $!");
|
|
|
|
my $saddr = sockaddr_un(SOCK_FILE);
|
|
|
|
if ( !connect(CLIENT, $saddr) ) {
|
|
# The server isn't there
|
|
print("Unable to connect, starting server\n");
|
|
close(CLIENT);
|
|
|
|
if ( my $cpid = fork() ) {
|
|
# Parent process just sleep and fall through
|
|
sleep(2);
|
|
logReinit();
|
|
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
|
connect(CLIENT, $saddr) or Fatal("Can't connect: $!");
|
|
} elsif ( defined($cpid) ) {
|
|
setpgrp();
|
|
|
|
logReinit();
|
|
X10Server::runServer();
|
|
} else {
|
|
Fatal("Can't fork: $!");
|
|
}
|
|
}
|
|
# The server is there, connect to it
|
|
CLIENT->autoflush();
|
|
my $message = $command;
|
|
$message .= ';'.$unit_code if $unit_code;
|
|
print(CLIENT $message);
|
|
shutdown(CLIENT, 1);
|
|
while ( my $line = <CLIENT> ) {
|
|
chomp($line);
|
|
print("$line\n");
|
|
}
|
|
close(CLIENT);
|
|
exit;
|
|
|
|
#
|
|
# ==========================================================================
|
|
#
|
|
# This is the X10 Server package
|
|
#
|
|
# ==========================================================================
|
|
#
|
|
package X10Server;
|
|
|
|
use strict;
|
|
use bytes;
|
|
|
|
use ZoneMinder;
|
|
use POSIX;
|
|
use DBI;
|
|
use Socket;
|
|
use X10::ActiveHome;
|
|
use autouse 'Data::Dumper'=>qw(Dumper);
|
|
|
|
our $dbh;
|
|
our $x10;
|
|
|
|
our %monitor_hash;
|
|
our %device_hash;
|
|
our %pending_tasks;
|
|
|
|
sub runServer {
|
|
Info('X10 server starting');
|
|
|
|
socket(SERVER, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
|
unlink(main::SOCK_FILE);
|
|
my $saddr = sockaddr_un(main::SOCK_FILE);
|
|
bind(SERVER, $saddr) or Fatal("Can't bind: $!");
|
|
listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!");
|
|
|
|
$dbh = zmDbConnect();
|
|
|
|
$x10 = new X10::ActiveHome(
|
|
port=>$Config{ZM_X10_DEVICE},
|
|
house_code=>$Config{ZM_X10_HOUSE_CODE},
|
|
debug=>1
|
|
);
|
|
Fatal("Failed to open x10 device at $Config{ZM_X10_DEVICE}") if !$x10;
|
|
|
|
loadTasks();
|
|
|
|
$x10->register_listener(\&x10listen);
|
|
|
|
my $rin = '';
|
|
vec($rin, fileno(SERVER), 1) = 1;
|
|
vec($rin, $x10->select_fds(), 1) = 1;
|
|
my $timeout = 1.0; # WHy .2? Why not 1s?
|
|
Debug(2, 'F:'.fileno(SERVER)."\n" );
|
|
my $reload = undef;
|
|
my $reload_count = 0;
|
|
my $reload_limit = $Config{ZM_X10_DB_RELOAD_INTERVAL} / $timeout;
|
|
while ( !$zm_terminate ) {
|
|
Debug(2, "Selecting for $timeout");
|
|
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
|
#print( "Off select, NF:$nfound, ER:$!\n" );
|
|
#print( vec( $rout, fileno(SERVER),1)."\n" );
|
|
#print( vec( $rout, $x10->select_fds(),1)."\n" );
|
|
if ( $nfound > 0 ) {
|
|
Debug(2, "Nfound $nfound");
|
|
if ( vec($rout, fileno(SERVER), 1) ) {
|
|
my $paddr = accept(CLIENT, SERVER);
|
|
my $message = <CLIENT>;
|
|
|
|
my ($command, $unit_code) = split(';', $message);
|
|
|
|
my $device;
|
|
if ( defined($unit_code) ) {
|
|
if ( $unit_code < 1 || $unit_code > 16 ) {
|
|
dPrint(ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n");
|
|
next;
|
|
}
|
|
|
|
$device = $device_hash{$unit_code};
|
|
if ( !$device ) {
|
|
$device = $device_hash{$unit_code} = {
|
|
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
|
status=>'unknown'
|
|
};
|
|
}
|
|
} # end if defined($unit_code)
|
|
|
|
my $result;
|
|
if ( $command eq 'on' ) {
|
|
$result = $device->{appliance}->on();
|
|
} elsif ( $command eq 'off' ) {
|
|
$result = $device->{appliance}->off();
|
|
}
|
|
#elsif ( $command eq 'dim' )
|
|
#{
|
|
#$result = $device->{appliance}->dim();
|
|
#}
|
|
#elsif ( $command eq 'bright' )
|
|
#{
|
|
#$result = $device->{appliance}->bright();
|
|
#}
|
|
elsif ( $command eq 'status' ) {
|
|
if ( $device ) {
|
|
dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n");
|
|
} else {
|
|
foreach my $unit_code ( sort( keys(%device_hash) ) ) {
|
|
my $device = $device_hash{$unit_code};
|
|
dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n");
|
|
}
|
|
}
|
|
} elsif ( $command eq 'shutdown' ) {
|
|
last;
|
|
} else {
|
|
dPrint(ZoneMinder::Logger::ERROR, "Invalid command '$command'\n");
|
|
}
|
|
if ( defined($result) ) {
|
|
# FIXME
|
|
if ( 1 || $result ) {
|
|
$device->{status} = uc($command);
|
|
dPrint(ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n");
|
|
#x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) );
|
|
} else {
|
|
dPrint(ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n");
|
|
}
|
|
} # end if defined result
|
|
close(CLIENT);
|
|
} elsif ( vec($rout, $x10->select_fds(), 1) ) {
|
|
$x10->handle_input();
|
|
} else {
|
|
Fatal('Bogus descriptor');
|
|
}
|
|
} elsif ( $nfound < 0 ) {
|
|
if ( $! != EINTR ) {
|
|
Fatal("Can't select: $!");
|
|
} else {
|
|
Debug(2, "Nfound $nfound $!");
|
|
}
|
|
} else {
|
|
Debug(2, "Select timed out");
|
|
# Check for state changes
|
|
foreach my $monitor_id ( sort(keys(%monitor_hash) ) ) {
|
|
my $monitor = $monitor_hash{$monitor_id};
|
|
my $state = zmGetMonitorState($monitor);
|
|
if ( !defined($state) ) {
|
|
$reload = !undef;
|
|
next;
|
|
}
|
|
if ( defined( $monitor->{LastState} ) ) {
|
|
my $task_list;
|
|
if ( ($state == STATE_ALARM || $state == STATE_ALERT)
|
|
&& ($monitor->{LastState} == STATE_IDLE )
|
|
) # Gone into alarm state
|
|
{
|
|
Debug("Applying ON_list for $monitor_id");
|
|
$task_list = $monitor->{ON_list};
|
|
} elsif ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) {
|
|
# Come out of alarm state
|
|
Debug("Applying OFF_list for $monitor_id");
|
|
$task_list = $monitor->{OFF_list};
|
|
}
|
|
if ( $task_list ) {
|
|
foreach my $task ( @$task_list ) {
|
|
processTask($task);
|
|
}
|
|
}
|
|
} # end if defined laststate
|
|
$monitor->{LastState} = $state;
|
|
} # end foreach monitor
|
|
|
|
# Check for pending tasks
|
|
my $now = time();
|
|
foreach my $activation_time ( sort(keys(%pending_tasks) ) ) {
|
|
last if ( $activation_time > $now );
|
|
my $pending_list = $pending_tasks{$activation_time};
|
|
foreach my $task ( @$pending_list ) {
|
|
processTask($task);
|
|
}
|
|
delete $pending_tasks{$activation_time};
|
|
}
|
|
if ( $reload or (++$reload_count >= $reload_limit) ) {
|
|
loadTasks();
|
|
$reload = undef;
|
|
$reload_count = 0;
|
|
}
|
|
}
|
|
}
|
|
Info("X10 server exiting");
|
|
close(SERVER);
|
|
exit();
|
|
}
|
|
|
|
sub addToDeviceList {
|
|
my $unit_code = shift;
|
|
my $event = shift;
|
|
my $monitor = shift;
|
|
my $function = shift;
|
|
my $limit = shift;
|
|
|
|
Debug("Adding to device list, uc:$unit_code, ev:$event, mo:"
|
|
.$monitor->{Id}.", fu:$function, li:$limit"
|
|
);
|
|
my $device = $device_hash{$unit_code};
|
|
if ( !$device ) {
|
|
$device = $device_hash{$unit_code} = {
|
|
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
|
status=>'unknown'
|
|
};
|
|
}
|
|
|
|
my $task = {
|
|
type=>'device',
|
|
monitor=>$monitor,
|
|
address=>$device->{appliance}->address(),
|
|
function=>$function
|
|
};
|
|
|
|
if ( $limit ) {
|
|
$task->{limit} = $limit
|
|
}
|
|
|
|
my $task_list = $device->{$event.'_list'};
|
|
if ( !$task_list ) {
|
|
$task_list = $device->{$event.'_list'} = [];
|
|
}
|
|
push @$task_list, $task;
|
|
} # end sub addToDeviceList
|
|
|
|
sub addToMonitorList {
|
|
my $monitor = shift;
|
|
my $event = shift;
|
|
my $unit_code = shift;
|
|
my $function = shift;
|
|
my $limit = shift;
|
|
|
|
Debug("Adding to monitor list, uc:$unit_code, ev:$event, mo:".$monitor->{Id}
|
|
.", fu:$function, li:$limit"
|
|
);
|
|
my $device = $device_hash{$unit_code};
|
|
if ( !$device ) {
|
|
$device = $device_hash{$unit_code} = {
|
|
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
|
status=>'unknown'
|
|
};
|
|
}
|
|
|
|
my $task = {
|
|
type=>'monitor',
|
|
device=>$device,
|
|
id=>$monitor->{Id},
|
|
function=>$function
|
|
};
|
|
if ( $limit ) {
|
|
$task->{limit} = $limit;
|
|
}
|
|
|
|
my $task_list = $monitor->{$event.'_list'};
|
|
if ( !$task_list ) {
|
|
$task_list = $monitor->{$event.'_list'} = [];
|
|
}
|
|
push @$task_list, $task;
|
|
} # end sub addToMonitorList
|
|
|
|
sub loadTasks {
|
|
%monitor_hash = ();
|
|
|
|
Debug('Loading tasks');
|
|
# Clear out all old device task lists
|
|
foreach my $unit_code ( sort keys(%device_hash) ) {
|
|
my $device = $device_hash{$unit_code};
|
|
$device->{ON_list} = [];
|
|
$device->{OFF_list} = [];
|
|
}
|
|
|
|
my $sql = 'SELECT M.*,T.* FROM Monitors as M
|
|
INNER JOIN TriggersX10 as T on (M.Id = T.MonitorId)
|
|
WHERE M.Capturing != \'None\'
|
|
AND find_IN_set(\'X10\', M.Triggers)';
|
|
my $sth = $dbh->prepare_cached( $sql )
|
|
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
|
|
my $res = $sth->execute()
|
|
or Fatal("Can't execute: ".$sth->errstr());
|
|
while( my $monitor = $sth->fetchrow_hashref() ) {
|
|
# Check shared memory ok
|
|
if ( !zmMemVerify($monitor) ) {
|
|
zmMemInvalidate($monitor);
|
|
next;
|
|
}
|
|
|
|
$monitor_hash{$monitor->{Id}} = $monitor;
|
|
|
|
if ( $monitor->{Activation} ) {
|
|
Debug("$monitor->{Name} has active string '$monitor->{Activation}'");
|
|
foreach my $code_string ( split(',', $monitor->{Activation}) ) {
|
|
#Debug( "Code string: $code_string\n" );
|
|
my ( $invert, $unit_code, $modifier, $limit )
|
|
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
|
$limit = 0 if !$limit;
|
|
if ( $unit_code ) {
|
|
if ( !$modifier || $modifier eq '+' ) {
|
|
addToDeviceList( $unit_code,
|
|
'ON',
|
|
$monitor,
|
|
(!$invert ? 'start_active' : 'stop_active'),
|
|
$limit
|
|
);
|
|
}
|
|
if ( !$modifier || $modifier eq '-' ) {
|
|
addToDeviceList( $unit_code,
|
|
'OFF',
|
|
$monitor,
|
|
(!$invert ? 'stop_active' : 'start_active'),
|
|
$limit
|
|
);
|
|
}
|
|
} # end if unit_code
|
|
} # end foreach code_string
|
|
}
|
|
if ( $monitor->{AlarmInput} ) {
|
|
Debug("$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'");
|
|
foreach my $code_string ( split(',', $monitor->{AlarmInput}) ) {
|
|
#Debug( "Code string: $code_string\n" );
|
|
my ( $invert, $unit_code, $modifier, $limit )
|
|
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
|
$limit = 0 if !$limit;
|
|
if ( $unit_code ) {
|
|
if ( !$modifier || $modifier eq '+' ) {
|
|
addToDeviceList( $unit_code,
|
|
'ON',
|
|
$monitor,
|
|
(!$invert ? 'start_alarm' : 'stop_alarm'),
|
|
$limit
|
|
);
|
|
}
|
|
if ( !$modifier || $modifier eq '-' ) {
|
|
addToDeviceList( $unit_code,
|
|
'OFF',
|
|
$monitor,
|
|
(!$invert ? 'stop_alarm' : 'start_alarm'),
|
|
$limit
|
|
);
|
|
}
|
|
} # end if unit_code
|
|
} # end foreach code_string
|
|
} # end if AlarmInput
|
|
if ( $monitor->{AlarmOutput} ) {
|
|
Debug("$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'");
|
|
foreach my $code_string ( split( ',', $monitor->{AlarmOutput} ) ) {
|
|
#Debug( "Code string: $code_string\n" );
|
|
my ( $invert, $unit_code, $modifier, $limit )
|
|
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
|
$limit = 0 if !$limit;
|
|
if ( $unit_code ) {
|
|
if ( !$modifier || $modifier eq '+' ) {
|
|
addToMonitorList( $monitor,
|
|
'ON',
|
|
$unit_code,
|
|
(!$invert ? 'on' : 'off'),
|
|
$limit
|
|
);
|
|
}
|
|
if ( !$modifier || $modifier eq '-' ) {
|
|
addToMonitorList( $monitor,
|
|
'OFF',
|
|
$unit_code,
|
|
(!$invert ? 'off' : 'on'),
|
|
$limit
|
|
);
|
|
}
|
|
} # end if unit_code
|
|
} # end foreach code_string
|
|
} # end if AlarmOutput
|
|
zmMemInvalidate($monitor);
|
|
}
|
|
} # end sub loadTasks
|
|
|
|
sub addPendingTask {
|
|
my $task = shift;
|
|
|
|
# Check whether we are just extending a previous pending task
|
|
# and remove it if it's there
|
|
foreach my $activation_time ( sort keys(%pending_tasks) ) {
|
|
my $pending_list = $pending_tasks{$activation_time};
|
|
my $new_pending_list = [];
|
|
foreach my $pending_task ( @$pending_list ) {
|
|
if ( $task->{type} ne $pending_task->{type} ) {
|
|
push( @$new_pending_list, $pending_task )
|
|
} elsif ( $task->{type} eq 'device' ) {
|
|
if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} )
|
|
|| ( $task->{function} ne $pending_task->{function} ))
|
|
{
|
|
push @$new_pending_list, $pending_task;
|
|
}
|
|
} elsif ( $task->{type} eq 'monitor' ) {
|
|
if (( $task->{device}->{appliance}->unit_code()
|
|
!= $pending_task->{device}->{appliance}->unit_code()
|
|
)
|
|
|| ( $task->{function} ne $pending_task->{function} )
|
|
) {
|
|
push @$new_pending_list, $pending_task;
|
|
}
|
|
} # end switch task->type
|
|
} # end foreach pending_task
|
|
|
|
if ( @$new_pending_list ) {
|
|
$pending_tasks{$activation_time} = $new_pending_list;
|
|
} else {
|
|
delete $pending_tasks{$activation_time};
|
|
}
|
|
} # end foreach activation_time
|
|
|
|
my $end_time = time() + $task->{limit};
|
|
my $pending_list = $pending_tasks{$end_time};
|
|
if ( !$pending_list ) {
|
|
$pending_list = $pending_tasks{$end_time} = [];
|
|
}
|
|
my $pending_task;
|
|
if ( $task->{type} eq 'device' ) {
|
|
$pending_task = {
|
|
type=>$task->{type},
|
|
monitor=>$task->{monitor},
|
|
function=>$task->{function}
|
|
};
|
|
$pending_task->{function} =~ s/start/stop/;
|
|
} elsif ( $task->{type} eq 'monitor' ) {
|
|
$pending_task = {
|
|
type=>$task->{type},
|
|
device=>$task->{device},
|
|
function=>$task->{function}
|
|
};
|
|
$pending_task->{function} =~ s/on/off/;
|
|
}
|
|
push @$pending_list, $pending_task;
|
|
} # end sub addPendingTask
|
|
|
|
sub processTask {
|
|
my $task = shift;
|
|
|
|
if ( $task->{type} eq 'device' ) {
|
|
my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ );
|
|
|
|
if ( $class eq 'active' ) {
|
|
if ( $instruction eq 'start' ) {
|
|
zmMonitorEnable($task->{monitor});
|
|
if ( $task->{limit} ) {
|
|
addPendingTask($task);
|
|
}
|
|
} elsif( $instruction eq 'stop' ) {
|
|
zmMonitorDisable($task->{monitor});
|
|
}
|
|
} elsif( $class eq 'alarm' ) {
|
|
if ( $instruction eq 'start' ) {
|
|
zmTriggerEventOn(
|
|
$task->{monitor},
|
|
0,
|
|
main::CAUSE_STRING,
|
|
$task->{address}
|
|
);
|
|
if ( $task->{limit} ) {
|
|
addPendingTask($task);
|
|
}
|
|
} elsif( $instruction eq 'stop' ) {
|
|
zmTriggerEventCancel($task->{monitor});
|
|
}
|
|
} # end switch class
|
|
} elsif( $task->{type} eq 'monitor' ) {
|
|
if ( $task->{function} eq 'on' ) {
|
|
$task->{device}->{appliance}->on();
|
|
if ( $task->{limit} ) {
|
|
addPendingTask($task);
|
|
}
|
|
} elsif ( $task->{function} eq 'off' ) {
|
|
$task->{device}->{appliance}->off();
|
|
}
|
|
}
|
|
}
|
|
|
|
sub dPrint {
|
|
my $dbg_level = shift;
|
|
if ( fileno(CLIENT) ) {
|
|
print CLIENT @_
|
|
}
|
|
if ( $dbg_level == ZoneMinder::Logger::DEBUG ) {
|
|
Debug(@_);
|
|
} elsif ( $dbg_level == ZoneMinder::Logger::INFO ) {
|
|
Info(@_);
|
|
} elsif ( $dbg_level == ZoneMinder::Logger::WARNING ) {
|
|
Warning(@_);
|
|
}
|
|
elsif ( $dbg_level == ZoneMinder::Logger::ERROR ) {
|
|
Error( @_ );
|
|
} elsif ( $dbg_level == ZoneMinder::Logger::FATAL ) {
|
|
Fatal( @_ );
|
|
}
|
|
}
|
|
|
|
sub x10listen {
|
|
foreach my $event ( @_ ) {
|
|
#print( Data::Dumper( $_ )."\n" );
|
|
if ( $event->house_code() eq $Config{ZM_X10_HOUSE_CODE} ) {
|
|
my $unit_code = $event->unit_code();
|
|
my $device = $device_hash{$unit_code};
|
|
if ( !$device ) {
|
|
$device = $device_hash{$unit_code} = {
|
|
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
|
status=>'unknown'
|
|
};
|
|
}
|
|
next if ( $event->func() !~ /(?:ON|OFF)/ );
|
|
$device->{status} = $event->func();
|
|
my $task_list = $device->{$event->func().'_list'};
|
|
if ( $task_list ) {
|
|
foreach my $task ( @$task_list ) {
|
|
processTask($task);
|
|
}
|
|
}
|
|
} else {
|
|
Debug("Not for out house code ".$event->house_code().' eq '.$Config{ZM_X10_HOUSE_CODE});
|
|
} # end if correct house code
|
|
Debug('Got event - '.$event->as_string());
|
|
}
|
|
} # end sub x10listen
|
|
|
|
1;
|
|
__END__
|