Flexibles Fertigmelder-Skript

Ich steuere heute mal wieder ein Skript bei aus der Abteilung „Dinge auslesen“. Diesmal geht es um die Haushaltsgeräte wie z.B. Spülmaschine, Waschmaschine, Trockner.

Ein verbreiteter Weg, um konventionelle Geräte dieser Art auszulesen ist der, über Master-Slave-Schaltungen, Stromsensoren etc. zu gehen. Das Prinzip ist eigentlich recht simpel: Im Betrieb verbraucht so ein Gerät relativ viel Strom, wenn es fertig ist, erkennt man das daran dass es nur noch sehr wenig Strom zieht.

Man kann so eine Überwachung z.B. mit dem FS20 FMS lösen. Das Teil ist, sagen wir mal, nervig zu programmieren, aber wenn man das hinter sich hat dann tut es meist was es soll.

Leider ist die Sache in der Praxis dann oft komplizierter als man es sich zunächst vorstellt. Folgende Probleme stellen sich ein:

-Das FMS ist ein Funksender. Es bemerkt nicht, ob sein Schaltsignal in IPS angekommen ist, es dürfte ihm auch herzlich egal sein. Wenn ein Schaltsignal ausbleibt, schlimmstenfalls das Ausschaltsignal am Ende des Durchlaufs, dann unterbleibt die Fertigmeldung.
-Haushaltsgeräte verbrauchen nicht kontinuierlich Strom bis sie fertig sind. Stichwort Einweichphase. Es gibt also zu erwartende Pausen, in denen der FMS dann einen Ausschaltbefehl sendet, obwohl der Durchlauf noch lange nicht abgeschlossen ist.

Das kann einem schon reichlich Kopfzerbrechen bereiten. Natürlich gibt es Quick&Dirty-Workarounds, um der Problematik beizukommen. Man kann bspw. dafür sorgen, dass immer erst 15 Minuten nach dem letzten Ausschaltbefehl die Fertigmeldung erfolgt. Oder man legt ein Timeout fest, nach dem die Fertigmeldung auf jeden Fall erfolgt, egal ob der Ausschaltbefehl empfangen wurde oder nicht.

Schön ist das aber nicht. Wenn es zum Beispiel unterschiedliche Waschprogramme gibt, etwa den Schnellwaschgang und die 90° Kochwäsche, dann möchte man in beiden Fällen sinnvolle Meldungen erzeugen und nicht unpräzise Schätzungen.

Mein Skript nutzt eine Handvoll Einstellparameter, um den Durchlauf eines Haushaltsgeräts halbwegs intelligent und sinnvoll zu überwachen. Ich würde mal behaupten, dass man mit noch mehr Komplexität noch genauere Ergebnisse erzielen könnte, aber es sollte so bereits wesentlich besser funktionieren als ein einfaches FMS+Timeout.

Zunächst das Skript:

<?

// es müssen folgende Variablen angelegt und die IDs hier ersetzt werden!

// Variablen für die Mindest- und Höchstdauer eines Durchlaufs, in Minuten (integer)
$minCycleDuration = GetValue(51473  /*[Setup\Fertigmelder\Spülmaschine\Mindestdauer eines Durchlaufs]*/);
$maxCycleDuration = GetValue(56333  /*[Setup\Fertigmelder\Spülmaschine\Höchstdauer eines Durchlaufs]*/);

// Variablen für das Zählen erwarteter Gerätepausen, sowie die längste Pause die nicht als Ende des
// Durchlaufs gewertet werden soll.
// die Anzahl erwarteter Pausen ist wichtig, da nach dem Erreichen dieser Anzahl eine längere Unterbrechung
// (>= $minPauseDuration) als Durchlauf-Ende gewertet wird.
$minPauseDuration = GetValue(27873  /*[Setup\Fertigmelder\Spülmaschine\Mindestdauer einer mitgezählten Pause]*/);
$maxPauseDuration = GetValue(11914  /*[Setup\Fertigmelder\Spülmaschine\Längste zu erwartende Pause]*/);
$expectedPauses = GetValue(42870  /*[Setup\Fertigmelder\Spülmaschine\Anzahl erwarteter Pausen]*/);

