proof of concept: websockets statt longpoll

Begonnen von justme1968, 26 Oktober 2016, 21:44:16

Vorheriges Thema - Nächstes Thema

justme1968

ich habe gerade für ein modul einen read only websocket client in ein modul eingebaut.

weil es so schön war habe ich mal mal probehalber den longpoll mechanismus auf websockets umgestellt.

was man dadurch vor allem gewinnt ist das keine buffer voll laufen und die verbindung nach einer bestimmten menge daten nicht regelmäßig wieder neu aufgebaut werden muss.

aktuell wird das websocket nur von fhem in richtung client benutzt. man könnte es eigentlich auch für die XHR requests der gegenrichtung verwenden.

es gibt aktuell noch kein error handling, man könnte noch über das protokoll selber nachdenken (die longpoll nachrichten haben einen ziemlichen overhead) und man könnte ein paar dinge noch aufräumen. es wäre also noch einiges zu tun um es vollständig zu machen und ich poste es eigentlich nur damit es nicht verloren geht. vielleicht interessiert es ja den ein oder anderen.

falls das ganze interesse findet und weiter verfolgt wird könne man das ganze auch für andere frontends verwenden.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Loredo

Sehr interessant, danke André! Kann man das vielleicht auch gleich so gestalten, dass eine Art Kompatibilitätslayer für zB smartVISU möglich ist?


Gruß

Julian
Hat meine Arbeit dir geholfen? ⟹ https://paypal.me/pools/c/8gDLrIWrG9

Maintainer:
FHEM-Docker Image, https://github.com/fhem, Astro(Co-Maintainer), ENIGMA2, GEOFANCY, GUEST, HP1000, Installer, LaMetric2, MSG, msgConfig, npmjs, PET, PHTV, Pushover, RESIDENTS, ROOMMATE, search, THINKINGCLEANER

justme1968

zu smartvisu kann ich dir nichts sagen. auch nicht was die implementierung dort noch alles kann und braucht.

prinzipiell fände ich es schon gut eine solche standardisierte websocket schnittstelle zu haben die auch andere frontends verwenden können. neben smartvisu wäre das eventuell auch etwas für tableui. homebridge-fhem wäre auch ein kandidat den ich auch umstellen würde.

dafür wäre es vermutlich sinnvoll die nachrichten umzubauen und reading/informid, wert und timestamp direkt als json abzubilden. etwas in der art: {"informid": <id>, "value": <wert>, "timestamp": <ts>}. neben solchen informid nachrichten könnte man auch andere nachrichten definieren um etwas ans frontend zu senden. popup windows, ...

die aktuelle implementierung ist im prinzip auch rückwärts kompatibel zu langoll. ich habe auf js seite noch nicht eingebaut das der upgrade auf websocket schief gehen könnte oder die klasse nicht da ist. der code ist aber da und statt if(1) muss man nur den fehler abfangen und verzweigen.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Markus Bloch

Finde ich eine tolle Idee, da man mit sowas einige Krücken beim Thema Longpoll/Event-Monitor loswerden könnte. Mit einem generischen Transport-Kanal hat man viele Optionen für die Zukunft.

Wäre das evtl. sogar etwas für ein eigenes Modul zwecks der Verbindung mit "allowed"?

Gruß
Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

für den aufbau der verbindung greifendie allowed regeln die für das web device gelten.

man könnte hier noch einbauen das beschränkt wird auf websocket und das man die nachrichten die gesendet (und empfangen) werden einschränken kann.

aber erst mal muss es genug anwender geben um es generell einzubauen :)

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Markus Bloch

Hallo Andre,

die interne Umstellung von FHEM auf Websockets sehe ich eher als "Vereinfachung unter der Haube" an, da dadurch eine einheitliche Event/Command-Schnittstelle für JS-basierte Frontends geschaffen wird. Davon profitiert auch FHEMWEB, da viele Kruxen im Rahmen des Event-Monitors und Longpoll beseitigt werden können. Die Schnittstelle ist einfach erweiterbar (Stichwort "Nachrichtentyp") und ist generell standardisiert und verbreitet.

Sobald ein Frontend aufgerufen wird, wird via JS eine Websockets-Verbindung aufgebaut. Darüber wird dann:

- Events angefordert (raumbezogen wie bei Longpoll oder alles wie im Event-Monitor mit Filterung)
- Befehle an FHEM übermittelt  => FW_cmd()
- asyncOutput() Meldungen von FHEM übermittelt
- ...

