#!/usr/bin/perl -w use strict; use IO::Select; use IO::Socket::INET; use Scalar::Util qw(looks_like_number); # XXX anywhere that gets arguments from @ARGV needs to check if it's exhausted and in the right syntax (especially for numerics) open(SETTINGS, 'tv.cfg') or die; my $username = ; chomp $username; my $password = ; chomp $password; close SETTINGS; my $delay = 5; my @keys = qw(0 1 2 3 4 5 6 7 8 9 . ent power display power-source rw play ff pause prev stop next rec option sleep rec-stop power-saving cc av-mode view-mode flashback mute vol- vol+ chup chdw input 37 menu smart-central enter up down left right return exit ch 48 49 a b c d freeze app1 app2 app3 2d/3d netflix aal manual); my %keys; { my $n = 0; %keys = map { $_ => $n++; } @keys; } my %RDIN = ( '-100' => 'Screen Sharing (Miracast) [input mira], or Bluetooth [input bluetooth], or manual [key manual]', '0100' => 'Analog TV channel [input air-analog]', '0300' => 'Digital TV channel [input air-digital]', '0400' => 'Digital Cable TV channel ###.### [chan ###.###]', '1100' => 'Composite or Component [input 5]', '5000' => 'HDMI1 [input hdmi1]', '5100' => 'HDMI2 [input hdmi2]', '5200' => 'HDMI3 [input hdmi3]', '5300' => 'HDMI4 [input hdmi4]', 'ERR' => 'Input currently switching', ); my %RDIN_exitValues = ( '-100' => 6, '0100' => 7, '0300' => 8, '0400' => 9, '1100' => 5, '5000' => 1, '5100' => 2, '5200' => 3, '5300' => 4, 'ERR' => 10, ); my %queryResults = ( 'ACSU' => { '1' => 'Normal [surround normal]', '2' => 'Off [surround off]', '4' => '3D Hall [surround hall]', '5' => '3D Movie [surround movie]', '6' => '3D Standard [surround standard]', '7' => '3D Stadium [surround stadium]', }, 'AVMD' => { '1' => 'STANDARD [av standard]', '2' => 'MOVIE [av movie]', '3' => 'GAME [av game]', '4' => 'USER [av user]', '5' => 'DYNAMIC(Fixed) [av fixed]', '6' => 'DYNAMIC [av dynamic]', '7' => 'PC [av pc]', '17' => 'MOVIE THX [av thx]', }, 'DMSL' => { '0' => 'Demo mode disabled [demo none]', '1' => 'Demo overlay mode enabled [demo overlay]', }, 'POWR' => { '0' => 'Power Off [off]', '1' => 'Power On [on]', }, 'IAVD' => { '1' => 'HDMI1 [input hdmi1]', '2' => 'HDMI2 [input hdmi2]', '3' => 'HDMI3 [input hdmi3]', '4' => 'HDMI4 [input hdmi4]', '5' => 'Composite or Component [input 5]', }, 'IDIN' => { '0' => 'Analog Cable TV channel (?) [input cable-analog]', '1' => 'Analog Air TV channel [input air-analog]', '2' => 'Digital Cable TV channel (?) [input cable-digital]', '3' => 'Digital Air TV channel [input air-digital]', '4' => 'Digital Cable TV channel [chan ###.###]', '11' => 'Composite or Component or Home Network or USB/SD', '50' => 'HDMI1 [input hdmi1]', '51' => 'HDMI2 [input hdmi2]', '52' => 'HDMI3 [input hdmi3]', '53' => 'HDMI4 [input hdmi4]', 'ERR' => 'Actively switching, or Miracast, or Bluetooth, or manual', }, 'INP5' => { '0' => 'Composite (?) [command INP5 0]', '1' => 'Composite (?) [input composite]', '2' => 'Component [input component]', }, 'MUTE' => { '1' => 'Muted', '2' => 'Not muted', }, 'WIDE' => { '1' => '4:3 [view 4:3]', '2' => 'side stretched [view side-stretch]', '3' => 'zoom [view zoom]', '4' => 'flat stretched [view stretch]', '9' => '16:9 [view 16:9]', }, ); my $socket = new IO::Socket::INET(PeerAddr => 'tv.:10002') or die; my $select = IO::Select->new($socket); send($socket, "$username\r$password\r", 0); defined recv($socket, $_, 6, 0) or die "TV was busy when logging in.\n"; $_ eq "Login:" or die "Got line 1: '$_'\n"; defined recv($socket, $_, 11, 0) or die; $_ eq "\x0d\x0aPassword:" or die "Got line 2: '$_'\n"; defined recv($socket, $_, 2, 0) or die; $_ eq "\x0d\x0a" or die "Got line 3: '$_'\n"; while (@ARGV) { my $arg = shift @ARGV; if ($arg eq 'on') { print "Sending power-on signal\n"; power(1); } elsif ($arg eq 'off') { print "Sending power-off signal\n"; power(0); } elsif ($arg eq 'key') { my $key = shift @ARGV; if (exists($keys{$key})) { print "Remote control '$key': "; print remoteControlKey($keys{$key}); } else { print "tv: unknown key argument '$key', try 'help'"; } print "\n"; } elsif ($arg eq 'input') { $arg = shift @ARGV; if ($arg eq 'next') { print "Switching to next input: " . command('ITGD', '') . "\n"; } elsif ($arg eq 'tv') { print "Switching to last TV input: " . command('ITVD', '') . "\n"; } elsif ($arg eq 'cable-analog') { print "Switching to last analog cable input: " . command('IDIN', '0') . "\n"; # or 4 } elsif ($arg eq 'air-analog') { print "Switching to last analog air input: " . command('IDIN', '1') . "\n"; } elsif ($arg eq 'cable-digital') { print "Switching to last digital cable input: " . command('IDIN', '2') . "\n"; } elsif ($arg eq 'air-digital') { print "Switching to last digital air input: " . command('IDIN', '3') . "\n"; } elsif ($arg eq 'hdmi1') { print "Switching to HDMI1 input: " . command('IAVD', '1') . "\n"; # or IDIN 11, or IDIN 50 (those return ERR if redundant) } elsif ($arg eq 'hdmi2') { print "Switching to HDMI2 input: " . command('IAVD', '2') . "\n"; # or IDIN 12, or IDIN 51 (those return ERR if redundant) } elsif ($arg eq 'hdmi3') { print "Switching to HDMI3 input: " . command('IAVD', '3') . "\n"; # or IDIN 13, or IDIN 52 (those return ERR if redundant) } elsif ($arg eq 'hdmi4') { print "Switching to HDMI4 input: " . command('IAVD', '4') . "\n"; # or IDIN 14, or IDIN 53 (those return ERR if redundant) } elsif ($arg eq '5') { print "Switching to input 5 (composite or component): " . command('IAVD', '5') . "\n"; # or IDIN 15 } elsif ($arg eq 'composite') { print "Switching to composite input: " . command('INP5', '1') . "\n"; # or 0? maybe that makes it auto? } elsif ($arg eq 'component') { print "Switching to component input: " . command('INP5', '2') . "\n"; } elsif ($arg eq 'network') { print "Switching to home network input: " . command('IDIN', '81') . "\n"; } elsif ($arg eq 'usb') { print "Switching to USB input: " . command('IDIN', '82') . "\n"; } elsif ($arg eq 'mira') { print "Switching to Miracast (screen sharing) input, via USB input: \n"; print " USB: " . command('IDIN', '82') . "\n"; print " Miracast: " . command('ITGD', '') . "\n"; } elsif ($arg eq 'bluetooth') { print "Switching to Bluetooth input, via USB and Miracast inputs: \n"; print " USB: " . command('IDIN', '82') . "\n"; print " Miracast: " . command('ITGD', '') . "\n"; print " Bluetooth: " . command('ITGD', '') . "\n"; } else { print "tv: unknown input argument '$arg', try 'help'\n"; } } elsif ($arg eq 'retry-input') { $arg = shift @ARGV; my $sendValue; my $receiveValue; if ($arg eq 'hdmi1') { $sendValue = '1'; $receiveValue = '5000'; } elsif ($arg eq 'hdmi2') { $sendValue = '2'; $receiveValue = '5100'; } elsif ($arg eq 'hdmi3') { $sendValue = '3'; $receiveValue = '5200'; } elsif ($arg eq 'hdmi4') { $sendValue = '4'; $receiveValue = '5300'; } elsif ($arg eq '5') { $sendValue = '5'; $receiveValue = '1100'; } else { print "tv: unknown retry-input argument '$arg', try 'help'\n"; } if (defined($sendValue)) { my $retries = 20; while ($retries > 0) { my $currentInput = getInput(); if ($currentInput eq $receiveValue) { print " RDIN: $RDIN{$receiveValue}"; last; } print "Switching to specified input: " . command('IAVD', $sendValue) . "\n"; $retries -= 1; sleep 1; } if (!$retries) { print "tv: failed to switch input"; } } } elsif ($arg eq 'chan') { $arg = shift @ARGV; # these hang if the TV has channel switching disabled if ($arg eq 'up') { print "Sending channel-up command: "; print channelUp(1); } elsif ($arg eq 'down') { print "Sending channel-down command: "; print channelDown(1); } elsif ($arg =~ m/^[0-9][0-9]$/) { print "Selecting analog air channel $arg: "; print directChannelAnalogAir(0+$arg); } elsif ($arg =~ m/^[0-9][0-9][0-9]$/) { print "Selecting analog cable channel $arg: "; print directChannelAnalogCable(0+$arg); } elsif ($arg =~ m/^[0-9][0-9][0-9][0-9][0-9]$/) { print "Selecting digital cable channel $arg: "; print directChannelDigitalFiveDigit(0+$arg); } elsif ($arg =~ m/^([0-9][0-9])\.([0-9][0-9])$/) { print "Selecting digital air channel $1.$2: "; print directChannelDigitalTwoDigit(0+$1, 0+$2); } elsif ($arg =~ m/^([0-9][0-9][0-9])\.([0-9][0-9][0-9])$/) { print "Selecting digital cable channel $1.$2: "; print directChannelDigitalThreeDigit(0+$1, 0+$2); } else { print "tv: unknown chan argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'mute') { print "Muting: "; print mute(1); print "\n"; } elsif ($arg eq 'unmute') { print "Unmuting: "; print mute(2); print "\n"; } elsif ($arg eq 'toggle-mute') { print "Toggling mute state: "; print mute(0); print "\n"; } elsif ($arg eq 'vol') { my $level = shift @ARGV; if ($level =~ m/^([0-9]+)$/ and $1 >= 0 and $1 <= 100) { print "Setting volume to level $level: "; print volume($level); } else { print "tv: unknown volume argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'surround') { my $setting = shift @ARGV; if ($setting eq 'next') { print "Switching Surround Sound setting to next setting: "; print surroundSound(0); } elsif ($setting eq 'normal') { print "Switching Surround Sound setting to 'normal': "; print surroundSound(1); } elsif ($setting eq 'off') { print "Switching Surround Sound setting to 'off': "; print surroundSound(2); } elsif ($setting eq 'hall') { # 3d print "Switching Surround Sound setting to '3D Hall': "; print surroundSound(4); } elsif ($setting eq 'movie') { # 3d print "Switching Surround Sound setting to '3D Movie': "; print surroundSound(5); } elsif ($setting eq 'standard') { # 3d print "Switching Surround Sound setting to '3D Standard': "; print surroundSound(6); } elsif ($setting eq 'stadium') { # 3d print "Switching Surround Sound setting to '3D Stadium': "; print surroundSound(7); } else { print "tv: unknown surround sound argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'av') { my $setting = shift @ARGV; if ($setting eq 'next') { print "Switching AV Mode to next setting: "; print avMode(0); } elsif ($setting eq 'standard') { print "Switching AV Mode to 'STANDARD': "; print avMode(1); } elsif ($setting eq 'movie') { print "Switching AV Mode to 'MOVIE': "; print avMode(2); } elsif ($setting eq 'game') { print "Switching AV Mode to 'GAME': "; print avMode(3); } elsif ($setting eq 'user') { print "Switching AV Mode to 'USER': "; print avMode(4); } elsif ($setting eq 'fixed') { print "Switching AV Mode to 'DYNAMIC(Fixed)': "; print avMode(5); } elsif ($setting eq 'dynamic') { print "Switching AV Mode to 'DYNAMIC': "; print avMode(6); } elsif ($setting eq 'pc') { print "Switching AV Mode to 'PC': "; print avMode(7); } elsif ($setting eq 'thx') { print "Switching AV Mode to 'MOVIE THX': "; print avMode(17); } else { print "tv: unknown av argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'view') { my $setting = shift @ARGV; if ($setting eq 'next') { print "Switching view mode to next setting: "; print viewMode(0); } elsif ($setting eq '4:3') { print "Switching view mode to 4:3: "; print viewMode(1); } elsif ($setting eq 'side-stretch') { print "Switching view mode to side-stretch: "; print viewMode(2); } elsif ($setting eq 'zoom') { print "Switching view mode to zoom: "; print viewMode(3); } elsif ($setting eq 'stretch') { print "Switching view mode to stretch: "; print viewMode(4); } elsif ($setting eq '16:9') { print "Switching view mode to 16:9: "; print viewMode(9); } else { print "tv: unknown view argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'hpos') { my $setting = shift @ARGV; if ($setting =~ m/^-?[1-8]$/os or $setting eq '0') { print "Setting horizontal position to $setting: "; print setHPos($setting); } elsif ($setting eq 'left') { $setting = getHPos(); if ($setting > -8) { $setting -= 1; print "Setting horizontal position to $setting: "; print setHPos($setting); } else { print "Horizontal position is already all the way to the left."; } } elsif ($setting eq 'right') { $setting = getHPos(); if ($setting < 8) { $setting += 1; print "Setting horizontal position to $setting: "; print setHPos($setting); } else { print "Horizontal position is already all the way to the right."; } } else { print "tv: unknown hpos argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'vpos') { my $setting = shift @ARGV; if ($setting =~ m/^-?[1-8]$/os or $setting eq '0') { print "Setting vertical position to $setting: "; print setVPos($setting); } elsif ($setting eq 'down') { $setting = getVPos(); if ($setting > -8) { $setting -= 1; print "Setting vertical position to $setting: "; print setVPos($setting); } else { print "Vertical position is already all the way to the bottom."; } } elsif ($setting eq 'up') { $setting = getVPos(); if ($setting < 8) { $setting += 1; print "Setting vertical position to $setting: "; print setVPos($setting); } else { print "Vertical position is already all the way to the up."; } } else { print "tv: unknown vpos argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'sleep') { my $setting = shift @ARGV; if ($setting eq 'off') { print "Disabling sleep timer: "; print sleepTimer(0); } elsif ($setting eq '30') { print "Setting sleep timer to 30 minutes: "; print sleepTimer(1); } elsif ($setting eq '60') { print "Setting sleep timer to 60 minutes: "; print sleepTimer(2); } elsif ($setting eq '90') { print "Setting sleep timer to 90 minutes: "; print sleepTimer(3); } elsif ($setting eq '120') { print "Setting sleep timer to 120 minutes: "; print sleepTimer(4); } else { print "tv: unknown sleep argument '$setting', try 'help'"; } print "\n"; } elsif ($arg eq 'demo') { $arg = shift @ARGV; if ($arg eq 'none') { print demo(0); } elsif ($arg eq 'overlay') { print demo(1); } else { print "tv: unknown demo argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'get') { $arg = shift @ARGV; if ($arg eq 'name') { print getTVName(); } elsif ($arg eq 'model') { print getModelName(); } elsif ($arg eq 'version') { print getSoftwareVersion(); } elsif ($arg eq 'ip-version') { print getIPVersion(); } elsif ($arg eq 'input') { my $input = getInput(); if (exists $RDIN{$input}) { print " RDIN: $RDIN{$input}"; } else { print " RDIN: $input"; } if ($input ne '-100') { print "\n IDIN: " . getSetting('IDIN'); } if ($input eq '0100') { print "\n Channel: " . command('DCCH', '?'); } elsif ($input eq '0300') { print "\n Channel: " . command('DA2P', '?'); } elsif ($input eq '0400') { print "\n DC2U: " . command('DC2U', '?'); #print "\n DC2L: " . command('DC2L', '?'); # they all return the same thing #print "\n DC10: " . command('DC10', '?'); #print "\n DC11: " . command('DC11', '?'); } elsif ($input =~ /^5/) { # HDMI print "\n IAVD: " . getSetting('IAVD'); } elsif ($input eq '1100') { # Composite or Component print "\n IAVD: " . getSetting('IAVD'); print "\n INP5: " . getSetting('INP5'); } } elsif ($arg eq 'power') { print getSetting('POWR'); } elsif ($arg eq 'vol') { my $level = getVolume(); print "$level"; } elsif ($arg eq 'mute') { print getSetting('MUTE'); } elsif ($arg eq 'surround') { print getSetting('ACSU'); } elsif ($arg eq 'av') { print getSetting('AVMD'); } elsif ($arg eq 'view') { print getSetting('WIDE'); } elsif ($arg eq 'hpos') { print getHPos(); } elsif ($arg eq 'vpos') { print getVPos(); } elsif ($arg eq 'sleep') { my $time = getSleep(); print "$time"; } elsif ($arg eq 'demo') { print getSetting('DMSL'); } else { print "tv: unknown get argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'batch-get') { $arg = shift @ARGV; if ($arg eq 'input') { my $input = getInput(); exit(exists($RDIN_exitValues{$input}) ? $RDIN_exitValues{$input} : $RDIN_exitValues{'ERR'}); } elsif ($arg eq 'power') { exit getSettingExitValue('POWR'); } elsif ($arg eq 'vol') { exit getVolume(); } elsif ($arg eq 'mute') { exit getSettingExitValue('MUTE'); } elsif ($arg eq 'surround') { exit getSettingExitValue('ACSU'); } elsif ($arg eq 'av') { exit getSettingExitValue('AVMD'); } elsif ($arg eq 'view') { exit getSettingExitValue('WIDE'); } elsif ($arg eq 'hpos') { exit getHPos(); } elsif ($arg eq 'vpos') { exit getVPos(); } elsif ($arg eq 'sleep') { exit getSleep(); } elsif ($arg eq 'demo') { exit getSettingExitValue('DMSL'); } else { print "tv: unknown get-batch argument '$arg', try 'help'"; } print "\n"; } elsif ($arg eq 'msg') { $arg = shift @ARGV; print "Displaying message '$arg': "; print showMessage($arg); print "\n"; } elsif ($arg eq 'scan') { print "Scanning message space with argument '1':\n"; scan(1); } elsif ($arg eq 'scan-with-arg') { my $arg = shift @ARGV; print "Scanning message space with argument '$arg':\n"; scan($arg); } elsif ($arg eq 'test') { $arg = shift @ARGV; print "Scanning argument space for command '$arg'\n"; test($arg); } elsif ($arg eq 'debug-delay') { $arg = shift @ARGV; $delay = 0+$arg; } elsif ($arg eq 'command') { my $command = shift @ARGV; my $arg = shift @ARGV; print "Sending command '$command$arg'\n"; if (try($command, $arg) == 0) { print "\n ERR\n"; } } elsif ($arg eq 'delay') { $arg = shift @ARGV; sleep 0+$arg; } elsif ($arg eq 'help') { print "Known commands:\n"; print " on\n"; print " off\n"; { local $" = '|'; print " key @keys\n"; } print " input next|tv|cable-analog|air-analog|cable-digital|air-digital|hdmi1|hdmi2|hdmi3|hdmi4|5|composite|component|network|usb|mira|bluetooth\n"; print " retry-input hdmi1|hdmi2|hdmi3|hdmi4|5\n"; print " chan up|down|##|###|#####|##.##|###.### (# = zero-padded digits)\n"; print " mute|unmute|toggle-mute\n"; print " vol (0..100)\n"; print " surround next|off|normal|hall|movie|standard|stadium\n"; print " av next|standard|movie|game|user|fixed|dynamic|pc|thx\n"; print " view next|4:3|16:9|stretch|side-stretch|zoom\n"; print " hpos |left|right (-8..8)\n"; print " vpos |up|down (-8..8)\n"; print " sleep off|30|60|90|120\n"; print " demo none|overlay\n"; print " get name|model|version|ip-version|input|power|vol|mute|surround|av|view|hpos|vpos|sleep|demo\n"; print " batch-get input|power|vol|mute|surround|av|view|hpos|vpos|sleep|demo\n"; print " msg \n"; print " scan\n"; print " scan-with-arg \n"; print " test \n"; print " debug-delay