// Variablen zum aktuellen Durchlauf. Für $deviceStatusId gelten folgende Werte:
// 0 = läuft nicht
// 1 = läuft
// 2 = läuft, bald fertig (erwartete Anzahl gezählter Pausen erreicht)
// 3 = fertig
$pauseCounterId = 58145  /*[Setup\Fertigmelder\Spülmaschine\Aktueller Durchlauf\Pausenzähler]*/;
$deviceStatusId = 43880  /*[Setup\Fertigmelder\Spülmaschine\Aktueller Durchlauf\Gerätestatus]*/;
$cycleStartTimeId = 11819  /*[Setup\Fertigmelder\Spülmaschine\Aktueller Durchlauf\Startzeitpunkt des Durchlaufs]*/;
$pauseStartTimeId = 19857  /*[Setup\Fertigmelder\Spülmaschine\Aktueller Durchlauf\Startzeitpunkt der letzten Pause]*/;

function deviceIsDone()
{
	// hier die Fertigmeldung auslösen!
}

// ab hier nichts mehr ändern!

$deviceStatus = GetValue($deviceStatusId);
$pauses = GetValue($pauseCounterId);

if($_IPS['SENDER'] == 'Variable')
{
	$rawState = $_IPS['VALUE'];

	switch($deviceStatus)
	{
	   case 3: // fertig
		case 0: // läuft nicht
		   if($rawState) // neuer Durchlauf beginnt
		   {
		      SetValue($cycleStartTimeId, time());
		      $pauses = 0;
		      $deviceStatus = 1;

		      IPS_SetScriptTimer($_IPS['SELF'], $maxCycleDuration * 60);
		   }
		   break;

		case 1: // läuft
		case 2: // läuft, bald fertig
		   if($rawState) // Pause zu ende
		   {
		      $thisPauseDuration = time() - GetValue($pauseStartTimeId);
		      if($thisPauseDuration >= $minPauseDuration * 60)
		      {
		         $pauses++;
		         if($pauses >= $expectedPauses && $expectedPauses > 0)
		         {
		            $deviceStatus = 2;
		         }
		      }
		      
		      $remainingTimeout = $maxCycleDuration * 60 -
					(time() - GetValue($cycleStartTimeId));

				if($remainingTimeout > 0)
			      IPS_SetScriptTimer($_IPS['SELF'], $remainingTimeout);
				else
				   IPS_RunScript($_IPS['SELF']);
		   }
		   else // Pause beginnt
		   {
		      SetValue($pauseStartTimeId, time());
		      
		      if($pauses >= $expectedPauses)
			      IPS_SetScriptTimer($_IPS['SELF'], $minPauseDuration * 60);
		      else
			      IPS_SetScriptTimer($_IPS['SELF'], $maxPauseDuration * 60);
		   }
		   break;
	}
}
else // timeout durch zu lange Gesamtdauer des Durchlaufs, zu lange Pause bzw. am regulären Ende des Durchlaufs
{
	if($deviceStatus == 1 || $deviceStatus == 2)
	{
	   // falls Mindestdauer des Durchlaufs noch nicht erreicht ist, zurücksetzen ohne Fertigmeldung!
		if(time() - GetValue($cycleStartTimeId) < $minCycleDuration)
		{
			$deviceStatus = 0;
			IPS_SetScriptTimer($_IPS['SELF'], 0);
		}
	   else
		{
			$deviceStatus = 3;
			IPS_SetScriptTimer($_IPS['SELF'], 0);
			
			deviceIsDone();
		}
	}
	else if($deviceStatus == 3) $deviceStatus = 0;
}

