433MHz Temperatur-Sensor in HASSIO
Posted on Fri 19 August 2022 in Computer & Electronics
Wie ich in einem früheren Post schon einmal erzählt hatte, habe ich hier diverse billige Funk-Temperatursensoren rumstehen. Die gehören zu einer einfachen Wetterstation, die ich aber aktuell garnicht mehr nutze. Aber es wäre doch ganz schön, wenn ich die Sensoren in meinen Homeassistant einbinden könnte.
Wie schon beim letzten mal beschrieben, ist das Protokoll bekannt und ich sehe zwei denkbare Wege, wie wir das anbinden könnten:
- RTL-SDR Empfänger an den Homeserver und Daten auf selbigem decodieren und irgendwie in HASSIO übertragen.
- RFlink Firmware auf einen Arduino Mega flashen und selbigem noch ein 433MHz Empfangsmodul spendieren. Die Daten werden dann über die serielle Schnittstelle geliefert, die wir dann an HASSIO anbinden müssten.
Beides sind gangbare Wege. Heute wollen wir mal Variante 1 probieren. Die zweite finde ich sehr spannend, aber es stört mich etwas, dass die RFlink Software zwar umsonst, aber nicht open source ist. Letzteres verhindert, dass sie z.B. auf den ESP8266 portiert wird, was viel mehr Sinn machen würde, da der erheblich billiger ist, als ein Arduino Mega und zudem WLAN kann, was die Anbindung an einen MQTT Server erheblich vereinfachen würde. Es scheint forks basierend auf alten Versionen zu geben, aber die scheinen noch keine richtige community zu haben.
Also mit RTL-SDR!
RTL-SDR installieren
Der RTL-SDR ist bekanntermaßen ein sehr preiswerter und dennoch vielseitiger SDR (Software Defined Radio) Receiver, der ursprünglich als DVB-T Empfänger für den Computer verkauft wurde. Inzwischen ist DVB-T obsolet, aber speziell für Hacker ausgelegte Varianten des Sticks erfreuen sich großer Beliebtheit wenn es darum geht Funksignale im computer zu verarbeiten. Ich habe zwei drei davon in der Bastelkiste...
Also Antenne anschrauben und den Stick an den Server anschließen. LINUX erkennt den Empfänger sofort:
> lsusb
[...]
Bus 003 Device 002: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T
Und auch passende Treiber-Module werden automatisch geladen.
Das ist zwar erfreulich, aber dennoch unerwünscht, denn wir wollen ja nicht fernsehen, sondern den Thermometern ihre Geheimnisse entreißen! Das bedeutet, dass wir verhindern wollen, dass der standard-LINUX-Treiber sich das Gerät schnappt:
sudo sh -c 'echo "blacklist dvb_usb_rtl28xxu" > /etc/modprobe.d/blacklist.conf'
Das sollte eventuelle Konflikte zuverlässig vermeiden ("usb_claim_interface error -6").
Daten empfangen und weiterleiten
Nun brauchen wir noch eine Software, die etwas mit den Signalen anfangen kann,
die wir mit dem SDR empfangen. Und die heißt rtl_433
und ist auf
github zu finden. Gängige LINUX
Distributionen bringen sie aber auch gleich mit und so genügt zur Installation
dies:
sudo apt install rtl-433
Nun können wir unseren Lauschangriff starten:
> rtl_433 -C si
rtl_433 version unknown inputs file rtl_tcp RTL-SDR SoapySDR
Use -h for usage help and see https://triq.org/ for documentation.
Trying conf file at "rtl_433.conf"...
Trying conf file at "/home/users/pagel/.config/rtl_433/rtl_433.conf"...
Trying conf file at "/usr/local/etc/rtl_433/rtl_433.conf"...
Trying conf file at "/etc/rtl_433/rtl_433.conf"...
Registered 145 out of 175 device decoding protocols [ 1-4 8 11-12 15-17 19-21 23 25-26 29-36 38-60 63 67-71 73-100 102-105 108-116 119 121 124-128 130-149 151-161 163-168 170-175 ]
Detached kernel driver
Found Rafael Micro R820T tuner
Exact sample rate is: 250000.000414 Hz
[R82XX] PLL not locked!
Sample rate set to 250000 S/s.
Tuner gain set to Auto.
Tuned to 433.920MHz.
Allocating 15 zero-copy buffers
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
time : 2022-08-19 17:15:37
model : inFactory-TH ID : 249
Channel : 2 Battery OK: 1 Temperature: 23.28 C
Humidity : 58 % Integrity : CRC
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
time : 2022-08-19 17:15:37
model : inFactory-TH ID : 249
Channel : 2 Battery OK: 1 Temperature: 23.28 C
Humidity : 58 % Integrity : CRC
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
time : 2022-08-19 17:15:37
model : inFactory-TH ID : 249
Channel : 2 Battery OK: 1 Temperature: 23.28 C
Humidity : 58 % Integrity : CRC
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
time : 2022-08-19 17:15:39
model : inFactory-TH ID : 79
Channel : 1 Battery OK: 0 Temperature: 24.33 C
Humidity : 63 % Integrity : CRC
[...]
Das sieht ja schon ganz gut aus. Und wir haben gelernt, dass der Sensor mit der ID 79 gerne mal eine neue Batterie hätte. Wenn ich jetzt noch wüsste, welcher das ist ...
Freundlicherweise bietet rtl_433
diverse Output-Optionen an, u.a. csv, json
und vor allem mqtt! Und so können wir die Ergebnisse direkt an unseren
MQTT-Server senden. Wie man den aufsetzt erkläre ich hier nicht – das findet
man überall im Netz, u.a. auch in einem älteren
Post von mir. Also werfen
wir das mal auf unserem Server an:
rtl_433 -F mqtt://hal:1883,user=phil,pass='TopSecret'
Um zu sehen, ob und was da ankommt starten wir anderswo einen mqtt client:
mosquitto_sub -u phil -P TopSecret -t '#' -h hal
Und in der Tat kommen dort Daten an:
{"time":"2022-08-19 17:27:10","model":"inFactory-TH","id":172,"channel":2,"battery_ok":1,"temperature_F":73.7,"humidity":62,"mic":"CRC"}
2022-08-19 17:27:10
172
2
1
73.7
62
CRC
{"time":"2022-08-19 17:27:10","model":"inFactory-TH","id":172,"channel":2,"battery_ok":1,"temperature_F":73.7,"humidity":62,"mic":"CRC"}
2022-08-19 17:27:10
172
2
1
73.7
62
CRC
Hm – irgendwie scheint es, dass wir jedes Event einmal im JSON Format und einmal in separaten Zeilen bekommen.
Also lesen wir doch mal den relevanten Abschnitt in man rtl_433
:
Output format option
[ -F kv|json|csv|mqtt|influx|syslog|null ]
Produce decoded output in given format.
Without this option the default is KV output. Use "-F null" to remove
the default.
Append output to file with :<filename> (e.g. -F csv:log.csv),
defaults to stdout.
Specify MQTT server with e.g. -F mqtt://localhost:1883
Add MQTT options with e.g. -F "mqtt://host:1883,opt=arg"
MQTT options are: user=foo, pass=bar, retain[=0|1], <format>[=topic]
Supported MQTT formats: (default is all)
events: posts JSON event data
states: posts JSON state data
devices: posts device and sensor info in nested topics
The topic string will expand keys like [/model]
E.g. -F "mqtt://localhost:1883,user=USERNAME,pass=PASSWORD,retain=0,devices=rtl_433[/id]"
[...]
Wir sind eigentlich nur an events interessiert. Und wir wollen bitte SI-Einheiten – also °C statt F:
rtl_433 -C si -F mqtt://hal:1883,user=phil,pass='TopSecret',retain=0,events=rtl_433[/model][/id]
Das sieht auf der Empfangsseite schon viel aufgeräumter aus:
{"time":"2022-08-19 17:42:23","model":"inFactory-TH","id":249,"channel":2,"battery_ok":1,"temperature_C":23.27778,"humidity":58,"mic":"CRC"}
{"time":"2022-08-19 17:42:24","model":"inFactory-TH","id":249,"channel":2,"battery_ok":1,"temperature_C":23.27778,"humidity":58,"mic":"CRC"}
{"time":"2022-08-19 17:42:24","model":"inFactory-TH","id":249,"channel":2,"battery_ok":1,"temperature_C":23.27778,"humidity":58,"mic":"CRC"}
rtl_433
als service
Das funktioniert zwar schon sehr schön, aber sobald wir das Programm abbrechen
ist die ganze Pracht dahin. D.h. als nächstes müssen wir dafür sorgen, dass
rtl_433
quasi als daemon/service auf unserem Server läuft.
Dazu erzeugen wir ein entsprechendes service file für systemd
(/etc/systemd/system/rtl_433.service
):
# See https://github.com/merbanan/rtl_433/issues/1651 for original
# suggestion
[Unit]
Description=RTL_433 service
Documentation=man:rtl_433
StartLimitIntervalSec=10
After=syslog.target network.target
[Service]
Type=exec
ExecStart=/usr/bin/rtl_433 -c /etc/rtl_433.conf
Restart=always
RestartSec=30s
# View with: sudo journalctl -f -u rtl_433 -o cat
SyslogIdentifier=rtl_433
[Install]
WantedBy=multi-user.target
Und wir brauchen noch ein config file für rtl_433
selbst
(/etc/rtl_433.conf
):
convert si
output mqtt://hal:1883,user=phil,pass='TopSecret',retain=0,events=rtl_433[/model][/id]
report_meta time:utc
Sobald wir das alles haben können wir unseren neuen Service starten:
systemctl start rtl_433
systemctl daemon-reload
und mal prüfen, ob er auch läuft:
> sudo systemctl status rtl_433.service
● rtl_433.service - RTL_433 service
Loaded: loaded (/etc/systemd/system/rtl_433.service; disabled; vendor preset: en>
Active: active (running) since Sat 2022-08-20 10:11:52 CEST; 22s ago
Docs: man:rtl_433
Main PID: 1040053 (rtl_433)
Tasks: 2 (limit: 19096)
Memory: 2.9M
CPU: 313ms
CGroup: /system.slice/rtl_433.service
└─1040053 /usr/bin/rtl_433 -c /etc/rtl_433.conf
Aug 20 10:11:52 hal rtl_433[1040053]: Registered 145 out of 175 device decoding proto>
Aug 20 10:11:52 hal rtl_433[1040053]: Found Rafael Micro R820T tuner
Aug 20 10:11:52 hal rtl_433[1040053]: Exact sample rate is: 250000.000414 Hz
Aug 20 10:11:52 hal rtl_433[1040053]: [R82XX] PLL not locked!
Aug 20 10:11:52 hal rtl_433[1040053]: Sample rate set to 250000 S/s.
Aug 20 10:11:52 hal rtl_433[1040053]: Tuner gain set to Auto.
Aug 20 10:11:52 hal rtl_433[1040053]: Tuned to 433.920MHz.
Aug 20 10:11:52 hal rtl_433[1040053]: Allocating 15 zero-copy buffers
Aug 20 10:11:54 hal rtl_433[1040053]: MQTT Connected...
Aug 20 10:11:54 hal rtl_433[1040053]: MQTT Connection established.
An dieser Stelle wäre noch zu erwägen, diesen Service nicht als root
laufen
zu lassen, denn rtl_433
benötigt nur normale user permissions und aus
Security-Sicht ist es sinnvoll, einem Service nicht mehr Rechte zu geben, als
er wirklich braucht. D.h. wir legen einen speziellen user dafür an:
sudo useradd rtl_433
Außerdem muss dieser user Zugriff auf USB-Geräte haben. D.h. er muss Mitglied der
Gruppe plugdev
sein:
sudo adduser rtl_433 plugdev
Damit der Service unter diesem User läuft ändern wir die [Service]
Sektion in
/etc/systemd/system/rtl_433.service
indem wir User
und Group
wie folgt
festlegen:
[Service]
Type=exec
User=rtl_433
Group=rtl_433
ExecStart=/usr/bin/rtl_433 -c /etc/rtl_433.conf
Und wir ändern die Besitzverhältnisse des Config files:
sudo chown rtl_433:rtl_433 /etc/rtl_433.conf
Nun noch den Service neu starten
sudo systemctl restart rtl_433.service
sudo systemctl daemon-reload
Und schon passt das:
> ps aux | grep rtl_433
rtl_433 1023646 1.2 0.0 56256 7516 ? Ssl 23:20 0:04 /usr/bin/rtl_433 -c /etc/rtl_433.conf
[...]
Zum Abschluss sollten wir noch sicherstellen, dass der Service automatisch gestartet wird:
sudo systemctl enable rtl_433
Homeassistant konfigurieren
Nun müssen wir unserem Homeassistant noch erklären, das da neue Sensoren sind.
Dazu müssen wir auf dem Server das configuration.yaml
file editieren. In
einer HASSIO Installation liegt das in /config
, so auch in meinem Docker
Container. Dieses Verzeichnis ahbe ich mir als Docker-Volume auf dem Server
unter /srv/homeassistant
bereitgestellt. Folgendes muss dort hinein:
sensor:
- platform: mqtt
name: Temp_Wohnzimmer
device_class: temperature
unit_of_measurement: '°C'
value_template: '{{ value_json.temperature_C | round(1) }}'
state_topic: rtl_433/inFactory-TH/79
json_attributes_topic: rtl_433/inFactory-TH/79
- platform: mqtt
name: Hum_Wohnzimmer
device_class: Humidity
unit_of_measurement: '%'
value_template: '{{ value_json.humidity }}'
state_topic: rtl_433/inFactory-TH/79
json_attributes_topic: rtl_433/inFactory-TH/79
Für jeden Sensor brauchen wir zwei solche Einträge. Die Details müsst Ihr natürlich Euren konkreten Sensoren und IDs anpassen.
Achtung: Bei meinen Sensoren ändert sich die ID jedes mal, wenn die Batterie
gewechselt wird. Dann muss ich die Items state_topic
und
json_attributes_topic
entsprechend anpassen.
Und nun ist es soweit – im Web Interface von Homeassistant können wir nun eine
entsprechende Karte anlegen und die ganzen Sensoren darstellen. Die einzelnen Sensoren findet Ihr
unter Config > Devices & Services > Entities
. Und im Dashboard Editor kann man sie ganz normal
aus der Liste auswählen:
Und schon haben wir alle eingebunden: