Good afternoon.
I've restructured these scripts and incorporated speed control, full autostop and a jog facility, plus the settings required if you ceiling mount the camera.
I've only tried it on my DB Power and Tenvis cameras which are Foscam clones, but as I started with the Foscam scripts I'm fairly certain that it will work on the Foscam.
Save this script in your normal directory for .pm files as FoscamClone.pm and set the permissions.
Goto the Control tab of your monitor setup and click the edit link next to the control type. On the window that pops up click 'Add New Control Type' - the 'Control Capability - New' Window pops up.
Main Tab
Name: Foscam Clone
Type: Remote
Protocol: FoscamClone
Check these boxes:-
Can Wake (Optional - see below)
Can Sleep (Optional - see below)
Can Reset
Move Tab
Check these boxes:-
Can Move
Can Move Diagonally
Can Move Continuous
Pan Tab
Check these boxes:-
Can Pan
Has pan Speed
Min Pan Speed: 10
Max Pan Speed: 0
Tilt Tab
Check these boxes:-
Can Tilt
Has Tilt Speed
Min Tilt Speed: 10
Max Tilt Speed: 0
Zoom Tab
Check these boxes:-
Can Zoom
Can Zoom Continuous
Has Zoom Speed (Optional - see below)
Min Zoom Speed: 10
Max Zoom Speed: 0
Iris Tab
Check these boxes:-
Can Iris
Can Iris Continuous
Has Iris Speed (Optional - see below)
Min Iris Speed: 10
Max Iris Speed: 0
Note: These cameras have neither Zoom or Iris control I'm using them as jog position controls
Presets Tab
Check these boxes:-
Has Presets
Has Home Preset
Can Set Presets
Num Presets: 16
Click the Save Button, close the Control Capabilities box then close and reopen the Monitor edit box.
Control Tab
Check box Controllable
Control Type: Foscam Clone
Control Device: user=admin&pwd= (default)
Control Address: The ip address of your cam
Auto Stop Timeout: 0.80
Note this is obviously a matter of choice, but on my system it does not like being changed. Most times I end up rebooting to make it take effect.
Misc Tab
Default View: Control (Optional)
Once it is setup, you can control the position of the monitor in the normal way, but the further from the centre that you click the arrow, the faster the camera will move and therefore the further it will move before timing out.
There is also a jog facility, if you click the horizontal or vertical arrows very close to the centre button then the camera will be jogged one step regardless of the autostop setting. It requires a steady finger on your mouse but gives good macro control and not time is wasted sending the speed and stop controls to the camera. This does not work for diagonal movement
The downside of this is that you lose some lower ranges of speed control. If you don't like it, search the script for 'jog movement' to disable or change.
Another way to jog the camera is to use the Zoom and Iris controls - I'm not sure whether this is a good idea as it is neither pretty or intuitive but it can easily be disabled by unchecking the Can Zoom and Can Iris boxes. Click on:-
Tele Zoom - to Jog Left
Wide Zoom - to Jog Right
Open Iris - to Jog Up
Close Iris - to Jog Down
If you have checked the Has Zoom Speed and Has Iris Speed boxes then the closer to the arrow tip that you click the faster the speed. This has no effect on my cameras, but it would appear from other posts that some cameras will jog faster/further with a higher speed. If it has no effect then uncheck those boxes, as sending the speed command can take significant time.
The wake and sleep controls seem to have no effect on my clone cameras, but some posters have said that they turn the IR LED's on/off so they are there if you want them.
My script uses the command value 30 to set the preset 1 position. Some scripts appear to use 32 instead which alters the position of all presets by one. Search the script for 'Code for Preset 1' to change.
The stop button still sends the stop command, but it should only be used to stop a call preset initiated move. If you use it to stop normal movement, the command will still be sent but not until after the autostop has timed out and sent the stop signal.
I've noticed some problems with excessive time for the camera to respond. This is not a fault of the script so much as my computer/network/cameras delaying the time to send and reply to commands. However, if there is poor network response then this sript makes it worse because it first sends the speed command, then the movement command and finally a stop command except for the jog mode which just sends one.
I would like to thank all those who have posted the scripts for these cameras which I was able to use as the basis of this script.
If you still want to try it, here is the code. I hope it is useful.
Code: Select all
# ==========================================================================
#
# ZoneMinder Foscam FI8908W IP Control Protocol Module, $Date: 2009-11-25 09:20:00 +0000 (Wed, 04 Nov 2009) $, $Revision: 0001 $
# Copyright (C) 2001-2008 Philip Coombes
# Modified for use with Foscam FI8908W IP Camera by Dave Harris
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This module contains the implementation of the Foscam FI8908W IP camera control
# protocol
#
# uncomment following line and comment the next one for camera mounted in normal position and see NORMAL SETTINGS below
package ZoneMinder::Control::FoscamClone;
#uncomment following line and comment the previous one for ceiling mounted camera
# package ZoneMinder::Control::FoscamCloneInv;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our $VERSION = $ZoneMinder::Base::VERSION;
# ==========================================================================
#
# Foscam FI8908W IP Control Protocol
#
# ==========================================================================
# uncomment following line and comment the next one if using Zoneminder prior to v1.25.0
#use ZoneMinder::Debug qw(:all);
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep gettimeofday tv_interval );
use feature "switch";
{
# maximum and minimum speed setings 0 & 10 should be OK but they MUST match those in the ZM Control Capability panel
my $maxspd = 0 ;
my $minspd = 10 ;
# The threshold for jog movement. If you select a low speed then instead of using the low speed, the camera will be
#moved onestep in the required direction and the autostop is not needed.
# Set to 0 to dissable this feature.
# Set to 1 or greater to enable.
# A higher setting makes it easier to select this feature but reduces the availible speed range.
my $jogthreshold = 3 ;
# Code for Preset 1. My camera uses the command value 30 to set the preset 1 position. Some scripts appear to
# use 32 instead which alters the position of all presets by one. Set this to 32 if required
my $prest1 = 30 ;
my %cont_str = (
# Command codes for movement
# NORMAL SETTINGS or INVERTED SETTINGS
# Comment out following entire section and uncomment all in the NVERTED SETTINGS
# if the camera is to be ceiling mounted
# It is best to also change the line
# package ZoneMinder::Control::FoscamClone;
# to
# package ZoneMinder::Control::FoscamCloneinverted;
# and save this file as FoscamCloneinverted.pm , then you can set a control for both
# configurations at once.
# START of NORMAL SETTINGS
"moveConUp" => "0" ,
"moveConDown" => "2" ,
"moveConLeft" => "4" ,
"moveConRight" => "6" ,
"moveConUpRight" => "91" ,
"moveConDownRight" => "93" ,
"moveConUpLeft" => "90" ,
"moveConDownLeft" => "92" ,
"moveRelUp" => "0&onestep=1" ,
"moveRelDown" => "2&onestep=1" ,
"moveRelLeft" => "4&onestep=1" ,
"moveRelRight" => "6&onestep=1" ,
"irisConOpen" => "0&onestep=1" ,
"irisConClose" => "2&onestep=1" ,
"zoomConTele" => "4&onestep=1" ,
"zoomConWide" => "6&onestep=1" ,
"moveRelUpRight" => "91" ,
"moveRelDownRight" => "93" ,
"moveRelUpLeft" => "90" ,
"moveRelDownLeft" => "92" ,
# END of NORMAL SETTINGS
# START of INVERTED SETTINGS
#
# "moveConUp" => "2" ,
# "moveConDown" => "0" ,
# "moveConLeft" => "6" ,
# "moveConRight" => "4" ,
#
# "moveConUpRight" => "92" ,
# "moveConDownRight" => "90" ,
# "moveConUpLeft" => "93" ,
# "moveConDownLeft" => "91" ,
#
# "moveRelUp" => "2&onestep=1" ,
# "moveRelDown" => "0&onestep=1" ,
# "moveRelLeft" => "6&onestep=1" ,
# "moveRelRight" => "4&onestep=1" ,
#
# "irisConOpen" => "2&onestep=1" ,
# "irisConClose" => "0&onestep=1" ,
# "zoomConTele" => "6&onestep=1" ,
# "zoomConWide" => "4&onestep=1" ,
#
# "moveRelUpRight" => "92" ,
# "moveRelDownRight" => "90" ,
# "moveRelUpLeft" => "93" ,
# "moveRelDownLeft" => "91" ,
# END of INVERTED SETTINGS
"moveStop" => "1" ,
"presetHome" => "25" ,
# Not sure about the next 2 values. Some people say that they turn the IR LED's off/on.
# It doesn't seem to do anything on my Foscam Clone and my documentation says they set Io output high/low whatever that is.
"sleep" => "94" ,
"wake" => "95" ,
) ;
my $enableAutostop = "0" ;
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open
{
my $self = shift;
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION );
$self->{state} = 'open';
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
Debug ( "Start of sub sendCmd") ;
my $self = shift;
my $cmd = shift;
my $result = undef;
my $stofsendcmd = [gettimeofday] ;
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd".$self->{Monitor}->{ControlDevice} );
my $res = $self->{ua}->request($req);
my ( $secstosend , $microsecstosend ) = tv_interval( $stofsendcmd ) ;
Debug ( "Command $cmd has been sent. Time to send $secstosend sec." ) ;
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );
}
return( $result );
}
sub reset
{
my $self = shift;
my $params = shift;
Debug( "Camera Reset" );
my $cmd = "reboot.cgi?";
$self->sendCmd( $cmd );
}
sub setSpeed
{
my $self = shift;
my $params = shift;
my $ctl = $self->getParam( $params, 'command' ) ;
my $speed = "-1" ;
my $newmov = $ctl ;
#set pan/tilt speed
Debug( "Start setSpeed sub" ) ;
given ( $ctl )
{
when ( "moveConRight" )
{
$speed = $self->getParam( $params, "panspeed" , -1 );
if ( $speed > ( $minspd - $jogthreshold ) )
{
$newmov = "moveRelRight" ;
$speed = "-1" ;
$enableAutostop = "0" ;
}
}
when ( "moveRelRight" )
{
$speed = $self->getParam( $params, "panspeed" , -1 );
}
when ( "moveConLeft" )
{
my $ps = $self->getParam( $params, "panspeed" , -1 );
if ( $ps >= "0" )
{
# ZM V1.25.0 reverses the fast/slow speed range for this arrow - this works round it
# if it is corrected then this line should be changed to $speed = $ps ;
$speed = $maxspd - $ps + $minspd ;
}
if ( $speed > ( $minspd - $jogthreshold ) )
{
$newmov = "moveRelLeft" ;
$speed = "-1" ;
$enableAutostop = "0" ;
}
}
when ( "moveRelLeft" )
{
my $ps = $self->getParam( $params, "panspeed" , -1 );
if ( $ps >= "0" )
{
# ZM V1.25.0 reverses the fast/slow speed range for this arrow - this works round it
# if it is corrected then this line should be changed to $speed = $ps ;
$speed = $maxspd - $ps + $minspd ;
}
}
when ( [ "moveRelUp" , "moveRelDown" ] )
{
$speed = $self->getParam( $params, "tiltspeed" , -1 );
}
when ( "moveConUp" )
{
$speed = $self->getParam( $params, "tiltspeed" , -1 );
if ( $speed > ( $minspd - $jogthreshold ) )
{
$newmov = "moveRelUp" ;
$speed = "-1" ;
$enableAutostop = "0" ;
}
}
when ( "moveConDown" )
{
$speed = $self->getParam( $params, "tiltspeed" , -1 );
if ( $speed > ( $minspd - $jogthreshold ) )
{
$newmov = "moveRelDown" ;
$speed = "-1" ;
$enableAutostop = "0" ;
}
}
when ( [ "moveConUpRight" ,"moveConDownRight" , "moveConUpLeft" , "moveConDownLeft" , "moveRelUpRight" ,
"moveRelDownRight" , "moveRelUpLeft" , "moveRelDownLeft" ] )
{
my $ts = $self->getParam( $params, "tiltspeed" , -1 );
my $ps = $self->getParam( $params, "tiltspeed" , -1 );
if ( $ps >= "0" )
{
$speed = int(( $ps + $ts ) / 2 ) ;
}
}
when ( [ "zoomConTele" , "zoomConWide" , "irisConOpen" , "irisConClose" ] )
{
$speed = $self->getParam( $params, "speed" , -1 );
}
}
if ( $speed >= "0" )
{
my $cmd = "set_misc.cgi?ptz_patrol_rate=$speed&";
Debug( "Ready to Send set speed command - $cmd to sub sendCmd" ) ;
$self->sendCmd( $cmd );
}
else
{
Debug( "No speed to set") ;
}
return $newmov ;
}
sub proAutostop
{
my $self = shift;
Debug( "Start Autostop" ) ;
my $asTimeOut = $self->{Monitor}->{AutoStopTimeout} ;
Debug( " Autostop- $asTimeOut" ) ;
if( $asTimeOut > 0 )
{
Debug( "Autostop start timing - $asTimeOut" ) ;
usleep( $asTimeOut );
Debug( "Autostop timed out" ) ;
my $c_str = $cont_str{"moveStop"} ;
$self->sendCmd( "decoder_control.cgi?command=$c_str&" ) ;
}
else
{
Debug( "Autostop Skipped") ;
}
Debug( "End of Autostop" ) ;
}
sub getCmd
{
my $self = shift;
my $params = shift;
my $mov = $self->setSpeed ( $params ) ;
# Debug( "Process Move Command = $mov" ) ;
given ( $mov )
{
when ( "moveConRight" )
{
#Right Arrow Continuous
$enableAutostop = "1" ;
Debug( "Move Right - comand = $mov" );
}
when ( "moveConLeft" )
{
#Left Arrow Continuous
$enableAutostop = "1" ;
Debug( "Move Left - comand = $mov" );
}
when ( "moveConUp" )
{
#Up Arrow Continuous
$enableAutostop = "1" ;
Debug( "Move Up - comand = $mov" );
}
when ( "moveConDown" )
{
#Down Arrow Continuous
$enableAutostop = "1" ;
Debug( "Move Down - comand = $mov" );
}
when ( "moveConUpRight" )
{
#Diagonally Up Right Arrow Continuous
$enableAutostop = "1" ;
Debug( "Move Diagonally Up Right - comand = $mov" );
}
when ( "moveConDownRight" )
{
#Diagonally Down Right Arrow Continuous
$enableAutostop = "1" ;
Debug( "Move Diagonally Down Right - comand = $mov" );
}
when ( "moveConUpLeft" )
{
#Diagonally Up Left Arrow Continuous
$enableAutostop = "1" ;
Debug( "Move Diagonally Up Left - comand = $mov" );
}
when ( "moveConDownLeft" )
{
#Diagonally Down Left Arrow Continuous
$enableAutostop = "1" ;
Debug( "Move Diagonally Down Left - comand = $mov" );
}
when ( "moveRelRight" )
{
#Right Arrow Relative
Debug( "Move Right - comand = $mov" );
}
when ( "moveRelLeft" )
{
#Left Arrow Relative
Debug( "Move Left - comand = $mov" );
}
when ( "moveRelUp" )
{
#Up Arrow Relative
Debug( "Move Up - comand = $mov" );
}
when ( "moveRelDown" )
{
#Down Arrow Relative
Debug( "Move Down - comand = $mov" );
}
when ( "moveRelUpRight" )
{
#Diagonally Up Right Arrow Relative
$enableAutostop = "1" ;
Debug( "Move Diagonally Up Right - comand = $mov" );
}
when ( "moveRelDownRight" )
{
#Diagonally Down Right Arrow Relative
$enableAutostop = "1" ;
Debug( "Move Diagonally Down Right - comand = $mov" );
}
when ( "moveRelUpLeft" )
{
#Diagonally Up Left Arrow Relative
$enableAutostop = "1" ;
Debug( "Move Diagonally Up Left - comand = $mov" );
}
when ( "moveRelDownLeft" )
{
#Diagonally Down Left Arrow Relative
$enableAutostop = "1" ;
Debug( "Move Diagonally Down Left - comand = $mov" );
}
when ( "moveStop" )
{
#Stop
$enableAutostop = "0" ;
Debug( "Move Stop - comand = $mov" );
}
when ( "presetHome" )
{
#Move Camera to Home Position
Debug( "Home Preset - comand = $mov" );
}
when ( "sleep" )
{
#Turn IR off
Debug( " - comand = $mov" );
}
when ( "wake" )
{
#Turn IR on
Debug( "Wake - comand = $mov" );
}
when ( "irisConOpen" )
{
#Onestep Up
Debug( "Onestep Up - comand = $mov" );
}
when ( "irisConClose" )
{
#Onestep Down
Debug( "Onestep Down - comand = $mov" );
}
when ( "zoomConTele" )
{
#Onestep Left
Debug( "Onestep Left - comand = $mov" );
}
when ( "zoomConWide" )
{
#Onestep Right
Debug( "Onestep Right - comand = $mov" );
}
}
#Send Movement command
my $c_str = $cont_str{$mov} ;
my $cmd = "decoder_control.cgi?command=$c_str&";
Debug( "Send Mov cmd $cmd" ) ;
$self->sendCmd( $cmd );
if ( $enableAutostop )
{
$self->proAutostop ;
}
}
#Up Arrow Relative
sub moveRelUp
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Up Arrow Continuous
sub moveConUp
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Down Arrow Relative
sub moveRelDown
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Down Arrow Continuous
sub moveConDown
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Left Arrow Relative
sub moveRelLeft
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Left Arrow Continuous
sub moveConLeft
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Right Arrow Relative
sub moveRelRight
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Right Arrow Continuous
sub moveConRight
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Diagonally Up Right Arrow Relative
sub moveRelUpRight
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Diagonally Up Right Arrow Continuous
sub moveConUpRight
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Diagonally Down Right Arrow Relative
sub moveRelDownRight
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Diagonally Down Right Arrow Continuous
sub moveConDownRight
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Diagonally Up Left Arrow Relative
sub moveRelUpLeft
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Diagonally Up Left Arrow Continuous
sub moveConUpLeft
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Diagonally Down Left Arrow Relative
sub moveRelDownLeft
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Diagonally Down Left Arrow Continuous
sub moveConDownLeft
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Stop
sub moveStop
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Move Camera to Home Position
sub presetHome
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Turn IR off
sub sleep
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Turn IR on
sub wake
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Onestep Left
sub zoomConTele
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Onestep Right
sub zoomConWide
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Onestep Up
sub irisConOpen
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
#Onestep Down
sub irisConClose
{
my $self = shift;
my $params = shift;
$self->getCmd ($params ) ;
}
# Set the preset to the current camera position
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
# the first preset is command value 30/31. Some scripts are using 32/33 as preset 1
#ammend value of $prest1 at start to change
my $presetCmd = ( $preset * 2) + $prest1 -2 ;
Debug( "Set Preset $preset" );
my $cmd = "decoder_control.cgi?command=$presetCmd&";
$self->sendCmd( $cmd );
}
#Move Camera to Preset Position
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
# the first preset is command value 30/31. Some scripts are using 32/33 as preset 1
#ammend value of $prest1 at start to change
my $presetCmd = ( $preset * 2) + $prest1 -1 ;
Debug( "Goto Preset No $preset" );
my $cmd = "decoder_control.cgi?command=$presetCmd&";
$self->sendCmd( $cmd );
}
}
1;