Ein neues MQTT2 Gerät in Betrieb nehmen

Begonnen von Otto123, 06 Dezember 2020, 00:26:50

Vorheriges Thema - Nächstes Thema

Otto123

Mal sehen: vielleicht wird das ein Teil vom MQTT2 Workshop, eine Anleitung oder nur eine Mitschrift. Am Ende brauche ich bestimmt noch Ideen und Hilfe.
Ich will erstmal ohne Template auskommen, ich tue mal so als gibt es noch keins für meinen Fall.
Ich mag es simpel und "generisch" - also MQTT2 :) und ein bisschen FHEM - aber nicht noch viele Geräte dazu.
Mein Ziel: Anwesenheit an bestimmten Orten feststellen.
Zutaten: Smartphone, Owntracks, MQTT Server in der Cloud
Die bisherigen Ansätze mit owntracks und Livetracking hier im Forum habe ich gelesen ;)
Der erste Schritt:
ein MQTT2_CLIENT zu meiner MQTT Cloud Instanz mit der der Owntracks Dienst auf meinem Smartphone auch verbunden ist. Er sollen Geräte per autocreate erzeugt werden:
define mqtt2Cloud MQTT2_CLIENT ...
attr mqtt2Cloud clientId fhem1
attr mqtt2Cloud autocreate simple

Und jetzt in der App mal eine Nachricht senden (im Hauptmenu der Pfeil nach oben)
Es entsteht sofort ein spannendes Device :)
defmod MQTT2_fhem1 MQTT2_DEVICE fhem1
attr MQTT2_fhem1 IODev mqtt2Cloud
attr MQTT2_fhem1 readingList fhem1:owntracks/user/device:.* { json2nameValue($EVENT) }
attr MQTT2_fhem1 room MQTT2_DEVICE

mit einer Reihe Readings über den aktuellen Standort.
Bridgedevice
Alles von diesem MQTT2_CLIENT mqtt2Cloud landet jetzt in diesem Device. Das will ich nicht, also muss ich den Datenstrom irgendwie lenken: bridgeRegexp
Damit werden die MQTT Nachrichten neu aufteilt und dabei nach Geräten sortiert. Wie mach ich das? Wo muss ich schauen?
Wie sieht denn die Nachricht/der Topic aus? offenbar so: owntracks/UserName/DeviceName - gibt es eine Doku dazu? Ja! Und auch die Json Beschreibung.
Subscription: owntracks/+/+ weitere Topic cmd, waypoints, event, info
Ein bridgeRegexp müsste also folgendes tun: owntracks ermitteln (wäre ein fester String), UserName DeviceName dynamisch ermitteln
Hilfsmittel https://regex101.com/ - und regExp Lernprogramm ;)
Beim probieren und etwas suchen lerne ich diesen Ausdruck:  [^/:]
Die Zeichenmenge / und : ([/:] match auf diese Zeichen) aber durch ^ wird umgekehrt: match auf alles außer / und :
Die Webseite sagt: man muss die / schützen -> \/
owntracks\/([^\/:]+)\/([^\/:]+).*:.*
Die Klammern() extrahieren das, was innerhalb gefunden wird, in Variablen $1 $2 $3 usw.
Eine neue Bridge und das ursprüngliche Device wird gelöscht
Edit: der Code etwas geändert, Rudi meint escapen muss man nicht und der : kann auch entfallen.
define MQTT2_owntracks_bridge MQTT2_DEVICE
attr MQTT2_owntracks_bridge IODev mqtt2Cloud
attr MQTT2_owntracks_bridge bridgeRegexp  owntracks/([^/]+)/([^/]+).*:.* "owntracks_$1_$2"
attr MQTT2_owntracks_bridge room MQTT2_DEVICE
delete MQTT2_fhem1,FileLog_MQTT2_fhem1

Die nächste Aktualisierung legt ein neues Gerät an, welches mit Ausnahmen des Namens genau wie oben aussieht. Das will ich jetzt noch weiter konfigurieren, z.B. Steuerelemente einbauen:
readingList ergänzen:
Die readingList kann sich automatisch noch mit anderen Topics füllen.
Der erste Eintrag verhindert genau dies, schreibt aber die Raw Payload in ein Reading zum experimentieren und verstehen.
Die beiden anderen Topics kenn ich aus der Doku oder sie sind schon automatisch entstanden, der event zur Standortanzeige und waypoints zum -einlesen aus der App.
owntracks/user/device.* raw
owntracks/user/device/event:.* { json2nameValue($EVENT,'',$JSONMAP) }
owntracks/user/device/waypoints:.* { json2nameValue($EVENT) }

