diff --git a/CHANGED b/CHANGED index 37eb437..af74819 100644 --- a/CHANGED +++ b/CHANGED @@ -1 +1 @@ - Reviewed-by: Marko Oldenburg + cleanup code, fix start up defined string diff --git a/FHEM/74_GardenaSmartDevice.pm b/FHEM/74_GardenaSmartDevice.pm index a68b12b..363f97d 100644 --- a/FHEM/74_GardenaSmartDevice.pm +++ b/FHEM/74_GardenaSmartDevice.pm @@ -1,1785 +1,1750 @@ -############################################################################### -# -# Developed with VSCodium and richterger perl plugin. -# -# (c) 2017-2022 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net) -# All rights reserved -# -# Special thanks goes to comitters: -# - Michael (mbrak) Thanks for Commandref -# - Matthias (Kenneth) Thanks for Wiki entry -# - BioS Thanks for predefined start points Code -# - fettgu Thanks for Debugging Irrigation Control data flow -# - Sebastian (BOFH) Thanks for new Auth Code after API Change -# -# -# This script 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 -# any later version. -# -# The GNU General Public License can be found at -# http://www.gnu.org/copyleft/gpl.html. -# A copy is found in the textfile GPL.txt and important notices to the license -# from the author is found in LICENSE.txt distributed with these scripts. -# -# This script 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. -# -# -# $Id$ -# -############################################################################### -## -## -## Das JSON Modul immer in einem eval aufrufen -# $data = eval{decode_json($data)}; -# -# if($@){ -# Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@"); -# -# readingsSingleUpdate($hash, "state", "error", 1); -# -# return; -# } -# -# -###### Wichtige Notizen -# -# apt-get install libio-socket-ssl-perl -# http://www.dxsdata.com/de/2016/07/php-class-for-gardena-smart-system-api/ -# -## -## - -## unserer packagename -package FHEM::GardenaSmartDevice; -use GPUtils qw(GP_Import GP_Export); - -use strict; -use warnings; -use POSIX; -use FHEM::Meta; -use Time::Local; -use Time::Piece; -use Time::Seconds; - -# try to use JSON::MaybeXS wrapper -# for chance of better performance + open code -eval { - require JSON::MaybeXS; - import JSON::MaybeXS qw( decode_json encode_json ); - 1; -} or do { - - # try to use JSON wrapper - # for chance of better performance - eval { - # JSON preference order - local $ENV{PERL_JSON_BACKEND} = - 'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP' - unless ( defined( $ENV{PERL_JSON_BACKEND} ) ); - - require JSON; - import JSON qw( decode_json encode_json ); - 1; - } or do { - - # In rare cases, Cpanel::JSON::XS may - # be installed but JSON|JSON::MaybeXS not ... - eval { - require Cpanel::JSON::XS; - import Cpanel::JSON::XS qw(decode_json encode_json); - 1; - } or do { - - # In rare cases, JSON::XS may - # be installed but JSON not ... - eval { - require JSON::XS; - import JSON::XS qw(decode_json encode_json); - 1; - } or do { - - # Fallback to built-in JSON which SHOULD - # be available since 5.014 ... - eval { - require JSON::PP; - import JSON::PP qw(decode_json encode_json); - 1; - } or do { - - # Fallback to JSON::backportPP in really rare cases - require JSON::backportPP; - import JSON::backportPP qw(decode_json encode_json); - 1; - }; - }; - }; - }; -}; - -## Import der FHEM Funktionen -#-- Run before package compilation -BEGIN { - - # Import from main context - GP_Import( - qw(readingsSingleUpdate - readingsBulkUpdate - readingsBulkUpdateIfChanged - readingsBeginUpdate - readingsEndUpdate - Log3 - CommandAttr - AttrVal - ReadingsVal - readingFnAttributes - AssignIoPort - modules - IOWrite - defs - makeDeviceName) - ); -} - -#-- Export to main context with different name -GP_Export( - qw( - Initialize - ) -); - -sub Initialize { - my $hash = shift; - - $hash->{Match} = '^{"id":".*'; - - $hash->{SetFn} = \&Set; - $hash->{DefFn} = \&Define; - $hash->{UndefFn} = \&Undef; - $hash->{ParseFn} = \&Parse; - - $hash->{AttrFn} = \&Attr; - $hash->{AttrList} = - "readingValueLanguage:de,en " - . "model:watering_computer,sensor,sensor2,mower,ic24,power,electronic_pressure_pump " - . "extendedState:0,1 " - . "IODev " - . $readingFnAttributes; - $hash->{parseParams} = 1; - - return FHEM::Meta::InitMod( __FILE__, $hash ); -} - -sub Define { - my $hash = shift // return; - my $aArg = shift // return; - - return $@ unless ( FHEM::Meta::SetInternals($hash) ); - use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); - - return - "too few parameters: define GardenaSmartDevice " - if ( scalar( @{$aArg} ) < 3 ); - - my $name = $aArg->[0]; - my $deviceId = $aArg->[2]; - my $category = $aArg->[3]; - - $hash->{DEVICEID} = $deviceId; - $hash->{VERSION} = version->parse($VERSION)->normal; - $hash->{helper}{STARTINGPOINTID} = ''; - $hash->{helper}{schedules_paused_until_id} = ''; - $hash->{helper}{eco_mode_id} = ''; - $hash->{helper}{button_config_time_id} = ''; - $hash->{helper}{winter_mode_id} = ''; - # Electroni Pressure Pump - $hash->{helper}{operating_mode_id} = ''; - $hash->{helper}{leakage_detection_id} = ''; - $hash->{helper}{turn_on_pressure_id} = ''; - - - $hash->{helper}{_id} = ''; - - # IrrigationControl valve control max 6 - $hash->{helper}{schedules_paused_until_1_id} = ''; - $hash->{helper}{schedules_paused_until_2_id} = ''; - $hash->{helper}{schedules_paused_until_3_id} = ''; - $hash->{helper}{schedules_paused_until_4_id} = ''; - $hash->{helper}{schedules_paused_until_5_id} = ''; - $hash->{helper}{schedules_paused_until_6_id} = ''; - - CommandAttr( undef, - "$name IODev $modules{GardenaSmartBridge}{defptr}{BRIDGE}->{NAME}" ) - if ( AttrVal( $name, 'IODev', 'none' ) eq 'none' ); - - my $iodev = AttrVal( $name, 'IODev', 'none' ); - - AssignIoPort( $hash, $iodev ) if ( !$hash->{IODev} ); - - if ( defined( $hash->{IODev}->{NAME} ) ) { - Log3 $name, 3, "GardenaSmartDevice ($name) - I/O device is " - . $hash->{IODev}->{NAME}; - } - else { - Log3 $name, 1, "GardenaSmartDevice ($name) - no I/O device"; - } - - $iodev = $hash->{IODev}->{NAME}; - - my $d = $modules{GardenaSmartDevice}{defptr}{$deviceId}; - - return -"GardenaSmartDevice device $name on GardenaSmartBridge $iodev already defined." - if ( defined($d) - && $d->{IODev} == $hash->{IODev} - && $d->{NAME} ne $name ); - - CommandAttr( undef, $name . ' room GardenaSmart' ) - if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); - - CommandAttr( undef, $name . ' model ' . $category ) - if ( AttrVal( $name, 'model', 'none' ) eq 'none' ); - - Log3 $name, 3, -"GardenaSmartDevice ($name) - defined GardenaSmartDevice with DEVICEID: $deviceId"; - readingsSingleUpdate( $hash, 'state', 'initialized', 1 ); - - $modules{GardenaSmartDevice}{defptr}{$deviceId} = $hash; - - return; -} - -sub Undef { - my $hash = shift; - my $arg = shift; - - my $name = $hash->{NAME}; - my $deviceId = $hash->{DEVICEID}; - - delete $modules{GardenaSmartDevice}{defptr}{$deviceId}; - - return; -} - -sub Attr { - - my ( $cmd, $name, $attrName, $attrVal ) = @_; - my $hash = $defs{$name}; - - return; -} - -sub Set { - my $hash = shift // return; - my $aArg = shift // return; - - my $name = shift @$aArg; - my $cmd = shift @$aArg - // return qq{"set $name" needs at least one argument}; - - my $payload; - my $abilities; - my $service_id; - my $mainboard_version = - ReadingsVal( $name, 'mower_type-mainboard_version', 0.0 ); - - my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, - $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time); - - my $timezone_offset = $Sommerzeit ? 0 : ( Time::Piece->new )->tzoffset; - - #set default abilitie ... overwrite in cmd to change - $abilities = 'mower' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' ); - $abilities = 'watering' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' - || AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ); - $abilities = 'power' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' ); - $abilities = 'watering' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'electronic_pressure_pump' ); - - ### mower - # service_id (eco, parkuntilfurhternotice, startpoints) - if ( lc $cmd eq 'parkuntilfurthernotice' ) { - $payload = '"name":"park_until_further_notice"'; - if ( $mainboard_version > 10.30 ) { - $payload = -' "settings":{"name":"schedules_paused_until","value":"2038-01-18T00:00:00.000Z","device":"' - . $hash->{DEVICEID} . '"}'; - $abilities = 'mower_settings'; - $service_id = $hash->{helper}{schedules_paused_until_id}; - } - } - elsif ( lc $cmd eq 'parkuntilnexttimer' ) { - $payload = '"name":"park_until_next_timer"'; - if ( $mainboard_version > 10.30 ) { - $payload = '"properties":{"name":"mower_timer","value":0}'; - $abilities = 'mower_timer'; - } - } - elsif ( lc $cmd eq 'startresumeschedule' ) { - $payload = '"name":"start_resume_schedule"'; - if ( $mainboard_version > 10.30 ) { - $payload = -' "settings":{"name":"schedules_paused_until","value":"","device":"' - . $hash->{DEVICEID} . '"}'; - $abilities = 'mower_settings'; - $service_id = $hash->{helper}{schedules_paused_until_id}; - } - } - elsif ( lc $cmd eq 'startoverridetimer' ) { - $payload = '"name":"start_override_timer","parameters":{"duration":' - . $aArg->[0] * 60 . '}'; - if ( $mainboard_version > 10.30 ) { - $payload = '"properties":{"name":"mower_timer","value":' - . $aArg->[0] * 60 . '}'; - $abilities = 'mower_timer'; - } - - } - elsif ( lc $cmd eq 'startpoint' ) { - my $err; - ( $err, $payload, $abilities ) = - SetPredefinedStartPoints( $hash, $aArg ); - $service_id = $hash->{helper}{STARTINGPOINTID}; - return $err if ( defined($err) ); - } - elsif ( lc $cmd eq 'eco' ) { - $payload = - '"settings": {"name": "eco_mode", "value": ' - . $aArg->[0] - . ', "device": "' - . $hash->{DEVICEID} . '"}'; - $abilities = 'mower_settings' if ( $mainboard_version > 10.30 ); - $service_id = $hash->{helper}{eco_mode_id}; - -#$abilities['service_id'] = $hash->{helper}{SCHEDULESID} if ( $mainboard_version > 10.30 ); - } - ### electronic_pressure_pump - # elsif ( lc $cmd eq 'pumptimer' ) { - # $payload = - # '"name":"pump_manual_watering_timer","parameters":{"duration":' - # . $aArg->[0] . '}'; - # } - ### watering_computer & electronic pump - elsif ( lc $cmd eq 'manualoverride' ) { - $payload = - '"properties":{"name":"watering_timer_1' - . '","value":{"state":"manual","duration":' - . $aArg->[0] * 60 - . ',"valve_id":1}}'; - } - elsif ( lc $cmd eq 'manualbuttontime' ) { - $service_id = $hash->{helper}{button_config_time_id}; - $payload = - '"properties":{"name":"button_config_time",' - . '"value":' - . $aArg->[0] * 60 - . ',"timestamp":"2021-05-26T19:06:23.680Z"' - . ',"at_bound":null,"unit":"seconds","ability":"' - . $service_id . '"}'; - $abilities = 'watering_button_config'; - } - elsif ( $cmd =~ m{\AcancelOverride}xms ) { - - my $valve_id = 1; - - if ( $cmd =~ m{\AcancelOverrideValve(\d)\z}xms ) { - $valve_id = $1; - } - - $payload = - '"properties":{"name":"watering_timer_' - . $valve_id - . '","value":{"state":"idle","duration":' - . 0 - . ',"valve_id":' - . $valve_id . '}}'; - } - elsif ( $cmd =~ /.*Schedule$/ ) { - my $duration = ( - ( - defined( $aArg->[0] ) - ? ( - ( - ( Time::Piece->new ) + - ( ONE_HOUR * $aArg->[0] ) - - $timezone_offset - )->datetime - ) - . '.000Z' - : '2038-01-18T00:00:00.000Z' - ) - ); - - $abilities = 'wateringcomputer_settings'; - $service_id = $hash->{helper}->{'schedules_paused_until_id'}; - $payload = - '"settings":{"name":"schedules_paused_until"' - . ', "value":"' - . ( $cmd eq 'resumeSchedule' ? '' : $duration ) - . '","device":"' - . $hash->{DEVICEID} . '"}'; - } - elsif ( lc $cmd eq 'on' || lc $cmd eq 'off' || lc $cmd eq 'on-for-timer' ) { - my $val = ( - scalar( !@$aArg == 0 ) && ref($aArg) eq 'ARRAY' - ? $aArg->[0] * 60 - : lc $cmd - ); - - $payload = - '"properties":{"name":"power_timer", "value":"' . $val . '"}'; - } - ### Watering ic24 - elsif ( $cmd =~ m{\AmanualDurationValve\d\z}xms ) { - my $valve_id; - - if ( $cmd =~ m{\AmanualDurationValve(\d)\z}xms ) { - $valve_id = $1; - } - - $payload = - '"properties":{"name":"watering_timer_' - . $valve_id - . '","value":{"state":"manual","duration":' - . $aArg->[0] * 60 - . ',"valve_id":' - . $valve_id . '}}'; - } - elsif ( $cmd eq 'closeAllValves' ) { - $payload = '"name":"close_all_valves","parameters":{}'; - } - elsif ( $cmd =~ /.*ScheduleValve$/ ) { - my $valve_id = $aArg->[0]; - my $duration = ( - ( - defined( $aArg->[1] ) - ? ( - ( - ( Time::Piece->new ) + - ( ONE_HOUR * $aArg->[1] ) - - $timezone_offset - )->datetime - ) - . '.000Z' - : '2038-01-18T00:00:00.000Z' - ) - ); - - $abilities = 'irrigation_settings'; - $service_id = - $hash->{helper}->{ 'schedules_paused_until_' . $valve_id . '_id' }; - $payload = - '"settings":{"name":"schedules_paused_until_' - . $valve_id - . '", "value":"' - . ( $cmd eq 'resumeScheduleValve' ? '' : $duration ) - . '","device":"' - . $hash->{DEVICEID} . '"}'; - } - ### Watering_pressure_pump - elsif ( lc $cmd eq 'operating_mode') { - my $op_mode = $aArg->[0]; - $payload = '"settings":{"name":"operating_mode",' - .'"value":"'.$op_mode.'",' - .'"device":"' - . $hash->{DEVICEID}.'"}'; - $abilities = 'watering_pressure_pump_settings'; - $service_id = $hash->{helper}->{ 'operating_mode_id' }; - } - elsif ( lc $cmd eq 'leakage_detection') { - my $leakdetection_mode = $aArg->[0]; - $payload = '"settings":{"name":"leakage_detection",' - .'"value":"'.$leakdetection_mode.'",' - .'"device":"' - . $hash->{DEVICEID}.'"}'; - $abilities = 'watering_pressure_pump_settings'; - $service_id = $hash->{helper}->{ 'leakage_detection_id' }; - } - elsif ( lc $cmd eq 'turn_on_pressure') { - my $turnonpressure = $aArg->[0]; - $payload = '"settings":{"name":"turn_on_pressure",' - .'"value":"'.$turnonpressure.'",' - .'"device":"' - . $hash->{DEVICEID}.'"}'; - $abilities = 'watering_pressure_pump_settings'; - $service_id = $hash->{helper}->{ 'turn_on_pressure_id' }; - } - elsif ( lc $cmd eq 'resetvalveerrors') { - $payload = '"name":"reset_valve_errors",' - .' "parameters": {}'; - $abilities = 'error'; - } - - ### Sensors - elsif ( lc $cmd eq 'refresh' ) { - - my $sensname = $aArg->[0]; - if ( lc $sensname eq 'temperature' ) { - if ( ReadingsVal( $name, 'device_info-category', 'sensor' ) eq - 'sensor' ) - { - $payload = '"name":"measure_ambient_temperature"'; - $abilities = 'ambient_temperature'; - } - else { - $payload = '"name":"measure_soil_temperature"'; - $abilities = 'soil_temperature'; - } - } - elsif ( lc $sensname eq 'light' ) { - $payload = '"name":"measure_light"'; - $abilities = 'light'; - - } - elsif ( lc $sensname eq 'humidity' ) { - $payload = '"name":"measure_soil_humidity"'; - $abilities = 'humidity'; - } - } - ## winter sleep - elsif ( lc $cmd eq 'winter_mode' ) { - $payload = - '"settings":{"name":"winter_mode","value":"' - . $aArg->[0] - . '","device":"' - . $hash->{DEVICEID} . '"}'; - $abilities = 'winter_settings'; - $service_id = $hash->{helper}->{'winter_mode_id'}; - } - else { - - my $list = ''; - - $list .= -'parkUntilFurtherNotice:noArg parkUntilNextTimer:noArg startResumeSchedule:noArg startOverrideTimer:slider,0,1,240 startpoint' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' ); - - $list .= -'manualOverride:slider,1,1,59 cancelOverride:noArg resumeSchedule:noArg stopSchedule manualButtonTime:slider,0,2,100' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ); - - - $list .= 'manualOverride:slider,1,1,90 cancelOverride:noArg operating_mode:automatic,scheduled leakage_detection:watering,washing_machine,domestic_water_supply,off turn_on_pressure:slider,2,0.2,3.0,1 resetValveErrors:noArg' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'electronic_pressure_pump' ); - - $list .= -'closeAllValves:noArg stopScheduleValve:select,'.ReadingsVal( $name, 'ic24-valves_connected', '1' ).' resumeScheduleValve:select,'.ReadingsVal( $name, 'ic24-valves_connected', '1' ) - if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ); - - foreach my $valve (split(',', ReadingsVal( $name, 'ic24-valves_connected', '1'))) { - $list .= ' manualDurationValve'.$valve.':slider,1,1,90 ' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ); - } - - foreach my $valve (split(',', ReadingsVal( $name, 'ic24-valves_connected', '1'))) { - $list .= ' cancelOverrideValve'.$valve.':noArg ' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ); - } - - $list .= 'refresh:temperature,humidity' - if ( AttrVal( $name, 'model', 'unknown' ) =~ /sensor.?/ ); - - # add light for old sensors - $list .= ',light' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'sensor' - && ReadingsVal( $name, 'device_info-category', 'unknown' ) eq - 'sensor' ); - - $list .= 'on:noArg off:noArg on-for-timer:slider,0,1,720' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' ); - - # all devices has abilitie to fall a sleep - $list .= ' winter_mode:awake,hibernate'; - return "Unknown argument $cmd, choose one of $list"; - } - - $hash->{helper}{deviceAction} = $payload; - readingsSingleUpdate( $hash, "state", "send command to gardena cloud", 1 ); - - IOWrite( $hash, $payload, $hash->{DEVICEID}, $abilities, $service_id ); - Log3 $name, 4, -"GardenaSmartBridge ($name) - IOWrite: $payload $hash->{DEVICEID} $abilities IODevHash=$hash->{IODev}"; - - return; -} - -sub Parse { - my $io_hash = shift; - my $json = shift; - - my $name = $io_hash->{NAME}; - - my $decode_json = eval { decode_json($json) }; - if ($@) { - Log3 $name, 3, - "GardenaSmartDevice ($name) - JSON error while request: $@"; - } - - Log3 $name, 4, "GardenaSmartDevice ($name) - ParseFn was called"; - Log3 $name, 4, "GardenaSmartDevice ($name) - JSON: $json"; - - if ( defined( $decode_json->{id} ) ) { - - my $deviceId = $decode_json->{id}; - - if ( my $hash = $modules{GardenaSmartDevice}{defptr}{$deviceId} ) { - my $name = $hash->{NAME}; - - WriteReadings( $hash, $decode_json ); - Log3 $name, 4, - "GardenaSmartDevice ($name) - find logical device: $hash->{NAME}"; - - return $hash->{NAME}; - - } - else { - - Log3 $name, 3, - "GardenaSmartDevice ($name) - autocreate new device " - . makeDeviceName( $decode_json->{name} ) - . " with deviceId $decode_json->{id}, model $decode_json->{category}"; - return - "UNDEFINED " - . makeDeviceName( $decode_json->{name} ) - . " GardenaSmartDevice $decode_json->{id} $decode_json->{category}"; - } - } - - return; -} - -sub WriteReadings { - my $hash = shift; - my $decode_json = shift; - - my $name = $hash->{NAME}; - my $abilities = scalar( @{ $decode_json->{abilities} } ); - my $settings = scalar( @{ $decode_json->{settings} } ); - my $scheduled_events = scalar( @{ $decode_json->{scheduled_events} } ); - - readingsBeginUpdate($hash); - - do { - - if ( - ref( $decode_json->{abilities}[$abilities]{properties} ) eq "ARRAY" - && scalar( @{ $decode_json->{abilities}[$abilities]{properties} } ) - > 0 ) - { - for my $propertie ( - @{ $decode_json->{abilities}[$abilities]{properties} } ) - { - if ( - exists( $decode_json->{abilities}[$abilities]{name} ) - && ( $decode_json->{abilities}[$abilities]{name} eq - 'watering' ) - ) - { - - if ( $propertie->{name} eq 'button_config_time' ) { - if ( $hash->{helper}{ $propertie->{name} . '_id' } ne - $decode_json->{abilities}[$abilities]{id} ) - { - $hash->{helper}{ $propertie->{name} . '_id' } = - $decode_json->{abilities}[$abilities]{id}; - } - readingsBulkUpdateIfChanged( - $hash, - 'manualButtonTime', - ( - RigReadingsValue( - $hash, $propertie->{value} / 60 - ) - ) - ); - next; - } - } - - readingsBulkUpdateIfChanged( - $hash, - $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name}, - ($propertie->{value} eq '') ? RigReadingsValue( $hash, 'n/a') : RigReadingsValue( $hash, $propertie->{value} ) - ) - if ( exists( $propertie->{value} ) # defined ignored 'value':null - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} ne 'radio-quality' - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} ne 'battery-level' - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} ne 'internal_temperature-temperature' - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} ne 'ambient_temperature-temperature' - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} ne 'soil_temperature-temperature' - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} ne 'humidity-humidity' - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} ne 'light-light' - && ref( $propertie->{value} ) ne "HASH" ); - - readingsBulkUpdateIfChanged( - $hash, - $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name}, - RigReadingsValue( $hash, $propertie->{value} ) - ) - if ( - defined( $propertie->{value} ) - && ( $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq 'radio-quality' - || $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq 'battery-level' - || $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq - 'internal_temperature-temperature' - || $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq - 'ambient_temperature-temperature' - || $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq 'soil_temperature-temperature' - || $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq 'humidity-humidity' - || $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq 'light-light' ) - ); - - readingsBulkUpdateIfChanged( - $hash, - $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} - . '_timestamp', - Time::Piece->strptime( - RigReadingsValue( $hash, $propertie->{timestamp} ), - "%Y-%m-%d %H:%M:%S" )->strftime('%s') - - ) - if ( - defined( $propertie->{value} ) - && ( $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq 'mower_timer-mower_timer' ) - ); - - readingsBulkUpdateIfChanged( - $hash, - $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name}, - join( ',', @{ $propertie->{value} } ) - ) - if ( defined( $propertie->{value} ) - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq 'ic24-valves_connected' ); - - readingsBulkUpdateIfChanged( - $hash, - $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name}, - join( ',', @{ $propertie->{value} } ) - ) - if ( defined( $propertie->{value} ) - && $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} eq 'ic24-valves_master_config' ); - - if ( ref( $propertie->{value} ) eq "HASH" ) { - my $sub_state = 0; my $sub_value = 0; - while ( my ( $r, $v ) = each %{ $propertie->{value} } ) { - if ( ref( $v ) ne "HASH" ) { - readingsBulkUpdate( - $hash, - $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} . '_' - . $r, - RigReadingsValue( $hash, $v ) - ); - } else { - while ( my ( $i_r, $i_v ) = each %{ $v } ) { - readingsBulkUpdate( - $hash, - $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} . '_' - . $r . '_' . $i_r, - RigReadingsValue( $hash, $i_v ) - ); - } - } - } - } - # ic24 and other watering devices calc irrigation left in sec - readingsBulkUpdateIfChanged( - $hash, - $decode_json->{abilities}[$abilities]{name} . '-' - . $propertie->{name} - . '_irrigation_left', - ( $propertie->{value}{duration} > 0 ) ? (Time::Piece::localtime->strptime( - RigReadingsValue($hash, $propertie->{timestamp}), "%Y-%m-%d %H:%M:%S") - + ($propertie->{value}{duration} + 3 ) - Time::Piece::localtime->new) : 0 - ) - if ( defined( $propertie->{value} ) - && $decode_json->{abilities}[$abilities]{name} eq 'watering' - ); - } - } - - $abilities--; - } while ( $abilities >= 0 ); - - - if ( - exists( $decode_json->{scheduled_events} ) - # && scalar ($decode_json->{scheduled_events} ) > 0 - && ref ($decode_json->{scheduled_events}) eq 'ARRAY' ) { - readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_events_count', - scalar( @{$decode_json->{scheduled_events} } ) ); - my $valve_id =1; my $event_id = 1; # ic24 [1..6] | wc, pump [1] - - for my $event_schedules ( @{ $decode_json->{scheduled_events} } ) { - $valve_id = $event_schedules->{valve_id} if ( exists($event_schedules->{valve_id} ) ); #ic24 - $event_id++; # event id - - while ( my ( $r, $v ) = each %{ $event_schedules } ) { - readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_event_' - . $event_id - . '_valve_' - . $valve_id - . '_' - . $r, - $v) if (ref($v) ne 'HASH' ); - readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_event_' - . $event_id - . '_valve_' - . $valve_id - . '_' - . $v->{type}, - join(',', @ { $v->{weekdays}}) ) if (ref($v) eq 'HASH' ); - }; - }; - - }; # fi scheduled_events - - my $winter_mode; - - do { -#Log3 $name, 1, "Settings pro Device : ".$decode_json->{settings}[$settings]{name}; -#Log3 $name, 1, " - KEIN ARRAY" if ( ref( $decode_json->{settings}[$settings]{value} ) ne "ARRAY"); -#Log3 $name, 1, " - IST ARRAY" if ( ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY"); - - if ( - exists( $decode_json->{settings}[$settings]{name} ) - && ( $decode_json->{settings}[$settings]{name} =~ - /schedules_paused_until_?\d?$/ - || $decode_json->{settings}[$settings]{name} eq 'eco_mode' - || $decode_json->{settings}[$settings]{name} eq 'winter_mode' - || $decode_json->{settings}[$settings]{name} eq 'operating_mode' - || $decode_json->{settings}[$settings]{name} eq 'leakage_detection' - || $decode_json->{settings}[$settings]{name} eq 'turn_on_pressure' ) - ) - { - if ( $hash->{helper} - { $decode_json->{settings}[$settings]{name} . '_id' } ne - $decode_json->{settings}[$settings]{id} ) - { - $hash->{helper} - { $decode_json->{settings}[$settings]{name} . '_id' } = - $decode_json->{settings}[$settings]{id}; - } - # check watering controler single schedules pause until - if ( $decode_json->{settings}[$settings]{name} eq 'schedules_paused_until' ) { - readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_paused_until', - $decode_json->{settings}[$settings]{value} ); - } - ##### - #ic24 schedules pause until - if ($decode_json->{settings}[$settings]{name} =~ /schedules_paused_until_?(\d)?$/) { - #my $ventil = substr($decode_json->{settings}[$settings]{name}, -1); # => 1 - 6 - # check if empty, clear scheduling-scheduled_watering_next_start_x - readingsBulkUpdateIfChanged( $hash, 'scheduling-'.$decode_json->{settings}[$settings]{name}, - $decode_json->{settings}[$settings]{value} ); - # CommandAttr( undef, $name . " scheduling-scheduled_watering_next_start_") if ($decode_json->{settings}[$settings]{value} eq '' ) - } - - # save electronid pressure pump settings as readings - if ( $decode_json->{settings}[$settings]{name} eq 'operating_mode' - || $decode_json->{settings}[$settings]{name} eq 'leakage_detection' - || $decode_json->{settings}[$settings]{name} eq 'turn_on_pressure' ) { - readingsBulkUpdateIfChanged( $hash, $decode_json->{settings}[$settings]{name}, - $decode_json->{settings}[$settings]{value} ); - - } - # save winter mode as reading - if ( $decode_json->{settings}[$settings]{name} eq 'winter_mode' ) { - readingsBulkUpdateIfChanged( $hash, 'winter_mode', - $decode_json->{settings}[$settings]{value} ); - - $winter_mode = $decode_json->{settings}[$settings]{value}; - } - } - - if ( defined( $decode_json->{settings}[$settings]{name} ) - && $decode_json->{settings}[$settings]{name} eq 'valve_names' - && ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY" ) { # or HASH ? - my @valves = @{$decode_json->{settings}[$settings]{value}}; - foreach my $valve( @valves ) { - Log3 $name, 4, "GardenaSmartDevice ($name) valve_name $valve->{'name'}"; - readingsBulkUpdateIfChanged( $hash, 'valve-valve_name_'.$valve->{"id"}, - $valve->{"name"} ); - } - } - - if ( ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY" - && $decode_json->{settings}[$settings]{name} eq 'starting_points' ) - { - #save the startingpointid needed to update the startingpoints - if ( $hash->{helper}{STARTINGPOINTID} ne - $decode_json->{settings}[$settings]{id} ) - { - $hash->{helper}{STARTINGPOINTID} = - $decode_json->{settings}[$settings]{id}; - } - - $hash->{helper}{STARTINGPOINTS} = - '{ "name": "starting_points", "value": ' - . encode_json( $decode_json->{settings}[$settings]{value} ) . '}'; - my $startpoint_cnt = 0; - - for my $startingpoint ( - @{ $decode_json->{settings}[$settings]{value} } ) - { - $startpoint_cnt++; - readingsBulkUpdateIfChanged( - $hash, - 'startpoint-' . $startpoint_cnt . '-enabled', - $startingpoint->{enabled} - ); - } - } - - $settings--; - } while ( $settings >= 0 ); - - if ( $winter_mode ne 'hibernate' ) { - setState($hash); - } - else { - readingsBulkUpdate( $hash, 'state', - RigReadingsValue( $hash, 'hibernate' ) ); - } - - readingsEndUpdate( $hash, 1 ); - - Log3 $name, 4, "GardenaSmartDevice ($name) - readings was written"; - - return; -} - -sub setState { - my $hash = shift; - my $name = $hash->{NAME}; - - my $online_state = - ReadingsVal( $name, 'device_info-connection_status', 'unknown' ); - - #online state mower - readingsBulkUpdate( $hash, 'state', - $online_state eq 'online' - ? ReadingsVal( $name, 'mower-status', 'readingsValError' ) - : 'offline' ) - if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' ); - - # ic24 / wc / electronic pump - - if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' - || AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' - || AttrVal( $name, 'model', 'unknown' ) eq 'electronic_pressure_pump' ){ - my @opened_valves; - my $state_string = ''; my $nearst_irrigation = '2999-12-12 12:00'; - my $has_schedule = 0; my $longest_duration = 0; my $processed_item = ''; - my $error_type = 'ok'; - my @valves_connected = AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ? split(',', ReadingsVal( $name, 'ic24-valves_connected', '')) : '1'; - - $has_schedule = 1 if ( ReadingsVal($name, 'scheduling-schedules_events_count', '') ne '' ); - for (@valves_connected){ # valves 1 or 1..6 - ## add to opened ventils, if watering active - push @opened_valves, $_ if ( ( ( ReadingsVal( $name, "watering-watering_timer_".$_."_duration", 0 ) =~ m{\A[1-9]([0-9]+)?\z}xms ) ? $_ : 0 ) > 0 ); - ## set error type (pumpe required) - $error_type = ReadingsVal( $name, 'error-valve_error_'.$_.'_type', 'ok' ) if (ReadingsVal( $name, 'error-valve_error_'.$_.'_type', 'ok' ) ne 'ok'); - ## find longest irrigation duration - $longest_duration = ReadingsVal( $name, "watering-watering_timer_".$_."_irrigation_left", 0 ) if ( - ( ReadingsVal( $name, "watering-watering_timer_".$_."_duration", 0 ) =~ m{\A[1-9]([0-9]+)?\z}xms - && ReadingsVal( $name, "watering-watering_timer_".$_."_duration", 0 ) > 0 - && ReadingsVal( $name, "watering-watering_timer_".$_."_duration", 0 ) > $longest_duration ) ); - - # y-m-d h:m - $processed_item = AttrVal( $name, 'model', 'unknown' ) eq 'ic24' - ? RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')) - : RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until', '')); - - Log3 $name, 5, "[DEBUG] - process: $processed_item"; - Log3 $name, 5, "[DEBUG] - next_start: ". ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''); # n/a RigReadingsValue( $hash, 'n/a') - # $nearst_irrigation = RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')) - if ( ReadingsVal($name, 'scheduling-scheduled_watering_next_start', '') eq RigReadingsValue( $hash, 'n/a') ) { # non next start, schedules paused permanently or next schedule > 1 year; get nearst paused_until - Log3 $name, 5, "[DEBUG] - next_start: empty "; - Log3 $name, 5, "[DEBUG] - empty pro item ".Time::Piece->strptime( $processed_item, "%Y-%m-%d %H:%M:%S"); - Log3 $name, 5, "[DEBUG] - empty nearst ".Time::Piece->strptime( $nearst_irrigation, "%Y-%m-%d %H:%M:%S"); - $nearst_irrigation = $processed_item - if ( Time::Piece->strptime( $processed_item, "%Y-%m-%d %H:%M:%S") - < Time::Piece->strptime( $nearst_irrigation, "%Y-%m-%d %H:%M:%S") - && $has_schedule - && Time::Piece->strptime( $processed_item, "%Y-%m-%d %H:%M:%S") - > Time::Piece->new - ) - } else { - $nearst_irrigation = ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''); - } - Log3 $name, 5, "[DEBUG] - choosed nearst: $nearst_irrigation"; - - -###### -###### -###### $has_scheduling = 1 if ( ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_ , '') ne '2038-01-18T00:00:00.000Z' ); #&& ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_ , '') eq '' ); -###### #$has_scheduling = 1 if ( ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_ , 'n/a') eq '' ); -###### -###### # if ( ReadingsVal($name, 'scheduling-scheduled_watering_next_start_'.$_, '') ne '' -###### # --- wenn next_start_x ne 'na' && paused_until_x eq '' -> start_x -###### ## scheduling-schedules_paused_until_* = leer oder n/a -> keine zeitpläne -###### -###### if ( ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_ , '') ne '2038-01-18T00:00:00.000Z' ) { -###### # $nearst_irrigation = ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''); -###### Log3 $name, 3, "[DEBUG] - "; -###### -###### # $nearst_irrigation = RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')) -###### # if ( -###### # Time::Piece->strptime( RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')), "%Y-%m-%d %H:%M") < Time::Piece->strptime( $nearst_irrigation, "%Y-%m-%d %H:%M") -###### # && $has_scheduling && Time::Piece->strptime( RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')), "%Y-%m-%d %H:%M") > Time::Piece->new -###### # ) -###### if ( ReadingsVal($name, 'scheduling-scheduled_watering_next_start', '') eq '' ) { # non next start, schedules paused permanently or next schedule > 1 year; get nearst paused_until -###### $nearst_irrigation = RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')) -###### if ( Time::Piece->strptime( RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')), "%Y-%m-%d %H:%M") -###### < Time::Piece->strptime( $nearst_irrigation, "%Y-%m-%d %H:%M") -###### && $has_scheduling -###### && Time::Piece->strptime( RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')), "%Y-%m-%d %H:%M") -###### > Time::Piece->new -###### ) -###### } else { -###### $nearst_irrigation = ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''); -###### } -###### } # fi -###### -###### #$nearst_irrigation = ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''); -###### # if ( ReadingsVal($name, 'scheduling-scheduled_watering_next_start_'.$_, 'n/a') ne 'n/a' ); -###### - } # for - # override state 4 extendedstates - if ( AttrVal( $name, "extendedState", 0 ) == 1) { - if (scalar(@opened_valves) > 0){ - ## valve 1 will be ir.. 23 minutes remaining - for (@valves_connected){ - $state_string .= sprintf(RigReadingsValue($hash,'valve').' '.$_.' '.(RigReadingsValue($hash, 'will be irrigated %.f minutes remaining.') .'
'), (ReadingsVal( $name, 'watering-watering_timer_'.$_.'_duration', 0 )/60)); - } # /for - } else { - $state_string .= RigReadingsValue($hash, 'closed'); - } - $state_string .= ($has_schedule) ? sprintf( RigReadingsValue($hash, 'next watering: %s'), RigReadingsValue($hash, ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''))) : sprintf( RigReadingsValue($hash, 'paused until %s') , $nearst_irrigation); - #TODO: Write state format for ventil 1-@valces_connected -> map ? - CommandAttr( undef, $name . ' stateFormat - { - - } - ' ) - if ( AttrVal( $name, 'stateFormat', 'none' ) eq 'none' ); - } else { - Log3 $name, 5, "[DEBUG] - Offene Ventile :".scalar(@opened_valves)." laengste bewaesserung: $longest_duration . hat Zeitplan: $has_schedule Naechster Zeitplan: $nearst_irrigation"; - $state_string = scalar(@opened_valves) > 0 - # offen - ? sprintf( (RigReadingsValue($hash, 'will be irrigated %.f minutes remaining.')), $longest_duration/60) - # zu - : - ( $has_schedule - && $nearst_irrigation ne '2999-12-12 12:00') - # zeitplan aktiv - # ? ( $nearst_irrigation eq '2038-01-18 00:00') sprintf( RigReadingsValue($hash, 'paused until %s') , $nearst_irrigation) - ? ( $nearst_irrigation eq RigReadingsValue( $hash, 'n/a') || $nearst_irrigation =~ '2038-01-18.*') - # dauerhaft pausiert - ? sprintf( (RigReadingsValue($hash, 'closed') .'. '.RigReadingsValue($hash , 'schedule permanently paused')) ) - # naechster zeutplan - : (ReadingsVal($name, 'scheduling-scheduled_watering_next_start', '') eq RigReadingsValue($hash, 'n/a')) - ? sprintf( RigReadingsValue($hash, 'paused until %s') , $nearst_irrigation) - : sprintf( (RigReadingsValue($hash, 'closed') .'. '.RigReadingsValue($hash, 'next watering: %s')), $nearst_irrigation ) - # zeitplan pausiert - : RigReadingsValue($hash, 'closed') - ; - # state offline | override - $state_string = 'offline' if ($online_state eq 'offline'); - $state_string = ( $error_type ne 'ok' ) ? $error_type : $state_string; - - } - readingsBulkUpdate( - $hash, 'state', RigReadingsValue( $hash, $state_string ) ); - } - - # Sensor / Sensor 2 - if ( AttrVal( $name, 'model', 'unknown' ) =~ /sensor.?/ ) { - my $state_string = - ( ReadingsVal( $name, 'device_info-category', 'unknown' ) eq - 'sensor' ) - ? 'T: ' - . ReadingsVal( $name, 'ambient_temperature-temperature', - 'readingsValError' ) - . '°C, ' - : 'T: ' - . ReadingsVal( $name, 'soil_temperature-temperature', - 'readingsValError' ) - . '°C, '; - $state_string .= 'H: ' - . ReadingsVal( $name, 'humidity-humidity', 'readingsValError' ) . '%'; - $state_string .= ', L: ' - . ReadingsVal( $name, 'light-light', 'readingsValError' ) . 'lux' - if ( ReadingsVal( $name, 'device_info-category', 'unknown' ) eq - 'sensor' ); - -# if ( $online_state eq 'offline') { -# readingsBulkUpdate( $hash, 'humidity-humidity', '-1' ); -# readingsBulkUpdate( $hash, 'ambient_temperature-temperature', '-1' ) if (ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor'); -# readingsBulkUpdate( $hash, 'light-light', '-1' ) if (ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor'); -# } - #online state sensor I II - readingsBulkUpdate( $hash, 'state', - $online_state eq 'online' ? RigReadingsValue( $hash, $state_string) : RigReadingsValue( $hash, 'offline') ); - } - - readingsBulkUpdate( $hash, 'state', - ReadingsVal( $name, 'power-power_timer', 'no info from power-timer' ) ) - if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' ); - - return; -} - -################################## -################################## -#### my little helpers ########### - -sub ReadingLangGerman { - my $hash = shift; - my $readingValue = shift; - - my $name = $hash->{NAME}; - my %langGermanMapp = ( - 'ok_cutting' => 'mähen', - 'paused' => 'pausiert', - 'ok_searching' => 'suche Ladestation', - 'ok_charging' => 'lädt', - 'ok_leaving' => 'unterwegs zum Startpunkt', - 'wait_updating' => 'wird aktualisiert ...', - 'wait_power_up' => 'wird eingeschaltet ...', - 'parked_timer' => 'geparkt nach Zeitplan', - 'parked_park_selected' => 'geparkt', - 'off_disabled' => 'der Mäher ist ausgeschaltet', - 'off_hatch_open' => 'deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich', - 'unknown' => 'unbekannter Status', - 'error' => 'Fehler', - 'error_at_power_up' => 'Neustart ...', - 'off_hatch_closed' => 'Deaktiviert. Manueller Start erforderlich', - 'ok_cutting_timer_overridden' => 'manuelles mähen', - 'parked_autotimer' => 'geparkt durch SensorControl', - 'parked_daily_limit_reached' => 'abgeschlossen', - 'no_message' => 'kein Fehler', - 'outside_working_area' => 'außerhalb des Arbeitsbereichs', - 'no_loop_signal' => 'kein Schleifensignal', - 'wrong_loop_signal' => 'falsches Schleifensignal', - 'loop_sensor_problem_front' => 'Problem Schleifensensor, vorne', - 'loop_sensor_problem_rear' => 'Problem Schleifensensor, hinten', - 'trapped' => 'eingeschlossen', - 'upside_down' => 'steht auf dem Kopf', - 'low_battery' => 'niedriger Batteriestand', - 'empty_battery' => 'Batterie leer', - 'no_drive' => 'fährt nicht', - 'lifted' => 'angehoben', - 'stuck_in_charging_station' => 'eingeklemmt in Ladestation', - 'charging_station_blocked' => 'Ladestation blockiert', - 'collision_sensor_problem_rear' => 'Problem Stoßsensor hinten', - 'collision_sensor_problem_front' => 'Problem Stoßsensor vorne', - 'wheel_motor_blocked_right' => 'Radmotor rechts blockiert', - 'wheel_motor_blocked_left' => 'Radmotor links blockiert', - 'wheel_drive_problem_right' => 'Problem Antrieb, rechts', - 'wheel_drive_problem_left' => 'Problem Antrieb, links', - 'cutting_system_blocked' => 'Schneidsystem blockiert', - 'invalid_sub_device_combination' => 'fehlerhafte Verbindung', - 'settings_restored' => 'Standardeinstellungen', - 'electronic_problem' => 'elektronisches Problem', - 'charging_system_problem' => 'Problem Ladesystem', - 'tilt_sensor_problem' => 'Kippsensor Problem', - 'wheel_motor_overloaded_right' => 'rechter Radmotor überlastet', - 'wheel_motor_overloaded_left' => 'linker Radmotor überlastet', - 'charging_current_too_high' => 'Ladestrom zu hoch', - 'temporary_problem' => 'vorübergehendes Problem', - 'guide_1_not_found' => 'SK 1 nicht gefunden', - 'guide_2_not_found' => 'SK 2 nicht gefunden', - 'guide_3_not_found' => 'SK 3 nicht gefunden', - 'difficult_finding_home' => 'Problem die Ladestation zu finden', - 'guide_calibration_accomplished' => 'Kalibrierung des Suchkabels beendet', - 'guide_calibration_failed' => 'Kalibrierung des Suchkabels fehlgeschlagen', - 'temporary_battery_problem' => 'kurzzeitiges Batterieproblem', - 'battery_problem' => 'Batterieproblem', - 'alarm_mower_switched_off' => 'Alarm! Mäher ausgeschalten', - 'alarm_mower_stopped' => 'Alarm! Mäher gestoppt', - 'alarm_mower_lifted' => 'Alarm! Mäher angehoben', - 'alarm_mower_tilted' => 'Alarm! Mäher gekippt', - 'connection_changed' => 'Verbindung geändert', - 'connection_not_changed' => 'Verbindung nicht geändert', - 'com_board_not_available' => 'COM Board nicht verfügbar', - 'slipped' => 'rutscht', - 'out_of_operation' => 'ausser Betrieb', - 'replace_now' => 'kritischer Batteriestand, wechseln Sie jetzt', - 'low' => 'niedrig', - 'ok' => 'ok', - 'no_source' => 'ok', - 'mower_charging' => 'Mäher wurde geladen', - 'completed_cutting_autotimer' => 'Sensor Control erreicht', - 'week_timer' => 'Wochentimer erreicht', - 'countdown_timer' => 'Stoppuhr Timer', - 'undefined' => 'unklar', - 'unknown' => 'unklar', - 'status_device_unreachable' => 'Gerät ist nicht in Reichweite', - 'status_device_alive' => 'Gerät ist in Reichweite', - 'bad' => 'schlecht', - 'poor' => 'schwach', - 'good' => 'gut', - 'undefined' => 'unklar', - 'idle' => 'nichts zu tun', - 'firmware_cancel' => 'Firmwareupload unterbrochen', - 'firmware_upload' => 'Firmwareupload', - 'unsupported' => 'nicht unterstützt', - 'up_to_date' => 'auf dem neusten Stand', - 'mower' => 'Mäher', - 'watering_computer' => 'Bewässerungscomputer', - 'no_frost' => 'kein Frost', - 'open' => 'offen', - 'closed' => 'geschlossen', - 'included' => 'inbegriffen', - 'active' => 'aktiv', - 'inactive' => 'nicht aktiv', - 'hibernate' => 'Winterschlaf', - 'awake' => 'Aufgewacht', - 'schedule permanently paused' => 'Zeitplan dauerhaft pausiert', - 'paused until %s' => 'pausiert bis %s', - 'will be irrigated %.f minutes remaining.'=> 'Wird bewässert. %.f Minuten verbleibend.', - 'next watering: %s' => 'Nächste Bewässerung: %s', - 'n/a' => 'nicht verfügbar', - 'pump_not_filled' => 'Pumpe nicht gefüllt', - ); - - if ( - defined( $langGermanMapp{$readingValue} ) - && ( AttrVal( 'global', 'language', 'none' ) eq 'DE' - || AttrVal( $name, 'readingValueLanguage', 'none' ) eq 'de' ) - && AttrVal( $name, 'readingValueLanguage', 'none' ) ne 'en' - ) - { - return $langGermanMapp{$readingValue}; - } - else { - return $readingValue; - } - - return; -} - -sub RigReadingsValue { - my $hash = shift; - my $readingValue = shift; - - my $rigReadingValue; - - if ( $readingValue =~ /^(\d+)-(\d\d)-(\d\d)T(\d\d)/ ) { - $rigReadingValue = Zulu2LocalString($readingValue); - } - else { - $rigReadingValue = ReadingLangGerman( $hash, $readingValue ); - } - - return $rigReadingValue; -} - -sub Zulu2LocalString { - my $t = shift; - - my ( $datehour, $datemin, $rest ) = split( /:/, $t, 3 ); - - my ( $year, $month, $day, $hour, $min ) = - $datehour =~ /(\d+)-(\d\d)-(\d\d)T(\d\d)/; - my $epoch = timegm( 0, 0, $hour, $day, $month - 1, $year ); - - my ( $lyear, $lmonth, $lday, $lhour, $isdst ) = - ( localtime($epoch) )[ 5, 4, 3, 2, -1 ]; - - $lyear += 1900; # year is 1900 based - $lmonth++; # month number is zero based - - if ( defined($rest) ) { - return ( - sprintf( - "%04d-%02d-%02d %02d:%02d:%s", - $lyear, $lmonth, $lday, - $lhour, $datemin, substr( $rest, 0, 2 ) - ) - ); - } - elsif ( $lyear < 2000 ) { - return 'temporarily unavailable'; - } - else { - return ( - sprintf( - "%04d-%02d-%02d %02d:%02d", - $lyear, $lmonth, $lday, $lhour, substr( $datemin, 0, 2 ) - ) - ); - } - - return; -} - -sub SetPredefinedStartPoints { - my $hash = shift; - my $aArg = shift; - - my ( $startpoint_state, $startpoint_num, @morestartpoints ) = @{$aArg}; - - my $name = $hash->{NAME}; - my $payload; - my $abilities; - - if ( defined($startpoint_state) && defined($startpoint_num) ) { - if ( defined( $hash->{helper}{STARTINGPOINTS} ) - && $hash->{helper}{STARTINGPOINTS} ne '' ) - { -# add needed parameters to saved settings config and change the value in request - my $decode_json_settings = - eval { decode_json( $hash->{helper}{STARTINGPOINTS} ) }; - if ($@) { - Log3 $name, 3, -"GardenaSmartBridge ($name) - JSON error while setting startpoint: $@"; - } - - $decode_json_settings->{device} = $hash->{DEVICEID}; - my $setval = $startpoint_state eq 'disable' ? \0 : \1; - $decode_json_settings->{value}[ $startpoint_num - 1 ]{enabled} = - $setval; - - #set more startpoints - if ( - defined scalar(@morestartpoints) - && ( scalar(@morestartpoints) == 2 - || scalar(@morestartpoints) == 4 ) - ) - { - if ( scalar(@morestartpoints) == 2 ) { - $setval = $morestartpoints[0] eq 'disable' ? \0 : \1; - $decode_json_settings->{value}[ $morestartpoints[1] - 1 ] - {enabled} = $setval; - - } - elsif ( scalar(@morestartpoints) == 4 ) { - $setval = $morestartpoints[0] eq 'disable' ? \0 : \1; - $decode_json_settings->{value}[ $morestartpoints[1] - 1 ] - {enabled} = $setval; - $setval = $morestartpoints[2] eq 'disable' ? \0 : \1; - $decode_json_settings->{value}[ $morestartpoints[3] - 1 ] - {enabled} = $setval; - } - } - - $payload = '"settings": ' . encode_json($decode_json_settings); - $abilities = 'mower_settings'; - - #$abilities['service_id'] = $hash->{helper}{STARTINGPOINTID}; - } - else { - return - "startingpoints not loaded yet, please wait a couple of minutes", - undef, undef; - } - } - else { - return - "startpoint usage: set " - . $hash->{NAME} - . " startpoint disable 1 [enable 2] [disable 3]", undef, undef; - } - - return undef, $payload, $abilities; -} - -1; - -=pod - -=item device -=item summary Modul to control GardenaSmart Devices -=item summary_DE Modul zur Steuerung von GardenaSmartgeräten - -=begin html - - -

