NEWS
Handhabung von Socket für VIS-Alternative
-
Moin,
ich bin gerade dabei meine eigene VIS-Alternative zu schreiben, die genau auf meine Bedürfnisse zugeschnitten ist. Bin jetzt schon so weit, dass die Website die aktuellen Zustände über Socket.io empfängt, visualisiert und auch vom User geänderte Zustände wieder an ioBroker überträgt. Das klappt meistens auch ganz gut, doch manchmal komme ich in eine Art Schleife, in der ein empfangener Wert gesetzt wird und zeitgleich ein geänderter Wert wieder an den Server übertragen wird. Weil das setzen eines Wertes wieder die Senden-Funktion triggert, wird dieser Wert wieder gesendet und zeitgleich wieder der vorherige Wert vom Server empfangen und gesetzt. Das führt dazu, dass z.B. der Slider im Screenshot sehr schnell zwischen diesen beiden Werten hin und her springt.Bisschen anschaulicher erklärt:
1 . Server-Wert = 80 und wird empfangen
1 . Zeitgleich wird Slider auf Lokal-Wert = 30 gesetzt und an den Server gesendet
2 . jetzt wird der empfangene Wert (80) auf den Slider übertragen
2 . Änderung des Sliders --> Sende Wert 80 an Server
3 . Zeitgleich wird der vorherige Wert (30) empfangen und wieder auf Slider übertragen
4 . Änderung führt wieder zum Senden an Server
5 . und das geht dann ewigBeide Seiten kämpfen richtig darum, ihren eigenen Wert durchzudrücken und dabei tauschen sie dann immer nur die jeweiligen Werte zyklisch aus.
Bis jetzt das Grundgerüst. Wenn die Verbindung erstmal vernünftig funktioniert, ist der Rest einfach:

Aktueller Code.
WennservConn()ein Update bekommt, werden die States emitted. Der nächste Abschnitt ist der Event Listener, der auf dieses Update hört, alle HTML Elemente mit der Klasse.iobroker-statesucht und je nach Typ (bis jetzt Toggle oder Slider) dann die entsprechenden Werte aus dem ArrayStatessetzt. Die HTML-Elemente haben noch ein Tagdata-id="Shelly.blabla"über die ich dann die richtigen Werte aus dem Array zuordne.
Der letzte Abschnitt achtet auf alle Elemente mit der Klasse.iobroker-stateund bei einer Änderung wird wieder nach Typ unterschieden und dann mit der entsprechenden ID der neue Wert an den Server gesendet.var states = []; servConn.init({ onUpdate: function (id, state) { setTimeout(function () { states[id] = state; app.emit('smarthomeStates', states); }, 0); }, [...] }); // RECEIVE states and update app.on('smarthomeStates', function (states) { elements = document.getElementsByClassName("iobroker-state") for(var i = 0; i < elements.length; i++) { if ((elements[i].classList).contains("toggle")) { app.toggle.get(elements[i]).checked = states[elements[i].dataset.id]["val"]; } else if ((elements[i].classList).contains("range-slider")) { app.range.get(elements[i]).setValue(states[elements[i].dataset.id]["val"]); } } }); // TRANSMIT states on change // erst wenn Änderung abgeschlossen wurde, also nicht kontinuierlich senden, während man den Slider noch verschiebt $('.iobroker-state').on('touchend mouseleave', function (e) { if ((e.srcElement.classList).contains("toggle")) { var toggle = app.toggle.get(e.target); servConn.setState(e.srcElement.dataset.id, toggle.checked) } else if ((e.srcElement.classList).contains("range-slider")) { var range = app.range.get(e.target); servConn.setState(e.srcElement.dataset.id, range.value) } });Wie wird das bei VIS gehandhabt bzw. wie kann ich verhindern, dass Client und Server sich darum prügeln, immer den aktuellen Wert an den jeweils anderen zu senden?
-
Moin,
ich bin gerade dabei meine eigene VIS-Alternative zu schreiben, die genau auf meine Bedürfnisse zugeschnitten ist. Bin jetzt schon so weit, dass die Website die aktuellen Zustände über Socket.io empfängt, visualisiert und auch vom User geänderte Zustände wieder an ioBroker überträgt. Das klappt meistens auch ganz gut, doch manchmal komme ich in eine Art Schleife, in der ein empfangener Wert gesetzt wird und zeitgleich ein geänderter Wert wieder an den Server übertragen wird. Weil das setzen eines Wertes wieder die Senden-Funktion triggert, wird dieser Wert wieder gesendet und zeitgleich wieder der vorherige Wert vom Server empfangen und gesetzt. Das führt dazu, dass z.B. der Slider im Screenshot sehr schnell zwischen diesen beiden Werten hin und her springt.Bisschen anschaulicher erklärt:
1 . Server-Wert = 80 und wird empfangen
1 . Zeitgleich wird Slider auf Lokal-Wert = 30 gesetzt und an den Server gesendet
2 . jetzt wird der empfangene Wert (80) auf den Slider übertragen
2 . Änderung des Sliders --> Sende Wert 80 an Server
3 . Zeitgleich wird der vorherige Wert (30) empfangen und wieder auf Slider übertragen
4 . Änderung führt wieder zum Senden an Server
5 . und das geht dann ewigBeide Seiten kämpfen richtig darum, ihren eigenen Wert durchzudrücken und dabei tauschen sie dann immer nur die jeweiligen Werte zyklisch aus.
Bis jetzt das Grundgerüst. Wenn die Verbindung erstmal vernünftig funktioniert, ist der Rest einfach:

Aktueller Code.
WennservConn()ein Update bekommt, werden die States emitted. Der nächste Abschnitt ist der Event Listener, der auf dieses Update hört, alle HTML Elemente mit der Klasse.iobroker-statesucht und je nach Typ (bis jetzt Toggle oder Slider) dann die entsprechenden Werte aus dem ArrayStatessetzt. Die HTML-Elemente haben noch ein Tagdata-id="Shelly.blabla"über die ich dann die richtigen Werte aus dem Array zuordne.
Der letzte Abschnitt achtet auf alle Elemente mit der Klasse.iobroker-stateund bei einer Änderung wird wieder nach Typ unterschieden und dann mit der entsprechenden ID der neue Wert an den Server gesendet.var states = []; servConn.init({ onUpdate: function (id, state) { setTimeout(function () { states[id] = state; app.emit('smarthomeStates', states); }, 0); }, [...] }); // RECEIVE states and update app.on('smarthomeStates', function (states) { elements = document.getElementsByClassName("iobroker-state") for(var i = 0; i < elements.length; i++) { if ((elements[i].classList).contains("toggle")) { app.toggle.get(elements[i]).checked = states[elements[i].dataset.id]["val"]; } else if ((elements[i].classList).contains("range-slider")) { app.range.get(elements[i]).setValue(states[elements[i].dataset.id]["val"]); } } }); // TRANSMIT states on change // erst wenn Änderung abgeschlossen wurde, also nicht kontinuierlich senden, während man den Slider noch verschiebt $('.iobroker-state').on('touchend mouseleave', function (e) { if ((e.srcElement.classList).contains("toggle")) { var toggle = app.toggle.get(e.target); servConn.setState(e.srcElement.dataset.id, toggle.checked) } else if ((e.srcElement.classList).contains("range-slider")) { var range = app.range.get(e.target); servConn.setState(e.srcElement.dataset.id, range.value) } });Wie wird das bei VIS gehandhabt bzw. wie kann ich verhindern, dass Client und Server sich darum prügeln, immer den aktuellen Wert an den jeweils anderen zu senden?
@KLVN
Ohne jetzt deinem Beispiel wirklich folgen zu können, würde ich es über die state propertyfromversuchen. Diese gibt an von wem der Wert geändert wurde. D.h. wenn eine Änderung durch vis erfolgte, darfst du mit deinem Code dann nicht darauf reagieren. Kommt die Änderung z.B. vom Shelly Adapter, dann in VIS darauf reagieren. -
@KLVN
Ohne jetzt deinem Beispiel wirklich folgen zu können, würde ich es über die state propertyfromversuchen. Diese gibt an von wem der Wert geändert wurde. D.h. wenn eine Änderung durch vis erfolgte, darfst du mit deinem Code dann nicht darauf reagieren. Kommt die Änderung z.B. vom Shelly Adapter, dann in VIS darauf reagieren.@Scrounger
Danke, das behebt schon mal den unendlichen Loop. Wenn ich den Slider gedrückt halte und verschiebe, zappelt er danach noch etwas, aber pendelt sich dann auch wieder ein - das war vorher nicht der Fall. Ich bin deinem Ansatz gefolgt und ignoriere jetzt States, die vonsystem.adapter.web.0odersystem.adapter.socketio.0kommen. Damit bleiben nur noch die Adapter der eigentlichen Geräte, wie z.B.system.adapter.shelly.0übrig. Zusätzlich pausiere ich die Funktion, die für das Aktualisieren sorgt, wenn der Benutzer gerade ein Element ändert.
Ich werde die Sache noch etwas verfeinern. -
@Scrounger
Danke, das behebt schon mal den unendlichen Loop. Wenn ich den Slider gedrückt halte und verschiebe, zappelt er danach noch etwas, aber pendelt sich dann auch wieder ein - das war vorher nicht der Fall. Ich bin deinem Ansatz gefolgt und ignoriere jetzt States, die vonsystem.adapter.web.0odersystem.adapter.socketio.0kommen. Damit bleiben nur noch die Adapter der eigentlichen Geräte, wie z.B.system.adapter.shelly.0übrig. Zusätzlich pausiere ich die Funktion, die für das Aktualisieren sorgt, wenn der Benutzer gerade ein Element ändert.
Ich werde die Sache noch etwas verfeinern.@KLVN
Das gezappel kenn ich von der Entwicklung der Material Design Widgets.
Du musst den Slider so auslegen, dass er nur bei touchend / mouse up / key up einen Wert schreibt bzw. an iob schickt.Bzgl. handling der state change events würde ich dir empfehlen, anzuschauen wie das der VIS Adapter macht: https://github.com/ioBroker/ioBroker.vis/blob/master/www/js/vis.js
-
Moin,
es ist etwas Zeit ins Land gegangen, aber nun habe ich auch wieder mehr Zeit für die Entwicklung. Die Funktion achtet jetzt auftouchend mouseup keydownund solange man den Schalter nicht gedrückt hält und hin und her schiebt, funktioniert das alles auch. Zusätzlich unterbreche ich den Event listener, der für das aktualisieren der Zustände verantwortlich ist, sobald man die Schalter benutzt hat. Aktueller Ablauf ist jetzt:- Zustände aktualisieren, wenn der Socket
onUpdate()auslöst - Wenn z.B. ein Schalter geändert wurde (
touchend ...), dann stoppe Aktualisierung, sende neuen Zustand, starte Aktualisierung
Dieses Flackern tritt immer noch auf, doch ich denke, dass es an dem Framework liegt, das ich für die Darstellung benutze. Das hat ein paar Kinderkrankheiten und sendet unter anderem ein Event, bevor die Animation durchgelaufen ist, was schon für ordentlich Verwirrung gesorgt hat. Ich muss wohl noch ein paar Timeouts/waits einfügen, damit eine State-Änderung erst als abgeschlossen gilt, wenn die betroffenen Elemente geändert wurden und auch mit den Animationen fertig sind.
Den Code von VIS habe ich mir angesehen und konnte auch ein paar Tricks finden. Auch deinen (@Scrounger ) Code für die Widgets habe ich gelesen, konnte jetzt aber nicht die Stelle finden, an der ein neuer Zustand gesendet wird.
- Zustände aktualisieren, wenn der Socket
Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.
Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.
Mit deinem Input könnte dieser Beitrag noch besser werden 💗
Registrieren Anmelden