#!/usr/bin/perl -w
#
# Script : foscamControlCenter.pl
# Object : used to control Foscam camera based on documented HTTP API
# entitled "Foscam IPCamera CGI User Guide - v1.0.4"
# Author: freddy@linuxtribe.fr
#
# Date: Fri Feb 12 15:49:50 CET 2016
# Release : 0.1
# ChangeLog : 0.1 - init release (bug with ptzSetCruiseMap script
# Missing API Info)
#
##############################################################################

use strict;
use Curses;

my $release = '0.1';

# MAKE YOUR SETUP HERE #######################################################

my $user='';
my $pass='';
my $host='';
my $port=88;

my $wget='/usr/bin/wget';

# END OF YOUR SETUP ##########################################################

unless (-x $wget) {
print "$0 needs wget. please add wget to your system or update wget path in script $0.\n";
exit 1;
}

# Foscam IPCamera CGI User Guide - v1.0.4 ####################################

my $url="http://$host:$port/cgi-bin/CGIProxy.fcgi\?cmd=";
my $credentials="\&usr=$user\&pwd=$pass";

my %results = (
'0' => 'success',
'-1' => 'CGI string format error',
'-2' => 'Username or password error',
'-3' => 'Access deny',
'-4' => 'CGI execute fail',
'-5' => 'Timeout',
'-6' => 'Reserve',
'-7' => 'Unknown error',
'-8' => 'Reserve',
'-9' => 'host unreachable'
);

my %ptzSpeed = (
'4' => 'very slow',
'3' => 'slow',
'2' => 'normal',
'1' => 'fast',
'0' => 'very fast'
);

my %alarmState = (
'0' => 'disabled',
'1' => 'no alarm',
'2' => 'detect alarm'
);

my %standardState = (
'0' => 'off',
'1' => 'on'
);

my %ntpState = (
'0' => 'disabled',
'1' => 'update success',
'2' => 'update failed'
);

# INTERNAL FUNCTIONS TO QUERY FOSCAM DEVICE AND TO ANALYSE RESULTS ############

sub getCmdResult {
my ($d,$r) = @_;

my $found = 0;

foreach my $data (@{$d}) {
foreach my $label (keys %{$r}) {
if ($data =~ /<$label>([^<]*)<\/$label>/) {
$r->{$label}=$1;
$found = 1 if ($label eq 'result');
}
}
}
$r->{'result'}='-9' unless($found);
}


sub IPCamCmd {
my ($line,$cmd,$opts) = @_;

my %IPCamCmdResults = ( 'result' => '' );

if (defined($opts)) {
foreach(@{$opts}) {
$IPCamCmdResults{$_}='';
}
}

clearScreen($line,$line+20);

addstr($line++, 0, ' ');
addstr($line++, 2, "Command: ".$cmd);
my @cmdResult = `$wget --quiet -O - "$url$cmd$credentials"`;
getCmdResult(\@cmdResult,\%IPCamCmdResults);
addstr($line++, 2, "Result: ".$results{$IPCamCmdResults{'result'}});

if (defined($opts)) {
my $l = $line+1;
addstr($l, 0, " var value");
foreach(@{$opts}) {
$l++;
addstr($l, 0, ' ');
if ($_ eq 'speed') {
addstr($l, 0, " $_ ".$ptzSpeed{$IPCamCmdResults{$_}});
} elsif (/IOAlarm|motionDetectAlarm|soundAlarm/) {
addstr($l, 0, " $_ ".$alarmState{$IPCamCmdResults{$_}});
} elsif ($_ eq 'ntpState') {
addstr($l, 0, " $_ ".$ntpState{$IPCamCmdResults{$_}});
} elsif (/isWifiConnected|infraLedState|record/) {
addstr($l, 0, " $_ ".$standardState{$IPCamCmdResults{$_}});
} else {
addstr($l, 0, " $_ ".$IPCamCmdResults{$_});
}
}
}
}

# FOSCAM CONTROL CENTER UI ####################################################

my $ch = 0;
initscr();
noecho();

cbreak();
eval { keypad(1) };

my $rl = controlScreen();