setList setzen:
Um komplette Json Beispiele aus der Doku in die Kommandozeile zu werfen und testen.
x_raw_payload:textField { my $payload = $EVENT;$payload =~ s/$EVTPART0 //; qq(owntracks/user/device/cmd $payload)}
getList verwenden
ein get Befehl ist vom Inhalt her genauso wie ein set Befehl - aber er wartet darauf, dass das Reading (welches in readingList erscheinen muss) gesetzt wird.
getList cmd reading [topic|perl]
Man kann sich in dem Fall mit dem raw Reading behelfen, wenn man "hole" Befehle nicht in die setList sondern in die getList schreiben will.
location:noArg raw owntracks/user/device/cmd {"_type":"cmd","action":"reportLocation"}
waypoints:noArg raw owntracks/user/device/cmd {"_type":"cmd","action":"waypoints"}

userReadings für die Ermittlung
Mein Ziel könnte ich mit dem Reading Place erreichen, welches entweder einen meiner Standorte(Waypoints) oder das Wort "away" enthält.
Das Gerät erzeugt zwei Events:

  • enter
  • leave
Also schreibe ich das Reading place bei jedem Event
place:event.* {my $evt=ReadingsVal($name,'event','');;my $desc=ReadingsVal($name,'desc','nowhere');;if ($evt eq 'leave'){$desc='away'};;return $desc}

Das weitere wird noch Feintuning, aber bis hierher läuft es schon?
Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

rudolfkoenig

ZitatDie Webseite sagt: man muss die / schützen -> \/
Das stimmt zwar fuer den Fall, wenn man den Regexp mit / hinschreibt, also "text" =~ m/owntracks\/([^\/:]+)/, aber nicht, wenn der Inhalt als Variable (wie in unserem Fall) uebertragen wird.

ComputerZOO

Hallo Otto,
ich suche gerade eine Möglichkeit um Owntracks zu nutzen ohne Löcher in meine Firewall zu bohren bzw. eine dauerhafte VPN-Verbindung aufzubauen.
Da kommt mir dieser Lösungsansatz sehr gelegen. Welchen MQTT Server in der Cloud kannst du da empfehlen?

Einen schönen zweiten Advent von der Ostsee.

Otto123

Hi,

naja empfehlen wäre zu viel gesagt, aus meinen Notizen:
ZitatMQTT über Internet
Steve hat hier eine Übersicht über kostenfreie MQTT Broker erstellt.
Bei myqtthub.com gibt es einen "open Plan" der kostenfrei einen MQTT Broker bietet (Oktober 2020).
Bei cloudmqtt.com gab es mal die "cute cat" kostenfrei.
Nachtrag Juli 2020:  Mein Account funktioniert weiter, obwohl das Angebot nicht mehr existiert:
Ich nutze dafür weiter die cloudmqtt man braucht ja dafür nicht viel Traffic. Die Langzeiterfahrung ist auch gut: Ich musste einmal die Instanz in der Cloud neu starten und man wird nicht belästigt. ;)

Gruß Otto
Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

Otto123

#4
Mein MQTT2_DEVICE weiß jetzt ob mein Smartphone an einem bestimmten Ort ist oder unterwegs. Das simple userReading place funktioniert.
Jetzt noch ein bisschen Feintuning ...
Zunächst eine simple Verbesserung des Codes: devicetopic damit kann man anschließend den Code einheitlich mit der Variablen $DEVICETOPIC schreiben.
attr MQTT2_owntracks_user_device devicetopic owntracks/user/device
readingList
Man sieht ja per default diesen perl Code{ json2nameValue($EVENT) } - geht da auch was anderes?
Was macht der eigentlich? Auszug aus der Doku: __perl expression, which has to return a hash consisting of readingName=>readingValue entries__
Immer wenn ich diesen Code Ausdruck in perl sehe "readingName=>readingValue" werde ich erfürchtig - hatte ich vorher in meinem Leben nie gesehen. Ob ich sowas auch zu Fuß hinbekomme?
Die Standorte aus meiner owntracks App hätte ich gern als kompletter Json Wert jeweils in einem Reading.
$DEVICETOPIC/waypoints:.* {$EVENT=~/.*\[(.*)\]/;my $evt=$1;;$evt=~s/\},\{/\} \{/g;my @way=split(' ',$evt);my %Hash;my $c=0;foreach (@way) {$Hash{"Waypoint_$c"}=$way[$c];$c++};return{%Hash}}
Aus {...[{WP1},{WP2},{WP3}]...} wird so etwas: {Waypoint_1=>{WP1},Waypoint_2=>{WP2},Waypoint_3=>{WP3}}
Ziel erreicht, fragt mich nicht wie lange das gedauert hat. ;)  Wäre schön wenn da einer der Perl Spezies sagt: geht viel einfacher :)

