setTimeOfDay - Mein Versionsvorschlag

Hallo Leute,

ich bin relativ neu im Bereich Z-Wave und habe mein kleines Smarthome zu meinem neuen Hobby ernannt.
Da ich von Beruf Software- und Systementwickler bin, möchte ich meine Fähigkeiten bei der LUA-Programmierung
mit euch teilen.

Mein erstes kleines Projekt ist das allgeliebte Thema TimeOfDay. Ich habe hier die Informationen aus verschiedenen
Threads des Forums gesammelt und meine Methodik mit einfliessen lassen.

Wer es gerne Testen bzw Nutzen möchte, über Feedback und Ideen freu ich mich immer gerne.
Gerne lass ich noch eure Wünsche mit einfliessen.

Zur Zeit befindet sich das Skript in der Testphase, wird aber schon aktiv von mir genutzt.

Viele Grüsse

Najib

Nun zum Technischen:

Die Tageszeiten liegen als Anordnung vor wobei folgendes möglich ist:

0 Uhr ======> Morgen ======> Tag ======> Abend ======> Nacht =====> 0 Uhr

oder Datumsübergreifend:

0 Uhr ==>Nacht ====> Morgen =====> Tag ======> Abend ===========> 0 Uhr

Alle Tageszeiten können mit dem Sonnenutergang/-aufgang kombiniert werden. Näheres dazu weiter unten

Allgemeine Einstellungen und Funktionen im Skript:

a. Angabe aller Tageszeiten ( Morgen, Tag, Abend, Nacht ) durch:

  • Feste Uhrzeiten
    z.B morning = { “08:00”, 0 } Morgen entspricht 08:00 Uhr

  • Angabe in durch Sonnenuntergang/-aufgang
    z.B morning = { sunrise, 0 } Morgen entspricht Sonnenaufgang
    evening = { sunset, 0 } Morgen entspricht Sonnenuntergang

  • Angabe durch Offset in Minuten
    z.B morning = { sunrise, 30 } Morgen entspricht 30 Minuten nach Sonnenaufgang
    evening = { sunset, -70 } Abend entspricht 70 Minuten vor Sonnenaufgang

b. Mapping der Tageszeitenbezeichnungen:

Es können beliebige Bezeichnungen (Tageszeiten) in der globalen Variable TimeOfDay
hinterlegt werden. Die Bezeichnung müssen lediglich in der Mapping-Variable in
Zeile 17 angepasst werden

Der Name der Gloablen Variable TimeOfDay kann in Zeile 13 festgelegt werden.

c. Berechnung der nächsten Ausführung

  • Die Szene pausiert bis zum nächsten Tageszeiten-Wechsel. Die Pause wurde in
    Abhängigkeit zur Startzeit der nächsten Tageszeit berechnet
    ** Hier hab ich noch kein Gefühl wie sich die CPU zu einer Szene verhält,
    die z.B. im 1 Minuten Intervall gestartet wird **

d. Tests der Einstellung durch 24 Stunden Simulation

  • Durch Setzen der Variable debugMode = true (Zeile 10: true => an; false => aus)
    wird der Simulationsmodus aktiviert. Im Simulationsmodus wird ein Tag von
    0 bis 24:00 Uhr Durchlaufen und die möglichen Ergebnisse angezeigt.
    Das Setzen der globale Variable und des Timeouts entfällt.

Hier das Skript setTimeOfDay


--[[
%% autostart
%% properties
%% globals
--]]

-- ------------- Skript setDayTime --------------------------------------------

-- Hier Debugmode auf true setzen und es wird ein Simulationstag (24 Std) durchlaufen, 
-- der dir die moeglichen Ergebnisse zeigt jedoch keine Globale Variable ändert 
-- und keinen Timeout setzt
local debugMode = false 

-- Name der Globalen Variable im Home Center
local timeOfDayGlobalName = "TimeOfDay"

-- Mapping der TimeOfDay Eintraege in beliebiger Sprache 
-- der globalen Variable TimeOfDay entsprechend
local timeOfDayLabelMap = {Morning="Morning", Day="Day", Evening="Evening", Night="Night"}

-- Strings Sonnenauf/-untergang im Format Stunde:Minute von fibaro geliefert
local sunrise = fibaro:getValue(1, "sunriseHour")   -- Sonnenaufgang
local sunset  = fibaro:getValue(1, "sunsetHour")    -- Sonnenuntergang

-- Angaben der Bereichsgrenzen:
--
-- Hier koennen die Grenzen angeben werden, ab wann ein neuer Tagesbereich anfaengt.
-- Der Erste Wert ist die Uhrzeit im Format Stunde:Minute, der zweite Wert  
-- entspricht einem Offset (Verzoegerung) in Minuten bezogen auf den ersten Wert.
-- Als ersten Wert koennen auch die Variablen sunset/sunrise genommen werden.
--
-- Beispiele:
-- 
-- morning = { sunrise,  30 }  => Morgen beginnt 30 Minuten nach Sonnenaufgang
-- morning = { sunrise, -30 }  => Morgen beginnt 30 Minuten vor Sonnenaufgang
-- evening = { "18:00",   0 }  => Abend beginnt um genau 18:00 Uhr
-- evening = { sunset,   80 }  => Abend beginnt 80 Minuten nach Sonnenaufgang
local morning = { sunrise, 0 }
local day     = { "10:00", 0 }
local evening = { sunset, 0 }  
local night   = { "22:00", 0 }

--
-- =========> Ab hier sind keine Einstellungen noetig  
--

local sourceTrigger = fibaro:getSourceTrigger()

-- Berechnet Uhrzeit in Sekunden (Differenz) ab 0:00 Uhr
function getTimeSecsDiff(s)
  local hours, minutes = string.match(s, "(%d+):(%d+)")

  -- Sekunden = Stunden * 60 (mins) * 60 (secs) + Minuten * 60 (secs)
  local calcSecs = (hours-1) * 60 * 60 + minutes * 60 
  return calcSecs
end                                                