while (1)
{
$ch = getch();
if ($ch =~ /\d/) {
if ($ch == KEY_UP) { IPCamCmd($rl, 'ptzMoveUp'); }
elsif ($ch == KEY_DOWN) { IPCamCmd($rl, 'ptzMoveDown'); }
elsif ($ch == KEY_LEFT) { IPCamCmd($rl, 'ptzMoveLeft'); }
elsif ($ch == KEY_RIGHT) {IPCamCmd($rl, 'ptzMoveRight'); }
} elsif ($ch eq ' ' ) { IPCamCmd($rl, 'ptzStopRun'); }
elsif ($ch eq 's' ) { IPCamCmd($rl, 'getPTZSpeed',['speed']); }
elsif ($ch eq 'S' ) {
clearScreen($rl,$rl+22);
addstr($rl+1, 0, "Please set new speed [0-4]: ");
my $s = getch();
if ($s =~ /^\d+$/) {
unless($s<=4&&$s>=0) {
addstr($rl+1, 0, "$s char not recognized as a correct value");
} else {
IPCamCmd($rl,"setPTZSpeed&speed=$s");
}
} else {
addstr($rl+1, 0, "$s char not recognized as a correct value");
}
}
elsif ($ch eq 'd' ) { IPCamCmd($rl, 'getDevState', ['IOAlarm','motionDetectAlarm','soundAlarm','record','ntpState','isWifiConnected','wifiConnectedAP','infraLedState']); }
elsif ($ch eq 'D' ) { IPCamCmd($rl, 'getDevInfo', ['productName','serialNo','devName','mac','year','mon','day','hour','min','sec','timeZone','firmwareVer','hardwareVer']); }
elsif ($ch eq 'x' ) { IPCamCmd($rl, 'setInfraLedConfig&mode=0'); }
elsif ($ch eq 'X' ) { IPCamCmd($rl, 'setInfraLedConfig&mode=1'); }
elsif ($ch eq 'n' ) { IPCamCmd($rl, 'openInfraLed'); }
elsif ($ch eq 'N' ) { IPCamCmd($rl, 'closeInfraLed' ); }
elsif ($ch eq 'i' ) { IPCamCmd($rl, 'getImageSetting',['brightness','contrast','hue','saturation','sharpness']); }
elsif ($ch eq 'I' ) {
clearScreen($rl,$rl+22);
addstr($rl+1, 0, "Which settings would you like to update : ");
addstr($rl+2, 0, "[b]rightness,[c]ontrast,[h]ue,[s]aturation,shar[p]ness,[r]eset");
addstr($rl+1, 45, "");
my $s = getch();
if ($s =~ /^[bchspr]$/) {
my $cmd = '';
if ($s eq 'b') {
$cmd = 'setBrightness&brightness=';
clearScreen($rl+2,$rl+3);
addstr($rl+2, 0, "Brightness [0-100] : ");
} elsif ($s eq 'c') {
clearScreen($rl+2,$rl+3);
$cmd = 'setContrast&contrast=';
addstr($rl+2, 0, "Contrast [0-100] : ");
} elsif ($s eq 'h') {
clearScreen($rl+2,$rl+3);
$cmd = 'setHue&hue=';
addstr($rl+2, 0, "Hue [0-100] : ");
} elsif ($s eq 's') {
clearScreen($rl+2,$rl+3);
$cmd = 'setSaturation&saturation=';
addstr($rl+2, 0, "Saturation [0-100] : ");
} elsif ($s eq 'p') {
clearScreen($rl+2,$rl+3);
$cmd = 'setSharpness&sharpness=';
addstr($rl+2, 0, "Sharpness [0-100] : ");
} elsif ($s eq 'r') {
clearScreen($rl+2,$rl+3);
IPCamCmd($rl+3,'resetImageSetting');
}
unless($s eq 'r') {
my $value = getEntry();
unless ($value =~ /^\d+$/) {
addstr($rl+3, 0, "$s char not recognized as a correct value");
} else {
if ($value >= 0 && $value <= 100) {
$cmd.=$value;
IPCamCmd($rl+3,$cmd);
} else {
addstr($rl+3, 0, "$value char not recognized as a correct value");
}
}
}

} else {
addstr($rl+3, 0, "$s char not recognized as a correct value");
}
}
elsif ($ch eq 'p' ) { IPCamCmd($rl,'getPTZPresetPointList',['cnt','point0','point1','point2','point3','point4','point5','point6','point7','point8','point9','point10','point11','point12','point13','point14','point15']); }
elsif ($ch eq 'P' ) {
clearScreen($rl,$rl+22);
addstr($rl+1, 0, "Select [a]dd or [r]emove : ");
my $s = getch();
if ($s =~ /^([ar])$/) {
if ($1 eq 'a') {
addstr($rl+1, 0, "Set patrol point [enter name] : ");
my $n = getEntry();
IPCamCmd($rl+2,"ptzAddPresetPoint\&name=$n");
} elsif ($1 eq 'r') {
addstr($rl+1, 0, "Delete patrol point [enter name] : ");
my $n = getEntry();
IPCamCmd($rl+2,"ptzDeletePresetPoint\&name=$n");
}
} else {
addstr($rl+1, 0, "$s char not recognized as a correct value");
}
}
elsif ($ch eq 'm' ) {
clearScreen($rl,$rl+22);
my $cmd = 'ptzStartCruise&mapName=';
addstr($rl+1, 0, "Which cruise map would you like to monitor : ");
my $m = getEntry();
$cmd .= $m;
IPCamCmd($rl+2, $cmd);
}
elsif ($ch eq 'M' ) { IPCamCmd($rl,'ptzStopCruise'); }
elsif ($ch eq 'l' ) { IPCamCmd($rl,'ptzGetCruiseMapList',['cnt','map0','map1','map2','map3','map4','map5','map6','map7']); }
elsif ($ch eq 'c' ) {
clearScreen($rl,$rl+22);
addstr($rl+1, 0, "Which cruise map would you like details on : ");
my $n=getEntry();
IPCamCmd($rl,"ptzGetCruiseMapInfo\&name=$n",['point0','point1','point2','point3','point4','point5','point6','point7']);
}
elsif ($ch eq 'C') {
clearScreen($rl,$rl+22);
my $cmd = 'ptzSetCruiseMap';

addstr($rl+1, 0, "Please enter cruise map name : ");
my $n = getEntry();
$cmd .= "\&name=$n";

my $pp='start';
my $i = 0;
while($pp ne 'end' || $i == 7) {
addstr($rl+2+$i, 0, "Please enter next patrol point (use 'end' to stop) : ");
$pp = getEntry();
unless ($pp eq 'end') {
$cmd .= "\&point$i=$pp";
$i++;
}
}
IPCamCmd($rl+3+$i,$cmd);
}
elsif ($ch eq 'g' ) {
clearScreen($rl,$rl+22);
addstr($rl+1, 0, "Which patrol point would you like to go : ");
my $n = getEntry();
IPCamCmd($rl+2,"ptzGotoPresetPoint\&name=$n");
}
elsif ($ch eq 'q' ) { print "\n"; system("stty echo"); last; }

refresh();
}