GardenaSmartDevice

-
    - In combination with GardenaSmartBridge this FHEM Module controls the GardenaSmart Device using the GardenaCloud -

    - Once the Bridge device is created, the connected devices are automatically recognized and created in FHEM.
    - From now on the devices can be controlled and changes in the GardenaAPP are synchronized with the state and readings of the devices. - -


    - Readings -
      -
    • battery-charging - Indicator if the Battery is charged (0/1) or with newer Firmware (false/true)
    • -
    • battery-level - load percentage of the Battery
    • -
    • battery-rechargeable_battery_status - healthyness of the battery (out_of_operation/replace_now/low/ok)
    • -
    • device_info-category - category of device (mower/watering_computer)
    • -
    • device_info-last_time_online - timestamp of last radio contact
    • -
    • device_info-manufacturer - manufacturer
    • -
    • device_info-product - product type
    • -
    • device_info-serial_number - serial number
    • -
    • device_info-sgtin -
    • -
    • device_info-version - firmware version
    • -
    • firmware-firmware_command - firmware command (idle/firmware_cancel/firmware_upload/unsupported)
    • -
    • firmware-firmware_status - firmware status
    • -
    • firmware-firmware_update_start - indicator when a firmwareupload is started
    • -
    • firmware-firmware_upload_progress - progress indicator of firmware update
    • -
    • firmware-inclusion_status - inclusion status
    • -
    • internal_temperature-temperature - internal device temperature
    • -
    • mower-error - actual error message -
        -
      • no_message
      • -
      • outside_working_area
      • -
      • no_loop_signal
      • -
      • wrong_loop_signal
      • -
      • loop_sensor_problem_front
      • -
      • loop_sensor_problem_rear
      • -
      • trapped
      • -
      • upside_down
      • -
      • low_battery
      • -
      • empty_battery
      • -
      • no_drive
      • -
      • lifted
      • -
      • stuck_in_charging_station
      • -
      • charging_station_blocked
      • -
      • collision_sensor_problem_rear
      • -
      • collision_sensor_problem_front
      • -
      • wheel_motor_blocked_right
      • -
      • wheel_motor_blocked_left
      • -
      • wheel_drive_problem_right
      • -
      • wheel_drive_problem_left
      • -
      • cutting_system_blocked
      • -
      • invalid_sub_device_combination
      • -
      • settings_restored
      • -
      • electronic_problem
      • -
      • charging_system_problem
      • -
      • tilt_sensor_problem
      • -
      • wheel_motor_overloaded_right
      • -
      • wheel_motor_overloaded_left
      • -
      • charging_current_too_high
      • -
      • temporary_problem
      • -
      • guide_1_not_found
      • -
      • guide_2_not_found
      • -
      • guide_3_not_found
      • -
      • difficult_finding_home
      • -
      • guide_calibration_accomplished
      • -
      • guide_calibration_failed
      • -
      • temporary_battery_problem
      • -
      • battery_problem
      • -
      • alarm_mower_switched_off
      • -
      • alarm_mower_stopped
      • -
      • alarm_mower_lifted
      • -
      • alarm_mower_tilted
      • -
      • connection_changed
      • -
      • connection_not_changed
      • -
      • com_board_not_available
      • -
      • slipped
      • -
      -
    • -
    • mower-manual_operation - (0/1) or with newer Firmware (false/true)
    • -
    • mower-override_end_time - manual override end time
    • -
    • mower-source_for_next_start - source for the next start -
        -
      • no_source
      • -
      • mower_charging
      • -
      • completed_cutting_autotimer
      • -
      • week_timer
      • -
      • countdown_timer
      • -
      • undefined
      • -
      -
    • -
    • mower-status - mower state (see state)
    • -
    • mower-timestamp_next_start - timestamp of next scheduled start
    • -
    • radio-connection_status - state of connection
    • -
    • radio-quality - percentage of the radio quality
    • -
    • radio-state - radio state (bad/poor/good/undefined)
    • -
    • state - state of the mower -
        -
      • paused
      • -
      • ok_cutting
      • -
      • ok_searching
      • -
      • ok_charging
      • -
      • ok_leaving
      • -
      • wait_updating
      • -
      • wait_power_up
      • -
      • parked_timer
      • -
      • parked_park_selected
      • -
      • off_disabled
      • -
      • off_hatch_open
      • -
      • unknown
      • -
      • error
      • -
      • error_at_power_up
      • -
      • off_hatch_closed
      • -
      • ok_cutting_timer_overridden
      • -
      • parked_autotimer
      • -
      • parked_daily_limit_reached
      • -
      -
    • -
    -

    - - Attributes -
      -
    • readingValueLanguage - Change the Language of Readings (de,en/if not set the default is english and the global language is not set at german)
    • -
    • model -
    • -
    -

    - - set -
      -
    • winter_mode - awake | hibernate
    • -
    -
      -

      mower

      -
    • parkUntilFurtherNotice
    • -
    • parkUntilNextTimer
    • -
    • startOverrideTimer - (in minutes, 60 = 1h, 1440 = 24h, 4320 = 72h)
    • -
    • startResumeSchedule
    • -
    • startpoint enable|disable 1|2|3 - enables or disables one or more predefined start points
    • -
        -
      • set NAME startpoint enable 1
      • -
      • set NAME startpoint disable 3 enable 1
      • -
      -

      irrigation control

      -
    • resumeScheduleValve - start schedule irrigation on valve n
    • -
    • stopScheduleValve - stop schedule irrigation on valve n (Default: 2038-01-18T00:00:00.000Z) | optional params hours (now + hours)
    • -
    • closeAllValves - close all valves
    • -

      water control

      -
    • manualButtonTime - set manual time for button press (in minutes) 0 disable button
    • -
    • stopSchedule - stop schedule for now + n hours (Default: 2038-01-18T00:00:00.000Z)
    • -
    • resumeSchedule - resume schedule
    • -
    -
