Zwei alternative Abbruchbedingungen

Moin,

ich möchte eine etwas kompliziertere Lichtsteuerung via LUA umsetzen.
Über einen Türkontakt soll unter bestimmten Bedingungen an diversen Stellen Licht eingeschaltet werden. Das Licht soll nun nach Ablauf einer gewissen Zeit ODER nachdem ein anderer Türkontakt betätigt wurde wieder ausgeschaltet werden.

Das mit der Zeitsteuerung lässt sich ja einfach über sleep… abbilden.
Wie kann man den zweiten Türsensor so in das Script einbinden, dass auf die Änderung des Zustands des Sensors reagiert werden kann?

So sieht das Skript bis jetzt aus.
Wie bekomme ich es hin, dass entweder die 30 Sekunden zum ausschalten oder ein (noch nicht definierter) Türsensor führen?


--[[
%% properties
44 value
%% globals
--]]

local sourceTrigger = fibaro:getSourceTrigger();
local currentDate = os.date("*t");

local switchValues = {};
switchValues['Bedroom'] = {};
switchValues['Bedroom']['id'] = 36;
switchValues['Bedroom']['oldValue'] = fibaro:getValue(switchValues['Bedroom']['id'], "value");
switchValues['Staircase'] = {};
switchValues['Staircase']['id'] = 34;
switchValues['Staircase']['oldValue'] = fibaro:getValue(switchValues['Staircase']['id'], "value");
switchValues['Corridor'] = {};
switchValues['Corridor']['id'] = 38;
switchValues['Corridor']['oldValue'] = fibaro:getValue(switchValues['Corridor']['id'], "value");
switchValues['LivingRoom'] = {};
switchValues['LivingRoom']['id'] = 6;
switchValues['LivingRoom']['oldValue'] = fibaro:getValue(switchValues['LivingRoom']['id'], "value");

-- Abbruch, wenn Szene mehrfach gestartet wurde
if (fibaro:countScenes() > 1)
    then
    fibaro:abort();   
end

if (fibaro:getValue(44, "value") == "0") then
  -- Nur tätig werden, wenn es dunkel ist.
  if (((os.date("%H:%M", os.time()-15*60)) >= fibaro:getValue(1, "sunsetHour")) or ((os.date("%H:%M", os.time()+15*60)) <= fibaro:getValue(1, "sunriseHour"))) then
    
    
  fibaro:debug('Ausgelöst');
  
  
  fibaro:call(switchValues['Bedroom']['id'], "turnOn");
  fibaro:call(switchValues['Staircase']['id'], "turnOn");
  fibaro:call(switchValues['Corridor']['id'], "turnOn");
  fibaro:call(switchValues['LivingRoom']['id'], "turnOn");
  
  
  setTimeout(function()
	fibaro:debug('Alles wieder ausschalten:');
  
    if(switchValues['Bedroom']['oldValue'] == "0") then
		fibaro:call(switchValues['Bedroom']['id'], "turnOff");
		fibaro:debug('Schlafzimmer');
    end
	if(switchValues['Staircase']['oldValue'] == "0") then
		fibaro:call(switchValues['Staircase']['id'], "turnOff");
		fibaro:debug('Treppenaufgang');
    end
	if(switchValues['Corridor']['oldValue'] == "0") then
		fibaro:call(switchValues['Corridor']['id'], "turnOff");
		fibaro:debug('Flur');
    end
	if(switchValues['LivingRoom']['oldValue'] == "0") then
		fibaro:call(switchValues['LivingRoom']['id'], "turnOff");
		fibaro:debug('Wohnzimmer');
    end
  end, 30000) -- 30*1000
	
	
  end
end

Hi,

das setTimeOut ruht 30 Sekunden und macht in derzeit gar nichts. Auch nicht, wenn du einen anderen Türsensor einbauen würdest. In deinem Falle, würde ich dir eine while do-Schleife empfehlen. Die könntest du beenden, wenn der andere Türsensor eine Veränderung meldet.

Gruß

Servus!

Ich habe eine ähnliche Anwendung wie Du, nur mit einer variablen Zeit.
Szenen vermeide ich wo es geht, ich versuche alles mit VD zu lösen.

