20150222 010026

Zoneminder V1.25.0 running on Debian wheezy

This is a cheap 640x480 pixels Chinese IP camera with pan/tilt capabilities. It can connect to the wired or wireless network. 

The camera management is done in a web browser, in MS Windows, by means of a specific plugin.

I could not find any detail neither about a possible equivalent camera, nor about the software it is driven by.

So, after messing arround for a while, trying all kind of solutions in Zoneminder,  the tools that helped solve the puzzle were iSpy and Wireshark. A scanner for camera parameters in Zoneminder, like the one of iSpy, would truly be highly helpful.

The video capture string came quite quickly as:

http://<IP>:<port>/videostream.cgi?User=<username>&password=<password>

The pan/tilt capabilities were a different story. Nothing that could be found on the internet did work. After digging into the PC-camera communication, it came that the camera movements were handled by xml scripts (long live Wireshark).

On the Linux machine the camera can be controlled by commands like:

curl --user <username>:<password> http://<IP>:<port>/moveptz.xml?dir=down

for moving down.

The other movements would require changing dir=down into dir=up, dir=left, dir=right and, finally, dir=stop to stop the movement.

Therefore, those commands would have to be implemented into a control script that would work for Zoneminder. After a summary read of a Perl tutorial and a lot of debugging, the beast was defeated. So, here is the control script, the debug statements, including the printMsg() subroutine, have been removed:

# ==========================================================================
#
# ZoneMinder ChinaNetipcam 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 ChinaNetipcam camera by Radmilo Felix on 2015-02-23 03:00:00
#
# 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 ChinaNetipcam camera control
# protocol.
#
# ==========================================================================
package ZoneMinder::Control::ChinaNetipcam;

use 5.006;
use strict;
use warnings;

require ZoneMinder::Base;
require ZoneMinder::Control;

our @ISA = qw(ZoneMinder::Control);

our $VERSION = $ZoneMinder::Base::VERSION;

# ==========================================================================
#
# ChinaNetipcam Control Protocol
#
# ==========================================================================

use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );

my $loopfactor=30000;

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 sendCmd
{
    my $self = shift;
    my $cmd = shift;
    my $result = undef;
    # username and password will be parsed from "ControlDevice" field.
    # Configure "ControlDevice" to hold "user:password"
    my @buffer=split(':', $self->{Monitor}->{ControlDevice});
    my $username=$buffer[0];
    my $password=$buffer[1];
    # Host data will be taken from ControlAddress field.
    # Configure "ControlAddress" to hold "IP:port"
    my $myhost=$self->{Monitor}->{ControlAddress};
    my $webstring="http://".$myhost."/$cmd";
    my $ua = LWP::UserAgent->new;
    my $req = HTTP::Headers->new;
    $req = HTTP::Request->new(GET => $webstring );
    $req->authorization_basic($username, $password);
    my $response = $ua->request($req);
    # This is the response->content:
    #     <?xml version="1.0" encoding="UTF-8" ?><Result>
    #     <Success>0</Success>
    #     <ErrorCode>eHttpError_No_Auth</ErrorCode>
    #     <ErrorDesc></ErrorDesc>

    my @responsebuffer=split('>',$response->content);
    my $respsuccess=$responsebuffer[3];
    my @buffer=split('<',$respsuccess);
    my $opresult=$buffer[0];
   if ( $opresult==1 )
    {
            $result = "Something to be put here, apparently the return value is never used.";
    }
    else
    {
            Error( "Error check failed: Unsuccessful authentication." );
            $result = "Error result string, apparently the return value is never used.";
    }
    return( $result );
}

sub reset
{
    my $self = shift;
    Debug( "Camera Reset" );
    my $cmd = "reboot.xml";
    $self->sendCmd( $cmd );
}

sub moveConUp
{
    my $self = shift;
    my $params = shift;
    my $cmd = "moveptz.xml?dir=up";
    $self->sendCmd( $cmd );
    my $autostop = $self->getParam( $params, 'autostop', 0 );
    if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
    {
        usleep( $self->{Monitor}->{AutoStopTimeout} );
        $self->moveStop( $params );
    }
}

sub moveConDown
{
    my $self = shift;
    my $params = shift;
    my $cmd = "moveptz.xml?dir=down";
    $self->sendCmd( $cmd );
    my $autostop = $self->getParam( $params, 'autostop', 0 );
    if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
    {
        usleep( $self->{Monitor}->{AutoStopTimeout} );
        $self->moveStop( $params );
    }
}

sub moveConRight
{
    my $self = shift;
    my $params = shift;
    Debug( "Move Right" );
    my $cmd = "moveptz.xml?dir=right";
    $self->sendCmd( $cmd );
    my $autostop = $self->getParam( $params, 'autostop', 0 );
    if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
    {
        usleep( $self->{Monitor}->{AutoStopTimeout} );
        $self->moveStop( $params );
    }
}