- -=end html -=begin html_DE - - -

GardenaSmartDevice

-
    - Zusammen mit dem Device GardenaSmartDevice stellt dieses FHEM Modul die Kommunikation zwischen der GardenaCloud und Fhem her. -

    - Wenn das GardenaSmartBridge Device erzeugt wurde, werden verbundene Geräte automatisch erkannt und in Fhem angelegt.
    - Von nun an können die eingebundenen Geräte gesteuert werden. Änderungen in der APP werden mit den Readings und dem Status syncronisiert. - -
-
-
    - Readings -
      -
    • battery-charging - Ladeindikator (0/1) oder mit neuerer Firmware (false/true)
    • -
    • battery-level - Ladezustand der Batterie in Prozent
    • -
    • battery-rechargeable_battery_status - Zustand der Batterie (Ausser Betrieb/Kritischer Batteriestand, wechseln Sie jetzt/Niedrig/oK)
    • -
    • device_info-category - Eigenschaft des Gerätes (Mäher/Bewässerungscomputer/Bodensensor)
    • -
    • device_info-last_time_online - Zeitpunkt der letzten Funkübertragung
    • -
    • device_info-manufacturer - Hersteller
    • -
    • device_info-product - Produkttyp
    • -
    • device_info-serial_number - Seriennummer
    • -
    • device_info-sgtin -
    • -
    • device_info-version - Firmware Version
    • -
    • firmware-firmware_command - Firmware Kommando (Nichts zu tun/Firmwareupload unterbrochen/Firmwareupload/nicht unterstützt)
    • -
    • firmware-firmware_status - Firmware Status
    • -
    • firmware-firmware_update_start - Firmwareupdate (0/1) oder mit neuerer Firmware (false/true)
    • -
    • firmware-firmware_upload_progress - Firmwareupdatestatus in Prozent
    • -
    • firmware-inclusion_status - Einbindungsstatus
    • -
    • internal_temperature-temperature - Interne Geräte Temperatur
    • -
    • mower-error - Aktuelle Fehler Meldung -
        -
      • Kein Fehler
      • -
      • Außerhalb des Arbeitsbereichs
      • -
      • Kein Schleifensignal
      • -
      • Falsches Schleifensignal
      • -
      • Problem Schleifensensor, vorne
      • -
      • Problem Schleifensensor, hinten
      • -
      • Eingeschlossen
      • -
      • Steht auf dem Kopf
      • -
      • Niedriger Batteriestand
      • -
      • Batterie ist leer
      • -
      • Kein Antrieb
      • -
      • Angehoben
      • -
      • Eingeklemmt in Ladestation
      • -
      • Ladestation blockiert
      • -
      • Problem Stoßsensor hinten
      • -
      • Problem Stoßsensor vorne
      • -
      • Radmotor rechts blockiert
      • -
      • Radmotor links blockiert
      • -
      • Problem Antrieb, rechts
      • -
      • Problem Antrieb, links
      • -
      • Schneidsystem blockiert
      • -
      • Fehlerhafte Verbindung
      • -
      • Standardeinstellungen
      • -
      • Elektronisches Problem
      • -
      • Problem Ladesystem
      • -
      • Kippsensorproblem
      • -
      • Rechter Radmotor überlastet
      • -
      • Linker Radmotor überlastet
      • -
      • Ladestrom zu hoch
      • -
      • Vorübergehendes Problem
      • -
      • SK 1 nicht gefunden
      • -
      • SK 2 nicht gefunden
      • -
      • SK 3 nicht gefunden
      • -
      • Problem die Ladestation zu finden
      • -
      • Kalibration des Suchkabels beendet
      • -
      • Kalibration des Suchkabels fehlgeschlagen
      • -
      • Kurzzeitiges Batterieproblem
      • -
      • Batterieproblem
      • -
      • Alarm! Mäher ausgeschalten
      • -
      • Alarm! Mäher gestoppt
      • -
      • Alarm! Mäher angehoben
      • -
      • Alarm! Mäher gekippt
      • -
      • Verbindung geändert
      • -
      • Verbindung nicht geändert
      • -
      • COM board nicht verfügbar
      • -
      • Rutscht
      • -
      -
    • -
    • mower-manual_operation - Manueller Betrieb (0/1) oder mit neuerer Firmware (false/true)
    • -
    • mower-override_end_time - Zeitpunkt wann der manuelle Betrieb beendet ist
    • -
    • mower-source_for_next_start - Grund für den nächsten Start -
        -
      • Kein Grund
      • -
      • Mäher wurde geladen
      • -
      • SensorControl erreicht
      • -
      • Wochentimer erreicht
      • -
      • Stoppuhr Timer
      • -
      • Undefiniert
      • -
      -
    • -
    • mower-status - Mäher Status (siehe state)
    • -
    • mower-timestamp_next_start - Zeitpunkt des nächsten geplanten Starts
    • -
    • radio-connection_status - Status der Funkverbindung
    • -
    • radio-quality - Indikator für die Funkverbindung in Prozent
    • -
    • radio-state - radio state (schlecht/schwach/gut/Undefiniert)
    • -
    • state - Staus des Mähers -
        -
      • Pausiert
      • -
      • Mähen
      • -
      • Suche Ladestation
      • -
      • Lädt
      • -
      • Mähen
      • -
      • Wird aktualisiert ...
      • -
      • Wird eingeschaltet ...
      • -
      • Geparkt nach Zeitplan
      • -
      • Geparkt
      • -
      • Der Mäher ist ausgeschaltet
      • -
      • Deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich
      • -
      • Unbekannter Status
      • -
      • Fehler
      • -
      • Neustart ...
      • -
      • Deaktiviert. Manueller Start erforderlich
      • -
      • Manuelles Mähen
      • -
      • Geparkt durch SensorControl
      • -
      • Abgeschlossen
      • -
      -
    • -
    -

    - - Attribute -
      -
    • readingValueLanguage - Änderung der Sprache der Readings (de,en/wenn nichts gesetzt ist, dann Englisch es sei denn deutsch ist als globale Sprache gesetzt)
    • -
    • model -
    • -
    - - set -
      -
    • winter_mode - aufwäcken (awake)| winterschlaf (hibernate)
    • -
    -
      -

      mäher

      -
    • parkUntilFurtherNotice - Parken des Mähers unter Umgehung des Zeitplans
    • -
    • parkUntilNextTimer - Parken bis zum nächsten Zeitplan
    • -
    • startOverrideTimer - Manuelles mähen (in Minuten, 60 = 1h, 1440 = 24h, 4320 = 72h)
    • -
    • startResumeSchedule - Weiterführung des Zeitplans
    • -
    • startpoint enable|disable 1|2|3 - Aktiviert oder deaktiviert einen vordefinierten Startbereich
    • -
        -
      • set NAME startpoint enable 1
      • -
      • set NAME startpoint disable 3 enable 1
      • -
      -

      irrigation control

      -
    • resumeScheduleValve - Startet Bew&aauml;sserung am Ventil n nach Zeitplan
    • -
    • stopScheduleValve - Setzt Bew&aauml;sserung am Ventil n aus (Default: 2038-01-18T00:00:00.000Z) | Optionaler Parameter Stunden (Jetzt + Stunden)
    • -
    • closeAllValves - Stopt Bew&aauml;sserung an allen Ventilen
    • -

      water control

      -
    • manualButtonTime - setzt die Dauer für den manuellen Knopf (in Minuten) 0 Schaltet den Knopf aus
    • -
    • stopSchedule - Halte Zeitplan an für x Stunden - (Default: 2038-01-18T00:00:00.000Z)
    • -
    • resumeSchedule - Weiterführung des Zeitplans
    • -
    -
