1151 lines
39 KiB
Perl
1151 lines
39 KiB
Perl
package main;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Data::Dumper;
|
|
use utf8;
|
|
use Encode qw( encode_utf8 );
|
|
use HttpUtils;
|
|
use JSON;
|
|
|
|
my %EaseeWallbox_gets = (
|
|
update => "noArg",
|
|
health => "noArg",
|
|
baseData => "noArg",
|
|
chargers => "noArg",
|
|
sites => "noArg",
|
|
profile => "noArg",
|
|
config => "noArg",
|
|
);
|
|
|
|
my %EaseeWallbox_sets = (
|
|
startCharging => "",
|
|
stopCharging => "",
|
|
pauseCharging => "",
|
|
resumeCharging => "",
|
|
toggleCharging => "",
|
|
interval => "",
|
|
refreshToken => "noArg",
|
|
cableLock => "true,false",
|
|
reboot => "noArg",
|
|
updateFirmware => "noArg",
|
|
enableSmartCharging => "true,false",
|
|
overrideChargingSchedule => "",
|
|
pairRfidTag => "",
|
|
pricePerKWH => "",
|
|
activateTimer => "",
|
|
deactivateTimer => "",
|
|
);
|
|
|
|
my %url = (
|
|
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 %reasonForNoCurrent = (
|
|
0 => 'OK', #charger is allocated current
|
|
1 => 'MaxCircuitCurrentTooLow',
|
|
2 => 'MaxDynamicCircuitCurrentTooLow',
|
|
3 => 'MaxDynamicOfflineFallbackCircuitCurrentTooLow',
|
|
4 => 'CircuitFuseTooLow',
|
|
5 => 'WaitingInQueue',
|
|
6 => 'WaitingInFully'
|
|
, #charged queue (charger assumes one of: EV uses delayed charging, EV charging complete)
|
|
7 => 'IllegalGridType',
|
|
8 => 'PrimaryUnitHasNotReceivedCurrentRequestFromSecondaryUnit',
|
|
50 => 'SecondaryUnitNotRequestingCurrent', #no car connected...
|
|
51 => 'MaxChargerCurrentTooLow',
|
|
52 => 'MaxDynamicChargerCurrentTooLow',
|
|
53 => 'ChargerDisabled',
|
|
54 => 'PendingScheduledCharging',
|
|
55 => 'PendingAuthorization',
|
|
56 => 'ChargerInErrorState',
|
|
100 => 'Undefined'
|
|
);
|
|
my %phaseMode = (
|
|
1 => 'Locked to single phase',
|
|
2 => 'Auto',
|
|
3 => 'Locked to three phase',
|
|
);
|
|
|
|
my %operationMode = (
|
|
1 => "Standby",
|
|
2 => "Paused",
|
|
3 => 'Charging',
|
|
4 => 'Completed',
|
|
5 => 'Error',
|
|
6 => 'CarConnected'
|
|
);
|
|
|
|
#Private function to evaluate command-lists
|
|
#############################
|
|
sub EaseeWallbox_getCmdList ($$$) {
|
|
my ( $hash, $cmd, $commands ) = @_;
|
|
|
|
my %cmdArray = %$commands;
|
|
my $name = $hash->{NAME};
|
|
|
|
#return, if cmd is valid
|
|
return undef if ( defined($cmd) and defined( $cmdArray{$cmd} ) );
|
|
|
|
#response for gui or the user, if command is invalid
|
|
my $retVal;
|
|
foreach my $mySet ( keys %cmdArray ) {
|
|
|
|
#append set-command
|
|
$retVal = $retVal . " " if ( defined($retVal) );
|
|
$retVal = $retVal . $mySet;
|
|
|
|
#get options
|
|
my $myOpt = $cmdArray{$mySet};
|
|
|
|
#append option, if valid
|
|
$retVal = $retVal . ":" . $myOpt
|
|
if ( defined($myOpt) and ( length($myOpt) > 0 ) );
|
|
$myOpt = "" if ( !defined($myOpt) );
|
|
|
|
#Logging makes me crazy...
|
|
#Log3 ($name, 5, "parse cmd-table - Set:$mySet, Option:$myOpt, RetVal:$retVal");
|
|
}
|
|
if ( !defined($retVal) ) {
|
|
$retVal = "error while parsing set-table";
|
|
}
|
|
else {
|
|
$retVal = "Unknown argument $cmd, choose one of " . $retVal;
|
|
}
|
|
return $retVal;
|
|
}
|
|
|
|
sub EaseeWallbox_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;
|
|
|
|
Log 3, "EaseeWallbox module initialized.";
|
|
}
|
|
|
|
sub EaseeWallbox_Define($$) {
|
|
my ( $hash, $def ) = @_;
|
|
my @param = split( "[ \t]+", $def );
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $name, 3, "EaseeWallbox_Define $name: called ";
|
|
|
|
my $errmsg = '';
|
|
|
|
# Check parameter(s) - Must be min 4 in total (counts strings not purly parameter, interval is optional)
|
|
if ( int(@param) < 4 ) {
|
|
$errmsg = return
|
|
"syntax error: define <name> EaseeWallbox <username> <password> [Interval]";
|
|
Log3 $name, 1, "EaseeWallbox $name: " . $errmsg;
|
|
return $errmsg;
|
|
}
|
|
|
|
#Check if the username is an email address
|
|
if ( $param[2] =~ /^.+@.+$/ ) {
|
|
my $username = $param[2];
|
|
$hash->{Username} = $username;
|
|
}
|
|
else {
|
|
$errmsg
|
|
= "specify valid email address within the field username. Format: define <name> EaseeWallbox <username> <password> [interval]";
|
|
Log3 $name, 1, "EaseeWallbox $name: " . $errmsg;
|
|
return $errmsg;
|
|
}
|
|
|
|
#Take password and use custom encryption.
|
|
# Encryption is taken from fitbit / withings module
|
|
my $password = EaseeWallbox_encrypt( $param[3] );
|
|
|
|
$hash->{Password} = $password;
|
|
|
|
if ( defined $param[4] ) {
|
|
$hash->{DEF} = sprintf( "%s %s %s",
|
|
InternalVal( $name, 'Username', undef ),
|
|
$password, $param[4] );
|
|
}
|
|
else {
|
|
$hash->{DEF} = sprintf( "%s %s",
|
|
InternalVal( $name, 'Username', undef ), $password );
|
|
}
|
|
|
|
#Check if interval is set and numeric.
|
|
#If not set -> set to 60 seconds
|
|
#If less then 5 seconds set to 5
|
|
#If not an integer abort with failure.
|
|
my $interval = 60;
|
|
if ( defined $param[4] ) {
|
|
if ( $param[4] =~ /^\d+$/ ) {
|
|
$interval = $param[4];
|
|
}
|
|
else {
|
|
$errmsg
|
|
= "Specify valid integer value for interval. Whole numbers > 5 only. Format: define <name> EaseeWallbox <username> <password> [interval]";
|
|
Log3 $name, 1, "EaseeWallbox $name: " . $errmsg;
|
|
return $errmsg;
|
|
}
|
|
}
|
|
|
|
if ( $interval < 5 ) { $interval = 5; }
|
|
$hash->{INTERVAL} = $interval;
|
|
|
|
readingsSingleUpdate( $hash, 'state', 'Undefined', 0 );
|
|
|
|
#Initial load of data
|
|
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);
|
|
return undef;
|
|
}
|
|
|
|
sub EaseeWallbox_Undef($$) {
|
|
my ( $hash, $arg ) = @_;
|
|
|
|
RemoveInternalTimer($hash);
|
|
return undef;
|
|
}
|
|
|
|
sub EaseeWallbox_Get($@) {
|
|
my ( $hash, $name, @args ) = @_;
|
|
|
|
return '"get EaseeWallbox" needs at least one argument'
|
|
if ( int(@args) < 1 );
|
|
|
|
my $opt = shift @args;
|
|
|
|
#create response, if cmd is wrong or gui asks
|
|
my $cmdTemp = EaseeWallbox_getCmdList( $hash, $opt, \%EaseeWallbox_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';
|
|
delete $hash->{LOCAL};
|
|
return undef;
|
|
}
|
|
|
|
sub EaseeWallbox_Set($@) {
|
|
my ( $hash, $name, @param ) = @_;
|
|
|
|
return '"set $name" needs at least one argument' if ( int(@param) < 1 );
|
|
|
|
my $opt = shift @param;
|
|
my $value = join( "", @param );
|
|
|
|
#create response, if cmd is wrong or gui asks
|
|
my $cmdTemp = EaseeWallbox_getCmdList( $hash, $opt, \%EaseeWallbox_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;
|
|
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));
|
|
}
|
|
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;
|
|
} 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';
|
|
|
|
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;
|
|
}
|
|
|
|
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)<br/>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 {
|
|
my $hash = shift;
|
|
my $name = $hash->{NAME};
|
|
my $tokenLifeTime = $hash->{TOKEN_LIFETIME};
|
|
$tokenLifeTime = 0 if ( !defined $tokenLifeTime || $tokenLifeTime eq '' );
|
|
my $Token = undef;
|
|
|
|
$Token = $hash->{'.TOKEN'};
|
|
|
|
if ( $@ || $tokenLifeTime < gettimeofday() ) {
|
|
Log3 $name, 5,
|
|
"EaseeWallbox $name" . ": "
|
|
. "Error while loading: $@ ,requesting new one"
|
|
if $@;
|
|
Log3 $name, 5,
|
|
"EaseeWallbox $name" . ": "
|
|
. "Token is expired, requesting new one"
|
|
if $tokenLifeTime < gettimeofday();
|
|
$Token = EaseeWallbox_NewTokenRequest($hash);
|
|
}
|
|
else {
|
|
Log3 $name, 5,
|
|
"EaseeWallbox $name" . ": "
|
|
. "Token expires at "
|
|
. localtime($tokenLifeTime);
|
|
|
|
# if token is about to expire, refresh him
|
|
if ( ( $tokenLifeTime - 45 ) < gettimeofday() ) {
|
|
Log3 $name, 5,
|
|
"EaseeWallbox $name" . ": "
|
|
. "Token will expire soon, refreshing";
|
|
$Token = EaseeWallbox_TokenRefresh($hash);
|
|
}
|
|
}
|
|
return $Token if $Token;
|
|
}
|
|
|
|
sub EaseeWallbox_NewTokenRequest {
|
|
my $hash = shift;
|
|
my $name = $hash->{NAME};
|
|
my $password
|
|
= EaseeWallbox_decrypt( InternalVal( $name, 'Password', undef ) );
|
|
my $username = InternalVal( $name, 'Username', undef );
|
|
|
|
Log3 $name, 5, "EaseeWallbox $name" . ": " . "calling NewTokenRequest()";
|
|
|
|
my $data = {
|
|
userName => $username,
|
|
password => $password,
|
|
};
|
|
|
|
my $param = {
|
|
url => $url{getOAuthToken},
|
|
header => { "Content-Type" => "application/json" },
|
|
method => 'POST',
|
|
timeout => 5,
|
|
hash => $hash,
|
|
data => encode_json $data
|
|
};
|
|
Log3 $name, 5, 'Request: ' . Dumper($param);
|
|
|
|
#Log3 $name, 5, 'Blocking GET: ' . Dumper($param);
|
|
#Log3 $name, $reqDebug, "EaseeWallbox $name" . ": " . "Request $AuthURL";
|
|
my ( $err, $returnData ) = HttpUtils_BlockingGet($param);
|
|
|
|
if ( $err ne "" ) {
|
|
Log3 $name, 3,
|
|
"EaseeWallbox $name" . ": "
|
|
. "NewTokenRequest: Error while requesting "
|
|
. $param->{url}
|
|
. " - $err";
|
|
}
|
|
elsif ( $returnData ne "" ) {
|
|
Log3 $name, 5, "url " . $param->{url} . " returned: $returnData";
|
|
my $decoded_data = eval { decode_json($returnData) };
|
|
if ($@) {
|
|
Log3 $name, 3, "EaseeWallbox $name" . ": "
|
|
. "NewTokenRequest: decode_json failed, invalid json. error: $@ ";
|
|
}
|
|
else {
|
|
#write token data in hash
|
|
if ( defined($decoded_data) ) {
|
|
$hash->{'.TOKEN'} = $decoded_data;
|
|
}
|
|
|
|
# token lifetime management
|
|
if ( defined($decoded_data) ) {
|
|
$hash->{TOKEN_LIFETIME}
|
|
= gettimeofday() + $decoded_data->{'expires_in'};
|
|
}
|
|
$hash->{TOKEN_LIFETIME_HR} = localtime( $hash->{TOKEN_LIFETIME} );
|
|
Log3 $name, 5,
|
|
"EaseeWallbox $name" . ": "
|
|
. "Retrived new authentication token successfully. Valid until "
|
|
. localtime( $hash->{TOKEN_LIFETIME} );
|
|
$hash->{STATE} = "reachable";
|
|
return $decoded_data;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub EaseeWallbox_TokenRefresh {
|
|
my $hash = shift;
|
|
my $name = $hash->{NAME};
|
|
|
|
my $Token = undef;
|
|
|
|
# load token
|
|
$Token = $hash->{'.TOKEN'};
|
|
|
|
my $data = {
|
|
accessToken => $Token->{'accessToken'},
|
|
refreshToken => $Token->{'refreshToken'}
|
|
};
|
|
|
|
my $param = {
|
|
url => $url{getRefreshToken},
|
|
header => { "Content-Type" => "application/json" },
|
|
method => 'POST',
|
|
timeout => 5,
|
|
hash => $hash,
|
|
data => encode_json $data
|
|
};
|
|
|
|
Log3 $name, 5, 'Request: ' . Dumper($param);
|
|
|
|
#Log3 $name, 5, 'Blocking GET TokenRefresh: ' . Dumper($param);
|
|
#Log3 $name, $reqDebug, "EaseeWallbox $name" . ": " . "Request $AuthURL";
|
|
my ( $err, $returnData ) = HttpUtils_BlockingGet($param);
|
|
|
|
if ( $err ne "" ) {
|
|
Log3 $name, 3,
|
|
"EaseeWallbox $name" . ": "
|
|
. "TokenRefresh: Error in token retrival while requesting "
|
|
. $param->{url}
|
|
. " - $err";
|
|
$hash->{STATE} = "error";
|
|
}
|
|
|
|
elsif ( $returnData ne "" ) {
|
|
Log3 $name, 5, "url " . $param->{url} . " returned: $returnData";
|
|
my $decoded_data = eval { decode_json($returnData); };
|
|
|
|
if ($@) {
|
|
Log3 $name, 3,
|
|
"EaseeWallbox $name" . ": "
|
|
. "TokenRefresh: decode_json failed, invalid json. error:$@\n"
|
|
if $@;
|
|
$hash->{STATE} = "error";
|
|
}
|
|
else {
|
|
#write token data in file
|
|
if ( defined($decoded_data) ) {
|
|
$hash->{'.TOKEN'} = $decoded_data;
|
|
|
|
}
|
|
|
|
# token lifetime management
|
|
$hash->{TOKEN_LIFETIME}
|
|
= gettimeofday() + $decoded_data->{'expires_in'};
|
|
$hash->{TOKEN_LIFETIME_HR} = localtime( $hash->{TOKEN_LIFETIME} );
|
|
Log3 $name, 5,
|
|
"EaseeWallbox $name" . ": "
|
|
. "TokenRefresh: Refreshed authentication token successfully. Valid until "
|
|
. localtime( $hash->{TOKEN_LIFETIME} );
|
|
$hash->{STATE} = "reachable";
|
|
return $decoded_data;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub EaseeWallbox_httpSimpleOperationOAuth($$$;$) {
|
|
my ( $hash, $url, $operation, $message ) = @_;
|
|
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'}"
|
|
},
|
|
method => $operation,
|
|
timeout => 6,
|
|
hideurl => 1
|
|
};
|
|
|
|
$request->{data} = $message if ( defined $message );
|
|
Log3 $name, 5, 'Request: ' . Dumper($request);
|
|
|
|
( $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;
|
|
}
|
|
}
|
|
|
|
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 = $url{$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_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 = $url{"getChargers"};
|
|
|
|
my $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);
|
|
|
|
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 = $url{"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($) {
|
|
|
|
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 = $url{"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",
|
|
$phaseMode{ $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 = $url{"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",
|
|
$operationMode{ $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",
|
|
$reasonForNoCurrent{ $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 = $url{"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 = $url{"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($) {
|
|
my ($decoded) = @_;
|
|
my $key = getUniqueId();
|
|
my $encoded;
|
|
|
|
return $decoded if ( $decoded =~ /crypt:/ );
|
|
|
|
for my $char ( split //, $decoded ) {
|
|
my $encode = chop($key);
|
|
$encoded .= sprintf( "%.2x", ord($char) ^ ord($encode) );
|
|
$key = $encode . $key;
|
|
}
|
|
|
|
return 'crypt:' . $encoded;
|
|
}
|
|
|
|
sub EaseeWallbox_decrypt($) {
|
|
my ($encoded) = @_;
|
|
my $key = getUniqueId();
|
|
my $decoded;
|
|
|
|
return $encoded if ( $encoded !~ /crypt:/ );
|
|
|
|
$encoded = $1 if ( $encoded =~ /crypt:(.*)/ );
|
|
|
|
for my $char ( map { pack( 'C', hex($_) ) } ( $encoded =~ /(..)/g ) ) {
|
|
my $decode = chop($key);
|
|
$decoded .= chr( ord($char) ^ ord($decode) );
|
|
$key = $decode . $key;
|
|
}
|
|
|
|
return $decoded;
|
|
}
|
|
|
|
1;
|
|
|
|
=pod
|
|
=begin html
|
|
|
|
|
|
=end html
|
|
|
|
=cut
|