Damit die Readings etwas lesbarer sind, kann man sie "umbenennen" (die alten bleiben stehen, müsste man dann mal löschen)
attr MQTT2_owntracks_user_device jsonMap acc:accuracy alt:altitude batt:batteryPercent lat:latitude lon:longitude vac:accuracyVertical vel:velocity
setList
Noch zwei setList Einträge um Standorte setzen (oder verändern) zu können und um den Modus zu ändern. Mal sehen ob das Sinn machen kann, in bestimmten Situationen von "Signifikante Änderung" auf "Bewegungsmodus" zu wechseln
attr MQTT2_owntracks_user_device setList waypoints:textField $DEVICETOPIC/cmd {"_type":"cmd","action":"setWaypoints","waypoints":{"waypoints":[$EVTPART1],"_type":"waypoints"}}\
mode:1Quite,2Manual,3Significant,4Move {$EVTPART1=~/(\d)/;;my $pl=$1-2;;qq($DEVICETOPIC/cmd {"_type":"cmd","action":"setConfiguration","configuration":{"_type":"configuration","monitoring":$pl}})}

Den Waypoint kann man im gleichen Syntax "einwerfen" wie er oben ausgelesen wird {"_type"  : "waypoint",elements} - wobei tst (Timestamp) der primäre Key ist, das bedeutet man kann existierende Waypoints überschreiben!
Beim "mode" wird aus einem Wort eine Zahl erzeugt, da fiel mir nichts Besseres ein. Vielleicht hat da jemand eine schönere Idee?
BTW: Man kann in der setList irgendwelchen Code ausführen, wenn er im MQTT2_DEVICE selbst nichts auslösen soll, muss man am Ende undef zurückgeben!
userReadings
Ich habe eine Weile darüber nachgedacht, ob man überhaupt userReadings machen sollte, oder ob man den Code auch gleich in readingList abhandeln könnte. Ich glaube userReadings ist doch irgendwie einfacher?

  • Die location in einer Form die man einfach bei Google Maps einsetzen kann
  • und die connection im Klartext. offline gibt es laut owntracks Handbuch, ich weiß noch nicht, wie das dann übertragen wird ;)
attr MQTT2_owntracks_user_device userReadings location:lat.* {ReadingsNum($name,'latitude',0).','.ReadingsNum($name,'longitude',0)},\
connection:conn.* { my $conn = ReadingsVal($name,'conn','error');;($conn eq 'w')?'wifi':($conn eq 'm')?'mobil':'offline'}


Jetzt muss ich noch mal in das schon existierende Template schauen, ob ich da noch Ideen bekomme oder sogar Verbesserungsfeedback zurück geben kann?
Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

Beta-User

Zitat von: Otto123 am 07 Dezember 2020, 11:39:37
Wäre schön wenn da einer der Perl Spezies sagt: geht viel einfacher :)
"Eigentlich" fühle ich mic bei "Perl Spezie" nicht wirklich angesprochen, und ohne irgendeinen Muster-JSON zum Spielen ist es auch nicht so einfach...

Das hier (oder eine Zwischen-Iteration auf dem Weg dahin) könnte klappen:
$DEVICETOPIC/waypoints:.* { $EVENT=~/\[(.*)\]/;my $evt=$1;$evt=~s/\},\{/\} \{/g; my %Hash; my @way=split(' ',$evt); for (my $i=0;$i<=way;$i++) { $Hash{"Waypoint_$i"}=$way[i] }; return \%Hash }
Vermutlich geht es mit einem "map" nochmal kompakter, und der Weg bis zum split sieht mir auch umständlich aus, aber ohne Spielmterial traue ich mich da auch nicht ran ::) .