sub clearScreen {
my ($l1,$l2) = @_;

return if ($l2<$l1);
for (my $i=$l1;$i<$l2;$i++) {
addstr($i,0,' ');
}
}

sub getEntry {
my $c = '';
my $out = 1;
my $e;

echo;
while ($out) {
$e .= $c if ($c ne '');
$c = getch();
$out=0 if ($c eq '
');
}
noecho;
return($e);
}

sub controlScreen {
my $l=0;
addstr($l++, 0, "###############################################################################");
addstr($l++, 0, "# Foscam FI9816P Control Center #");
addstr($l++, 0, "# release v$release - freddy\@linuxtribe.fr #");
addstr($l++, 0, "###############################################################################");
$l++;
addstr($l++, 0, " arrows - move cam");
addstr($l++, 0, " space - stop cam");
$l++;
addstr($l++, 0, " s/S - get/set cam speed");
addstr($l++, 0, " i/I - get/set image settings");
addstr($l++, 0, " x/X - on/off night detection");
addstr($l++, 0, " n/N - on/off night vision");
$l-=7;
addstr($l++, 40, " d/D - get camera state/info");
$l++;
addstr($l++, 40, " p/P - get/modify patrol points");
addstr($l++, 40, " l/c/C - list/get/modify cruise map");
addstr($l++, 40, " g - go to patrol point");
addstr($l++, 40, " m/M - start/stop monitoring");
$l+=2;
addstr($l++, 0, " q - quit");
$l++;
addstr($l++, 0, "###############################################################################");
refresh();
return($l);
}