1# ==========================================================================
2#
3# ZoneMinder ONVIF Control Protocol Module
4# Based on the Netcat onvif script by Andrew Bauer (knnniggett@users.sourceforge.net)
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License
8# as published by the Free Software Foundation; either version 2
9# of the License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# ==========================================================================
21#
22# This module contains the implementation of onvif protocol
23#
24package ZoneMinder::Control::onvif;
25
26use 5.006;
27use strict;
28use warnings;
29use MIME::Base64;
30use Digest::SHA;
31use DateTime;
32use URI;
33use Data::Dumper;
34
35require ZoneMinder::Base;
36require ZoneMinder::Control;
37
38our @ISA = qw(ZoneMinder::Control);
39
40our %CamParams = ();
41
42our ($profileToken, $address, $port, %identity);
43my ( $controlUri, $scheme );
44
45# ==========================================================================
46#
47# This script sends ONVIF compliant commands and may work with other cameras
48#
49# Configuration options (Source->Control tab)
50# - Control Type: ONVIF
51# - Control Device: prof0 - this is dependant on camera. It maybe required to sniff the traffic using Wireshark to find this out. If left empty value of "000" will be used.
52# - Control Address: <scheme>://[<user>:<password>@]<ip_or_host_of_onvif_enabled_camera>[:port][control_uri]
53# - Auto Stop Timeout: 1.00 - how long shold the camera move for when move command is issued. Value of 1.00 means 1s.
54# - Track Motion: NOT IMPLEMENTED - this suppose to be a feature for automatic camera scrolling (moving).
55# - Track Delay: NOT IMPLEMENTED
56# - Return Location: Home|Preset 1 - NOT IMPLEMENTED
57#
58# Absolute minimum required supported "Control Address" would be:
59#   - 192.168.1.199
60# This will use the following defaults:
61#   - port: 80
62#   - Control Device: 000
63#   - Control URI: /onvif/PTZ
64#   - No authentication
65#   - No Auto Stop Timeout (on movement command the camera will keep moving until it reaches it's edge)
66#
67# Example Control Address values:
68#   - http://user:password@192.168.1.199:888/onvif/device_control :Connect to camera at IP: 192.168.1.199 on port 888 with "username" and "password" credentials using /onvif/device_control URI
69#   - user:password@192.168.1.199                                 :Connect to camera at IP: 192.168.1.199 on default port 80 with "username" and "password" credentials using default /onvif/PTZ URI
70#   - 192.168.1.199                                               :Connect to camera at IP: 192.168.1.199 without any authentication and use the default /onvif/PTZ URI over HTTP.
71#
72# ==========================================================================
73
74use ZoneMinder::Logger qw(:all);
75use ZoneMinder::Config qw(:all);
76
77use Time::HiRes qw( usleep );
78
79sub open {
80  my $self = shift;
81
82  $self->loadMonitor();
83
84  $profileToken = $self->{Monitor}->{ControlDevice};
85  if ($profileToken eq '') { $profileToken = '000'; }
86
87  parseControlAddress($self->{Monitor}->{ControlAddress});
88
89  use LWP::UserAgent;
90  $self->{ua} = LWP::UserAgent->new;
91  $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
92
93  $self->{state} = 'open';
94}
95
96sub parseControlAddress {
97
98    my $controlAddress = shift;
99
100    #make sure url start with a scheme
101    if ( $controlAddress !~ m'^https?://') {
102        $scheme = "http";
103        $controlAddress = $scheme."://".$controlAddress;
104    }
105
106    my $url = URI->new($controlAddress);
107
108    #set the scheme
109    $scheme = $url->scheme;
110
111    #If we have authinfo
112    if ($url->userinfo){
113        my ($username , $password) = split /:/, $url->userinfo;
114        %identity = (username => $username, password => $password);
115    }
116
117    #If we have no explicitly defined port
118    if (!$url->port){
119        $port = $url->default_port;
120    } else {
121        $port = $url->port;
122    }
123
124    if (!$url->path){
125        $controlUri = "/onvif/PTZ";
126    } else {
127        $controlUri = $url->path;
128    }
129
130    $address = $url->host;
131}
132
133sub digestBase64 {
134  my ($nonce, $date, $password) = @_;
135  my $shaGenerator = Digest::SHA->new(1);
136  $shaGenerator->add($nonce . $date . $password);
137  return encode_base64($shaGenerator->digest, '');
138}
139
140sub authentificationHeader {
141  my ($username, $password) = @_;
142  my @set = ('0' ..'9', 'A' .. 'Z', 'a' .. 'z');
143  my $nonce = join '' => map $set[rand @set], 1 .. 20;
144
145  my $nonceBase64 = encode_base64($nonce, '');
146  my $currentDate = DateTime->now()->iso8601().'Z';
147
148  return '
149<s:Header>
150  <Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
151    <UsernameToken xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
152      <Username>' . $username . '</Username>
153      <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">' . digestBase64($nonce, $currentDate, $password) . '</Password>
154      <Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">' . $nonceBase64 . '</Nonce>
155      <Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">' . $currentDate . '</Created>
156    </UsernameToken>
157  </Security>
158</s:Header>';
159}
160
161sub sendCmd {
162  my $self = shift;
163  my $cmd = shift;
164  my $msg_body = shift;
165  my $content_type = shift;
166  my $result = undef;
167
168  my $msg = '
169    <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">'.
170        ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') .
171    $msg_body . '
172    </s:Envelope>';
173
174  $self->printMsg($cmd, 'Tx');
175
176  my $server_endpoint = $scheme.'://'.$address.':'.$port.$controlUri;
177  my $req = HTTP::Request->new(POST => $server_endpoint);
178  $req->header('content-type' => $content_type);
179  $req->header('Host' => $address . ':' . $port);
180  $req->header('content-length' => length($msg));
181  $req->header('accept-encoding' => 'gzip, deflate');
182  $req->header('connection' => 'Close');
183  $req->content($msg);
184
185
186  my $res = $self->{ua}->request($req);
187
188  if ( $res->is_success ) {
189    $result = !undef;
190  } else {
191    Error("After sending PTZ command, camera returned the following error:'".$res->status_line()."'\nMSG:$msg\nResponse:".$res->content);
192  }
193  return $result;
194}
195
196sub getCamParams {
197  my $self = shift;
198  my $msg = '
199	<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
200		<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
201			<GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl">
202				<VideoSourceToken>000</VideoSourceToken>
203			</GetImagingSettings>
204		</s:Body>
205	</s:Envelope>';
206
207  my $server_endpoint = $scheme.'://'.$address.':'.$port.'/onvif/imaging';
208
209  my $req = HTTP::Request->new(POST => $server_endpoint);
210  $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
211  $req->header('Host' => $address . ':' . $port);
212  $req->header('content-length' => length($msg));
213  $req->header('accept-encoding' => 'gzip, deflate');
214  $req->header('connection' => 'Close');
215  $req->content($msg);
216
217  my $res = $self->{ua}->request($req);
218
219  if ( $res->is_success ) {
220    # We should really use an xml or soap library to parse the xml tags
221    my $content = $res->decoded_content;
222
223    if ( $content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/ ) {
224      $CamParams{$1} = $2;
225    }
226    if ( $content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/ ) {
227      $CamParams{$1} = $2;
228    }
229  } else {
230    Error("Unable to retrieve camera image settings:'".$res->status_line()."'");
231  }
232}
233
234#autoStop
235#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
236sub autoStop {
237  my $self = shift;
238  my $autostop = shift;
239  my $iszoom = shift;
240
241  if ( $autostop ) {
242    Debug('Auto Stop');
243    my $cmd = $controlUri;
244    my $msg_body;
245    if( $iszoom) {
246	    $msg_body = '
247		    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
248			<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
249				<ProfileToken>'.$profileToken.'</ProfileToken>
250				<PanTilt>
251				    false
252				</PanTilt>
253				<Zoom>
254				    true
255				</Zoom>
256			</Stop>
257		    </s:Body>';
258    } else {
259	    $msg_body = '
260		    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
261			<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
262				<ProfileToken>'.$profileToken.'</ProfileToken>
263				<PanTilt>
264				    true
265				</PanTilt>
266				<Zoom>
267				    false
268				</Zoom>
269			</Stop>
270		    </s:Body>';
271    }
272
273    my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
274    usleep($autostop);
275    $self->sendCmd($cmd, $msg_body, $content_type);
276  }
277}
278
279# Reboot
280sub reboot {
281  Debug('Camera reboot');
282  my $self = shift;
283  my $cmd = '';
284  my $msg_body = << "END_MESSAGE";
285        <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
286                <SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/>
287        </s:Body>
288END_MESSAGE
289
290  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
291  $self->sendCmd($cmd, $msg_body, $content_type);
292
293}
294
295# Reset(Reboot) the Camera
296sub reset {
297  Debug('Camera Reset');
298  my $self = shift;
299  my $cmd = '';
300  my $msg_body = << "END_MESSAGE";
301 	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
302		<SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/>
303	</s:Body>
304END_MESSAGE
305
306  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
307  $self->sendCmd($cmd, $msg_body, $content_type);
308}
309
310sub moveMap {
311  my $self = shift;
312  my $params = shift;
313  my $x = $self->getParam($params,'xcoord');
314  my $y = $self->getParam($params,'ycoord');
315  Debug("Move map to $x x $y");
316
317  my $cmd = $controlUri;
318  my $msg_body ='
319    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
320        <AbsoluteMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
321            <ProfileToken>' . $profileToken . '</ProfileToken>
322            <Position>
323                <PanTilt x="'.$x.'" y="'.$y.'" xmlns="http://www.onvif.org/ver10/schema"/>
324            </Position>
325            <Speed>
326                <Zoom x="1" xmlns="http://www.onvif.org/ver10/schema"/>
327            </Speed>
328        </AbsoluteMove>
329    </s:Body>';
330  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
331  $self->sendCmd($cmd, $msg_body, $content_type);
332}
333
334sub moveRel {
335  my $self = shift;
336  my $params = shift;
337  my $x = $self->getParam($params,'xcoord');
338  my $speed = $self->getParam($params,'speed');
339  my $y = $self->getParam($params,'ycoord');
340  Debug("Move rel to $x x $y");
341
342  my $cmd = $controlUri;
343  my $msg_body ='
344    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
345        <RelativeMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
346            <ProfileToken>' . $profileToken . '</ProfileToken>
347            <Translation>
348                <PanTilt x="'.$x.'" y="'.$y.'" xmlns="http://www.onvif.org/ver10/schema" space="http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace"/>
349                <Zoom x="1"/>
350            </Translation>
351        </RelativeMove>
352    </s:Body>';
353  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
354  $self->sendCmd($cmd, $msg_body, $content_type);
355}
356
357sub moveCamera {
358  my $type = shift;
359  my $x = shift;
360  my $y = shift;
361  my $msg_move_body = '';
362
363  if ( $type eq 'move' ) {
364  	$msg_move_body = '
365  	    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
366  	        <ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
367                <ProfileToken>'.$profileToken.'</ProfileToken>
368                    <Velocity>
369                        <PanTilt
370  	                      x="'.$x.'"
371  	                      y="'.$y.'"
372  	                      xmlns="http://www.onvif.org/ver10/schema"/>
373                    </Velocity>
374            </ContinuousMove>
375        </s:Body>';
376
377  } elsif ( $type eq 'zoom' ) {
378  	$msg_move_body = '
379		<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
380			<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
381				<ProfileToken>'.$profileToken.'</ProfileToken>
382				<Velocity>
383					<Zoom
384						x="'.$x.'"
385						xmlns="http://www.onvif.org/ver10/schema"/>
386				</Velocity>
387			</ContinuousMove>
388        </s:Body>';
389  }
390
391  return $msg_move_body;
392}
393
394#Up Arrow
395sub moveConUp {
396  Debug('Move Up');
397  my $self = shift;
398  my $cmd = $controlUri;
399  my $msg_body = moveCamera("move", "0","0.5");
400  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
401  $self->sendCmd($cmd, $msg_body, $content_type);
402  $self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
403}
404
405
406#Down Arrow
407sub moveConDown {
408  Debug('Move Down');
409  my $self = shift;
410  my $cmd = $controlUri;
411  my $msg_body = moveCamera("move","0","-0.5");
412  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
413  $self->sendCmd($cmd, $msg_body, $content_type);
414  $self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
415}
416
417#Left Arrow
418sub moveConLeft {
419  Debug('Move Left');
420  my $self = shift;
421  my $cmd = $controlUri;
422  my $msg_body = moveCamera("move","-0.49","0");
423  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
424  $self->sendCmd($cmd, $msg_body, $content_type);
425  $self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
426}
427
428#Right Arrow
429sub moveConRight {
430  Debug('Move Right');
431  my $self = shift;
432  my $cmd = $controlUri;
433  my $msg_body = moveCamera("move","0.49","0");
434  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
435  $self->sendCmd($cmd, $msg_body, $content_type);
436  $self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
437}
438
439#Zoom In
440sub zoomConTele {
441  Debug('Zoom Tele');
442  my $self = shift;
443  my $cmd = $controlUri;
444  my $msg_body = moveCamera("zoom","0.49","0");
445  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
446  $self->sendCmd($cmd, $msg_body, $content_type);
447  $self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
448}
449
450#Zoom Out
451sub zoomConWide {
452  Debug('Zoom Wide');
453  my $self = shift;
454  my $cmd = $controlUri;
455  my $msg_body = moveCamera("zoom","-0.49","0");
456  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
457  $self->sendCmd($cmd, $msg_body, $content_type);
458  $self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
459}
460
461sub zoomStop {
462  Debug('Zoom Stop');
463  my $self = shift;
464  my $cmd = $controlUri;
465  $self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
466  Error('Zoom Stop not implemented');
467}
468
469
470
471#Diagonally Up Right Arrow
472#This camera does not have builtin diagonal commands so we emulate them
473sub moveConUpRight {
474  Debug('Move Diagonally Up Right');
475  my $self = shift;
476  my $cmd = $controlUri;
477  my $msg_body = moveCamera("move","0.5","0.5");
478  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
479  $self->sendCmd($cmd, $msg_body, $content_type);
480  $self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
481}
482
483#Diagonally Down Right Arrow
484#This camera does not have builtin diagonal commands so we emulate them
485sub moveConDownRight {
486  Debug('Move Diagonally Down Right');
487  my $self = shift;
488  my $cmd = $controlUri;
489  my $msg_body = moveCamera("move","0.5","-0.5");
490  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
491  $self->sendCmd($cmd, $msg_body, $content_type);
492  $self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
493}
494
495#Diagonally Up Left Arrow
496#This camera does not have builtin diagonal commands so we emulate them
497sub moveConUpLeft {
498  Debug('Move Diagonally Up Left');
499  my $self = shift;
500  my $cmd = $controlUri;
501  my $msg_body = moveCamera("move","-0.5","0.5");
502  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
503  $self->sendCmd($cmd, $msg_body, $content_type);
504  $self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
505}
506
507#Diagonally Down Left Arrow
508#This camera does not have builtin diagonal commands so we emulate them
509sub moveConDownLeft {
510  Debug('Move Diagonally Down Left');
511  my $self = shift;
512  my $cmd = $controlUri;
513  my $msg_body = moveCamera("move","-0.5","-0.5");
514  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
515  $self->sendCmd($cmd, $msg_body, $content_type);
516  $self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
517}
518
519#Stop
520sub moveStop {
521  Debug('Move Stop');
522  my $self = shift;
523  my $cmd = $controlUri;
524  my $msg_body = '
525	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
526		<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
527			<ProfileToken>'.$profileToken.'</ProfileToken>
528				<PanTilt>true</PanTilt>
529				<Zoom>true</Zoom>
530		</Stop>
531	</s:Body>';
532  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
533  $self->sendCmd($cmd, $msg_body, $content_type);
534}
535
536#Set Camera Preset
537sub presetSet {
538  my $self = shift;
539  my $params = shift;
540  my $preset = $self->getParam($params, 'preset');
541  Debug("Set Preset $preset");
542  my $cmd = $controlUri;
543  my $msg_body ='
544	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
545		<SetPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl">
546			<ProfileToken>' . $profileToken . '</ProfileToken>
547			<PresetToken>'.$preset.'</PresetToken>
548		</SetPreset>
549	</s:Body>';
550  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"';
551  $self->sendCmd($cmd, $msg_body, $content_type);
552}
553
554#Recall Camera Preset
555sub presetGoto {
556  my $self = shift;
557  my $params = shift;
558  my $preset = $self->getParam($params, 'preset');
559
560  Debug("Goto Preset $preset");
561  my $cmd = $controlUri;
562  my $msg_body ='
563	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
564		<GotoPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl">
565			<ProfileToken>' . $profileToken . '</ProfileToken>
566			<PresetToken>'.$preset.'</PresetToken>
567		</GotoPreset>
568	</s:Body>';
569  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
570  $self->sendCmd( $cmd, $msg_body, $content_type );
571}
572
573#Recall Camera Preset
574sub presetHome {
575  my $self = shift;
576  my $params = shift;
577
578  Debug("Goto Home preset");
579  my $cmd = $controlUri;
580  my $msg_body ='
581	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
582		<GotoHomePosition xmlns="http://www.onvif.org/ver20/ptz/wsdl">
583			<ProfileToken>' . $profileToken . '</ProfileToken>
584		</GotoHomePosition>
585	</s:Body>';
586  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
587  $self->sendCmd( $cmd, $msg_body, $content_type );
588}
589
590#Horizontal Patrol
591#To be determined if this camera supports this feature
592sub horizontalPatrol {
593  Debug('Horizontal Patrol');
594  my $self = shift;
595  my $cmd = '';
596  my $msg ='';
597  my $content_type = '';
598  # $self->sendCmd( $cmd, $msg, $content_type );
599  Error('PTZ Command not implemented in control script.');
600}
601
602#Horizontal Patrol Stop
603#To be determined if this camera supports this feature
604sub horizontalPatrolStop {
605  Debug('Horizontal Patrol Stop');
606  my $self = shift;
607  my $cmd = '';
608  my $msg ='';
609  my $content_type = '';
610  #    $self->sendCmd( $cmd, $msg, $content_type );
611  Error('PTZ Command not implemented in control script.');
612}
613
614# Increase Brightness
615sub irisAbsOpen {
616  Debug("Iris $CamParams{Brightness}");
617  my $self = shift;
618  my $params = shift;
619  $self->getCamParams() unless($CamParams{Brightness});
620  my $step = $self->getParam($params, 'step');
621  my $max = 100;
622
623  $CamParams{Brightness} += $step;
624  $CamParams{Brightness} = $max if ($CamParams{Brightness} > $max);
625
626  my $cmd = 'onvif/imaging';
627  my $msg_body ='
628	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
629		<SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl">
630			<VideoSourceToken>000</VideoSourceToken>
631			<ImagingSettings>
632				<Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Brightness}.'</Brightness>
633			</ImagingSettings>
634			<ForcePersistence>true</ForcePersistence>
635		</SetImagingSettings>
636	</s:Body>';
637  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
638  $self->sendCmd($cmd, $msg_body, $content_type);
639}
640
641# Decrease Brightness
642sub irisAbsClose {
643  Debug("Iris $CamParams{Brightness}");
644  my $self = shift;
645  my $params = shift;
646  $self->getCamParams() unless($CamParams{brightness});
647  my $step = $self->getParam($params, 'step');
648  my $min = 0;
649
650  $CamParams{Brightness} -= $step;
651  $CamParams{Brightness} = $min if ($CamParams{Brightness} < $min);
652
653  my $cmd = 'onvif/imaging';
654  my $msg_body ='
655	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
656		<SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl">
657			<VideoSourceToken>000</VideoSourceToken>
658			<ImagingSettings>
659				<Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Brightness}.'</Brightness>
660			</ImagingSettings>
661			<ForcePersistence>true</ForcePersistence>
662		</SetImagingSettings>
663	</s:Body>';
664  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
665  $self->sendCmd($cmd, $msg_body, $content_type);
666}
667
668# Increase Contrast
669sub whiteAbsIn {
670  Debug("Iris $CamParams{Contrast}");
671  my $self = shift;
672  my $params = shift;
673  $self->getCamParams() unless($CamParams{Contrast});
674  my $step = $self->getParam($params, 'step');
675  my $max = 100;
676
677  $CamParams{Contrast} += $step;
678  $CamParams{Contrast} = $max if ($CamParams{Contrast} > $max);
679
680  my $cmd = 'onvif/imaging';
681  my $msg_body ='
682	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
683		<SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl">
684			<VideoSourceToken>000</VideoSourceToken>
685			<ImagingSettings>
686				<Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Contrast}.'</Contrast>
687			</ImagingSettings>
688			<ForcePersistence>true</ForcePersistence>
689		</SetImagingSettings>
690    </s:Body>';
691  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
692  $self->sendCmd($cmd, $msg_body, $content_type);
693}
694
695# Decrease Contrast
696sub whiteAbsOut {
697  Debug("Iris $CamParams{Contrast}");
698  my $self = shift;
699  my $params = shift;
700  $self->getCamParams() unless($CamParams{Contrast});
701  my $step = $self->getParam($params, 'step');
702  my $min = 0;
703
704  $CamParams{Contrast} -= $step;
705  $CamParams{Contrast} = $min if ($CamParams{Contrast} < $min);
706
707  my $cmd = 'onvif/imaging';
708  my $msg_body ='
709	<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
710		<SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl">
711			<VideoSourceToken>000</VideoSourceToken>
712			<ImagingSettings>
713				<Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Contrast}.'</Contrast>
714			</ImagingSettings>
715			<ForcePersistence>true</ForcePersistence>
716		</SetImagingSettings>
717	</s:Body>';
718  my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
719  $self->sendCmd($cmd, $msg_body, $content_type);
720}
721
7221;
723__END__
724