mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-09 20:57:11 +00:00
48_MieleAtHome: v2.1.0 - support optional event-api
git-svn-id: https://svn.fhem.de/fhem/trunk@27502 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
554a7e074a
commit
185cc3f46a
@ -1,6 +1,7 @@
|
||||
# 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.
|
||||
- bugfix: 72_FRITZBOX: weitere Stabilisierung
|
||||
- feature: 48_MieleAtHome: support optional event-api
|
||||
- bugfix: 72_FRITZBOX: weitere Stabilisierung
|
||||
- feature: 72_FRITZBOX: neue Readings
|
||||
box_dns_Server<n>, box_connection_Type,
|
||||
box_last_auth_err, box_mac_Address
|
||||
|
@ -35,7 +35,7 @@ use Encode qw(encode_utf8);
|
||||
use List::Util qw[min max];
|
||||
use JSON;
|
||||
|
||||
my $version = "1.2.0";
|
||||
my $version = "2.1.0";
|
||||
|
||||
my $MAH_hasMimeBase64 = 1;
|
||||
|
||||
@ -155,6 +155,7 @@ sub MieleAtHome_Initialize($)
|
||||
$hash->{RenameFn} = "MAH_RenameFn";
|
||||
|
||||
$hash->{AttrList} = "";
|
||||
$hash->{AttrList} .= "api:poll,event ";
|
||||
$hash->{AttrList} .= "clientId ";
|
||||
$hash->{AttrList} .= "disable:1 ";
|
||||
$hash->{AttrList} .= "login ";
|
||||
@ -190,7 +191,7 @@ sub MAH_DefFn($$)
|
||||
# check syntax
|
||||
my $pCount = int(@a);
|
||||
if ($pCount < 1 || $pCount > 3) {
|
||||
return "Wrong syntax: use define <name> MAH [deviceId] [interval]";
|
||||
return "Wrong syntax: use define <name> MieleAtHome [deviceId] [interval]";
|
||||
}
|
||||
|
||||
my $name = shift(@a);
|
||||
@ -248,15 +249,15 @@ sub MAH_DefFn($$)
|
||||
|
||||
if (defined($deviceId)) {
|
||||
# this will call MAH_refreshAccessToken itself if required
|
||||
InternalTimer(gettimeofday()+($init_done ? 0 : 10), "MAH_updateValues", $hash);
|
||||
InternalTimer(gettimeofday()+($init_done ? 0 : 10), "MAH_start", $hash);
|
||||
} else {
|
||||
InternalTimer(gettimeofday()+($init_done ? 0 : 10), "MAH_refreshAccessToken", $hash);
|
||||
}
|
||||
|
||||
# if MAH_getAccessToken returns "", it will request a new token on its own
|
||||
if (MAH_getAccessToken($hash) ne "") {
|
||||
InternalTimer(gettimeofday()+0, "MAH_updateValues", $hash);
|
||||
}
|
||||
# if (MAH_getAccessToken($hash) ne "") {
|
||||
# InternalTimer(gettimeofday()+0, "MAH_startPoll", $hash);
|
||||
# }
|
||||
|
||||
return undef;
|
||||
}
|
||||
@ -270,6 +271,7 @@ sub MAH_UndefFn($$)
|
||||
my ($hash, $name) = @_;
|
||||
|
||||
RemoveInternalTimer($hash);
|
||||
MAH_closeEventSource($hash);
|
||||
|
||||
delete($modules{MieleAtHome}{defptr}{"mah_".$name});
|
||||
|
||||
@ -313,14 +315,27 @@ sub MAH_AttrFn(@)
|
||||
if ($cmd eq "set" && $attrVal eq "1") {
|
||||
readingsSingleUpdate ( $hash, "state", "disabled", 1 );
|
||||
MAH_Log($hash, 3, "disabled");
|
||||
InternalTimer(gettimeofday()+0, "MAH_stopPoll", $hash);
|
||||
InternalTimer(gettimeofday()+0, "MAH_closeEventSource", $hash);
|
||||
|
||||
}
|
||||
elsif ($cmd eq "del") {
|
||||
readingsSingleUpdate ( $hash, "state", "active", 1 );
|
||||
MAH_Log($hash, 3, "enabled");
|
||||
InternalTimer(gettimeofday()+0, "MAH_updateValues", $hash);
|
||||
InternalTimer(gettimeofday()+0, "MAH_start", $hash);
|
||||
}
|
||||
}
|
||||
|
||||
###############
|
||||
#### api ######
|
||||
|
||||
if ($attrName eq "api") {
|
||||
if ($cmd eq "set") {
|
||||
return "Invalid value for attribute $attrName" if ($attrVal ne "poll" && $attrVal ne "event");
|
||||
}
|
||||
InternalTimer(gettimeofday()+0, "MAH_start", $hash) if ($init_done);
|
||||
}
|
||||
|
||||
#################
|
||||
#### lang ######
|
||||
|
||||
@ -442,7 +457,7 @@ sub MAH_SetFn($$@)
|
||||
else
|
||||
{
|
||||
$list .= "autocreate:noArg " if (!defined($hash->{IODevName}));
|
||||
$list .= "update:noArg " if (defined($hash->{DEVICE_ID}));
|
||||
$list .= "update:noArg " if (defined($hash->{DEVICE_ID}) && !defined($hash->{EventSource}));
|
||||
|
||||
$list .= "on:noArg " if (defined($hash->{DEVICE_ID}) && ReadingsNum($name, "actions_powerOn", 0) == 1);
|
||||
$list .= "off:noArg " if (defined($hash->{DEVICE_ID}) && ReadingsNum($name, "actions_powerOff", 0) == 1);
|
||||
@ -556,16 +571,55 @@ sub MAH_RenameFn($$)
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# request values from 3rd party api
|
||||
# start either poll- or event-based requests
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_updateValues($)
|
||||
sub MAH_start($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
RemoveInternalTimer($hash, "MAH_updateValues");
|
||||
return undef if (MAH_isDisabled($hash));
|
||||
return undef unless (defined($hash->{DEVICE_ID}));
|
||||
return undef unless (MAH_hasLoginCredentials($hash));
|
||||
|
||||
my $api = AttrVal($name, "api", "poll");
|
||||
MAH_Log($hash, 3, "starting $api");
|
||||
|
||||
if ($api eq "poll") {
|
||||
MAH_closeEventSource($hash);
|
||||
MAH_startPoll($hash);
|
||||
} elsif ($api eq "event") {
|
||||
MAH_stopPoll($hash);
|
||||
MAH_openEventSource($hash);
|
||||
}
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# stop polling the API
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_stopPoll($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
RemoveInternalTimer($hash, "MAH_startPoll");
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# start polling the API
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_startPoll($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
MAH_stopPoll($hash);
|
||||
|
||||
return undef if (MAH_isDisabled($hash));
|
||||
return undef unless (defined($hash->{DEVICE_ID}));
|
||||
@ -574,17 +628,231 @@ sub MAH_updateValues($)
|
||||
my $interval = $hash->{INTERVAL};
|
||||
# force interval of 60s while != Off
|
||||
$interval = min($interval, 60) if ReadingsNum($name, "statusRaw", 1) != 1; # != Off
|
||||
InternalTimer(gettimeofday()+$interval, "MAH_updateValues", $hash) if (defined($interval));
|
||||
InternalTimer(gettimeofday()+$interval, "MAH_startPoll", $hash) if (defined($interval));
|
||||
|
||||
# MAH_getAccessToken will request a new one, if there is none
|
||||
if (MAH_getAccessToken($hash) eq "") {
|
||||
return;
|
||||
}
|
||||
|
||||
MAH_updateValues($hash);
|
||||
}
|
||||
|
||||
sub MAH_updateValues($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
MAH_sendGetDeviceIdentAndState($hash);
|
||||
MAH_sendGetDeviceActionsRequest($hash);
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# MAH_closeEventSource
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_closeEventSource($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
RemoveInternalTimer($hash, "MAH_eventSourceKeepAlive");
|
||||
|
||||
return if(!defined($hash->{helper}{HTTP_CONNECTION}));
|
||||
|
||||
HttpUtils_Close( $hash->{helper}{HTTP_CONNECTION} );
|
||||
delete $hash->{helper}{HTTP_CONNECTION};
|
||||
MAH_updateEventSourceReadings($hash, undef, undef, undef)
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# request values via event-api
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_openEventSource($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
MAH_closeEventSource($hash);
|
||||
|
||||
return undef if (MAH_isDisabled($hash));
|
||||
return undef unless (defined($hash->{DEVICE_ID}));
|
||||
return undef unless (MAH_hasLoginCredentials($hash));
|
||||
|
||||
my $deviceId = $hash->{DEVICE_ID};
|
||||
if ($deviceId eq "") {
|
||||
return "Please set deviceId first";
|
||||
}
|
||||
|
||||
# after a new token is requested, MAH_start() is called again
|
||||
my $token = MAH_getAccessToken($hash);
|
||||
if ($token eq "") {
|
||||
return "Please authenticate first";
|
||||
}
|
||||
|
||||
MAH_updateEventSourceReadings($hash, "connecting", TimeNow(), undef);
|
||||
|
||||
my $lang = AttrVal($name, "lang", "en");
|
||||
my $params = {
|
||||
# loglevel => 1,
|
||||
url => "https://api.mcs3.miele.com/v1/devices/${deviceId}/events",
|
||||
httpversion => '1.1',
|
||||
method => 'GET',
|
||||
timeout => 60, # ping interval from Miele seems to be 20s
|
||||
incrementalTimeout => 1,
|
||||
noshutdown => 1,
|
||||
keepalive => 1,
|
||||
header => { "Accept" => "text/event-stream",
|
||||
"Accept-Language" => "${lang}",
|
||||
"Authorization" => "Bearer " . $token },
|
||||
hash => $hash,
|
||||
callback => \&MAH_eventSourceDispatch,
|
||||
};
|
||||
|
||||
map { $hash->{helper}{HTTP_CONNECTION}{$_} = $params->{$_} } keys %{$params};
|
||||
|
||||
HttpUtils_NonblockingGet( $hash->{helper}{HTTP_CONNECTION} );
|
||||
|
||||
# start keepalive
|
||||
MAH_eventSourceKeepAlive($hash);
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_eventSourceDispatch($$$)
|
||||
{
|
||||
my ($param, $err, $data) = @_;
|
||||
my $hash = $param->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_LogReply($hash, $param, $err, $data);
|
||||
|
||||
if ($err) {
|
||||
MAH_updateEventSourceReadings($hash, "error: ${err}", undef, undef);
|
||||
|
||||
MAH_Log($hash, 1, "Error: $err");
|
||||
# MAH_openEventSource() will close first
|
||||
MAH_openEventSource($hash);
|
||||
return undef;
|
||||
}
|
||||
|
||||
MAH_updateEventSourceReadings($hash, "connected", undef, TimeNow());
|
||||
|
||||
# FORMAT: (https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format)
|
||||
# event: ping
|
||||
# data: ping
|
||||
#while ($data ne "" && $data =~ m/event:\s*(.+)(?:\r?\n)?data:\s*(.+)(?:\r?\n)?(.*)/) {
|
||||
|
||||
my $event = "";
|
||||
my $eventData = "";
|
||||
while($data =~ m/([^:]*):\s*(.+)(?:\r?\n)?(.*)/) {
|
||||
my $key = $1;
|
||||
my $value = $2;
|
||||
$data = $3;
|
||||
|
||||
$event = $2 if ($key eq "event");
|
||||
$eventData = $2 if ($key eq "data");
|
||||
|
||||
next unless ($event ne "" && $eventData ne "");
|
||||
|
||||
MAH_Log($hash, 5, "$event >> $eventData");
|
||||
|
||||
# Attention
|
||||
# currently the /all/-request replies with "devices" and "actions" (both with the indirection {"1234":<DATA>})
|
||||
# while the /1234/-request replys replies wwith "device[]" and "action[s]" ("device" without- and "actions" with the indirection {"1234":<DATA>})
|
||||
# but MAH_updateDeviceReadings() and MAH_updateActionReadings() remove the indirections if needed
|
||||
if ($event eq "device" || $event eq "devices") {
|
||||
MAH_updateDeviceReadings($hash, $eventData);
|
||||
} elsif ($event eq "action" || $event eq "actions") {
|
||||
MAH_updateActionReadings($hash, $eventData);
|
||||
}
|
||||
|
||||
$event = "";
|
||||
$eventData = "";
|
||||
}
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# MAH_eventSourceKeepAlive
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_eventSourceKeepAlive($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
my $api = AttrVal($name, "api", "poll");
|
||||
return unless ($api eq "event");
|
||||
|
||||
InternalTimer(gettimeofday()+30, "MAH_eventSourceKeepAlive", $hash);
|
||||
|
||||
my $now = time();
|
||||
my $timeout = 180;
|
||||
|
||||
# if we received an update within last 10s, all is fine ...
|
||||
if (defined ($hash->{EventSourceLastUpdate})) {
|
||||
my $lastUpdate = time_str2num($hash->{EventSourceLastUpdate});
|
||||
if ($now - $lastUpdate <= $timeout) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
MAH_Log($hash, 5, "EventSourceLastUpdate is > 10s ...");
|
||||
|
||||
# if we started withing the last 10s, all is fine, too ...
|
||||
if (defined ($hash->{EventSourceLastConnect})) {
|
||||
my $lastConnect = time_str2num($hash->{EventSourceLastConnect});
|
||||
if ($now - $lastConnect <= $timeout) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
MAH_Log($hash, 5, "EventSourceLastConnect is > $[timeout}s ...");
|
||||
|
||||
# ... otherwise ... timeout
|
||||
MAH_Log($hash, 4, "timout in event stream, reconnecting...");
|
||||
MAH_updateEventSourceReadings($hash, "timeout", undef, undef);
|
||||
|
||||
MAH_openEventSource($hash);
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# MAH_updateEventSourceReadings
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_updateEventSourceReadings($$$$)
|
||||
{
|
||||
my ($hash, $status, $lastConnect, $lastUpdate) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
if (!$status && !$lastConnect && !$lastUpdate) {
|
||||
delete($hash->{EventSource});
|
||||
delete($hash->{EventSourceLastUpdate});
|
||||
delete($hash->{EventSourceLastConnect});
|
||||
fhem("deletereading $name _EventSource.*");
|
||||
return;
|
||||
}
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
if ($status) {
|
||||
$hash->{EventSource} = $status;
|
||||
readingsBulkUpdate($hash, "_EventSource", $status);
|
||||
}
|
||||
if ($lastConnect) {
|
||||
$hash->{EventSourceLastConnect} = $lastConnect;
|
||||
readingsBulkUpdate($hash, "_EventSourceLastConnect", $lastConnect);
|
||||
|
||||
}
|
||||
if ($lastUpdate) {
|
||||
$hash->{EventSourceLastUpdate} = $lastUpdate;
|
||||
readingsBulkUpdate($hash, "_EventSourceLastUpdate", $lastUpdate);
|
||||
}
|
||||
readingsEndUpdate($hash, 1);
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# MAH_refreshAccessToken
|
||||
@ -625,7 +893,7 @@ sub MAH_refreshAccessToken($)
|
||||
my $refreshToken = MAH_getRefreshTokenPrivate($hash);
|
||||
if ($refreshToken ne "" && MAH_getRemainingTokenLifetime($hash) > 0) {
|
||||
MAH_Log($hash, 4, "already have a refresh-token, using this for token-refresh");
|
||||
MAH_doThirdpartyTokenRequest($hash, "", $refreshToken)
|
||||
MAH_doThirdpartyTokenRequest($hash, "", $refreshToken);
|
||||
} else {
|
||||
MAH_doThirdpartyLoginRequest($hash);
|
||||
}
|
||||
@ -669,7 +937,7 @@ sub MAH_onThirdpartyLoginReply($$$)
|
||||
my $hash = $param->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "reply: err:$err, code:$param->{code}, headers:$param->{httpheader}, data:$data");
|
||||
MAH_LogReply($hash, $param, $err, $data);
|
||||
|
||||
if ($err) {
|
||||
MAH_Log($hash, 3, "Error: $err");
|
||||
@ -737,7 +1005,7 @@ sub MAH_onOauthLoginReply($$$)
|
||||
my $hash = $param->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "reply: err:$err, code:$param->{code}, headers:$param->{httpheader}, data:$data");
|
||||
MAH_LogReply($hash, $param, $err, $data);
|
||||
|
||||
if ($err) {
|
||||
MAH_Log($hash, 3, "Error: $err");
|
||||
@ -858,7 +1126,7 @@ sub MAH_onThirdpartyTokenReply($$$)
|
||||
my $hash = $param->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "reply: err:$err, code:$param->{code}, headers:$param->{httpheader}, data:$data");
|
||||
MAH_LogReply($hash, $param, $err, $data);
|
||||
|
||||
if ($err) {
|
||||
MAH_Log($hash, 3, "Error: $err");
|
||||
@ -901,7 +1169,7 @@ sub MAH_onThirdpartyTokenReply($$$)
|
||||
$hash->{TOKEN_REFRESH_IN_PROGRESS} = 0;
|
||||
|
||||
if (MAH_getAccessToken($hash) ne "") {
|
||||
InternalTimer(gettimeofday()+0, "MAH_updateValues", $hash);
|
||||
InternalTimer(gettimeofday()+0, "MAH_start", $hash);
|
||||
}
|
||||
}
|
||||
|
||||
@ -930,7 +1198,7 @@ sub MAH_blockingGetAllDevicesRequest($)
|
||||
method => "GET",
|
||||
});
|
||||
|
||||
MAH_Log($hash, 5, "reply: err:$err, data:$data");
|
||||
MAH_LogReply($hash, undef, $err, $data);
|
||||
|
||||
if ($err) {
|
||||
MAH_Log($hash, 3, "Error: $err");
|
||||
@ -997,7 +1265,7 @@ sub MAH_onGetDeviceIdentAndStateReply($$$)
|
||||
my $hash = $param->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "reply: err:$err, code:$param->{code}, data:$data");
|
||||
MAH_LogReply($hash, $param, $err, $data);
|
||||
|
||||
if ($err) {
|
||||
MAH_Log($hash, 3, "Error: $err");
|
||||
@ -1009,6 +1277,16 @@ sub MAH_onGetDeviceIdentAndStateReply($$$)
|
||||
return "invalid status code: " . $param->{code};
|
||||
}
|
||||
|
||||
MAH_updateDeviceReadings($hash, $data);
|
||||
}
|
||||
|
||||
sub MAH_updateDeviceReadings($$)
|
||||
{
|
||||
my ($hash, $data) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
my $json = eval{decode_json($data)};
|
||||
if ($@) {
|
||||
MAH_Log($hash, 3, "JSON error while request: $@");
|
||||
@ -1026,6 +1304,14 @@ sub MAH_onGetDeviceIdentAndStateReply($$$)
|
||||
return;
|
||||
}
|
||||
|
||||
# the "devices"-reply (from event-sourcing) might be in the
|
||||
# form {"12345":<DATA>} instead of <DATA> so get rid of this indirection
|
||||
my $deviceId = $hash->{DEVICE_ID};
|
||||
if (exists($json->{$deviceId})) {
|
||||
MAH_Log($hash, 5, "dropping indirection for deviceId");
|
||||
$json = $json->{$deviceId};
|
||||
}
|
||||
|
||||
# decode_utf8() is required due do something like:
|
||||
# dein json ist utf8 aber das problem scheint zu sein das decode_json bei zeichen
|
||||
# <255 aus dem \u ein \x{..} macht. d.h. es erzeugt kein utf8 2-byte zeichen wie
|
||||
@ -1050,6 +1336,7 @@ sub MAH_onGetDeviceIdentAndStateReply($$$)
|
||||
|
||||
readingsBulkUpdate($hash, "dryingStep", encode_utf8($json->{state}->{dryingStep}->{value_localized}));
|
||||
readingsBulkUpdate($hash, "light", encode_utf8($json->{state}->{light}));
|
||||
readingsBulkUpdate($hash, "batteryLevel", encode_utf8($json->{state}->{batteryLevel}));
|
||||
readingsBulkUpdate($hash, "programID", encode_utf8($json->{state}->{ProgramID}->{value_localized}));
|
||||
readingsBulkUpdate($hash, "programPhase", encode_utf8($json->{state}->{programPhase}->{value_localized}));
|
||||
readingsBulkUpdate($hash, "programType", encode_utf8($json->{state}->{programType}->{value_localized}));
|
||||
@ -1067,11 +1354,12 @@ sub MAH_onGetDeviceIdentAndStateReply($$$)
|
||||
readingsBulkUpdate($hash, "ecoFeedbackWaterForecast", encode_utf8($json->{state}->{ecoFeedback}->{waterForecast}));
|
||||
readingsBulkUpdate($hash, "ecoFeedbackEnergyForecast", encode_utf8($json->{state}->{ecoFeedback}->{energyForecast}));
|
||||
|
||||
readingsBulkUpdate($hash, "remoteEnableFullRC", $json->{state}->{remoteEnable}->{fullRemoteControl});
|
||||
readingsBulkUpdate($hash, "remoteEnableSmartGrid", $json->{state}->{remoteEnable}->{smartGrid});
|
||||
readingsBulkUpdate($hash, "signalDoor", $json->{state}->{signalDoor});
|
||||
readingsBulkUpdate($hash, "signalFailure", $json->{state}->{signalFailure});
|
||||
readingsBulkUpdate($hash, "signalInfo", $json->{state}->{signalInfo});
|
||||
readingsBulkUpdate($hash, "remoteEnableFullRC", $json->{state}->{remoteEnable}->{fullRemoteControl});
|
||||
readingsBulkUpdate($hash, "remoteEnableSmartGrid", $json->{state}->{remoteEnable}->{smartGrid});
|
||||
readingsBulkUpdate($hash, "remoteEnableMobileStart", $json->{state}->{remoteEnable}->{mobileStart});
|
||||
readingsBulkUpdate($hash, "signalDoor", $json->{state}->{signalDoor});
|
||||
readingsBulkUpdate($hash, "signalFailure", $json->{state}->{signalFailure});
|
||||
readingsBulkUpdate($hash, "signalInfo", $json->{state}->{signalInfo});
|
||||
|
||||
# target temperature
|
||||
my @targetTemperatures = MAH_decodeTemperature($hash, @{$json->{state}->{targetTemperature}});
|
||||
@ -1146,7 +1434,7 @@ sub MAH_onGetDeviceActionsReply($$$)
|
||||
my $hash = $param->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "reply: err:$err, code:$param->{code}, data:$data");
|
||||
MAH_LogReply($hash, $param, $err, $data);
|
||||
|
||||
if ($err) {
|
||||
MAH_Log($hash, 3, "Error: $err");
|
||||
@ -1158,6 +1446,16 @@ sub MAH_onGetDeviceActionsReply($$$)
|
||||
return;
|
||||
}
|
||||
|
||||
MAH_updateActionReadings($hash, $data)
|
||||
}
|
||||
|
||||
sub MAH_updateActionReadings($$)
|
||||
{
|
||||
my ($hash, $data) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "called");
|
||||
|
||||
my $json = eval{decode_json($data)};
|
||||
if ($@) {
|
||||
MAH_Log($hash, 3, "JSON error while request: $@");
|
||||
@ -1175,6 +1473,14 @@ sub MAH_onGetDeviceActionsReply($$$)
|
||||
return;
|
||||
}
|
||||
|
||||
# the "actions"-reply (from event-sourcing) might be in the
|
||||
# form {"12345":<DATA>} instead of <DATA> so get rid of this indirection
|
||||
my $deviceId = $hash->{DEVICE_ID};
|
||||
if (exists($json->{$deviceId})) {
|
||||
MAH_Log($hash, 5, "dropping indirection for deviceId");
|
||||
$json = $json->{$deviceId};
|
||||
}
|
||||
|
||||
no strict "refs";
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
@ -1574,13 +1880,14 @@ sub MAH_sendSetActionRequest($$)
|
||||
|
||||
return undef;
|
||||
}
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_onSetActionReply($$$)
|
||||
{
|
||||
my ($param, $err, $data) = @_;
|
||||
my $hash = $param->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
MAH_Log($hash, 5, "reply: err:$err, code:$param->{code}, data:$data");
|
||||
MAH_LogReply($hash, $param, $err, $data);
|
||||
|
||||
if ($err) {
|
||||
MAH_Log($hash, 3, "Error: $err");
|
||||
@ -1951,6 +2258,28 @@ sub MAH_deleteKeyValue($$)
|
||||
my $err = setKeyValue($key, undef);
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# Util: Log replys of network requests
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_LogReply($$$$)
|
||||
{
|
||||
my ($hash, $param, $err, $data) = @_;
|
||||
my $logLevel = 5;
|
||||
|
||||
# prevent warnings with uninitialized variables
|
||||
my $logMessage = "received reply";
|
||||
$logMessage .= ", err:$err" if ($err);
|
||||
$logMessage .= ", code:$param->{code}" if ($param && defined($param->{code}));
|
||||
$logMessage .= ", headers:$param->{httpheader}" if ($param && defined($param->{httpheader}));
|
||||
$logMessage .= ", data:$data" if ($data);
|
||||
|
||||
my $line = ( caller(0) )[2];
|
||||
my $modAndSub = ( caller(1) )[3];
|
||||
my $subroutine = ( split(':', $modAndSub) )[2];
|
||||
|
||||
MAH_LogPrivate($hash, $logLevel, $logMessage, $line, $subroutine);
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# Util: Log
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
@ -1960,10 +2289,20 @@ sub MAH_Log($$$)
|
||||
my $line = ( caller(0) )[2];
|
||||
my $modAndSub = ( caller(1) )[3];
|
||||
my $subroutine = ( split(':', $modAndSub) )[2];
|
||||
my $name = ( ref($hash) eq "HASH" ) ? $hash->{NAME} : "MieleAtHome";
|
||||
|
||||
MAH_LogPrivate($hash, $logLevel, $logMessage, $line, $subroutine);
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
# Util: LogPrivates
|
||||
#------------------------------------------------------------------------------------------------------
|
||||
sub MAH_LogPrivate($$$$$)
|
||||
{
|
||||
my ($hash, $logLevel, $logMessage, $line, $subroutine) = @_;
|
||||
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;
|
||||
$logMessage =~ s/([\x{00}-\x{1f}\x{7f}-\x{7fffffff}])/'<'.unpack('(H2)',$1).'>'/ge;
|
||||
|
||||
Log3($hash, $logLevel, "${name} (MieleAtHome::${subroutine}:${line}) " . $logMessage);
|
||||
#Log3($hash, $logLevel, "${name} (MieleAtHome::${subroutine}:${line}) Stack was: " . MAH_getStacktrace());
|
||||
@ -2046,6 +2385,11 @@ sub MAH_getStacktrace()
|
||||
<br>
|
||||
This statement creates the instance of your specific Miele@home appliance with the name Waschmaschine and the DeviceId 000123456789 and a refresh-interval of 120 seconds. The interval is optional, its default is 120 seconds.<br>
|
||||
<br>
|
||||
<b>Remote starting appliences</b><br>
|
||||
<br>
|
||||
Appliences like wasching mashines can be started remotely via FHEM, but this is rather unintuitive:<br>
|
||||
Therefore the user loads the applience, selects a start or end time within the next 24 hours and presses the start button. The applience then waits for a "remote start" and can be started by FHEM before the start or end time has elapsed. If this is not received within the specified start or end time, it starts itself to be ready in time. <br>
|
||||
<br>
|
||||
|
||||
<!--
|
||||
<a name="MieleAtHomereadings"></a>
|
||||
@ -2167,6 +2511,10 @@ sub MAH_getStacktrace()
|
||||
<a id="MieleAtHome-attr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>
|
||||
<li><a id="MieleAtHome-attr-api"></a>
|
||||
<dt><code><b>api [poll|event]</b></code></dt>
|
||||
set to <i>poll</i> if you want FHEM to repeatedly poll the Miele server for updates. Set to <i>event</i> if you want to have a keepalive-connection to the Miele server. With <i>event</i>, updates are received as soon as they are updated on the Miele server. With <i>poll</i> updates are only received in the configured interval. <br><i>poll</i> is the current default but the default will be switched to <i>event</i> after a testing period.
|
||||
</li>
|
||||
<li><a id="MieleAtHome-attr-clientId"></a>
|
||||
<dt><code><b>clientId</b></code></dt>
|
||||
set the <i>clientId</i> of your Miele@home-developer account.
|
||||
|
Loading…
x
Reference in New Issue
Block a user