Ble2mqtt

Aus FHEMWiki
Version vom 9. November 2023, 12:24 Uhr von Drhirn (Diskussion | Beiträge) (Baustelle entfernt)
Ble2mqtt
Zweck / Funktion
Anwesenheitserkennung von Bluetooth-Geräten
Allgemein
Typ Utilities
Details
Dokumentation Thema
Support (Forum) Unterstützende Dienste
Modulname ble2mqtt
Ersteller PatrickR
Wichtig: sofern vorhanden, gilt im Zweifel immer die (englische) Beschreibung in der commandref!

ble2mqtt ist ein Script, dass den Anwesenheitsstatus eines oder mehrerer Bluetooth Low Energy Geräte überprüfen und das Ergebnis an einen MQTT Broker schicken kann.

Es verwendet dazu die Linux Tools bluetoothctl und gatttool.

ble2mqtt ist in Perl geschrieben und wurde von PatrickR erstellt. Weitere Informationen finden sich in diesem Forums-Topic. Fragen dazu bitte in den Bereich Unterstützende Dienste

Voraussetzungen

  • Debian od. Raspberry Pi OS
  • Bluetooth Empfänger

Parameter

Beim Start von ble2mqtt können folgende Parameter angegeben werden:

Parameter Beschreibung Standardwert Beispiel Optional
--mqttserver Die Adresse des MQTT Brokers inklusive Port --mqttserver mqtt.example.org:1883 nein
--mqttfingerprint Der Fingerprint des für die TLS-verschlüsselte Kommunikation mit dem MQTT Broker verwendeten Zertifikates --mqttfingerprint bfe5d244a821194230f1479fe2f8f7bbcd2a8cb8 ja
--mqtttopic An welches MQTT-Topic das Tool die Informationen schicken soll. ble2mqtt --mqtttopic ble2mqtt/Wohnzimmer ja
--mqttuser Der Benutzernamen, unter dem die Verbindung zum Broker hergestellt werden soll --mqttuser ble2mqtt ja bzw. abhängig vom Broker
--mqttpass Das zum Benutzernamen gehörende Passwort --mqttpass verysecret3 ja bzw. abhängig vom Broker
--retain Versendet die Nachrichten present und lastseen als retained. 0 --retain ja
--daemonize Wenn ble2mqtt im Hintergrund (als Daemon) ausgeführt werden soll 0 --daemonize ja
--loglevel Wie und was genau protokolliert werden soll. Mögliche Loglevel sind LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. LOG_INFO --loglevel LOG_ERR ja
--logtarget Wohin gelogged werden soll. Mögliche Optionen sind syslog und stdout. syslog --logtarget stdout ja
--absentinterval Anzahl an Sekunden, nach denen das Gerät als abwesend bezeichnet werden soll. Mindestwert ist 30. 60 --absentinterval 120 ja
--rssithreshold Ab welcher Abweichung des RSSI Wertes ein Update getriggert werden soll. Mindestwert ist 5. 10 --rssithreshold 8 ja
--watchdogthreshold Ab welchem Zeitraum in Sekunden nach Empfangen des letzten BT-Signals ble2mqtt neu gestartet werden soll. Mindestwert ist 30. Der Wert 0 bedeutet deaktiviert, also nie. 0 --watchdogthreshold 30 ja
--debug Kann nur gemeinsam mit --daemonize verwendet werden, um Logeinträge auf stdout auszugeben. Je höher die Nummer, desto mehr Output. 0 --daemonize --debug 4 ja
--mac Filtert mittels Regex nach MAC-Adressen der von ble2mqtt zu beachtenden BLE-Geräte. Alle anderen Geräte werden ignoriert. 77:88:99:00:AA:BB)' ja
--batterymaxage Zeit in Stunden, nach denen ein Batterie-Wert als veraltet angesehen und aktualisiert wird. Der Wert 0 deaktiviert den Batterie-Check. 48 --batterymaxage 6 ja
--forcebattery Nur in Kombination mit --mac. Erzwingt einen Batterie-Check, auch wenn das battery-service am Gerät nicht erkannt wird. 0 --forcebattery ja

