2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-28 11:01:59 +00:00

98_Heating_Control, 98_WeekdayTimer, 59_Twilight: big refactoring update with some minor improvements

git-svn-id: https://svn.fhem.de/fhem/trunk@8305 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
dietmar63 2015-03-27 19:40:25 +00:00
parent 3131a14ac2
commit 76df7c129b
3 changed files with 704 additions and 636 deletions

View File

@ -364,7 +364,7 @@ sub Twilight_WeatherCallback(@) {
################################################################################ ################################################################################
sub Twilight_StandardTimerSet($) { sub Twilight_StandardTimerSet($) {
my ($hash) = @_; my ($hash) = @_;
my $midnight = time() - Twilight_midnight_seconds(time()) + 24*3600 + 30; my $midnight = time() - Twilight_midnight_seconds(time()) + 24*3600 + 1;
myRemoveInternalTimer ("Midnight", $hash); myRemoveInternalTimer ("Midnight", $hash);
myInternalTimer ("Midnight", $midnight, "Twilight_Midnight", $hash, 0); myInternalTimer ("Midnight", $midnight, "Twilight_Midnight", $hash, 0);

View File

@ -3,7 +3,6 @@
# #
# 98_Heating_Control.pm # 98_Heating_Control.pm
# written by Dietmar Ortmann # written by Dietmar Ortmann
# modified by Tobias Faust
# #
# This file is part of fhem. # This file is part of fhem.
# #
@ -26,24 +25,22 @@ package main;
use strict; use strict;
use warnings; use warnings;
use POSIX; use POSIX;
use Time::Local 'timelocal_nocheck'; ########################################################################
#####################################
sub Heating_Control_Initialize($) sub Heating_Control_Initialize($)
{ {
my ($hash) = @_; my ($hash) = @_;
if(!$modules{Twilight}{LOADED} && -f "$attr{global}{modpath}/FHEM/59_Twilight.pm") { if(!$modules{WeekdayTimer}{LOADED} && -f "$attr{global}{modpath}/FHEM/98_WeekdayTimer.pm") {
my $ret = CommandReload(undef, "59_Twilight"); my $ret = CommandReload(undef, "98_WeekdayTimer");
Log3 undef, 1, $ret if($ret); Log3 undef, 1, $ret if($ret);
} }
# Consumer # Consumer
$hash->{SetFn} = "Heating_Control_Set"; $hash->{SetFn} = "Heating_Control_Set";
$hash->{AttrFn} = "Heating_Control_Attr";
$hash->{DefFn} = "Heating_Control_Define"; $hash->{DefFn} = "Heating_Control_Define";
$hash->{UndefFn} = "Heating_Control_Undef"; $hash->{UndefFn} = "Heating_Control_Undef";
$hash->{GetFn} = "Heating_Control_Get"; $hash->{GetFn} = "Heating_Control_Get";
$hash->{AttrFn} = "Heating_Control_Attr";
$hash->{UpdFn} = "Heating_Control_Update"; $hash->{UpdFn} = "Heating_Control_Update";
$hash->{AttrList}= "disable:0,1 delayedExecutionCond windowSensor ". $hash->{AttrList}= "disable:0,1 delayedExecutionCond windowSensor ".
$readingFnAttributes; $readingFnAttributes;
@ -67,519 +64,32 @@ sub Heating_Control_Set($@) {
} }
return undef; return undef;
} }
################################################################################ ########################################################################
sub Heating_Control_Get($@) sub Heating_Control_Get($@) {
{ return WeekdayTimer_Get($@);
my ($hash, @a) = @_;
return "argument is missing" if(int(@a) != 2);
$hash->{LOCAL} = 1;
delete $hash->{LOCAL};
my $reading= $a[1];
my $value;
if(defined($hash->{READINGS}{$reading})) {
$value= $hash->{READINGS}{$reading}{VAL};
} else {
return "no such reading: $reading";
}
return "$a[0] $reading => $value";
} }
################################################################################ ########################################################################
sub Heating_Control_Define($$) sub Heating_Control_Define($$){
{
my ($hash, $def) = @_; my ($hash, $def) = @_;
my %longDays = ( "de" => ["Sonntag", "Montag","Dienstag","Mittwoch", "Donnerstag","Freitag", "Samstag" ], my $ret = WeekdayTimer_Define($hash, $def);
"en" => ["Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], $hash->{helper}{DESIRED_TEMP_READING} = "";
"fr" => ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi","Samedi" ]); return $ret;
my %shortDays = ( "de" => ["so","mo","di","mi","do","fr","sa"],
"en" => ["su","mo","tu","we","th","fr","sa"],
"fr" => ["di","lu","ma","me","je","ve","sa"]);
my @a = split("[ \t]+", $def);
return "Usage: define <name> $hash->{TYPE} <device> <language> <switching times> <condition|command>"
if(@a < 4);
my $name = shift @a;
my $type = shift @a;
my $device = shift @a;
my @switchingtimes;
my $conditionOrCommand = "";
# ggf. language optional Parameter
my $language = shift @a;
my $langRegExp = "(";
foreach my $l (keys(%shortDays)) {
$langRegExp .= $l . "|";
}
$langRegExp =~ s/\|$//g;
$langRegExp .= ")";
if ($language =~ m/^$langRegExp$/g) {
$hash->{LANGUAGE} = $language;
} else {
Log3 $hash, 3, "[$name] illegal language: $language, use one of $langRegExp" if (length($language) == 2);
unshift (@a,$language) if (length($language) != 2) ;
$hash->{LANGUAGE} = "de";
}
$language = $hash->{LANGUAGE};
# test if device is defined
Log3 $hash, 3, "[$name] invalid device, <$device> not found" if(!$defs{$device});
#fuer den modify Altlasten bereinigen
delete($hash->{TIME_AS_PERL}) if($hash->{TIME_AS_PERL});
delete($hash->{helper}{CONDITION}) if($hash->{helper}{CONDITION});
delete($hash->{helper}{COMMAND}) if($hash->{helper}{COMMAND});
delete($hash->{helper}{SWITCHINGTIMES}) if($hash->{helper}{SWITCHINGTIMES});
delete($hash->{helper}{SWITCHINGTIME}) if($hash->{helper}{SWITCHINGTIME});
foreach my $l (keys(%shortDays)) {
for (my $w=0; $w<7; $w++) {
delete($hash->{"PROFILE ".($w).": ".$longDays{$l}[$w]}) if($hash->{"PROFILE ".($w).": ".$longDays{$l}[$w]});
}
}
for(my $i=0; $i<@a; $i++) {
#pruefen auf Angabe eines Schaltpunktes
my @t = split(/\|/, $a[$i]);
my $anzahl = @t;
if ( $anzahl >= 2 && $anzahl <= 3) {
push(@switchingtimes, $a[$i]);
} else {
#der Rest ist das auzufuehrende Kommando/condition
$conditionOrCommand = trim(join(" ", @a[$i..@a-1]));
last;
}
}
# wenn keine switchintime angegeben ist, dann Fehler
Log3 $hash, 3, "no Switchingtime found in <$conditionOrCommand>, check first parameter" if (@switchingtimes == 0);
$hash->{NAME} = $name;
$hash->{DEVICE} = $device;
$modules{$hash->{TYPE}}{defptr}{$hash->{NAME}} = $hash;
$hash->{helper}{SWITCHINGTIMES} = join(" ", @switchingtimes);
if($conditionOrCommand =~ m/^\(.*\)$/g) { #condition (*)
$hash->{helper}{CONDITION} = $conditionOrCommand;
} elsif(length($conditionOrCommand) > 0 ) {
$hash->{helper}{COMMAND} = $conditionOrCommand;
}
# jetzt die switchingtimes und Tagesangaben verarbeiten.
if (!Heating_Control_ParseSwitchingProfile($hash, \@switchingtimes, \$shortDays{$language})) {
return;
}
# Profile sortiert aufbauen
for (my $d=0; $d<=6; $d++) {
foreach my $st (sort (keys %{ $hash->{helper}{SWITCHINGTIME}{$d} })) {
my $para = $hash->{helper}{SWITCHINGTIME}{$d}{$st};
$hash->{"PROFILE ".($d).": ".$longDays{$language}[$d]} .= sprintf("%s %s, ", substr ($st,0,5), $para);
}
}
my $now = time();
if ($hash->{TIME_AS_PERL} ) {
Heating_Control_UpdatePerlTime_TimerSet($hash);
}
$hash->{PERLTIMEUPDATEMODE} = 0 if (!defined($hash->{PERLTIMEUPDATEMODE}));
myRemoveInternalTimer("Update", $hash);
myInternalTimer ("Update", $now+1, "$hash->{TYPE}_Update", $hash, 0);
readingsBeginUpdate ($hash);
readingsBulkUpdate ($hash, "nextUpdate", strftime("Heute, %H:%M:%S",localtime($now+30)));
readingsBulkUpdate ($hash, "nextValue", "???");
readingsBulkUpdate ($hash, "state", "waiting...");
readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1));
return undef;
} }
################################################################################ ########################################################################
sub Heating_Control_ParseSwitchingProfile($$$) { sub Heating_Control_Undef($$){
my ($hash, $switchingtimes, $shortDays) = @_;
my $name = $hash->{NAME};
my $language = $hash->{LANGUAGE};
my %dayNumber=();
my $daysRegExp = "(";
for(my $idx=0; $idx<7; $idx++) {
my $day = @{$$shortDays}[$idx];
$dayNumber{$day} = $idx;
$daysRegExp .= $day . "|";
}
$daysRegExp =~ s/\|$//g;
$daysRegExp .= ")";
my (@st, @days, $daylist, $time, $timeString, $para);
for(my $i=0; $i<@{$switchingtimes}; $i++) {
@st = split(/\|/, @{$switchingtimes}[$i]);
if ( @st == 2) {
$daylist = "1234567"; #jeden Tag/Woche ist vordefiniert
$time = $st[0];
$para = $st[1];
} elsif ( @st == 3) {
$daylist = lc($st[0]);
$time = $st[1];
$para = $st[2];
}
my %hdays=();
# Angaben der Tage verarbeiten
# Aufzaehlung 1234 ...
if ( $daylist =~ m/^(\d){0,7}$/g) {
$daylist =~ s/7/0/g;
@days = split("", $daylist);
@hdays{@days}=1;
# Aufzaehlung Sa,So,... | Mo-Di,Do,Fr-Mo
} elsif ($daylist =~ m/^($daysRegExp(,|-|$)){0,7}$/g ) {
my $oldDay = "", my $oldDel = "";
for (;length($daylist);) {
my $day = substr($daylist,0,2,"");
my $del = substr($daylist,0,1,"");
my @subDays;
if ($oldDel eq "-" ){
# von bis Angabe: Mo-Di
my $low = $dayNumber{$oldDay};
my $high = $dayNumber{$day};
if ($low <= $high) {
@subDays = ($low .. $high);
} else {
#@subDays = ($dayNumber{so} .. $high, $low .. $dayNumber{sa});
@subDays = ( 00 .. $high, $low .. 06);
}
@hdays{@subDays}=1;
} else {
#einzelner Tag: Sa
$hdays{$dayNumber{$day}} = 1;
}
$oldDay = $day;
$oldDel = $del;
}
} else{
Log3 $hash, 1, "invalid daylist in $name <$daylist> use one of 123...|Sa,So,...|Mo-Di,Do,Fr|Su-Th,We|Lu-Me";
return 0;
}
@days = sort(SortNumber keys %hdays);
my $TIME_AS_PERL = 0;
if($time =~ m/^\{.*\}$/g) {
$TIME_AS_PERL = 1;
$hash->{TIME_AS_PERL} |= 1;
}
my $now = time();
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($now);
my $listOfDays = "";
for (my $d=0; $d<@days; $d++) {
# Zeitangabe verarbeiten.
if ($TIME_AS_PERL) { # Perlausdruck {*}
my $date = $now+($d-$wday)*86400;
$timeString = '{ my $date='."$date;" .$time."}";
$timeString = eval( $timeString ); # must deliver HH:MM[:SS]
$@ =~ s/\n/ /g; Log3 ($hash, 3, "[$hash->{NAME}] " . $@) if ($@);
} else {
$timeString = $time;
}
if ($timeString =~ m/^[0-2][0-9]:[0-5][0-9]$/g) { # HH:MM
$timeString .= ":00"; # HH:MM:SS erzeugen
} elsif ($timeString =~ m/^[0-2][0-9](:[0-5][0-9]){2,2}$/g) { # HH:MM:SS
; # ok.
} else {
Log3 $hash, 1, "[$name] invalid time <$timeString> HH:MM[:SS]";
#return 0;
}
$listOfDays .= @{$$shortDays}[$days[$d]] . ",";
$hash->{helper}{SWITCHINGTIME}{$days[$d]}{$timeString} = $para;
}
$listOfDays =~ s/,$//g;
Log3 $hash, 5, "[$name] Switchingtime: @{$switchingtimes}[$i] : $listOfDays -> $time -> $para ";
}
return 1;
}
################################################################################
sub Heating_Control_Undef($$) {
my ($hash, $arg) = @_; my ($hash, $arg) = @_;
return WeekdayTimer_Undef($hash, $arg);
myRemoveInternalTimer("Update", $hash);
myRemoveInternalTimer("UpdatePerlTime", $hash);
delete $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}};
return undef;
} }
################################################################################ ########################################################################
sub Heating_Control_UpdatePerlTime_TimerSet($) { sub Heating_Control_Update($){
my ($hash) = @_; my ($hash) = @_;
return WeekdayTimer_Update($hash);
my $now = time();
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
my $secToMidnight = 24*3600 -(3600*$hour + 60*$min + $sec) + 10*60;
#my $secToMidnight = + 01*60;
myRemoveInternalTimer("UpdatePerlTime", $hash);
myInternalTimer ("UpdatePerlTime", $now+$secToMidnight, "$hash->{TYPE}_UpdatePerlTime", $hash, 0);
} }
################################################################################ ################################################################################
sub Heating_Control_UpdatePerlTime($) { sub Heating_Control_SetTimerOfDay($) {
my ($myHash) = @_; my ($hash) = @_;
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); return WeekdayTimer_SetTimerOfDay($hash);
return if (!defined($hash));
if (defined($hash->{TIME_AS_PERL})) {
$hash->{PERLTIMEUPDATEMODE} = 1;
Heating_Control_Define($hash, $hash->{NAME} . " " . $hash->{TYPE} . " " . $hash->{DEF} );
}
}
########################################################################
sub Heating_Control_Update($) {
my ($myHash) = @_;
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
return if (!defined($hash));
my $mod = "[".$hash->{NAME} ."] "; ###
my $name = $hash->{NAME};
my $now = time() + 5; # garantiert > als die eingestellte Schlatzeit
# Schaltparameter ermitteln
my ($nowSwitch,$nextSwitch,$newParam,$nextParam) = Heating_Control_akt_next_param($now, $hash);
# Fenserkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern
if (Heating_Control_FensterOffen($hash, $nowSwitch, $newParam)) {
return;
}
# ggf. Device schalten
Heating_Control_Device_Schalten($hash, $now, $nowSwitch, $newParam);
$hash->{PERLTIMEUPDATEMODE} = 0;
Log3 $hash, 4, $mod .strftime('Next switch %d.%m.%Y %H:%M:%S',localtime($nextSwitch));
# Timer und Readings setzen.
myRemoveInternalTimer("Update", $hash);
myInternalTimer ("Update", $nextSwitch, "$hash->{TYPE}_Update", $hash, 0);
my $active = 1;
if (defined $hash->{helper}{CONDITION}) {
$active = AnalyzeCommandChain(undef, "{".$hash->{helper}{CONDITION}."}");
}
readingsBeginUpdate($hash);
readingsBulkUpdate ($hash, "nextUpdate", strftime("%d.%m.%Y %H:%M:%S",localtime($nextSwitch)));
readingsBulkUpdate ($hash, "nextValue", $nextParam);
readingsBulkUpdate ($hash, "state", $active ? $newParam : "inactive" );
readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1));
return 1;
}
########################################################################
sub Heating_Control_FensterOffen ($$$) {
my ($hash, $tim, $event) = @_;
my $mod = "[".$hash->{NAME} ."]";
my $verzoegerteAusfuehrungCond = AttrVal($hash->{NAME}, "delayedExecutionCond", "0");
my %specials= (
"%HEATING_CONTROL" => $hash->{NAME},
"%WEEKDAYTIMER" => $hash->{NAME},
"%TIME" => $tim,
"%NAME" => $hash->{DEVICE},
"%EVENT" => $event
);
$verzoegerteAusfuehrungCond = EvalSpecials($verzoegerteAusfuehrungCond, %specials);
my $verzoegerteAusfuehrung = eval($verzoegerteAusfuehrungCond);
if ($verzoegerteAusfuehrung) {
if (!defined($hash->{VERZOEGRUNG})) {
Log3 $hash, 3, "$mod switch of $hash->{DEVICE} delayed - $verzoegerteAusfuehrungCond is TRUE";
}
myRemoveInternalTimer("Update", $hash);
myInternalTimer ("Update", time()+60, "$hash->{TYPE}_Update", $hash, 0);
$hash->{VERZOEGRUNG} = 1;
return 1
}
my %contacts = ( "CUL_FHTTK" => { "READING" => "Window", "STATUS" => "(Open)", "MODEL" => "r" },
"CUL_HM" => { "READING" => "state", "STATUS" => "(open|tilted)", "MODEL" => "r" },
"MAX" => { "READING" => "state", "STATUS" => "(open)", "MODEL" => "r" },
"WeekdayTimer" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" },
"Heating_Control" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" }
);
my $fensterKontakte = AttrVal($hash->{NAME}, "windowSensor", "")." ".$hash->{NAME};
$fensterKontakte =~ s/^\s+//;
$fensterKontakte =~ s/\s+$//;
Log3 $hash, 5, "$mod list of senors found: '$fensterKontakte'";
if ($fensterKontakte ne "" ) {
my @kontakte = split("[ \t]+", $fensterKontakte);
foreach my $fk (@kontakte) {
if(!$defs{$fk}) {
Log3 $hash, 3, "$mod sensor <$fk> not found - check name.";
} else {
my $fk_hash = $defs{$fk};
my $fk_typ = $fk_hash->{TYPE};
if (!defined($contacts{$fk_typ})) {
Log3 $hash, 3, "$mod TYPE '$fk_typ' of $fk not yet supported, $fk ignored - inform maintainer";
} else {
my $reading = $contacts{$fk_typ}{READING};
my $statusReg = $contacts{$fk_typ}{STATUS};
my $model = $contacts{$fk_typ}{MODEL};
my $windowStatus;
if ($model eq "r") { ### Reading, sonst Attribut
$windowStatus = ReadingsVal($fk,$reading,"nF");
}else{
$windowStatus = AttrVal ($fk,$reading,"nF");
}
if ($windowStatus eq "nF") {
Log3 $hash, 3, "$mod Reading/Attribute '$reading' of $fk not found, $fk ignored - inform maintainer" if ($model eq "r");
} else {
Log3 $hash, 5, "$mod sensor '$fk' Reading/Attribute '$reading' is '$windowStatus'";
if ($windowStatus =~ m/^$statusReg$/g) {
if (!defined($hash->{VERZOEGRUNG})) {
Log3 $hash, 3, "$mod switch of $hash->{DEVICE} delayed - sensor '$fk' Reading/Attribute '$reading' is '$windowStatus'";
}
myRemoveInternalTimer("Update", $hash);
myInternalTimer ("Update", time()+60, "$hash->{TYPE}_Update", $hash, 0);
$hash->{VERZOEGRUNG} = 1;
return 1
}
}
}
}
}
}
if ($hash->{VERZOEGRUNG}) {
Log3 $hash, 3, "$mod delay of switching $hash->{DEVICE} stopped.";
}
delete $hash->{VERZOEGRUNG};
return 0;
}
########################################################################
sub Heating_Control_akt_next_param($$) {
my ($now, $hash) = @_;
my $mod = "[".$hash->{NAME} ."] "; ###
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
my ($nextParam, $next, $nextSwitch, $nowSwitch, $newParam) = (0,0,0,0,0);
# aktuellen und nächsten Schaltzeitpunkt ermitteln.
my $startIdx;
for (my $d=-1; $d>=-7; $d--) {
my $wd = ($d+$wday) % 7;
my $anzSwitches = keys %{ $hash->{helper}{SWITCHINGTIME}{$wd} };
$startIdx = $d;
last if ($anzSwitches > 0);
}
for (my $d=$startIdx; $d<=7; $d++) {
#ueber jeden Tag
last if ($nextSwitch > 0);
my $wd = ($d+$wday) % 7;
foreach my $st (sort (keys %{ $hash->{helper}{SWITCHINGTIME}{$wd} })) {
# Tagediff + Sekunden des Tages addieren
my @t = split(/:/, $st); # HH MM SS
#my $secondsToSwitch = $d*24*3600 + 3600*($t[0] - $hour) + 60*($t[1] - $min) + $t[2] - $sec;
my $next = zeitErmitteln ($now, $t[0], $t[1], $t[2], $d);
my $secondsToSwitch = $next - $now;
if ($secondsToSwitch<=10 && $secondsToSwitch>=-20) {
Log3 $hash, 4, $mod."Jetzt:".strftime('%d.%m.%Y %H:%M:%S',localtime($now))." -> Next: ".strftime('%d.%m.%Y %H:%M:%S',localtime($next))." -> Param: $hash->{helper}{SWITCHINGTIME}{$wd}{$st} ".$secondsToSwitch;
}
if ($secondsToSwitch<=0) {
$newParam = $hash->{helper}{SWITCHINGTIME}{$wd}{$st};
$nowSwitch = $next;
} else {
$nextParam = $hash->{helper}{SWITCHINGTIME}{$wd}{$st};
$nextSwitch = $next;
last;
}
}
}
if ($now > $nextSwitch) {
$nextSwitch = max ($now+60,$nextSwitch);
}
return ($nowSwitch,$nextSwitch,$newParam,$nextParam);
}
################################################################################
sub Heating_Control_Device_Schalten($$$$) {
my ($hash, $now, $nowSwitch, $newParam) = @_;
my $command = "";
my $mod = "[".$hash->{NAME} ."] "; ###
#modifier des Zieldevices auswaehlen
my $setModifier = Heating_Control_isHeizung($hash);
# Kommando aufbauen
if (defined $hash->{helper}{CONDITION}) {
$command = '{ fhem("set @ '. $setModifier .' %") if' . $hash->{helper}{CONDITION} . '}';
} elsif (defined $hash->{helper}{COMMAND}) {
$command = $hash->{helper}{COMMAND};
} else {
$command = '{ fhem("set @ '. $setModifier .' %") }';
}
my $isHeating = $setModifier gt "";
my $aktParam = ReadingsVal($hash->{DEVICE}, $setModifier, 0);
$aktParam = sprintf("%.1f", $aktParam) if ($isHeating && $aktParam =~ m/^[0-9]{1,3}$/i);
$newParam = sprintf("%.1f", $newParam) if ($isHeating && $newParam =~ m/^[0-9]{1,3}$/i);
Log3 $hash, 4, $mod .strftime('%d.%m.%Y %H:%M:%S',localtime($nowSwitch))." ; aktParam: $aktParam ; newParam: $newParam";
my $disabled = AttrVal($hash->{NAME}, "disable", 0);
my $disabled_txt = $disabled ? " " : " not";
Log3 $hash, 4, $mod . "is$disabled_txt disabled";
#Kommando ausführen
my $secondsSinceSwitch = $nowSwitch - $now;
if ($hash->{PERLTIMEUPDATEMODE} == 1) {
Log3 $hash, 5, $mod."no switch of device in PERLTIMEUPDATEMODE at 00:10 o'clock";
return;
}
my $setAllTempMode = defined ($hash->{setAllTempMode});
if (defined $hash->{helper}{COMMAND} || ($nowSwitch gt "" && $aktParam ne $newParam )) {
if (!$setAllTempMode && !$setModifier && $secondsSinceSwitch < -60) {
Log3 $hash, 5, $mod."no switch in the yesterdays because of the devices type($hash->{DEVICE} is not a heating).";
} else {
if ($command && !$disabled) {
$newParam =~ s/:/ /g;
$command = SemicolonEscape($command);
my %specials= (
"%NAME" => $hash->{DEVICE},
"%EVENT" => $newParam,
);
$command= EvalSpecials($command, %specials);
Log3 $hash, 4, $mod."command: $command executed";
my $ret = AnalyzeCommandChain(undef, $command);
Log3 ($hash, 3, $ret) if($ret);
}
}
}
} }
######################################################################## ########################################################################
sub Heating_Control_Attr($$$) { sub Heating_Control_Attr($$$) {
@ -592,90 +102,16 @@ sub Heating_Control_Attr($$$) {
return undef; return undef;
} }
######################################################################## ########################################################################
sub Heating_Control_isHeizung($) {
my ($hash) = @_;
my %setmodifiers =
("FHT" => "desired-temp",
"PID20" => "desired",
"EnOcean" => { "subTypeReading" => "subType", "setModifier" => "desired-temp",
"roomSensorControl.05" => 1,
"hvac.01" => 1 },
"MAX" => { "subTypeReading" => "type", "setModifier" => "desiredTemperature",
"HeatingThermostatPlus" => 1,
"HeatingThermostat" => 1,
"WallMountedThermostat" => 1 },
"CUL_HM" => { "subTypeReading" => "model","setModifier" => "desired-temp",
"HM-CC-TC" => 1,
"HM-TC-IT-WM-W-EU" => 1,
"HM-CC-RT-DN" => 1 } );
my $dHash = $defs{$hash->{DEVICE}}; ###
my $dType = $dHash->{TYPE};
return "" if (!defined($dType));
my $setModifier = $setmodifiers{$dType};
$setModifier = "" if (!defined($setModifier));
if (ref($setModifier)) {
my $subTypeReading = $setmodifiers{$dType}{subTypeReading};
my $model;
if ($subTypeReading eq "type" ) {
$model = $dHash->{type};
} else {
$model = AttrVal($hash->{DEVICE}, $subTypeReading, "nF");
}
if (defined($setmodifiers{$dType}{$model})) {
$setModifier = $setmodifiers{$dType}{setModifier}
} else {
$setModifier = "";
}
}
return $setModifier;
}
################################################################################
sub Heating_Control_SetAllTemps() { # {Heating_Control_SetAllTemps()} sub Heating_Control_SetAllTemps() { # {Heating_Control_SetAllTemps()}
foreach my $hc ( sort keys %{$modules{Heating_Control}{defptr}} ) { foreach my $hc ( sort keys %{$modules{Heating_Control}{defptr}} ) {
my $hash = $modules{Heating_Control}{defptr}{$hc}; my $hash = $modules{Heating_Control}{defptr}{$hc};
if($hash->{helper}{CONDITION}) { Heating_Control_SetTimer($hash);
if (!(eval ($hash->{helper}{CONDITION}))) { Log3 undef, 3, "Heating_Control_SetTimer() for $hash->{NAME} done!";
readingsSingleUpdate ($hash, "state", "inactive", 1);
next;
}
}
my $myHash->{HASH}=$hash;
$hash->{setAllTempMode} = 1;
Heating_Control_Update($myHash);
delete $hash->{setAllTempMode};
Log3 undef, 3, "Heating_Control_Update() for $hash->{NAME} done!";
} }
Log3 undef, 3, "Heating_Control_SetAllTemps() done!"; Log3 undef, 3, "Heating_Control_SetAllTemps() done!";
} }
########################################################################
sub zeitErmitteln ($$$$$) {
my ($now, $hour, $min, $sec, $days) = @_;
my @jetzt_arr = localtime($now);
#Stunden Minuten Sekunden
$jetzt_arr[2] = $hour; $jetzt_arr[1] = $min; $jetzt_arr[0] = $sec;
$jetzt_arr[3] += $days;
my $next = timelocal_nocheck(@jetzt_arr);
return $next;
}
########################################################################
sub SortNumber {
if($a < $b)
{ return -1; }
elsif($a == $b)
{ return 0; }
else
{ return 1; }
}
1; 1;
@ -1004,3 +440,4 @@ sub SortNumber {
=end html_DE =end html_DE
=cut =cut

View File

@ -3,6 +3,7 @@
# #
# 98_WeekdayTimer.pm # 98_WeekdayTimer.pm
# written by Dietmar Ortmann # written by Dietmar Ortmann
# modified by Tobias Faust
# #
# This file is part of fhem. # This file is part of fhem.
# #
@ -25,27 +26,43 @@ package main;
use strict; use strict;
use warnings; use warnings;
use POSIX; use POSIX;
########################################################################
sub WeekdayTimer_Initialize($) use Time::Local 'timelocal_nocheck';
{
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
################################################################################
sub WeekdayTimer_Initialize($){
my ($hash) = @_; my ($hash) = @_;
if(!$modules{Heating_Control}{LOADED} && -f "$attr{global}{modpath}/FHEM/98_Heating_Control.pm") { if(!$modules{Twilight}{LOADED} && -f "$attr{global}{modpath}/FHEM/59_Twilight.pm") {
my $ret = CommandReload(undef, "98_Heating_Control"); my $ret = CommandReload(undef, "59_Twilight");
Log3 undef, 1, $ret if($ret); Log3 undef, 1, $ret if($ret);
} }
# Consumer # Consumer
$hash->{SetFn} = "WeekdayTimer_Set"; $hash->{SetFn} = "WeekdayTimer_Set";
$hash->{AttrFn} = "WeekdayTimer_Attr";
$hash->{DefFn} = "WeekdayTimer_Define"; $hash->{DefFn} = "WeekdayTimer_Define";
$hash->{UndefFn} = "WeekdayTimer_Undef"; $hash->{UndefFn} = "WeekdayTimer_Undef";
$hash->{GetFn} = "WeekdayTimer_Get"; $hash->{GetFn} = "WeekdayTimer_Get";
$hash->{AttrFn} = "WeekdayTimer_Attr";
$hash->{UpdFn} = "WeekdayTimer_Update"; $hash->{UpdFn} = "WeekdayTimer_Update";
$hash->{AttrList}= "disable:0,1 delayedExecutionCond ". $hash->{AttrList}= "disable:0,1 delayedExecutionCond ".
$readingFnAttributes; $readingFnAttributes;
} }
################################################################################ ################################################################################
sub WeekdayTimer_InitHelper($) {
my ($hash) = @_;
$hash->{longDays} = { "de" => ["Sonntag", "Montag","Dienstag","Mittwoch", "Donnerstag","Freitag", "Samstag", "Wochenende", "Werktags" ],
"en" => ["Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "weekend", "weekdays" ],
"fr" => ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi","Samedi", "weekend", "jours de la semaine"]};
$hash->{shortDays} = { "de" => ["so", "mo", "di", "mi", "do", "fr", "sa", '$we', '!$we' ],
"en" => ["su", "mo", "tu", "we", "th", "fr", "sa", '$we', '!$we' ],
"fr" => ["di", "lu", "ma", "me", "je", "ve", "sa", '$we', '!$we' ]};
}
################################################################################
sub WeekdayTimer_Set($@) { sub WeekdayTimer_Set($@) {
my ($hash, @a) = @_; my ($hash, @a) = @_;
@ -64,34 +81,657 @@ sub WeekdayTimer_Set($@) {
} }
return undef; return undef;
} }
######################################################################## ################################################################################
sub WeekdayTimer_Get($@) { sub WeekdayTimer_Get($@) {
return Heating_Control_Get($@); my ($hash, @a) = @_;
} return "argument is missing" if(int(@a) != 2);
########################################################################
sub WeekdayTimer_Define($$){
my ($hash, $def) = @_;
my $ret = Heating_Control_Define($hash, $def); $hash->{LOCAL} = 1;
$hash->{helper}{DESIRED_TEMP_READING} = ""; delete $hash->{LOCAL};
return $ret; my $reading= $a[1];
my $value;
if(defined($hash->{READINGS}{$reading})) {
$value= $hash->{READINGS}{$reading}{VAL};
} else {
return "no such reading: $reading";
}
return "$a[0] $reading => $value";
} }
######################################################################## ################################################################################
sub WeekdayTimer_Undef($$){ sub WeekdayTimer_Undef($$) {
my ($hash, $arg) = @_; my ($hash, $arg) = @_;
return Heating_Control_Undef($hash, $arg);
foreach my $time (keys %{$hash->{profil}}) {
myRemoveInternalTimer($time, $hash);
}
myRemoveInternalTimer("SetTimerOfDay", $hash);
delete $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}};
return undef;
} }
######################################################################## ################################################################################
sub WeekdayTimer_UpdatePerlTime($) { sub WeekdayTimer_Define($$) {
my ($hash, $def) = @_;
WeekdayTimer_InitHelper($hash);
my @a = split("[ \t]+", $def);
return "Usage: define <name> $hash->{TYPE} <device> <language> <switching times> <condition|command>"
if(@a < 4);
#fuer den modify Altlasten bereinigen
delete($hash->{helper});
my $name = shift @a;
my $type = shift @a;
my $device = shift @a;
WeekdayTimer_DeleteTimer($hash);
my $delVariables = "(CONDITION|COMMAND|profile|Profil)";
map { delete $hash->{$_} if($_=~ m/^$delVariables.*/g) } keys %{$hash};
my $language = WeekdayTimer_Language ($hash, \@a);
my $idx = 0;
$hash->{dayNumber} = {map {$_ => $idx++} @{$hash->{shortDays}{$language}}};
$hash->{helper}{daysRegExp} = '(' . join ("|", @{$hash->{shortDays}{$language}}) . ")";
$hash->{helper}{daysRegExpMessage} = $hash->{helper}{daysRegExp};
$hash->{helper}{daysRegExp} =~ s/\$/\\\$/g;
$hash->{helper}{daysRegExp} =~ s/\!/\\\!/g;
WeekdayTimer_GlobalDaylistSpec ($hash, \@a);
my @switchingtimes = WeekdayTimer_gatherSwitchingTimes (\@a);
my $conditionOrCommand = join (" ", @a);
# test if device is defined
Log3 ($hash, 3, "[$name] invalid device, <$device> not found") if(!$defs{$device});
# wenn keine switchintime angegeben ist, dann Fehler
Log3 ($hash, 3, "[$name] no valid Switchingtime found in <$conditionOrCommand>, check first parameter") if (@switchingtimes == 0);
$hash->{TYPE} = $type;
$hash->{NAME} = $name;
$hash->{DEVICE} = $device;
$hash->{SWITCHINGTIMES} = \@switchingtimes;
$modules{$hash->{TYPE}}{defptr}{$hash->{NAME}} = $hash;
if($conditionOrCommand =~ m/^\(.*\)$/g) { #condition (*)
$hash->{CONDITION} = $conditionOrCommand;
} elsif(length($conditionOrCommand) > 0 ) {
$hash->{COMMAND} = $conditionOrCommand;
}
#WeekdayTimer_DeleteTimer($hash); am Anfang dieser Routine
WeekdayTimer_Profile ($hash);
WeekdayTimer_SetTimer ($hash);
WeekdayTimer_SetTimerForMidnightUpdate( { HASH => $hash} );
return undef;
}
################################################################################
sub WeekdayTimer_Profile($) {
my $hash = shift;
my $nochZuAendern = 0; # $d
my $language = $hash->{LANGUAGE};
my %longDays = %{$hash->{longDays}};
delete $hash->{profil};
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time());
my $now = time();
# ------------------------------------------------------------------------------
foreach my $st (@{$hash->{SWITCHINGTIMES}}) {
my ($tage,$time,$parameter) = WeekdayTimer_SwitchingTime ($hash, $st);
foreach my $d (@{$tage}) {
my $dayOfEchteZeit = $d;
if ($d==7) { # Weekend
$dayOfEchteZeit = ($wday ~~ [1..5]) ? 6 : $wday; # ggf. Samstag
} elsif ($d==8) { # day of Week
$dayOfEchteZeit = ($wday ~~ [0..6]) ? 1 : $wday; # ggf. Montag
}
my $echtZeit = WeekdayTimer_EchteZeit($hash, $dayOfEchteZeit, $time);
$hash->{profile}{$d}{$echtZeit} = $parameter;
}
}
# ------------------------------------------------------------------------------
foreach my $st (@{$hash->{SWITCHINGTIMES}}) {
my ($tage,$time,$parameter) = WeekdayTimer_SwitchingTime ($hash, $st);
my $echtZeit = WeekdayTimer_EchteZeit ($hash, $wday, $time);
my ($stunde, $minute, $sekunde) = split (":",$echtZeit);
$hash->{profil} {$echtZeit}{PARA} = $parameter;
$hash->{profil} {$echtZeit}{TIM} = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, 0);
$hash->{profil} {$echtZeit}{TAGE} = $tage;
}
# ------------------------------------------------------------------------------
Log3 $hash, 4, "[$hash->{NAME}] " . sunrise_abs() . " " . sunset_abs() . " " . $longDays{$language}[$wday];
foreach my $d (sort keys %{$hash->{profile}}) {
my $profiltext = "";
foreach my $t (sort keys %{$hash->{profile}{$d}}) {
$profiltext .= "$t " . $hash->{profile}{$d}{$t} . ", ";
}
my $profilKey = "Profil $d: $longDays{$language}[$d]";
$profiltext =~ s/, $//;
$hash->{$profilKey} = $profiltext;
Log3 $hash, 4, "[$hash->{NAME}] $profiltext ($profilKey)";
}
delete $hash->{profile};
}
################################################################################
sub WeekdayTimer_SwitchingTime($$) {
my ($hash, $switchingtime) = @_;
my $name = $hash->{NAME};
my $globalDaylistSpec = $hash->{GlobalDaylistSpec};
my @tageGlobal = @{WeekdayTimer_daylistAsArray($hash, $globalDaylistSpec)};
my (@st, $daylist, $time, $timeString, $para);
@st = split(/\|/, $switchingtime);
if ( @st == 2) {
$daylist = ($globalDaylistSpec gt "") ? $globalDaylistSpec : "0123456";
$time = $st[0];
$para = $st[1];
} elsif ( @st == 3) {
$daylist = $st[0];
$time = $st[1];
$para = $st[2];
}
my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)};
my $tage=@tage;
if ( $tage==0 ) {
Log3 ($hash, 1, "[$name] invalid daylist in $name <$daylist> use one of 012345678 or $hash->{helper}{daysRegExpMessage}");
}
my %hdays=();
@hdays{@tageGlobal} = undef;
@hdays{@tage} = undef;
@tage = sort keys %hdays;
#Log3 $hash, 3, "Tage: " . Dumper \@tage;
return (\@tage,$time,$para);
}
################################################################################
sub WeekdayTimer_daylistAsArray($$){
my ($hash, $daylist) = @_;
my $name = $hash->{NAME};
my @days;
my %hdays=();
$daylist = lc($daylist);
# Angaben der Tage verarbeiten
# Aufzaehlung 1234 ...
if ( $daylist =~ m/^[0-8]{0,9}$/g) {
Log3 ($hash, 3, "[$name] " . '"7" in daylist now means $we(weekend) - see dokumentation!!!' )
if (index($daylist, '7') != -1);
@days = split("", $daylist);
@hdays{@days} = undef;
# Aufzaehlung Sa,So,... | Mo-Di,Do,Fr-Mo
} elsif ($daylist =~ m/^($hash->{helper}{daysRegExp}(,|-|$)){0,7}$/g ) {
my @subDays;
my @aufzaehlungen = split (",", $daylist);
foreach my $einzelAufzaehlung (@aufzaehlungen) {
my @days = split ("-", $einzelAufzaehlung);
my $days = @days;
if ($days == 1) {
#einzelner Tag: Sa
$hdays{$hash->{dayNumber}{$days[0]}} = undef;
} else {
# von bis Angabe: Mo-Di
my $von = $hash->{dayNumber}{$days[0]};
my $bis = $hash->{dayNumber}{$days[1]};
if ($von <= $bis) {
@subDays = ($von .. $bis);
} else {
#@subDays = ($dayNumber{so} .. $bis, $von .. $dayNumber{sa});
@subDays = ( 00 .. $bis, $von .. 06);
}
@hdays{@subDays}=undef;
}
}
} else{
%hdays = ();
}
my @tage = sort keys %hdays;
return \@tage;
}
################################################################################
sub WeekdayTimer_EchteZeit($$$) {
my ($hash, $d, $time) = @_;
my $name = $hash->{NAME};
my $now = time();
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($now);
my $listOfDays = "";
# Zeitangabe verarbeiten.
$time = '"' . "$time" . '"' if($time !~ m/^\{.*\}$/g);
my $date = $now+($d-$wday)*86400;
my $timeString = '{ my $date='."$date;" .$time."}";
my $eTimeString = eval( $timeString ); # must deliver HH:MM[:SS]
if ($@) {
$@ =~ s/\n/ /g;
Log3 ($hash, 3, "[$name] " . $@ . ">>>$timeString<<<");
$eTimeString = "00:00:00";
}
if ($eTimeString =~ m/^[0-2][0-9]:[0-5][0-9]$/g) { # HH:MM
$eTimeString .= ":00"; # HH:MM:SS erzeugen
} elsif ($eTimeString =~ m/^[0-2][0-9](:[0-5][0-9]){2,2}$/g) { # HH:MM:SS
; # ok.
} else {
Log3 ($hash, 1, "[$name] invalid time <$eTimeString> HH:MM[:SS]");
$eTimeString = "00:00:00";
}
return $eTimeString;
}
################################################################################
sub WeekdayTimer_zeitErmitteln ($$$$$) {
my ($now, $hour, $min, $sec, $days) = @_;
my @jetzt_arr = localtime($now);
#Stunden Minuten Sekunden
$jetzt_arr[2] = $hour; $jetzt_arr[1] = $min; $jetzt_arr[0] = $sec;
$jetzt_arr[3] += $days;
my $next = timelocal_nocheck(@jetzt_arr);
return $next;
}
################################################################################
sub WeekdayTimer_gatherSwitchingTimes {
my $a = shift;
my @switchingtimes = ();
my $conditionOrCommand;
# switchingtime einsammeln
while (@$a > 0) {
#pruefen auf Angabe eines Schaltpunktes
my $element = shift @$a;
my @t = split(/\|/, $element);
my $anzahl = @t;
if ( $anzahl >= 2 && $anzahl <= 3) {
push(@switchingtimes, $element);
} else {
unshift @$a, $element;
last;
}
}
return (@switchingtimes);
}
################################################################################
sub WeekdayTimer_Language {
my ($hash, $a) = @_;
my $name = $hash->{NAME};
# ggf. language optional Parameter
my $langRegExp = "(" . join ("|", keys(%{$hash->{shortDays}})) . ")";
my $language = shift @$a;
if ($language =~ m/^$langRegExp$/g) {
} else {
Log3 ($hash, 3, "[$name] language: $language not recognized, use one of $langRegExp") if (length($language) == 2);
unshift @$a, $language;
$language = "de";
}
$hash->{LANGUAGE} = $language;
$language = $hash->{LANGUAGE};
return ($langRegExp, $language);
}
################################################################################
sub WeekdayTimer_GlobalDaylistSpec {
my ($hash, $a) = @_;
my $daylist = shift @$a;
my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)};
my $tage = @tage;
if ($tage > 0) {
;
} else {
unshift (@$a,$daylist);
$daylist = "";
}
$hash->{GlobalDaylistSpec} = $daylist;
}
################################################################################
sub WeekdayTimer_SetTimerForMidnightUpdate($) {
my ($myHash) = @_;
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
return if (!defined($hash));
my $now = time();
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now);
my $secToMidnight = 24*3600 -(3600*$hour + 60*$min + $sec) + 5;
#my $secToMidnight = + 01*60;
myRemoveInternalTimer("SetTimerOfDay", $hash);
myInternalTimer ("SetTimerOfDay", $now+$secToMidnight, "$hash->{TYPE}_SetTimerOfDay", $hash, 0);
}
################################################################################
sub WeekdayTimer_SetTimerOfDay($) {
my ($myHash) = @_;
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
return if (!defined($hash));
WeekdayTimer_DeleteTimer($hash);
WeekdayTimer_Profile ($hash);
WeekdayTimer_SetTimer ($hash);
WeekdayTimer_SetTimerForMidnightUpdate( { HASH => $hash} );
}
################################################################################
sub WeekdayTimer_SetTimer($) {
my $hash = shift;
my $name = $hash->{NAME};
my $now = time();
my $switchedInThePast = 0;
my $isHeating = WeekdayTimer_isHeizung($hash);
my $grenzSeconds = $isHeating ? -24*3600 : -5;
my @switches = sort keys %{$hash->{profil}};
if ($#switches < 0) {
Log3 $hash, 3, "[$name] no switches to send, due to possible errors.";
return;
}
my $nextSwitch = $switches[0];
my $nextPara = $hash->{profil}{$switches[0]}{PARA};
my @reverseSwitches = ((reverse @switches), $switches[$#switches]);
for(my $i=0; $i<=$#reverseSwitches; $i++) {
my $time = $reverseSwitches[$i];
$hash->{profil}{$time}{NEXTPARA} = $nextPara;
$hash->{profil}{$time}{NEXTSWITCH} = $nextSwitch;
my $timToSwitch = $hash->{profil}{$time}{TIM};
$nextPara = $hash->{profil}{$time}{PARA};
$nextSwitch = $time;
$timToSwitch -= 24*3600 if ($i == $#reverseSwitches);
my $secondsToSwitch = $timToSwitch - $now;
if ($secondsToSwitch>$grenzSeconds && !$switchedInThePast) {
myInternalTimer ("$time", $timToSwitch, "$hash->{TYPE}_Update", $hash, 0);
$switchedInThePast = ($secondsToSwitch<0);
}
}
}
################################################################################
sub WeekdayTimer_DeleteTimer($) {
my $hash = shift;
map {myRemoveInternalTimer ($_, $hash)} keys %{$hash->{profil}};
}
################################################################################
sub WeekdayTimer_Update($) {
my ($myHash) = @_;
my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
return if (!defined($hash));
my $name = $hash->{NAME};
my $time = $myHash->{MODIFIER};
my $now = time();
# Schaltparameter ermitteln
my $tage = $hash->{profil}{$time}{TAGE};
my $newParam = $hash->{profil}{$time}{PARA};
my $nextSwitch = $hash->{profil}{$time}{NEXTSWITCH};
my $nextParam = $hash->{profil}{$time}{NEXTPARA};
# Fenserkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern
if (WeekdayTimer_FensterOffen($hash, $newParam, $time)) {
return;
}
my $active = 1;
my $condition = WeekdayTimer_Condition ($hash, $tage);
if ($condition) {
$active = AnalyzeCommandChain(undef, "{". $condition ."}");
}
Log3 $hash, 4, "[$name] seems to be active: $condition" if($active);
# ggf. Device schalten
WeekdayTimer_Device_Schalten($hash, $newParam, $tage);
readingsBeginUpdate($hash);
readingsBulkUpdate ($hash, "nextUpdate", $nextSwitch);
readingsBulkUpdate ($hash, "nextUpdate", $nextSwitch);
readingsBulkUpdate ($hash, "nextValue", $nextParam);
readingsBulkUpdate ($hash, "state", $active ? $newParam : "inactive" );
readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1));
return 1;
}
################################################################################
sub WeekdayTimer_isHeizung($) {
my ($hash) = @_; my ($hash) = @_;
Heating_Control_UpdatePerlTime($hash);
my %setmodifiers =
("FHT" => "desired-temp",
"PID20" => "desired",
"EnOcean" => { "subTypeReading" => "subType", "setModifier" => "desired-temp",
"roomSensorControl.05" => 1,
"hvac.01" => 1 },
"MAX" => { "subTypeReading" => "type", "setModifier" => "desiredTemperature",
"HeatingThermostatPlus" => 1,
"HeatingThermostat" => 1,
"WallMountedThermostat" => 1 },
"CUL_HM" => { "subTypeReading" => "model","setModifier" => "desired-temp",
"HM-CC-TC" => 1,
"HM-TC-IT-WM-W-EU" => 1,
"HM-CC-RT-DN" => 1 } );
my $dHash = $defs{$hash->{DEVICE}};
my $dType = $dHash->{TYPE};
return "" if (!defined($dType));
my $setModifier = $setmodifiers{$dType};
$setModifier = "" if (!defined($setModifier));
if (ref($setModifier)) {
my $subTypeReading = $setmodifiers{$dType}{subTypeReading};
my $model;
if ($subTypeReading eq "type" ) {
$model = $dHash->{type};
} else {
$model = AttrVal($hash->{DEVICE}, $subTypeReading, "nF");
}
if (defined($setmodifiers{$dType}{$model})) {
$setModifier = $setmodifiers{$dType}{setModifier}
} else {
$setModifier = "";
}
}
return $setModifier;
} }
######################################################################## ################################################################################
sub WeekdayTimer_Update($){ #
my ($hash) = @_; sub WeekdayTimer_FensterOffen ($$$) {
return Heating_Control_Update($hash); my ($hash, $event, $time) = @_;
my $name = $hash->{NAME};
my $verzoegerteAusfuehrungCond = AttrVal($hash->{NAME}, "delayedExecutionCond", "0");
my %specials= (
"%HEATING_CONTROL" => $hash->{NAME},
"%WEEKDAYTIMER" => $hash->{NAME},
"%NAME" => $hash->{DEVICE},
"%EVENT" => $event
);
$verzoegerteAusfuehrungCond = EvalSpecials($verzoegerteAusfuehrungCond, %specials);
my $verzoegerteAusfuehrung = eval($verzoegerteAusfuehrungCond);
if ($verzoegerteAusfuehrung) {
if (!defined($hash->{VERZOEGRUNG})) {
Log3 $hash, 3, "[$name] switch of $hash->{DEVICE} delayed - $verzoegerteAusfuehrungCond is TRUE";
}
myRemoveInternalTimer("Update", $hash);
myInternalTimer ("$time", time()+60, "$hash->{TYPE}_Update", $hash, 0);
$hash->{VERZOEGRUNG} = 1;
return 1
}
my %contacts = ( "CUL_FHTTK" => { "READING" => "Window", "STATUS" => "(Open)", "MODEL" => "r" },
"CUL_HM" => { "READING" => "state", "STATUS" => "(open|tilted)", "MODEL" => "r" },
"MAX" => { "READING" => "state", "STATUS" => "(open)", "MODEL" => "r" },
"WeekdayTimer" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" },
"Heating_Control" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" }
);
my $fensterKontakte = AttrVal($hash->{NAME}, "windowSensor", "")." ".$hash->{NAME};
$fensterKontakte =~ s/^\s+//;
$fensterKontakte =~ s/\s+$//;
Log3 $hash, 5, "[$name] list of window sensors found: '$fensterKontakte'";
if ($fensterKontakte ne "" ) {
my @kontakte = split("[ \t]+", $fensterKontakte);
foreach my $fk (@kontakte) {
if(!$defs{$fk}) {
Log3 $hash, 3, "[$name] sensor <$fk> not found - check name.";
} else {
my $fk_hash = $defs{$fk};
my $fk_typ = $fk_hash->{TYPE};
if (!defined($contacts{$fk_typ})) {
Log3 $hash, 3, "[$name] TYPE '$fk_typ' of $fk not yet supported, $fk ignored - inform maintainer";
} else {
my $reading = $contacts{$fk_typ}{READING};
my $statusReg = $contacts{$fk_typ}{STATUS};
my $model = $contacts{$fk_typ}{MODEL};
my $windowStatus;
if ($model eq "r") { ### Reading, sonst Attribut
$windowStatus = ReadingsVal($fk,$reading,"nF");
}else{
$windowStatus = AttrVal ($fk,$reading,"nF");
}
if ($windowStatus eq "nF") {
Log3 $hash, 3, "[$name] Reading/Attribute '$reading' of $fk not found, $fk ignored - inform maintainer" if ($model eq "r");
} else {
Log3 $hash, 5, "[$name] sensor '$fk' Reading/Attribute '$reading' is '$windowStatus'";
if ($windowStatus =~ m/^$statusReg$/g) {
if (!defined($hash->{VERZOEGRUNG})) {
Log3 $hash, 3, "[$name] switch of $hash->{DEVICE} delayed - sensor '$fk' Reading/Attribute '$reading' is '$windowStatus'";
}
myRemoveInternalTimer("Update", $hash);
#myInternalTimer ("Update", time()+60, "$hash->{TYPE}_Update", $hash, 0);
myInternalTimer ("$time", time()+60, "$hash->{TYPE}_Update", $hash, 0);
$hash->{VERZOEGRUNG} = 1;
return 1
}
}
}
}
}
}
if ($hash->{VERZOEGRUNG}) {
Log3 $hash, 3, "[$name] delay of switching $hash->{DEVICE} stopped.";
}
delete $hash->{VERZOEGRUNG};
return 0;
} }
######################################################################## ################################################################################
sub WeekdayTimer_Device_Schalten($$$) {
my ($hash, $newParam, $tage) = @_;
my ($command, $condition) = "";
my $name = $hash->{NAME}; ###
my $now = time();
#modifier des Zieldevices auswaehlen
my $setModifier = WeekdayTimer_isHeizung($hash);
$command = '{ fhem("set @ '. $setModifier .' %") }';
$command = $hash->{COMMAND} if (defined $hash->{COMMAND});
$condition = WeekdayTimer_Condition($hash, $tage);
$command = "{ if " .$condition . " " . $command . "}";
my $isHeating = $setModifier gt "";
my $aktParam = ReadingsVal($hash->{DEVICE}, $setModifier, "");
$aktParam = sprintf("%.1f", $aktParam) if ($isHeating && $aktParam =~ m/^[0-9]{1,3}$/i);
$newParam = sprintf("%.1f", $newParam) if ($isHeating && $newParam =~ m/^[0-9]{1,3}$/i);
my $disabled = AttrVal($hash->{NAME}, "disable", 0);
my $disabled_txt = $disabled ? " " : " not";
Log3 $hash, 5, "[$name] aktParam:$aktParam newParam:$newParam - is $disabled_txt disabled";
#Kommando ausführen
if ($command && !$disabled && $aktParam ne $newParam) {
$newParam =~ s/:/ /g;
$command = SemicolonEscape($command);
my %specials= (
"%NAME" => $hash->{DEVICE},
"%EVENT" => $newParam,
);
$command= EvalSpecials($command, %specials);
Log3 $hash, 4, "[$name] command: $command executed";
my $ret = AnalyzeCommandChain(undef, $command);
Log3 ($hash, 3, $ret) if($ret);
}
}
################################################################################
sub WeekdayTimer_Condition($$) {
my ($hash, $tage) = @_;
my $condition = "( ";
$condition .= (defined $hash->{CONDITION}) ? $hash->{CONDITION} : 1 ;
$condition .= " && " . WeekdayTimer_TageAsCondition($tage);
$condition .= ")";
return $condition;
}
################################################################################
sub WeekdayTimer_TageAsCondition ($) {
my $tage = shift;
my %days = map {$_ => 1} @$tage;
my $we = $days{7}; delete $days{7}; # $we
my $notWe = $days{8}; delete $days{8}; #!$we
my $tageExp = '($wday ~~ [' . join (",", sort keys %days) . "]";
$tageExp .= ' || $we' if defined $we;
$tageExp .= ' || !$we' if defined $notWe;
$tageExp .= ')';
return $tageExp;
}
################################################################################
sub WeekdayTimer_Attr($$$) { sub WeekdayTimer_Attr($$$) {
my ($cmd, $name, $attrName, $attrVal) = @_; my ($cmd, $name, $attrName, $attrVal) = @_;
@ -101,23 +741,14 @@ sub WeekdayTimer_Attr($$$) {
} }
return undef; return undef;
} }
######################################################################## ################################################################################
sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()} sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()}
foreach my $hc ( sort keys %{$modules{WeekdayTimer}{defptr}} ) { foreach my $hc ( sort keys %{$modules{WeekdayTimer}{defptr}} ) {
my $hash = $modules{WeekdayTimer}{defptr}{$hc}; my $hash = $modules{WeekdayTimer}{defptr}{$hc};
if($hash->{helper}{CONDITION}) { WeekdayTimer_SetTimer($hash);
if (!(eval ($hash->{helper}{CONDITION}))) { Log3 undef, 3, "WeekdayTimer_SetAllParms() for $hash->{NAME} done!";
readingsSingleUpdate ($hash, "state", "inactive", 1);
next;
}
}
my $myHash->{HASH}=$hash;
$hash->{setAllTempMode} = 1;
WeekdayTimer_Update($myHash);
delete $hash->{setAllTempMode};
Log3 undef, 3, "WeekdayTimer_Update() for $hash->{NAME} done!";
} }
Log3 undef, 3, "WeekdayTimer_SetAllParms() done!"; Log3 undef, 3, "WeekdayTimer_SetAllParms() done!";
} }