Status-Updates via websocket (longpoll) zu Java

Begonnen von philipptrenz, 12 April 2017, 18:54:26

Vorheriges Thema - Nächstes Thema

philipptrenz

Hallo Forum,

nach etwas Tüftelei und dank Andrés homebridge-fhem Code auf Github bekomme ich mittlerweile mit der Bibliothek https://github.com/TooTallNate/Java-WebSocket Status-Updates zu Java gepusht - sehr schön!

Als Frischling, was die FHEM-Architektur betrifft benötige ich aber etwas Hilfestellung, was das longpoll-Format angeht. Sind es immer drei durch \n getrennte Arrays mit drei Strings? Welche Konventionen gibt es dabei?

Vielen Dank euch!

rudolfkoenig

Eine Doku gibt es nicht, das Format hat sich nach den Beduerfnissen von FHEMWEB entwickelt.

Alt: eine Zeile (d.h. mit \n beendet), drei Werte, mit << getrennt.
Neu: (d.h. falls man fmt=JSON spezifiziert hat): drei Werte als JSON Array, auch mit \n beendet.

Moegliche Werte-Tripel:
- Falls STATE betroffen ist: Geraetename, <STATE-Wert>, angezeigtes Status-Bild als HTML-Code
- Fuer Readings zwei Zeilen: Zeile 1: Geraetename, readingsname, readingsval; Zeile 2: Geraetename-ts, Zeitstempel, Zeitstempel
- Fuer JS Notifikation: #FHEMWEB:<Instanzname>, JS-Code, ""

Siehe auch FHEM/01_FHEMWEB.pm, sub FW_longpollInfo(), bzw. alle Aufrufe.

philipptrenz


philipptrenz

Spricht denn etwas gegen eine Auslieferung der Daten als valides Json? Ich könnte mir für Status-Updates ein angelehntes Format an jsonlist2 vorstellen, möglicherweise etwas reduzierter (Readings nur mit betroffenen Werten):


{
"device_notify": {
"Name": "...",
"PossibleSets": "...",
"PossibleAttrs": "...",
"Internals": {
"NAME": "...",
"STATE": "...",
"TYPE": "...",
},
"Readings": {
"state": {
"Value": "...",
"Time": "..."
}
},
"Attributes": {
"group": "...",
"model": "...",
"room": "..."
}
}
}


Da ich zum Mappen der Werte mich auf TYPE, ggf. model oder PossibleSets beziehe, würde ich im Interesse meines Projekts und mit Blick auf ähnliche Vorhaben vorschlagen, dass PossibleSets, PossibleAttrs, Internals und Attributes vollständig übernommen werden, sofern die Werte zur Verfügung stehen und es nicht zu sehr an den Ressourcen zehrt.

Für die JS Notifications könnte es dann so aussehen:


{
"js_notify": {
"module": "FHEMWEB",
"Name":"<Instanzname>",
"code":"<js-code>"
}
}


Damit kann leicht der Typ überprüft werden und ggf. weitere Typen sicher hinzugefügt werden.
Aus Kompatibilitätsgründen könnte ja optional über fmt=JSON-Advanced ausgeliefert werden.

Stößt der Vorschlag auf Interesse?

justme1968

dazu gibt es schon einen thread und vorschläge.

die websockets selbsf sind noch ziemlich neun. da sie scheinbar funktionieren wäre das json format der nächste schritt. der knackpunkt hier ist aber das format.

es gibt auch die möglichkeit das ein modul eigene daten über longpoll/websocket ans frontend sendet. das muss im json format mit berücksichtigt werden. diese môglichkeit der erweiterbarkeit ist meiner meinung nach der haupt grund für json.

auf jeden fall sollten aber keine readundanten oder statischen daten geschickt werden. z.b. die  possible sets und gets aus einem modul abzufragen ist aktuell recht aufwändig.

es würden auch auch ziemlich viele daten zusammen kommen wenn man die gleiche information immer wieder sendet.

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

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

philipptrenz

Zitat von: justme1968 am 13 April 2017, 09:43:16
dazu gibt es schon einen thread und vorschläge.

die websockets selbsf sind noch ziemlich neun. da sie scheinbar funktionieren wäre das json format der nächste schritt. der knackpunkt hier ist aber das format.

