It seems to work, as far as pan and tilt is concerned. I can't seem to get the zoom or focus stuff working yet, but I'm still working on it. The camera uses an HI3510 chipset, but Alptop doesn't provide any documentation regarding how it's used. This script is based on a lot of testing and playing around. It does have provisions for all of the main functions provided in the Zoneminder control interface, but I can only swear to the pan/tilt mechanisms at this time.
The camera provides video at "rtsp://ipaddr:554/11" (high resolution) and "rtsp://ipaddr:554/12" (low resolution).
However, in case someone needs to get up and running, I'm attaching my script.
Code: Select all
# ==========================================================================
#
# ZoneMinder Alptop AT-200DW Protocol Module
# Copyright (c) 2023, Richard P. Stevens, II ("rps2" in the comments)
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ==========================================================================
#
# This module contains the implementation of the Alptop AT-200DW device
# control protocol
#
package ZoneMinder::Control::AlptopAT200DW;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our %CamParams = ();
# ==========================================================================
#
# Alptop AT-200DW Control Protocol
#
# In the "Control Address" field of the Control section for the camera,
# use the format:
#
# USERNAME:PASSWORD@ADDRESS:PORT
#
# For example:
#
# admin:admin@192.168.1.1:80
#
# As shipped, the administrator account for the camera is:
#
# Username: "admin" (no quotes, all lower case)
# Password: "admin" (no quotes, all lower case)
#
# Also, note that, by default, the video is available at
#
# rtsp://192.168.1.41:554/11 (main, high-res video feed)
# rtsp://192.168.1.41:554/12 (secondary low-res video feed)
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
#-----------------------------------------------------------------------------
# NOTE: These cameras have speeds associated with their pan and tilt
# movements. These values can be between 0 and 63. We're going to choose a
# medium # value of 32 (half speed). I've found that the cameras slew REALLY
# fast, so adjust this "$camMoveSpeed" value if you want to speed it up or
# slow it down (0 = slow, 63 = fast)
#-----------------------------------------------------------------------------
our $camMoveSpeed = 32;
sub open
{
my $self = shift;
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
my $result = undef;
printMsg( $cmd, "Tx" );
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
# Info( "http://".$self->{Monitor}->{ControlAddress}."/$cmd".$self->{Monitor}->{ControlDevice} );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );
}
return( $result );
}
sub getCamParams
{
my $self = shift;
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=getimageattr";
my $req = $self->sendCmd( $cmd );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
# Parse results setting values in %FCParams
my $content = $res->decoded_content;
while ($content =~ s/var\s+([^=]+)=([^;]+);//ms) {
$CamParams{$1} = $2;
}
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );
}
}
# Auto Stop
# This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
my $self = shift;
my $delay = shift;
# Wait "$delay" microseconds and stop the camera movement...
Debug( "Auto Stop" );
usleep( $delay );
my $cmd = "/cgi-bin/hi3510/ptzctrl.cgi?-act=stop";
$self->sendCmd( $cmd );
}
# Reset the Camera
sub reset
{
my $self = shift;
Debug( "Camera Reset" );
# Move to default position
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-act=home";
$self->sendCmd( $cmd );
# Reset all other values to default
# ### NEEDS WORK! (RPS2) ####
# I'd suggest using a "reboot camera" rather than a full-on reset.
# It'd be dangerous to do a reset, so this code is commented out.
# $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-image_type=1&-default=on";
#$self->sendCmd( $cmd );
}
# Reboot Camera (on Sleep button)
sub sleep
{
my $self = shift;
Debug( "Camera Reboot" );
my $cmd = "cgi-bin/hi3510/sysreboot.cgi";
$self->sendCmd( $cmd );
}
# Stop the Camera
sub moveStop
{
my $self = shift;
Debug( "Camera Stop" );
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-act=stop";
$self->sendCmd( $cmd );
}
# Up Arrow
sub moveConUp
{
my $self = shift;
Debug( "Move Up" );
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=up&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
# Down Arrow
sub moveConDown
{
my $self = shift;
Debug( "Move Down" );
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=down&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
# Left Arrow
sub moveConLeft
{
my $self = shift;
Debug( "Move Left" );
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=left&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
# Right Arrow
sub moveConRight
{
my $self = shift;
Debug( "Move Right" );
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=right&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#-----------------------------------------------------------------------------
# Zooming. The API for the hi3510 chipset says it can zoom, but I can't
# seem to make it work (nor does the web interface for the camera) so it
# MAY not work. I'll leave this code in place, just the same.
#-----------------------------------------------------------------------------
# Zoom In (NOTE: We use the self-stop feature of the camera to stop zoom)
sub zoomConTele
{
my $self = shift;
Debug( "Zoom Tele" );
# Not sure if it's "zoom in" or "zoomin". We'll try "zoomin"
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=1&-act=zoomin&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
}
# Zoom Out (NOTE: We use the self-stop feature of the camera to stop zoom)
sub zoomConWide
{
my $self = shift;
Debug( "Zoom Wide" );
# Not sure if it's "zoom out" or "zoomout". We'll try "zoomout"
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=1&-act=zoomout&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
}
#-----------------------------------------------------------------------------
# These cameras don't not have built-in diagonal commands, so we emulate them
#-----------------------------------------------------------------------------
# Diagonally Up Right Arrow
sub moveConUpRight
{
my $self = shift;
Debug( "Move Diagonally Up Right" );
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=up&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=right&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
# Diagonally Down Right Arrow
sub moveConDownRight
{
my $self = shift;
Debug( "Move Diagonally Down Right" );
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=down&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=right&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
# Diagonally Up Left Arrow
sub moveConUpLeft
{
my $self = shift;
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=up&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=left&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
# Diagonally Down Left Arrow
sub moveConDownLeft
{
my $self = shift;
my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=down&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$cmd = "cgi-bin/hi3510/ptzctrl.cgi?-step=0&-act=left&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#-----------------------------------------------------------------------------
# NOTE: I'm not sure how many presets these cameras support. I chose 8.
# Seems reasonable.
#-----------------------------------------------------------------------------
# Set Camera Preset
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Set Preset $preset" );
if (( $preset >= 1 ) && ( $preset <= 8 )) {
my $cmd = "cgi-bin/hi3510/preset.cgi?-act=set&-status=1&-number=".$preset;
$self->sendCmd( $cmd );
}
}
# Recall Camera Preset
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Goto Preset $preset" );
if (( $preset >= 1 ) && ( $preset <= 8 )) {
my $cmd = "cgi-bin/hi3510/preset.cgi?-act=goto&-number=".$preset;
$self->sendCmd( $cmd );
}
if ( $preset == 9 ) {
$self->horizontalPatrol();
}
if ( $preset == 10 ) {
$self->verticalPatrol();
}
}
#-----------------------------------------------------------------------------
# Do horizontal or vertical scanning.
#-----------------------------------------------------------------------------
# Horizontal Patrol
sub horizontalPatrol
{
my $self = shift;
Debug( "Horizontal Patrol" );
my $cmd = "cgi-bin/hi3510/cmd=ptzctrl.cgi?-step=0&-act=hscan&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
}
# Vertical Patrol
sub verticalPatrol
{
my $self = shift;
Debug( "Vertical Patrol" );
my $cmd = "cgi-bin/hi3510/cmd=ptzctrl.cgi?-step=0&-act=vscan&-speed=".$camMoveSpeed;
$self->sendCmd( $cmd );
}
#-----------------------------------------------------------------------------
# NOTE: These cameras use brightness values of 0-255.
#-----------------------------------------------------------------------------
# Increase Brightness
sub irisAbsOpen
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'brightness'} += $step;
$CamParams{'brightness'} = 255 if ($CamParams{'brightness'} > 255);
Debug( "Increase Brightness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-brightness=".$CamParams{'brightness'};
$self->sendCmd( $cmd );
}
# Decrease Brightness
sub irisAbsClose
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'brightness'} -= $step;
$CamParams{'brightness'} = 0 if ($CamParams{'brightness'} < 0);
Debug( "Decrease Brightness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-brightness=".$CamParams{'brightness'};
$self->sendCmd( $cmd );
}
#-----------------------------------------------------------------------------
# NOTE: These cameras use contrast values of 0-255.
#-----------------------------------------------------------------------------
# Increase Contrast
sub whiteAbsIn
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'contrast'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'contrast'} += $step;
$CamParams{'contrast'} = 255 if ($CamParams{'contrast'} > 255);
Debug( "Increase Contrast" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-contrast=".$CamParams{'contrast'};
$self->sendCmd( $cmd );
}
# Decrease Contrast
sub whiteAbsOut
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'contrast'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'contrast'} -= $step;
$CamParams{'contrast'} = 0 if ($CamParams{'contrast'} < 0);
Debug( "Decrease Contrast" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-contrast=".$CamParams{'contrast'};
$self->sendCmd( $cmd );
}
#-----------------------------------------------------------------------------
# NOTE: These cameras use saturation values of 0-255.
#-----------------------------------------------------------------------------
# Increase Saturation
sub satIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'saturation'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'saturation'} += $step;
$CamParams{'saturation'} = 255 if ($CamParams{'saturation'} > 255);
Debug( "Increase Saturation" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-saturation=".$CamParams{'saturation'};
$self->sendCmd( $cmd );
}
# Decrease Saturation
sub satDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'saturation'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'saturation'} -= $step;
$CamParams{'saturation'} = 0 if ($CamParams{'saturation'} < 0);
Debug( "Decrease Saturation" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-saturation=".$CamParams{'saturation'};
$self->sendCmd( $cmd );
}
#-----------------------------------------------------------------------------
# NOTE: These cameras use sharpness values of 0-255.
#-----------------------------------------------------------------------------
# Increase Sharpness
sub sharpIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'sharpness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'sharpness'} += $step;
$CamParams{'sharpness'} = 255 if ($CamParams{'sharpness'} > 255);
Debug( "Increase Sharpness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-sharpness=".$CamParams{'sharpness'};
$self->sendCmd( $cmd );
}
# Decrease Sharpness
sub sharpDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'sharpness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'sharpness'} -= $step;
$CamParams{'sharpness'} = 0 if ($CamParams{'sharpness'} < 0);
Debug( "Decrease Sharpness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-sharpness=".$CamParams{'sharpness'};
$self->sendCmd( $cmd );
}
#-----------------------------------------------------------------------------
# NOTE: These cameras use hue values of 0-127.
#-----------------------------------------------------------------------------
# Increase Hue
sub hueIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'hue'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'hue'} += $step;
$CamParams{'hue'} = 127 if ($CamParams{'hue'} > 127);
Debug( "Increase Hue" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-hue=".$CamParams{'hue'};
$self->sendCmd( $cmd );
}
# Decrease Hue
sub hueDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'hue'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'hue'} -= $step;
$CamParams{'hue'} = 0 if ($CamParams{'hue'} < 0);
Debug( "Decrease Hue" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-hue=".$CamParams{'hue'};
$self->sendCmd( $cmd );
}
1;