IPCamera-Reolink
view release on metacpan or search on metacpan
lib/IPCamera/Reolink.pm view on Meta::CPAN
return $response_content;
}else{
my $t5 = Time::HiRes::time() if($DEBUG > 2);
print STDERR scalar(localtime()) . ": debug: IPCamera::Reolink::_sendCameraCommand($camera_command): " . $t5 . ": call JSON::decode_json()\n" if($DEBUG > 2);
my $response_r;
eval{
$response_r = JSON::decode_json($response_content);
};
if($@){
print STDERR scalar(localtime()) . ": error: IPCamera::Reolink::_sendCameraCommand($camera_command): JSON::decode_json() failed - '$@' - responseContent '$response_content'\n";
return undef;
} # if
my $t6 = Time::HiRes::time() if($DEBUG > 1);
print STDERR scalar(localtime()) . ": debug: IPCamera::Reolink::_sendCameraCommand($camera_command): JSON encode " . ($t2 - $t1) . " POST " . ($t3 - $t2) . " responseCode " . ($t4 - $t3) . " responseContent " . ($t5 - $t4) . " JSON decode " ....
if($DEBUG > 1){
if($DEBUG > 3){
my @headers = $camera_rest_client->responseHeaders();
foreach my $header (@headers){
print STDERR scalar(localtime()) . ": debug: IPCamera::Reolink::_sendCameraCommand($camera_command): header '" . $header . "' '" . $camera_rest_client->responseHeader($header) . "' \n";
} # for
} # if
my $save_linewidth = $Data::Dump::LINEWIDTH;
$Data::Dump::LINEWIDTH = 500; # no linebreak
print STDERR scalar(localtime()) . ": debug: IPCamera::Reolink::_sendCameraCommand($camera_command): command '" . $camera_command . "' token '" . (defined($token) ? $token : 'undef') . "' request '" . Dumper($request_r) . "' response '" ....
$Data::Dump::LINEWIDTH = $save_linewidth;
} # if
my $code = @$response_r[0]->{code};
if($response_code ne '200' || $code != 0){
print STDERR scalar(localtime()) . ": error: IPCamera::Reolink::_sendCameraCommand($camera_command): exit ERROR: response_code '$response_code ' code '$code'\n" if($DEBUG > 2);
return undef;
}else{
print STDERR scalar(localtime()) . ": debug: IPCamera::Reolink::_sendCameraCommand($camera_command): exit OK response_code '$response_code ' code '$code'\n" if($DEBUG > 2);
return $response_r;
} # if
} # if
} # _sendCameraCommand()
# _checkLoginLeaseTime() - determine if camera Login lease for Login token has expired and reacquire on expiry
sub _checkLoginLeaseTime($){
my($self) = @_;
my $now = time();
my($_camera_login_lease_time, $_camera_login_lease_start_time) = ($self->{_camera_login_lease_time}, $self->{_camera_login_lease_start_time});
if($now >= $_camera_login_lease_start_time + $_camera_login_lease_time){
# Perhaps re-establish the REST connection as long intervals with no activity seem to make the camera "forget" the connection. @@@
my($login_token, $login_lease_time) = Login();
if(defined($login_token)){
$self->{_camera_login_lease_start_time} = $now; # New Login token lease time
$self->{_camera_login_lease_time} = $login_lease_time; # New Login token
return 1; # new Login token
}else{
return 0; # failed to acquire new Login token
} # if
}else{
return 1; # Login token still valid
} # if
} # _checkLoginLeaseTime()
# Login() - provides login credentials (username/password) to camera and returns API access token good for specified number of seconds
sub Login(){
my($self) = @_;
my($_camera_rest_client, $camera_url, $camera_user_name, $camera_password, $camera_X509_certificate_file, $camera_X509_key_file, $camera_certificate_authority_file, $_is_https) = ($self->{_camera_rest_client}, $self->{camera_url}, $self->{camera_...
if(!defined($_camera_rest_client)){
# disable check for valid certificate matching the expected hostname
$_camera_rest_client = REST::Client->new(host => $camera_url, timeout => 10, cert => $camera_X509_certificate_file, key => $camera_X509_key_file, ca => $camera_certificate_authority_file);
$_camera_rest_client->getUseragent()->ssl_opts(verify_hostname => 0);
$_camera_rest_client->getUseragent()->ssl_opts(SSL_verify_mode => SSL_VERIFY_NONE);
$self->{_camera_rest_client} = $_camera_rest_client;
} # if
my $login_r = [ {cmd => "Login", param => { User => { Version => "0", userName => $camera_user_name, password => $camera_password, }}} ];
my $response_r = _sendCameraCommand($_camera_rest_client, 'Login', $login_r, undef);
if(defined($response_r)){
$self->{_camera_login_token} = @$response_r[0]->{value}->{Token}->{name};
$self->{_camera_login_lease_time} = @$response_r[0]->{value}->{Token}->{leaseTime};
$self->{_camera_login_lease_start_time} = time(); # detect lease expiry
print STDERR scalar(localtime()) . ": info: Camera Login for user '" . $self->{camera_user_name} . "' OK, token leaseTime '" . $self->{_camera_login_lease_time} . "' name '" . $self->{_camera_login_token} . "'\n" if($DEBUG > 0);
return ($self->{_camera_login_token}, $self->{_camera_login_lease_time});
}else{
print STDERR scalar(localtime()) . ": error: Camera Login for user '" . $self->{camera_user_name} . "' failed\n" if($DEBUG > 0);
return (undef, undef);
} # if
} # Login()
# Logout() - release login credentials from previous Login()
sub Logout(){
my($self) = @_;
my($_camera_rest_client, $_camera_login_token) = ($self->{_camera_rest_client}, $self->{_camera_login_token});
if(defined($_camera_rest_client)){
$self->{_camera_login_token} = undef;
$self->{_camera_login_lease_time} = 0;
$self->{_camera_login_lease_start_time} = time();
}else{
# No Login()
return 0;
} # if
my $logout_r = [ {cmd => "Logout", param => { }} ];
my $response_r = _sendCameraCommand($_camera_rest_client, 'Logout', $logout_r, $_camera_login_token);
$self->{_camera_rest_client} = undef; # hopefully drops the REST connection
if(defined($response_r)){
return 1;
}else{
return 0;
} # if
} # Logout()
# GetChannelstatus() - implement camera API GetChannelstatus interface.
sub GetChannelstatus($){
my($self) = @_;
if(!_checkLoginLeaseTime($self)){
return (undef, undef);
} # if
my $get_channelstatus_r = [ {cmd => "GetChannelstatus", } ];
my($_camera_rest_client, $_camera_login_token) = ($self->{_camera_rest_client}, $self->{_camera_login_token});
my $response_r = _sendCameraCommand($_camera_rest_client, 'GetChannelstatus', $get_channelstatus_r, $_camera_login_token);
if(defined($response_r)){
return (@$response_r[0]->{value}->{count}, @$response_r[0]->{value}->{status});
}else{
return (undef, undef);
} # if
} # GetChannelstatus()
# GetDevInfo() - implement camera API GetDevInfo interface.
sub GetDevInfo($){
my($self) = @_;
if(!_checkLoginLeaseTime($self)){
return undef;
} # if
my $get_dev_info_r = [ {cmd => "GetDevInfo", } ];
my($_camera_rest_client, $_camera_login_token) = ($self->{_camera_rest_client}, $self->{_camera_login_token});
my $response_r = _sendCameraCommand($_camera_rest_client, 'GetDevInfo', $get_dev_info_r, $_camera_login_token);
if(defined($response_r)){
return @$response_r[0]->{value}->{DevInfo};
}else{
return undef;
} # if
} # GetDevInfo()
# PtzCtrl() - implement camera API PtzCtrl interface, used to control the operation of PTZ (Pan/Tilt/Zoom).
sub PtzCtrl($$$$;$){
my($self, $channel, $op, $speed, $preset_id) = @_;
if(!_checkLoginLeaseTime($self)){
return 0;
} # if
my($_camera_rest_client, $_camera_login_token) = ($self->{_camera_rest_client}, $self->{_camera_login_token});
lib/IPCamera/Reolink.pm view on Meta::CPAN
if(defined($mode)){
${$setwhiteled_r->[0]->{param}->{WhiteLed}}{mode} = int($mode);
} # if
if(defined($bright)){
${$setwhiteled_r->[0]->{param}->{WhiteLed}}{bright} = int($bright);
} # if
if(defined($lighting_schedule_r)){
${$setwhiteled_r->[0]->{param}->{WhiteLed}}{LightingSchedule} = $lighting_schedule_r;
} # if
if(defined($ai_detect_type_r)){
${$setwhiteled_r->[0]->{param}->{WhiteLed}}{wlAiDetectType} = $ai_detect_type_r;
} # if
my $response_r = _sendCameraCommand($_camera_rest_client, 'SetWhiteLed', $setwhiteled_r, $_camera_login_token);
if(defined($response_r)){
return 1;
}else{
return 0;
} # if
} # SetWhiteLed()
# GetWhiteLed() - get configuration of white led (spot light)
sub GetWhiteLed($$){
my($self, $channel) = @_;
if(!_checkLoginLeaseTime($self)){
return 0;
} # if
my($_camera_rest_client, $_camera_login_token) = ($self->{_camera_rest_client}, $self->{_camera_login_token});
my $getwhiteled_r = [ {cmd => "GetWhiteLed", action => 0, param => { channel => int($channel), }} ];
my $response_r = _sendCameraCommand($_camera_rest_client, 'GetWhiteLed', $getwhiteled_r, $_camera_login_token);
if(defined($response_r)){
return (@$response_r[0]->{value}, @$response_r[0]->{range}, @$response_r[0]->{initial}, );
}else{
return (undef, undef, undef);
} # if
} # GetWhiteLed()
=pod
=encoding UTF-8
=head1 NAME
IPCamera::Reolink - Reolink API provides access to the System, Security, Network, Video input, Enc, Record, PTZ, and Alarm functions of a Reolink IP Camera or NVR via HTTP(S)/REST
=head1 VERSION
1.10
=head1 SYNOPSIS
use IPCamera::Reolink;
my $camera_url = "http://192.168.1.160";
my $camera_user_name = "vlc"; # non-admin user recommended
my $camera_password = 'this-is-a-bad-password';
my $camera = IPCamera::Reolink->new($camera_url, $camera_user_name, $camera_password);
# or
# my $camera = IPCamera::Reolink->new( {camera_url => $camera_url_http, camera_user_name => $camera_user_name, camera_password => $camera_password, camera_x509_certificate_file => undef, camera_x509_key_file => undef, camera_certificate_authority_fi...
# Optionally Login to the camera immmediately to validate the camera URL and credentials, otherwise a Login will be implicitly performed by the API on the first camera command.
die "IPCamera::Reolink::Login failed" if(!$camera->Login());
# Some camera info
my $devinfo_r = $camera->GetDevInfo();
print "Camera model : " . $devinfo_r->{model} . "\n";
print "Camera name : " . $devinfo_r->{name} . "\n";
# Camera presets
my($ptzpreset_value_r, $ptzpreset_range_r, $ptzpreset_initial_r) = $camera->GetPtzPreset(IPCamera::Reolink::ChannelDefault);
my $ptzpresets_r = $ptzpreset_value_r->{PtzPreset};
foreach my $ptzpreset (@$ptzpresets_r){
my $enable = $ptzpreset->{enable};
if($enable){
my $preset_id = $ptzpreset->{id};
$camera->PtzCtrl(IPCamera::Reolink::ChannelDefault, IPCamera::Reolink::PTZ_ToPos, IPCamera::Reolink::PTZ_SpeedMax, $preset_id);
} # if
} # foreach
# Start a camera pan to the left.
$camera->PtzCtrl(IPCamera::Reolink::ChannelDefault, IPCamera::Reolink::PTZ_Left, IPCamera::Reolink::PTZ_SpeedMax);
# Times passes, stop the last camera PTZ function.
$camera->PtzCtrl(IPCamera::Reolink::ChannelDefault, IPCamera::Reolink::PTZ_Stop, IPCamera::Reolink::PTZ_SpeedMin);
# Camera snapshot
my $jpg_image_data = $camera->Snap(IPCamera::Reolink::ChannelDefault);
die "IPCamera::Reolink::Snap() failed\n" if(!defined($jpg_image_data));
my $now = time();
my($sec, $min, $hour, $dd, $mon, $yr, $wday, $yday, $isdst) = localtime($now);
($dd < 10) && ($dd = "0" . $dd);
my $mm = $mon + 1;
($mm < 10) && ($mm = "0" . $mm);
($hour < 10) && ($hour = "0" . $hour);
my $yyyy = $yr + 1900;
my $file_name = sprintf("%04d%02d%02d-%2s%02d%02d.jpg", $yyyy, $mm, $dd, $hour, $min, $sec);
my $fh;
open $fh, ">$file_name" || die("open($file_name) failed - $!");
my $l = length($jpg_image_data);
(syswrite($fh, $jpg_image_data, $l) == $l) || die("syswrite($file_name, $l) failed - $!\n");
close($fh);
# Play Audio Alarm 1 time
my $r = $camera->AudioAlarmPlay(IPCamera::Reolink::ChannelDefault, 0, 1, IPCamera::Reolink::AAP_AlarmModeTimes);
# Turn the spotlight on.
$camera->SetWhiteLed(IPCamera::Reolink::ChannelDefault, IPCamera::Reolink::WhiteLed_StateOn, undef, undef, undef, undef);
# Turn the spotlight off.
$camera->SetWhiteLed(IPCamera::Reolink::ChannelDefault, IPCamera::Reolink::WhiteLed_StateOff, undef, undef, undef, undef);
lib/IPCamera/Reolink.pm view on Meta::CPAN
I<$IPCamera::Reolink::OSD_pos_list> is the IPCamera::Reolink::SetOsd() pos values as list.
=head2 AAP_AlarmMode_list
I<IPCamera::Reolink::AAP_AlarmMode_list> is the IPCamera::Reolink::AudioAlarmPlay() alarm_mode values as list.
=head2 IrLights_State_list
I<$IPCamera::Reolink::IrLights_State_list> is the IPCamera::Reolink::GetIrLights() and IPCamera::Reolink::SetIrLights() IrLights_State values as list.
=head1 METHODS
=head2 new({camera_url => "http://192.168.1.99/", camera_user_name => "admin", camera_password => "password", camera_x509_certificate_file => undef, camera_x509_key_file => undef, camera_certificate_authority_file => undef, })
Hash reference version.
See below for details.
=head2 new($camera_url, $camera_user_name, $camera_password)
Construct a new IPCamera::Reolink object.
Takes the following manditory parameters:
=over 4
=item $camera_url
The camera URL for access via HTTP(S), i.e. "http://192.168.1.123".
=item $camera_user_name
The camera account name for access via HTTP(S), i.e. "vlc".
You should set up a non admin acccount for access via this API, the admin account will work as well but is not recommended.
But just to complicate things, certain methods (described below) require admin access to use with the current version of the camera firmware.
Reolink may fix this in the future.
=item $camera_password
The camera account password for access via HTTP(S), i.e. "this-is-a-bad-password".
=item $camera_x509_certificate_file
TBD.
=item $camera_x509_key_file
TBD.
=item $camera_x509_authority_file
TBD.
=back
=head2 Login()
Login to the camera using the credentials provided to new() (above).
Upon successful Login the camera passes back a Login token and lease time that are used internally by other IPCamera::Reolink camera API methods.
The token is valid for the specified lease time.
IPCamera::Reolink will manage the Login token and will call Login() internally as needed when the Login token expires.
This should all be invisible to the caller.
=over 4
=item return
Returns (undef, undef) if the Login was rejected by the camera.
Returns ($camera_login_token, $camera_login_lease_time) if the Login is successful,
where $camera_login_token is the token passed to other API methods and $camera_login_lease_time is the time in seconds for which the token is valid, after which a new token must be aquired.
Usually the caller is not interested in these values as they are used internally by IPCamera::Reolink.
=back
=head2 Logout()
Release Login() credentials.
=over 4
=item return
Returns 1 if Logout() succeeded else 0 (zero) on failure, typically if Login() not called.
=back
=head2 GetChannelstatus()
Return camera/NVR per channel status.
=over 4
=item return
Returns ($count, $status_r) if GetChannelstatus() succeeded.
=over 4
=item $count
The number of channels in the camera/NVR, typically 1 for a camera.
The number of elements in the I<@status> array.
=item $status_r
reference to Per channel status array with I<$count> elements.
=over 4
=item $statu_r->[$i]->{channel} (0)
Channel index, 0 < $i < $count.
=item $status_r->[$i]->{name} ("CAM1")
Channel name.
=item $status_r->[$i]->{online} (1)
0 if channel offline, 1 if channel online.
=item $status_r->[$i]->{typeInfo} ("CAM1")
Channel description.
=back
=back
Returns (undef, undef) if GetChannelstatus() failed.
=back
=head2 GetDevInfo()
Return useful information about the camera.
lib/IPCamera/Reolink.pm view on Meta::CPAN
=back
=item $camera_preset_id
Move camera to specfied camera preset, >= IPCamera::Reolink::PTZ_PresetMin, <= IPCamera::Reolink::PTZ_PresetMax.
Used only for IPCamera::Reolink::PTZ_ToPos camera operation.
=back
=head2 GetZoomFocus($camera_channel)
Return camera current Zoom and Focus values.
=over 4
=item $camera_channel
Perform camera operation on specified camera channel:
=over 4
=item IPCamera::Reolink::ChannelDefault
If you are connected to a camera then there is (usually) only 1 channel.
=item integer >= 0
If you are connected to an NVR then there is usually one channel per attached camera starting at integer value 0.
=back
=item return
Returns undef if the GetZoomFocus function failed.
Returns $zoom_focus_r hash reference to Zoom/Focus information if successful, fields most likely subject to change (typical values in parenthesis):
=over 4
=item $zoom_focus_r->{channel} (0)
Camera channel.
=item $zoom_focus_r->{focus}->{pos} (23)
Current camera focus value in the range IPCamera::Reolink::ZF_FocusPosMin, IPCamera::Reolink::ZF_FocusPosMax.
=item $zoom_focus_r->{zoom}->{pos} (0)
Current camera zoom value in the range IPCamera::Reolink::ZF_ZoomPosMin, IPCamera::Reolink::ZF_ZoomPosMax.
=back
=back
=head2 StartZoomFocus($camera_channel, $camera_operation, $camera_zoom_pos|$camera_focus_pos)
Set camera current Zoom or Focus value.
Note that the current version of the firmware on the authors camera (Firmware Version v3.1.0.2347_23061923_v1.0.0.93) requires a Login() using admin credentials to use this function.
According to Reolink this may be fixed in a future firmware version.
If in doubt, set $DEBUG to 1 and if you see a log message of the form:
Tue Dec 19 18:17:07 2023: debug: IPCamera::Reolink::_sendCameraCommand(): command 'StartZoomFocus' token '5b34aab0bb481ba' request '[{ action => 0, cmd => "StartZoomFocus", param => { ZoomFocus => { channel => 0, op => "ZoomPos", pos => 1 } } }]' res...
then you need to use admin credentials to use this function.
=over 4
=item return
Returns 1 if StartZoomFocus() succeeded else 0 (zero) on failure.
=back
=over 4
=item $camera_channel
Perform camera operation on specified camera channel.
=over 4
=item IPCamera::Reolink::ChannelDefault
If you are connected to a camera then there is (usually) only 1 channel.
=item integer >= 0
If you are connected to an NVR then there is usually one channel per attached camera starting at integer value 0.
=back
=item $camera_operation
Camera operation to perform.
=over 4
=item IPCamera::Reolink::ZF_ZoomPos
Set Zoom to specified pos value.
=over 4
=item $camera_zoom_pos
Set the camera Zoom to the specified pos value in the range:
=over 4
=item IPCamera::Reolink::ZF_ZoomPosMin
Minimum camera zoom value.
=item IPCamera::Reolink::ZF_ZoomPosMax
Maximum camera zoom value.
=back
=back
=item IPCamera::Reolink::ZF_FocusPos
Set Focus to specified pos value.
lib/IPCamera/Reolink.pm view on Meta::CPAN
OSD display value.
=item $osd_value_r->{Osd}->{osdChannel}->{pos} ("Lower Right")
OSD display position.
=back
=item $osd_value_r->{Osd}->{osdTime}
Current OSD time display values for specified camera channel.
=over 4
=item $osd_value_r->{Osd}->{osdTime}->{enable} (0/1)
OSD time display enabled if 1 else disabled if 0.
=item $osd_value_r->{Osd}->{osdTime}->{pos} ("Lower Right")
OSD time display position.
=back
=back
=back
=item $osd_range_r
Hash reference to camera OSD value ranges.
=over 4
=item TBD
=back
=item $osd_initial_r
Hash reference to camera OSD initial values.
=over 4
=item TBD
=back
=back
=back
=head2 SetOsd($camera_channel, $enableChannel, $channelName, $channelPos, $enableTime, $timePos)
Set camera On Screen Display (OSD) values.
Note that the current version of the firmware on the authors camera
(Firmware Version v3.1.0.2347_23061923_v1.0.0.93) requires a Login()
using admin credentials to use this function.
=over 4
=item return
Returns 1 if SetOsd() succeeded else 0 (zero) on failure.
=back
=over 4
=item $camera_channel
Perform camera operation on specified camera channel.
=over 4
=item IPCamera::Reolink::ChannelDefault
If you are connected to a camera then there is (usually) only 1 channel.
=item integer >= 0
If you are connected to an NVR then there is usually one channel per attached camera starting at integer value 0.
=back
=item $enableChannel (0/1)
1 to enable display of channelName else 0.
=item $channelName ("rlc-823a-16x")
Channel OSD text to display
=item $channelPos (IPCamera::Reolink::OSD_UpperLeft)
Channel OSD text position:
=over 4
=item IPCamera::Reolink::OSD_UpperLeft
OSD position "Upper Left"
=item IPCamera::Reolink::OSD_TopCenter
OSD position "Top Center"
=item IPCamera::Reolink::OSD_UpperRight
OSD position "Upper Right"
=item IPCamera::Reolink::OSD_LowerLeft
OSD position "Lower Left"
=item IPCamera::Reolink::OSD_BottomCenter
OSD position "Bottom Center"
( run in 1.032 second using v1.01-cache-2.11-cpan-39bf76dae61 )