es gibt auch die möglichkeit das ein modul eigene daten über longpoll/websocket ans frontend sendet. das muss im json format mit berücksichtigt werden. diese môglichkeit der erweiterbarkeit ist meiner meinung nach der haupt grund für json.

auf jeden fall sollten aber keine readundanten oder statischen daten geschickt werden. z.b. die  possible sets und gets aus einem modul abzufragen ist aktuell recht aufwändig.

es würden auch auch ziemlich viele daten zusammen kommen wenn man die gleiche information immer wieder sendet.

Alles klar, das ist natürlich schlüssig. Ich such mir mal den entsprechenden Thread. Danke!

rudolfkoenig

ZitatSpricht denn etwas gegen eine Auslieferung der Daten als valides Json?
Ja, Arbeit an einer Stelle, der z.Zt. keine Probleme hat.

Dein Vorschlag ist mir nicht sympatisch, da:
- mir noch nicht klar ist, was durch die Umformatierung gewonnen wird (ausser dass es "schoener" ist).
- es einen erheblichen Umbau bedeutet
- es zu viele Daten enthaelt, die nicht wiederholt werden muessen. Die Menge der Daten ist je nach Modul relativ gross, da es alle moeglichen Parameter enthaelt => Datenbandbreite wird vergeudet
- PossibleSets bedeutet jeweils einen (je nach Modul teuren) Funktionsaufruf im Modul, was evtl. auch noch SetExtensions ruft => Rechenzeit wird wiederholt vergeudet.

ZitatAus Kompatibilitätsgründen könnte ja optional über fmt=JSON-Advanced ausgeliefert werden.
Das ist klar, und nicht optional.

philipptrenz

Was die Laufzeit der benötigten Funktionen angeht, hab ich leider keine Ahnung. Damit sind zusätzliche Daten selbstverständlich unsinnig.

Kurz zur Erklärung:

Der Vorteil eines validen JSON-Objekts ist aus meiner Sicht, dass es in JavaScript damit ja direkt als Objekt vorliegt und die entsprechenden Werte ohne parsen als Attribute angesprochen werden können. Auch in den meisten anderen Sprachen stehen entsprechende Mechanismen zur Verfügung, um das zu erreichen. Dies reduziert Fehlerquellen (keine String-Operationen notwendig) und Einstiegshürden (schöner = leichter verständlich, bestenfalls selbsterklärend).


Auch gegen so eine abgespeckte Variante mit nur den vorhandenen Werten habe ich gar nichts einzuwenden:


{
"device_notify": {
"Name": "...",
"updated_Readings": {
"state": {
"Value": "...",
"Time": "..."
}
}
}
}

justme1968

@rudi: abgesehen vom aufwand es einzubauen wird eine umstellung auf json jedem (neuen) frontend das die daten verwendet ein standardisiertes und erweiterteres format angeboten. vor allem die erweiterbarkeit ist für einige module die direkt mit dem frontend kommunizieren wollen/müssen wichtig.

natürlich sollen auf keinen fall redundante daten immer wieder gesendet werden sondern nur events.

das volumen der übertragenen daten lässt sich mit einer umstellung auf json vermutlich sogar reduzieren. die zwei zeilen für reading und timestamp lassen sich zu einer nachricht zusammen fassen. dabei fällt ein redundanter gerätename, ein redundanter reading wert und ein zeitstempel weg (aktuell wird auch bei readings der wert doppelt gesendet).  auch sonst lässt sich das json format so kompakt wählen das es nicht mehr daten werden als jetzt.


für die possible sets und gets und attr wäre übrigens ein event basierter weg aus dem modul schön um ein frontend aktiv über änderungen informieren zu können. aber das ist ein anderes thema :)
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

rudolfkoenig

ZitatDer Vorteil eines validen JSON-Objekts ist aus meiner Sicht, dass es in JavaScript damit ja direkt als Objekt vorliegt und die entsprechenden Werte ohne parsen als Attribute angesprochen werden können.
Einer von uns beiden uebersieht was. Die Daten werden z.Zt. in fhemweb.js mit "var d = JSON.parse(l);" geparst, wobei l eine Zeile ist, d.h. endet mit \n, damit ist es aus dem websocket relativ einfach "auszulesen".

Wie schaut Auslesen / Parsen bei deinem mehrzeiligen Format aus?

