# $Id$ ############################################################################## # # 98_Heating_Control.pm # written by Dietmar Ortmann # modified by Tobias Faust # # This file is part of fhem. # # Fhem is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Fhem is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with fhem. If not, see . # ############################################################################## package main; use strict; use warnings; use POSIX; use Time::Local 'timelocal_nocheck'; ##################################### sub Heating_Control_Initialize($) { my ($hash) = @_; # Consumer $hash->{DefFn} = "Heating_Control_Define"; $hash->{UndefFn} = "Heating_Control_Undef"; $hash->{GetFn} = "Heating_Control_Get"; $hash->{UpdFn} = "Heating_Control_Update"; $hash->{AttrList}= "disable:0,1 windowSensor ". $readingFnAttributes; } ################################################################################ sub Heating_Control_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($$) { my ($hash, $def) = @_; my %longDays = ( "de" => ["Sonntag", "Montag","Dienstag","Mittwoch", "Donnerstag","Freitag", "Samstag" ], "en" => ["Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], "fr" => ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi","Samedi" ]); 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 $hash->{TYPE} " 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 return "invalid Device, given 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 (!defined($hash->{PERLTIMEUPDATEMODE})) { Heating_Control_UpdatePerlTime($hash); } RemoveInternalTimer($hash); InternalTimer ($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($$$) { 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, $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); # Zeitangabe verarbeiten. if($time =~ m/^\{.*\}$/g) { # Perlausdruck {*} $time = eval($time); # must deliver HH:MM[:SS] $hash->{TIME_AS_PERL} = 1; } if ($time =~ m/^[0-2][0-9]:[0-5][0-9]$/g) { # HH:MM $time .= ":00"; # HH:MM:SS erzeugen } elsif ($time =~ m/^[0-2][0-9](:[0-5][0-9]){2,2}$/g) { # HH:MM:SS ; # ok. } else { Log3 $hash, 1, "[$name] invalid time in $name <$time> HH:MM[:SS]"; return 0; } my $listOfDays = ""; for (my $d=0; $d<@days; $d++) { $listOfDays .= @{$$shortDays}[$days[$d]] . ","; $hash->{helper}{SWITCHINGTIME}{$days[$d]}{$time} = $para; } $listOfDays =~ s/,$//g; Log3 $hash, 5, "[$name] Switchingtime: @{$switchingtimes}[$i] : $listOfDays -> $time -> $para "; } return 1; } ################################################################################ sub Heating_Control_Undef($$) { my ($hash, $arg) = @_; RemoveInternalTimer($hash); delete $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}}; return undef; } ################################################################################ sub Heating_Control_UpdatePerlTime($) { my ($hash) = @_; if (defined($hash->{TIME_AS_PERL})) { 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; if (abs($secToMidnight-24*3600)<10) { $hash->{PERLTIMEUPDATEMODE} = 1; Heating_Control_Define($hash, $hash->{NAME} . " " . $hash->{TYPE} . " " . $hash->{DEF} ); delete $hash->{PERLTIMEUPDATEMODE}; } InternalTimer ($now+$secToMidnight, "Heating_Control_UpdatePerlTime", $hash, 0); } } ################################################################################ sub Heating_Control_Update($) { my ($hash) = @_; my $mod = "[".$hash->{NAME} ."] "; my $name = $hash->{NAME}; my $now = time() + 5; # garantiert > als die eingestellte Schlatzeit # Fenserkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern if (Heating_Control_FensterOffen($hash)) { return; } # Schaltparameter ermitteln my ($nowSwitch,$nextSwitch,$newParam,$nextParam) = Heating_Control_akt_next_param($now, $hash); # ggf. Device schalten Heating_Control_Device_Schalten($hash, $now, $nowSwitch, $newParam); Log3 $hash, 4, $mod .strftime('Next switch %d.%m.%Y %H:%M:%S',localtime($nextSwitch)); # Timer und Readings setzen. InternalTimer ($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) = @_; my $mod = "[".$hash->{NAME} ."]"; my %contacts = ( "CUL_FHTTK" => { "READING" => "Window", "STATUS" => "(Open)" }, "CUL_HM" => { "READING" => "state", "STATUS" => "(open|tilted)", "model" => 1 }, "MAX" => { "READING" => "state", "STATUS" => "(open)" }); my $fensterKontakte = AttrVal($hash->{NAME}, "windowSensor", ""); Log3 $hash, 5, "$mod list of windowsenors found: '$fensterKontakte'"; if ($fensterKontakte ne "" ) { my @kontakte = split(/ /, $fensterKontakte); foreach my $fk (@kontakte) { if(!$defs{$fk}) { Log3 $hash, 3, "$mod Window 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 $windowStatus = ReadingsVal($fk,$reading,"nF"); if ($windowStatus eq "nF") { Log3 $hash, 3, "$mod READING '$reading' of $fk not found, $fk ignored - inform maintainer"; } else { Log3 $hash, 5, "$mod windowsensor '$fk' Reading '$reading' is '$windowStatus'"; if ($windowStatus =~ m/^$statusReg$/g) { if (!defined($hash->{VERZOEGRUNG})) { Log3 $hash, 3, "$mod switch of $hash->{DEVICE} delayed - windowsensor '$fk' Reading '$reading' is '$windowStatus'"; } InternalTimer (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 = $now + $secondsToSwitch; 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}; $newParam = sprintf("%.1f", $newParam) if ($newParam =~ m/^[0-9]{1,3}$/i); $nowSwitch = $next; } else { $nextParam = $hash->{helper}{SWITCHINGTIME}{$wd}{$st}; $nextParam = sprintf("%.1f", $nextParam) if ($nextParam =~ m/^[0-9]{1,3}$/i); $nextSwitch = $next; last; } } } $nextSwitch += Heating_Control_DSTOffset($hash, $now, $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 = 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 $aktParam = ReadingsVal($hash->{DEVICE}, $setModifier, 0); $aktParam = sprintf("%.1f", $aktParam) if ($aktParam =~ 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 (defined $hash->{helper}{COMMAND} || ($nowSwitch gt "" && $aktParam ne $newParam )) { if (!$setModifier && $secondsSinceSwitch < -60) { Log3 $hash, 5, $mod."no switch in the yesterdays because of the devices type."; } else { if ($command && !$disabled) { $newParam =~ s/:/ /g; $command =~ s/@/$hash->{DEVICE}/g; $command =~ s/%/$newParam/g; $command = SemicolonEscape($command); Log3 $hash, 4, $mod."command: $command executed"; my $ret = AnalyzeCommandChain(undef, $command); Log3 ($hash, 3, $ret) if($ret); } } } } ################################################################################ sub isHeizung($) { my ($hash) = @_; my %setmodifiers = ("FHT" => "desired-temp", #"HCS" => 1, "MAX" => { "mode" => "type", "setModifier" => "desiredTemperature", "HeatingThermostatPlus" => 1, "HeatingThermostat" => 1, "WallMountedThermostat" => 1 }, "CUL_HM" => { "mode" => "model","setModifier" => "desired-temp", "HM-CC-TC" => 1, "HM-CC-RT-DN" => 1 } ); my $dHash = $defs{$hash->{DEVICE}}; my $dType = $dHash->{TYPE}; my $setModifier = $setmodifiers{$dType}; $setModifier = "" if (!defined($setModifier)); if (ref($setModifier)) { my $mode = $setmodifiers{$dType}{mode}; my $model; if ($mode eq "model" ) { $model = AttrVal($hash->{DEVICE}, "model", "nF"); } elsif ($mode eq "type") { $model = $dHash->{type}; } if (defined($setmodifiers{$dType}{$model})) { $setModifier = $setmodifiers{$dType}{setModifier} } else { $setModifier = ""; } } return $setModifier; } ################################################################################ sub Heating_Control_SetAllTemps() { # {Heating_Control_SetAllTemps()} foreach my $hc ( sort keys %{$modules{Heating_Control}{defptr}} ) { my $hash = $modules{Heating_Control}{defptr}{$hc}; if($hash->{helper}{CONDITION}) { if (!(eval ($hash->{helper}{CONDITION}))) { readingsSingleUpdate ($hash, "state", "inactive", 1); next; } } Heating_Control_Update($hash); Log3 undef, 3, "Heating_Control_Update() for $hash->{NAME} done!"; } Log3 undef, 3, "Heating_Control_SetAllTemps() done!"; } ################################################################################ sub Heating_Control_DSTOffset($$$) { my ($hash,$t1,$t2)= @_; my @lt1 = localtime($t1); my @lt2 = localtime($t2); # wenn t2 in der ersten Stunde der dst liegt darf nicht gleich 3600 Sekunden abgezogen werden. # dstEin ist immer am letzten Sonntag im März. my $dstEin = timelocal_nocheck(0,0,2,31,2,$lt2[5]); # 31. Maerz aktuelles Jahr my @aNow = localtime($dstEin); $dstEin += -$lt2[6]*24*3600; # letzen Maerzsonntag ermittlen. my $offset = $t2 - $dstEin + 1; # $secsSinceDstEin $offset = 3600 if ($offset >3600); # maximal 3600 Sekunden $offset *= ($lt1[8] - $lt2[8]); # nur wenn Wechsel der dst Log 3, "[$hash->{NAME}] DST offset=$offset" if ($offset != 0); return $offset; } ################################################################################ sub SortNumber { if($a < $b) { return -1; } elsif($a == $b) { return 0; } else { return 1; } } 1; =pod =begin html