Man müsste ein geeignetes Format wählen wie man die Nachrichten von ihrem Typ her unterscheiden kann und je nach Typ unterschiedliche Datenstrukturen wählt (z.B. JSON was am naheliegensten wär).

Das Problem ist eher der Aufwand der Umsetzung und die tiefgreifenden Änderungen.

Gruß
Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

genau das ist die idee dahinter. der aktuelle patch ist auch erst mal recht klein. selbst wenn die fehler behandlung drin ist ist der js websocket teil kleiner als der aktuelle lonpoll code.

eigentlich sind 'nur' zwei dinge zu tun bevor man diesen ersten schritt einbaut:
- rudi muss ja sagen :)
- wir müssen uns auf ein erweiterteres format einigen. mein vorschlag wäre für die events wie sie aktuell per longpoll zum update verwendet werden etwas in der art:{"type": "event", "informid": <id>, "value": <wert>, "timestamp": <ts>}

die anderen verbesserungen und erweiterungen kann man dann nach und nach angehen. was mir einfällt ist:
- ich würde zumindest für das äussere format json festlegen. wenn ein modul etwas anderes braucht kann es das ja in ein
  {"type": "meinModul.privat", "otherdata": <anderes format>, ... } packen.
- nachrichten von fhem an bestimmte teile des frontends: FW_directNotify mit übergabe des event type und einigung
  wie die types aufgebaut sein sollen.  z.b. <modul>[.<device>[.<subtype]]. der rest der nachricht wäre dann modul
  spezifisch.
- asyncOutput wäre eine variante hiervon
- bestimmte events abonnieren
- der rückweg für kommandos: schön aber nicht so dringend. vor allem braucht man dann einen weg um die
  rückgabe des kommandos eindeutig zu identifizieren. eine art callback mit serien nummer.
- ...

der longpoll code muss je erst mal drin bleiben da es noch einige zeit 'alten' js code an neuem fhem und neuen js code an 'altem' fhem geben wird.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Ich habe kein Problem mit websockets, eher im Gegenteil, will es einbauen.
Ich verstehe einiges im Patch nicht (bzw. will nicht raten)
- wozu dient im Patch der erste Abschnitt? Nur fuer den "Log 1, $data;" am Schluss? Ist vermutlich irrelevant, da darueber (noch?) keine JavaScript->perl Kommunikation stattfindet, wird als erst spaeter relevant sein.
- wieso wurde $FW_userAgent entfernt?
- was passiert nach dem "Upgrade", falls kein FW_inform gesetzt ist?
- der Code im Upgrade-Block scheint auch noch nicht vollstaendig zu sein, stimmt das?
- Wozu braucht man im CommandSet ein "$evalSpecials->{'%DEV'} = $sdev;" ?
- warum ist der timeout im websocket Fall 0.5s und nicht 5s?

Neben fhemweb.js muesste auch console.js umgebaut werden.
Bin unentschlossen, ob Befehle wie set/get auch ueber websockets laufen sollten.

Die JSON-Erweiterungsvorschlaege verstehe ich nicht wirklich, meine aber, dass es nichts mit websockets zu tun hat (kann ueber longpoll genauso angewendet werden), und schlage vor, es separat zu diskutieren/implementieren.

justme1968

- ja. der erste teil ist für die richtung js -> perl und wird noch nicht verwendet.
  ich würde aber gerne zumindest alles was keine antwort erwartet auch über das websocket senden.
  wenn eine antwort erwartet wird würde ich wie bisher jeweils eine neue anfrage per XHR machen.
  es wird einfach nur das socket nach rfc ausgelesen damit nichts liegenbleibt und blockiert.

- $FW_userAgent ist nicht entfernt sondern nur nach unten gerutscht

- in fhemweb.js wird das upgrade quasi mit dem setzen von inform gemacht. wenn jemand ein upgrade
  ohne inform macht bleibt das socket aktuell einfach offen bis z.b. das browser fenster geschlossen wird
  und es kommen nie daten. das würde aber genau so passieren wenn man aktuell ein longpoll startet ohne
  inform zu setzen.

- was fehlt dir im upgrade block? es werden nicht alle laut rfc erlaubten möglichkeiten unterstütz
  und extentions habe ich nicht getestet. aber die funktionalität sollte alles enthalten was für longpoll
  nötig ist.

- $evalSpecials->{'%DEV'} ist ein überbleibsel aus einem nicht dazugehörenden patch um im set feststellen
  zu können woher ein kommando gekommen ist. damit kann man unterscheiden ob ein set interaktiv durch
  den benutzer erfolgt ist oder automatisch z.b. durch ein at. 
  also komplett anderes thema für einen anderen thread. aber trotzdem potentiell wichtig damit manuelle
  interaktionen z.b. eine automatik überschreiben können.

