substitutions: name: "energymonitor" friendly_name: "Energy Monitor" esphome: name: ${name} friendly_name: ${friendly_name} name_add_mac_suffix: false project: name: monitor.energymonitor version: "1.0" on_boot: then: - light.turn_on: display_backlight esp32: board: m5stack-core-esp32 framework: type: esp-idf network: enable_ipv6: true wifi: networks: - ssid: "Voltage-legacy" password: !secret voltage_legacy_psk priority: 10 - ssid: "Moppelkotze" password: !secret moppelkotze_psk priority: 5 use_address: 192.168.126.250 on_connect: - lambda: id(wifi_connected) = true; on_disconnect: - lambda: id(wifi_connected) = false; power_save_mode: high min_auth_mode: WPA2 ap: ssid: "Energymonitor Fallback Hotspot" password: !secret fallback_psk captive_portal: time: - platform: sntp id: sntp_time timezone: Europe/Berlin servers: - de.pool.ntp.org # Values are maintained in secrets.yaml. Add further peer_allowed_ips entries as # needed when the tunnel and Home Assistant network details are available. wireguard: id: energymonitor_wireguard address: !secret energymonitor_wireguard_address netmask: !secret energymonitor_wireguard_netmask private_key: !secret energymonitor_wireguard_private_key peer_endpoint: !secret energymonitor_wireguard_peer_endpoint peer_port: !secret energymonitor_wireguard_peer_port peer_public_key: !secret energymonitor_wireguard_peer_public_key peer_allowed_ips: - !secret energymonitor_wireguard_peer_allowed_ip peer_persistent_keepalive: 25s # Avoid rebooting while the placeholder endpoint is intentionally unreachable. reboot_timeout: 0s logger: api: encryption: key: !secret apikey ota: platform: esphome password: !secret ota spi: clk_pin: GPIO18 mosi_pin: GPIO23 output: - platform: gpio pin: GPIO32 id: display_backlight_output light: - platform: binary id: display_backlight name: "Display Backlight" output: display_backlight_output restore_mode: ALWAYS_ON font: - file: "fonts/Roboto-Regular.ttf" id: font_small size: 18 glyphsets: - GF_Latin_Kernel glyphs: ["äöüÄÖÜß"] - file: "fonts/Roboto-Bold.ttf" id: font_title size: 28 glyphsets: - GF_Latin_Kernel glyphs: ["äöüÄÖÜß"] - file: "fonts/Roboto-Bold.ttf" id: font_value size: 52 glyphsets: - GF_Latin_Kernel glyphs: ["äöüÄÖÜß"] # M5CORE is the mipi_spi preset for the M5Stack Core's integrated display. display: - platform: mipi_spi id: tft model: M5CORE cs_pin: GPIO14 dc_pin: GPIO27 reset_pin: GPIO33 invert_colors: true update_interval: 1s lambda: |- it.fill(Color::BLACK); if (id(sntp_time).now().is_valid()) { it.strftime(260, 10, id(font_small), Color::WHITE, TextAlign::TOP_RIGHT, "%H:%M", id(sntp_time).now()); } else { it.printf(260, 10, id(font_small), Color::WHITE, TextAlign::TOP_RIGHT, "--:--"); } // The WireGuard key reflects the remote peer status. const bool wireguard_online = id(energymonitor_wireguard).is_peer_up(); const Color wireguard_color = wireguard_online ? Color(64, 255, 64) : Color(255, 80, 80); it.circle(270, 15, 5, wireguard_color); it.line(274, 19, 284, 29, wireguard_color); it.line(279, 24, 282, 21, wireguard_color); it.line(282, 27, 285, 24, wireguard_color); if (!wireguard_online) { it.line(265, 30, 285, 10, wireguard_color); } // A radio-wave Wi-Fi symbol shows RSSI quality; a red slash means disconnected. const bool wifi_online = id(wifi_connected); int wifi_bars = 1; if (wifi_online && id(wifi_rssi).has_state()) { const float rssi = id(wifi_rssi).state; wifi_bars = rssi >= -55 ? 4 : rssi >= -67 ? 3 : rssi >= -75 ? 2 : 1; } const Color wifi_color = wifi_online ? Color(64, 255, 64) : Color(255, 80, 80); it.filled_circle(299, 28, 2, wifi_color); if (wifi_bars >= 2 || !wifi_online) { it.line(296, 25, 299, 22, wifi_color); it.line(299, 22, 302, 25, wifi_color); } if (wifi_bars >= 3 || !wifi_online) { it.line(293, 23, 299, 17, wifi_color); it.line(299, 17, 305, 23, wifi_color); } if (wifi_bars >= 4 || !wifi_online) { it.line(290, 20, 299, 12, wifi_color); it.line(299, 12, 308, 20, wifi_color); } if (!wifi_online) { it.line(290, 30, 308, 12, wifi_color); } if (id(page_index) == 0) { it.printf(12, 12, id(font_small), Color::WHITE, "Netzleistung"); if (id(grid_power).has_state()) { it.printf(308, 32, id(font_value), Color::WHITE, TextAlign::TOP_RIGHT, "%+.0f W", id(grid_power).state); } else { it.printf(308, 42, id(font_value), Color::WHITE, TextAlign::TOP_RIGHT, "-- W"); } it.printf(12, 99, id(font_small), Color::WHITE, "Balkonkraftwerk"); if (id(balcony_solar_power).has_state()) { it.printf(308, 119, id(font_value), Color(255, 215, 0), TextAlign::TOP_RIGHT, "%.0f W", id(balcony_solar_power).state); } else { it.printf(308, 129, id(font_value), Color(255, 215, 0), TextAlign::TOP_RIGHT, "-- 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"); } // 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"); it.printf(312, 239, id(font_small), Color::WHITE, TextAlign::BOTTOM_RIGHT, "Weiter >"); it.printf(160, 239, id(font_small), Color::WHITE, TextAlign::BOTTOM_CENTER, "%d / 2", id(page_index) + 1); globals: - id: page_index type: int restore_value: true initial_value: '0' - id: wifi_connected type: bool restore_value: false initial_value: 'false' sensor: - platform: wifi_signal id: wifi_rssi internal: true update_interval: 10s - platform: homeassistant id: grid_power entity_id: sensor.netzleistung internal: true - platform: homeassistant id: balcony_solar_power entity_id: sensor.balkonkraftwerk_power internal: true binary_sensor: # M5Stack Core button A (left). GPIO39 is input-only and has an external pull-up. - platform: gpio pin: number: GPIO39 mode: INPUT inverted: true id: button_left on_press: then: - lambda: |- id(page_index) = (id(page_index) + 2 - 1) % 2; - component.update: tft # M5Stack Core button C (right). GPIO37 is input-only and has an external pull-up. - platform: gpio pin: number: GPIO37 mode: INPUT inverted: true id: button_right on_press: then: - lambda: |- id(page_index) = (id(page_index) + 1) % 2; - component.update: tft