2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-16 04:36:02 +00:00

48_MieleAtHome: v1.2.0 - support set targetTemperature and mode

git-svn-id: https://svn.fhem.de/fhem/trunk@25100 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
choenig 2021-10-21 11:53:36 +00:00
parent f0d8d23053
commit ad83e0df00
2 changed files with 190 additions and 45 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- feature: 48_MieleAtHome: support set targetTemperature and mode
- bugfix: 50_Signalbot: Fixed issue with Babble and invite - bugfix: 50_Signalbot: Fixed issue with Babble and invite
- bugfix: 73_ElectricityCalculator: use Data:Dumper inserted - bugfix: 73_ElectricityCalculator: use Data:Dumper inserted
- bugfix: 73_GasCalculator: use Data:Dumper inserted - bugfix: 73_GasCalculator: use Data:Dumper inserted

View File

@ -35,10 +35,11 @@ use Encode qw(encode_utf8);
use List::Util qw[min max]; use List::Util qw[min max];
use JSON; use JSON;
my $version = "1.1.1"; my $version = "1.2.0";
my $MAH_hasMimeBase64 = 1; my $MAH_hasMimeBase64 = 1;
# DONE: <option value="processAction">processAction</option>
use constant PROCESS_ACTIONS => { use constant PROCESS_ACTIONS => {
0x01 => "start", # 1 START 0x01 => "start", # 1 START
0x02 => "stop", # 2 STOP 0x02 => "stop", # 2 STOP
@ -49,11 +50,13 @@ use constant PROCESS_ACTIONS => {
0x07 => "stopSuperCooling", # 7 STOP SUPERCOOLING 0x07 => "stopSuperCooling", # 7 STOP SUPERCOOLING
}; };
# DONE: <option value="light">light</option>
use constant LIGHT_ACTIONS => { use constant LIGHT_ACTIONS => {
0x01 => "enable", # 1 Enable 0x01 => "enable", # 1 Enable
0x02 => "disable", # 2 Disable 0x02 => "disable", # 2 Disable
}; };
# DONE: <option value="ventilationStep">ventilationStep</option>
use constant VENTILATION_STEPS => { use constant VENTILATION_STEPS => {
0x01 => "Step1", # 1 Step1 0x01 => "Step1", # 1 Step1
0x02 => "Step2", # 2 Step2 0x02 => "Step2", # 2 Step2
@ -61,11 +64,20 @@ use constant VENTILATION_STEPS => {
0x04 => "Step4", # 4 Step4 0x04 => "Step4", # 4 Step4
}; };
# DONE: <option value="modes">modes</option>
use constant MODE_ACTIONS => {
0x00 => "normalOperationMode", # 0 Normal operation mode
0x01 => "sabbathMode", # 1 Sabbath mode
};
# DONE: <option value="startTime">startTime</option>
# DONE: <option value="powerOff">powerOff</option>
# DONE: <option value="powerOn">powerOn</option>
# DONE: <option value="targetTemperature">targetTemperature</option>
# TODO: <option value="programId">programId</option> # TODO: <option value="programId">programId</option>
# TODO: <option value="targetTemperature">targetTemperature</option>
# TODO: <option value="deviceName">deviceName</option> # TODO: <option value="deviceName">deviceName</option>
# TODO: <option value="colors">colors</option> # TODO: <option value="colors">colors</option>
# TODO: <option value="modes">modes</option>
use constant COUNTRIES => { use constant COUNTRIES => {
"Miele-Deutschland" => "de-DE", "Miele-Deutschland" => "de-DE",
@ -404,7 +416,7 @@ sub MAH_SetFn($$@)
} }
elsif( $cmd eq 'startTime') { elsif( $cmd eq 'startTime') {
return "usage: startTime <OFFSET_H:MM>" if(@args != 1); return "usage: startTime <OFFSET_H:MM>" if(@args != 1);
return MAH_setStartTime($hash, $args[0]) return MAH_setStartTime($hash, $args[0]);
} }
elsif( $cmd eq 'update' ) { elsif( $cmd eq 'update' ) {
return "use $cmd without arguments" if(@args != 0); return "use $cmd without arguments" if(@args != 0);
@ -413,11 +425,19 @@ sub MAH_SetFn($$@)
} }
elsif( $cmd eq 'ventilationStep') { elsif( $cmd eq 'ventilationStep') {
return "usage: ventilationStep <step>" if(@args != 1); return "usage: ventilationStep <step>" if(@args != 1);
return MAH_setVentilationStep($hash, $args[0]) return MAH_setVentilationStep($hash, $args[0]);
} }
elsif( $cmd eq 'light') { elsif( $cmd eq 'light') {
return "usage: light enable|disable" if(@args != 1); return "usage: light enable|disable" if(@args != 1);
return MAH_setLight($hash, $args[0]) return MAH_setLight($hash, $args[0]);
}
elsif( $cmd eq 'mode') {
return "usage: mode <mode>" if(@args != 1);
return MAH_setMode($hash, $args[0]);
}
elsif( $cmd =~ /targetTemperature_zone([0-9]+)/) {
return "usage: targetTemperature_zone${1} <temp>" if(@args != 1);
return MAH_setTargetTemperature($hash, $1, $args[0]);
} }
else else
{ {
@ -436,32 +456,49 @@ sub MAH_SetFn($$@)
} }
} }
# light actions # light
my $lightCmds = ""; my $lightCmds = MAH_getAvailableCommands($hash, "actions_light", LIGHT_ACTIONS());
my @lightIds = split(/,/, ReadingsVal($name, "actions_light", ""));
foreach my $lightId (@lightIds) {
if (defined LIGHT_ACTIONS->{$lightId}) {
$lightCmds .= LIGHT_ACTIONS->{$lightId} . ",";
}
}
chop($lightCmds); # remove trailing ','
$list .= "light:${lightCmds} " if ($lightCmds ne ""); $list .= "light:${lightCmds} " if ($lightCmds ne "");
# ventilation steps # ventilation steps
my $ventilationStepCmds = ""; my $ventilationStepCmds = MAH_getAvailableCommands($hash, "actions_ventilationStep", VENTILATION_STEPS());
my @ventilationStepIds = split(/,/, ReadingsVal($name, "actions_ventilationStep", "")); $list .= "ventilationStep:${ventilationStepCmds} " if ($ventilationStepCmds ne "");
foreach my $ventilationStepId (@ventilationStepIds) {
if (defined VENTILATION_STEPS->{$ventilationStepId}) { # modes
$ventilationStepCmds .= VENTILATION_STEPS->{$ventilationStepId} . ","; my $modeCmds = MAH_getAvailableCommands($hash, "actions_modes", MODE_ACTIONS());
$list .= "mode:${modeCmds} " if ($modeCmds ne "");
# target temperatures
my @availableTargetTemperatures = split(/ /, ReadingsVal($name, "actions_targetTemperature", ""));
foreach my $targetTemperature (@availableTargetTemperatures) {
if ($targetTemperature =~ /^([0-9]+)\[(-?[0-9]+),(-?[0-9]+)\]$/) {
$list .= "targetTemperature_zone$1:slider,$2,1,$3 "
} }
} }
chop($ventilationStepCmds); # remove trailing ','
$list .= "ventilationStep:${ventilationStepCmds} " if ($ventilationStepCmds ne "");
return "Unknown argument $cmd, choose one of $list"; return "Unknown argument $cmd, choose one of $list";
} }
} }
#------------------------------------------------------------------------------------------------------
# returns the strings of the commands from const ACTIONS that are currently available (via `actions_...`)
#------------------------------------------------------------------------------------------------------
sub MAH_getAvailableCommands($$$)
{
my ($hash, $action_reading, $ACTIONS) = @_;
my $name = $hash->{NAME};
my $cmds = "";
my @ids = split(/,/, ReadingsVal($name, $action_reading, ""));
foreach my $id (@ids) {
if (defined $ACTIONS->{$id}) {
$cmds .= $ACTIONS->{$id} . ",";
}
}
chop($cmds); # remove trailing ','
return $cmds;
}
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
# SetFn # SetFn
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
@ -716,7 +753,7 @@ sub MAH_onOauthLoginReply($$$)
} }
if ($code eq "") { if ($code eq "") {
$code = scrapeGrantAccessPage($hash, $data); $code = MAH_scrapeGrantAccessPage($hash, $data);
if ($code ne "") { if ($code ne "") {
MAH_Log($hash, 5, "Bearer found in HTML"); MAH_Log($hash, 5, "Bearer found in HTML");
} }
@ -731,7 +768,7 @@ sub MAH_onOauthLoginReply($$$)
MAH_doThirdpartyTokenRequest($hash, $code, ""); MAH_doThirdpartyTokenRequest($hash, $code, "");
} }
sub scrapeGrantAccessPage($$) sub MAH_scrapeGrantAccessPage($$)
{ {
my ($hash, $data) = (@_); my ($hash, $data) = (@_);
@ -1036,9 +1073,25 @@ sub MAH_onGetDeviceIdentAndStateReply($$$)
readingsBulkUpdate($hash, "signalFailure", $json->{state}->{signalFailure}); readingsBulkUpdate($hash, "signalFailure", $json->{state}->{signalFailure});
readingsBulkUpdate($hash, "signalInfo", $json->{state}->{signalInfo}); readingsBulkUpdate($hash, "signalInfo", $json->{state}->{signalInfo});
# target temperature
my @targetTemperatures = MAH_decodeTemperature($hash, @{$json->{state}->{targetTemperature}});
if (scalar(@targetTemperatures) == 1) {
readingsBulkUpdate($hash, "targetTemperature", ${targetTemperatures[0]});
} else {
for (my $i = 0; $i < scalar(@targetTemperatures); $i++) {
readingsBulkUpdate($hash, "targetTemperature_zone".($i+1), ${targetTemperatures[$i]});
}
}
# temperature # temperature
readingsBulkUpdate($hash, "targetTemperature", MAH_decodeTemperature($hash, @{$json->{state}->{targetTemperature}})); my @temperatures = MAH_decodeTemperature($hash, @{$json->{state}->{temperature}});
readingsBulkUpdate($hash, "temperature", MAH_decodeTemperature($hash, @{$json->{state}->{temperature}})); if (scalar(@temperatures) == 1) {
readingsBulkUpdate($hash, "temperature", ${temperatures[0]});
} else {
for (my $i = 0; $i < scalar(@temperatures); $i++) {
readingsBulkUpdate($hash, "temperature_zone".($i+1), ${temperatures[$i]});
}
}
#eta & state #eta & state
my ($eta, $etaHR) = MAH_calculateETA($json->{state}->{remainingTime}, my ($eta, $etaHR) = MAH_calculateETA($json->{state}->{remainingTime},
@ -1122,27 +1175,20 @@ sub MAH_onGetDeviceActionsReply($$$)
return; return;
} }
# possible processAction out of
# 1 START
# 2 STOP
# 3 PAUSE
# 4 START SUPERFREEZING
# 5 STOP SUPERFREEZING
# 6 START SUPERCOOLING
# 7 STOP SUPERCOOLING
no strict "refs"; no strict "refs";
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "actions_processAction", join(",", @{$json->{processAction}})); readingsBulkUpdate($hash, "actions_processAction", join(",", @{$json->{processAction}}));
readingsBulkUpdate($hash, "actions_light", join(",", @{$json->{light}})); readingsBulkUpdate($hash, "actions_light", join(",", @{$json->{light}}));
readingsBulkUpdate($hash, "actions_startTime", join(",", @{$json->{startTime}})); readingsBulkUpdate($hash, "actions_startTime", join(",", MAH_parseActionsStartTime($json->{startTime})));
readingsBulkUpdate($hash, "actions_ventilationStep", join(",", @{$json->{ventilationStep}})); readingsBulkUpdate($hash, "actions_ventilationStep", join(",", @{$json->{ventilationStep}}));
readingsBulkUpdate($hash, "actions_programId", join(",", @{$json->{programId}})); readingsBulkUpdate($hash, "actions_programId", join(",", @{$json->{programId}}));
readingsBulkUpdate($hash, "actions_startTime", join(",", MAH_parseActionsStartTime($json->{startTime}))); readingsBulkUpdate($hash, "actions_targetTemperature", MAH_parseActionsTargetTemperature($hash,$json->{targetTemperature}));
readingsBulkUpdate($hash, "actions_deviceName", $json->{deviceName}); readingsBulkUpdate($hash, "actions_deviceName", $json->{deviceName});
readingsBulkUpdate($hash, "actions_powerOn", defined($json->{powerOn}) ? $json->{powerOn} : "0"); readingsBulkUpdate($hash, "actions_powerOff", defined($json->{powerOff}) ? $json->{powerOff} : "0");
readingsBulkUpdate($hash, "actions_powerOff", defined($json->{powerOff}) ? $json->{powerOff} : "0"); readingsBulkUpdate($hash, "actions_powerOn", defined($json->{powerOn}) ? $json->{powerOn} : "0");
# readingsBulkUpdate($hash, "actions_colors", );
readingsBulkUpdate($hash, "actions_modes", join(",", @{$json->{modes}}));
readingsEndUpdate($hash, 1 ); readingsEndUpdate($hash, 1 );
use strict "refs"; use strict "refs";
@ -1152,6 +1198,14 @@ sub MAH_onGetDeviceActionsReply($$$)
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
# format time from array # format time from array
# "targetTemperature":[
# {"value_raw":600,"value_localized":6.0,"unit":"Celsius"},
# {"value_raw":-1800,"value_localized":-18.0,"unit":"Celsius"},
# {"value_raw":-32768,"value_localized":null,"unit":"Celsius"}],
# "temperature":[
# {"value_raw":593,"value_localized":5.93,"unit":"Celsius"},
# {"value_raw":-1800,"value_localized":-18.0,"unit":"Celsius"},
# {"value_raw":-32768,"value_localized":null,"unit":"Celsius"}],
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
sub MAH_decodeTemperature($@) sub MAH_decodeTemperature($@)
{ {
@ -1165,7 +1219,7 @@ sub MAH_decodeTemperature($@)
} }
} }
return join(", ", @retval); return @retval;
} }
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
@ -1187,6 +1241,40 @@ sub MAH_parseActionsStartTime($)
return "[?]"; return "[?]";
} }
#------------------------------------------------------------------------------------------------------
# parse the target temperature from actions
# [
# {
# "zone": 1,
# "min": 1,
# "max": 9
# },
# {
# "zone": 2,
# "min": -26,
# "max": -16
# }
# ]
#------------------------------------------------------------------------------------------------------
sub MAH_parseActionsTargetTemperature($$)
{
my ($hash,$json) = @_;
no strict "refs";
my $retval = "";
my @zones = @{$json};
for (my $i = 0; $i < scalar(@zones); $i++) {
my $zone = $zones[$i];
$retval .= $zone->{zone} . "[" . $zone->{min} . "," . $zone->{max} . "] ";
}
use strict "refs";
chop($retval); # remove trailing space
return $retval;
}
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
# calculate the estimated time of arrival (as HH:MM and as human readable version) # calculate the estimated time of arrival (as HH:MM and as human readable version)
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
@ -1353,6 +1441,51 @@ sub MAH_setLight($$)
return MAH_setAction($hash, "light", "${lightActionId}"); return MAH_setAction($hash, "light", "${lightActionId}");
} }
#------------------------------------------------------------------------------------------------------
# MAH_setMode
#------------------------------------------------------------------------------------------------------
sub MAH_setMode($$)
{
my ($hash, $modeActionName) = @_;
my $name = $hash->{NAME};
my ($modeActionId) = grep{ MODE_ACTIONS->{$_} eq $modeActionName } keys %{MODE_ACTIONS()};
if (!defined $modeActionId) {
return "invalid mode action: '${modeActionName}'";
}
my @availableModeActions = split(/,/, ReadingsVal($name, "actions_modes", ""));
if (! grep {$_ eq $modeActionId} @availableModeActions) {
return "'${modeActionName}' is currently not available";
}
return MAH_setAction($hash, "modes", "${modeActionId}");
}
#------------------------------------------------------------------------------------------------------
# MAH_setTargetTemperature
#------------------------------------------------------------------------------------------------------
sub MAH_setTargetTemperature($$$)
{
my ($hash, $zone, $temp) = @_;
my $name = $hash->{NAME};
my @availableTargetTemperatures = split(/ /, ReadingsVal($name, "actions_targetTemperature", ""));
foreach my $targetTemperature (@availableTargetTemperatures) {
if ($targetTemperature =~ /^([0-9]+)\[(-?[0-9]+),(-?[0-9]+)\]$/) {
if ($1 eq $zone) {
if ($2 <= int($temp) && int($temp) <= $3) {
return MAH_setAction($hash, "targetTemperature", "[{\"zone\": $zone, \"value\": $temp}]");
} else {
return "temperature for zone ${zone} out of range, must be between ${2} and ${3}";
}
}
}
}
return "zone ${zone} not setable";
}
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
# MAH_setVentilationStep # MAH_setVentilationStep
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
@ -1829,6 +1962,9 @@ sub MAH_Log($$$)
my $subroutine = ( split(':', $modAndSub) )[2]; my $subroutine = ( split(':', $modAndSub) )[2];
my $name = ( ref($hash) eq "HASH" ) ? $hash->{NAME} : "MieleAtHome"; my $name = ( ref($hash) eq "HASH" ) ? $hash->{NAME} : "MieleAtHome";
# replace non-printable characters by "<xx>"
$logMessage =~ s/([\x{00}-\x{1f}\x{7f}-\x{ffffffff}])/'<'.unpack('(H2)',$1).'>'/ge;
Log3($hash, $logLevel, "${name} (MieleAtHome::${subroutine}:${line}) " . $logMessage); Log3($hash, $logLevel, "${name} (MieleAtHome::${subroutine}:${line}) " . $logMessage);
#Log3($hash, $logLevel, "${name} (MieleAtHome::${subroutine}:${line}) Stack was: " . MAH_getStacktrace()); #Log3($hash, $logLevel, "${name} (MieleAtHome::${subroutine}:${line}) Stack was: " . MAH_getStacktrace());
} }
@ -1955,6 +2091,10 @@ sub MAH_getStacktrace()
<dt><code><b>light [enable|disable]</b></code></dt> <dt><code><b>light [enable|disable]</b></code></dt>
enable/disable the light of your device. only available depending on the type and state of your appliance. enable/disable the light of your device. only available depending on the type and state of your appliance.
</li> </li>
<li><a id="MieleAtHome-set-mode"></a>
<dt><code><b>mode &lt;mode&gt;</b></code></dt>
set the <i>mode</i> of your applience. can be either <i>sabbathMode</i> or <i>normalOperationMode</i>.
</li>
<li><a id="MieleAtHome-set-on"></a> <li><a id="MieleAtHome-set-on"></a>
<dt><code><b>on</b></code></dt> <dt><code><b>on</b></code></dt>
power up your device. only available depending on the type and state of your appliance. power up your device. only available depending on the type and state of your appliance.
@ -1999,6 +2139,10 @@ sub MAH_getStacktrace()
<dt><code><b>stopSuperCooling</b></code></dt> <dt><code><b>stopSuperCooling</b></code></dt>
stop super cooling your device. only available depending on the type and state of your appliance. stop super cooling your device. only available depending on the type and state of your appliance.
</li> </li>
<li><a id="MieleAtHome-set-targetTemperature"></a>
<dt><code><b>targetTemperature_zoneN &lt;temp&gt;</b></code></dt>
set the <i>targetTemperature</i> of zone <i>N</i> of your applience. The available temperature-range is determined by the Miele-API.
</li>
<li><a id="MieleAtHome-set-update"></a> <li><a id="MieleAtHome-set-update"></a>
<dt><code><b>update</b></code></dt> <dt><code><b>update</b></code></dt>
instantly update all readings. instantly update all readings.