diff --git a/esphome/energymonitor.yaml b/esphome/energymonitor.yaml index 864a3df..5fefa12 100644 --- a/esphome/energymonitor.yaml +++ b/esphome/energymonitor.yaml @@ -118,6 +118,12 @@ font: glyphsets: - GF_Latin_Kernel glyphs: ["äöüÄÖÜß"] + - file: "fonts/Roboto-Regular.ttf" + id: font_device + size: 13 + glyphsets: + - GF_Latin_Kernel + glyphs: ["äöüÄÖÜß"] - file: "fonts/materialdesignicons-webfont.ttf" id: font_icon size: 24 @@ -143,6 +149,10 @@ globals: type: bool restore_value: true initial_value: 'false' + - id: device_usage_view + type: int + restore_value: true + initial_value: '0' sensor: # WiFi signal strength @@ -170,10 +180,118 @@ sensor: id: grid_export_energy_daily entity_id: sensor.energy_export_daily internal: true + - platform: homeassistant + id: hallway_fragrance_power + entity_id: sensor.flur_raumduft_power + internal: true + - platform: homeassistant + id: cellar_power + entity_id: sensor.keller_power + internal: true + - platform: homeassistant + id: sideboard_power + entity_id: sensor.kuche_anrichte_power + internal: true + - platform: homeassistant + id: kitchen_music_power + entity_id: sensor.kuche_musik_power + internal: true + - platform: homeassistant + id: bedroom_bed_power + entity_id: sensor.schlafzimmer_bett_power + internal: true + - platform: homeassistant + id: bedroom_cinema_power + entity_id: sensor.schlafzimmer_heimkino_power + internal: true + - platform: homeassistant + id: desk_decoration_power + entity_id: sensor.schreibtisch_deko_power + internal: true + - platform: homeassistant + id: desk_power + entity_id: sensor.schreibtisch_power + internal: true + - platform: homeassistant + id: server_room_power + entity_id: sensor.serverraum_power + internal: true + - platform: homeassistant + id: living_room_cinema_power + entity_id: sensor.wohnzimmer_heimkino_power + internal: true + - platform: homeassistant + id: games_cabinet_power + entity_id: sensor.wohnzimmer_spieleschrank_power + internal: true + - platform: homeassistant + id: refrigerator_power + entity_id: sensor.tz3000_ww6drja5_ts011f_leistung + internal: true + - platform: homeassistant + id: coffee_machine_power + entity_id: sensor.kaffeemaschine_leistung_2 + internal: true + - platform: homeassistant + id: dryer_power + entity_id: sensor.ikea_of_sweden_inspelning_smart_plug_leistung + internal: true + - platform: homeassistant + id: washing_machine_power + entity_id: sensor.waschmaschine_leistung + internal: true + - platform: homeassistant + id: arcade_power + entity_id: sensor.arcade_automat_leistung + internal: true + - platform: homeassistant + id: dishwasher_power + entity_id: sensor.spulmaschine_leistung + internal: true + - platform: homeassistant + id: microwave_power + entity_id: sensor.waschetrockner_leistung + internal: true - platform: wireguard latest_handshake: name: 'WireGuard Latest Handshake' +script: + - id: show_previous_page + then: + - lambda: id(page_index) = (id(page_index) + 2 - 1) % 2; + - 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 { + id(device_usage_view) = (id(device_usage_view) + 1) % 2; + } + - component.update: tft + - id: show_next_page + then: + - lambda: id(page_index) = (id(page_index) + 1) % 2; + - component.update: tft + +button: + - platform: template + name: "Zurück" + icon: mdi:arrow-left + on_press: + - script.execute: show_previous_page + - platform: template + name: "Modus" + icon: mdi:view-dashboard-outline + on_press: + - script.execute: toggle_page_mode + - platform: template + name: "Weiter" + icon: mdi:arrow-right + on_press: + - script.execute: show_next_page + binary_sensor: - platform: wireguard status: @@ -189,9 +307,7 @@ binary_sensor: inverted: true id: button_middle on_press: - then: - - lambda: id(page_one_gauge_view) = !id(page_one_gauge_view); - - component.update: tft + - script.execute: toggle_page_mode # M5Stack Core button A (left). GPIO39 is input-only and has an external pull-up. - platform: gpio @@ -201,10 +317,7 @@ binary_sensor: inverted: true id: button_left on_press: - then: - - lambda: |- - id(page_index) = (id(page_index) + 2 - 1) % 2; - - component.update: tft + - script.execute: show_previous_page # M5Stack Core button C (right). GPIO37 is input-only and has an external pull-up. - platform: gpio pin: @@ -213,10 +326,7 @@ binary_sensor: inverted: true id: button_right on_press: - then: - - lambda: |- - id(page_index) = (id(page_index) + 1) % 2; - - component.update: tft + - script.execute: show_next_page text_sensor: - platform: wireguard @@ -353,10 +463,79 @@ display: it.printf(gauge_center_x, 169, id(font_title), solar_gauge_color, TextAlign::TOP_CENTER, "-- W"); } } else { - it.printf(160, 45, id(font_title), Color::WHITE, TextAlign::TOP_CENTER, - "Weitere Seite"); - it.printf(160, 95, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, - "Für zusätzliche Werte vorbereitet"); + // 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); + const int page_start = usage_group * 9; + const char *device_names[device_count] = { + "Raumduft", "Keller", "Anrichte", "Musik", "Bett", "HK Schlafzimmer", + "Deko", "Schreibtisch", "Serverraum", "HK Wohnzimmer", "Spieleschrank", + "Kühlschrank", "Kaffeemaschine", "Wäschetrockner", "Waschmaschine", "Arcade", + "Spülmaschine", "Mikrowelle"}; + float device_values[device_count] = { + id(hallway_fragrance_power).state, id(cellar_power).state, id(sideboard_power).state, + id(kitchen_music_power).state, id(bedroom_bed_power).state, id(bedroom_cinema_power).state, + id(desk_decoration_power).state, id(desk_power).state, id(server_room_power).state, + id(living_room_cinema_power).state, id(games_cabinet_power).state, id(refrigerator_power).state, + id(coffee_machine_power).state, id(dryer_power).state, id(washing_machine_power).state, + id(arcade_power).state, id(dishwasher_power).state, id(microwave_power).state}; + bool device_has_state[device_count] = { + id(hallway_fragrance_power).has_state(), id(cellar_power).has_state(), id(sideboard_power).has_state(), + id(kitchen_music_power).has_state(), id(bedroom_bed_power).has_state(), id(bedroom_cinema_power).has_state(), + id(desk_decoration_power).has_state(), id(desk_power).has_state(), id(server_room_power).has_state(), + id(living_room_cinema_power).has_state(), id(games_cabinet_power).has_state(), id(refrigerator_power).has_state(), + id(coffee_machine_power).has_state(), id(dryer_power).has_state(), id(washing_machine_power).has_state(), + id(arcade_power).has_state(), id(dishwasher_power).has_state(), id(microwave_power).has_state()}; + Color device_colors[device_count] = { + Color(255, 170, 40), Color(69, 189, 255), Color(170, 116, 217), Color(93, 207, 140), + Color(238, 112, 112), Color(107, 139, 255), Color(255, 130, 180), Color(122, 204, 174), + Color(240, 195, 90), Color(151, 120, 220), Color(91, 189, 193), Color(212, 132, 82), + Color(143, 190, 223), Color(233, 106, 142), Color(132, 205, 92), Color(255, 181, 80), + Color(111, 167, 229), Color(190, 137, 226)}; + + // The title uses the free area to the left of the status indicators. + it.printf(12, 10, id(font_device), Color::WHITE, "Geräteverbrauch"); + for (int display_index = 0; display_index < 9; display_index++) { + const int index = page_start + display_index; + const int column = display_index % 2; + const int row = display_index / 2; + const int x = column == 0 ? 12 : 166; + const int y = 43 + row * 25; + if (device_has_state[index]) { + it.printf(x, y, id(font_device), device_colors[index], "%s: %.0f W", + device_names[index], device_values[index]); + } else { + it.printf(x, y, id(font_device), device_colors[index], "%s: -- W", device_names[index]); + } + } + + // The stacked bar always represents all 18 consumer devices, not only this view's rows. + const int bar_x = 12; + const int bar_y = 180; + const int bar_width = 296; + const int bar_height = 18; + float usage_sum = 0.0f; + int last_active = -1; + for (int index = 0; index < device_count; index++) { + if (device_has_state[index] && device_values[index] > 0.0f) { + usage_sum += device_values[index]; + last_active = index; + } + } + it.filled_rectangle(bar_x, bar_y, bar_width, bar_height, Color(42, 42, 42)); + if (usage_sum > 0.0f) { + int cursor_x = bar_x; + for (int index = 0; index < device_count; index++) { + if (!device_has_state[index] || device_values[index] <= 0.0f) continue; + const int segment_width = index == last_active + ? bar_x + bar_width - cursor_x + : static_cast(device_values[index] * bar_width / usage_sum); + if (segment_width > 0) { + it.filled_rectangle(cursor_x, bar_y, segment_width, bar_height, device_colors[index]); + cursor_x += segment_width; + } + } + } } // Labels correspond to the left (A) and right (C) hardware buttons. @@ -367,7 +546,7 @@ display: if (id(page_index) == 0) { it.printf(160, 239, id(font_small), Color::WHITE, TextAlign::BOTTOM_CENTER, "Ansicht · 1 / 2"); - } else { + } else { it.printf(160, 239, id(font_small), Color::WHITE, TextAlign::BOTTOM_CENTER, - "2 / 2"); + "Geräte %d / 2", id(device_usage_view) + 1); }