Quellcode durchsuchen

Move annoucements into annouce. Publish more state from projectorCtrl

Zachary Kotlarek vor 10 Jahren
Ursprung
Commit
aee7a268b9
4 geänderte Dateien mit 146 neuen und 131 gelöschten Zeilen
  1. 64 8
      dmx/announce.pl
  2. 3 8
      dmx/launchd/com.uberzach.projectorCtrl.plist
  3. 73 115
      dmx/projectorCtrl.pl
  4. 6 0
      dmx/state.pl

+ 64 - 8
dmx/announce.pl

@@ -20,6 +20,8 @@ my $PULL_TIMEOUT = $PUSH_TIMEOUT * 3;
 my $DELAY        = $PULL_TIMEOUT / 2;
 my $ALARM_DELAY  = 5;
 my $MODE_DELAY   = 3;
+my $PROJ_DELAY   = 59;
+my $PROJ_FACTOR  = 0.9;
 
 # Debug
 my $DEBUG = 0;
@@ -33,6 +35,7 @@ my %last      = ();
 my %mtime     = ();
 my $alarmLast = 0;
 my $modeLast  = 0;
+my $projLast  = 0;
 my $pullLast  = time();
 
 # Sockets
@@ -93,17 +96,70 @@ while (1) {
 	}
 
 	# 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'}));
-			}
+	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'}));
+	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'} ne 'SHUTDOWN') {
+			$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

+ 3 - 8
dmx/launchd/com.uberzach.projectorPower.plist → dmx/launchd/com.uberzach.projectorCtrl.plist

@@ -4,15 +4,10 @@
 <dict>
 	<key>LimitLoadToSessionType</key>
 	<string>Aqua</string>
-	<key>LimitLoadToHosts</key>
-	<array>
-		<string>Loki.local</string>
-		<string>loki.uberzach.com</string>
-	</array>
 	<key>RunAtLoad</key>
 	<true/>
 	<key>Label</key>
-	<string>com.uberzach.projectorPower</string>
+	<string>com.uberzach.projectorCtrl</string>
 	<key>KeepAlive</key>
 	<dict>
 		<key>OtherJobEnabled</key>
@@ -25,7 +20,7 @@
 	</dict>
 	<key>ProgramArguments</key>
 	<array>
-		<string>/Users/tv/bin/video/dmx/projectorPower.pl</string>
+		<string>/Users/tv/bin/video/dmx/projectorCtrl.pl</string>
 	</array>
 	<!--
 		<key>EnvironmentVariables</key>
@@ -34,7 +29,7 @@
 			<string>1</string>
 		</dict>
 		<key>StandardErrorPath</key>
-		<string>/Users/tv/Library/Logs/com.uberzach.projectorPower.log</string>
+		<string>/Users/tv/Library/Logs/com.uberzach.projectorCtrl.log</string>
 	-->
 </dict>
 </plist>

+ 73 - 115
dmx/projectorPower.pl → dmx/projectorCtrl.pl

@@ -3,8 +3,9 @@ use strict;
 use warnings;
 
 # Local modules
-use Cwd qw(abs_path);
-use File::Basename qw(dirname);
+use Cwd qw( abs_path );
+use File::Temp qw( tempfile );
+use File::Basename qw( dirname );
 use lib dirname(abs_path($0));
 use DMX;
 
