ie20 legeek onvif setup

Discussions related to the 1.36.x series of ZoneMinder
Post Reply
frankz66
Posts: 46
Joined: Tue Dec 21, 2021 2:17 pm

ie20 legeek onvif setup

Post by frankz66 »

Hello everyone, I would like to configure on zoneminder the possibility of having the control of the camera via onvif or other.

The model is ie20 Legeek . Can anyone help me?
clipo
Posts: 101
Joined: Sat Sep 06, 2008 9:55 am
Location: Manchester, UK
Contact:

Re: ie20 legeek onvif setup

Post by clipo »

I've had partial success getting the iegeek i20 camera working on ZM

It should work under the ONVIF but the logs give Error 400 bad command

But if you add the camera as a Reolink RLC-423
with the control address as

user:password@ipaddress:8080

as per the below snip.

using an ONVIF client on an Android phone you can have full control of the camera so the camera is capable of ONVIF so there is an issue with the ZM ONVIF config.
Screenshot at 2022-06-05 10-52-12.png
Screenshot at 2022-06-05 10-52-12.png (81.29 KiB) Viewed 3009 times
frankz66
Posts: 46
Joined: Tue Dec 21, 2021 2:17 pm

Re: ie20 legeek onvif setup

Post by frankz66 »

[quote=clipo post_id=126695 time=1654423048 user_id=13492]
I've had partial success getting the iegeek i20 camera working on ZM

It should work under the ONVIF but the logs give Error 400 bad command

But if you add the camera as a Reolink RLC-423
with the control address as

user:password@ipaddress:8080

as per the below snip.

using an ONVIF client on an Android phone you can have full control of the camera so the camera is capable of ONVIF so there is an issue with the ZM ONVIF config.

Screenshot at 2022-06-05 10-52-12.png
[/quote]
Thank you so much! At the moment I've seen that the turn signals work, but not the preset keys or anything else. You gave me a lot of help, I had lost hope for us! If you have anything else to try, I would be grateful!
clipo
Posts: 101
Joined: Sat Sep 06, 2008 9:55 am
Location: Manchester, UK
Contact:

Re: ie20 legeek onvif setup

Post by clipo »

Ive managed to fix the continuous movement issue with the Reolink PTZ config.

Place the below code into the /usr/share/perl5/ZoneMinder/Control folder or where ever your distro keeps the config files and name the file as ieGeek.pm

Then you need to make your own profile inside ZM and reference this ieGeek file, if you just look at the other configs you will start to see how it is formatted..

I'll add this to Github when I get time.
Screenshot at 2022-06-09 21-57-13.png
Screenshot at 2022-06-09 21-57-13.png (56.16 KiB) Viewed 2954 times
Screenshot at 2022-06-09 21-55-19.png
Screenshot at 2022-06-09 21-55-19.png (201.04 KiB) Viewed 2954 times
Screenshot at 2022-06-09 21-55-44.png
Screenshot at 2022-06-09 21-55-44.png (26.96 KiB) Viewed 2954 times

Code: Select all

# ==========================================================================
#
# Basic ZoneMinder ieGeek IE20 ONVIF IP PTZ Camera Control Module 9-June-2022
# By Clipo (ZoneMinder Forums)
#
# This file is based up on the Reolink IP Control Module 2016-01-19
# Copyright (C) 2016 Chris Swertfeger
#
# 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 first implementation of the ieGeek ONVIF IP camera control
# protocol
#
package ZoneMinder::Control::ieGeek;

use 5.006;
use strict;
use warnings;

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

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

our %CamParams = ();

# ==========================================================================
#
# ieGeek ONVIF IP Control Protocol
# The ieGeek camera support ONVIF in a limited way with 
# this script sends ONVIF style commands and may work with other cameras
# that require authentication
#
# The script was developed against an IE20 Camera.
#
# Basic preset functions are supported only.
# #
# On ControlAddress use the format :
#   USERNAME:PASSWORD@ADDRESS:PORT
#   eg : admin:pass@10.1.2.1:8080
#        
# ieGeek cameras use port 8080 by default
#
# The Auto Stop Timeout field.
# Recommend starting with a value of 1 second, and adjust accordingly.
#
# ==========================================================================

use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);

use Time::HiRes qw( usleep );

use MIME::Base64;
use Digest::SHA;
use DateTime;

my ($username,$password,$host,$port);