Ich kann Dir keine fertige Lösung liefern, aber ein paar Code-Schnippsel.
Im Grunde gibt es eine langsame Hauptschleife, z.B. alle 60 s als Iterationszeit.
Ich habe bei meiner Lösung nur eine Heizzeit im Stundentakt erlaubt, daher kann die Hauptschleife auch im Zyklus in Minuten abgearbeitet werden!
Wenn Du den Timer minutengenau haben möchtest, muss der Code angepasst werden.
Nun Triggert Du mit dem Türkontakt einen Timer der eine gewisse Zeit läuft.
Wenn nun vorher der andere Türkontakt aktiv ist, wird vor Ablauf der festgelegten Zeit zurückgesetzt.

Folgende globale Variablen werden benötigt:

HEIZDAUER      -- 1 - 10 Stunden
AUFWAERMEN     -- 0/1 als Hilfsschalter
HEIZZEITENDE   -- xx:xx berechnete Uhrzeit (+1 bis +10h nach dem Start)

Diesen Code habe ich in einen Ein-Schalter eine VD gesetzt: Hier starte ich die ganze Aktion

local SelfId = fibaro:getSelfId()
local Zeit = os.date("%H:%M") -- aktuelle Zeit
local Heizdauer = fibaro:getGlobal("HEIZDAUER") -- Laufzeit in Stunden als globale Variable, kann auch hardcodiert sein...
local H
local M
local StopZeit -- Berechnete Stoppzeit
local SwBadID = 63 	-- ID Schalter Bad
local SwWzID =  47 	-- ID Schalter Wohnzimmer
local SwSzID =  72 	-- ID Schalter Schlafzimmer

H = string.sub(Zeit, 1, 2) -- Stunden extrahieren
M = string.sub(Zeit, 4, 5) -- Minuten

H = tonumber(H) -- Stunden als Zahl speichern
--fibaro:debug('Heizzeitende nach der Berechnung: '..Zeit .." vorMatch: " ..tostring(string.match(tostring(Zeit), "[0-2][0-3]:[0-5][0-9]")))  
if (H +  Heizdauer) <= 23 then -- berechne Heizdauer ab aktueller Uhrzeit
  	H = (H + Heizdauer) 
  	StopZeit = string.format("%02d:%s",H,M)
  	-- fibaro:call(SelfId, "setProperty", "ui.lbStatus.value", "Heizzeit ENDE: " ..StopZeit) -- Label im VD zur Anzeige
  	fibaro:setGlobal("HEIZZEITENDE",StopZeit)
  	fibaro:debug('Heizzeitende: '..StopZeit)
else   -- wenn Heizzeit über 24Uhr hinaus geht, Zeit anpassen
 	H = Heizdauer - (24 - H)
  	StopZeit = string.format("%02d:%s",H,M)
  	--fibaro:call(SelfId, "setProperty", "ui.lbStatus.value", "Heizzeit ENDE: " ..StopZeit) -- Label im VD zur Anzeige
  	fibaro:setGlobal("HEIZZEITENDE",StopZeit) 
	fibaro:debug('Heizzeitende: '..StopZeit)
end
fibaro:debug('Heizzeitende nach der Berechnung: '..StopZeit .." Match: " ..tostring(string.match(StopZeit, "[0-2][0-3]:[0-5][0-9]")))  
if (string.match(StopZeit, "[0-2][0-9]:[0-5][0-9]") ~= NIL) then  --rudimentärer Test auf korrektes Uhrzeitformat
    fibaro:setGlobal("AUFWAERMEN", 1) -- Aufwärmen EIN
    ---------------------------------------
    -- HIER Heizrelais direkt starten !!!!!
    ---------------------------------------
  	fibaro:call(SwWzID,'turnOn'); -- Wohnzimmer ein
 	fibaro:call(SwSzID,'turnOn'); -- Schlafzimmer ein
  	fibaro:call(SwBadID,'turnOn'); -- Badezimmer ein

	fibaro:debug('Vorwärmen ein')
else
  	fibaro:call(SelfId, "setProperty", "ui.lbStatus.value", "Fehler in der Heizzeit")
  	fibaro:setGlobal("AUFWAERMEN", 0) -- Aufwärmen AUS
  	fibaro:call(2, "sendEmail", "Häuschen", "Haus vorwärmen Fehler im Uhrzeitformat!")
  	fibaro:debug('Vorwärmen Fehler im Uhrzeitformat!')  