- -=end html_DE - -=for :application/json;q=META.json 74_GardenaSmartDevice.pm -{ - "abstract": "Modul to control GardenaSmart Devices", - "x_lang": { - "de": { - "abstract": "Modul zur Steuerung von Gardena Smart Geräten" - } - }, - "keywords": [ - "fhem-mod-device", - "fhem-core", - "Garden", - "Gardena", - "Smart" - ], - "release_status": "stable", - "license": "GPL_2", - "version": "v2.5.5", - "author": [ - "Marko Oldenburg " - ], - "x_fhem_maintainer": [ - "CoolTux" - ], - "x_fhem_maintainer_github": [ - "LeonGaultier" - ], - "prereqs": { - "runtime": { - "requires": { - "FHEM": 5.00918799, - "perl": 5.016, - "Meta": 0, - "JSON": 0, - "Time::Local": 0 - }, - "recommends": { - }, - "suggests": { - } - } - } -} -=end :application/json;q=META.json - -=cut +############################################################################### +# +# Developed with VSCodium and richterger perl plugin. +# +# (c) 2017-2022 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net) +# All rights reserved +# +# Special thanks goes to comitters: +# - Michael (mbrak) Thanks for Commandref +# - Matthias (Kenneth) Thanks for Wiki entry +# - BioS Thanks for predefined start points Code +# - fettgu Thanks for Debugging Irrigation Control data flow +# - Sebastian (BOFH) Thanks for new Auth Code after API Change +# +# +# This script 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 +# any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# +# This script 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. +# +# +# $Id$ +# +############################################################################### +## +## +## Das JSON Modul immer in einem eval aufrufen +# $data = eval{decode_json($data)}; +# +# if($@){ +# Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@"); +# +# readingsSingleUpdate($hash, "state", "error", 1); +# +# return; +# } +# +# +###### Wichtige Notizen +# +# apt-get install libio-socket-ssl-perl +# http://www.dxsdata.com/de/2016/07/php-class-for-gardena-smart-system-api/ +# +## +## + +## unserer packagename +package FHEM::GardenaSmartDevice; +use GPUtils qw(GP_Import GP_Export); + +use strict; +use warnings; +use POSIX; +use FHEM::Meta; +use Time::Local; +use Time::Piece; +use Time::Seconds; + +# try to use JSON::MaybeXS wrapper +# for chance of better performance + open code +eval { + require JSON::MaybeXS; + import JSON::MaybeXS qw( decode_json encode_json ); + 1; +} or do { + + # try to use JSON wrapper + # for chance of better performance + eval { + # JSON preference order + local $ENV{PERL_JSON_BACKEND} = + 'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP' + unless ( defined( $ENV{PERL_JSON_BACKEND} ) ); + + require JSON; + import JSON qw( decode_json encode_json ); + 1; + } or do { + + # In rare cases, Cpanel::JSON::XS may + # be installed but JSON|JSON::MaybeXS not ... + eval { + require Cpanel::JSON::XS; + import Cpanel::JSON::XS qw(decode_json encode_json); + 1; + } or do { + + # In rare cases, JSON::XS may + # be installed but JSON not ... + eval { + require JSON::XS; + import JSON::XS qw(decode_json encode_json); + 1; + } or do { + + # Fallback to built-in JSON which SHOULD + # be available since 5.014 ... + eval { + require JSON::PP; + import JSON::PP qw(decode_json encode_json); + 1; + } or do { + + # Fallback to JSON::backportPP in really rare cases + require JSON::backportPP; + import JSON::backportPP qw(decode_json encode_json); + 1; + }; + }; + }; + }; +}; + +## Import der FHEM Funktionen +#-- Run before package compilation +BEGIN { + + # Import from main context + GP_Import( + qw(readingsSingleUpdate + readingsBulkUpdate + readingsBulkUpdateIfChanged + readingsBeginUpdate + readingsEndUpdate + Log3 + CommandAttr + AttrVal + ReadingsVal + readingFnAttributes + AssignIoPort + modules + IOWrite + defs + makeDeviceName) + ); +} + +#-- Export to main context with different name +GP_Export( + qw( + Initialize + ) +); + +sub Initialize { + my $hash = shift; + + $hash->{Match} = '^{"id":".*'; + + $hash->{SetFn} = \&Set; + $hash->{DefFn} = \&Define; + $hash->{UndefFn} = \&Undef; + $hash->{ParseFn} = \&Parse; + + $hash->{AttrFn} = \&Attr; + $hash->{AttrList} = + "readingValueLanguage:de,en " + . "model:watering_computer,sensor,sensor2,mower,ic24,power,electronic_pressure_pump " + . "extendedState:0,1 " + . "IODev " + . $readingFnAttributes; + $hash->{parseParams} = 1; + + return FHEM::Meta::InitMod( __FILE__, $hash ); +} + +sub Define { + my $hash = shift // return; + my $aArg = shift // return; + + return $@ unless ( FHEM::Meta::SetInternals($hash) ); + use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); + + return + "too few parameters: define GardenaSmartDevice " + if ( scalar( @{$aArg} ) < 3 ); + + my $name = $aArg->[0]; + my $deviceId = $aArg->[2]; + my $category = $aArg->[3]; + + $hash->{DEVICEID} = $deviceId; + $hash->{VERSION} = version->parse($VERSION)->normal; + $hash->{helper}{STARTINGPOINTID} = ''; + $hash->{helper}{schedules_paused_until_id} = ''; + $hash->{helper}{eco_mode_id} = ''; + $hash->{helper}{button_config_time_id} = ''; + $hash->{helper}{winter_mode_id} = ''; + # Electroni Pressure Pump + $hash->{helper}{operating_mode_id} = ''; + $hash->{helper}{leakage_detection_id} = ''; + $hash->{helper}{turn_on_pressure_id} = ''; + + + $hash->{helper}{_id} = ''; + + # IrrigationControl valve control max 6 + $hash->{helper}{schedules_paused_until_1_id} = ''; + $hash->{helper}{schedules_paused_until_2_id} = ''; + $hash->{helper}{schedules_paused_until_3_id} = ''; + $hash->{helper}{schedules_paused_until_4_id} = ''; + $hash->{helper}{schedules_paused_until_5_id} = ''; + $hash->{helper}{schedules_paused_until_6_id} = ''; + + CommandAttr( undef, + "$name IODev $modules{GardenaSmartBridge}{defptr}{BRIDGE}->{NAME}" ) + if ( AttrVal( $name, 'IODev', 'none' ) eq 'none' ); + + my $iodev = AttrVal( $name, 'IODev', 'none' ); + + AssignIoPort( $hash, $iodev ) if ( !$hash->{IODev} ); + + if ( defined( $hash->{IODev}->{NAME} ) ) { + Log3 $name, 3, "GardenaSmartDevice ($name) - I/O device is " + . $hash->{IODev}->{NAME}; + } + else { + Log3 $name, 1, "GardenaSmartDevice ($name) - no I/O device"; + } + + $iodev = $hash->{IODev}->{NAME}; + + my $d = $modules{GardenaSmartDevice}{defptr}{$deviceId}; + + return +"GardenaSmartDevice device $name on GardenaSmartBridge $iodev already defined." + if ( defined($d) + && $d->{IODev} == $hash->{IODev} + && $d->{NAME} ne $name ); + + CommandAttr( undef, $name . ' room GardenaSmart' ) + if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); + + CommandAttr( undef, $name . ' model ' . $category ) + if ( AttrVal( $name, 'model', 'none' ) eq 'none' ); + + Log3 $name, 3, +"GardenaSmartDevice ($name) - defined GardenaSmartDevice with DEVICEID: $deviceId"; + readingsSingleUpdate( $hash, 'state', 'initialized', 1 ); + + $modules{GardenaSmartDevice}{defptr}{$deviceId} = $hash; + + return; +} + +sub Undef { + my $hash = shift; + my $arg = shift; + + my $name = $hash->{NAME}; + my $deviceId = $hash->{DEVICEID}; + + delete $modules{GardenaSmartDevice}{defptr}{$deviceId}; + + return; +} + +sub Attr { + + my ( $cmd, $name, $attrName, $attrVal ) = @_; + my $hash = $defs{$name}; + + return; +} + +sub Set { + my $hash = shift // return; + my $aArg = shift // return; + + my $name = shift @$aArg; + my $cmd = shift @$aArg + // return qq{"set $name" needs at least one argument}; + + my $payload; + my $abilities; + my $service_id; + my $mainboard_version = + ReadingsVal( $name, 'mower_type-mainboard_version', 0.0 ); + + my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, + $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time); + + my $timezone_offset = $Sommerzeit ? 0 : ( Time::Piece->new )->tzoffset; + + #set default abilitie ... overwrite in cmd to change + $abilities = 'mower' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' ); + $abilities = 'watering' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' + || AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ); + $abilities = 'power' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' ); + $abilities = 'watering' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'electronic_pressure_pump' ); + + ### mower + # service_id (eco, parkuntilfurhternotice, startpoints) + if ( lc $cmd eq 'parkuntilfurthernotice' ) { + $payload = '"name":"park_until_further_notice"'; + if ( $mainboard_version > 10.30 ) { + $payload = +' "settings":{"name":"schedules_paused_until","value":"2038-01-18T00:00:00.000Z","device":"' + . $hash->{DEVICEID} . '"}'; + $abilities = 'mower_settings'; + $service_id = $hash->{helper}{schedules_paused_until_id}; + } + } + elsif ( lc $cmd eq 'parkuntilnexttimer' ) { + $payload = '"name":"park_until_next_timer"'; + if ( $mainboard_version > 10.30 ) { + $payload = '"properties":{"name":"mower_timer","value":0}'; + $abilities = 'mower_timer'; + } + } + elsif ( lc $cmd eq 'startresumeschedule' ) { + $payload = '"name":"start_resume_schedule"'; + if ( $mainboard_version > 10.30 ) { + $payload = +' "settings":{"name":"schedules_paused_until","value":"","device":"' + . $hash->{DEVICEID} . '"}'; + $abilities = 'mower_settings'; + $service_id = $hash->{helper}{schedules_paused_until_id}; + } + } + elsif ( lc $cmd eq 'startoverridetimer' ) { + $payload = '"name":"start_override_timer","parameters":{"duration":' + . $aArg->[0] * 60 . '}'; + if ( $mainboard_version > 10.30 ) { + $payload = '"properties":{"name":"mower_timer","value":' + . $aArg->[0] * 60 . '}'; + $abilities = 'mower_timer'; + } + + } + elsif ( lc $cmd eq 'startpoint' ) { + my $err; + ( $err, $payload, $abilities ) = + SetPredefinedStartPoints( $hash, $aArg ); + $service_id = $hash->{helper}{STARTINGPOINTID}; + return $err if ( defined($err) ); + } + elsif ( lc $cmd eq 'eco' ) { + $payload = + '"settings": {"name": "eco_mode", "value": ' + . $aArg->[0] + . ', "device": "' + . $hash->{DEVICEID} . '"}'; + $abilities = 'mower_settings' if ( $mainboard_version > 10.30 ); + $service_id = $hash->{helper}{eco_mode_id}; + +#$abilities['service_id'] = $hash->{helper}{SCHEDULESID} if ( $mainboard_version > 10.30 ); + } + ### electronic_pressure_pump + # elsif ( lc $cmd eq 'pumptimer' ) { + # $payload = + # '"name":"pump_manual_watering_timer","parameters":{"duration":' + # . $aArg->[0] . '}'; + # } + ### watering_computer & electronic pump + elsif ( lc $cmd eq 'manualoverride' ) { + $payload = + '"properties":{"name":"watering_timer_1' + . '","value":{"state":"manual","duration":' + . $aArg->[0] * 60 + . ',"valve_id":1}}'; + } + elsif ( lc $cmd eq 'manualbuttontime' ) { + $service_id = $hash->{helper}{button_config_time_id}; + $payload = + '"properties":{"name":"button_config_time",' + . '"value":' + . $aArg->[0] * 60 + . ',"timestamp":"2021-05-26T19:06:23.680Z"' + . ',"at_bound":null,"unit":"seconds","ability":"' + . $service_id . '"}'; + $abilities = 'watering_button_config'; + } + elsif ( $cmd =~ m{\AcancelOverride}xms ) { + + my $valve_id = 1; + + if ( $cmd =~ m{\AcancelOverrideValve(\d)\z}xms ) { + $valve_id = $1; + } + + $payload = + '"properties":{"name":"watering_timer_' + . $valve_id + . '","value":{"state":"idle","duration":' + . 0 + . ',"valve_id":' + . $valve_id . '}}'; + } + elsif ( $cmd =~ /.*Schedule$/ ) { + my $duration = ( + ( + defined( $aArg->[0] ) + ? ( + ( + ( Time::Piece->new ) + + ( ONE_HOUR * $aArg->[0] ) - + $timezone_offset + )->datetime + ) + . '.000Z' + : '2038-01-18T00:00:00.000Z' + ) + ); + + $abilities = 'wateringcomputer_settings'; + $service_id = $hash->{helper}->{'schedules_paused_until_id'}; + $payload = + '"settings":{"name":"schedules_paused_until"' + . ', "value":"' + . ( $cmd eq 'resumeSchedule' ? '' : $duration ) + . '","device":"' + . $hash->{DEVICEID} . '"}'; + } + elsif ( lc $cmd eq 'on' || lc $cmd eq 'off' || lc $cmd eq 'on-for-timer' ) { + my $val = ( + scalar( !@$aArg == 0 ) && ref($aArg) eq 'ARRAY' + ? $aArg->[0] * 60 + : lc $cmd + ); + + $payload = + '"properties":{"name":"power_timer", "value":"' . $val . '"}'; + } + ### Watering ic24 + elsif ( $cmd =~ m{\AmanualDurationValve\d\z}xms ) { + my $valve_id; + + if ( $cmd =~ m{\AmanualDurationValve(\d)\z}xms ) { + $valve_id = $1; + } + + $payload = + '"properties":{"name":"watering_timer_' + . $valve_id + . '","value":{"state":"manual","duration":' + . $aArg->[0] * 60 + . ',"valve_id":' + . $valve_id . '}}'; + } + elsif ( $cmd eq 'closeAllValves' ) { + $payload = '"name":"close_all_valves","parameters":{}'; + } + elsif ( $cmd =~ /.*ScheduleValve$/ ) { + my $valve_id = $aArg->[0]; + my $duration = ( + ( + defined( $aArg->[1] ) + ? ( + ( + ( Time::Piece->new ) + + ( ONE_HOUR * $aArg->[1] ) - + $timezone_offset + )->datetime + ) + . '.000Z' + : '2038-01-18T00:00:00.000Z' + ) + ); + + $abilities = 'irrigation_settings'; + $service_id = + $hash->{helper}->{ 'schedules_paused_until_' . $valve_id . '_id' }; + $payload = + '"settings":{"name":"schedules_paused_until_' + . $valve_id + . '", "value":"' + . ( $cmd eq 'resumeScheduleValve' ? '' : $duration ) + . '","device":"' + . $hash->{DEVICEID} . '"}'; + } + ### Watering_pressure_pump + elsif ( lc $cmd eq 'operating_mode') { + my $op_mode = $aArg->[0]; + $payload = '"settings":{"name":"operating_mode",' + .'"value":"'.$op_mode.'",' + .'"device":"' + . $hash->{DEVICEID}.'"}'; + $abilities = 'watering_pressure_pump_settings'; + $service_id = $hash->{helper}->{ 'operating_mode_id' }; + } + elsif ( lc $cmd eq 'leakage_detection') { + my $leakdetection_mode = $aArg->[0]; + $payload = '"settings":{"name":"leakage_detection",' + .'"value":"'.$leakdetection_mode.'",' + .'"device":"' + . $hash->{DEVICEID}.'"}'; + $abilities = 'watering_pressure_pump_settings'; + $service_id = $hash->{helper}->{ 'leakage_detection_id' }; + } + elsif ( lc $cmd eq 'turn_on_pressure') { + my $turnonpressure = $aArg->[0]; + $payload = '"settings":{"name":"turn_on_pressure",' + .'"value":"'.$turnonpressure.'",' + .'"device":"' + . $hash->{DEVICEID}.'"}'; + $abilities = 'watering_pressure_pump_settings'; + $service_id = $hash->{helper}->{ 'turn_on_pressure_id' }; + } + elsif ( lc $cmd eq 'resetvalveerrors') { + $payload = '"name":"reset_valve_errors",' + .' "parameters": {}'; + $abilities = 'error'; + } + + ### Sensors + elsif ( lc $cmd eq 'refresh' ) { + + my $sensname = $aArg->[0]; + if ( lc $sensname eq 'temperature' ) { + if ( ReadingsVal( $name, 'device_info-category', 'sensor' ) eq + 'sensor' ) + { + $payload = '"name":"measure_ambient_temperature"'; + $abilities = 'ambient_temperature'; + } + else { + $payload = '"name":"measure_soil_temperature"'; + $abilities = 'soil_temperature'; + } + } + elsif ( lc $sensname eq 'light' ) { + $payload = '"name":"measure_light"'; + $abilities = 'light'; + + } + elsif ( lc $sensname eq 'humidity' ) { + $payload = '"name":"measure_soil_humidity"'; + $abilities = 'humidity'; + } + } + ## winter sleep + elsif ( lc $cmd eq 'winter_mode' ) { + $payload = + '"settings":{"name":"winter_mode","value":"' + . $aArg->[0] + . '","device":"' + . $hash->{DEVICEID} . '"}'; + $abilities = 'winter_settings'; + $service_id = $hash->{helper}->{'winter_mode_id'}; + } + else { + + my $list = ''; + + $list .= +'parkUntilFurtherNotice:noArg parkUntilNextTimer:noArg startResumeSchedule:noArg startOverrideTimer:slider,0,1,240 startpoint' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' ); + + $list .= +'manualOverride:slider,1,1,59 cancelOverride:noArg resumeSchedule:noArg stopSchedule manualButtonTime:slider,0,2,100' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ); + + + $list .= 'manualOverride:slider,1,1,90 cancelOverride:noArg operating_mode:automatic,scheduled leakage_detection:watering,washing_machine,domestic_water_supply,off turn_on_pressure:slider,2,0.2,3.0,1 resetValveErrors:noArg' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'electronic_pressure_pump' ); + + $list .= +'closeAllValves:noArg stopScheduleValve:select,'.ReadingsVal( $name, 'ic24-valves_connected', '1' ).' resumeScheduleValve:select,'.ReadingsVal( $name, 'ic24-valves_connected', '1' ) + if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ); + + foreach my $valve (split(',', ReadingsVal( $name, 'ic24-valves_connected', '1'))) { + $list .= ' manualDurationValve'.$valve.':slider,1,1,90 ' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ); + } + + foreach my $valve (split(',', ReadingsVal( $name, 'ic24-valves_connected', '1'))) { + $list .= ' cancelOverrideValve'.$valve.':noArg ' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ); + } + + $list .= 'refresh:temperature,humidity' + if ( AttrVal( $name, 'model', 'unknown' ) =~ /sensor.?/ ); + + # add light for old sensors + $list .= ',light' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'sensor' + && ReadingsVal( $name, 'device_info-category', 'unknown' ) eq + 'sensor' ); + + $list .= 'on:noArg off:noArg on-for-timer:slider,0,1,720' + if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' ); + + # all devices has abilitie to fall a sleep + $list .= ' winter_mode:awake,hibernate'; + return "Unknown argument $cmd, choose one of $list"; + } + + $hash->{helper}{deviceAction} = $payload; + readingsSingleUpdate( $hash, "state", "send command to gardena cloud", 1 ); + + IOWrite( $hash, $payload, $hash->{DEVICEID}, $abilities, $service_id ); + Log3 $name, 4, +"GardenaSmartBridge ($name) - IOWrite: $payload $hash->{DEVICEID} $abilities IODevHash=$hash->{IODev}"; + + return; +} + +sub Parse { + my $io_hash = shift; + my $json = shift; + + my $name = $io_hash->{NAME}; + + my $decode_json = eval { decode_json($json) }; + if ($@) { + Log3 $name, 3, + "GardenaSmartDevice ($name) - JSON error while request: $@"; + } + + Log3 $name, 4, "GardenaSmartDevice ($name) - ParseFn was called"; + Log3 $name, 4, "GardenaSmartDevice ($name) - JSON: $json"; + + if ( defined( $decode_json->{id} ) ) { + + my $deviceId = $decode_json->{id}; + + if ( my $hash = $modules{GardenaSmartDevice}{defptr}{$deviceId} ) { + my $name = $hash->{NAME}; + + WriteReadings( $hash, $decode_json ); + Log3 $name, 4, + "GardenaSmartDevice ($name) - find logical device: $hash->{NAME}"; + + return $hash->{NAME}; + + } + else { + + Log3 $name, 3, + "GardenaSmartDevice ($name) - autocreate new device " + . makeDeviceName( $decode_json->{name} ) + . " with deviceId $decode_json->{id}, model $decode_json->{category}"; + return + "UNDEFINED " + . makeDeviceName( $decode_json->{name} ) + . " GardenaSmartDevice $decode_json->{id} $decode_json->{category}"; + } + } + + return; +} + +sub WriteReadings { + my $hash = shift; + my $decode_json = shift; + + my $name = $hash->{NAME}; + my $abilities = scalar( @{ $decode_json->{abilities} } ); + my $settings = scalar( @{ $decode_json->{settings} } ); + my $scheduled_events = scalar( @{ $decode_json->{scheduled_events} } ); + + readingsBeginUpdate($hash); + + do { + + if ( + ref( $decode_json->{abilities}[$abilities]{properties} ) eq "ARRAY" + && scalar( @{ $decode_json->{abilities}[$abilities]{properties} } ) + > 0 ) + { + for my $propertie ( + @{ $decode_json->{abilities}[$abilities]{properties} } ) + { + if ( + exists( $decode_json->{abilities}[$abilities]{name} ) + && ( $decode_json->{abilities}[$abilities]{name} eq + 'watering' ) + ) + { + + if ( $propertie->{name} eq 'button_config_time' ) { + if ( $hash->{helper}{ $propertie->{name} . '_id' } ne + $decode_json->{abilities}[$abilities]{id} ) + { + $hash->{helper}{ $propertie->{name} . '_id' } = + $decode_json->{abilities}[$abilities]{id}; + } + readingsBulkUpdateIfChanged( + $hash, + 'manualButtonTime', + ( + RigReadingsValue( + $hash, $propertie->{value} / 60 + ) + ) + ); + next; + } + } + + readingsBulkUpdateIfChanged( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name}, + ($propertie->{value} eq '') ? RigReadingsValue( $hash, 'n/a') : RigReadingsValue( $hash, $propertie->{value} ) + ) + if ( exists( $propertie->{value} ) # defined ignored 'value':null + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} ne 'radio-quality' + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} ne 'battery-level' + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} ne 'internal_temperature-temperature' + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} ne 'ambient_temperature-temperature' + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} ne 'soil_temperature-temperature' + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} ne 'humidity-humidity' + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} ne 'light-light' + && ref( $propertie->{value} ) ne "HASH" ); + + readingsBulkUpdateIfChanged( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name}, + RigReadingsValue( $hash, $propertie->{value} ) + ) + if ( + defined( $propertie->{value} ) + && ( $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq 'radio-quality' + || $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq 'battery-level' + || $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq + 'internal_temperature-temperature' + || $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq + 'ambient_temperature-temperature' + || $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq 'soil_temperature-temperature' + || $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq 'humidity-humidity' + || $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq 'light-light' ) + ); + + readingsBulkUpdateIfChanged( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} + . '_timestamp', + Time::Piece->strptime( + RigReadingsValue( $hash, $propertie->{timestamp} ), + "%Y-%m-%d %H:%M:%S" )->strftime('%s') + + ) + if ( + defined( $propertie->{value} ) + && ( $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq 'mower_timer-mower_timer' ) + ); + + readingsBulkUpdateIfChanged( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name}, + join( ',', @{ $propertie->{value} } ) + ) + if ( defined( $propertie->{value} ) + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq 'ic24-valves_connected' ); + + readingsBulkUpdateIfChanged( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name}, + join( ',', @{ $propertie->{value} } ) + ) + if ( defined( $propertie->{value} ) + && $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} eq 'ic24-valves_master_config' ); + + if ( ref( $propertie->{value} ) eq "HASH" ) { + my $sub_state = 0; my $sub_value = 0; + while ( my ( $r, $v ) = each %{ $propertie->{value} } ) { + if ( ref( $v ) ne "HASH" ) { + readingsBulkUpdate( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} . '_' + . $r, + RigReadingsValue( $hash, $v ) + ); + } else { + while ( my ( $i_r, $i_v ) = each %{ $v } ) { + readingsBulkUpdate( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} . '_' + . $r . '_' . $i_r, + RigReadingsValue( $hash, $i_v ) + ); + } + } + } + } + # ic24 and other watering devices calc irrigation left in sec + readingsBulkUpdateIfChanged( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} + . '_irrigation_left', + ( $propertie->{value}{duration} > 0 ) ? (Time::Piece::localtime->strptime( + RigReadingsValue($hash, $propertie->{timestamp}), "%Y-%m-%d %H:%M:%S") + + ($propertie->{value}{duration} + 3 ) - Time::Piece::localtime->new) : 0 + ) + if ( defined( $propertie->{value} ) + && $decode_json->{abilities}[$abilities]{name} eq 'watering' + ); + } + } + + $abilities--; + } while ( $abilities >= 0 ); + + + if ( + exists( $decode_json->{scheduled_events} ) + # && scalar ($decode_json->{scheduled_events} ) > 0 + && ref ($decode_json->{scheduled_events}) eq 'ARRAY' ) { + readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_events_count', + scalar( @{$decode_json->{scheduled_events} } ) ); + my $valve_id =1; my $event_id = 1; # ic24 [1..6] | wc, pump [1] + + for my $event_schedules ( @{ $decode_json->{scheduled_events} } ) { + $valve_id = $event_schedules->{valve_id} if ( exists($event_schedules->{valve_id} ) ); #ic24 + $event_id++; # event id + + while ( my ( $r, $v ) = each %{ $event_schedules } ) { + readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_event_' + . $event_id + . '_valve_' + . $valve_id + . '_' + . $r, + $v) if (ref($v) ne 'HASH' ); + readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_event_' + . $event_id + . '_valve_' + . $valve_id + . '_' + . $v->{type}, + join(',', @ { $v->{weekdays}}) ) if (ref($v) eq 'HASH' ); + }; + }; + + }; # fi scheduled_events + + my $winter_mode; + + do { +#Log3 $name, 1, "Settings pro Device : ".$decode_json->{settings}[$settings]{name}; +#Log3 $name, 1, " - KEIN ARRAY" if ( ref( $decode_json->{settings}[$settings]{value} ) ne "ARRAY"); +#Log3 $name, 1, " - IST ARRAY" if ( ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY"); + + if ( + exists( $decode_json->{settings}[$settings]{name} ) + && ( $decode_json->{settings}[$settings]{name} =~ + /schedules_paused_until_?\d?$/ + || $decode_json->{settings}[$settings]{name} eq 'eco_mode' + || $decode_json->{settings}[$settings]{name} eq 'winter_mode' + || $decode_json->{settings}[$settings]{name} eq 'operating_mode' + || $decode_json->{settings}[$settings]{name} eq 'leakage_detection' + || $decode_json->{settings}[$settings]{name} eq 'turn_on_pressure' ) + ) + { + if ( $hash->{helper} + { $decode_json->{settings}[$settings]{name} . '_id' } ne + $decode_json->{settings}[$settings]{id} ) + { + $hash->{helper} + { $decode_json->{settings}[$settings]{name} . '_id' } = + $decode_json->{settings}[$settings]{id}; + } + # check watering controler single schedules pause until + if ( $decode_json->{settings}[$settings]{name} eq 'schedules_paused_until' ) { + readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_paused_until', + $decode_json->{settings}[$settings]{value} ); + } + ##### + #ic24 schedules pause until + if ($decode_json->{settings}[$settings]{name} =~ /schedules_paused_until_?(\d)?$/) { + #my $ventil = substr($decode_json->{settings}[$settings]{name}, -1); # => 1 - 6 + # check if empty, clear scheduling-scheduled_watering_next_start_x + readingsBulkUpdateIfChanged( $hash, 'scheduling-'.$decode_json->{settings}[$settings]{name}, + $decode_json->{settings}[$settings]{value} ); + # CommandAttr( undef, $name . " scheduling-scheduled_watering_next_start_") if ($decode_json->{settings}[$settings]{value} eq '' ) + } + + # save electronid pressure pump settings as readings + if ( $decode_json->{settings}[$settings]{name} eq 'operating_mode' + || $decode_json->{settings}[$settings]{name} eq 'leakage_detection' + || $decode_json->{settings}[$settings]{name} eq 'turn_on_pressure' ) { + readingsBulkUpdateIfChanged( $hash, $decode_json->{settings}[$settings]{name}, + $decode_json->{settings}[$settings]{value} ); + + } + # save winter mode as reading + if ( $decode_json->{settings}[$settings]{name} eq 'winter_mode' ) { + readingsBulkUpdateIfChanged( $hash, 'winter_mode', + $decode_json->{settings}[$settings]{value} ); + + $winter_mode = $decode_json->{settings}[$settings]{value}; + } + } + + if ( defined( $decode_json->{settings}[$settings]{name} ) + && $decode_json->{settings}[$settings]{name} eq 'valve_names' + && ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY" ) { # or HASH ? + my @valves = @{$decode_json->{settings}[$settings]{value}}; + foreach my $valve( @valves ) { + Log3 $name, 4, "GardenaSmartDevice ($name) valve_name $valve->{'name'}"; + readingsBulkUpdateIfChanged( $hash, 'valve-valve_name_'.$valve->{"id"}, + $valve->{"name"} ); + } + } + + if ( ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY" + && $decode_json->{settings}[$settings]{name} eq 'starting_points' ) + { + #save the startingpointid needed to update the startingpoints + if ( $hash->{helper}{STARTINGPOINTID} ne + $decode_json->{settings}[$settings]{id} ) + { + $hash->{helper}{STARTINGPOINTID} = + $decode_json->{settings}[$settings]{id}; + } + + $hash->{helper}{STARTINGPOINTS} = + '{ "name": "starting_points", "value": ' + . encode_json( $decode_json->{settings}[$settings]{value} ) . '}'; + my $startpoint_cnt = 0; + + for my $startingpoint ( + @{ $decode_json->{settings}[$settings]{value} } ) + { + $startpoint_cnt++; + readingsBulkUpdateIfChanged( + $hash, + 'startpoint-' . $startpoint_cnt . '-enabled', + $startingpoint->{enabled} + ); + } + } + + $settings--; + } while ( $settings >= 0 ); + + if ( $winter_mode ne 'hibernate' ) { + setState($hash); + } + else { + readingsBulkUpdate( $hash, 'state', + RigReadingsValue( $hash, 'hibernate' ) ); + } + + readingsEndUpdate( $hash, 1 ); + + Log3 $name, 4, "GardenaSmartDevice ($name) - readings was written"; + + return; +} + +sub setState { + my $hash = shift; + my $name = $hash->{NAME}; + + my $online_state = + ReadingsVal( $name, 'device_info-connection_status', 'unknown' ); + + #online state mower + readingsBulkUpdate( $hash, 'state', + $online_state eq 'online' + ? ReadingsVal( $name, 'mower-status', 'readingsValError' ) + : 'offline' ) + if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' ); + + # ic24 / wc / electronic pump + + if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' + || AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' + || AttrVal( $name, 'model', 'unknown' ) eq 'electronic_pressure_pump' ){ + my @opened_valves; + my $state_string = ''; my $nearst_irrigation = '2999-12-12 12:00'; + my $has_schedule = 0; my $longest_duration = 0; my $processed_item = ''; + my $error_type = 'ok'; + my @valves_connected = AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ? split(',', ReadingsVal( $name, 'ic24-valves_connected', '')) : '1'; + + $has_schedule = 1 if ( ReadingsVal($name, 'scheduling-schedules_events_count', '') ne '' ); + for (@valves_connected){ # valves 1 or 1..6 + ## add to opened ventils, if watering active + push @opened_valves, $_ if ( ( ( ReadingsVal( $name, "watering-watering_timer_".$_."_duration", 0 ) =~ m{\A[1-9]([0-9]+)?\z}xms ) ? $_ : 0 ) > 0 ); + ## set error type (pumpe required) + $error_type = ReadingsVal( $name, 'error-valve_error_'.$_.'_type', 'ok' ) if (ReadingsVal( $name, 'error-valve_error_'.$_.'_type', 'ok' ) ne 'ok'); + ## find longest irrigation duration + $longest_duration = ReadingsVal( $name, "watering-watering_timer_".$_."_irrigation_left", 0 ) if ( + ( ReadingsVal( $name, "watering-watering_timer_".$_."_duration", 0 ) =~ m{\A[1-9]([0-9]+)?\z}xms + && ReadingsVal( $name, "watering-watering_timer_".$_."_duration", 0 ) > 0 + && ReadingsVal( $name, "watering-watering_timer_".$_."_duration", 0 ) > $longest_duration ) ); + + # y-m-d h:m + $processed_item = AttrVal( $name, 'model', 'unknown' ) eq 'ic24' + ? RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')) + : RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until', '')); + + Log3 $name, 5, "[DEBUG] - process: $processed_item"; + Log3 $name, 5, "[DEBUG] - next_start: ". ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''); # n/a RigReadingsValue( $hash, 'n/a') + # $nearst_irrigation = RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until_'.$_, '')) + if ( ReadingsVal($name, 'scheduling-scheduled_watering_next_start', '') eq RigReadingsValue( $hash, 'n/a') ) { # non next start, schedules paused permanently or next schedule > 1 year; get nearst paused_until + Log3 $name, 5, "[DEBUG] - next_start: empty "; + Log3 $name, 5, "[DEBUG] - empty pro item ".Time::Piece->strptime( $processed_item, "%Y-%m-%d %H:%M:%S"); + Log3 $name, 5, "[DEBUG] - empty nearst ".Time::Piece->strptime( $nearst_irrigation, "%Y-%m-%d %H:%M:%S"); + $nearst_irrigation = $processed_item + if ( Time::Piece->strptime( $processed_item, "%Y-%m-%d %H:%M:%S") + < Time::Piece->strptime( $nearst_irrigation, "%Y-%m-%d %H:%M:%S") + && $has_schedule + && Time::Piece->strptime( $processed_item, "%Y-%m-%d %H:%M:%S") + > Time::Piece->new + ) + } else { + $nearst_irrigation = ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''); + } + Log3 $name, 5, "[DEBUG] - choosed nearst: $nearst_irrigation"; + + } # for + # override state 4 extendedstates + if ( AttrVal( $name, "extendedState", 0 ) == 1) { + if (scalar(@opened_valves) > 0){ + ## valve 1 will be ir.. 23 minutes remaining + for (@valves_connected){ + $state_string .= sprintf(RigReadingsValue($hash,'valve').' '.$_.' '.(RigReadingsValue($hash, 'will be irrigated %.f minutes remaining.') .'
'), (ReadingsVal( $name, 'watering-watering_timer_'.$_.'_duration', 0 )/60)); + } # /for + } else { + $state_string .= RigReadingsValue($hash, 'closed'); + } + $state_string .= ($has_schedule) ? sprintf( RigReadingsValue($hash, 'next watering: %s'), RigReadingsValue($hash, ReadingsVal($name, 'scheduling-scheduled_watering_next_start', ''))) : sprintf( RigReadingsValue($hash, 'paused until %s') , $nearst_irrigation); + #TODO: Write state format for ventil 1-@valces_connected -> map ? + CommandAttr( undef, $name . ' stateFormat + { + + } + ' ) + if ( AttrVal( $name, 'stateFormat', 'none' ) eq 'none' ); + } else { + Log3 $name, 5, "[DEBUG] - Offene Ventile :".scalar(@opened_valves)." laengste bewaesserung: $longest_duration . hat Zeitplan: $has_schedule Naechster Zeitplan: $nearst_irrigation"; + $state_string = scalar(@opened_valves) > 0 + # offen + ? sprintf( (RigReadingsValue($hash, 'will be irrigated %.f minutes remaining.')), $longest_duration/60) + # zu + : + ( $has_schedule + && $nearst_irrigation ne '2999-12-12 12:00') + # zeitplan aktiv + # ? ( $nearst_irrigation eq '2038-01-18 00:00') sprintf( RigReadingsValue($hash, 'paused until %s') , $nearst_irrigation) + ? ( $nearst_irrigation eq RigReadingsValue( $hash, 'n/a') || $nearst_irrigation =~ '2038-01-18.*') + # dauerhaft pausiert + ? sprintf( (RigReadingsValue($hash, 'closed') .'. '.RigReadingsValue($hash , 'schedule permanently paused')) ) + # naechster zeutplan + : (ReadingsVal($name, 'scheduling-scheduled_watering_next_start', '') eq RigReadingsValue($hash, 'n/a')) + ? sprintf( RigReadingsValue($hash, 'paused until %s') , $nearst_irrigation) + : sprintf( (RigReadingsValue($hash, 'closed') .'. '.RigReadingsValue($hash, 'next watering: %s')), $nearst_irrigation ) + # zeitplan pausiert + : RigReadingsValue($hash, 'closed') + ; + # state offline | override + $state_string = 'offline' if ($online_state eq 'offline'); + $state_string = ( $error_type ne 'ok' ) ? $error_type : $state_string; + + } + readingsBulkUpdate( + $hash, 'state', RigReadingsValue( $hash, $state_string ) ); + } + + # Sensor / Sensor 2 + if ( AttrVal( $name, 'model', 'unknown' ) =~ /sensor.?/ ) { + my $state_string = + ( ReadingsVal( $name, 'device_info-category', 'unknown' ) eq + 'sensor' ) + ? 'T: ' + . ReadingsVal( $name, 'ambient_temperature-temperature', + 'readingsValError' ) + . '°C, ' + : 'T: ' + . ReadingsVal( $name, 'soil_temperature-temperature', + 'readingsValError' ) + . '°C, '; + $state_string .= 'H: ' + . ReadingsVal( $name, 'humidity-humidity', 'readingsValError' ) . '%'; + $state_string .= ', L: ' + . ReadingsVal( $name, 'light-light', 'readingsValError' ) . 'lux' + if ( ReadingsVal( $name, 'device_info-category', 'unknown' ) eq + 'sensor' ); + +# if ( $online_state eq 'offline') { +# readingsBulkUpdate( $hash, 'humidity-humidity', '-1' ); +# readingsBulkUpdate( $hash, 'ambient_temperature-temperature', '-1' ) if (ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor'); +# readingsBulkUpdate( $hash, 'light-light', '-1' ) if (ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor'); +# } + #online state sensor I II + readingsBulkUpdate( $hash, 'state', + $online_state eq 'online' ? RigReadingsValue( $hash, $state_string) : RigReadingsValue( $hash, 'offline') ); + } + + readingsBulkUpdate( $hash, 'state', + ReadingsVal( $name, 'power-power_timer', 'no info from power-timer' ) ) + if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' ); + + return; +} + +################################## +################################## +#### my little helpers ########### + +sub ReadingLangGerman { + my $hash = shift; + my $readingValue = shift; + + my $name = $hash->{NAME}; + my %langGermanMapp = ( + 'ok_cutting' => 'mähen', + 'paused' => 'pausiert', + 'ok_searching' => 'suche Ladestation', + 'ok_charging' => 'lädt', + 'ok_leaving' => 'unterwegs zum Startpunkt', + 'wait_updating' => 'wird aktualisiert ...', + 'wait_power_up' => 'wird eingeschaltet ...', + 'parked_timer' => 'geparkt nach Zeitplan', + 'parked_park_selected' => 'geparkt', + 'off_disabled' => 'der Mäher ist ausgeschaltet', + 'off_hatch_open' => 'deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich', + 'unknown' => 'unbekannter Status', + 'error' => 'Fehler', + 'error_at_power_up' => 'Neustart ...', + 'off_hatch_closed' => 'Deaktiviert. Manueller Start erforderlich', + 'ok_cutting_timer_overridden' => 'manuelles mähen', + 'parked_autotimer' => 'geparkt durch SensorControl', + 'parked_daily_limit_reached' => 'abgeschlossen', + 'no_message' => 'kein Fehler', + 'outside_working_area' => 'außerhalb des Arbeitsbereichs', + 'no_loop_signal' => 'kein Schleifensignal', + 'wrong_loop_signal' => 'falsches Schleifensignal', + 'loop_sensor_problem_front' => 'Problem Schleifensensor, vorne', + 'loop_sensor_problem_rear' => 'Problem Schleifensensor, hinten', + 'trapped' => 'eingeschlossen', + 'upside_down' => 'steht auf dem Kopf', + 'low_battery' => 'niedriger Batteriestand', + 'empty_battery' => 'Batterie leer', + 'no_drive' => 'fährt nicht', + 'lifted' => 'angehoben', + 'stuck_in_charging_station' => 'eingeklemmt in Ladestation', + 'charging_station_blocked' => 'Ladestation blockiert', + 'collision_sensor_problem_rear' => 'Problem Stoßsensor hinten', + 'collision_sensor_problem_front' => 'Problem Stoßsensor vorne', + 'wheel_motor_blocked_right' => 'Radmotor rechts blockiert', + 'wheel_motor_blocked_left' => 'Radmotor links blockiert', + 'wheel_drive_problem_right' => 'Problem Antrieb, rechts', + 'wheel_drive_problem_left' => 'Problem Antrieb, links', + 'cutting_system_blocked' => 'Schneidsystem blockiert', + 'invalid_sub_device_combination' => 'fehlerhafte Verbindung', + 'settings_restored' => 'Standardeinstellungen', + 'electronic_problem' => 'elektronisches Problem', + 'charging_system_problem' => 'Problem Ladesystem', + 'tilt_sensor_problem' => 'Kippsensor Problem', + 'wheel_motor_overloaded_right' => 'rechter Radmotor überlastet', + 'wheel_motor_overloaded_left' => 'linker Radmotor überlastet', + 'charging_current_too_high' => 'Ladestrom zu hoch', + 'temporary_problem' => 'vorübergehendes Problem', + 'guide_1_not_found' => 'SK 1 nicht gefunden', + 'guide_2_not_found' => 'SK 2 nicht gefunden', + 'guide_3_not_found' => 'SK 3 nicht gefunden', + 'difficult_finding_home' => 'Problem die Ladestation zu finden', + 'guide_calibration_accomplished' => 'Kalibrierung des Suchkabels beendet', + 'guide_calibration_failed' => 'Kalibrierung des Suchkabels fehlgeschlagen', + 'temporary_battery_problem' => 'kurzzeitiges Batterieproblem', + 'battery_problem' => 'Batterieproblem', + 'alarm_mower_switched_off' => 'Alarm! Mäher ausgeschalten', + 'alarm_mower_stopped' => 'Alarm! Mäher gestoppt', + 'alarm_mower_lifted' => 'Alarm! Mäher angehoben', + 'alarm_mower_tilted' => 'Alarm! Mäher gekippt', + 'connection_changed' => 'Verbindung geändert', + 'connection_not_changed' => 'Verbindung nicht geändert', + 'com_board_not_available' => 'COM Board nicht verfügbar', + 'slipped' => 'rutscht', + 'out_of_operation' => 'ausser Betrieb', + 'replace_now' => 'kritischer Batteriestand, wechseln Sie jetzt', + 'low' => 'niedrig', + 'ok' => 'ok', + 'no_source' => 'ok', + 'mower_charging' => 'Mäher wurde geladen', + 'completed_cutting_autotimer' => 'Sensor Control erreicht', + 'week_timer' => 'Wochentimer erreicht', + 'countdown_timer' => 'Stoppuhr Timer', + 'undefined' => 'unklar', + 'unknown' => 'unklar', + 'status_device_unreachable' => 'Gerät ist nicht in Reichweite', + 'status_device_alive' => 'Gerät ist in Reichweite', + 'bad' => 'schlecht', + 'poor' => 'schwach', + 'good' => 'gut', + 'undefined' => 'unklar', + 'idle' => 'nichts zu tun', + 'firmware_cancel' => 'Firmwareupload unterbrochen', + 'firmware_upload' => 'Firmwareupload', + 'unsupported' => 'nicht unterstützt', + 'up_to_date' => 'auf dem neusten Stand', + 'mower' => 'Mäher', + 'watering_computer' => 'Bewässerungscomputer', + 'no_frost' => 'kein Frost', + 'open' => 'offen', + 'closed' => 'geschlossen', + 'included' => 'inbegriffen', + 'active' => 'aktiv', + 'inactive' => 'nicht aktiv', + 'hibernate' => 'Winterschlaf', + 'awake' => 'Aufgewacht', + 'schedule permanently paused' => 'Zeitplan dauerhaft pausiert', + 'paused until %s' => 'pausiert bis %s', + 'will be irrigated %.f minutes remaining.'=> 'Wird bewässert. %.f Minuten verbleibend.', + 'next watering: %s' => 'Nächste Bewässerung: %s', + 'n/a' => 'nicht verfügbar', + 'pump_not_filled' => 'Pumpe nicht gefüllt', + ); + + if ( + defined( $langGermanMapp{$readingValue} ) + && ( AttrVal( 'global', 'language', 'none' ) eq 'DE' + || AttrVal( $name, 'readingValueLanguage', 'none' ) eq 'de' ) + && AttrVal( $name, 'readingValueLanguage', 'none' ) ne 'en' + ) + { + return $langGermanMapp{$readingValue}; + } + else { + return $readingValue; + } + + return; +} + +sub RigReadingsValue { + my $hash = shift; + my $readingValue = shift; + + my $rigReadingValue; + + if ( $readingValue =~ /^(\d+)-(\d\d)-(\d\d)T(\d\d)/ ) { + $rigReadingValue = Zulu2LocalString($readingValue); + } + else { + $rigReadingValue = ReadingLangGerman( $hash, $readingValue ); + } + + return $rigReadingValue; +} + +sub Zulu2LocalString { + my $t = shift; + + my ( $datehour, $datemin, $rest ) = split( /:/, $t, 3 ); + + my ( $year, $month, $day, $hour, $min ) = + $datehour =~ /(\d+)-(\d\d)-(\d\d)T(\d\d)/; + my $epoch = timegm( 0, 0, $hour, $day, $month - 1, $year ); + + my ( $lyear, $lmonth, $lday, $lhour, $isdst ) = + ( localtime($epoch) )[ 5, 4, 3, 2, -1 ]; + + $lyear += 1900; # year is 1900 based + $lmonth++; # month number is zero based + + if ( defined($rest) ) { + return ( + sprintf( + "%04d-%02d-%02d %02d:%02d:%s", + $lyear, $lmonth, $lday, + $lhour, $datemin, substr( $rest, 0, 2 ) + ) + ); + } + elsif ( $lyear < 2000 ) { + return 'temporarily unavailable'; + } + else { + return ( + sprintf( + "%04d-%02d-%02d %02d:%02d", + $lyear, $lmonth, $lday, $lhour, substr( $datemin, 0, 2 ) + ) + ); + } + + return; +} + +sub SetPredefinedStartPoints { + my $hash = shift; + my $aArg = shift; + + my ( $startpoint_state, $startpoint_num, @morestartpoints ) = @{$aArg}; + + my $name = $hash->{NAME}; + my $payload; + my $abilities; + + if ( defined($startpoint_state) && defined($startpoint_num) ) { + if ( defined( $hash->{helper}{STARTINGPOINTS} ) + && $hash->{helper}{STARTINGPOINTS} ne '' ) + { +# add needed parameters to saved settings config and change the value in request + my $decode_json_settings = + eval { decode_json( $hash->{helper}{STARTINGPOINTS} ) }; + if ($@) { + Log3 $name, 3, +"GardenaSmartBridge ($name) - JSON error while setting startpoint: $@"; + } + + $decode_json_settings->{device} = $hash->{DEVICEID}; + my $setval = $startpoint_state eq 'disable' ? \0 : \1; + $decode_json_settings->{value}[ $startpoint_num - 1 ]{enabled} = + $setval; + + #set more startpoints + if ( + defined scalar(@morestartpoints) + && ( scalar(@morestartpoints) == 2 + || scalar(@morestartpoints) == 4 ) + ) + { + if ( scalar(@morestartpoints) == 2 ) { + $setval = $morestartpoints[0] eq 'disable' ? \0 : \1; + $decode_json_settings->{value}[ $morestartpoints[1] - 1 ] + {enabled} = $setval; + + } + elsif ( scalar(@morestartpoints) == 4 ) { + $setval = $morestartpoints[0] eq 'disable' ? \0 : \1; + $decode_json_settings->{value}[ $morestartpoints[1] - 1 ] + {enabled} = $setval; + $setval = $morestartpoints[2] eq 'disable' ? \0 : \1; + $decode_json_settings->{value}[ $morestartpoints[3] - 1 ] + {enabled} = $setval; + } + } + + $payload = '"settings": ' . encode_json($decode_json_settings); + $abilities = 'mower_settings'; + + #$abilities['service_id'] = $hash->{helper}{STARTINGPOINTID}; + } + else { + return + "startingpoints not loaded yet, please wait a couple of minutes", + undef, undef; + } + } + else { + return + "startpoint usage: set " + . $hash->{NAME} + . " startpoint disable 1 [enable 2] [disable 3]", undef, undef; + } + + return undef, $payload, $abilities; +} + +1; + +=pod + +=item device +=item summary Modul to control GardenaSmart Devices +=item summary_DE Modul zur Steuerung von GardenaSmartgeräten + +=begin html + + +

