| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- #!/usr/bin/perl
- use strict;
- use warnings;
- use Email::MIME;
- use Email::Sender::Simple qw(sendmail);
- use File::Basename qw(dirname);
- use Sys::Hostname;
- use POSIX qw(ceil);
- # Local modules
- use Cwd qw(abs_path);
- use lib dirname(abs_path($0));
- use DMX;
- # App config
- my $DATA_DIR = DMX::dataDir();
- my $STATE_SOCK = 'ANNOUNCE';
- my $OUTPUT_FILE = $DATA_DIR . $STATE_SOCK;
- my $PUSH_TIMEOUT = 20;
- my $PULL_TIMEOUT = $PUSH_TIMEOUT * 3;
- my $DELAY = $PULL_TIMEOUT / 2;
- my $ALARM_DELAY = 10;
- my $MODE_DELAY = 7.5;
- my $PROJ_DELAY = 59;
- my $PROJ_FACTOR = 0.95;
- # Debug
- my $DEBUG = 0;
- if ($ENV{'DEBUG'}) {
- $DEBUG = 1;
- }
- # State
- my %exists = ();
- my %last = ();
- my %mtime = ();
- my $alarmLast = 0;
- my $modeLast = 0;
- my $projLast = 0;
- my $pullLast = time();
- # Sockets
- DMX::stateSocket($STATE_SOCK);
- DMX::stateSubscribe($STATE_SOCK);
- # Loop forever
- while (1) {
- # Remember the last state, for comparison
- %last = %exists;
- # Wait for state updates
- my $cmdState = DMX::readState($DELAY, \%exists, \%mtime, undef());
- # Avoid repeated calls to time()
- my $now = time();
- # Record only valid states
- if (defined($cmdState)) {
- $pullLast = $now;
- }
- # Die if we don't see regular updates
- if ($now - $pullLast > $PULL_TIMEOUT) {
- die('No update on state socket in past ' . $PULL_TIMEOUT . " seconds. Exiting...\n");
- }
- # Speak when the theater mode changes
- if (exists($exists{'GAME'}) && exists($last{'GAME'}) && $exists{'GAME'} ne $last{'GAME'}) {
- my $mode = 'PLEX';
- if ($exists{'GAME'}) {
- $mode = 'GAME';
- }
- # Wake the input system when we switch to it
- my $wol = $ENV{'HOME'} . '/bin/video/wol/wol-' . lc($mode) . '.sh';
- if (-x $wol) {
- system($wol);
- }
- # Allow other checks to opt-out if they are mode-switch related
- $modeLast = $now;
- DMX::say('Theater mode: ' . lc($mode));
- }
- # Speak when LIGHTS changes
- if (exists($exists{'LIGHTS'}) && exists($last{'LIGHTS'}) && $exists{'LIGHTS'} ne $last{'LIGHTS'}) {
- if ($now - $modeLast > $MODE_DELAY) {
- my $mode = 'DOWN';
- if ($exists{'LIGHTS'}) {
- $mode = 'UP';
- }
- DMX::say('Lights ' . lc($mode));
- }
- }
- # Speak when AMPLIFIER_INPUT changes
- if ( exists($exists{'AMPLIFIER_INPUT'})
- && exists($last{'AMPLIFIER_INPUT'})
- && $exists{'AMPLIFIER_INPUT'} ne $last{'AMPLIFIER_INPUT'})
- {
- if ($now - $modeLast > $MODE_DELAY) {
- DMX::say('Amplifier input: ' . lc($exists{'AMPLIFIER_INPUT'}));
- }
- }
- # Speak when AMPLIFIER_MODE changes
- if ( exists($exists{'AMPLIFIER_MODE'})
- && exists($last{'AMPLIFIER_MODE'})
- && $exists{'AMPLIFIER_MODE'} ne $last{'AMPLIFIER_MODE'})
- {
- DMX::say('Amplifier mode: ' . lc($exists{'AMPLIFIER_MODE'}));
- }
- # Speak when PROJECTOR_CTRL_TIMER changes
- if (exists($exists{'PROJECTOR_CTRL_TIMER'}) && $exists{'PROJECTOR_CTRL_TIMER'}) {
- if ($now - $projLast > $PROJ_DELAY) {
- # Determine the unit
- my $unit = 'second';
- my $timeLeft = $exists{'PROJECTOR_CTRL_TIMER'};
- if ($exists{'PROJECTOR_CTRL_TIMER'} > 60) {
- $timeLeft = $exists{'PROJECTOR_CTRL_TIMER'} / 60;
- $unit = 'minute';
- }
- # Avoid saying "0" unless we *really* mean it
- $timeLeft = ceil($timeLeft);
- # Add an "s" as needed
- my $plural = 's';
- if ($timeLeft == 1) {
- $plural = '';
- }
- $unit .= $plural;
- # Speak
- DMX::say('Projector shutdown in about ' . $timeLeft . ' ' . $unit);
- # Update the delay timer
- $projLast = $now;
- }
- }
- # Speak when PROJECTOR_CTRL_POWER changes
- if ( exists($exists{'PROJECTOR_CTRL_POWER'})
- && exists($last{'PROJECTOR_CTRL_POWER'})
- && $exists{'PROJECTOR_CTRL_POWER'} ne $last{'PROJECTOR_CTRL_POWER'})
- {
- if ($exists{'PROJECTOR_CTRL_POWER'} eq 'OFF' || $exists{'PROJECTOR_CTRL_POWER'} eq 'ON') {
- $projLast = 0;
- DMX::say('Projector: ' . lc($exists{'PROJECTOR_CTRL_POWER'}));
- # Announce lamp life status, if near/past $LAMP_LIFE
- if (exists($exists{'PROJECTOR_CTRL_LIFE'}) && $exists{'PROJECTOR_CTRL_LIFE'} > $PROJ_FACTOR) {
- my $chance = ($exists{'PROJECTOR_CTRL_LIFE'} - $PROJ_FACTOR) * (1 / (1 - $PROJ_FACTOR));
- if (rand(1) <= $chance) {
- DMX::say('Projector lamp has run for ' . int($exists{'PROJECTOR_CTRL_LIFE'} * 100) . '% of its estimated life.');
- }
- }
- }
- }
- # Speak when BRIGHT changes
- if (exists($exists{'BRIGHT'}) && exists($last{'BRIGHT'}) && $exists{'BRIGHT'} ne $last{'BRIGHT'}) {
- my $mode = 'NOMINAL';
- if ($exists{'BRIGHT'}) {
- $mode = 'FULL';
- }
- DMX::say('Lights - ' . lc($mode) . ' power');
- }
- # Speak when NO_MOTION changes
- if (exists($exists{'NO_MOTION'}) && exists($last{'NO_MOTION'}) && $exists{'NO_MOTION'} ne $last{'NO_MOTION'}) {
- my $mode = 'ENABLED';
- if ($exists{'NO_MOTION'}) {
- $mode = 'DISABLED';
- }
- DMX::say('Motion detectors: ' . lc($mode));
- }
- # Speak when LOCK changes
- if (exists($exists{'LOCK'}) && exists($last{'LOCK'}) && $exists{'LOCK'} ne $last{'LOCK'}) {
- my $mode = 'UNLOCKED';
- if ($exists{'LOCK'}) {
- $mode = 'LOCKED';
- }
- DMX::say('System ' . lc($mode));
- }
- # Speak when ALARM_ENABLE changes
- if (exists($exists{'ALARM_ENABLE'}) && exists($last{'ALARM_ENABLE'}) && $exists{'ALARM_ENABLE'} ne $last{'ALARM_ENABLE'}) {
- my $mode = 'DISABLED';
- if ($exists{'ALARM_ENABLE'}) {
- $mode = 'ENABLED';
- }
- DMX::say('Alarm ' . lc($mode));
- }
- # Email when ALARM is asserted
- if (exists($exists{'ALARM'}) && exists($last{'ALARM'}) && $exists{'ALARM'} && !$last{'ALARM'}) {
- my @exists_tmp = ();
- foreach my $key (sort(keys(%exists))) {
- push(@exists_tmp, $key . ":\t" . $exists{$key} . ' @ ' . $mtime{$key});
- }
- my $message = Email::MIME->create(
- header_str => [
- To => 'zach@kotlarek.com',
- From => $ENV{'USER'} . '@' . Sys::Hostname::hostname(),
- Subject => 'Alarm Activated',
- ],
- attributes => {
- encoding => 'quoted-printable',
- charset => 'ISO-8859-1',
- },
- body_str => 'Alarm activated: ' . localtime() . "\n\nState: " . $cmdState . "\n" . join("\n", @exists_tmp) . "\n",
- );
- sendmail($message);
- }
- # Ongoing spoken ALARM
- if ($exists{'ALARM'}) {
- if ($now - $alarmLast > $ALARM_DELAY) {
- $alarmLast = $now;
- DMX::say('Unauthorized access detected.');
- }
- }
- }
|