sub moveConLeft
{
    my $self = shift;
    my $params = shift;
    my $cmd = "moveptz.xml?dir=left";
    $self->sendCmd( $cmd );
    my $autostop = $self->getParam( $params, 'autostop', 0 );
    if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
    {
        usleep( $self->{Monitor}->{AutoStopTimeout} );
        $self->moveStop( $params );
    }
}


# The camera can not move diagonally, so we will implement those movements here:
sub moveConUpLeft
{
    my $self = shift;
    my $params = shift;
    my $loopcount=int($self->{Monitor}->{AutoStopTimeout}/$loopfactor);
    my $autostop = $self->getParam( $params, 'autostop', 0 );
    if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
    {
        while ( $loopcount  )
        {
             $loopcount--;
             my $cmd = "moveptz.xml?dir=left";
             $self->sendCmd( $cmd );
             my $cmd = "moveptz.xml?dir=up";
             $self->sendCmd( $cmd );
         }
        $self->moveStop( $params );
    }
}

sub moveConUpRight
{
   my $self = shift;
   my $params = shift;
   my $loopcount=int($self->{Monitor}->{AutoStopTimeout}/$loopfactor);
   my $autostop = $self->getParam( $params, 'autostop', 0 );
   if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
   {
         while ( $loopcount  )
         {
             $loopcount--;
             my $cmd = "moveptz.xml?dir=right";
             $self->sendCmd( $cmd );
             my $cmd = "moveptz.xml?dir=up";
             $self->sendCmd( $cmd );
         }
            $self->moveStop( $params );
  }
}

sub moveConDownRight
{
    my $self = shift;
    my $params = shift;
    my $loopcount=int($self->{Monitor}->{AutoStopTimeout}/$loopfactor);
    my $autostop = $self->getParam( $params, 'autostop', 0 );
    if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
    {
        while ( $loopcount  )
        {
            $loopcount--;
            my $cmd = "moveptz.xml?dir=right";
            $self->sendCmd( $cmd );
            my $cmd = "moveptz.xml?dir=down";
            $self->sendCmd( $cmd );
        }
        $self->moveStop( $params );
    }
}

sub moveConDownLeft
{
    my $self = shift;
    my $params = shift;
    my $loopcount=int($self->{Monitor}->{AutoStopTimeout}/$loopfactor);
    my $autostop = $self->getParam( $params, 'autostop', 0 );
    if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
    {
        while ( $loopcount  )
        {
            $loopcount--;
            my $cmd = "moveptz.xml?dir=left";
            $self->sendCmd( $cmd );
            my $cmd = "moveptz.xml?dir=down";
            $self->sendCmd( $cmd );
        }
        $self->moveStop( $params );
    }
}

sub moveStop
{
    my $self = shift;
    print("autostop\n");
    my $cmd = "moveptz.xml?dir=stop";
    $self->sendCmd( $cmd );
}

1;

Monitor configuration

Create a new script file in the control script directory. Mind that the filename has to match the name in the script "package" statement.

#touch /usr/share/perl5/ZoneMinder/Control/ChinaNetipcam.pm

Copy the script above and paste it into the file.

In Zoneminder, make the following settings:

Make sure that OPT_CONTROL is enabled in Options/System.

Edit the camera monitor:

    in the "General" tab, set Source Type to Remote.

    in the "Source" tab, set:
            Remote protocol to HTTP,
            Remote method to Simple,
            Remote Host Name to IP or Name,
            Remote Host Port to port,
            Remote Host Path to /videostream.cgi?User=username&password=password,
            the other parameters are obvious.

    in the "Control" tab, at Control type, click on the "Edit" link to create the new control.
        Click "Add New Control" button, in the new window set the following:
                "Main" tab:
                           Name-ChinaNetipcam,
                           Type-Remote,
                           Protocol-ChinaNetipcam,
                           Can Reset-tick.
                "Move" tab:
                           Can Move-tick,
                           Can Moce Diagonally-tick,
                           Can Move Continuous-tick.
                "Pan" tab: Can Pan-tick.
                 "Tilt" tab: Can Tilt-tick.
    Hit "Save" button to save the settings, the new control should now be in the control capabilities list, close the ZM-Control Capabilities window.
      Back, in the "Control" tab:
           Controllable-tick,
           Select ChinaNetipcam as control type (you will have to close the monitor and open it again for the new control to be shown in the drop-down list),
           Control Device-username:password,
           Control Address-IP:port,
           AutoStopTimeout-0.20.

Save and that's all.

 

 

Add comment


Security code
Refresh