Files
homeassistant-config/esphome/energymonitor.yaml
T
Commander1024 f35f1eee22 Add WireGuard monitoring sensors
Expose WireGuard handshake, status, enabled state, and address in
ESPHome.
2026-07-29 23:42:58 +02:00

267 lines
7.5 KiB
YAML

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: ["äöüÄÖÜß"]
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
- platform: wireguard
latest_handshake:
name: 'WireGuard Latest Handshake'
binary_sensor:
- platform: wireguard
status:
name: 'WireGuard Status'
- platform: wireguard
enabled:
name: 'WireGuard Enabled'
# 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
text_sensor:
- platform: wireguard
address:
name: 'WireGuard Address'
# 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);