substitutions: device_id: !include device_id.txt esphome: name: "smartwave-epaper-${device_id}" friendly_name: "SmartWave ePaper ExternalScreen ${device_id}" includes: - display_api.h on_boot: priority: -100.0 then: - lambda: |- setup_display_api([]() { // Force immediate e-Paper refresh! id(my_display).update(); }); web_server: port: 5000 mdns: disabled: false services: - service: "_displaytcp" protocol: "_tcp" port: 5000 txt: version: "1.0" type: "epaper_2.13" esp32: variant: esp32h2 framework: type: esp-idf log_level: INFO advanced: loop_task_stack_size: 32768 sdkconfig_options: CONFIG_ESP_MAIN_TASK_STACK_SIZE: "16384" CONFIG_OPENTHREAD_TASK_STACK_SIZE: "8192" CONFIG_ESP_WIFI_ENABLED: "n" CONFIG_OPENTHREAD_ENABLED: "y" CONFIG_MBEDTLS_DYNAMIC_BUFFER: "y" CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA: "y" logger: level: VERBOSE hardware_uart: UART0 logs: openthread: INFO network: enable_ipv6: true openthread: force_dataset: true tlv: 00030000184a0300000b35060004001fffe00208f7754c0c4a4dea9a0708fd1adbbe7435e95405103f543281275a135e38982974aa71e987030f4f70656e5468726561642d3766343101027f410410a500c86abfe7ef17888c2232feac2dc60c0402a0f7f80e080000000000010000 # ------------------------------------------------------------------- # MQTT Connection over Thread IPv6 # ------------------------------------------------------------------- # 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 # ------------------------------------------------------------------- text_sensor: - platform: openthread_info role: name: "Thread Role" id: thread_role channel: name: "Thread Channel" id: thread_channel # ------------------------------------------------------------------- # Hardware SPI Pin Mapping (ESP32-H2) # ------------------------------------------------------------------- spi: clk_pin: GPIO10 mosi_pin: GPIO11 # ------------------------------------------------------------------- # Fonts # ------------------------------------------------------------------- font: - file: type: gfonts family: Open+Sans weight: 700 size: 20 id: font_header - file: type: gfonts family: Overpass weight: 600 id: font_body size: 14 - file: type: gfonts family: Overpass weight: 500 id: font_small size: 12 # ------------------------------------------------------------------- # Waveshare 2.13-inch e-Paper Display (250x122 standard resolution) # ------------------------------------------------------------------- display: - platform: waveshare_epaper cs_pin: GPIO4 dc_pin: GPIO1 reset_pin: GPIO2 busy_pin: GPIO3 model: 2.13inv3 rotation: 90° update_interval: 30min id: my_display lambda: |- // --- STARTUP SCREEN --- if (!g_received_update) { int center_x = it.get_width() / 2; int center_y = it.get_height() / 2; 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 updates..."); return; } // --- MICROWAVE LAYOUT --- int y_offset = 0; for (const auto& mw : g_microwaves) { // Name it.print(0, y_offset, id(font_header), mw.name.c_str()); // Decode State Text std::string state_str; switch(mw.state) { case 0: state_str = mw.paused ? "PAUSED" : "COOKING"; break; case 1: state_str = "STIRRING REQ."; break; case 2: state_str = "DONE"; break; case 3: state_str = "ALERT"; break; case 4: state_str = "IDLE"; break; default: state_str = "UNKNOWN"; break; } // Print Status (Top Right Align) it.printf(it.get_width(), y_offset + 5, id(font_body), TextAlign::TOP_RIGHT, "%s", state_str.c_str()); // Process active/cooking visualisations if (mw.state == 0) { int bar_y = y_offset + 28; int bar_w = 175; int bar_h = 16; // Draw progress bar outline & fill it.rectangle(0, bar_y, bar_w, bar_h); if (mw.progress > 0) { int fill_w = (mw.progress * bar_w) / 100; it.filled_rectangle(0, bar_y, fill_w, bar_h); } // Draw time remaining (or paused state) if (mw.paused) { it.printf(185, bar_y + 1, id(font_body), "PAUSED"); } else { int mins = mw.remaining_time / 60; int secs = mw.remaining_time % 60; it.printf(185, bar_y + 1, id(font_body), "%02d:%02d", mins, secs); } } else if (mw.state == 1) { // Stirring Required it.filled_rectangle(0, y_offset + 28, 250, 20); it.printf(125, y_offset + 28, id(font_body), COLOR_OFF, TextAlign::TOP_CENTER, "ACTION REQUIRED: STIR"); } // Move cursor down (if we have more than 1 microwave on future larger screens) y_offset += 60; } // --- SYSTEM FOOTER --- // Line separator right above footer it.line(0, 102, it.get_width(), 102); // Bottom Left: Cloud Reachability & Last Update Time std::string cloud_status = g_cloud_alert ? "Cloud: ERR" : "Cloud: OK"; it.printf(0, 105, id(font_small), "%s | %s", cloud_status.c_str(), g_update_time.c_str()); // Bottom Right: OpenThread Diagnostics std::string role = id(thread_role).state; it.printf(it.get_width(), 105, id(font_small), TextAlign::TOP_RIGHT, "Th: %s (Ch %s)", role.c_str(), id(thread_channel).state.c_str());