YAWN – Yet Another anWesenheitssimulatioN

User stellen ihre Haussteuerung vor

Moderator: Co-Administratoren

Benutzeravatar
splunge
Beiträge: 65
Registriert: 10.04.2013, 22:43

YAWN – Yet Another anWesenheitssimulatioN

Beitrag von splunge » 25.06.2013, 20:49

Hallo zusammen,

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
Ziel meiner Anwesenheitssimulation ist, wie der Name schon sagt, in unserer Abwesenheit den Anschein zu erwecken, dass jemand zu Hause ist, indem Rollläden und Lampen zu plausiblen Zeitpunkten hoch- und runtergefahren werden, bzw. ein- und ausgeschaltet werden.

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
Notwendige AddOns:
  • CUxD (V0.58) (mit eingerichteten System.Devices „System.Timer“ und „System.Exec“)
Grobkonzept:

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.

Bild

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.

Bild

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);
}
Twilight.tcl:
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
Es werden folgende Systemvariablen benötigt:
  • „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)
Korrektur vom 26.12.2013: In meiner vorherigen Beschreibung hatte ich den Datentyp von "Sekunden_SA_Buergerlich" etc. fälschlicherweise als Zeichenkette angegeben...

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
CalcActionTimes:
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.

Bild

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.
schreibend:
  • "YAWN_ZeitenListe" (Typ Zeichenkette)
  • "YAWN_AktionsListe" (Typ Zeichenkette)
Korrektur vom 26.12.2013: In meiner vorherigen Beschreibung hatte ich den Datentyp von "Sekunden_SA_Buergerlich" etc. fälschlicherweise als Zeichenkette angegeben...

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

! ##############################################################
TriggerActions:
Das HomeMatic-Skript TriggerActions wird durch den CUxD-Timer aufgerufen.

Bild

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.
schreibend:
  • "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);
}
WebUI-Programme für die Aktionen:
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:

Bild

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);
}
LogMe.tcl:
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.)
Aufgerufen wird das TCL-Skript „LogMe.tcl mit folgenden Parametern:
  • param 0: Dateiname
  • param 1: Option: 1 = aktueller Tag wird dem Dateinamen hinzugefügt
  • param 2..n: Logtext
Der erste Parameter gibt den Dateinamen (plus „.log“) an.

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"}
}
MailLogfile.tcl:
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.

Bild

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 ###    
}
Für die Zukunft überlege ich, wie ich mein Konzept überarbeiten könnte, so dass auch die wetterabhängigen Sonnenauf- und Untergänge einbezogen werden können.
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
Zuletzt geändert von splunge am 01.03.2015, 20:20, insgesamt 4-mal geändert.

EnergyStar
Beiträge: 1276
Registriert: 27.07.2010, 11:38
Danksagung erhalten: 1 Mal

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von EnergyStar » 26.06.2013, 10:40

Hallo Splunge,

vielen Dank für die sehr ausführliche Darstellung und Erklärung Deiner Lösung. Das kann man ja quasi 1:1 übernehmen.

Ich muss gestehen, das jetzt alles durchzulesen kann ich zeitlich nicht. Aber eine Anregung habe ich. Eventuell hast Du sie ja auch schon drin:

Bei meiner persöhnlichen Lösung der Rollladenzeiten z.B. verhalten wir uns anders, wenn der nächste Tag eine andere Kategorie hat. Also z.B. heute Normal und morgen Feiertag. Dann bleiben die Rollläden länger auf und die Lichter länger an, weil man ja morgen länger schlafen kann. Und genauso umgekehrt, heute Feiertag und morgen Normal, dann gehen die Lichter früher aus und dir Rollläden eher runter. Deshalb nutze ich auch beim Feiertagsscript die Info, was denn morgen für ein Tag ist.

Gruß
EnergyStar

PS: Wenn das schon berücksichtigt ist, dann hab ich es auf die Schnelle nicht gesehen...
--------------------------------------------
CCU1 mit 1.514, CUxD 0.59b, Historian V0.7.6hf1
161 Kanäle in 35 Geräten
in schrittweiser Migration auf die
CCU2 mit 2.15.5, CUxD 0.68, Historian V0.7.6hf1
254 Kanäle in 88 Geräten
gesamte Funktionalität über die
CL-Box mit homeputer CLX Ver. 4.0 Rel. 150625
Ansichten: 17, Objekte: 882, Zeilen: 19863, Variablen: 1966

Benutzeravatar
splunge
Beiträge: 65
Registriert: 10.04.2013, 22:43

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von splunge » 26.06.2013, 12:55

Hallo EnergyStar,

Danke für das Feedback.

Musste meine Antwort nochmal überarbeiten, da ich dein Posting ursprünglich missverstanden hatte...

