From c44ddf34dfa4bd2327b76c3ab11cf730af7afb10 Mon Sep 17 00:00:00 2001 From: xasher <> Date: Tue, 7 Apr 2020 14:20:20 +0000 Subject: [PATCH] 70_HYDRAWISE.pm: controlling Hydrawise irrigation git-svn-id: https://svn.fhem.de/fhem/trunk@21617 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/70_HYDRAWISE.pm | 1021 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1022 insertions(+) create mode 100755 fhem/FHEM/70_HYDRAWISE.pm diff --git a/fhem/CHANGED b/fhem/CHANGED index bda8b1a2a..39b14d491 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. + - new: 70_HYDRAWISE: New module for controlling Hunter Hydrawise irrigation - feature: 93_Log2Syslog: support time-secfrac of RFC 3339, minor fix - feature: 93_Log2Syslog: new attribute 'timeSpec', send and parse messages according to UTC or Local tiime, diff --git a/fhem/FHEM/70_HYDRAWISE.pm b/fhem/FHEM/70_HYDRAWISE.pm new file mode 100755 index 000000000..bf7a1a6f8 --- /dev/null +++ b/fhem/FHEM/70_HYDRAWISE.pm @@ -0,0 +1,1021 @@ +# $Id$ +############################################################################## +# +# 70_HYDRAWISE.pm +# An FHEM Perl module for controlling a Hunter Hydrawise irigation controller. +# +# This file is part of fhem. +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +############################################################################## + +package main; + +use 5.012; +use strict; +use warnings; + +use Time::HiRes qw(gettimeofday); +use JSON qw(decode_json encode_json); +use Encode qw(encode_utf8 decode_utf8); +use Time::Piece; +use Time::Local; + +# use Data::Dumper; +use HttpUtils; + +my %sets = ( + stop => "", + stopall => "noArg", + run => "", + runall => "", + suspend => "", + suspendall => "", + renewContext => "noArg", + renewRelays => "noArg", +); + +my %gets = ( "help" => "noArg", ); + +################################### +sub HYDRAWISE_Initialize { + my $hash = shift; + + Log3 $hash, 5, 'HYDRAWISE_Initialize: Entering'; + + %{$hash} = ( + GetFn => 'HYDRAWISE_Get', + SetFn => 'HYDRAWISE_Set', + DefFn => 'HYDRAWISE_Define', + UndefFn => 'HYDRAWISE_Undefine', + AttrList => "disable:0,1 $readingFnAttributes", + ); + + return; +} + +################################### +sub HYDRAWISE_Define { + my ( $hash, $def ) = @_; + + my @a = split( "[ \t][ \t]*", $def ); + my $name = $hash->{NAME}; + + Log3 $name, 5, "HYDRAWISE $name: called function HYDRAWISE_Define()"; + + if ( @a < 3 ) { + my $msg = + "Wrong syntax: define HYDRAWISE []"; + Log3 $name, 4, $msg; + return $msg; + } + + $hash->{TYPE} = "HYDRAWISE"; + + my $api_key = $a[2]; + $hash->{helper}{APIKEY} = $api_key; + + # use interval of 300 sec if not defined + my $interval = $a[3] || 300; + $hash->{INTERVAL} = $interval; + + unless ( defined( AttrVal( $name, "webCmd", undef ) ) ) { + $attr{$name}{webCmd} = 'stopall renewContext renewRelays'; + } + + # start the status update timer + RemoveInternalTimer($hash); + InternalTimer( gettimeofday() + 2, "HYDRAWISE_GetStatus", $hash, 1 ); + + return; +} + +################################### +sub HYDRAWISE_Undefine { + my ( $hash, $arg ) = @_; + + my $name = $hash->{NAME}; + + Log3 $name, 5, "HYDRAWISE $name: called function HYDRAWISE_Undefine()"; + + # De-Authenticate + HYDRAWISE_SendCommand( $hash, "deauthenticate" ); + + # Stop the internal GetStatus-Loop and exit + RemoveInternalTimer($hash); + + return; +} + +##################################### +sub HYDRAWISE_GetStatus { + my ( $hash, $update ) = @_; + + my $name = $hash->{NAME}; + my $interval = $hash->{INTERVAL}; + + Log3 $name, 5, "HYDRAWISE $name: called function HYDRAWISE_GetStatus()"; + + RemoveInternalTimer($hash); + InternalTimer( gettimeofday() + $interval, "HYDRAWISE_GetStatus", $hash, + 0 ); + + return if ( AttrVal( $name, "disable", 0 ) == 1 ); + + # check device availability + if ( !$update ) { + HYDRAWISE_SendCommand( $hash, "state" ); + } + + return; +} + +################################### +sub HYDRAWISE_Get { + my ( $hash, @a ) = @_; + + my $name = $hash->{NAME}; + my $what; + + Log3 $name, 5, "HYDRAWISE $name: called function HYDRAWISE_Get()"; + + return "argument is missing" if ( @a < 2 ); + + $what = $a[1]; + + return _HYDRAWISE_help() if ( $what =~ /^(help)$/ ); + return "Unknown argument $what, choose one of help:noArg"; +} + +sub _HYDRAWISE_help { + return << 'EOT'; +----------------------------------------------------------------------------------------------------- +|renewcontext | Refresh readings customerdetail | +----------------------------------------------------------------------------------------------------- +|renewRelays | Refresh readings statusdetails | +----------------------------------------------------------------------------------------------------- +|run | Run zone for a period of time. 2 Parameters: "relay_id" "time_in_seconds" | +----------------------------------------------------------------------------------------------------- +|runall | Run all zones for a period of time. 1 Parameter: "time_in_seconds" | +----------------------------------------------------------------------------------------------------- +|stop | Stop zone. 1 Parameter: "relay_id" | +----------------------------------------------------------------------------------------------------- +|stopall | Stop all currently running zones. | +----------------------------------------------------------------------------------------------------- +|suspend | Suspend zone for a period of time. 3 Parameters: "relay_id" "DD.MM.YYYY" "HH24:MI" | +----------------------------------------------------------------------------------------------------- +|suspendall | Suspend all zones for a period of time. 2 Parameters: "DD.MM.YYYY" "HH24:MI" | +----------------------------------------------------------------------------------------------------- +EOT +} + +################################### +sub HYDRAWISE_Set { + my ( $hash, $name, $cmd, @a ) = @_; + + Log3 $name, 5, "HYDRAWISE $name: called function HYDRAWISE_Set()"; + + return "\"sets\" needs at least one parameter" if ( !$cmd ); + + # stopall () + if ( $cmd eq "stopall" ) { + Log3 $name, 2, "HYDRAWISE set $name " . $cmd . $a[0]; + + HYDRAWISE_SendCommand( $hash, "stopall" ); + readingsSingleUpdate( $hash, "state", "Set_stopall", 1 ); + } + + # stop (relay_id) + elsif ( $cmd eq "stop" ) { + Log3 $name, 2, "HYDRAWISE set $name " . $cmd . " " . $a[0]; + + return "Expected: \"\"" if ( !defined( $a[0] ) ); + + HYDRAWISE_SendCommand( $hash, "stop", $a[0] ); + readingsSingleUpdate( $hash, "state", "Set_stop", 1 ); + } + + # runall (custom) + elsif ( $cmd eq "runall" ) { + Log3 $name, 2, "HYDRAWISE set $name " . $cmd . " " . $a[0]; + + return "Expected: \"\"" if ( !defined( $a[0] ) ); + + HYDRAWISE_SendCommand( $hash, "runall", "$a[0]" ); + readingsSingleUpdate( $hash, "state", "Set_runall", 1 ); + } + + # run (relay_id, custom) + elsif ( $cmd eq "run" ) { + Log3 $name, 2, + "HYDRAWISE set $name " . $cmd . " " . $a[0] . " " . $a[1]; + + return "Expected: \" \"" + if ( !defined( $a[0] ) || !defined( $a[1] ) ); + + HYDRAWISE_SendCommand( $hash, "run", "$a[0] $a[1]" ); + readingsSingleUpdate( $hash, "state", "Set_run", 1 ); + } + + # suspendall (date time) + elsif ( $cmd eq "suspendall" ) { + Log3 $name, 2, + "HYDRAWISE set $name " . $cmd . " " . $a[0] . " " . $a[1]; + return "Expected: \" \"" + if ( !defined( $a[0] ) || !defined( $a[1] ) ); + + my ( $day, $month, $year ) = split /\./, $a[0]; + my ( $hour, $min ) = split /\:/, $a[1]; + my $time = timelocal( 00, $min, $hour, $day, $month - 1, $year - 1900 ); + + HYDRAWISE_SendCommand( $hash, "suspendall", $time ); + readingsSingleUpdate( $hash, "state", "Set_suspendall", 1 ); + } + + # suspend (relay_id, date, time) + elsif ( $cmd eq "suspend" ) { + Log3 $name, 2, + "HYDRAWISE set $name " . $cmd . $a[0] . " " . $a[1] . " " . $a[2]; + return "Expected: \" \"" + if ( !defined( $a[0] ) || !defined( $a[1] ) || !defined( $a[2] ) ); + + my ( $day, $month, $year ) = split /\./, $a[1]; + my ( $hour, $min ) = split /\:/, $a[2]; + my $time = timelocal( 00, $min, $hour, $day, $month - 1, $year - 1900 ); + + HYDRAWISE_SendCommand( $hash, "suspend", "$a[0] $time" ); + readingsSingleUpdate( $hash, "state", "Set_suspend", 1 ); + } + + # renewContext + elsif ( $cmd eq "renewContext" ) { + Log3 $name, 2, "HYDRAWISE set $name " . $cmd; + + HYDRAWISE_SendCommand( $hash, "authenticate" ); + } + + # relays + elsif ( $cmd eq "renewRelays" ) { + Log3 $name, 2, "HYDRAWISE set $name " . $cmd; + + HYDRAWISE_SendCommand( $hash, "relays" ); + } + + # return usage hint + else { + return "Unknown argument $cmd, choose one of " + . join( " ", + map { "$_" . ( $sets{$_} ? ":$sets{$_}" : "" ) } keys %sets ); + } + + return; +} + +############################################################################################################ +# +# Begin of helper functions +# +############################################################################################################ + +################################### +sub HYDRAWISE_SendCommand { + my ( $hash, $service, $type ) = @_; + + my $name = $hash->{NAME}; + my $api_key = $hash->{helper}{APIKEY}; + my $timestamp = gettimeofday(); + my $timeout = 30; + my $data; + my $method = "GET"; + + Log3 $name, 5, "HYDRAWISE $name: called function HYDRAWISE_SendCommand()"; + + my $URL_STATUSSCHEDULE = + "https://api.hydrawise.com/api/v1/statusschedule.php?api_key=" . $api_key; + my $URL_CUSTOMERDETAILS = + "https://api.hydrawise.com/api/v1/customerdetails.php?api_key=" + . $api_key; + my $URL_SETZONE = + "https://api.hydrawise.com/api/v1/setzone.php?api_key=$api_key"; + my $URL = ""; + my $ACTION = ""; + + Log3 $name, 4, "HYDRAWISE $name: REQ $service"; + + if ( $service eq "authenticate" ) { + $ACTION = 1; + $URL = $URL_CUSTOMERDETAILS; + } + elsif ( $service eq "relays" ) { + $ACTION = 1; + $URL = $URL_STATUSSCHEDULE; + } + elsif ( $service eq "stopall" ) { + $ACTION = 2; + $URL = $URL_SETZONE . "&action=stopall"; + } + elsif ( $service eq "stop" ) { + $ACTION = 2; + $URL = $URL_SETZONE . "&action=stop&relay_id=$type"; + } + elsif ( $service eq "runall" ) { + $ACTION = 2; + $URL = $URL_SETZONE . "&action=runall&period_id=999&custom=$type"; + } + elsif ( $service eq "run" ) { + $ACTION = 2; + my @a = split( "[ \t][ \t]*", $type ); + $URL = $URL_SETZONE + . "&action=run&period_id=999&relay_id=$a[0]&custom=$a[1]"; + } + elsif ( $service eq "suspendall" ) { + $ACTION = 2; + $URL = $URL_SETZONE . "&action=suspendall&period_id=999&custom=$type"; + } + elsif ( $service eq "suspend" ) { + $ACTION = 2; + my @a = split( "[ \t][ \t]*", $type ); + $URL = $URL_SETZONE + . "&action=suspend&period_id=999&relay_id=$a[0]&custom=$a[1]"; + } + elsif ( $service eq "state" ) { + $ACTION = 3; + } + else { + # $URL .= $api_key; + } + + # send request via HTTP-GET method + Log3 $name, 5, "HYDRAWISE $name: $method $URL (" . urlDecode($data) . ")" + if ( defined($data) ); + Log3 $name, 5, "HYDRAWISE $name: $method $URL" + if ( !defined($data) ); + + if ( defined($type) && $type eq "blocking" ) { + my ( $err, $data ) = HttpUtils_BlockingGet( + { + url => $URL, + timeout => 15, + noshutdown => 1, + data => $data, + method => $method, + hash => $hash, + service => $service, + timestamp => $timestamp, + } + ); + return $data; + } + else { + if ( $ACTION eq "3" ) { + HttpUtils_NonblockingGet( + { + url => $URL_STATUSSCHEDULE, + timeout => $timeout, + noshutdown => 1, + data => $data, + method => $method, + hash => $hash, + service => $service, + timestamp => $timestamp, + callback => \&HYDRAWISE_ReceiveCommand, + } + ); + HttpUtils_NonblockingGet( + { + url => $URL_CUSTOMERDETAILS, + timeout => $timeout, + noshutdown => 1, + data => $data, + method => $method, + hash => $hash, + service => $service, + timestamp => $timestamp, + callback => \&HYDRAWISE_ReceiveCommand, + } + ); + } + elsif ( $ACTION eq "2" ) { + HttpUtils_NonblockingGet( + { + url => $URL, + timeout => $timeout, + noshutdown => 1, + data => $data, + method => $method, + hash => $hash, + service => $service, + timestamp => $timestamp, + } + ); + } + elsif ( $ACTION eq "1" ) { + HttpUtils_NonblockingGet( + { + url => $URL, + timeout => $timeout, + noshutdown => 1, + data => $data, + method => $method, + hash => $hash, + service => $service, + timestamp => $timestamp, + callback => \&HYDRAWISE_ReceiveCommand, + } + ); + } + } + + return; +} + +################################### +sub HYDRAWISE_ReceiveCommand { + my ( $param, $err, $data ) = @_; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + my $service = $param->{service}; + my $cmd = $param->{cmd}; + my $state = ReadingsVal( $name, "state", "off" ); + my $presence = ReadingsVal( $name, "presence", "absent" ); + + my $rc = ( $param->{buf} ) ? $param->{buf} : $param; + my $return; + + Log3 $name, 5, +"HYDRAWISE $name: called function HYDRAWISE_ReceiveCommand() rc: $rc err: $err data: $data "; + + readingsBeginUpdate($hash); + + # device not reachable + if ($err) { + $presence = "absent"; + $state = "off"; + + if ( !defined($cmd) || $cmd eq "" ) { + Log3 $name, 4, "HYDRAWISE $name:$service RCV $err"; + } + else { + Log3 $name, 4, "HYDRAWISE $name:$service/$cmd RCV $err"; + } + + readingsBulkUpdateIfChanged( $hash, "presence", $presence ); + readingsBulkUpdateIfChanged( $hash, "state", $state ); + + # keep last state + #HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, "state", "Error" ); + } + + # data received + elsif ($data) { + $presence = "present"; + $state = "on"; + + # Set reading for presence + # + #readingsSingleUpdate( $hash, "presence", $presence, 1 ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, "presence", "$presence" ); + + # Set reading for state + # + #readingsSingleUpdate( $hash, "state", $state, 1 ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, "state", "$state" ); + + if ( !defined($cmd) ) { + Log3 $name, 4, "HYDRAWISE $name: RCV $service"; + } + else { + Log3 $name, 4, "HYDRAWISE $name: RCV $service/$cmd"; + } + + if ( $data ne "" ) { + if ( $data =~ /^{/ || $data =~ /^\[/ ) { + if ( !defined($cmd) || $cmd eq "" ) { + Log3 $name, 4, + "HYDRAWISE $name: RES $service - DATA: $data"; + } + else { + Log3 $name, 4, "HYDRAWISE $name: RES $service/$cmd - $data"; + } + $return = decode_json( encode_utf8($data) ); + + #print "Decoded return: ".Dumper($return); + #Debug $return; + } + else { + Log3 $name, 4, "HYDRAWISE $name: RES ERROR $service\n" . $data; + if ( !defined($cmd) || $cmd eq "" ) { + Log3 $name, 5, "HYDRAWISE $name: RES ERROR $service\n$data"; + } + else { + Log3 $name, 5, + "HYDRAWISE $name: RES ERROR $service/$cmd\n$data"; + } + return; + } + } + + ####################### + # process return data + # + + # state + + if ( $service eq "state" or $service eq "longpollState" ) { + if ( ref($return) eq "HASH" && !defined($cmd) ) { + + # controllers + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, "customer_id", + $return->{customer_id} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "cur_controller_id", $return->{controller_id} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "cur_controller_name", $return->{current_controller} ); + + if ( ref( $return->{controllers} ) eq "ARRAY" + && scalar( @{ $return->{controllers} } ) > 0 ) + { + my $lnnumer = 1; + my $last_contact; + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "controller_counts", + scalar( @{ $return->{controllers} } ) ); + for my $controllers ( @{ $return->{controllers} } ) { + HYDRAWISE_ReadingsBulkUpdateIfChanged( + $hash, + "ct" . $lnnumer . "_controller_id", + $controllers->{controller_id} + ); + $last_contact = + localtime( $controllers->{last_contact} ) + ->strftime('%F %T'); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "ct" . $lnnumer . "_last_contact", + $last_contact ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "ct" . $lnnumer . "_controller_name", + $controllers->{name} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( + $hash, + "ct" . $lnnumer . "_controller_message", + $controllers->{status} + ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( + $hash, + "ct" . $lnnumer . "_serial_number", + $controllers->{serial_number} + ); + $lnnumer++; + } + } + + # relays + + if ( ref( $return->{relays} ) eq "ARRAY" + and scalar( @{ $return->{relays} } ) > 0 ) + { + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "relay_counts", scalar( @{ $return->{relays} } ) ); + for my $relays ( @{ $return->{relays} } ) { + Log3 $name, 5, +"HYDRAWISE $name: $relays->{relay} $relays->{relay_id}"; + + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "rl" . $relays->{relay} . "_relay", + $relays->{relay} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "rl" . $relays->{relay} . "_relay_id", + $relays->{relay_id} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "rl" . $relays->{relay} . "_name", + $relays->{name} ); + +#HYDRAWISE_ReadingsBulkUpdateIfChanged($hash, "rl".$relays->{relay}."_time", $relays->{time}); + HYDRAWISE_ReadingsBulkUpdateIfChanged( + $hash, + "rl" . $relays->{relay} . "_next", + HYDRAWISE_GetDay( $hash, $relays->{timestr} ) + ); + +#HYDRAWISE_ReadingsBulkUpdateIfChanged($hash, "rl".$relays->{relay}."_period", HYDRAWISE_GetDuration($hash,$relays->{period})); + HYDRAWISE_ReadingsBulkUpdateIfChanged( + $hash, + "rl" . $relays->{relay} . "_run_minutes", + $relays->{run} / 60 + ); + +#HYDRAWISE_ReadingsBulkUpdateIfChanged($hash, "rl".$relays->{relay}."_nicetime", $relays->{nicetime}); + } + } + } + + readingsEndUpdate( $hash, 1 ); + + HYDRAWISE_CheckLongpoll($hash) if ( $service eq "state" ); + + HYDRAWISE_SendCommand( $hash, "longpollState" ) + if ( $service eq "longpollState" ); + + } + + # relays + elsif ( $service eq "relays" ) { + + if ( ref( $return->{relays} ) eq "ARRAY" + and scalar( @{ $return->{relays} } ) > 0 ) + { + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, "relay_counts", + scalar( @{ $return->{relays} } ) ); + for my $relays ( @{ $return->{relays} } ) { + Log3 $name, 5, + "HYDRAWISE $name: $relays->{relay} $relays->{relay_id}"; + + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "rl" . $relays->{relay} . "_relay", + $relays->{relay} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "rl" . $relays->{relay} . "_relay_id", + $relays->{relay_id} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, + "rl" . $relays->{relay} . "_name", + $relays->{name} ); + +#HYDRAWISE_ReadingsBulkUpdateIfChanged($hash, "rl".$relays->{relay}."_time", $relays->{time}); + HYDRAWISE_ReadingsBulkUpdateIfChanged( + $hash, + "rl" . $relays->{relay} . "_next", + HYDRAWISE_GetDay( $hash, $relays->{timestr} ) + ); + +#HYDRAWISE_ReadingsBulkUpdateIfChanged($hash, "rl".$relays->{relay}."_period", HYDRAWISE_GetDuration($hash,$relays->{period})); + HYDRAWISE_ReadingsBulkUpdateIfChanged( + $hash, + "rl" . $relays->{relay} . "_run_minutes", + $relays->{run} / 60 + ); + +#HYDRAWISE_ReadingsBulkUpdateIfChanged($hash, "rl".$relays->{relay}."_nicetime", $relays->{nicetime}); + } + } + readingsEndUpdate( $hash, 1 ); + } + + # authenticate + elsif ( $service eq "authenticate" ) { + if ( ref($return) eq "HASH" ) { + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, "controller_id", + $return->{controller_id} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, "customer_id", + $return->{customer_id} ); + HYDRAWISE_ReadingsBulkUpdateIfChanged( $hash, "user_id", + $return->{user_id} ); + + readingsEndUpdate( $hash, 1 ); + + # new context received - reload state + HYDRAWISE_SendCommand( $hash, "state" ); + HYDRAWISE_TriggerFullDataUpdate($hash); + + # re-execute previous command + if ( defined($cmd) ) { + if ( $cmd =~ /(\w+)\/(\w+)/ ) { + HYDRAWISE_SendCommand( $hash, $1, $2 ); + } + else { + HYDRAWISE_SendCommand( $hash, $cmd ); + } + } + } + } + + # all other command results + else { + Log3 $name, 2, +"HYDRAWISE $name: ERROR: method to handle response of $service not implemented"; + } + + } + else { + if ( $rc =~ /401/ ) { + Log3 $name, 4, + "HYDRAWISE $name: authentication context invalidated"; + if ( $service =~ /deleteAlert|setCalendar/ ) { + HYDRAWISE_SendCommand( $hash, "authenticate", "$service" ); + } + elsif ( $service eq "state" and defined($cmd) ) { + HYDRAWISE_SendCommand( $hash, "authenticate", "$service/$cmd" ); + } + else { + readingsSingleUpdate( $hash, "contextId", "", 1 ); + } + $hash->{LONGPOLL} = 0 if ( $service eq "longpollState" ); + } + + } + + return; +} + +sub HYDRAWISE_CheckLongpoll { + my $hash = shift; + my $name = $hash->{NAME}; + + return if ( AttrVal( $name, "disable", 0 ) == 1 ); + + if ( !defined( $hash->{LONGPOLL} ) || time() - $hash->{LONGPOLL} > 3600 ) { + Log3 $name, 4, "HYDRAWISE $name: Request GET state (longPoll)"; + HYDRAWISE_SendCommand( $hash, "longpollState" ); + } + + return; +} + +sub HYDRAWISE_TriggerFullDataUpdate { + my $hash = shift; + + # HYDRAWISE_SendCommand($hash, "firmware"); + # HYDRAWISE_SendCommand($hash, "automaticUpdate"); + # HYDRAWISE_SendCommand($hash, "calendar"); + # HYDRAWISE_SendCommand($hash, "updates"); + # HYDRAWISE_SendCommand($hash, "security"); + # HYDRAWISE_SendCommand($hash, "predictive/location"); + # HYDRAWISE_SendCommand($hash, "predictive/weather"); + + return; +} + +sub HYDRAWISE_ReadingsBulkUpdateIfChanged { + my ( $hash, $reading, $value ) = @_; + my $name = $hash->{NAME}; + + $value = "" if ( !defined($value) ); + readingsBulkUpdate( $hash, $reading, $value ) + if ( ReadingsVal( $name, $reading, "" ) ne $value ); + + return; +} + +sub HYDRAWISE_GetDuration { + my ( $hash, $duration ) = @_; + + return sprintf( "%d:%02d", + int( $duration / 60 ), + $duration - int( $duration / 60 ) * 60 ); +} + +sub HYDRAWISE_GetDay { + my ( $hash, $day ) = @_; + my $days = { + 'Mon' => "Montag", + 'Tue' => "Dienstag", + 'Wed' => "Mittwoch", + 'Thu' => "Donnerstag", + 'Fri' => "Freitag", + 'Sat' => "Samstag", + 'Sun' => "Sonntag", + 'Now' => "Running", + }; + + if ( defined( $days->{$day} ) ) { + return $days->{$day}; # Wochentag + } + + return $day; # Uhrzeit bei heutigem Tag +} + +1; + +=pod +=item command +=item summary controlling Hydrawise irrigation +=item summary_DE Steuerung der Hydrawise-Bewässerung + +=begin html + + +