-- Umrechnung der Bereichsgrenze in Sekunden ab 0:00 Uhr (Differenz)
morning = getTimeSecsDiff(morning[1]) + morning[2] * 60
day     = getTimeSecsDiff(day[1]) + day[2] * 60
evening = getTimeSecsDiff(evening[1]) + evening[2] * 60                
night   = getTimeSecsDiff(night[1]) + night[2] * 60

if (debugMode) then
  fibaro:debug("---------------- Voreinstellungen -----------------")
  fibaro:debug(string.format("%s Uhr Sonnenaufgang",sunrise))
  fibaro:debug(string.format("%s Uhr Sonnenuntergang",sunset))                                                                              
  fibaro:debug(string.format("%s Uhr Morgen",os.date("%H:%M", morning)))
  fibaro:debug(string.format("%s Uhr Tag",os.date("%H:%M", day)))
  fibaro:debug(string.format("%s Uhr Abend",os.date("%H:%M", evening)))
  fibaro:debug(string.format("%s Uhr Nacht",os.date("%H:%M", night)))
end

-- Table der Bereichsgrenzen
local dayTimeList = {
                      {label = timeOfDayLabelMap.Morning, secs = morning},
                      {label = timeOfDayLabelMap.Day,     secs = day},
                      {label = timeOfDayLabelMap.Evening, secs = evening},
                      {label = timeOfDayLabelMap.Night,   secs = night},
                    }
                    
-- Sortierung der Bereichsgrenzen nach Zeit (secs)                    
local sort_func = function( a,b ) return a.secs < b.secs end
table.sort(dayTimeList, sort_func)
              