sub open
{
    my $self = shift;

    $self->loadMonitor();
    #
    # Extract the username/password host/port from ControlAddress
    #
    if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ ) 
    { # user:pass@host...
      $username = $1;
      $password = $2;
      $host = $3;
    }
    elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ )  
    { # user@host...
      $username = $1;
      $host = $2;
    }
    else { # Just a host
      $host = $self->{Monitor}{ControlAddress};
    }
    # Check if it is a host and port or just a host
    if( $host =~ /([^:]+):(.+)/ ) 
    {
      $host = $1;
      $port = $2;
    }
    else 
    {
      $port = 80;
    }

    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 $msg = shift;
    my $content_type = shift;
    my $result = undef;

    printMsg( $cmd, "Tx" );

    my $server_endpoint = "http://".$host.":".$port."/$cmd";
    my $req = HTTP::Request->new( POST => $server_endpoint );
    $req->header('content-type' => $content_type);
    $req->header('Host' => $host.':'.$port);
    $req->header('content-length' => length($msg));
    $req->header('accept-encoding' => 'gzip, deflate');
    $req->header('connection' => 'close');
    $req->content($msg);

    my $res = $self->{ua}->request($req);

    if ( $res->is_success ) {
        $result = !undef;
    } else {
        Error("After sending PTZ command to $server_endpoint, camera returned the following error:'".$res->status_line()."'" );
    }
    return $result;
}

sub getCamParams
{
    my $self = shift;
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl">
	<VideoSourceToken>000</VideoSourceToken>
	</GetImagingSettings>
	</s:Body>
	</s:Envelope>';
    my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
    my $req = HTTP::Request->new( POST => $server_endpoint );
    $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
    $req->header('Host' => $host.":".$port);
    $req->header('content-length' => length($msg));
    $req->header('accept-encoding' => 'gzip, deflate');
    $req->header('connection' => 'Close');
    $req->content($msg);

    my $res = $self->{ua}->request($req);

    if ( $res->is_success ) {
        # We should really use an xml or soap library to parse the xml tags
        my $content = $res->decoded_content;

        if ($content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/) {
            $CamParams{$1} = $2;
        }
        if ($content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/) {
            $CamParams{$1} = $2;
        }
    } 
    else
    {
        Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" );
    }
}

#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
    my $self = shift;
    my $autostop = shift;

    if( $autostop ) {
        Debug( "Auto Stop" );
        my $cmd = 'onvif/PTZ';
        my $nonce;
        for (0..20){$nonce .= chr(int(rand(254)));}
        my $mydate = DateTime->now()->iso8601().'Z';
        my $sha = Digest::SHA->new(1);
        $sha->add($nonce.$mydate.$password);
        my $digest = encode_base64($sha->digest,"");
        my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
		<s:Header>
		<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
		<UsernameToken>
		<Username>'.$username.'</Username>
		<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
		<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
		<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
		</UsernameToken>
		</Security>
		</s:Header>
		<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
		<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
		<ProfileToken>000</ProfileToken>
		<Velocity>
		<PanTilt x="0" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
		</Velocity>
		</ContinuousMove>
		</s:Body>
		</s:Envelope>';
    	my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
        usleep( $autostop );
        $self->sendCmd( $cmd, $msg, $content_type );
    }
}

# Reset the Camera
sub reset
{
    Debug( "Camera Reset" );
    my $self = shift;
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $cmd = "";
    my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
    $self->sendCmd( $cmd, $msg, $content_type );
}

