External Screen working
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "esphome.h"
|
||||
#include "esphome/components/web_server_base/web_server_base.h"
|
||||
#include "esphome/components/json/json_util.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
// Callback signature: void(device_id, cloud_alert, defrost_mode)
|
||||
using DisplayUpdateCallback = std::function<void(const std::string &device_id, bool cloud_alert, bool defrost_mode)>;
|
||||
|
||||
static DisplayUpdateCallback g_display_callback = nullptr;
|
||||
|
||||
class DisplayApiHandler : public esphome::web_server_idf::AsyncWebHandler {
|
||||
public:
|
||||
bool canHandle(esphome::web_server_idf::AsyncWebServerRequest *request) const override {
|
||||
return request->url_to() == "/api/display";
|
||||
}
|
||||
|
||||
void handleRequest(esphome::web_server_idf::AsyncWebServerRequest *request) override {
|
||||
request->send(200, "application/json", "{\"status\":\"ok\"}");
|
||||
}
|
||||
|
||||
void handleBody(esphome::web_server_idf::AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override {
|
||||
std::string body((char *)data, len);
|
||||
|
||||
esphome::json::parse_json(body, [](JsonObject root) -> bool {
|
||||
std::string device_id = root["device_id"] | "unknown";
|
||||
bool cloud_alert = root["cloud_alert"] | false;
|
||||
bool defrost_mode = root["defrost_mode"] | false;
|
||||
|
||||
ESP_LOGI("custom_api", "Received update for %s (Alert: %d, Defrost: %d)",
|
||||
device_id.c_str(), cloud_alert, defrost_mode);
|
||||
|
||||
// Execute the callback passed from YAML
|
||||
if (g_display_callback != nullptr) {
|
||||
g_display_callback(device_id, cloud_alert, defrost_mode);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
request->send(200, "application/json", "{\"status\":\"ok\"}");
|
||||
}
|
||||
};
|
||||
|
||||
inline void setup_display_api(DisplayUpdateCallback callback) {
|
||||
g_display_callback = callback;
|
||||
if (esphome::web_server_base::global_web_server_base != nullptr) {
|
||||
esphome::web_server_base::global_web_server_base->add_handler(new DisplayApiHandler());
|
||||
ESP_LOGI("custom_api", "Successfully registered /api/display POST handler");
|
||||
} else {
|
||||
ESP_LOGE("custom_api", "Failed to register endpoint: global_web_server_base is null");
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,57 @@ substitutions:
|
||||
device_id: !include device_id.txt
|
||||
|
||||
esphome:
|
||||
name: epaper-node
|
||||
friendly_name: "Microwave Thread Display Node"
|
||||
name: "smartwave-epaper-${device_id}"
|
||||
friendly_name: "SmartWave ePaper ExternalScreen ${device_id}"
|
||||
includes:
|
||||
- display_api.h
|
||||
on_boot:
|
||||
priority: -100.0
|
||||
then:
|
||||
- component.update: my_display
|
||||
- lambda: |-
|
||||
setup_display_api([](const std::string &dev_id, bool alert, bool defrost) {
|
||||
// Update global state
|
||||
id(cloud_alert) = alert;
|
||||
id(defrost_mode) = defrost;
|
||||
|
||||
// Set status message text
|
||||
std::string msg = "Microwave " + dev_id;
|
||||
id(status_message).publish_state(msg.c_str());
|
||||
|
||||
// Force immediate e-Paper refresh!
|
||||
id(my_display).update();
|
||||
});
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# State Globals
|
||||
# -------------------------------------------------------------------
|
||||
globals:
|
||||
- id: cloud_alert
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: defrost_mode
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
|
||||
web_server:
|
||||
port: 5000
|
||||
|
||||
mdns:
|
||||
disabled: false
|
||||
services:
|
||||
- service: "_displaytcp"
|
||||
protocol: "_tcp"
|
||||
port: 5000
|
||||
txt:
|
||||
version: "1.0"
|
||||
type: "epaper_2.13"
|
||||
|
||||
esp32:
|
||||
board: esp32-h2-devkitm-1
|
||||
board: epaper-node-display
|
||||
variant: esp32h2
|
||||
framework:
|
||||
type: esp-idf
|
||||
log_level: INFO
|
||||
advanced:
|
||||
loop_task_stack_size: 32768
|
||||
sdkconfig_options:
|
||||
@@ -25,43 +64,39 @@ esp32:
|
||||
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA: "y"
|
||||
|
||||
logger:
|
||||
level: INFO
|
||||
level: VERBOSE
|
||||
hardware_uart: UART0
|
||||
logs:
|
||||
openthread: INFO
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# OpenThread Network Settings
|
||||
# -------------------------------------------------------------------
|
||||
openthread:
|
||||
network_name: "OpenThread-7f41"
|
||||
pan_id: 0x7f41
|
||||
channel: 24
|
||||
network_key: "0x3f543281275a135e38982974aa71e987"
|
||||
force_dataset: true
|
||||
tlv: 00030000184a0300000b35060004001fffe00208f7754c0c4a4dea9a0708fd1adbbe7435e95405103f543281275a135e38982974aa71e987030f4f70656e5468726561642d3766343101027f410410a500c86abfe7ef17888c2232feac2dc60c0402a0f7f80e080000000000010000
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# MQTT Connection over Thread IPv6
|
||||
# -------------------------------------------------------------------
|
||||
mqtt:
|
||||
broker: "fd1a:dbbe:7435:e954:4455:28c5:b6cf:e990"
|
||||
port: 8884
|
||||
skip_cert_cn_check: true
|
||||
topic_prefix: microwave/display
|
||||
keepalive: 60s
|
||||
on_connect:
|
||||
- mqtt.publish:
|
||||
topic: smartwave/hello
|
||||
payload: !lambda |-
|
||||
return std::string("{\"id_microwave\":\"") + "${device_id}" + "\",\"type\":\"externalDisplay\"}";
|
||||
on_message:
|
||||
- topic: microwave/display/status
|
||||
then:
|
||||
- text_sensor.template.publish:
|
||||
id: status_message
|
||||
state: !lambda 'return x;'
|
||||
- component.update: my_display
|
||||
# mqtt:
|
||||
# broker: "mqtt.thread.smartwave.matthiasg.dev"
|
||||
# # broker: "mqtt://[fd36:5102:8d0:1:f1cd:3499:e546:dbec]"
|
||||
# # port: 1883
|
||||
# topic_prefix: "microwave/display"
|
||||
# keepalive: 60s
|
||||
# on_connect:
|
||||
# - mqtt.publish:
|
||||
# topic: smartwave/hello
|
||||
# payload: !lambda |-
|
||||
# return std::string("{\"id_microwave\":\"") + "${device_id}" + "\",\"type\":\"externalDisplay\"}";
|
||||
# on_message:
|
||||
# - topic: microwave/display/status
|
||||
# then:
|
||||
# - text_sensor.template.publish:
|
||||
# id: status_message
|
||||
# state: !lambda 'return x;'
|
||||
# - component.update: my_display
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Template Text Sensor
|
||||
@@ -70,6 +105,19 @@ text_sensor:
|
||||
- platform: template
|
||||
name: "Display Status Message"
|
||||
id: status_message
|
||||
- platform: openthread_info
|
||||
ip_address:
|
||||
name: "Thread IP"
|
||||
id: thread_ip
|
||||
role:
|
||||
name: "Thread Role"
|
||||
id: thread_role
|
||||
channel:
|
||||
name: "Thread Channel"
|
||||
id: thread_channel
|
||||
rloc16:
|
||||
name: "Thread RLOC16"
|
||||
id: thread_rloc16
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Hardware SPI Pin Mapping (ESP32-H2)
|
||||
@@ -82,12 +130,18 @@ spi:
|
||||
# Fonts
|
||||
# -------------------------------------------------------------------
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
- file:
|
||||
type: gfonts
|
||||
family: Open+Sans
|
||||
weight: 700
|
||||
size: 20
|
||||
id: font_header
|
||||
size: 18
|
||||
- file: "gfonts://Roboto"
|
||||
- file:
|
||||
type: gfonts
|
||||
family: Overpass
|
||||
weight: 400
|
||||
id: font_body
|
||||
size: 12
|
||||
size: 14
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Waveshare 2.13-inch e-Paper Display (V4 panel = 2.13inv3 in ESPHome)
|
||||
@@ -100,16 +154,38 @@ display:
|
||||
busy_pin: GPIO3
|
||||
model: 2.13inv3
|
||||
rotation: 90°
|
||||
update_interval: 30s
|
||||
update_interval: 30min
|
||||
id: my_display
|
||||
lambda: |-
|
||||
if (id(status_message).has_state()) {
|
||||
it.print(5, 3, id(font_header), "MICROWAVE STATUS");
|
||||
it.line(0, 20, it.get_width(), 20);
|
||||
it.print(5, 25, id(font_body), id(status_message).state.c_str());
|
||||
// Header
|
||||
it.print(5, 3, id(font_header), id(status_message).state.c_str());
|
||||
it.line(0, 26, it.get_width(), 26);
|
||||
|
||||
// Mode Status
|
||||
if (id(defrost_mode)) {
|
||||
it.print(5, 32, id(font_header), "MODE: DEFROST");
|
||||
} else {
|
||||
it.print(5, 32, id(font_header), "MODE: NORMAL");
|
||||
}
|
||||
|
||||
// Cloud Alert Status Box
|
||||
if (id(cloud_alert)) {
|
||||
it.filled_rectangle(5, 60, 240, 24, COLOR_OFF); // Inverse background if supported, or frame box
|
||||
it.print(10, 62, id(font_body), "CLOUD ALERT ACTIVE");
|
||||
} else {
|
||||
it.print(10, 62, id(font_body), "System Normal");
|
||||
}
|
||||
|
||||
// Footer: Thread Info
|
||||
it.line(0, 95, it.get_width(), 95);
|
||||
it.printf(5, 100, id(font_body), "Role: %s | Ch: %s",
|
||||
id(thread_role).state.c_str(),
|
||||
id(thread_channel).state.c_str());
|
||||
} else {
|
||||
int center_x = it.get_width() / 2;
|
||||
int center_y = it.get_height() / 2;
|
||||
|
||||
it.printf(center_x, center_y, id(font_header), TextAlign::CENTER, "SmartWave");
|
||||
it.printf(center_x, center_y - 10, id(font_header), TextAlign::CENTER, "SmartWave");
|
||||
it.printf(center_x, center_y + 15, id(font_body), TextAlign::CENTER, "Waiting for Pi updates...");
|
||||
}
|
||||
Reference in New Issue
Block a user