-- Hauptfunktion zum setzen der Globalen Variable TimeOfDay 
function setTimeOfDay() 
      
  local aktTime = 0;
  local doLoop = true;
  local loopHour = 0; 
  local timeout = 0
  local setDayTime = nil

  if (debugMode) then
    fibaro:debug("---------------- Beginne Auswertung --------------")
  end 
        
  repeat

    local timeOfDayCur = fibaro:getGlobalValue(timeOfDayGlobalName)
    fibaro:debug(string.format("Globale Variable '%s' aktueller Wert: '%s'", timeOfDayGlobalName, timeOfDayCur))
    
    if (debugMode) then
      -- Testzeit zur Simulation         
      aktTime = getTimeSecsDiff(string.format("%02d:00",loopHour)) 
      if (loopHour == 24)then
        doLoop = false
      end
      loopHour = loopHour + 1
    else  
      -- aktuelle Zeit in Sekunden (Differenz)
      aktTime = getTimeSecsDiff(os.date("%H:%M"))
      doLoop = false
    end
    
    for i, record in ipairs( dayTimeList ) do
      if (aktTime < record.secs) then
        if (i == 1) then
          -- falls match beim ersten Element nehme 
          -- den letzte Zeitbereich in der Liste
          setDayTime = dayTimeList[#dayTimeList].label
        else  
          setDayTime = dayTimeList[i-1].label
        end   
        timeout = record.secs - aktTime;
        break;
      end  
      
      lastDayTime = record.label 
      
      if (i == 1) then
        firstSec = record.secs
      end  
    end
    
    -- Falls Zeitgrenze ueber Datumswechsel muss der Timeout extra berechnet werden
    if ( setDayTime == nil ) then
      setDayTime = lastDayTime
      timeout = 86400 - aktTime + firstSec 
    end  
    fibaro:debug(string.format("Es ist '%s' Uhr => setze Globale Variable '%s' auf '%s'", os.date("%H:%M", aktTime), timeOfDayGlobalName, setDayTime))
    
    -- setze globale Variable
    if (not debugMode) then
      fibaro:debug(string.format("Globale Variable '%s' auf den Wert '%s' gesetzt", timeOfDayGlobalName, setDayTime))
      fibaro:setGlobal(timeOfDayGlobalName, setDayTime)
    end; 
    
    -- Die Szene pausiert bis zum Wechsel in den naechsten Tagesbereich
    fibaro:debug(string.format("Timeout für %.2f Minuten", timeout/60))

  until not doLoop   
    
  if (not debugMode) then
    -- Setze Timeout
    --fibaro:debug( string.format("vor Timeeout:"));
    setTimeout(setTimeOfDay, timeout*1000)
  end;
     
end

--
-- ================ Main ( Aufruf Verarbeitung ) ==============================             
--

setTimeOfDay()

Hi,

danke fürs Teilen und Deine Erklärungen dazu!

VG Hoggle

Super Skript, danke fürs Teilen!

Hi Najib,
sehr schickes script und prima dokumentiert. Läuft in der Ursprungsform prima bei mir. Ich hatte vorher ein anderes Tageszeitenscript bei dem ich die Zeiten mit Hilfe einer globalen Variable “Jahreszeit” im Sommer und Winter jeweils unterschiedlich definiert habe (brauche ich für einige Beleuchtungsszenen).

Ich habbe also jetzt versucht das auch in Dein Script einzubauen, bekomme aber immer einen Fehler “[DEBUG] 16:29:14: line 97: attempt to index global ‘morning’ (a nil value)”. Irgendwie mag die Szene das “If” statement nicht. Hab schon hin und her probiert und kann einfach keinen Fehler finden.
Kannst du vielleicht mal schauen wo der Fehler liegen könnte. Wesentliche Änderungen: Zeilen 37-41 (zusätzliche Variablen); Zeilen 57-78 (Jahreszeitenfunktion)
Danke
Alex

--[[
%% autostart
%% properties
%% globals
--]]

-- ------------- Skript setDayTime ----------------
--------- Farbiges Debug --------------------------
varDebug 			= true 			-- Debug logs true oder false
function ColDebug(color, message) 
	if (varDebug) then 
		fibaro:debug(string.format('<%s style="color:%s;">%s</%s>', "span", color, message, "span"));
	end
end
--------- Schleifenschutz -------------------------

if (fibaro:countScenes()>1) then
fibaro:debug('Kill the second scene!');
fibaro:abort();
end

-- Hier Debugmode auf true setzen und es wird ein Simulationstag (24 Std) durchlaufen, 
-- der dir die moeglichen Ergebnisse zeigt jedoch keine Globale Variable ändert 
-- und keinen Timeout setzt
local debugMode = true 

-- Name der Globalen Variable im Home Center
local timeOfDayGlobalName = "TimeOfDay"

-- Mapping der TimeOfDay Eintraege in beliebiger Sprache 
-- der globalen Variable TimeOfDay entsprechend
local timeOfDayLabelMap = {Morning="Morning", Day="Day", Evening="Evening", Night="Night"}

-- Strings Sonnenauf/-untergang im Format Stunde:Minute und Jahreszeit von fibaro geliefert
local sunrise 			= fibaro:getValue(1, "sunriseHour")   -- Sonnenaufgang
local sunset  			= fibaro:getValue(1, "sunsetHour")    -- Sonnenuntergang
local season  			= fibaro:getGlobal("Jahreszeit")
local startMorning 		= '05:00'		-- Startzeit Morning
local startDay			= '09:30'		-- Startzeit Day
local startEvening 		= '18:00'		-- Startzeit Evening
local startNight 		= '22:30' 		-- Startzeit Night

-- Angaben der Bereichsgrenzen:
--
-- Hier koennen die Grenzen angeben werden, ab wann ein neuer Tagesbereich anfaengt.
-- Der Erste Wert ist die Uhrzeit im Format Stunde:Minute, der zweite Wert  
-- entspricht einem Offset (Verzoegerung) in Minuten bezogen auf den ersten Wert.
-- Als ersten Wert koennen auch die Variablen sunset/sunrise genommen werden.
--
-- Beispiele:
-- 
-- morning = { sunrise,  30 }  => Morgen beginnt 30 Minuten nach Sonnenaufgang
-- morning = { sunrise, -30 }  => Morgen beginnt 30 Minuten vor Sonnenaufgang
-- evening = { "18:00",   0 }  => Abend beginnt um genau 18:00 Uhr
-- evening = { sunset,   80 }  => Abend beginnt 80 Minuten nach Sonnenaufgang

function adjustSeason()
  if (season == "Fruehling") or (season == "Sommer") --Fruehling und Sommer
   then
	local morning = { sunrise, 0 }
	local day     = { startDay, 0 }
	local evening = { startEvening, 0 }  
	local night   = { startNight, 0 }
--  ColDebug('green', 'Es ist  '..season..'. Nur Morgen wird gemäss Astrofunktion gesetzt')
--  ColDebug('yellow', 'Tag beginnt fix um '..startDay..' Uhr.')
--	ColDebug('magenta', 'Abend beginnt fix um '..startEvening..' Uhr.')
--	ColDebug('blue', 'Nacht beginnt fix um '..startNight..' Uhr.')
   else
	local morning = { startMorning, 0 }
	local day     = { startDay, 0 }
	local evening = { sunset, 0 }  
	local night   = { startNight, 0 }
--	ColDebug('green', 'Es ist  '..season..'. Nur Abend wird gemäss Astrofunktion gesetzt')
--	ColDebug('orange', 'Morgen beginnt fix um '..startMorning..' Uhr.')
--	ColDebug('yellow', 'Tag beginnt fix um '..startDay..' Uhr.')
--	ColDebug('blue', 'Nacht beginnt fix um '..startNight..' Uhr.')
   end
end
   
--
-- =========> Ab hier sind keine Einstellungen noetig  
--

local sourceTrigger = fibaro:getSourceTrigger()

-- Berechnet Uhrzeit in Sekunden (Differenz) ab 0:00 Uhr
function getTimeSecsDiff(s)
  local hours, minutes = string.match(s, "(%d+):(%d+)")

  -- Sekunden = Stunden * 60 (mins) * 60 (secs) + Minuten * 60 (secs)
  local calcSecs = (hours-1) * 60 * 60 + minutes * 60 
  return calcSecs
end                                                

-- Umrechnung der Bereichsgrenze in Sekunden ab 0:00 Uhr (Differenz)
adjustSeason()
morning = getTimeSecsDiff(morning[1]) + morning[2] * 60
day     = getTimeSecsDiff(day[1]) + day[2] * 60
evening = getTimeSecsDiff(evening[1]) + evening[2] * 60                
night   = getTimeSecsDiff(night[1]) + night[2] * 60

if (debugMode) then
  fibaro:debug("---------------- Voreinstellungen -----------------")
  fibaro:debug(string.format("%s Uhr Sonnenaufgang",sunrise))
  fibaro:debug(string.format("%s Uhr Sonnenuntergang",sunset))                                                                              
  fibaro:debug(string.format("%s Uhr Morgen",os.date("%H:%M", morning)))
  fibaro:debug(string.format("%s Uhr Tag",os.date("%H:%M", day)))
  fibaro:debug(string.format("%s Uhr Abend",os.date("%H:%M", evening)))
  fibaro:debug(string.format("%s Uhr Nacht",os.date("%H:%M", night)))
end

-- Table der Bereichsgrenzen
local dayTimeList = {
                      {label = timeOfDayLabelMap.Morning, secs = morning},
                      {label = timeOfDayLabelMap.Day,     secs = day},
                      {label = timeOfDayLabelMap.Evening, secs = evening},
                      {label = timeOfDayLabelMap.Night,   secs = night},
                    }
                    
-- Sortierung der Bereichsgrenzen nach Zeit (secs)                    
local sort_func = function( a,b ) return a.secs < b.secs end
table.sort(dayTimeList, sort_func)
              
-- Hauptfunktion zum setzen der Globalen Variable TimeOfDay 
function setTimeOfDay() 
      
  local aktTime = 0;
  local doLoop = true;
  local loopHour = 0; 
  local timeout = 0
  local setDayTime = nil

  if (debugMode) then
    fibaro:debug("---------------- Beginne Auswertung --------------")
  end 
        
  repeat

    local timeOfDayCur = fibaro:getGlobalValue(timeOfDayGlobalName)
    fibaro:debug(string.format("Globale Variable '%s' aktueller Wert: '%s'", timeOfDayGlobalName, timeOfDayCur))
    
    if (debugMode) then
      -- Testzeit zur Simulation         
      aktTime = getTimeSecsDiff(string.format("%02d:00",loopHour)) 
      if (loopHour == 24)then
        doLoop = false
      end
      loopHour = loopHour + 1
    else  
      -- aktuelle Zeit in Sekunden (Differenz)
      aktTime = getTimeSecsDiff(os.date("%H:%M"))
      doLoop = false
    end
    
    for i, record in ipairs( dayTimeList ) do
      if (aktTime < record.secs) then
        if (i == 1) then
          -- falls match beim ersten Element nehme 
          -- den letzte Zeitbereich in der Liste
          setDayTime = dayTimeList[#dayTimeList].label
        else  
          setDayTime = dayTimeList[i-1].label
        end   
        timeout = record.secs - aktTime;
        break;
      end  
      
      lastDayTime = record.label 
      
      if (i == 1) then
        firstSec = record.secs
      end  
    end
    
    -- Falls Zeitgrenze ueber Datumswechsel muss der Timeout extra berechnet werden
    if ( setDayTime == nil ) then
      setDayTime = lastDayTime
      timeout = 86400 - aktTime + firstSec 
    end  
    fibaro:debug(string.format("Es ist '%s' Uhr => setze Globale Variable '%s' auf '%s'", os.date("%H:%M", aktTime), timeOfDayGlobalName, setDayTime))
    
    -- setze globale Variable
    if (not debugMode) then
      fibaro:debug(string.format("Globale Variable '%s' auf den Wert '%s' gesetzt", timeOfDayGlobalName, setDayTime))
      fibaro:setGlobal(timeOfDayGlobalName, setDayTime)
    end; 
    
    -- Die Szene pausiert bis zum Wechsel in den naechsten Tagesbereich
    fibaro:debug(string.format("Timeout für %.2f Minuten", timeout/60))

  until not doLoop   
    
  if (not debugMode) then
    -- Setze Timeout
    --fibaro:debug( string.format("vor Timeeout:"));
    setTimeout(setTimeOfDay, timeout*1000)
  end;
     
end

--
-- ================ Main ( Aufruf Verarbeitung ) ==============================             
--

setTimeOfDay()

Hallo Alex,

hatte gar nicht gesehen das es inzwischen hier Interessenten gibt. Ich wird mir dein Skript morgen mal anschauen. Heute Schaff ich das nicht mehr.

Inzwischen nutz ich sehr komplexe Skripte aus dem englischen Forum, aber hierbei kann ich dir sicher helfen was vernünftiges aufzustellen.

Für mich sind es gute Übungen in LUA :slight_smile:

Grüsse

Hallo Alex,

der Fehler war doch schnell zu finden, deshalb schreib ich dir vorab was genau die Ursache ist.

Du deklarierst die Tageszeit Arrays morning usw. in deiner Funktion adjustSeason als lokale Variable, die auch nur innerhalb der Funktion bekannt ist.
Deshalb hat z.B. in Zeile 97 morning[1] den Wert nil also keinen Wert. Statt deinen Code in Zeile 96 durch eine Funktion aufzurufen steck den doch einfach da rein, dann hast auch kein Problem mit den Variableskope.

Ich wollte aber ehe eine Version rausbringen die die Wochentage, Monate und Jahreszeiten berücksichtigt.
Kann aber nicht genau versprechen wann ich das poste.

Grüße

Hallo zusammen,

ich hab mal den Vorschlag von Alex mit den Jahreszeiten übernommen und etwas dynamischer gemacht.
Zeitgleich ist mir ein Bug aufgefallen wo die letzte Tageszeit nicht richtig übernommen worden ist. Das hab ich jetzt behoben.
Weitere Fragen und Wünsche einfach hier posten :slight_smile: Wochtags/Woechend Berücksichtigung bau ich ein, sobald ich dazu komme.


--[[
--%% autostart
%% properties
%% globals
--]]

-- ------------- Skript setDayTime --------------------------------------------

-- Hier Debugmode auf true setzen und es wird ein Simulationstag (24 Std) durchlaufen, 
-- der dir die moeglichen Ergebnisse zeigt jedoch keine Globale Variable ändert 
-- und keinen Timeout setzt
local debugMode = false 

-- Name der Globalen Variable im Home Center
local timeOfDayGlobalName = "TimeOfDay"

-- Mapping der TimeOfDay Eintraege in beliebiger Sprache 
-- der globalen Variable TimeOfDay entsprechend
local timeOfDayLabelMap = {Morning="Morning", Day="Day", Evening="Evening", Night="Night"}

-- Strings Sonnenauf/-untergang im Format Stunde:Minute von fibaro geliefert
local sunrise = fibaro:getValue(1, "sunriseHour")   -- Sonnenaufgang
local sunset  = fibaro:getValue(1, "sunsetHour")    -- Sonnenuntergang

-- Auslesen der aktuellen Jahreszeit
local season = fibaro:getGlobal("Jahreszeit")
if (debugMode) then 
  season = 'Winter'
end

-- Angaben der Bereichsgrenzen:
--
-- Hier koennen die Grenzen angeben werden, ab wann ein neuer Tagesbereich anfaengt.
-- Der Erste Wert ist die Uhrzeit im Format Stunde:Minute, der zweite Wert  
-- entspricht einem Offset (Verzoegerung) in Minuten bezogen auf den ersten Wert.
-- Als ersten Wert koennen auch die Variablen sunset/sunrise genommen werden.
--
-- Beispiele:
-- 
-- globalMorning = { sunrise,  30 }  => Morgen beginnt 30 Minuten nach Sonnenaufgang
-- globalMorning = { sunrise, -30 }  => Morgen beginnt 30 Minuten vor Sonnenaufgang
-- globalEvening = { "18:00",   0 }  => Abend beginnt um genau 18:00 Uhr
-- globalEvening = { sunset,   80 }  => Abend beginnt 80 Minuten nach Sonnenaufgang

-- Allgemeine Zeiten; Gelten immer falls keine Zeiten angegeben wurden fuer die aktuelle Jahreszeit (Uebersteuerung)
local globalMorning = { sunrise, 0 }
local globalDay     = { "10:00", 0 }
local globalEvening = { sunset, 0 }  
local globalNight   = { "22:00", 0 }

-- Hier koennen beliebige Zeiten eingegebn werden fuer die aktuelle Jahreszeit. 
-- Der nil Wert bedeutet hier, das die globale Einstellung uebernommen werden soll
-- Die Werte muessen der Reihenfolge Morgen Tag Abend und Nacht entsprechen.
local seasonTimeTable = { Fruehling = { nil, nil, nil, nil },
                       
                          Sommer    = { nil, nil, nil, nil },                       
                       
                          Herbst    = { nil, nil, nil, nil },
                       
                          Winter    = { { sunrise, 0 }, 
                                        { "11:00", 0 }, 
                                        globalEvening,
                                        { "23:00", 0 } }
                        }

--
-- =========> Ab hier sind keine Einstellungen noetig  
--

local sourceTrigger = fibaro:getSourceTrigger()

-- Berechnet Uhrzeit in Sekunden (Differenz) ab 0:00 Uhr
function getTimeSecsDiff(s)
  local hours, minutes = string.match(s, "(%d+):(%d+)")

  -- Sekunden = Stunden * 60 (mins) * 60 (secs) + Minuten * 60 (secs)
  local calcSecs = (hours-1) * 60 * 60 + minutes * 60 
  return calcSecs
end                                                

-- hier werden die Werte der Jahreszeit entsprechend (falls angegeben) uebernommen
-- ansonsten gelten die globalen Angaben
morning = globalMorning          
if (seasonTimeTable[season][1]) then
  morning = seasonTimeTable[season][1]
end

day = globalDay          
if (seasonTimeTable[season][2]) then
  day = seasonTimeTable[season][2]
end

evening = globalEvening          
if (seasonTimeTable[season][3]) then
  evening = seasonTimeTable[season][3]
end

night = globalNight          
if (seasonTimeTable[season][4]) then
  night = seasonTimeTable[season][4]
end

-- Umrechnung der Bereichsgrenze in Sekunden ab 0:00 Uhr (Differenz)
morning = getTimeSecsDiff(morning[1]) + morning[2] * 60
day     = getTimeSecsDiff(day[1]) + day[2] * 60
evening = getTimeSecsDiff(evening[1]) + evening[2] * 60                
night   = getTimeSecsDiff(night[1]) + night[2] * 60

if (debugMode) then
  fibaro:debug("---------------- Voreinstellungen -----------------")
  fibaro:debug(string.format("Es ist die Jahreszeit %s",season))
  fibaro:debug(string.format("%s Uhr Sonnenaufgang",sunrise))
  fibaro:debug(string.format("%s Uhr Sonnenuntergang",sunset))                                                                              
  fibaro:debug(string.format("%s Uhr Morgen",os.date("%H:%M", morning)))
  fibaro:debug(string.format("%s Uhr Tag",os.date("%H:%M", day)))
  fibaro:debug(string.format("%s Uhr Abend",os.date("%H:%M", evening)))
  fibaro:debug(string.format("%s Uhr Nacht",os.date("%H:%M", night)))
end

-- Table der Bereichsgrenzen
local dayTimeList = {
                      {label = timeOfDayLabelMap.Morning, secs = morning},
                      {label = timeOfDayLabelMap.Day,     secs = day},
                      {label = timeOfDayLabelMap.Evening, secs = evening},
                      {label = timeOfDayLabelMap.Night,   secs = night},
                    }
                    
-- Sortierung der Bereichsgrenzen nach Zeit (secs)                    
local sort_func = function( a,b ) return a.secs < b.secs end
table.sort(dayTimeList, sort_func)
              
-- Hauptfunktion zum setzen der Globalen Variable TimeOfDay 
function setTimeOfDay() 
      
  local aktTime = 0;
  local doLoop = true;
  local loopHour = 0; 
  local timeout = 0
  local setDayTime = nil

  if (debugMode) then
    fibaro:debug("---------------- Beginne Auswertung --------------")
  end 
        
  repeat

    local timeOfDayCur = fibaro:getGlobalValue(timeOfDayGlobalName)
    fibaro:debug(string.format("Globale Variable '%s' aktueller Wert: '%s'", timeOfDayGlobalName, timeOfDayCur))
    
    if (debugMode) then
      -- Testzeit zur Simulation         
      aktTime = getTimeSecsDiff(string.format("%02d:00",loopHour)) 
      if (loopHour == 24)then
        doLoop = false
      end
      loopHour = loopHour + 1
    else  
      -- aktuelle Zeit in Sekunden (Differenz)
      aktTime = getTimeSecsDiff(os.date("%H:%M"))
      doLoop = false
    end
    
    for i, record in ipairs( dayTimeList ) do
      
      if (aktTime < record.secs) then
        if (i == 1) then
          -- falls match beim ersten Element nehme 
          -- den letzte Zeitbereich in der Liste
          setDayTime = dayTimeList[#dayTimeList].label
        else  
          setDayTime = dayTimeList[i-1].label
        end   
        timeout = record.secs - aktTime;
        break;
      else
        setDayTime = nil  
      end  
      
      lastDayTime = record.label 
      
      if (i == 1) then
        firstSec = record.secs
      end  
    end
    
    -- Falls Zeitgrenze ueber Datumswechsel muss der Timeout extra berechnet werden
    if ( setDayTime == nil ) then
      setDayTime = lastDayTime
      timeout = 86400 - aktTime + firstSec 
    end  
    fibaro:debug(string.format("Es ist '%s' Uhr => setze Globale Variable '%s' auf '%s'", os.date("%H:%M", aktTime), timeOfDayGlobalName, setDayTime))
    
    -- setze globale Variable
    if (not debugMode) then
      fibaro:debug(string.format("Globale Variable '%s' auf den Wert '%s' gesetzt", timeOfDayGlobalName, setDayTime))
      fibaro:setGlobal(timeOfDayGlobalName, setDayTime)
    end; 
    
    -- Die Szene pausiert bis zum Wechsel in den naechsten Tagesbereich
    fibaro:debug(string.format("Timeout für %.2f Minuten", timeout/60))

  until not doLoop   
    
  if (not debugMode) then
    -- Setze Timeout
    --fibaro:debug( string.format("vor Timeeout:"));
    setTimeout(setTimeOfDay, timeout*1000)
  end;
     
end

--
-- ================ Main ( Aufruf Verarbeitung ) ==============================             
--

setTimeOfDay()

Ich hab aus versehen den autostart auskommentiert (Zeile 3).
Den müsst ihr wieder einbauen (-- rausnehmen), damit es auch automatisch ausgeführt wird.

Grüße

Hi Najib,

das sieht schon wesentlich eleganter aus. Prima bau ich nachher mal so ein und berichte.

Vielen Dank
Alex

Hi Najib,

Läuft bestens. Besonders das mit der zuschaltbaren Szenensimulation (DebugMode) finde ich gut. Hatte ich so noch nicht gesehen.

Danke
Alex

Hallo Alex,

Prima das es klappt. Sollten noch Fehler auftauchen, melde die hier bitte.
Ansonst bau ich auch demnächst die Wochendunterscheidung ein.

Grüsse

Die erste Version gefällt mir! Super gemacht… aber das Update bekomme ich nicht zum laufen…

[DEBUG] 21:50:26: [1;31m2018-04-15 21:50:26.562088 [ fatal] Unknown exception: /opt/fibaro/scenes/7.lua:94: attempt to index field ‘?’ (a nil value)


--[[
%% autostart
%% properties
%% globals
--]]

-- ------------- Skript setDayTime --------------------------------------------

-- Hier Debugmode auf true setzen und es wird ein Simulationstag (24 Std) durchlaufen, 
-- der dir die moeglichen Ergebnisse zeigt jedoch keine Globale Variable ändert 
-- und keinen Timeout setzt
local debugMode = false 

-- Name der Globalen Variable im Home Center
local timeOfDayGlobalName = "TimeOfDay"

-- Mapping der TimeOfDay Eintraege in beliebiger Sprache 
-- der globalen Variable TimeOfDay entsprechend
local timeOfDayLabelMap = {Morning="Morning", Day="Day", Evening="Evening", Night="Night"}

-- Strings Sonnenauf/-untergang im Format Stunde:Minute von fibaro geliefert
local sunrise = fibaro:getValue(1, "sunriseHour")   -- Sonnenaufgang
local sunset  = fibaro:getValue(1, "sunsetHour")    -- Sonnenuntergang

-- Auslesen der aktuellen Jahreszeit
local season = fibaro:getGlobal("Jahreszeit")
if (debugMode) then 
  season = 'Winter'
end

-- Angaben der Bereichsgrenzen:
--
-- Hier koennen die Grenzen angeben werden, ab wann ein neuer Tagesbereich anfaengt.
-- Der Erste Wert ist die Uhrzeit im Format Stunde:Minute, der zweite Wert  
-- entspricht einem Offset (Verzoegerung) in Minuten bezogen auf den ersten Wert.
-- Als ersten Wert koennen auch die Variablen sunset/sunrise genommen werden.
--
-- Beispiele:
-- 
-- globalMorning = { sunrise,  30 }  => Morgen beginnt 30 Minuten nach Sonnenaufgang
-- globalMorning = { sunrise, -30 }  => Morgen beginnt 30 Minuten vor Sonnenaufgang
-- globalEvening = { "18:00",   0 }  => Abend beginnt um genau 18:00 Uhr
-- globalEvening = { sunset,   80 }  => Abend beginnt 80 Minuten nach Sonnenaufgang

-- Allgemeine Zeiten; Gelten immer falls keine Zeiten angegeben wurden fuer die aktuelle Jahreszeit (Uebersteuerung)
local globalMorning = { sunrise, 0 }
local globalDay     = { "10:00", 0 }
local globalEvening = { sunset, 0 }  
local globalNight   = { "22:00", 0 }

-- Hier koennen beliebige Zeiten eingegebn werden fuer die aktuelle Jahreszeit. 
-- Der nil Wert bedeutet hier, das die globale Einstellung uebernommen werden soll
-- Die Werte muessen der Reihenfolge Morgen Tag Abend und Nacht entsprechen.
local seasonTimeTable = { Fruehling = { { sunrise, 0 }, 
                                        { "10:00", 0 }, 
                                        globalEvening,
                                        { "23:00", 0 } },
                       
                          Sommer    = { { sunrise, 0 }, 
                                        { "09:00", 0 }, 
                                        globalEvening,
                                        { "23:00", 0 } },                       
                       
                          Herbst    = { { sunrise, 0 }, 
                                        { "10:00", 0 }, 
                                        globalEvening,
                                        { "23:00", 0 } },
                       
                          Winter    = { { sunrise, 0 }, 
                                        { "11:00", 0 }, 
                                        globalEvening,
                                        { "23:00", 0 } }
                        }

--
-- =========> Ab hier sind keine Einstellungen noetig  
--

local sourceTrigger = fibaro:getSourceTrigger()

-- Berechnet Uhrzeit in Sekunden (Differenz) ab 0:00 Uhr
function getTimeSecsDiff(s)
  local hours, minutes = string.match(s, "(%d+):(%d+)")

  -- Sekunden = Stunden * 60 (mins) * 60 (secs) + Minuten * 60 (secs)
  local calcSecs = (hours-1) * 60 * 60 + minutes * 60 
  return calcSecs
end                                                

-- hier werden die Werte der Jahreszeit entsprechend (falls angegeben) uebernommen
-- ansonsten gelten die globalen Angaben
morning = globalMorning          
if (seasonTimeTable[season][1]) then
  morning = seasonTimeTable[season][1]
end

day = globalDay          
if (seasonTimeTable[season][2]) then
  day = seasonTimeTable[season][2]
end

evening = globalEvening          
if (seasonTimeTable[season][3]) then
  evening = seasonTimeTable[season][3]
end

night = globalNight          
if (seasonTimeTable[season][4]) then
  night = seasonTimeTable[season][4]
end

-- Umrechnung der Bereichsgrenze in Sekunden ab 0:00 Uhr (Differenz)
morning = getTimeSecsDiff(morning[1]) + morning[2] * 60
day     = getTimeSecsDiff(day[1]) + day[2] * 60
evening = getTimeSecsDiff(evening[1]) + evening[2] * 60                
night   = getTimeSecsDiff(night[1]) + night[2] * 60

if (debugMode) then
  fibaro:debug("---------------- Voreinstellungen -----------------")
  fibaro:debug(string.format("Es ist die Jahreszeit %s",season))
  fibaro:debug(string.format("%s Uhr Sonnenaufgang",sunrise))
  fibaro:debug(string.format("%s Uhr Sonnenuntergang",sunset))                                                                              
  fibaro:debug(string.format("%s Uhr Morgen",os.date("%H:%M", morning)))
  fibaro:debug(string.format("%s Uhr Tag",os.date("%H:%M", day)))
  fibaro:debug(string.format("%s Uhr Abend",os.date("%H:%M", evening)))
  fibaro:debug(string.format("%s Uhr Nacht",os.date("%H:%M", night)))
end

-- Table der Bereichsgrenzen
local dayTimeList = {
                      {label = timeOfDayLabelMap.Morning, secs = morning},
                      {label = timeOfDayLabelMap.Day,     secs = day},
                      {label = timeOfDayLabelMap.Evening, secs = evening},
                      {label = timeOfDayLabelMap.Night,   secs = night},
                    }
                    
-- Sortierung der Bereichsgrenzen nach Zeit (secs)                    
local sort_func = function( a,b ) return a.secs < b.secs end
table.sort(dayTimeList, sort_func)
              
-- Hauptfunktion zum setzen der Globalen Variable TimeOfDay 
function setTimeOfDay() 
      
  local aktTime = 0;
  local doLoop = true;
  local loopHour = 0; 
  local timeout = 0
  local setDayTime = nil

  if (debugMode) then
    fibaro:debug("---------------- Beginne Auswertung --------------")
  end 
        
  repeat

    local timeOfDayCur = fibaro:getGlobalValue(timeOfDayGlobalName)
    fibaro:debug(string.format("Globale Variable '%s' aktueller Wert: '%s'", timeOfDayGlobalName, timeOfDayCur))
    
    if (debugMode) then
      -- Testzeit zur Simulation         
      aktTime = getTimeSecsDiff(string.format("%02d:00",loopHour)) 
      if (loopHour == 24)then
        doLoop = false
      end
      loopHour = loopHour + 1
    else  
      -- aktuelle Zeit in Sekunden (Differenz)
      aktTime = getTimeSecsDiff(os.date("%H:%M"))
      doLoop = false
    end
    
    for i, record in ipairs( dayTimeList ) do
      
      if (aktTime < record.secs) then
        if (i == 1) then
          -- falls match beim ersten Element nehme 
          -- den letzte Zeitbereich in der Liste
          setDayTime = dayTimeList[#dayTimeList].label
        else  
          setDayTime = dayTimeList[i-1].label
        end   
        timeout = record.secs - aktTime;
        break;
      else
        setDayTime = nil  
      end  
      
      lastDayTime = record.label 
      
      if (i == 1) then
        firstSec = record.secs
      end  
    end
    
    -- Falls Zeitgrenze ueber Datumswechsel muss der Timeout extra berechnet werden
    if ( setDayTime == nil ) then
      setDayTime = lastDayTime
      timeout = 86400 - aktTime + firstSec 
    end  
    fibaro:debug(string.format("Es ist '%s' Uhr => setze Globale Variable '%s' auf '%s'", os.date("%H:%M", aktTime), timeOfDayGlobalName, setDayTime))
    
    -- setze globale Variable
    if (not debugMode) then
      fibaro:debug(string.format("Globale Variable '%s' auf den Wert '%s' gesetzt", timeOfDayGlobalName, setDayTime))
      fibaro:setGlobal(timeOfDayGlobalName, setDayTime)
    end; 
    
    -- Die Szene pausiert bis zum Wechsel in den naechsten Tagesbereich
    fibaro:debug(string.format("Timeout für %.2f Minuten", timeout/60))

  until not doLoop   
    
  if (not debugMode) then
    -- Setze Timeout
    --fibaro:debug( string.format("vor Timeeout:"));
    setTimeout(setTimeOfDay, timeout*1000)
  end;
     
end

--
-- ================ Main ( Aufruf Verarbeitung ) ==============================             
--

setTimeOfDay()

Hi,

Hast du alle hier genutzten globalen Variablen angelegt?

Viele Grüße Hoggle

@Najib
Danke für’s Teilen. Probiere gerade rum :slight_smile:

@n4f3ts
Vermutlich liegt es an der Globalen “Jahreszeit”? Ich hatte eine ähnliche Meldung -> Vordefiniert angelegt und schon geht’s.

Wo findet man eigentlich das Skript für die Jahreszeiten welches die 2. Globale befüllt? Hab schon etwas hier und auf der siio-Seite gesucht, bin aber leider nicht fündig geworden oder ich bin blind.

Also meine Var. sind

also vordef. Var.

TimeOfDay: Morning - Day - Evening - Night

Und als Var.
Sunset
Sunrise

ist das falsch oder habe ich etwas übersehen?

Sunset und Sunrise wird vom HC geliefert. Die brauchst du nicht anlegen. Aber in Zeile 27 wird “Jahreszeit” ausgelesen. Die muss vordefiniert sein.

Hi,

Ändy hat recht die Jahreszeiten musst Du entweder manuell setzen :frowning: oder per script automatisch anpassen lassen. Ich weiss es garnicht mehr so genau, aber den Wesentlichen Teil des Scripts habe ich mal aus dem englischen oder französischen Forum kopiert und dann ein bisschen angepasst. Bei mir läuft das seit geraumer Zeit prima. Du musst vorab eine globale Variable “Jahreszeit” mit den Einträgen “Fruehling”, “Sommer”, “Herbst” und “Winter” anlegen und dann dieses Script laufen lassen. Ist aber recht simpel gehalten. Fortgeschrittenes Zeug wie z.B Schaltjahre werden nicht beachtet.
Cheers
Alex

--[[
%% autostart
%% properties
%% globals
--]]
---------------------------------------------------
----------some variables as explanation------------
-- 		Setting the time variables:
--		%a	abbreviated weekday name (e.g., Wed)
--		%A	full weekday name (e.g., Wednesday)
--		%b	abbreviated month name (e.g., Sep)
--		%B	full month name (e.g., September)
--		%c	date and time (e.g., 09/16/98 23:48:10)
--		%d	day of the month (16) [01-31]
--		%H	hour, using a 24-hour clock (23) [00-23]
--		%I	hour, using a 12-hour clock (11) [01-12]
--		%M	minute (48) [00-59]
--		%m	month (09) [01-12]
--		%p	either "am" or "pm" (pm)
--		%S	second (10) [00-61]
--		%w	weekday (3) [0-6 = Sunday-Saturday]
--		%x	date (e.g., 09/16/98)
--		%X	time (e.g., 23:48:10)
--		%Y	full year (1998)
--		%y	two-digit year (98) [00-99]

----------------------------------------------------------------------------------------------------------
local debugging = true --(true= on / false=off)
----------------------------------------------------------------------------------------------------------

--Color debugging
function ExtraDebug( color, message )
  if (debugging) then
    fibaro:debug(string.format('<%s style="color:%s;">%s</%s>', "span", color, message, "span")); 
  end
end

function WhichSeason()
	year 	= tonumber(os.date("%Y"));
	month 	= tonumber(os.date("%m"));
	day 	= tonumber(os.date("%d"));
	hour 	= tonumber(os.date("%H"));
	min 	= tonumber(os.date("%M"));
	weekday = tonumber(os.date("%w"));
	local tNow = os.date("*t")
	local dayofyear = tNow.yday
	local season
	if (dayofyear >= 79) and (dayofyear < 172) then season = "Fruehling"
		elseif (dayofyear >= 172) and (dayofyear < 265) then season = "Sommer"
		elseif (dayofyear >= 265) and (dayofyear < 355) then season = "Herbst"
		else season = "Winter"
	end
	if (fibaro:getGlobal("Jahreszeit") ~= season)
		then fibaro:setGlobal("Jahreszeit", tostring(season))
		ExtraDebug("red", "Season has changed, Updating variable Jahreszeit");
	end
--	if Debug=="YES" then
		ExtraDebug("purple", " DayofYear: "  .. dayofyear .. " ");		
  		ExtraDebug("white", " Year: "  .. year .. " ");
		ExtraDebug("white", " Month: "  .. month .. " ");
		ExtraDebug("white", " Day: "  .. day .. " ");
		ExtraDebug("white", " Hour: "  .. hour .. " ");
		ExtraDebug("white", " Minute: "  .. min .. " ");
		ExtraDebug("white", " Weekday: "  .. weekday .. " ");
		ExtraDebug("white", " Season: " .. season .. " ");
--	end
end

-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- SZENE JEDEN TAG UM 00:05 AUSFÜHREN
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
local sourceTrigger = fibaro:getSourceTrigger();

function tempFunc()
  local currentDate = os.date("*t");
  local startSource = fibaro:getSourceTrigger();
  if ((((currentDate.wday == 1 or currentDate.wday == 2 or currentDate.wday == 3 or currentDate.wday == 4 or currentDate.wday == 5 or currentDate.wday == 6 or    currentDate.wday == 7) and string.format("%02d", currentDate.hour) .. ":" .. string.format("%02d", currentDate.min) == "00:05") )) then
    WhichSeason()
  end
  setTimeout(tempFunc, 60*1000)
end

if (sourceTrigger["type"] == "autostart") then
  tempFunc()
else
  local currentDate = os.date("*t");
  local startSource = fibaro:getSourceTrigger();
  if (startSource["type"] == "other") then
    WhichSeason()
  end
end

Hallo zusammen,

hmm, bin gerade etwas stutzig geworden. Kann es sein, dass die Infos sunset, sunrise sowie Jahreszeit (wenn benutzt) nur beim Start des Scripts ermittelt werden? Später läuft dieses ja per Timeout in einer Schleife und somit würden diese Infos gar nicht mehr aktualisiert. Dazu bräuchte es einen expliziten Neustart der Szene oder des HC2. Oder bin ich da auf dem Holzweg? :slight_smile:

PS: Alex, danke noch für die Season-Berechung!

Hi Ändy,
gut beobachtet. Das war mir noch garnicht aufgefallen, aber tatsächlich werden die Variablen nur am Anfang gesetzt und dann nicht mehr geändert. Neustart der Szene wäre sicher eine Lösung aber nicht sehr elegant. Besser wär es natürlich wenn die Variablen bei jeder Schleife neu gesetzt würden. Ich bin mir aber nicht sicher wie aufwändig das wäre (bzw. ob ich das selbst hinbekomme :slight_smile: ). Einen Szenenneustart benutzt ja z.B. das Rollladenscript Link (Zeile 72 ff) . Ich muss mal schauen wann ich Zeit finde um mir das Script mal genauer anzusehen.
Grüße
Alex

Hallo Alex,

Interessant, automatisierten Szenenneustart kannte ich noch nicht.
Ich habe mir jedoch kurzer Hand so beholfen: Alles was mit der Initialisierung zu tun hat, in eine eigene Funktion rein. Diese wird dann innerhalb der eigentlichen Timeout-Funktion aufgerufen, also bei jedem Tageszeitenwechsel. Ist vielleicht der bessere Weg als Neustart. Also in der Form:

function setTimeOfDay() 

  InitTimeOfDay() --Initialisierung in die Hauptfunktion mit rein! Sonst wird sunset, sunrise, season nicht aktualisiert.


Das funktioniert seit ein paar Tagen ohne Probleme.
Finde das, was ich mit dem ursprünglichen code gemacht hab, jedoch etwas “unschön” und wollte mal abwarten ob @Najib einschreitet und was sauberes anbietet :wink: Es ist ja in der Urform sein Werk. Geniale Lösung mit dem angepassten Timeout!

Wenn gewünscht, kann ich meine Anpassung aber schon hier teilen. Muss da aber nochmal ausführlich drüber schauen.