Du hast Recht. Bei meinem Konzept wird dieser Fakt nicht berücksichtigt, da wir momentan in der Regel abends unabhängig davon, ob der nächste Tag frei ist oder nicht das Abendprogramm gestaltet. Dies ist vor allem der Tatsache geschuldet, dass wir zwei kleine Kinder haben. Hinzu kommt Gleitzeit auf der Arbeit...

Will man dein "Profil" berücksichtigen, müsste man tatsächlich den nächsten Tag mit in die Planung einbeziehen.
Dazu müsste die Feiertagsberechnung für den nächsten Tag einfach wieder ins Skript eingebaut und die Planung erweitert werden. Dürfte ber ohne großen Aufwand möglich sein. Hier und da ein paar Abfragen und Systemvariablen hinzu, fertig...

Gruß
Splunge

starfish
Beiträge: 154
Registriert: 23.01.2009, 17:05

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von starfish » 23.08.2013, 10:30

lcd_msg und daemonizer.tcl
bin am Zusammensuchen und Installieren der nötigen Addons. Dabei war das addon lcd_msg sehr schwierig aufzufinden. Es gibt zwar den Thread
http://homematic-forum.de/forum/viewtop ... cu+lcd+msg
mit einem Link zu Dirchs server, das addon-archiv selber ist unvollständig, insbesondere fehlt daemonizer.tcl, was in YAWN (bzw. twilight.tcl) gebraucht wird.
Irgendwo stand der Hinweis zu homematic-wiki.info - diese domain ist aber seit Juli bewusst ausgelaufen und nicht mehr erreichbar.
Verdankenswerterweise hat owagner den Inhalt unter http://hmwiki.vapor.com temporär wieder verfügbar gemacht, das vollständige lcd_msg-addon-archive gibts hier

http://hmwiki.vapor.com/mw/index.php/Da ... ccu.tar.gz

wie gesagt: vorübergehend. (hoffentlich noch möglichst lange, es wäre schade, wenn das homematic - wiki verschwinden würde)

Gruss
starfish
Standort Schweden: 1 CCU1 / 18 Aktoren (Licht, Heizung)/ Sensoren: 5 Temp, 1 Bewegung, Helligkeit
Standort Schweiz: 1 CCU1 / 6 Aktoren (Licht) / 7 Einzelraumheizungsregelungen /
Sensoren: 3 Temp. / 2 Bewegung (innen, aussen)
Server: Netbook mit Linux für beide Standorte (Schweden über VPN)
Software: Homecontrol Suite auf Apache, Mysql / Zabbix für Visualisierungen
Bedienung: hauptsächl. über Tablets und Smartphones.

Benutzeravatar
splunge
Beiträge: 65
Registriert: 10.04.2013, 22:43

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von splunge » 30.10.2013, 21:14

Da inzwischen mehrere Anfragen zum Daemonizer aus dem inzwischen nicht mehr verfügbaren lcd_msg AddOn eingegangen sind, hier ein Lösungsvorschlag, den ich selber aber noch nicht verifiziert habe. Vielleicht könnte das jemand von euch einmal testen.

Wie unter http://homematic-forum.de/forum/viewtop ... 60#p104915 beschrieben, reicht es auch aus, nur das Daemonize.tcl Skript zusammen mit dem Twilight.tcl Skript abzulegen.

Hier das Deamonizer.tcl Skript:

Code: Alles auswählen

set scriptname [lindex [file split $argv0] end]
if {[file exists /tmp/$scriptname.stop]} then {exit 0}

set daemonize 0
if {[file exists /tmp/$scriptname.pid]} then {
  catch {
    set f [open /tmp/$scriptname.pid]
    set filepid [read $f]
    close $f
    if {[pid] != $filepid} then {
      catch {exec kill $filepid}
      set daemonize 1
    }
  }
} else {
  set daemonize 1
}

if {$daemonize && ![regexp -- "-nodaemonize" $argv dummy]} then {
  eval exec /sbin/daemonize -p /tmp/$scriptname.pid -o /tmp/$scriptname.stdout -e /tmp/$scriptname.stderr /bin/tclsh $argv0 $argv
  exit 0
}
Im Twilight.tcl Skript muss dann noch der Pfad in Zeile 24 angepasst werden, je nachdem, wo ihr das Daemonizer.tcl Skript abgelegt habt.


Noch ein Hinweis zum Aufruf des Twilight.tcl Skriptes:

Das Twilight.tcl Skript muss nach einem Neustart der CCU einmalig aufgerufen werden, danach wird es zum Selbstläufer. Den initialen Aufruf erledige ich mit einem WebUI-Programm ohne Bedingungen:
Bild

Zur Sicherheit, falls es zu einem Programmabruch des Twilight-Skripts kommt, rufe ich es zusätzlich noch einmal pro Tag auf. Läuft das Skript noch, wird der erneute Aufruf im Skript abgefangen:
Bild

