Frage zu: Script soll letztes FS20 Telegramm wiederholen

Ich würde gerne - „zur Sicherheit“ und weil einige meiner Sender ausserhalb der Reichweite des Empfägers aber in Reichweite der FHZ sitzen - jedesmal, wenn ein Sensor einen Funkbefehl ausgesendet hat, den (dann neuen Soll-) Status eines Empfängers nochmals aussenden.

Grundsätzlich arbeite ich bei den Sendern mit denselben FS20 Adressen wie die Empfänger haben (also keine Adress-Umsetzung), um ein (wenn auch nur teilweise) Funktionieren des Systems bei ausgeschalteter FHZ zu gewährleisten.

Hierzu hatte ich eigentlich vor, mit dem Event des Statusvariablen-Updates (damit auch ein „aus-Befehl“ bei einem bereits ausgeschalteten Empfänger trotzdem nochmals geschickt wird) zu arbeiten.

Zwei Fragen:
1.) kriege ich (oder ich stell mich zu blöd an) den aktuellen (Soll)Wert nicht ausgelesen, sondern nur den Wert vor dem Event, also mir gibt in meinem Script (s.u.)

$InvokerVarValNow = GetValue($InvokerVarOID);

denselben Wert wie $IPS_VALUE, bei einem eingehenden Toggle sollten die aber doch gerade umgedreht sein?

2.) Wie kann ich verhindern, in eine endlos-Loop z ulaufen, wenn ich den Befehl dann nochmals aussende? Denn dannwird ja die Variable (im Moment die Statusvariable) wieder upgedatet und das Script würde wieder aufgerufen werden …

1000 Dank!


$ScriptOID = $IPS_SELF; // OID of this script
$InvokerType = $IPS_SENDER; // What type invoked the script i.e. "Variable" / "TimerEvent"
$InvokerEventOID = $IPS_EVENT; // OID of the event that invoked the script

$InvokerVarOID = -1; // OID of the variable that invoked the script
$InvokerVarOID = $IPS_VARIABLE;

$InvokerVarValPre = NULL; // Value of the variable BEFORE the Event
$InvokerVarValPre = $IPS_VALUE;

$InvokerEvent = IPS_GetEvent( $InvokerEventOID); // Infos about the event
$InvokerVarValNow = GetValue($InvokerVarOID); // Value of the variable actual (AFTER the Event)
$InvokerVarValTxt = "FALSE";
if( $InvokerVarValNow ) $InvokerVarValTxt = "TRUE";


$InvokerInstanceName = IPS_GetName($InvokerInstanceOID);

$ScrIdentifier = "GlobalScript [" . $ScriptOID . "]";
$ScrMessage = "RX ID: [". $InvokerEventOID . "]" . "   EVT: [" . $InvokerType .
					"]  EVT-INST: [" . $InvokerInstanceName .
					"] VAR: [" . $InvokerVarOID . "] VAL: [" . $InvokerVarValTxt .
					"] IPS-TRG: [" . $IPS_TRIGGER . "]";


// BEEP TO SEND AUDIO ACKNOWLEDGE
//IPS_ExecuteEx( "D:/IPS/01 externals/ton.exe", "3500", false, false,1 );

// PROTOCOL TO LOGFILE
IPS_LogMessage( $ScrIdentifier, $ScrMessage);
IPS_LogMessage("state" , $InvokerVarValTxt);



Ich habe eben gerade etwas Zeit gehabt und mal eine Minute drüber nachgedacht. Meine einfache Lösung: Wird eine Variablenaktualisierung registriert, wird ein Timer auf eine Sekunde gesetzt. Kommen innerhalb der Sekunde weitere Werte, wird der Timer immer wieder auf eine Sekunde gesetzt. So werden Mehrfache Tastendrücke abgefangen. Bei Ablauf des Timers wird der aktuelle Variablenwert erneut gesendet und das wiederholte Aussenden für eine Sekunde gesperrt, sodass der Skriptaufruf durch Wiederholung nicht zur erneuten Wiederholung führt. Wer so unentschlossen ist, hat halt Pech gehabt, im Normalfall sollte die Wiederholung so aber funktionieren. Die Wiederholungs-Timer für die Variablen legt das Skript unterhalb von sich selbst an, d.h. die einzelnen Instanzen werden nicht vollgemüllt.
Das Skript musst Du jetzt nur noch für alle gewünschten Variablen auf Variablenaktualisierung registrieren. Wie Du das per Massenverarbeitungsskript machen kannst, weißt Du ja mittlerweile.
Das ganze ist jetzt spontan runtergeschrieben und nicht getestet, aber an sich sollte ich so ein einfaches Skript noch fehlerfrei runtertippen können ;).