Und ich gebe zu, d.device_notify.updated_Readings.state.Value ist unterhaltsamer zu lesen, als d[1], ob es auf Dauer so viel besser ist, sei hingestellt. Ich strukturiere gerne, wenn ich weiss was es mir bringt. Nur weil man es kann, ist noch kein Argument.

Zitatvor allem die erweiterbarkeit ist für einige module die direkt mit dem frontend kommunizieren wollen/müssen wichtig.
Gibt es einen aktuellen Anlass?

justme1968

Wie schaut Auslesen / Parsen bei deinem mehrzeiligen Format aus?
die nachricht im json format würde nicht wirklich mehrzeilig gesendet sondern als eine zeile. die mehrzeilige darstellung macht es nur übersichtlicher wenn man drüber redet.

ZitatGibt es einen aktuellen Anlass?
nicht aktuell. schon so lange es die json/websocket diskussion gibt :)

einfach nur auf json umstellen weil es 'hübscher' ist hat tatsächlich nur beschränkten nutzen. die anderen angesprochenen punkte  werden dann besser ersichtlich sobald es den ersten patchvorschlag gibt der alle punkte berücksichtigt.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

rudolfkoenig

Zitateinfach nur auf json umstellen
Das aktuelle Format ist doch auch JSON, oder irre ich mich?

philipptrenz

#12
Zitat von: rudolfkoenig am 13 April 2017, 10:22:24
Einer von uns beiden uebersieht was. Die Daten werden z.Zt. in fhemweb.js mit "var d = JSON.parse(l);" geparst, wobei l eine Zeile ist, d.h. endet mit \n, damit ist es aus dem websocket relativ einfach "auszulesen".

Wie schaut Auslesen / Parsen bei deinem mehrzeiligen Format aus?

Wie schon André meinte, wird es nur mehrzeilig dargestellt. Bisher sind es drei Zeilen mit validem JSON-Array. Ein JSON-Objekt hat den o.g. Aufbau mit geschweiften Klammern und besteht aus key-value-Paaren. Wird der String dann als Ganzes geparst, sind die Paramter direkt ansprechbar:


var json = JSON.parse(input);

var value = json.updated_Readings.state.Value;
var ts = json.updated_Readings.state.Time;


Streng genommen ist die bisherige Darstellung kein valides JSON, wenn ich mich nicht irre.

justme1968

es ist das alte drei heilige format 1:1 in ein json array gesteckt. es nutzt aber keine der möglichkeiten die json in diesem zusammen bieten würde. 'richtiges' und für die angesprochenen punkte sinnvolles json könnte z.b. so aussehen:

{ "event": {"reading": <name>, "value": <wert>, "ts": <ts>} }

oder vielleicht noch besser:

{ "readingsUpdate": {"name": <name>, "value": <wert>, "ts": <ts>} }
wenn man hier alle kurze sprechende namen wählt ist diese eine zeile nicht länger als die aktuell verwendeten zwei.

d.h. jedes element bekommt einen namen über denn man dann direkt drauf zugreifen kann. ohne wissen zu müssen an wievielter stelle etwas steht. und ohne device und reading namen wieder aus dem kombinierten "<device>-<reading>" string heraus zu parsen.