end

Im Ausschalt Knopf der VD steht folgendes:

local SelfId = fibaro:getSelfId()
local SwBadID = 63 	-- ID Schalter Bad
local SwWzID =  47 	-- ID Schalter Wohnzimmer
local SwSzID =  72 	-- ID Schalter Schlafzimmer

fibaro:setGlobal("AUFWAERMEN", 0) -- Aufwärmen AUS
fibaro:call(SelfId, "setProperty", "ui.lbStatus.value", "Heizen AUS")
---------------------------------------
-- HIER Heizrelais direkt stoppen !!!!!
---------------------------------------
fibaro:call(SwWzID,'turnOff'); -- Wohnzimmer aus
fibaro:call(SwSzID,'turnOff'); -- Schlafzimmer aus
fibaro:call(SwBadID,'turnOff'); -- Badezimmer aus
fibaro:debug('################################') 
fibaro:debug(' Aufwärmzeit manuell gestoppt !') 
fibaro:debug('################################')

In der Hauptschleife des VD wird nun die aktuelle Zeit mit dem vorausberechneten Heizzeitende verglichen:

local ZykluszeitSekunden = 30  -- Aufrufzeit der Hauptschleife 10-60 Sekunden um Ressourcen zu schonen, nicht zu lange wegen Wartezeit beim Rücksetzen!
local SelfId = fibaro:getSelfId()
local HeizDauer = fibaro:getGlobal("HEIZDAUER")   -- in Stunden
local AufwaermenStart =  fibaro:getGlobal("AUFWAERMEN")  -- Toggle 
local HeizZeitEnde = fibaro:getGlobal("HEIZZEITENDE") -- Uhrzeit für aus
local Zeit = os.date("%H:%M") -- aktuelle Zeit
local SwBadID = 63 	-- ID Schalter Bad
local SwWzID =  47 	-- ID Schalter Wohnzimmer
local SwSzID =  72 	-- ID Schalter Schlafzimmer 

fibaro:debug('Zeit: ' ..Zeit ..' Heizdauer: ' ..tostring(HeizDauer) ..'h  Start: ' ..tostring(AufwaermenStart)..'   Ende: ' ..tostring(HeizZeitEnde))

if (tostring(AufwaermenStart) == '1') and (Zeit == HeizZeitEnde) then  -- Vorausberechnete Heizzeit erreicht 
                                                                       -- ### HIER NOCH DEN TÜRKONTAKT MIT VERODERN! !!! *#####
    fibaro:call(SwWzID,'turnOff'); -- Wohnzimmer aus                   -- Abfrage auf == HeizZeitEnde O.K. da nur auf Stunden gerechnet wird!
    fibaro:call(SwSzID,'turnOff'); -- Schlafzimmer aus
    fibaro:call(SwBadID,'turnOff'); -- Badezimmer aus
    fibaro:setGlobal("AUFWAERMEN", 0)  -- Flanke, damit nur einmalig ausgeschaltet wird!
  	AufwaermenStart = 0
    fibaro:debug('################################') 
    fibaro:debug('Aufwärmzeit erreicht, alles aus!') 
    fibaro:debug('################################') 
end
fibaro:sleep(ZykluszeitSekunden*1000); 

Hi,

Welchen Vorteil erwartest du von dem Einsatz von VDs? Da gibt es nämlich ein wichtiges Motto:

Never put something Important in the Main Loop.

Da dieser dauerhaft läuft, stürzt dieser regelmäßig ab und du hast viel größere Ressourcenverbraucher als bei Szenen, die nur durch Änderungen getriggert werden. Zu dem Absturz des MainLoop gibt es auch bereits ein Ticket bei Fibaro :wink:

Nur so als Tipp.

Hi!

Diese VD läuft nun schon wochenlang ohne Fehler durch…
Proleme gibt es (zumindest bei mir) nur bei HTTP Zugriffen, da musste ich meine VD umschreiben…
Wo steht dieses Motto?

