324 lines
8.1 KiB
YAML
324 lines
8.1 KiB
YAML
---
|
|
|
|
###############################################################################
|
|
# SuperSensor v2.0 ESPHome configuration
|
|
###############################################################################
|
|
#
|
|
# Copyright (C) 2025 Joshua M. Boniface <joshua@boniface.me>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, version 3.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
###############################################################################
|
|
|
|
esphome:
|
|
name: microenv
|
|
name_add_mac_suffix: true
|
|
friendly_name: "MicroEnv Sensor"
|
|
project:
|
|
name: "Joshua Boniface.microenv"
|
|
version: "1.0"
|
|
min_version: 2025.5.0
|
|
|
|
dashboard_import:
|
|
package_import_url: github://joshuaboniface/microenv/microenv.yaml
|
|
|
|
esp32:
|
|
board: esp32-c3-devkitm-1
|
|
variant: esp32c3
|
|
framework:
|
|
type: esp-idf
|
|
|
|
preferences:
|
|
flash_write_interval: 15sec
|
|
|
|
globals:
|
|
# Defaults to -5 due to heating from the ESP
|
|
- id: temperature_offset
|
|
type: float
|
|
restore_value: true
|
|
initial_value: "-5.0"
|
|
|
|
- id: humidity_offset
|
|
type: float
|
|
restore_value: true
|
|
initial_value: "0.0"
|
|
|
|
logger:
|
|
level: INFO
|
|
baud_rate: 115200
|
|
|
|
api:
|
|
reboot_timeout: 15min
|
|
|
|
ota:
|
|
platform: esphome
|
|
|
|
web_server:
|
|
port: 80
|
|
|
|
captive_portal:
|
|
|
|
mdns:
|
|
disabled: false
|
|
|
|
wifi:
|
|
ap: {}
|
|
domain: ""
|
|
output_power: 8.5dB
|
|
reboot_timeout: 15min
|
|
power_save_mode: none
|
|
|
|
i2c:
|
|
- id: i2c_bus
|
|
sda: GPIO21
|
|
scl: GPIO20
|
|
scan: true
|
|
|
|
sensor:
|
|
- platform: sgp30
|
|
address: 0x58
|
|
eco2:
|
|
name: "SGP30 eCO2"
|
|
id: sgp30_eco2
|
|
accuracy_decimals: 1
|
|
filters:
|
|
- sliding_window_moving_average:
|
|
window_size: 20
|
|
send_every: 1
|
|
tvoc:
|
|
name: "SGP30 TVOC"
|
|
id: sgp30_tvoc
|
|
accuracy_decimals: 1
|
|
filters:
|
|
- sliding_window_moving_average:
|
|
window_size: 20
|
|
send_every: 1
|
|
eco2_baseline:
|
|
name: "SGP30 Baseline eCO2"
|
|
id: sgp30_baseline_ec02
|
|
tvoc_baseline:
|
|
name: "SGP30 Baseline TVOC"
|
|
id: sgp30_baseline_tvoc
|
|
compensation:
|
|
temperature_source: sht45_temperature
|
|
humidity_source: sht45_humidity
|
|
store_baseline: yes
|
|
update_interval: 15s
|
|
|
|
- platform: sht4x
|
|
temperature:
|
|
name: "SHT45 Temperature"
|
|
id: sht45_temperature
|
|
accuracy_decimals: 1
|
|
filters:
|
|
- offset: !lambda return id(temperature_offset);
|
|
- sliding_window_moving_average:
|
|
window_size: 20
|
|
send_every: 1
|
|
humidity:
|
|
name: "SHT45 Relative Humidity"
|
|
id: sht45_humidity
|
|
accuracy_decimals: 1
|
|
filters:
|
|
- offset: !lambda return id(humidity_offset);
|
|
- sliding_window_moving_average:
|
|
window_size: 20
|
|
send_every: 1
|
|
heater_max_duty: 0.0
|
|
update_interval: 15s
|
|
|
|
- platform: absolute_humidity
|
|
name: "SHT45 Absolute Humidity"
|
|
temperature: sht45_temperature
|
|
humidity: sht45_humidity
|
|
id: sht45_absolute_humidity
|
|
|
|
- platform: template
|
|
name: "SHT45 Dew Point"
|
|
icon: mdi:thermometer-water
|
|
id: sht45_dew_point
|
|
unit_of_measurement: "°C"
|
|
lambda: |-
|
|
float temp = id(sht45_temperature).state;
|
|
float rh = id(sht45_humidity).state;
|
|
if (isnan(temp) || isnan(rh)) return NAN;
|
|
float a = 17.27, b = 237.7;
|
|
float alpha = ((a * temp) / (b + temp)) + log(rh / 100.0);
|
|
return (b * alpha) / (a - alpha);
|
|
update_interval: 15s
|
|
|
|
# IAQ Index (1-5, 5=Great))
|
|
- platform: template
|
|
name: "IAQ Index"
|
|
icon: mdi:air-purifier
|
|
id: iaq_index
|
|
lambda: |-
|
|
int tvoc = id(sgp30_tvoc).state;
|
|
int eco2 = id(sgp30_eco2).state;
|
|
if (tvoc > 2200 || eco2 > 2000) return 1; // Bad
|
|
if (tvoc > 660 || eco2 > 1200) return 2; // Poor
|
|
if (tvoc > 220 || eco2 > 800) return 3; // Fair
|
|
if (tvoc > 65 || eco2 > 500) return 4; // Good
|
|
return 5; // Great
|
|
update_interval: 15s
|
|
|
|
# Room Health Score (1-4, 4=Optimal)
|
|
- platform: template
|
|
name: "Room Health Score"
|
|
icon: mdi:home-thermometer
|
|
id: room_health
|
|
lambda: |-
|
|
float temp = id(sht45_temperature).state;
|
|
float rh = id(sht45_humidity).state;
|
|
int iaq = id(iaq_index).state;
|
|
|
|
bool temp_ok = (temp >= 18 && temp <= 24);
|
|
bool hum_ok = (rh >= 30 && rh <= 70);
|
|
bool iaq_ok = (iaq >= 4);
|
|
|
|
int conditions_met = 0;
|
|
if (temp_ok) conditions_met++;
|
|
if (hum_ok) conditions_met++;
|
|
if (iaq_ok) conditions_met++;
|
|
|
|
if (iaq_ok && temp_ok && hum_ok) {
|
|
return 4; // Optimal: All conditions met and IAQ is excellent/good
|
|
} else if (iaq >= 3 && conditions_met >= 2) {
|
|
return 3; // Fair: IAQ is moderate and at least 2 conditions met
|
|
} else if (iaq >= 2 && conditions_met >= 1) {
|
|
return 2; // Poor: IAQ is poor and at least 1 condition met
|
|
} else {
|
|
return 1; // Bad: All conditions failed or IAQ is unhealthy
|
|
}
|
|
|
|
- platform: wifi_signal
|
|
name: "WiFi Signal"
|
|
update_interval: 60s
|
|
entity_category: diagnostic
|
|
|
|
- platform: uptime
|
|
name: "Uptime"
|
|
update_interval: 60s
|
|
entity_category: diagnostic
|
|
|
|
text_sensor:
|
|
- platform: version
|
|
name: "ESPHome Version"
|
|
entity_category: diagnostic
|
|
|
|
- platform: wifi_info
|
|
ip_address:
|
|
name: "WiFi IP Address"
|
|
ssid:
|
|
name: "WiFi SSID"
|
|
bssid:
|
|
name: "WiFi BSSID"
|
|
mac_address:
|
|
name: "WiFi MAC Address"
|
|
|
|
# VOC Level
|
|
- platform: template
|
|
name: "VOC Level"
|
|
icon: mdi:radiator
|
|
lambda: |-
|
|
int tvoc = id(sgp30_tvoc).state;
|
|
if (tvoc < 65) return {"Great"};
|
|
if (tvoc < 220) return {"Good"};
|
|
if (tvoc < 660) return {"Fair"};
|
|
if (tvoc < 2200) return {"Poor"};
|
|
return {"Bad"};
|
|
update_interval: 15s
|
|
|
|
# CO2 Level
|
|
- platform: template
|
|
name: "CO2 Level"
|
|
icon: mdi:molecule-co2
|
|
lambda: |-
|
|
int eco2 = id(sgp30_eco2).state;
|
|
if (eco2 < 500) return {"Great"};
|
|
if (eco2 < 800) return {"Good"};
|
|
if (eco2 < 1200) return {"Fair"};
|
|
if (eco2 < 2000) return {"Poor"};
|
|
return {"Bad"};
|
|
update_interval: 15s
|
|
|
|
# IAQ Classification
|
|
- platform: template
|
|
name: "IAQ Classification"
|
|
icon: mdi:air-purifier
|
|
lambda: |-
|
|
int iaq = id(iaq_index).state;
|
|
if (iaq == 5) return {"Great"};
|
|
if (iaq == 4) return {"Good"};
|
|
if (iaq == 3) return {"Fair"};
|
|
if (iaq == 2) return {"Poor"};
|
|
return {"Bad"};
|
|
update_interval: 15s
|
|
|
|
# Room Health
|
|
- platform: template
|
|
name: "Room Health"
|
|
icon: mdi:home-thermometer
|
|
lambda: |-
|
|
int score = id(room_health).state;
|
|
if (score == 4) return {"Optimal"};
|
|
if (score == 3) return {"Fair"};
|
|
if (score == 2) return {"Poor"};
|
|
return {"Bad"};
|
|
update_interval: 15s
|
|
|
|
button:
|
|
- platform: restart
|
|
name: "ESP32 Restart"
|
|
icon: mdi:power-cycle
|
|
entity_category: diagnostic
|
|
|
|
- platform: factory_reset
|
|
name: "ESP32 Factory Reset"
|
|
icon: mdi:restart-alert
|
|
entity_category: diagnostic
|
|
|
|
number:
|
|
# Temperature offset:
|
|
# A calibration from -30 to +5 for the temperature sensor
|
|
- platform: template
|
|
name: "Temperature Offset"
|
|
id: temperature_offset_setter
|
|
min_value: -30
|
|
max_value: 10
|
|
step: 0.1
|
|
lambda: |-
|
|
return id(temperature_offset);
|
|
set_action:
|
|
then:
|
|
- globals.set:
|
|
id: temperature_offset
|
|
value: !lambda 'return float(x);'
|
|
|
|
# Humidity offset:
|
|
# A calibration from -20 to +20 for the humidity sensor
|
|
- platform: template
|
|
name: "Humidity Offset"
|
|
id: humidity_offset_setter
|
|
min_value: -20
|
|
max_value: 20
|
|
step: 0.1
|
|
lambda: |-
|
|
return id(humidity_offset);
|
|
set_action:
|
|
then:
|
|
- globals.set:
|
|
id: humidity_offset
|
|
value: !lambda 'return float(x);'
|
|
|