Der Skripttext lautet jeweils:

Code: Alles auswählen

dom.GetObject("CUxD.CUX2801001:1.CMD_EXEC").State("/bin/tclsh /usr/local/addons/TCL/Twilight.tcl 51.xx 7.xx 2 <Wetterstation> <API KEY>");
Breitengrad, Längengrad, Ort der Wonderground Wetterstation sowie der Wonderground API-Key müssen natürlich angepasst werden.
Die "xx" bitte durch die entsprechenden Zahlenwerte ersetzen. Ich möchte nur nicht meinen Wohnort auf wenige 100 Meter genau angeben... ;)
Zuletzt geändert von splunge am 07.03.2014, 22:16, insgesamt 2-mal geändert.

ak1
Beiträge: 77
Registriert: 29.08.2013, 11:46

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von ak1 » 01.11.2013, 11:40

Danke für Deine Unterstützung!
In den nächsten Tagen bastele ich dann erst mal ... :)

Viele Grüße
ak1

ak1
Beiträge: 77
Registriert: 29.08.2013, 11:46

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von ak1 » 24.12.2013, 09:06

Hallo und Frohe Weihnachten!

Hat noch jemand YAWN am Laufen und kann mit Tipps geben? Ich bin nun endlich dazu gekommen, die Skripte anzupassen und Programme anzulegen, habe aber noch Schwierigkeiten:

1. Es werden nicht alle Aktionen in die Aktionsliste aufgenommen. Vom Vormittag sind gar keine drin, vom Nachmittag nur eine Teilmenge. Woran könnte das liegen?
Ausschnitt aus dem Log:

Code: Alles auswählen

06:10:00: Berechne Aktionszeiten...
06:10:00: Sonnenaufgang Bürgerlich: 0 - 00:59:59
06:10:00: Sonnenaufgang Indoor: 0 - 00:59:59
06:10:00: Sonnenuntergang Indoor: 0 - 00:59:59
06:10:00: Sonnenuntergang Bürgerlich: 0 - 00:59:59
06:10:00: Zufallszahl für den Vormittag: 63223
06:10:00: Zufallszahl für den Nachmittag: 40462
06:10:00: Aktionszeiten: 63420,76020,82680,82920
06:10:00: Aktionenliste: 8,7,10,9
06:10:00: Timer aufgezogen auf: 17:37:0


2. Außerdem, wie sind hier die Sonnenaufgangs- bzw. Sonnenuntergangszeiten zu interpretieren (siehe oben)?

Im Twilight Log stehen die nämlich so drin:

Code: Alles auswählen

00:32:46: timezone: 1.0, twilight midnight: 00:32:46, dayofyear: 357.504962355
00:32:46: Die Variable acosvar muss zwischen -1 und 1, liegen und ist momentan 0.00255181059943
00:32:46: calculated twilight times: sunrise 24., 06:33:21, sunset 24., 18:32:11 for astronomical (1)
00:32:46: calculated twilight times: sunrise 24., 07:13:56, sunset 24., 17:51:36 for nautical (2)
00:32:46: calculated twilight times: sunrise 24., 07:56:52, sunset 24., 17:08:40 for civil (3)
00:32:46: calculated twilight times: sunrise 24., 08:36:53, sunset 24., 16:28:38 for standard (4)
00:32:46: calculated twilight times: sunrise 24., 09:00:43, sunset 24., 16:04:49 for indoor (5)
00:32:46: calculated twilight times: sunrise 24., 09:00:44, sunset 24., 15:16:16 for weather (6)
00:32:46: current twilight state 0 (Nacht), sunlight state 0, next at 06:33:21
00:32:46: waiting 21635 s until Tue Dec 24 06:33:21 CET 2013
06:33:21: current twilight state 1 (Astronom.), sunlight state 1, next at 07:13:56
06:33:21: waiting 2435 s until Tue Dec 24 07:13:56 CET 2013
07:13:56: current twilight state 2 (Nautisch), sunlight state 2, next at 07:56:52
07:13:56: waiting 2576 s until Tue Dec 24 07:56:52 CET 2013
07:56:52: current twilight state 3 (Buergerlich), sunlight state 3, next at 08:36:53
07:56:52: waiting 2401 s until Tue Dec 24 08:36:53 CET 2013
08:36:53: current twilight state 4 (Standard), sunlight state 4, next at 09:00:43
08:36:54: waiting 1429 s until Tue Dec 24 09:00:43 CET 2013
Und in der WebGUI so: Bild

Vorher war ich noch auf zwei weitere Probleme gestoßen:
Als CUxD Timer hatte splunge in den Skripten CUX2800002 benutzt. Das musste ich auf CUX2800001 ändern.