- ich glaube die 0.5 sekunden hatte ich nur zum testen eingebaut will ich nicht immer
  5 sekunden warten wollte :)


ja. die console müsste dann auch angepasst werden.

das problem bei den set und get ist das im websocket fall die zuordnung des ergebnisses zum 'wartenden' js code nicht mehr direkt geht sondern eine art protokoll bedarf. darauf würde ich zumindest im ersten schritt verzichten. siehe oben.

die json erweiterung haben nicht direkt etwas mit den websockets zu tun sondern helfen nur allen die auch an die events wollen. das format ist erweiterbar und auch wert und timestamp kommen in einem rutsch. kann in einen anderen thread. sollte aber nicht vergessen werden ;)
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Ich habe eine erste Version eingecheckt:
- aktivieren kann man es mit "attr WEB longpoll websocket"
- Target ist nicht hartkodiert (war localhost:8083)
- scheint ueber http und https zu funktionieren
- die Initalisierung von inform in 01_FHEMWEB.pm  ist in eine Funktion ausgelagert
- fhem.pl habe ich nicht modifiziert, stattdessen gibt es ein FW_addToWritebuffer
- Datenempfang ist noch nicht eingebaut
- type wird in .js auf "close" und nicht auf "closex" geprueft.

Habe versucht websockets auch in console.js einzubauen, da funktioniert es aber nicht, keine Ahnung wieso, kommt immer ein Close-Event (mit wasClean:false), braeuchte Hilfe.

justme1968

stimmt. der localhost war noch vom testen.

FW_addToWritebuffer statt addToWritebuffer bedeutet das alle module außer fhemweb die das verwenden nicht mehr gehen und auch umgestellt werden müssen. ist es nicht besser alles in
addToWritebuffer zu lassen?

console.js schaue ich mir
morgen mal an.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Koennen andere Module direkt mit .js Kommunizieren, ohne FHEMWEB-Code?
Musste gerade einen haesslichen Code einbauen fuer Systeme, die kein Digest::SHA installiert haben.

rudolfkoenig

Zitat$evalSpecials->{'%DEV'} ist ein überbleibsel aus einem nicht dazugehörenden patch um im set feststellen
  zu können woher ein kommando gekommen ist.
Bin verwirrt. Dafuer gibt es doch $cl

justme1968

ja. über DoTrigger. das verwendet addToWritebuffer und alle so erzeugten events (das sind einige) bringen aktuell das websocket durcheinander. vielleicht ist das auch schon das problem von console.js.

wie wäre es gleich in FHEMWEB_Initialize zu prüfen ob es Digest::SHA gibt und sich das zu merken. dann kann man in FW_Define websocket auch gleich aus der attribut liste weg lassen.

kannst du bitte den code zum decodieren des websockets bei js->perl noch einchecken? zumindest in einer minimal version.

$cl und auch $hash->{CL} waren nicht ausreichend. ich erinnere mich aber gerade nicht mehr warum. ich muss das bei gelegenheit noch mal nachstellen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Zitatja. über DoTrigger. das verwendet addToWritebuffer und alle so erzeugten events (das sind einige) bringen aktuell das websocket durcheinander. vielleicht ist das auch schon das problem von console.js.
Hat fuer console.js nicht geholfen. Waere aber auch geschockt. Habs wieder zurueckgeschoben.
Das Problem war die falsche Laengenkodierung fuer len<126. Habs gefixt, jetzt klappt es mit console.js.

Zitatwie wäre es gleich in FHEMWEB_Initialize zu prüfen ob es Digest::SHA gibt und sich das zu merken. dann kann man in FW_Define websocket auch gleich aus der attribut liste weg lassen.
Gefaellt mir auch nicht: da wundert man sich, dass man was nicht setzen kann obwohl es in der doku steht, man bekommt eine Fehlermeldung, obwohl man kein websocket will. Habe das require ins AttrFn verbannt.

Zitatkannst du bitte den code zum decodieren des websockets bei js->perl noch einchecken? zumindest in einer minimal version.
Habs gemacht, musste aber die Schleife auskommentieren, da sie Syntaxfehler verursacht, und ich weiss nicht, wie es richtig heisst. Bitte korrigieren. Nach jedem connect wird was geschickt, habe deswegen die debug-ausgabe auch auskommentiert.

Stand: FHEMWEB Status und Event-Monitor updates funktionieren per websocket, getestet mit Chrome und Firefox