Last active
July 18, 2024 01:36
-
-
Save kevin-david/28b38921330d26cd75a4b33cd359f838 to your computer and use it in GitHub Desktop.
AirGradient DIY: ESPHome with Sleep (extend PM2.5 sensor lifetime)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
## See https://github.com/kevin-david/home-assistant-shared/blob/main/esphome/airgradient.yml for future updates | |
## | |
substitutions: | |
name: airgradient-02 | |
### Everything below this can be copy/paste between similar devices | |
esphome: | |
name: $name | |
# https://esphome.io/components/wifi.html | |
wifi: | |
ssid: !secret wifi_ssid | |
password: !secret wifi_password | |
fast_connect: true | |
ap: | |
ssid: "$name Fallback Hotspot" | |
password: !secret fallback_password | |
esp8266: | |
board: d1_mini | |
logger: | |
api: | |
ota: | |
captive_portal: | |
# Configuration for AirGradient DIY v2 device | |
# https://www.esphome-devices.com/devices/AirGradient-DIY/ | |
# https://esphome.io/components/uart.html#uart | |
uart: | |
- rx_pin: D4 | |
tx_pin: D3 | |
baud_rate: 9600 | |
id: senseair_s8_uart | |
- rx_pin: D5 | |
tx_pin: D6 | |
baud_rate: 9600 | |
id: pms5003_uart | |
i2c: | |
sda: D2 | |
scl: D1 | |
sensor: | |
# https://esphome.io/components/sensor/pmsx003.html?highlight=pms5003 | |
- platform: pmsx003 | |
type: PMSX003 | |
uart_id: pms5003_uart | |
pm_1_0: | |
name: "$name PM <1.0µm" | |
id: pm1 | |
filters: | |
- sliding_window_moving_average: | |
window_size: 30 | |
send_every: 30 | |
pm_2_5: | |
name: "$name PM <2.5µm" | |
id: pm2_5 | |
filters: | |
- sliding_window_moving_average: | |
window_size: 30 | |
send_every: 30 | |
pm_10_0: | |
name: "$name PM <10.0µm" | |
id: pm10 | |
filters: | |
- sliding_window_moving_average: | |
window_size: 30 | |
send_every: 30 | |
# https://esphome.io/components/sensor/senseair.html | |
- platform: senseair | |
id: senseair_s8 | |
co2: | |
name: "$name CO2" | |
id: co2 | |
update_interval: 60s | |
uart_id: senseair_s8_uart | |
# https://esphome.io/components/sensor/sht3xd.html?highlight=sht31 | |
- platform: sht3xd | |
temperature: | |
name: "$name Temperature" | |
id: temp | |
humidity: | |
name: "$name Humidity" | |
id: humidity | |
address: 0x44 | |
update_interval: 60s | |
- platform: sgp30 | |
eco2: | |
name: "$name eCO2" | |
tvoc: | |
name: "$name TVOC" | |
eco2_baseline: | |
name: "$name eCO2 baseline" | |
tvoc_baseline: | |
name: "$name TVOC baseline" | |
compensation: | |
temperature_source: temp | |
humidity_source: humidity | |
store_baseline: yes | |
address: 0x58 | |
update_interval: 1s | |
# The WHO guidelines work with 24-hour averages of the PM2.5 and PM10 sensors | |
- platform: template | |
name: "$name PM <2.5µm 24h average" | |
id: pm2_5_avg_24h | |
icon: mdi:chemical-weapon | |
unit_of_measurement: µg/m³ | |
lambda: |- | |
return id(pm2_5).state; | |
update_interval: 60s | |
filters: | |
- sliding_window_moving_average: | |
window_size: 1440 # = 24 hours x 60 minutes | |
send_every: 1 | |
on_value: | |
then: | |
- script.execute: update_aqi | |
- platform: template | |
name: "$name PM <10.0µm 24h average" | |
id: pm10_avg_24h | |
icon: mdi:chemical-weapon | |
unit_of_measurement: µg/m³ | |
lambda: |- | |
return id(pm10).state; | |
update_interval: 60s | |
filters: | |
- sliding_window_moving_average: | |
window_size: 1440 # = 24 hours x 60 minutes | |
send_every: 1 | |
on_value: | |
then: | |
- script.execute: update_aqi | |
- platform: wifi_signal | |
name: "$name WiFi Signal" | |
update_interval: 60s | |
- platform: uptime # Uptime in Seconds | |
name: $name Uptime Sensor | |
id: uptime_sensor | |
update_interval: 60s | |
internal: True | |
on_raw_value: | |
then: | |
- text_sensor.template.publish: | |
id: uptime_human | |
state: !lambda |- | |
int seconds = round(id(uptime_sensor).raw_state); | |
int days = seconds / (24 * 3600); | |
seconds = seconds % (24 * 3600); | |
int hours = seconds / 3600; | |
seconds = seconds % 3600; | |
int minutes = seconds / 60; | |
seconds = seconds % 60; | |
return ( | |
(days ? String(days) + "d " : "") + | |
(hours ? String(hours) + "h " : "") + | |
(minutes ? String(minutes) + "m " : "") + | |
(String(seconds) + "s") | |
).c_str(); | |
# A textual presentation of the AQI | |
text_sensor: | |
- platform: template | |
name: "$name Air Quality Index" | |
id: aqi | |
icon: mdi:air-filter | |
- platform: template # Human Readable Uptime | |
name: $name Uptime | |
id: uptime_human | |
icon: mdi:clock-start | |
# This script is called on every update of the relevant sensor values. | |
script: | |
- id: update_aqi | |
mode: restart | |
then: | |
# Bad if at least one of the sensor values is bad | |
- if: | |
condition: | |
or: | |
- sensor.in_range: | |
id: co2 | |
above: 1199 | |
- sensor.in_range: | |
id: pm2_5_avg_24h | |
above: 25 | |
- sensor.in_range: | |
id: pm10_avg_24h | |
above: 50 | |
then: | |
- text_sensor.template.publish: | |
id: aqi | |
state: Bad | |
else: | |
# Acceptable if at least one of the sensor values is acceptable | |
- if: | |
condition: | |
or: | |
- sensor.in_range: | |
id: co2 | |
above: 899 | |
- sensor.in_range: | |
id: pm2_5_avg_24h | |
above: 12 | |
- sensor.in_range: | |
id: pm10_avg_24h | |
above: 25 | |
then: | |
- text_sensor.template.publish: | |
id: aqi | |
state: Acceptable | |
else: | |
# Otherwise good (all of the sensor values are good) | |
- text_sensor.template.publish: | |
id: aqi | |
state: Good | |
display: | |
- platform: ssd1306_i2c | |
# https://esphome.io/components/display/ssd1306.html?highlight=display | |
model: "SH1106 128x64" | |
id: oled_display | |
reset_pin: D0 | |
address: 0x3C | |
pages: | |
- id: display_pm2_10 | |
lambda: |- | |
if (id(homeassistant_time).now().hour > 22 || id(homeassistant_time).now().hour < 7) | |
{ | |
it.print(0, 0, id(roboto), "."); | |
} | |
else | |
{ | |
it.printf(0, 0, id(roboto), "2.5 %.0f", id(pm2_5).state); | |
it.printf(0, 24, id(roboto), "10 %.0f",id(pm10).state); | |
} | |
- id: display_co2 | |
lambda: |- | |
if (id(homeassistant_time).now().hour > 22 || id(homeassistant_time).now().hour < 7) | |
{ | |
it.print(it.get_width()/2, 0, id(roboto), TextAlign::TOP_CENTER, "."); | |
} | |
else | |
{ | |
it.print(0, 0, id(roboto), "CO2"); | |
it.printf(64, 24, id(roboto), TextAlign::TOP_RIGHT, "%.0f",id(co2).state); | |
} | |
# Display temp and humidity on the same page | |
- id: display_temp_humidity | |
lambda: |- | |
if (id(homeassistant_time).now().hour > 22 || id(homeassistant_time).now().hour < 7) | |
{ | |
it.print(it.get_width(), 0, id(roboto), TextAlign::TOP_RIGHT, "."); | |
} | |
else | |
{ | |
it.printf(0, 0, id(roboto), "%.1f°F",(id(temp).state * 1.8) + 32); | |
it.printf(64, 24, id(roboto), TextAlign::TOP_RIGHT, "%.0f%%",id(humidity).state); | |
} | |
time: | |
- platform: homeassistant | |
id: homeassistant_time | |
font: | |
# gfonts://family[@weight] | |
- file: "gfonts://Roboto" | |
id: roboto | |
size: 21 | |
interval: | |
- interval: 3s | |
# Cycle through page on display | |
then: | |
- display.page.show_next: oled_display | |
- component.update: oled_display | |
- interval: 180s | |
# Two-minute interval to extend the life span of the PMS5003 sensor | |
then: | |
- switch.turn_on: pms_switch | |
- delay: 45s | |
- switch.turn_off: pms_switch | |
# Source: https://github.com/airgradienthq/arduino/blob/master/AirGradient.cpp#L123 | |
switch: | |
- platform: template | |
name: "$name PMS5003" | |
internal: true | |
id: pms_switch | |
optimistic: true | |
turn_on_action: | |
- uart.write: | |
id: pms5003_uart | |
data: [0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74] | |
turn_off_action: | |
- uart.write: | |
id: pms5003_uart | |
data: [0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73] | |
- platform: template | |
name: "$name ABC" | |
optimistic: true | |
on_turn_on: | |
senseair.abc_enable: senseair_s8 | |
on_turn_off: | |
senseair.abc_disable: senseair_s8 | |
button: | |
- platform: template | |
name: $name Manual Senseair S8 Calibration | |
id: calibrate_sensair_s8 | |
on_press: | |
then: | |
- senseair.background_calibration: senseair_s8 | |
- delay: 75s | |
- senseair.background_calibration_result: senseair_s8 | |
- platform: template | |
name: $name Log Senseair S8 ABC Period | |
id: get_senseair_s8_abc_period | |
on_press: | |
then: | |
- senseair.abc_get_period: senseair_s8 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To resolve:
[10:35:43][W][pmsx003:108]: PMSX003 length 4 doesn't match. Are you using the correct PMSX003 type?
On sleep call.
Maybe[0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73]
should be[0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x73]
(4th pos change), like turn on/wake?edit: Nope, it's the response to the sleep command.