GardenaSmartDevice

+
    + In combination with GardenaSmartBridge this FHEM Module controls the GardenaSmart Device using the GardenaCloud +

    + Once the Bridge device is created, the connected devices are automatically recognized and created in FHEM.
    + From now on the devices can be controlled and changes in the GardenaAPP are synchronized with the state and readings of the devices. + +


    + Readings +
      +
    • battery-charging - Indicator if the Battery is charged (0/1) or with newer Firmware (false/true)
    • +
    • battery-level - load percentage of the Battery
    • +
    • battery-rechargeable_battery_status - healthyness of the battery (out_of_operation/replace_now/low/ok)
    • +
    • device_info-category - category of device (mower/watering_computer)
    • +
    • device_info-last_time_online - timestamp of last radio contact
    • +
    • device_info-manufacturer - manufacturer
    • +
    • device_info-product - product type
    • +
    • device_info-serial_number - serial number
    • +
    • device_info-sgtin -
    • +
    • device_info-version - firmware version
    • +
    • firmware-firmware_command - firmware command (idle/firmware_cancel/firmware_upload/unsupported)
    • +
    • firmware-firmware_status - firmware status
    • +
    • firmware-firmware_update_start - indicator when a firmwareupload is started
    • +
    • firmware-firmware_upload_progress - progress indicator of firmware update
    • +
    • firmware-inclusion_status - inclusion status
    • +
    • internal_temperature-temperature - internal device temperature
    • +
    • mower-error - actual error message +
        +
      • no_message
      • +
      • outside_working_area
      • +
      • no_loop_signal
      • +
      • wrong_loop_signal
      • +
      • loop_sensor_problem_front
      • +
      • loop_sensor_problem_rear
      • +
      • trapped
      • +
      • upside_down
      • +
      • low_battery
      • +
      • empty_battery
      • +
      • no_drive
      • +
      • lifted
      • +
      • stuck_in_charging_station
      • +
      • charging_station_blocked
      • +
      • collision_sensor_problem_rear
      • +
      • collision_sensor_problem_front
      • +
      • wheel_motor_blocked_right
      • +
      • wheel_motor_blocked_left
      • +
      • wheel_drive_problem_right
      • +
      • wheel_drive_problem_left
      • +
      • cutting_system_blocked
      • +
      • invalid_sub_device_combination
      • +
      • settings_restored
      • +
      • electronic_problem
      • +
      • charging_system_problem
      • +
      • tilt_sensor_problem
      • +
      • wheel_motor_overloaded_right
      • +
      • wheel_motor_overloaded_left
      • +
      • charging_current_too_high
      • +
      • temporary_problem
      • +
      • guide_1_not_found
      • +
      • guide_2_not_found
      • +
      • guide_3_not_found
      • +
      • difficult_finding_home
      • +
      • guide_calibration_accomplished
      • +
      • guide_calibration_failed
      • +
      • temporary_battery_problem
      • +
      • battery_problem
      • +
      • alarm_mower_switched_off
      • +
      • alarm_mower_stopped
      • +
      • alarm_mower_lifted
      • +
      • alarm_mower_tilted
      • +
      • connection_changed
      • +
      • connection_not_changed
      • +
      • com_board_not_available
      • +
      • slipped
      • +
      +
    • +
    • mower-manual_operation - (0/1) or with newer Firmware (false/true)
    • +
    • mower-override_end_time - manual override end time
    • +
    • mower-source_for_next_start - source for the next start +
        +
      • no_source
      • +
      • mower_charging
      • +
      • completed_cutting_autotimer
      • +
      • week_timer
      • +
      • countdown_timer
      • +
      • undefined
      • +
      +
    • +
    • mower-status - mower state (see state)
    • +
    • mower-timestamp_next_start - timestamp of next scheduled start
    • +
    • radio-connection_status - state of connection
    • +
    • radio-quality - percentage of the radio quality
    • +
    • radio-state - radio state (bad/poor/good/undefined)
    • +
    • state - state of the mower +
        +
      • paused
      • +
      • ok_cutting
      • +
      • ok_searching
      • +
      • ok_charging
      • +
      • ok_leaving
      • +
      • wait_updating
      • +
      • wait_power_up
      • +
      • parked_timer
      • +
      • parked_park_selected
      • +
      • off_disabled
      • +
      • off_hatch_open
      • +
      • unknown
      • +
      • error
      • +
      • error_at_power_up
      • +
      • off_hatch_closed
      • +
      • ok_cutting_timer_overridden
      • +
      • parked_autotimer
      • +
      • parked_daily_limit_reached
      • +
      +
    • +
    +

    + + Attributes +
      +
    • readingValueLanguage - Change the Language of Readings (de,en/if not set the default is english and the global language is not set at german)
    • +
    • model -
    • +
    +

    + + set +
      +
    • winter_mode - awake | hibernate
    • +
    +
      +

      mower

      +
    • parkUntilFurtherNotice
    • +
    • parkUntilNextTimer
    • +
    • startOverrideTimer - (in minutes, 60 = 1h, 1440 = 24h, 4320 = 72h)
    • +
    • startResumeSchedule
    • +
    • startpoint enable|disable 1|2|3 - enables or disables one or more predefined start points
    • +
        +
      • set NAME startpoint enable 1
      • +
      • set NAME startpoint disable 3 enable 1
      • +
      +

      irrigation control

      +
    • resumeScheduleValve - start schedule irrigation on valve n
    • +
    • stopScheduleValve - stop schedule irrigation on valve n (Default: 2038-01-18T00:00:00.000Z) | optional params hours (now + hours)
    • +
    • closeAllValves - close all valves
    • +

      water control

      +
    • manualButtonTime - set manual time for button press (in minutes) 0 disable button
    • +
    • stopSchedule - stop schedule for now + n hours (Default: 2038-01-18T00:00:00.000Z)
    • +
    • resumeSchedule - resume schedule
    • +
    +