@@ -48,16 +49,17 @@ my %COLORS    = (
 
 # App config
 my $DATA_DIR     = DMX::dataDir();
-my $STATE_SOCK   = 'PROJECTOR_POWER';
+my $STATE_SOCK   = 'PROJECTOR_CTRL';
 my $PROJ_SOCK    = 'PROJECTOR';
 my $OUTPUT_FILE  = $DATA_DIR . $STATE_SOCK;
+my $POWER_FILE   = $OUTPUT_FILE . '_POWER';
+my $LIFE_FILE    = $OUTPUT_FILE . '_LIFE';
+my $TIMER_FILE   = $OUTPUT_FILE . '_TIMER';
+my $COLOR_FILE   = $OUTPUT_FILE . '_COLOR';
 my $PUSH_TIMEOUT = 20;
 my $PULL_TIMEOUT = $PUSH_TIMEOUT * 3;
 my $DELAY        = $PULL_TIMEOUT / 2;
 
-# Prototypes
-sub sayShutdown($);
-
 # Debug
 my $DEBUG = 0;
 if ($ENV{'DEBUG'}) {
@@ -74,17 +76,22 @@ my $state        = 'OFF';
 my $stateLast    = $state;
 my %exists       = ();
 my %mtime        = ();
-my $pushLast     = 0;
 my $pullLast     = time();
-my $update       = 0;
 my $lastAnnounce = 0;
 my $lastUser     = time();
 my $shutdown     = 0;
 my $life         = 0;
+my $lifeLast     = 0;
 my @color_sets   = sort { $a <=> $b } keys(%COLORS);
 my $color_set    = $color_sets[0];
 my $color        = $COLORS{$color_set}{'low'};
 my $lastPlay     = 0;
+my $timeLeft     = 0;
+my $timeLeftLast = 0;
+my $colorCmd     = undef();
+my $colorLast    = 0;
+my $powerCmd     = undef();
+my $powerLast    = 0;
 
 # Loop forever
 while (1) {
@@ -130,12 +137,12 @@ while (1) {
 	if ($exists{'GAME'} && $mtime{'MOTION'} > $lastUser) {
 		$lastUser = $mtime{'MOTION'};
 	}
-	
+
 	# Motion counts as activity when PLEX is not active
 	if (!$exists{'PLEX'} && $mtime{'MOTION'} > $lastUser) {
 		$lastUser = $mtime{'MOTION'};
 	}
-	
+
 	# Explict no-motion counts as activity when Plex is not foreground
 	if (!$exists{'PLEX'} && $exists{'NO_MOTION'} && $lastUser < $now) {
 		$lastUser = $now;
@@ -162,7 +169,6 @@ while (1) {
 	}
 
 	# Calculate the new state
-	$stateLast = $state;
 	if ($exists{'PROJECTOR'}) {
 		if ($shutdown) {
 			$state = 'SHUTDOWN';
@@ -237,134 +243,86 @@ while (1) {
 		print STDERR 'Selected color: ' . $color . "\n";
 	}
 
-	# Force updates on a periodic basis
-	if (!$update && $now - $pushLast > $PUSH_TIMEOUT) {
-
-		# Not for the projector
-		#if ($DEBUG) {
-		#	print STDERR "Forcing periodic update\n";
-		#}
-		#$update = 1;
+	# Calculate the shutdown counter
+	$timeLeft = 0;
+	if ($state eq 'SHUTDOWN') {
+		$timeLeft = $OFF_DELAY - $elapsed;
+	} elsif ($state eq 'COUNTDOWN') {
+		$timeLeft = ($TIMEOUT + $COUNTDOWN) - $elapsed;
 	}
 
-	# Force updates on any state change
-	if (!$update && $stateLast ne $state) {
+	# Update power when there is a state mismatch
+	if (($state eq 'OFF' && $exists{'PROJECTOR'}) || ($state eq 'ON' && !$exists{'PROJECTOR'})) {
 		if ($DEBUG) {
-			print STDERR 'State change: ' . $stateLast . ' => ' . $state . "\n";
+			print STDERR 'Power state mismatch: ' . $state . ':' . $exists{'PROJECTOR'} . "\n";
 		}
-		$update = 1;
+		$powerCmd = $state;
 	}
 
-	# Force updates when there is a physical state mistmatch
-	if (!$update && $state eq 'OFF' && $exists{'PROJECTOR'}) {
+	# Update color when there is a state mismatch and the projector is on
+	if ($exists{'PROJECTOR'} && $exists{'PROJECTOR_COLOR'} ne $color) {
 		if ($DEBUG) {
-			print STDERR 'Physical state mismatch: ' . $state . ':' . $exists{'PROJECTOR'} . "\n";
+			print STDERR 'Color state mismatch: ' . $color . ':' . $exists{'PROJECTOR_COLOR'} . "\n";
 		}
-		$update = 1;
+		$colorCmd = $color;
 	}
 
-	# Set the color mode as needed
-	if ($exists{'PROJECTOR'} && $exists{'PROJECTOR_COLOR'} ne $color) {
-		if ($DEBUG) {
-			print STDERR 'Setting color to: ' . $color . "\n";
-		}
-		$proj->send($color)
-		  or die('Unable to write command to projector socket: ' . $color . ": ${!}\n");
+	# Save the power state to disk
+	if ($state ne $stateLast) {
+		my ($fh, $tmp) = tempfile($POWER_FILE . '.XXXXXXXX', 'UNLINK' => 0);
+		print $fh $state . "\n";
+		close($fh);
+		rename($tmp, $POWER_FILE);
+		$stateLast = $state;
 	}
 
-	# Announce a pending shutdown
-	if ($state eq 'COUNTDOWN' || $state eq 'SHUTDOWN') {
-		my $timeLeft = 0;
-		if ($state eq 'SHUTDOWN') {
-			$timeLeft = $OFF_DELAY - $elapsed;
-		} else {
-			$timeLeft = ($TIMEOUT + $COUNTDOWN) - $elapsed;
-		}
-		sayShutdown($timeLeft / 60);
+	# Save the color state to disk
+	if ($color ne $colorLast) {
+		my ($fh, $tmp) = tempfile($COLOR_FILE . '.XXXXXXXX', 'UNLINK' => 0);
+		print $fh $color . "\n";
+		close($fh);
+		rename($tmp, $COLOR_FILE);
+		$colorLast = $color;
 	}
 
-	# Only allow updates to "ON" or "OFF" -- the projector knows no other states
-	if ($update && ($state ne 'ON' && $state ne 'OFF')) {
-		if ($DEBUG) {
-			print STDERR 'Ignoring update state other than ON/OFF: ' . $state . "\n";
-		}
-		$update = 0;
+	# Save the lamp life to disk
+	if ($life != $lifeLast) {
+		my ($fh, $tmp) = tempfile($LIFE_FILE . '.XXXXXXXX', 'UNLINK' => 0);
+		print $fh $life . "\n";
+		close($fh);
+		rename($tmp, $LIFE_FILE);
+		$lifeLast = $life;
 	}
 
-	# Only allow updates every few seconds -- the projector goes dumb during power state changes
-	if ($update && $now < $pushLast + $CMD_DELAY) {
+	# Save the shutdown time to disk
+	if ($timeLeft != $timeLeftLast) {
+		my ($fh, $tmp) = tempfile($TIMER_FILE . '.XXXXXXXX', 'UNLINK' => 0);
+		print $fh $timeLeft . "\n";
+		close($fh);
+		rename($tmp, $TIMER_FILE);
+		$timeLeftLast = $timeLeft;
+	}
+
+	# Send the color mode, if requested
+	if ($colorCmd) {
 		if ($DEBUG) {
-			print STDERR 'Ignoring overrate update: ' . $state . "\n";
+			print STDERR 'Setting color mode: ' . $color . "\n";
 		}
-		$update = 0;
+		$proj->send($color)
+		  or die('Unable to write command to projector socket: ' . $color . ": ${!}\n");
+		$colorCmd = undef();
 	}
 
-	# Update the projector
-	if ($update) {
-
-		# Extra debugging to record pushes
+	# Send master power state, if requested
+	# Only allow updates every few seconds -- the projector goes dumb during power state changes
+	if ($powerCmd && $now > $powerLast + $CMD_DELAY) {
 		if ($DEBUG) {
-			print STDERR 'State: ' . $state . "\n";
+			print STDERR 'Setting power state: ' . $state . "\n";
 		}
 
-		# Send master power state
 		$proj->send($state)
 		  or die('Unable to write command to proj socket: ' . $state . ": ${!}\n");
-
-		# Annouce the state change, after the fact
-		DMX::say('Projector ' . $state);
-
-		# Announce lamp life status, if near/past $LAMP_LIFE
-		if (exists($exists{'PROJECTOR_LAMP'})) {
-			if ($life > 0.9) {
-				my $chance = ($life - 0.9) * 10;
-				if (rand(1) <= $chance) {
-					DMX::say('Projector lamp has run for ' . int($life * 100) . '% of its estimated life.');
-				}
-			}
-		}
-
-		# No output file
-
-		# Update the push time
-		$pushLast = $now;
-
-		# Clear the lastAnnounce timer
-		$lastAnnounce = 0;
-
-		# Clear the update flag
-		$update = 0;
+		$powerLast = $now;
+		$powerCmd  = undef();
 	}
 }
-
-sub sayShutdown($) {
-	my ($minutesLeft) = @_;
-
-	# Only allow annoucements once per minute
-	my $now = time();
-	if ($now < $lastAnnounce + 60) {
-		return;
-	}
-	$lastAnnounce = $now;
-
-	# Determine the unit
-	my $unit     = 'minute';
-	my $timeLeft = $minutesLeft;
-	if ($minutesLeft < 1) {
-		$timeLeft = $minutesLeft * 60;
-		$unit     = 'second';
-	}
-
-	# 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);
-}

+ 6 - 0
dmx/state.pl

@@ -73,6 +73,12 @@ if ( $HOST =~ /loki/i ) {
     $MON_FILES{'PROJECTOR_INPUT'} = 'LINE-NOUPDATE';
     $MON_FILES{'PROJECTOR_LAMP'}  = 'LINE-NOUPDATE';
 
+    # Projector Control
+    $MON_FILES{'PROJECTOR_CTRL_COLOR'} = 'LINE-NOUPDATE';
+    $MON_FILES{'PROJECTOR_CTRL_LIFE'}  = 'LINE-NOUPDATE';
+    $MON_FILES{'PROJECTOR_CTRL_POWER'} = 'LINE-NOUPDATE';
+    $MON_FILES{'PROJECTOR_CTRL_TIMER'} = 'LINE-NOUPDATE';
+
     # Amplifier
     $MON_FILES{'AMPLIFIER'}       = 'STATUS';
     $MON_FILES{'AMPLIFIER_VOL'}   = 'LINE-NOUPDATE';