diff --git a/esphome/energymonitor.yaml b/esphome/energymonitor.yaml index 5fefa12..c710de1 100644 --- a/esphome/energymonitor.yaml +++ b/esphome/energymonitor.yaml @@ -149,6 +149,10 @@ globals: type: bool restore_value: true initial_value: 'false' + - id: clock_digital_view + type: bool + restore_value: true + initial_value: 'false' - id: device_usage_view type: int restore_value: true @@ -259,20 +263,22 @@ sensor: script: - id: show_previous_page then: - - lambda: id(page_index) = (id(page_index) + 2 - 1) % 2; + - lambda: id(page_index) = (id(page_index) + 3 - 1) % 3; - component.update: tft - id: toggle_page_mode then: - lambda: |- if (id(page_index) == 0) { id(page_one_gauge_view) = !id(page_one_gauge_view); - } else { + } else if (id(page_index) == 1) { id(device_usage_view) = (id(device_usage_view) + 1) % 2; + } else { + id(clock_digital_view) = !id(clock_digital_view); } - component.update: tft - id: show_next_page then: - - lambda: id(page_index) = (id(page_index) + 1) % 2; + - lambda: id(page_index) = (id(page_index) + 1) % 3; - component.update: tft button: @@ -299,7 +305,7 @@ binary_sensor: - platform: wireguard enabled: name: 'WireGuard Enabled' - # M5Stack Core button B (middle) toggles the alternate gauge view on page one. + # M5Stack Core button B (middle) switches the current page's view. - platform: gpio pin: number: GPIO38 @@ -462,7 +468,7 @@ display: } else { it.printf(gauge_center_x, 169, id(font_title), solar_gauge_color, TextAlign::TOP_CENTER, "-- W"); } - } else { + } 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; const int usage_group = id(device_usage_view); @@ -538,6 +544,93 @@ display: } } + if (id(page_index) == 2) { + const auto now = id(sntp_time).now(); + const Color accent(143, 190, 223); + const Color gold(255, 170, 40); + const Color muted(55, 65, 80); + it.print(12, 10, id(font_device), accent, "Uhr · Berlin"); + if (!now.is_valid()) { + it.print(160, 100, id(font_title), accent, TextAlign::TOP_CENTER, "--:--:--"); + it.print(160, 190, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, + "Warte auf Zeitsynchronisation"); + } else { + if (!id(clock_digital_view)) { + const int cx = 160, cy = 111, radius = 74; + const float tau = 6.28318530718f; + it.filled_circle(cx, cy, radius, Color(12, 18, 28)); + it.circle(cx, cy, radius, accent); + it.circle(cx, cy, radius - 3, muted); + for (int tick = 0; tick < 60; tick++) { + const float angle = tick * tau / 60.0f; + const int inner = tick % 5 == 0 ? radius - 13 : radius - 8; + it.line(cx + lroundf(sinf(angle) * inner), cy - lroundf(cosf(angle) * inner), + cx + lroundf(sinf(angle) * (radius - 5)), + cy - lroundf(cosf(angle) * (radius - 5)), + tick % 5 == 0 ? accent : muted); + } + it.print(cx, cy - 49, id(font_device), Color::WHITE, TextAlign::CENTER, "12"); + it.print(cx + 50, cy, id(font_device), Color::WHITE, TextAlign::CENTER, "3"); + it.print(cx, cy + 49, id(font_device), Color::WHITE, TextAlign::CENTER, "6"); + it.print(cx - 50, cy, id(font_device), Color::WHITE, TextAlign::CENTER, "9"); + // Round-ended hands; minutes and hours advance with the seconds. + auto hand = [&](float turns, int length, int thickness, Color color) { + const float angle = turns * tau; + for (int r = 0; r <= length; r++) { + it.filled_circle(cx + lroundf(sinf(angle) * r), + cy - lroundf(cosf(angle) * r), thickness, color); + } + }; + hand((now.hour % 12 + now.minute / 60.0f + now.second / 3600.0f) / 12.0f, + 34, 3, Color::WHITE); + hand((now.minute + now.second / 60.0f) / 60.0f, 51, 2, accent); + const float second_angle = now.second * tau / 60.0f; + it.line(cx - lroundf(sinf(second_angle) * 12), cy + lroundf(cosf(second_angle) * 12), + cx + lroundf(sinf(second_angle) * 60), cy - lroundf(cosf(second_angle) * 60), gold); + it.filled_circle(cx, cy, 4, gold); + it.filled_circle(cx, cy, 1, Color::BLACK); + } else { + // Draw seven-segment digits directly, avoiding an extra font dependency. + const uint8_t masks[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, + 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; + const int digits[6] = {now.hour / 10, now.hour % 10, now.minute / 10, + now.minute % 10, now.second / 10, now.second % 10}; + const int positions[6] = {17, 61, 121, 165, 225, 269}; + const int y = 77; + for (int digit = 0; digit < 6; digit++) { + const int x = positions[digit]; + const Color lit = digit < 4 ? accent : gold; + for (int segment = 0; segment < 7; segment++) { + const Color color = (masks[digits[digit]] & (1 << segment)) + ? lit : Color(20, 27, 36); + const bool horizontal = segment == 0 || segment == 3 || segment == 6; + const int sx = horizontal ? x + 6 : x + ((segment == 1 || segment == 2) ? 30 : 0); + const int sy = y + (segment == 0 ? 0 : segment == 3 ? 72 : segment == 6 ? 36 + : (segment == 1 || segment == 5) ? 6 : 42); + // Taper each segment's ends for the classic LED clock shape. + for (int stripe = 0; stripe < 6; stripe++) { + const int inset = stripe < 3 ? 2 - stripe : stripe - 3; + if (horizontal) + it.line(sx + inset, sy + stripe, sx + 23 - inset, sy + stripe, color); + else + it.line(sx + stripe, sy + inset, sx + stripe, sy + 29 - inset, color); + } + } + } + for (int x : {109, 213}) { + it.filled_circle(x, y + 24, 3, accent); + it.filled_circle(x, y + 54, 3, accent); + } + it.line(40, 172, 280, 172, muted); + } + const char *weekdays[] = {"Sonntag", "Montag", "Dienstag", "Mittwoch", + "Donnerstag", "Freitag", "Samstag"}; + it.printf(160, 190, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, + "%s, %02d.%02d.%04d", weekdays[now.day_of_week - 1], + now.day_of_month, now.month, now.year); + } + } + // Labels correspond to the left (A) and right (C) hardware buttons. it.printf(8, 239, id(font_small), Color::WHITE, TextAlign::BOTTOM_LEFT, "< Zurück"); @@ -545,8 +638,11 @@ display: "Weiter >"); if (id(page_index) == 0) { it.printf(160, 239, id(font_small), Color::WHITE, TextAlign::BOTTOM_CENTER, - "Ansicht · 1 / 2"); - } else { + "Ansicht · 1 / 3"); + } else if (id(page_index) == 1) { it.printf(160, 239, id(font_small), Color::WHITE, TextAlign::BOTTOM_CENTER, "Geräte %d / 2", id(device_usage_view) + 1); + } else { + it.printf(160, 239, id(font_small), Color::WHITE, TextAlign::BOTTOM_CENTER, + "%s · 3 / 3", id(clock_digital_view) ? "Analog" : "Digital"); }