ZWaveJS2MQTT: Anbindung an FHEM mittels MQTT2

Begonnen von krikan, 10 September 2021, 18:14:48

Vorheriges Thema - Nächstes Thema

krikan

Habe https://github.com/zwave-js/zwavejs2mqtt mittels Docker aufgesetzt und experimentiere mit der Anbindung an FHEM mittels MQTT2.

Derzeitige Einstellungen in zwavejs2mqtt:
- Topic type: ValueIDtopics
- Payload type: JSON time value (bei just value fehlten die Angaben zum Zustand der WAKEUP-Devices sleep/awake)
- Send ZWave Events ist ausgeschaltet

FHEM:
autocreate am MQTT2_SERVER-Device steht auf complex.
Das MQTT2-DEVICE sieht derzeit folgendermaßen aus:
Internals:
   CID        ZWAVE_GATEWAY_zwavejs2mqtt
   DEF        ZWAVE_GATEWAY_zwavejs2mqtt
   DEVICETOPIC MQTT2_ZWAVE_GATEWAY_zwavejs2mqtt
   FUUID      613234ef-f33f-a83c-92fc-6aca4fa719f8c8ed
   IODev      fhemMqtt
   LASTInputDev fhemMqtt
   MSGCNT     2
   NAME       MQTT2_ZWAVE_GATEWAY_zwavejs2mqtt
   NR         354
   STATE      ???
   TYPE       MQTT2_DEVICE
   fhemMqtt_MSGCNT 2
   fhemMqtt_TIME 2021-09-10 17:20:59
   READINGS:
     2021-09-10 17:20:57   IODev           fhemMqtt
     2021-09-10 17:20:59   status_value    true
     2021-09-10 17:20:59   version_value   5.5.3
