diff --git a/fhem/CHANGED b/fhem/CHANGED index 9a3422170..7e414ca93 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - new: 73_AutoShuttersControl: Module for the automated control of + shutters based on certain properties - feature: 93_DbRep: V8.8.0, attribute valueFilter applied to more functions, new attribute 'fastStart'. (see 'get ... versionNotes') - feature: 00_10_MQTT2_CLIENT added (Forum #92888) diff --git a/fhem/FHEM/73_AutoShuttersControl.pm b/fhem/FHEM/73_AutoShuttersControl.pm new file mode 100644 index 000000000..7b4a39471 --- /dev/null +++ b/fhem/FHEM/73_AutoShuttersControl.pm @@ -0,0 +1,3356 @@ +############################################################################### +# +# Developed with Kate +# +# (c) 2018 Copyright: Marko Oldenburg (leongaultier at gmail dot com) +# All rights reserved +# +# Special thanks goes to: +# - Bernd (Cluni) this module is based on the logic of his script "Rollladensteuerung für HM/ROLLO inkl. Abschattung und Komfortfunktionen in Perl" (https://forum.fhem.de/index.php/topic,73964.0.html) +# - Beta-User for many tests and ideas +# +# +# 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$ +# +############################################################################### + +### Notizen +# - Feststellen ob ein Rolladen fährt oder nicht + +package main; + +use strict; +use warnings; + +my $version = "0.2.0"; + +sub AutoShuttersControl_Initialize($) { + my ($hash) = @_; + +## Da ich mit package arbeite müssen in die Initialize für die jeweiligen hash Fn Funktionen der Funktionsname + # und davor mit :: getrennt der eigentliche package Name des Modules + $hash->{SetFn} = "AutoShuttersControl::Set"; + $hash->{GetFn} = "AutoShuttersControl::Get"; + $hash->{DefFn} = "AutoShuttersControl::Define"; + $hash->{NotifyFn} = "AutoShuttersControl::Notify"; + $hash->{UndefFn} = "AutoShuttersControl::Undef"; + $hash->{AttrFn} = "AutoShuttersControl::Attr"; + $hash->{AttrList} = + "disable:0,1 " + . "disabledForIntervals " + . "ASC_guestPresence:on,off " + . "ASC_temperatureSensor " + . "ASC_temperatureReading " + . "ASC_brightnessMinVal " + . "ASC_brightnessMaxVal " + . "ASC_autoShuttersControlMorning:on,off " + . "ASC_autoShuttersControlEvening:on,off " + . "ASC_autoShuttersControl_Shading:on,off " + . "ASC_autoShuttersControlComfort:on,off " + . "ASC_sunPosDevice " + . "ASC_sunPosReading " + . "ASC_sunElevationDevice " + . "ASC_sunElevationReading " + . "ASC_residentsDevice " + . "ASC_residentsDeviceReading " + . "ASC_rainSensorDevice " + . "ASC_rainSensorReading " + . "ASC_rainSensorShuttersClosedPos:0,10,20,30,40,50,60,70,80,90,100 " + . "ASC_autoAstroModeMorning:REAL,CIVIL,NAUTIC,ASTRONOMIC,HORIZON " + . "ASC_autoAstroModeMorningHorizon:-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9 " + . "ASC_autoAstroModeEvening:REAL,CIVIL,NAUTIC,ASTRONOMIC,HORIZON " + . "ASC_autoAstroModeEveningHorizon:-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9 " + . "ASC_freezeTemp:-5,-4,-3,-2,-1,0,1,2,3,4,5 " + . "ASC_timeUpHolidayDevice " + . "ASC_timeUpHolidayReading " + . "ASC_shuttersDriveOffset " + . "ASC_twilightDevice " + . $readingFnAttributes; + $hash->{NotifyOrderPrefix} = "51-"; # Order Nummer für NotifyFn + +## Ist nur damit sich bei einem reload auch die Versionsnummer erneuert. + foreach my $d ( sort keys %{ $modules{AutoShuttersControl}{defptr} } ) { + my $hash = $modules{AutoShuttersControl}{defptr}{$d}; + $hash->{VERSION} = $version; + } +} + +## unserer packagename +package AutoShuttersControl; +no warnings "experimental::declared_refs"; +use feature "declared_refs"; +use strict; +use warnings; +use POSIX; + +use GPUtils qw(:all) + ; # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt +use Data::Dumper; #only for Debugging +use Date::Parse; + +my $missingModul = ""; +eval "use JSON qw(decode_json encode_json);1" or $missingModul .= "JSON "; + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw(devspec2array + readingsSingleUpdate + readingsBulkUpdate + readingsBulkUpdateIfChanged + readingsBeginUpdate + readingsEndUpdate + defs + modules + Log3 + CommandAttr + attr + CommandDeleteAttr + CommandDeleteReading + CommandSet + AttrVal + ReadingsVal + Value + IsDisabled + deviceEvents + init_done + addToDevAttrList + addToAttrList + delFromDevAttrList + delFromAttrList + gettimeofday + sunset_abs + sunrise_abs + InternalTimer + RemoveInternalTimer + computeAlignTime + ReplaceEventMap) + ); +} + +## Die Attributsliste welche an die Rolläden verteilt wird. Zusammen mit Default Werten +my %userAttrList = ( + 'ASC_Mode_Up:absent,always,off,home' => 'always', + 'ASC_Mode_Down:absent,always,off,home' => 'always', + 'ASC_Up:time,astro,brightness' => 'astro', + 'ASC_Down:time,astro,brightness' => 'astro', + 'ASC_AutoAstroModeMorning:REAL,CIVIL,NAUTIC,ASTRONOMIC,HORIZON' => 'none', +'ASC_AutoAstroModeMorningHorizon:-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9' + => 'none', + 'ASC_AutoAstroModeEvening:REAL,CIVIL,NAUTIC,ASTRONOMIC,HORIZON' => 'none', +'ASC_AutoAstroModeEveningHorizon:-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9' + => 'none', + 'ASC_Open_Pos:0,10,20,30,40,50,60,70,80,90,100' => [ '', 0, 100 ], + 'ASC_Closed_Pos:0,10,20,30,40,50,60,70,80,90,100' => [ '', 100, 0 ], + 'ASC_Pos_Reading' => [ '', 'position', 'pct' ], + 'ASC_Time_Up_Early' => '04:30', + 'ASC_Time_Up_Late' => '09:00', + 'ASC_Time_Up_WE_Holiday' => '08:30', + 'ASC_Time_Down_Early' => '15:30', + 'ASC_Time_Down_Late' => '22:30', + 'ASC_WindowRec' => 'none', + 'ASC_Ventilate_Window_Open:on,off' => 'on', + 'ASC_lock-out:soft,hard' => 'soft', + 'ASC_lock-outCmd:inhibit,blocked' => 'none', + +# 'ASC_Shading_Direction' => 178, +# 'ASC_Shading_Pos:10,20,30,40,50,60,70,80,90,100' => 30, +# 'ASC_Shading:on,off,delayed,present,absent' => 'off', +# 'ASC_Shading_Pos_after_Shading:-1,0,10,20,30,40,50,60,70,80,90,100' => -1, +# 'ASC_Shading_Angle_Left:0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90' +# => 85, +# 'ASC_Shading_Angle_Right:0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90' +# => 85, + 'ASC_Shading_Brightness_Sensor' => 'none', + 'ASC_Shading_Brightness_Reading' => 'brightness', + + # 'ASC_Shading_StateChange_Sunny' => '6000', + # 'ASC_Shading_StateChange_Cloudy' => '4000', + # 'ASC_Shading_WaitingPeriod' => 20, + # 'ASC_Shading_Min_Elevation' => 'none', + # 'ASC_Shading_Min_OutsideTemperature' => 18, + # 'ASC_Shading_BlockingTime_After_Manual' => 20, + # 'ASC_Shading_BlockingTime_Twilight' => 45, + # 'ASC_Shading_Fast_Open:on,off' => 'none', + # 'ASC_Shading_Fast_Close:on,off' => 'none', + 'ASC_Drive_Offset' => -1, + 'ASC_WindowRec_subType:twostate,threestate' => 'twostate', + 'ASC_ShuttersPlace:window,terrace' => 'window', + 'ASC_Ventilate_Pos:10,20,30,40,50,60,70,80,90,100' => [ '', 70, 30 ], + 'ASC_Pos_after_ComfortOpen:0,10,20,30,40,50,60,70,80,90,100' => + [ '', 20, 80 ], + 'ASC_GuestRoom:on,off' => 'none', + 'ASC_Antifreeze:off,on' => 'off', + 'ASC_Partymode:on,off' => 'off', + 'ASC_Roommate_Device' => 'none', + 'ASC_Roommate_Reading' => 'state', + 'ASC_Self_Defense_Exclude:on,off' => 'off', + 'ASC_BrightnessMinVal' => -1, + 'ASC_BrightnessMaxVal' => -1, +); + +my %posSetCmds = ( + ZWave => 'dim', + Siro => 'position', + CUL_HM => 'pct', + ROLLO => 'pct', + SOMFY => 'position', + tahoma => 'dim', + KLF200Node => 'pct', + DUOFERN => 'position', +); + +my $shutters = new ASC_Shutters(); +my $ascDev = new ASC_Dev(); + +sub Define($$) { + my ( $hash, $def ) = @_; + my @a = split( "[ \t][ \t]*", $def ); + + return "only one AutoShuttersControl instance allowed" + if ( devspec2array('TYPE=AutoShuttersControl') > 1 ) + ; # es wird geprüft ob bereits eine Instanz unseres Modules existiert,wenn ja wird abgebrochen + return "too few parameters: define ShuttersControl" if ( @a != 2 ); + return +"Cannot define ShuttersControl device. Perl modul ${missingModul}is missing." + if ($missingModul) + ; # Abbruch wenn benötigte Hilfsmodule nicht vorhanden sind / vorerst unwichtig + + my $name = $a[0]; + + $hash->{VERSION} = $version; + $hash->{MID} = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' + ; # eine Ein Eindeutige ID für interne FHEM Belange / nicht weiter wichtig + $hash->{NOTIFYDEV} = "global," + . $name; # Liste aller Devices auf deren Events gehört werden sollen + $ascDev->setName($name); + + readingsSingleUpdate( + $hash, + "state", +"please set attribute ASC with value 1 or 2 in all auto controlled shutter devices and then execute 'set DEVICENAME scanForShutters'", + 1 + ); + CommandAttr( undef, $name . ' room ASC' ) + if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); + CommandAttr( undef, $name . ' icon fts_shutter_automatic' ) + if ( AttrVal( $name, 'icon', 'none' ) eq 'none' ); + CommandAttr( undef, $name . ' ASC_autoAstroModeEvening REAL' ) + if ( $ascDev->getAutoAstroModeEvening eq 'none' ); + CommandAttr( undef, $name . ' ASC_autoAstroModeMorning REAL' ) + if ( $ascDev->getAutoAstroModeMorning eq 'none' ); + CommandAttr( undef, $name . ' ASC_autoShuttersControlMorning on' ) + if ( $ascDev->getAutoShuttersControlMorning eq 'none' ); + CommandAttr( undef, $name . ' ASC_autoShuttersControlEvening on' ) + if ( $ascDev->getAutoShuttersControlEvening eq 'none' ); + CommandAttr( undef, $name . ' ASC_temperatureReading temperature' ) + if ( $ascDev->getTempReading eq 'none' ); + CommandAttr( undef, $name . ' ASC_freezeTemp 3' ) + if ( $ascDev->getAntifreezeTemp eq 'none' ); + CommandAttr( undef, + $name + . ' devStateIcon selfeDefense.terrace:fts_door_tilt created.new.drive.timer:clock .*asleep:scene_sleeping roommate.(awoken|home):user_available residents.(home|awoken):status_available manual:fts_shutter_manual selfeDefense.active:status_locked selfeDefense inactive:status_open day.open:scene_day night close:scene_night' + ) if ( AttrVal( $name, 'devStateIcon', 'none' ) eq 'none' ); + + addToAttrList('ASC:0,1,2'); + + Log3( $name, 3, "AutoShuttersControl ($name) - defined" ); + + $modules{AutoShuttersControl}{defptr}{ $hash->{MID} } = $hash; + + return undef; +} + +sub Undef($$) { + my ( $hash, $arg ) = @_; + + my $name = $hash->{NAME}; + + UserAttributs_Readings_ForShutters( $hash, 'del' ) + ; # es sollen alle Attribute und Readings in den Rolläden Devices gelöscht werden welche vom Modul angelegt wurden + delFromAttrList('ASC:0,1,2'); + + delete( $modules{AutoShuttersControl}{defptr}{ $hash->{MID} } ); + + Log3( $name, 3, "AutoShuttersControl ($name) - delete device $name" ); + return undef; +} + +sub Attr(@) { + my ( $cmd, $name, $attrName, $attrVal ) = @_; + my $hash = $defs{$name}; + + if ( $attrName eq "disable" ) { + if ( $cmd eq "set" and $attrVal eq "1" ) { + Log3( $name, 3, "AutoShuttersControl ($name) - disabled" ); + } + elsif ( $cmd eq "del" ) { + Log3( $name, 3, "AutoShuttersControl ($name) - enabled" ); + } + } + elsif ( $attrName eq "disabledForIntervals" ) { + if ( $cmd eq "set" ) { + return +"check disabledForIntervals Syntax HH:MM-HH:MM or 'HH:MM-HH:MM HH:MM-HH:MM ...'" + unless ( $attrVal =~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/ ); + Log3( $name, 3, + "AutoShuttersControl ($name) - disabledForIntervals" ); + + #readingsSingleUpdate ($hash,"state","disabled",1); + } + elsif ( $cmd eq "del" ) { + Log3( $name, 3, "AutoShuttersControl ($name) - enabled" ); + + #readingsSingleUpdate ($hash,"state","active",1); + } + } + return undef; +} + +sub Notify($$) { + my ( $hash, $dev ) = @_; + my $name = $hash->{NAME}; + return if ( IsDisabled($name) ); + + my $devname = $dev->{NAME}; + my $devtype = $dev->{TYPE}; + my $events = deviceEvents( $dev, 1 ); + return if ( !$events ); + + Log3( $name, 5, + "AutoShuttersControl ($name) - Devname: " + . $devname + . " Name: " + . $name + . " Notify: " + . Dumper $events); # mit Dumper + + if ( + ( + grep /^DEFINED.$name$/, + @{$events} and $devname eq 'global' and $init_done + ) + or ( + grep /^INITIALIZED$/, + @{$events} or grep /^REREADCFG$/, + @{$events} or grep /^MODIFIED.$name$/, + @{$events} + ) + and $devname eq 'global' + ) + { + readingsSingleUpdate( $hash, 'partyMode', 'off', 0 ) + if ( $ascDev->getPartyMode eq 'none' ); + readingsSingleUpdate( $hash, 'lockOut', 'off', 0 ) + if ( $ascDev->getLockOut eq 'none' ); + readingsSingleUpdate( $hash, 'sunriseTimeWeHoliday', 'off', 0 ) + if ( $ascDev->getSunriseTimeWeHoliday eq 'none' ); + readingsSingleUpdate( $hash, 'selfDefense', 'off', 0 ) + if ( $ascDev->getSelfDefense eq 'none' ); + CommandDeleteReading( undef, $name . ' selfDefence' ) + if ( ReadingsVal( $name, 'selfDefence', 'none' ) ne 'none' ) + ; # temporär kann später entfernt werden. + CommandAttr( undef, $name . ' ASC_twilightDevice ' . ( devspec2array('TYPE=(Astro|Twilight)'))[0] ) if ( AttrVal($name,'ASC_twilightDevice','none') eq 'none' ); + +# Ist der Event ein globaler und passt zum Rest der Abfrage oben wird nach neuen Rolläden Devices gescannt und eine Liste im Rolladenmodul sortiert nach Raum generiert + ShuttersDeviceScan($hash) + unless ( ReadingsVal( $name, 'userAttrList', 'none' ) eq 'none' ); + } + return + unless ( ref( $hash->{helper}{shuttersList} ) eq 'ARRAY' + and scalar( @{ $hash->{helper}{shuttersList} } ) > 0 ); + + my $posReading = $shutters->getPosCmd; + + if ( $devname eq $name ) { + if ( grep /^userAttrList:.rolled.out$/, @{$events} ) { + unless ( scalar( @{ $hash->{helper}{shuttersList} } ) == 0 ) { + WriteReadingsShuttersList($hash); + UserAttributs_Readings_ForShutters( $hash, 'add' ); + InternalTimer( gettimeofday() + 3, + 'AutoShuttersControl::RenewSunRiseSetShuttersTimer', + $hash ); + } + } + elsif ( grep /^partyMode:.off$/, @{$events} ) { + PartyModeEventProcessing($hash); + } + elsif ( grep /^sunriseTimeWeHoliday:.(on|off)$/, @{$events} ) { + RenewSunRiseSetShuttersTimer($hash); + } + } + elsif ( $devname eq "global" ) + { # Kommt ein globales Event und beinhaltet folgende Syntax wird die Funktion zur Verarbeitung aufgerufen + if ( + grep +/^(ATTR|DELETEATTR)\s(.*ASC_Roommate_Device|.*ASC_WindowRec|.*ASC_residentsDevice|.*ASC_rainSensorDevice|.*ASC_Shading_Brightness_Sensor)(\s.*|$)/, + @{$events} + ) + { + GeneralEventProcessing( $hash, undef, join( ' ', @{$events} ) ); + } + elsif ( + grep +/^(ATTR|DELETEATTR)\s(.*ASC_Time_Up_WE_Holiday|.*ASC_Up|.*ASC_Down|.*ASC_AutoAstroModeMorning|.*ASC_AutoAstroModeMorningHorizon|.*ASC_AutoAstroModeEvening|.*ASC_AutoAstroModeEveningHorizon|.*ASC_Time_Up_Early|.*ASC_Time_Up_Late|.*ASC_Time_Down_Early|.*ASC_Time_Down_Late|.*ASC_autoAstroModeMorning|.*ASC_autoAstroModeMorningHorizon|.*ASC_autoAstroModeEvening|.*ASC_autoAstroModeEveningHorizon)(\s.*|$)/, + @{$events} + ) + { + GeneralEventProcessing( $hash, undef, join( ' ', @{$events} ) ); + } + } + elsif ( grep /^($posReading):\s\d+$/, @{$events} ) { + ShuttersEventProcessing( $hash, $devname, join( ' ', @{$events} ) ); + } + else { + GeneralEventProcessing( $hash, $devname, join( ' ', @{$events} ) ) + ; # bei allen anderen Events wird die entsprechende Funktion zur Verarbeitung aufgerufen + } + return; +} + +sub GeneralEventProcessing($$$) { + my ( $hash, $devname, $events ) = @_; + my $name = $hash->{NAME}; + + if ( defined($devname) and ($devname) ) + { # es wird lediglich der Devicename der Funktion mitgegeben wenn es sich nicht um global handelt daher hier die Unterschiedung + while ( my ( $device, $deviceAttr ) = + each %{ $hash->{monitoredDevs}{$devname} } ) + { + WindowRecEventProcessing( $hash, $device, $events ) + if ( $deviceAttr eq 'ASC_WindowRec' ) + ; # ist es ein Fensterdevice wird die Funktion gestartet + RoommateEventProcessing( $hash, $device, $events ) + if ( $deviceAttr eq 'ASC_Roommate_Device' ) + ; # ist es ein Bewohner Device wird diese Funktion gestartet + ResidentsEventProcessing( $hash, $device, $events ) + if ( $deviceAttr eq 'ASC_residentsDevice' ); + RainEventProcessing( $hash, $device, $events ) + if ( $deviceAttr eq 'ASC_rainSensorDevice' ); + + $shutters->setShuttersDev($device) + if ( $deviceAttr eq 'ASC_Shading_Brightness_Sensor' ); + BrightnessEventProcessing( $hash, $device, $events ) + if ( + $deviceAttr eq 'ASC_Shading_Brightness_Sensor' + and ( $shutters->getDown eq 'brightness' + or $shutters->getUp eq 'brightness' ) + ); + } + } + else { # alles was kein Devicenamen mit übergeben hat landet hier + if ( $events =~ +m#^ATTR\s(.*)\s(ASC_Roommate_Device|ASC_WindowRec|ASC_residentsDevice|ASC_rainSensorDevice|ASC_Shading_Brightness_Sensor)\s(.*)$# + ) + { # wurde den Attributen unserer Rolläden ein Wert zugewiesen ? + AddNotifyDev( $hash, $3, $1, $2 ) if ( $3 ne 'none' ); + Log3( $name, 4, + "AutoShuttersControl ($name) - EventProcessing: ATTR" ); + } + elsif ( $events =~ +m#^DELETEATTR\s(.*)\s(ASC_Roommate_Device|ASC_WindowRec|ASC_residentsDevice|ASC_rainSensorDevice|ASC_Shading_Brightness_Sensor)$# + ) + { # wurde das Attribut unserer Rolläden gelöscht ? + Log3( $name, 4, + "AutoShuttersControl ($name) - EventProcessing: DELETEATTR" ); + DeleteNotifyDev( $hash, $1, $2 ); + } + elsif ( $events =~ +m#^ATTR\s(.*)\s(ASC_Time_Up_WE_Holiday|ASC_Up|ASC_Down|ASC_AutoAstroModeMorning|ASC_AutoAstroModeMorningHorizon|ASC_AutoAstroModeEvening|ASC_AutoAstroModeEveningHorizon|ASC_Time_Up_Early|ASC_Time_Up_Late|ASC_Time_Down_Early|ASC_Time_Down_Late)\s(.*)$# + ) + { + CreateSunRiseSetShuttersTimer( $hash, $1 ) + if ( + $2 ne 'ASC_Time_Up_WE_Holiday' + or ( $2 eq 'ASC_Time_Up_WE_Holiday' + and $ascDev->getSunriseTimeWeHoliday eq 'on' ) + ); + } + elsif ( $events =~ +m#^ATTR\s(.*)\s(ASC_autoAstroModeMorning|ASC_autoAstroModeMorningHorizon|ASC_autoAstroModeEvening|ASC_autoAstroModeEveningHorizon)\s(.*)$# + ) + { + RenewSunRiseSetShuttersTimer($hash); + } + } +} + +sub Set($$@) { + my ( $hash, $name, @aa ) = @_; + my ( $cmd, @args ) = @aa; + + if ( lc $cmd eq 'renewsetsunrisesunsettimer' ) { + return "usage: $cmd" if ( @args != 0 ); + RenewSunRiseSetShuttersTimer($hash); + } + elsif ( lc $cmd eq 'scanforshutters' ) { + return "usage: $cmd" if ( @args != 0 ); + ShuttersDeviceScan($hash); + } + elsif ( lc $cmd eq 'createnewnotifydev' ) { + return "usage: $cmd" if ( @args != 0 ); + CreateNewNotifyDev($hash); + } + elsif ( lc $cmd eq 'partymode' ) { + return "usage: $cmd" if ( @args > 1 ); + readingsSingleUpdate( $hash, $cmd, join( ' ', @args ), 1 ); + } + elsif ( lc $cmd eq 'lockout' ) { + return "usage: $cmd" if ( @args > 1 ); + readingsSingleUpdate( $hash, $cmd, join( ' ', @args ), 1 ); + SetHardewareBlockForShutters( $hash, join( ' ', @args ) ); + } + elsif ( lc $cmd eq 'sunrisetimeweholiday' ) { + return "usage: $cmd" if ( @args > 1 ); + readingsSingleUpdate( $hash, $cmd, join( ' ', @args ), 1 ); + } + elsif ( lc $cmd eq 'selfdefense' ) { + return "usage: $cmd" if ( @args > 1 ); + readingsSingleUpdate( $hash, $cmd, join( ' ', @args ), 1 ); + } + elsif ( lc $cmd eq 'wiggle' ) { + return "usage: $cmd" if ( @args > 1 ); + + ( $args[0] eq 'all' ? wiggleAll($hash) : wiggle( $hash, $args[0] ) ); + } + else { + my $list = "scanForShutters:noArg"; + $list .= +" renewSetSunriseSunsetTimer:noArg partyMode:on,off lockOut:on,off sunriseTimeWeHoliday:on,off selfDefense:on,off wiggle:all," + . join( ',', @{ $hash->{helper}{shuttersList} } ) + if ( ReadingsVal( $name, 'userAttrList', 'none' ) eq 'rolled out' ); + $list .= " createNewNotifyDev:noArg" + if ( ReadingsVal( $name, 'userAttrList', 'none' ) eq 'rolled out' + and AttrVal( $name, 'verbose', 3 ) > 3 ); + + return "Unknown argument $cmd,choose one of $list"; + } + return undef; +} + +sub Get($$@) { + my ( $hash, $name, @aa ) = @_; + + my ( $cmd, @args ) = @aa; + + if ( lc $cmd eq 'showshuttersinformations' ) { + return "usage: $cmd" if ( @args != 0 ); + my $ret = GetShuttersInformation($hash); + return $ret; + } + elsif ( lc $cmd eq 'shownotifydevsinformations' ) { + return "usage: $cmd" if ( @args != 0 ); + my $ret = GetMonitoredDevs($hash); + return $ret; + } + else { + my $list = ""; + $list .= " showShuttersInformations:noArg" + if ( ReadingsVal( $name, 'userAttrList', 'none' ) eq 'rolled out' ); + $list .= " showNotifyDevsInformations:noArg" + if ( ReadingsVal( $name, 'userAttrList', 'none' ) eq 'rolled out' + and AttrVal( $name, 'verbose', 3 ) > 3 ); + + return "Unknown argument $cmd,choose one of $list"; + } +} + +sub ShuttersDeviceScan($) { + my $hash = shift; + my $name = $hash->{NAME}; + + delete $hash->{helper}{shuttersList}; + + my @list; + @list = devspec2array('ASC=[1-2]'); + + CommandDeleteReading( undef, $name . ' .*_nextAstroTimeEvent' ); + + unless ( scalar(@list) > 0 ) { + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, 'userAttrList', 'none' ); + readingsBulkUpdate( $hash, 'state', 'no shutters found' ); + readingsEndUpdate( $hash, 1 ); + return; + } + my $shuttersList = ''; + foreach (@list) { + push( @{ $hash->{helper}{shuttersList} }, $_ ) + ; ## einem Hash wird ein Array zugewiesen welches die Liste der erkannten Rollos beinhaltet + + delFromDevAttrList( $_, 'ASC_Up:time,astro' ) + if ( + AttrVal( $_, 'userattr', 'none' ) =~ /\sASC_Up:time,astro\sASC_/ ) + ; # temporär muss später gelöscht werden ab Version 0.1.80 + delFromDevAttrList( $_, 'ASC_Down:time,astro' ) + if ( + AttrVal( $_, 'userattr', 'none' ) =~ /\sASC_Down:time,astro\sASC_/ ) + ; # temporär muss später gelöscht werden ab Version 0.1.80 + delFromDevAttrList( $_, 'ASC_Mode_Up:absent,always,off' ) + if ( AttrVal( $_, 'userattr', 'none' ) =~ + /\sASC_Mode_Up:absent,always,off\sASC_/ ) + ; # temporär muss später gelöscht werden ab Version 0.1.81 + delFromDevAttrList( $_, 'ASC_Mode_Down:absent,always,off' ) + if ( AttrVal( $_, 'userattr', 'none' ) =~ + /\sASC_Mode_Down:absent,always,off\sASC_/ ) + ; # temporär muss später gelöscht werden ab Version 0.1.81 + delFromDevAttrList( $_, 'ASC_Self_Defence_Exclude:on,off' ) + ; # temporär muss später gelöscht werden ab Version 0.1.80 + delFromDevAttrList( $_, 'ASC_Offset_Minutes_Morning' ) + ; # temporär muss später gelöscht werden ab Version 0.1.81 + delFromDevAttrList( $_, 'ASC_Offset_Minutes_Evening' ) + ; # temporär muss später gelöscht werden ab Version 0.1.81 + + delFromDevAttrList( $_, 'ASC_Direction' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, + 'ASC_Shading_Pos:10,20,30,40,50,60,70,80,90,100' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Rand_Minutes' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading:on,off,delayed,present,absent' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, + 'ASC_Shading_Pos_after_Shading:-1,0,10,20,30,40,50,60,70,80,90,100' + ); # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, +'ASC_Shading_Angle_Left:0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90' + ); # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, +'ASC_Shading_Angle_Right:0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90' + ); # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_StateChange_Sunny' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_StateChange_Cloudy' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_WaitingPeriod' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_Min_Elevation' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_Min_OutsideTemperature' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_BlockingTime_After_Manual' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_BlockingTime_Twilight' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_Fast_Open:on,off' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Shading_Fast_Close:on,off' ) + ; # temporär muss später gelöscht werden ab Version 0.1.89 + delFromDevAttrList( $_, 'ASC_Pos_Cmd' ) + ; # temporär muss später gelöscht werden ab Version 0.1.93 + + CommandDeleteReading( undef, + $_ . ' .AutoShuttersControl_InternalTimerFuncHash' ) + ; # temporär muss später gelöscht werden ab Version 0.1.81 + CommandDeleteReading( undef, + $_ . ' .AutoShuttersControl_LastPosition' ) + ; # temporär muss später gelöscht werden ab Version 0.1.81 + CommandDeleteReading( undef, $_ . ' .AutoShuttersControl_DelayCmd' ) + ; # temporär muss später gelöscht werden ab Version 0.1.82 + + $shuttersList = $shuttersList . ',' . $_; + $shutters->setShuttersDev($_); + $shutters->setLastManPos( $shutters->getStatus ); + $shutters->setLastPos( $shutters->getStatus ); + $shutters->setDelayCmd('none'); + $shutters->setNoOffset(0); + $shutters->setPosSetCmd( $posSetCmds{ $hash->{TYPE} } ); + } + $hash->{NOTIFYDEV} = $hash->{NOTIFYDEV} . $shuttersList; + + if ( $ascDev->getMonitoredDevs ne 'none' ) { + $hash->{monitoredDevs} = + eval { decode_json( $ascDev->getMonitoredDevs ) }; + my $notifyDevString = $hash->{NOTIFYDEV}; + while ( each %{ $hash->{monitoredDevs} } ) { + $notifyDevString .= ',' . $_; + } + $hash->{NOTIFYDEV} = $notifyDevString; + } + readingsSingleUpdate( $hash, 'userAttrList', 'rolled out', 1 ); +} + +## Die Funktion schreibt in das Moduldevice Readings welche Rolläden in welchen Räumen erfasst wurden. +sub WriteReadingsShuttersList($) { + my $hash = shift; + my $name = $hash->{NAME}; + + CommandDeleteReading( undef, $name . ' room_.*' ); + + readingsBeginUpdate($hash); + foreach ( @{ $hash->{helper}{shuttersList} } ) { + readingsBulkUpdate( + $hash, + 'room_' . makeReadingName( AttrVal( $_, 'room', 'unsorted' ) ), + ReadingsVal( + $name, + 'room_' . makeReadingName( AttrVal( $_, 'room', 'unsorted' ) ), + '' + ) + . ',' + . $_ + ) + if ( + ReadingsVal( + $name, + 'room_' . makeReadingName( AttrVal( $_, 'room', 'unsorted' ) ), + 'none' + ) ne 'none' + ); + + readingsBulkUpdate( $hash, + 'room_' . makeReadingName( AttrVal( $_, 'room', 'unsorted' ) ), $_ ) + if ( + ReadingsVal( + $name, + 'room_' . makeReadingName( AttrVal( $_, 'room', 'unsorted' ) ), + 'none' + ) eq 'none' + ); + } + readingsBulkUpdate( $hash, 'state', 'active' ); + readingsEndUpdate( $hash, 0 ); +} + +sub UserAttributs_Readings_ForShutters($$) { + my ( $hash, $cmd ) = @_; + my $name = $hash->{NAME}; + + while ( my ( $attrib, $attribValue ) = each %{userAttrList} ) { + foreach ( @{ $hash->{helper}{shuttersList} } ) { + addToDevAttrList( $_, $attrib ) + ; ## fhem.pl bietet eine Funktion um ein userAttr Attribut zu befüllen. Wir schreiben also in den Attribut userAttr alle unsere Attribute rein. Pro Rolladen immer ein Attribut pro Durchlauf + ## Danach werden die Attribute die im userAttr stehen gesetzt und mit default Werten befüllt + if ( $cmd eq 'add' ) { + if ( ref($attribValue) ne 'ARRAY' ) { + $attr{$_}{ ( split( ':', $attrib ) )[0] } = $attribValue + if ( + not + defined( $attr{$_}{ ( split( ':', $attrib ) )[0] } ) ); + } + else { + $attr{$_}{ ( split( ':', $attrib ) )[0] } = + $attribValue->[ AttrVal( $_, 'ASC', 2 ) ] + if ( + not + defined( $attr{$_}{ ( split( ':', $attrib ) )[0] } ) ); + } + ## Oder das Attribut wird wieder gelöscht. + } + elsif ( $cmd eq 'del' ) { + $shutters->setShuttersDev($_); + + RemoveInternalTimer( $shutters->getInTimerFuncHash ); + CommandDeleteReading( undef, + $_ . ' .?(AutoShuttersControl|ASC)_.*' ); + CommandDeleteAttr( undef, $_ . ' ASC' ); + delFromDevAttrList( $_, $attrib ); + } + } + } +} + +## Fügt dem NOTIFYDEV Hash weitere Devices hinzu +sub AddNotifyDev($@) { + my ( $hash, $dev, $shuttersDev, $shuttersAttr ) = @_; + my $name = $hash->{NAME}; + + my $notifyDev = $hash->{NOTIFYDEV}; + $notifyDev = "" if ( !$notifyDev ); + my %hash; + + %hash = map { ( $_ => 1 ) } + split( ",", "$notifyDev,$dev" ); + + $hash->{NOTIFYDEV} = join( ",", sort keys %hash ); + + my @devs = split( ',', $dev ); + foreach (@devs) { + $hash->{monitoredDevs}{$_}{$shuttersDev} = $shuttersAttr; + } + + readingsSingleUpdate( $hash, '.monitoredDevs', + eval { encode_json( $hash->{monitoredDevs} ) }, 0 ); +} + +## entfernt aus dem NOTIFYDEV Hash Devices welche als Wert in Attributen steckten +sub DeleteNotifyDev($@) { + my ( $hash, $shuttersDev, $shuttersAttr ) = @_; + my $name = $hash->{NAME}; + + my $notifyDevs = + ExtractNotifyDevFromEvent( $hash, $shuttersDev, $shuttersAttr ); + + foreach my $notifyDev ( keys( %{$notifyDevs} ) ) { + Log3( $name, 4, + "AutoShuttersControl ($name) - DeleteNotifyDev - NotifyDev: " + . $_ ); + delete $hash->{monitoredDevs}{$notifyDev}{$shuttersDev}; + + if ( !keys %{ $hash->{monitoredDevs}{$notifyDev} } ) { + delete $hash->{monitoredDevs}{$notifyDev}; + my $notifyDevString = $hash->{NOTIFYDEV}; + $notifyDevString = "" if ( !$notifyDevString ); + my %hash; + %hash = map { ( $_ => 1 ) } + grep { " $notifyDev " !~ m/ $_ / } + split( ",", "$notifyDevString,$notifyDev" ); + + $hash->{NOTIFYDEV} = join( ",", sort keys %hash ); + } + } + readingsSingleUpdate( $hash, '.monitoredDevs', + eval { encode_json( $hash->{monitoredDevs} ) }, 0 ); +} + +## Sub zum steuern der Rolläden bei einem Fenster Event +sub WindowRecEventProcessing($@) { + my ( $hash, $shuttersDev, $events ) = @_; + my $name = $hash->{NAME}; + + if ( $events =~ m#state:\s(open|closed|tilted)# ) { + $shutters->setShuttersDev($shuttersDev); + $shutters->setNoOffset(1); + + my $queryShuttersPosWinRecTilted = ( + $shutters->getShuttersPosCmdValueNegate + ? $shutters->getStatus > $shutters->getVentilatePos + : $shutters->getStatus < $shutters->getVentilatePos + ); + + if ( $shutters->getDelayCmd ne 'none' ) + { # Es wird geschaut ob wärend der Fenster offen Phase ein Fahrbefehl über das Modul kam,wenn ja wird dieser aus geführt + if ( $1 eq 'closed' ) { + ShuttersCommandSet( $hash, $shuttersDev, + $shutters->getClosedPos ); + } + elsif ( + ( + $1 eq 'tilted' + or ( $1 eq 'open' and $shutters->getSubTyp eq 'twostate' ) + ) + and $shutters->getVentilateOpen eq 'on' + and $queryShuttersPosWinRecTilted + ) + { + ShuttersCommandSet( $hash, $shuttersDev, + $shutters->getVentilatePos ); + } + } + elsif ( $1 eq 'closed' + ) # wenn nicht dann wird entsprechend dem Fensterkontakt Event der Rolladen geschlossen oder zum lüften geöffnet + { + ShuttersCommandSet( $hash, $shuttersDev, $shutters->getClosedPos ) + if ( $shutters->getStatus == $shutters->getVentilatePos + or $shutters->getStatus == $shutters->getPosAfterComfortOpen ); + } + elsif ( + ( + $1 eq 'tilted' + or ( $1 eq 'open' and $shutters->getSubTyp eq 'twostate' ) + ) + and $shutters->getVentilateOpen eq 'on' + and $queryShuttersPosWinRecTilted + ) + { + ShuttersCommandSet( $hash, $shuttersDev, + $shutters->getVentilatePos ); + } + elsif ( $1 eq 'open' + and $shutters->getSubTyp eq 'threestate' + and $ascDev->getAutoShuttersControlComfort eq 'on' + and $queryShuttersPosWinRecTilted ) + { + ShuttersCommandSet( $hash, $shuttersDev, + $shutters->getPosAfterComfortOpen ); + } + } +} + +## Sub zum steuern der Rolladen bei einem Bewohner/Roommate Event +sub RoommateEventProcessing($@) { + my ( $hash, $shuttersDev, $events ) = @_; + my $name = $hash->{NAME}; + + $shutters->setShuttersDev($shuttersDev); + my $reading = $shutters->getRoommatesReading; + + if ( $events =~ m#$reading:\s(absent|gotosleep|asleep|awoken|home)# ) { + Log3( $name, 4, +"AutoShuttersControl ($name) - RoommateEventProcessing: $shutters->getRoommatesReading" + ); + Log3( $name, 4, +"AutoShuttersControl ($name) - RoommateEventProcessing: $shuttersDev und Events $events" + ); + + if ( + ( $1 eq 'home' or $1 eq 'awoken' ) + and ( $shutters->getRoommatesStatus eq 'home' + or $shutters->getRoommatesStatus eq 'awoken' ) + and $ascDev->getAutoShuttersControlMorning eq 'on' + + and ( $shutters->getModeUp eq 'always' + or $shutters->getModeUp eq 'home' ) + ) + { + + if ( + ( + $shutters->getRoommatesLastStatus eq 'asleep' + or $shutters->getRoommatesLastStatus eq 'awoken' + ) + and IsDay( $hash, $shuttersDev ) + ) + { + $shutters->setLastDrive('roommate awoken'); + ShuttersCommandSet( $hash, $shuttersDev, + $shutters->getOpenPos ); + } + + if ( + ( + $shutters->getRoommatesLastStatus eq 'absent' + or $shutters->getRoommatesLastStatus eq 'gone' + or $shutters->getRoommatesLastStatus eq 'home' + ) + and ( $shutters->getModeUp eq 'home' + or $shutters->getModeUp eq 'always' + or $shutters->getModeDown eq 'home' + or $shutters->getModeDown eq 'always' ) + and $shutters->getRoommatesStatus eq 'home' + ) + { + if ( not IsDay( $hash, $shuttersDev ) ) { + my $position; + if ( CheckIfShuttersWindowRecOpen($shuttersDev) == 0 + or $shutters->getVentilateOpen eq 'off' ) + { + $position = $shutters->getClosedPos; + } + else { $position = $shutters->getVentilatePos; } + + $shutters->setLastDrive('roommate home'); + ShuttersCommandSet( $hash, $shuttersDev, $position ); + } + elsif ( IsDay( $hash, $shuttersDev ) + and $shutters->getStatus == $shutters->getClosedPos ) + { + $shutters->setLastDrive('roommate home'); + ShuttersCommandSet( $hash, $shuttersDev, + $shutters->getOpenPos ); + } + } + } + elsif ( + ( + $shutters->getModeDown eq 'always' + or $shutters->getModeDown eq 'home' + ) + and ( $1 eq 'gotosleep' or $1 eq 'asleep' ) + and $ascDev->getAutoShuttersControlEvening eq 'on' + ) + { + my $position; + if ( CheckIfShuttersWindowRecOpen($shuttersDev) == 0 + or $shutters->getVentilateOpen eq 'off' ) + { + $position = $shutters->getClosedPos; + } + else { $position = $shutters->getVentilatePos; } + + $shutters->setLastDrive('roommate asleep'); + ShuttersCommandSet( $hash, $shuttersDev, $position ); + } + elsif ( $shutters->getModeDown eq 'absent' + and $1 eq 'absent' ) + { + $shutters->setLastDrive('roommate absent'); + ShuttersCommandSet( $hash, $shuttersDev, $shutters->getClosedPos ); + } + } +} + +sub ResidentsEventProcessing($@) { + my ( $hash, $device, $events ) = @_; + + my $name = $device; + my $reading = $ascDev->getResidentsReading; + + if ( $events =~ m#$reading:\s(absent)# ) { + foreach my $shuttersDev ( @{ $hash->{helper}{shuttersList} } ) { + $shutters->setShuttersDev($shuttersDev); + + if ( + CheckIfShuttersWindowRecOpen($shuttersDev) != 0 + and $ascDev->getSelfDefense eq 'on' + and $shutters->getSelfDefenseExclude eq 'off' + or ( + ( + $shutters->getModeDown eq 'absent' + or $shutters->getModeDown eq 'always' + ) + and not IsDay( $hash, $shuttersDev ) + ) + ) + { + if ( CheckIfShuttersWindowRecOpen($shuttersDev) != 0 + and $ascDev->getSelfDefense eq 'on' + and $shutters->getSelfDefenseExclude eq 'off' ) + { + $shutters->setLastDrive('selfeDefense active'); + } + else { $shutters->setLastDrive('residents absent'); } + + $shutters->setDriveCmd( $shutters->getClosedPos ); + } + } + } + elsif ( $events =~ m#$reading:\s(gone)# + and $ascDev->getSelfDefense eq 'on' ) + { + foreach my $shuttersDev ( @{ $hash->{helper}{shuttersList} } ) { + $shutters->setShuttersDev($shuttersDev); + + if ( $shutters->getShuttersPlace eq 'terrace' ) { + $shutters->setLastDrive('selfeDefense terrace'); + $shutters->setDriveCmd( $shutters->getClosedPos ); + } + } + } + elsif ( + $events =~ m#$reading:\s(home)# + and ( $ascDev->getResidentsLastStatus eq 'absent' + or $ascDev->getResidentsLastStatus eq 'gone' + or $ascDev->getResidentsLastStatus eq 'asleep' + or $ascDev->getResidentsLastStatus eq 'awoken' ) + ) + { + foreach my $shuttersDev ( @{ $hash->{helper}{shuttersList} } ) { + $shutters->setShuttersDev($shuttersDev); + + if ( + $shutters->getStatus != $shutters->getClosedPos + and not IsDay( $hash, $shuttersDev ) + and $shutters->getRoommatesStatus eq 'none' + and ( $shutters->getModeDown eq 'home' + or $shutters->getModeDown eq 'always' ) + ) + { + $shutters->setLastDrive('residents home'); + $shutters->setDriveCmd( $shutters->getClosedPos ); + } + elsif ( + $ascDev->getSelfDefense eq 'on' + and CheckIfShuttersWindowRecOpen($shuttersDev) != 0 + and $shutters->getSelfDefenseExclude eq 'off' + or ( $ascDev->getResidentsLastStatus eq 'gone' + and $shutters->getShuttersPlace eq 'terrace' ) + and ( $shutters->getModeUp eq 'absent' + or $shutters->getModeUp eq 'off' ) + ) + { + $shutters->setLastDrive('selfeDefense inactive'); + $shutters->setDriveCmd( $shutters->getLastPos ); + } + elsif ( + $shutters->getStatus == $shutters->getClosedPos + and IsDay( $hash, $shuttersDev ) + and $shutters->getRoommatesStatus eq 'none' + and ( $shutters->getModeUp eq 'home' + or $shutters->getModeUp eq 'always' ) + ) + { + if ( $ascDev->getResidentsLastStatus eq 'asleep' + or $ascDev->getResidentsLastStatus eq 'awoken' ) + { + $shutters->setLastDrive('residents awoken'); + } + else { $shutters->setLastDrive('residents home'); } + $shutters->setDriveCmd( $shutters->getOpenPos ); + } + } + } +} + +sub RainEventProcessing($@) { + my ( $hash, $device, $events ) = @_; + my $name = $device; + my $reading = $ascDev->getRainSensorReading; + my $val; + + if ( $events =~ m#$reading:\s(\d+|rain|dry)# ) { + if ( $1 eq 'rain' ) { $val = 1000 } + elsif ( $1 eq 'dry' ) { $val = 0 } + else { $val = $1 } + + foreach my $shuttersDev ( @{ $hash->{helper}{shuttersList} } ) { + $shutters->setShuttersDev($shuttersDev); + if ( $val > 100 + and $shutters->getStatus != + $ascDev->getRainSensorShuttersClosedPos ) + { + $shutters->setLastDrive('rain protection'); + $shutters->setDriveCmd( + $ascDev->getRainSensorShuttersClosedPos ); + } + elsif ( $val == 0 + and $shutters->getStatus == + $ascDev->getRainSensorShuttersClosedPos ) + { + $shutters->setLastDrive('rain un-protection'); + $shutters->setDriveCmd( $shutters->getLastPos ); + } + } + } +} + +sub BrightnessEventProcessing($@) { + my ( $hash, $shuttersDev, $events ) = @_; + my $name = $hash->{NAME}; + $shutters->setShuttersDev($shuttersDev); + + return + unless ( + int( gettimeofday() / 86400 ) != + int( computeAlignTime( '24:00', $shutters->getTimeUpEarly ) / 86400 ) + and int( gettimeofday() / 86400 ) == + int( computeAlignTime( '24:00', $shutters->getTimeUpLate ) / 86400 ) + or int( gettimeofday() / 86400 ) != + int( computeAlignTime( '24:00', $shutters->getTimeDownEarly ) / 86400 ) + and int( gettimeofday() / 86400 ) == + int( computeAlignTime( '24:00', $shutters->getTimeDownLate ) / 86400 ) + ); + + my $reading = $shutters->getShadingBrightnessReading; + if ( $events =~ m#$reading:\s(\d+)# ) { + my $brightnessMinVal; + if ( $shutters->getBrightnessMinVal > -1 ) { + $brightnessMinVal = $shutters->getBrightnessMinVal; + } + else { + $brightnessMinVal = $ascDev->getBrightnessMinVal; + } + + if ( + int( gettimeofday() / 86400 ) != int( + computeAlignTime( '24:00', $shutters->getTimeUpEarly ) / 86400 + ) + and int( gettimeofday() / 86400 ) == int( + computeAlignTime( '24:00', $shutters->getTimeUpLate ) / 86400 + ) + and $1 > $brightnessMinVal + and $shutters->getUp eq 'brightness' + ) + { + Log3( $name, 4, +"AutoShuttersControl ($shuttersDev) - BrightnessEventProcessing: Steuerung für Morgens" + ); + my $homemode = $shutters->getRoommatesStatus; + $homemode = $ascDev->getResidentsStatus + if ( $homemode eq 'none' ); + $shutters->setLastDrive('minimum brightness threshold exceeded'); + + if ( $shutters->getModeUp eq $homemode + or $homemode eq 'none' + or $shutters->getModeUp eq 'always' ) + { + ShuttersCommandSet( $hash, $shuttersDev, $shutters->getOpenPos ) + if ( + ( + $shutters->getRoommatesStatus eq 'home' + or $shutters->getRoommatesStatus eq 'awoken' + or $shutters->getRoommatesStatus eq 'absent' + or $shutters->getRoommatesStatus eq 'gone' + or $shutters->getRoommatesStatus eq 'none' + ) + and $ascDev->getSelfDefense eq 'off' + or ( $ascDev->getSelfDefense eq 'on' + and CheckIfShuttersWindowRecOpen($shuttersDev) == 0 ) + ); + } + } + elsif ( + int( gettimeofday() / 86400 ) != int( + computeAlignTime( '24:00', $shutters->getTimeDownEarly ) / 86400 + ) + and int( gettimeofday() / 86400 ) == int( + computeAlignTime( '24:00', $shutters->getTimeDownLate ) / 86400 + ) + and $1 < $brightnessMinVal + and $shutters->getDown eq 'brightness' + ) + { + Log3( $name, 4, +"AutoShuttersControl ($shuttersDev) - BrightnessEventProcessing: Steuerung für Abends" + ); + + my $posValue; + if ( CheckIfShuttersWindowRecOpen($shuttersDev) == 0 + or $shutters->getVentilateOpen eq 'off' ) + { + $posValue = $shutters->getClosedPos; + } + else { $posValue = $shutters->getVentilatePos; } + + my $homemode = $shutters->getRoommatesStatus; + $homemode = $ascDev->getResidentsStatus + if ( $homemode eq 'none' ); + $shutters->setLastDrive('minimum brightness threshold fell below'); + + ShuttersCommandSet( $hash, $shuttersDev, $shutters->getClosedPos ) + if ( $shutters->getModeDown eq $homemode + or $homemode eq 'none' + or $shutters->getModeDown eq 'always' ); + } + } +} + +sub PartyModeEventProcessing($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + + foreach my $shuttersDev ( @{ $hash->{helper}{shuttersList} } ) { + $shutters->setShuttersDev($shuttersDev); + if ( not IsDay( $hash, $shuttersDev ) ) { + if ( CheckIfShuttersWindowRecOpen($shuttersDev) == 2 + and $shutters->getSubTyp eq 'threestate' ) + { + Log3( $name, 4, +"AutoShuttersControl ($name) - PartyModeEventProcessing Fenster offen" + ); + $shutters->setDelayCmd( $shutters->getClosedPos ); + Log3( $name, 4, +"AutoShuttersControl ($name) - PartyModeEventProcessing - Spring in ShuttersCommandDelaySet" + ); + } + else { + Log3( $name, 4, +"AutoShuttersControl ($name) - PartyModeEventProcessing Fenster nicht offen" + ); + ShuttersCommandSet( + $hash, + $shuttersDev, + ( + CheckIfShuttersWindowRecOpen($shuttersDev) == 0 + ? $shutters->getClosedPos + : $shutters->getVentilatePos + ) + ); + } + } + } +} + +sub ShuttersEventProcessing($@) { + my ( $hash, $shuttersDev, $events ) = @_; + my $name = $hash->{NAME}; + + if ( $events =~ m#.*:\s(\d+)# ) { + $shutters->setShuttersDev($shuttersDev); + $ascDev->setPosReading; + if ( ( int( gettimeofday() ) - $shutters->getLastPosTimestamp ) > 60 + and $shutters->getLastPos != $shutters->getStatus ) + { + $shutters->setLastDrive('manual'); + $shutters->setLastDriveReading; + $ascDev->setStateReading; + $shutters->setLastManPos($1); + } + } +} + +# Sub für das Zusammensetzen der Rolläden Steuerbefehle +sub ShuttersCommandSet($$$) { + my ( $hash, $shuttersDev, $posValue ) = @_; + my $name = $hash->{NAME}; + + $shutters->setShuttersDev($shuttersDev); + + if ( + ( $shutters->getPartyMode eq 'on' and $ascDev->getPartyMode eq 'on' ) + or ( CheckIfShuttersWindowRecOpen($shuttersDev) == 2 + and $shutters->getSubTyp eq 'threestate' + and $ascDev->getAutoShuttersControlComfort eq 'off' + and $shutters->getVentilateOpen eq 'on' ) + or ( + CheckIfShuttersWindowRecOpen($shuttersDev) == 2 + and ( $shutters->getLockOut eq 'soft' + or $shutters->getLockOut eq 'hard' ) + and $ascDev->getLockOut eq 'on' + ) + or ( $shutters->getAntiFreeze eq 'on' + and $ascDev->getOutTemp <= $ascDev->getAntifreezeTemp ) + ) + { + $shutters->setDelayCmd($posValue); + $ascDev->setDelayCmdReading; + } + else { + $shutters->setDriveCmd($posValue); + $shutters->setDelayCmd('none') + if ( $shutters->getDelayCmd ne 'none' ) + ; # setzt den Wert auf none da der Rolladen nun gesteuert werden kann. + $ascDev->setLastPosReading; + } +} + +## Sub welche die InternalTimer nach entsprechenden Sunset oder Sunrise zusammen stellt +sub CreateSunRiseSetShuttersTimer($$) { + my ( $hash, $shuttersDev ) = @_; + my $name = $hash->{NAME}; + my $shuttersDevHash = $defs{$shuttersDev}; + $shutters->setShuttersDev($shuttersDev); + + return if ( IsDisabled($name) ); + + my $shuttersSunriseUnixtime = + ShuttersSunrise( $hash, $shuttersDev, 'unix' ) + 1; + my $shuttersSunsetUnixtime = + ShuttersSunset( $hash, $shuttersDev, 'unix' ) + 1; + + $shutters->setSunriseUnixTime($shuttersSunriseUnixtime); + $shutters->setSunsetUnixTime($shuttersSunsetUnixtime); + + ## In jedem Rolladen werden die errechneten Zeiten hinterlegt,es sei denn das autoShuttersControlEvening/Morning auf off steht + readingsBeginUpdate($shuttersDevHash); + readingsBulkUpdate( + $shuttersDevHash, + 'ASC_Time_DriveDown', + ( + $ascDev->getAutoShuttersControlEvening eq 'on' + ? strftime( + "%e.%m.%Y - %H:%M", localtime($shuttersSunsetUnixtime) + ) + : 'AutoShuttersControl off' + ), + 1 + ); + readingsBulkUpdate( + $shuttersDevHash, + 'ASC_Time_DriveUp', + ( + $ascDev->getAutoShuttersControlMorning eq 'on' + ? strftime( "%e.%m.%Y - %H:%M", + localtime($shuttersSunriseUnixtime) ) + : 'AutoShuttersControl off' + ), + 1 + ); + readingsEndUpdate( $shuttersDevHash, 0 ); + + readingsBeginUpdate($hash); + readingsBulkUpdateIfChanged( + $hash, + $shuttersDev . '_nextAstroTimeEvent', + ( + $shuttersSunriseUnixtime < $shuttersSunsetUnixtime + ? strftime( "%e.%m.%Y - %H:%M", + localtime($shuttersSunriseUnixtime) ) + : strftime( + "%e.%m.%Y - %H:%M", localtime($shuttersSunsetUnixtime) + ) + ) + ); + readingsEndUpdate( $hash, 1 ); + + CommandDeleteReading( undef, + $name . ' ' . $shuttersDev . '_nextAstroEvent' ) + if ( ReadingsVal( $name, $shuttersDev . '_nextAstroEvent', 'none' ) ne + 'none' ); # temporär + CommandDeleteReading( undef, + $shuttersDev . ' AutoShuttersControl_Time_Sunrise' ) + if ( + ReadingsVal( $shuttersDev, 'AutoShuttersControl_Time_Sunrise', 'none' ) + ne 'none' ); # temporär + CommandDeleteReading( undef, + $shuttersDev . ' AutoShuttersControl_Time_Sunset' ) + if ( + ReadingsVal( $shuttersDev, 'AutoShuttersControl_Time_Sunset', 'none' ) + ne 'none' ); # temporär + CommandDeleteReading( undef, + $shuttersDev . ' AutoShuttersControl_Time_DriveDown' ) + if ( + ReadingsVal( $shuttersDev, 'AutoShuttersControl_Time_DriveDown', + 'none' ) ne 'none' + ); # temporär + CommandDeleteReading( undef, + $shuttersDev . ' AutoShuttersControl_Time_DriveUp' ) + if ( + ReadingsVal( $shuttersDev, 'AutoShuttersControl_Time_DriveUp', 'none' ) + ne 'none' ); # temporär + + RemoveInternalTimer( $shutters->getInTimerFuncHash ) + if ( defined( $shutters->getInTimerFuncHash ) ); + + ## kleine Hilfe für InternalTimer damit ich alle benötigten Variablen an die Funktion übergeben kann welche von Internal Timer aufgerufen wird. + my %funcHash = ( + hash => $hash, + shuttersdevice => $shuttersDev, + sunsettime => $shuttersSunsetUnixtime, + sunrisetime => $shuttersSunriseUnixtime + ); + + ## Ich brauche beim löschen des InternalTimer den Hash welchen ich mitgegeben habe,dieser muss gesichert werden + $shutters->setInTimerFuncHash( \%funcHash ); + + InternalTimer( $shuttersSunsetUnixtime, + 'AutoShuttersControl::SunSetShuttersAfterTimerFn', \%funcHash ) + if ( $ascDev->getAutoShuttersControlEvening eq 'on' ); + InternalTimer( $shuttersSunriseUnixtime, + 'AutoShuttersControl::SunRiseShuttersAfterTimerFn', \%funcHash ) + if ( $ascDev->getAutoShuttersControlMorning eq 'on' ); + + $ascDev->setStateReading('created new drive timer'); +} + +## Funktion zum neu setzen der Timer und der Readings für Sunset/Rise +sub RenewSunRiseSetShuttersTimer($) { + my $hash = shift; + + foreach ( @{ $hash->{helper}{shuttersList} } ) { + $shutters->setShuttersDev($_); + + RemoveInternalTimer( $shutters->getInTimerFuncHash ); + $shutters->setInTimerFuncHash(undef); + CreateSunRiseSetShuttersTimer( $hash, $_ ); + } +} + +## Funktion zum hardwareseitigen setzen des lock-out oder blocking beim Rolladen selbst +sub SetHardewareBlockForShutters($$) { + my ( $hash, $cmd ) = @_; + foreach ( @{ $hash->{helper}{shuttersList} } ) { + if ( AttrVal( $_, 'ASC_lock-out', 'soft' ) eq 'hard' + and AttrVal( $_, 'ASC_lock-outCmd', 'none' ) ne 'none' ) + { + CommandSet( undef, $_ . ' inhibit ' . $cmd ) + if ( AttrVal( $_, 'ASC_lock-outCmd', 'none' ) eq 'inhibit' ); + CommandSet( undef, + $_ . ' ' . ( $cmd eq 'on' ? 'blocked' : 'unblocked' ) ) + if ( AttrVal( $_, 'ASC_lock-outCmd', 'none' ) eq 'blocked' ); + } + } +} + +## Funktion für das wiggle aller Shutters zusammen +sub wiggleAll($) { + my $hash = shift; + + foreach ( @{ $hash->{helper}{shuttersList} } ) { + wiggle( $hash, $_ ); + } +} + +sub wiggle($$) { + my ( $hash, $shuttersDev ) = @_; + $shutters->setShuttersDev($shuttersDev); + $shutters->setNoOffset(1); + + my %h = ( + shuttersDev => $shutters->getShuttersDev, + posValue => $shutters->getStatus, + ); + + if ( $shutters->getShuttersPosCmdValueNegate ) { + if ( $shutters->getStatus >= $shutters->getOpenPos / 2 ) { + $shutters->setDriveCmd( $shutters->getStatus + 5 ); + } + else { $shutters->setDriveCmd( $shutters->getStatus - 5 ); } + } + else { + if ( $shutters->getStatus >= $shutters->getOpenPos / 2 ) { + $shutters->setDriveCmd( $shutters->getStatus - 5 ); + } + else { $shutters->setDriveCmd( $shutters->getStatus + 5 ); } + } + + InternalTimer( gettimeofday() + 60, 'AutoShuttersControl::SetCmdFn', \%h ); +} +#### + +## Funktion welche beim Ablaufen des Timers für Sunset aufgerufen werden soll +sub SunSetShuttersAfterTimerFn($) { + my $funcHash = shift; + my $hash = $funcHash->{hash}; + my $shuttersDev = $funcHash->{shuttersdevice}; + $shutters->setShuttersDev($shuttersDev); + + my $posValue; + if ( CheckIfShuttersWindowRecOpen($shuttersDev) == 0 + or $shutters->getVentilateOpen eq 'off' ) + { + $posValue = $shutters->getClosedPos; + } + else { $posValue = $shutters->getVentilatePos; } + + my $homemode = $shutters->getRoommatesStatus; + $homemode = $ascDev->getResidentsStatus if ( $homemode eq 'none' ); + + if ( $shutters->getModeDown eq $homemode + or $homemode eq 'none' + or $shutters->getModeDown eq 'always' ) + { + $shutters->setLastDrive('night close'); + ShuttersCommandSet( $hash, $shuttersDev, $posValue ); + } + + CreateSunRiseSetShuttersTimer( $hash, $shuttersDev ); +} + +## Funktion welche beim Ablaufen des Timers für Sunrise aufgerufen werden soll +sub SunRiseShuttersAfterTimerFn($) { + my $funcHash = shift; + my $hash = $funcHash->{hash}; + my $shuttersDev = $funcHash->{shuttersdevice}; + $shutters->setShuttersDev($shuttersDev); + + my $homemode = $shutters->getRoommatesStatus; + $homemode = $ascDev->getResidentsStatus if ( $homemode eq 'none' ); + + if ( $shutters->getModeUp eq $homemode + or $homemode eq 'none' + or $shutters->getModeUp eq 'always' ) + { + if ( + ( + $shutters->getRoommatesStatus eq 'home' + or $shutters->getRoommatesStatus eq 'awoken' + or $shutters->getRoommatesStatus eq 'absent' + or $shutters->getRoommatesStatus eq 'gone' + or $shutters->getRoommatesStatus eq 'none' + ) + and $ascDev->getSelfDefense eq 'off' + or ( $ascDev->getSelfDefense eq 'on' + and CheckIfShuttersWindowRecOpen($shuttersDev) == 0 ) + ) + { + $shutters->setLastDrive('day open'); + ShuttersCommandSet( $hash, $shuttersDev, $shutters->getOpenPos ); + } + } + CreateSunRiseSetShuttersTimer( $hash, $shuttersDev ); +} + +sub CreateNewNotifyDev($) { + my $hash = shift; + my $name = $hash->{NAME}; + + $hash->{NOTIFYDEV} = "global," . $name; + delete $hash->{monitoredDevs}; + + CommandDeleteReading( undef, $name . ' .monitoredDevs' ); + my $shuttersList = ''; + foreach ( @{ $hash->{helper}{shuttersList} } ) { + AddNotifyDev( $hash, AttrVal( $_, 'ASC_Roommate_Device', 'none' ), + $_, 'ASC_Roommate_Device' ) + if ( AttrVal( $_, 'ASC_Roommate_Device', 'none' ) ne 'none' ); + AddNotifyDev( $hash, AttrVal( $_, 'ASC_WindowRec', 'none' ), + $_, 'ASC_WindowRec' ) + if ( AttrVal( $_, 'ASC_WindowRec', 'none' ) ne 'none' ); + AddNotifyDev( $hash, + AttrVal( $_, 'ASC_Shading_Brightness_Sensor', 'none' ), + $_, 'ASC_Shading_Brightness_Sensor' ) + if ( + AttrVal( $_, 'ASC_Shading_Brightness_Sensor', 'none' ) ne 'none' ); + $shuttersList = $shuttersList . ',' . $_; + } + AddNotifyDev( $hash, AttrVal( $name, 'ASC_residentsDevice', 'none' ), + $name, 'ASC_residentsDevice' ) + if ( AttrVal( $name, 'ASC_residentsDevice', 'none' ) ne 'none' ); + AddNotifyDev( $hash, AttrVal( $name, 'ASC_rainSensorDevice', 'none' ), + $name, 'ASC_rainSensorDevice' ) + if ( AttrVal( $name, 'ASC_rainSensorDevice', 'none' ) ne 'none' ); + $hash->{NOTIFYDEV} = $hash->{NOTIFYDEV} . $shuttersList; +} + +sub GetShuttersInformation($) { + my $hash = shift; + my $ret = ''; + $ret .= '
'; + $ret .= ''; + $ret .= ''; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ''; + + my $linecount = 1; + foreach my $shutter ( @{ $hash->{helper}{shuttersList} } ) { + $shutters->setShuttersDev($shutter); + + if ( $linecount % 2 == 0 ) { $ret .= ''; } + else { $ret .= ''; } + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ''; + $linecount++; + } + $ret .= '
Shutters Next DriveUp Next DriveDown ASC Up ASC Down ASC Mode Up ASC Mode Down Partymode Lock-Out Last Drive Position Last Position
$shutter " + . strftime( "%e.%m.%Y - %H:%M:%S", + localtime( $shutters->getSunriseUnixTime ) ) + . " " + . strftime( "%e.%m.%Y - %H:%M:%S", + localtime( $shutters->getSunsetUnixTime ) ) + . " " . $shutters->getUp . " " . $shutters->getDown . " " . $shutters->getModeUp . " " . $shutters->getModeDown . " " . $shutters->getPartyMode . " " . $shutters->getLockOut . " " . $shutters->getLastDrive . " " . $shutters->getStatus . " " . $shutters->getLastPos . "
'; + return $ret; +} + +sub GetMonitoredDevs($) { + my $hash = shift; + my $notifydevs = eval { + decode_json( ReadingsVal( $hash->{NAME}, '.monitoredDevs', 'none' ) ); + }; + my $ret = ''; + $ret .= '
'; + $ret .= ''; + $ret .= ''; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ''; + + if ( ref($notifydevs) eq "HASH" ) { + my $linecount = 1; + foreach my $notifydev ( sort keys( %{$notifydevs} ) ) { + if ( ref( $notifydevs->{$notifydev} ) eq "HASH" ) { + foreach + my $shutters ( sort keys( %{ $notifydevs->{$notifydev} } ) ) + { + if ( $linecount % 2 == 0 ) { $ret .= ''; } + else { $ret .= ''; } + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ''; + $linecount++; + } + } + } + } + + ###### create Links + my $aHref; + +# create define Link +# $aHref="{host}."/fhem?cmd=set+".$::FW_CSRF."\">Create new NOTIFYDEV structure"; +# $aHref="Create new NOTIFYDEV structure"; +# $aHref="{station}{name})."+Aqicn+".$dataset->{uid}.$FW_CSRF."\">Create Station Device"; + + # $ret .= ''; + # $ret .= ''; + # $ret .= ""; + # $ret .= ""; + # $ret .= ""; + # $ret .= ""; + # $ret .= ""; + $ret .= '
Shutters/ASC-Device NOTIFYDEV Attribut
$shutters $notifydev $notifydevs->{$notifydev}{$shutters}
".$aHref."
'; + + return $ret; +} + +################################# +## my little helper +################################# + +# Hilfsfunktion welche meinen ReadingString zum finden der getriggerten Devices und der Zurdnung was das Device überhaupt ist und zu welchen Rolladen es gehört aus liest und das Device extraiert +sub ExtractNotifyDevFromEvent($$$) { + my ( $hash, $shuttersDev, $shuttersAttr ) = @_; + my %notifyDevs; + while ( my $notifyDev = each %{ $hash->{monitoredDevs} } ) { + Log3( $hash->{NAME}, 4, +"AutoShuttersControl ($hash->{NAME}) - ExtractNotifyDevFromEvent - NotifyDev: " + . $notifyDev ); + Log3( $hash->{NAME}, 5, +"AutoShuttersControl ($hash->{NAME}) - ExtractNotifyDevFromEvent - ShuttersDev: " + . $shuttersDev ); + + if ( defined( $hash->{monitoredDevs}{$notifyDev}{$shuttersDev} ) + and $hash->{monitoredDevs}{$notifyDev}{$shuttersDev} eq + $shuttersAttr ) + { + Log3( $hash->{NAME}, 4, +"AutoShuttersControl ($hash->{NAME}) - ExtractNotifyDevFromEvent - ShuttersDevHash: " + . $hash->{monitoredDevs}{$notifyDev}{$shuttersDev} ); + Log3( $hash->{NAME}, 5, +"AutoShuttersControl ($hash->{NAME}) - ExtractNotifyDevFromEvent - return ShuttersDev: " + . $notifyDev ); + $notifyDevs{$notifyDev} = $shuttersDev; + } + } + return \%notifyDevs; +} + +## Ist Tag oder Nacht für den entsprechende Rolladen +sub IsDay($$) { + my ( $hash, $shuttersDev ) = @_; + my $name = $hash->{NAME}; + return ( ShuttersSunrise( $hash, $shuttersDev, 'unix' ) > + ShuttersSunset( $hash, $shuttersDev, 'unix' ) ? 1 : 0 ); +} + +sub ShuttersSunrise($$$) { + my ( $hash, $shuttersDev, $tm ) = + @_; # Tm steht für Timemode und bedeutet Realzeit oder Unixzeit + my $name = $hash->{NAME}; + my $autoAstroMode; + $shutters->setShuttersDev($shuttersDev); + + if ( $shutters->getAutoAstroModeMorning ne 'none' ) { + $autoAstroMode = $shutters->getAutoAstroModeMorning; + $autoAstroMode = + $autoAstroMode . '=' . $shutters->getAutoAstroModeMorningHorizon + if ( $autoAstroMode eq 'HORIZON' ); + } + else { + $autoAstroMode = $ascDev->getAutoAstroModeMorning; + $autoAstroMode = + $autoAstroMode . '=' . $ascDev->getAutoAstroModeMorningHorizon + if ( $autoAstroMode eq 'HORIZON' ); + } + my $oldFuncHash = $shutters->getInTimerFuncHash; + my $shuttersSunriseUnixtime; + + if ( $tm eq 'unix' ) { + if ( $shutters->getUp eq 'astro' ) { + if ( ( IsWe() or IsWeTomorrow() ) + and $ascDev->getSunriseTimeWeHoliday eq 'on' ) + { + if ( not IsWeTomorrow() ) { + if ( + IsWe() + and int( gettimeofday() / 86400 ) == int( + ( + computeAlignTime( + '24:00', + sunrise_abs( + $autoAstroMode, 0, + $shutters->getTimeUpWeHoliday + ) + ) + 1 + ) / 86400 + ) + ) + { + $shuttersSunriseUnixtime = ( + computeAlignTime( + '24:00', + sunrise_abs( + $autoAstroMode, 0, + $shutters->getTimeUpWeHoliday + ) + ) + 1 + ); + } + elsif ( + int( gettimeofday() / 86400 ) == int( + ( + computeAlignTime( + '24:00', + sunrise_abs( + $autoAstroMode, + 0, + $shutters->getTimeUpEarly, + $shutters->getTimeUpLate + ) + ) + 1 + ) / 86400 + ) + ) + { + $shuttersSunriseUnixtime = ( + computeAlignTime( + '24:00', + sunrise_abs( + $autoAstroMode, 0, + $shutters->getTimeUpWeHoliday + ) + ) + 1 + ); + } + else { + $shuttersSunriseUnixtime = ( + computeAlignTime( + '24:00', + sunrise_abs( + $autoAstroMode, + 0, + $shutters->getTimeUpEarly, + $shutters->getTimeUpLate + ) + ) + 1 + ); + } + } + else { + $shuttersSunriseUnixtime = ( + computeAlignTime( + '24:00', + sunrise_abs( + $autoAstroMode, 0, + $shutters->getTimeUpWeHoliday + ) + ) + 1 + ); + } + } + else { + $shuttersSunriseUnixtime = ( + computeAlignTime( + '24:00', + sunrise_abs( + $autoAstroMode, + 0, + $shutters->getTimeUpEarly, + $shutters->getTimeUpLate + ) + ) + 1 + ); + } + if ( defined($oldFuncHash) + and ref($oldFuncHash) eq 'HASH' + and ( IsWe() or IsWeTomorrow() ) + and $ascDev->getSunriseTimeWeHoliday eq 'on' ) + { + if ( not IsWeTomorrow() ) { + if ( + int( gettimeofday() / 86400 ) == int( + ( + computeAlignTime( + '24:00', + sunrise_abs( + $autoAstroMode, + 0, + $shutters->getTimeUpEarly, + $shutters->getTimeUpLate + ) + ) + 1 + ) / 86400 + ) + ) + { + $shuttersSunriseUnixtime = + ( $shuttersSunriseUnixtime + 86400 ) + if ( $shuttersSunriseUnixtime < + ( $oldFuncHash->{sunrisetime} + 180 ) + and $oldFuncHash->{sunrisetime} < gettimeofday() ); + } + } + } + elsif ( defined($oldFuncHash) and ref($oldFuncHash) eq 'HASH' ) { + $shuttersSunriseUnixtime = ( $shuttersSunriseUnixtime + 86400 ) + if ( $shuttersSunriseUnixtime < + ( $oldFuncHash->{sunrisetime} + 180 ) + and $oldFuncHash->{sunrisetime} < gettimeofday() ); + } + } + elsif ( $shutters->getUp eq 'time' ) { + $shuttersSunriseUnixtime = + computeAlignTime( '24:00', $shutters->getTimeUpEarly ); + } + elsif ( $shutters->getUp eq 'brightness' ) { + $shuttersSunriseUnixtime = + computeAlignTime( '24:00', $shutters->getTimeUpLate ); + } + return $shuttersSunriseUnixtime; + } + elsif ( $tm eq 'real' ) { + return sunrise_abs( $autoAstroMode, 0, $shutters->getTimeUpEarly, + $shutters->getTimeUpLate ) + if ( $shutters->getUp eq 'astro' ); + return $shutters->getTimeUpEarly if ( $shutters->getUp eq 'time' ); + } +} + +sub ShuttersSunset($$$) { + my ( $hash, $shuttersDev, $tm ) = + @_; # Tm steht für Timemode und bedeutet Realzeit oder Unixzeit + my $name = $hash->{NAME}; + my $autoAstroMode; + $shutters->setShuttersDev($shuttersDev); + + if ( $shutters->getAutoAstroModeEvening ne 'none' ) { + $autoAstroMode = $shutters->getAutoAstroModeEvening; + $autoAstroMode = + $autoAstroMode . '=' . $shutters->getAutoAstroModeEveningHorizon + if ( $autoAstroMode eq 'HORIZON' ); + } + else { + $autoAstroMode = $ascDev->getAutoAstroModeEvening; + $autoAstroMode = + $autoAstroMode . '=' . $ascDev->getAutoAstroModeEveningHorizon + if ( $autoAstroMode eq 'HORIZON' ); + } + my $oldFuncHash = $shutters->getInTimerFuncHash; + my $shuttersSunsetUnixtime; + + if ( $tm eq 'unix' ) { + if ( $shutters->getDown eq 'astro' ) { + $shuttersSunsetUnixtime = ( + computeAlignTime( + '24:00', + sunset_abs( + $autoAstroMode, + 0, + $shutters->getTimeDownEarly, + $shutters->getTimeDownLate + ) + ) + 1 + ); + if ( defined($oldFuncHash) and ref($oldFuncHash) eq 'HASH' ) { + $shuttersSunsetUnixtime = ( $shuttersSunsetUnixtime + 86400 ) + if ( $shuttersSunsetUnixtime < + ( $oldFuncHash->{sunsettime} + 180 ) + and $oldFuncHash->{sunsettime} < gettimeofday() ); + } + } + elsif ( $shutters->getDown eq 'time' ) { + $shuttersSunsetUnixtime = + computeAlignTime( '24:00', $shutters->getTimeDownEarly ); + } + elsif ( $shutters->getDown eq 'brightness' ) { + $shuttersSunsetUnixtime = + computeAlignTime( '24:00', $shutters->getTimeDownLate ); + } + return $shuttersSunsetUnixtime; + } + elsif ( $tm eq 'real' ) { + return sunset_abs( + $autoAstroMode, 0, + $shutters->getTimeDownEarly, + $shutters->getTimeDownLate + ) if ( $shutters->getDown eq 'astro' ); + return $shutters->getTimeDownEarly + if ( $shutters->getDown eq 'time' ); + } +} + +## Kontrolliert ob das Fenster von einem bestimmten Rolladen offen ist +sub CheckIfShuttersWindowRecOpen($) { + my $shuttersDev = shift; + $shutters->setShuttersDev($shuttersDev); + + if ( $shutters->getWinStatus eq 'open' ) { return 2; } + elsif ( $shutters->getWinStatus eq 'tilted' + and $shutters->getSubTyp eq 'threestate' ) + { + return 1; + } + elsif ( $shutters->getWinStatus eq 'closed' ) { return 0; } +} + +sub makeReadingName($) { + my ($name) = @_; + my %charHash = ( + "ä" => "ae", + "Ä" => "Ae", + "ü" => "ue", + "Ü" => "Ue", + "ö" => "oe", + "Ö" => "Oe", + "ß" => "ss" + ); + my $charHashkeys = join( "|", keys(%charHash) ); + + $name = "UNDEFINED" if ( !defined($name) ); + return $name if ( $name =~ m/^\./ ); + $name =~ s/($charHashkeys)/$charHash{$1}/gi; + $name =~ s/[^a-z0-9._\-\/]/_/gi; + return $name; +} + +sub TimeMin2Sec($) { + my $min = shift; + my $sec; + + $sec = $min * 60; + return $sec; +} + +sub IsWe() { + my ( undef, undef, undef, undef, undef, undef, $wday, undef, undef ) = + localtime( gettimeofday() ); + my $we = ( ( $wday == 0 || $wday == 6 ) ? 1 : 0 ); + + if ( !$we ) { + foreach my $h2we ( split( ",", AttrVal( "global", "holiday2we", "" ) ) ) + { + my ( $a, $b ) = + ReplaceEventMap( $h2we, [ $h2we, Value($h2we) ], 0 ); + $we = 1 if ( $b && $b ne "none" ); + } + } + return $we; +} + +sub IsWeTomorrow() { + my ( undef, undef, undef, undef, undef, undef, $wday, undef, undef ) = + localtime( gettimeofday() ); + my $we = ( + ( ( ( $wday + 1 == 7 ? 0 : $wday + 1 ) ) == 0 || ( $wday + 1 ) == 6 ) + ? 1 + : 0 + ); + + if ( !$we ) { + foreach my $h2we ( split( ",", AttrVal( "global", "holiday2we", "" ) ) ) + { + my ( $a, $b ) = ReplaceEventMap( $h2we, + [ $h2we, ReadingsVal( $h2we, "tomorrow", "none" ) ], 0 ); + $we = 1 if ( $b && $b ne "none" ); + } + } + return $we; +} + +sub IsHoliday($) { + my $hash = shift; + my $name = $hash->{NAME}; + + return ( + ReadingsVal( AttrVal( $name, 'ASC_timeUpHolidayDevice', 'none' ), + AttrVal( $name, 'ASC_timeUpHolidayReading', 'state' ), 0 ) == 1 ? 1 : 0 + ); +} + +sub SetCmdFn($) { + my $h = shift; + my $shuttersDev = $h->{shuttersDev}; + my $posValue = $h->{posValue}; + + $shutters->setShuttersDev($shuttersDev); + if ( $shutters->getStatus != $posValue ) { + $shutters->setLastPos( $shutters->getStatus ); + $shutters->setLastDriveReading; + $ascDev->setStateReading; + } + else { + $shutters->setLastDrive( + ReadingsVal( $shuttersDev, 'ASC_ShuttersLastDrive', 'none' ) ); + } + + CommandSet( undef, + $shuttersDev + . ':FILTER=' + . $shutters->getPosCmd . '!=' + . $posValue . ' ' + . $shutters->getPosSetCmd . ' ' + . $posValue ); +} + +########## Begin der Klassendeklarierungen für OOP (Objektorientierte Programmierung) ######################### +## Klasse Rolläden (Shutters) und die Subklassen Attr und Readings ## +## desweiteren wird noch die Klasse ASC_Roommate mit eingebunden +package ASC_Shutters; +our @ISA = + qw(ASC_Shutters::Readings ASC_Shutters::Attr ASC_Roommate ASC_Window); + +use strict; +use warnings; + +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw( + defs + ReadingsVal + readingsSingleUpdate + gettimeofday + InternalTimer) + ); +} + +sub new { + my $class = shift; + my $self = { + shuttersDev => undef, + defaultarg => undef, + roommate => undef, + }; + + bless $self, $class; + return $self; +} + +sub setShuttersDev { + my ( $self, $shuttersDev ) = @_; + + $self->{shuttersDev} = $shuttersDev if ( defined($shuttersDev) ); + return $self->{shuttersDev}; +} + +sub getShuttersDev { + my $self = shift; + + return $self->{shuttersDev}; +} + +sub setNoOffset { + my ( $self, $noOffset ) = @_; + + $self->{ $self->{shuttersDev} }{noOffset} = $noOffset; + return 0; +} + +sub setDriveCmd { + my ( $self, $posValue ) = @_; + my $offSet = 0; + my %h = ( + shuttersDev => $self->{shuttersDev}, + posValue => $posValue, + ); + + $offSet = $shutters->getOffset if ( $shutters->getOffset > 0 ); + $offSet = $ascDev->getShuttersOffset if ( $shutters->getOffset == -1 ); + + InternalTimer( gettimeofday() + int( rand($offSet) ), + 'AutoShuttersControl::SetCmdFn', \%h ) + if ( $offSet > 0 and not $shutters->getNoOffset ); + AutoShuttersControl::SetCmdFn( \%h ) + if ( $offSet == 0 or $shutters->getNoOffset ); + $shutters->setNoOffset(0); + + return 0; +} + +sub setSunsetUnixTime { + my ( $self, $unixtime ) = @_; + + $self->{ $self->{shuttersDev} }{sunsettime} = $unixtime; + return 0; +} + +sub setSunriseUnixTime { + my ( $self, $unixtime ) = @_; + + $self->{ $self->{shuttersDev} }{sunrisetime} = $unixtime; + return 0; +} + +sub setDelayCmd { + my ( $self, $posValue ) = @_; + + $self->{ $self->{shuttersDev} }{delayCmd} = $posValue; + return 0; +} + +sub setLastDrive { + my ( $self, $lastDrive ) = @_; + + $self->{ $self->{shuttersDev} }{lastDrive} = $lastDrive; + return 0; +} + +sub setPosSetCmd { + my ( $self, $posSetCmd ) = @_; + + $self->{ $self->{shuttersDev} }{posSetCmd} = $posSetCmd; + return 0; +} + +sub setLastDriveReading { + my $self = shift; + my $shuttersDevHash = $defs{ $self->{shuttersDev} }; + + readingsSingleUpdate( $shuttersDevHash, 'ASC_ShuttersLastDrive', + $shutters->getLastDrive, 1 ); + return 0; +} + +sub setLastPos +{ # letzte ermittelte Position bevor die Position des Rolladen über ASC geändert wurde + my ( $self, $position ) = @_; + + $self->{ $self->{shuttersDev} }{lastPos}{VAL} = $position + if ( defined($position) ); + $self->{ $self->{shuttersDev} }{lastPos}{TIME} = int( gettimeofday() ) + if ( defined( $self->{ $self->{shuttersDev} }{lastPos} ) ); + return 0; +} + +sub setLastManPos +{ # letzte ermittelte Position bevor die Position des Rolladen manuell (nicht über ASC) geändert wurde + my ( $self, $position ) = @_; + + $self->{ $self->{shuttersDev} }{lastManPos}{VAL} = $position + if ( defined($position) ); + $self->{ $self->{shuttersDev} }{lastManPos}{TIME} = int( gettimeofday() ) + if ( defined( $self->{ $self->{shuttersDev} }{lastManPos} ) ); + return 0; +} + +sub setDefault { + my ( $self, $defaultarg ) = @_; + + $self->{defaultarg} = $defaultarg if ( defined($defaultarg) ); + return $self->{defaultarg}; +} + +sub setRoommate { + my ( $self, $roommate ) = @_; + + $self->{roommate} = $roommate if ( defined($roommate) ); + return $self->{roommate}; +} + +sub setInTimerFuncHash { + my ( $self, $inTimerFuncHash ) = @_; + + $self->{ $self->{shuttersDev} }{inTimerFuncHash} = $inTimerFuncHash + if ( defined($inTimerFuncHash) ); + return 0; +} + +sub getShuttersPosCmdValueNegate { + my $self = shift; + + return ( $shutters->getOpenPos < $shutters->getClosedPos ? 1 : 0 ); +} + +sub getPosSetCmd { + my $self = shift; + + return ( + defined( $self->{ $self->{shuttersDev} }{posSetCmd} ) + ? $self->{ $self->{shuttersDev} }{posSetCmd} + : $shutters->getPosCmd ); +} + +sub getNoOffset { + my $self = shift; + + return $self->{ $self->{shuttersDev} }{noOffset}; +} + +sub getLastDrive { + my $self = shift; + + $self->{ $self->{shuttersDev} }{lastDrive} = + ReadingsVal( $self->{shuttersDev}, 'ASC_ShuttersLastDrive', 'none' ) + if ( not defined( $self->{ $self->{shuttersDev} }{lastDrive} ) ); + + return $self->{ $self->{shuttersDev} }{lastDrive}; +} + +sub getLastPos +{ # letzte ermittelte Position bevor die Position des Rolladen über ASC geändert wurde + my $self = shift; + + return $self->{ $self->{shuttersDev} }{lastPos}{VAL} + if ( defined( $self->{ $self->{shuttersDev} }{lastPos} ) + and defined( $self->{ $self->{shuttersDev} }{lastPos}{VAL} ) ); +} + +sub getLastPosTimestamp { + my $self = shift; + + return $self->{ $self->{shuttersDev} }{lastPos}{TIME} + if ( defined( $self->{ $self->{shuttersDev} } ) + and defined( $self->{ $self->{shuttersDev} }{lastPos} ) + and defined( $self->{ $self->{shuttersDev} }{lastPos}{TIME} ) ); +} + +sub getLastManPos +{ # letzte ermittelte Position bevor die Position des Rolladen manuell (nicht über ASC) geändert wurde + my $self = shift; + + return $self->{ $self->{shuttersDev} }{lastManPos}{VAL} + if ( defined( $self->{ $self->{shuttersDev} }{lastManPos} ) + and defined( $self->{ $self->{shuttersDev} }{lastManPos}{VAL} ) ); +} + +sub getLastManPosTimestamp { + my $self = shift; + + return $self->{ $self->{shuttersDev} }{lastManPos}{TIME} + if ( defined( $self->{ $self->{shuttersDev} } ) + and defined( $self->{ $self->{shuttersDev} }{lastManPos} ) + and defined( $self->{ $self->{shuttersDev} }{lastManPos}{TIME} ) ); +} + +sub getInTimerFuncHash { + my $self = shift; + + return $self->{ $self->{shuttersDev} }{inTimerFuncHash}; +} + +sub getSunsetUnixTime { + my $self = shift; + + return $self->{ $self->{shuttersDev} }{sunsettime}; +} + +sub getSunriseUnixTime { + my $self = shift; + + return $self->{ $self->{shuttersDev} }{sunrisetime}; +} + +sub getRoommatesStatus { + my $self = shift; + my $loop = 0; + my @roState; + my %statePrio = ( + 'asleep' => 1, + 'gotosleep' => 2, + 'awoken' => 3, + 'home' => 4, + 'absent' => 5, + 'gone' => 6, + 'none' => 7 + ); + my $minPrio = 10; + + foreach my $ro ( split( ",", $shutters->getRoommates ) ) { + $shutters->setRoommate($ro); + my $currentPrio = $statePrio{ $shutters->_getRoommateStatus }; + $minPrio = $currentPrio if ( $minPrio > $currentPrio ); + } + + my %revStatePrio = reverse %statePrio; + return $revStatePrio{$minPrio}; +} + +sub getRoommatesLastStatus { + my $self = shift; + my $loop = 0; + my @roState; + my %statePrio = ( + 'asleep' => 1, + 'gotosleep' => 2, + 'awoken' => 3, + 'home' => 4, + 'absent' => 5, + 'gone' => 6, + 'none' => 7 + ); + my $minPrio = 10; + + foreach my $ro ( split( ",", $shutters->getRoommates ) ) { + $shutters->setRoommate($ro); + my $currentPrio = $statePrio{ $shutters->_getRoommateLastStatus }; + $minPrio = $currentPrio if ( $minPrio > $currentPrio ); + } + + my %revStatePrio = reverse %statePrio; + return $revStatePrio{$minPrio}; +} + +## Subklasse Attr von ASC_Shutters## +package ASC_Shutters::Attr; + +use strict; +use warnings; + +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw( + AttrVal) + ); +} + +sub getShuttersPlace { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_ShuttersPlace', 'window' ); +} + +sub getSelfDefenseExclude { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Self_Defense_Exclude', 'off' ); +} + +sub getShadingBrightnessSensor { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $self->{shuttersDev}, 'ASC_Shading_Brightness_Sensor', + $default ); +} + +sub getShadingBrightnessReading { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'brightness' if ( not defined($default) ); + return AttrVal( $self->{shuttersDev}, 'ASC_Shading_Brightness_Reading', + $default ); +} + +sub getOffset { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Drive_Offset', 0 ); +} + +sub getPosCmd { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Pos_Reading', 'pct' ); +} + +sub getOpenPos { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Open_Pos', 0 ); +} + +sub getVentilatePos { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Ventilate_Pos', 80 ); +} + +sub getClosedPos { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Closed_Pos', 100 ); +} + +sub getVentilateOpen { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Ventilate_Window_Open', 'off' ); +} + +sub getPosAfterComfortOpen { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Pos_after_ComfortOpen', 50 ); +} + +sub getPartyMode { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Partymode', 'off' ); +} + +sub getRoommates { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $self->{shuttersDev}, 'ASC_Roommate_Device', $default ); +} + +sub getRoommatesReading { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'state' if ( not defined($default) ); + return AttrVal( $self->{shuttersDev}, 'ASC_Roommate_Reading', $default ); +} + +sub getModeUp { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Mode_Up', 'off' ); +} + +sub getModeDown { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Mode_Down', 'off' ); +} + +sub getLockOut { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_lock-out', 'soft' ); +} + +sub getAntiFreeze { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Antifreeze', 'off' ); +} + +sub getAutoAstroModeMorning { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $self->{shuttersDev}, 'ASC_AutoAstroModeMorning', + $default ); +} + +sub getAutoAstroModeEvening { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $self->{shuttersDev}, 'ASC_AutoAstroModeEvening', + $default ); +} + +sub getAutoAstroModeMorningHorizon { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_AutoAstroModeMorningHorizon', + 0 ); +} + +sub getAutoAstroModeEveningHorizon { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_AutoAstroModeEveningHorizon', + 0 ); +} + +sub getUp { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Up', 'astro' ); +} + +sub getDown { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Down', 'astro' ); +} + +sub getTimeUpEarly { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Time_Up_Early', '04:30:00' ); +} + +sub getTimeUpLate { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Time_Up_Late', '09:00:00' ); +} + +sub getTimeDownEarly { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Time_Down_Early', '15:30:00' ); +} + +sub getTimeDownLate { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Time_Down_Late', '22:00:00' ); +} + +sub getTimeUpWeHoliday { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_Time_Up_WE_Holiday', + '04:00:00' ); +} + +sub getBrightnessMinVal { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_BrightnessMinVal', -1 ); +} + +sub getBrightnessMaxVal { + my $self = shift; + + return AttrVal( $self->{shuttersDev}, 'ASC_BrightnessMaxVal', -1 ); +} + +## Subklasse Readings von ASC_Shutters ## +package ASC_Shutters::Readings; + +use strict; +use warnings; + +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw( + ReadingsVal) + ); +} + +sub getBrightness { + my $self = shift; + + return ReadingsVal( $shutters->getShadingBrightnessSensor, + $shutters->getShadingBrightnessReading, 0 ); +} + +sub getStatus { + my $self = shift; + + return ReadingsVal( $self->{shuttersDev}, $shutters->getPosCmd, 0 ); +} + +sub getDelayCmd { + my $self = shift; + my $default = $self->{defaultarg}; + + return $self->{ $self->{shuttersDev} }{delayCmd}; +} + +## Klasse Fenster (Window) und die Subklassen Attr und Readings ## +package ASC_Window; +our @ISA = qw(ASC_Window::Attr ASC_Window::Readings); + +## Subklasse Attr von Klasse ASC_Window ## +package ASC_Window::Attr; + +use strict; +use warnings; + +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw( + AttrVal) + ); +} + +sub getSubTyp { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'twostate' if ( not defined($default) ); + return AttrVal( $self->{shuttersDev}, 'ASC_WindowRec_subType', $default ); +} + +sub _getWinDev { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $self->{shuttersDev}, 'ASC_WindowRec', $default ); +} + +## Subklasse Readings von Klasse ASC_Window ## +package ASC_Window::Readings; + +use strict; +use warnings; + +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw( + ReadingsVal) + ); +} + +sub getWinStatus { + my $self = shift; + my $default = $self->{defaultarg}; + + $default = 'closed' if ( not defined($default) ); + return ReadingsVal( $shutters->_getWinDev, 'state', $default ); +} + +## Klasse ASC_Roommate ## +package ASC_Roommate; + +use strict; +use warnings; + +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw( + ReadingsVal) + ); +} + +sub _getRoommateStatus { + my $self = shift; + my $roommate = $self->{roommate}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return ReadingsVal( $roommate, $shutters->getRoommatesReading, $default ); +} + +sub _getRoommateLastStatus { + my $self = shift; + my $roommate = $self->{roommate}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return ReadingsVal( $roommate, 'lastState', $default ); +} + +## Klasse ASC_Dev plus Subklassen ASC_Attr_Dev und ASC_Readings_Dev## +package ASC_Dev; +our @ISA = qw(ASC_Dev::Readings ASC_Dev::Attr); + +use strict; +use warnings; + +sub new { + my $class = shift; + my $self = { name => undef, }; + + bless $self, $class; + return $self; +} + +sub setName { + my ( $self, $name ) = @_; + + $self->{name} = $name if ( defined($name) ); + return $self->{name}; +} + +sub setDefault { + my ( $self, $defaultarg ) = @_; + + $self->{defaultarg} = $defaultarg if ( defined($defaultarg) ); + return $self->{defaultarg}; +} + +## Subklasse Readings ## +package ASC_Dev::Readings; + +use strict; +use warnings; + +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw( + readingsSingleUpdate + ReadingsVal + defs) + ); +} + +sub setDelayCmdReading { + my $self = shift; + my $name = $self->{name}; + my $hash = $defs{$name}; + + readingsSingleUpdate( $hash, + $shutters->getShuttersDev . '_lastDelayPosValue', + $shutters->getDelayCmd, 1 ); + return 0; +} + +sub setStateReading { + my $self = shift; + my $value = shift; + my $name = $self->{name}; + my $hash = $defs{$name}; + + readingsSingleUpdate( $hash, 'state', + ( defined($value) ? $value : $shutters->getLastDrive ), 1 ); + return 0; +} + +sub setPosReading { + my $self = shift; + my $name = $self->{name}; + my $hash = $defs{$name}; + + readingsSingleUpdate( $hash, $shutters->getShuttersDev . '_PosValue', + $shutters->getStatus, 1 ); + return 0; +} + +sub setLastPosReading { + my $self = shift; + my $name = $self->{name}; + my $hash = $defs{$name}; + + readingsSingleUpdate( $hash, $shutters->getShuttersDev . '_lastPosValue', + $shutters->getLastPos, 1 ); + return 0; +} + +sub getPartyMode { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return ReadingsVal( $name, 'partyMode', $default ); +} + +sub getLockOut { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return ReadingsVal( $name, 'lockOut', $default ); +} + +sub getSunriseTimeWeHoliday { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return ReadingsVal( $name, 'sunriseTimeWeHoliday', $default ); +} + +sub getMonitoredDevs { + my $self = shift; + my $name = $self->{name}; + + $self->{monitoredDevs} = ReadingsVal( $name, '.monitoredDevs', 'none' ); + return $self->{monitoredDevs}; +} + +sub getOutTemp { + my $self = shift; + my $name = $self->{name}; + + return ReadingsVal( $ascDev->getTempSensor, $ascDev->getTempReading, 100 ); +} + +sub getResidentsStatus { + my $self = shift; + my $name = $self->{name}; + + return ReadingsVal( $ascDev->_getResidentsDev, $ascDev->getResidentsReading, + 'none' ); +} + +sub getResidentsLastStatus { + my $self = shift; + my $name = $self->{name}; + + return ReadingsVal( $ascDev->_getResidentsDev, 'lastState', 'none' ); +} + +sub getSelfDefense { + my $self = shift; + my $name = $self->{name}; + + return ReadingsVal( $name, 'selfDefense', 'none' ); +} + +## Subklasse Attr ## +package ASC_Dev::Attr; + +use strict; +use warnings; + +use GPUtils qw(GP_Import); + +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw( + AttrVal) + ); +} + +sub getShuttersOffset { + my $self = shift; + my $name = $self->{name}; + + return AttrVal( $name, 'ASC_shuttersDriveOffset', 0 ); +} + +sub getBrightnessMinVal { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 8000 if ( not defined($default) ); + return AttrVal( $name, 'ASC_brightnessMinVal', $default ); +} + +sub getBrightnessMaxVal { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 20000 if ( not defined($default) ); + return AttrVal( $name, 'ASC_brightnessMaxVal', $default ); +} + +sub getAutoAstroModeEvening { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_autoAstroModeEvening', $default ); +} + +sub getAutoAstroModeEveningHorizon { + my $self = shift; + my $name = $self->{name}; + + return AttrVal( $name, 'ASC_autoAstroModeEveningHorizon', 0 ); +} + +sub getAutoAstroModeMorning { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_autoAstroModeMorning', $default ); +} + +sub getAutoAstroModeMorningHorizon { + my $self = shift; + my $name = $self->{name}; + + return AttrVal( $name, 'ASC_autoAstroModeMorningHorizon', 0 ); +} + +sub getAutoShuttersControlMorning { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_autoShuttersControlMorning', $default ); +} + +sub getAutoShuttersControlEvening { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_autoShuttersControlEvening', $default ); +} + +sub getAutoShuttersControlComfort { + my $self = shift; + my $name = $self->{name}; + + return AttrVal( $name, 'ASC_autoShuttersControlComfort', 'off' ); +} + +sub getAntifreezeTemp { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_antifreezeTemp', $default ); +} + +sub getTempSensor { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_temperatureSensor', $default ); +} + +sub getTempReading { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_temperatureReading', $default ); +} + +sub _getResidentsDev { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_residentsDevice', $default ); +} + +sub getResidentsReading { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'state' if ( not defined($default) ); + return AttrVal( $name, 'ASC_residentsDeviceReading', $default ); +} + +sub getRainSensor { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'none' if ( not defined($default) ); + return AttrVal( $name, 'ASC_rainSensorDevice', $default ); +} + +sub getRainSensorReading { + my $self = shift; + my $name = $self->{name}; + my $default = $self->{defaultarg}; + + $default = 'state' if ( not defined($default) ); + return AttrVal( $name, 'ASC_rainSensorReading', $default ); +} + +sub getRainSensorShuttersClosedPos { + my $self = shift; + my $name = $self->{name}; + + return AttrVal( $name, 'ASC_rainSensorShuttersClosedPos', 50 ); +} +1; + +=pod +=item device +=item summary Modul +=item summary_DE Modul zur Automatischen Rolladensteuerung auf Basis bestimmter Ereignisse + +=begin html + + +

Automatic shutter control - ASC

+ + +=end html + +=begin html_DE + + +

Automatische Rolladensteuerung - ASC

+ + +=end html_DE + +=cut diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 071c1f4dc..aa2e5b314 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -349,6 +349,7 @@ FHEM/73_PRESENCE.pm markusbloch Unterstuetzende Dienste FHEM/73_MPD.pm Wzut Multimedia FHEM/73_AMADCommBridge.pm CoolTux Sonstige Systeme FHEM/73_GardenaSmartBridge.pm CoolTux Sonstige Systeme +FHEM/73_AutoShuttersControl.pm CoolTux Automatisierung FHEM/74_NUKIBridge.pm CoolTux Sonstige Systeme FHEM/74_AMADDevice.pm CoolTux Sonstige Systeme FHEM/74_GardenaSmartDevice.pm CoolTux Sonstige Systeme