Add the Elecrow CrowPanel 7.0" as a third board, and take the packages and the add-on from Gitea (0.2.93)
CI / checks (push) Successful in 12m50s
CI / firmware (add-on) (push) Successful in 18m19s
CI / firmware (min_version) (push) Successful in 17m55s

A board file for the Elecrow CrowPanel 7.0" (DIS08070H, ESP32-S3-WROOM-1-N4R8): 800x480 through mipi_rgb, GT911 on the
ESP32-S3's USB pins (elecrow_usb_phy.h frees them, behind a flag only this board sets), the Guition's vertical sizes
across a wider page, no rotation. Its 4 MB of flash take the CYD's budget: a partition table of the component's
(smart_display: partitions: 4mb_wide_slots, 1,984 KB slots), the icon fonts at 2 bits a pixel and the TLS client only
bring the firmware to 1,865,440 bytes, 91.8 % of a slot, with camera images kept. docs/ELECROW.md has the numbers and
the trade.

The add-on reads a large board's name from the type sensor's value, the editor offers the board under New screen and
draws the wide preview, tools/check.sh builds it with the budget, render_topbar and capture_ui take the panel's size.
Firmware 0.2.79; the CYD image is unchanged (1,633,760 bytes).

This fork builds from gittea.trippedonmy.name/Xandrossian/homeassistant_espscreen: the packages, the fonts, the
component and the add-on repository point there instead of GitHub.
This commit is contained in:
Xandrossian
2026-09-20 23:18:01 -07:00
parent efbab04b39
commit 9857cc3bda
64 changed files with 1664 additions and 145 deletions
+10 -7
View File
@@ -4,7 +4,7 @@
#
# tools/check.sh the Python tests, every tests/*.cpp, the package check, the icon generator's --check, the editor's
# tests, types and build, and whether the editor bundle in Git equals that build
# tools/check.sh --firmware compiles both board profiles and applies the CYD's flash budget
# tools/check.sh --firmware compiles every board profile and applies the flash budget to the 4 MB boards (CYD, Elecrow)
# tools/check.sh --all both
# --baseline BYTES with --firmware: the CYD image of the last release, to print the growth
#
@@ -175,8 +175,8 @@ api_encryption_key: "Y2hlY2stYnVpbGQtcGxhY2Vob2xkZXIta2V5LTMyYnk="
ota_password: "check-build-ota-password-0000000"
ap_password: "check-ap-passwd0"
EOF
for board in cyd guition; do
file=$([[ $board == cyd ]] && echo home-like-2432s028.yaml || echo guition-4848s040.yaml)
for board in cyd guition elecrow; do
case $board in cyd) file=home-like-2432s028.yaml ;; guition) file=guition-4848s040.yaml ;; elecrow) file=elecrow-crowpanel7.yaml ;; esac
cp "$ROOT/$file" "$config/check-$board.yaml" || return 1
if ! grep -q '^captive_portal:' "$config/check-$board.yaml" || ! grep -q '^ ap:' "$config/check-$board.yaml"; then
echo "$file has no captive_portal: or wifi ap: any more, unlike the YAML users get; the flash figures would read low."
@@ -186,16 +186,16 @@ EOF
echo "Check profiles in $config"
}
compile_board() { # compile_board cyd|guition
compile_board() { # compile_board cyd|guition|elecrow
local board=$1
cd "$WORK/config" || return 1
"${ESPHOME_CMD[@]}" -s DEVICE_NAME "check-$board" -s DEVICE_FRIENDLY_NAME "Check $board" compile "check-$board.yaml" || return 1
flash_report "$board"
}
# flash_report BOARD [budget]: the image against its update slot, both read from the build, and on the CYD the growth
# against --baseline and the budget of docs/RELEASING.md step 2. Exit 3 means over 90 %: it passes, but the release has
# to say why.
# flash_report BOARD [budget]: the image against its update slot, both read from the build, and on the 4 MB boards (the
# CYD, the Elecrow) the growth against --baseline (the CYD only) and the budget of docs/RELEASING.md step 2. Exit 3 means
# over 90 %: it passes, but the release has to say why.
flash_report() {
local board=$1 mode=${2:-} status=0 out="$WORK/flash-$1.txt" against=""
[[ $board == cyd ]] && against=$baseline
@@ -251,6 +251,7 @@ EOF
}
cyd_budget() { flash_report cyd budget; }
elecrow_budget() { flash_report elecrow budget; }
# ---- Run ----
@@ -282,6 +283,8 @@ if ((want_firmware)); then
run "Firmware: CYD" compile_board cyd
if ((last_ok)); then run "CYD flash budget" cyd_budget; else skip "CYD flash budget" "no CYD build"; fi
run "Firmware: Guition" compile_board guition
run "Firmware: Elecrow" compile_board elecrow
if ((last_ok)); then run "Elecrow flash budget" elecrow_budget; else skip "Elecrow flash budget" "no Elecrow build"; fi
else
skip "Firmware builds" "ESPHome or the check profiles are missing"
fi
+1 -1
View File
@@ -200,7 +200,7 @@ LINT_KEEP = {
'None', 'Auto', 'Wh',
# Placeholders of the YAML tree that the runtime tiles replace before a screen shows them, and profile defaults.
'Lamp', 'Plug', 'Evening', 'All off', 'AC', 'Vacuum', 'Tile 7', 'Tile 8', 'Tile 9', 'Tile 10', 'Light', 'Light Color',
'Climate', 'Example lamp', 'My CYD', 'My Guition',
'Climate', 'Example lamp', 'My CYD', 'My Guition', 'My CrowPanel',
}
LINT_FILES = ('components/smart_display/*.h', 'packages/core.yaml', 'packages/boards/*.yaml')
+9 -7
View File
@@ -1,7 +1,8 @@
"""The board profiles as the tests and tools read them (app 0.2.84+).
A screen is three files: an entry (`home-like-2432s028.yaml` or `packages/cyd.yaml` for a CYD, `guition-4848s040.yaml`
or `packages/guition.yaml` for a Guition), `packages/core.yaml` that every board shares, and the board's own file under
or `packages/guition.yaml` for a Guition, `elecrow-crowpanel7.yaml` or `packages/elecrow.yaml` for an Elecrow CrowPanel 7.0"),
`packages/core.yaml` that every board shares, and the board's own file under
`packages/boards/`. `text(name)` is the three of them one after the other, so a check that looks for a line finds it
wherever it lives; `resolved(name)` also fills in the `${NAME}` substitutions from the three files (the sizes, the fonts
and the hooks with the board's code), for a check that needs the numbers or the whole lambda. Neither is what ESPHome
@@ -15,12 +16,13 @@ import yaml
ROOT = Path(__file__).resolve().parents[1]
CORE = ROOT / 'packages/core.yaml'
BOARDS = {'cyd': ROOT / 'packages/boards/cyd-2432s028.yaml', 'guition': ROOT / 'packages/boards/guition-4848s040.yaml'}
ENTRIES = {'home-like-2432s028.yaml': 'cyd', 'guition-4848s040.yaml': 'guition',
'packages/cyd.yaml': 'cyd', 'packages/guition.yaml': 'guition'}
# The names the four entry files are known by, in the order the older tests listed them.
PROFILES = ('home-like-2432s028.yaml', 'guition-4848s040.yaml')
PACKAGES = ('packages/cyd.yaml', 'packages/guition.yaml')
BOARDS = {'cyd': ROOT / 'packages/boards/cyd-2432s028.yaml', 'guition': ROOT / 'packages/boards/guition-4848s040.yaml',
'elecrow': ROOT / 'packages/boards/elecrow-crowpanel7.yaml'}
ENTRIES = {'home-like-2432s028.yaml': 'cyd', 'guition-4848s040.yaml': 'guition', 'elecrow-crowpanel7.yaml': 'elecrow',
'packages/cyd.yaml': 'cyd', 'packages/guition.yaml': 'guition', 'packages/elecrow.yaml': 'elecrow'}
# The names the six entry files are known by, in the order the older tests listed them.
PROFILES = ('home-like-2432s028.yaml', 'guition-4848s040.yaml', 'elecrow-crowpanel7.yaml')
PACKAGES = ('packages/cyd.yaml', 'packages/guition.yaml', 'packages/elecrow.yaml')
NAMES = PROFILES + PACKAGES
+18 -12
View File
@@ -20,14 +20,16 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
WORK = ROOT / '.esphome' / 'render-topbar'
PROFILES = {'guition': 'guition-4848s040.yaml', 'cyd': 'home-like-2432s028.yaml'}
PROFILES = {'guition': 'guition-4848s040.yaml', 'cyd': 'home-like-2432s028.yaml', 'elecrow': 'elecrow-crowpanel7.yaml'}
sys.path.insert(0, str(ROOT / 'tools'))
import profiles # noqa: E402
FONTS = ('headline', 'time_label', 'sublabel_big', 'label', 'materialdesign_icons', 'materialdesign_icons_mini')
# Hardware headers the host cannot compile; the top bar needs none of them.
SKIP = {'guition_diagnostics.h', '__pycache__'}
# Header band per board: page width and height above the tiles.
BANDS = {'guition': (480, 72), 'cyd': (320, 40)}
# Header band per board: page width and height above the tiles. The Elecrow's bar is the Guition's across 800 px
# (the same fonts and insets), so it renders with the Guition's fonts.
BANDS = {'guition': (480, 72), 'cyd': (320, 40), 'elecrow': (800, 72)}
LARGE = ('guition', 'elecrow')
# (slug, title, items) with items as C++ expressions of the render lambda's helpers.
SCENARIOS = (
@@ -56,8 +58,8 @@ def font_blocks(board):
def band(board, y):
width, height = BANDS[board]
margin, top = (16, 22) if board == 'guition' else (11, 14)
tile_y, tile_w, tile_h, col2, radius = (72, 218, 108, 246, 22) if board == 'guition' else (40, 147, 52, 164, 18)
margin, top = (16, 22) if board in LARGE else (11, 14)
tile_y, tile_w, tile_h, col2, radius = {'guition': (72, 218, 108, 246, 22), 'elecrow': (72, 378, 108, 406, 22), 'cyd': (40, 147, 52, 164, 18)}[board]
return f''' - obj:
id: {board}_page
x: 0
@@ -85,7 +87,7 @@ def band(board, y):
text: ""
text_font: time_label_{board}
- obj:
x: {margin if board == 'guition' else 9}
x: {margin if board in LARGE else 9}
y: {tile_y}
width: {tile_w}
height: {tile_h}
@@ -128,6 +130,7 @@ struct Board {{ const char *name; lv_obj_t *page, *room, *time; const lv_font_t
std::vector<Board> boards = {{
{{"guition", id(guition_page), id(guition_room), id(guition_time), id(sublabel_big_guition)->get_lv_font(), id(materialdesign_icons_mini_guition)->get_lv_font()}},
{{"cyd", id(cyd_page), id(cyd_room), id(cyd_time), id(sublabel_big_cyd)->get_lv_font(), id(materialdesign_icons_mini_cyd)->get_lv_font()}},
{{"elecrow", id(elecrow_page), id(elecrow_room), id(elecrow_time), id(sublabel_big_guition)->get_lv_font(), id(materialdesign_icons_mini_guition)->get_lv_font()}},
}};
for (auto &scenario : scenarios) {{
for (auto &board : boards) {{
@@ -200,8 +203,8 @@ display:
- platform: sdl
id: sdl_display
dimensions:
width: 480
height: 480
width: 800
height: 540
auto_clear_enabled: false
update_interval: never
@@ -227,7 +230,7 @@ lvgl:
points:
- 0, 0
- 1, 1
{band('guition', 0)}{band('cyd', 220)}
{band('guition', 0)}{band('cyd', 220)}{band('elecrow', 340).replace('headline_elecrow', 'headline_guition').replace('time_label_elecrow', 'time_label_guition')}
script:
- id: action_seed
then:
@@ -254,15 +257,18 @@ def crop_scale(image, width, height, scale):
return bytes(out)
def sheet(out):
"""Header bands of every scenario: Guition 2x above CYD 3x, 12 px white between."""
"""Header bands of every scenario: Guition 2x above CYD 3x above the Elecrow 1x, 12 px white between."""
width, gap = 960, 12
rows = []
for slug, _, _ in SCENARIOS:
for board, scale in (('guition', 2), ('cyd', 3)):
for board, scale in (('guition', 2), ('cyd', 3), ('elecrow', 1)):
image = read_ppm(out / f'{slug}-{board}.ppm')
band_w, band_h = BANDS[board]
png(out / f'{slug}-{board}.png', image[0], image[1], image[2])
rows.append((crop_scale(image, band_w, band_h, scale), band_h * scale))
band = crop_scale(image, band_w, band_h, scale)
# A band narrower than the sheet (the Elecrow's 800) gets white at its right.
pad = bytes([255]) * (width - band_w * scale) * 3
rows.append((b''.join(band[y * band_w * scale * 3:(y + 1) * band_w * scale * 3] + pad for y in range(band_h * scale)), band_h * scale))
rows.append((bytes([255]) * width * 3 * gap * 2, gap * 2))
height = sum(h for _, h in rows)
png(out / 'sheet.png', width, height, b''.join(data for data, _ in rows))