|
|
@@ -6,6 +6,7 @@ Panels are selected via `model:`, each backed by an EpdPanelDescriptor
|
|
|
"""
|
|
|
|
|
|
import re
|
|
|
+from collections import namedtuple
|
|
|
|
|
|
import esphome.codegen as cg
|
|
|
import esphome.config_validation as cv
|
|
|
@@ -43,28 +44,32 @@ CONF_INVERT_MONO = "invert_mono"
|
|
|
gxepd2_ns = cg.esphome_ns.namespace("gxepd2")
|
|
|
GxEPD2Display = gxepd2_ns.class_("GxEPD2Display", display.DisplayBuffer)
|
|
|
|
|
|
-# model name -> (C++ descriptor global declared in the matching panel_*.h
|
|
|
-# header - gxepd2_display.h includes them all, so no extra include wiring here;
|
|
|
-# Good Display part number on the panel's silkscreen).
|
|
|
+# The panel registry - the single source of truth for which panels ship, their
|
|
|
+# metadata, and whether each is hardware-verified. tools/panel_table.py renders
|
|
|
+# the supported-panels table from this, and set_verified() surfaces the status
|
|
|
+# in the device's boot log.
|
|
|
#
|
|
|
-# The part number, not the GxEPD2 class name, is the source of truth for the
|
|
|
-# on-screen label (see _panel_label / test_pattern): GxEPD2's older naming
|
|
|
-# style drops the GDEx prefix and the 4-digit size - GxEPD2_213_Z98C is really
|
|
|
-# GDEY0213Z98 - so the class name alone can't reconstruct what's on the tag.
|
|
|
-MODELS = {
|
|
|
- "GxEPD2_154_Z90c": ("PANEL_154_Z90C", "GDEH0154Z90"),
|
|
|
- "GxEPD2_213_Z98C": ("PANEL_213_Z98C", "GDEY0213Z98"),
|
|
|
- "GxEPD2_213c_GDEY0213F51": ("PANEL_213C_GDEY0213F51", "GDEY0213F51"),
|
|
|
- "GxEPD2_213c_GDEY0213F52": ("PANEL_213C_GDEY0213F52", "GDEY0213F52"),
|
|
|
- "GxEPD2_426_GDEQ0426T82": ("PANEL_426_GDEQ0426T82", "GDEQ0426T82"),
|
|
|
- "GxEPD2_583_GDEQ0583T31": ("PANEL_583_GDEQ0583T31", "GDEQ0583T31"),
|
|
|
- "GxEPD2_290c_GDEY029F51": ("PANEL_290C_GDEY029F51", "GDEY029F51"),
|
|
|
- "GxEPD2_579c_GDEY0579F51": ("PANEL_579C_GDEY0579F51", "GDEY0579F51"),
|
|
|
- # Debug variant: master half only, as if it were a self-contained
|
|
|
- # 396x272 panel - see panel_579c_gdey0579f51_master_only.h. Kept
|
|
|
- # around as a diagnostic tool, not the panel's real mode. Same physical
|
|
|
- # part as the full panel, so same label.
|
|
|
- "GxEPD2_579c_GDEY0579F51_MASTER_ONLY": ("PANEL_579C_GDEY0579F51_MASTER_ONLY", "GDEY0579F51"),
|
|
|
+# descriptor: the C++ EpdPanelDescriptor global declared in the matching
|
|
|
+# panel_*.h (all `#include`d by gxepd2_display.h, so no extra wiring here).
|
|
|
+# part: the Good Display part number on the silkscreen - the source of truth for
|
|
|
+# the on-screen label (GxEPD2's class name drops the GDEx prefix and size, so
|
|
|
+# GxEPD2_213_Z98C alone can't reconstruct GDEY0213Z98).
|
|
|
+# status: "verified" (rendered correctly on real hardware), "structural"
|
|
|
+# (descriptor captured and compiles, not yet bench-confirmed), or "diagnostic".
|
|
|
+Panel = namedtuple("Panel", "descriptor part controller fmt status")
|
|
|
+
|
|
|
+PANELS = {
|
|
|
+ "GxEPD2_154_Z90c": Panel("PANEL_154_Z90C", "GDEH0154Z90", "SSD1681", "3-color", "verified"),
|
|
|
+ "GxEPD2_213_Z98C": Panel("PANEL_213_Z98C", "GDEY0213Z98", "SSD1680", "3-color", "verified"),
|
|
|
+ "GxEPD2_213c_GDEY0213F51": Panel("PANEL_213C_GDEY0213F51", "GDEY0213F51", "JD79661", "4-color", "structural"),
|
|
|
+ "GxEPD2_213c_GDEY0213F52": Panel("PANEL_213C_GDEY0213F52", "GDEY0213F52", "JD79676", "4-color", "verified"),
|
|
|
+ "GxEPD2_426_GDEQ0426T82": Panel("PANEL_426_GDEQ0426T82", "GDEQ0426T82", "SSD1677", "mono", "verified"),
|
|
|
+ "GxEPD2_583_GDEQ0583T31": Panel("PANEL_583_GDEQ0583T31", "GDEQ0583T31", "UC8179", "mono", "verified"),
|
|
|
+ "GxEPD2_290c_GDEY029F51": Panel("PANEL_290C_GDEY029F51", "GDEY029F51", "JD79667", "4-color", "structural"),
|
|
|
+ "GxEPD2_579c_GDEY0579F51": Panel("PANEL_579C_GDEY0579F51", "GDEY0579F51", "HX8717", "4-color", "structural"),
|
|
|
+ # Diagnostic variant: the 396x272 master region as a self-contained panel.
|
|
|
+ "GxEPD2_579c_GDEY0579F51_MASTER_ONLY": Panel(
|
|
|
+ "PANEL_579C_GDEY0579F51_MASTER_ONLY", "GDEY0579F51", "HX8717", "4-color", "diagnostic"),
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -81,7 +86,7 @@ CONFIG_SCHEMA = cv.All(
|
|
|
{
|
|
|
cv.GenerateID(): cv.declare_id(GxEPD2Display),
|
|
|
cv.GenerateID(CONF_SPI_ID): cv.use_id(spi.SPIComponent),
|
|
|
- cv.Required(CONF_MODEL): cv.one_of(*MODELS),
|
|
|
+ cv.Required(CONF_MODEL): cv.one_of(*PANELS),
|
|
|
cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
|
|
|
cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
|
|
|
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
|
|
@@ -103,8 +108,8 @@ CONFIG_SCHEMA = cv.All(
|
|
|
async def to_code(config: ConfigType) -> None:
|
|
|
var = cg.new_Pvariable(config[CONF_ID])
|
|
|
|
|
|
- descriptor, part_number = MODELS[config[CONF_MODEL]]
|
|
|
- cg.add(var.set_descriptor(cg.RawExpression(f"&esphome::gxepd2::{descriptor}")))
|
|
|
+ panel = PANELS[config[CONF_MODEL]]
|
|
|
+ cg.add(var.set_descriptor(cg.RawExpression(f"&esphome::gxepd2::{panel.descriptor}")))
|
|
|
cg.add(var.set_cs_pin(await cg.gpio_pin_expression(config[CONF_CS_PIN])))
|
|
|
cg.add(var.set_dc_pin(await cg.gpio_pin_expression(config[CONF_DC_PIN])))
|
|
|
if reset_pin := config.get(CONF_RESET_PIN):
|
|
|
@@ -122,7 +127,8 @@ async def to_code(config: ConfigType) -> None:
|
|
|
# Panel label for test_pattern(): the Good Display part number minus its
|
|
|
# series prefix (GDEQ0583T31 -> 0583T31), so a tested panel shows what it
|
|
|
# is on the shelf, not the GxEPD2 C++ class id.
|
|
|
- cg.add(var.set_model_name(_panel_label(part_number)))
|
|
|
+ cg.add(var.set_model_name(_panel_label(panel.part)))
|
|
|
+ cg.add(var.set_verified(panel.status == "verified"))
|
|
|
|
|
|
# A top-level `lambda:` must be processed and attached via set_writer()
|
|
|
# here - register_display() only wires up `pages:` (same pattern as every
|