Add CO2 alerts and refine climate graph

This commit is contained in:
2026-11-06 14:29:54 +01:00
parent cdc89e68d3
commit 64de5da5a1
+44 -17
View File
@@ -2,6 +2,7 @@ substitutions:
device_description: "M5Stack Core dashboard for Home Assistant energy and internet usage, SCD30 room climate, analog/digital clock and WireGuard"
name: "energymonitor"
friendly_name: "Energy Monitor"
co2_beep_gain: "1%"
esphome:
comment: ${device_description}
@@ -101,6 +102,16 @@ output:
- platform: gpio
pin: GPIO32
id: display_backlight_output
# The original M5Stack Core's speaker amplifier is connected to GPIO25.
- platform: ledc
pin: GPIO25
id: speaker_output
frequency: 880Hz
rtttl:
id: co2_tones
output: speaker_output
gain: ${co2_beep_gain}
light:
- platform: binary
@@ -180,6 +191,10 @@ globals:
type: int
restore_value: true
initial_value: '0'
- id: co2_alert_level
type: int
restore_value: false
initial_value: '-1'
- id: climate_graph_view
type: bool
restore_value: true
@@ -233,6 +248,19 @@ sensor:
name: "CO2"
filters:
- timeout: 30s
on_value:
then:
- lambda: |-
if (!std::isfinite(x)) return;
const int level = x > 1600.0f ? 2 : x > 1200.0f ? 1 : 0;
const int previous_level = id(co2_alert_level);
id(co2_alert_level) = level;
// Establish a silent baseline at startup; only upward crossings beep.
if (previous_level < 0 || level <= previous_level) return;
id(co2_tones).stop();
id(co2_tones).play(level == 2
? "co2high:d=16,o=5,b=150:a,p,a"
: "co2warn:d=16,o=5,b=150:a");
temperature:
id: scd30_temperature
name: "Temperature"
@@ -969,7 +997,7 @@ display:
it.print(80, 140, id(font_small), temperature_color, TextAlign::TOP_CENTER, "Temperatur");
it.printf(80, 164, id(font_title), temperature_color, TextAlign::TOP_CENTER,
temperature_valid ? "%.1f °C" : "-- °C", id(scd30_temperature).state);
it.print(240, 140, id(font_small), humidity_color, TextAlign::TOP_CENTER, "Feuchte");
it.print(240, 140, id(font_small), humidity_color, TextAlign::TOP_CENTER, "Feuchtigkeit");
it.printf(240, 164, id(font_title), humidity_color, TextAlign::TOP_CENTER,
humidity_valid ? "%.1f %%" : "-- %%", id(scd30_humidity).state);
if (!co2_valid && !temperature_valid && !humidity_valid) {
@@ -978,18 +1006,23 @@ display:
}
} else {
draw_page_title("Raumklima · 1 h");
const int left = 28, right = 308;
const int left = 28, right = 308, top = 40, bottom = 142;
const int count = id(climate_history_count);
const char *labels[] = {"CO2", "Temperatur", "Feuchte"};
const char *labels[] = {"CO2", "Temperatur", "Feuchtigkeit"};
const char *units[] = {"ppm", "°C", "%"};
const Color colors[] = {co2_color, temperature_color, humidity_color};
const std::array<float, 361> *histories[] = {
&id(climate_co2_history), &id(climate_temperature_history), &id(climate_humidity_history)};
const float scale_steps[] = {100.0f, 1.0f, 5.0f};
const float minimum_ranges[] = {200.0f, 2.0f, 10.0f};
for (int minute = 0; minute <= 60; minute += 15) {
const int x = left + minute * (right - left) / 60;
it.line(x, top, x, bottom, Color(42, 42, 42));
}
it.line(left, top, right, top, Color(42, 42, 42));
it.line(left, bottom, right, bottom, Color(42, 42, 42));
for (int graph = 0; graph < 3; graph++) {
const int top = 52 + graph * 54;
const int bottom = top + 31;
const int legend_y = 168 + graph * 16;
float minimum = INFINITY, maximum = -INFINITY;
for (int sample = 0; sample < count; sample++) {
const int slot = (id(climate_history_next) - count + sample + 361) % 361;
@@ -1006,20 +1039,14 @@ display:
minimum = floorf((middle - range / 2.0f) / scale_steps[graph]) * scale_steps[graph];
maximum = ceilf((middle + range / 2.0f) / scale_steps[graph]) * scale_steps[graph];
}
it.print(12, top - 16, id(font_device), colors[graph], labels[graph]);
it.print(12, legend_y, id(font_device), colors[graph], labels[graph]);
if (has_samples) {
it.printf(right, top - 16, id(font_device), colors[graph], TextAlign::TOP_RIGHT,
it.printf(right, legend_y, id(font_device), colors[graph], TextAlign::TOP_RIGHT,
"%.0f - %.0f %s", minimum, maximum, units[graph]);
} else {
it.print(right, top - 16, id(font_device), colors[graph], TextAlign::TOP_RIGHT,
it.print(right, legend_y, id(font_device), colors[graph], TextAlign::TOP_RIGHT,
"Warte auf Messwerte");
}
for (int minute = 0; minute <= 60; minute += 15) {
const int x = left + minute * (right - left) / 60;
it.line(x, top, x, bottom, Color(42, 42, 42));
}
it.line(left, top, right, top, Color(42, 42, 42));
it.line(left, bottom, right, bottom, Color(42, 42, 42));
int previous_x = 0, previous_y = 0;
bool previous_valid = false;
for (int sample = 0; sample < count; sample++) {
@@ -1037,10 +1064,10 @@ display:
previous_valid = valid;
}
}
it.print(left, 202, id(font_device), Color::WHITE, "-60 min");
it.print((left + right) / 2, 202, id(font_device), Color::WHITE,
it.print(left, 144, id(font_device), Color::WHITE, "-60 min");
it.print((left + right) / 2, 144, id(font_device), Color::WHITE,
TextAlign::TOP_CENTER, "-30 min");
it.print(right, 202, id(font_device), Color::WHITE, TextAlign::TOP_RIGHT, "Jetzt");
it.print(right, 144, id(font_device), Color::WHITE, TextAlign::TOP_RIGHT, "Jetzt");
}
}