Heating Control


    Define
      define <name> Heating_Control <device> [<language>] <profile> <command>|<condition>

      to set a weekly profile for <device>, eg. a heating sink.
      You can define different switchingtimes for every day.
      The new temperature is sent to the <device> automatically with

      set <device> (desired-temp|desiredTemerature) <temp>

      Because of the fhem-type of structures, a structures of heating sinks is sent "desired-temp": Use an explicit command if you have structures of MAX heating thermostats.
      If you have defined a <condition> and this condition is false if the switchingtime has reached, no command will executed.
      A other case is to define an own perl command with <command>.

      The following parameter are defined:

        device
        The device to switch at the given time.

        language
        Specifies the language used for definition and profiles. de,en,fr are possible. The parameter is optional.

        profile
        Define the weekly profile. All timings are separated by space. One switchingtime are defined by the following example:
          [<weekdays>|]<time>|<parameter>

        weekdays: optional, if not set every day is using.
        Otherwise you can define one day as number or as shortname.
        time:define the time to switch, format: HH:MM:[SS](HH in 24 hour format) or a Perlfunction like {sunrise_abs()}
        parameter:the temperature to be set, using a float with mask 99.9 or a sybolic value like eco or comfort - whatever your thermostat understands. The symbolic value can be added an additional parameter: dayTemp:16 night-temp:15. See examples

        command
        If no condition is set, all others is interpreted as command. Perl-code is setting up by well-known Block with {}.
        Note: if a command is defined only this command are executed. In case of executing a "set desired-temp" command, you must define it explicit.
        The following parameter are replaced:
        1. @ => the device to switch
        2. % => the new temperature

        condition
        if a condition is defined you must declared this with () and a valid perl-code.
        The returnvalue must be boolean.
        The parameter @ and % will be interpreted.

      Example:

        define HCB Heating_Control Bad_Heizung 12345|05:20|21 12345|05:25|comfort 17:20|21 17:25|eco
        Mo-Fr are setting the temperature at 05:20 to 21°C, and at 05:25 to comfort. Every day will be set the temperature at 17:20 to 21°C and 17:25 to eco.

        define HCW Heating_Control WZ_Heizung 07:00|16 Mo,Tu,Th-Fr|16:00|18.5 20:00|12 {fhem("set dummy on"); fhem("set @ desired-temp %");}
        At the given times and weekdays only(!) the command will be executed.

        define HCW Heating_Control WZ_Heizung Sa-Su,We|08:00|21 (ReadingsVal("WeAreThere", "state", "no") eq "yes")
        The temperature is only set if the dummy variable WeAreThere is "yes".

        define HCW Heating_Control WZ_Heizung en Su-Fr|{sunrise_abs()}|21 Mo-Fr|{sunset_abs()}|16
        The device is switched at sunrise/sunset. Language: english. define HCW Heating_Control WZ_Heizung en Mo-Fr|{myFunction}|night-temp:18 Mo-Fr|{myFunction()}|dayTemp:16
        The is switched at time myFunction(). It is sent the Command "night-temp 18" and "dayTemp 16". If you want to have set all Heating_Controls their current value (after a temperature lowering phase holidays) you can call the function Heating_Control_SetAllTemps (). This call can be automatically coupled to a dummy by notify:        define HeizStatus2 notify Heating:. * {Heating_Control_SetAllTemps ()}

    Set
      N/A

    Get
      N/A

    Attributes