Beispiele

  • Broker verlangt Username/Passwort; es sollen keine Batterie-Werte ausgelesen werden
ble2mqtt --mqttserver mqtt.example.org:1883 --mqttuser ble2mqtt --mqttpass verysecret3 --batterymaxage 0
  • Broker verlangt keine Authentifizierung; es soll nach zwei G-Tags gesucht werden; definiertes MQTT-Topic; Batteriewerte benötigt
ble2mqtt --mqttserver mqtt.example.org:1883 --mqtttopic ble2mqtt/Wohnzimmer --mac '(11:22:33:44:55:66|77:88:99:00:AA:BB)'
  • Broker verlangt keine Authentifizierung; Geräte sollen nach 45s ohne Kontakt als abwesend angezeigt werden; MQTT Nachrichten sollen gespeichert bleiben
ble2mqtt --mqttserver mqtt.example.org:1883 --absentinterval 45 --retain

Installation

Zur Installation muss das ble2mqtt-Script auf den Server kopiert werden und es müssen diverse Linux- und Perl-Pakete installiert werden.


Zuerst das Script nach /usr/local/bin kopieren, um beim Aufruf in der Bash nicht immer den ganzen Pfad voranstellen zu müssen.

sudo cp -a ble2mqttd /usr/local/bin/

Danach das Script noch als ausführbar markieren

sudo chmod +x /usr/local/bin/ble2mqttd

Benötigte Linux Pakete

Die benötigten Pakete können ganz einfach mit apt installiert werden. Dabei werden auch mögliche Abhängigkeiten automatisch installiert.

sudo apt install -y libssl-dev libio-socket-ssl-perl libreadonly-perl libtest-expect-perl libnet-ssleay-perl libnet-server-perl

Benötigte Perl-Module

Es wird das Modul Net::MQTT::Simple (libnet-mqtt-simple-perl) benötigt. Das ist - Stand 08.11.2023 - noch nicht in den Stable-Repositories von Debian verfügbar und muss daher mittels cpan installiert werden. Voraussetzung dafür ist das Modul CPAN::DistnameInfo, welches zuerst installiert werden muss.

sudo cpan CPAN::DistnameInfo
sudo cpan Net::MQTT::Simple

Sobald libnet-mqtt-simple-perl verfügbar ist, kann dieses statt der Perl-Module wie ein normales Linux-Paket installiert werden.

Erster Test

Mit einem einfachen Aufruf von ble2mqtt kann getestet werden, ob alle benötigten Module und Pakete vorhanden sind.

sudo /usr/local/bin/ble2mqttd

Erstellen einens Systemd-Daemons

Damit ble2mqtt bei jedem Systemstart automatisch gestartet wird, muss ein Systemd-Daemon erstellt werden. Das wird gemacht, in dem eine neue Textdatei mit folgendem Inhalt angelegt und der Daemon dann aktiviert wird.


Info blue.png
Wichtig: Das Kommando, mit welchen Parametern ble2mqtt gestartet wird, muss natürlich individuell angepasst werden!


sudo nano /etc/systemd/system/ble2mqttd.service
[Unit]
Description=ble2mqttd
After=bluetooth.target

[Service]
Type=simple
Restart=always
ExecStart=/usr/local/bin/ble2mqttd --mqttserver mqtt.example.com:1883 --mqtttopic ble2mqtt/wohnzimmer --mac '(11:22:33:44:55:66|77:88:99:00:AA:BB)' --absentinterval 30 --mqttuser <username> --mqttpass <password>

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable ble2mqttd.service
sudo systemctl start ble2mqttd.service

Einbindung in FHEM

Da ble2mqtt alle Informationen an einen MQTT-Broker sendet, können diese Informationen ganz einfach als MQTT2_DEVICE in FHEM eingebunden werden. Wie dabei vorgegangen werden kann, unterscheidet sich je nach Anwendungsbereich und den eigenen Vorlieben.

Als Beispiel sie hier die Anwesenheitserkennung mittels BT-Tags angeführt.

Anwesenheitserkennung mittels BT-Tags

