ShutterControl während Fahrt unterbrechen

Nabend!
Ich suche jetzt schon etwas länger und habe auch 1-2 vermutlich nicht funktionierende Konzepte im Kopf:
wie kann man ein Rollo stoppen, welches durch den ShutterControl an eine bestimmte Position gefahren wird?
Szenario während: Druck auf Taster fährt das Rollo normalerweise ganz zu. Nach x% drückt man nocheinmal, Rollo hält an. Das ist ja kein Problem…aber wie erklärt man der ShutterControl, dass es sich diese Position merken soll?

Hintergrund: EnOcean-System mit Eltako FSB12. Die Shuttercontrol läuft, die Taster setzen bisher einfach Position 100 oder 0.

Hallo,

auch wenn schon ein wenig her, habe hier (http://www.ip-symcon.de/forum/f50/shuttercontrol-hm-lc-sw4-pcb-moeglich-16129/index2.html#post139386) das gleiche Problem bzw. die gleiche Anfrage. Allerdings mit Homematic.

Hast Du da schon eine (zwischenzeitliche) Lösung?

Nein, noch nicht wirklich, aber eine Idee. Es ist möglich, per Skript auf die gespeicherten Laufzeiten zuzugreifen. Anschliessend kann man bei erneutem Drücken der Taste mit PHP auswerten, wie weit das Teil gefahren ist, den neuen Ist-Zustand schreiben und den Rolladen anhalten.

Es liesse sich auch so anpassen, dass man bis zum nächsten anzeigbaren Wert der Variable fährt (z.B. 25%).

Das wird wohl die nächste grössere Baustelle, die ich in der Hütte bearbeite.

Ich gehe das Problem derzeit an und ich verspreche, dass es etwas umständlicher ist als gedacht. Aber es funktioniert soweit. Die Skripts zeige ich, wenn ich das aufgeräumt habe. Ausserdem fürchte ich, dass es ein wenig Erklärungsbedarf hat, wenn ihr das Zeug einsetzen möchtet.

Grundsätzlich funktioniert es so, dass man die Anforderungen (SC_Move()) nicht direkt an die SC sendet, sondern an ein Script, welches alle Rahmenbedingen prüfen kann (darf es überhaupt fahren? Wenn ja, wohin? Fährt es bereits, soll man es anhalten?). Über zur Laufzeit erstellte Variablen wird der Zustand über die Laufzeit hinaus aufrecht erhalten. Zur Steuerung setze ich Timer ein, sonst hat man keine Chance, die Fahrt zu unterbrechen.
Ich denke, das löst auch das ein oder andere weitere Problem, welches in Bezug auf die Shutter erwähnt wurde (z.B. Abfahrverhinderung mit Nicht-Aktualisieren des Status).

Ich melde mich, wenns fertig ist.

Soo, es ist vollbracht. Ich werde versuchen, die Lösung hier zu präsentieren.
Dazu werden aber ein paar Anmerkungen notwendig sein, weil unsere IPS-Umgebung vermutlich ein wenig speziell ist.

Grundsätzlich ist die Lösung in 2 Skripts versteckt: eines stellt ein PHP-Objekt bereit, welches quasi das Umfeld einer ShutterControl-Instanz darstellt und bequem alle möglichen Eigenschaften und Methoden bereitstellt. Vorteil ist die gute Lesbarkeit und Strukturierbarkeit von objektorientierten Ansätzen und auch die gute Zugänglichkeit von Daten bei der Verwendung in eigenen Skripts.

Ziel ist, dass ein Shutter bei Druck auf die obere Taste z.B. nach ganz oben fahren will, aber bei einem Druck auf die andere Taste während der Fahrt der Shutter an der nächsten der Standardposition anhält.

Zuerst gibts mal die Objektdefinition, die bei mir in einem Skript steht, welches vom 2.5er autoload automatisch eingebunden wird und daher immer zur Verfügung steht:


<?
// Class definition for a shutter actuator
class actuator_shutter
// Erweitern um den zugehörigen Antrieb und dessen daten
// Am Ende erweitern um transiente Info (gerade in Betrieb, aktuelle Fahrrichtung, Fahrzeit)(ermöglicht die Überprüfung beim wiederholten Drücken des Knopfes)

{
	private $instance_id = 0;							// ID der IPS-Instanz
	public $actuator_id = 0;
	public $interface_id = 0;
	public $position = false;							// Shutter Control StatusVariable Position
	public $name = "";								// Shutter Control Name
	public $state = "unknown";							// Shutter Actuator active?
	public $addEndTime = 5;								// SC adds 5s when going to end position
	public $driveTiming;

	// These properties are only relevant when shutter is moving:
	public $direction = false;							// Shutter moving direction (true: up; false: down)

	// These properties are only relevant when override is set:
	public $override = false;							// true, wenn override aktiv

	// Constructor erstellen, der Daten füllt
	function __construct() {
		if (func_num_args() === 1) {
			$this->instance_id = func_get_arg(0);
			$this->actuator_id = SC_GetTransmitDevice($this->instance_id);
			$this->interface_id = SC_GetHandlerScript($this->instance_id);
			$this->getProps();
   		}
	   	else {
	   	   throw new Exception('Only one argument allowed!');
	   	}
	}

// Private functions
	private function getProps() {
		$this->name = IPS_GetName($this->instance_id);
		$this->position = GetValue(IPS_GetStatusVariableID($this->instance_id, "StatusVariable"));

		$event_id = @IPS_GetEventIDByName("StopTimer", $this->actuator_id);
		if ($event_id !== false) {
			$this->state = "moving";
		}
		else {
			$this->state = "idle";
		}

		$this->direction = GetValue(IPS_GetStatusVariableID($this->actuator_id, "StatusVariable"));
		$this->getDriveTiming($this->direction);				// Get drive timings of current direction

		$override_id = @IPS_GetVariableIDByName("Override", $this->actuator_id);
		if ($override_id !== false) {						// Override state hat highest priority
			$this->state = "override";
		}
	}

	private function getDriveTiming($direction) {
		$info = IPS_GetVariableProfile("~ShutterAssociation");			// Select the position definition used in this environment (may be changed by user)
		if ($direction) {							// driving up
			$timing = SC_GetDriveUpTimings($this->instance_id);
			foreach ($info['Associations'] as $value) {
				$pos = $value['Value'];
				switch ($pos) {
					case 100: $timings[$pos] = 0; break;
					case 99: $timings[$pos] = $timing['Down']; break;
					case 0: $timings[$pos] = $timing['Open'] + $this->addEndTime; break;
					default:
						if ($pos <= 50) {
							$timings[$pos] = $pos / 50 * ($timing['Half'] - $timing['Open']) + $timing['Open'];
						}
						else {

							$timings[$pos] = ($pos / 50 - 1) * ($timing['Down'] - $timing['Half']) + $timing['Half'];
						}
						break;
				}
			}
		}
		else {									// driving down
			$timing = SC_GetDriveDownTimings($this->instance_id);
			foreach ($info['Associations'] as $value) {
				$pos = $value['Value'];
				switch ($pos) {
					case 99: $timings[$pos] = $timing['Down']; break;
					case 100: $timings[$pos] = $timing['Close'] + $this->addEndTime; break;
					default:
						if ($pos <= 50) {
							$timings[$pos] = $pos / 50 * $timing['Half'];
						}
						else {
							$timings[$pos] = ($pos / 50 - 1) * ($timing['Down'] - $timing['Half']) + $timing['Half'];
						}
						break;
				}
			}
		}
		$this->driveTiming = $timings;						// Save as property
	}

	private function setStopTimer($target_time) {
	// Funktion erstellt oder aktualisiert den Anhaltetimer

		$id_timer = @IPS_GetEventIDByName("StopTimer", $this->actuator_id);	// Search for an event named StopTimer on the actuator
		if ($id_timer === false) {						// Neuen Timer anlegen
			$id_timer = IPS_CreateEvent(1);					// Create new cyclic event
			IPS_SetName($id_timer, "StopTimer");				// Set new name - always the same
			IPS_SetParent($id_timer, $this->actuator_id);			// Attach the event to the corresponding actuator
			IPS_SetEventCyclic($id_timer, 0, 0, 0, 0, 0, 0);		// Run single event
			IPS_SetEventScript($id_timer, "IPS_RunScriptEx($this->interface_id, array(\"INSTANCE\" => $this->instance_id));");
			IPS_SetEventActive($id_timer, true);				// Activate the timer.
		}
		IPS_SetEventCyclicTimeBounds($id_timer, $target_time, 0);		// Set time some seconds away from now
	}
	public function clearStopTimer() {						// Called by interface script
		$id_timer = @IPS_GetEventIDByName("StopTimer", $this->actuator_id);
		IPS_DeleteEvent($id_timer);
		$override_id = @IPS_GetVariableIDByName("Override", $this->actuator_id);// 
		if ($override_id !== false) { IPS_DeleteVariable($override_id); }
	}

	private function startShutter($position) {
		// Check the validity of target position and direction
		if ($position == $this->position) { return true; }			// nothing to do.
		SC_Move($this->instance_id, $position);					// Erlaubnis, die SC zu triggern.

		if ($position < $this->position) { $newDirection = true; }		// up
		else { $newDirection = false; }						// down
		
		$this->getDriveTiming($newDirection);					// Read the corresponding timing from SC instance
		$runtime = time() + $this->driveTiming[$position] - $this->driveTiming[$this->position];			// Fahrzeit zu "jetzt" addieren
		$this->setStopTimer($runtime);
		return true;
	}

	private function interruptShutter($position) {
		// Aktuelle Position bestimmen, Timer updaten, override aktivieren und SC aufrufen.
		$event_id = @IPS_GetEventIDByName("StopTimer", $this->actuator_id);
		if ($event_id !== false && $position != $this->position) {		// Wenns fährt und nicht dahin, wo wir schon hinfahren
			$targetPos = $this->position;					// Set current target as initial destination
			$info = IPS_getEvent($event_id);
			$stopduration = $info['CyclicTimeFrom'] - time();		// Duration until stop event
			
			$targetTime = $this->driveTiming[$this->position];		// Initial duration to target
			asort($this->driveTiming);
				
			reset($this->driveTiming);					// Set pointer to the beginning...jedenfalls fürs runterfahren..
			while (1) {
				$test = current($this->driveTiming);
				$compare = ($targetTime - $test +1) - $stopduration;	// +1 ist Toleranz zum durchfahren...
				if ($compare < 0) {
					$targetPos = key($this->driveTiming);
					break;
				}

				if (!next($this->driveTiming)) { break; }		// Stop if all positions checked.
			}
			if ($targetPos != $this->position) {
				$override_id = IPS_CreateVariable(0);			// Create Boolean override signal
				IPS_SetName($override_id, "Override");			// Set the name...
				IPS_SetParent($override_id, $this->actuator_id);	// Set the hierarchy...
				SetValueBoolean($override_id, true);			// And set true.

				$this->setStopTimer(time() + round(abs($compare)) + 1);	// Update the timer

				SC_move($this->instance_id, $targetPos);		// Call the SC instance
			}
		}
		return true;
	}

// Public functions
	function move($position) {							// No comment
		switch ($this->state) {
			case "idle": $this->startShutter($position); break;		// Langeweile -> abfahren.
			case "moving": $this->interruptShutter($position); break;	// Oha, schnell anhalten.
			case "override": break;						// Da machen wir schön garnix.
		}
		return true;
	}
}

function moveShutter($id, $position) {
	$myShutter = new actuator_shutter($id);
	$myShutter->move($position);
	return true;
}
?>

In eurem Variablenevent oder einer Standardaktion muss nur noch die Funktion moveShutter(id, zielposition) aufgerufen werden und los gehts.

Der entsprechende Interface-Skript vereinfacht sich ein wenig. Ich habe es abgespeckt auf den Einsatz von EnOcean. Die anderen Systeme lassen sich aber vermutlich recht easy wieder hinzufügen. Wie bei jeder Timer-basierten Lösung kann das Skript von zwei Dingen gestartet werden: RunScript und ShutterControl.


<?

//Variables provided by ShutterControl Module
//IPS_LogMessage("InstanceID", $_IPS['INSTANCE']); /* InstanceID of the actuator! */
//IPS_LogMessage("Direction", $_IPS['DIRECTION']); /* {0..2} Stop, Up, Down */
//IPS_LogMessage("Duration", $_IPS['DURATION']); /* ms */

//Modified By Markus Loeben 2010
//Modified by Tobias Schluer, 2011 for local EnOcean Equipment
//Modified by Tobias Schluer, 2012 for new features (event driven and interrupt)

switch ($_IPS['SENDER']) {
	case "ShutterControl":
		define("SC_DIRECTION_STOP", 0);
		define("SC_DIRECTION_UP", 1);
		define("SC_DIRECTION_DOWN", 2);

		$info = IPS_GetInstance($_IPS['INSTANCE']);
		$direction = GetValue(IPS_GetStatusVariableID($_IPS['INSTANCE'], "StatusVariable"));
		$override = @IPS_GetVariableIDByName("Override", $_IPS['INSTANCE']);
		if ($override !== false) { break; }                                  		// Abbrechen, wenn es einen Override hat
			switch($info['ModuleInfo']['ModuleID']) {
	     		case ("{8492CEAF-ED62-4634-8A2F-B09A7CEDDE5B}" || "{FD46DA33-724B-489E-A931-C00BFD0166C9}"): //EnOcean RCM100 || Eltako Switch
					switch($_IPS['DIRECTION']) {                                      // Wunschrichtung der SC
						case SC_DIRECTION_UP:
							ENO_SwitchMode($_IPS['INSTANCE'], true);							// Taster "oben" simulieren für Öffnen
							break;
						case SC_DIRECTION_DOWN:
							ENO_SwitchMode($_IPS['INSTANCE'], false);                  	// Taster "unten" simulieren für Schliessen
							break;
						case SC_DIRECTION_STOP:
							ENO_SwitchMode($_IPS['INSTANCE'], $direction);       			// Anhalten Taster in gleiche Richtung
							break;
      			}
      			break;
				default: die("No supported module used.");
			}
		break;
	case "RunScript":
		$mySC = new actuator_shutter($_IPS['INSTANCE']);
		$info = IPS_GetInstance($mySC->actuator_id);
		switch($info['ModuleInfo']['ModuleID']) {
     		case ("{8492CEAF-ED62-4634-8A2F-B09A7CEDDE5B}" || "{FD46DA33-724B-489E-A931-C00BFD0166C9}"): //EnOcean RCM100 || Eltako Switch
		      ENO_SwitchMode($mySC->actuator_id, $mySC->direction);                //FSA12 stoppen bei gleicher Richtung
				$mySC->clearStopTimer();
				break;
		}
	   break;
	default: die("Script may only be called by ShutterControl or RunScript!");
}
?>

Probierts mal ein wenig aus. Bei Bedarf helfe ich gerne weiter. Bin grad schreibfaul.

Grüsse,
Tobias

PS: grundsätzlich wäre es natürlich geil, wenn die ShutterControl-Instanz diese Funktionalität direkt implementieren würde…die Möglichkeiten wären vermutlich alle vorhanden.

Am Wochenende konnte ich ein paar Tests mit der neuen Steuerungen machen. Dabei fiel eine zu kurze Fahrzeit beim Interrupt auf.

In der Funktion interruptShutter() wurde eine Zeile angepasst:


$this->setStopTimer(time() + round(abs($compare)) + 1);	// Update the timer

Hallo Schluer,

ich bin erst vor relativ kurzer Zeit neu zu IPS gestossen. Deshalb möchte ich darum bitten meine Unerfahrenheit zu entschuldigen. Eines meiner Hauptanliegen ist die zentrale Steuerung meiner Rolläden mit Hilfe von IPS. Da ich im Haus eine vollständig eigenkreierte Ansteuerung der Rolläden implementiert habe, habe ich mich zunächst damit beschäftigt diese Hardwaremäßig anzubinden. Das ist mir mittlerweile gelungen und mit einem modifiziertem Shutter Script kann ich meine Rolläden zum Öffnen und Schließen bewegen. Alles gut soweit. Nur leider ist es auch aus meiner Sicht so, dass dem Standard Script wesentliche Möglichkeiten fehlen.
Die offensichtlich aber durch deine Lösung nachgereicht werden. Eine deutliche Aufwertung also…
Da du ja geschrieben hast bei Bedarf gerne weiterzuhelfen… nun ja der ist da… :slight_smile:

Einige Fragen hätte ich da schon…

  • Erstens das mit dem Autoload deiner Klasse. Leider konnte ich in der Doku nichts dazu finden. Könntest du die notwendigen Schritte näher erleutern, wie ich die Klasse in IPS integriere… oder falls ich etwas in der Doku übersehen habe, mich darauf stoßen…

  • Zweitens, ich kann gerade nicht erkennen, wie in deinem Ersatz Shutter Script (das zweite also…) im Case Fall ‚ShutterControl‘ bezug auf deine Klasse genommen wird. Weil du ja im Gegensatz zu ‚RunScript‘ ein neues Objekt, deiner Klasse instanziirst.
    Dazu gleich noch eine Anfänger Frage… Müssten deine Objekte nicht persistent am Anfang einmal für jeden Rolladen angelegt werden? Das heißt eine Variable in Symcon angelegt werden um die Referenz ID zu speichern…?

…ich weiß, ich schwimme gerade ganz gewaltig…

Lieben Dank…

Grüße

Ralf

Hallo,

kann mir jemand sagen was das für eine Funktion ist

SC_GetTransmitDevice

Gruß

Ich habe leider gerade keine Möglichkeit, das Script mit dem aktuellsten IPS zu testen…sehe aber keinen Grund, wieso es nicht gehen sollte. Den Part muss ich also euch überlassen.

Zu den übrigen Fragen werde ich heute abend eine Antwort posten…nach der Arbeit. :slight_smile:

Hi,

ok passt … dann warten ich :slight_smile:

Gruß

Guten Abend,

ich hoffe, ich habe alle offenen Fragen überblickt.

  1. Autoload: im Skripte-Ordner eine Datei namens „__autoload.php“ anlegen. Diese wird beim Start von IPS ausgeführt. Darin kannst du allerlei bunte Dinge machen, unter anderem das hier:
    require_once(„actuator_shutter.php“);
    Darin ist bei mir die Klasse „actuator_shutter“ definiert.

  2. Solange sich die Methoden des ShutterControl-Moduls nicht geändert haben, sollte das Skript funktionieren. Die private Variable instance_id ist die ID der ShutterControl, die du bewegen möchtest.
    SC_GetTransmitDevice() ist m.W.n. der im ShutterControl hinterlegte Aktor, der den Rolladen steuert.

Die ShutterControl ruft dann ihrerseits das ShutterInterface auf, um den Motor anzusteuern. Das ist das zweite Skript, welches aber keinen Autoload benötigt.

Wenn, dann sind nur Anpassungen am Shutter Interface notwendig, weil du wohl kein EnOcean verwendest.

Liebe Grüsse,
Tobi

Hi,

danke erstmal für die Beschreibung. Mittlerweile weiß ich wie man mit dem autload um dann automatisch beim start die Objekte instanziert werden.

Die Funktion SC_GetTransmitDevice, ist glaub ich dann die SC_Move, aber genau kann ich das nicht sagen.
Ich weiß das man mit SC_Move den Aktor steuern kann, ob das dann der Ersatz wäre weiß ich nicht genau, müsste man dann prüfen.

Die entsprechende Hersteller spezifischen Ansteuerung ist kein Problem. Das habe ich auch schon anderweitig mal umgesetzt.

Aber ansonsten von der Funktionsweise, wäre es dann so wenn ich das verstanden habe, das du das ganze Event basiert aufgebaut hast, man auch einen Stopp setzen kann und entsprechend auch die Positionswert festgehalten wird und man auch von einem Taster aus das ganze bedienen kann, sprich einmal über das WebFront anstoßen und auch über einen Taster.

Gruß
MAcToolz

Nein, die Funktionen SC_GetTransmitDevice() und SC_Move() haben nichts miteinander zu tun. Die erste gibt die hinterlegte ID des Aktors zurück, die zweite gibt den Startbefehl an die ShutterControl.

Ja, den ganzen Aufwand habe ich nur deshalb getrieben, damit man mit einem Taster die Fahrt stoppen kann UND etwas sinnvolles in der Visualisierung angezeigt wird. Daher kann man nur an den vorher definierten Positionen anhalten.

Das Problem ist mit dem neuen Shutter Control gelöst (kommt mit dem nächsten 5.0 Update), welches dann interne Timer nutzt und auch die Position bei manueller Betätigung nachführen kann. Ihr könnt also einfach auf der Shutter Instanz ein Stop ausführen und das Shutter Control merkt dies.

paresy

kurze Frage:
hat sich anstonsten noch etwas am Shutter-Ctrl getan?

Gäbe es vielleicht die Möglichkeit, das nicht bei 99% geschlossen ist, sondern bei z.B. 90%?

Was hätte das für einen Vorteil?

paresy

Das hätte (zumindest für mich) den Vorteil, das z.B. die Rolladen so gesteuert werden können, das nur die oberen Lamellen zum lüften geöffnet bleiben und nicht alle.

Analog dem GIF:
rolladen_33.gif

Moin,

ab welcher Version ist die Änderung denn drin? 5.0 oder 5.1?

In der Doku Steht noch:
Beschreibung

Stoppt einen Bewegungsvorgang für das Gerät mit der ID InstanzID.

Die Statusvariable wird auf -1 zurückgesetzt.

Ups ist wollte dich Paresy doch zitieren… :slight_smile:

Dies gibt es seit der 5.0. Wichtig ist, dass du das neue Shutter Control und nicht das (Legacy) verwendest.

paresy