SetValue($deviceStatusId, $deviceStatus);
SetValue($pauseCounterId, $pauses);

?>

Um das Skript nutzen zu können, muss zunächst die Variable (boolean) des FMS (oder anderem Gerät ähnlicher Funktionsweise) geloggt werden, so dass man sich einen Eindruck von der Durchlauflänge, den Pausen etc. machen kann.

Hierzu macht es Sinn, zunächst einige Durchläufe mitzuloggen, bevor man das Skript in Betrieb nimmt. Um es in Betrieb zu nehmen, hängt man ein Ereignis an, das auf Änderung der Fertigmelder-Statusvariable reagiert.

Anschließend sollte man anhand der Kurve der Fertigmelder-Variable im Webfront Pausenzeiten und -anzahl einstellen (hierzu die Konfigurationsvariablen verwenden). Sinnvollerweise visualisiert man diese im Webfront.

Um den Gerätestatus zurückzusetzen, muss das Skript einmal manuell z.B. aus dem Webfront aufgerufen werden.

Hallo sokkederheld,

an diesem Thema bin ich auch jetzt dran, und habe mir auch schon ein paar Scripte im Forum „Thema Waschmaschine“ dazu angeschaut.

Bin aber noch nicht so weit, als dass irgendetwas funktioniert :wink:

Dein Script scheint mir bisher das ausgefeilteste zu sein, aber ich habe es noch nicht richtig verstanden…

Ich nutze kein FS20. Bei mir hängt die Waschmaschine (und Trockner) an den Plugwise Komponenten. Jetzt ist mir der Einstieg nicht klar, da Du an einer Boolean Variable des FMS anfängst … ?
Bei den Plugwise habe ich die Leistung in einer Float, und mit „Status“ den Zustand ob die Steckdose an oder aus ist… (ist eigentlich immer an…)

Kannst Du mir einen Tipp geben, wie ich die 2 Sachen zusammenbringe ?

Merci,

Oliver

Mein Skript geht von einer Boolean-Variable aus, weil oft nur ein Boolean-Wert zur Verfügung steht. False steht dann für „keine Last / Grundlast“ und true für „Betriebslast“. Im Betrieb des Geräts kommt es mehrmals zum Umschalten zwischen den beiden Zuständen, folglich zu Pausen, in denen die Variable false ist, obgleich das Gerät noch läuft (weil es einweicht oder sonstwie wartet).

Der FMS ist eine Möglichkeit, solch einen Boolean-Wert zu erzeugen; es gibt sicherlich auch andere Komponenten die das können.

In deinem Fall, wo du ja eine wesentlich präzisere Angabe über die Leistungsaufnahme hast, könntest du eventuell noch wesentlich detailliertere Informationen entnehmen. Beispielsweise die Phase des Waschgangs etc. (falls das irgendwie von Bedeutung sein sollte für die Visu).

Du kannst aber auch einfach einen Boolean-Wert aus deinem Leistungsaufnahme-Messwert „gewinnen“ indem du mit einem Schwellwert arbeitest (Variable True bei Überschreiten von Wert X + Pufferwert, false bei Unterschreiten von Wert X - Pufferwert). Dann könntest du den so gewonnenen Boolean-Wert in meinem Skript verwenden. Ob das elegant ist, steht auf einem anderen Blatt. Probieren kann man es aber sicherlich mal.

Interessant wäre auf jeden Fall mal, die Leistungsaufnahme-Variable zu loggen und sich die Kurve im Webfront anzusehen. So habe ich auch angefangen, um mich der Problematik anzunähern.

Ich stoße mit meinem Skript aber auch schon an Grenzen: Eines der Geräte bei uns ist ein Waschtrockner. Der hat etliche Programme und da wird es vermutlich noch nötig werden, einiges an Komplexität hinzuzunehmen um immer eine punktgenaue Fertigmeldung zu erhalten. Für den Geschirrspüler funktioniert das Skript aber bereits sehr gut.

