tomsta13 und Andere ...
Ich glaube, dass ich das Skript funktionstüchtig fertig habe.
Hier ist es, Erläuterungen unten:
// Created by Gerhard Eichelsdörfer alias eiche - 2024-03-13
let Retrigger = true, // Set to false if you don't want the motion to retrigger!
MotionName = "motion",
RemoteName = "remote",
on = null, // output status
th = null, // timer handle
src = null; // source of switching
function send_response(response, body) {
response.code = 200;
response.body = body;
response.send();
}
function switchSet(state) {
Shelly.call("Switch.set", {id:0, on:state},
function(result, errcode, errmsg) {
let msg = "switchSet(" + state + "): ";
if(errcode!==0) msg += "error " + errcode + ", " + errmsg;
else msg += state ? "is on" : "is off";
print(msg);
}
);
}
function startTimer(dur) {
Timer.clear(th);
if(dur>0) th = Timer.set(Math.floor(1000*dur), false, function () {switchSet(false);});
}
HTTPServer.registerEndpoint('on',
function (request, response) {
send_response(response, "OK");
let dur = 0;
if(request.query.length > 0) dur = JSON.parse(request.query);
if(!on) src = MotionName;
if(!isNaN(dur) && dur>0 && (!on || (Retrigger && src===MotionName))) startTimer(dur);
if(!on) {
src = MotionName;
switchSet(true);
}
}
);
Shelly.addEventHandler(function(e) {
//print(JSON.stringify(e));
if(e.info.event==="toggle") {
on = e.info.state;
let st = Shelly.getComponentStatus("switch:0");
//print(JSON.stringify(st));
if(st.source!=="loopback") src = st.source;
if (e.component==="input:0" || st.source==="button" || st.source==="WS_in" || st.source==="SHC" || src===RemoteName) {
print("source: ", src);
Timer.clear(th);
}
}
});
// from a remote toggle call, e.g. from an i4.
function remoteButton() {
let err = "error in remoteButton:";
let st = Shelly.getComponentStatus("switch:0");
if(!st.output || src===MotionName) Shelly.call("Switch.Set", {id:0, on:true},
function (result, errcode, errmsg, err) {
if(errcode!==0) print(err, errcode, ", ", errmsg);
else src = RemoteName;
}, err
);
else Shelly.call("Switch.Toggle", {id:0},
function (result, errcode, errmsg, err) {
if(errcode!==0) print(err, errcode, ", ", errmsg);
else src = RemoteName;
}, err
);
}
//Do some user friendly printout
function messages() {
let ipAddress = Shelly.getComponentConfig("wifi").sta.ip;
if(ipAddress===null) ipAddress = '<IP address of your switching Shelly>';
let myId = Shelly.getCurrentScriptId();
print('The action URL of your motion is: http://' + ipAddress + '/script/' + myId + '/on?<duration in seconds>');
print('When switching via remote command, e.g. Shelly i4, the URL for the remote action is:');
print('http://' + ipAddress + '/rpc/script.eval?id=' + myId + '&code="remoteButton()"');
}
messages();
let info = Shelly.getComponentStatus("switch:0");
//print(JSON.stringify(info));
src = info.source;
on = info.output;
Alles anzeigen
Ich testete es mit einem bereitliegenden Shelly Plus Plug S. Dieser Shelly ähnelt einem Shelly 1PM Mini (Gen 3), da beide u.a. die entnommene Leistung messen. Somit sollte dieses Skript auch auf dem Mini stabil laufen und sich nichts Irritierendes ereignen. Es könnte noch verbessert werden, indem ich den HTTP Endpoint für weitere Parameter ausbaue. Ich bin aber nun froh, dass es so funktioniert.
Es berücksichtigt verschiedene Quellen des Schaltens - Schalter/Taster am Eingang, den Button am Plus Plug S, das Klick Button Symbol in der Web UI, per Cloud und damit auch per Sprachassistent sowie insbesondere das Schalten per Shelly i4 ...
Entscheidend ist die Unterscheidung zwischen Bewegungsmelder(Motion)-Aktionen und anderen Aktionen.
Alle Quellen, die den Verbraucher (Lampe) für eine gewisse Dauer einschalten (wie Motions), müssen folgenden URL nutzen:
http://<IP address of your switching Shelly>/script/<script id>/on?<duration in seconds>
Alle Quellen ohne eine solche Dauer müssen diesen URL verwenden:
http://<IP address of your switching Shelly>/rpc/script.eval?id=<script id>&code="remoteButton()"
Beide URL werden mit dem Skriptstart im Protokollfenster ausgegeben, um den Anwender ein wenig zu unterstützen - eine Idee von ostfriese.
Die Funktion zu letzterem habe ich "remoteButton()" genannt, weil das Skript deren Aufruf wie einen Umschalte-Button behandelt, d.h. mit jedem Auftrag an diese Funktion wird ein Umschalteauftrag abgearbeitet.
Ist eine Dauer (hier Timer) aktiv und remoteButton() wird aufgerufen, dann wird diese Dauer gelöscht, wodurch der Verbraucher eingeschaltet bleibt, bis remoteButton() erneut aufgerufen wird. oder mit einer anderen, nicht Dauer behafteter Quelle ausgeschaltet wird. Somit ist der Anwender sehr flexibel in der Nutzung einer bspw. manuell bedienbarer Schaltquelle - auch ein Shelly Button 1 ist dazu geeignet.
Dauer behaftetes Triggern ist per Variable "Retrigger = true;" auf Retriggerung eingestellt, d.h. die Dauer beginnt mit jedem Dauer behafteten URL von vorne. Will man das nicht, setzt man schlicht Retrigger auf false.
Einige print() Aufrufe sind enthalten, mache auskommentiert und wenige aktiv. So lässt sich eine evtl. erforderliche Fehlersuche leichter durchführen.
Ich glaube aber, dass dieses Skript fehlerfrei arbeitet.
Für weitere Fragen stehe ich zur Verfügung. Viel Freude damit!
Edit:
Der Vollständigkeit wegen sei noch einmal erwähnt, dass die Konfigurationsabfrage in der Firmware 1.2.3 bei Nutzung von DHCP keine IP-Adresse liefert, sondern nur einen null Wert. Dies beeinträchtigt aber nicht die Funktion des Skripts.