781 lines
22 KiB
YAML
781 lines
22 KiB
YAML
substitutions: #substitute your own values in this section
|
|
internal_temp_sensor: sensor.meat_heater_current_temperature #entity from Home Assistant
|
|
outside_temp_sensor: sensor.home_realfeel_temperature #entity from Home Assistant
|
|
weather_entity: weather.home #entity from Home Assistant
|
|
todays_forecast_high_entity: sensor.home_realfeel_temperature_max_day_0 #entity from Home Assistant
|
|
todays_forecast_low_entity: sensor.home_realfeel_temperature_min_day_0 #entity from Home Assistant
|
|
|
|
esphome:
|
|
name: airqualitysensor-1
|
|
friendly_name: AirQualitySensor-1
|
|
|
|
on_boot:
|
|
priority: -100
|
|
then:
|
|
- light.turn_on:
|
|
id: led
|
|
brightness: 0.4
|
|
effect: "Rainbow Effect"
|
|
|
|
esp32:
|
|
board: esp32-s3-devkitc-1
|
|
cpu_frequency: 240MHz
|
|
variant: esp32s3
|
|
flash_size: 16MB
|
|
framework:
|
|
type: esp-idf
|
|
|
|
# Enable logging
|
|
logger:
|
|
|
|
# Enable Home Assistant API
|
|
api:
|
|
encryption:
|
|
key: "gnQzjc98wQKUP3qX6+FeU9JchMPtjJwlKQejOB/mQDU="
|
|
|
|
ota:
|
|
- platform: esphome
|
|
password: "d4d36e1c98d4b7e2069295540a20a1aa"
|
|
|
|
wifi:
|
|
ssid: !secret wifi_ssid
|
|
password: !secret wifi_password
|
|
|
|
# manual_ip:
|
|
# static_ip: 192.168.1.61
|
|
# gateway: 192.168.1.1
|
|
# subnet: 255.255.255.0
|
|
|
|
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
|
ap:
|
|
ssid: "Airqualitysensor-1"
|
|
password: "MpBryi70smN4"
|
|
|
|
captive_portal:
|
|
|
|
time:
|
|
- platform: homeassistant
|
|
id: homeassistant_time
|
|
|
|
on_time:
|
|
- seconds: 0
|
|
minutes: 0
|
|
hours: 6 # 6 AM
|
|
then:
|
|
- logger.log: "Automatic morning turn ON OLED"
|
|
- switch.turn_on: oled_power
|
|
|
|
- seconds: 0
|
|
minutes: 0
|
|
hours: 19 # 7 PM
|
|
then:
|
|
- logger.log: "Automatic evening turn OFF OLED"
|
|
- switch.turn_off: oled_power
|
|
|
|
script:
|
|
# SCRIPT 1: Runs when the mode is changed from the select
|
|
- id: update_led_state
|
|
then:
|
|
- lambda: |-
|
|
if (!id(iaq_reading).has_state() || id(iaq_reading).state == "Booting...") {
|
|
return; // Exit script, leave rainbow alone
|
|
}
|
|
|
|
std::string quality = id(iaq_reading).state;
|
|
float lux = id(ambient_light).state;
|
|
std::string mode = id(led_mode).state;
|
|
|
|
float brightness = 0.15;
|
|
float r = 0.0, g = 0.0, b = 0.0;
|
|
std::string selected_effect = "None";
|
|
|
|
// --- 1. Determine desired color ---
|
|
if (quality == "Excellent") { r = 0.0; g = 1.0; b = 0.0; }
|
|
else if (quality == "Good") { r = 0.3; g = 0.6; b = 0.3; }
|
|
else if (quality == "Moderate") { r = 1.0; g = 0.85; b = 0.38; }
|
|
else if (quality == "Poor") { r = 1.0; g = 0.0; b = 0.0; }
|
|
else if (quality == "Unhealthy") {
|
|
r = 1.0; g = 0.0; b = 1.0;
|
|
selected_effect = "Alert Flash"; // Alert overrides mode
|
|
}
|
|
|
|
// --- 2. Determine desired effect based on mode (if not alerting) ---
|
|
if (selected_effect != "Alert Flash") {
|
|
if (mode == "Breathing") {
|
|
selected_effect = "Breathing";
|
|
} else if (mode == "Scanner") {
|
|
selected_effect = "Scanner";
|
|
}
|
|
}
|
|
|
|
// --- 3. Apply changes ---
|
|
auto call = id(led).turn_on();
|
|
call.set_rgb(r, g, b); // Always set color
|
|
|
|
if (selected_effect == "None") {
|
|
// We want a solid light (Auto/Manual)
|
|
if (mode == "Auto") {
|
|
if (isnan(lux)) lux = 0;
|
|
brightness = 0.15 + (lux / 200.0) * (0.7 - 0.15);
|
|
if (brightness > 0.7) brightness = 0.7;
|
|
if (brightness < 0.15) brightness = 0.15;
|
|
} else { // Manual
|
|
brightness = id(iaq_led_brightness).state;
|
|
}
|
|
call.set_brightness(brightness);
|
|
}
|
|
|
|
// This script *always* sets the effect
|
|
call.set_effect(selected_effect);
|
|
call.perform();
|
|
|
|
# SCRIPT 2: Runs when IAQ *value* changes (every 10s)
|
|
- id: update_led_color
|
|
then:
|
|
- lambda: |-
|
|
if (!id(iaq_reading).has_state() || id(iaq_reading).state == "Booting...") {
|
|
return; // Exit script, leave rainbow alone
|
|
}
|
|
|
|
std::string quality = id(iaq_reading).state;
|
|
std::string mode = id(led_mode).state;
|
|
float lux = id(ambient_light).state;
|
|
|
|
float r = 0.0, g = 0.0, b = 0.0;
|
|
|
|
// --- 1. Determine desired color ---
|
|
if (quality == "Excellent") { r = 0.0; g = 1.0; b = 0.0; }
|
|
else if (quality == "Good") { r = 0.3; g = 0.6; b = 0.3; }
|
|
else if (quality == "Moderate") { r = 1.0; g = 0.85; b = 0.38; }
|
|
else if (quality == "Poor") { r = 1.0; g = 0.0; b = 0.0; }
|
|
else if (quality == "Unhealthy") {
|
|
r = 1.0; g = 0.0; b = 1.0;
|
|
// Force Alert Flash and EXIT
|
|
id(led).turn_on().set_rgb(r, g, b).set_effect("Alert Flash").perform();
|
|
return;
|
|
}
|
|
|
|
// --- 2. If we are here, AQ is OK. Reset Effect! ---
|
|
auto call = id(led).turn_on();
|
|
call.set_rgb(r, g, b);
|
|
|
|
// Determine which effect to restore based on the Select Mode
|
|
std::string restore_effect = "None";
|
|
if (mode == "Breathing") restore_effect = "Breathing";
|
|
else if (mode == "Scanner") restore_effect = "Scanner";
|
|
|
|
// Explicitly set the effect (This stops the Alert Flash)
|
|
call.set_effect(restore_effect);
|
|
|
|
// --- 3. Apply Brightness ---
|
|
if (mode == "Auto") {
|
|
float brightness = 0.15;
|
|
if (isnan(lux)) lux = 0;
|
|
brightness = 0.15 + (lux / 200.0) * (0.7 - 0.15);
|
|
if (brightness > 0.7) brightness = 0.7;
|
|
if (brightness < 0.15) brightness = 0.15;
|
|
call.set_brightness(brightness);
|
|
} else if (mode == "Manual") {
|
|
call.set_brightness(id(iaq_led_brightness).state);
|
|
}
|
|
|
|
call.perform();
|
|
|
|
globals:
|
|
- id: iaq_index
|
|
type: int
|
|
restore_value: no
|
|
initial_value: '0'
|
|
|
|
- id: computed_brightness
|
|
type: float
|
|
restore_value: no
|
|
initial_value: '0.15'
|
|
|
|
|
|
bluetooth_proxy:
|
|
active: true
|
|
connection_slots: 3
|
|
|
|
uart:
|
|
tx_pin: GPIO17
|
|
rx_pin: GPIO18
|
|
baud_rate: 9600
|
|
|
|
light:
|
|
- platform: esp32_rmt_led_strip
|
|
rgb_order: GRB
|
|
chipset: WS2812
|
|
pin: GPIO16
|
|
num_leds: 5
|
|
name: "LED"
|
|
id: led
|
|
icon: mdi:led-on
|
|
default_transition_length: 0s
|
|
disabled_by_default: False
|
|
effects:
|
|
- pulse:
|
|
name: "Breathing"
|
|
min_brightness: 0.15
|
|
max_brightness: 0.45
|
|
transition_length: 3000ms
|
|
update_interval: 3000ms
|
|
- addressable_scan:
|
|
name: "Scanner"
|
|
scan_width: 2
|
|
move_interval: 100ms
|
|
- flicker:
|
|
name: "Alert Flash"
|
|
alpha: 95%
|
|
intensity: 1.5%
|
|
- addressable_rainbow:
|
|
name: "Rainbow Effect"
|
|
speed: 10
|
|
width: 50
|
|
|
|
i2c:
|
|
sda: GPIO8
|
|
scl: GPIO9
|
|
scan: true
|
|
id: bus_a
|
|
frequency: 100kHz
|
|
|
|
switch:
|
|
- platform: template
|
|
name: "OLED Power"
|
|
id: oled_power
|
|
optimistic: true
|
|
restore_mode: RESTORE_DEFAULT_ON
|
|
turn_on_action:
|
|
- logger.log: "OLED turned ON"
|
|
turn_off_action:
|
|
- logger.log: "OLED turned OFF"
|
|
|
|
select:
|
|
- platform: template
|
|
name: "LED Mode"
|
|
id: led_mode
|
|
options:
|
|
- "Auto"
|
|
- "Manual"
|
|
- "Breathing"
|
|
- "Scanner"
|
|
optimistic: true
|
|
restore_value: true
|
|
on_value:
|
|
then:
|
|
- script.execute: update_led_state
|
|
|
|
number:
|
|
- platform: template
|
|
name: "IAQ LED Brightness"
|
|
id: iaq_led_brightness
|
|
min_value: 0.15
|
|
max_value: 1.0
|
|
step: 0.01
|
|
optimistic: true
|
|
restore_value: true
|
|
|
|
sensor:
|
|
- platform: uptime
|
|
name: Uptime Sensor
|
|
|
|
- platform: homeassistant
|
|
id: inside_temperature
|
|
entity_id: $internal_temp_sensor
|
|
internal: true
|
|
|
|
- platform: homeassistant
|
|
id: outside_temperature
|
|
entity_id: $outside_temp_sensor
|
|
internal: true
|
|
|
|
- platform: homeassistant
|
|
id: todays_forecast_high
|
|
entity_id: $todays_forecast_high_entity
|
|
internal: true
|
|
|
|
- platform: homeassistant
|
|
id: todays_forecast_low
|
|
entity_id: $todays_forecast_low_entity
|
|
internal: true
|
|
|
|
- platform: pmsx003
|
|
type: PMSX003
|
|
pm_1_0:
|
|
id: pm1
|
|
name: "PM <1.0µm Concentration"
|
|
pm_2_5:
|
|
id: pm25
|
|
name: "PM <2.5µm Concentration"
|
|
pm_10_0:
|
|
id: pm10
|
|
name: "PM <10.0µm Concentration"
|
|
|
|
- platform: bme680
|
|
temperature:
|
|
name: "BME680 Temperature"
|
|
id: bmetemp
|
|
oversampling: 16x
|
|
pressure:
|
|
name: "BME680 Pressure"
|
|
id: bmepressure
|
|
humidity:
|
|
name: "BME680 Humidity"
|
|
id: bmehum
|
|
gas_resistance:
|
|
name: "BME680 Gas Resistance"
|
|
id: bmegas
|
|
address: 0x77
|
|
update_interval: 10s
|
|
|
|
## CO²/VOC Sensor
|
|
- platform: ccs811
|
|
eco2:
|
|
name: "CCS811 CO²"
|
|
accuracy_decimals: 0
|
|
id: eco2
|
|
tvoc:
|
|
name: "CCS811 T-VOC"
|
|
accuracy_decimals: 0
|
|
id: tvoc
|
|
address: 0x5A
|
|
update_interval: 10s
|
|
temperature: bmetemp
|
|
humidity: bmehum
|
|
## After Calibration, Uncomment and change "baseline:"
|
|
baseline: 0x9CB1
|
|
|
|
- platform: template
|
|
name: "Humidity Sensor"
|
|
id: humi
|
|
unit_of_measurement: "%"
|
|
accuracy_decimals: 1
|
|
lambda: |-
|
|
return id(bmehum).state;
|
|
update_interval: 10s
|
|
|
|
- platform: wifi_signal
|
|
name: AQ WiFi Signal
|
|
update_interval: 60s
|
|
|
|
- platform: veml7700
|
|
address: 0x10
|
|
update_interval: 10s
|
|
# short variant of sensor definition:
|
|
ambient_light:
|
|
name: "Ambient Light"
|
|
id: ambient_light
|
|
filters:
|
|
- sliding_window_moving_average:
|
|
window_size: 5
|
|
send_every: 1
|
|
|
|
font:
|
|
- file: "fonts/Roboto-Regular.ttf"
|
|
id: robotto
|
|
size: 10
|
|
|
|
- file: "fonts/Roboto-Regular.ttf"
|
|
id: font2
|
|
size: 12
|
|
|
|
- file: "fonts/Poppins-Regular.ttf"
|
|
id: font1
|
|
size: 10
|
|
|
|
- file: "fonts/Poppins-Regular.ttf"
|
|
id: poppinslarger
|
|
size: 12
|
|
|
|
- file: "fonts/Poppins-SemiBold.ttf"
|
|
id: poppinsbold
|
|
size: 10
|
|
|
|
- file: 'fonts/materialdesignicons-webfont.ttf'
|
|
id: font3
|
|
size: 18
|
|
glyphs:
|
|
- "\U000F13D5" #mdi:home-minus-outline
|
|
|
|
- file: 'fonts/materialdesignicons-webfont.ttf'
|
|
id: font4
|
|
size: 40
|
|
glyphs:
|
|
- "\U000F0594" #"clear-night"
|
|
- "\U000F0590" #"cloudy"
|
|
- "\U000F0591" #"fog"
|
|
- "\U000F0592" #"hail"
|
|
- "\U000F0593" #"lightning"
|
|
- "\U000F067E" #"lightning-rainy"
|
|
- "\U000F0595" #"partlycloudy"
|
|
- "\U000F0596" #"pouring"
|
|
- "\U000F0597" #"rainy"
|
|
- "\U000F0598" #"snowy"
|
|
- "\U000F067F" #"snowy-rainy"
|
|
- "\U000F0599" #"sunny"
|
|
- "\U000F059D" #"windy"
|
|
- "\U000F059E" #"windy-variant"
|
|
|
|
display:
|
|
- platform: ssd1306_i2c
|
|
model: "SSD1306 128x64"
|
|
address: 0x3C
|
|
id: oled_display
|
|
rotation: 0
|
|
i2c_id: bus_a
|
|
pages:
|
|
- id: page_air_quality
|
|
lambda: |-
|
|
if (id(oled_power).state) { // only draw if display is ON
|
|
{
|
|
// Combine the final string first
|
|
char full_text[64];
|
|
|
|
if (id(iaq_reading).has_state()) {
|
|
sprintf(full_text, "Air Quality: %s", id(iaq_reading).state.c_str());
|
|
} else {
|
|
sprintf(full_text, "Air Quality: Booting...");
|
|
}
|
|
|
|
int x, y, w, h;
|
|
it.get_text_bounds(0, 0, full_text, id(poppinsbold), TextAlign::TOP_LEFT, &x, &y, &w, &h);
|
|
int center_x = (128 - w) / 2; // OLED is 128px wide
|
|
|
|
it.printf(center_x, 0, id(poppinsbold), "%s", full_text);
|
|
}
|
|
|
|
// --- Temp + Humidity on same line ---
|
|
it.printf(0, 16, id(font1), "TEMP: %.1f°C", id(bmetemp).state);
|
|
it.printf(70, 16, id(font1), "HUM: %.0f%%", id(bmehum).state);
|
|
|
|
// --- Other sensor lines ---
|
|
it.printf(0, 26, id(font1), "CO2: %.0f ppm", id(eco2).state);
|
|
it.printf(0, 36, id(font1), "TVOC: %.0f ppb", id(tvoc).state);
|
|
|
|
if (!isnan(id(pm25).state)) {
|
|
it.printf(0, 46, id(font1), "PM2.5: %.0f", id(pm25).state);
|
|
} else {
|
|
it.printf(0, 46, id(font1), "PM2.5: ---");
|
|
}
|
|
}
|
|
|
|
- id: page_environment
|
|
lambda: |-
|
|
if (id(oled_power).state) { // only draw if display is ON
|
|
{
|
|
// Combine the final string first
|
|
char full_text[64];
|
|
|
|
if (id(iaq_reading).has_state()) {
|
|
sprintf(full_text, "Air Quality: %s", id(iaq_reading).state.c_str());
|
|
} else {
|
|
sprintf(full_text, "Air Quality: Booting...");
|
|
}
|
|
|
|
int x, y, w, h;
|
|
it.get_text_bounds(0, 0, full_text, id(poppinsbold), TextAlign::TOP_LEFT, &x, &y, &w, &h);
|
|
int center_x = (128 - w) / 2; // OLED is 128px wide
|
|
|
|
it.printf(center_x, 0, id(poppinsbold), "%s", full_text);
|
|
}
|
|
// --- Temp + Humidity on same line ---
|
|
it.printf(0, 16, id(font1), "TEMP: %.1f°C", id(bmetemp).state);
|
|
it.printf(70, 16, id(font1), "HUM: %.0f%%", id(bmehum).state);
|
|
// --- Other sensor lines ---
|
|
it.printf(0, 26, id(font1), "CO2: %.0f ppm", id(ambient_light).state);
|
|
}
|
|
|
|
- id: page_weather
|
|
lambda: |-
|
|
if (id(weather_state).has_state()) {
|
|
std::map<std::string, std::string> weather_icon_map
|
|
{
|
|
{"clear-night", "\U000F0594"},
|
|
{"cloudy", "\U000F0590"},
|
|
{"fog", "\U000F0591"},
|
|
{"hail", "\U000F0592"},
|
|
{"lightning", "\U000F0593"},
|
|
{"lightning-rainy", "\U000F067E"},
|
|
{"partlycloudy", "\U000F0595"},
|
|
{"pouring", "\U000F0596"},
|
|
{"rainy", "\U000F0597"},
|
|
{"snowy", "\U000F0598"},
|
|
{"snowy-rainy", "\U000F067F"},
|
|
{"sunny", "\U000F0599"},
|
|
{"windy", "\U000F059D"},
|
|
{"windy-variant", "\U000F059E"},
|
|
};
|
|
it.printf(0, it.get_height(), id(font4), TextAlign::BASELINE_LEFT, weather_icon_map[id(weather_state).state.c_str()].c_str());
|
|
}
|
|
|
|
// Print time in HH:MM format
|
|
it.strftime(0, 0, id(font1), TextAlign::TOP_LEFT, "%H:%M %a", id(homeassistant_time).now());
|
|
|
|
//Print day of week
|
|
//it.strftime(40, 0, id(font1), TextAlign::TOP_LEFT, "%a", id(homeassistant_time).now());
|
|
|
|
it.line(0, 20, it.get_width(), 20);
|
|
|
|
//Print home icon
|
|
//it.printf(70, 0, id(font3), "\U000F13D5");
|
|
|
|
// Print inside temperature (from homeassistant sensor)
|
|
if (id(inside_temperature).has_state()) {
|
|
it.printf(it.get_width(), 0, id(font1), TextAlign::TOP_RIGHT , "%7.1f°", id(inside_temperature).state);
|
|
}
|
|
|
|
// Print outside temperature (from homeassistant sensor)
|
|
if (id(outside_temperature).has_state()) {
|
|
it.printf(42, 32, id(font2), "%.1f°", id(outside_temperature).state);
|
|
}
|
|
|
|
// Print Forecast High
|
|
if (id(todays_forecast_high).has_state()) {
|
|
it.printf(it.get_width(), 32, id(font1), TextAlign::TOP_RIGHT, "%7.1f°", id(todays_forecast_high).state);
|
|
}
|
|
|
|
// Print Forecast Low
|
|
if (id(todays_forecast_low).has_state()) {
|
|
it.printf(it.get_width(), 48, id(font1), TextAlign::TOP_RIGHT, "%7.1f°", id(todays_forecast_low).state);
|
|
}
|
|
|
|
text_sensor:
|
|
- platform: homeassistant
|
|
id: weather_state
|
|
name: "Current Weather Icon"
|
|
entity_id: $weather_entity
|
|
internal: true
|
|
|
|
- platform: template
|
|
name: "PM 2.5 Air Quality"
|
|
icon: mdi:air-filter
|
|
id: aq_reading
|
|
lambda: |-
|
|
if (id(pm25).state <= 12) {
|
|
return {"Good"};
|
|
}
|
|
else if ((id(pm25).state >= 12.1) && (id(pm25).state <= 35.4)) {
|
|
return {"Moderate"};
|
|
}
|
|
else if ((id(pm25).state >= 35.5) && (id(pm25).state <= 55.4)) {
|
|
return {"Unhealthy(SG)"};
|
|
}
|
|
else if ((id(pm25).state >= 55.5) && (id(pm25).state <= 150.4)) {
|
|
return {"Unhealthy"};
|
|
}
|
|
else if ((id(pm25).state >= 150.5) && (id(pm25).state <= 250.4)) {
|
|
return {"Very Unhealthy"};
|
|
}
|
|
else if (id(pm25).state >= 250.5) {
|
|
return {"Hazardous"};
|
|
}
|
|
return {};
|
|
update_interval: 30s
|
|
|
|
- platform: template
|
|
name: "PM 10 Air Quality"
|
|
icon: mdi:air-filter
|
|
id: aq_10_reading
|
|
lambda: |-
|
|
if (id(pm10).state <= 54) {
|
|
return {"Good"};
|
|
}
|
|
else if ((id(pm10).state >= 55) && (id(pm10).state <= 154)) {
|
|
return {"Moderate"};
|
|
}
|
|
else if ((id(pm10).state >= 155) && (id(pm10).state <= 254)) {
|
|
return {"Unhealthy(SG)"};
|
|
}
|
|
else if ((id(pm10).state >= 255) && (id(pm10).state <= 354)) {
|
|
return {"Unhealthy"};
|
|
}
|
|
else if ((id(pm10).state >= 355) && (id(pm10).state <= 424)) {
|
|
return {"Very Unhealthy"};
|
|
}
|
|
else if (id(pm10).state >= 425) {
|
|
return {"Hazardous"};
|
|
}
|
|
return {};
|
|
update_interval: 30s
|
|
|
|
- platform: template
|
|
name: "Livingroom IAQ"
|
|
icon: "mdi:air-filter"
|
|
id: iaq_reading
|
|
lambda: |-
|
|
// 1. Safety Check: Ensure all sensors have valid numbers
|
|
if (isnan(id(humi).state) || isnan(id(pm25).state) || isnan(id(eco2).state) || isnan(id(tvoc).state)) {
|
|
return {"Booting..."};
|
|
}
|
|
id(iaq_index) = 0;
|
|
|
|
if (id(humi).state < 10 or id(humi).state > 90) {
|
|
id(iaq_index) += 1;
|
|
}
|
|
else if (id(humi).state < 20 or id(humi).state > 80) {
|
|
id(iaq_index) += 2;
|
|
}
|
|
else if (id(humi).state < 30 or id(humi).state > 70) {
|
|
id(iaq_index) += 3;
|
|
}
|
|
else if (id(humi).state < 40 or id(humi).state > 60) {
|
|
id(iaq_index) += 4;
|
|
}
|
|
else if (id(humi).state >= 40 and id(humi).state <= 60) {
|
|
id(iaq_index) += 5;
|
|
}
|
|
|
|
if (id(pm25).state <= 12) {
|
|
id(iaq_index) += 6;
|
|
}
|
|
else if ((id(pm25).state >= 12.1) && (id(pm25).state <= 35.4)) {
|
|
id(iaq_index) += 5;
|
|
}
|
|
else if ((id(pm25).state >= 35.5) && (id(pm25).state <= 55.4)) {
|
|
id(iaq_index) += 4;
|
|
}
|
|
else if ((id(pm25).state >= 55.5) && (id(pm25).state <= 150.4)) {
|
|
id(iaq_index) += 3;
|
|
}
|
|
else if ((id(pm25).state >= 150.5) && (id(pm25).state <= 250.4)) {
|
|
id(iaq_index) += 2;
|
|
}
|
|
else if (id(pm25).state >= 250.5) {
|
|
id(iaq_index) += 1;
|
|
}
|
|
|
|
if (id(eco2).state <= 600) {
|
|
id(iaq_index) += 5;
|
|
}
|
|
else if (id(eco2).state <= 800) {
|
|
id(iaq_index) += 4;
|
|
}
|
|
else if (id(eco2).state <= 1500) {
|
|
id(iaq_index) += 3;
|
|
}
|
|
else if (id(eco2).state <= 1800) {
|
|
id(iaq_index) += 2;
|
|
}
|
|
else if (id(eco2).state > 1800) {
|
|
id(iaq_index) += 1;
|
|
}
|
|
|
|
if (id(tvoc).state <= 65) {
|
|
id(iaq_index) += 5;
|
|
}
|
|
else if (id(tvoc).state <= 220) {
|
|
id(iaq_index) += 4;
|
|
}
|
|
else if (id(tvoc).state <= 660) {
|
|
id(iaq_index) += 3;
|
|
}
|
|
else if (id(tvoc).state <= 2200) {
|
|
id(iaq_index) += 2;
|
|
}
|
|
else if (id(tvoc).state > 2200) {
|
|
id(iaq_index) += 1;
|
|
}
|
|
|
|
ESP_LOGD("main", "Current IAQ index %d", id(iaq_index));
|
|
|
|
if (id(iaq_index) <= 11) {
|
|
return {"Unhealthy"};
|
|
}
|
|
else if (id(iaq_index) <= 14) {
|
|
return {"Poor"};
|
|
}
|
|
else if (id(iaq_index) <= 17) {
|
|
return {"Moderate"};
|
|
}
|
|
else if (id(iaq_index) <= 19) {
|
|
return {"Good"};
|
|
}
|
|
else if (id(iaq_index) > 19) {
|
|
return {"Excellent"};
|
|
}
|
|
|
|
return {};
|
|
update_interval: 10s
|
|
on_value:
|
|
then:
|
|
- script.execute: update_led_color
|
|
|
|
- platform: template
|
|
name: "Livingroom IAQ Calculation"
|
|
icon: "mdi:air-filter"
|
|
id: iaq_reading_calculation
|
|
lambda: |-
|
|
id(iaq_index) = 0;
|
|
|
|
if (id(humi).state < 10 or id(humi).state > 90) {
|
|
id(iaq_index) += 1;
|
|
}
|
|
else if (id(humi).state < 20 or id(humi).state > 80) {
|
|
id(iaq_index) += 2;
|
|
}
|
|
else if (id(humi).state < 30 or id(humi).state > 70) {
|
|
id(iaq_index) += 3;
|
|
}
|
|
else if (id(humi).state < 40 or id(humi).state > 60) {
|
|
id(iaq_index) += 4;
|
|
}
|
|
else if (id(humi).state >= 40 and id(humi).state <= 60) {
|
|
id(iaq_index) += 5;
|
|
}
|
|
|
|
if (id(pm25).state <= 12) {
|
|
id(iaq_index) += 6;
|
|
}
|
|
else if ((id(pm25).state >= 12.1) && (id(pm25).state <= 35.4)) {
|
|
id(iaq_index) += 5;
|
|
}
|
|
else if ((id(pm25).state >= 35.5) && (id(pm25).state <= 55.4)) {
|
|
id(iaq_index) += 4;
|
|
}
|
|
else if ((id(pm25).state >= 55.5) && (id(pm25).state <= 150.4)) {
|
|
id(iaq_index) += 3;
|
|
}
|
|
else if ((id(pm25).state >= 150.5) && (id(pm25).state <= 250.4)) {
|
|
id(iaq_index) += 2;
|
|
}
|
|
else if (id(pm25).state >= 250.5) {
|
|
id(iaq_index) += 1;
|
|
}
|
|
|
|
if (id(eco2).state <= 600) {
|
|
id(iaq_index) += 5;
|
|
}
|
|
else if (id(eco2).state <= 800) {
|
|
id(iaq_index) += 4;
|
|
}
|
|
else if (id(eco2).state <= 1500) {
|
|
id(iaq_index) += 3;
|
|
}
|
|
else if (id(eco2).state <= 1800) {
|
|
id(iaq_index) += 2;
|
|
}
|
|
else if (id(eco2).state > 1800) {
|
|
id(iaq_index) += 1;
|
|
}
|
|
|
|
if (id(tvoc).state <= 65) {
|
|
id(iaq_index) += 5;
|
|
}
|
|
else if (id(tvoc).state <= 220) {
|
|
id(iaq_index) += 4;
|
|
}
|
|
else if (id(tvoc).state <= 660) {
|
|
id(iaq_index) += 3;
|
|
}
|
|
else if (id(tvoc).state <= 2200) {
|
|
id(iaq_index) += 2;
|
|
}
|
|
else if (id(tvoc).state > 2200) {
|
|
id(iaq_index) += 1;
|
|
}
|
|
|
|
ESP_LOGD("main", "Current IAQ index %d", id(iaq_index));
|
|
return std::to_string(id(iaq_index));
|
|
|
|
update_interval: 10s |