From c859a43b465f6e54c24ac0c1119e5b1be4fa1b7e Mon Sep 17 00:00:00 2001 From: moises <> Date: Thu, 11 May 2017 15:21:40 +0000 Subject: [PATCH] 38_netatmo: home notification settings git-svn-id: https://svn.fhem.de/fhem/trunk@14247 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/38_netatmo.pm | 172 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 166 insertions(+), 7 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 157dc77b8..650605d24 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -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: 38_netatmo: home notification settings - feature: 98_alarmclock: New feature RepRoutine - feature: 31_PLAYBULB: support for new Garden Model, move battery Reading to powerLevel and add powerCharge Reading for Garden diff --git a/fhem/FHEM/38_netatmo.pm b/fhem/FHEM/38_netatmo.pm index 91e05f932..049a9f476 100644 --- a/fhem/FHEM/38_netatmo.pm +++ b/fhem/FHEM/38_netatmo.pm @@ -361,12 +361,13 @@ netatmo_Define($$) delete($hash->{refresh_token_app}); delete($hash->{expires_at}); delete($hash->{expires_at_app}); + delete($hash->{csrf_token}); my $user = $a[@a-4]; my $pass = $a[@a-3]; my $username = netatmo_encrypt($user); my $password = netatmo_encrypt($pass); - Log3 $name, 2, "$name: encrypt $user/$pass to $username/$password" if($user ne $username); + Log3 $name, 2, "$name: encrypt $user/$pass to $username/$password" if($user ne $username || $pass ne $password); my $client_id = $a[@a-2]; my $client_secret = $a[@a-1]; @@ -511,7 +512,7 @@ netatmo_Set($$@) $list = "autocreate:noArg autocreate_homes:noArg autocreate_thermostats:noArg autocreate_homecoachs:noArg" if( $hash->{SUBTYPE} eq "ACCOUNT" ); #$list .= " unban:noArg" if( $hash->{SUBTYPE} eq "ACCOUNT" ); $list = "home:noArg away:noArg" if ($hash->{SUBTYPE} eq "PERSON"); - $list = "empty:noArg" if ($hash->{SUBTYPE} eq "HOME"); + $list = "empty:noArg notify_movements:never,empty,always notify_unknowns:empty,always record_movements:never,empty,always record_alarms:never,empty,always presence_record_humans:ignore,record,record_and_notify presence_record_vehicles:ignore,record,record_and_notify presence_record_animals:ignore,record,record_and_notify presence_record_movements:ignore,record,record_and_notify presence_record_alarms:ignore,record,record_and_notify gone_after presence_enable_notify_from_to:empty,always presence_notify_from presence_notify_to smart_notifs:on,off" if ($hash->{SUBTYPE} eq "HOME"); $list = "enable disable irmode:auto,always,never led_on_live:on,off mirror:off,on audio:on,off" if ($hash->{SUBTYPE} eq "CAMERA"); $list = "enable disable light_mode:auto,on,off floodlight intensity:slider,0,1,100 night_always:true,false night_person:true,false night_vehicle:true,false night_animal:true,false night_movement:true,false" if ($hash->{SUBTYPE} eq "CAMERA" && defined($hash->{model}) && $hash->{model} eq "NOC"); $list = "calibrate:noArg" if ($hash->{SUBTYPE} eq "TAG"); @@ -552,6 +553,10 @@ netatmo_Set($$@) return netatmo_setPresence($hash, "empty"); return undef; } + elsif( $cmd =~ /^notify_/ || $cmd =~ /^record_/ || $cmd =~ /^presence_/ || $cmd eq "gone_after" || $cmd eq "smart_notifs" ) { + return netatmo_setNotifications($hash, $cmd, $parameters[0]); + return undef; + } elsif( $cmd eq "enable" ) { my $pin = $parameters[0]; $pin = "0000" if(!defined($pin) || length($pin) != 4); @@ -764,6 +769,7 @@ netatmo_refreshAppToken($;$) return undef; } + delete($hash->{csrf_token}); Log3 $name, 3, "$name: refreshing app token"; my $auth = "QXV0aG9yaXphdGlvbjogQmFzaWMgYm1GZlkyeHBaVzUwWDJsdmN6bzFObU5qTmpSaU56azBOak5oT1RrMU9HSTNOREF4TkRjeVpEbGxNREUxT0E9PQ=="; @@ -851,7 +857,12 @@ netatmo_parseConnection($$$) Log3 $name, 2, "$name: invalid json on connection check"; return undef; } - my $json = JSON->new->utf8(0)->decode($data); + my $json = eval { JSON->new->utf8(0)->decode($data) }; + if($@) + { + Log3 $name, 2, "$name: invalid json evaluation on connection check ".$@; + return undef; + } $hash->{network} = "ok" if($json->{status} eq "ok"); } return undef; @@ -1782,6 +1793,93 @@ netatmo_setPresence($$) } + +sub +netatmo_setNotifications($$$) +{ + my ($hash,$setting,$value) = @_; + my $name = $hash->{NAME}; + + return undef if( !defined($hash->{IODev}) ); + + my $iohash = $hash->{IODev}; + netatmo_refreshAppToken($iohash, defined($iohash->{access_token_app})); + + return Log3 $name, 1, "$name: No access token was found! (setNotifications)" if(!defined($iohash->{access_token_app})); + + if( !defined($iohash->{csrf_token}) ) + { + my($err0,$data0) = HttpUtils_BlockingGet({ + url => "https://auth.netatmo.com/access/checklogin", + timeout => 10, + noshutdown => 1, + }); + if($err0 || !defined($data0)) + { + Log3 $name, 1, "$name: csrf call failed! ".$err0; + return undef; + } + $data0 =~ /ci_csrf_netatmo" value="(.*)"/; + my $tmptoken = $1; + $iohash->{csrf_token} = $tmptoken; + if(!defined($iohash->{csrf_token})) { + Log3 $name, 1, "$name: CSRF ERROR "; + return undef; + } + Log3 $name, 4, "$name: csrf_token ".$iohash->{csrf_token}; + } + + my $homeid = $hash->{Home}; + + my %data; + + if($setting eq "presence_notify_from" || $setting eq "presence_notify_to" || $setting eq "gone_after") + { + my @timevalue = split(":",$value); + if(defined($timevalue[1])) + { + $value = int($timevalue[0])*3600 + int($timevalue[1])*60; + } + else + { + $value *= 60; + } + $value = 0 if($value < 0); + $value = 86400 if($value > 86400 && $setting ne "gone_after"); + } + elsif($setting eq "smart_notifs") + { + $value = (($value eq "on") ? "true" : "false"); + } + + if($setting eq "presence_enable_notify_from_to" || $setting =~ /^presence_record_/ || $setting =~ /^presence_notify_/ ) + { + %data = (home_id => $homeid, 'presence_settings['.$setting.']' => $value, ci_csrf_netatmo => $iohash->{csrf_token}); + } + else + { + %data = (home_id => $homeid, $setting => $value, ci_csrf_netatmo => $iohash->{csrf_token}); + } + + Log3 $name, 5, "$name: setNotifications ($setting $value)"; + + + HttpUtils_NonblockingGet({ + url => "https://my.netatmo.com/api/updatehome", + timeout => 20, + noshutdown => 1, + method => "POST", + header => "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nAuthorization: Bearer ".$iohash->{access_token_app}, + data => \%data, + hash => $hash, + type => 'sethomesettings', + callback => \&netatmo_dispatch, + }); + + +} + + sub netatmo_setCamera($$$) { @@ -2263,8 +2361,12 @@ netatmo_dispatch($$$) $hash->{network} = "ok" if($hash->{SUBTYPE} eq "ACCOUNT"); $hash->{IODev}->{network} = "ok" if($hash->{SUBTYPE} ne "ACCOUNT"); - my $json; - $json = JSON->new->utf8(0)->decode($data); + my $json = eval { JSON->new->utf8(0)->decode($data) }; + if($@) + { + Log3 $name, 2, "$name: invalid json evaluation on dispatch type ".$param->{type}." ".$@; + return undef; + } Log3 "unknown", 2, "unknown (no name) ".Dumper($hash) if(!defined($name)); Log3 $name, 4, "$name: dispatch return: ".$param->{type}; @@ -2334,6 +2436,8 @@ netatmo_dispatch($$$) return netatmo_webhookStatus($hash,$json,"added"); } elsif( $param->{type} eq 'dropwebhook' ) { return netatmo_webhookStatus($hash,$json,"dropped"); + } elsif( $param->{type} eq 'sethomesettings' ) { + return netatmo_refreshHomeSettings($hash); } else { Log3 $name, 1, "$name: unknown '$param->{type}' ".Dumper($json); } @@ -2960,10 +3064,19 @@ netatmo_parseReadings($$;$) $hash->{helper}{NEXT_POLL} = $nextdata; Log3 $name, 3, "$name: next dynamic update from device ($requested) at ".FmtDateTime($nextdata); } else { + $nextdata += $step_time; + if($nextdata >= (gettimeofday()+155)) + { + RemoveInternalTimer($hash, "netatmo_poll"); + InternalTimer($nextdata, "netatmo_poll", $hash); + $hash->{helper}{NEXT_POLL} = $nextdata; + Log3 $name, 3, "$name: next extended dynamic update from device ($requested) at ".FmtDateTime($nextdata); + } else { Log3 $name, 2, "$name: invalid time for dynamic update from device ($requested): ".FmtDateTime($nextdata); } } } + } elsif($nextdata >= (gettimeofday()+280)) { RemoveInternalTimer($hash, "netatmo_poll"); @@ -2971,9 +3084,18 @@ netatmo_parseReadings($$;$) $hash->{helper}{NEXT_POLL} = $nextdata; Log3 $name, 3, "$name: next dynamic update ($requested) at ".FmtDateTime($nextdata); } else { + $nextdata += $step_time; + if($nextdata >= (gettimeofday()+280)) + { + RemoveInternalTimer($hash, "netatmo_poll"); + InternalTimer($nextdata, "netatmo_poll", $hash); + $hash->{helper}{NEXT_POLL} = $nextdata; + Log3 $name, 3, "$name: next extended dynamic update ($requested) at ".FmtDateTime($nextdata); + } else { Log3 $name, 2, "$name: invalid time for dynamic update ($requested): ".FmtDateTime($nextdata); } } + } } } @@ -3478,6 +3600,25 @@ netatmo_parseHomeReadings($$;$) readingsSingleUpdate($hash, "name", encode_utf8($homedata->{name}), 1) if(defined($homedata->{name})); + readingsSingleUpdate($hash, "presence_record_humans", $homedata->{presence_record_humans}, 1) if(defined($homedata->{presence_record_humans})); + readingsSingleUpdate($hash, "presence_record_vehicles", $homedata->{presence_record_vehicles}, 1) if(defined($homedata->{presence_record_vehicles})); + readingsSingleUpdate($hash, "presence_record_animals", $homedata->{presence_record_animals}, 1) if(defined($homedata->{presence_record_animals})); + readingsSingleUpdate($hash, "presence_record_movements", $homedata->{presence_record_movements}, 1) if(defined($homedata->{presence_record_movements})); + readingsSingleUpdate($hash, "presence_record_alarms", $homedata->{presence_record_alarms}, 1) if(defined($homedata->{presence_record_alarms})); + + readingsSingleUpdate($hash, "gone_after", sprintf("%02d",(int($homedata->{gone_after}/60)/60)).":".sprintf("%02d",(int($homedata->{gone_after}/60)%60)), 1) if(defined($homedata->{gone_after})); + readingsSingleUpdate($hash, "smart_notifs", ($homedata->{smart_notifs} eq "1")?"on":"off", 1) if(defined($homedata->{smart_notifs})); + + readingsSingleUpdate($hash, "presence_enable_notify_from_to", $homedata->{presence_enable_notify_from_to}, 1) if(defined($homedata->{presence_enable_notify_from_to})); + readingsSingleUpdate($hash, "presence_notify_from", sprintf("%02d",(int($homedata->{presence_notify_from}/60)/60)).":".sprintf("%02d",(int($homedata->{presence_notify_from}/60)%60)), 1) if(defined($homedata->{presence_notify_from})); + readingsSingleUpdate($hash, "presence_notify_to", sprintf("%02d",(int($homedata->{presence_notify_to}/60)/60)).":".sprintf("%02d",(int($homedata->{presence_notify_to}/60)%60)), 1) if(defined($homedata->{presence_notify_to})); + + readingsSingleUpdate($hash, "notify_unknowns", $homedata->{notify_unknowns}, 1) if(defined($homedata->{notify_unknowns})); + readingsSingleUpdate($hash, "notify_movements", $homedata->{notify_movements}, 1) if(defined($homedata->{notify_movements})); + readingsSingleUpdate($hash, "record_alarms", $homedata->{record_alarms}, 1) if(defined($homedata->{record_alarms})); + readingsSingleUpdate($hash, "record_movements", $homedata->{record_movements}, 1) if(defined($homedata->{record_movements})); + + if( $homedata->{place} ) { $hash->{country} = encode_utf8($homedata->{place}{country}) if(defined($homedata->{place}{country})); $hash->{bssid} = $homedata->{place}{bssid} if(defined($homedata->{place}{bssid})); @@ -3976,6 +4117,19 @@ netatmo_parseHomeReadings($$;$) } + +sub +netatmo_refreshHomeSettings($) +{ + my($hash) = @_; + my $name = $hash->{NAME}; + + InternalTimer(gettimeofday()+5, "netatmo_poll", $hash); + + return undef; +} + + sub netatmo_parseCameraPing($$;$) { @@ -5520,8 +5674,12 @@ sub netatmo_Webhook() { Log3 $name, 5, "Netatmo webhook JSON:\n".$data; - my $json = JSON->new->utf8(0)->decode($data); - + my $json = eval { JSON->new->utf8(0)->decode($data) }; + if($@) + { + Log3 $name, 2, "$name: invalid json evaluation for webhook ".$@; + return undef; + } readingsBeginUpdate($hash);