+ +=end html +=begin html_DE + + +

GardenaSmartDevice

+
    + Zusammen mit dem Device GardenaSmartDevice stellt dieses FHEM Modul die Kommunikation zwischen der GardenaCloud und Fhem her. +

    + Wenn das GardenaSmartBridge Device erzeugt wurde, werden verbundene Geräte automatisch erkannt und in Fhem angelegt.
    + Von nun an können die eingebundenen Geräte gesteuert werden. Änderungen in der APP werden mit den Readings und dem Status syncronisiert. + +
+
+
    + Readings +
      +
    • battery-charging - Ladeindikator (0/1) oder mit neuerer Firmware (false/true)
    • +
    • battery-level - Ladezustand der Batterie in Prozent
    • +
    • battery-rechargeable_battery_status - Zustand der Batterie (Ausser Betrieb/Kritischer Batteriestand, wechseln Sie jetzt/Niedrig/oK)
    • +
    • device_info-category - Eigenschaft des Gerätes (Mäher/Bewässerungscomputer/Bodensensor)
    • +
    • device_info-last_time_online - Zeitpunkt der letzten Funkübertragung
    • +
    • device_info-manufacturer - Hersteller
    • +
    • device_info-product - Produkttyp
    • +
    • device_info-serial_number - Seriennummer
    • +
    • device_info-sgtin -
    • +
    • device_info-version - Firmware Version
    • +
    • firmware-firmware_command - Firmware Kommando (Nichts zu tun/Firmwareupload unterbrochen/Firmwareupload/nicht unterstützt)
    • +
    • firmware-firmware_status - Firmware Status
    • +
    • firmware-firmware_update_start - Firmwareupdate (0/1) oder mit neuerer Firmware (false/true)
    • +
    • firmware-firmware_upload_progress - Firmwareupdatestatus in Prozent
    • +
    • firmware-inclusion_status - Einbindungsstatus
    • +
    • internal_temperature-temperature - Interne Geräte Temperatur
    • +
    • mower-error - Aktuelle Fehler Meldung +
        +
      • Kein Fehler
      • +
      • Außerhalb des Arbeitsbereichs
      • +
      • Kein Schleifensignal
      • +
      • Falsches Schleifensignal
      • +
      • Problem Schleifensensor, vorne
      • +
      • Problem Schleifensensor, hinten
      • +
      • Eingeschlossen
      • +
      • Steht auf dem Kopf
      • +
      • Niedriger Batteriestand
      • +
      • Batterie ist leer
      • +
      • Kein Antrieb
      • +
      • Angehoben
      • +
      • Eingeklemmt in Ladestation
      • +
      • Ladestation blockiert
      • +
      • Problem Stoßsensor hinten
      • +
      • Problem Stoßsensor vorne
      • +
      • Radmotor rechts blockiert
      • +
      • Radmotor links blockiert
      • +
      • Problem Antrieb, rechts
      • +
      • Problem Antrieb, links
      • +
      • Schneidsystem blockiert
      • +
      • Fehlerhafte Verbindung
      • +
      • Standardeinstellungen
      • +
      • Elektronisches Problem
      • +
      • Problem Ladesystem
      • +
      • Kippsensorproblem
      • +
      • Rechter Radmotor überlastet
      • +
      • Linker Radmotor überlastet
      • +
      • Ladestrom zu hoch
      • +
      • Vorübergehendes Problem
      • +
      • SK 1 nicht gefunden
      • +
      • SK 2 nicht gefunden
      • +
      • SK 3 nicht gefunden
      • +
      • Problem die Ladestation zu finden
      • +
      • Kalibration des Suchkabels beendet
      • +
      • Kalibration des Suchkabels fehlgeschlagen
      • +
      • Kurzzeitiges Batterieproblem
      • +
      • Batterieproblem
      • +
      • Alarm! Mäher ausgeschalten
      • +
      • Alarm! Mäher gestoppt
      • +
      • Alarm! Mäher angehoben
      • +
      • Alarm! Mäher gekippt
      • +
      • Verbindung geändert
      • +
      • Verbindung nicht geändert
      • +
      • COM board nicht verfügbar
      • +
      • Rutscht
      • +
      +
    • +
    • mower-manual_operation - Manueller Betrieb (0/1) oder mit neuerer Firmware (false/true)
    • +
    • mower-override_end_time - Zeitpunkt wann der manuelle Betrieb beendet ist
    • +
    • mower-source_for_next_start - Grund für den nächsten Start +
        +
      • Kein Grund
      • +
      • Mäher wurde geladen
      • +
      • SensorControl erreicht
      • +
      • Wochentimer erreicht
      • +
      • Stoppuhr Timer
      • +
      • Undefiniert
      • +
      +
    • +
    • mower-status - Mäher Status (siehe state)
    • +
    • mower-timestamp_next_start - Zeitpunkt des nächsten geplanten Starts
    • +
    • radio-connection_status - Status der Funkverbindung
    • +
    • radio-quality - Indikator für die Funkverbindung in Prozent
    • +
    • radio-state - radio state (schlecht/schwach/gut/Undefiniert)
    • +
    • state - Staus des Mähers +
        +
      • Pausiert
      • +
      • Mähen
      • +
      • Suche Ladestation
      • +
      • Lädt
      • +
      • Mähen
      • +
      • Wird aktualisiert ...
      • +
      • Wird eingeschaltet ...
      • +
      • Geparkt nach Zeitplan
      • +
      • Geparkt
      • +
      • Der Mäher ist ausgeschaltet
      • +
      • Deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich
      • +
      • Unbekannter Status
      • +
      • Fehler
      • +
      • Neustart ...
      • +
      • Deaktiviert. Manueller Start erforderlich
      • +
      • Manuelles Mähen
      • +
      • Geparkt durch SensorControl
      • +
      • Abgeschlossen
      • +
      +
    • +
    +

    + + Attribute +
      +
    • readingValueLanguage - Änderung der Sprache der Readings (de,en/wenn nichts gesetzt ist, dann Englisch es sei denn deutsch ist als globale Sprache gesetzt)
    • +
    • model -
    • +
    + + set +
      +
    • winter_mode - aufwäcken (awake)| winterschlaf (hibernate)
    • +
    +
      +

      mäher

      +
    • parkUntilFurtherNotice - Parken des Mähers unter Umgehung des Zeitplans
    • +
    • parkUntilNextTimer - Parken bis zum nächsten Zeitplan
    • +
    • startOverrideTimer - Manuelles mähen (in Minuten, 60 = 1h, 1440 = 24h, 4320 = 72h)
    • +
    • startResumeSchedule - Weiterführung des Zeitplans
    • +
    • startpoint enable|disable 1|2|3 - Aktiviert oder deaktiviert einen vordefinierten Startbereich
    • +
        +
      • set NAME startpoint enable 1
      • +
      • set NAME startpoint disable 3 enable 1
      • +
      +

      irrigation control

      +
    • resumeScheduleValve - Startet Bew&aauml;sserung am Ventil n nach Zeitplan
    • +
    • stopScheduleValve - Setzt Bew&aauml;sserung am Ventil n aus (Default: 2038-01-18T00:00:00.000Z) | Optionaler Parameter Stunden (Jetzt + Stunden)
    • +
    • closeAllValves - Stopt Bew&aauml;sserung an allen Ventilen
    • +

      water control

      +
    • manualButtonTime - setzt die Dauer für den manuellen Knopf (in Minuten) 0 Schaltet den Knopf aus
    • +
    • stopSchedule - Halte Zeitplan an für x Stunden - (Default: 2038-01-18T00:00:00.000Z)
    • +
    • resumeSchedule - Weiterführung des Zeitplans
    • +
    +
