From 9859eb546dd6c8a8f90380758c7151a5e0e1f96c Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Fri, 18 Mar 2022 10:55:08 +0100 Subject: [PATCH 01/11] started refactoring to make all calls async --- 98_EaseeWallbox.pm | 242 ++++++++++++++++++++++++++++++--------------- 1 file changed, 161 insertions(+), 81 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index 4a30dcd..13583dd 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -9,7 +9,11 @@ use HttpUtils; use JSON; my %EaseeWallbox_gets = ( - update => "noArg", + update => { + args => "noArg", + urlTemplate => "", + callback => \&EaseeWallbox_httpSimpleOperationCallback + }, health => "noArg", baseData => "noArg", chargers => "noArg", @@ -148,7 +152,7 @@ sub EaseeWallbox_getCmdList ($$$) { if ( defined($myOpt) and ( length($myOpt) > 0 ) ); $myOpt = "" if ( !defined($myOpt) ); - Log3 ($name, 5, "parse cmd-table - Set:$mySet, Option:$myOpt, RetVal:$retVal"); + #Log3 ($name, 5, "parse cmd-table - Set:$mySet, Option:$myOpt, RetVal:$retVal"); } if ( !defined($retVal) ) { $retVal = "error while parsing set-table"; @@ -533,8 +537,58 @@ sub EaseeWallbox_TokenRefresh { return; } -sub EaseeWallbox_httpSimpleOperationOAuth($$$;$) { - my ( $hash, $url, $operation, $message ) = @_; +sub EaseeWallbox_httpSimpleOperationCallback { + my ( $param, $err, $data ) = @_; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + + Log3 $name, 4, "Callback received." . $param->{url}; + + if ( $err ne "" ) # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist + { + Log3 $name, 3,"error while requesting ". $param->{url}. " - $err"; # Eintrag fürs Log + readingsSingleUpdate( $hash, "lastResponse", "ERROR $err", 1 ); + return undef; + } + + my $code = $param->{code}; + if ($code >= 400){ + Log3 $name, 3,"HTTPS error while requesting ". $param->{url}. " - $code"; # Eintrag fürs Log + readingsSingleUpdate( $hash, "lastResponse", "ERROR: HTTP Code $code", 1 ); + return undef; + } + + Log3 $name, 3, + "Received non-blocking data from EaseeWallbox regarding current session "; + + Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{url}; + Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{message} + if ( defined $param->{message} ); + Log3 $name, 4, "EaseeWallbox -> FHEM: " . $data; + Log3 $name, 5, '$err: ' . $err; + Log3 $name, 5, "method: " . $param->{method}; + Log3 $name, 2, "Something gone wrong" + if ( $data =~ "/EaseeWallboxMode/" ); + eval { + my $d = decode_json($data) if ( !$err ); + Log3 $name, 5, 'Decoded: ' . Dumper($d); + if ( defined $d and not $d eq '') { + readingsSingleUpdate( $hash, "lastResponse", 'OK - Action '. $d->{commandId}, 1 ) if defined $d->{commandId}; + readingsSingleUpdate( $hash, "lastResponse", 'ERROR: '. $d->{title}.' ('.$d->{status}.')', 1 ) if defined $d->{status} and defined $d->{title}; + return undef; + } else { + readingsSingleUpdate( $hash, "lastResponse", 'OK', 1); + return undef; + } + }; + if ($@) { + readingsSingleUpdate( $hash, "lastResponse", 'ERROR while deconding response: '. $@, 1 ); + Log3 $name, 5, 'Failure decoding: ' . $@; + } +} + +sub EaseeWallbox_httpSimpleOperationOAuth { + my ( $hash, $url, $operation, $message, $callback ) = @_; my ( $json, $err, $data, $decoded ); my $name = $hash->{NAME}; my $CurrentTokenData = EaseeWallbox_LoadToken($hash); @@ -549,42 +603,21 @@ sub EaseeWallbox_httpSimpleOperationOAuth($$$;$) { "Authorization" => "$CurrentTokenData->{'tokenType'} $CurrentTokenData->{'accessToken'}" }, + callback => \&EaseeWallbox_httpSimpleOperationCallback, method => $operation, timeout => 6, - hideurl => 1 + hideurl => 1, }; + $request->{callback} = $callback if defined $callback; $request->{data} = $message if ( defined $message ); + Log3 $name, 5, 'Request: ' . Dumper($request); + $request->{hash} = $hash; - ( $err, $data ) = HttpUtils_BlockingGet($request); - - $json = "" if ( !$json ); - $data = "" if ( !$data ); - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $url; - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $message - if ( defined $message ); - Log3 $name, 4, "EaseeWallbox -> FHEM: " . $data if ( defined $data ); - Log3 $name, 4, "EaseeWallbox -> FHEM: Got empty response." - if ( not defined $data ); - Log3 $name, 5, '$err: ' . $err; - Log3 $name, 5, "method: " . $operation; - Log3 $name, 2, "Something gone wrong" - if ( $data =~ "/EaseeWallboxMode/" ); - - $err = 1 if ( $data =~ "/EaseeWallboxMode/" ); - if ( defined $data and ( not $data eq '' ) and $operation ne 'DELETE' ) { - eval { - $decoded = decode_json($data) if ( !$err ); - Log3 $name, 5, 'Decoded: ' . Dumper($decoded); - return $decoded; - } or do { - Log3 $name, 5, 'Failure decoding: ' . $@; - } - } - else { - return undef; - } + HttpUtils_NonblockingGet($request); + Log3 $name, 3, + "Async call executed. Waiting for callback..."; } sub EaseeWallbox_ExecuteParameterlessCommand($$) { @@ -649,6 +682,98 @@ sub EaseeWallbox_Attr(@) { return undef; } +sub EaseeWallbox_GetChargersCallback(){ + + my ( $param, $err, $data ) = @_; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + my $d; + + Log3 $name, 4, "Callback received." . $param->{url}; + + if ( $err ne "" ) # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist + { + Log3 $name, 3,"error while requesting ". $param->{url}. " - $err"; # Eintrag fürs Log + readingsSingleUpdate( $hash, "lastResponse", "ERROR $err", 1 ); + return undef; + } + + my $code = $param->{code}; + if ($code >= 400){ + Log3 $name, 3,"HTTPS error while requesting ". $param->{url}. " - $code"; # Eintrag fürs Log + readingsSingleUpdate( $hash, "lastResponse", "ERROR: HTTP Code $code", 1 ); + return undef; + } + + Log3 $name, 3, + "Received non-blocking data from EaseeWallbox regarding current session "; + + Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{url}; + Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{message} + if ( defined $param->{message} ); + Log3 $name, 4, "EaseeWallbox -> FHEM: " . $data; + Log3 $name, 5, '$err: ' . $err; + Log3 $name, 5, "method: " . $param->{method}; + Log3 $name, 2, "Something gone wrong" + if ( $data =~ "/EaseeWallboxMode/" ); + eval { + $d = decode_json($data) if ( !$err ); + Log3 $name, 5, 'Decoded: ' . Dumper($d); + }; + if ($@) { + readingsSingleUpdate( $hash, "lastResponse", 'ERROR while deconding response: '. $@, 1 ); + Log3 $name, 5, 'Failure decoding: ' . $@; + return undef; + } + + if ( defined $d && ref($d) eq "HASH" && defined $d->{errors} ) { + log 1, Dumper $d; + readingsSingleUpdate( $hash, + "Error: $d->{errors}[0]->{code} / $d->{errors}[0]->{title}", + 'Undefined', 1 ); + return undef; + + } + else { + + readingsBeginUpdate($hash); + + my $site = $d->[0]; + my $circuit = $site->{circuits}->[0]; + my $charger = $circuit->{chargers}->[0]; + + readingsBeginUpdate($hash); + my $chargerId = $charger->{id}; + readingsBulkUpdate( $hash, "charger_id", $chargerId ); + readingsBulkUpdate( $hash, "charger_name", $charger->{name} ); + #readingsBulkUpdate( $hash, "charger_isTemporary", $charger->{isTemporary} ); + #readingsBulkUpdate( $hash, "charger_createdOn", $charger->{createdOn} ); + readingsEndUpdate( $hash, 1 ); + + # $readTemplate = $EaseeWallbox_urls{"getChargerDetails"}; + # $readTemplate =~ s/#ChargerID#/$chargerId/g; + # $d = EaseeWallbox_httpSimpleOperationOAuth( $hash, $readTemplate, + # 'GET' ); + + # if ( defined $d && ref($d) eq "HASH" && defined $d->{errors} ) { + # log 1, Dumper $d; + # readingsSingleUpdate( $hash, + # "Error: $d->{errors}[0]->{code} / $d->{errors}[0]->{title}", + # 'Undefined', 1 ); + # return undef; + # } + # else { + # readingsBeginUpdate($hash); + # readingsBulkUpdate( $hash, "product", $d->{product} ); + # readingsBulkUpdate( $hash, "pincode", $d->{pinCode} ); + # readingsBulkUpdate( $hash, "unitType", $d->{unitType} ); + # readingsEndUpdate( $hash, 1 ); + # } + readingsSingleUpdate( $hash, "lastResponse", 'OK', 1); + return undef; + } +} + sub EaseeWallbox_GetChargers($) { my ($hash) = @_; @@ -662,55 +787,10 @@ sub EaseeWallbox_GetChargers($) { my $readTemplate = $EaseeWallbox_urls{"getChargers"}; - my $d = EaseeWallbox_httpSimpleOperationOAuth( $hash, $readTemplate, - 'GET' ); + EaseeWallbox_httpSimpleOperationOAuth( $hash, $readTemplate, 'GET', '', \&EaseeWallbox_GetChargersCallback ); + return undef; - if ( defined $d && ref($d) eq "HASH" && defined $d->{errors} ) { - log 1, Dumper $d; - readingsSingleUpdate( $hash, - "Error: $d->{errors}[0]->{code} / $d->{errors}[0]->{title}", - 'Undefined', 1 ); - return undef; - - } - else { - - readingsBeginUpdate($hash); - - my $site = $d->[0]; - my $circuit = $site->{circuits}->[0]; - my $charger = $circuit->{chargers}->[0]; - - readingsBeginUpdate($hash); - my $chargerId = $charger->{id}; - readingsBulkUpdate( $hash, "charger_id", $chargerId ); - readingsBulkUpdate( $hash, "charger_name", $charger->{name} ); - #readingsBulkUpdate( $hash, "charger_isTemporary", $charger->{isTemporary} ); - #readingsBulkUpdate( $hash, "charger_createdOn", $charger->{createdOn} ); - readingsEndUpdate( $hash, 1 ); - - $readTemplate = $EaseeWallbox_urls{"getChargerDetails"}; - $readTemplate =~ s/#ChargerID#/$chargerId/g; - $d = EaseeWallbox_httpSimpleOperationOAuth( $hash, $readTemplate, - 'GET' ); - - if ( defined $d && ref($d) eq "HASH" && defined $d->{errors} ) { - log 1, Dumper $d; - readingsSingleUpdate( $hash, - "Error: $d->{errors}[0]->{code} / $d->{errors}[0]->{title}", - 'Undefined', 1 ); - return undef; - } - else { - readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "product", $d->{product} ); - readingsBulkUpdate( $hash, "pincode", $d->{pinCode} ); - readingsBulkUpdate( $hash, "unitType", $d->{unitType} ); - readingsEndUpdate( $hash, 1 ); - } - - return undef; - } + } sub EaseeWallbox_GetChargerConfig($) { From 197c314120db2c1ff86982c194fee8fecccd76ea Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Fri, 18 Mar 2022 22:44:22 +0100 Subject: [PATCH 02/11] filled new skeleton with basic data --- 98_EaseeWallbox.pm | 1187 ++++++++++++++------------------------------ 1 file changed, 383 insertions(+), 804 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index 13583dd..fb2356c 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -1,4 +1,5 @@ -package main; +package FHEM::EaseeWallbox; +use GPUtils qw(GP_Import GP_Export); use strict; use warnings; @@ -8,12 +9,101 @@ use Encode qw( encode_utf8 ); use HttpUtils; use JSON; -my %EaseeWallbox_gets = ( - update => { - args => "noArg", - urlTemplate => "", - callback => \&EaseeWallbox_httpSimpleOperationCallback - }, +# try to use JSON::MaybeXS wrapper +# for chance of better performance + open code +#eval { +# require JSON::MaybeXS; +# import JSON::MaybeXS qw( decode_json encode_json ); +# 1; +#} or do { + + # try to use JSON wrapper + # for chance of better performance + # eval { + # JSON preference order + # local $ENV{PERL_JSON_BACKEND} = + # 'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP' + # unless ( defined( $ENV{PERL_JSON_BACKEND} ) ); + + # require JSON; + # import JSON qw( decode_json encode_json ); + # 1; + # } or do { + + # In rare cases, Cpanel::JSON::XS may + # be installed but JSON|JSON::MaybeXS not ... +# eval { +# require Cpanel::JSON::XS; +# import Cpanel::JSON::XS qw(decode_json encode_json); +# 1; +# } or do { + + # In rare cases, JSON::XS may + # be installed but JSON not ... + # eval { + # require JSON::XS; + # import JSON::XS qw(decode_json encode_json); + # 1; + # } or do { + + # Fallback to built-in JSON which SHOULD + # be available since 5.014 ... + # eval { + # require JSON::PP; + # import JSON::PP qw(decode_json encode_json); + # 1; + # } or do { + + # Fallback to JSON::backportPP in really rare cases + # require JSON::backportPP; + # import JSON::backportPP qw(decode_json encode_json); + # 1; + # }; + # }; + # }; + # }; +#}; + +# Import von Funktionen und/oder Variablen aus der FHEM main +# man kann ::Funktionaname wählen und sich so den Import schenken. Variablen sollten aber +# sauber importiert werden +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +#-- Run before package compilation +BEGIN { + # Import from main context + GP_Import( + qw( + readingFnAttributes + Log3 + readingsBeginUpdate + readingsEndUpdate + readingsBulkUpdate + readingsSingleUpdate + InternalVal + ReadingsVal + RemoveInternalTimer + InternalTimer + HttpUtils_NonblockingGet + HttpUtils_BlockingGet + gettimeofday + getUniqueId + Attr + ) + ); +} + +#-- Export to main context with different name +GP_Export( + qw( + Initialize + ) +); + + +my %gets = ( + update => "noArg", health => "noArg", baseData => "noArg", chargers => "noArg", @@ -22,7 +112,7 @@ my %EaseeWallbox_gets = ( config => "noArg", ); -my %EaseeWallbox_sets = ( +my %sets = ( startCharging => "", stopCharging => "", pauseCharging => "", @@ -41,56 +131,37 @@ my %EaseeWallbox_sets = ( deactivateTimer => "", ); -my %EaseeWallbox_urls = ( - getOAuthToken => 'https://api.easee.cloud/api/accounts/login', - getRefreshToken => 'https://api.easee.cloud/api/accounts/refresh_token', - getProfile => 'https://api.easee.cloud/api/accounts/profile', - getChargingSession => - 'https://api.easee.cloud/api/chargers/#ChargerID#/sessions/ongoing', - getChargers => 'https://api.easee.cloud/api/accounts/chargers', - getProducts => - 'https://api.easee.cloud/api/accounts/products?userId=#UserId#', - getChargerSite => 'https://api.easee.cloud/api/chargers/#ChargerID#/site', - getChargerDetails => - 'https://api.easee.cloud/api/chargers/#ChargerID#/details', - getChargerConfiguration => - 'https://api.easee.cloud/api/chargers/#ChargerID#/config', - getChargerSessionsMonthly => - 'https://api.easee.cloud/api/sessions/charger/#ChargerID#/monthly', - getChargerSessionsDaily => - 'https://api.easee.cloud/api/sessions/charger/#ChargerID#/daily', - getChargerState => - 'https://api.easee.cloud/api/chargers/#ChargerID#/state', - getCurrentSession => - 'https://api.easee.cloud/api/chargers/#ChargerID#/sessions/ongoing', - setCableLockState => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/lock_state', - setReboot => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/reboot', - setUpdateFirmware => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/update_firmware', - setEnableSmartCharging => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/smart_charging', - setStartCharging => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/start_charging', - setStopCharging => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/stop_charging', - setPauseCharging => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/pause_charging', - setResumeCharging => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/resume_charging', - setToggleCharging => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/toggle_charging', - setOverrideChargingSchedule => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/override_schedule', - setPairRFIDTag => - 'https://api.easee.cloud/api/chargers/#ChargerID#/commands/set_rfid_pairing_mode_async', - changeChargerSettings => - 'https://api.easee.cloud/api/chargers/#ChargerID#/settings', - setChargingPrice => 'https://api.easee.cloud/api/sites/#SiteID#/price', -); -my %EaseeWallbox_reasonsForNoCurrent = ( +## Datapoint, all behind API URI +my %dpoints = ( + getOAuthToken => 'accounts/login', + getRefreshToken => 'accounts/refresh_token', + getProfile => 'accounts/profile', + getChargingSession => 'chargers/#ChargerID#/sessions/ongoing', + getChargers => 'accounts/chargers', + getProducts => 'accounts/products?userId=#UserId#', + getChargerSite => 'chargers/#ChargerID#/site', + getChargerDetails => 'chargers/#ChargerID#/details', + getChargerConfiguration => 'chargers/#ChargerID#/config', + getChargerSessionsMonthly => 'sessions/charger/#ChargerID#/monthly', + getChargerSessionsDaily => 'sessions/charger/#ChargerID#/daily', + getChargerState => 'chargers/#ChargerID#/state', + getCurrentSession => 'chargers/#ChargerID#/sessions/ongoing', + setCableLockState => 'chargers/#ChargerID#/commands/lock_state', + setReboot => 'chargers/#ChargerID#/commands/reboot', + setUpdateFirmware => 'chargers/#ChargerID#/commands/update_firmware', + setEnableSmartCharging => 'chargers/#ChargerID#/commands/smart_charging', + setStartCharging => 'chargers/#ChargerID#/commands/start_charging', + setStopCharging => 'chargers/#ChargerID#/commands/stop_charging', + setPauseCharging => 'chargers/#ChargerID#/commands/pause_charging', + setResumeCharging => 'chargers/#ChargerID#/commands/resume_charging', + setToggleCharging => 'chargers/#ChargerID#/commands/toggle_charging', + setOverrideChargingSchedule => 'chargers/#ChargerID#/commands/override_schedule', + setPairRFIDTag => 'chargers/#ChargerID#/commands/set_rfid_pairing_mode_async', + changeChargerSettings => 'chargers/#ChargerID#/settings', + setChargingPrice => 'sites/#SiteID#/price', +); +my %reasonsForNoCurrent = ( 0 => 'OK', #charger is allocated current 1 => 'MaxCircuitCurrentTooLow', 2 => 'MaxDynamicCircuitCurrentTooLow', @@ -110,13 +181,12 @@ my %EaseeWallbox_reasonsForNoCurrent = ( 56 => 'ChargerInErrorState', 100 => 'Undefined' ); -my %EaseeWallbox_phaseModes = ( +my %phaseModes = ( 1 => 'Locked to single phase', 2 => 'Auto', 3 => 'Locked to three phase', ); - -my %EaseeWallbox_operationModes = ( +my %operationModes = ( 1 => "Standby", 2 => "Paused", 3 => 'Charging', @@ -126,8 +196,10 @@ my %EaseeWallbox_operationModes = ( ); #Private function to evaluate command-lists +# private funktionen beginnen immer mit _ + ############################# -sub EaseeWallbox_getCmdList ($$$) { +sub _GetCmdList { my ( $hash, $cmd, $commands ) = @_; my %cmdArray = %$commands; @@ -163,32 +235,35 @@ sub EaseeWallbox_getCmdList ($$$) { return $retVal; } -sub EaseeWallbox_Initialize($) { +sub Initialize { my ($hash) = @_; - $hash->{DefFn} = 'EaseeWallbox_Define'; - $hash->{UndefFn} = 'EaseeWallbox_Undef'; - $hash->{SetFn} = 'EaseeWallbox_Set'; - $hash->{GetFn} = 'EaseeWallbox_Get'; - $hash->{AttrFn} = 'EaseeWallbox_Attr'; - $hash->{ReadFn} = 'EaseeWallbox_Read'; - $hash->{WriteFn} = 'EaseeWallbox_Write'; - $hash->{Clients} = ':EaseeWallbox:'; - $hash->{MatchList} = { '1:EaseeWallbox' => '^EaseeWallbox;.*' }; - $hash->{AttrList} - = 'expertMode:yes,no ' - . 'ledStuff:yes,no ' - . 'SmartCharging:true,false ' - . $readingFnAttributes; + $hash->{DefFn} = \&Define; + $hash->{UndefFn} = \&Undef; + $hash->{SetFn} = \&Set; + $hash->{GetFn} = \&Get; + $hash->{AttrFn} = \&Attr; + $hash->{ReadFn} = \&Read; + $hash->{WriteFn} = \&Write; - Log 3, "EaseeWallbox module initialized."; + $hash->{AttrList} = + 'expertMode:yes,no ' + . 'ledStuff:yes,no ' + . 'SmartCharging:true,false ' + . $readingFnAttributes; + + #Log3, 'EaseeWallbox', 3, "EaseeWallbox module initialized."; + return; } -sub EaseeWallbox_Define($$) { +sub Define { my ( $hash, $def ) = @_; my @param = split( "[ \t]+", $def ); my $name = $hash->{NAME}; + # set API URI as Internal Key + $hash->{APIURI} = 'https://api.easee.cloud/api/'; + Log3 $name, 3, "EaseeWallbox_Define $name: called "; my $errmsg = ''; @@ -215,7 +290,7 @@ sub EaseeWallbox_Define($$) { #Take password and use custom encryption. # Encryption is taken from fitbit / withings module - my $password = EaseeWallbox_encrypt( $param[3] ); + my $password = _encrypt( $param[3] ); $hash->{Password} = $password; @@ -252,22 +327,22 @@ sub EaseeWallbox_Define($$) { readingsSingleUpdate( $hash, 'state', 'Undefined', 0 ); #Initial load of data - EaseeWallbox_UpdateBaseData($hash); - EaseeWallbox_RefreshData($hash); + #EaseeWallbox_UpdateBaseData($hash); + #EaseeWallbox_RefreshData($hash); Log3 $name, 1, sprintf("EaseeWallbox_Define %s: Starting timer with interval %s", $name, InternalVal($name,'INTERVAL', undef)); - InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "EaseeWallbox_UpdateDueToTimer", $hash) if (defined $hash); + #InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "EaseeWallbox_UpdateDueToTimer", $hash) if (defined $hash); return undef; } -sub EaseeWallbox_Undef($$) { +sub Undef { my ( $hash, $arg ) = @_; RemoveInternalTimer($hash); return undef; } -sub EaseeWallbox_Get($@) { +sub Get { my ( $hash, $name, @args ) = @_; return '"get EaseeWallbox" needs at least one argument' @@ -276,20 +351,20 @@ sub EaseeWallbox_Get($@) { my $opt = shift @args; #create response, if cmd is wrong or gui asks - my $cmdTemp = EaseeWallbox_getCmdList( $hash, $opt, \%EaseeWallbox_gets ); + my $cmdTemp = _GetCmdList( $hash, $opt, \%gets ); return $cmdTemp if ( defined($cmdTemp) ); $hash->{LOCAL} = 1; - EaseeWallbox_GetChargers($hash) if $opt eq "chargers"; - EaseeWallbox_GetChargerConfig($hash) if $opt eq "config"; - EaseeWallbox_GetChargerSite($hash) if $opt eq "sites"; - EaseeWallbox_RefreshData($hash) if $opt eq "update"; - EaseeWallbox_UpdateBaseData($hash) if $opt eq 'baseData'; + WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "chargers"; + WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "config"; + WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "sites"; + WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "update"; + WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq 'baseData'; delete $hash->{LOCAL}; - return undef; + return undef; } -sub EaseeWallbox_Set($@) { +sub Set { my ( $hash, $name, @param ) = @_; return '"set $name" needs at least one argument' if ( int(@param) < 1 ); @@ -298,73 +373,219 @@ sub EaseeWallbox_Set($@) { my $value = join( "", @param ); #create response, if cmd is wrong or gui asks - my $cmdTemp = EaseeWallbox_getCmdList( $hash, $opt, \%EaseeWallbox_sets ); + my $cmdTemp = _GetCmdList( $hash, $opt, \%sets ); return $cmdTemp if ( defined($cmdTemp) ); +} - if ( $opt eq "deactivateTimer" ) { - RemoveInternalTimer($hash); - Log3 $name, 1, - "EaseeWallbox_Set $name: Stopped the timer to automatically update readings"; - readingsSingleUpdate( $hash, 'state', 'Initialized', 0 ); +sub Attr { + my ( $cmd, $name, $attrName, $attrVal ) = @_; + return; +} + +sub WriteToCloudAPI { + my $hash = shift; + my $dpoint = shift; + my $method = shift; + my $name = $hash->{NAME}; + my $url = $hash->{APIURI} . $dpoints{$dpoint}; + + ######### + # CHANGE THIS + my $payload = ''; + my $deviceId = "WC1"; + + if ( not defined $hash ) { + my $msg = "Error on EaseeWallbox_WriteToCloudAPI. Missing hash variable"; + Log3 'EaseeWallbox', 1, $msg; + return $msg; + } + + #Check if chargerID is required in URL and replace or alert. + if ( $url =~ m/#ChargerID#/ ) { + my $chargerId = ReadingsVal( $name, 'charger_id', undef ); + if ( not defined $chargerId ) { + my $error = "Error on EaseeWallbox_WriteToCloudAPI. Missing charger_id. Please ensure basic data is available."; + Log3 'EaseeWallbox', 1, $error; + return $error; + } + $url =~ s/#ChargerID#/$chargerId/g; + } + + #Check if siteID is required in URL and replace or alert. + if ( $url =~ m/#SiteID#/ ) { + my $siteId = ReadingsVal( $name, 'site_id', undef ); + if ( not defined $siteId ) { + my $error = "Error on EaseeWallbox_WriteToCloudAPI. Missing site_id. Please ensure basic data is available."; + Log3 'EaseeWallbox', 1, $error; + return $error; + } + $url =~ s/#SiteID#/$siteId/g; + } + + my $CurrentTokenData = _loadToken($hash); + my $header = + { + "Content-Type" => "application/json;charset=UTF-8", + "Authorization" => + "$CurrentTokenData->{'tokenType'} $CurrentTokenData->{'accessToken'}" + }; + + # $method ist GET oder POST + # bei POST ist $payload gleich data + + HttpUtils_NonblockingGet( + { + url => $url, + timeout => 15, + incrementalTimeout => 1, + hash => $hash, + dpoint => $dpoint, + device_id => $deviceId, + data => $payload, + method => $method, + header => $header, + callback => \&ResponseHandling + } + ); + return; +} + +sub ResponseHandling { + my $param = shift; + my $err = shift; + my $data = shift; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + + Log3 $name, 4, "Callback received." . $param->{url}; + + if ( $err ne "" ) # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist + { + Log3 $name, 3,"error while requesting ". $param->{url}. " - $err"; # Eintrag fürs Log + readingsSingleUpdate( $hash, "lastResponse", "ERROR $err", 1 ); return undef; } - elsif ( $opt eq "activateTimer" ) { - #Update once manually and then start the timer - RemoveInternalTimer($hash); - $hash->{LOCAL} = 1; - EaseeWallbox_RefreshData($hash); - delete $hash->{LOCAL}; - InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "EaseeWallbox_UpdateDueToTimer", $hash); - readingsSingleUpdate($hash,'state','Started',0); - Log3 $name, 1, sprintf("EaseeWallbox_Set %s: Updated readings and started timer to automatically update readings with interval %s", $name, InternalVal($name,'INTERVAL', undef)); + + my $code = $param->{code}; + if ($code >= 400){ + Log3 $name, 3,"HTTPS error while requesting ". $param->{url}. " - $code"; # Eintrag fürs Log + readingsSingleUpdate( $hash, "lastResponse", "ERROR: HTTP Code $code", 1 ); + return undef; } - elsif ( $opt eq "interval" ) { - my $interval = shift @param; - $interval = 60 unless defined($interval); - if ( $interval < 5 ) { $interval = 5; } + Log3 $name, 3, + "Received non-blocking data from EaseeWallbox regarding current session "; - Log3 $name, 1, "EaseeWallbox_Set $name: Set interval to" . $interval; - $hash->{INTERVAL} = $interval; - } else { - $hash->{LOCAL} = 1; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setStartCharging" ) if $opt eq "startCharging"; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setStopCharging" ) if $opt eq 'stopCharging'; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setPauseCharging" ) if $opt eq 'pauseCharging'; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setResumeCharging" ) if $opt eq 'resumeCharging'; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setToggleCharging" ) if $opt eq 'toggleCharging'; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setUpdateFirmware" ) if $opt eq 'updateFirmware'; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setOverrideChargingSchedule" ) if $opt eq 'overrideChargingSchedule'; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setPairRFIDTag" ) if $opt eq 'pairRfidTag'; + Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{url}; + Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{message} + if ( defined $param->{message} ); + Log3 $name, 4, "EaseeWallbox -> FHEM: " . $data; + Log3 $name, 5, '$err: ' . $err; + Log3 $name, 5, "method: " . $param->{method}; + Log3 $name, 2, "Something gone wrong" + if ( $data =~ "/EaseeWallboxMode/" ); + + my $d; + eval { + my $d = decode_json($data); + Log3 $name, 5, 'Decoded: ' . Dumper($d); + if ( defined $d and $d ne '') { - EaseeWallbox_ExecuteParameterlessCommand( $hash, "setReboot" ) if $opt eq 'reboot'; - EaseeWallbox_ExecuteParameterlessCommand( $hash, "toBeDone" ) if $opt eq 'enableSmartCharging'; - EaseeWallbox_SetCableLock( $hash, shift @param ) if $opt eq 'cableLock'; - EaseeWallbox_SetPrice( $hash, shift @param ) if $opt eq 'pricePerKWH'; - EaseeWallbox_LoadToken($hash) if $opt eq 'refreshToken'; - delete $hash->{LOCAL}; - } - readingsSingleUpdate( $hash, 'state', 'Initialized', 0 ); - return undef; + if($param->{dpoint} eq 'getChargers') + { + my $site = $d->[0]; + my $circuit = $site->{circuits}->[0]; + my $charger = $circuit->{chargers}->[0]; + + readingsBeginUpdate($hash); + my $chargerId = $charger->{id}; + readingsBulkUpdate( $hash, "site_id", $site->{id} ); + readingsBulkUpdate( $hash, "charger_id", $chargerId ); + readingsBulkUpdate( $hash, "charger_name", $charger->{name} ); + readingsBulkUpdate( $hash, "lastResponse", 'OK - getReaders', 1); + readingsEndUpdate( $hash, 1 ); + WriteToCloudAPI($hash, 'getChargerConfiguration', 'GET'); + return; + } + + if($param->{dpoint} eq 'getChargerConfiguration') + { + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, "charger_isEnabled", $d->{isEnabled} ); + readingsBulkUpdate( $hash, "lockCablePermanently", $d->{lockCablePermanently} ); + readingsBulkUpdate($hash, "authorizationRequired", $d->{authorizationRequired}); + readingsBulkUpdate( $hash, "remoteStartRequired", $d->{remoteStartRequired} ); + readingsBulkUpdate( $hash, "smartButtonEnabled", $d->{smartButtonEnabled} ); + readingsBulkUpdate( $hash, "wiFiSSID", $d->{wiFiSSID} ); + readingsBulkUpdate( $hash, "charger_phaseModeId", $d->{phaseMode} ); + readingsBulkUpdate( $hash, "charger_phaseMode",$phaseModes{ $d->{phaseMode} } ); + readingsBulkUpdate($hash, "localAuthorizationRequired",$d->{localAuthorizationRequired}); + readingsBulkUpdate( $hash, "maxChargerCurrent", $d->{maxChargerCurrent} ); + readingsBulkUpdate( $hash, "ledStripBrightness", $d->{ledStripBrightness} ); + #readingsBulkUpdate( $hash, "charger_offlineChargingMode", + # $d->{offlineChargingMode} ); + #readingsBulkUpdate( $hash, "charger_circuitMaxCurrentP1", + # $d->{circuitMaxCurrentP1} ); + #readingsBulkUpdate( $hash, "charger_circuitMaxCurrentP2", + # $d->{circuitMaxCurrentP2} ); + #readingsBulkUpdate( $hash, "charger_circuitMaxCurrentP3", + # $d->{circuitMaxCurrentP3} ); + #readingsBulkUpdate( $hash, "charger_enableIdleCurrent", + # $d->{enableIdleCurrent} ); + #readingsBulkUpdate( + # $hash, + # "charger_limitToSinglePhaseCharging", + # $d->{limitToSinglePhaseCharging} + #); + + #readingsBulkUpdate( $hash, "charger_localNodeType", + # $d->{localNodeType} ); + + #readingsBulkUpdate( $hash, "charger_localRadioChannel", + # $d->{localRadioChannel} ); + #readingsBulkUpdate( $hash, "charger_localShortAddress", + # $d->{localShortAddress} ); + #readingsBulkUpdate( + # $hash, + # "charger_localParentAddrOrNumOfNodes", + # $d->{localParentAddrOrNumOfNodes} + #); + #readingsBulkUpdate( + # $hash, + # "charger_localPreAuthorizeEnabled", + # $d->{localPreAuthorizeEnabled} + #); + #readingsBulkUpdate( + # $hash, + # "charger_allowOfflineTxForUnknownId", + # $d->{allowOfflineTxForUnknownId} + #); + #readingsBulkUpdate( $hash, "chargingSchedule", + # $d->{chargingSchedule} ); + readingsBulkUpdate( $hash, "lastResponse", 'OK - getReaderConfig', 1); + readingsEndUpdate( $hash, 1 ); + + return undef; + } + + readingsSingleUpdate( $hash, "lastResponse", 'OK - Action '. $d->{commandId}, 1 ) if defined $d->{commandId}; + readingsSingleUpdate( $hash, "lastResponse", 'ERROR: '. $d->{title}.' ('.$d->{status}.')', 1 ) if defined $d->{status} and defined $d->{title}; + return undef; + } else { + readingsSingleUpdate( $hash, "lastResponse", 'OK - empty', 1); + return undef; + } + }; + if ($@) { + readingsSingleUpdate( $hash, "lastResponse", 'ERROR while deconding response: '. $@, 1 ); + Log3 $name, 5, 'Failure decoding: ' . $@; + } + + return; } -sub EaseeWallbox_RefreshData($){ - my $hash = shift; - my $name = $hash->{NAME}; - EaseeWallbox_GetChargerSite($hash); - EaseeWallbox_RequestChargerState($hash); - EaseeWallbox_RequestCurrentSession($hash); - readingsSingleUpdate( $hash, "state", sprintf('%s (%.2f)
Current Session: %.2f kWH (%.2f€)', ReadingsVal($name,"operationMode","N/A"), ReadingsVal($name,"power","0"), ReadingsVal($name,"kWhInSession","0"), ReadingsVal($name,"session_chargingCost","0")), 1 ); -} -sub EaseeWallbox_UpdateBaseData($){ - my $hash = shift; - EaseeWallbox_GetChargers($hash); - EaseeWallbox_GetChargerConfig($hash); - EaseeWallbox_RefreshData($hash); -} - -sub EaseeWallbox_LoadToken { +sub _loadToken { my $hash = shift; my $name = $hash->{NAME}; my $tokenLifeTime = $hash->{TOKEN_LIFETIME}; @@ -382,7 +603,7 @@ sub EaseeWallbox_LoadToken { "EaseeWallbox $name" . ": " . "Token is expired, requesting new one" if $tokenLifeTime < gettimeofday(); - $Token = EaseeWallbox_NewTokenRequest($hash); + $Token = _newTokenRequest($hash); } else { Log3 $name, 5, @@ -395,17 +616,17 @@ sub EaseeWallbox_LoadToken { Log3 $name, 5, "EaseeWallbox $name" . ": " . "Token will expire soon, refreshing"; - $Token = EaseeWallbox_TokenRefresh($hash); + $Token = _tokenRefresh($hash); } } return $Token if $Token; } -sub EaseeWallbox_NewTokenRequest { +sub _newTokenRequest { my $hash = shift; my $name = $hash->{NAME}; my $password - = EaseeWallbox_decrypt( InternalVal( $name, 'Password', undef ) ); + = _decrypt( InternalVal( $name, 'Password', undef ) ); my $username = InternalVal( $name, 'Username', undef ); Log3 $name, 5, "EaseeWallbox $name" . ": " . "calling NewTokenRequest()"; @@ -416,7 +637,7 @@ sub EaseeWallbox_NewTokenRequest { }; my $param = { - url => $EaseeWallbox_urls{getOAuthToken}, + url => $hash->{APIURI} . $dpoints{getOAuthToken}, header => { "Content-Type" => "application/json" }, method => 'POST', timeout => 5, @@ -466,7 +687,7 @@ sub EaseeWallbox_NewTokenRequest { return; } -sub EaseeWallbox_TokenRefresh { +sub _tokenRefresh { my $hash = shift; my $name = $hash->{NAME}; @@ -481,7 +702,7 @@ sub EaseeWallbox_TokenRefresh { }; my $param = { - url => $EaseeWallbox_urls{getRefreshToken}, + url => $hash->{APIURI} . $dpoints{getRefreshToken}, header => { "Content-Type" => "application/json" }, method => 'POST', timeout => 5, @@ -537,654 +758,7 @@ sub EaseeWallbox_TokenRefresh { return; } -sub EaseeWallbox_httpSimpleOperationCallback { - my ( $param, $err, $data ) = @_; - my $hash = $param->{hash}; - my $name = $hash->{NAME}; - - Log3 $name, 4, "Callback received." . $param->{url}; - - if ( $err ne "" ) # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist - { - Log3 $name, 3,"error while requesting ". $param->{url}. " - $err"; # Eintrag fürs Log - readingsSingleUpdate( $hash, "lastResponse", "ERROR $err", 1 ); - return undef; - } - - my $code = $param->{code}; - if ($code >= 400){ - Log3 $name, 3,"HTTPS error while requesting ". $param->{url}. " - $code"; # Eintrag fürs Log - readingsSingleUpdate( $hash, "lastResponse", "ERROR: HTTP Code $code", 1 ); - return undef; - } - - Log3 $name, 3, - "Received non-blocking data from EaseeWallbox regarding current session "; - - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{url}; - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{message} - if ( defined $param->{message} ); - Log3 $name, 4, "EaseeWallbox -> FHEM: " . $data; - Log3 $name, 5, '$err: ' . $err; - Log3 $name, 5, "method: " . $param->{method}; - Log3 $name, 2, "Something gone wrong" - if ( $data =~ "/EaseeWallboxMode/" ); - eval { - my $d = decode_json($data) if ( !$err ); - Log3 $name, 5, 'Decoded: ' . Dumper($d); - if ( defined $d and not $d eq '') { - readingsSingleUpdate( $hash, "lastResponse", 'OK - Action '. $d->{commandId}, 1 ) if defined $d->{commandId}; - readingsSingleUpdate( $hash, "lastResponse", 'ERROR: '. $d->{title}.' ('.$d->{status}.')', 1 ) if defined $d->{status} and defined $d->{title}; - return undef; - } else { - readingsSingleUpdate( $hash, "lastResponse", 'OK', 1); - return undef; - } - }; - if ($@) { - readingsSingleUpdate( $hash, "lastResponse", 'ERROR while deconding response: '. $@, 1 ); - Log3 $name, 5, 'Failure decoding: ' . $@; - } -} - -sub EaseeWallbox_httpSimpleOperationOAuth { - my ( $hash, $url, $operation, $message, $callback ) = @_; - my ( $json, $err, $data, $decoded ); - my $name = $hash->{NAME}; - my $CurrentTokenData = EaseeWallbox_LoadToken($hash); - - Log3 $name, 3, - "$CurrentTokenData->{'tokenType'} $CurrentTokenData->{'accessToken'}"; - - my $request = { - url => $url, - header => { - "Content-Type" => "application/json;charset=UTF-8", - "Authorization" => - "$CurrentTokenData->{'tokenType'} $CurrentTokenData->{'accessToken'}" - }, - callback => \&EaseeWallbox_httpSimpleOperationCallback, - method => $operation, - timeout => 6, - hideurl => 1, - }; - - $request->{callback} = $callback if defined $callback; - $request->{data} = $message if ( defined $message ); - - Log3 $name, 5, 'Request: ' . Dumper($request); - $request->{hash} = $hash; - - HttpUtils_NonblockingGet($request); - Log3 $name, 3, - "Async call executed. Waiting for callback..."; -} - -sub EaseeWallbox_ExecuteParameterlessCommand($$) { - my ( $hash, $template ) = @_; - EaseeWallbox_ExecuteCommand($hash, 'POST', $template, undef) -} - -sub EaseeWallbox_ExecuteCommand($@) { - my ( $hash, $method, $template, $message ) = @_; - my $name = $hash->{NAME}; - my $urlTemplate = $EaseeWallbox_urls{$template}; - - if ( not defined $hash ) { - Log3 'EaseeWallbox', 1, - "Error on EaseeWallbox_ExecuteCommand. Missing hash variable"; - return undef; - } - - #Check if chargerID is required in URL and replace or alert. - if ( $urlTemplate =~ m/#ChargerID#/ ) { - my $chargerId = ReadingsVal( $name, 'charger_id', undef ); - if ( not defined $chargerId ) { - Log3 'EaseeWallbox', 1, - "Error on EaseeWallbox_ExecuteCommand. Missing charger_id. Please ensure basic data is available."; - return undef; - } - $urlTemplate =~ s/#ChargerID#/$chargerId/g; - } - - #Check if siteID is required in URL and replace or alert. - if ( $urlTemplate =~ m/#SiteID#/ ) { - my $siteId = ReadingsVal( $name, 'site_id', undef ); - if ( not defined $siteId ) { - Log3 'EaseeWallbox', 1, - "Error on EaseeWallbox_ExecuteCommand. Missing site_id. Please ensure basic data is available."; - return undef; - } - $urlTemplate =~ s/#SiteID#/$siteId/g; - } - - Log3 $name, 4, "EaseeWallbox_ExecuteCommand will call Easee API for blocking value update. Name: $name"; - my $d = EaseeWallbox_httpSimpleOperationOAuth( $hash, $urlTemplate, $method, encode_json \%$message ); -} - -sub EaseeWallbox_SetCableLock($$) { - my ( $hash, $value ) = @_; - my %message; - $message{'state'} = $value; - EaseeWallbox_ExecuteCommand($hash, "POST", "setCableLockState", \%message); -} - -sub EaseeWallbox_SetPrice($$) { - my ( $hash, $value ) = @_; - my %message; - $message{'currencyId'} = "EUR"; - $message{'vat'} = "19"; - $message{'costPerKWh'} = $value; - EaseeWallbox_ExecuteCommand($hash, "POST", "setChargingPrice", \%message); -} - -sub EaseeWallbox_Attr(@) { - return undef; -} - -sub EaseeWallbox_GetChargersCallback(){ - - my ( $param, $err, $data ) = @_; - my $hash = $param->{hash}; - my $name = $hash->{NAME}; - my $d; - - Log3 $name, 4, "Callback received." . $param->{url}; - - if ( $err ne "" ) # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist - { - Log3 $name, 3,"error while requesting ". $param->{url}. " - $err"; # Eintrag fürs Log - readingsSingleUpdate( $hash, "lastResponse", "ERROR $err", 1 ); - return undef; - } - - my $code = $param->{code}; - if ($code >= 400){ - Log3 $name, 3,"HTTPS error while requesting ". $param->{url}. " - $code"; # Eintrag fürs Log - readingsSingleUpdate( $hash, "lastResponse", "ERROR: HTTP Code $code", 1 ); - return undef; - } - - Log3 $name, 3, - "Received non-blocking data from EaseeWallbox regarding current session "; - - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{url}; - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{message} - if ( defined $param->{message} ); - Log3 $name, 4, "EaseeWallbox -> FHEM: " . $data; - Log3 $name, 5, '$err: ' . $err; - Log3 $name, 5, "method: " . $param->{method}; - Log3 $name, 2, "Something gone wrong" - if ( $data =~ "/EaseeWallboxMode/" ); - eval { - $d = decode_json($data) if ( !$err ); - Log3 $name, 5, 'Decoded: ' . Dumper($d); - }; - if ($@) { - readingsSingleUpdate( $hash, "lastResponse", 'ERROR while deconding response: '. $@, 1 ); - Log3 $name, 5, 'Failure decoding: ' . $@; - return undef; - } - - if ( defined $d && ref($d) eq "HASH" && defined $d->{errors} ) { - log 1, Dumper $d; - readingsSingleUpdate( $hash, - "Error: $d->{errors}[0]->{code} / $d->{errors}[0]->{title}", - 'Undefined', 1 ); - return undef; - - } - else { - - readingsBeginUpdate($hash); - - my $site = $d->[0]; - my $circuit = $site->{circuits}->[0]; - my $charger = $circuit->{chargers}->[0]; - - readingsBeginUpdate($hash); - my $chargerId = $charger->{id}; - readingsBulkUpdate( $hash, "charger_id", $chargerId ); - readingsBulkUpdate( $hash, "charger_name", $charger->{name} ); - #readingsBulkUpdate( $hash, "charger_isTemporary", $charger->{isTemporary} ); - #readingsBulkUpdate( $hash, "charger_createdOn", $charger->{createdOn} ); - readingsEndUpdate( $hash, 1 ); - - # $readTemplate = $EaseeWallbox_urls{"getChargerDetails"}; - # $readTemplate =~ s/#ChargerID#/$chargerId/g; - # $d = EaseeWallbox_httpSimpleOperationOAuth( $hash, $readTemplate, - # 'GET' ); - - # if ( defined $d && ref($d) eq "HASH" && defined $d->{errors} ) { - # log 1, Dumper $d; - # readingsSingleUpdate( $hash, - # "Error: $d->{errors}[0]->{code} / $d->{errors}[0]->{title}", - # 'Undefined', 1 ); - # return undef; - # } - # else { - # readingsBeginUpdate($hash); - # readingsBulkUpdate( $hash, "product", $d->{product} ); - # readingsBulkUpdate( $hash, "pincode", $d->{pinCode} ); - # readingsBulkUpdate( $hash, "unitType", $d->{unitType} ); - # readingsEndUpdate( $hash, 1 ); - # } - readingsSingleUpdate( $hash, "lastResponse", 'OK', 1); - return undef; - } -} - -sub EaseeWallbox_GetChargers($) { - - my ($hash) = @_; - my $name = $hash->{NAME}; - - if ( not defined $hash ) { - my $msg = "Error on EaseeWallbox_GetChargers. Missing hash variable"; - Log3 'EaseeWallbox', 1, $msg; - return $msg; - } - - my $readTemplate = $EaseeWallbox_urls{"getChargers"}; - - EaseeWallbox_httpSimpleOperationOAuth( $hash, $readTemplate, 'GET', '', \&EaseeWallbox_GetChargersCallback ); - return undef; - - -} - -sub EaseeWallbox_GetChargerConfig($) { - - my ($hash) = @_; - my $name = $hash->{NAME}; - - my $chargerId = ReadingsVal( $name, "charger_id", undef ); - if ( not defined $chargerId ) { - my $msg - = "Error on EaseeWallbox_GetDevices. Missing Charger ID. Please get Chargers first."; - Log3 'EaseeWallbox', 1, $msg; - return $msg; - } - - my $readTemplate = $EaseeWallbox_urls{"getChargerConfiguration"}; - $readTemplate =~ s/#ChargerID#/$chargerId/g; - my $d = EaseeWallbox_httpSimpleOperationOAuth( $hash, $readTemplate, - 'GET' ); - - if ( defined $d && ref($d) eq "HASH" && defined $d->{errors} ) { - log 1, Dumper $d; - readingsSingleUpdate( $hash, 'state', - "Error: $d->{errors}[0]->{code} / $d->{errors}[0]->{title}", 1 ); - return undef; - - } - else { - - readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "charger_isEnabled", $d->{isEnabled} ); - readingsBulkUpdate( - $hash, - "lockCablePermanently", - $d->{lockCablePermanently} - ); - readingsBulkUpdate( - $hash, - "authorizationRequired", - $d->{authorizationRequired} - ); - readingsBulkUpdate( $hash, "remoteStartRequired", - $d->{remoteStartRequired} ); - readingsBulkUpdate( $hash, "smartButtonEnabled", - $d->{smartButtonEnabled} ); - readingsBulkUpdate( $hash, "wiFiSSID", $d->{wiFiSSID} ); - #readingsBulkUpdate( $hash, "charger_offlineChargingMode", - # $d->{offlineChargingMode} ); - #readingsBulkUpdate( $hash, "charger_circuitMaxCurrentP1", - # $d->{circuitMaxCurrentP1} ); - #readingsBulkUpdate( $hash, "charger_circuitMaxCurrentP2", - # $d->{circuitMaxCurrentP2} ); - #readingsBulkUpdate( $hash, "charger_circuitMaxCurrentP3", - # $d->{circuitMaxCurrentP3} ); - #readingsBulkUpdate( $hash, "charger_enableIdleCurrent", - # $d->{enableIdleCurrent} ); - #readingsBulkUpdate( - # $hash, - # "charger_limitToSinglePhaseCharging", - # $d->{limitToSinglePhaseCharging} - #); - readingsBulkUpdate( $hash, "charger_phaseModeId", $d->{phaseMode} ); - readingsBulkUpdate( $hash, "charger_phaseMode", - $EaseeWallbox_phaseModes{ $d->{phaseMode} } ); - #readingsBulkUpdate( $hash, "charger_localNodeType", - # $d->{localNodeType} ); - readingsBulkUpdate( - $hash, - "localAuthorizationRequired", - $d->{localAuthorizationRequired} - ); - #readingsBulkUpdate( $hash, "charger_localRadioChannel", - # $d->{localRadioChannel} ); - #readingsBulkUpdate( $hash, "charger_localShortAddress", - # $d->{localShortAddress} ); - #readingsBulkUpdate( - # $hash, - # "charger_localParentAddrOrNumOfNodes", - # $d->{localParentAddrOrNumOfNodes} - #); - #readingsBulkUpdate( - # $hash, - # "charger_localPreAuthorizeEnabled", - # $d->{localPreAuthorizeEnabled} - #); - #readingsBulkUpdate( - # $hash, - # "charger_allowOfflineTxForUnknownId", - # $d->{allowOfflineTxForUnknownId} - #); - readingsBulkUpdate( $hash, "maxChargerCurrent", - $d->{maxChargerCurrent} ); - readingsBulkUpdate( $hash, "ledStripBrightness", - $d->{ledStripBrightness} ); - #readingsBulkUpdate( $hash, "chargingSchedule", - # $d->{chargingSchedule} ); - - readingsEndUpdate( $hash, 1 ); - - return undef; - } - - EaseeWallbox_RequestDeviceUpdate($hash); -} - -sub EaseeWallbox_GetChargerSite($) { - - my ($hash) = @_; - my $name = $hash->{NAME}; - - my $chargerId = ReadingsVal( $name, "charger_id", undef ); - if ( not defined $chargerId ) { - my $msg - = "Error on EaseeWallbox_GetChargerSite. Missing Charger ID. Please get Chargers first."; - Log3 'EaseeWallbox', 1, $msg; - return $msg; - } - - my $readTemplate = $EaseeWallbox_urls{"getChargerSite"}; - $readTemplate =~ s/#ChargerID#/$chargerId/g; - my $d = EaseeWallbox_httpSimpleOperationOAuth( $hash, $readTemplate, - 'GET' ); - - if ( defined $d && ref($d) eq "HASH" && defined $d->{errors} ) { - log 1, Dumper $d; - readingsSingleUpdate( $hash, 'state', - "Error: $d->{errors}[0]->{code} / $d->{errors}[0]->{title}", 1 ); - return undef; - - } - else { - readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "site_key", $d->{siteKey} ); - readingsBulkUpdate( $hash, "site_id", $d->{id} ); - readingsBulkUpdate( $hash, "cost_perKWh", $d->{costPerKWh} ); - readingsBulkUpdate( $hash, "cost_perKwhExcludeVat", - $d->{costPerKwhExcludeVat} ); - readingsBulkUpdate( $hash, "cost_vat", $d->{vat} ); - readingsBulkUpdate( $hash, "cost_currency", $d->{currencyId} ); - #readingsBulkUpdate( $hash, "site_ratedCurrent", $d->{ratedCurrent} ); - #readingsBulkUpdate( $hash, "site_createdOn", $d->{createdOn} ); - #readingsBulkUpdate( $hash, "site_updatedOn", $d->{updatedOn} ); - readingsEndUpdate( $hash, 1 ); - return undef; - } - - EaseeWallbox_RequestDeviceUpdate($hash); -} - -sub EaseeWallbox_RequestCurrentSessionCallback($) { - - my ( $param, $err, $data ) = @_; - my $hash = $param->{hash}; - my $name = $hash->{NAME}; - - if ( $err ne "" ) # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist - { - Log3 $name, 3, - "error while requesting " - . $param->{url} - . " - $err"; # Eintrag fürs Log - readingsSingleUpdate( $hash, "state", "ERROR", 1 ); - return undef; - } - - Log3 $name, 3, - "Received non-blocking data from EaseeWallbox regarding current session "; - - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{url}; - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{message} - if ( defined $param->{message} ); - Log3 $name, 4, "EaseeWallbox -> FHEM: " . $data; - Log3 $name, 5, '$err: ' . $err; - Log3 $name, 5, "method: " . $param->{method}; - Log3 $name, 2, "Something gone wrong" - if ( $data =~ "/EaseeWallboxMode/" ); - eval { - my $d = decode_json($data) if ( !$err ); - Log3 $name, 5, 'Decoded: ' . Dumper($d); - - readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "session_energy", $d->{sessionEnergy} ); - readingsBulkUpdate( $hash, "session_start", $d->{sessionStart} ); - readingsBulkUpdate( $hash, "session_end", $d->{sessionEnd} ); - readingsBulkUpdate( - $hash, - "session_chargeDurationInSeconds", - $d->{chargeDurationInSeconds} - ); - readingsBulkUpdate( $hash, "session_firstEnergyTransfer", - $d->{firstEnergyTransferPeriodStart} ); - readingsBulkUpdate( $hash, "session_lastEnergyTransfer", - $d->{lastEnergyTransferPeriodStart} ); - readingsBulkUpdate( $hash, "session_pricePerKWH", - $d->{pricePrKwhIncludingVat} ); - readingsBulkUpdate( $hash, "session_chargingCost", - $d->{costIncludingVat} ); - readingsBulkUpdate( $hash, "session_id", $d->{sessionId} ); - readingsEndUpdate( $hash, 1 ); - - return undef; - } or do { - Log3 $name, 5, 'Failure decoding: ' . $@; - return undef; - } -} - -sub EaseeWallbox_RequestChargerStateCallback($) { - - my ( $param, $err, $data ) = @_; - my $hash = $param->{hash}; - my $name = $hash->{NAME}; - - if ( $err ne "" ) # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist - { - Log3 $name, 3, - "error while requesting " - . $param->{url} - . " - $err"; # Eintrag fürs Log - readingsSingleUpdate( $hash, "state", "ERROR", 1 ); - return undef; - } - - Log3 $name, 3, - "Received non-blocking data from EaseeWallbox regarding current state "; - - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{url}; - Log3 $name, 4, "FHEM -> EaseeWallbox: " . $param->{message} - if ( defined $param->{message} ); - Log3 $name, 4, "EaseeWallbox -> FHEM: " . $data; - Log3 $name, 5, '$err: ' . $err; - Log3 $name, 5, "method: " . $param->{method}; - Log3 $name, 2, "Something gone wrong" - if ( $data =~ "/EaseeWallboxMode/" ); - eval { - my $d = decode_json($data) if ( !$err ); - Log3 $name, 5, 'Decoded: ' . Dumper($d); - - readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "operationModeCode", - $d->{chargerOpMode} ); - readingsBulkUpdate( $hash, "operationMode", - $EaseeWallbox_operationModes{ $d->{chargerOpMode} } ); - - readingsBulkUpdate( $hash, "power", $d->{totalPower} ); - readingsBulkUpdate( $hash, "kWhInSession", - $d->{sessionEnergy} ); - readingsBulkUpdate( $hash, "phase", $d->{outputPhase} ); - readingsBulkUpdate( $hash, "latestPulse", $d->{latestPulse} ); - readingsBulkUpdate( $hash, "current", $d->{outputCurrent} ); - readingsBulkUpdate( $hash, "dynamicCurrent", - $d->{dynamicChargerCurrent} ); - - readingsBulkUpdate( - $hash, - "reasonCodeForNoCurrent", - $d->{reasonForNoCurrent} - ); - readingsBulkUpdate( $hash, "reasonForNoCurrent", - $EaseeWallbox_reasonsForNoCurrent{ $d->{reasonForNoCurrent} } ); - - readingsBulkUpdate( $hash, "errorCode", $d->{errorCode} ); - readingsBulkUpdate( $hash, "fatalErrorCode", $d->{fatalErrorCode} ); - - readingsBulkUpdate( $hash, "lifetimeEnergy", $d->{lifetimeEnergy} ); - readingsBulkUpdate( $hash, "online", $d->{isOnline} ); - readingsBulkUpdate( $hash, "voltage", $d->{voltage} ); - readingsBulkUpdate( $hash, "wifi_rssi", $d->{wiFiRSSI} ); - readingsBulkUpdate( $hash, "wifi_apEnabled", $d->{wiFiAPEnabled} ); - readingsBulkUpdate( $hash, "cell_rssi", $d->{cellRSSI} ); - readingsEndUpdate( $hash, 1 ); - return undef; - } or do { - Log3 $name, 5, 'Failure decoding: ' . $@; - return undef; - } -} - -sub EaseeWallbox_UpdateDueToTimer($) { - - my ($hash) = @_; - my $name = $hash->{NAME}; - - #local allows call of function without adding new timer. - #must be set before call ($hash->{LOCAL} = 1) and removed after (delete $hash->{LOCAL};) - if ( !$hash->{LOCAL} ) { - RemoveInternalTimer($hash); - - #Log3 "Test", 1, Dumper($hash); - InternalTimer( - gettimeofday() + InternalVal( $name, 'INTERVAL', undef ), - "EaseeWallbox_UpdateDueToTimer", $hash ); - readingsSingleUpdate( $hash, 'state', 'Polling', 0 ); - } - EaseeWallbox_RefreshData($hash); -} - -sub EaseeWallbox_RequestCurrentSession($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - if ( not defined $hash ) { - Log3 'EaseeWallbox', 1, - "Error on EaseeWallbox_RequestCurrentSession. Missing hash variable"; - return undef; - } - - if ( not defined ReadingsVal( $name, 'charger_id', undef ) ) { - Log3 'EaseeWallbox', 1, - "Error on EaseeWallbox_RequestCurrentSession. Missing charger_id. Please fetch basic data first."; - return undef; - } - my $chargerId = ReadingsVal( $name, "charger_id", undef ); - - $hash->{charger} = $chargerId; - - Log3 $name, 4, - "EaseeWallbox_RequestCurrentSession Called for non-blocking value update. Name: $name"; - - my $readTemplate = $EaseeWallbox_urls{"getCurrentSession"}; - $readTemplate =~ s/#ChargerID#/$chargerId/g; - - my $CurrentTokenData = EaseeWallbox_LoadToken($hash); - my $token - = "$CurrentTokenData->{'tokenType'} $CurrentTokenData->{'accessToken'}"; - Log3 $name, 4, "token beeing used: " . $token; - - my $request = { - url => $readTemplate, - header => { - "Content-Type" => "application/json;charset=UTF-8", - "Authorization" => $token, - }, - method => 'GET', - timeout => 5, - hideurl => 1, - callback => \&EaseeWallbox_RequestCurrentSessionCallback, - hash => $hash - }; - - Log3 $name, 5, 'NonBlocking Request: ' . Dumper($request); - - HttpUtils_NonblockingGet($request); -} - -sub EaseeWallbox_RequestChargerState($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - if ( not defined $hash ) { - Log3 'EaseeWallbox', 1, - "Error on EaseeWallbox_RequestChargerState. Missing hash variable"; - return undef; - } - - if ( not defined ReadingsVal( $name, 'charger_id', undef ) ) { - Log3 'EaseeWallbox', 1, - "Error on EaseeWallbox_RequestChargerState. Missing charger_id. Please fetch basic data first."; - return undef; - } - my $chargerId = ReadingsVal( $name, "charger_id", undef ); - - $hash->{charger} = $chargerId; - - Log3 $name, 4, - "EaseeWallbox_RequestChargerState Called for non-blocking value update. Name: $name"; - - my $readTemplate = $EaseeWallbox_urls{"getChargerState"}; - $readTemplate =~ s/#ChargerID#/$chargerId/g; - - my $CurrentTokenData = EaseeWallbox_LoadToken($hash); - my $token - = "$CurrentTokenData->{'tokenType'} $CurrentTokenData->{'accessToken'}"; - Log3 $name, 4, "token beeing used: " . $token; - - my $request = { - url => $readTemplate, - header => { - "Content-Type" => "application/json;charset=UTF-8", - "Authorization" => $token, - }, - method => 'GET', - timeout => 5, - hideurl => 1, - callback => \&EaseeWallbox_RequestChargerStateCallback, - hash => $hash - }; - - Log3 $name, 5, 'NonBlocking Request: ' . Dumper($request); - - HttpUtils_NonblockingGet($request); -} - -sub EaseeWallbox_encrypt($) { +sub _encrypt($) { my ($decoded) = @_; my $key = getUniqueId(); my $encoded; @@ -1200,7 +774,7 @@ sub EaseeWallbox_encrypt($) { return 'crypt:' . $encoded; } -sub EaseeWallbox_decrypt($) { +sub _decrypt($) { my ($encoded) = @_; my $key = getUniqueId(); my $decoded; @@ -1220,6 +794,11 @@ sub EaseeWallbox_decrypt($) { 1; + + + + + =pod =begin html From c198d333bfd857ce1765525be3296b74ede599b5 Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Fri, 18 Mar 2022 23:10:05 +0100 Subject: [PATCH 03/11] Added update routine --- 98_EaseeWallbox.pm | 68 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index fb2356c..e235081 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -358,7 +358,7 @@ sub Get { WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "chargers"; WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "config"; WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "sites"; - WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "update"; + RefreshData($hash) if $opt eq "update"; WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq 'baseData'; delete $hash->{LOCAL}; return undef; @@ -382,6 +382,15 @@ sub Attr { return; } +sub RefreshData{ + my $hash = shift; + my $name = $hash->{NAME}; + WriteToCloudAPI($hash, 'getChargerSite', 'GET'); + WriteToCloudAPI($hash, 'getChargerState', 'GET'); + WriteToCloudAPI($hash, 'getCurrentSession', 'GET'); +} + + sub WriteToCloudAPI { my $hash = shift; my $dpoint = shift; @@ -564,10 +573,65 @@ sub ResponseHandling { # $d->{chargingSchedule} ); readingsBulkUpdate( $hash, "lastResponse", 'OK - getReaderConfig', 1); readingsEndUpdate( $hash, 1 ); - return undef; } + if($param->{dpoint} eq 'getCurrentSession') + { + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, "session_energy", $d->{sessionEnergy} ); + readingsBulkUpdate( $hash, "session_start", $d->{sessionStart} ); + readingsBulkUpdate( $hash, "session_end", $d->{sessionEnd} ); + readingsBulkUpdate( $hash, "session_chargeDurationInSeconds", $d->{chargeDurationInSeconds} ); + readingsBulkUpdate( $hash, "session_firstEnergyTransfer",$d->{firstEnergyTransferPeriodStart} ); + readingsBulkUpdate( $hash, "session_lastEnergyTransfer", $d->{lastEnergyTransferPeriodStart} ); + readingsBulkUpdate( $hash, "session_pricePerKWH", $d->{pricePrKwhIncludingVat} ); + readingsBulkUpdate( $hash, "session_chargingCost", $d->{costIncludingVat} ); + readingsBulkUpdate( $hash, "session_id", $d->{sessionId} ); + readingsEndUpdate( $hash, 1 ); + return undef; + } + + if($param->{dpoint} eq 'getChargerSite') + { + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, "site_key", $d->{siteKey} ); + readingsBulkUpdate( $hash, "cost_perKWh", $d->{costPerKWh} ); + readingsBulkUpdate( $hash, "cost_perKwhExcludeVat", $d->{costPerKwhExcludeVat} ); + readingsBulkUpdate( $hash, "cost_vat", $d->{vat} ); + readingsBulkUpdate( $hash, "cost_currency", $d->{currencyId} ); + #readingsBulkUpdate( $hash, "site_ratedCurrent", $d->{ratedCurrent} ); + #readingsBulkUpdate( $hash, "site_createdOn", $d->{createdOn} ); + #readingsBulkUpdate( $hash, "site_updatedOn", $d->{updatedOn} ); + readingsEndUpdate( $hash, 1 ); + return undef; + } + + if($param->{dpoint} eq 'getChargerState') + { + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, "operationModeCode", $d->{chargerOpMode} ); + readingsBulkUpdate( $hash, "operationMode", $operationModes{ $d->{chargerOpMode} } ); + readingsBulkUpdate( $hash, "power", $d->{totalPower} ); + readingsBulkUpdate( $hash, "kWhInSession", $d->{sessionEnergy} ); + readingsBulkUpdate( $hash, "phase", $d->{outputPhase} ); + readingsBulkUpdate( $hash, "latestPulse", $d->{latestPulse} ); + readingsBulkUpdate( $hash, "current", $d->{outputCurrent} ); + readingsBulkUpdate( $hash, "dynamicCurrent", $d->{dynamicChargerCurrent} ); + readingsBulkUpdate( $hash, "reasonCodeForNoCurrent", $d->{reasonForNoCurrent} ); + readingsBulkUpdate( $hash, "reasonForNoCurrent", $reasonsForNoCurrent{ $d->{reasonForNoCurrent} } ); + readingsBulkUpdate( $hash, "errorCode", $d->{errorCode} ); + readingsBulkUpdate( $hash, "fatalErrorCode", $d->{fatalErrorCode} ); + readingsBulkUpdate( $hash, "lifetimeEnergy", $d->{lifetimeEnergy} ); + readingsBulkUpdate( $hash, "online", $d->{isOnline} ); + readingsBulkUpdate( $hash, "voltage", $d->{voltage} ); + readingsBulkUpdate( $hash, "wifi_rssi", $d->{wiFiRSSI} ); + readingsBulkUpdate( $hash, "wifi_apEnabled", $d->{wiFiAPEnabled} ); + readingsBulkUpdate( $hash, "cell_rssi", $d->{cellRSSI} ); + readingsEndUpdate( $hash, 1 ); + return undef; + } + readingsSingleUpdate( $hash, "lastResponse", 'OK - Action '. $d->{commandId}, 1 ) if defined $d->{commandId}; readingsSingleUpdate( $hash, "lastResponse", 'ERROR: '. $d->{title}.' ('.$d->{status}.')', 1 ) if defined $d->{status} and defined $d->{title}; return undef; From ce687d1adbee7aa46cb3c7c3edc417eec81eb3b8 Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Fri, 18 Mar 2022 23:58:40 +0100 Subject: [PATCH 04/11] Re-added set functionalities and timer --- 98_EaseeWallbox.pm | 95 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index e235081..7d0b6ba 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -105,11 +105,7 @@ GP_Export( my %gets = ( update => "noArg", health => "noArg", - baseData => "noArg", - chargers => "noArg", - sites => "noArg", - profile => "noArg", - config => "noArg", + charger => "noArg", ); my %sets = ( @@ -327,11 +323,10 @@ sub Define { readingsSingleUpdate( $hash, 'state', 'Undefined', 0 ); #Initial load of data - #EaseeWallbox_UpdateBaseData($hash); - #EaseeWallbox_RefreshData($hash); + WriteToCloudAPI($hash, 'getChargers', 'GET'); Log3 $name, 1, sprintf("EaseeWallbox_Define %s: Starting timer with interval %s", $name, InternalVal($name,'INTERVAL', undef)); - #InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "EaseeWallbox_UpdateDueToTimer", $hash) if (defined $hash); + InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "UpdateDueToTimer", $hash) if (defined $hash); return undef; } @@ -355,11 +350,8 @@ sub Get { return $cmdTemp if ( defined($cmdTemp) ); $hash->{LOCAL} = 1; - WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "chargers"; - WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "config"; - WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "sites"; - RefreshData($hash) if $opt eq "update"; - WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq 'baseData'; + WriteToCloudAPI($hash, 'getChargers', 'GET') if $opt eq "charger"; + RefreshData($hash) if $opt eq "update"; delete $hash->{LOCAL}; return undef; } @@ -375,6 +367,63 @@ sub Set { #create response, if cmd is wrong or gui asks my $cmdTemp = _GetCmdList( $hash, $opt, \%sets ); return $cmdTemp if ( defined($cmdTemp) ); + + if ( $opt eq "deactivateTimer" ) { + RemoveInternalTimer($hash); + Log3 $name, 1, + "EaseeWallbox_Set $name: Stopped the timer to automatically update readings"; + readingsSingleUpdate( $hash, 'state', 'Initialized', 0 ); + return undef; + } + elsif ( $opt eq "activateTimer" ) { + #Update once manually and then start the timer + RemoveInternalTimer($hash); + $hash->{LOCAL} = 1; + RefreshData($hash); + delete $hash->{LOCAL}; + InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "UpdateDueToTimer", $hash); + readingsSingleUpdate($hash,'state','Started',0); + Log3 $name, 1, sprintf("EaseeWallbox_Set %s: Updated readings and started timer to automatically update readings with interval %s", $name, InternalVal($name,'INTERVAL', undef)); + } + elsif ( $opt eq "interval" ) { + my $interval = shift @param; + + $interval = 60 unless defined($interval); + if ( $interval < 5 ) { $interval = 5; } + + Log3 $name, 1, "EaseeWallbox_Set $name: Set interval to" . $interval; + $hash->{INTERVAL} = $interval; + } + elsif ( $opt eq "cableLock" ) { + my %message; + $message{'state'} = $value; + WriteToCloudAPI($hash, 'setCableLockState', 'POST', \%message) + } + elsif ( $opt eq "pricePerKWH" ) { + my %message; + $message{'currencyId'} = "EUR"; + $message{'vat'} = "19"; + $message{'costPerKWh'} = shift @param; + WriteToCloudAPI($hash, 'setChargingPrice', 'POST', \%message) + } + else + { + $hash->{LOCAL} = 1; + WriteToCloudAPI($hash, 'setStartCharging', 'POST') if $opt eq "startCharging"; + WriteToCloudAPI($hash, 'setStopCharging', 'POST') if $opt eq 'stopCharging'; + WriteToCloudAPI($hash, 'setPauseCharging', 'POST') if $opt eq 'pauseCharging'; + WriteToCloudAPI($hash, 'setResumeCharging', 'POST') if $opt eq 'resumeCharging'; + WriteToCloudAPI($hash, 'setToggleCharging', 'POST') if $opt eq 'toggleCharging'; + WriteToCloudAPI($hash, 'setUpdateFirmware', 'POST') if $opt eq 'updateFirmware'; + WriteToCloudAPI($hash, 'setOverrideChargingSchedule', 'POST') if $opt eq 'overrideChargingSchedule'; + WriteToCloudAPI($hash, 'setPairRFIDTag', 'POST') if $opt eq 'pairRfidTag'; + WriteToCloudAPI($hash, 'setReboot', 'POST') if $opt eq 'reboot'; + WriteToCloudAPI($hash, 'tobedone', 'POST') if $opt eq 'enableSmartCharging'; + _loadToken($hash) if $opt eq 'refreshToken'; + delete $hash->{LOCAL}; + } + readingsSingleUpdate( $hash, 'state', 'Initialized', 0 ); + return undef; } sub Attr { @@ -390,17 +439,33 @@ sub RefreshData{ WriteToCloudAPI($hash, 'getCurrentSession', 'GET'); } +sub UpdateDueToTimer($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + + #local allows call of function without adding new timer. + #must be set before call ($hash->{LOCAL} = 1) and removed after (delete $hash->{LOCAL};) + if ( !$hash->{LOCAL} ) { + RemoveInternalTimer($hash); + #Log3 "Test", 1, Dumper($hash); + InternalTimer( + gettimeofday() + InternalVal( $name, 'INTERVAL', undef ), "UpdateDueToTimer", $hash ); + } + RefreshData($hash); +} sub WriteToCloudAPI { my $hash = shift; my $dpoint = shift; my $method = shift; + my $message = shift; my $name = $hash->{NAME}; my $url = $hash->{APIURI} . $dpoints{$dpoint}; ######### # CHANGE THIS - my $payload = ''; + my $payload; + $payload = encode_json \%$message if defined $message; my $deviceId = "WC1"; if ( not defined $hash ) { @@ -571,7 +636,7 @@ sub ResponseHandling { #); #readingsBulkUpdate( $hash, "chargingSchedule", # $d->{chargingSchedule} ); - readingsBulkUpdate( $hash, "lastResponse", 'OK - getReaderConfig', 1); + readingsBulkUpdate( $hash, "lastResponse", 'OK - getChargerConfig', 1); readingsEndUpdate( $hash, 1 ); return undef; } From e06d486cdc9c3a5f97ed0b605aa4407df54a6f0d Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Sat, 19 Mar 2022 14:16:28 +0100 Subject: [PATCH 05/11] Fixed timer. Added response status to all calls. --- 98_EaseeWallbox.pm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index 7d0b6ba..d2e8ee4 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -326,7 +326,7 @@ sub Define { WriteToCloudAPI($hash, 'getChargers', 'GET'); Log3 $name, 1, sprintf("EaseeWallbox_Define %s: Starting timer with interval %s", $name, InternalVal($name,'INTERVAL', undef)); - InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "UpdateDueToTimer", $hash) if (defined $hash); + InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "FHEM::EaseeWallbox::UpdateDueToTimer", $hash) if (defined $hash); return undef; } @@ -381,7 +381,7 @@ sub Set { $hash->{LOCAL} = 1; RefreshData($hash); delete $hash->{LOCAL}; - InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "UpdateDueToTimer", $hash); + InternalTimer(gettimeofday()+ InternalVal($name,'INTERVAL', undef), "FHEM::EaseeWallbox::UpdateDueToTimer", $hash); readingsSingleUpdate($hash,'state','Started',0); Log3 $name, 1, sprintf("EaseeWallbox_Set %s: Updated readings and started timer to automatically update readings with interval %s", $name, InternalVal($name,'INTERVAL', undef)); } @@ -449,7 +449,7 @@ sub UpdateDueToTimer($) { RemoveInternalTimer($hash); #Log3 "Test", 1, Dumper($hash); InternalTimer( - gettimeofday() + InternalVal( $name, 'INTERVAL', undef ), "UpdateDueToTimer", $hash ); + gettimeofday() + InternalVal( $name, 'INTERVAL', undef ), "FHEM::EaseeWallbox::UpdateDueToTimer", $hash ); } RefreshData($hash); } @@ -653,6 +653,7 @@ sub ResponseHandling { readingsBulkUpdate( $hash, "session_pricePerKWH", $d->{pricePrKwhIncludingVat} ); readingsBulkUpdate( $hash, "session_chargingCost", $d->{costIncludingVat} ); readingsBulkUpdate( $hash, "session_id", $d->{sessionId} ); + readingsBulkUpdate( $hash, "lastResponse", 'OK - getCurrentSession', 1); readingsEndUpdate( $hash, 1 ); return undef; } @@ -668,6 +669,7 @@ sub ResponseHandling { #readingsBulkUpdate( $hash, "site_ratedCurrent", $d->{ratedCurrent} ); #readingsBulkUpdate( $hash, "site_createdOn", $d->{createdOn} ); #readingsBulkUpdate( $hash, "site_updatedOn", $d->{updatedOn} ); + readingsBulkUpdate( $hash, "lastResponse", 'OK - getChargerSite', 1); readingsEndUpdate( $hash, 1 ); return undef; } @@ -693,6 +695,7 @@ sub ResponseHandling { readingsBulkUpdate( $hash, "wifi_rssi", $d->{wiFiRSSI} ); readingsBulkUpdate( $hash, "wifi_apEnabled", $d->{wiFiAPEnabled} ); readingsBulkUpdate( $hash, "cell_rssi", $d->{cellRSSI} ); + readingsBulkUpdate( $hash, "lastResponse", 'OK - getChargerState', 1); readingsEndUpdate( $hash, 1 ); return undef; } From 7ced8df0a9b9bc567d9ec368f382bf890f978901 Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Sat, 19 Mar 2022 14:44:14 +0100 Subject: [PATCH 06/11] changed dates to current time zone and rounded decimals to 2 fractional digits --- 98_EaseeWallbox.pm | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index d2e8ee4..f2b9820 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -8,6 +8,8 @@ use utf8; use Encode qw( encode_utf8 ); use HttpUtils; use JSON; +use DateTime; +use DateTime::Format::Strptime; # try to use JSON::MaybeXS wrapper # for chance of better performance + open code @@ -573,7 +575,8 @@ sub ResponseHandling { readingsBeginUpdate($hash); my $chargerId = $charger->{id}; - readingsBulkUpdate( $hash, "site_id", $site->{id} ); + readingsBulkUpdate( $hash, "site_id", $site->{id} ); + readingsBulkUpdate( $hash, "site_key", $d->{siteKey} ); readingsBulkUpdate( $hash, "charger_id", $chargerId ); readingsBulkUpdate( $hash, "charger_name", $charger->{name} ); readingsBulkUpdate( $hash, "lastResponse", 'OK - getReaders', 1); @@ -644,14 +647,14 @@ sub ResponseHandling { if($param->{dpoint} eq 'getCurrentSession') { readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "session_energy", $d->{sessionEnergy} ); - readingsBulkUpdate( $hash, "session_start", $d->{sessionStart} ); - readingsBulkUpdate( $hash, "session_end", $d->{sessionEnd} ); + readingsBulkUpdate( $hash, "session_energy", sprintf("%.2f",$d->{sessionEnergy}) ); + readingsBulkUpdate( $hash, "session_start", _transcodeDate($d->{sessionStart}) ); + readingsBulkUpdate( $hash, "session_end", _transcodeDate($d->{sessionEnd}) ); readingsBulkUpdate( $hash, "session_chargeDurationInSeconds", $d->{chargeDurationInSeconds} ); - readingsBulkUpdate( $hash, "session_firstEnergyTransfer",$d->{firstEnergyTransferPeriodStart} ); + readingsBulkUpdate( $hash, "session_firstEnergyTransfer", _transcodeDate($d->{firstEnergyTransferPeriodStart}) ); readingsBulkUpdate( $hash, "session_lastEnergyTransfer", $d->{lastEnergyTransferPeriodStart} ); readingsBulkUpdate( $hash, "session_pricePerKWH", $d->{pricePrKwhIncludingVat} ); - readingsBulkUpdate( $hash, "session_chargingCost", $d->{costIncludingVat} ); + readingsBulkUpdate( $hash, "session_chargingCost", sprintf("%.2f",$d->{costIncludingVat}) ); readingsBulkUpdate( $hash, "session_id", $d->{sessionId} ); readingsBulkUpdate( $hash, "lastResponse", 'OK - getCurrentSession', 1); readingsEndUpdate( $hash, 1 ); @@ -661,7 +664,6 @@ sub ResponseHandling { if($param->{dpoint} eq 'getChargerSite') { readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "site_key", $d->{siteKey} ); readingsBulkUpdate( $hash, "cost_perKWh", $d->{costPerKWh} ); readingsBulkUpdate( $hash, "cost_perKwhExcludeVat", $d->{costPerKwhExcludeVat} ); readingsBulkUpdate( $hash, "cost_vat", $d->{vat} ); @@ -680,18 +682,18 @@ sub ResponseHandling { readingsBulkUpdate( $hash, "operationModeCode", $d->{chargerOpMode} ); readingsBulkUpdate( $hash, "operationMode", $operationModes{ $d->{chargerOpMode} } ); readingsBulkUpdate( $hash, "power", $d->{totalPower} ); - readingsBulkUpdate( $hash, "kWhInSession", $d->{sessionEnergy} ); + readingsBulkUpdate( $hash, "kWhInSession", sprintf("%.2f",$d->{sessionEnergy}) ); readingsBulkUpdate( $hash, "phase", $d->{outputPhase} ); - readingsBulkUpdate( $hash, "latestPulse", $d->{latestPulse} ); + readingsBulkUpdate( $hash, "latestPulse", _transcodeDate($d->{latestPulse}) ); readingsBulkUpdate( $hash, "current", $d->{outputCurrent} ); readingsBulkUpdate( $hash, "dynamicCurrent", $d->{dynamicChargerCurrent} ); readingsBulkUpdate( $hash, "reasonCodeForNoCurrent", $d->{reasonForNoCurrent} ); readingsBulkUpdate( $hash, "reasonForNoCurrent", $reasonsForNoCurrent{ $d->{reasonForNoCurrent} } ); readingsBulkUpdate( $hash, "errorCode", $d->{errorCode} ); readingsBulkUpdate( $hash, "fatalErrorCode", $d->{fatalErrorCode} ); - readingsBulkUpdate( $hash, "lifetimeEnergy", $d->{lifetimeEnergy} ); + readingsBulkUpdate( $hash, "lifetimeEnergy", sprintf("%.2f",$d->{lifetimeEnergy}) ); readingsBulkUpdate( $hash, "online", $d->{isOnline} ); - readingsBulkUpdate( $hash, "voltage", $d->{voltage} ); + readingsBulkUpdate( $hash, "voltage", sprintf("%.2f",$d->{voltage}) ); readingsBulkUpdate( $hash, "wifi_rssi", $d->{wiFiRSSI} ); readingsBulkUpdate( $hash, "wifi_apEnabled", $d->{wiFiAPEnabled} ); readingsBulkUpdate( $hash, "cell_rssi", $d->{cellRSSI} ); @@ -926,7 +928,14 @@ sub _decrypt($) { 1; - +sub _transcodeDate{ + my $datestr = shift; + my $strp = DateTime::Format::Strptime->new(on_error=>'croak', + pattern => '%Y-%m-%dT%H:%M:%S%z'); + my $dt = $strp->parse_datetime($datestr); + $dt->set_time_zone('Europe/Berlin'); + return $dt->strftime('%Y-%m-%d %H:%M:%S'); +} From 736d7a4de7b0bff04f30c19ec07dd378a5c80052 Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Sat, 19 Mar 2022 15:19:19 +0100 Subject: [PATCH 07/11] Fixed RFID Pairing command. Added command descriptions in lastResponse Tag --- 98_EaseeWallbox.pm | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index f2b9820..a696a39 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -185,6 +185,7 @@ my %phaseModes = ( 3 => 'Locked to three phase', ); my %operationModes = ( + 0 => "Disconnected", 1 => "Standby", 2 => "Paused", 3 => 'Charging', @@ -193,6 +194,33 @@ my %operationModes = ( 6 => 'CarConnected' ); +my %commandCodes = ( + 1 => "Reboot", + 2 => "Poll single observation", + 3 => "Poll all observations", + 4 => "Upgrade Firmware", + 5 => "Download settings", + 7 => "Scan Wifi", + 11 => "Set smart charging", + 23 => "Abort charging", + 25 => "Start Charging", + 26 => "Stop Charging", + 29 => "Set enabled", + 30 => "Set cable lock", + 11 => "Set smart charging", + 40 => "Set lightstripe brightness", + 43 => "Add keys", + 44 => "Clear keys", + 48 => "Pause/Resume/Toggle Charging", + 60 => "Add schedule", + 61 => "Cear Schedule", + 62 => "Get Schedule", + 63 => "Override Schedule", + 64 => "Purge Schedule", + 69 => "Set RFID Pairing Mode", +); + + #Private function to evaluate command-lists # private funktionen beginnen immer mit _ @@ -408,6 +436,14 @@ sub Set { $message{'costPerKWh'} = shift @param; WriteToCloudAPI($hash, 'setChargingPrice', 'POST', \%message) } + elsif ( $opt eq "pairRfidTag" ) { + my $timeout = shift @param; + #if (defined $timeout and /^\d+$/) { print "is a whole number\n" } + $timeout = '60' if not defined $timeout or $timeout = ''; + my %message; + $message{'timeout'} = "60"; + WriteToCloudAPI($hash, 'setPairRFIDTag', 'POST', \%message) + } else { $hash->{LOCAL} = 1; @@ -417,8 +453,7 @@ sub Set { WriteToCloudAPI($hash, 'setResumeCharging', 'POST') if $opt eq 'resumeCharging'; WriteToCloudAPI($hash, 'setToggleCharging', 'POST') if $opt eq 'toggleCharging'; WriteToCloudAPI($hash, 'setUpdateFirmware', 'POST') if $opt eq 'updateFirmware'; - WriteToCloudAPI($hash, 'setOverrideChargingSchedule', 'POST') if $opt eq 'overrideChargingSchedule'; - WriteToCloudAPI($hash, 'setPairRFIDTag', 'POST') if $opt eq 'pairRfidTag'; + WriteToCloudAPI($hash, 'setOverrideChargingSchedule', 'POST') if $opt eq 'overrideChargingSchedule'; WriteToCloudAPI($hash, 'setReboot', 'POST') if $opt eq 'reboot'; WriteToCloudAPI($hash, 'tobedone', 'POST') if $opt eq 'enableSmartCharging'; _loadToken($hash) if $opt eq 'refreshToken'; @@ -702,7 +737,7 @@ sub ResponseHandling { return undef; } - readingsSingleUpdate( $hash, "lastResponse", 'OK - Action '. $d->{commandId}, 1 ) if defined $d->{commandId}; + readingsSingleUpdate( $hash, "lastResponse", 'OK - Action: '. $commandCodes{$d->{commandId}}, 1 ) if defined $d->{commandId}; readingsSingleUpdate( $hash, "lastResponse", 'ERROR: '. $d->{title}.' ('.$d->{status}.')', 1 ) if defined $d->{status} and defined $d->{title}; return undef; } else { From dbf7fb844ecb433c3824eaf22bc0f93745b30d3f Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Sat, 19 Mar 2022 15:32:48 +0100 Subject: [PATCH 08/11] implemented enable smartCharging and fixed issue in return evaluation in case of empty response. --- 98_EaseeWallbox.pm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index a696a39..8b2dc5f 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -444,6 +444,11 @@ sub Set { $message{'timeout'} = "60"; WriteToCloudAPI($hash, 'setPairRFIDTag', 'POST', \%message) } + elsif ( $opt eq "enableSmartCharging" ) { + my %message; + $message{'smartCharging'} = shift @param; + WriteToCloudAPI($hash, 'changeChargerSettings', 'POST', \%message) + } else { $hash->{LOCAL} = 1; @@ -455,7 +460,6 @@ sub Set { WriteToCloudAPI($hash, 'setUpdateFirmware', 'POST') if $opt eq 'updateFirmware'; WriteToCloudAPI($hash, 'setOverrideChargingSchedule', 'POST') if $opt eq 'overrideChargingSchedule'; WriteToCloudAPI($hash, 'setReboot', 'POST') if $opt eq 'reboot'; - WriteToCloudAPI($hash, 'tobedone', 'POST') if $opt eq 'enableSmartCharging'; _loadToken($hash) if $opt eq 'refreshToken'; delete $hash->{LOCAL}; } @@ -600,7 +604,7 @@ sub ResponseHandling { eval { my $d = decode_json($data); Log3 $name, 5, 'Decoded: ' . Dumper($d); - if ( defined $d and $d ne '') { + if ( defined $d and $d ne '' and ref($d) eq "HASH") { if($param->{dpoint} eq 'getChargers') { @@ -736,7 +740,6 @@ sub ResponseHandling { readingsEndUpdate( $hash, 1 ); return undef; } - readingsSingleUpdate( $hash, "lastResponse", 'OK - Action: '. $commandCodes{$d->{commandId}}, 1 ) if defined $d->{commandId}; readingsSingleUpdate( $hash, "lastResponse", 'ERROR: '. $d->{title}.' ('.$d->{status}.')', 1 ) if defined $d->{status} and defined $d->{title}; return undef; From 4f7350cc3e50bb6533cc258a8e7a9ad1ef05fd65 Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Sat, 19 Mar 2022 16:00:05 +0100 Subject: [PATCH 09/11] added commands: enabled/disabled/smartbutton/authrequired --- 98_EaseeWallbox.pm | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index 8b2dc5f..37da2d3 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -111,6 +111,10 @@ my %gets = ( ); my %sets = ( + enabled => "", + disabled => "", + enableSmartButton => "true,false", + authorizationRequired => "true,false", startCharging => "", stopCharging => "", pauseCharging => "", @@ -449,9 +453,29 @@ sub Set { $message{'smartCharging'} = shift @param; WriteToCloudAPI($hash, 'changeChargerSettings', 'POST', \%message) } + elsif ( $opt eq "enabled" ) { + my %message; + $message{'enabled'} = "true"; + WriteToCloudAPI($hash, 'changeChargerSettings', 'POST', \%message) + } + elsif ( $opt eq "disabled" ) { + my %message; + $message{'enabled'} = "false"; + WriteToCloudAPI($hash, 'changeChargerSettings', 'POST', \%message) + } + elsif ( $opt eq "authorizationRequired" ) { + my %message; + $message{'authorizationRequired'} = shift @param; + WriteToCloudAPI($hash, 'changeChargerSettings', 'POST', \%message) + } + elsif ( $opt eq "enableSmartButton" ) { + my %message; + $message{'smartButtonEnabled'} = shift @param; + WriteToCloudAPI($hash, 'changeChargerSettings', 'POST', \%message) + } else { - $hash->{LOCAL} = 1; + $hash->{LOCAL} = 1; WriteToCloudAPI($hash, 'setStartCharging', 'POST') if $opt eq "startCharging"; WriteToCloudAPI($hash, 'setStopCharging', 'POST') if $opt eq 'stopCharging'; WriteToCloudAPI($hash, 'setPauseCharging', 'POST') if $opt eq 'pauseCharging'; @@ -604,18 +628,20 @@ sub ResponseHandling { eval { my $d = decode_json($data); Log3 $name, 5, 'Decoded: ' . Dumper($d); + Log3 $name, 5, 'Ref of d: ' . ref($d); + $d = $d->[0] if ref($d) eq "ARRAY"; if ( defined $d and $d ne '' and ref($d) eq "HASH") { if($param->{dpoint} eq 'getChargers') { - my $site = $d->[0]; + my $site = $d; my $circuit = $site->{circuits}->[0]; my $charger = $circuit->{chargers}->[0]; readingsBeginUpdate($hash); my $chargerId = $charger->{id}; readingsBulkUpdate( $hash, "site_id", $site->{id} ); - readingsBulkUpdate( $hash, "site_key", $d->{siteKey} ); + readingsBulkUpdate( $hash, "site_key", $site->{siteKey} ); readingsBulkUpdate( $hash, "charger_id", $chargerId ); readingsBulkUpdate( $hash, "charger_name", $charger->{name} ); readingsBulkUpdate( $hash, "lastResponse", 'OK - getReaders', 1); From a6a6105a6eb09d4ab3ac195b083194e1f87709d8 Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Sun, 20 Mar 2022 00:03:46 +0100 Subject: [PATCH 10/11] added some historic data --- 98_EaseeWallbox.pm | 55 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index 37da2d3..321b7a0 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -83,6 +83,7 @@ BEGIN { readingsEndUpdate readingsBulkUpdate readingsSingleUpdate + readingsDelete InternalVal ReadingsVal RemoveInternalTimer @@ -502,6 +503,8 @@ sub RefreshData{ WriteToCloudAPI($hash, 'getChargerSite', 'GET'); WriteToCloudAPI($hash, 'getChargerState', 'GET'); WriteToCloudAPI($hash, 'getCurrentSession', 'GET'); + WriteToCloudAPI($hash, 'getChargerSessionsMonthly', 'GET'); + WriteToCloudAPI($hash, 'getChargerSessionsDaily', 'GET'); } sub UpdateDueToTimer($) { @@ -606,6 +609,19 @@ sub ResponseHandling { } my $code = $param->{code}; + if ($code eq 404 and $param->{dpoint} eq 'getCurrentSession'){ + readingsDelete($hash, 'session_energy' ); + readingsDelete($hash, 'session_start' ); + readingsDelete($hash, 'session_end' ); + readingsDelete($hash, 'session_chargeDurationInSeconds' ); + readingsDelete($hash, 'session_firstEnergyTransfer' ); + readingsDelete($hash, 'session_lastEnergyTransfer' ); + readingsDelete($hash, 'session_pricePerKWH' ); + readingsDelete($hash, 'session_chargingCost' ); + readingsDelete($hash, 'session_id' ); + return undef; + } + if ($code >= 400){ Log3 $name, 3,"HTTPS error while requesting ". $param->{url}. " - $code"; # Eintrag fürs Log readingsSingleUpdate( $hash, "lastResponse", "ERROR: HTTP Code $code", 1 ); @@ -629,12 +645,11 @@ sub ResponseHandling { my $d = decode_json($data); Log3 $name, 5, 'Decoded: ' . Dumper($d); Log3 $name, 5, 'Ref of d: ' . ref($d); - $d = $d->[0] if ref($d) eq "ARRAY"; - if ( defined $d and $d ne '' and ref($d) eq "HASH") { + if ( defined $d and $d ne '' and ref($d) eq "HASH" or (ref($d) eq "ARRAY" and $d gt 0)) { if($param->{dpoint} eq 'getChargers') { - my $site = $d; + my $site = $d->[0]; my $circuit = $site->{circuits}->[0]; my $charger = $circuit->{chargers}->[0]; @@ -650,6 +665,37 @@ sub ResponseHandling { return; } + if($param->{dpoint} eq 'getChargerSessionsDaily') + { + Log3 $name, 5, 'Evaluating getChargerSessionsDaily'; + my @x = $d; + my @a = (-5..-1); + readingsBeginUpdate($hash); + for(@a){ + Log3 $name, 5, 'laeuft noch: '. $_; + readingsBulkUpdate( $hash, "daily_".($_ +1)."_energy", sprintf("%.2f",$d->[$_]->{'totalEnergyUsage'}) ); + readingsBulkUpdate( $hash, "daily_".($_ +1)."_cost", sprintf("%.2f",$d->[$_]->{'totalCost'}) ); + } + readingsEndUpdate( $hash, 1 ); + return; + } + + if($param->{dpoint} eq 'getChargerSessionsMonthly') + { + Log3 $name, 5, 'Evaluating getChargerSessionsMonthly'; + my @x = $d; + my @a = (-6..-1); + readingsBeginUpdate($hash); + for(@a){ + Log3 $name, 5, 'laeuft noch: '. $_; + readingsBulkUpdate( $hash, "monthly_".($_ +1)."_energy", sprintf("%.2f",$d->[$_]->{'totalEnergyUsage'}) ); + readingsBulkUpdate( $hash, "monthly_".($_ +1)."_cost", sprintf("%.2f",$d->[$_]->{'totalCost'}) ); + } + readingsEndUpdate( $hash, 1 ); + return; + } + + if($param->{dpoint} eq 'getChargerConfiguration') { readingsBeginUpdate($hash); @@ -766,6 +812,8 @@ sub ResponseHandling { readingsEndUpdate( $hash, 1 ); return undef; } + + $d = $d->[0] if ref($d) eq "ARRAY"; readingsSingleUpdate( $hash, "lastResponse", 'OK - Action: '. $commandCodes{$d->{commandId}}, 1 ) if defined $d->{commandId}; readingsSingleUpdate( $hash, "lastResponse", 'ERROR: '. $d->{title}.' ('.$d->{status}.')', 1 ) if defined $d->{status} and defined $d->{title}; return undef; @@ -774,6 +822,7 @@ sub ResponseHandling { return undef; } }; + if ($@) { readingsSingleUpdate( $hash, "lastResponse", 'ERROR while deconding response: '. $@, 1 ); Log3 $name, 5, 'Failure decoding: ' . $@; From 1d86d6d756db93cd914bdefbbab16f12965e7cde Mon Sep 17 00:00:00 2001 From: Matthias Sandmann Date: Sun, 20 Mar 2022 20:48:37 +0100 Subject: [PATCH 11/11] added led brightness cmd and fixed date and time issues --- 98_EaseeWallbox.pm | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/98_EaseeWallbox.pm b/98_EaseeWallbox.pm index 321b7a0..1e28bfc 100644 --- a/98_EaseeWallbox.pm +++ b/98_EaseeWallbox.pm @@ -127,6 +127,7 @@ my %sets = ( reboot => "noArg", updateFirmware => "noArg", enableSmartCharging => "true,false", + ledStripBrightness => "", overrideChargingSchedule => "", pairRfidTag => "", pricePerKWH => "", @@ -474,6 +475,11 @@ sub Set { $message{'smartButtonEnabled'} = shift @param; WriteToCloudAPI($hash, 'changeChargerSettings', 'POST', \%message) } + elsif ( $opt eq "ledStripBrightness" ) { + my %message; + $message{'ledStripBrightness'} = shift @param; + WriteToCloudAPI($hash, 'changeChargerSettings', 'POST', \%message) + } else { $hash->{LOCAL} = 1; @@ -503,6 +509,7 @@ sub RefreshData{ WriteToCloudAPI($hash, 'getChargerSite', 'GET'); WriteToCloudAPI($hash, 'getChargerState', 'GET'); WriteToCloudAPI($hash, 'getCurrentSession', 'GET'); + WriteToCloudAPI($hash, 'getChargerConfiguration', 'GET'); WriteToCloudAPI($hash, 'getChargerSessionsMonthly', 'GET'); WriteToCloudAPI($hash, 'getChargerSessionsDaily', 'GET'); } @@ -699,15 +706,15 @@ sub ResponseHandling { if($param->{dpoint} eq 'getChargerConfiguration') { readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "charger_isEnabled", $d->{isEnabled} ); - readingsBulkUpdate( $hash, "lockCablePermanently", $d->{lockCablePermanently} ); - readingsBulkUpdate($hash, "authorizationRequired", $d->{authorizationRequired}); - readingsBulkUpdate( $hash, "remoteStartRequired", $d->{remoteStartRequired} ); - readingsBulkUpdate( $hash, "smartButtonEnabled", $d->{smartButtonEnabled} ); + readingsBulkUpdate( $hash, "isEnabled", $d->{isEnabled} ); + readingsBulkUpdate( $hash, "isCablePermanentlyLocked", $d->{lockCablePermanently} ); + readingsBulkUpdate($hash, "isAuthorizationRequired", $d->{authorizationRequired}); + readingsBulkUpdate( $hash, "isRemoteStartRequired", $d->{remoteStartRequired} ); + readingsBulkUpdate( $hash, "isSmartButtonEnabled", $d->{smartButtonEnabled} ); readingsBulkUpdate( $hash, "wiFiSSID", $d->{wiFiSSID} ); - readingsBulkUpdate( $hash, "charger_phaseModeId", $d->{phaseMode} ); - readingsBulkUpdate( $hash, "charger_phaseMode",$phaseModes{ $d->{phaseMode} } ); - readingsBulkUpdate($hash, "localAuthorizationRequired",$d->{localAuthorizationRequired}); + readingsBulkUpdate( $hash, "phaseModeId", $d->{phaseMode} ); + readingsBulkUpdate( $hash, "phaseMode",$phaseModes{ $d->{phaseMode} } ); + readingsBulkUpdate($hash, "isLocalAuthorizationRequired",$d->{localAuthorizationRequired}); readingsBulkUpdate( $hash, "maxChargerCurrent", $d->{maxChargerCurrent} ); readingsBulkUpdate( $hash, "ledStripBrightness", $d->{ledStripBrightness} ); #readingsBulkUpdate( $hash, "charger_offlineChargingMode", @@ -759,11 +766,15 @@ sub ResponseHandling { { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "session_energy", sprintf("%.2f",$d->{sessionEnergy}) ); - readingsBulkUpdate( $hash, "session_start", _transcodeDate($d->{sessionStart}) ); - readingsBulkUpdate( $hash, "session_end", _transcodeDate($d->{sessionEnd}) ); + my $value = defined $d->{sessionStart} ? _transcodeDate($d->{sessionStart}) : 'N/A'; + readingsBulkUpdate( $hash, "session_start", $value ); + my $value = defined $d->{sessionEnd} ? _transcodeDate($d->{sessionEnd}) : 'N/A'; + readingsBulkUpdate( $hash, "session_end", $value ); readingsBulkUpdate( $hash, "session_chargeDurationInSeconds", $d->{chargeDurationInSeconds} ); - readingsBulkUpdate( $hash, "session_firstEnergyTransfer", _transcodeDate($d->{firstEnergyTransferPeriodStart}) ); - readingsBulkUpdate( $hash, "session_lastEnergyTransfer", $d->{lastEnergyTransferPeriodStart} ); + my $value = defined $d->{firstEnergyTransferPeriodStart} ? _transcodeDate($d->{firstEnergyTransferPeriodStart}) : 'N/A'; + readingsBulkUpdate( $hash, "session_firstEnergyTransfer", $value ); + my $value = defined $d->{lastEnergyTransferPeriodStart} ? _transcodeDate($d->{lastEnergyTransferPeriodStart}) : 'N/A'; + readingsBulkUpdate( $hash, "session_lastEnergyTransfer", $value ); readingsBulkUpdate( $hash, "session_pricePerKWH", $d->{pricePrKwhIncludingVat} ); readingsBulkUpdate( $hash, "session_chargingCost", sprintf("%.2f",$d->{costIncludingVat}) ); readingsBulkUpdate( $hash, "session_id", $d->{sessionId} ); @@ -792,7 +803,7 @@ sub ResponseHandling { readingsBeginUpdate($hash); readingsBulkUpdate( $hash, "operationModeCode", $d->{chargerOpMode} ); readingsBulkUpdate( $hash, "operationMode", $operationModes{ $d->{chargerOpMode} } ); - readingsBulkUpdate( $hash, "power", $d->{totalPower} ); + readingsBulkUpdate( $hash, "power", sprintf("%.2f",$d->{totalPower}) ); readingsBulkUpdate( $hash, "kWhInSession", sprintf("%.2f",$d->{sessionEnergy}) ); readingsBulkUpdate( $hash, "phase", $d->{outputPhase} ); readingsBulkUpdate( $hash, "latestPulse", _transcodeDate($d->{latestPulse}) ); @@ -1043,6 +1054,7 @@ sub _decrypt($) { sub _transcodeDate{ my $datestr = shift; + Log3 'EaseeWallbox', 5, 'date to parse: ' . $datestr; my $strp = DateTime::Format::Strptime->new(on_error=>'croak', pattern => '%Y-%m-%dT%H:%M:%S%z'); my $dt = $strp->parse_datetime($datestr);