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

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@25100 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
choenig 2021-10-21 11:53:36 +00:00
parent f5d80e3181
commit 6cea15221a
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.
# 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: 73_ElectricityCalculator: 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 JSON;
my $version = "1.1.1";
my $version = "1.2.0";
my $MAH_hasMimeBase64 = 1;
# DONE: <option value="processAction">processAction</option>
use constant PROCESS_ACTIONS => {
0x01 => "start", # 1 START
0x02 => "stop", # 2 STOP
@ -49,11 +50,13 @@ use constant PROCESS_ACTIONS => {
0x07 => "stopSuperCooling", # 7 STOP SUPERCOOLING
};
# DONE: <option value="light">light</option>
use constant LIGHT_ACTIONS => {
0x01 => "enable", # 1 Enable
0x02 => "disable", # 2 Disable
};
# DONE: <option value="ventilationStep">ventilationStep</option>
use constant VENTILATION_STEPS => {
0x01 => "Step1", # 1 Step1
0x02 => "Step2", # 2 Step2
@ -61,11 +64,20 @@ use constant VENTILATION_STEPS => {
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="targetTemperature">targetTemperature</option>
# TODO: <option value="deviceName">deviceName</option>
# TODO: <option value="colors">colors</option>
# TODO: <option value="modes">modes</option>
use constant COUNTRIES => {
"Miele-Deutschland" => "de-DE",
@ -404,7 +416,7 @@ sub MAH_SetFn($$@)
}
elsif( $cmd eq 'startTime') {
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' ) {
return "use $cmd without arguments" if(@args != 0);
@ -413,11 +425,19 @@ sub MAH_SetFn($$@)
}
elsif( $cmd eq 'ventilationStep') {
return "usage: ventilationStep <step>" if(@args != 1);
return MAH_setVentilationStep($hash, $args[0])
return MAH_setVentilationStep($hash, $args[0]);
}
elsif( $cmd eq 'light') {
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
{
@ -436,32 +456,49 @@ sub MAH_SetFn($$@)
}
}
# light actions
my $lightCmds = "";
my @lightIds = split(/,/, ReadingsVal($name, "actions_light", ""));
foreach my $lightId (@lightIds) {
if (defined LIGHT_ACTIONS->{$lightId}) {
$lightCmds .= LIGHT_ACTIONS->{$lightId} . ",";
}
}
chop($lightCmds); # remove trailing ','
# light
my $lightCmds = MAH_getAvailableCommands($hash, "actions_light", LIGHT_ACTIONS());
$list .= "light:${lightCmds} " if ($lightCmds ne "");
# ventilation steps
my $ventilationStepCmds = "";
my @ventilationStepIds = split(/,/, ReadingsVal($name, "actions_ventilationStep", ""));
foreach my $ventilationStepId (@ventilationStepIds) {
if (defined VENTILATION_STEPS->{$ventilationStepId}) {
$ventilationStepCmds .= VENTILATION_STEPS->{$ventilationStepId} . ",";
my $ventilationStepCmds = MAH_getAvailableCommands($hash, "actions_ventilationStep", VENTILATION_STEPS());
$list .= "ventilationStep:${ventilationStepCmds} " if ($ventilationStepCmds ne "");
# modes
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";
}
}
#------------------------------------------------------------------------------------------------------
# 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
#------------------------------------------------------------------------------------------------------
@ -716,7 +753,7 @@ sub MAH_onOauthLoginReply($$$)
}
if ($code eq "") {
$code = scrapeGrantAccessPage($hash, $data);
$code = MAH_scrapeGrantAccessPage($hash, $data);
if ($code ne "") {
MAH_Log($hash, 5, "Bearer found in HTML");
}
@ -731,7 +768,7 @@ sub MAH_onOauthLoginReply($$$)
MAH_doThirdpartyTokenRequest($hash, $code, "");
}
sub scrapeGrantAccessPage($$)
sub MAH_scrapeGrantAccessPage($$)
{
my ($hash, $data) = (@_);
@ -1036,9 +1073,25 @@ sub MAH_onGetDeviceIdentAndStateReply($$$)
readingsBulkUpdate($hash, "signalFailure", $json->{state}->{signalFailure});
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
readingsBulkUpdate($hash, "targetTemperature", MAH_decodeTemperature($hash, @{$json->{state}->{targetTemperature}}));
readingsBulkUpdate($hash, "temperature", MAH_decodeTemperature($hash, @{$json->{state}->{temperature}}));
my @temperatures = 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
my ($eta, $etaHR) = MAH_calculateETA($json->{state}->{remainingTime},
@ -1122,27 +1175,20 @@ sub MAH_onGetDeviceActionsReply($$$)
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";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "actions_processAction", join(",", @{$json->{processAction}}));
readingsBulkUpdate($hash, "actions_light", join(",", @{$json->{light}}));
readingsBulkUpdate($hash, "actions_startTime", join(",", @{$json->{startTime}}));
readingsBulkUpdate($hash, "actions_ventilationStep", join(",", @{$json->{ventilationStep}}));
readingsBulkUpdate($hash, "actions_programId", join(",", @{$json->{programId}}));
readingsBulkUpdate($hash, "actions_startTime", join(",", MAH_parseActionsStartTime($json->{startTime})));
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_processAction", join(",", @{$json->{processAction}}));
readingsBulkUpdate($hash, "actions_light", join(",", @{$json->{light}}));
readingsBulkUpdate($hash, "actions_startTime", join(",", MAH_parseActionsStartTime($json->{startTime})));
readingsBulkUpdate($hash, "actions_ventilationStep", join(",", @{$json->{ventilationStep}}));
readingsBulkUpdate($hash, "actions_programId", join(",", @{$json->{programId}}));
readingsBulkUpdate($hash, "actions_targetTemperature", MAH_parseActionsTargetTemperature($hash,$json->{targetTemperature}));
readingsBulkUpdate($hash, "actions_deviceName", $json->{deviceName});
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 );
use strict "refs";
@ -1152,6 +1198,14 @@ sub MAH_onGetDeviceActionsReply($$$)
#------------------------------------------------------------------------------------------------------
# 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($@)
{
@ -1165,7 +1219,7 @@ sub MAH_decodeTemperature($@)
}
}
return join(", ", @retval);
return @retval;
}
#------------------------------------------------------------------------------------------------------
@ -1187,6 +1241,40 @@ sub MAH_parseActionsStartTime($)
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)
#------------------------------------------------------------------------------------------------------
@ -1353,6 +1441,51 @@ sub MAH_setLight($$)
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
#------------------------------------------------------------------------------------------------------
@ -1829,6 +1962,9 @@ sub MAH_Log($$$)
my $subroutine = ( split(':', $modAndSub) )[2];
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}) Stack was: " . MAH_getStacktrace());
}
@ -1955,6 +2091,10 @@ sub MAH_getStacktrace()
<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.
</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>
<dt><code><b>on</b></code></dt>
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>
stop super cooling your device. only available depending on the type and state of your appliance.
</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>
<dt><code><b>update</b></code></dt>
instantly update all readings.