72_UBUS.pm Code Review / Feedback

Begonnen von xenos1984, 08 August 2021, 17:47:27

Vorheriges Thema - Nächstes Thema

xenos1984

Das ist wahrscheinlich eine Stilfrage - für mich ist ein "elsif" ein Zeichen, dass dieser Zweig nur dann ausgeführt wird, wenn der vorherige nicht ausgeführt wurde :D Das "return" am Ende eines vorherigen if sagt natürlich das gleiche, aber das im vorherigen Zweig zu suchen, um herauszufinden, wann eine if/elsif abgefragt wird, finde ich wiederum kontra-intuitiv...

Ich habe mir inzwischen angesehen, welche Abfragen es noch gibt, und da sind einige Dabei, bei denen ein Parameter direkt im Namen der aufgerufenen Funktion steckt (sowas wie "network.interface.wan" + "down" oder "led.broadband" + "0" etc.) und man die nicht durch einen statischen Hash aufteilen kann, wenn man nicht die Namen aller LEDs oder Netzwerk-Interfaces in dem Hash haben will. Da ist ein

if($module =~ m/network\.interface\.(.*)/)

o.ä. dann wohl doch die bessere Wahl.

Übrigens gibt es doch nichts schöneres als eine stabile API... Letzte Nacht hat mein Router ein Update durchlaufen, und ich dachte mir, das wäre doch ein perfekter Test für die Robustheit meines Moduls. Seitdem ist ein Teil meiner Readings verschwunden, die Interfaces haben neue Namen bekommen (meine externe IP steht jetzt unter "wan" statt "internet" und mein Dynamic DNS Client hat sie nicht mehr gefunden), manche RPCs sind komplett geändert (statt "asterisk" liefert mir jetzt "voice.asterisk" den Status der Telefonleitungen - mit anders strukturierter Antwort, für die ich den Parser umschreiben muss) oder die Daten liegen woanders ("clients" zeigt mir nicht mehr, an welchem Port ein Gerät hängt - dafür listet "interfaces" jetzt die damit verbundenen Geräte auf). Kurzum, ich muss meinen Parser nicht nur anpassen, sondern das Modul wohl so umbauen, dass die RPC-Struktur konfigurierbar ist. Wer weiß, was sich noch für mysteriöse Geräte "im freien Feld" finden lassen... Da muss ich wohl mal sehen, ob und wo ich dazu Informationen finde...

Beta-User

Zitat von: xenos1984 am 14 September 2021, 09:46:36
Das "return" am Ende eines vorherigen if sagt natürlich das gleiche, aber das im vorherigen Zweig zu suchen, um herauszufinden, wann eine if/elsif abgefragt wird, finde ich wiederum kontra-intuitiv...
...wie angedeutet, ist vermutlich auch eine Gewohnheitsfrage...

Zitat
Ich habe mir inzwischen angesehen, welche Abfragen es noch gibt, und da sind einige Dabei, bei denen ein Parameter direkt im Namen der aufgerufenen Funktion steckt (sowas wie "network.interface.wan" + "down" oder "led.broadband" + "0" etc.) und man die nicht durch einen statischen Hash aufteilen kann, [...]
Eventuell gibt es Möglichkeiten, das zu mischen. Macht -  ::) - RHASSPY z.B. so, dass gefragt wird, ob es eine CODE-Referenz auf eine Dispatch-Funktion gibt, und wenn nicht, dann geht es eben (nach einem return für den anderen Fall 8) ) mit der nächsten if-Abfrage weiter...

Zitat
Übrigens gibt es doch nichts schöneres als eine stabile API... Letzte Nacht [...]
Arg, sowas ist ärgerlich... Du hast mein Mitgefühl!
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Beta-User

fyi: Es gibt hier eine User-Anfrage dazu. Evtl. wäre es sinnvoll, irgendwie eine (interne) Debugging-Ausgabe zu ermöglichen, um z.B. die jeweils empfangenen JSON sichtbar zu machen?

Ergänzend: Man kann einzelne Infos auch im Geräte-Hash versteckt vorhalten, indem man den Namen mit einem Punkt beginnen läßt (das braucht dann aber Quotes...), falls da sensible Daten drin stehen sollten. Um sowas anzuzeigen gibt es dann einen Schalter in "global", und ich meine mich entsinnen zu können, dass es die Option gibt, bestimmte Dinge auch als "privat" zu markieren.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