Hunter Hydrawise

+
    + The module receives data and sends commands via the Hunter Hydrawise API.
    + All zones are identified by a unique ID - this ID is used to modify zone watering schedules, + including running a zone, stopping a zone and suspending a zone for a period of time. + Status information on all zones associated with an account can also be queried. +
    + +
    + Prerequisits +
      +
      + API keys can be obtained from your Hydrawise account under My Account -> Generate API Key. + This has the format XXXX-XXXX-XXXX-XXXX. +
      + + +
    +
    + + + Definition and usage +
      +
      + The module is defined using the API key and the refresh interval! +
    +
    +
      + Definition of the module +
      + +
        +
        + define <name> HYDAWISE <API-KEY> <Interval>
        +
        +
      +
    +
    + Example of a definition:
    + +
      +
      + define myHydrawise HYDAWISE 1234-5678-90AB-CDEF 60
      +
      +
    + + + + Set +
    + +
      + + + + + + + + + + +
      renewcontext Returns details of all controllers associated with the customer account.
      renewRelays Return of irrigation plans for control units
      run Execute a zone for a certain period of time. 2 Parameters: "relay_id" "time_in_seconds
      runall Execute all zones for a certain period of time. 1 Parameter: "time_in_seconds"
      stop Stops a zone. 1 Parameter: "relay_id"
      stopall Stops all running zones.
      suspend Suspends a zone for a certain time. 3 Parameters: "relay_id" "DD.MM.YYYY" "HH24:MI".
      suspendall Suspends all zones for a certain time. 2 Parameters: "DD.MM.YYYY" "HH24:MI"
      +
    +
    + + Get +
    + +
      + + + +
      help Displays help for the SET commands
      +
    +
    + + + Readings +
      +
      + + + + + + + + + + + + + + + + + + + + +
      controller_counts Number of available controllers
      controller_id Controller ID
      ct1_controller_id Controler 1: ID
      ct1_controller_message Controler 1: Status message from Hydrawise
      ct1_controller_name Controler 1: Defined name in Hydrawise
      ct1_last_contact Controler 1: Last contact from Hydrawise to the controller
      ct1_serial_number Controler 1: Serial number of the controller
      cur_controller_id Current Controller (ID)
      cur_controller_name Current Controller name
      customer_id Customer ID of Hydrawise
      presence Status of the module: presence or absent
      relay_counts Number of relays
      rl1_name Relay 1: Name
      rl1_next Relay 1: Next time this zone will water
      rl1_relay Relay 1: Physical zone number
      rl1_relay_id Relay 1: Unique ID for this zone
      rl1_run_minutes Relay 1: Length of next run time. If a run is in progress value will indicate number of seconds remaining.
      +
      +
    +
