Let op: Tweakers stopt per 2023 met Tweakblogs. In
dit artikel
leggen we uit waarom we hiervoor hebben gekozen.
Push data to Luftdaten and OpenSenseMap API with Home Assistant
Over the last few weeks I've been working on a nice tinkering project with my oldest son (10). We wanted to create a little bird house containing a few environmental sensors and get that data into Home Assistant. But because sharing = caring, we also wanted to push the data to several Citizen Science projects such as Luftdaten (also called sensor.community) and OpenSenseMap. This blog will describe the steps we took to make this happen. All Home Assistant code is also available in my GitHub repo.
:strip_exif()/f/image/fCLoylqM5pT90NaM6f5eugOk.jpg?f=fotoalbum_small)
:strip_exif()/f/image/4BbKyzrIfKujfWSqPLUE1eQ4.jpg?f=fotoalbum_small)
:strip_exif()/f/image/mwYJin33iqNLruoBcoV7ZxPR.jpg?f=fotoalbum_small)
The ESPHome firmware was built as follows:
Initial upload was really easy by using the ESPHome Flasher tool - just connect the device with a USB cable, install the correct drivers (see sensor.community building instructions) and you are good to go.
/f/image/Eq9xx6ht7I6tU9JLEHWuIPO3.png?f=fotoalbum_small)
So that works! The device is now pushing data to Home Assistant, ready for us to play with. Let's start playing.
Change the following items to show the data from your account:
And the work-around to get the !secret data into the rest template (Source at my GitHub):
:strip_exif()/f/image/nMhbeOU67HaKRQTTDJJ8sm4b.jpg?f=fotoalbum_small)
:strip_exif()/f/image/G6Qj7JWlIuISXJu97vApmldS.jpg?f=fotoalbum_small)
:strip_exif()/f/image/3Jq03oCT9S1RlrN77Zm2S5jg.jpg?f=fotoalbum_small)
(Still need to put it on the wall - scheduled for later this week)
At OpenSenseMap:
/f/image/aepLIJ9PqOwFMm8mVfJW4RMO.png?f=fotoalbum_small)
Luftdaten info at Madavi:
/f/image/aG2O3erYx2Km2wdGH8xkkuR4.png?f=fotoalbum_small)
Building the sensor
We started building the sensor with the sensor.community building instructions, using the following components:- NodeMCU ESP8266
- SDS011 Particle Sensor
- BME280 Temperature/Pressure/Humidity Sensor
:strip_exif()/f/image/fCLoylqM5pT90NaM6f5eugOk.jpg?f=fotoalbum_small)
:strip_exif()/f/image/4BbKyzrIfKujfWSqPLUE1eQ4.jpg?f=fotoalbum_small)
:strip_exif()/f/image/mwYJin33iqNLruoBcoV7ZxPR.jpg?f=fotoalbum_small)
Firmware
After plugging all the components together, it was time to flash the firmware. We initially started using the default Luftdaten firmware, but overnight I was thinking that this did not match the "local first, cloud when I want to" philosophy that I use for my Home Assistant. That is why we decided to flash the NodeMCU with the ESPHome software and push data directly into my Home Assistant instance and, from there, push it towards different cloud APIs. This opposite to letting the device push it to the cloud first and then collect it from the cloud into Home Assistant. As added benefit the device now lives in a different, restricted, subnet without direct internet access.The ESPHome firmware was built as follows:
YAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
| esphome: name: weerhuisje platform: ESP8266 board: nodemcuv2 wifi: networks: - ssid: "<<My SSID>>" password: "<<My WiFi Password>>" ap: ssid: "Weerhuisje AP" password: "<<Backup AP Password>>" manual_ip: static_ip: 10.0.1.2 gateway: 10.0.1.1 subnet: 255.255.255.0 power_save_mode: none captive_portal: web_server: logger: # Enable Home Assistant API api: reboot_timeout: 5min ota: time: - platform: homeassistant id: homeassistant_time timezone: Europe/Amsterdam # Enable UART for SDS011 uart: rx_pin: D1 tx_pin: D2 baud_rate: 9600 # Enable i2c for BME280 i2c: sda: D5 scl: D6 sensor: - platform: sds011 pm_2_5: name: "Particulate Matter <2.5µm Concentration" accuracy_decimals: 2 pm_10_0: name: "Particulate Matter <10.0µm Concentration" accuracy_decimals: 2 update_interval: 30min - platform: bme280 temperature: name: "BME280 Temperature" accuracy_decimals: 2 pressure: name: "BME280 Pressure" accuracy_decimals: 4 humidity: name: "BME280 Humidity" accuracy_decimals: 2 update_interval: 5min address: 0x76 - platform: uptime name: "Weerhuisje uptime" update_interval: 60s - platform: wifi_signal name: "Weerhuisje WiFi signaal" update_interval: 60s binary_sensor: - platform: status name: "Weerhuisje Status" text_sensor: - platform: wifi_info ip_address: name: Weerhuisje IP Address ssid: name: Weerhuisje Connected SSID bssid: name: Weerhuisje Connected BSSID - platform: version name: Weerhuisje Version |
Initial upload was really easy by using the ESPHome Flasher tool - just connect the device with a USB cable, install the correct drivers (see sensor.community building instructions) and you are good to go.
Integrating with Home Assistant
When the device was done booting, I found a bunch of new sensors in my Home Assistant:/f/image/Eq9xx6ht7I6tU9JLEHWuIPO3.png?f=fotoalbum_small)
So that works! The device is now pushing data to Home Assistant, ready for us to play with. Let's start playing.
REST commands
In order to upload the data to the various APIs, one needs to create a few rest commands in HA. We would like to push to Luftdaten (and their associates at Madavi.de) and OpenSenseMap. I'll leave it up to you to navigate to their websites and set-up your accounts there (Madavi uses the same Sensor-ID as Luftdaten). These accounts will need to be set up first to make this work. Source at my GitHubYAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
| # rest_command.yaml, included from configuration.yaml ################################## ## Push values to Air Quality APIs ################################## # The BME280 pressure value in HA is in hPa - the APIs expect it in Pa, hence the # multiplication of the value in the templates below. # Push to Luftdaten API. Luftdaten uses headers to distinguish between different sensor # types, so we need to push twice. The X-Sensor header contains the sensorID from Luftdaten, # typically formatted as esp8266-12345678 or similar. send_luftdaten_pm: url: https://api.sensor.community/v1/push-sensor-data/ method: POST content_type: 'application/json' headers: X-Pin: 1 ## This tells Luftdaten that it is SDS011 data X-Sensor: !secret luftdaten_x_sensor payload: > { "software_version": "HomeAssistant-{{ states('sensor.current_version') }}", "sensordatavalues":[ {"value_type":"P1","value":"{{ states('sensor.particulate_matter_10_0um_concentration') }}"}, {"value_type":"P2","value":"{{ states('sensor.particulate_matter_2_5um_concentration') }}"} ] } send_luftdaten_tph: url: https://api.sensor.community/v1/push-sensor-data/ method: POST content_type: 'application/json' headers: X-Pin: 11 ## This tells Luftdaten that it is BME280 data X-Sensor: !secret luftdaten_x_sensor payload: > { "software_version": "HomeAssistant-{{ states('sensor.current_version') }}", "sensordatavalues":[ {"value_type":"temperature","value":"{{ states('sensor.bme280_temperature') }}"}, {"value_type":"pressure","value":"{{ states('sensor.bme280_pressure') | float * 100 }}"}, {"value_type":"humidity","value":"{{ states('sensor.bme280_humidity') }}"} ] } # Push to Madavi. This is related to Luftdaten and stores data for use in Grafana. send_madavi: url: https://api-rrd.madavi.de/data.php method: POST content_type: 'application/json' headers: X-Pin: 0 X-Sensor: !secret luftdaten_x_sensor payload: > { "software_version": "HomeAssistant-{{ states('sensor.current_version') }}", "sensordatavalues":[ {"value_type":"SDS_P1","value":"{{ states('sensor.particulate_matter_10_0um_concentration') }}"}, {"value_type":"SDS_P2","value":"{{ states('sensor.particulate_matter_2_5um_concentration') }}"}, {"value_type":"BME280_temperature","value":"{{ states('sensor.bme280_temperature') }}"}, {"value_type":"BME280_pressure","value":"{{ states('sensor.bme280_pressure') | float * 100 }}"}, {"value_type":"BME280_humidity","value":"{{ states('sensor.bme280_humidity') }}"} ] } # Push to OpenSenseBox / OpenSenseMap. The url !secret contains the openSenseBox API url, # which looks like https://api.opensensemap.org/boxes/<<yoursenseboxid>>/data # The input_text items contain the sensor-IDs you need to publish the data to the API. # You can find those on your SenseBox page on https://opensensemap.org/account post_opensensebox: url: !secret opensensebox_api_url method: post headers: content-type: "application/json; charset=utf-8" payload: >- { "{{ states('input_text.opensensebox_sensorid_temp') }}": "{{ states('sensor.bme280_temperature') }}", "{{ states('input_text.opensensebox_sensorid_press') }}": "{{ states('sensor.bme280_pressure') | float * 100 }}", "{{ states('input_text.opensensebox_sensorid_hum') }}": "{{ states('sensor.bme280_humidity') }}", "{{ states('input_text.opensensebox_sensorid_pm25') }}": "{{ states('sensor.particulate_matter_2_5um_concentration') }}", "{{ states('input_text.opensensebox_sensorid_pm10') }}": "{{ states('sensor.particulate_matter_10_0um_concentration') }}" } |
Secrets
I'm using secrets above to keep the API endpoints for my sensors off GitHub. As the template payload for OpenSenseMap doesn't support the use of !secret, I worked around this by using input_text items.Change the following items to show the data from your account:
YAML:
1
2
3
4
5
6
7
8
9
| # secrets.yaml luftdaten_x_sensor: esp8266-99999999 opensensebox_api_url: https://api.opensensemap.org/boxes/<<YourSenseBoxID>>/data opensensebox_sensorid_temp: 123456789012345678901231 opensensebox_sensorid_press: 123456789012345678901232 opensensebox_sensorid_hum: 123456789012345678901233 opensensebox_sensorid_pm25: 123456789012345678901234 opensensebox_sensorid_pm10: 123456789012345678901235 |
And the work-around to get the !secret data into the rest template (Source at my GitHub):
YAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # input_text.yaml, included from configuration.yaml ################################## ## Input text configuration ################################## # I am using this workarond to hide sensor-IDs needed in the rest_command # payloads. These templates cannot handle secrets, but you can set these # input_text items with !secret, so in this way the IDs are obfuscated. # I don't mind having these in my HA instance, I just don't want them on # GitHub to prevent copy-paste errors. opensensebox_sensorid_temp: initial: !secret opensensebox_sensorid_temp opensensebox_sensorid_press: initial: !secret opensensebox_sensorid_press opensensebox_sensorid_hum: initial: !secret opensensebox_sensorid_hum opensensebox_sensorid_pm25: initial: !secret opensensebox_sensorid_pm25 opensensebox_sensorid_pm10: initial: !secret opensensebox_sensorid_pm10 |
Automations
Now all data is available and the REST commands are ready for use, let's start using them! Source at my GitHubYAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| #air_quality.yaml, included from configuraiton.yaml automations ################################## ## Push values to Air Quality APIs ################################## # Sensor uses a NodeMCUv2 and SDS011+BME280 sensors. NodeMCU is flashed with ESPHome # software. SDS011 updates every 30 mins, the BME280 every 5 minutes. As all BME280 # sensors update at the same time, we only need to monitor one sensor to trigger # the automation. Temperature is the sensor that changes most often. - alias: 'Weerhuisje: Update Air Quality Sensors' initial_state: true mode: queued trigger: # Trigger on every update of the temperature platform: state entity_id: sensor.bme280_temperature action: ## Send to Luftdaten / Sensor.Community API ## Split over two calls as the headers differ - service: rest_command.send_luftdaten_tph - service: rest_command.send_luftdaten_pm ## Send updates to Madavi.de too for statistics. - service: rest_command.send_madavi ## Send data to OpenSenseMap. - service: rest_command.post_opensensebox - alias: "Weerhuisje: Notify if weerhuisje Offline" initial_state: true trigger: - platform: state entity_id: binary_sensor.weerhuisje_status to: 'off' for: seconds: 60 action: service: notify.notify data_template: title: "Weerhuisje is Offline" message: "Weerhuisje is offline" |
End result
:strip_exif()/f/image/nMhbeOU67HaKRQTTDJJ8sm4b.jpg?f=fotoalbum_small)
:strip_exif()/f/image/G6Qj7JWlIuISXJu97vApmldS.jpg?f=fotoalbum_small)
:strip_exif()/f/image/3Jq03oCT9S1RlrN77Zm2S5jg.jpg?f=fotoalbum_small)
(Still need to put it on the wall - scheduled for later this week)
At OpenSenseMap:
/f/image/aepLIJ9PqOwFMm8mVfJW4RMO.png?f=fotoalbum_small)
Luftdaten info at Madavi:
/f/image/aG2O3erYx2Km2wdGH8xkkuR4.png?f=fotoalbum_small)
NS Reisinformatie in Home Assistant
Ik ben al een tijdje aan het spelen met Home Assistant om diverse automations in te stellen voor mijn dagelijkse bezigheden. Sinds versie 0.57 is er ook een sensor aanwezig om reisinformatie van de Nederlandse Spoorwegen in HA beschikbaar te krijgen, gemaakt door Menno Blom. Om mijn dagelijkse trips wat makkelijker te maken ben ik daar wat mee gaan spelen.
Component installatie
Om deze component te kunnen gebruiken binnen HA zal je deze moeten configureren. In configuration.yaml (of sensors.yaml, als je dat gebruikt):
Om een wachtwoord te verkrijgen moet je bij de NS een API account aanmaken, waarna het wachtwoord je meteen wordt toegestuurd. Voor de routes moeten de stations-codes gebruikt worden zoals de NS die gebruikt: die zijn hier te vinden.
Na deze configuratiestappen zijn er twee sensoren beschikbaar in het startscherm van HA. Om een en ander overzichtelijker te maken heb ik ze opgenomen in een group:
Met het volgende resultaat:
Automations op basis van locatie
Helaas woon ik niet dicht bij het station, dus ik moet met de bus naar mijn vertrekstation. Als ik in de buurt van het vertrekstation kom wil ik graag een melding krijgen wanneer mijn trein vanaf welk perron vertrekt. De vertrektijd wisselt nog wel eens qua tijd omdat ik op verschillende tijden op het station kom, dus mijn automation moet er rekening mee houden of ik sowieso wel in de buurt van het station ben. Binnen Home Assistant kun je dat heel eenvoudig bereiken door middel van zones en het gebruiken van een device tracker zoals Owntracks. Het instellen daarvan sla ik in deze post over, maar een zone is daarna vrij eenvoudig in je configuration.yaml (of zones.yaml) aan te maken:
In Home Assistant kun je acties triggeren als je een bepaalde zone in komt, en dat is exact wat ik gebruik om mezelf een (Pushbullet) bericht te sturen met de laatste vertrekinfo van mijn trein, in automations.yaml. Als trigger gebruik ik het event "enter" van het zone platform, maar ik breng een tijdsconditie aan om te zorgen dat dit alleen gebeurd tijdens mijn ochtendreis: ik heb 's middags geen behoefte aan deze melding want dan reis ik de andere kant op.
Zoals je ziet ontvang ik dan een Pushbullet berichtje met alle informatie die ik nodig heb, inclusief het vertrekspoor* en de vertrektijd:

*Het verstrekspoor wordt pas vanaf HA 0.58 aangeboden in de state attributes van deze sensor, na een voorstel van mijzelf daartoe. Mocht je voor die tijd al willen experimenteren: het zijn 4 simpele toe te voegen regels in homeassistant/components/sensor/nederlandse_spoorwegen.py, zie de Github PR voor details., dus even updaten naar de op 19 november 2017 uitgebrachte versie 0.58
Voor mijn terugreis gebruik ik een ander systeem, omdat het station dicht bij mijn werkplek ligt. Die werkplek heb ik ook als zone aangemaakt in HA, waardoor ik die ook als condition kan gebruiken voor mijn locatie. Maar omdat ik deze berichten enkel in de middag wil ontvangen als ik daadwerkelijk op het werk ben, gebruik ik als condition zowel de tijd als mijn locatie.
Door het gebruik van de state als trigger van de automation triggert hij steeds als de sensor van state (of een van de attributen) veranderd: daardoor ben ik altijd op de hoogte van eventuele vertragingen of uitval.
Component installatie
Om deze component te kunnen gebruiken binnen HA zal je deze moeten configureren. In configuration.yaml (of sensors.yaml, als je dat gebruikt):
YAML:
1
2
3
4
5
6
7
8
9
10
| - platform: nederlandse_spoorwegen email: !secret ns_user password: !secret ns_password routes: - name: DenBosch-Breda from: Ht to: Bd - name: Breda-DenBosch from: Bd to: Ht |
Om een wachtwoord te verkrijgen moet je bij de NS een API account aanmaken, waarna het wachtwoord je meteen wordt toegestuurd. Voor de routes moeten de stations-codes gebruikt worden zoals de NS die gebruikt: die zijn hier te vinden.
Na deze configuratiestappen zijn er twee sensoren beschikbaar in het startscherm van HA. Om een en ander overzichtelijker te maken heb ik ze opgenomen in een group:
YAML:
1
2
3
4
5
| treinen: name: Treinen entities: - sensor.denboschbreda - sensor.bredadenbosch |
Met het volgende resultaat:
Automations op basis van locatie
Helaas woon ik niet dicht bij het station, dus ik moet met de bus naar mijn vertrekstation. Als ik in de buurt van het vertrekstation kom wil ik graag een melding krijgen wanneer mijn trein vanaf welk perron vertrekt. De vertrektijd wisselt nog wel eens qua tijd omdat ik op verschillende tijden op het station kom, dus mijn automation moet er rekening mee houden of ik sowieso wel in de buurt van het station ben. Binnen Home Assistant kun je dat heel eenvoudig bereiken door middel van zones en het gebruiken van een device tracker zoals Owntracks. Het instellen daarvan sla ik in deze post over, maar een zone is daarna vrij eenvoudig in je configuration.yaml (of zones.yaml) aan te maken:
YAML:
1
2
3
4
5
6
| zone: - name: stationdb latitude: 51.690256 longitude: 5.294090 radius: 700 icon: mdi:train |
In Home Assistant kun je acties triggeren als je een bepaalde zone in komt, en dat is exact wat ik gebruik om mezelf een (Pushbullet) bericht te sturen met de laatste vertrekinfo van mijn trein, in automations.yaml. Als trigger gebruik ik het event "enter" van het zone platform, maar ik breng een tijdsconditie aan om te zorgen dat dit alleen gebeurd tijdens mijn ochtendreis: ik heb 's middags geen behoefte aan deze melding want dan reis ik de andere kant op.
YAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| - id: Notification_trein_naar_Breda alias: Vertrektijd trein naar Breda trigger: - entity_id: device_tracker.hmmbob event: enter platform: zone zone: zone.stationdb condition: - after: 06:15:00 before: 08:30:00 condition: time weekday: - mon - tue - wed - thu - fri action: - data_template: message: Vertrektijd {{ states.sensor.denboschbreda.attributes.departure_time_actual }} vanaf spoor {{ states.sensor.denboschbreda.attributes.departure_platform }}, aankomst om {{ states.sensor.denboschbreda.attributes.arrival_time_actual }}. Volgende trein vertrekt om {{ states.sensor.denboschbreda.attributes.next }}. title: 'Trein naar Breda: {{ states.sensor.denboschbreda.attributes.status }}.' service: notify.pushbullet |
Zoals je ziet ontvang ik dan een Pushbullet berichtje met alle informatie die ik nodig heb, inclusief het vertrekspoor* en de vertrektijd:

*Het verstrekspoor wordt pas vanaf HA 0.58 aangeboden in de state attributes van deze sensor, na een voorstel van mijzelf daartoe. Mocht je voor die tijd al willen experimenteren: het zijn 4 simpele toe te voegen regels in homeassistant/components/sensor/nederlandse_spoorwegen.py, zie de Github PR voor details., dus even updaten naar de op 19 november 2017 uitgebrachte versie 0.58
Voor mijn terugreis gebruik ik een ander systeem, omdat het station dicht bij mijn werkplek ligt. Die werkplek heb ik ook als zone aangemaakt in HA, waardoor ik die ook als condition kan gebruiken voor mijn locatie. Maar omdat ik deze berichten enkel in de middag wil ontvangen als ik daadwerkelijk op het werk ben, gebruik ik als condition zowel de tijd als mijn locatie.
YAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| - id: Notification_vertrek_naar_Den_Bosch alias: Vertrek naar Den Bosch trigger: - entity_id: sensor.bredadenbosch platform: state condition: - condition: and conditions: - after: '15:00:00' before: '16:30:00' condition: time weekday: - mon - tue - wed - thu - fri - condition: state entity_id: device_tracker.hmmbob state: work action: - data: message: Vertrektijd {{ states.sensor.bredadenbosch.attributes.departure_time_actual }} vanaf spoor {{ states.sensor.bredadenbosch.attributes.departure_platform }}, aankomst om {{ states.sensor.bredadenbosch.attributes.arrival_time_actual }} op spoor {{ states.sensor.bredadenbosch.attributes.arrival_platform }}. Volgende trein vertrekt om {{ states.sensor.bredadenbosch.attributes.next }}. title: 'Trein naar Den Bosch: {{ states.sensor.bredadenbosch.attributes.status }}.' service: notify.pushbullet |
Door het gebruik van de state als trigger van de automation triggert hij steeds als de sensor van state (of een van de attributen) veranderd: daardoor ben ik altijd op de hoogte van eventuele vertragingen of uitval.