Make power gauges scale to live values

This commit is contained in:
2026-11-06 09:35:52 +01:00
parent c743670724
commit 8f54583a0c
+33 -21
View File
@@ -2,8 +2,6 @@ substitutions:
device_description: "M5Stack Core dashboard for Home Assistant energy and internet usage, with analog/digital clock and WireGuard" device_description: "M5Stack Core dashboard for Home Assistant energy and internet usage, with analog/digital clock and WireGuard"
name: "energymonitor" name: "energymonitor"
friendly_name: "Energy Monitor" friendly_name: "Energy Monitor"
grid_power_gauge_max_w: "5000.0"
balcony_solar_gauge_max_w: "800.0"
esphome: esphome:
comment: ${device_description} comment: ${device_description}
@@ -457,6 +455,15 @@ display:
} }
it.printf(312, 8, id(font_icon), wifi_color, TextAlign::TOP_RIGHT, "%s", wifi_icon); it.printf(312, 8, id(font_icon), wifi_color, TextAlign::TOP_RIGHT, "%s", wifi_icon);
// Round live gauges up to a 1/2/5 scale, with a unit-specific minimum.
auto gauge_maximum = [](float value, float minimum) -> float {
const float peak = fmaxf(value, minimum);
const float magnitude = powf(10.0f, floorf(log10f(peak)));
const float normalized = peak / magnitude;
return magnitude * (normalized <= 1 ? 1 : normalized <= 2 ? 2
: normalized <= 5 ? 5 : 10);
};
if (id(page_index) == 0 && !id(page_one_gauge_view)) { if (id(page_index) == 0 && !id(page_one_gauge_view)) {
it.printf(12, 12, id(font_small), Color::WHITE, "Netzleistung"); it.printf(12, 12, id(font_small), Color::WHITE, "Netzleistung");
if (id(grid_power).has_state()) { if (id(grid_power).has_state()) {
@@ -506,9 +513,11 @@ display:
const int gauge_center_y = 188; const int gauge_center_y = 188;
// The outer band represents the absolute grid-power value with its signed color. // The outer band represents the absolute grid-power value with its signed color.
float grid_power_value = id(grid_power).has_state() ? id(grid_power).state : 0.0f; const bool grid_power_valid = id(grid_power).has_state() && std::isfinite(id(grid_power).state);
float grid_power_magnitude = grid_power_value < 0.0f ? -grid_power_value : grid_power_value; const float grid_power_value = grid_power_valid ? id(grid_power).state : 0.0f;
int grid_gauge_percent = static_cast<int>(grid_power_magnitude * 100.0f / ${grid_power_gauge_max_w}); const float grid_power_magnitude = fabsf(grid_power_value);
const float grid_maximum = gauge_maximum(grid_power_magnitude, 1.0f);
int grid_gauge_percent = static_cast<int>(grid_power_magnitude / grid_maximum * 100.0f);
if (grid_gauge_percent > 100) grid_gauge_percent = 100; if (grid_gauge_percent > 100) grid_gauge_percent = 100;
const Color grid_gauge_color = grid_power_value >= 0.0f const Color grid_gauge_color = grid_power_value >= 0.0f
? Color(143, 190, 223) // #8FBEDF ? Color(143, 190, 223) // #8FBEDF
@@ -516,10 +525,12 @@ display:
it.filled_gauge(gauge_center_x, gauge_center_y, 140, 122, 100, gauge_background); it.filled_gauge(gauge_center_x, gauge_center_y, 140, 122, 100, gauge_background);
it.filled_gauge(gauge_center_x, gauge_center_y, 140, 122, grid_gauge_percent, grid_gauge_color); it.filled_gauge(gauge_center_x, gauge_center_y, 140, 122, grid_gauge_percent, grid_gauge_color);
// The inner band is solar power, normalized against the 800 W inverter maximum. // The inner band scales independently to the current solar power.
float solar_power_value = id(balcony_solar_power).has_state() ? id(balcony_solar_power).state : 0.0f; const bool solar_power_valid = id(balcony_solar_power).has_state()
int solar_gauge_percent = static_cast<int>(solar_power_value * 100.0f / ${balcony_solar_gauge_max_w}); && std::isfinite(id(balcony_solar_power).state) && id(balcony_solar_power).state >= 0.0f;
if (solar_gauge_percent < 0) solar_gauge_percent = 0; const float solar_power_value = solar_power_valid ? id(balcony_solar_power).state : 0.0f;
const float solar_maximum = gauge_maximum(solar_power_value, 1.0f);
int solar_gauge_percent = static_cast<int>(solar_power_value / solar_maximum * 100.0f);
if (solar_gauge_percent > 100) solar_gauge_percent = 100; if (solar_gauge_percent > 100) solar_gauge_percent = 100;
const Color solar_gauge_color = Color(255, 170, 40); // #FFAA28 const Color solar_gauge_color = Color(255, 170, 40); // #FFAA28
it.filled_gauge(gauge_center_x, gauge_center_y, 116, 98, 100, gauge_background); it.filled_gauge(gauge_center_x, gauge_center_y, 116, 98, 100, gauge_background);
@@ -527,7 +538,7 @@ display:
it.printf(gauge_center_x, 102, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, it.printf(gauge_center_x, 102, id(font_small), Color::WHITE, TextAlign::TOP_CENTER,
"Netzleistung"); "Netzleistung");
if (id(grid_power).has_state()) { if (grid_power_valid) {
it.printf(gauge_center_x, 120, id(font_title), grid_gauge_color, TextAlign::TOP_CENTER, it.printf(gauge_center_x, 120, id(font_title), grid_gauge_color, TextAlign::TOP_CENTER,
"%+.0f W", grid_power_value); "%+.0f W", grid_power_value);
} else { } else {
@@ -535,12 +546,21 @@ display:
} }
it.printf(gauge_center_x, 151, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, it.printf(gauge_center_x, 151, id(font_small), Color::WHITE, TextAlign::TOP_CENTER,
"Balkonkraftwerk"); "Balkonkraftwerk");
if (id(balcony_solar_power).has_state()) { if (solar_power_valid) {
it.printf(gauge_center_x, 169, id(font_title), solar_gauge_color, TextAlign::TOP_CENTER, it.printf(gauge_center_x, 169, id(font_title), solar_gauge_color, TextAlign::TOP_CENTER,
"%.0f W", solar_power_value); "%.0f W", solar_power_value);
} else { } else {
it.printf(gauge_center_x, 169, id(font_title), solar_gauge_color, TextAlign::TOP_CENTER, "-- W"); it.printf(gauge_center_x, 169, id(font_title), solar_gauge_color, TextAlign::TOP_CENTER, "-- W");
} }
auto format_power = [](float watts) -> std::string {
char text[32];
snprintf(text, sizeof(text), "%.0f W", watts);
return std::string(text);
};
it.printf(gauge_center_x, 204, id(font_device), Color::WHITE, TextAlign::TOP_CENTER,
"Max: Netz %s / Solar %s",
grid_power_valid ? format_power(grid_maximum).c_str() : "--",
solar_power_valid ? format_power(solar_maximum).c_str() : "--");
} else if (id(page_index) == 1) { } else if (id(page_index) == 1) {
// Page 2 lists nine consumers at once in two columns; button B changes group. // Page 2 lists nine consumers at once in two columns; button B changes group.
const int device_count = 18; const int device_count = 18;
@@ -727,16 +747,8 @@ display:
if (id(internet_view) == 0) { if (id(internet_view) == 0) {
it.print(12, 10, id(font_small), Color::WHITE, "Internet"); it.print(12, 10, id(font_small), Color::WHITE, "Internet");
const int cx = 160, cy = 188; const int cx = 160, cy = 188;
// Round each live rate up to a 1/2/5 scale, with a 1 kbps minimum. const float download_maximum = gauge_maximum(download_valid ? download : 0.0f, 0.001f);
auto gauge_maximum = [](float rate) -> float { const float upload_maximum = gauge_maximum(upload_valid ? upload : 0.0f, 0.001f);
const float peak = fmaxf(rate, 0.001f);
const float magnitude = powf(10.0f, floorf(log10f(peak)));
const float normalized = peak / magnitude;
return magnitude * (normalized <= 1 ? 1 : normalized <= 2 ? 2
: normalized <= 5 ? 5 : 10);
};
const float download_maximum = gauge_maximum(download_valid ? download : 0.0f);
const float upload_maximum = gauge_maximum(upload_valid ? upload : 0.0f);
const float download_fraction = download_valid const float download_fraction = download_valid
? fminf(download / download_maximum, 1.0f) : 0.0f; ? fminf(download / download_maximum, 1.0f) : 0.0f;
const float upload_fraction = upload_valid const float upload_fraction = upload_valid