Im TriggerActions Skript hatte ich beim Überprüfen in der WebGUI immer einen Fehler. Nach Entfernen einer Kommentarzeile!?! in der Nähe des Fehlers war dieser verschwunden.

Wenn jemand Hinweise zu meinen geschilderten Problemen hat, wäre ich sehr dankbar.

Viele Grüße
ak1
Dateianhänge
2013-12-24_08h59_04.png
2013-12-24_08h59_04.png (5.37 KiB) 20343 mal betrachtet

Benutzeravatar
splunge
Beiträge: 65
Registriert: 10.04.2013, 22:43

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von splunge » 24.12.2013, 09:50

Hallo ak1,
1. Es werden nicht alle Aktionen in die Aktionsliste aufgenommen. Vom Vormittag sind gar keine drin, vom Nachmittag nur eine Teilmenge. Woran könnte das liegen?
Ausschnitt aus dem Log:

Code: Alles auswählen

06:10:00: Sonnenaufgang Bürgerlich: 0 - 00:59:59
06:10:00: Sonnenaufgang Indoor: 0 - 00:59:59
06:10:00: Sonnenuntergang Indoor: 0 - 00:59:59
06:10:00: Sonnenuntergang Bürgerlich: 0 - 00:59:59
So wie es aussieht, werden die Sonnenauf- und untergangszeiten nicht korrekt vom CalcActionTimes Skript eingelesen. Statt 1387868212 etc. wird immer "0" aus den Umgebungsvariablen gelesen.
Prüf bitte nochmal, ob im Skript auf die richtigen Variablen zugegriffen wird.
Ich könnte mir aber auch vorstellen, dass das TwilightScript zu dem Zeitpunkt (6:10) noch gar nicht durchgelaufen ist und die Variablen auf Initialwert stehen.

Nachtrag vom 26.12.2013: wie sich gezeigt hat, lag der Fehler in meiner Programmbeschreibung. Statt als Zeichenkette müssen die vier Umgebungsvariablen "Sekunden_SA_Buergerlich" etc. als Zahl von 0 bis 2147483647 angelegt werden. Ich habe das inzwischen in meinem Ursprungsposting korrigiert.
2. Außerdem, wie sind hier die Sonnenaufgangs- bzw. Sonnenuntergangszeiten zu interpretieren (siehe oben)?
Die erste Zahl ist die Uhrzeit in Sekunden (Stunden * 3600 + Minuten * 60 + Sekunden), die zweite die entsprechende Uhrzeit.
In den Umgebungsvariablen ist die Zeit aus dem TwilightScript in Sekunden seit dem 1.1.1970 angegeben.
Vorher war ich noch auf zwei weitere Probleme gestoßen:
Als CUxD Timer hatte splunge in den Skripten CUX2800002 benutzt. Das musste ich auf CUX2800001 ändern.
Das hängt davon ab, in welcher Reihenfolge du die CUxD-Geräte hinzufügst. Ich habe zuerst die SystemExec-Erweiterung angelegt, daher hat diese die ID "CUX2800001". Als zweites dann den System Timer mit der ID CUX2800002.
Im TriggerActions Skript hatte ich beim Überprüfen in der WebGUI immer einen Fehler. Nach Entfernen einer Kommentarzeile!?! in der Nähe des Fehlers war dieser verschwunden.
Kann ich leider nichts zu sagen. Kann dort im Moment keinen Fehler finden. Evtl. ein Copy&Paste-Problem bzgl. Zeileumbruch?

ak1
Beiträge: 77
Registriert: 29.08.2013, 11:46

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von ak1 » 30.12.2013, 20:47

Ich möchte nur kurz informieren, dass die Anwesenheitssimulation nun seit zwei Tagen bei mir wie gewünscht läuft.

Die Lösung ist einigermaßen komplex, zumindest hat es mich etwas Zeit gekostet, das Wesentliche zu verstehen. Aber sie ist auch recht flexibel. So kann man sie z.B. beliebig ausschalten, wenn man Abends nach Hause kommt, ohne das noch etwas "nachläuft".

Vielen Dank an splunge für dieses klasse Projekt und die gute Beschreibung!

ak1

thoralfbrandt
Beiträge: 61
Registriert: 10.02.2014, 12:18

Re: YAWN – Yet Another anWesenheitssimulatioN

Beitrag von thoralfbrandt » 28.02.2014, 13:10

Hallo, ich bin ein absoluter Anfänger und würde das gerne nachbauen. Programme anlegen kann ich, bei mir scheitert es aber beim TCL Script. Gibt es für deine tolle Idee ein Install Package? Wenn nein, wie muss ich tcl anlegen und aufspielen? Danke

Antworten

Zurück zu „Projektvorstellungen“