ZitatJetzt muss ich noch mal in das schon existierende Template schauen, ob ich da noch Ideen bekomme oder sogar Verbesserungsfeedback zurück geben kann?
Na ja, viele der exisitierenden Templates sind nur so schlau wie der, der es erstellt hat bzw. der, der den Anstoß dazu gegeben hat den Weg mitgehen wollte. Irgendwie sind die alle (hoffentlich) funktional, aber nicht unbedingt "optimal" (so es das gibt).

Von daher: room for improvement ist ganz sicher vorhanden, und ich freue mich, wenn jemand kommt und zeigt, wie es (noch) besser geht :) .

Danke auch für diesen Beitrag zum "workshop", den Eingangsbeitrag habe ich mit ein paar kleinen Kommentaren versehen dazugenommen, aber das hast du ja ganz bestimmt schon gesehen.

[OT] Falls jemand das mit einem shellyplug S und den ganzen event-on-change-reading & Co. - Attributen ähnlich aufziehen würde, wäre vermutlich vielen geholfen, die verstehen wollen, was zum einen eigentlich die ganzen MQTT2_DEVICE-spezifischen Attribute bewirken und/oder zum anderen, wie man die "event-on"-Attributfamilie sinnvoll einsetzt...
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

ZitatWäre schön wenn da einer der Perl Spezies sagt: geht viel einfacher :)
Dafuer braeuchte man Rohdaten.
Wenn dein Beispiel stimmt, dann sollte json2nameValue das erwartete Ergebnis liefern.

Beta-User

Zitat von: Otto123 am 07 Dezember 2020, 11:39:37
Wäre schön wenn da einer der Perl Spezies sagt: geht viel einfacher :)
Na ja, bei diesem Spielchen hatte ich mal unterstellt, dass da wieder JSON-Pakete in einem Array drin waren, die durch json2nameValue() "zu weit" aufgebrochen worden wären. In dem owntracks-Thread neulich ging es grade darum, dass das betreffende FHEM-Modul dann eigentlich Roh-Daten im JSON-Format benötig...?

(Sowas wie ein anonymisierter Test-JSON wäre in der Tat hilfreich.)
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

Otto123

#8
na klar doch:
{"_type":"waypoints","waypoints":[{"_type":"waypoint","desc":"someHome","lat":50.1234567,"lon":13.1234567,"rad":80,"tst":1604958453},{"_type":"waypoint","desc":"Lattenhalle","lat":51.333967,"lon":12.244327,"rad":30,"tst":1605016074},{"_type":"waypoint","desc":"Farbenkontor","lat":51.3277511,"lon":12.319985,"rad":200,"tst":1606468640},{"_type":"waypoint","desc":"Someplace","lat":46.935260881,"lon":10.428771973,"rad":188,"tst":1437552714}]}
ich möchte daraus Readings mit jeweils dem kompletten Waypoint - die weitere Aufdröselung der json Inhalte finde ich in dem Fall nicht sinnvoll, das macht die Readings nur unübersichtlich.
Readings1 => {"_type":"waypoint","desc":"someHome","lat":50.1234567,"lon":13.1234567,"rad":80,"tst":1604958453}
Readings2 => {"_type":"waypoint","desc":"Lattenhalle","lat":51.333967,"lon":12.244327,"rad":30,"tst":1605016074}
Readings3 => {"_type":"waypoint","desc":"Farbenkontor","lat":51.3277511,"lon":12.319985,"rad":200,"tst":1606468640}
Readings4 => {"_type":"waypoint","desc":"Someplace","lat":46.935260881,"lon":10.428771973,"rad":188,"tst":1437552714}


Die Anzahl ist natürlich nicht vorhersehbar kann ein Waypoint sein aber sicher auch 10 :)

Als Textaufgabe: den Inhalt von [json,json] extrahieren, das drumherum wegwerfen und die durch Komma getrennten json Strings einzeln in Readings werfen.
Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

rudolfkoenig

{ my (%h,$cnt);; $EVENT=~ s/(\{[^[]*?})/$h{"Readings".++$cnt}=$1/ge;; \%h }

Ich verstehe zwar nicht, wieso man mit einem JSON als Reading-Wert gluecklich wird, aber das war auch nicht Teil der Aufgabe :)

TomLee

#10
Versteh nur Bahnhof was genau du willst, versuche es aber nachzuvollziehen, mit Filter kann man mit den gewonnenen Readings wenigstens was anfangen ? :
 