xenos1984

Zitat von: Beta-User am 14 September 2021, 16:38:14
fyi: Es gibt hier eine User-Anfrage dazu.
Danke für die Info! Ich habe auch schon eine PM dazu bekommen.

Zitat
Evtl. wäre es sinnvoll, irgendwie eine (interne) Debugging-Ausgabe zu ermöglichen, um z.B. die jeweils empfangenen JSON sichtbar zu machen?
An und für sich sollte mit verbose 5 genau das passieren, d.h. alle gesendeten und empfangenen Daten werden geloggt.

Zitat
Ergänzend: Man kann einzelne Infos auch im Geräte-Hash versteckt vorhalten, indem man den Namen mit einem Punkt beginnen läßt (das braucht dann aber Quotes...), falls da sensible Daten drin stehen sollten. Um sowas anzuzeigen gibt es dann einen Schalter in "global", und ich meine mich entsinnen zu können, dass es die Option gibt, bestimmte Dinge auch als "privat" zu markieren.

Hast du einen Vorschlag, was man auf diese Weise versteckt vorhalten sollte? Ich hatte temporäre Daten unter $hash->{helper} oder tiefer unter $hash->{rpc} abgelegt. Das Passwort ist über ein Hilfsmodul gespeichert.

Beta-User

Der Link war v.a. auch, damit ich das ggf. selbst wiederfinde...

Ansonsten hatte ich nicht in den Code geschaut, was wann warum wo landet, und auch Funktionalität war nicht der Schwerpunkt meiner Durchsichten ::) . Wenn bei Bedarf schon ausreichend geloggt wird, ist alles gut :) .

Meine Anmerkungen waren eher in die Richtung "falls es hilreich ist..." gemeint, und manchmal ist es eben für's helfen einfacher, man kann (für typische Stolperfallen) ein list anfordern, wie den User erst mal ins Log zu schicken. Aber auch das bezieht sich nicht auf irgendeine konkrete Stelle im Code :) .
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

xenos1984

Die Funktionalität ist auch etwas schwer zu prüfen, wenn man kein Testsystem / UBUS-fähiges Gerät hat :D

Stimmt, das ist natürlich eine gute Idee. Wenn ich gezählt hätte, wie viele Lists von "DOIF im falschen Zustand" ich schon wie die Suchbilder in der Tageszeitung betrachtet habe... ::) Mit dem Testlauf im Devolo-Thread habe ich schon ein paar Ideen, z.B. den letzten Fehler könnte man als Internal speichern, damit man ihn im List sieht. Ich hatte bisher nur die Strategie "Fehlermeldungen ins Log, Daten zur späteren, internen Verwendung in Internals" auf dem Schirm.

Mit den aktuellen Testergebnissen und Erfahrungen, was die Vielzahl von Implementierungen / APIs angeht, die auf UBUS aufsetzen können, überlege ich, wie ich das Modul am besten flexibel gestalte. Wahrscheinlich ist es wenig sinnvoll, alle Feld, Wald und Wiesenrouter, deren Firmware-Versionen und ihre APIs zu erfassen und zu implementieren. Da bietet es sich wohl eher an, die konkreten Funktionsaufrufe und die Datenauswertung dem User zu überlassen. Das wäre so ähnlich wie beim MQTT-Topic und dessen Payload, die ja auch unterschiedlich sein können.