+ +=end html_DE + +=for :application/json;q=META.json 74_GardenaSmartDevice.pm +{ + "abstract": "Modul to control GardenaSmart Devices", + "x_lang": { + "de": { + "abstract": "Modul zur Steuerung von Gardena Smart Geräten" + } + }, + "keywords": [ + "fhem-mod-device", + "fhem-core", + "Garden", + "Gardena", + "Smart" + ], + "release_status": "stable", + "license": "GPL_2", + "version": "v2.5.5", + "author": [ + "Marko Oldenburg " + ], + "x_fhem_maintainer": [ + "CoolTux" + ], + "x_fhem_maintainer_github": [ + "LeonGaultier" + ], + "prereqs": { + "runtime": { + "requires": { + "FHEM": 5.00918799, + "perl": 5.016, + "Meta": 0, + "JSON": 0, + "Time::Local": 0 + }, + "recommends": { + }, + "suggests": { + } + } + } +} +=end :application/json;q=META.json + +=cut diff --git a/controls_GardenaSmartDevice.txt b/controls_GardenaSmartDevice.txt index af773db..280e02b 100644 --- a/controls_GardenaSmartDevice.txt +++ b/controls_GardenaSmartDevice.txt @@ -1,2 +1,2 @@ UPD 2022-07-12_20:03:00 49634 FHEM/73_GardenaSmartBridge.pm -UPD 2022-07-19_20:20:03 71804 FHEM/74_GardenaSmartDevice.pm +UPD 2022-07-20_12:15:45 73554 FHEM/74_GardenaSmartDevice.pm