From a942b22541e5bf3ff95d1b42f3662f0c89cc6042 Mon Sep 17 00:00:00 2001
From: Robin Daermann <robin.daermann@ruhr-uni-bochum.de>
Date: Wed, 23 Sep 2015 16:18:49 +0200
Subject: [PATCH 1/5] Add camera control module for Vivotek ePTZ protocol

---
 .../lib/ZoneMinder/Control/Vivotek_ePTZ.pm    | 227 ++++++++++++++++++
 1 file changed, 227 insertions(+)
 create mode 100644 scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm

diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm
new file mode 100644
index 000000000..b407a9bde
--- /dev/null
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm
@@ -0,0 +1,227 @@
+# ==========================================================================
+#
+# ZoneMinder Vivotek ePTZ Control Protocol Module
+# Copyright (C) 2015 Robin Daermann
+#
+# 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 Vivotek ePTZ camera control
+# protocol
+#
+package ZoneMinder::Control::Vivotek_ePTZ;
+
+use 5.006;
+use strict;
+use warnings;
+
+require ZoneMinder::Base;
+require ZoneMinder::Control;
+
+our @ISA = qw(ZoneMinder::Control);
+
+# ==========================================================================
+#
+# Vivotek ePTZ Control Protocol
+#
+# ==========================================================================
+
+use ZoneMinder::Logger qw(:all);
+use ZoneMinder::Config qw(:all);
+
+use Time::HiRes qw( usleep );
+
+sub new
+{
+    my $class = shift;
+    my $id = shift;
+    my $self = ZoneMinder::Control->new( $id );
+    Debug( "Camera New" );
+    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;
+    Debug( "Camera 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();
+    Debug( "Camera open" );
+    use LWP::UserAgent;
+    $self->{ua} = LWP::UserAgent->new;
+    $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
+
+    $self->{state} = 'open';
+}
+
+sub close
+{
+    my $self = shift;
+    $self->{state} = 'closed';
+}
+
+sub printMsg
+{
+    my $msg = shift;
+    my $msg_len = length($msg);
+
+    Debug( $msg."[".$msg_len."]" );
+}
+
+sub sendCmd
+{
+    my ($self, $cmd, $speedcmd) = @_;
+
+    my $result = undef;
+
+    printMsg( $speedcmd, "Tx" );
+    printMsg( $cmd, "Tx" );
+
+    my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/camctrl/eCamCtrl.cgi?stream=0&$speedcmd&$cmd" );
+    my $res = $self->{ua}->request($req);
+
+    if ( $res->is_success )
+    {
+        $result = !undef;
+    }
+    else
+    {
+        Error( "Error check failed: '".$res->status_line()."'" );
+    }
+
+    return( $result );
+}
+
+sub moveConUp
+{
+    my ($self, $params) = @_;
+    my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
+    Debug( "Move Up" );
+    $self->sendCmd( 'move=up', $speed );
+}
+
+sub moveConDown
+{
+    my ($self, $params) = @_;
+    my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
+    Debug( "Move Down" );
+    $self->sendCmd( 'move=down', $speed );
+}
+
+sub moveConLeft
+{
+    my ($self, $params) = @_;
+    my $speed = 'speedpan=-' . $params->{panspeed};
+    Debug( "Move Left" );
+    $self->sendCmd( 'move=left', $speed );
+}
+
+sub moveConRight
+{
+    my ($self, $params) = @_;
+    my $speed = 'speedpan=' . ($params->{panspeed} - 6);
+    Debug( "Move Right" );
+    $self->sendCmd( 'move=right', $speed );
+}
+
+sub moveStop
+{
+    my $self = shift;
+    Debug( "Move Stop" );
+    # not implemented
+}
+
+sub zoomConTele
+{
+    my ($self, $params) = @_;
+    my $speed = 'speedzoom=' . ($params->{speed} - 6);
+    Debug( "Zoom In" );
+    $self->sendCmd( 'zoom=tele', $speed );
+}
+
+sub zoomConWide
+{
+    my ($self, $params) = @_;
+    my $speed = 'speedzoom=' . ($params->{speed} - 6);
+    Debug( "Zoom Out" );
+    $self->sendCmd( 'zoom=wide', $speed );
+}
+
+sub reset
+{
+    my $self = shift;
+    Debug( "Camera Reset" );
+    $self->sendCmd( 'move=home' );
+}
+
+1;
+__END__
+
+=head1 NAME
+
+ZoneMinder::Control::Vivotek_ePTZ - ZoneMinder Perl extension for Vivotek ePTZ
+camera control protocol
+
+=head1 SYNOPSIS
+
+  use ZoneMinder::Control::Vivotek_ePTZ;
+
+=head1 DESCRIPTION
+
+This module implements the ePTZ protocol used in various Vivotek IP cameras,
+developed with a Vivotek IB8369 model.
+
+Currently, only simple pan, tilt and zoom function is implemented. Presets will
+follow later.
+
+=head2 EXPORT
+
+None.
+
+=head1 SEE ALSO
+
+I would say, see ZoneMinder::Control documentation. But it is a stub.
+
+=head1 AUTHOR
+
+Robin Daermann E<lt>r.daermann@ids-services.deE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2015 by Robin Daermann
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.8.3 or,
+at your option, any later version of Perl 5 you may have available.
+
+=cut

From 330e0d7adf431f6798c1e1fd53f920467705cf6c Mon Sep 17 00:00:00 2001
From: Robin Daermann <robin.daermann@ruhr-uni-bochum.de>
Date: Wed, 4 Nov 2015 17:29:07 +0100
Subject: [PATCH 2/5] Add Vivotek ePTZ control module to database

---
 db/zm_update-1.28.109.sql | 110 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 db/zm_update-1.28.109.sql

diff --git a/db/zm_update-1.28.109.sql b/db/zm_update-1.28.109.sql
new file mode 100644
index 000000000..298668e08
--- /dev/null
+++ b/db/zm_update-1.28.109.sql
@@ -0,0 +1,110 @@
+--
+-- This updates a 1.28.108 database to 1.28.109
+--
+
+--
+-- Add Controls definition for Vivotek ePTZ
+--
+INSERT INTO Controls 
+SELECT * FROM (SELECT NULL as Id,
+                    'Vivotek ePTZ' as Name,
+                    'Remote' as Type,
+                    'Vivotek_ePTZ' as Protocol,
+                    0 as CanWake,
+                    0 as CanSleep,
+                    1 as CanReset,
+                    1 as CanZoom,
+                    0 as CanAutoZoom,
+                    0 as CanZoomAbs,
+                    0 as CanZoomRel,
+                    1 as CanZoomCon,
+                    0 as MinZoomRange,
+                    0 as MaxZoomRange,
+                    0 as MinZoomStep,
+                    0 as MaxZoomStep,
+                    1 as HasZoomSpeed,
+                    0 as MinZoomSpeed,
+                    5 as MaxZoomSpeed,
+                    0 as CanFocus,
+                    0 as CanAutoFocus,
+                    0 as CanFocusAbs,
+                    0 as CanFocusRel,
+                    0 as CanFocusCon,
+                    0 as MinFocusRange,
+                    0 as MaxFocusRange,
+                    0 as MinFocusStep,
+                    0 as MaxFocusStep,
+                    0 as HasFocusSpeed,
+                    0 as MinFocusSpeed,
+                    0 as MaxFocusSpeed,
+                    0 as CanIris,
+                    0 as CanAutoIris,
+                    0 as CanIrisAbs,
+                    0 as CanIrisRel,
+                    0 as CanIrisCon,
+                    0 as MinIrisRange,
+                    0 as MaxIrisRange,
+                    0 as MinIrisStep,
+                    0 as MaxIrisStep,
+                    0 as HasIrisSpeed,
+                    0 as MinIrisSpeed,
+                    0 as MaxIrisSpeed,
+                    0 as CanGain,
+                    0 as CanAutoGain,
+                    0 as CanGainAbs,
+                    0 as CanGainRel,
+                    0 as CanGainCon,
+                    0 as MinGainRange,
+                    0 as MaxGainRange,
+                    0 as MinGainStep,
+                    0 as MaxGainStep,
+                    0 as HasGainSpeed,
+                    0 as MinGainSpeed,
+                    0 as MaxGainSpeed,
+                    0 as CanWhite,
+                    0 as CanAutoWhite,
+                    0 as CanWhiteAbs,
+                    0 as CanWhiteRel,
+                    0 as CanWhiteCon,
+                    0 as MinWhiteRange,
+                    0 as MaxWhiteRange,
+                    0 as MinWhiteStep,
+                    0 as MaxWhiteStep,
+                    0 as HasWhiteSpeed,
+                    0 as MinWhiteSpeed,
+                    0 as MaxWhiteSpeed,
+                    0 as HasPresets,
+                    0 as NumPresets,
+                    0 as HasHomePreset,
+                    0 as CanSetPresets,
+                    1 as CanMove,
+                    0 as CanMoveDiag,
+                    0 as CanMoveMap,
+                    0 as CanMoveAbs,
+                    0 as CanMoveRel,
+                    1 as CanMoveCon,
+                    1 as CanPan,
+                    0 as MinPanRange,
+                    0 as MaxPanRange,
+                    0 as MinPanStep,
+                    0 as MaxPanStep,
+                    1 as HasPanSpeed,
+                    0 as MinPanSpeed,
+                    5 as MaxPanSpeed,
+                    0 as HasTurboPan,
+                    0 as TurboPanSpeed,
+                    1 as CanTilt,
+                    0 as MinTiltRange,
+                    0 as MaxTiltRange,
+                    0 as MinTiltStep,
+                    0 as MaxTiltStep,
+                    1 as HasTiltSpeed,
+                    0 as MinTiltSpeed,
+                    5 as MaxTiltSpeed,
+                    0 as HasTurboTilt,
+                    0 as TurboTiltSpeed,
+                    0 as CanAutoScan,
+                    0 as NumScanPaths) AS tmp
+WHERE NOT EXISTS (
+    SELECT Name FROM Controls WHERE name = 'Vivotek ePTZ'
+) LIMIT 1;

From b611d9fcae619a662a7fb439218c9bb01bf721f9 Mon Sep 17 00:00:00 2001
From: Robin Daermann <robin.daermann@ruhr-uni-bochum.de>
Date: Wed, 4 Nov 2015 17:29:37 +0100
Subject: [PATCH 3/5] Bump database version to 1.28.109

---
 CMakeLists.txt | 2 +-
 configure.ac   | 2 +-
 version        | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ea67474d6..c7be1ceec 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@
 #
 cmake_minimum_required (VERSION 2.6)
 project (zoneminder)
-set(zoneminder_VERSION "1.28.108")
+set(zoneminder_VERSION "1.28.109")
 # make API version a minor of ZM version
 set(zoneminder_API_VERSION "${zoneminder_VERSION}.1")
 
diff --git a/configure.ac b/configure.ac
index 436be34cf..b9d4ef2ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@
 # For instructions on building with cmake, please see INSTALL
 #
 AC_PREREQ(2.59)
-AC_INIT(zm,1.28.108,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html)
+AC_INIT(zm,1.28.109,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html)
 AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR(src/zm.h)
 AC_CONFIG_HEADERS(config.h)
diff --git a/version b/version
index ca2c9c84c..a960d2a6d 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-1.28.108
+1.28.109

From 1f1b48ed5137e33e026647f8e02825a33db29469 Mon Sep 17 00:00:00 2001
From: Robin Daermann <robin.daermann@ruhr-uni-bochum.de>
Date: Wed, 4 Nov 2015 19:17:47 +0100
Subject: [PATCH 4/5] Added Vivotek ePTZ control to zm_create.sql

---
 db/zm_create.sql.in | 1 +
 1 file changed, 1 insertion(+)

diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in
index 53bfa1fa8..22c9af7f6 100644
--- a/db/zm_create.sql.in
+++ b/db/zm_create.sql.in
@@ -588,6 +588,7 @@ INSERT INTO Controls VALUES (NULL,'ONVIF Camera','Ffmpeg','onvif',0,0,1,1,0,0,0,
 INSERT INTO `Controls` VALUES (NULL,'Foscam 9831W','Ffmpeg','FI9831W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,0,0,0,0,0,0,0);
 INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
 INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0);
 
 
 

From 49a89b75a851ddbbace2766b938e13c008a7052f Mon Sep 17 00:00:00 2001
From: Robin Daermann <robin.daermann@ruhr-uni-bochum.de>
Date: Mon, 9 Nov 2015 16:15:53 +0100
Subject: [PATCH 5/5] More verbose error reporting

---
 scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm
index b407a9bde..3649b35bc 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm
@@ -107,7 +107,7 @@ sub sendCmd
     printMsg( $speedcmd, "Tx" );
     printMsg( $cmd, "Tx" );
 
-    my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/camctrl/eCamCtrl.cgi?stream=0&$speedcmd&$cmd" );
+    my $req = HTTP::Request->new( GET => "http://" . $self->{Monitor}->{ControlAddress} . "/cgi-bin/camctrl/eCamCtrl.cgi?stream=0&$speedcmd&$cmd" );
     my $res = $self->{ua}->request($req);
 
     if ( $res->is_success )
@@ -116,7 +116,7 @@ sub sendCmd
     }
     else
     {
-        Error( "Error check failed: '".$res->status_line()."'" );
+        Error( "Request failed: '" . $res->status_line() . "' (URI: '" . $req->as_string() . "')" );
     }
 
     return( $result );