mirror of
https://github.com/fhem/fhem-mirror.git
synced 2024-11-22 09:49:50 +00:00
4d33b0ce2a
git-svn-id: https://svn.fhem.de/fhem/trunk@28119 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2011 lines
83 KiB
Perl
2011 lines
83 KiB
Perl
###############################################################################
|
|
#
|
|
# $Id: 96_RenaultZE.pm 2023-10-15 plin $
|
|
# 96_RenaultZE.pm
|
|
#
|
|
# Forum : https://forum.fhem.de/index.php/topic,116273.0.html
|
|
# Ref https://renault-api.readthedocs.io/en/latest/endpoints.html
|
|
#
|
|
###############################################################################
|
|
#
|
|
# (c) 2017 Copyright: plin
|
|
# All rights reserved
|
|
#
|
|
# This script 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.
|
|
#
|
|
# The GNU General Public License can be found at
|
|
# http://www.gnu.org/copyleft/gpl.html.
|
|
# A copy is found in the textfile GPL.txt and imPORTant notices to the license
|
|
# from the author is found in LICENSE.txt distributed with these scripts.
|
|
#
|
|
# This script 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.
|
|
#
|
|
##################################################################################
|
|
|
|
#######################################################################
|
|
# need:
|
|
# - HttpUtils
|
|
# - Time::Piece
|
|
# - JSON
|
|
#
|
|
########################################################################
|
|
|
|
############################################################################################################################
|
|
# Version History
|
|
# v 1.14 changed code for automatic update of readings distance/home during
|
|
# v 1.13 recalculation of varius readings distance/home during update
|
|
# v 1.12 implemented new Attribute ze_homeRadius
|
|
# v 1.11 implemented new function GET checkAPIkeys
|
|
# v 1.10 addedd attribute brand, 'Dacia' will allow start/stop charge, additional readings after issueing get vehicles
|
|
# v 1.09 fixed problem with readingsBulkUpdate/readingsSingleUpdate in lines 1002ff
|
|
# v 1.08 new KAMERON API key
|
|
# v 1.07 adjusting to new output format from charges
|
|
# v 1.06 logging "well known error" Failed to forward request to remote service only at log level 5
|
|
# v 1.05 fixed timing problem in update request
|
|
# v 1.04 typo denbled corrected
|
|
# v 1.03 hvac settings output corrected
|
|
# v 1.02 some minor corrections
|
|
# v 1.01 added hvac-settings
|
|
# v 1.00 added module to the contrib directory
|
|
# v 0.32 added attribute disabled
|
|
# v 0.31 changed API keys due to change by Renault
|
|
# v 0.30 fixed problem with bulk update
|
|
# v 0.29 fixed problem with from_json
|
|
# v 0.28 fixed timestamp issue
|
|
# v 0.27 added error-Reading in case of malformed json string
|
|
# v 0.26 fixed decode_json issue (additional tests)
|
|
# v 0.25 fixed decode_json issue
|
|
# v 0.24 get link for car image from vehicles listing
|
|
# v 0.23 pretty print ze_lastErr
|
|
# v 0.22 interpret charges data, default time frames for histories
|
|
# v 0.21 implemented further get options implemented for Phase 1 already
|
|
# v 0.20 implemented zTest attribute to test new options
|
|
# v 0.19 fix for time format "2021-01-27T16:41:42+01:00"
|
|
# v 0.18 renamed distance to distanceFromHome
|
|
# v 0.17 added reverse geocoding
|
|
# v 0.16 added distance from home
|
|
# v 0.15 minor fix (warning messages)
|
|
# v 0.14 detect '<html>' in $data (RenaultZE_gData_Step2)
|
|
# v 0.13 fixed timezone problem for UTC timestamps
|
|
# v 0.12 fixed attr problem country/county
|
|
# v 0.11 fixed parameter problem when using timer
|
|
# v 0.10 fixed timer problem
|
|
# v 0.9 changed logic, new readings
|
|
# v 0.8 suppress 0 readings
|
|
# v 0.7 fixed timer problem
|
|
# v 0.6 bug fixes
|
|
# v 0.5 improved feedback and error code checking
|
|
# v 0.4 fix bug when accId = 0
|
|
# v 0.3 adjusted options an placed hint about untested option
|
|
# v 0.2 set commands were added
|
|
# v 0.1 first version with get options
|
|
############################################################################################################################
|
|
# code basis
|
|
# - https://github.com/jamesremuscat/pyze
|
|
# - https://gist.github.com/mountbatt/772e4512089802a2aa2622058dd1ded7
|
|
# API keys
|
|
# - https://renault-wrd-prod-1-euw1-myrapp-one.s3-eu-west-1.amazonaws.com/configuration/android/config_de_DE.json
|
|
# KAMEREON_API -> "wiredProd" -> apikey
|
|
# GIGYA_API -> "gigyaProd" -> apikey
|
|
# oder https://renault-wrd-prod-1-euw1-myrapp-one.s3-eu-west-1.amazonaws.com/configuration/iOS/config_de_DE.json ???
|
|
############################################################################################################################
|
|
|
|
# lock-status
|
|
|
|
package main;
|
|
use strict;
|
|
use warnings;
|
|
|
|
use HttpUtils;
|
|
use Time::Piece;
|
|
#use JSON qw(decode_json);
|
|
use JSON;
|
|
|
|
my $RenaultZE_version ="V1.14 / 1.11.2023";
|
|
|
|
my %RenaultZE_sets = (
|
|
"AC:on,cancel" => "",
|
|
"charge:start,stop" => "",
|
|
"password" => "",
|
|
"state" => ""
|
|
);
|
|
|
|
my %RenaultZE_gets = (
|
|
"charge-history" => "",
|
|
"charges" => "",
|
|
"charging-settings:noArg" => "",
|
|
"hvac-history" => "",
|
|
"hvac-settings:noArg" => "",
|
|
"notification-settings:noArg" => "",
|
|
"update:noArg" => "",
|
|
"vehicles:noArg" => "",
|
|
"checkAPIkeys:noArg" => "",
|
|
"zTest" => ""
|
|
);
|
|
|
|
sub RenaultZE_Initialize($) {
|
|
my ($hash) = @_;
|
|
|
|
$hash->{DefFn} = 'RenaultZE_Define';
|
|
$hash->{UndefFn} = 'RenaultZE_Undef';
|
|
$hash->{SetFn} = 'RenaultZE_Set';
|
|
$hash->{GetFn} = 'RenaultZE_Get';
|
|
$hash->{AttrFn} = 'RenaultZE_Attr';
|
|
$hash->{ReadFn} = 'RenaultZE_Read';
|
|
$hash->{AsyncOutputFn} = 'RenaultZE_AsyncOutput';
|
|
|
|
$hash->{AttrList} = "ze_phase:1,2 ".
|
|
"ze_brand:Renault,Dacia ".
|
|
"ze_user ".
|
|
"ze_country ".
|
|
"ze_latitude ".
|
|
"ze_longitude ".
|
|
"ze_homeRadius ".
|
|
"ze_showaddress:0,1 ".
|
|
"ze_showimage:0,1,2 ".
|
|
"disabled:0,1 ".
|
|
$readingFnAttributes;
|
|
}
|
|
|
|
sub RenaultZE_Define($$) {
|
|
my ($hash, $def) = @_;
|
|
my @param = split('[ \t]+', $def);
|
|
|
|
if(int(@param) < 3) {
|
|
return "too few parameters: define <name> RenaultZE <vin> <interval>";
|
|
}
|
|
|
|
my $name = $param[0];
|
|
$hash->{VIN} = $param[2];
|
|
$hash->{INTERVAL} = $param[3];
|
|
|
|
|
|
$hash->{STATE} = "defined";
|
|
$hash->{GIGYA_API} = '3_7PLksOyBRkHv126x5WhHb-5pqC1qFR8pQjxSeLB6nhAnPERTUlwnYoznHSxwX668';
|
|
#$hash->{KAMEREON_API} = 'Ae9FDWugRxZQAGm3Sxgk7uJn6Q4CGEA2';
|
|
#$hash->{KAMEREON_API} = 'VAX7XYKGfa92yMvXculCkEFyfZbuM7Ss';
|
|
#$hash->{KAMEREON_API} = 'YjkKtHmGfaceeuExUDKGxrLZGGvtVS0J';
|
|
$hash->{KAMEREON_API} = 'YjkKtHmGfaceeuExUDKGxrLZGGvtVS0J';
|
|
#$hash->{KAMEREON_API} = 'oF09WnKqvBDcrQzcW1rJNpjIuy7KdGaB';
|
|
$hash->{VERSION} = $RenaultZE_version;
|
|
|
|
readingsSingleUpdate($hash,"ze_Gigya_JWT_lastCall","0",1) unless (ReadingsVal($name,"ze_Gigya_JWT_lastCall","empty") ne "empty");
|
|
readingsSingleUpdate($hash,"ze_Gigya_JWT_Token","",1) unless (ReadingsVal($name,"ze_Gigya_JWT_Token","empty") ne "empty");
|
|
|
|
$attr{$name}{ze_country} = 'DE' unless (exists($attr{$name}{ze_country}));
|
|
$attr{$name}{ze_showaddress} = '1' unless (exists($attr{$name}{ze_showaddress}));
|
|
$attr{$name}{ze_showimage} = '1' unless (exists($attr{$name}{ze_showimage}));
|
|
|
|
my $firstTrigger = gettimeofday() + 2;
|
|
$hash->{TRIGGERTIME} = $firstTrigger;
|
|
$hash->{TRIGGERTIME_FMT} = FmtDateTime($firstTrigger);
|
|
|
|
RemoveInternalTimer($hash);
|
|
InternalTimer($firstTrigger, "RenaultZE_UpdateTimer", $hash, 0);
|
|
Log3 $hash, 5, "TRAFFIC: ($name) InternalTimer set to call GetUpdate in 2 seconds for the first time";
|
|
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Undef($$) {
|
|
my ($hash, $arg) = @_;
|
|
# nothing to do
|
|
RemoveInternalTimer( $hash );
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Get($@) {
|
|
my ($hash, @param) = @_;
|
|
|
|
my $name = shift @param;
|
|
my $opt = shift @param;
|
|
my $value = join("", @param);
|
|
|
|
if ($opt ne "?")
|
|
{
|
|
$hash->{FUNCTION} = 'GET';
|
|
$hash->{PARMS} = $opt;
|
|
$hash->{PARMVALUE} = $value;
|
|
$hash->{curCL} = $hash->{CL};
|
|
}
|
|
|
|
Log3 $name, 5, "RenaultZE_Get - opt = $opt, value = $value";
|
|
|
|
readingsSingleUpdate($hash,"ze_lastErr","",1) if ($opt ne "?");
|
|
|
|
if ($opt eq "update")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","getStatus",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
elsif ($opt eq "vehicles")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","getVehicles",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
elsif ($opt eq "charge-history")
|
|
{
|
|
if ($value eq "") {
|
|
my $tt = localtime()->strftime('%Y%m%d');
|
|
$value = "type=day&start=20000101&end=".$tt;
|
|
$hash->{PARMVALUE} = $value;
|
|
}
|
|
if ( $value =~ /type=month&start=\d{6}&end=\d{6}/ or $value =~ /type=day&start=\d{8}&end=\d{8}/) {
|
|
readingsSingleUpdate($hash,"ze_Step","getHistory",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
} else {
|
|
return "Syntax error for $opt, correct pattern is 'type=month&start=202012&end=202101' or 'type=day&start=20201212&end=20210120'";
|
|
}
|
|
}
|
|
|
|
elsif ($opt eq "charges")
|
|
{
|
|
if ($value eq "") {
|
|
my $tt = localtime()->strftime('%Y%m%d');
|
|
$value = "start=20000101&end=".$tt;
|
|
$hash->{PARMVALUE} = $value;
|
|
}
|
|
if ( $value =~ /start=\d{8}&end=\d{8}/ ) {
|
|
readingsSingleUpdate($hash,"ze_Step","getCharges",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
} else {
|
|
return "Syntax error for $opt, correct pattern is 'start=20201212&end=20210120'";
|
|
}
|
|
}
|
|
|
|
elsif ($opt eq "hvac-history")
|
|
{
|
|
if ($value eq "") {
|
|
my $tt = localtime()->strftime('%Y%m%d');
|
|
$value = "type=day&start=20000101&end=".$tt;
|
|
$hash->{PARMVALUE} = $value;
|
|
}
|
|
if ( $value =~ /type=month&start=\d{6}&end=\d{6}/ or $value =~ /type=day&start=\d{8}&end=\d{8}/) {
|
|
readingsSingleUpdate($hash,"ze_Step","getHvacHistory",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
} else {
|
|
return "Syntax error for $opt, correct pattern is 'type=month&start=202012&end=202101' or 'type=day&start=20201212&end=20210120'";
|
|
}
|
|
}
|
|
|
|
elsif ($opt eq "hvac-settings")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","getHvacSettings",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
elsif ($opt eq "charging-settings")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","getChargingSettings",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
elsif ($opt eq "notification-settings")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","getNotificationSettings",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
elsif ($opt eq "checkAPIkeys")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","getcheckAPIkeys",1);
|
|
RenaultZE_checkAPIkeys1($hash);
|
|
}
|
|
|
|
elsif ($opt eq "zTest")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","getzTest",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
elsif($opt eq "?") {
|
|
my @cList = keys %RenaultZE_gets;
|
|
return "Unknown argument $opt, choose one of " . join(" ", @cList);
|
|
}
|
|
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Set($@) {
|
|
my ($hash, @param) = @_;
|
|
|
|
my $name = shift @param;
|
|
my $opt = shift @param;
|
|
my $value = join("", @param);
|
|
Log3 $name, 5, "RenaultZE_Set - opt = $opt, value = $value";
|
|
|
|
if ($opt ne "?")
|
|
{
|
|
$hash->{FUNCTION} = 'SET';
|
|
$hash->{PARMS} = $opt;
|
|
$hash->{PARMVALUE} = $value;
|
|
$hash->{curCL} = $hash->{CL};
|
|
}
|
|
|
|
|
|
if($opt eq "?") {
|
|
my @cList = keys %RenaultZE_sets;
|
|
return "Unknown argument $opt, choose one of " . join(" ", @cList);
|
|
}
|
|
|
|
readingsSingleUpdate($hash,"ze_lastErr","",1) if ($opt ne "?");
|
|
|
|
if ($opt eq "AC")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","setAC",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
elsif ($opt eq "charge")
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","setCharge",1);
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
elsif ($opt eq "password" && $value ne "")
|
|
{
|
|
return RenaultZE_storePassword($name,$value);
|
|
}
|
|
|
|
elsif ($opt eq "state")
|
|
{
|
|
$hash->{STATE} = $value;
|
|
}
|
|
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_AsyncOutput ($$)
|
|
{
|
|
my ( $client_hash, $text ) = @_;
|
|
|
|
return $text;
|
|
}
|
|
|
|
sub RenaultZE_UpdateTimer($) {
|
|
my ( $hash ) = @_;
|
|
my $name = $hash->{NAME};
|
|
|
|
if(AttrVal($name, "disabled", 0 ) == 1){
|
|
RemoveInternalTimer ($hash);
|
|
Log3 $hash, 3, "RenaultZE ($name) is disabled";
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE ($name) is disabled",1);
|
|
return undef;
|
|
}
|
|
|
|
if ( $hash->{INTERVAL}) {
|
|
RemoveInternalTimer ($hash);
|
|
delete($hash->{UPDATESCHEDULE});
|
|
|
|
my $nextTrigger = gettimeofday() + $hash->{INTERVAL};
|
|
$hash->{TRIGGERTIME} = $nextTrigger;
|
|
$hash->{TRIGGERTIME_FMT} = FmtDateTime($nextTrigger);
|
|
InternalTimer($nextTrigger, "RenaultZE_UpdateTimer", $hash, 0);
|
|
Log3 $hash, 4, "RenaultZE ($name) internal interval timer set to call StartUpdate again at " . $hash->{TRIGGERTIME_FMT};
|
|
|
|
readingsSingleUpdate($hash,"ze_Step","getStatus",1);
|
|
$hash->{PARMS} = "update";
|
|
$hash->{FUNCTION} = 'GET';
|
|
$hash->{PARMS} = 'update';
|
|
my @param = ('GET', 'update');
|
|
RenaultZE_Main1($hash, @param);
|
|
}
|
|
|
|
}
|
|
|
|
sub RenaultZE_Main1($@) {
|
|
my ($hash, @param) = @_;
|
|
|
|
#my $name = shift @param;
|
|
#my $opt = shift @param;
|
|
|
|
my $function = $hash->{FUNCTION};
|
|
my $opt = $hash->{PARMS};
|
|
my $value = $hash->{PARMVALUE};
|
|
my $key = $function."_".$opt;
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_Main1 - In, key=".$key;
|
|
|
|
#if ($key eq "GET_update" || $key eq "GET_vehicles" || $key eq "GET_ac-state" || $key eq "SET_AC" || $key eq "SET_charge")
|
|
#{
|
|
readingsSingleUpdate($hash,"ze_Step","Main1",1);
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
readingsSingleUpdate($hash,"ze_Gigya_JWT_Token","",1) if ($lastErr ne "");
|
|
my $ze_Gigya_JWT_Token = $hash->{READINGS}{ze_Gigya_JWT_Token}{VAL};
|
|
my $ze_Gigya_JWT_lastCall = $hash->{READINGS}{ze_Gigya_JWT_lastCall}{TIME};
|
|
my $res = 0;
|
|
|
|
Log3 $name, 5, "RenaultZE_Main1 - ze_Gigya_JWT_lastCall=".$ze_Gigya_JWT_lastCall;
|
|
my $gigya_time = Time::Piece->strptime( $ze_Gigya_JWT_lastCall, '%Y-%m-%d %H:%M:%S')->epoch;
|
|
Log3 $name, 5, "RenaultZE_Main1 - ze_Gigya_JWT_lastCall=".$gigya_time;
|
|
Log3 $name, 5, "RenaultZE_Main1 - gettimeofday=".gettimeofday();
|
|
|
|
if ( $ze_Gigya_JWT_Token eq "" || $gigya_time < gettimeofday() - 70000 ) {
|
|
my $res = RenaultZE_getCreds_Step1($hash);
|
|
Log3 $name, 5, "RenaultZE_Main1 - RC=".$res if defined($res);
|
|
}
|
|
else
|
|
{
|
|
Log3 $name, 5, "RenaultZE_Main1 - ze_Gigya_JWT_Token=>".$ze_Gigya_JWT_Token."<";
|
|
}
|
|
RenaultZE_Main2($hash);
|
|
return undef;
|
|
#}
|
|
|
|
Log3 $name, 5, "RenaultZE_Main1 - Out";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Main2($) {
|
|
my ($hash) = @_;
|
|
|
|
my $function = $hash->{FUNCTION};
|
|
my $opt = $hash->{PARMS};
|
|
my $value = $hash->{PARMVALUE};
|
|
my $key = $function."_".$opt;
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_Main2 - In, key=".$key;
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
#if ($key eq "GET_update" || $key eq "GET_vehicles" || $key eq "GET_ac-state" || $key eq "SET_AC" || $key eq "SET_charge")
|
|
#{
|
|
readingsSingleUpdate($hash,"ze_Step","Main2",1);
|
|
my $ze_Renault_AccId = $hash->{READINGS}{ze_Renault_AccId}{VAL};
|
|
my $res = 0;
|
|
|
|
Log3 $name, 5, "RenaultZE_Main2 - ze_Renault_AccId: ".$ze_Renault_AccId;
|
|
if ( $ze_Renault_AccId eq "" || $ze_Renault_AccId eq "0" ){
|
|
$res = RenaultZE_getAccId_Step1($hash);
|
|
Log3 $name, 5, "RenaultZE_getAccId_Step1 - RC=".$res;
|
|
}
|
|
RenaultZE_Main3($hash);
|
|
return undef;
|
|
#}
|
|
|
|
Log3 $name, 5, "RenaultZE_Main2 - Out";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Main3($) {
|
|
my ($hash) = @_;
|
|
|
|
my $function = $hash->{FUNCTION};
|
|
my $opt = $hash->{PARMS};
|
|
my $value = $hash->{PARMVALUE};
|
|
my $key = $function."_".$opt;
|
|
my $name = $hash->{NAME};
|
|
|
|
readingsSingleUpdate($hash,"ze_Step","Main3",1);
|
|
Log3 $name, 5, "RenaultZE_Main3 - In, key=".$key;
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
my $phase = AttrVal($name,"ze_phase","");
|
|
|
|
if ($key eq "GET_update")
|
|
{
|
|
#my $res = RenaultZE_getData_Step1($hash);
|
|
my $res = RenaultZE_gData_Step1($hash,'battery-status');
|
|
my $model = $hash->{READINGS}{vehicleDetails_model_label}{VAL};
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - battery-status - RC=".$res;
|
|
InternalTimer( gettimeofday() + 1, sub() { my $a = 1;
|
|
$res = RenaultZE_gData_Step1($hash,'cockpit');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - cockpit - RC=".$res;
|
|
}, undef);
|
|
InternalTimer( gettimeofday() + 2, sub() { my $a = 1;
|
|
$res = RenaultZE_gData_Step1($hash,'location') if ($phase eq "2");
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - location - RC=".$res if ($phase eq "2");
|
|
}, undef);
|
|
InternalTimer( gettimeofday() + 3, sub() { my $a = 1;
|
|
$res = RenaultZE_gData_Step1($hash,'hvac-status') if ($phase eq "1");
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - hvac-status - RC=".$res if ($phase eq "1");
|
|
}, undef);
|
|
InternalTimer( gettimeofday() + 4, sub() { my $a = 1;
|
|
$res = RenaultZE_gData_Step1($hash,'charge-mode') if ($model ne "SPRING");
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - charge-mode - RC=".$res if ($model ne "SPRING");
|
|
}, undef);
|
|
}
|
|
|
|
if ($key eq "GET_vehicles")
|
|
{
|
|
my $res = RenaultZE_gData_Step1($hash,'vehicles');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - vehicles - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "GET_charge-history")
|
|
{
|
|
my $res = RenaultZE_gData_Step1($hash,'charge-history');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - charge-history - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "GET_charges")
|
|
{
|
|
my $res = RenaultZE_gData_Step1($hash,'charges');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - charges - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "GET_charging-settings")
|
|
{
|
|
my $res = RenaultZE_gData_Step1($hash,'charging-settings');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - charging-settings - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "GET_hvac-history")
|
|
{
|
|
my $res = RenaultZE_gData_Step1($hash,'hvac-history');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - hvac-history - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "GET_hvac-settings")
|
|
{
|
|
my $res = RenaultZE_gData_Step1($hash,'hvac-settings');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - hvac-settings - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "GET_notification-settings")
|
|
{
|
|
my $res = RenaultZE_gData_Step1($hash,'notification-settings');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - notification-settings - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "GET_zTest")
|
|
{
|
|
my $res = RenaultZE_gData_Step1($hash,'zTest');
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - zTest - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "SET_AC")
|
|
{
|
|
my $res = RenaultZE_AC_Step1($hash);
|
|
Log3 $name, 5, "RenaultZE_AC_Step1 - RC=".$res;
|
|
}
|
|
|
|
if ($key eq "SET_charge")
|
|
{
|
|
my $res = RenaultZE_Charge_Step1($hash);
|
|
Log3 $name, 5, "RenaultZE_Charge_Step1 - RC=".$res;
|
|
}
|
|
|
|
Log3 $name, 5, "RenaultZE_Main3 - Out";
|
|
}
|
|
|
|
sub RenaultZE_Main4($) {
|
|
my ($hash) = @_;
|
|
my $name = $hash->{NAME};
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
readingsSingleUpdate($hash,"ze_Step","done",1);
|
|
$hash->{STATE} = "updated";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Attr(@) {
|
|
my ($cmd,$name,$attrName,$attrVal) = @_;
|
|
my $hash = $defs{$name};
|
|
if($cmd eq "set") {
|
|
if (substr($attrName ,0,3) eq "ze_")
|
|
{
|
|
$_[3] = $attrVal;
|
|
$hash->{".reset"} = 1 if defined($hash->{LPID});
|
|
}
|
|
if (($attrName eq "disabled") && ($attrVal == 1))
|
|
{
|
|
readingsSingleUpdate($hash,"state","disabled",1);
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE ($name) is disabled",1);
|
|
$_[3] = $attrVal;
|
|
$hash->{".reset"} = 1 if defined($hash->{LPID});
|
|
RemoveInternalTimer ($hash);
|
|
}
|
|
elsif (($attrName eq "disabled") && ($attrVal == 0))
|
|
{
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE ($name) is enabled",1);
|
|
$_[3] = $attrVal;
|
|
$hash->{".reset"} = 1 if defined($hash->{LPID});
|
|
my $firstTrigger = gettimeofday() + 2;
|
|
$hash->{TRIGGERTIME} = $firstTrigger;
|
|
$hash->{TRIGGERTIME_FMT} = FmtDateTime($firstTrigger);
|
|
InternalTimer($firstTrigger, "RenaultZE_UpdateTimer", $hash, 0);
|
|
}
|
|
}
|
|
elsif ($cmd eq "del")
|
|
{
|
|
if (substr($attrName,0,3) eq "ze_")
|
|
{
|
|
$_[3] = $attrVal;
|
|
$hash->{".reset"} = 1 if defined($hash->{LPID});
|
|
}
|
|
elsif (($attrName eq "disabled") )
|
|
{
|
|
readingsSingleUpdate($hash,"state","enabled",1);
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE ($name) is enabled",1);
|
|
$_[3] = $attrVal;
|
|
$hash->{".reset"} = 1 if defined($hash->{LPID});
|
|
my $firstTrigger = gettimeofday() + 2;
|
|
$hash->{TRIGGERTIME} = $firstTrigger;
|
|
$hash->{TRIGGERTIME_FMT} = FmtDateTime($firstTrigger);
|
|
InternalTimer($firstTrigger, "RenaultZE_UpdateTimer", $hash, 0);
|
|
}
|
|
}
|
|
if ($attrName eq "ze_homeRadius")
|
|
{
|
|
my $gpsLatitude = ReadingsVal($name,"gpsLatitude","empty");
|
|
my $gpsLongitude = ReadingsVal($name,"gpsLongitude","empty");
|
|
my $homeRadius = 20; # defule radius
|
|
$homeRadius = $attrVal if ( $cmd eq "set" );
|
|
$homeRadius = 20 if ( $homeRadius eq "" ); # just in case
|
|
#Log3 $name, 5, "pre RenaultZE_distanceFromHome - In ".$cmd."/".$gpsLatitude." ".$gpsLongitude."/".$homeRadius;
|
|
RenaultZE_distanceFromHome($hash,$gpsLatitude,$gpsLongitude,$homeRadius);
|
|
}
|
|
|
|
return undef;
|
|
}
|
|
|
|
######################################################
|
|
# storePW & readPW Code geklaut aus 72_FRITZBOX.pm :)
|
|
######################################################
|
|
sub RenaultZE_storePassword($$)
|
|
{
|
|
my ($name, $password) = @_;
|
|
my $index = "ZE_".$name."_passwd";
|
|
my $key = getUniqueId().$index;
|
|
my $e_pwd = "";
|
|
|
|
if (eval "use Digest::MD5;1")
|
|
{
|
|
$key = Digest::MD5::md5_hex(unpack "H*", $key);
|
|
$key .= Digest::MD5::md5_hex($key);
|
|
}
|
|
|
|
for my $char (split //, $password)
|
|
{
|
|
my $encode=chop($key);
|
|
$e_pwd.=sprintf("%.2x",ord($char)^ord($encode));
|
|
$key=$encode.$key;
|
|
}
|
|
|
|
my $error = setKeyValue($index, $e_pwd);
|
|
return "error while saving ZE user password : $error" if(defined($error));
|
|
return "ZE user password successfully saved in FhemUtils/uniqueID Key $index";
|
|
}
|
|
|
|
sub RenaultZE_readPassword($)
|
|
{
|
|
my ($name) = @_;
|
|
my $index = "ZE_".$name."_passwd";
|
|
my $key = getUniqueId().$index;
|
|
|
|
my ($password, $error);
|
|
|
|
#Log3 $name,5,"$name, read ZE user password from FhemUtils/uniqueID Key $key";
|
|
($error, $password) = getKeyValue($index);
|
|
|
|
if ( defined($error) )
|
|
{
|
|
Log3 $name,5, "$name, cant't read ZE user password from FhemUtils/uniqueID: $error";
|
|
return undef;
|
|
}
|
|
|
|
if ( defined($password) )
|
|
{
|
|
if (eval "use Digest::MD5;1")
|
|
{
|
|
$key = Digest::MD5::md5_hex(unpack "H*", $key);
|
|
$key .= Digest::MD5::md5_hex($key);
|
|
}
|
|
|
|
my $dec_pwd = '';
|
|
|
|
for my $char (map { pack('C', hex($_)) } ($password =~ /(..)/g))
|
|
{
|
|
my $decode=chop($key);
|
|
$dec_pwd.=chr(ord($char)^ord($decode));
|
|
$key=$decode.$key;
|
|
}
|
|
return $dec_pwd;
|
|
}
|
|
else
|
|
{
|
|
Log3 $name,3,"$name, no ZE user password found in FhemUtils/uniqueID";
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
####### getStatus Dialog #####
|
|
#
|
|
sub RenaultZE_getCreds_Step1($)
|
|
{
|
|
my ($hash) = @_;
|
|
my $name = $hash->{NAME};
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step1 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_getCreds_Step1",1);
|
|
my $gigya_api = $hash->{GIGYA_API};
|
|
my $username = AttrVal($name,"ze_user","");
|
|
my $password = RenaultZE_readPassword($name);
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step1 - Parms: ".$gigya_api."/".$username."/".$password;
|
|
|
|
my $step1= {
|
|
ApiKey => $gigya_api,
|
|
loginId => $username,
|
|
password => $password,
|
|
include => 'data',
|
|
sessionExpiration => 60
|
|
};
|
|
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step1 - Data".$step1;
|
|
my $param = {
|
|
url => "https://accounts.eu1.gigya.com/accounts.login",
|
|
header => "Content-type: application/x-www-form-urlencoded",
|
|
hash => $hash,
|
|
timeout => 15,
|
|
method => "POST",
|
|
data => $step1,
|
|
callback => \&RenaultZE_getCreds_Step2
|
|
};
|
|
|
|
HttpUtils_NonblockingGet($param); # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step1 - Out";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_getCreds_Step2($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step2 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_getCreds_Step2",1);
|
|
|
|
RenaultZE_Error_err($hash,"RenaultZE_getCreds_Step2",$param->{url},$err,$data) if($err ne "");
|
|
RenaultZE_Log_Data($hash,"RenaultZE_getCreds_Step2",$param->{url},$err,$data) if($data ne "");
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
my $errorCode = $decode_json->{errorCode};
|
|
RenaultZE_Error_errorCode1($hash,"RenaultZE_getCreds_Step2",$param->{url},$err,$data) if($errorCode ne 0);
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
$decode_json = from_json($data);
|
|
my $ze_personId = $decode_json->{data}->{personId};
|
|
my $oauth_token = $decode_json->{sessionInfo}->{cookieValue};
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step2 - ze_personId:".$ze_personId.", ze_cookieValue:".$oauth_token;
|
|
readingsBeginUpdate($hash);
|
|
readingsBulkUpdate($hash,"ze_personId",$ze_personId);
|
|
readingsBulkUpdate($hash,"ze_cookieValue",$oauth_token);
|
|
readingsEndUpdate($hash, 1 );
|
|
|
|
my $gigya_api = $hash->{GIGYA_API};
|
|
my $step2= {
|
|
login_token => $oauth_token,
|
|
ApiKey => $gigya_api,
|
|
fields => 'data.personId,data.gigyaDataCenter',
|
|
expiration => 87000
|
|
};
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step2 - Data".$step2;
|
|
my $param2 = {
|
|
url => "https://accounts.eu1.gigya.com/accounts.getJWT",
|
|
header => "Content-type: application/x-www-form-urlencoded",
|
|
hash => $hash,
|
|
timeout => 15,
|
|
method => "POST",
|
|
data => $step2,
|
|
callback => \&RenaultZE_getCreds_Step3
|
|
};
|
|
|
|
HttpUtils_NonblockingGet($param2); # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
|
|
#my $res = RenaultZE_getStatusPerformHttpRequest2($i, $e, $o, $a);
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step2 - Out";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_getCreds_Step3($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step3 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_getCreds_Step3",1);
|
|
|
|
RenaultZE_Error_err($hash,"RenaultZE_getCreds_Step3",$param->{url},$err,$data) if($err ne "");
|
|
RenaultZE_Log_Data($hash,"RenaultZE_getCreds_Step3",$param->{url},$err,$data) if($data ne "");
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
my $errorCode = $decode_json->{errorCode};
|
|
RenaultZE_Error_errorCode1($hash,"RenaultZE_getCreds_Step3",$param->{url},$err,$data) if($errorCode ne 0);
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
$decode_json = from_json($data);
|
|
my $id_token = $decode_json->{id_token};
|
|
readingsBeginUpdate($hash);
|
|
readingsBulkUpdate($hash,"ze_Gigya_JWT_Token",$id_token);
|
|
readingsBulkUpdate($hash,"ze_Gigya_JWT_lastCall",localtime(time));
|
|
readingsEndUpdate($hash, 1 );
|
|
|
|
RenaultZE_Main2($hash);
|
|
|
|
#my $res = RenaultZE_getStatusPerformHttpRequest2($i, $e, $o, $a);
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step3 - Out";
|
|
}
|
|
|
|
sub RenaultZE_getAccId_Step1($)
|
|
{
|
|
my ($hash) = @_;
|
|
my $name = $hash->{NAME};
|
|
Log3 $name, 5, "RenaultZE_getAccId_Step1 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_getAccId_Step1",1);
|
|
my $kamereon_api = $hash->{KAMEREON_API};
|
|
my $id_token = $hash->{READINGS}{ze_Gigya_JWT_Token}{VAL};
|
|
my $ze_personId = $hash->{READINGS}{ze_personId}{VAL};
|
|
my $country = AttrVal($name,"ze_country","DE");
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step1 - Parms: ".$kamereon_api."/".$id_token;
|
|
|
|
return undef if ( $id_token eq "" || $ze_personId eq "" );
|
|
|
|
my $step1= {
|
|
'ApiKey' => $kamereon_api,
|
|
'x-gigya-id_token' => $id_token
|
|
};
|
|
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step1 - Data".$step1;
|
|
my $url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/persons/".$ze_personId."?country=".$country;
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step1 - URL ".$url;
|
|
my $param = {
|
|
url => $url,
|
|
header => $step1,
|
|
hash => $hash,
|
|
timeout => 15,
|
|
method => "GET",
|
|
callback => \&RenaultZE_getAccId_Step2
|
|
};
|
|
|
|
HttpUtils_NonblockingGet($param);
|
|
Log3 $name, 5, "RenaultZE_getAccId_Step1 - Out";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_getAccId_Step2($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_getAccId_Step2 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_getAccId_Step2",1);
|
|
|
|
RenaultZE_Error_err($hash,"RenaultZE_getAccId_Step2",$param->{url},$err,$data) if($err ne "");
|
|
RenaultZE_Log_Data($hash,"RenaultZE_getAccId_Step2",$param->{url},$err,$data) if($data ne "");
|
|
RenaultZE_Error_errorCode2($hash,"RenaultZE_getAccId_Step2",$param->{url},$err,$data) if($data =~ /error/);
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
my $accountId = $decode_json->{accounts}[0]->{accountId};
|
|
Log3 $name, 5, "RenaultZE_getCreds_Step2 - accountId:".$accountId;
|
|
readingsSingleUpdate($hash,"ze_Renault_AccId",$accountId,1);
|
|
|
|
RenaultZE_Main3($hash);
|
|
|
|
Log3 $name, 5, "RenaultZE_getCAccId_Step2 - Out";
|
|
}
|
|
|
|
sub RenaultZE_gData_Step1($$)
|
|
{
|
|
my ($hash,$tree) = @_;
|
|
my $name = $hash->{NAME};
|
|
|
|
my $v1v2 = "v1";
|
|
$v1v2 = "v2" if ( $tree eq "battery-status");
|
|
my $shortlong = "long";
|
|
$shortlong = "short" if ( $tree eq "vehicles");
|
|
my $popup = "no";
|
|
$popup = "yes" if ( $tree eq "vehicles");
|
|
$popup = "yes" if ( $tree eq "charge-mode");
|
|
my $testparms = $hash->{PARMVALUE};
|
|
my $timespecs = "";
|
|
if ( $tree eq "charge-history" or $tree eq "hvac-history" or $tree eq "charges") {
|
|
$timespecs = "&".$testparms;
|
|
}
|
|
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - In ".$hash."/".$tree."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_gData_Step1",1);
|
|
my $kamereon_api = $hash->{KAMEREON_API};
|
|
my $id_token = $hash->{READINGS}{ze_Gigya_JWT_Token}{VAL};
|
|
my $accId = $hash->{READINGS}{ze_Renault_AccId}{VAL};
|
|
my $vin = $hash->{VIN};
|
|
my $country = AttrVal($name,"ze_country","DE");
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - Parms: ".$kamereon_api."/".$id_token;
|
|
|
|
return 4 if ( $id_token eq "" || $accId eq "" );
|
|
my $header= {
|
|
'apikey' => $kamereon_api,
|
|
'x-gigya-id_token' => $id_token
|
|
};
|
|
|
|
Log3 $name, 5, "RenaultZE_getData_Step1 - Data".$header;
|
|
my $url = "";
|
|
if ( $shortlong eq "long") {
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/".$v1v2."/cars/".$vin."/".$tree."?country=".$country.$timespecs;
|
|
}else {
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/".$tree."?country=".$country;
|
|
}
|
|
|
|
# for development of new options and users of a phase 1 Zoe
|
|
if ( $tree eq "zTest") {
|
|
my $testparms = $hash->{PARMVALUE};
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/".$v1v2."/cars/".$vin."/".$testparms;
|
|
}
|
|
# In Vorbereit8ung, aber derzeit noch nicht seitens Renault unterstützt:
|
|
#$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/".$v1v2."/cars/".$vin."/hvac-history?type=day&start=20201101&end=20210108&country=".$country;
|
|
#$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/".$v1v2."/cars/".$vin."/hvac-sessions?start=20201101&end=20210108&country=".$country;
|
|
#$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/".$v1v2."/cars/".$vin."/charges?start=20201101&end=20210108&country=".$country;
|
|
#$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/".$v1v2."/cars/".$vin."/charge-history?type=day&start=20201101&end=20210108&country=".$country;
|
|
#$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/".$v1v2."/cars/".$vin."/charge-history?type=month&start=202011&end=202101&country=".$country;
|
|
#$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/".$v1v2."/cars/".$vin."/lock-status?country=".$country;
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - URL ".$url;
|
|
my $param = {
|
|
url => $url,
|
|
header => $header,
|
|
hash => $hash,
|
|
timeout => 15,
|
|
method => "GET",
|
|
callback => \&RenaultZE_gData_Step2
|
|
};
|
|
|
|
HttpUtils_NonblockingGet($param); # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - Out";
|
|
return 0;
|
|
}
|
|
|
|
sub RenaultZE_gData_Step2($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_gData_Step2 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_gData_Step2",1);
|
|
|
|
RenaultZE_Error_err($hash,"RenaultZE_gData_Step2",$param->{url},$err,$data) if($err ne "");
|
|
RenaultZE_Log_Data($hash,"RenaultZE_gData_Step2",$param->{url},$err,$data) if($data ne "");
|
|
RenaultZE_Error_errorCode2($hash,"RenaultZE_gData_Step2",$param->{url},$err,$data) if($data =~ /errorMessage/);
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
my $lastUrl = $hash->{READINGS}{ze_lastUrl}{VAL};
|
|
|
|
Log3 $name, 3, "RenaultZE_gData_Step2 - DataError ".$data if ($data =~ /\<html\>/);
|
|
return undef if ($data =~ /\<html\>/);
|
|
|
|
my $phase = AttrVal($name,"ze_phase","");
|
|
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
|
|
if($data =~ /batteryLevel/) {
|
|
my $timestamp = $decode_json->{data}->{attributes}->{timestamp};
|
|
#$timestamp =~ s/\+01:00/Z/sg; # fix for time format "2021-01-27T16:41:42+01:00"
|
|
#my $t = Time::Piece->strptime($timestamp, "%Y-%m-%dT%H:%M:%SZ")->epoch;
|
|
my $t = RenaultZE_EpochFromDateTime($timestamp);
|
|
my $tt = localtime($t)->strftime('%Y-%m-%d %H:%M:%S');
|
|
readingsBeginUpdate($hash);
|
|
readingsBulkUpdate($hash,"timestamp",$tt);
|
|
readingsBulkUpdate($hash,"batteryLevel",$decode_json->{data}->{attributes}->{batteryLevel});
|
|
readingsBulkUpdate($hash,"batteryTemperature",$decode_json->{data}->{attributes}->{batteryTemperature}) if ($phase eq "1");
|
|
readingsBulkUpdate($hash,"batteryAutonomy",$decode_json->{data}->{attributes}->{batteryAutonomy});
|
|
readingsBulkUpdate($hash,"batteryCapacity",$decode_json->{data}->{attributes}->{batteryCapacity}) if ($decode_json->{data}->{attributes}->{batteryCapacity} gt 0);
|
|
readingsBulkUpdate($hash,"batteryAvailableEnergy",$decode_json->{data}->{attributes}->{batteryAvailableEnergy}) if ($decode_json->{data}->{attributes}->{batteryAvailableEnergy} gt 0);
|
|
readingsBulkUpdate($hash,"plugStatus",$decode_json->{data}->{attributes}->{plugStatus});
|
|
readingsBulkUpdate($hash,"chargingStatus",$decode_json->{data}->{attributes}->{chargingStatus});
|
|
readingsBulkUpdate($hash,"chargingRemainingTime",$decode_json->{data}->{attributes}->{chargingRemainingTime});
|
|
readingsBulkUpdate($hash,"chargingInstantaneousPower",$decode_json->{data}->{attributes}->{chargingInstantaneousPower});
|
|
readingsEndUpdate($hash, 1 );
|
|
return 0;
|
|
}
|
|
|
|
### cockpit ###
|
|
if($data =~ /totalMileage/) {
|
|
readingsBeginUpdate($hash);
|
|
readingsBulkUpdate($hash,"totalMileageKm",$decode_json->{data}->{attributes}->{totalMileage});
|
|
readingsBulkUpdate($hash,"fuelAutonomy",$decode_json->{data}->{attributes}->{fuelAutonomy}) if ($decode_json->{data}->{attributes}->{fuelAutonomy} gt 0);
|
|
readingsBulkUpdate($hash,"fuelQuantity",$decode_json->{data}->{attributes}->{fuelQuantity}) if ($decode_json->{data}->{attributes}->{fuelQuantity} gt 0);
|
|
readingsEndUpdate($hash, 1 );
|
|
return 0;
|
|
}
|
|
|
|
### hvac-status ###
|
|
if($data =~ /hvacStatus/) {
|
|
readingsBeginUpdate($hash);
|
|
readingsBulkUpdate($hash,"hvacStatus",$decode_json->{data}->{attributes}->{hvacStatus});
|
|
readingsBulkUpdate($hash,"socThreshold",$decode_json->{data}->{attributes}->{socThreshold}) if ($decode_json->{data}->{attributes}->{socThreshold} gt 0);
|
|
readingsBulkUpdate($hash,"xternalTemperature",$decode_json->{data}->{attributes}->{xternalTemperature}) if ($decode_json->{data}->{attributes}->{xternalTemperature} gt 0);
|
|
readingsEndUpdate($hash, 1 );
|
|
return 0;
|
|
}
|
|
|
|
my $gpsLatitude = "";
|
|
my $gpsLongitude = "";
|
|
my $lastUpdateTime = "";
|
|
if($data =~ /gpsLatitude/) {
|
|
if ($data =~ /locationStatus/) {
|
|
$gpsLatitude = $decode_json->{locationStatus}->{attributes}->{gpsLatitude};
|
|
$gpsLongitude = $decode_json->{locationStatus}->{attributes}->{gpsLongitude};
|
|
$lastUpdateTime = $decode_json->{locationStatus}->{attributes}->{lastUpdateTime};
|
|
} else {
|
|
$gpsLatitude = $decode_json->{data}->{attributes}->{gpsLatitude};
|
|
$gpsLongitude = $decode_json->{data}->{attributes}->{gpsLongitude};
|
|
$lastUpdateTime = $decode_json->{data}->{attributes}->{lastUpdateTime};
|
|
}
|
|
my $gpsLatitude = $decode_json->{data}->{attributes}->{gpsLatitude};
|
|
my $gpsLongitude = $decode_json->{data}->{attributes}->{gpsLongitude};
|
|
my $lastUpdateTime = $decode_json->{data}->{attributes}->{lastUpdateTime};
|
|
#$lastUpdateTime =~ s/\+01:00/Z/sg; # fix for time format "2021-01-27T16:41:42+01:00"
|
|
#my $t = Time::Piece->strptime($lastUpdateTime, "%Y-%m-%dT%H:%M:%SZ")->epoch;
|
|
my $t = RenaultZE_EpochFromDateTime($lastUpdateTime);
|
|
my $tt = localtime($t)->strftime('%Y-%m-%d %H:%M:%S');
|
|
my $oldlat = ReadingsVal($name,"gpsLatitude","empty");
|
|
my $oldlong = ReadingsVal($name,"gpsLongitude","empty");
|
|
my $link = "<html><a href=\"https://www.google.com/maps/place/".$gpsLatitude.",".$gpsLongitude."\" target=\”_blank\”>Google Maps</a></html>";
|
|
if ( $oldlat != $gpsLatitude or $oldlong != $gpsLongitude ) {
|
|
Log3 $name, 5, "RenaultZE_gData_Step2 - GPS ".$oldlat."/".$gpsLatitude." ".$oldlong."/".$gpsLongitude;
|
|
RenaultZE_distanceFromHome($hash,$gpsLatitude,$gpsLongitude,"auto");
|
|
}
|
|
readingsBeginUpdate($hash);
|
|
readingsBulkUpdate($hash,"gpsLatitude",$gpsLatitude);
|
|
readingsBulkUpdate($hash,"gpsLongitude",$gpsLongitude);
|
|
readingsBulkUpdate($hash,"gpsLastUpdateTime",$tt);
|
|
readingsBulkUpdate($hash,"gpsGoogleMaps",$link);
|
|
readingsEndUpdate($hash, 1 );
|
|
return 0;
|
|
}
|
|
|
|
if($data =~ /vehicleLink/) {
|
|
my $decode_json = from_json($data);
|
|
my $output = JSON->new->ascii->pretty->encode(decode_json join '', $data);
|
|
|
|
# extract image urls
|
|
my $mtab = $decode_json->{vehicleLinks};
|
|
foreach my $item( @$mtab ) {
|
|
next if ($item->{vin} ne $hash->{VIN});
|
|
my $assets = $item->{vehicleDetails}->{assets};
|
|
foreach my $ass( @$assets ) {
|
|
my $cars = $ass->{renditions};
|
|
foreach my $car( @$cars ) {
|
|
my $url = $car->{url};
|
|
my $size = $car->{resolutionType};
|
|
my $link = "<html><img src=\"".$url."\"></html>";
|
|
readingsSingleUpdate($hash,"img_".$size."_url",$url,1);
|
|
readingsSingleUpdate($hash,"img_".$size."_img",$link,1) if (AttrVal($name,"ze_showimage","1") gt 0 and $size =~ /SMALL/ );
|
|
readingsSingleUpdate($hash,"img_".$size."_img",$link,1) if (AttrVal($name,"ze_showimage","1") eq 2 and $size =~ /LARGE/ );
|
|
}
|
|
}
|
|
my $detail = $item->{vehicleDetails}->{family}->{code};
|
|
readingsSingleUpdate($hash,"vehicleDetails_family_code",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{family}->{label};
|
|
readingsSingleUpdate($hash,"vehicleDetails_family_label",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{family}->{group};
|
|
readingsSingleUpdate($hash,"vehicleDetails_family_group",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{engineEnergyType};
|
|
readingsSingleUpdate($hash,"vehicleDetails_engineEnergyType",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{navigationAssistanceLevel}->{code};
|
|
readingsSingleUpdate($hash,"vehicleDetails_navigationAssistanceLevel_code",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{navigationAssistanceLevel}->{label};
|
|
readingsSingleUpdate($hash,"vehicleDetails_navigationAssistanceLevel_label",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{navigationAssistanceLevel}->{group};
|
|
readingsSingleUpdate($hash,"vehicleDetails_navigationAssistanceLevel_group",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{version}->{code};
|
|
readingsSingleUpdate($hash,"vehicleDetails_version_code",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{gearbox}->{code};
|
|
readingsSingleUpdate($hash,"vehicleDetails_gearbox_code",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{gearbox}->{label};
|
|
readingsSingleUpdate($hash,"vehicleDetails_gearbox_label",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{gearbox}->{group};
|
|
readingsSingleUpdate($hash,"vehicleDetails_gearbox_group",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{radioType}->{code};
|
|
readingsSingleUpdate($hash,"vehicleDetails_radioType_code",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{radioType}->{label};
|
|
readingsSingleUpdate($hash,"vehicleDetails_radioType_label",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{radioType}->{group};
|
|
readingsSingleUpdate($hash,"vehicleDetails_radioType_group",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{tcu}->{code};
|
|
readingsSingleUpdate($hash,"vehicleDetails_tcu_code",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{tcu}->{label};
|
|
readingsSingleUpdate($hash,"vehicleDetails_tcu_label",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{tcu}->{group};
|
|
readingsSingleUpdate($hash,"vehicleDetails_tcu_group",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{model}->{code};
|
|
readingsSingleUpdate($hash,"vehicleDetails_model_code",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{model}->{label};
|
|
readingsSingleUpdate($hash,"vehicleDetails_model_label",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{model}->{group};
|
|
readingsSingleUpdate($hash,"vehicleDetails_model_group",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{engineType};
|
|
readingsSingleUpdate($hash,"vehicleDetails_engineType",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{modelSCR};
|
|
readingsSingleUpdate($hash,"vehicleDetails_modelSCR",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{battery}->{code};
|
|
readingsSingleUpdate($hash,"vehicleDetails_battery_code",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{battery}->{label};
|
|
readingsSingleUpdate($hash,"vehicleDetails_battery_label",$detail,1);
|
|
$detail = $item->{vehicleDetails}->{battery}->{group};
|
|
readingsSingleUpdate($hash,"vehicleDetails_battery_group",$detail,1);
|
|
#
|
|
$detail = $item->{vehicleDetails}->{brand}->{label};
|
|
readingsSingleUpdate($hash,"vehicleDetails_brand_label",$detail,1);
|
|
|
|
}
|
|
|
|
asyncOutput( $hash->{curCL}, $output );
|
|
return 0;
|
|
}
|
|
|
|
if($data =~ /externalTemperature/) {
|
|
my $decode_json = from_json($data);
|
|
readingsSingleUpdate($hash,"externalTemperature",$decode_json->{data}->{attributes}->{externalTemperature},1);
|
|
return 0;
|
|
}
|
|
|
|
if($data =~ /chargeMode/) {
|
|
my $decode_json = from_json($data);
|
|
readingsSingleUpdate($hash,"chargeMode",$decode_json->{data}->{attributes}->{chargeMode},1);
|
|
return 0;
|
|
}
|
|
|
|
### charge-history?country=DE&type=day&start=20201212&end=20210120
|
|
### charge-history?country=DE&type=month&start=202012&end=202101
|
|
if($data =~ /chargeSummaries.*totalChargesNumber/) {
|
|
my $mtab = $decode_json->{data}->{attributes}->{chargeSummaries};
|
|
my $tperiod = "day";
|
|
$tperiod = "month" if ($data =~ /month/);
|
|
print ">>>".$tperiod."\n";
|
|
my $output = "<html><body><b>Charge Summaries (".$tperiod.")</b><table border=1 center>";
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$tperiod."</td>";
|
|
$output = $output."<td align=center>totalChargesNumber</td>";
|
|
$output = $output."<td align=center>totalChargesDuration</td>";
|
|
$output = $output."<td align=center>totalChargesErrors</td>";
|
|
$output = $output."</tr>";
|
|
foreach my $item( @$mtab ) {
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$item->{$tperiod}."</td>";
|
|
$output = $output."<td align=center>".$item->{totalChargesNumber}."</td>";
|
|
$output = $output."<td align=center>".$item->{totalChargesDuration}."</td>";
|
|
$output = $output."<td align=center>".$item->{totalChargesErrors}."</td>";
|
|
$output = $output."</tr>";
|
|
}
|
|
$output = $output."</table></body></html>";
|
|
readingsSingleUpdate($hash,"chargeHistory",$output,1);
|
|
return 0;
|
|
}
|
|
|
|
### hvac-history?country=DE&type=month&start=202012&end=202101
|
|
### hvac-history?country=DE&type=day&start=20201212&end=20210120
|
|
if($data =~ /hvacSessionsSummaries.*totalHvacSessionsNumber/) {
|
|
my $mtab = $decode_json->{data}->{attributes}->{hvacSessionsSummaries};
|
|
my $tperiod = "day";
|
|
$tperiod = "month" if ($data =~ /month/);
|
|
print ">>>".$tperiod."\n";
|
|
my $output = "<html><body><b>HVAC Session Summaries (".$tperiod.")</b><table border=1 center>";
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$tperiod."</td>";
|
|
$output = $output."<td align=center>totalHvacSessionsNumber</td>";
|
|
$output = $output."<td align=center>totalHvacSessionsErrors</td>";
|
|
$output = $output."</tr>";
|
|
foreach my $item( @$mtab ) {
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$item->{$tperiod}."</td>";
|
|
$output = $output."<td align=center>".$item->{totalHvacSessionsNumber}."</td>";
|
|
$output = $output."<td align=center>".$item->{totalHvacSessionsErrors}."</td>";
|
|
$output = $output."</tr>";
|
|
}
|
|
$output = $output."</table></body></html>";
|
|
readingsSingleUpdate($hash,"hvacHistory",$output,1);
|
|
return 0;
|
|
}
|
|
|
|
### hvac-sessions?country=DE&start=20201210&end=20210110
|
|
if($data =~ /hvacSessions.*hvacSessionRequestDate/) {
|
|
my $mtab = $decode_json->{data}->{attributes}->{hvacSessions};
|
|
#print scalar @$mtab."\n";
|
|
my $output = "<html><body><b>HVAC Sessions</b><table border=1 center>";
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>hvacSessionRequestDate</td>";
|
|
$output = $output."<td align=center>hvacSessionStartDate</td>";
|
|
$output = $output."<td align=center>hvacSessionEndStatus</td>";
|
|
$output = $output."</tr>";
|
|
foreach my $item( @$mtab ) {
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$item->{hvacSessionRequestDate}."</td>";
|
|
$output = $output."<td align=center>".$item->{hvacSessionStartDate}."</td>";
|
|
$output = $output."<td align=center>".$item->{hvacSessionEndStatus}."</td>";
|
|
$output = $output."</tr>";
|
|
}
|
|
$output = $output."</table></body></html>";
|
|
readingsSingleUpdate($hash,"hvacSessions",$output,1);
|
|
return 0;
|
|
}
|
|
|
|
### charging-settings?country=DE
|
|
if(($data =~ /mode.*schedules/) && ($lastUrl =~ /charg/)){
|
|
my $mtab = $decode_json->{data}->{attributes}->{schedules};
|
|
my $sss = @$mtab;
|
|
#print scalar @$mtab."\n";
|
|
my $output = "<html><body><b>Charging Settings</b><p>Mode=".$decode_json->{data}->{attributes}->{mode}."<p> <p>Schedules:<p> <p>";
|
|
my @wdays = ("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "saturday" );
|
|
if ( $sss > 0 ) {
|
|
foreach my $item( @$mtab ) {
|
|
$output = $output."activated =".$item->{activated}."<table border=1 center>";
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>Day of Week</td>";
|
|
$output = $output."<td align=center>startTime</td>";
|
|
$output = $output."<td align=center>duration</td>";
|
|
$output = $output."</tr>";
|
|
foreach my $wd( @wdays ) {
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$wd."</td>";
|
|
$output = $output."<td align=center>".$item->{$wd}->{startTime}."</td>";
|
|
$output = $output."<td align=center>".$item->{$wd}->{duration}."</td>";
|
|
$output = $output."</tr>";
|
|
}
|
|
last;
|
|
}
|
|
$output = $output."</table>";
|
|
}
|
|
$output = $output."</body></html>";
|
|
readingsSingleUpdate($hash,"chargingSettings",$output,1);
|
|
return 0;
|
|
}
|
|
|
|
### hvac-settings?country=DE
|
|
if(($data =~ /mode.*schedules/) && ($lastUrl =~ /hvac/)){
|
|
my $mtab = $decode_json->{data}->{attributes}->{schedules};
|
|
my $sss = @$mtab;
|
|
#print scalar @$mtab."\n";
|
|
my $output = "<html><body><b>HVAC Settings</b><p>Mode=".$decode_json->{data}->{attributes}->{mode}."<br>";
|
|
my @wdays = ("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "saturday" );
|
|
if ( $sss > 0 ) {
|
|
$output = $output."<p>Schedules";
|
|
foreach my $item( @$mtab ) {
|
|
if ($item->{activated} == 1 ) {
|
|
$output = $output."<p>activated =".$item->{activated}."<table border=1 center>";
|
|
$output = $output.", Target Temperature =".$item->{targetTemperature}."<table border=1 center>";
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>Day of Week</td>";
|
|
$output = $output."<td align=center>readyAtTime</td>";
|
|
$output = $output."</tr>";
|
|
foreach my $wd( @wdays ) {
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$wd."</td>";
|
|
$output = $output."<td align=center>".$item->{$wd}->{readyAtTime}."</td>";
|
|
$output = $output."</tr>";
|
|
}
|
|
$output = $output."</table>";
|
|
}
|
|
}
|
|
}
|
|
$output = $output."</body></html>";
|
|
readingsSingleUpdate($hash,"hvacSettings",$output,1);
|
|
return 0;
|
|
}
|
|
|
|
|
|
### notification-settings?country=DE
|
|
if($data =~ /settings.*messageKey/) {
|
|
my $mtab = $decode_json->{data}->{attributes}->{settings};
|
|
my $output = "<html><body><b>Notification Settings</b><table border=1 center>";
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>messageKey</td>";
|
|
$output = $output."<td align=center>email</td>";
|
|
$output = $output."<td align=center>sms</td>";
|
|
$output = $output."<td align=center>pushApp</td>";
|
|
$output = $output."</tr>";
|
|
foreach my $item( @$mtab ) {
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$item->{messageKey}."</td>";
|
|
$output = $output."<td align=center>".$item->{email}."</td>";
|
|
$output = $output."<td align=center>".$item->{sms}."</td>";
|
|
$output = $output."<td align=center>".$item->{pushApp}."</td>";
|
|
$output = $output."</tr>";
|
|
}
|
|
$output = $output."</table></body></html>";
|
|
readingsSingleUpdate($hash,"notificationSettings",$output,1);
|
|
return 0;
|
|
}
|
|
|
|
### charges start=20200202&end=20210202
|
|
if($data =~ /charges/) {
|
|
my $mtab = $decode_json->{data}->{attributes}->{charges};
|
|
my $output = "<html><body><b>Charges</b><table border=1 align=middle>";
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>charge Start Date</td>";
|
|
$output = $output."<td align=center>charge End Date</td>";
|
|
$output = $output."<td align=center>charge Duration</td>";
|
|
$output = $output."<td align=center>charge Start Battery Level</td>";
|
|
$output = $output."<td align=center>charge End Battery Level</td>";
|
|
$output = $output."<td align=center>charge Energy Recovered</td>";
|
|
$output = $output."<td align=center>charge End Status</td>";
|
|
$output = $output."</tr>";
|
|
foreach my $item( @$mtab ) {
|
|
$output = $output."<tr>";
|
|
$output = $output."<td align=center>".$item->{chargeStartDate}."</td>";
|
|
$output = $output."<td align=center>".$item->{chargeEndDate}."</td>";
|
|
$output = $output."<td align=center>".$item->{chargeDuration}."</td>";
|
|
$output = $output."<td align=center>".$item->{chargeStartBatteryLevel}."</td>";
|
|
$output = $output."<td align=center>".$item->{chargeEndBatteryLevel}."</td>";
|
|
$output = $output."<td align=center>".$item->{chargeEnergyRecovered}."</td>";
|
|
$output = $output."<td align=center>".$item->{chargeEndStatus}."</td>";
|
|
$output = $output."</tr>";
|
|
}
|
|
$output = $output."</table></body></html>";
|
|
$output =~ s/charge//g;
|
|
readingsSingleUpdate($hash,"chargesDetails",$output,1);
|
|
return 0;
|
|
}
|
|
|
|
Log3 $name, 5, "RenaultZE_gData_Step2 - opt=".$hash->{PARMS};
|
|
if($hash->{PARMS} eq "zTest") {
|
|
asyncOutput( $hash->{curCL}, $data );
|
|
return 0;
|
|
}
|
|
|
|
Log3 $name, 5, "RenaultZE_gData_Step2 - Out";
|
|
return 0;
|
|
}
|
|
|
|
sub RenaultZE_AC_Step1($)
|
|
{
|
|
my ($hash) = @_;
|
|
my $name = $hash->{NAME};
|
|
my $value = $hash->{PARMVALUE};
|
|
Log3 $name, 5, "RenaultZE_AC_Step1 - In ".$hash."/".$name;
|
|
Log3 $name, 5, "RenaultZE_Set - value = >$value<";
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_AC_Step1",1);
|
|
my $kamereon_api = $hash->{KAMEREON_API};
|
|
my $id_token = $hash->{READINGS}{ze_Gigya_JWT_Token}{VAL};
|
|
my $accId = $hash->{READINGS}{ze_Renault_AccId}{VAL};
|
|
my $vin = $hash->{VIN};
|
|
my $country = AttrVal($name,"ze_country","DE");
|
|
Log3 $name, 5, "RenaultZE_AC_Step1 - Parms: ".$kamereon_api."/".$id_token;
|
|
|
|
return undef if ( $id_token eq "" || $accId eq "" );
|
|
|
|
my $step1= {
|
|
'Content-type' => 'application/vnd.api+json',
|
|
'apikey' => $kamereon_api,
|
|
'x-gigya-id_token' => $id_token
|
|
};
|
|
|
|
Log3 $name, 5, "RenaultZE_AC_Step1 - Data".$step1;
|
|
my $url= "empty";
|
|
my $jsonData = "empty";
|
|
if ( $value eq "on" )
|
|
{
|
|
$jsonData = '{"data":{"type":"HvacStart","attributes":{"action":"start","targetTemperature":"21"}}}';
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/v1/cars/".$vin."/actions/hvac-start?country=".$country;
|
|
} else {
|
|
$jsonData = '{"data":{"type":"HvacStart","attributes":{"action":"cancel"}}}';
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/v1/cars/".$vin."/actions/hvac-start?country=".$country;
|
|
}
|
|
Log3 $name, 5, "RenaultZE_AC_Step1 - URL ".$url;
|
|
Log3 $name, 5, "RenaultZE_AC_Step1 - jsonData ".$jsonData;
|
|
my $param = {
|
|
url => $url,
|
|
header => $step1,
|
|
hash => $hash,
|
|
timeout => 15,
|
|
method => "POST",
|
|
data => $jsonData,
|
|
callback => \&RenaultZE_AC_Step2
|
|
};
|
|
|
|
HttpUtils_NonblockingGet($param); # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
|
|
Log3 $name, 5, "RenaultZE_AC_Step1 - Out";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_AC_Step2($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_AC_Step2 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_AC_Step2",1);
|
|
|
|
RenaultZE_Error_err($hash,"RenaultZE_AC_Step2",$param->{url},$err,$data) if($err ne "");
|
|
RenaultZE_Log_Data($hash,"RenaultZE_AC_Step2",$param->{url},$err,$data) if($data ne "");
|
|
RenaultZE_Error_errorCode2($hash,"RenaultZE_AC_Step2",$param->{url},$err,$data) if($data =~ /error/);
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
Log3 $name, 5, "RenaultZE_AC_Step2 - returned".$decode_json;
|
|
my $acstatus = $decode_json->{data}->{attributes}->{action};
|
|
my $msg = "AC:".$acstatus;
|
|
Log3 $name, 5, "RenaultZE_AC_Step2 - acstatus=".$msg;
|
|
$hash->{STATE} = $msg;
|
|
#asyncOutput( $hash->{curCL}, $output );
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Charge_Step1($)
|
|
{
|
|
my ($hash) = @_;
|
|
my $name = $hash->{NAME};
|
|
my $value = $hash->{PARMVALUE};
|
|
Log3 $name, 5, "RenaultZE_Charge_Step1 - In ".$hash."/".$name;
|
|
Log3 $name, 5, "RenaultZE_Charge_Step1 - value = >$value<";
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_Charge_Step1",1);
|
|
my $kamereon_api = $hash->{KAMEREON_API};
|
|
my $id_token = $hash->{READINGS}{ze_Gigya_JWT_Token}{VAL};
|
|
my $accId = $hash->{READINGS}{ze_Renault_AccId}{VAL};
|
|
my $vin = $hash->{VIN};
|
|
my $country = AttrVal($name,"ze_country","DE");
|
|
Log3 $name, 5, "RenaultZE_Charge_Step1 - Parms: ".$kamereon_api."/".$id_token;
|
|
|
|
return undef if ( $id_token eq "" || $accId eq "" );
|
|
|
|
my $step1= {
|
|
'Content-type' => 'application/vnd.api+json',
|
|
'apikey' => $kamereon_api,
|
|
'x-gigya-id_token' => $id_token
|
|
};
|
|
|
|
Log3 $name, 5, "RenaultZE_Charge_Step1 - Data".$step1;
|
|
my $jsonData = "empty";
|
|
my $url= "empty";
|
|
my $brand = AttrVal($name,"ze_brand","Renault");
|
|
if ( $value eq "start" )
|
|
{
|
|
if ( $brand eq "Renault" )
|
|
{
|
|
$jsonData = '{"data":{"type":"ChargingStart","attributes":{"action":"start"}}}';
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/v1/cars/".$vin."/actions/charging-start?country=".$country;
|
|
}
|
|
if ( $brand eq "Dacia" )
|
|
{
|
|
$jsonData = '{"data":{"type":"ChargePauseResume","attributes":{"action":"resume"}}}';
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kcm/v1/vehicles/".$vin."/charge/pause-resume?country=".$country;
|
|
}
|
|
} else {
|
|
if ( $brand eq "Renault" )
|
|
{
|
|
$jsonData = '{"data":{"type":"ChargingStart","attributes":{"action":"stop"}}}';
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kca/car-adapter/v1/cars/".$vin."/actions/charging-start?country=".$country;
|
|
}
|
|
if ( $brand eq "Dacia" )
|
|
{
|
|
$jsonData = '{"data":{"type":"ChargePauseResume","attributes":{"action":"pause"}}}';
|
|
$url = "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/".$accId."/kamereon/kcm/v1/vehicles/".$vin."/charge/pause-resume?country=".$country;
|
|
}
|
|
}
|
|
Log3 $name, 5, "RenaultZE_Charge_Step1 - URL ".$url;
|
|
Log3 $name, 5, "RenaultZE_Charge_Step1 - jsonData ".$jsonData;
|
|
my $param = {
|
|
url => $url,
|
|
header => $step1,
|
|
hash => $hash,
|
|
timeout => 15,
|
|
method => "POST",
|
|
data => $jsonData,
|
|
callback => \&RenaultZE_Charge_Step2
|
|
};
|
|
|
|
HttpUtils_NonblockingGet($param); # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
|
|
Log3 $name, 5, "RenaultZE_Charge_Step1 - Out";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Charge_Step2($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_Charge_Step2 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_Charge_Step2",1);
|
|
|
|
RenaultZE_Error_err($hash,"RenaultZE_Charge_Step2",$param->{url},$err,$data) if($err ne "");
|
|
RenaultZE_Log_Data($hash,"RenaultZE_Charge_Step2",$param->{url},$err,$data) if($data ne "");
|
|
RenaultZE_Error_errorCode2($hash,"RenaultZE_Charge_Step2",$param->{url},$err,$data) if($data =~ /error/);
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
Log3 $name, 5, "RenaultZE_Charge_Step2 - returned".$decode_json;
|
|
my $chargestatus = $decode_json->{data}->{attributes}->{action};
|
|
my $msg = "Charge:".$chargestatus;
|
|
Log3 $name, 5, "RenaultZE_Charge_Step2 - chargestatus=".$msg;
|
|
$hash->{STATE} = $msg;
|
|
#asyncOutput( $hash->{curCL}, $output );
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Log_Data($$$$$)
|
|
{
|
|
my ($hash, $step, $url, $err, $data) = @_;
|
|
my $name = $hash->{NAME};
|
|
Log3 $name, 5, "INFO: ".$step.", url: ".$url.", data: ".$data.", error: ".$err;
|
|
|
|
$err = RenaultZE_pp_err($hash,$err) if($err ne "");;
|
|
|
|
readingsSingleUpdate($hash,"ze_lastUrl",$url,1);
|
|
readingsSingleUpdate($hash,"ze_lastErr",$err,1);
|
|
readingsSingleUpdate($hash,"ze_lastData",$data,1);
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_Error_err($$$$$)
|
|
{
|
|
my ($hash, $step, $url, $err, $data) = @_;
|
|
my $name = $hash->{NAME};
|
|
Log3 $name, 3, "ERROR: ".$step.", error while calling ".$url." - $err";
|
|
|
|
$err = RenaultZE_pp_err($hash,$err) if($err ne "");;
|
|
|
|
readingsSingleUpdate($hash,"ze_lastUrl",$url,1);
|
|
readingsSingleUpdate($hash,"ze_lastErr",$err,1);
|
|
readingsSingleUpdate($hash,"ze_lastData",$data,1);
|
|
}
|
|
|
|
sub RenaultZE_Error_errorCode1($$$$$)
|
|
{
|
|
my ($hash, $step, $url, $err, $data) = @_;
|
|
my $name = $hash->{NAME};
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
my $errorCode = $decode_json->{errorCode};
|
|
my $errorDetails = $decode_json->{errorDetails};
|
|
my $errorMessage = $decode_json->{errorMessage};
|
|
my $statusReason = $decode_json->{statusReason};
|
|
my $msg = "errorCode=".$errorCode.", errorDetails=".$errorDetails.", errorMessage=".$errorMessage.", statusReason=".$statusReason;
|
|
Log3 $name, 3, "ERROR: (1) ".$step.", errorCode while calling ".$url." - $msg";
|
|
|
|
$msg = RenaultZE_pp_err($hash,$errorMessage) if($errorMessage ne "");;
|
|
|
|
readingsSingleUpdate($hash,"ze_lastUrl",$url,1);
|
|
readingsSingleUpdate($hash,"ze_lastErr",$msg,1);
|
|
readingsSingleUpdate($hash,"ze_lastData",$data,1);
|
|
}
|
|
|
|
|
|
sub RenaultZE_Error_errorCode2($$$$$)
|
|
{
|
|
my ($hash, $step, $url, $err, $data) = @_;
|
|
my $name = $hash->{NAME};
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
my $errorCode = $decode_json->{errors}[0]->{errorCode};
|
|
my $errorMessage = $decode_json->{errors}[0]->{errorMessage};
|
|
my $msg = "errorCode=".$errorCode.", errorMessage=".$errorMessage;
|
|
Log3 $name, 3, "ERROR: (2) ".$step.", error (data) while calling ".$url." - $msg" if($errorMessage !~ /Failed to forward request to remote service/);;
|
|
Log3 $name, 5, "ERROR: (2) ".$step.", error (data) while calling ".$url." - $msg" if($errorMessage =~ /Failed to forward request to remote service/);;
|
|
|
|
$msg = RenaultZE_pp_err($hash,$msg) if($msg ne "");;
|
|
|
|
readingsSingleUpdate($hash,"ze_lastUrl",$url,1);
|
|
readingsSingleUpdate($hash,"ze_lastErr",$msg,1);
|
|
readingsSingleUpdate($hash,"ze_lastData",$data,1);
|
|
}
|
|
|
|
sub RenaultZE_pp_err($$)
|
|
{
|
|
my ($hash,$err) = @_;
|
|
my $name = $hash->{NAME};
|
|
Log3 $name, 3, "INFO: pretty printing error ".$err;
|
|
my $errj = $err;
|
|
$errj =~ s/.*errorMessage=//g;
|
|
my $json_out = eval { decode_json($errj) };
|
|
my $output = "";
|
|
if ($@)
|
|
{
|
|
$output = $err;
|
|
} else {
|
|
my $decode_json = from_json($errj);
|
|
my $mtab = $decode_json->{errors};
|
|
$output = "<html><body><b>Error</b><table border=1 center><colgroup><col width=\"10%\"><col width=\"*\"></colgroup>";
|
|
$output = $output."<tr>";
|
|
$output = $output."<td><b>raw</b></td><td>".$err."</td></tr>";
|
|
foreach my $item( @$mtab ) {
|
|
$output = $output."<td><b>status</b></td><td>".$item->{status}."</td></tr>";
|
|
$output = $output."<td><b>code</b></td><td>".$item->{code}."</td></tr>";
|
|
$output = $output."<td><b>title</b></td><td>".$item->{title}."</td></tr>";
|
|
$output = $output."<td><b>detail</b></td><td>".$item->{detail}."</td></tr>";
|
|
}
|
|
$output = $output."</table></body></html>";
|
|
}
|
|
return $output;
|
|
}
|
|
sub RenaultZE_distanceFromHome($$$$)
|
|
{
|
|
my ($hash, $lat, $long, $homeRadius) = @_;
|
|
my $name = $hash->{NAME};
|
|
Log3 $name, 5, "RenaultZE_distanceFromHome - In ".$hash."/".$lat." ".$long."/".$homeRadius;
|
|
my $hlong = AttrVal( $name, "ze_longitude", AttrVal( "global", "longitude", 0.0 ) );
|
|
my $hlat = AttrVal( $name, "ze_latitude", AttrVal( "global", "latitude", 0.0 ) );
|
|
|
|
#Kreiszahl Pi
|
|
my $pi = 3.14159;
|
|
|
|
#Umrechnung von Grad in Radius
|
|
my $long1 = $long / 180 * $pi;
|
|
my $lat1 = $lat / 180 * $pi;
|
|
my $long2 = $hlong / 180 * $pi;
|
|
my $lat2 = $hlat / 180 * $pi;
|
|
|
|
#Entfernungsberechnung
|
|
my $distance = acos(sin($long1)*sin($long2) + cos($long1)*cos($long2)*cos($lat2-$lat1));
|
|
|
|
#Erdrundung einbeziehen
|
|
$distance = $distance * 6378.137;
|
|
|
|
my $dim = "km";
|
|
my $homeinfo = "";
|
|
my $homestate = "away";
|
|
$homeRadius = AttrVal( $name, "ze_homeRadius", 20 ) if ( $homeRadius eq "auto");
|
|
Log3 $name, 5, "RenaultZE_distanceFromHome - Check ".$hash."/".$lat." ".$long."/".$homeRadius;
|
|
|
|
if ($distance < $homeRadius) {
|
|
$distance = $distance * 1000;
|
|
$dim = "m";
|
|
if ($distance < $homeRadius) {
|
|
$homeinfo = "home";
|
|
$homestate = "home";
|
|
}
|
|
}
|
|
$distance = sprintf("%.3f", $distance);
|
|
$homeinfo = $distance." ".$dim." away" if ( $homeinfo eq "");
|
|
if ( $hlong != 0.0 and $hlat != 0.0) {
|
|
readingsSingleUpdate($hash,"distanceFromHome",$distance,1);
|
|
readingsSingleUpdate($hash,"distanceUnit",$dim,1);
|
|
readingsSingleUpdate($hash,"homeInfo",$homeinfo,1);
|
|
readingsSingleUpdate($hash,"homeState",$homestate,1);
|
|
RenaultZE_gAddress1($hash,$lat,$long) if (AttrVal($name,"ze_showaddress","1") eq 1);
|
|
}
|
|
|
|
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_gAddress1($$$)
|
|
{
|
|
my ($hash,$lat,$long) = @_;
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_gAddress1 - In ".$hash."/".$name." ".$lat."/".$long;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_gAddress1",1);
|
|
|
|
my $url = "https://www.google.com/maps/place/$lat+$long";
|
|
Log3 $name, 5, "RenaultZE_gData_Step1 - URL ".$url;
|
|
my $param = {
|
|
url => $url,
|
|
hash => $hash,
|
|
timeout => 15,
|
|
method => "GET",
|
|
callback => \&RenaultZE_gAddress2
|
|
};
|
|
|
|
HttpUtils_NonblockingGet($param); # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
|
|
Log3 $name, 5, "RenaultZE_gAddress1 - Out";
|
|
return 0;
|
|
}
|
|
|
|
sub RenaultZE_gAddress2($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 5, "RenaultZE_gAddress2 - In ".$hash."/".$name;
|
|
Log3 $name, 5, "RenaultZE_gAddress2 - In err".$hash."/".$err;
|
|
Log3 $name, 5, "RenaultZE_gAddress2 - In data".$hash."/".$data;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_gAddress2",1);
|
|
|
|
# RenaultZE_Error_err($hash,"RenaultZE_gAddress2",$param->{url},$err,$data) if($err ne "");
|
|
# RenaultZE_Log_Data($hash,"RenaultZE_gAddress2",$param->{url},$err,$data) if($data ne "");
|
|
# RenaultZE_Error_errorCode2($hash,"RenaultZE_gAddress2",$param->{url},$err,$data) if($data =~ /error/);
|
|
|
|
# my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
# return undef if ($lastErr ne "");
|
|
|
|
$data =~ s/.*meta content=\"(.*)\" itemprop=\"description\".*/$1/sg;
|
|
Log3 $name, 5, "RenaultZE_gAddress2 - Address ".$data;
|
|
my $oldinfo = ReadingsVal($name,"homeInfo","");
|
|
my $newinfo = $oldinfo." (".$data.")";
|
|
readingsSingleUpdate($hash,"homeInfo",$newinfo,1);
|
|
|
|
Log3 $name, 5, "RenaultZE_gAddress2 - Out";
|
|
}
|
|
|
|
sub RenaultZE_checkAPIkeys1($)
|
|
{
|
|
my ($hash) = @_;
|
|
my $name = $hash->{NAME};
|
|
my $value = $hash->{PARMVALUE};
|
|
Log3 $name, 5, "RenaultZE_checkAPIkeys_Step1 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_checkAPIkeys1",1);
|
|
my $kamereon_api = $hash->{KAMEREON_API};
|
|
my $id_token = $hash->{READINGS}{ze_Gigya_JWT_Token}{VAL};
|
|
my $country = AttrVal($name,"ze_country","DE");
|
|
|
|
my $step1= {
|
|
'Content-type' => 'application/vnd.api+json'
|
|
};
|
|
|
|
my $url= "https://renault-wrd-prod-1-euw1-myrapp-one.s3-eu-west-1.amazonaws.com/configuration/android/config_de_DE.json";
|
|
my $jsonData = "empty";
|
|
Log3 $name, 5, "RenaultZE_checkAPIkeys1 ".$url;
|
|
my $param = {
|
|
url => $url,
|
|
header => $step1,
|
|
hash => $hash,
|
|
timeout => 15,
|
|
method => "GET",
|
|
data => $jsonData,
|
|
callback => \&RenaultZE_checkAPIkeys2
|
|
};
|
|
|
|
HttpUtils_NonblockingGet($param); # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
|
|
Log3 $name, 5, "RenaultZE_checkAPIkeys1 - Out";
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_checkAPIkeys2($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash->{NAME};
|
|
my $kamereon_api = $hash->{KAMEREON_API};
|
|
my $gigya_api = $hash->{GIGYA_API};
|
|
|
|
Log3 $name, 5, "RenaultZE_checkAPIkeys2 - In ".$hash."/".$name;
|
|
readingsSingleUpdate($hash,"ze_Step","RenaultZE_checkAPIkeys2",1);
|
|
|
|
RenaultZE_Error_err($hash,"RenaultZE_checkAPIkeys2",$param->{url},$err,$data) if($err ne "");
|
|
RenaultZE_Log_Data($hash,"RenaultZE_checkAPIkeys2",$param->{url},$err,$data) if($data ne "");
|
|
RenaultZE_Error_errorCode2($hash,"RenaultZE_checkAPIkeys2",$param->{url},$err,$data) if($data =~ /error/);
|
|
|
|
my $lastErr = $hash->{READINGS}{ze_lastErr}{VAL};
|
|
return undef if ($lastErr ne "");
|
|
|
|
return undef if (RenaultZE_CheckJson($hash,$data));
|
|
my $decode_json = from_json($data);
|
|
Log3 $name, 5, "RenaultZE_checkAPIkeys2 - returned".$decode_json;
|
|
#my $kameronkey = $decode_jmson->{data}->{servers}->{wiredProd}->{apikey};
|
|
#my $gigyakey = $decode_json->{data}->{servers}->{gigyaProd}->{apikey};
|
|
my $kameronkey = $decode_json->{servers}->{wiredProd}->{apikey};
|
|
my $gigyakey = $decode_json->{servers}->{gigyaProd}->{apikey};
|
|
Log3 $name, 5, "RenaultZE_checkAPIkeys2 KAMERON: ".$kameronkey;
|
|
Log3 $name, 5, "RenaultZE_checkAPIkeys2 GIGYA".$gigyakey;
|
|
Log3 $name, 5, "RenaultZE_checkAPIkeys2 Out";
|
|
my $message = "";
|
|
if ($kameronkey ne $kamereon_api) {
|
|
$message = "Neuer KAMERON API Key: \nIst: ".$kamereon_api."\nNeu: ".$kameronkey."\n\n";
|
|
} else {
|
|
$message = "KAMERON API Key ist OK: \nIst: ".$kamereon_api."\nNeu: ".$kameronkey."\n\n";
|
|
}
|
|
if ($gigyakey ne $gigya_api) {
|
|
$message = $message."Neuer GIGYA API Key: \nIst: ".$gigya_api."\nNeu: ".$gigyakey."\n";
|
|
} else {
|
|
$message = $message."GIGYA API Key ist OK: \nIst: ".$gigya_api."\nNeu: ".$gigyakey."\n";
|
|
}
|
|
asyncOutput( $hash->{curCL}, $message );
|
|
return undef;
|
|
}
|
|
|
|
sub RenaultZE_CheckJson($$)
|
|
{
|
|
my ($hash,$json) = @_;
|
|
my $name = $hash->{NAME};
|
|
my $json_out = eval { decode_json($json) };
|
|
if ($@)
|
|
{
|
|
readingsSingleUpdate($hash,"ze_lastErr","unexpected json error",1);
|
|
readingsSingleUpdate($hash,"ze_lastData",$json,1);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
sub RenaultZE_EpochFromDateTime($) {
|
|
my ($timestamp) = @_;
|
|
my $t;
|
|
|
|
if ( substr($timestamp,-1,1) eq "Z" )
|
|
{
|
|
$t = eval { Time::Piece->strptime($timestamp, "%Y-%m-%dT%H:%M:%SZ")->epoch };
|
|
}
|
|
elsif ( substr($timestamp,-3,1) eq ":" )
|
|
{
|
|
$timestamp =~ s/\+(\d{2}):(\d{2})$/+$1$2/;
|
|
$timestamp =~ s/\.\d{3}+/+/;
|
|
$t = eval { Time::Piece->strptime($timestamp, "%Y-%m-%dT%H:%M:%S%z")->epoch };
|
|
}
|
|
elsif ( substr($timestamp,-5,5) =~ /\+(\d{4})/ )
|
|
{
|
|
$t = eval { Time::Piece->strptime($timestamp, "%Y-%m-%dT%H:%M:%S%z")->epoch };
|
|
}
|
|
|
|
$t = 0 if ($t eq "");
|
|
|
|
return $t;
|
|
}
|
|
|
|
##############################
|
|
1;
|
|
|
|
=pod
|
|
=begin html
|
|
|
|
<a name="RenaultZE"></a>
|
|
<h3>RenaultZE</h3>
|
|
<ul>
|
|
<i>RenaultZE</i> implements an interface to the Renualt ZE API<br>
|
|
<br><br>
|
|
<a name="RenaultZEdefine"></a>
|
|
<b>Define</b>
|
|
<ul>
|
|
<code>define <name> RenaultZE <VIN> <Interval></code>
|
|
<br><br>
|
|
Example: <code>define myZoe RenaultZE VF1....... 300</code>
|
|
<br><br>
|
|
VIN is the Vehicle Identification Number (you'll find it in your registration)<br>
|
|
Interval is the interval in seconds between status updates
|
|
</ul>
|
|
<br>
|
|
|
|
<a name="RenaultZEset"></a>
|
|
<b>Set</b><br>
|
|
<ul>
|
|
<code>set <name> <option> <value></code>
|
|
<br><br>
|
|
You can <i>set</i><br>
|
|
<ul>
|
|
<li><i>password</i><br>
|
|
enter your Renault ZE accounts password</li>
|
|
<li><i>AC</i><br>
|
|
either on or cancel (which probably only cancles a timer)</li>
|
|
<li><i>Charge</i><br>
|
|
either on or off (off doesn't work if you have set 'charge mode walways')</li>
|
|
<li><i>state</i><br>
|
|
</ul>
|
|
</ul>
|
|
<br>
|
|
|
|
<a name="RenaultZEget"></a>
|
|
<b>Get</b><br>
|
|
<ul>
|
|
<code>get <name> <option></code>
|
|
<br><br>
|
|
You can <i>get</i><br>
|
|
<ul>
|
|
<li><a name="charge-history"></a><i>charge-history</i><br>
|
|
summary of charges, parameter options: <br>
|
|
type=day&start=YYYYMMDD&end=YYYYMMDD (default: 1.1.2000 till today)<br>
|
|
type=month&start=YYYYMM&end=YYYYMM
|
|
</li>
|
|
<li><a name="charges"></a><i>charges</i><br>
|
|
<b>only for Phase1 models</b><br>
|
|
list of charges, parameter options: <br>
|
|
start=YYYYMMDD&end=YYYYMMD (default: 1.1.2000 till today)
|
|
</li>
|
|
<li><a name="charging-settings"></a><i>charging-settings</i><br>
|
|
lists the settings for charging
|
|
</li>
|
|
<li><a name="hvac-history"></a><i>hvac-history</i><br>
|
|
<b>only for Phase1 models</b><br>
|
|
shows the air condition history, parameter options: <br>
|
|
type=day&start=YYYYMMDD&end=YYYYMMDD (default: 1.1.2000 till today)<br>
|
|
type=month&start=YYYYMM&end=YYYYMM
|
|
</li>
|
|
<li><a name="hvac-settings"></a><i>hvac-settings</i><br>
|
|
shows the ac settingS <br>
|
|
</li>
|
|
<li><a name="notification-settings"></a><i>notification-settings</i><br>
|
|
<b>only for Phase1 models</b><br>
|
|
lists the settings for cnotifications
|
|
</li>
|
|
<li><a name="update"></a><i>update</i><br>
|
|
force update of the current readings (battery-status, cockpit, location, hvac-status, charge-mode)</li>
|
|
<li><a name="checkAPIkeys"></a><i>checkAPIkeys</i><br>
|
|
check if the API are uptodate<br>
|
|
<li><a name="vehicles"></a><i>vehicles</i><br>
|
|
get a list of your vehicles with details, set the readings for the images and some technical details<br>
|
|
if the attribute ze_showimage is set you get readings with the cars images</li>
|
|
<li><a name="zTest"></a><i>zTest</i><br>
|
|
Option to test new API functions which might be implemented one day ...<br>
|
|
sub parameters are<br>
|
|
hvac-sessions?start=20201101&end=20210108&country=DE<br>
|
|
charge-history?type=day&start=20201101&end=20210108&country=DE<br>
|
|
charge-history?type=month&start=202011&end=202101&country=DE<br>
|
|
lock-status?country=DE<br>
|
|
res-state?country=DE<br>
|
|
As result you will either get a msgbox when the function is implemented by Renault, otherwise the ze-Readings will tell you more
|
|
</li>
|
|
</ul>
|
|
</ul>
|
|
<br>
|
|
|
|
<a name="RenaultZEattr"></a>
|
|
<b>Attributes</b>
|
|
<ul>
|
|
<code>attr <name> <attribute> <value></code>
|
|
<br><br>
|
|
See <a href="http://fhem.de/commandref.html#attr">commandref#attr</a> for more info about
|
|
the attr command.
|
|
<br><br>
|
|
Attributes:
|
|
<ul>
|
|
<li><i>ze_brand</i> Renault|Dacia<br>
|
|
Car brand, default is Renault
|
|
</li>
|
|
<li><i>ze_phase</i> 1|2<br>
|
|
The phase of ZE technology, either 1 or 2, right now only phase 2 is supported
|
|
</li>
|
|
<li><i>ze_country</i><br>
|
|
2 letter country code, e.g. DE ot GB
|
|
</li>
|
|
<li><i>ze_homeRadius</i><br>
|
|
allowed distance of car from home in m that is still considered 'home'
|
|
</li>
|
|
<li><i>ze_user</i><br>
|
|
The user-id that you used to register at Renault
|
|
</li>
|
|
<li><i>ze_latitude</i><br>
|
|
Latitude of your home location. Is being used to calculate homeInfo. Function also checks for global attribte latitude als default.
|
|
</li>
|
|
<li><i>ze_longitude</i><br>
|
|
Longitude of your home location. Is being used to calculate homeInfo. Function also checks for global attribte longitude als default.
|
|
</li>
|
|
<li><i>ze_showaddress</i><br>
|
|
Retrieve address via reverse geocoding fromn Google Maps and add it to homeInfo.
|
|
</li>
|
|
<li><i>ze_showimage</i><br>
|
|
Show the image of the car that you get from vehicles as reading<br>
|
|
0 = off
|
|
1 = only the small image (default)<br>
|
|
2 = both, small and large image
|
|
</li>
|
|
</ul>
|
|
</ul>
|
|
</ul>
|
|
|
|
=end html
|
|
|
|
=cut
|