From 8f54583a0c0002d5f46689acb05cc38e50733fe1 Mon Sep 17 00:00:00 2001 From: Commander1024 Date: Fri, 6 Nov 2026 09:35:52 +0100 Subject: [PATCH] Make power gauges scale to live values --- esphome/energymonitor.yaml | 54 +++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/esphome/energymonitor.yaml b/esphome/energymonitor.yaml index db011c1..bb738a0 100644 --- a/esphome/energymonitor.yaml +++ b/esphome/energymonitor.yaml @@ -2,8 +2,6 @@ substitutions: device_description: "M5Stack Core dashboard for Home Assistant energy and internet usage, with analog/digital clock and WireGuard" name: "energymonitor" friendly_name: "Energy Monitor" - grid_power_gauge_max_w: "5000.0" - balcony_solar_gauge_max_w: "800.0" esphome: comment: ${device_description} @@ -457,6 +455,15 @@ display: } 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)) { it.printf(12, 12, id(font_small), Color::WHITE, "Netzleistung"); if (id(grid_power).has_state()) { @@ -506,9 +513,11 @@ display: const int gauge_center_y = 188; // 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; - float grid_power_magnitude = grid_power_value < 0.0f ? -grid_power_value : grid_power_value; - int grid_gauge_percent = static_cast(grid_power_magnitude * 100.0f / ${grid_power_gauge_max_w}); + const bool grid_power_valid = id(grid_power).has_state() && std::isfinite(id(grid_power).state); + const float grid_power_value = grid_power_valid ? id(grid_power).state : 0.0f; + 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(grid_power_magnitude / grid_maximum * 100.0f); if (grid_gauge_percent > 100) grid_gauge_percent = 100; const Color grid_gauge_color = grid_power_value >= 0.0f ? 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, grid_gauge_percent, grid_gauge_color); - // The inner band is solar power, normalized against the 800 W inverter maximum. - float solar_power_value = id(balcony_solar_power).has_state() ? id(balcony_solar_power).state : 0.0f; - int solar_gauge_percent = static_cast(solar_power_value * 100.0f / ${balcony_solar_gauge_max_w}); - if (solar_gauge_percent < 0) solar_gauge_percent = 0; + // The inner band scales independently to the current solar power. + const bool solar_power_valid = id(balcony_solar_power).has_state() + && std::isfinite(id(balcony_solar_power).state) && id(balcony_solar_power).state >= 0.0f; + 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(solar_power_value / solar_maximum * 100.0f); if (solar_gauge_percent > 100) solar_gauge_percent = 100; const Color solar_gauge_color = Color(255, 170, 40); // #FFAA28 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, "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, "%+.0f W", grid_power_value); } else { @@ -535,12 +546,21 @@ display: } it.printf(gauge_center_x, 151, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, "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, "%.0f W", solar_power_value); } else { 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) { // Page 2 lists nine consumers at once in two columns; button B changes group. const int device_count = 18; @@ -727,16 +747,8 @@ display: if (id(internet_view) == 0) { it.print(12, 10, id(font_small), Color::WHITE, "Internet"); const int cx = 160, cy = 188; - // Round each live rate up to a 1/2/5 scale, with a 1 kbps minimum. - auto gauge_maximum = [](float rate) -> float { - 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_maximum = gauge_maximum(download_valid ? download : 0.0f, 0.001f); + const float upload_maximum = gauge_maximum(upload_valid ? upload : 0.0f, 0.001f); const float download_fraction = download_valid ? fminf(download / download_maximum, 1.0f) : 0.0f; const float upload_fraction = upload_valid