Beiträge von eiche

    Die Firmware ist imho sehr gut, weil sie viele Möglichkeiten bietet. Wenn man das erst einmal erkennt, nerven die RPC nicht mehr. Dann sind diese Remote Procedure Calls schlicht sehr erfreulich.

    Inzwischen verstehe ich, dass damit das System offen und sehr vielseitig nutzbar ist.

    Anfangs muss man sich halt eine höhere Frustrationsschwelle zulegen. ;)

    Mit einem Web-Frontend pflegst du dein Skript. Verbinde dich doch einfach mal per Browser mit deinem Shelly Plus X und sieh dir die Oberfläche an!

    Vergiss dazu die Cloud!

    Und bringe vorher den Shelly in dein WLAN. Danach führst du ein Firmware Upgrade durch. Dann findest du leicht die Abteilung Script. ...

    Die Skripte laufen ausschließlich auf den Shellies. Die Firmware bietet ein vielseitiges API. Insbesondere die RPC solltest du testen, was auch per HTTP GET gut geht - auch ohne Skript.

    Ein einfaches Beispiel: http://<ip-address>/rpc/script.list

    Dieses zeigt dir alle deine auf dem Shelly gespeicherten Skripte und ob sie gerade laufen ...

    Informationen zu den Remote Procedure Calls: https://shelly-api-docs.shelly.cloud/gen2/General/RPCProtocol

    Noch etwas:

    Du musst unbedingt das JSON Format kennen! Ohne diese Kenntnisse wirst du dich sehr schwer tun.

    Zwei Methoden stehen zur Verfügung.

    JSON.stringify(Objekt) liefert die Struktur als String, auch zum Senden von Nachrichten geeignet.

    JSON.parse(String) macht das Umgekehrte. Ein String im JSON Format wird in ein Objekt (Struktur) transformiert (geparst), wenn der String fehlerfreies JSON enthält.

    Axel_zwo

    https://shelly-api-docs.shelly.cloud/gen2/Addons/ShellySensorAddon

    Dieser Link führt dich direkt zu den AddOn. Da geht es aber um das Einbinden und Konfigurieren von Sensoren.

    Zum Abfragen eines Sensors ist dieser Link geeignet: https://shelly-api-docs.shelly.cloud/gen2/Component…ces/Temperature

    Am einfachsten geht es aber per Eventhandler, den du relativ leicht hinzufügen kannst:

    https://shelly-api-docs.shelly.cloud/gen2/Scripts/S…ddstatushandler

    Die Firmware liefert in regelmäßigen Abständen von ca. 1 Minute die Sensorwerte als Ereignis, welches dein Eventhandler nur entgegenzunehmen braucht. Zusätzlich kommen auch Sensorevents rein, wenn sich der Messwert ändert.

    Zum Skripten gibt es ein sehr grundlegendes Tutorial, welches allerdings nicht sehr weit führt:

    https://shelly-api-docs.shelly.cloud/gen2/Scripts/Tutorial

    Am besten ist es, wenn du folgendermaßen beginnst:

    Code
    Shelly.addEventHandler(
        function (event) {
            print(JSON.stringify(event));
        }
    );

    Dies gibt dir die Informationen aus, welche mit dem Event reinkommen. Diese solltest du dir genau ansehen und versuchen, das herauszuarbeiten, was du brauchst.

    Btw, recht gute Informationen zu verschiedenen Sprachen sind bei https://www.w3schools.com/ zu finden, auch zu JavaScript.

    Durcharbeiten musst du aber selbst. ;)

    horkatz  thgoebel  @dekat win und andere

    Zum Entwicklungs-Zwischenstand der Hauptuhr-Emulation per Shelly Plus 2:

    Ich habe mittlerweile das Projekt auf einer Website beschrieben.

    Mit diesem Link geht es dorthin.

    Es fehlt nur noch die Möglichkeit die angezeigte Uhrzeit per Software einzustellen. Das sollte mein (vorläufig) letzter Schritt in diesem Projekt sein.
    Danach will ich das Skript zusammen mit einer Installationsanleitung auf obiger Website zur Verfügung stellen.

    Btw, die automatische Zeitumstellung funktionierte, sogar bei "Stromausfall" während der offiziellen Zeitumstellung. :)

    Noch ein paar Hinweise zur Reduktion der RPC (Shelly.call()) und der Timer in einem Skript:

    1. Wann immer es möglich ist, kann eine Verzögerung eines RPC-Aufrufs - per Timer oder, wie in meinem Projekt per Schedule Job, die Akkumulation an laufenden RPC verringern.
    2. Entsprechendes gilt für Timer. Wann immer ein Timer durch einen Schedule Job ersetzt werden kann, würde ich den Schedule Job bevorzugen - es sei denn, es sind bereits viele davon aktiv.
    3. Bei Verwendung des KVS mit mehreren Einträgen lassen sich alle diese Einträge bspw. durch Verwendung eines gemeinsamen Prefix per Shelly.call("KVS.GetMany", {"match":Prefix+'*'} ... mit einem statt mehrerer RPC einlesen und in der empfangenen Datenstruktur selektieren - auch fehlertolerant.
      Alternativ kann man stattdessen eine größere Datenstruktur in JSON einsetzen und per "KVS.Get" mit dem key arbeiten. Letzteres empfiehlt sich aber nur dann, wenn beim Schreiben in den KVS alle Komponenten der Struktur auf einmal geschrieben werden. Andernfalls empfehle ich, das Prefix einzusetzen, damit möglichst wenige Schreibzugriffe mit möglichst kleinen Umfängen stattfinden.

    zu 3. In meinem Projekt wird der timestamp zum Minutenimpuls naturgegeben minütlich gespeichert, während ein Abstandswert bei Zeitumstellungen nur zweimal im Jahr gespeichert wird. Deshalb verwende ich dort die Prefix-Variante.

    Die Limits sind unangenehm, aber verständlich. Und das API stellt imho hinreichend viele Varianten zur Verfügung, um viele Probleme damit umgehen zu können. Hier ist halt, wie so oft in der Entwicklung, Kreativität gefragt. ;)

    @dekat win

    Zitat

    Funktioniert deine Uhr nur über Sekunden Pulse? Dann könnte man das 5 Timer Limit damit umgehen und eine Art Easy Delay zusammenbauen.

    Wie hast du die Last Verzögerung kompensiert?

    Es wäre toll, wenn du deinen Code in einem Beitrag unter Vorlagen verlinken könntest.

    Die vom Shelly plus 2 gesteuerte Nebenuhr ist, streng betrachtet, nur eine Anzeige, deren Zeiger im Minutentakt durch Pulse weiterrücken. Die Uhr besitzt selbst keinerlei Taktgeber.

    Ich lasse also minutenweise Pulse per Ausgänge ausgeben. Ein Ausgang führt den Taktimpuls, der andere sorgt für die erforderliche Umschaltung eines Polwenderelais.

    Ausgang 2 reagiert per simpler Shelly-Konfiguration auf Ausgang 1. Mein Projekt besteht aus dem Skript (inkl. KVS-Einträgen) und drei Schedule Jobs.

    Der erste Schedule Job mit einem timespec="59 * * * * *" und einem Funktionsaufruf im Skript per Methode "Script.Eval" sorgt für die Ausgabe der minütlichen Pulse an die Uhr.

    Per Eventhandler beim Schalten von Ausgang 1 lasse ich einige Prüfungen vornehmen, die Zeit betreffend.

    Darin wird ermittelt, ob zwischenzeitlich der Shelly bzw. das Skript nicht arbeitete (typischerweise wegen Stromausfall). Ist dies der Fall, wird auf den zweiten Schedule Job gewechselt, der (aus guten Gründen) in 2s Abständen dieselbe Funktion aufruft wie der erste Job. Damit wird die Uhr in kurzer Zeit auf die richtige Anzeige geführt. Danach wird wieder auf den ersten Job gewechselt, der für die minütlichen Pulse sorgt.

    Du siehst daran, dass hierfür kein Timer.set gebraucht und auch nicht eingesetzt wird.

    Trotzdem brauche ich wegen der Asynchronität bei RPC Aufrufen und beim Stellen der Uhr (bspw. Zeitumstellung) Verzögerungen per Timer.set. Dies ist u.a. wegen möglicher Stromausfälle erforderlich, da der timestamp des letzten Impulses persistent - im KVS - gespeichert werden muss.

    Dieser und ein zweiter Wert wird immer beim Start des Skripts (bspw. nach Power On) in den RAM kopiert, um damit einen synchronen Zugriff zur Verfügung zu haben.

    Ich gerate tatsächlich mitunter an die Grenze von 5 zugleich arbeitenden RPC, weshalb ich das Timing zur Prüfung auf Zeitumstellung einem dritten Schedule Job überlassen muss.

    Dieser hat den timespec="29 * * * * *" und ist somit zeitlich verzahnt mit den beiden anderen Jobs. Damit reduziere ich die gleichzeitig aktiven RPC, damit sich (hoffentlich) kein Skript-Abbruch wegen zu vieler RPC ereignet.

    Das Projekt befindet sich in einer Beta-Version und wird von mir und einem Mitstreiter weiterhin getestet.

    Fazit zu deinen Fragen:

    Ja, es ist notwendig, die Anzahl an Timer.set soweit wie möglich zu reduzieren, was ich per Schedule Jobs erreiche.

    Falls eine Nebenuhr sekündliche Pulse brauchen sollte, würde ich einen weiteren Shelly Gen 2 einsetzen, welcher diese liefern kann. Damit wäre mein jetziger Shelly mit Skript ... tatsächlich überfordert.

    Im timespec von Schedule Job 1 kann man bei längeren Verzögerungszeiten der Uhr, bspw. wegen träger Mechanik, den Sekundenwert reduzieren, bspw. auf 58 oder 57, was ja am Minutentalkt nichts ändert.

    Die Dauer der Pulse ist in den ersten beiden Schedule Jobs im Aufruf der Skript-Funktion als Parameter eingebaut und kann leicht per Schedule.Update dem Bedarf angepasst werden.

    Ohne diese Schedule Jobs wäre das Projekt in meiner jetzigen Fassung nicht implementierbar. Es ist tatsächlich sehr wichtig, mit den RPC- und Timer-Ressourcen sparsam umzugehen und auf deren Limits zu achten.

    Beispiel eines RPC zum Anlegen des ersten Schedule Jobs (aus dem Gedächtnis):

    http://<ip-address>/rpc/schedule.create?timespec="59 * * * * *"&calls=[{"method":"script.eval","params":{"id":1,"code":"pulse(100)"}}]

    Dieser Job ruft minütlich bei Sekunde 59 im Skript mit Id=1 die Funktion pulse(100) auf, worin 100 eine Pulsdauer von 100ms festlegt.

    Bei weiteren Fragen stehe ich zur Verfügung.

    Ich bin evtl. bereit, den Skriptcode hier zur Verfügung zu stellen - allerdings nicht in der noch vorliegenden Laborversion. Diese Version muss ich gelegentlich noch von Zustandserfassungsnachrichten für Testzwecke und Fehlersuchen befreien. Dann bin ich nach einer ausreichenden Testphase prinzipiell bereit, den Skriptcode hier zur Verfügung zu stellen. Oder ich platziere diesen dann zusammen mit einer Installationsanleitung auf einer meiner Websites und verlinke von hier dorthin.

    Gruß aus Rheinland-Pfalz :)

    Edit:

    Evtl. sind in meinen obigen Erläuterungen kleinere, aber unwesentliche Fehler enthalten, weil ich diese zügig herunterschrieb. Im wesentlichen stehe ich aber zu diesen Erläuterungen und weiß an allen Stellen des Skripts und der Zusätze (Schedules, KVS) wofür diese zuständig sind. ;)

    Nur mal so.

    Im Projekt "Emulation einer Hauptuhr per Shelly Gen 2" habe ich inzwischen eine vollautomatische Zeitumstellung implementiert. Das Skript erkennt selbst, wenn eine Zeitumstellung stattfand und liefert die erforderlichen Pulse an die Nebenuhr bzw. pausiert entsprechend lange (mit Pulsen). Dies gelingt überall auf der Welt mit beliebiger Zeitumstellung (selbstverständlich auch ohne eine solche), solange der Shelly einen Zugang zu einem Zeitserver hat.

    Das gelingt auch, wenn just bei der Zeitumstellung der Strom ausgefallen ist.

    Hm, deine sTage-Erklärung leuchtet mir noch nicht ein.

    In der Schleife darüber wird ausschließlich sTage manipuliert - und dies machst du mit Zeile 18 zunichte.

    Ich sehe in der Schleife keine sonstige Änderung.

    Du siehst also den DAU-Skripter vor?

    Es ist ja kein Problem deinen Konverter wie folgt anzuwenden:

    Code
    //Beispiel Anwendung
    let shellyTime = Shelly.getComponentStatus("sys").unixtime;
    let newDate = TsKonverter(shellyTime, +1);
    
    if (newDate.valid) {
        print(newDate.String.Datum);
        print(newDate.String.Zeit);
        //...
    }

    Hinweis, u.a. an thgoebel :

    Wenn man sich per 'http://<ip-address>/settings' oder 'http://<ip-address>/status' interne Daten eines Shelly Gen. 1 anschauen will, kann ich Firefox empfehlen.

    Entsprechendes gilt für Shellies der zweiten Gen. per 'http://<ip-address>/rpc/Shelly.GetConfig' bzw. '.../rpc/Shelly.GetStatus'.

    Dieser Browser kann die JSON-Datenstrukturen sehr übersichtlich darstellen.

    Zuerst einmal Danke für deine Überarbeitung.

    Zwei Bemerkungen dazu:

    1. Zeile 18 mit sTage = 0; kann nicht gut sein, weil diese Initialisierung bereits in Zeile 13 stattfindet und insbesondere die Schalttage in der Schleife vor Zeile 18 ebendiese Anzahl an Schalttagen inkrementell ermittelt.
    2. Nur eine "Schönheitsoperation" für die Schleife zum prüfen der Datentypen (nur so getippt, nicht geprüft):
    Code
    for (let i in input) 
        if (typeof (input[i]) === 'undefined' || (typeof (input[i]) !=='number' && typeof (input[i]) !== 'bigint')) input[i] = 0;

    Ich weiß, dass "Schönheit" Geschmackssache ist, aber kürzer schadet nur recht selten. ;)

    Btw, ich weiß tatsächlich nicht, wozu die 0 Zuweisungen bei unpassenden Datentypen gut sind, weil danach offenbar mit diesen eigentlich ungültigen Werten gearbeitet wird.

    Vielleicht kannst du das klären.

    Ich habe noch nicht alles darin analysiert ...

    Zusatz: Für den Rückgabewert könnte im Objekt/Struktur ein valid:true|false ergänzt werden, etwa so:

    Code
    let ausgabe = { valid: true, Number: number, String: string, TwoDigits: twoDigits };

    Bzw. bei ungültigen Datentypen:

    Code
    return { valid: false };

    Schließlich vielleicht in Kombination mit obiger Schleife:

    Code
    for (let i in input) 
        if (typeof (input[i]) === 'undefined'
            || (typeof (input[i]) !=='number' && typeof (input[i]) !== 'bigint'))
            return {valid:false};

    Mein Heureka von #42 war deutlich verfrüht. So funktioniert es nicht.

    Nun denke ich darüber nach, die timestamp Werte der Zeitumstellungen eines Jahres im KVS abzulegen.

    Solche timestamps lassen sich sehr leicht vergleichen, auch lässt sich damit bei Bedarf rechnen.

    Im KVS kann etwas wie folgt abgelegt werden:

    {"summer":{"ts":<timestamp der Umstellung auf Sommerzeit>, "todo":true}

    Hier ist "summer" der key, der JSON-Ausdruck dahinter der value, bestehend aus ts (timestamp) und dem todo-Wahrheitswert.

    Mit dem key "normal" wird eine gleiche value-Struktur im KVS abgelegt.

    Zur Laufzeit des Skripts ist es sehr leicht, den aktuellen ts mit denen von "summer" und "normal" zu vergleichen und auch die todo-Werte als Bedingungen einzusetzen.

    Hierfür werden einfach beim Start des Skripts - also auch nach einem Stromausfall - diese key-value Paare in den RAM kopiert.

    Vorteil dieses Verfahrens:

    Ich setze voraus, dass diese ts-Werte nur einmal im Laufe eines Jahres ermittelt werden müssen.

    Unter Umständen könnte hierfür ein zweites Skript gestartet werden, müsste aber nicht zwingend.

    Angenommen, die Tage der Umstellungen werden von einem Anwender dem System mitgeteilt. Dann kann bspw. das vom Anwender dafür verwendete Frontend die Berechnung der ts-Werte übernehmen.

    Alternativ könnte ein IT affiner Anwender eine Webseite finden, auf welcher aus Datum und Uhrzeit der timestamp berechnet wird und diesen Wert dem Shelly mitteilen.

    Jedenfalls werde ich erst einmal per Node-RED versuchen, aus einem eingegeben Datum den timestamp zu generieren.

    Bisher habe ich mit der, wenn auch sparsamen, Nutzung des KVS keine Probleme festgestellt.

    Es ist aber wichtig zu beachten, dass der lesende Zugriff (der Schreibzugriff ist hier wenig relevant) asynchron erfolgt. Deshalb lasse ich solche Werte per RAM-Zugriff schreiben (parallel zum KVS) und lesen.

    Die Speicherung im KVS ist imho im wesentlichen zur Persistenz gut, aber nicht zum schnellen Zugriff.

    Es sollte auch klar sein, dass diese Skripte wirklich nur Event gesteuert arbeiten können - ähnlich den Rules in Tasmota - nur deutlich flexibler nutzbar.

    Danke für die Hinweise.

    Bisher speichere ich im Minutentakt bei Einschalten des Impulses den Timestamp (ts) sowohl im Status (RAM) als auch im KVS. Nach Stromausfall erfolgt der Skriptstart .

    Dann erfolgen diese Schritte:

    1. Beide Schedule Jobs werden disabled - Job 1 für Minutentakt, Job 2 zum nachliefern der Pulse nach Stromausfall mit höherer Frequenz.
    2. Der ts wird vom KVS in den Status (RAM) kopiert. Der Status muss parallel im RAM liegen wegen des verzögerten Zugriffs auf den KVS.
    3. Nach 1s wird der Schedule Job für den regulären Betrieb enabled.

    Das hat bisher bestens funktioniert.

    Ich werde mal den Router aus der Shelly-Konfiguration nehmen, um dann die ts-Werte beim Event "switch on" (= Start des Impulses) zu prüfen.

    Und ich bin zuversichtlich, dass ich hier doch ohne deinen wertgeschätzten timestamp converter auskommen kann.

    Genau das (ohne Sommerzeit Info) habe ich bereits implementiert und Tests zeigten, dass dies ohne zwischenzeitlichen Stromausfall fehlerfrei gelingt.

    Dazu genügen die inkrementellen Werte. Wenn alles richtig funktioniert, erscheint es mir unerheblich, ob Sommer- oder Normalzeit vorliegt.

    Schließlich kommt es nur auf die Anzahl an Steuerpulsen an.

    Derzeit teste ich hardcore, d.h. während der Zeitumstellung Stromausfall.

    Dazu habe ich inzwischen kleine Skriptverbesserungen vorgenommen und teste weiter.

    Dabei erfreue ich mich über eine präzise Zeitplanung der Ereignisse per schedule jobs. Dies lässt recht genaue Analysen zu.

    Ergänzung:

    Im Gegensatz zum Skriptstop wirkt die per Schedule getriggerte Zeitumstellung während eines Stromausfalls nicht.

    Puh, es ist nicht so einfach, alle Gedanken zu ordnen. Doch, die Wirkung ist die gleiche, da der zuständige Schedule Job per Methode Script.Eval eine Skriptfunktion aufruft.

    Und genau dieser Test läuft nun. Es sollte sich zeigen, dass die Umstellung so nicht funktioniert, da der Schedule Job seine Aufgabe nicht erfüllen kann.

    Ich werde also noch eine Abfrage der beiden Zeitumstellungs-Schedules mit dem Skriptstart einbauen.

    Dann kann nachträglich noch die Zeitumstellung abgearbeitet werden.

    Dazu stellt sich mir die Frage, ob thgoebel mit seiner Idee der Statusspeicherung dem Problem nicht doch nahe kommt.

    Ich werde gelegentlich prüfen, wie aufwändig der Vergleich zwischen Datum (ohne Jahreszahl) und Zeitstempel ist, denn das müsste ich dann noch einbauen.

    Evtl. nehme ich hierfür eine angepasste Version der Umrechnung timestamp -> Datum und Uhrzeit von @dekat win.

    Da dies ausschließlich beim Skriptstart abzuarbeiten ist, ist diese aufwändige Transformation gut zu vertreten - für den kleinen ESP32. ;)

    Heureka!

    Ich werde nicht die Methode Script.Eval nutzen, sondern versuchen, im KVS den Status zu speichern. Das sollte gelingen.

    Man muss einfach immer mal seine Gedanken äußern, in diesem Fall hier schriftlich. Dann können neue Ideen kommen.

    Dann wäre dem Shelly (Skript) vom ablesenden Menschen mitzuteilen, welche Zeit die Nebenuhr gerade anzeigt.

    Das lässt sich, bspw. per Dashboard und MQTT/HTTP, implementieren.

    Eine andere Vorgehensweise könnte das manuelle (per Taster) Stellen der Nebenuhr auf die aktuelle Zeit sein.

    Letzteres ist schon deshalb reizvoll, weil dazu keine zusätzlichen Systeme erforderlich sind.

    Nachfrage/Bemerkung:

    Ich kann mir nicht vorstellen, dass eine Nebenuhr nach Power On eine feste Zeit einstellt, wie bspw. 6 Uhr.

    Dass sie ohne zugeführte Pulse stehen bleibt, ist mir sehr verständlich.

    Gibt es dazu noch Hinweise oder habe ich hierzu das Notwendige erfasst?