Im Moment überlege ich sogar, ob es sinnvoll wäre, ein zweistufiges Modul zu benutzen. Die erste Stufe würde die Verbindung als solche aufbauen und verwalten, also Transport-Protokoll (Websocket, HTTP...), Login, Kommunikation mit dem Gerät. Die zweite Stufe würde dann jeweils einen Funktionsaufruf und die Auswertung der Antwort übernehmen. Also so ähnlich wie MQTT2_CLIENT und MQTT2_DEVICE. Das erscheint mir sinnvoll, weil ein Funktionsaufruf u.U. recht komplex sein kann und diverse Parameter braucht (Name der aufgerufenen Funktion, zusätzliche Parameter, Häufigkeit des Aufrufs, Funktion zur Auswertung...), die man dann als Attribute (module, function, parameters, interval, response...) eines zweiten Moduls implementieren könnte, von dem man jeweils eine Instanz pro gewünschtem Funktionsaufruf anlegt. Das hätte auch den Vorteil, dass man nicht ein Gerät mit 100+ Readings hätte, sondern die "Last" etwas verteilt. Bei diesen Attributen müsste man wohl am besten auch Variablen parsen - sofern der User z.B. nicht die Namen aller Ethernet-Ports von Hand eingeben will und sich die zwischen Firmware-Versionen ändern, sondern diese aus Readings ersetzen möchte.

Inzwischen habe ich auch herausgefunden, dass es neben dem "call" Aufruf auch noch andere Anfragen mit anderer Semantik gibt. Darunter ist z.B. ein "subscribe", mit dem man eine Benachrichtigung für bestimmte Ereignisse anfordern kann. Damit ist die Struktur ganz anders, weil es keine einfache Anfrage-Antwort Abfolge mehr ist, sondern man ein Bestellen, viele Benachrichtigungen und ein Abbestellen hätte. Da wäre es vermutlich sogar sinnvoll, ein separates Modul als zweite Stufe zu haben, und zu schauen, wie man am besten den gemeinsamen Code-Anteil verwaltet. Also sowas wie UBUS_CLIENT für die Verbindung, UBUS_CALL und UBUS_SUBSCRIBE für die Inhalte.

Ich bin offen für Kommentare / Kritik :)

Beta-User

Noch schwieriger zu beurteilen...