Attributes:
   bridgeRegexp zwave/(nodeID_\d+)[/]?.*:.* "zwave_$1"
   readingList ZWAVE_GATEWAY_zwavejs2mqtt:zwave/_CLIENTS/ZWAVE_GATEWAY-zwavejs2mqtt/status:.* { my $a=$EVENT; $a=~s/.time.:\d{13},?//;json2nameValue($a, 'status_', $JSONMAP) }
ZWAVE_GATEWAY_zwavejs2mqtt:zwave/_CLIENTS/ZWAVE_GATEWAY-zwavejs2mqtt/version:.* { my $a=$EVENT; $a=~s/.time.:\d{13},?//;json2nameValue($a, 'version_', $JSONMAP) }
   room       MQTT2_DEVICE


Für jede Nodeid werden damit gesonderte Devices der Art MQTT2_zwave_nodeID_NN angelegt.

Die readingList-Werte löschen vor dem Auspacken per json2nameValue aus dem Payload "time":<time> heraus. Die Info halte ich momentan für überflüssig und will nicht noch mehr Readings generieren.

Frage zur Anlage der readingList, da zwavejs2mqtt sehr viele Infos/Topics liefert und dementsprechend viele Zeilen im readingList generiert werden, die ich manuell bisher Zeile für Zeile um "my $a=$EVENT; $a=~s/.time.:\d{13},?//;" erweitert habe: Kann ich das irgendwie -außer über notify auf global Event ATTR- automatisieren?  (Hier sind es nur 2 readingList Zeilen, aber beim Philio Bewegungsmelder sind es bereits 35!)

Ein wenig Arbeit könnte ich mir evtl. noch mit der direkten Manipulation von $EVENT sparen, aber das ist auch nicht zielführend. Es bleibt das Anpacken aller readingList-Zeilen.

Andere Ideen und Ansätze sind auch willkommen :-)

Gruß, Christian


rudolfkoenig

Vorneweg: Ich habe nicht genau verstanden, was das Problem ist.
So ins "blaue" geraten:
Hilft jsonMap nicht? Etwas nach 0 gemapped verhindert das Anlegen.
Argument 4 beim json2nameValue ist ein positiver Filter, Argument 5 ein negativer Filter (jeweils Regexp).

Um besser helfen zu koennen: kannst du bitte ein JSON anhaengen?

Beta-User

Öhm, spontan und daher möglicherweise nicht richtig:

Eigene myUtils-Routine hernehmen, die den JSON "vorbehandelt". Vielleicht schaust du dir mal die myUtils (contrib) zu ebus (da gibt's einen wrapper für json2nameValue()) und dem go-e-charger mal an.

Meine Idee wäre, den timestamp dann gleich für das Readings-update mit zu verwerten und gar keinen Hash mehr aus der sub zurückzugeben...

Aber ohne die von Rudi angefragten JSON-Beispiele wird das schwierig...
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

krikan

#3
zwave/nodeID_1/status:{"time":1631215140561,"value":true,"status":"Unknown","nodeId":1}
zwave/nodeID_18/status:{"time":1631215140579,"value":true,"status":"Asleep","nodeId":18}
zwave/nodeID_18/48/0/Tamper:{"time":1631215140604,"value":false}


Die "time":<time> möchte ich weg haben, da bei autocreate=complex daraus mAn überflüssige "XY_time"-Readings werden. "time":<time> kann auch am Ende des Payloads stehen, statt wie in den obigen Beispielen vorne.

Eure Vorschläge muss ich mir noch anschauen, dazu später ggfs. mehr.

krikan

Zitat von: rudolfkoenig am 10 September 2021, 18:29:18
Hilft jsonMap nicht? Etwas nach 0 gemapped verhindert das Anlegen.
Bei jsonMap funktioniert ein Regex im Attributwert nicht; hatte ich aus der commandref auch nicht anders erlesen.
.*_time:0
Müsste zum vollständigen Löschen also wieder viel tippen oder Routine erstellen.

Zitat von: rudolfkoenig am 10 September 2021, 18:29:18
Argument 4 beim json2nameValue ist ein positiver Filter, Argument 5 ein negativer Filter (jeweils Regexp).
Da das in der commandref nicht steht  (Absicht?) , kannte ich das nicht.
Getestet mit negfilter '.time.:\d{13},?': Payload wird dann insgesamt nicht verarbeitet und nicht nur begrenzt auf den matchenden Wert time.

Zitat von: Beta-User am 10 September 2021, 18:31:36
Eigene myUtils-Routine hernehmen, die den JSON "vorbehandelt". Vielleicht schaust du dir mal die myUtils (contrib) zu ebus (da gibt's einen wrapper für json2nameValue()) und dem go-e-charger mal an.
Dazu brauche ich länger, hatte gehofft so etwas umgehen zu können. Habe heute schon (gefühlt) Stunden auf die MQTT2 attrTemplate-Beispiele gestarrt, um zu lernen/verstehen.

Zitat von: Beta-User am 10 September 2021, 18:31:36
Meine Idee wäre, den timestamp dann gleich für das Readings-update mit zu verwerten und gar keinen Hash mehr aus der sub zurückzugeben...
Du meinst statt dem Timestamp der automatisch über ReadingsUpdate gesetzt wird, den aus dem Payload zu nehmen? Hatte ich auch schon mal darüber nachgedacht, aber aus erst mal verworfen, da der Readingstimestamp, dann nicht mehr wie in FHEM üblich dem Erzeugungszeitpunkt des Readings entspricht. .Irgendwie habe ich dabei ein bisher nicht weiter hinterfragtes Störgefühl.

Beta-User

Wenn es wirklich nur darum geht, den time-Teil rauszufiltern, ist Rudi's Vorschlag mit dem 4. Argument bei json2nameValue() der direkte Weg. Das ist ein regex für die Keys, die man weghaben will.

Das sollte ein guter Startpunkt sein:
{ json2nameValue($EVENT,undef,$JSONMAP,'time|nodeId' ) }
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

krikan

So bspw.
json2nameValue($EVENT, 'wakeUpInterval_', $JSONMAP,'','time')
wird time ausgefiltert und nur "wakeUpInterval_value" als Reading gesetzt. Also ist das Ergebnis so, wie bei meiner oben gezeigten Anpassung der readingsList.
In Antwort #4 hatte ich beim Test das 4. Argument in json2nameValue nicht mit '' definiert. Ob das oder mein Regex im negFilter zum Problem in #4 geführt haben, habe ich nicht getestet.

Das Ergebnis der erzeugten Readings passt jetzt zwar, aber wie passe ich jetzt einfach bspw. diese readingList an:
zwave/nodeID_18/status:.* { json2nameValue($EVENT, 'status_', $JSONMAP) }
zwave/nodeID_18/49/0/Air_temperature:.* { json2nameValue($EVENT, 'Air_temperature_', $JSONMAP) }
zwave/nodeID_18/48/0/Tamper:.* { json2nameValue($EVENT, 'Tamper_', $JSONMAP) }
zwave/nodeID_18/48/0/Door-Window:.* { json2nameValue($EVENT, 'Door-Window_', $JSONMAP) }
zwave/nodeID_18/49/0/Illuminance:.* { json2nameValue($EVENT, 'Illuminance_', $JSONMAP) }
zwave/nodeID_18/112/0/2:.* { json2nameValue($EVENT, '2_', $JSONMAP) }
zwave/nodeID_18/112/0/3:.* { json2nameValue($EVENT, '3_', $JSONMAP) }
zwave/nodeID_18/112/0/4:.* { json2nameValue($EVENT, '4_', $JSONMAP) }
zwave/nodeID_18/112/0/5:.* { json2nameValue($EVENT, '5_', $JSONMAP) }
zwave/nodeID_18/112/0/6:.* { json2nameValue($EVENT, '6_', $JSONMAP) }
zwave/nodeID_18/112/0/8:.* { json2nameValue($EVENT, '8_', $JSONMAP) }
zwave/nodeID_18/112/0/9:.* { json2nameValue($EVENT, '9_', $JSONMAP) }
zwave/nodeID_18/112/0/10:.* { json2nameValue($EVENT, '10_', $JSONMAP) }
zwave/nodeID_18/112/0/11:.* { json2nameValue($EVENT, '11_', $JSONMAP) }
zwave/nodeID_18/112/0/12:.* { json2nameValue($EVENT, '12_', $JSONMAP) }
zwave/nodeID_18/112/0/13:.* { json2nameValue($EVENT, '13_', $JSONMAP) }
zwave/nodeID_18/112/0/20:.* { json2nameValue($EVENT, '20_', $JSONMAP) }
zwave/nodeID_18/112/0/21:.* { json2nameValue($EVENT, '21_', $JSONMAP) }
zwave/nodeID_18/112/0/22:.* { json2nameValue($EVENT, '22_', $JSONMAP) }
zwave/nodeID_18/112/0/7/8:.* { json2nameValue($EVENT, '8_', $JSONMAP) }
zwave/nodeID_18/112/0/7/16:.* { json2nameValue($EVENT, '16_', $JSONMAP) }
zwave/nodeID_18/112/0/7/32:.* { json2nameValue($EVENT, '32_', $JSONMAP) }
zwave/nodeID_18/112/0/7/64:.* { json2nameValue($EVENT, '64_', $JSONMAP) }
zwave/nodeID_18/113/0/Access_Control/Door_state:.* { json2nameValue($EVENT, 'Door_state_', $JSONMAP) }
zwave/nodeID_18/114/0/manufacturerId:.* { json2nameValue($EVENT, 'manufacturerId_', $JSONMAP) }
zwave/nodeID_18/114/0/productType:.* { json2nameValue($EVENT, 'productType_', $JSONMAP) }
zwave/nodeID_18/114/0/productId:.* { json2nameValue($EVENT, 'productId_', $JSONMAP) }
zwave/nodeID_18/128/0/level:.* { json2nameValue($EVENT, 'level_', $JSONMAP) }
zwave/nodeID_18/128/0/isLow:.* { json2nameValue($EVENT, 'isLow_', $JSONMAP) }
zwave/nodeID_18/132/0/wakeUpInterval:.* { json2nameValue($EVENT, 'wakeUpInterval_', $JSONMAP,'','time') }
zwave/nodeID_18/132/0/controllerNodeId:.* { json2nameValue($EVENT, 'controllerNodeId_', $JSONMAP) }
zwave/nodeID_18/134/0/libraryType:.* { json2nameValue($EVENT, 'libraryType_', $JSONMAP) }
zwave/nodeID_18/134/0/protocolVersion:.* { json2nameValue($EVENT, 'protocolVersion_', $JSONMAP) }
zwave/nodeID_18/134/0/firmwareVersions:.* { json2nameValue($EVENT, 'firmwareVersions_', $JSONMAP) }
zwave/nodeID_18/134/0/hardwareVersion:.* { json2nameValue($EVENT, 'hardwareVersion_', $JSONMAP) }


Letztlich betrifft das alle per autocreate complex generierten readingList-Zeilen aller ZWave-Nodes.

Irgendwie sehe ich noch nicht den einfachen Weg.

krikan

Eventuell ist mein Problem noch nicht klar genug. Neuer Versuch:
In allen Payloads zu jedem Topic, die von ZWaveJS2MQtt kommen, ist ein key "time" enthalten, den ich nicht brauche und auch dazu kein Reading haben möchte.
Wie kann ich das am Einfachsten ausfiltern, ohne dass ich jedes Devices mit der jeder einzelnen readingList-Zeile manuell bearbeiten muss?

Beta-User

Soweit mir bekannt, kann man bei autocreate nur akzeptieren, dass eine der beiden json2nameValue-Varianten kommt.

Das einzige mir auf die Schnelle einfallende Hilfsmittel, das in diese Richtung geht, ist der zu ebus-myUtils gehörende "analyzer", der die per complex generierten readingList-Einträge durchgeht, und dann durch (teils zur Topic-Struktur passende) wrapper-Funktionen ersetzt. Vermutlich wäre das hier auch sinnvoll, weil diese /n/m/o/w-Topicstrukturen danach riechen, dass überallher ähnliche Infos kommen, die man dann nicht mehr ohne weiteres auseinanderhalten kann. Kann aber auch falsch sein...
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

rudolfkoenig

Ich habe das Gefuehl, dass autocreate fuer solche Daten nicht optimal ist.

Man koennte alle Geraete manuell mit so einem readingList anlegen:
zwave/nodeID_18/.* { $TOPIC =~ m,([^/]*)$,;; json2nameValue($EVENT, "$1_", undef, ".", "time") }


Das erzeugt aus
mosquitto_pub -t 'zwave/nodeID_18/48/0/Door-Window' -m '{"time":1631215140561,"value":true,"status":"Unknown","nodeId":1}'
folgende Readings:
Door-Window_nodeId 1
Door-Window_status Unknown
Door-Window_value true


Der folgende (etwas esoterische) readingList:
zwave/nodeID_18/.* { $TOPIC =~ m,([^/]*)$,; hashKeyRename(json2nameValue($EVENT, "$1_"), "((.*))_status", "(.*)_status") }

erzeugt aus dem obigen Input nur ein Reading:
Door-Window     Unknown

krikan

Zitat von: rudolfkoenig am 10 September 2021, 23:15:16Ich habe das Gefuehl, dass autocreate fuer solche Daten nicht optimal ist.
"Solche Daten" verknüpft mit meinen Wünschen(!) sind doch vermutlich Spezialfälle.  :)

Zitat von: rudolfkoenig am 10 September 2021, 23:15:16
Man koennte alle Geraete manuell mit so einem readingList anlegen:
zwave/nodeID_18/.* { $TOPIC =~ m,([^/]*)$,;; json2nameValue($EVENT, "$1_", undef, ".", "time") }
Das gefällt mir. Wenn ich ein passendes notify auf Event DEFINED ansetze, ist das vmtl. auch einfach automatisierbar und ich erspare mir das riesige Attibut readingList mit x Einzelzeilen.

Als Nebeneffekt habe ich jetzt einen einfachen Ansatz, wie ich die Readings durch passende Regex auf die Form "/48/0/Door-Window_.*" bekomme. Dann sollte die setList auch halbwegs schnell umsetzbar sein, da man zum Befehlsabgabe einfach /set mit passendem Argument anhängen kann  (hier falls zulässig bspw.: /48/0/Door-Window/set").

Jedoch würde mich bei setList auch eine noch einfachere Umsetzung interessieren:
Gibt es beim Attribut setList die Möglichkeit mehrere Commands mit einer Zeile analog obiger readingsList anzulegen? Beispiele habe ich nicht gefunden und laut commandref und meinen Anwender-Versuchen wird bei Attribut setList im Parameter cmd kein Perl geschluckt. Zum Experimentieren wäre es schön, für jedes Reading ein set-Command zu haben. Also quasi ein set-Command mit Namen des Readings, das als Befehl den (gesäubert) Readingsnamen ergänzt um "/set" nutzt.

rudolfkoenig

ZitatGibt es beim Attribut setList die Möglichkeit mehrere Commands mit einer Zeile analog obiger readingsList anzulegen?
Mir faellt nur was Indirektes, mit etlichen Schoenheitsfehler ein: ein Befehl kann einen Perl-Ausdruck ausfuehren. Wenn man die gewuenschten Befehlsnamen als Parameter zu einem festen Befehl (z.Bsp. cmd) akzeptiert, dann koennte der Perl-Ausdruck diesen zu fhem("set $mqtt_server publish $topic $message") zusammenbasteln.