Readings eines Devices, dass mit diesem Code erstellt wurde

Der folgende Code geht davon aus, dass es mehrere ble2mqtt-Instanzen gibt und ein BT-Tag mit der MAC-Adresse AA:BB:CC:DD:EE:FF erkannt werden soll.

Die verschiedenen ble2mqtt-Instanzen schicken ihre Nachrichten in unterschiedliche Topics. Für jede Instanz ein eigenes Topic:

  • ble2mqtt/wohnzimmer
  • ble2mqtt/schlafzimmer
  • ble2mqtt/kueche

Im Attribut devicetopic werden diese Topics dann gemeinsam ausgewertet. Das funktioniert, in dem der Name des Subtopics einfach durch den Regex-Ausdruck .* ersetzt wird.

Bedeutet aber auch, dass die Informationen - wenn gewünscht - wieder in unterschiedliche Readings zerlegt werden müssen. Um z.B. schnell zu erkennen, in welchem Raum der BT-Tag gerade liegt. Das passiert hier z.B. bei den Nachrichten für rssi und presence. Daraus resultieren dann eigene Readings für jedes MQTT-Subtopic.

Das "raumübergreifende" Reading presence wird anhand der des Alters in Sekunden der letzten lastseen-Nachricht gesteuert. Die lastseen-Nachricht wird immer gesendet, wenn der Scanvorgang von ble2mqtt das gewünschte Gerät findet.

define ble2mqttGTag MQTT2_DEVICE FHEM
attr ble2mqttGTag devicetopic ble2mqtt/.*/AA_BB_CC_DD_EE_FF
attr ble2mqttGTag event-on-change-reading .*
attr ble2mqttGTag readingList ble2mqtt/.*/heartbeat:.* heartbeat
$DEVICETOPIC/rssi:.* {my $room=(split m{[/]}x,$TOPIC)[1]; my $roomMax=$room; my $rssiMax=$EVENT; my @readings = grep { $_ =~ m{\Arssi_(?!$room).*}x } keys %{$defs{"$NAME"}->{READINGS}}; for (@readings) {my $rssiTmp=ReadingsVal($NAME,$_,'-100'); my $roomTmp=(split m{[_]}x,$_)[1]; if(($rssiMax gt $rssiTmp)&&(ReadingsVal($NAME,"presence_".$roomTmp,'absent') eq 'present')) {$rssiMax=$rssiTmp; $roomMax=$roomTmp}}; {rssi=>$rssiMax,room=>$roomMax,"rssi_$room"=>$EVENT}}
$DEVICETOPIC/lastseen:.* {lastseen=>strftime "%Y-%m-%d %H:%M:%S", localtime($EVENT)}
$DEVICETOPIC/present:.* {my $roomAct=(split m{[/]}x,$TOPIC)[1]; my $rssi=ReadingsVal($NAME,"rssi_".$roomAct,'-100'); my $presenceAct=$EVENT?'present':'absent'; my $room=$EVENT ? $roomAct:''; my @readings = grep { $_ =~ m{\Apresence_(?!$roomAct).*}x } keys %{$defs{$NAME}->{READINGS}}; for (@readings) {if(ReadingsVal($NAME,$_,'absent') eq 'present') {my $roomTmp=(split m{[_]}x,$_)[1]; my $rssiTmp=ReadingsVal($NAME,"rssi_".$roomTmp,'-100'); if($rssi gt $rssiTmp){$room=$roomTmp; $rssi=$rssiTmp}}}; {"presence_$roomAct"=>$presenceAct, room=>$room}}
ble2mqtt/.*/state:.* state
$DEVICETOPIC/battery:.* batteryLevel
attr ble2mqttGTag userReadings presence {if (ReadingsAge($NAME,"lastseen",0)>60) {return "absent";}else{return "present";}}

Das Ergebnis ist unter anderem ein Reading presence mit den Zuständen absent und present. Es kann somit ganz einfach als "presenceDevice" in einem ROOMMATE-Device verwendet werden:

attr rr_Franz rr_presenceDevices ble2mqttGTag