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