IMHO gibt es 2 verschiedene Szenen, jene mit Interrupt die von Ereignissen getriggert werden, und zeitgesteuerte die gleich laufen wie VDs, nur dass statt sleep() setTimeOut() zum Zug kommt.
In der schwachen LUA Doku von Fibaro steht da nicht viel dazu, aber der ressourcen Monitor meiner HC2 sagt 1-2% CPU Last…
Ich hätte präzisieren sollen: Zeitgesteuerte Szenen vermeide ich …

Dein Ansatz mit einer Do…While Schleife kommt einer Endlosschleife schon sehr nahe, oder? :wink:

Es gibt auch keinen Fehler. Der Main-Loop stürzt einfach ab… Du kannst es natürlich so machen wie du willst. Mir ja egal :wink: Mit deinem Zusatz mit den zeitgesteuerten Szenen ist es ok :wink:

Eine while do-Schleife kann grundsätzlich als Endlosschleife genutzt werden. Du kannst aber auch einen Counter einbauen, dass diese nach einer bestimmten Zeit einfach beendet wird.

Hi,

vielen Dank für die vielen Infos.

Mein Codeschnipsel funktioniert im abgebildeten Zustand schon ganz gut. Nach 30 Sekunden wird wieder ausgeschaltet.

Ich habe das Prinzip noch nicht so ganz durchschaut.
Ist es möglich mit dem zweiten Türkontakt die Szene irgendwie zu triggern oder muss ich eine relativ schnell iterierende Schleife bauen, die dann den Status des Türkontakts abfragt um beim Erkennen des geöffneten Zustands reagiert?

Hi,

du kannst mit einem zweiten Türkontakt triggern, den Trigger auslesen und wenn der zweite Türsensor der Trigger ist, das Licht ausschalten. Das geht :slight_smile:

Gruß

Könntest du mir noch einen Tipp geben, wie ich das mit dem zweiten Trigger LUA-Technisch umsetze? Womit kann ich das abfangen?

Der Trigger wird dann in der schon laufenden Szene erkannt, nicht wahr?

Hi,

nein, das ist dann eine neue Instanz. Wenn es in der gleichen Instanz laufen soll, dann kannst du das nur mit der while-Schleife machen. Den Trigger kannst du mit

local trigger = fibaro:getSourceTrigger()
local doorID = tonumber(trigger['deviceID'])

abfragen.

Gruß

Endergebnis:

--[[
%% properties
44 value
%% globals
--]]

-- Prüfung, ob Terrassentür geöffnet und geschlossen wurde.
function checkPatioDoor(switchValues)
	local res = false;
	switchValues['PatioDoor']['currentValue'] = tonumber(fibaro:getValue(switchValues['PatioDoor']['id'], "value"));
	res = switchValues['PatioDoor']['oldValue'] == 0 and switchValues['PatioDoor']['lastExecValue'] == 1 and switchValues['PatioDoor']['currentValue'] == 0;
	switchValues['PatioDoor']['lastExecValue'] = switchValues['PatioDoor']['currentValue'];
	return res;
end

-- Funktion die das Script entweder bis zum Ablauf der Zeitspanne pausiert oder bis die Terrassentür geöffnet und wieder geschlossen wurde.
function wait(waitTime, switchValues)
	local i = 1;
	while (i <= waitTime * 4) and (not switchValues['PatioDoor']['closedAgain']) do
		i = i + 1;
		switchValues['PatioDoor']['closedAgain'] = checkPatioDoor(switchValues);
		fibaro:sleep(250) -- 250 ms bis zur nächsten Prüfung warten.	
	end
end

-- Schaltet Lampen
function setLightBrightness(id,value,construction)
	if (construction == "binary")  then
		if (value == 0) then
			fibaro:call(id, "turnOff");
		else
			fibaro:call(id, "turnOn");
		end
	else
		fibaro:call(id, "setValue", value);
	end
end

local sourceTrigger 	= fibaro:getSourceTrigger();
local currentTime 		= os.date("*t");
local waitTimeBedroom 	= 40; -- Sekunden
local waitTimeMisc 		= 80; -- Sekunden

local switchValues = {};

switchValues['DME'] = {};
switchValues['DME']['id'] 						= 44;
switchValues['DME']['Value'] 					= tonumber(fibaro:getValue(switchValues['DME']['id'], "value"));