=end html =begin html_DE

Heating Control


    Define
      define <name> Heating_Control <device> [<language>] <profile> <command>|<condition>

      Bildet ein Wochenprofil für ein <device>, zb. Heizkörper, ab.
      Es können für jeden Tag unterschiedliche Schaltzeiten angegeben werden.
      Ist das <device> ein Heizkörperthermostat (zb. FHT8b, MAX) so wird bei FHT8b/MAX die zu setzende Temperatur im <profile> automatisch mittels

      set <device> (desired-temp|desiredTemerature) <temp>

      gesendet. Struktuen von Heizkörperthermostaten bekommen aufgrund des fhem-Typs auch desired-temp gesendet: Nutze bitte explizite Kommandos wenn Strukturen von MAX Heizthermostaten gesteuert werden sollen.

      Ist eine <condition> angegeben und ist zum Schaltpunkt der Ausdruck unwahr, so wird dieser Schaltpunkt nicht ausgeführt.
      Alternativ zur Automatik kann stattdessen eigener Perl-Code im <command> ausgeführt werden.

      Folgende Parameter sind im Define definiert:

        device
        Name des zu schaltenden Device.

        language
        Spezifiziert die Sprache für die Definition und die Anzeige der Profile in der Weboberfläche. Zurzeit sind de,en,fr definiert. Der Parameter ist optional.

        profile
        Angabe des Wochenprofils. Die einzelnen Schaltzeiten sind durch Leerzeichen getrennt Die Angabe der Schaltzeiten ist nach folgendem Muster definiert:
          [<Wochentage>|]<Uhrzeit>|<Parameter>

        Wochentage: optionale Angabe, falls nicht gesetzt wird der Schaltpunkt jeden Tag ausgeführt. Für die Tage an denen dieser Schaltpunkt aktiv sein soll, ist jeder Tag mit seiner Tagesnummer (Mo=1, ..., So=7) oder Name des Tages (Mo, Di, ..., So) einzusetzen.
        Uhrzeit:Angabe der Uhrzeit zu der geschaltet werden soll, Format: HH:MM:[SS](HH im 24 Stunden Format) oder eine Perlfunction wie {sunrise_abs()}
        Parameter:Angabe der zu setzenden Temperatur als Zahl mit Format 99.9 oder als symbolische Konstante eco or comfort - was immer das Heizkörperthermostat versteht. Symbolischen Werten kann ein zusätzlicher Parameter angehängt werden: dayTemp:16 night-temp:15. Unten folgen Beispiele

        command
        Falls keine Condition in () angegeben wurde, so wird alles weitere als Command interpretiert. Perl-Code ist in {} zu setzen.
        Wichtig: Falls ein Command definiert ist, so wird zu den definierten Schaltzeiten nur(!) das Command ausgeführt. Falls ein desired-temp Befehl abgesetzt werde soll, so muss dies explizit angegeben werden.
        Folgende Parameter werden ersetzt:
        1. @ => das zu schaltende Device
        2. % => die zu setzende Temperatur

        condition
        Bei Angabe einer Condition ist diese in () zu setzen und mit validem Perl-Code zu versehen.
        Der Rückgabedatentyp der condition muss boolean sein.
        Die Parameter @ und % werden interpretiert.

      Beispiel:

        define HCW Heating_Control Bad_Heizung 12345|05:20|21 12345|05:25|comfort 17:20|21 17:25|eco
        Mo-Fr wird die Temperatur um 05:20Uhr auf 21°C, und um 05:25Uhr auf comfort gesetzt. Jeden Tag wird die Temperatur um 17:20Uhr auf 21°C und 17:25Uhr auf eco gesetzt.

        define HCW Heating_Control WZ_Heizung 07:00|16 Mo,Di,Mi|16:00|18.5 20:00|12 {fhem("set dummy on"); fhem("set @ desired-temp %");}
        Zu den definierten Schaltzeiten wird nur(!) der in {} angegebene Perl-Code ausgeführt.

        define HCW Heating_Control WZ_Heizung Sa-So,Mi|08:00|21 (ReadingsVal("WeAreThere", "state", "no") eq "yes")
        Die zu setzende Temperatur wird nur gesetzt, falls die Dummy Variable WeAreThere = "yes" ist.

        define HCW Heating_Control WZ_Heizung en Su-Fr|{sunrise_abs()}|21 Mo-Fr|{sunset_abs()}|16
        Das Gerät wird bei Sonnenaufgang und Sonnenuntergang geschaltet. Sprache: Englisch. define HCW Heating_Control WZ_Heizung en Mo-Fr|{myFunction}|night-temp:18 Mo-Fr|{myFunction()}|dayTemp:16
        Das Gerät wird bei myFunction() geschaltet. Es wird das Kommando "night-temp 18" bzw. "dayTemp 16" gesendet. Wenn du beispielsweise nach einer Temperaturabsenkungsphase erreichen willst, dass alle Heating_Controls ihren aktuellen Wert einstellen sollen, kannst du die Funktion Heating_Control_SetAllTemps() aufrufen. Dieser Aufruf kann per notify automatisch an ein dummy gekoppelt werden: define HeizStatus2 notify Heizung:.* {Heating_Control_SetAllTemps()}

    Set
      N/A

    Get
      N/A

    Attributes
=end html_DE =cut