Ok, verstanden…

Hmm, mir scheint das Thema hat eine hohe Individualität durch die unterschiedlichen Lastkurven und Wege das zu ermitteln.

Ich denke mit dem von Dir vorgeschlagenen Weg werde ich es hinkriegen, mit der Eleganz gebe ich Dir dann Recht…

Merci für die Hinweise, und falls Du auch mal die Leistung direkt mist und ein passendes Script entwickelt hast, bin ich ein dankbarer Abnehmer :wink:

Grüße,

Oliver

Ich nutze Plugwise für den Geschirrspüler (Wenn Last > X, dann an, sehr simpel) und FS20 für die Waschmaschine (Plugwise ist mir zu unzuverlässig dafür, eigentlich witzig dann FS20 zu nehmen ^^).

Im Moment mache ich die Fertigmeldung noch mit einem 15 min Timer - Dein Script gefällt mir aber viel besser - ich werde mich da in den nächsten Tagen ransetzen, Daten habe ich ja genug geloggt :slight_smile:

Vielen Dank für’s Skript !

Gruß
Martin

So, implementiert und die erste Runde Wäsche ist durch - funktioniert !

Vielen Dank. Ein oder zwei Fragen:

Während $minCycleDuration (und $max) recht klar sind, war es mit bei $minPauseDuration nicht wirklich klar in welchem Format ich die Zeiten der Pausen vorzugeben sind. Die gemessenen „Päuschen“ sind bei mir teilweise nur 1 oder 2 Sekunden lang (aber bis zu 12 Minuten) - ich habe „Minuten“ und „0“ bei der Länge gewählt - klappt. Was ist denn richtig ?

Die Startzeitpunkte sind ja in Unix Time - kann ich die problemlos in „lesbare“ Zeit umwandeln, oder soll ich dafür lieber ein Hilfsvariable benutzen ?

Ansonsten: Super!!

Viele Grüße
Martin

Die Startzeitpunkte sind ja in Unix Time - kann ich die problemlos in „lesbare“ Zeit umwandeln, oder soll ich dafür lieber ein Hilfsvariable benutzen ?

Sollte es eine Integer-Variable sein setze doch das Profil ~UnixTimestamp darauf und siehst Du den Wert im lesbaren Format.

Ja, ich war mir nur sich sicher ob der Wert der Variable evtl noch irgendwo im Skript in genau der Form benötigt wird. Daher wollte ich vorher fragen.

Ansonsten hast Du natürlich recht^^

Der Wert der Variable bleibt ja derselbe nur die Anzeige ändert sich.

Sent from my iPhone using Tapatalk

$minPauseDuration - die kürzeste Pausendauer, damit eine Unterbrechung als Pause mitgezählt wird. Diese kurzen 2-Sekunden-Unterbrechungen würde ich nicht dazuzählen, eher die längeren "Einweichpausen etc.

Bei unserem Waschtrockner „spinnt“ das Skript übrigens doch etwas häufiger, da die unterschiedlichen Wasch- und Trockengänge sehr unterschiedliche Durchlaufzeiten und Pausen zur Folge haben. Es gibt zwar gewisse Fallback-Mechanismen im Skript (Maximaldauer etc.) so dass schlimmstenfalls die Fertigmeldung nur ein paar Minuten zu spät kommt, aber wenn man die WaMa gerade ausgeladen hat und dann piept er eine Viertelstunde danach plötzlich rum von wegen „Wäsche fertig“… das ist noch nicht optimal gelöst.

Kann also gut sein, dass es demnächst noch ein Update des Skripts gibt.

Wo es bei mir schon sehr gut funktioniert ist beim Geschirrspüler.

ich würde mich argh freuen wenn es da noch ein update geben würde (:

Wofür denn ein Update ? Bei mir läuft das seit 2 Jahren problemlos. Was funktioniert denn bei dir nicht ?