switchValues['Bedroom'] = {};
switchValues['Bedroom']['id'] 					= 36; -- Nachttisch
switchValues['Bedroom']['oldValue'] 			= tonumber(fibaro:getValue(switchValues['Bedroom']['id'], "value"));
switchValues['Bedroom']['targetBrightness'] 	= 1;

switchValues['Staircase'] = {};
switchValues['Staircase']['id'] 				= 34;
switchValues['Staircase']['oldValue'] 			= tonumber(fibaro:getValue(switchValues['Staircase']['id'], "value"));
switchValues['Staircase']['targetBrightness'] 	= 1;

switchValues['Corridor'] = {};
switchValues['Corridor']['id'] 					= 38;
switchValues['Corridor']['oldValue'] 			= tonumber(fibaro:getValue(switchValues['Corridor']['id'], "value"));
switchValues['Corridor']['targetBrightness'] 	= 1;

switchValues['LivingRoom'] = {};
switchValues['LivingRoom']['id'] 				= 55; -- Esstisch
switchValues['LivingRoom']['oldValue'] 			= tonumber(fibaro:getValue(switchValues['LivingRoom']['id'], "value"));
switchValues['LivingRoom']['targetBrightness'] 	= 40;

switchValues['PatioDoor'] = {};
switchValues['PatioDoor']['id'] 				= 8;
switchValues['PatioDoor']['oldValue'] 			= tonumber(fibaro:getValue(switchValues['PatioDoor']['id'], "value"));
switchValues['PatioDoor']['currentValue'] 		= switchValues['PatioDoor']['oldValue'];
switchValues['PatioDoor']['lastExecValue'] 		= switchValues['PatioDoor']['oldValue'];
switchValues['PatioDoor']['closedAgain'] 		= false;

-- Abbruch, wenn Szene mehrfach gestartet wurde
if (fibaro:countScenes() > 1) then
  fibaro:abort();   
end

-- Ablauf nur starten, wenn Kontakt auch wirklich geschlossen ist.
if (switchValues['DME']['Value'] == 0) then
	
	-- Nur tätig werden, wenn es dunkel ist.
	if (((os.date("%H:%M", os.time()-15*60)) >= fibaro:getValue(1, "sunsetHour")) or ((os.date("%H:%M", os.time()+15*60)) <= fibaro:getValue(1, "sunriseHour"))) then

		fibaro:debug('Ausgelöst');

		-- Licht überall einschalten
		setLightBrightness(switchValues['Bedroom']['id'], 	switchValues['Bedroom']['targetBrightness'], 	"binary");
		setLightBrightness(switchValues['Staircase']['id'], switchValues['Staircase']['targetBrightness'], 	"binary");
		setLightBrightness(switchValues['Corridor']['id'], 	switchValues['Corridor']['targetBrightness'], 	"binary");
		
		if (switchValues['LivingRoom']['oldValue'] < switchValues['LivingRoom']['targetBrightness']) then
			setLightBrightness(switchValues['LivingRoom']['id'], switchValues['LivingRoom']['targetBrightness'], "dimmable");
		end	

		
		-- Zeit abwarten, bis Licht im Schlafzimmer wieder ausgehen soll oder wenn Terrassentür geöffnet und wieder geschlossen wurde.
		wait(waitTimeBedroom, switchValues);
		-- Schlafzimmer ausschalten
		setLightBrightness(switchValues['Bedroom']['id'], 		switchValues['Bedroom']['oldValue'], 	"binary");
		

		-- Zeit abwarten oder fortsetzen, wenn Terrassentür geöffnet und wieder geschlossen wurde.
		wait(waitTimeMisc - waitTimeBedroom, switchValues);
	    -- Restl. Licht ausschalten
		setLightBrightness(switchValues['Staircase']['id'], 	switchValues['Staircase']['oldValue'], 	"binary");
		setLightBrightness(switchValues['Corridor']['id'], 		switchValues['Corridor']['oldValue'], 	"binary");
		setLightBrightness(switchValues['LivingRoom']['id'], 	switchValues['LivingRoom']['oldValue'], "dimmable");
		

	end
end