<?php
    if ($IPS_SENDER == "TimerEvent") { $IPS_VARIABLE = intval(substr(IPS_GetName($IPS_EVENT), -5)); }
    
    if (IPS_SemaphoreEnter("RepeatSemaphore" + $IPS_VARIABLE, 1000))
    {
        if ($IPS_SENDER == "Variable")
        {
            SetTimerByName($IPS_SELF, "RepeatTimer" + $IPS_VARIABLE, time() + 1);
        }
        else if ($IPS_SENDER == "TimerEvent")
        {
            SetTimerByName($IPS_SELF, "RepeatTimer" + $IPS_VARIABLE, 0);
            
            $instanceID = IPS_GetObject($IPS_VARIABLE);
            $instanceID = $instanceID['ParentID'];
            FS20_SwitchMode($instanceID, GetValueBoolean(CreateVariableByName($IPS_SELF, "RepeatValue" + $IPS_VARIABLE, 0, "")));
            
            IPS_Sleep(1000);
        }
        
        IPS_SemaphoreLeave("RepeatSemaphore" + $IPS_VARIABLE);
    }

    function CreateVariableByName($id, $name, $type, $profile = "")
    {
        global $IPS_SELF;
        $vid = @IPS_GetVariableIDByName($name, $id);
        if($vid === false)
        {
            $vid = IPS_CreateVariable($type);
            IPS_SetParent($vid, $id);
            IPS_SetName($vid, $name);
            IPS_SetInfo($vid, "this variable was created by script #$IPS_SELF");
            if($profile !== "") { IPS_SetVariableCustomProfile($vid, $profile); }
        }
        return $vid;
    }

    function SetTimerByName($id, $name, $ts)
    {
        global $IPS_SELF;
        $eid = @IPS_GetEventIDByName($name, $id);
        if($eid === false)
        {
            $eid = IPS_CreateEvent(1); 
            IPS_SetParent($eid, $id);
            IPS_SetName($eid, $name);
            IPS_SetInfo($eid, "this timer was created by script #$IPS_SELF");
            IPS_SetEventScript($eid, $id);
        }
        IPS_SetEventCyclic($eid, 0, 0, 0, 0, 0, 0);
        IPS_SetEventCyclicTimeBounds($eid, $ts, 0);
        IPS_SetEventActive($eid, ($ts > 0));
        return $eid;
    }
?>

Uiii! ein bischen Zeit gehabt … klasse.
Ich werde wohl morgen abend oder Sonntag wieder an IPS kommen (wenn man da mal anfängt, sind gleich Stunden weg).

Dann sehe ich mir Deinen Vorschlag mal an und werde mich melden.

Ich habe gerade an einer ähnlichen Variante gearbeitet, aber ich will da mit 100-ms Schritten vorgehen können, weil ich dann weniger verpasse. Im Prinzip denke ich daran, die endlos-Schleife ruhig laufen zu lassen, aber keinen Sendebefehl auszusnden, wenn eine eigene Zeitvariable (pro FS20 Instanz) nich um X ms zurückliegt.

Mal schaun, was besser funzt. Werde berichten …

Danke nochmals!
jwka

Bei meiner Variante sind die 1000ms bei Semaphore und Sleep nur so drin, weil ich nicht mehr weiß, wie viel Zeit FS20 zum Senden braucht. Kannst Du dementsprechend auch runter setzen.

Also, habe mal getestet und getüftelt.

Dein Script hat bei mir irgendwie immer wieder das Gegenteil getoggelt, also Schalter drücken - Licht an, nach kurzer Zeit (ca. 1 Sek) Licht wieder aus.

