#!/usr/bin/perl # # SPDX-License-Identifier: AGPL-3.0-or-later # Copyright (c) Rainer Gümpelein, TeilRad GmbH # #Examples #./src/scripts/Ilockit_cloud.pl shareedms-operator get_events 95 # #./src/scripts/Ilockit_cloud.pl shareedms-operator get_positions # #Ilockit GPS cloud # #4. Get a list of positions #fetch_ >> https://tracking.ilockit.bike/api/positions?from=2021-05-31T07:44:10Z&to=2021-06-06T07:44:10Z&deviceId=4272 #5. Get a list of Events #fetch_ >> https://tracking.ilockit.bike/api/reports/events?from=2021-05-31T07:44:10Z&to=2021-06-06T07:44:10Z&deviceId=4272 use vars qw($syshost); BEGIN { $syshost = $ARGV[0] || die; } use lib "/var/www/copri-bike/$syshost/src"; use strict; use warnings; use POSIX; use JSON; use LWP::UserAgent; use DateTime; use Time::Piece; use Mod::DBtank; use Data::Dumper; my $dbt = new DBtank; my $now_dt = strftime "%Y-%m-%d %H:%M:%S", localtime; my $api_file = "/var/www/copri4/shareeconf/apikeys.cfg"; my $aconf = Config::General->new($api_file); my %apikeyconf = $aconf->getall; #print "---> " . $apikeyconf{Ilockitcloud}->{username} . "\n"; my $ua = LWP::UserAgent->new; $ua->agent("sharee APIclient"); $ua->credentials( 'tracking.ilockit.bike:443', 'api', "$apikeyconf{Ilockitcloud}->{username}", "$apikeyconf{Ilockitcloud}->{passwd}"); my $json = JSON->new->allow_nonref; my $owner = 183; my $todo = $ARGV[1]; my $groupId = $ARGV[2] || ""; my $deviceId = $ARGV[3] || ""; open(FILE,">>$dbt->{copri_conf}->{logdir}/Ilockit_cloud.log"); print FILE "\n\n*** $now_dt\n"; #2022-11-08 looping and rest changed to groupId and limit (max 10000) #get Ilockit events by groupId my $endpoint = "https://tracking.ilockit.bike/api/reports/events"; my $rest = "groupId=$groupId\&limit=100"; my $ret_json = fetch_ilockit_cloud("","$endpoint",$rest); my $response_in = {}; $response_in = decode_json($ret_json); print FILE "Ilockit $todo response_in:" . Dumper($response_in); #main #loop operators DB while (my ($key, $op_name) = each %{ $dbt->{operator} }) { if($op_name->{database}->{dbname} && $op_name->{hwtype} ne "sigo"){ print FILE "\nON----------$op_name->{database}->{dbname}---$now_dt | $todo\n"; my $rows = 0; my $sharee_operator = $op_name->{database}->{dbname}; my $dbh = ""; $dbh = $dbt->dbconnect_extern($sharee_operator); #per cronjob once a day to get and update content with cloud device id #./src/scripts/Ilockit_cloud.pl shareedms-fr01 get_devices &get_devices($dbh,$op_name) if($todo eq "get_devices"); #./src/scripts/Ilockit_cloud.pl shareedms-fr01 get_events 95 if($todo eq "get_events"){ &get_events($dbh,$op_name,$response_in); &get_positions($dbh,$op_name); } #./src/scripts/Ilockit_cloud.pl shareedms-fr01 get_positions 6572 #&get_positions($dbh,$op_name->{oprefix}) if($todo eq "get_positions"); } } #my $endpoint = "https://tracking.ilockit.bike/api/commands"; #utc to localtime sub localizedtime { my $date = shift; $date =~ s/\..*$//; my $time = Time::Piece->strptime($date, "%Y-%m-%dT%H:%M:%S"); print FILE "localizedtime GMT alias Zulu: " . $time->datetime . "\n";#GMT alias Zulu $time = localtime($time->epoch);#epoch print FILE "localizedtime localized date time: " . $time->datetime . "\n";#localized date time return $time->datetime; } #print localizedtime("2021-06-11T11:58:09.000+0000") . " example\n"; #localtime to utc #my $now = strftime "%Y-%m-%dT%H:%M:%S", localtime; #utctime($now); sub utctime { my $date = shift; my $latency = shift || 0; $date =~ s/\..*$//; $date =~ s/\+\d+$//; print FILE "requested datetime: " . $date . "\n"; my $time = Time::Piece->strptime($date, "%Y-%m-%dT%H:%M:%S"); print FILE "localtime: " . $time->datetime . "\n";#localtime my $utc_epoch = $time->epoch; # #only -1 hour, because of deviceTime and serverTime differs #'deviceTime' => '2021-10-14T08:19:35.000+0000', #'serverTime' => '2021-10-14T07:19:37.000+0000', $utc_epoch -= 2*60*60;# -2 std (Sommerzeit) #$utc_epoch -= 1*60*60;# -1 std (Winterzeit) $utc_epoch += $latency; $time = gmtime($utc_epoch);#epoch print FILE "utctime: " . $time->datetime . "\n";#utc zulu date time # return $time->datetime; } #get all device localy sub get_devicesONcontent_all { my $dbh = shift; my $serialnr = shift || ""; my $pref = { table => "content", fetch => "all", keyfield => "int13", template_id => "205", int13 => ">::0", }; $pref->{txt22} = "ilike::$serialnr%" if($serialnr); my $record = $dbt->fetch_record($dbh,$pref); return $record; } #get last (end_time) device locally in contenttranspos to get end_time of last state sub get_devicesONcontenttranspos { my $dbh = shift; my $deviceId = shift; my $pref = { table => "contenttranspos", fetch => "one", order => "end_time", int13 => "$deviceId", }; my $record = $dbt->fetch_tablerecord($dbh,$pref); return $record; } #get one device localy sub get_devicesONcontent { my $dbh = shift; my $deviceId = shift; my $pref = { table => "content", fetch => "one", template_id => "205", int13 => "$deviceId", }; my $record = $dbt->fetch_record($dbh,$pref); return $record; } #get and check if theft exist in contenttranspos not older than 1 day sub get_devicesONcontenttheftpos { my $dbh = shift; my $key = shift; my $id = shift; my $end_time = shift || ""; my $pref = { table => "contenttheftpos", fetch => "one", $key => "$id", }; $pref->{end_time} = ">=::$end_time" if($end_time); my $record = $dbt->fetch_tablerecord($dbh,$pref); return $record; } sub get_devices { my $dbh = shift; my $op_name = shift; my $endpoint = "https://tracking.ilockit.bike/api/devices"; my $rest = ""; my $ret_json = fetch_ilockit_cloud("","$endpoint",$rest); my $response_in = {}; $response_in = decode_json($ret_json); print FILE "ilockit get_devices response_in:" . Dumper($response_in); my $pref = { table => "content", fetch => "all", keyfield => "barcode", template_id => "205", #int10 => "1",#1 = "available" }; my $record = $dbt->fetch_record($dbh,$pref); my $rows = 0; if(1==1 && ref($response_in) eq "ARRAY"){ foreach my $id (sort { $record->{$a}->{barcode} <=> $record->{$b}->{barcode} } keys (%$record)){ foreach my $resp (sort { $a->{id} <=> $b->{id} } (@{ $response_in })) { #print "if($resp->{name} eq $record->{$id}->{txt18} && ($resp->{id} && $resp->{id} ne $record->{$id}->{int13}))\n"; if($resp->{name} eq $record->{$id}->{txt18} && ($resp->{id} && $resp->{id} ne $record->{$id}->{int13})){ my $update = { table => "content", mtime => "now()", owner => "$owner", int13 => "$resp->{id}", }; $rows = $dbt->update_record($dbh,$update,$record->{$id}); print FILE "update_record content.int13=$resp->{id}" . $rows . "\n"; } } } } }#end get_devices #get_events sub get_events { my $dbh = shift; my $op_name = shift; my $response_in = shift; #1. select all devices on content print FILE "ilockit get_events get_devicesONcontent_all\n"; my $record_cc = get_devicesONcontent_all($dbh,""); my $today = DateTime->now( time_zone => "Europe/Berlin" ); #$today .= "Z"; #my $from_datetime = DateTime->now( time_zone => "Europe/Berlin" ); #$from_datetime->subtract( days => 1 ); #$from_datetime .= "Z"; #my $endpoint = "https://tracking.ilockit.bike/api/reports/events"; #my $rest = "from=2021-05-31T07:44:10Z\&to=2021-06-06T07:44:10Z\&deviceId=4272"; #my $rest = "from=2021-06-11T07:44:10Z\&to=2021-06-11T12:44:10Z\&deviceId=$deviceId"; #2. loope content to get deviceId foreach my $id (sort { $record_cc->{$a}->{barcode} <=> $record_cc->{$b}->{barcode} } keys (%$record_cc)){ #get Ilockit events by deviceId #my $rest = "from=$from_datetime\&to=$today\&deviceId=$record_cc->{$id}->{int13}"; #my $ret_json = fetch_ilockit_cloud("","$endpoint",$rest); #$response_in = decode_json($ret_json); #print FILE "ilockit get_events response_in:" . Dumper($response_in); foreach my $resp (sort { $a->{id} <=> $b->{id} } (@{ $response_in })) { #if($record_cc->{$id}->{int13} eq $resp->{deviceId} && $resp->{type} eq "deviceOnline"){ if($record_cc->{$id}->{int13} && $record_cc->{$id}->{int13} eq $resp->{deviceId} && $resp->{type} eq "lockStatus" && ref($resp->{attributes}) eq "HASH" && $resp->{attributes}->{statusCode}){ my $theft_record = { c_id => 0 }; $theft_record = get_devicesONcontenttheftpos($dbh,"int01",$resp->{id},"");#Alarm detected print FILE "shareeTime: $today\n"; print FILE "bike: $op_name->{oprefix}$record_cc->{$id}->{barcode}\n"; print FILE "id: $resp->{id}\n"; print FILE "deviceId: $resp->{deviceId}\n"; print FILE "type: $resp->{type}\n"; print FILE "statusCode: $resp->{attributes}->{statusCode}\n"; print FILE "serverTime: $resp->{serverTime}\n\n"; my $int10 = 0; my $txt10 = $resp->{attributes}->{statusCode}; if($resp->{attributes}->{statusCode} eq "alarm"){ $int10 = 7; } #insert theft alarm if no theft alarm with theft id if(!$theft_record->{c_id}){ my $serverTime = localizedtime($resp->{serverTime}); my $insert = { table => "contenttheftpos", cc_id => "$record_cc->{$id}->{c_id}", barcode => "$record_cc->{$id}->{barcode}", int04 => "$record_cc->{$id}->{int04}",#end station txt06 => "$record_cc->{$id}->{txt06}",#end gps txt10 => "$resp->{attributes}->{statusCode}", txt18 => "$record_cc->{$id}->{txt18}", owner => $owner, mtime => "now()", int10 => "$int10",#theft alarm int01 => "$resp->{id}",#keeps id for event_type int13 => "$resp->{deviceId}", int20 => "$record_cc->{$id}->{int20}", start_time => "$serverTime", end_time => "$serverTime", }; my $c_id = 0; $c_id = $dbt->insert_contentoid($dbh,$insert); print FILE "insert theft_record:" . Dumper($insert); } } } } }#end get_events sub get_positions { my $dbh = shift; my $op_name = shift; my $record_cc = get_devicesONcontent_all($dbh,"C2-04"); my $endpoint = "https://tracking.ilockit.bike/api/positions"; my $today = DateTime->now( time_zone => "Europe/Berlin" ); $today .= "Z"; my $interval = 3; my $from_datetime = DateTime->now( time_zone => "Europe/Berlin" ); $from_datetime->subtract( minutes => $interval ); $from_datetime = utctime($from_datetime,"0"); $from_datetime .= "Z"; #2. loope cloud foreach my $id (sort { $record_cc->{$a}->{barcode} <=> $record_cc->{$b}->{barcode} } keys (%$record_cc)){ my $ctpos = { c_id => 0 }; $ctpos = get_devicesONcontenttranspos($dbh,$record_cc->{$id}->{int13});#get last rental #checke also Ilockit lockStatus my $theft_record_closed = { c_id => 0 }; my $pref_th = { table => "contenttheftpos", fetch => "one", txt10 => "ilike::closed%", int13 => $record_cc->{$id}->{int13}, end_time => ">::$ctpos->{end_time}", }; $theft_record_closed = $dbt->fetch_tablerecord($dbh,$pref_th) if($ctpos->{end_time}); #For Closed locks #if copri.locked OR Ilockit.closed, then get position in range of end_time to now if($record_cc->{$id}->{int13} && (($ctpos->{int20} && $ctpos->{int20} == 1) || $theft_record_closed->{c_id})){ print FILE "get_position bike $ctpos->{ct_name} $ctpos->{int13} --> copri lock_state:$ctpos->{int20} end_time:$ctpos->{end_time} | Ilockit.statusCode: $theft_record_closed->{txt10}\n"; #3 minutes until now last Alarm detected with deviceId my $theft_record_detect = { c_id => 0 }; $pref_th = { table => "contenttheftpos", fetch => "one", int10 => 7, int13 => $record_cc->{$id}->{int13}, end_time => ">=::(now() - interval '$interval min')", }; #end_time => ">::$ctpos->{end_time}", $theft_record_detect = $dbt->fetch_tablerecord($dbh,$pref_th); print FILE "theft_record_detect bike: $theft_record_detect->{barcode} | $theft_record_detect->{c_id} | last end_time:$theft_record_detect->{end_time}\n"; #get only positions if locked and last alarm before 3 minutes if($theft_record_detect->{c_id} && $ctpos->{int13} && $ctpos->{end_time} =~ /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/){ #$ctpos->{end_time} =~ s/\..*$//; #my $end_time = $ctpos->{end_time}; #$end_time =~ s/\s/T/; #my $from_datetime = utctime($end_time,"0"); #$from_datetime .= "Z"; my $rest = "from=$from_datetime\&to=$today\&deviceId=$record_cc->{$id}->{int13}"; my $ret_json = fetch_ilockit_cloud("","$endpoint",$rest); my $response_in = {}; $response_in = decode_json($ret_json); print FILE "ilockit get_positions response_in:" . Dumper($response_in); my $theftmove_count = 0; foreach my $resp (sort { $a->{id} <=> $b->{id} } (@{ $response_in })) { print FILE "$resp->{id} shareeTime: $today | serverTime: $resp->{serverTime} | ilockit get_positions movement: $theft_record_detect->{barcode} | $record_cc->{$id}->{int13} eq $resp->{deviceId} && speed:$resp->{speed} && distance:$resp->{attributes}->{distance})\n"; #if($record_cc->{$id}->{int13} eq $resp->{deviceId} && $resp->{speed} > 0 && $resp->{attributes}->{distance} >= 200){ if($record_cc->{$id}->{int13} eq $resp->{deviceId}){ $theftmove_count++;#count if move holds on for min 2 times my $theft_record = get_devicesONcontenttheftpos($dbh,"int02",$resp->{id},"");#Alarm movement print FILE "shareeTime: $today\n"; print FILE "bike: $op_name->{oprefix}$record_cc->{$id}->{barcode}\n"; print FILE "id: $resp->{id}\n"; print FILE "deviceId: $resp->{deviceId}\n"; print FILE "serverTime: $resp->{serverTime}\n\n"; #if(!$theft_record->{c_id} && $theftmove_count >= 2){ if(!$theft_record->{c_id}){ my $serverTime = localizedtime($resp->{serverTime}); my $insert = { table => "contenttheftpos", cc_id => "$record_cc->{$id}->{c_id}", barcode => "$record_cc->{$id}->{barcode}", txt06 => "$resp->{latitude}, $resp->{longitude}", owner => $owner, mtime => "now()", int02 => "$resp->{id}",#keeps id for event_type int03 => "$theftmove_count", int07 => "$resp->{speed}", int08 => "$resp->{attributes}->{distance}", int09 => "$resp->{attributes}->{totalDistance}", int10 => "8",#gps position marker int13 => "$resp->{deviceId}", int20 => "$record_cc->{$id}->{int20}", start_time => "$serverTime", end_time => "$serverTime", }; my $c_id = 0; $c_id = $dbt->insert_contentoid($dbh,$insert); print FILE "insert sub get_positions:" . Dumper($insert); print FILE "send_alarm2hotline condition: syshost:$op_name->{syshost}, c_id:$c_id && $theftmove_count > $dbt->{copri_conf}->{theftalarm}->{move_count} && ($resp->{speed} > $dbt->{copri_conf}->{theftalarm}->{speed} && $resp->{attributes}->{distance} > $dbt->{copri_conf}->{theftalarm}->{meter})\n"; #theft mailing if($c_id && $theftmove_count > $dbt->{copri_conf}->{theftalarm}->{move_count} && ($resp->{speed} > $dbt->{copri_conf}->{theftalarm}->{speed} && $resp->{attributes}->{distance} > $dbt->{copri_conf}->{theftalarm}->{meter})){ #send mail #max ~ 10 mails if($theftmove_count < 10){ system("$dbt->{copri_conf}->{basedir}/$op_name->{syshost}/src/scripts/mailTransportcms.pl '$op_name->{syshost}' 'send_alarm2hotline' '1' '$c_id' ''"); print FILE "$dbt->{copri_conf}->{basedir}/$op_name->{syshost}/src/scripts/mailTransportcms.pl '$op_name->{syshost}' 'send_alarm2hotline' '1' '$c_id' ''\n\n"; if($op_name->{sms_alert}){ system("$dbt->{copri_conf}->{basedir}/$op_name->{syshost}/src/scripts/sms_message.pl '$op_name->{syshost}' 'send_alarm2hotline' '' '$c_id'"); } } } } } } } } } }#end get_positions #ilockit http request sub fetch_ilockit_cloud { my $self = shift; my $ilockitserver = shift || ""; my $rest = shift || ""; my $ilockit_request = "$ilockitserver?$rest"; print FILE "fetch_ >> " . $ilockit_request . "\n"; my $req = HTTP::Request->new(GET => "$ilockit_request"); #$req->content_type('application/x-www-form-urlencoded'); $req->content_type('application/json'); $req->content($rest); my $res = $ua->request($req); if ($res->is_success) { #print $res->content; return $res->content; print $res->status_line, "\n"; }else { print $res->status_line, "\n"; } } close(FILE);