+=end html + +=begin html_DE + + +

Hunter Hydrawise

+
    + Das Modul empfängt Daten und sendet Befehle über die Hunter Hydrawise API.
    + Alle Zonen werden durch eine eindeutige ID identifiziert - diese ID wird verwendet, um die Bewässerungspläne der Zonen zu modifizieren, + einschließlich des Betreibens einer Zone, des Anhaltens einer Zone und des Aussetzens einer Zone für eine bestimmte Zeit. + Statusinformationen zu allen Zonen, die mit einem Konto verbunden sind, können ebenfalls abgefragt werden. +
    +
    + Voraussetzungen +
      +
      + Einen API-Schlüssel können Sie von Ihrem Hydrawise-Konto unter Mein Konto generieren lassen. + Dieser hat das Format XXXX-XXXX-XXXX-XXXX. +
      +
    +
    + + + Definition und Verwendung +
      +
      + Das Modul wird mithilfe des API-Keys und des Refreshintervals definiert! +
    +
    +
      + Definition des Moduls +
      + +
        +
        + define <name> HYDAWISE <API-KEY> <Intervall>
        +
        +
      +
    +
    + Beispiel für eine Moduldefinition:
    + +
      +
      + define myHydrawise HYDAWISE 1234-5678-90AB-CDEF 60
      +
      +
    + + + + Set +
    + +
      + + + + + + + + + + +
      renewcontext Gibt Details zu allen Controllern zurück, die mit dem Kundenkonto verbunden sind.
      renewRelays Rückgabe von Bewässerungsplänen für Steuergeräte
      run Eine Zone für eine bestimmte Zeitspanne ausführen. 2 Parameter: "relay_id" "zeit_in_sekunden"
      runall Alle Zonen für eine bestimmte Zeitspanne ausführen. 1 Parameter: "zeit_in_sekunden"
      stop Stoppt eine Zone. 1 Parameter: "relay_id"
      stopall Stoppt alle laufenden Zonen.
      suspend Setzt eine Zone für eine bestimmte Zeit aus. 3 Parameter: "relay_id" "DD.MM.YYYY" "HH24:MI"
      suspendall Setzt alle Zonen für eine bestimmte Zeit aus. 2 Parameter: "DD.MM.YYYY" "HH24:MI"
      +
    +
    + + Get +
    + +
      + + + +
      help Zeigt die Hilfe für die SET Befehle an
      +
    +
    + + + Readings +
      +
      + + + + + + + + + + + + + + + + + + + + +
      controller_counts Anzahl der vorhandenen Controller
      controller_id Controller ID
      ct1_controller_id Controler 1: ID
      ct1_controller_message Controler 1: Statusnachricht von Hydrawise
      ct1_controller_name Controler 1: Definierter Name in Hydrawise
      ct1_last_contact Controler 1: Letzter Kontakt von Hydrawise zum Controller
      ct1_serial_number Controler 1: Seriennummer des Controllers
      cur_controller_id Aktiver Controller (ID)
      cur_controller_name Aktiver Controllername
      customer_id KundenID von Hydrawise
      presence Status des Moduls: presence oder absent
      relay_counts Anzahl der Relays
      rl1_name Relay 1: Name
      rl1_next Relay 1: Nächste Ausführung der Zone
      rl1_relay Relay 1: Physikalische Zonennummer
      rl1_relay_id Relay 1: Eindeutige ID für diese Zone
      rl1_run_minutes Relay 1: Länge der nächsten Laufzeit. Wenn ein Lauf im Gange ist, gibt der Wert die Anzahl der verbleibenden Sekunden an.
      +
      +
    +
+=end html_DE +=cut