defmod MQTT2_root.1607344388244 MQTT2_DEVICE root.1607344388244
attr MQTT2_root.1607344388244 IODev mqtt2s
attr MQTT2_root.1607344388244 readingList root.1607344388244:tele/bla:.* { json2nameValue($EVENT,'','$JSONMAP','desc|lon|lat') }
attr MQTT2_root.1607344388244 room MQTT2_DEVICE

setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_1_desc someHome
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_1_lat 50.1234567
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_1_lon 13.1234567
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_2_desc Lattenhalle
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_2_lat 51.333967
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_2_lon 12.244327
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_3_desc Farbenkontor
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_3_lat 51.3277511
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_3_lon 12.319985
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_4_desc Someplace
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_4_lat 46.935260881
setstate MQTT2_root.1607344388244 2020-12-07 14:08:19 waypoints_4_lon 10.428771973
[/s]

edit:
Näher mit beschäftigt und jetzt erst verstanden um was es genau geht

Otto123

#11
Ich danke euch für den Stoff :)
@TomLee was anfangen ist ja relativ, ich finde die Wegpunkte sind rein informativ. In meiner Art den json String zu haben -> da weiß man wenigsten genau wie er aussehen muss wenn man einen neuen Wegpunkt setzen will ;) Aber ja, mit den Filtern wird es vielleicht erträglich.

mich interessierte ja auch zu erfahren: was muss der Perlcode im readingList denn wirklich machen. Da habe ich jetzt eine leise Vorstellung von :)

@Rudi vielen Dank, da brauche ich heute Abend ne Flasche Wein um diesen Code zu verstehen. Aber ich glaube das wird schon :) immerhin habe ich jetzt schon eine leise Ahnung - ich schau auch schon ne halbe Stunde drauf.

Der Versuch der interaktiven Erklärung für die Nachwelt :)
{ my (%h,$cnt);; $EVENT=~ s/(\{[^[]*?})/$h{"Readings".++$cnt}=$1/ge;; \%h }

Anfang und Ende sind ja noch einfach

  • $cnt ist ein Counter %h ein Hash
  • Am Ende wird mit \ die Referenz auf den Hash %h ausgegeben.
Der Mittelteil ist spannend - Suchen und ersetzen im $EVENT wird zur Schleife über die Fundstücke

  • s/regExp/Ausdruck/ suchen und "ersetzen" -> g führt die Suche über den gesamten String aus, e bewirkt das der Ausdruck ausgeführt wird. Es wird eigentlich nicht ersetzt, sondern der Ausdruck ausgeführt
  • Die Erklärung vom regExp findet man detailliert im Link.
  • $h{key} -> dem Hash Key "Readings".++$cnt wird das Suchergebnis $1 zugewiesen.
  • jedesmal wenn ++$cnt aufgerufen wird, wird $cnt um eins erhöht bevor die Variable den Wert erhält
Viele Grüße aus Leipzig  ⇉  nächster Stammtisch an der Lindennaundorfer Mühle
RaspberryPi B B+ B2 B3 B3+ ZeroW,HMLAN,HMUART,Homematic,Fritz!Box 7590,WRT3200ACS-OpenWrt,Sonos,VU+,Arduino nano,ESP8266,MQTT,Zigbee,deconz

TomLee

#12
Hey Otto,

nur so nebenbei eine Idee für ein mögliches devstateIcon, muss nicht aber könnte in die Richtung gehen  8), mag den Gedanken nur mal festhalten, ein Beitrag zu weblink von dir selbst hat mich heute Mittag auf den Ansatz gebracht:

userReadings:
location_url:lat.* {my @ar = split ',',ReadingsVal($name,'location','0,0');;\
sprintf(q(<iframe src="https://maps.google.com/maps?hl=en&q=%s,%s&t=k&z=19&ie=UTF8&iwloc=&output=embed" width=600 height=400></iframe>),$ar[0],$ar[1])}


devstateIcon:
{ReadingsVal($name,'location_url','0,0')}

edit:

oder besser so, falls man die url noch wo anders braucht:

location_url:lat.* {my @ar = split ',',ReadingsVal($name,'location','0,0');;\
sprintf(q(https://maps.google.com/maps?hl=en&q=%s,%s&t=k&z=19&ie=UTF8&iwloc=&output=embed),$ar[0],$ar[1])}


{sprintf(q(<iframe src="%s" width=600 height=400></iframe>),ReadingsVal($name,'location_url','0,0'))}