Allgemeine Anmerkungen:
- viele Teil-Module => viel Erklärungsbedarf (Bauchgefühl sagt: aufsplitten ist eher für "wenn es gar nicht mehr anders geht")
- evtl. ist eine Implementierung verschiedener APIs denkbar (analog Weather, aber den eigentlichen API-Code dann nach ./lib/UBUS/API/*.pm )? Die Attributierung müßte dann halt ggf. "modellabhängig" im Define untergebracht werden. parseParams kann helfen, viele verschiedene Optionen zur Definitionszeit zu ermöglichen, ohne immer alles setzen zu müssen;
- User-Code über Attribute zu ermöglichen, ist evtl. eine gute Idee, hat aber einen Haken: Der sollte im Main-Kontext laufen => evalSpecials und via Analyze.*-Funktionen aufrufen, mappings, Filter etc. ggf. über den Geräte-Hash bereitstellen (analog jsonMap@MQTT2_DEVICE). Das muss man relativ gut vorbereiten, sonst fallen ggf. die User damit auf die Nase.

Insgesamt fühle ich mich nicht hinreichend kompetent und in den konkreten UBUS-Details, um da was "gefühlt fundiertes" sagen zu können, bin also mal gespannt, was wirklich Wissende dazu äußern.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

xenos1984

Danke für die Einschätzung! Inzwischen habe ich mir auch die Doku im Wiki sowie die Implementierung der MQTT2-Module angesehen. Das sieht an und für sich recht überschaubar aus. Die Nachrichten sind beim zweistufigen Modulkonzept immer immer Zeichenketten, da bietet sich also an, die JSON-Nachrichten zu übergeben.

Zitat von: Beta-User am 15 September 2021, 09:33:41
- viele Teil-Module => viel Erklärungsbedarf (Bauchgefühl sagt: aufsplitten ist eher für "wenn es gar nicht mehr anders geht")
Ja, da stimme ich zu. Ich würde auch den Erklärungsbedarf möglichst begrenzen wollen, wobei ich den Eindruck habe, dass das mit dem zweistufigen Aufbau vielleicht sogar einfacher wäre. Wenn man nur ein Modul und damit nur ein Device für ein UBUS-fähiges Gerät hat, landen alle Readings in diesem Device. Wenn der User diese konfiguriert, muss er sich also selbst um diese relativ komplexe Struktur kümmern (und z.B. identische Reading-Namen verhindern). Außerdem könnten die Attribute recht komplex werden, wenn z.B. verschiedene Abfragen unterschiedlich häufig aufgerufen werden sollen. Und für die Auswertung müsste man im Prinzip die ganze Funktionalität von ParseCall in User-Code stecken... Da erscheint es mir zumindest einfacher, pro Funktionsaufruf ein Device anzulegen, das diesen konkreten Aufruf ausführt und sich einfacher konfigurieren lässt. (Wie häufig? Welche Funktion? Welche Parameter? Wie verteilt sich die Antwort auf Readings?) Vielleicht könnte man die Konfiguration mit attrTemplate unterstützen / vereinfachen.

Zitat
- evtl. ist eine Implementierung verschiedener APIs denkbar (analog Weather, aber den eigentlichen API-Code dann nach ./lib/UBUS/API/*.pm )? Die Attributierung müßte dann halt ggf. "modellabhängig" im Define untergebracht werden. parseParams kann helfen, viele verschiedene Optionen zur Definitionszeit zu ermöglichen, ohne immer alles setzen zu müssen;
Das wäre auch eine Möglichkeit, wobei ich den Eindruck habe, dass die API-Vielfalt bei UBUS-Geräten deutlich größer ist. Es handelt sich ja zunächst einmal nur um einen Bus bzw. ein Transport-Protokoll, und da kann jeder Hersteller drauf implementieren, was er möchte. Bei Weather ist es einfacher, weil einfach eine Hand voll APIs unterstützt werden, auf die im Prinzip jeder (Entwickler) zugreifen kann. Um die API eines bestimmten Routers zu implementieren, bräuchte man Informationen über diesen Router, und die ist meistens nur durch Lauschen des Datenverkehrs zu bekommen. Die Implementierung funktioniert dann u.U. nur für diesen konkreten Typ und Firmware-Version. Von daher denke ich, es wäre sinnvoller, dem User zumindest die Möglichkeit einer eigenen Konfiguration der API zu geben (und für "bekannte" Geräte eine fertige Konfiguration anzubieten, die dann idealerweise über den gleichen Mechanismus importiert werden kann -> attrTemplate).

Zitat
- User-Code über Attribute zu ermöglichen, ist evtl. eine gute Idee, hat aber einen Haken: Der sollte im Main-Kontext laufen => evalSpecials und via Analyze.*-Funktionen aufrufen, mappings, Filter etc. ggf. über den Geräte-Hash bereitstellen (analog jsonMap@MQTT2_DEVICE). Das muss man relativ gut vorbereiten, sonst fallen ggf. die User damit auf die Nase.
Stimmt, wobei sich das sicher hinbekommen lässt, wenn man weiß, wie es geht.

Beta-User

Kurz zu AttrTemplate: SetExtensions würden das implizit mitbringen. Wenn man es isoliert haben will, wäre https://svn.fhem.de/trac/changeset/23561/trunk/fhem/FHEM/10_MQTT_GENERIC_BRIDGE.pm ein gutes Beispiel, wie das geht. Da hier vermutlich on/off nicht schaltbar sind, wäre das die m.E. sinnvolle Variante, sonst würde ich zum "Gesamtpaket" raten.

Ansonsten teile ich die Einschätzung, dass größere gerätespezifische Einrichtungsoptionen durch die User damit gut bedient werden könnten.

Wie die templates selbst dann strukturell aussehen müßten, ist vermutlich klar? (Das m.E. einfachste Beispiel wäre https://svn.fhem.de/trac/browser/trunk/fhem/FHEM/lib/AttrTemplate/max.template?order=name)
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

xenos1984

Zitat von: Beta-User am 15 September 2021, 20:13:08
Kurz zu AttrTemplate: SetExtensions würden das implizit mitbringen. Wenn man es isoliert haben will, wäre https://svn.fhem.de/trac/changeset/23561/trunk/fhem/FHEM/10_MQTT_GENERIC_BRIDGE.pm ein gutes Beispiel, wie das geht. Da hier vermutlich on/off nicht schaltbar sind, wäre das die m.E. sinnvolle Variante, sonst würde ich zum "Gesamtpaket" raten.
Richtig, i.A. sollen die Geräte kein on/off als Befehle haben, von daher wäre SetExtensions hier fehl am Platz. Die isolierte Variante hört sich gut an. Was mir noch nicht ganz klar ist, ist der Aufruf von AttrTemplate_Set wenn die Set-Funktion 1. noch andere Befehle verarbeitet und 2. parseParams benutzt. Ersteres sehe ich u.a. bei MAX, da wird sie einfach am Ende von Set aufgerufen, wenn nichts anderes zutrifft. Letzteres finde ich bei HMCCUCHN und HMCCUDEV, aber die Struktur der übergebenen Werte ist mir nicht ganz klar. Hier soll wohl parseParams "rückgängig" gemacht werden?

Zitat
Wie die templates selbst dann strukturell aussehen müßten, ist vermutlich klar? (Das m.E. einfachste Beispiel wäre https://svn.fhem.de/trac/browser/trunk/fhem/FHEM/lib/AttrTemplate/max.template?order=name)
Mehr oder weniger ;) Angesehen habe ich es mir schon mal, weil ich eine AttrTemplate für HTTPMOD und das VLC-Player HTTP Interface erstellen wollte. Das könnte ich eigentlich mal als vorbereitende Fingerübung angehen, ich wollte da ohnehin noch ein paar Dinge implementieren :)

Beta-User

Ja, die Logik ist: Was das Modul selbst nicht versteht, gibt es an SE bzw. AttrTemplate weiter, und wenn die es auch nicht wissen, gibt's die "choose one of ..."-Meldung. Ob man das Array so übergeben kann, wie parseParams das liefert, oder ob man erst wieder ein join q{ } vorab braucht, müßte man (Sandbox-) testen...

Was die "Fingerübung" angeht, ist es vermutlich ein vergleichsweise steiler Einstieg, das aber eher weniger wegen der "Vertemplatung", sondern eher wegen HTTPMOD ;D . Ansonsten ist es eine simple Textersetzung die im wesentlichen besteht aus DEVICE=>Name der Instanz... Alles im Prinzip GAAANZZZZ EINFACH :P .
Wenn es komplizierter werden sollte mit unterschiedlichen Optionen oder Ausbaustufen, gibt es dazu auch Möglichkeiten, aber vielleicht sollten wir mal ein paar Attribute haben und einen Gleitflug absolvieren, bevor wir das Ding mit Düsentriebwerken ausstatten ;D ...

(OT: Das mit VLC hört sich interessant an).
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

xenos1984

In der Zwischenzeit habe ich mir mal die Struktur von ein paar Funktionsaufrufen und den zurückgegebenen Daten angesehen, und frage mich, wie sich diese am besten durch Geräte und Attribute abbilden lassen. Als Veranschaulichung ein Beispiel.

Abfrage der Netzwerkschnittstellen:

{"jsonrpc":"2.0","method":"call","id":"...","params":["...","iwinfo","devices",{}]}

Alle Parameter sind konstant; als Antwort kommt z.B.

{"jsonrpc":"2.0","id":"...","result":[0,{"devices":["wifi0","wifi1","ath1","ath0"]}]}

Als Antwort kommt die Liste der Schnittstellennamen. Wenn man nun mehr über eine Schnittstelle wissen möchte, z.B. die verbundenen Clients, braucht es diese als Parameter:

{"jsonrpc":"2.0","method":"call","id":"...","params":["...","network.info","clients",{"device":"ath0"}]}

Als Antwort kommt die Liste der Clients.

Nun zur Frage der Umsetzung, bei der ich noch nach einer guten Lösung suche. Eine Idee wäre das zweistufige Modulkonzept zu benutzen. Also z.B. ein physikalisches Modul, mit dem man eine Schnittstelle öffnen kann:

define ubus UBUS_CLIENT http://a.b.c.d/ubus
attr ubus username xyz
attr ubus timeout 900

Dieses Modul würde dann die Kommunikation mit dem Gerät übernehmen, d.h. Login, Verbindung mittels WebSocket / HTTP / Kommandozeile etc. sowie Sessions. Für jeden Funktionsaufruf könnte man ein Gerät definieren, das auf das logische Modul zugreift. Beim ersten Aufruf könnte das Gerät z.B. in etwa so aussehen

define ubus_iwinfo UBUS_CALL
attr ubus_iwinfo IODev ubus
attr ubus_iwinfo module iwinfo
attr ubus_iwinfo function devices
attr ubus_iwinfo interval 3600
attr ubus_iwinfo readings {json2nameValue($EVENT)}

...und Readings der Form devices_0 = wifi0 etc. erzeugen. So lange alles statisch ist, sieht die Implementierung relativ gut umsetzbar aus. Mehr Kopfzerbrechen bereitet mir der komplexe Fall, dass man dynamische Informationen übergeben möchte. Also beim zweiten Aufruf:

define ubus_clients UBUS_CALL
attr ubus_clients IODev ubus
attr ubus_clients module network.info
attr ubus_clients function clients
attr ubus_clients interval 60

Nun braucht es aber noch die Schnittstelle als Parameter. So lange man nur eine fest vorgegebene abfragen möchte, könnte man den Parameter als Attribut setzen:

attr ubus_clients params {device => "ath0"}
attr ubus_clients readings {json2nameValue($EVENT, $PARAMS{device})}

Aber wenn man es nun dynamisch haben möchte, wird es trickreicher... Einen Namen abfragen geht noch gut:

attr ubus_clients params {device => ReadingsVal("ubus_iwinfo", "devices_0", "")}

Wenn man nun mehrere haben möchte, muss das Attribut params statt eines einzelnen Parameter-Hash auch eine Liste davon verdauen können, und dann statt eines einzigen Funktionsaufrufs mehrere an das physikalische Gerät senden. Wenn man das nach dem gleichen Schema handhaben wollte, müsste man dem User schon zutrauen, Perl-Code mit map zu basteln, der die Readings ausliest.

Das obige Beispiel hat nur die Parameter als dynamische Größe. Bei meinem Router finde ich aber auch Funktionen der Form

{"jsonrpc":"2.0","method":"call","id":"...","params":["...","network.interface.XXX","up",{}]}

Hier ist der Name XXX des Interface also an einer anderen Stelle zu finden. Demzufolge müsste man diese Attribute ähnlich dynamisch gestalten...

Ideen und Kommentare sind willkommen :)

Beta-User

Irgendwie habe ich bei zweistufig immer noch einen gedanklichen Hänger - kann aber sein, dass ich damit komplett falsch liege.

Die Schnittstellennamen scheinen generisch zu sein. Hat man also zwei UBUS-Instanzen (z.B. für zwei Router), hat man potentiell auch dieselben "devices"-Namen. Wie hält man dann auseinander, was vom jeweiligen UBUS-IO-Modul kommt. Das geht mAn. nur, wenn man die Verbindung zwischen IO und Client ähnlich "hart" vercoded, wie das bei MySensors und MQTT-classic der Fall ist - und das "gehört verboten", um unseren Chef-Architekten zu zitieren...

Eine wirkliche Lösung habe ich dafür nicht anzubieten, aber wenn ich das richtig verstanden habe, liefe zweistufig darauf hinaus, erst den JSON in UBUS auszupacken, dann zu analysieren, die einzelnen Teile wieder einzupacken (hab nicht nachgesehen, aber Dispatch braucht einen "flachen" Text, oder?), mit einem Flag zu kennzeichnen, von welcher UBUS-Instanz sie kommen und dann in den sub-Devices wieder auszupacken.

Es erscheint mir einfacher, die konkreten setter und getter dann eben erst im laufenden Betrieb einer UBUS-Instanz zu ermitteln und diesen Teil dynamisch aufzubauen, eben je nachdem, was tatsächlich gefunden wird (ähnlich, wie CUL_HM das in Abhängigkeit vom Gerätetyp macht).

Hoffe mal, dass sich dazu jemand äußert, der mehr Ahnung von zweistufig hat...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

xenos1984

Zitat von: Beta-User am 19 September 2021, 09:48:24
Die Schnittstellennamen scheinen generisch zu sein. Hat man also zwei UBUS-Instanzen (z.B. für zwei Router), hat man potentiell auch dieselben "devices"-Namen. Wie hält man dann auseinander, was vom jeweiligen UBUS-IO-Modul kommt. Das geht mAn. nur, wenn man die Verbindung zwischen IO und Client ähnlich "hart" vercoded, wie das bei MySensors und MQTT-classic der Fall ist - und das "gehört verboten", um unseren Chef-Architekten zu zitieren...
Mehr oder weniger, ja. (Bei mir heißen sie stattdessen "ethX".) Sollte diese Verbindung zwischen physikalischem und logischem Modul nicht mittels IODev hergestellt werden? D.h. ein logisches Gerät weiß, an welches physikalische Gerät es Anfragen schicken muss, und von wo es Antworten zu erwarten hat.

Zitat
Eine wirkliche Lösung habe ich dafür nicht anzubieten, aber wenn ich das richtig verstanden habe, liefe zweistufig darauf hinaus, erst den JSON in UBUS auszupacken, dann zu analysieren, die einzelnen Teile wieder einzupacken (hab nicht nachgesehen, aber Dispatch braucht einen "flachen" Text, oder?), mit einem Flag zu kennzeichnen, von welcher UBUS-Instanz sie kommen und dann in den sub-Devices wieder auszupacken.
Ich denke, so kompliziert ist es gar nicht. Der Hash des physikalischen Gerätes wird in Parse übergeben, zusammen mit den Daten (und die sind tatsächlich nur ein Text). Also weiß das logische Modul, von welchem Gerät die Daten kommen, und kann das bei der Auswertung berücksichtigen. Also braucht es kein solches Flag und man kann einfach direkt den JSON-Text übergeben. Man könnte höchstens vorher noch per RegEx prüfen, ob es eine Antwort auf ein "call" ist oder eine mit "subscribe" bestelltes Event. Die sind recht verschieden von ihrer Funktionsweise und Auswertung her und könnten vielleicht am besten von zwei getrennten logischen Modulen verarbeitet werden.

Zitat
Es erscheint mir einfacher, die konkreten setter und getter dann eben erst im laufenden Betrieb einer UBUS-Instanz zu ermitteln und diesen Teil dynamisch aufzubauen, eben je nachdem, was tatsächlich gefunden wird (ähnlich, wie CUL_HM das in Abhängigkeit vom Gerätetyp macht).
Das wäre auch eine Möglichkeit. Zu einem gewissen Grad kann man diese Information abfragen. Z.B. gibt es einen Aufruf "list", der die verfügbaren Funktionen und die Datentypen der zu übergebenden Parameter liefert. Allerdings fehlt dann noch der Zusammenhang, was genau diese Funktionen tun und welchen Inhalt diese Parameter haben sollen - das alleine aus den Namen zu schließen erscheint mir nicht möglich... Zumindest basierend auf meinen bisherigen Beobachtungen (zwei Geräte, eins davon mit alter und mit neuer Firmware). Auch die zurückgegebenen Daten unterscheiden sich zwischen Firmware-Versionen bei ansonsten identischem Funktionsaufruf. Normalerweise wird die Client-Software (z.B. als JavaScript-Anwendung in einem Web-Interface) zusammen mit der Firmware verpackt und die entsprechenden Funktionsaufrufe sind hart verdrahtet.
Hoffe mal, dass sich dazu jemand äußert, der mehr Ahnung von zweistufig hat...
[/quote]

rudolfkoenig

ZitatHoffe mal, dass sich dazu jemand äußert, der mehr Ahnung von zweistufig hat...
Bin nicht sicher, ob ich das eigentliche Problem verstehe, falls ich daneben liegen sollte, bitte korrigiert mich.

Logische Module kriegen in der ParseFn den hash des physikalischen Moduls als Parameter mit ($iodev).
IOWrite (wird im logischen Modul verwendet) ruft WriteFn des physikalischen Moduls mit $hash->{IODev} des logischen Moduls auf. Im physikalischen Modul kann man die Daten mit weiteren, vom physikalischen Modul abhaengigen Parameter bereichern.
Das logische Modul muss mAn (bis auf esoterische Faelle, wie bei HM, wo FHEM fuer ein dynamisches Routing zustaendig ist) nichts ueber das physikalische ​Modul wissen.

Die Liste der moeglichen set/get Befehle und Attribute kann fuer jede Instanz des gleichen Moduls unterschiedlich sein.

Dispatch braucht einfache Strings als Parameter, da die Verteilung per Regexp realisiert wird.