die fhemweb js notification könnte so aussehen:
{ "#FHEMWEB": { "name: <Instanzname>, "code": <JS-Code>} }

jedes modul das eigene nachrichten ans frontend senden möchte  würde so etwas verwenden:
Zitat{ "#<modul-name>": {...} }

die #... nachrichten könnte man dann auch direkt der modul spezifischen updateLine routine zustellen ohne der reihe nach alle durch zu probieren.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

philipptrenz

Zitat von: justme1968 am 13 April 2017, 10:51:44
{ "event": {"reading": <name>, "value": <wert>, "ts": <ts>} }

{ "#FHEMWEB": { "name: <Instanzname>, "code": <JS-Code>} }

da würde ich sofort mitgehen ;)

rudolfkoenig

readingsUpdate statt event ist irrefuehrend, weil ersteres nicht immer ein event erzeugt.

Wer den Patch baut, soll bitte nicht vergessen, dass:
- er etliches umbauen (FHEMWEB.pm, fhemweb.js, fhemweb_*.js) und alles testen darf.
- es weitere Applikationen gibt, die auf dem aktuellen Format angewiesen sind, d.h. "tabula rasa" ist nicht drin.

justme1968

naja... es ist die nachricht die über die änderung eines readings informiert. von daher passt es schon. der punkt ist das nicht alle events readings änderungen sind und man das auch im format zum ausdruck bringen sollte.

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

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

justme1968

#17
ich habe hier: https://forum.fhem.de/index.php/topic,70903.0.html mal etwas code gepostet mit dem man das ganze testen und dann darüber diskutieren kann.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

philipptrenz

#18
Danke dir! Es scheint als hätte ich (noch?) keine Berechtigung in FHEM Development zu schreiben. Deshalb eine kurze Anmerkung hier, vielleicht kannst du sie übernehmen:

Ich bin gestern über die HUEDevice Websocket Message gestolpert, diese gibt mehrere Readings zurück. Das sollte auf jeden Fall berücksichtigt werden


["HUEDevice1","dim06%","..."]
["HUEDevice1-onoff","1","1"]
["HUEDevice1-onoff-ts","2017-04-20 17:42:20","2017-04-20 17:42:20"]
["HUEDevice1-pct","1","1"]
["HUEDevice1-pct-ts","2017-04-20 17:42:20","2017-04-20 17:42:20"]
["HUEDevice1-state","dim06%","dim06%"]
["HUEDevice1-state-ts","2017-04-20 17:42:20","2017-04-20 17:42:20"]


Mein Vorschlag wäre daher die Readings zu entkoppel. In dieser Schreibweise besteht auch die Möglichkeit, weitere Devices mitzuliefern etc.:


{
"event": {
"HUEDevice1": {
"onoff": { "value":"1", "timestamp":"2017-04-20 17:42:20" },
"ptc": { "value":"1", "timestamp":"2017-04-20 17:42:20" },
"state": { "value":"dim06%", "timestamp":"2017-04-20 17:42:20" }
}
}
}

justme1968

ja. mehrere reading events in eine nachricht zusammen zu fassen könnte sinnvoll sein. wenn alle timestamps identisch sind könnte man die auch eine ebene höher ziehen und die nachricht noch kürzer machen.

generell sollten alle devices änderungen an mehreren readings zusammen fassen. aktuell ist das aber nur fhem intern zu sehen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

justme1968

als format würde ich eher so etwas vorschlagen:{
"event": {
"device": "HUEDevice1", "timestamp":"2017-04-20 17:42:20" , "readings" : [
{ "reading": "onoff", "value":"1" },
{ "reading": "ptc", "value":"1" },
{ "reading": "state", "value":"dim06%" }
]
}
}
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

philipptrenz

Sofern der timestamp stets identisch ist, müsste das ja passen. Können mit einem Event mehrere Devices getriggert werden? Oder ist das evtl. zukünftig geplant?
Wenn der Name root-Node ist, wäre auch das möglich. Damit bleibt das Format ausbaubar.

{
"event": {
"HUEDevice1": {
"timestamp":"2017-04-20 17:42:20",
"readings" : [
{ "reading": "onoff", "value":"1" },
{ "reading": "ptc", "value":"1" },
{ "reading": "state", "value":"dim06%" }
]
},
"HUEDevice2": {
"timestamp":"2017-04-20 17:42:20",
"readings" : [
{ "reading": "onoff", "value":"1" },
{ "reading": "ptc", "value":"1" },
{ "reading": "state", "value":"dim06%" }
]
}
}
}

justme1968

nein. in einem BulkUpdate block kann immer nur ein device aktualisiert werden.

wenn ein reading mit einem abweichenden timestamp dabei ist kann man das so abbilden:{
"event": {
"device": "HUEDevice1", "timestamp":"2017-04-20 17:42:20" , "readings" : [
{ "reading": "onoff", "value":"1" },
{ "reading": "ptc", "value":"1", "timestamp":"2017-04-20 17:42:10" },
{ "reading": "state", "value":"dim06%" }
]
}
}


das ist aber eine ziemliche ausnahme. bzw. passiert eigentlich nur wenn in einem rutsch mehrere historische werte für das gleiche reading gesetzt werden. d.h. das gleiche reading kann im prinzip mehrfach auftauchen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

philipptrenz

Alles klar! Aber solche Ausnahmen sind immer gefährlich in der Implementierung übersehen zu werden. Ist die Nachrichtenlänge so entscheidend für die Performance? Werden die longpolls nicht asynchron gepusht? Sonst fände ich die Ausgabe mit timestamp je reading doch schlüssiger und weniger missverständlich.