ich möchte im Folgenden meine Umsetzung einer Anwesenheitssimulation vorstellen. Im Prinzip nichts Spektakuläres, aber vielleicht findet der eine oder andere noch etwas Hilfreiches für seine eigene Umsetzung. Eingesetzt wurden HomeMatic Skripte, TCL-Skripte sowie HomeMatic WebUI-Programme.
Die Skripte und Programme dürfen gerne übernommen werden, der Einsatz erfolgt natürlich auf eigene Verantwortung.
Vorab ein paar kleine „Highlights“:
- Berechnung der Schaltzeiten für einen Tag im Voraus abhängig von Sonnenauf- und Untergängen, Bedingungen wie „spätestens um“ oder „wenn früher als“, sowie der Einbeziehung von Zufallszahlen
- Chronologische Sortierung der Schaltzeiten
- Triggerung der Ereignisse per CUxD-Timer
- Logging der Berechnung und Aktionen
- Tägliche Versendung des Logfiles per Mail als Attachment
Eingesetzte Skripte und Programme:
- Freier_Tag: HomeMatic Skript auf Basis der Feiertagsberechnung von Homematic-Inside.de. Erweitert um die Einbeziehung des Wochenendes, um freie Tage zu identifizieren
- Twilight.tcl: TCL-Skript zur Berechnung der Sonnenauf- und Untergänge auf Basis des Programmes von DocZoid. Ergänzt wurden zusätzliche Ausgaben in Systemvariablen sowie eine Erweiterung der Wetterverarbeitung
- CalcActionTimes: HomeMatic Skript zur Berechnung der Schaltzeiten für einen Tag im Voraus
- TriggerActions: HomeMatic Skript zum Auslösen der jeweiligen Aktion zum entsprechenden Zeitpunkt, getriggert per CUxD-Timer
- HomeMatic WebUI-Programm für jede Aktion zur Ansteuerung der Aktoren
- LogMe.tcl: TCL-Skript zum Loggen von Ereignissen während der Ausführung in eine Tagesbezogene Logdatei
- MailLogfile.tcl: TCL-Skript zum täglichen Versenden des aktuellen Logfiles per Mail
- CUxD (V0.58) (mit eingerichteten System.Devices „System.Timer“ und „System.Exec“)
Nach der Planung des Bedarfs der zu steuernden Rollläden und Lichter, sowie dem Einbau der Aktoren habe ich zunächst die Schaltzeiten in Excel geplant. Dazu habe ich mir überlegt, wann wir in etwa die einzelnen Licht üblicherweise ein- bzw. ausschalten. Rollläden werden üblicherweise zum bürgerlichen Sonnenauf- bzw. Untergang geschaltet (SA bzw. SU). Die Zeiten für die Lichter hingegen basieren meist auf den Zeiten des Indoor- Sonnenauf- bzw. Untergangs (SA_Indoor, SU_Indoor). Ursprünglich hatte ich mal eine Steuerung in Abhängigkeit des Wetterabhängigen Sonnenauf/Untergangs (SA_Wetter bzw. SU_Wetter) geplant. Dies ist mit meinem jetzigen Konzept aber nicht ohne weiteres Umsetzbar, da die Zeiten für SA/SU_Wetter noch nicht zu Beginn des Tages vorliegen, sondern erst unmittelbar vor dem eigentlichen Zeitpunkt.
Bei der Zeitplanung sind neben einem Schaltzeitpunkt auch Einschränkungen und Bedingungen eingeflossen. Im Falle eines variablen Zeitpunktes, kann ich diesen über untere bzw. obere Grenzen beschränken (frühestens/spätestens um). Ebenso können Bedingungen definiert werden, wie z.B. sinngemäß: Licht nur einschalten, wenn der die Sonne noch nicht aufgegangen ist. Abschließend kann eine Varianz über Zufallszahl eingerechnet werden.
Bei der Zeitplanung kann zwischen Wochentag und freiem Tag unterschieden werden.
Die Berechnung der Zeiten wird an jedem Morgen für alle Aktionen durchgeführt. Die Schaltzeiten und die dazugehörige Aktionsnummern werden jeweils in eine Liste (Komma-getrennter String) eingetragen.
Anschließend werden die beiden Liste chronologisch aufsteigend sortiert. Abschließend wird der CUxD-Timer auf den ersten Zeitpunkt in der Schaltzeitenliste eingestellt.
Der CUxD-Timer triggert ein zweites HomeMatic-Skript, welches die aktuelle Aktion aus der Liste ausliest und diese über eine Systemvariable triggert (dazu später mehr). Anschließend werden die aktuelle Schaltzeit und Aktion aus den beiden Listen entfernt und der Timer auf die nächste Schaltzeit aufgezogen.
Durch das Setzen der Umgebungsvariable auf die aktuelle Aktion wird das zur jeweiligen Aktion gehörige WebUI-Programm getriggert, das wiederum die Aktoren ansteuert.
Die Ansteuerung der Aktoren habe ich bewusst nicht im HomeMatic-Skript durchgeführt, da ich mehrere Aktoren unter einer Aktion zusammengefasst habe, die wiederum zeitversetzt angesteuert werden sollen. Mit einem WebUI-Programm ist dies wesentlich einfacher zu realisieren.
In den HomeMatic-Skripten und den WebUI-Programmen werden Loglevel-abhängig die Eriegnisse in einem Logfile protokolliert. Für jeden Tag gibt es ein eigenes Logfile, nach 14 Tagen wird ein Logfile automatisch gelöscht.
Am Ende des Tages wird das aktuelle Logfile per Email als Attachment an mich gesendet, so dass ich auch im Urlaub schnell und einfach kontrollieren kann, ob das Programm noch sauber läuft.
Im Folgenden werde ich die einzelnen Module näher erläutern
Freier_Tag:
Dieses HomeMatic-Skript basiert auf dem Feiertags-Skript von Homematic-Inside.de (Autoren: Petrus (Peter Beck), randyp). Ich habe die Feiertagsbestimmung für den nächsten Tag entfernt, da für mich irrelevant. Dafür habe ich die Abfrage ergänzt, ob es sich um ein Wochenende handelt. Handelt es sich um ein Wochenende oder um einen Feiertag, wird die Systemvariable „Freier_Tag“ auf True gesetzt.
Das Skript sollte kurz nach Mitternacht aufgerufen werden. Im Prinzip reicht es aber auch, es kurz vor dem Skript „CalcActionTimes“ aufzurufen.
Es werden folgende Systemvariablen benötigt:
- „Feiertag“ (Typ: Boolean)
- „Wochenende“ (Typ: Boolean)
- „Freier_Tag“ (Typ: Boolean)
Code: Alles auswählen
! Das Skript berechnet das Osterdatum und die Feiertage im aktuellen Jahr,
! prueft ob heute ein Feiertag ist und hinterlegt das Ergebnis in der Systemvariable "Feiertag"
! Script von Peter Beck (01-2011)
! Überarbeitet von Splunge (Feiertag_morgen entfernt, Wochenende und Freier_Tag hinzugefügt)
! Version 2.2.2s
! Es werden folgende Systemvariablen benötigt:
! Feiertag (Typ: Boolean)
! Wochenende (Typ: Boolean)
! Freier_Tag (Typ: Boolean)
boolean Rosenmontag = 0; ! Rosenmontag ein Brauchtumstag? 1=Ja/0=Nein
boolean Silvester = 1; ! Silvester stets ein freier Tag? 1=Ja/0=Nein
boolean Heiligabend = 1; ! Heiligabend stets ein freier Tag? 1=Ja/0=Nein
string land = "DE"; ! AT oder DE
string bland = "NW"; ! Bundesland in DE, in den wir wohnen (in Österreich auch für bland AT eintragen)
! Fuer "bland" bitte folgende Abkuerzungen benutzen:
! BW = Baden-Württemberg
! BY = Bayern
! BE = Berlin
! BB = Brandenburg
! HB = Bremen
! HH = Hamburg
! HE = Hessen
! MV = Mecklenburg-Vorpommern
! NI = Niedersachsen
! NW = Nordrhein-Westfalen
! RP = Rheinland-Pfalz
! SL = Saarland
! SN = Sachsen
! ST = Sachen-Anhalt
! SH = Schleswig-Holstein
! TH = Thüringen
! AT = Österreich
!****************************************************
! Ab hier Script CODE - Don't change behind this line
!****************************************************
! Berechnen des Ostersonntags im aktuellen Jahr
integer Jahr = system.Date("%Y").ToInteger(); ! Das aktuelle Jahr ermitteln
integer oTag; ! Das Tagesdatum vom Ostersonntag
integer oMonat; ! Das Monatsdatum von Ostersonntag
string oDatum; ! Das komplette Datum vom Ostersonntag
integer LVar1; ! Wird für die Berechnungen benötigt
integer LVar2; ! Wird für die Berechnungen benötigt
integer LVar3; ! Wird für die Berechnungen benötigt
integer zahl; ! Wird für die Berechnungen benötigt
! Die modifizierte Gauss-Formel nach Lichtenberg
LVar1 = ((19 * (Jahr % 19)) + (15 + (((3 * (Jahr / 100)) + 3) / 4) - (((8 * (Jahr / 100)) + 13) / 25))) % 30;
LVar2 = (LVar1 / 29) + (((LVar1 / 28) - (LVar1 / 29)) * ((Jahr % 19) / 11));
LVar3 = 7 - (((21 + LVar1 - LVar2) - (7 - ((Jahr + (Jahr / 4) + (2 - (((3 * (Jahr / 100)) + 3) / 4))) % 7))) % 7);
oTag = (21 + LVar1 - LVar2) + LVar3;
! Den errechneten Wert formatieren. Ausgehend vom Monat März
if (oTag > 31) { ! Der Monat März hat nur 31 Tage -> Ostern im April
oMonat = 4;
oTag = oTag - 31;
} else { oMonat = 3; } ! Ostern ist im März
! Das Ergebnis in Variable oDatum hinterlegen
if (oTag < 10) {
oDatum = "0" # oTag.ToString() # "." # "0" # oMonat.ToString() # "." # Jahr.ToString();
} else {
oDatum = oTag.ToString() # "." # "0" # oMonat.ToString() # "." # Jahr.ToString();
}
! Ermitteln, wieviele Tage der Februar im aktuellen Jahr hat
zahl = Jahr % 4;
if (zahl == 0) { ! Ein Schaltjahr -> Feb. = 29 Tage
string mTage = "31,29,31,30,31,30,31,31,30,31,30,31"; ! Anzahl der Tage im Monat Jan.-Dez.
} else {
string mTage = "31,28,31,30,31,30,31,31,30,31,30,31"; ! Anzahl der Tage im Monat Jan.-Dez.
}
string sDatum = system.Date("%d.%m."); ! Das heutige Datum
string sWochentag = system.Date("%w"); ! der heutige Wochentag
boolean Feiertag = 0; ! 0/1 = kein/Feiertag
boolean Wochenende = 0; ! 0/1 = kein/Wochenende
if ((sWochentag == "0") || (sWochentag == "6")) { Wochenende = 1; } ! Wochenende
!***************************************************************************
! Feste Feiertage bundesweit, fuer DE und AT
!***************************************************************************
if (sDatum == "01.01.") { Feiertag = 1; } ! Neujahr
if (sDatum == "01.05.") { Feiertag = 1; } ! Maifeiertag - Tag der Arbeit
if (sDatum == "25.12.") { Feiertag = 1; } ! 1. Weihnachtstag
if (sDatum == "26.12.") { Feiertag = 1; } ! 2. Weihnachtstag
!***************************************************************************
! Feste Feiertage bundesweit in DE
!***************************************************************************
if (land == "DE") {
if (sDatum == "03.10.") { Feiertag = 1; } ! Tag der Deutschen Einheit
}
!***************************************************************************
! Feste Feiertage bundesweit in AT
!***************************************************************************
if (land == "AT") {
if (sDatum == "26.10.") { Feiertag = 1; } ! Österreichischer Nationalfeiertag
if (sDatum == "08.12.") { Feiertag = 1; } ! Maria Empfaengnis
}
!***************************************************************************
! Eigene, feste Feiertage?
!***************************************************************************
if ((Heiligabend) && (sDatum == "24.12.")) { Feiertag = 1; } ! Weihnachten
if ((Silvester) && (sDatum == "31.12.")) { Feiertag = 1; } ! Silvester
!***************************************************************************
! Fester Feiertag in BW, BY, ST, AT
!***************************************************************************
if ((bland == "BW") || (bland == "BY") || (bland == "ST") || (land == "AT")) {
if (sDatum == "06.01.") { Feiertag = 1; } ! Erscheinungsfest - Hl. 3 Koenige
}
!***************************************************************************
! Fester Feiertag in BB, MV, SA, ST, TH
!***************************************************************************
if ((bland == "BB") || (bland == "MV") || (bland == "SA") || (bland == "ST") || (bland == "TH")) {
if (sDatum == "31.10.") { Feiertag = 1; } ! Reformationstag
}
!***************************************************************************
! Fester Feiertag in BW, BY, NW, RP, SL, AT
!***************************************************************************
if ((bland == "BW") || (bland == "BY") || (bland == "NW") || (bland == "RP") || (bland == "SL") || (land == "AT")) {
if (sDatum == "01.11.") { Feiertag = 1; } ! Allerheiligen
}
!***************************************************************************
! Fester Feiertag in BY (nicht überall), SL, AT
!***************************************************************************
if ((bland == "BY") || (bland == "SL") || (land == "AT")) {
if (sDatum == "15.08.") { Feiertag = 1; } ! Maria Himmelfahrt
}
!***************************************************************************
!** Bewegliche Feiertage bundesweit **
!***************************************************************************
string feiertage = "";
if (land == "DE") {
feiertage = "0,1,-2,39,50"; ! Array mit Differenztage zum Ostersonntag
! Ostersonntag, Ostermontag, Karfreitag, Christi Himmelfahrt, Pfingstmontag
}
if (land == "AT") {
feiertage = "0,1,39,50"; ! Array mit Differenztage zum Ostersonntag
! Ostersonntag, Ostermontag, Christi Himmelfahrt, Pfingstmontag
}
!***************************************************************************
! Bewegliche Feiertage BW, BY, HE, NW, RP, SL, (SA, TH nicht überall), AT
!***************************************************************************
if ((bland == "BW") || (bland == "BY") || (bland == "HE") || (bland == "NW") || (bland == "RP") || (bland == "SL") || (bland == "SA") || (bland == "TH") || (land == "AT")) {
feiertage = feiertage # ",60"; ! Fronleichnam
}
!***************************************************************************
!*** Hier evtl. weitere Feier-/Brauchtumstage ***
!***************************************************************************
if (Rosenmontag) {
feiertage = feiertage # ",-48"; ! Rosenmontag stets 48 Tage vor dem Ostersonntag
}
!***************************************************************************
! Ab hier werden die Feiertage berechnet
!***************************************************************************
! Anzahl der Tage im Ostermonat
zahl = (3 * oMonat) - 3;
integer oTage = mTage.Substr(zahl, 2).ToInteger(); ! Anzahl der Tage im Ostermonat
string fDatum; ! Das Feiertagsdatum
integer fDiff; ! Anzahl der Tage von Ostersonntag entfernt
boolean Schleife; ! Für die Berechnungen in der while-Schleife notwendig
string index;
foreach(index, feiertage.Split(",")) {
fDiff = index.ToInteger();
Schleife = 1;
zahl = oTag + fDiff; ! Differenztage zum Referenztag hinzurechnen
if (fDiff > 0) {
if (zahl > oTage) {
zahl = zahl - oTage; ! Sind wir ausserhalb des Referenzmonats?
} else { ! Nein => Berechnungschleife nicht durchlaufen.
Schleife = 0;
LVar2 = oMonat;
}
} else {
if (zahl > 0) { ! Wenn innerhalb des Referenzmonats => Berechnungsschleife nicht durchlaufen.
Schleife = 0;
LVar2 = oMonat;
}
}
LVar1 = 0;
if (fDiff > 0) {
while (Schleife) {
LVar1 = LVar1 + 1.00;
LVar2 = oMonat + LVar1;
LVar3 = (3 * LVar2) - 3;
zahl = zahl - mTage.Substr(LVar3, 2).ToInteger();
if (zahl <= 0) { ! Aktueller Monat gefunden
Schleife = 0;
zahl = zahl + mTage.Substr(LVar3, 2).ToInteger(); ! Wieder addieren, um den Tag zu berechnen
}
}
} else {
if (fDiff < 0) {
while (Schleife) {
LVar1 = LVar1 + 1.00;
LVar2 = oMonat - LVar1;
LVar3 = (3 * LVar2) - 3;
zahl = zahl + mTage.Substr(LVar3, 2).ToInteger();
if (zahl > 0) { ! Aktueller Monat gefunden
Schleife = 0;
}
}
}
}
if (zahl < 10) {
fDatum = "0" # zahl.ToString() # ".";
} else {
fDatum = zahl.ToString() # ".";
}
if (LVar2 < 10) {
fDatum = fDatum # "0" # LVar2.ToString() # ".";
} else {
fDatum = fDatum # LVar2.ToString() # ".";
}
if (sDatum == fDatum) { Feiertag = 1; }
}
!****************************
! Beweglicher Feiertag im SL
!****************************
if (bland == "SL") {
! Buß- und Bettag (Mittwoch vor dem Sonntag vor dem 1. Advent)
fDiff = 32; ! 32 Tage vor dem 4. Advent
! Um den 4. Advent zu ermitteln, muss zunächst der Wochentag des 24. Dez. ermittelt werden
! Wochentagberechnung wie folgt:
! TZ = Tag mod 7 => Tag = 24 fuer den 24.12.
! MZ = 5 => 0,3,3,6,1,4,6,2,5,0,3,5 (Fuer jeden Monat Jan. bis Dez. eine Ziffer)
! JZ = (Zahl + (Zahl / 4)) mod 7 => Zahl = 10, die letzten beiden Ziffern der Jahreszahl 2010
! JHZ = (3 - (Zahl mod 4)) * 2 => Zahl = 20, die ersten beiden Ziffern der Jahreszahl 2010
! SJK = Zahl => Schaltjahreskorrektur: Zahl = 0 wenn kein Schaltjahr, sonst Zahl = 6
! Ergebnis = (TZ + MZ + JZ + JHZ + SJK) mod 7 => 0 = So, 1 = Mo, 2 = Di, 3 = Mi, 4 = Do, 5 = Fr, 6 = Sa
! Mit dieser Rechnung kann man zu jedem Datum den Wochentag berechnen.
LVar1 = oDatum.Substr(8, 2).ToInteger();
zahl = (LVar1 + (LVar1 / 4)) % 7;
LVar1 = oDatum.Substr(6, 2).ToInteger();
LVar2 = ((3 - (LVar1 % 4)) * 2) + zahl;
LVar1 = oDatum.Substr(6, 4).ToInteger() % 4;
if (LVar1 == 0) { zahl = 6; } else { zahl = 0; }
LVar1 = ((24 % 7) + 5 + LVar2 + zahl) % 7; ! Ergebnis (LVar1) ist die Wochentagszahl
! Feiertag ermitteln: 30 Novembertage - (fDiff + Wochentagszahl - 24)
! Der Monat ist stets der November
zahl = 30 - (fDiff + LVar1 - 24);
fDatum = zahl.ToString() # ".11.";
if (sDatum == fDatum) { Feiertag = 1; }
}
dom.GetObject('Feiertag').State(Feiertag);
dom.GetObject('Wochenende').State(Wochenende);
if ((Feiertag == 1) || (Wochenende == 1))
{
dom.GetObject('Freier_Tag').State(1);
}
else
{
dom.GetObject('Freier_Tag').State(0);
}
Dieses TCL-Skript ist eine überarbeitete Fassung des Dämmerungsskriptes von DocZoid. Wie schon beschrieben habe ich die Ausgabe der Sonnenauf- und Untergangszeiten in Systemvariablen hinzugefügt, um sie in meiner Schaltzeitenberechnung verarbeiten zu können.
Erweitert habe ich ebenso die wetterabhängige Berechnung der Zeiten. Nach der Umstellung von Google auf Wunderground waren die Wetterzustände noch nicht angepasst worden.
Ausserdem habe ich die Programmparameter angepasst. Die PLZ wird nicht mehr benötigt. Dafür habe ich den Ortsnamen und den Wunderground-API-Key als Parameter vorgesehen.
Letztlich habe ich noch eine Überlaufsverrechnung beim Löschen des ältesten Logfiles implementiert und diverse kleinere Bugs behoben.
Das Skript läuft nach einmaligem Start autonom im Hintergrund. Aufgerufen wird das skript mit folgenden Parametern:
- param 0 = Breitengrad (noerdlich)
- param 1 = Laengengrad (oestlich)
- param 2 = kuenstlicher Horizont
- param 3 = Stadt für Wetterinformation
- param 4 = API Key für Wunderground.com
- „Daemmerungszeiten“ (Type Zeichenkette)
- „Daemmerung“ (Type Werteliste: „Nacht;Aufgang Astronomisch;Aufgang Nautisch;Aufgang Bürgerlich;Aufgang;Aufgang Indoor;Aufgang Wetterabhängig;Untergang Wetterabhängig;Untergang Indoor;Untergang;Untergang Bürgerlich;Untergang Nautisch;Untergang Astronomisch“)
- „Tageslicht“ (Type Zahl)
- „Wetterzustand“ (Type Zeichenkette)
- „Sekunden_SA_Buergerlich“ (Typ Zahl - 0 bis 2147483647)
- „Sekunden_SU_Buergerlich“ (Typ Zahl - 0 bis 2147483647)
- „Sekunden_SA_Indoor“ (Typ Zahl - 0 bis 2147483647)
- „Sekunden_SU_Indoor“ (Typ Zahl - 0 bis 2147483647)
Code: Alles auswählen
#!/bin/tclsh
#Version 1.5c with wunderground patch (extended weather handling) and acosinus fix
#param 0 = Breitengrad (noerdlich)
#param 1 = Laengengrad (oestlich)
#param 2 = kuenstlicher Horizont
#param 3 = Stadt für Wetterinformation
#param 4 = API Key für Wunderground.com
#
# Systemvariables to be set up in CCU:
# "Daemmerungszeiten" (Type Zeichenkette)
# "Daemmerung" (Type Werteliste: Nacht;Aufgang Astronomisch;Aufgang Nautisch;Aufgang Bürgerlich;Aufgang;Aufgang Indoor;Aufgang Wetterabhängig;Untergang Wetterabhängig;Untergang Indoor;Untergang;Untergang Bürgerlich;Untergang Nautisch;Untergang Astronomisch)
# "Tageslicht" (Type Zahl)
# "Wetterzustand" (Type Zeichenkette)
# "Sekunden_SA_Buergerlich" (Typ Zahl - 0 bis 2147483647)
# "Sekunden_SU_Buergerlich" (Typ Zahl - 0 bis 2147483647)
# "Sekunden_SA_Indoor" (Typ Zahl - 0 bis 2147483647)
# "Sekunden_SU_Indoor" (Typ Zahl - 0 bis 2147483647)
#only run once, check if locking port 60 is opened
if {[catch {socket -server unknown -myaddr 127.0.0.1 60} locksock]} then {
exit 0
}
source /www/addons/lcd_msg/daemonize.tcl
load tclrega.so
proc showTime x {
if {$x && $x != 2000000000} then {
return [clock format $x -format "%H:%M:%S"]
} else {
return "--:--:--"
}
}
proc log x {
puts $::logfile "[showTime [clock seconds]]: $x"
flush $::logfile
}
#define constants for the list access to make review easier
set NIGHT 0
set SR_ASTRONOMICAL 1
set SR_NAUTICAL 2
set SR_CIVIL 3
set SR_SUNRISE 4
set SR_INDOOR 5
set SR_WEATHER 6
set SS_WEATHER 7
set SS_INDOOR 8
set SS_SUNRISE 9
set SS_CIVIL 10
set SS_NAUTICAL 11
set SS_ASTRONOMICAL 12
proc getTimeDiff {dayofyear} {
return [expr {-0.171*sin(0.0337 * $dayofyear + 0.465) - 0.1299*sin(0.01787 * $dayofyear - 0.168)}]
}
proc calcTwilightTimes {latitude longitude timezone dayofyear horizon sunrisevar sunsetvar} {
upvar $sunrisevar sunrise
upvar $sunsetvar sunset
set timediff [expr {-0.171*sin(0.0337 * $dayofyear + 0.465) - 0.1299*sin(0.01787 * $dayofyear - 0.168)}]
set declination [expr {0.4095 * sin(0.016906 * ($dayofyear - 80.086) ) }]
set acosvar [expr ((sin($horizon/57.29578) - sin($latitude/57.29578)*sin($declination)) / (cos($latitude/57.29578)*cos($declination)))]
if {$acosvar <-1} {
set acosvar -1
log "Die Variable acosvar wurde auf -1 gesetzt, da ihr ursprünglicher Wert kleiner -1 war"
}
if {$horizon == -18} {
log "Die Variable acosvar muss zwischen -1 und 1, liegen und ist momentan $acosvar"
}
set suntime [expr {12*acos($acosvar)/3.141592}]
set sunrise [expr {[clock scan "0"] + round((12 - $timediff - $suntime - $longitude/15.0 + $timezone)*3600)}]
set sunset [expr {[clock scan "0"] + round((12 - $timediff + $suntime - $longitude/15.0 + $timezone)*3600)}]
}
proc getTwilightHorizon {base_horizon city apikey} {
set url "http://api.wunderground.com/api/$apikey/conditions/lang:DL/q/germany/$city.xml"
set twilightHorizon [expr {$base_horizon + 1.0}]
exec /usr/bin/wget -q -O /tmp/twilightweather.xml $url
set f [open "/tmp/twilightweather.xml"]
set input [read $f]
close $f
regexp "<current_observation>(.*?)</current_observation>" $input dummy current ; #get current observation
regexp "<weather>(.*?)</weather>" $current dummy weather ; #get current weather
log "current condition is \"$weather\""
file delete /tmp/twilightweather.xml
rega_script "dom.GetObject('Wetterzustand').State('$weather');"
switch $weather {
"Heiter" {set twilightHorizon [expr {$base_horizon + 0.0}]}
"Teils Wolkig" {set twilightHorizon [expr {$base_horizon + 1.0}]}
"Wolkig" {set twilightHorizon [expr {$base_horizon + 1.5}]}
"Bedeckt" {set twilightHorizon [expr {$base_horizon + 2.0}]}
"Dunst" {set twilightHorizon [expr {$base_horizon + 1.0}]}
"Nebel" {set twilightHorizon [expr {$base_horizon + 1.5}]}
"Leichtes Nieseln" {set twilightHorizon [expr {$base_horizon + 2.0}]}
"Nieselregen" {set twilightHorizon [expr {$base_horizon + 2.3}]}
"Leichter Regen" {set twilightHorizon [expr {$base_horizon + 2.8}]}
"Leichte Regenschauer" {set twilightHorizon [expr {$base_horizon + 3.0}]}
"Regen" {set twilightHorizon [expr {$base_horizon + 3.7}]}
"Starkregen" {set twilightHorizon [expr {$base_horizon + 4.5}]}
"Gewitter" {set twilightHorizon [expr {$base_horizon + 4.0}]}
"Gewitter mit Regen" {set twilightHorizon [expr {$base_horizon + 5.0}]}
"Kleinkörniger Hagel" {set twilightHorizon [expr {$base_horizon + 4.0}]}
"Leichter Schneefall" {set twilightHorizon [expr {$base_horizon + 3.0}]}
"Schneefall" {set twilightHorizon [expr {$base_horizon + 5.0}]}
}
log "twilight horizon set to $twilightHorizon"
return $twilightHorizon
}
proc getDayOfYear {timestamp} {
set dayofyear [string trimleft [clock format $timestamp -format "%j"] "0"]
#subtract the fractional part to the next leap-year february 29th from the current day of the year
set dayofyear [expr {$dayofyear - ([clock format $timestamp -format "%Y"]%4)/4.0 - ($dayofyear)/365.25/4.0}]
return $dayofyear
}
proc waitToTime {logfile destTime} {
set waittime [expr {1000*($destTime - [clock seconds])}]
if {$waittime > 0} then {
log "waiting [expr $waittime/1000] s until [clock format $destTime]"
#after is off of several seconds (exactly 1/1000?) after long waits, so wait 1% shorter and then wait again
after [expr { 990*($destTime - [clock seconds])}]
after [expr {1000*($destTime - [clock seconds])}]
} else {
log "not waiting [expr $waittime/1000] s until [clock format $destTime]"
}
}
proc displayValues {} {
uplevel {
set display "s. folgende Tabelle vom [clock format [lindex $twilight_times 6] -format "%d.%m."]"
append display "</div><br/><table border=\"1\" bordercolor=\"#000000\" style=\"background-color:#5d6373\" width=\"100%\">\r\n"
append display "<tr><td><b>Daemmerung</b></td><td><b>Morgens</b></td><td><b>Abends</b></td></tr>\r\n"
for {set j 1} {$j <= 6} {incr j} {
set insert "[lindex $twilight_descr $j]:"
if {$j == $i || $j == 12-$i} then {set insert "<b>$insert</b>"}
append display "<tr><td>$insert</td>"
set insert "[showTime [lindex $twilight_times [expr $j-1]]]"
if {$j == $i} then {set insert "<b>$insert</b>"}
if {$j == 6 && !$srw_set} then {set insert "<i>$insert</i>"}
append display "<td>$insert</td>"
set insert "[showTime [lindex $twilight_times [expr 12-$j]]]"
if {$j == 13-$i} then {set insert "<b>$insert</b>"}
if {$j == 6 && !$ssw_set} then {set insert "<i>$insert</i>"}
append display "<td>$insert</td></tr>\r\n"
#log "updating twilight time table: [showTime [lindex $twilight_times [expr $j-1]]] [showTime [lindex $twilight_times [expr 12-$j]]] [string map {: { }} [lindex $twilight_descr [expr $j-1]]]"
}
append display "</table></td>"
rega_script "dom.GetObject('Daemmerungszeiten').State('$display');"
rega_script "dom.GetObject('Sekunden_SA_Buergerlich').State('[lindex $twilight_times 2]');"
rega_script "dom.GetObject('Sekunden_SA_Indoor').State('[lindex $twilight_times 4]');"
rega_script "dom.GetObject('Sekunden_SU_Indoor').State('[lindex $twilight_times 7]');"
rega_script "dom.GetObject('Sekunden_SU_Buergerlich').State('[lindex $twilight_times 9]');"
}
}
#
# Main execution
#
set latitude [lindex $argv 0]
set longitude [lindex $argv 1]
set indoor_horizon [lindex $argv 2]
set city [lindex $argv 3]
set apikey [lindex $argv 4]
#some vars we need
set horizon_values [list 0 -18 -12 -6 [expr {- 50.0 / 60.0}] $indoor_horizon [expr {$indoor_horizon + 5.1}]]
set twilight_names [list night astronomical nautical civil standard indoor weather]
set twilight_descr [list Nacht Astronom. Nautisch Buergerlich Standard Indoor Wetterabh.]
while {1} {
set timezone [expr {([clock scan "0 UTC"] - [clock scan "0"]) / 3600.0}]
set now [clock seconds]
set twilight_midnight [expr {[clock scan "0"] + round((0 - [getTimeDiff [getDayOfYear $now]] - $longitude/15.0 + $timezone)*3600)}]
#get the day of the year as numeric value based on twilight midnight (time between midnight and twilight midnight belongs to day before)
set yesterday_offset 0
if {$now < $twilight_midnight} then {
#use time table from day before
set yesterday_offset 86400
}
set dayofyear [getDayOfYear [expr $now-$yesterday_offset]]
set writeday [string trimleft [clock format $now -format "%j"] "0"]
set deleteday [expr $writeday-14]
if {$deleteday <= 0} {
set jahr [clock format [clock seconds] -format "%Y"]
if {(int($jahr)-1) % 4 == 0 && ((int($jahr)-1) % 100 != 0 || (int($jahr)-1) % 400 == 0)} {
set deleteday [expr $deleteday + 366]
} else {
set deleteday [expr $deleteday + 365]
}
}
catch {file delete "/tmp/twilight.$deleteday.log"}
set logfile [open "/tmp/twilight.$writeday.log" w]
log "args: $argv"
if {$now < $twilight_midnight} then {log "twilight midnight not reached, using yesterdays time table"}
log "timezone: $timezone, twilight midnight: [showTime $twilight_midnight], dayofyear: $dayofyear"
set twilight_times ""
for {set i 1} {$i <= 6} {incr i} {
calcTwilightTimes $latitude $longitude $timezone $dayofyear [lindex $horizon_values $i] sunrise_time sunset_time
if {$sunrise_time == 0} then {
log "no [lindex $twilight_names $i] twilight here and now"
} elseif {$i != 6} then {
log "calculated twilight times: sunrise [clock format $sunrise_time -format "%d., %H:%M:%S"], sunset [clock format $sunset_time -format "%d., %H:%M:%S"] for [lindex $twilight_names $i] ($i)"
}
#do not append weather sunrise
if {$i != 6} then {
lappend twilight_times [expr $sunrise_time-$yesterday_offset]
}
#append indoor sunrise again to replace weather sunrise
if {$i == 5} then {
lappend twilight_times [expr $sunrise_time-$yesterday_offset+1]
}
#always append sunset
lappend twilight_times [expr $sunset_time-$yesterday_offset]
}
set twilight_times [lsort $twilight_times]
log "calculated twilight times: sunrise [clock format [lindex $twilight_times 5] -format "%d., %H:%M:%S"], sunset [clock format [lindex $twilight_times 6] -format "%d., %H:%M:%S"] for [lindex $twilight_names 6] (6)"
set srw_set 0
set ssw_set 0
displayValues
#in the following calculation, assume that the weather sunrise and sunset are always subsequently on the current dayofyear
for {set i 0} {$i < 12} {incr i} {
set next_time [lindex $twilight_times $i]
if {$i == 5 && [clock seconds] < [lindex $twilight_times 6]} then {
#sunrise weather
log "retrieving weather condition for sunrise"
calcTwilightTimes $latitude $longitude $timezone $dayofyear [getTwilightHorizon $indoor_horizon $city $apikey] sunrise_weather dummy
set next_time $sunrise_weather
set srw_set 1
set twilight_times [lreplace $twilight_times 5 5 $sunrise_weather]
displayValues
log "weather sunrise at [showTime $next_time]"
}
if {[clock seconds] < $next_time && $next_time != 2000000000} then {
log "current twilight state $i ([lindex $twilight_descr [expr {6-abs($i-6)}]]), sunlight state [expr {6-abs($i-6)}], next at [showTime $next_time]"
rega_script "dom.GetObject('Daemmerung').State('$i');"
rega_script "dom.GetObject('Tageslicht').State('[expr {6-abs($i-6)}]');"
displayValues
waitToTime $logfile $next_time
}
if {$i == 6 && [clock seconds] < [lindex $twilight_times 7]} then {
#sunset weather
log "retrieving weather condition for sunset"
calcTwilightTimes $latitude $longitude $timezone $dayofyear [getTwilightHorizon $indoor_horizon $city $apikey] dummy sunset_weather
set next_time $sunset_weather
set ssw_set 1
set twilight_times [lreplace $twilight_times 6 6 $sunset_weather]
displayValues
log "weather sunset at [showTime $next_time]"
waitToTime $logfile $next_time
}
}
rega_script "dom.GetObject('Daemmerung').State('12');"
rega_script "dom.GetObject('Tageslicht').State('0');"
log "twilight cycle completed for day of year $dayofyear, waiting until twilight midnight for next run"
waitToTime $logfile [expr {$twilight_midnight + 86400 - $yesterday_offset}]
close $logfile
}
if {[file exists /var/run/twilight.tcl.pid]} then {
catch {
set f [open /var/run/twilight.tcl.pid]
set filepid [read $f]
close $f
if {[pid] == $filepid} then {
file delete /var/run/twilight.tcl.pid
}
}
}
close $locksock
Aufgerufen wird dieses HomeMatic-Skript kurz vor dem theoretisch frühesten Zeitpunkt. Ein zu frühes Aufrufen führt dazu, dass z.B. In Wintermonaten noch nicht die Sonnenauf- und Untergangszeiten für den aktuellen Tag vom Twilight-Skript berechnet wurden. Es werden dann die Werte vom Vortag verwendet. Der Fehler von 1-2 Minuten dürfte aber im Rauschen der Zufallszahl untergehen.
Auf Basis der Sonnenauf/Untergangszeiten und der Information, ob es sich um einen freien Tag handelt, werden in diesem HomeMatic-Skript nun die Zeiten für die einzelnen Aktionen berechnet, ausgehend von der Planung in Excel.
Die Basis für die Zufallszahlen wird für den Vormittag und Nachmittag getrennt gebildet. Die einzelnen Verlängerungen werden dann per Modulo-Operation ermittelt. Dadurch haben alle Verlängerungen auf Basis einer Zufallszahl zwischen 0 und 20min am Vormittag den gleichen Wert, die Verlängerungen auf Basis einer Zufallszahl zwischen 0 und 30min aber einen anderen Wert. Das Gleichhalten der Verlängerungen ist wichtig bei Aktionen auf gleiche Aktion, um z.B. das Ein- und Ausschalten des Lichtes in der richtigen Reihenfolge zu gewährleisten (Vermeidung von Überschneidungen).
Nach der Ermittlung der Schaltezeiten, werden diese zusammen mit den Aktionen chronologisch sortiert. Dies geschieht etwas antiquiert über eine Art Bubble-Sort. Eine effizientere Lösung ist mit einem HomeMatic-Skript leider nicht möglich. Evtl. werde ich es mal in ein TCL-Skript ausgliedern. Momentan sehe ich aber kein Problem damit, da die Anzahl der zu sortierenden Elemente sehr übersichtlich ist.
Nach dem Sortieren wird der CUxD-Timer auf den Zeitpunkt der ersten Aktion aufgezogen. Damit ist dieses Skript beendet.
Umgebungsvariablen, die benötigt werden:
lesend:
- "Sekunden_SA_Buergerlich" (Typ Zahl - 0 bis 2147483647)
- "Sekunden_SU_Buergerlich" (Typ Zahl - 0 bis 2147483647)
- "Sekunden_SA_Indoor" (Typ Zahl - 0 bis 2147483647)
- "Sekunden_SU_Indoor" (Typ Zahl - 0 bis 2147483647)
- "Freier_Tag" (Typ Logikwert)
- "YAWN_LogLevel" (Typ Integer - 0 bis 3) s.u.
- "YAWN_ZeitenListe" (Typ Zeichenkette)
- "YAWN_AktionsListe" (Typ Zeichenkette)
Code: Alles auswählen
! #############################################################
! Yet Another anWesenheitssimulatioN (YAWN)
! CalcActionTimes
! V1.1
! by Splunge
! #############################################################
! Beschreibung:
! Festlegung verschiedener Aktionen zu bestimmten Zeitpunkten
! Die Zeitpunkte können fest oder von verschiedenen Sonnenauf-
! bzw. untergangszeiten abhängig sein.
! Die Zeiten können eine Untergrenze (frühestens) oder Obergrenze
! (spätestens) haben
! Die Zeiten können Bedingungen haben (nur wenn vor/nach xx:xx)
! Es kann eine Zufallszeit eingerechnet werden
! Die ermittelten Zeiten werden parallel mit der zugehörigen
! Aktionsnummer in jeweils eine Liste geschrieben.
! Anschließend werden die Listen in chronologischer Reihenfolge
! sortiert.
! Abschließend wird der erste Zeitwert verwendet und der
! CUxD-Timer damit aufgezogen.
! Damit ist dieses Skript beendet.
! Der CUxD-Timer triggert ein weiteres Programm, dass im folgenden
! die jeweils aktuelle Aktionen ausführt und den CUxD-Timer für
! die nächste Aktion aufzieht.
! #############################################################
! Änderungen:
! V1.1:
! Kommentar bzgl. Datentyp von Sekunde_SA_Buergerlich etc. korrigiert.
! #############################################################
! Systemvoraussetzungen:
! - CUxD (V0.58) muss installiert sein (als Geräte müssen SystemExec
! und Timer eingerichtet sein)
! - modifiziertes Twilight.tcl (V1.5b) muss installiert und aktiv sein
! (setzt wiederum den daemonizer aus dem LCD_Msg-Addon voraus)
! - zur Bestimmung eines freien Tages (Wochenende oder Feiertag) kann
! das modifizierte Feiertagsscript (V2.2.2s) auf Basis der Version
! von Homematic-Inside verwendet werden
! - LogMe.tcl (>= V1.0) muss vorhanden sein, wenn Logging verwendet
! werden soll
! #############################################################
! Ausführungszeitpunkt des Skriptes:
! um 06:10, da der theoretisch frühest mögliche Zeitpunkt für eine
! Aktion 06:15 ist. Das hat jedoch zur Folge, dass in Wintermonaten,
! in denen der astronomische Sonnenaufgang nach 06:10 liegt
! (ca. 06.12. bis 03.02.), die Sonnenauf- und untergangswerte vom
! Vortag verwendet werden.
! Der Zufallswert sollte dies jedoch überdecken
! #############################################################
! Umgebungsvariablen, die benötigt werden
!
! lesend:
! "Sekunden_SA_Buergerlich" (Typ Zahl - 0 bis 2147483647)
! "Sekunden_SU_Buergerlich" (Typ Zahl - 0 bis 2147483647)
! "Sekunden_SA_Indoor" ((Typ Zahl - 0 bis 2147483647)
! "Sekunden_SU_Indoor" (Typ Zahl - 0 bis 2147483647)
! "Freier_Tag" (Typ Logikwert)
! "YAWN_LogLevel" (Typ Integer - 0 bis 3)
!
! schreibend:
! "YAWN_ZeitenListe" (Typ Zeichenkette)
! "YAWN_AktionsListe" (Typ Zeichenkette)
! #############################################################
! Variablendefinition
! Variablen für die Sonnenauf/untergangszeiten
time SA_Buergerlich; ! Absolute Zeit des bürgerlichen Sonnenaufgangs
time SU_Buergerlich; ! Absolute Zeit des bürgerlichen Sonnenuntergangs
time SA_Indoor; ! Absolute Zeit des Indoor Sonnenaufgangs
time SU_Indoor; ! Absolute Zeit des Indoor Sonnenuntergang
! Variablen für die Zufallszahl
string tmpRand; ! Hilfsvariable für Zufallszahl (inkl. Modulo für aktuelle Aktion)
integer RAND; ! Zufallszahl für Vormittag bzw. Nachmittag
! Variablen für die Zeitberechnung
boolean freierTag;
integer tmpStd; ! Hilsvariable zur Berechnung der Tagessekunden
integer tmpMin; ! Hilsvariable zur Berechnung der Tagessekunden
integer tmpSek; ! Hilsvariable zur Berechnung der Tagessekunden
integer tmpStartTime; ! Startzeitpunkt der aktuellen Aktion in Tagessekungen
integer tmpLowerLimit; ! Unteres Limit in Tagessekunden
integer tmpUpperLimit; ! Oberes Limit in Tagessekunden
integer tmpIfBefore; ! Bedingungenszeitpunkt "wenn vor Zeitpunkt" in Tagessekunden
integer tmpIfAfter; ! Bedingungszeitpunkt "wenn nach Zeitpunkt" in Tagessekunden
integer tmpJetzt; ! Enthält den aktuellen Zeitpunkt in Tagessekunden
integer tmpTwilight; ! Hilfsvariable für Dämmerungszeiten
string actAktion; ! Aktuell berechnete Aktion
! Variablen für das Sortieren
string unsortedTimes = ""; ! unsortierte Werteliste
string unsortedActions = ""; ! unsortierte Aktionsliste
string tmpTimes; ! Hilfsliste für die Werte
string tmpActions; ! Hilfsliste für die Aktionen
string sepr = ","; ! Listentrennzeichen
integer i; ! Schleifenindex
integer minIndex; ! Index des minimalen Elementes
string actValue; ! aktueller Wert der unsortierten Liste
integer minValue; ! kleinster gefundener Wert
string sortedTimes = ""; ! sortierte Werteliste
string sortedActions = ""; ! sortierte Aktionsliste
! Variablen für die Timersteuerung
string timerWert; ! Weckzeit für das erste Ereignis
! Variablen für das Logging
integer LogLevel; ! Level des Loggings (0 = Nichts, 1 = Aktionen, 2 = Berechnung und Aktionen, 3 = Alles)
string logtext = "Berechne Aktionszeiten... _nl_ "; ! Logtext
! #############################################################
! Sonnenauf- und untergangszeiten holen
SA_Buergerlich = (dom.GetObject('Sekunden_SA_Buergerlich').State()).ToTime();
SU_Buergerlich = (dom.GetObject('Sekunden_SU_Buergerlich').State()).ToTime();
SA_Indoor = (dom.GetObject('Sekunden_SA_Indoor').State()).ToTime();
SU_Indoor = (dom.GetObject('Sekunden_SU_Indoor').State()).ToTime();
! Information ob freier Tag (Wochenende, Feiertag) holen
freierTag = dom.GetObject('Freier_Tag').State();
! jetzigen Uhrzeit ermitteln
tmpStd = system.Date("%H").ToInteger();
tmpMin = system.Date("%M").ToInteger();
tmpSek = system.Date("%S").ToInteger();
tmpJetzt = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
! Logging-Status holen
LogLevel = dom.GetObject('YAWN_LogLevel').State();
if (LogLevel >= 2)
{
tmpStd = SA_Buergerlich.Hour();
tmpMin = SA_Buergerlich.Minute();
tmpSek = SA_Buergerlich.Second();
tmpTwilight = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
logtext = logtext # "Sonnenaufgang Bürgerlich: " # tmpTwilight # " - " # SA_Buergerlich.Format("%H:%M:%S") # " _nl_ ";
tmpStd = SA_Indoor.Hour();
tmpMin = SA_Indoor.Minute();
tmpSek = SA_Indoor.Second();
tmpTwilight = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
logtext = logtext # "Sonnenaufgang Indoor: " # tmpTwilight # " - " # SA_Indoor.Format("%H:%M:%S") # " _nl_ ";
tmpStd = SU_Indoor.Hour();
tmpMin = SU_Indoor.Minute();
tmpSek = SU_Indoor.Second();
tmpTwilight = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
logtext = logtext # "Sonnenuntergang Indoor: " # tmpTwilight # " - " # SU_Indoor.Format("%H:%M:%S") # " _nl_ ";
tmpStd = SU_Buergerlich.Hour();
tmpMin = SU_Buergerlich.Minute();
tmpSek = SU_Buergerlich.Second();
tmpTwilight = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
logtext = logtext # "Sonnenuntergang Bürgerlich: " # tmpTwilight # " - " # SU_Buergerlich.Format("%H:%M:%S") # " _nl_ ";
}
! #############################################################
! Vormittagsaktionen
! Zufallszahl ermitteln
dom.GetObject("CUxD.CUX2801001:1.CMD_SETS").State("hexdump -d -n 2 /dev/urandom");
dom.GetObject("CUxD.CUX2801001:1.CMD_QUERY_RET").State(1);
string tmpRand = dom.GetObject("CUxD.CUX2801001:1.CMD_RETS").State();
RAND = (tmpRand.Substr(10)).ToInteger();
if (LogLevel >= 2)
{
logtext = logtext # "Zufallszahl für den Vormittag: " # RAND # " _nl_ ";
}
! *************************************************************
! Aktion 0 - DG Flurlicht hinten und Treppe an
! WT: um 06:15 + Zufall(30min)
! WE: um 07:00 + Zufall(40min)
! *************************************************************
actAktion = "0";
if (freierTag == false)
{
tmpStd = 6;
tmpMin = 15;
tmpSek = 0;
}
else
{
tmpStd = 7;
tmpMin = 0;
tmpSek = 0;
}
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (freierTag == false)
{
tmpStartTime = tmpStartTime + ((RAND % 31)*60);
}
else
{
tmpStartTime = tmpStartTime + ((RAND % 41)*60);
}
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 1 - Licht Diele, Küche, Wohnzimmer an
! WT: um 07:00, wenn früher als SA_Indoor - 10min, + Zufall(20min)
! WE: um 07:15, wenn früher als SA_Indoor - 10min, + Zufall(40min)
! *************************************************************
actAktion = "1";
if (freierTag == false)
{
tmpStd = 7;
tmpMin = 0;
tmpSek = 0;
}
else
{
tmpStd = 7;
tmpMin = 15;
tmpSek = 0;
}
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = SA_Indoor.Hour();
tmpMin = SA_Indoor.Minute();
tmpSek = SA_Indoor.Second();
tmpIfBefore = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpIfBefore = tmpIfBefore - (10 * 60);
if (tmpStartTime < tmpIfBefore)
{
if (freierTag == false)
{
tmpStartTime = tmpStartTime + ((RAND % 21)*60);
}
else
{
tmpStartTime = tmpStartTime + ((RAND % 41)*60);
}
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
}
! *************************************************************
! Aktion 2 - EG Rollläden hoch
! WT: um SA_Buergerlich - 10min, frühestens um 07:00, spätestens um 07:45, + Zufall(20min)
! WE: um SA_Buergerlich - 20min, frühestens um 07:15, spätestens um 08:30, + Zufall(40min)
! *************************************************************
actAktion = "2";
tmpStd = SA_Buergerlich.Hour();
tmpMin = SA_Buergerlich.Minute();
tmpSek = SA_Buergerlich.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (freierTag == false)
{
tmpStartTime = tmpStartTime - (10 * 60);
tmpStd = 7;
tmpMin = 0;
tmpSek = 0;
tmpLowerLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = 7;
tmpMin = 45;
tmpSek = 0;
tmpUpperLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
}
else
{
tmpStartTime = tmpStartTime - (20 * 60);
tmpStd = 7;
tmpMin = 15;
tmpSek = 0;
tmpLowerLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = 8;
tmpMin = 30;
tmpSek = 0;
tmpUpperLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
}
if (tmpStartTime < tmpLowerLimit)
{
tmpStartTime = tmpLowerLimit;
}
else
{
if (tmpStartTime > tmpUpperLimit)
{
tmpStartTime = tmpUpperLimit;
}
}
if (freierTag == false)
{
tmpStartTime = tmpStartTime + ((RAND % 21)*60);
}
else
{
tmpStartTime = tmpStartTime + ((RAND % 41)*60);
}
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 3 - DG Rollläden hoch, DG Flurlicht hinten und Treppe aus
! WT: um 07:15 + Zufall(40min)
! WE: um 08:00 + Zufall(60min)
! *************************************************************
actAktion = "3";
if (freierTag == false)
{
tmpStd = 7;
tmpMin = 15;
tmpSek = 0;
}
else
{
tmpStd = 8;
tmpMin = 0;
tmpSek = 0;
}
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (freierTag == false)
{
tmpStartTime = tmpStartTime + ((RAND % 41)*60);
}
else
{
tmpStartTime = tmpStartTime + ((RAND % 61)*60);
}
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 4 - Licht Diele, Küche, Wohnzimmer aus
! WT: um SA_Indoor - 10min, wenn später als 07:00, + Zufall(20min)
! WE: um SA_Indoor - 20min, wenn später als 07:15, + Zufall(40min)
! *************************************************************
actAktion = "4";
tmpStd = SA_Indoor.Hour();
tmpMin = SA_Indoor.Minute();
tmpSek = SA_Indoor.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (freierTag == false)
{
tmpStd = 7;
tmpMin = 0;
tmpSek = 0;
}
else
{
tmpStd = 7;
tmpMin = 15;
tmpSek = 0;
}
tmpIfAfter = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime > tmpIfAfter)
{
if (freierTag == false)
{
tmpStartTime = tmpStartTime - (10 * 60) + ((RAND % 21)*60);
}
else
{
tmpStartTime = tmpStartTime - (20 * 60) + ((RAND % 41)*60);
}
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
}
! #############################################################
! Nachmittagsaktionen
! Zufallszahl neu ermitteln
dom.GetObject("CUxD.CUX2801001:1.CMD_SETS").State("hexdump -d -n 2 /dev/urandom");
dom.GetObject("CUxD.CUX2801001:1.CMD_QUERY_RET").State(1);
string tmpRand = dom.GetObject("CUxD.CUX2801001:1.CMD_RETS").State();
RAND = (tmpRand.Substr(10)).ToInteger();
if (LogLevel >= 2)
{
logtext = logtext # "Zufallszahl für den Nachmittag: " # RAND # " _nl_ ";
}
! *************************************************************
! Aktion 5 - Licht Diele und DG Flurlicht hinten und Treppe an
! WT & WE: um SU_Indoor - 10min, wenn früher als 19:30, + Zufall(20min)
! *************************************************************
actAktion = "5";
tmpStd = SU_Indoor.Hour();
tmpMin = SU_Indoor.Minute();
tmpSek = SU_Indoor.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime - (10 * 60);
tmpStd = 19;
tmpMin = 30;
tmpSek = 0;
tmpIfBefore = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime < tmpIfBefore)
{
tmpStartTime = tmpStartTime + ((RAND % 21)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
}
! *************************************************************
! Aktion 6 - Licht Wohnzimmer an
! WT & WE: um SU_Indoor - 10min, spätestens um 21:40, + Zufall(20min)
! *************************************************************
actAktion = "6";
tmpStd = SU_Indoor.Hour();
tmpMin = SU_Indoor.Minute();
tmpSek = SU_Indoor.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime - (10 * 60);
tmpStd = 21;
tmpMin = 40;
tmpSek = 0;
tmpUpperLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime > tmpUpperLimit)
{
tmpStartTime = tmpUpperLimit;
}
tmpStartTime = tmpStartTime + ((RAND % 21)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 7 - Licht Küche an
! WT & WE: um SU_Indoor - 10min, wenn früher als 20:30, + Zufall(20min)
! *************************************************************
actAktion = "7";
tmpStd = SU_Indoor.Hour();
tmpMin = SU_Indoor.Minute();
tmpSek = SU_Indoor.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime - (10 * 60);
tmpStd = 20;
tmpMin = 30;
tmpSek = 0;
tmpIfBefore = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime < tmpIfBefore)
{
tmpStartTime = tmpStartTime + ((RAND % 21)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
}
! *************************************************************
! Aktion 8 - Rollläden Kinder runter
! WT & WE: um SU_Buergerlich - 15min, frühestens um 17:30, spätestens um 19:30, + Zufall(30min)
! *************************************************************
actAktion = "8";
tmpStd = SU_Buergerlich.Hour();
tmpMin = SU_Buergerlich.Minute();
tmpSek = SU_Buergerlich.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime - (15 * 60);
tmpStd = 17;
tmpMin = 30;
tmpSek = 0;
tmpLowerLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = 19;
tmpMin = 30;
tmpSek = 0;
tmpUpperLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime < tmpLowerLimit)
{
tmpStartTime = tmpLowerLimit;
}
else
{
if (tmpStartTime > tmpUpperLimit)
{
tmpStartTime = tmpUpperLimit;
}
}
tmpStartTime = tmpStartTime + ((RAND % 31)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 9 - Rollläden Schlafzimmer und Bad runter
! WT & WE: um SU_Buergerlich - 15min, frühestens um 17:30, spätestens um 21:30, + Zufall(30min)
! *************************************************************
actAktion = "9";
tmpStd = SU_Buergerlich.Hour();
tmpMin = SU_Buergerlich.Minute();
tmpSek = SU_Buergerlich.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime - (15 * 60);
tmpStd = 17;
tmpMin = 30;
tmpSek = 0;
tmpLowerLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = 21;
tmpMin = 30;
tmpSek = 0;
tmpUpperLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime < tmpLowerLimit)
{
tmpStartTime = tmpLowerLimit;
}
else
{
if (tmpStartTime > tmpUpperLimit)
{
tmpStartTime = tmpUpperLimit;
}
}
tmpStartTime = tmpStartTime + ((RAND % 31)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 10 - EG Rollläden runter
! WT & WE: um SU_Buergerlich - 10min, frühestens um 17:30, spätestens um 21:45, + Zufall(30min)
! *************************************************************
actAktion = "10";
tmpStd = SU_Buergerlich.Hour();
tmpMin = SU_Buergerlich.Minute();
tmpSek = SU_Buergerlich.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime - (10 * 60);
tmpStd = 17;
tmpMin = 30;
tmpSek = 0;
tmpLowerLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = 21;
tmpMin = 45;
tmpSek = 0;
tmpUpperLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime < tmpLowerLimit)
{
tmpStartTime = tmpLowerLimit;
}
else
{
if (tmpStartTime > tmpUpperLimit)
{
tmpStartTime = tmpUpperLimit;
}
}
tmpStartTime = tmpStartTime + ((RAND % 31)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 11 - Licht Diele, DG Flurlicht hinten und Treppe aus
! WT & WE: um 19:30, wenn später als SU_Indoor - 10min, + Zufall(20min)
! *************************************************************
actAktion = "11";
tmpStd = 19;
tmpMin = 30;
tmpSek = 0;
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = SU_Indoor.Hour();
tmpMin = SU_Indoor.Minute();
tmpSek = SU_Indoor.Second();
tmpIfAfter = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpIfAfter = tmpIfAfter - (10 * 60);
if (tmpStartTime > tmpIfAfter)
{
tmpStartTime = tmpStartTime + ((RAND % 21)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
}
! *************************************************************
! Aktion 12 - DG Flurlicht vorne an
! WT & WE: um SU_Indoor - 10min, frühestens um 19:30, spätestens um 20:30, + Zufall(30min)
! *************************************************************
actAktion = "12";
tmpStd = SU_Indoor.Hour();
tmpMin = SU_Indoor.Minute();
tmpSek = SU_Indoor.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime - (10 * 60);
tmpStd = 19;
tmpMin = 30;
tmpSek = 0;
tmpLowerLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = 20;
tmpMin = 30;
tmpSek = 0;
tmpUpperLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime < tmpLowerLimit)
{
tmpStartTime = tmpLowerLimit;
}
else
{
if (tmpStartTime > tmpUpperLimit)
{
tmpStartTime = tmpUpperLimit;
}
}
tmpStartTime = tmpStartTime + ((RAND % 31)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 13 - Licht Küche aus
! WT & WE: um 20:30, wenn später als SU_Indoor - 10min, + Zufall(20min)
! *************************************************************
actAktion = "13";
tmpStd = 20;
tmpMin = 30;
tmpSek = 0;
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = SU_Indoor.Hour();
tmpMin = SU_Indoor.Minute();
tmpSek = SU_Indoor.Second();
tmpIfAfter = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpIfAfter = tmpIfAfter - (10 * 60);
if (tmpStartTime > tmpIfAfter)
{
tmpStartTime = tmpStartTime + ((RAND % 21)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
}
! *************************************************************
! Aktion 14 - Licht Arbeitszimmer an
! WT & WE: um SU_Indoor - 10min, frühestens um 18:00, spätestens um 21:10, + Zufall(30min)
! *************************************************************
actAktion = "14";
tmpStd = SU_Indoor.Hour();
tmpMin = SU_Indoor.Minute();
tmpSek = SU_Indoor.Second();
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime - (10 * 60);
tmpStd = 18;
tmpMin = 0;
tmpSek = 0;
tmpLowerLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStd = 21;
tmpMin = 10;
tmpSek = 0;
tmpUpperLimit = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
if (tmpStartTime < tmpLowerLimit)
{
tmpStartTime = tmpLowerLimit;
}
else
{
if (tmpStartTime > tmpUpperLimit)
{
tmpStartTime = tmpUpperLimit;
}
}
tmpStartTime = tmpStartTime + ((RAND % 31)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 15 - DG FLurlicht vorne aus
! WT & WE: um 21:30 + Zufall(30min)
! *************************************************************
actAktion = "15";
tmpStd = 21;
tmpMin = 30;
tmpSek = 0;
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime + ((RAND % 31)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 16 - Licht Wohnzimmer aus
! WT & WE: um 22:00 + Zufall(90min)
! *************************************************************
actAktion = "16";
tmpStd = 22;
tmpMin = 0;
tmpSek = 0;
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime + ((RAND % 91)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! *************************************************************
! Aktion 17 - Licht Arbeitszimmer aus
! WT & WE: um 22:00 + Zufall(100min)
! *************************************************************
actAktion = "17";
tmpStd = 22;
tmpMin = 0;
tmpSek = 0;
tmpStartTime = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
tmpStartTime = tmpStartTime + ((RAND % 101)*60);
! Aktion nur hinzufügen, wenn sie frühestens in 10 Sekunden stattfindet
if (tmpStartTime >= tmpJetzt + 10)
{
if (unsortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
unsortedTimes = tmpStartTime.ToString();
unsortedActions = actAktion;
}
else
{
unsortedTimes = unsortedTimes # sepr # tmpStartTime.ToString();
unsortedActions = unsortedActions # sepr # actAktion;
}
}
! ##############################################################
! Zeiten und Aktionen chronologisch (aufsteigend) sortieren
while (unsortedTimes.Length() > 0)
{
! Suchhilfen initialisieren
minValue = 2147483647; ! Integer-Maximalwert
minIndex = 0;
i = 0;
! Werteliste durchlaufen und aktuellen Listenwert ermitteln
foreach(actValue,unsortedTimes.Split(sepr))
{
! Ist der aktuelle Listenwert kleiner als der aktuelle Minimalwert?
if (actValue.ToInteger() < minValue)
{
! dann Wert und Index zwischenspeichern
minValue = actValue.ToInteger();
minIndex = i;
}
i = i + 1;
}
! Nach Durchlaufen der Werteliste, das Ergebnis wegschreiben und die zu sortierende Werteliste aktualisieren
! Ergebnis wegschreiben
if (sortedTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
sortedTimes = minValue.ToString();
sortedActions = unsortedActions.StrValueByIndex(sepr,minIndex);
}
else
{
sortedTimes = sortedTimes # sepr # minValue.ToString();
sortedActions = sortedActions # sepr # unsortedActions.StrValueByIndex(sepr,minIndex);
}
! unsortierte Werteliste um gefundenen Minimalwert reduzieren, indem zunächst alle Elemente ausser dem Minimalelement in eine Hilfsliste kopiert werden.
! Anschließend wird die Hilfsliste in die zu sortierende Liste umkopiert.
! Hilfsvariablen initialisieren
tmpTimes = "";
tmpActions = "";
i = 0;
foreach(actValue,unsortedTimes.Split(sepr))
{
! Wenn der aktuelle Index nicht dem des Minimalwertes entspricht, dann übernehmen
if (i != minIndex)
{
if (tmpTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
tmpTimes = actValue;
tmpActions = unsortedActions.StrValueByIndex(sepr,i);
}
else
{
tmpTimes = tmpTimes # sepr # actValue;
tmpActions = tmpActions # sepr # unsortedActions.StrValueByIndex(sepr,i);
}
}
i = i + 1;
}
! neue zu sortierende Liste aus Hilfsliste übernehmen
unsortedTimes = tmpTimes;
unsortedActions = tmpActions;
}
dom.GetObject('YAWN_ZeitenListe').State(sortedTimes);
dom.GetObject('YAWN_AktionsListe').State(sortedActions);
if (LogLevel >= 2)
{
logtext = logtext # "Aktionszeiten: " # sortedTimes # " _nl_ ";
logtext = logtext # "Aktionenliste: " # sortedActions # " _nl_ ";
}
! ##############################################################
! Timer für erste Aktion aufziehen
tmpStartTime = (sortedTimes.StrValueByIndex(sepr,0)).ToInteger();
tmpStd = tmpStartTime/3600;
tmpMin = (tmpStartTime%3600)/60;
tmpSek = tmpStartTime%60;
timerWert = tmpStd.ToString() # ":" # tmpMin.ToString() # ":" # tmpSek.ToString();
dom.GetObject("CUxD.CUX2800002:1.TIMER_SET").State(timerWert);
if (LogLevel >= 3)
{
logtext = logtext # "Timer aufgezogen auf: " # timerWert # " _nl_ ";
}
! ##############################################################
! Logfile wegschreiben
if (LogLevel >= 2)
{
dom.GetObject("CUxD.CUX2801001:1.CMD_EXEC").State("/bin/tclsh /usr/local/addons/TCL/LogMe.tcl YAWN 1 "# logtext);
}
! ##############################################################
! Fertig! Ab jetzt wird ein separates Programm zum Schalten
! der Aktoren per CUxD-Timer getriggert
! ##############################################################
Das HomeMatic-Skript TriggerActions wird durch den CUxD-Timer aufgerufen.
In ihm wird die aktuell auszuführende Aktion in eine Systemvariable geschrieben. Anschließend wird die aktuelle Aktion und Schaltzeit aus der jeweiligen Liste entfernt und die CUxD-Timer auf die nächste Schaltzeit aufgezogen. Ist die nächste Schaltzeit identisch mit der aktuellen Schaltzeit, wird der Timer auf 5s aufgezogen.
Umgebungsvariablen, die benötigt werden:
lesend:
- "YAWN_ZeitenListe" (Typ Zeichenkette)
- "YAWN_AktionsListe" (Typ Zeichenkette)
- "YAWN_LogLevel" (Typ Integer - 0 bis 3) s.u.
- "YAWN_LetzteAktion" (Typ Zahl)
- "YAWN_ZeitenListe" (Typ Zeichenkette)
- "YAWN_AktionsListe" (Typ Zeichenkette)
Code: Alles auswählen
! #############################################################
! Yet Another anWesenheitssimulatioN (YAWN)
! TriggerActions
! V1.1
! by Splunge
! #############################################################
! Beschreibung:
! Durchläuft die aktuelle Zeiten- und Aktionsliste
! Das erste Element ist das aktuelle Element. Dessen Aktion soll
! als nächstes ausgeführt werden. Dazu wird eine Systemvariable auf
! den entsprechenden Wert gesetzt. Dies triggert das entsprechende
! Programm
! Alle anderen Elemente werden in temporäre Listen geschrieben,
! die am Ende wieder in die Umgebungsvariablen für die Zeiten- und
! Aktionsliste geschrieben werden.
! Die Zeit der nächsten Aktion wird vorab bestimmt. Ist die gleich der
! Zeit der aktuellen Aktion, wird der Timer nicht auf diese Uhrzeit
! eingestellt, sondern auf 5 Sekunden.
! #############################################################
! Änderungen:
! V1.1: Fehlerkorrektur Kommentar in Zeile 104
! #############################################################
! Systemvoraussetzungen:
! - CUxD muss installiert sein (als Gerät muss der Timer eingerichtet sein)
! - LogMe.tcl muss vorhanden sein, wenn Logging verwendet werden soll
! #############################################################
! Ausführungszeitpunkt des Skriptes:
! wird durch den CUxD-Timer getriggert, wenn eine Aktion ansteht.
! #############################################################
! Umgebungsvariablen, die benötigt werden
!
! lesend:
! "YAWN_ZeitenListe" (Typ Zeichenkette)
! "YAWN_AktionsListe" (Typ Zeichenkette)
! "YAWN_LogLevel" (Typ Integer - 0 bis 3)
! schreibend:
! "YAWN_LetzteAktion" (Typ Zahl)
! "YAWN_ZeitenListe" (Typ Zeichenkette)
! "YAWN_AktionsListe" (Typ Zeichenkette)
! #############################################################
! Variablendefinition
string sortedTimes; ! sortierte Werteliste
string sortedActions; ! sortierte Aktionsliste
string currentAction; ! aktuelle Aktion
integer i; ! Schleifenindex
string sepr = ","; ! Listentrennzeichen
string actValue; ! aktueller Wert der unsortierten Liste
integer currentTime; ! aktuelle Aktionszeit
integer nextTime; ! nächste Aktionszeit
string tmpTimes = ""; ! Hilfsliste für die Werte
string tmpActions = ""; ! Hilfsliste für die Aktionen
integer tmpStd; ! Hilsvariable zur Berechnung der Tagessekunden
integer tmpMin; ! Hilsvariable zur Berechnung der Tagessekunden
integer tmpSek; ! Hilsvariable zur Berechnung der Tagessekunden
integer tmpJetzt; ! Enthält den aktuellen Zeitpunkt in Tagessekunden
string timerWert; ! Weckzeit für das nächste Ereignis
integer LogLevel; ! Level des Loggings (0 = Nichts, 1 = Aktionen, 2 = Berechnung und Aktionen, 3 = Alles)
string logtext = "Aktion getriggert... _nl_ "; ! Logtext
boolean bRetrigger; ! Merker, ob die aktuelle Aktion erneut getriggert werden muss, z.B. auf Grund einer Fehltriggerung dieses Skriptes
sortedTimes = dom.GetObject('YAWN_ZeitenListe').State();
sortedActions = dom.GetObject('YAWN_AktionsListe').State();
LogLevel = dom.GetObject('YAWN_LogLevel').State();
! jetzigen Uhrzeit ermitteln
tmpStd = system.Date("%H").ToInteger();
tmpMin = system.Date("%M").ToInteger();
tmpSek = system.Date("%S").ToInteger();
tmpJetzt = (tmpStd * 3600) + (tmpMin * 60) + tmpSek;
! alle Werte der aktuellen Liste durchlaufen
if (sortedTimes.Length() > 0)
{
i = 0;
bRetrigger = false;
foreach(actValue,sortedTimes.Split(sepr))
{
! Beim ersten Element die Zeit und Aktion als aktuelle Werte speichern
if (i == 0)
{
currentTime = actValue.ToInteger();
currentAction = sortedActions.StrValueByIndex(sepr,i);
! Liegt die aktuelle Aktion im richtigen Zeitfenster (früher als jetzt plus max. 10 Sekunden)?
if (currentTime <= tmpJetzt + 10)
{
! Aktuelle Aktion triggern, indem die Systemvariable "YAWN_LetzteAktion" gesetzt wird.
dom.GetObject('YAWN_LetzteAktion').State(currentAction.ToInteger());
if (LogLevel >= 3)
{
logtext = logtext # "Aktuelle Aktion: " # currentAction # " _nl_ ";
}
}
else
{
! Ansonsten wahrscheinlich Fehltriggerung (zu früh). Aktion wieder in Liste aufnehmen und als nächste Aktion setzen
nextTime = actValue.ToInteger();
bRetrigger = true;
if (tmpTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
tmpTimes = actValue;
tmpActions = sortedActions.StrValueByIndex(sepr,i);
}
else
{
tmpTimes = tmpTimes # sepr # actValue;
tmpActions = tmpActions # sepr # sortedActions.StrValueByIndex(sepr,i);
}
}
}
else
{
! Beim zweiten Element die Zeit für die nächste Aktion speichern, wenn es keine Retriggerung gibt.
if ((i == 1) && (bRetrigger == false))
{
nextTime = actValue.ToInteger();
}
! ab dem zweiten Element die Zeiten und Aktionen in eine temporäre Liste speichern. Dies wird die neue Aktionsliste
if (tmpTimes.Length() == 0)
{
! beim ersten Element noch kein Listentrennzeichen hinzufügen
tmpTimes = actValue;
tmpActions = sortedActions.StrValueByIndex(sepr,i);
}
else
{
tmpTimes = tmpTimes # sepr # actValue;
tmpActions = tmpActions # sepr # sortedActions.StrValueByIndex(sepr,i);
}
}
i = i + 1;
}
! Um aktuelles Element reduzierte Listen wieder in Systemvariablen zurückschreiben
dom.GetObject('YAWN_ZeitenListe').State(tmpTimes);
dom.GetObject('YAWN_AktionsListe').State(tmpActions);
if (LogLevel >= 3)
{
logtext = logtext # "Neue Aktionszeiten: " # tmpTimes # " _nl_ ";
logtext = logtext # "Neue Aktionenliste: " # tmpActions # " _nl_ ";
}
! Timer für die nächste Aktion aufziehen
if (nextTime == currentTime)
{
! Wenn die nächste Aktion zum gleichen Zeitpunkt wie die aktuelle stattfinden soll,
! dann nicht den Timer auf die Uhrzeit einstellen, sondern auf 5 Sekunden aufziehen.
! So wird sichergestellt, dass die Systemvariable für die aktuelle Aktion verarbeitet
! werden kann
dom.GetObject("CUxD.CUX2800002:1.TIMER_SET").State(5);
if (LogLevel >= 3)
{
logtext = logtext # "Timer aufgezogen auf 5 Sekunden" # " _nl_ ";
}
}
else
{
if (nextTime > currentTime)
{
! Timer auf nächste Uhrzeit einstellen
tmpStd = nextTime/3600;
tmpMin = (nextTime%3600)/60;
tmpSek = nextTime%60;
timerWert = tmpStd.ToString() # ":" # tmpMin.ToString() # ":" # tmpSek.ToString();
dom.GetObject("CUxD.CUX2800002:1.TIMER_SET").State(timerWert);
if (LogLevel >= 3)
{
logtext = logtext # "Timer aufgezogen auf: " # timerWert # " _nl_ ";
}
}
}
}
! ##############################################################
! Logfile wegschreiben
if (LogLevel >= 3)
{
dom.GetObject("CUxD.CUX2801001:1.CMD_EXEC").State("/bin/tclsh /usr/local/addons/TCL/LogMe.tcl YAWN 1 "# logtext);
}
Für jede Aktion muss ein WebUI-Programm angelegt werden, welches getriggert wird, wenn die Aktions-Systemvariable den Wert der entsprechenden Aktionsnummer annimmt. Hier ein Bespiel:
An dieser Stelle sollte auch abgefragt werden, ob momentan jemand zu Hause ist, oder ob alle abwesend sind. Dies wird aus einer entsprechenden Umgebungsvariable ausgelesen, die manuell oder einen Automatismus (z.B. Haustürverriegelung etc.) gesetzt wird.
Das Skript dient zum Loggen und hat den folgenden Inhalt:
Code: Alles auswählen
integer LogLevel = dom.GetObject('YAWN_LogLevel').State();
if (LogLevel >= 1)
{
logtext = "Ausgeführt wurde Aktion 2: EG Rollläden hoch";
dom.GetObject("CUxD.CUX2801001:1.CMD_EXEC").State("/bin/tclsh /usr/local/addons/TCL/LogMe.tcl YAWN 1 " # logtext);
}
Um die Vorgänge bei der Zeitenberechnung und Aktionsausführung nachvollziehen zu können, sind die einzelnen HomeMatic-Skripte und WebUI-Programme mit Logging-Funktionen ausgestattet. Es basiert auf der Logging-Funktion des Twilight-Skripts von DocZoid.
Bei meiner Anwesenheitssimulation gehe ich momentan von vier Logstufen aus, die über die Systemvariable „YAWN_LogLevel“ festgelegt wird:
- YAWN_LogLevel = 0: kein Logging
- YAWN_LogLevel = 1: Nur die Ausführung von Aktionen wird geloggt
- YAWN_LogLevel = 2: Die Berechnung der Zeiten und die Ausführung der Aktionen wird geloggt
- YAWN_LogLevel = 3: Alles wird geloggt (inkl. Aufbereitung der Aktionsliste etc.)
- param 0: Dateiname
- param 1: Option: 1 = aktueller Tag wird dem Dateinamen hinzugefügt
- param 2..n: Logtext
Mit dem zweiten Parameter wird definiert, ob die aktuelle Tagesnummer in den Dateinamen aufgenommen werden soll (1). Ist dies nicht der Fall, wird die Logdatei für jeden Tag beibehalten und füllt sich immer mehr. Eine Größenbeschränkung gibt es derzeit nicht! Die Verwendugn der Tages-Option ist daher sehr zu empfehlen, um einen Überlauf zu vermeiden.
Dateien älter als 14 Tage werden automatisch gelöscht.
Im dritten und den folgenden Parametern steht dann der zu schreibenden Text. Die einzelnen Parameter werden dann Wort für Wort zu einem Satz zusammengesetzt. Mit Hilfe des Schlüsselwortes „ _nl_ „ kann ein Zeilenumbruch erzwungen werden.
Beim Logging-Text müssen ein paar Einschränkungen in Kauf genommen werden. Da der Text als Parameter übergeben wird, sind gewissen Zeichen nicht erlaubt, da sie sonst zu einem Programmabbruch im LogMe-Skript führen. So sind z.B. Klammern oder Semikolon nicht zulässig. Wenn jemand eine Lösung zu diesem Problem kennt, bin ich sehr interessiert.
Der Loggingtext wird dann inkl. Zeitstempel in das aktuelle Logfile geschrieben.
Beispiel1:
Aufruf um 12:13:47:
"LogMe.tcl test 0 Dies ist nur ein Test!"
Inhalt der Datei "test.log":
12:13:47: Dies ist nur ein Test!
Beispiel2:
Aufruf am 123. Tag um 08:43:39:
"LogMe.tcl test2 1 Dies ist noch ein Test, _nl_ diesmal mit Leerzeile"
Inhalt der Datei "test2.123.log":
08:43:39: Dies ist noch ein Test,
08:43:39: diesmal mit Leerzeile
Code: Alles auswählen
###########################################################
# LogMe.tcl
# V1.0
#
# Autor: Splunge
# Basierend auf Logging-Funktion des Skripts Twilight.tcl
# von DocZoid
#
# Parameter:
# arg0: Dateiname
# arg1: Option: 1 = aktueller Tag wird dem Dateinamen hinzugefügt
# arg2..n: Logtext
#
# Funktion:
# Schreibt den als Parameter übergebenen Logtext inkl.
# Zeitstempel in eine Datei im /tmp-Verzeichnis.
# Der Dateiname bildet sich aus dem übergebenen Dateinamen
# und gegebenenfalls (arg1 = 1) aus dem Tag des Jahres.
# Die Paramter arg2 bis argn werden zusammengesetzt und
# bilden den zu schreibenden Logtext
# Neue Zeilen können mit dem Schlüsselword " _nl_ " erzwungen
# werden.
# Während des Schreibvorganges wird ein Lockfile angelegt.
# Ist bereits ein Lockfile vorhanden, wird gewartet, bis es
# gelöscht wurde. Wird ein Timeout erreicht, wird das
# Schreiben abgebrochen.
# Logfile älter als zwei Wochen werden automatisch gelöscht.
#
# Konfiguration:
# Ist ein früheres Löschen alter Logfile gewünscht, muss der
# Wert "14" im Ausdruck "set deleteday [expr $writeday-14]"
# angepasst werden. Dies ist die Anzahl der Tage, für die ein
# Logfile aufbewahrt wird.
#
# Einschränkungen:
# Bestimmte Sonderzeichen wie Klammern oder bestimtme Satzzeichen
# in den Parametern können dazu führen, dass das Skript nicht
# korrekt durchlaufen wird. Der Logtext sollte daher keine solche
# Zeichen enthalten.
#
# Beispiel1:
# Aufruf um 12:13:47: "LogMe.tcl test 0 Dies ist nur ein Test!"
# Inhalt der Datei "test.log":
# 12:13:47: Dies ist nur ein Test!
#
# Beispiel2:
# Aufruf am 123. Tag um 08:43:39: "LogMe.tcl test2 1 Dies ist noch ein Test, _nl_ diesmal mit Leerzeile"
# Inhalt der Datei "test2.123.log":
# 08:43:39: Dies ist noch ein Test,
# 08:43:39: diesmal mit Leerzeile
###########################################################
# gibt den Zeitstempel für die zu schreibenden Zeile im Logfile zurück
proc showTime x {
if {$x && $x != 2000000000} then {
return [clock format $x -format "%H:%M:%S"]
} else {
return "--:--:--"
}
}
# schreibt den Logtext samt Zeitstempel in die Logdatei
proc log x {
puts $::logfile "[showTime [clock seconds]]: $x"
flush $::logfile
}
# ermittelt den aktuellen Tag des Jahres
proc getDayOfYear {timestamp} {
set dayofyear [string trimleft [clock format $timestamp -format "%j"] "0"]
return [expr int($dayofyear)]
}
# Dateinamen aus erstem Parameter holen
set filename [lindex $argv 0]
# Tag-Option aus zweitem Parameter holen
set bDayOfYear [lindex $argv 1]
# Wenn die Tag-Option eingeschaltet ist
if {$bDayOfYear == 1} {
# Tagesnummer für aktuelles Logfile setzen
set writeday [getDayOfYear [clock seconds]]
# Tagesnummer für zu löschendes Logfile setzen
set deleteday [expr $writeday-14]
# Bei Unterlauf, auf vorheriges Jahres zurückrechnen
if {$deleteday <= 0} {
# Auf Schaltjahr im vorherigen Jahr prüfen
set jahr [clock format [clock seconds] -format "%Y"]
if {(int($jahr)-1) % 4 == 0 && ((int($jahr)-1) % 100 != 0 || (int($jahr)-1) % 400 == 0)} {
set deleteday [expr $deleteday + 366]
} else {
set deleteday [expr $deleteday + 365]
}
}
# ältestes Logfile löschen
catch {file delete "/tmp/$filename.$deleteday.log"}
# Dateinamen für Logfile mit Tagesnummer festlegen
set writefilename "/tmp/$filename.$writeday"
} else {
# Dateinamen für Logfile ohne Tagesnummer festlegen
set writefilename "/tmp/$filename"
}
# Lockfile erzeugen, falls nicht schon vorhanden, sonst warten bis Timeout (5s)
set lockAttempts 0
while {([file exists "$writefilename.lock"] == 1) && ($lockAttempts < 50)} {
set lockAttempts [expr $lockAttempts + 1]
after 100
}
# Wenn es keinen Timeout gab...
if {$lockAttempts < 50} {
# Lockfile erzeugen
set lockfile [open "$writefilename.lock" w]
puts $lockfile "Locked on [clock format [clock seconds] -format "%d.%m.%Y"] at [clock format [clock seconds] -format "%H:%M:%S"]"
close $lockfile
# Aktuelles Logfile öffnen
set logfile [open "$writefilename.log" a+]
# Logtext initialisieren
set logtext ""
# Alle Parameter nach dem zweiten Parameter durchlaufen, um den Logtext zusammenzusetzen
for {set i 2} {$i < $argc} {incr i} {
# wenn es sich um das Schlüsselwort für einen Zeilenumbruch handelt
if {[lindex $argv $i] == "_nl_"} {
# bisherigen Text wegschreiben und damit eine neue Zeile beginnen
log $logtext
# Logtext für nächste Zeile neu initialisieren
set logtext ""
} else {
# wenn es der erste Eintrag der Zeile ist, dann das Wort direkt übernehmen
if {$logtext == ""} {
set logtext [lindex $argv $i]
} else {
# sonst neues Wort anhängen
set logtext "$logtext [lindex $argv $i]"
}
}
}
# letzte Zeile wegschreiben (falls nicht leer)
if {$logtext != ""} {
log $logtext
}
# Logfile schließen
close $logfile
# Lockfile löschen
catch {file delete "$writefilename.lock"}
}
Abschließend noch ein Skript, mit dem das aktuelle Logfile per Mail als Attachment versendet werden kann. Das ist z.B. hilfreich, um bei längerer Abwesenheit die Korrektheit des Programmablaufs überprüfen zu können, ohne sich irgendwo einloggen zu müssen.
Das TCL-Skript basiert auf dem Programm „Firemail“ von hier: http://siliconbiscuits.blogspot.de/2012 ... p-and.html
Es setzt das TCL-Package „smtp“ voraus. Bei meiner CCU1 war keine Nachinstallation notwendig, allerdings hatte ich bereits das Email-Integration-Addon von Homematic-inside.de (Autor: Harima-kun, Mathias) installiert. Ob das SMTP-Paket bereits auf der CCU1 vorinstalliert ist oder durch das Email-Addon nachinstalliert wurde, kann ich im Nachhinein nicht mehr sagen, ich vermute aber, dass es bereits von Werk aus vorhanden ist.
Innerhalb des Skriptes müssen die E-Mail-Adresse, Account-Informationen, Email-Texte etc. angepasst werden.
Aufgerufen wird das Skript am Ende des Tages über ein zeitgesteuertes WebUI-Programm. Der Zeitpunkt sollte nach der letzten theoretisch möglichen Aktion erfolgen, aber noch am selben Tag, damit das korrekte Logfile verschickt wird.
Parameter sind, in Anlehnung an das LogMe-Skript:
- param 0: Dateiname
- param 1: Option: 1 = aktueller Tag wird dem Dateinamen hinzugefügt
Code: Alles auswählen
###########################################################
# MailLogfile.tcl
# V1.0
#
# Autor: Splunge
# Basierend auf Firemail von hier:
# http://siliconbiscuits.blogspot.de/2012/01/getting-tcl-to-play-nice-with-smtp-and.html
# Basierend auf Logging-Funktion des Skripts Twilight.tcl
# von DocZoid
#
# Voraussetzung:
# Das TCL-Package "smtp" muss installiert sein.
# Für HomeMatic CCU1: soweit ich es nachvollziehen kann, ist
# dieses Package bereits vorinstalliert. Ansonsten kann es
# notwendig sein das HomeMatic-Addon "EMail-Integration" zu
# installieren.
#
# Parameter:
# arg0: Dateiname
# arg1: Option: 1 = aktueller Tag wird dem Dateinamen hinzugefügt
#
# Funktion:
# Sendet eine Mail mit dem angegebenen Logfile als
# Attachment an eine feste E-Mail Adresse über den definierten
# Mail-Server.
# Der Dateiname des Logfiles bildet sich aus dem übergebenen
# Dateinamen und gegebenenfalls (arg1 = 1) aus dem Tag des Jahres.
# Ist das Logfile über das zugehörige Lockfile (siehe LogMe.tcl)
# noch gesperrt, wird max. 5s gewartet. Ist die Datei dann immmer
# noch gesperrt, wird ein entsprechender Vermerk im Mail-Titel
# eingetragen.
#
# Konfiguration:
# Im Konfigurationsblock (am Ende des Skripts) müssen die
# entsprechenden Angaben zum Mail-Server, User-Account,
# Mailtext etc. angepasst werden.
# Alle Zeilen mit "<-- Anpassen" müssen ausgefüllt werden und der
# "<-- Anpassen"-Eintrag entfernt werden
###########################################################
package require smtp
namespace eval FIREMAIL {
variable mailhost {}
variable mailport {}
variable mailuser {}
variable mailpass {}
variable attcs_options {}
proc mailer {to from subject body {headers ""} {bcc ""} {opt_attcs_type ""} {attcs_array_name ""} } {
if {[string length $opt_attcs_type]} {
if {$opt_attcs_type != "-file" && $opt_attcs_type != "-string"} {
error "FIREMAIL Error: unknown option $opt_attcs_type"
}
if {![string length $attcs_array_name]} {
error "FIREMAIL Error: unknown value for option $opt_attcs_type"
}
set parts [::mime::initialize -canonical text/plain -string $body]
foreach {attc_name attc_options} [array get $attcs_array_name] {
set part [eval ::mime::initialize $attc_options]
lappend parts $part
}
set messageT [::mime::initialize -canonical multipart/mixed -parts $parts]
} else {
set messageT [::mime::initialize -canonical text/plain -string $body]
}
variable mailhost
variable mailport
variable mailuser
variable mailpass
set command [list ::smtp::sendmessage $messageT -servers $mailhost -ports $mailport -username $mailuser -password $mailpass]
lappend command -header [list From $from]
lappend command -header [list To $to]
lappend command -header [list Subject $subject]
if {[string length $bcc]} {
lappend command -header [list Bcc $bcc]
}
if {[string length $headers]} {
foreach {key value} $headers {
lappend command -header [list $key $value]
}
}
set err [catch { eval $command } result]
::mime::finalize $messageT -subordinates all
if {$err} {
error "FIREMAIL Error: $result"
} else {
return
}
}
proc send {to from subject body attcs } {
variable mailhost
variable mailport
variable mailuser
variable mailpass
if { $to == {} || $from == {} } {
puts stderr "FIREMAIL Error: syntax is\nfiremail::mailer \[to list] \[from] \[subject string] \[email body string] \
\[file list] "
error "Syntax Error"
}
if { $mailhost == {} || $mailport == {} } {
puts stderr "FIREMAIL needs you to specify a SMTP server and port."
error "Unknown server or port."
}
foreach attc $attcs {
if { ![file exists $attc]} {
error "FIREMAIL Error: file $attc does not exist."
}
set opts [list -canonical "text/plain; name=\"[file tail $attc]\""]
lappend opts -encoding base64
lappend opts -header {Content-Disposition attachment}
lappend opts -file $attc
set [namespace current]::attachments($attc) $opts
}
return [ FIREMAIL::mailer $to $from $subject $body {} {} -file [namespace current]::attachments ]
}
}
# ermittelt den aktuellen Tag des Jahres
proc getDayOfYear {timestamp} {
set dayofyear [string trimleft [clock format $timestamp -format "%j"] "0"]
return [expr int($dayofyear)]
}
# Dateinamen aus erstem Parameter holen
set filename [lindex $argv 0]
# Tag-Option aus zweitem Parameter holen
set bDayOfYear [lindex $argv 1]
# Wenn ein Dateiname für das Logfile angeben ist
if {$filename != ""} {
# Ist die Tagesnummer im Logfilenamen enthalten?
if {$bDayOfYear == 1} {
set dayofyear [getDayOfYear [clock seconds]]
set sendfilename "/tmp/$filename.$dayofyear"
} else {
set sendfilename "/tmp/$filename"
}
set logfile "$sendfilename.log"
# Lockfile prüfen. Wenn vorhanden, dann warten, bis es gelöscht wurde (max. 5s)
set lockAttempts 0
while {([file exists "$sendfilename.lock"] == 1) && ($lockAttempts < 50)} {
set lockAttempts [expr $lockAttempts + 1]
after 100
}
### Konfigurationsblock Anfang ###
set FIREMAIL::mailhost {!!!SMTP-SERVER!!!} <-- Anpassen (z.B. mail.gmx.net)
set FIREMAIL::mailport {!!!SMTP-PORT!!!} <-- Anpassen (z.B. 25 oder 465, je nach Verschlüsselung)
set FIREMAIL::mailuser {!!!USERNAME!!!} <-- Anpassen (z.B. max.mustermann@gmx.de)
set FIREMAIL::mailpass {!!!PASSWORD!!!} <-- Anpassen (Passwort zum Mail-Account)
set mailsubject "Homematic $filename Protokoll für Tag $dayofyear"
# Prüfen, ob das Logfile existiert
if { [file exists $logfile]} {
if {$lockAttempts < 50} {
set mailtext "Anbei das $filename Protokoll des Tages $dayofyear"
} else {
set mailtext "Anbei das $filename Protokoll des Tages $dayofyear (Achtung: Datei war gelockt!)"
}
FIREMAIL::send {!!!EMAIL_ADRESSE_EMPFAENGER!!!} {!!!EMAIL_ADRESSE_SENDER!!!} $mailsubject $mailtext $logfile <-- Anpassen
} else {
set mailtext "Für den Tag $dayofyear konnte kein Protokoll ($logfile) gefunden werden."
FIREMAIL::send {!!!EMAIL_ADRESSE_EMPFAENGER!!!} {!!!EMAIL_ADRESSE_SENDER!!!} $mailsubject $mailtext {} <-- Anpassen
}
### Konfigurationsblock Ende ###
}
Eine flexiblere Umsetzung des Logging-Skripts könnte auch nicht schaden.
Elegant wäre es sicherlich auch, wenn mann die Zeitplanung aus Excel direkt in eine Skript-lesbare Tabelle kopieren könnte und sich das Skript alle Informationen daraus holt und nicht jede Aktion einzeln programmiert werden muss.
Für den Anfang reicht mir das aber erstmal. Im Testbetrieb sieht alles soweit gut aus.
So, das war's auch schon. Bei Fragen, Anmerkungen oder Verbesserungsvorschlägen: immer her damit. Danke!
Gruß
Splunge