Add CO2 alerts and refine climate graph
This commit is contained in:
+44
-17
@@ -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"
|
device_description: "M5Stack Core dashboard for Home Assistant energy and internet usage, SCD30 room climate, analog/digital clock and WireGuard"
|
||||||
name: "energymonitor"
|
name: "energymonitor"
|
||||||
friendly_name: "Energy Monitor"
|
friendly_name: "Energy Monitor"
|
||||||
|
co2_beep_gain: "1%"
|
||||||
|
|
||||||
esphome:
|
esphome:
|
||||||
comment: ${device_description}
|
comment: ${device_description}
|
||||||
@@ -101,6 +102,16 @@ output:
|
|||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: GPIO32
|
pin: GPIO32
|
||||||
id: display_backlight_output
|
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:
|
light:
|
||||||
- platform: binary
|
- platform: binary
|
||||||
@@ -180,6 +191,10 @@ globals:
|
|||||||
type: int
|
type: int
|
||||||
restore_value: true
|
restore_value: true
|
||||||
initial_value: '0'
|
initial_value: '0'
|
||||||
|
- id: co2_alert_level
|
||||||
|
type: int
|
||||||
|
restore_value: false
|
||||||
|
initial_value: '-1'
|
||||||
- id: climate_graph_view
|
- id: climate_graph_view
|
||||||
type: bool
|
type: bool
|
||||||
restore_value: true
|
restore_value: true
|
||||||
@@ -233,6 +248,19 @@ sensor:
|
|||||||
name: "CO2"
|
name: "CO2"
|
||||||
filters:
|
filters:
|
||||||
- timeout: 30s
|
- 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:
|
temperature:
|
||||||
id: scd30_temperature
|
id: scd30_temperature
|
||||||
name: "Temperature"
|
name: "Temperature"
|
||||||
@@ -969,7 +997,7 @@ display:
|
|||||||
it.print(80, 140, id(font_small), temperature_color, TextAlign::TOP_CENTER, "Temperatur");
|
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,
|
it.printf(80, 164, id(font_title), temperature_color, TextAlign::TOP_CENTER,
|
||||||
temperature_valid ? "%.1f °C" : "-- °C", id(scd30_temperature).state);
|
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,
|
it.printf(240, 164, id(font_title), humidity_color, TextAlign::TOP_CENTER,
|
||||||
humidity_valid ? "%.1f %%" : "-- %%", id(scd30_humidity).state);
|
humidity_valid ? "%.1f %%" : "-- %%", id(scd30_humidity).state);
|
||||||
if (!co2_valid && !temperature_valid && !humidity_valid) {
|
if (!co2_valid && !temperature_valid && !humidity_valid) {
|
||||||
@@ -978,18 +1006,23 @@ display:
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
draw_page_title("Raumklima · 1 h");
|
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 int count = id(climate_history_count);
|
||||||
const char *labels[] = {"CO2", "Temperatur", "Feuchte"};
|
const char *labels[] = {"CO2", "Temperatur", "Feuchtigkeit"};
|
||||||
const char *units[] = {"ppm", "°C", "%"};
|
const char *units[] = {"ppm", "°C", "%"};
|
||||||
const Color colors[] = {co2_color, temperature_color, humidity_color};
|
const Color colors[] = {co2_color, temperature_color, humidity_color};
|
||||||
const std::array<float, 361> *histories[] = {
|
const std::array<float, 361> *histories[] = {
|
||||||
&id(climate_co2_history), &id(climate_temperature_history), &id(climate_humidity_history)};
|
&id(climate_co2_history), &id(climate_temperature_history), &id(climate_humidity_history)};
|
||||||
const float scale_steps[] = {100.0f, 1.0f, 5.0f};
|
const float scale_steps[] = {100.0f, 1.0f, 5.0f};
|
||||||
const float minimum_ranges[] = {200.0f, 2.0f, 10.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++) {
|
for (int graph = 0; graph < 3; graph++) {
|
||||||
const int top = 52 + graph * 54;
|
const int legend_y = 168 + graph * 16;
|
||||||
const int bottom = top + 31;
|
|
||||||
float minimum = INFINITY, maximum = -INFINITY;
|
float minimum = INFINITY, maximum = -INFINITY;
|
||||||
for (int sample = 0; sample < count; sample++) {
|
for (int sample = 0; sample < count; sample++) {
|
||||||
const int slot = (id(climate_history_next) - count + sample + 361) % 361;
|
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];
|
minimum = floorf((middle - range / 2.0f) / scale_steps[graph]) * scale_steps[graph];
|
||||||
maximum = ceilf((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) {
|
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]);
|
"%.0f - %.0f %s", minimum, maximum, units[graph]);
|
||||||
} else {
|
} 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");
|
"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;
|
int previous_x = 0, previous_y = 0;
|
||||||
bool previous_valid = false;
|
bool previous_valid = false;
|
||||||
for (int sample = 0; sample < count; sample++) {
|
for (int sample = 0; sample < count; sample++) {
|
||||||
@@ -1037,10 +1064,10 @@ display:
|
|||||||
previous_valid = valid;
|
previous_valid = valid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it.print(left, 202, id(font_device), Color::WHITE, "-60 min");
|
it.print(left, 144, id(font_device), Color::WHITE, "-60 min");
|
||||||
it.print((left + right) / 2, 202, id(font_device), Color::WHITE,
|
it.print((left + right) / 2, 144, id(font_device), Color::WHITE,
|
||||||
TextAlign::TOP_CENTER, "-30 min");
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user