#Up Arrow
sub moveConUp
{
    Debug( "Move Up" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    		<s:Header>
		<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
		<UsernameToken>
		<Username>'.$username.'</Username>
		<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
		<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
		<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
		</UsernameToken>
		</Security>
		</s:Header>
		<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
		<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
		<ProfileToken>000</ProfileToken>
		<Velocity>
		<PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/>
		</Velocity>
		</ContinuousMove>
		</s:Body>
		</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
    #AutoStop Commnented out to give fine control
    #$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Down Arrow
sub moveConDown
{
    Debug( "Move Down" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
	<ProfileToken>000</ProfileToken
	><Velocity>
	<PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/>
	</Velocity>
	</ContinuousMove>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
    #AutoStop Commnented out to give fine control
    #$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Left Arrow
sub moveConLeft
{
    Debug( "Move Left" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
	<ProfileToken>000</ProfileToken>
	<Velocity>
	<PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
	</Velocity>
	</ContinuousMove>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
    #AutoStop Commnented out to give fine control
    #$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Right Arrow
sub moveConRight
{
    Debug( "Move Right" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
     my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
     	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
	<ProfileToken>000</ProfileToken>
	<Velocity>
	<PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
	</Velocity>
	</ContinuousMove>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
    #AutoStop Commnented out to give fine control
    #$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Stop
sub moveStop
{
    Debug( "Move Stop" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header>
    <Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <UsernameToken>
    <Username>'.$username.'</Username>
    <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
    <Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
    <Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
    </UsernameToken>
    </Security>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
    <ProfileToken>000</ProfileToken>
    <Velocity>
    <PanTilt x="0" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
    </Velocity>
    </ContinuousMove>
    </s:Body>
    </s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
}

1;
frankz66
Posts: 46
Joined: Tue Dec 21, 2021 2:17 pm

Re: ie20 legeek onvif setup

Post by frankz66 »

[quote=clipo post_id=126801 time=1654808824 user_id=13492]
Ive managed to fix the continuous movement issue with the Reolink PTZ config.

Place the below code into the /usr/share/perl5/ZoneMinder/Control folder or where ever your distro keeps the config files and name the file as ieGeek.pm

Then you need to make your own profile inside ZM and reference this ieGeek file, if you just look at the other configs you will start to see how it is formatted..

I'll add this to Github when I get time.

Screenshot at 2022-06-09 21-57-13.png

Screenshot at 2022-06-09 21-55-19.png

Screenshot at 2022-06-09 21-55-44.png

[code]# ==========================================================================
#
# Basic ZoneMinder ieGeek IE20 ONVIF IP PTZ Camera Control Module 9-June-2022
# By Clipo (ZoneMinder Forums)
#
# This file is based up on the Reolink IP Control Module 2016-01-19
# Copyright (C) 2016 Chris Swertfeger
#
# 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 first implementation of the ieGeek ONVIF IP camera control
# protocol
#
package ZoneMinder::Control::ieGeek;

use 5.006;
use strict;
use warnings;

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

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

our %CamParams = ();

# ==========================================================================
#
# ieGeek ONVIF IP Control Protocol
# The ieGeek camera support ONVIF in a limited way with
# this script sends ONVIF style commands and may work with other cameras
# that require authentication
#
# The script was developed against an IE20 Camera.
#
# Basic preset functions are supported only.
# #
# On ControlAddress use the format :
# USERNAME:PASSWORD@ADDRESS:PORT
# eg : admin:pass@10.1.2.1:8080
#
# ieGeek cameras use port 8080 by default
#
# The Auto Stop Timeout field.
# Recommend starting with a value of 1 second, and adjust accordingly.
#
# ==========================================================================

use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);

use Time::HiRes qw( usleep );

use MIME::Base64;
use Digest::SHA;
use DateTime;

my ($username,$password,$host,$port);

sub open
{
my $self = shift;

$self->loadMonitor();
#
# Extract the username/password host/port from ControlAddress
#
if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ )
{ # user:pass@host...
$username = $1;
$password = $2;
$host = $3;
}
elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ )
{ # user@host...
$username = $1;
$host = $2;
}
else { # Just a host
$host = $self->{Monitor}{ControlAddress};
}
# Check if it is a host and port or just a host
if( $host =~ /([^:]+):(.+)/ )
{
$host = $1;
$port = $2;
}
else
{
$port = 80;
}

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 $msg = shift;
my $content_type = shift;
my $result = undef;

printMsg( $cmd, "Tx" );

my $server_endpoint = "http://".$host.":".$port."/$cmd";
my $req = HTTP::Request->new( POST => $server_endpoint );
$req->header('content-type' => $content_type);
$req->header('Host' => $host.':'.$port);
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'close');
$req->content($msg);

my $res = $self->{ua}->request($req);

if ( $res->is_success ) {
$result = !undef;
} else {
Error("After sending PTZ command to $server_endpoint, camera returned the following error:'".$res->status_line()."'" );
}
return $result;
}

sub getCamParams
{
my $self = shift;
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/ ... xt-1.0.xsd">
<UsernameToken>
<Username>'.$username.'</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/ ... </Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/ ... ).'</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/ ... '</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl">
<VideoSourceToken>000</VideoSourceToken>
</GetImagingSettings>
</s:Body>
</s:Envelope>';
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
my $req = HTTP::Request->new( POST => $server_endpoint );
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl ... ngSettings"');
$req->header('Host' => $host.":".$port);
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close');
$req->content($msg);

my $res = $self->{ua}->request($req);

if ( $res->is_success ) {
# We should really use an xml or soap library to parse the xml tags
my $content = $res->decoded_content;

if ($content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/) {
$CamParams{$1} = $2;
}
if ($content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/) {
$CamParams{$1} = $2;
}
}
else
{
Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" );
}
}

#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
my $self = shift;
my $autostop = shift;

if( $autostop ) {
Debug( "Auto Stop" );
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/ ... xt-1.0.xsd">
<UsernameToken>
<Username>'.$username.'</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/ ... </Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/ ... ).'</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/ ... '</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>000</ProfileToken>
<Velocity>
<PanTilt x="0" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
</Velocity>
</ContinuousMove>
</s:Body>
</s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
usleep( $autostop );
$self->sendCmd( $cmd, $msg, $content_type );
}
}

# Reset the Camera
sub reset
{
Debug( "Camera Reset" );
my $self = shift;
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $cmd = "";
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/ ... xt-1.0.xsd">
<UsernameToken>
<Username>'.$username.'</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/ ... </Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/ ... ).'</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/ ... '</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/>
</s:Body>
</s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
$self->sendCmd( $cmd, $msg, $content_type );
}

#Up Arrow
sub moveConUp
{
Debug( "Move Up" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/ ... xt-1.0.xsd">
<UsernameToken>
<Username>'.$username.'</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/ ... </Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/ ... ).'</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/ ... '</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>000</ProfileToken>
<Velocity>
<PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/>
</Velocity>
</ContinuousMove>
</s:Body>
</s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
#AutoStop Commnented out to give fine control
#$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Down Arrow
sub moveConDown
{
Debug( "Move Down" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/ ... xt-1.0.xsd">
<UsernameToken>
<Username>'.$username.'</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/ ... </Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/ ... ).'</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/ ... '</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>000</ProfileToken
><Velocity>
<PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/>
</Velocity>
</ContinuousMove>
</s:Body>
</s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
#AutoStop Commnented out to give fine control
#$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Left Arrow
sub moveConLeft
{
Debug( "Move Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/ ... xt-1.0.xsd">
<UsernameToken>
<Username>'.$username.'</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/ ... </Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/ ... ).'</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/ ... '</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>000</ProfileToken>
<Velocity>
<PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
</Velocity>
</ContinuousMove>
</s:Body>
</s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
#AutoStop Commnented out to give fine control
#$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Right Arrow
sub moveConRight
{
Debug( "Move Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/ ... xt-1.0.xsd">
<UsernameToken>
<Username>'.$username.'</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/ ... </Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/ ... ).'</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/ ... '</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>000</ProfileToken>
<Velocity>
<PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
</Velocity>
</ContinuousMove>
</s:Body>
</s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
#AutoStop Commnented out to give fine control
#$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Stop
sub moveStop
{
Debug( "Move Stop" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/ ... xt-1.0.xsd">
<UsernameToken>
<Username>'.$username.'</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/ ... </Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/ ... ).'</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/ ... '</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>000</ProfileToken>
<Velocity>
<PanTilt x="0" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
</Velocity>
</ContinuousMove>
</s:Body>
</s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
}

1;
[/code]
[/quote]

Thank you so much for your contribution, when I will apply the changes first.
frankz66
Posts: 46
Joined: Tue Dec 21, 2021 2:17 pm

Re: ie20 legeek onvif setup

Post by frankz66 »

I tried to create and enter the code as per your instructions, but I don't find myself "ieGeek.pm" in the choice menu.
clipo
Posts: 101
Joined: Sat Sep 06, 2008 9:55 am
Location: Manchester, UK
Contact:

Re: ie20 legeek onvif setup

Post by clipo »

The ieGeek wont appear by its self the above scrip is the background control "protocol" file.

Once you have the control "protocol" file you now need to manually make the "Control Type" in the database by pressing the "Edit" button as shown in the first screen shot in my previous post.

You will then be give the menu in the second screen shot in my previous post, you can then either edit one of the existing profiles or make a new one by pressing the "+" button and then enter the "Name" of your choice but enter the "Protocol" name as ieGeek as per the file name of the "protocol" file you made at the start and choose the check boxes required for the control types similar to those shown in this post.

This will make an entry in the database and give this as an option for you to select under the Control tab.
Screenshot at 2022-06-09 21-56-09.png
Screenshot at 2022-06-09 21-56-09.png (40.25 KiB) Viewed 2906 times
Screenshot at 2022-06-09 21-55-58.png
Screenshot at 2022-06-09 21-55-58.png (25.99 KiB) Viewed 2906 times
garyc
Posts: 32
Joined: Fri Feb 26, 2010 5:24 pm

Re: ie20 legeek onvif setup

Post by garyc »

Thank you so much for this post.

I received my IE50 in the post today and started playing to get it working in ZM. Took a while but was then stumped concerning the control functions (fisrt camera with such functionality after all these years with an old Y-Cam that has just had its wifi fail after 15+ years on the garage wall...).

Took me a while to understand the instructions and to realise, I think, that this is still embyonic and provides only pan and tilt functions but hey, it works a treat on the IE50.

The Android App for teh camera has presets, gain control and a host of other stuff and the camera has a digital zoom (IE20 too and IE30 an optical one). Do you think that at some point a comprehensive profile and protocol will be incorporated into ZM, or at least able to be created DIY as in this thread?

Thanks again.
clipo
Posts: 101
Joined: Sat Sep 06, 2008 9:55 am
Location: Manchester, UK
Contact:

Re: ie20 legeek onvif setup

Post by clipo »

garyc wrote: Fri Jul 01, 2022 3:41 pm Do you think that at some point a comprehensive profile and protocol will be incorporated into ZM, or at least able to be created DIY as in this thread?
I posted the below code to the Zoneminder Git but after reading other peoples submits on this topic I removed the code as I dont think the ZM admins will incorporate it into the main builds as they are looking for a 100% working ONVIF

The below code is my final version for the ieGeek ie20 camera, I tried to get presets working and some of the other features but the ZM code and or the built in ONVIF functions are not 100% compliant so it would always fail.

Code: Select all

# ==========================================================================
#
# Basic ZoneMinder ieGeek IE20 ONVIF IP PTZ Camera Control Module 9-June-2022
# By Clipo (ZoneMinder Forums)
#
# This file is based up on the Reolink IP Control Module 2016-01-19
# Copyright (C) 2016 Chris Swertfeger
#
# 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 first implementation of the ieGeek ONVIF IP camera control
# protocol
#
package ZoneMinder::Control::ieGeek;

use 5.006;
use strict;
use warnings;

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

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

our %CamParams = ();

# ==========================================================================
#
# ieGeek ONVIF IP Control Protocol
# The ieGeek camera support ONVIF in a limited way with 
# this script sends ONVIF style commands and may work with other cameras
# that require authentication
#
# The script was developed against an IE20 Camera.
#
# Basic preset functions are supported only.
# #
# On ControlAddress use the format :
#   USERNAME:PASSWORD@ADDRESS:PORT
#   eg : admin:pass@10.1.2.1:8080
#        
# ieGeek cameras use port 8080 by default
#
# The Auto Stop Timeout field.
# Recommend starting with a value of 1 second, and adjust accordingly.
#
# ==========================================================================

use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);

use Time::HiRes qw( usleep );

use MIME::Base64;
use Digest::SHA;
use DateTime;

my ($username,$password,$host,$port);

sub open
{
    my $self = shift;

    $self->loadMonitor();
    #
    # Extract the username/password host/port from ControlAddress
    #
    if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ ) 
    { # user:pass@host...
      $username = $1;
      $password = $2;
      $host = $3;
    }
    elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ )  
    { # user@host...
      $username = $1;
      $host = $2;
    }
    else { # Just a host
      $host = $self->{Monitor}{ControlAddress};
    }
    # Check if it is a host and port or just a host
    if( $host =~ /([^:]+):(.+)/ ) 
    {
      $host = $1;
      $port = $2;
    }
    else 
    {
      $port = 80;
    }

    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 $msg = shift;
    my $content_type = shift;
    my $result = undef;

    printMsg( $cmd, "Tx" );

    my $server_endpoint = "http://".$host.":".$port."/$cmd";
    my $req = HTTP::Request->new( POST => $server_endpoint );
    $req->header('content-type' => $content_type);
    $req->header('Host' => $host.':'.$port);
    $req->header('content-length' => length($msg));
    $req->header('accept-encoding' => 'gzip, deflate');
    $req->header('connection' => 'close');
    $req->content($msg);

    my $res = $self->{ua}->request($req);

    if ( $res->is_success ) {
        $result = !undef;
    } else {
        Error("After sending PTZ command to $server_endpoint, camera returned the following error:'".$res->status_line()."'" );
    }
    return $result;
}

sub getCamParams
{
    my $self = shift;
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl">
	<VideoSourceToken>000</VideoSourceToken>
	</GetImagingSettings>
	</s:Body>
	</s:Envelope>';
    my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
    my $req = HTTP::Request->new( POST => $server_endpoint );
    $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
    $req->header('Host' => $host.":".$port);
    $req->header('content-length' => length($msg));
    $req->header('accept-encoding' => 'gzip, deflate');
    $req->header('connection' => 'Close');
    $req->content($msg);

    my $res = $self->{ua}->request($req);

    if ( $res->is_success ) {
        # We should really use an xml or soap library to parse the xml tags
        my $content = $res->decoded_content;

        if ($content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/) {
            $CamParams{$1} = $2;
        }
        if ($content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/) {
            $CamParams{$1} = $2;
        }
    } 
    else
    {
        Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" );
    }
}

#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
    my $self = shift;
    my $autostop = shift;

    if( $autostop ) {
        Debug( "Auto Stop" );
        my $cmd = 'onvif/PTZ';
        my $nonce;
        for (0..20){$nonce .= chr(int(rand(254)));}
        my $mydate = DateTime->now()->iso8601().'Z';
        my $sha = Digest::SHA->new(1);
        $sha->add($nonce.$mydate.$password);
        my $digest = encode_base64($sha->digest,"");
        my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
		<s:Header>
		<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
		<UsernameToken>
		<Username>'.$username.'</Username>
		<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
		<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
		<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
		</UsernameToken>
		</Security>
		</s:Header>
		<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
		<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
		<ProfileToken>000</ProfileToken>
		<Velocity>
		<PanTilt x="0" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
		</Velocity>
		</ContinuousMove>
		</s:Body>
		</s:Envelope>';
    	my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
        usleep( $autostop );
        $self->sendCmd( $cmd, $msg, $content_type );
    }
}

# Reboot the Camera
sub reboot
{
    Debug( "Camera Reboot" );
    my $self = shift;
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $cmd = "";
    my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
    $self->sendCmd( $cmd, $msg, $content_type );
}

#Up Arrow
sub moveConUp
{
    Debug( "Move Up" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    		<s:Header>
		<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
		<UsernameToken>
		<Username>'.$username.'</Username>
		<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
		<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
		<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
		</UsernameToken>
		</Security>
		</s:Header>
		<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
		<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
		<ProfileToken>000</ProfileToken>
		<Velocity>
		<PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/>
		</Velocity>
		</ContinuousMove>
		</s:Body>
		</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
    #AutoStop Commnented out to give fine control
    #$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Down Arrow
sub moveConDown
{
    Debug( "Move Down" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
	<ProfileToken>000</ProfileToken
	><Velocity>
	<PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/>
	</Velocity>
	</ContinuousMove>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
    #AutoStop Commnented out to give fine control
    #$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Left Arrow
sub moveConLeft
{
    Debug( "Move Left" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
	<ProfileToken>000</ProfileToken>
	<Velocity>
	<PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
	</Velocity>
	</ContinuousMove>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
    #AutoStop Commnented out to give fine control
    #$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Right Arrow
sub moveConRight
{
    Debug( "Move Right" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
     my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
     	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
	<ProfileToken>000</ProfileToken>
	<Velocity>
	<PanTilt x="0.5" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
	</Velocity>
	</ContinuousMove>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
    #AutoStop Commnented out to give fine control
    #$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}

#Stop
#The ieGeek implimentaion of ONVIF seems to not support the Stop command so a zero motion contious move command is sent.
sub moveStop
{
    Debug( "Move Stop" );
    my $self = shift;
    my $cmd = 'onvif/PTZ';
    my $nonce;
    for (0..20){$nonce .= chr(int(rand(254)));}
    my $mydate = DateTime->now()->iso8601().'Z';
    my $sha = Digest::SHA->new(1);
    $sha->add($nonce.$mydate.$password);
    my $digest = encode_base64($sha->digest,"");
    my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    	<s:Header>
	<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<UsernameToken>
	<Username>'.$username.'</Username>
	<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password>
	<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce>
	<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created>
	</UsernameToken>
	</Security>
	</s:Header>
	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
	<ProfileToken>000</ProfileToken>
	<Velocity>
	<PanTilt x="0" y="0" xmlns="http://www.onvif.org/ver10/schema"/>
	</Velocity>
	</ContinuousMove>
	</s:Body>
	</s:Envelope>';
    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
    $self->sendCmd( $cmd, $msg, $content_type );
}

1;
garyc
Posts: 32
Joined: Fri Feb 26, 2010 5:24 pm

Re: ie20 legeek onvif setup

Post by garyc »

Thanks. At least I am up and running with basic functionality...
Post Reply