Aber: Dein Script hat mir den Weg geleuchtet. Wo ich selbst noch am rumbasteln war, um mir eine eigene Semaphore zu bauen (die dann aber gleichzeitig ein „Last Run mit Parameter [ID]“ gespeichert hätte, hat mich Dein Script auf die Idee gebracht, die IPS Semaphoren zu benutzen.

Über eine empfindliche Schwachstelle dieser Methode (wiederholen des Sendebefehls) bin ich per Zufall gestossen, weil ich zwei Geräte unterschiedlichen Namens mit derselben FS20 Adresse belegt hatte (was ja vorkommen kann).

Sind diese beiden Geräte „out of synch“, dann toggeln sie sich gegenseitig und es kommt zu sehr seltsamem Verhalten.

Besonders dramatisch ist das bei den Steckdosen. Irgendwie sind die wohl schneller (oder langsamer) als andere Emfänger, jedenfalls ergab sich da ein „Stottereffekt“.

Hier mal mein Script in Version 0.1. Lauffähig (ein paar -zig mal getestet) aber noch nicht ganz fertig, weil ich im Moment nur die Statusvariable behandle und nicht den Fall abprüfe, ob Intensity oder Data gegsetzt wurde.

Mal sehen, wann ich dazu komme, das noch einzubauen.


<?
/*******************************************************************************
GLOBAL SCRIPT:
SHOULD BE INVOKED ON USE OF ANY FS20 SYSTEM COMPONENT
SENDS OUT LAST STATUS AGAIN
!! BE SURE TO HAVE THE EVENTS SET ACCORDINGLY !!
!! PROBLEMS CAN OCCUR IF TWO DEVICES SHARE THE SAME FS20 ADRESS!!
!! IF OUT OF SYNCH THE WILL TOGGLE EACH OTHER WITH ODD RESULTS!!

Version: 0.1
Author: JW
Last Edit: 2009-10-04

Caveats:
At the moment, this script does ONLY adress the TOGGLE and ON/OFF commands of s sender
NOT COVERED YET:
- Timer Events
- Dimm telegrams
- Key pressed long (Data =18)

*******************************************************************************/
// SIMULATE EVENT VARIABLE IF RUN BY USER FROM CONSOLE - FOR TEST PURPOSE
if ($IPS_SENDER == "Execute") {
	echo "Running from Execute";
	$IPS_VARIABLE = 36112 /*[9-Test\zzFS20-TestGerät 4CH - 2331\Status]*/;
	$IPS_SENDER = "Variable";
};

// ############# ADJUST HERE IF NECESARY ####################
// Time the 2nd instance of this script will wait and try again - in an ideal world
// where the FS20 telegram would need no time), this would be at least 1 less than RunSleep
// to ensure that it waits less time than the 1st instance would take to finish it's work
// In the real world, we need to have a difference of some ms to ensure the send of a telegram
// will be thru before we release the semaphore
// !! This "base time" will need to be increased when Repeaters are in the air !!
$RunSemaphoreTimeOut = 200;

// Have a break until the FS20 telegram will be sent.
// Add time here if Repeaters are installed
$RunSleep = $RunSemaphoreTimeOut + 300;

// Time the 2nd instance of this script will wait until the report is written to the logfile
// This is just to make sure the report of the 2nd (timed out) script will be before the 1st one
$RunSleepTimeout = $RunSleep + 50;
// ############# ADJUST HERE END ####################

/*******************************************************************************
// EVENT - SCRIPT DO NOT CHANGE AFTER THIS LINE
*******************************************************************************/
$ScriptID = $IPS_SELF; // ID of this script
$ScriptName = IPS_GetName($ScriptID);

$InvokerEventID = $IPS_EVENT; // ID of the event that invoked the script
$InvokerType = $IPS_SENDER; // What type invoked the script i.e. "Variable" / "TimerEvent"

$InvokerVarID = $IPS_VARIABLE; // ID of the variable that invoked the script
$InvokerVarVal = $IPS_VALUE; // Value of the variable when event is triggered (after change)

/* ALTERNATIVE USE vs. IPS_TRIGGER ???
$InvokerEvent = IPS_GetEvent( $InvokerEventID); // Infos about the event
$InvokerTrigger = $InvokerEvent["TriggerType"];

switch ($InvokerTrigger){
	case 0: $InvokerTriggerTx = "Update" ; break;
	case 1: $InvokerTriggerTx = "Change" ; break;
	case 2: $InvokerTriggerTx = "Threshold undercut" ; break;
	case 3: $InvokerTriggerTx = "Threshold overstep" ; break;
	case 4: $InvokerTriggerTx = "Threshold match" ; break;
}
*/

// TO BE COMPLETED HERE FOR OTHER INVOKER TYPES MAYBE DIFFERENT THINGS TO DO
// SAME MAY APPLY FOR THE RUNSEND BELOW
// IN THIS SCRIPT; WE NEED TO ADDRESS ALSO IF THE DATA-VAR IS SET TO 18 IF KEY IS PRESSED **LONG**
switch ($InvokerType){
	case "Variable":
		$InvokerVarValTxt = "FALSE";
		if( $InvokerVarVal ) $InvokerVarValTxt = "TRUE";
		if (true) {
		$InvokerVarInfo = IPS_GetObject($InvokerVarID);
		$InvokerInstanceID = $InvokerVarInfo["ParentID"];
		}
		break;
}

$InvokerInstanceName = IPS_GetName($InvokerInstanceID);

// NOW TRY TO DO THE JOB OR WAIT-EXIT
$RunSemaphoreName = "RepeatSemaphore" + $InvokerVarID; // Use a separate semaphore per ID that triggers this script
$RunSend = IPS_SemaphoreEnter($RunSemaphoreName, $RunSemaphoreTimeOut); // Check if no other script instance currently runs on same event

if ($RunSend) {
	FS20_SwitchMode($InvokerInstanceID, $IPS_VALUE);
   IPS_Sleep($RunSleep);
   IPS_SemaphoreLeave($RunSemaphoreName);
	$RunMessage = "  REPEATED Telegram";
}
else {
	$RunMessage = "  NO SEND - semaphore timed out";
   IPS_Sleep($RunSleepTimeout);
}

$ScrIdentifier = "SCR " . $ScriptName . "[" . $ScriptID . "]";
$ScrMessage = "E-ID: [". $InvokerEventID . "]" . "   E-TYPE: [" . $InvokerType .
					"]   E-INST: [" . $InvokerInstanceName .
					"] MON: [" . $InvokerVarID . "] VAL: [" . $InvokerVarValTxt .
					"] IPS-TRG: [" . $IPS_TRIGGER . "]  *** " . $RunMessage . "***";
					
// PROTOCOL TO LOGFILE
IPS_LogMessage( $ScrIdentifier, $ScrMessage);

// BEEP TO SEND AUDIO ACKNOWLEDGE
IPS_ExecuteEx( "D:/IPS/01 externals/ton.exe", "3500", false, false,1 );

?>