3e959b3df9
Everything a user sees or reads is now English: both boards' labels, status replies and logs, the ESP Screens editor and Alerts cheatsheet, and the README and guides (docs/TILES.md, CALIBRATING.md, ACCEPTANCE.md and TROUBLESHOOTING.md replace the Dutch file names). Labels follow Home Assistant's own English names (Heat/Cool modes, Heating/Cooling actions, Away, Returning to dock, weather conditions), dates use English abbreviations and the top bar writes numbers the English way. Docs use HA 2026.9's menu names (Settings -> Apps, App store, Settings -> Tools). Unused legacy files (home-like.yaml, buttons.yaml, a.out) are removed. Firmware 0.2.34 names its entities in English. Home Assistant registers a renamed ESPHome entity under a new entity ID, so the inbox text.<screen>_tegelinstellingen becomes text.<screen>_tile_settings and the layout stored under the old ID would be lost. The manager now recognises the screen by its device, during a run or after a restart, and moves its layout and update history along; the Update button and the nightly round follow the screen through the rename. Screens that still run 0.2.33 or older keep working: both languages are matched. Main is the only release branch from now on. Tested on a Home Assistant Yellow (HA 2026.9.1) with both bench boards: see docs/TEST_RESULTS_0240.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
480 lines
20 KiB
HTML
480 lines
20 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<title>ESP Screens</title>
|
||
<link rel="stylesheet" href="static/style.css" />
|
||
<script src="static/app.js" defer></script>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<div class="brand">
|
||
<span class="mark">▦</span>
|
||
<div>
|
||
<strong>ESP Screens</strong
|
||
><small>Tiles for your CYD and Guition, without reflashing</small>
|
||
</div>
|
||
</div>
|
||
<nav class="toolbar" aria-label="Actions">
|
||
<button id="new-screen" class="tool tool-primary">
|
||
<span class="tool-icon" aria-hidden="true">+</span>
|
||
<span class="tool-text"
|
||
><b>New screen</b><small>Connect and install</small></span
|
||
>
|
||
</button>
|
||
<button id="open-firmware" class="tool">
|
||
<span class="tool-icon" aria-hidden="true">⇪</span>
|
||
<span class="tool-text"
|
||
><b>Firmware & USB</b
|
||
><small>Build and install via USB or Wi-Fi</small></span
|
||
>
|
||
</button>
|
||
<button id="open-alerts" class="tool">
|
||
<span class="tool-icon" aria-hidden="true">!</span>
|
||
<span class="tool-text"
|
||
><b>Alerts</b
|
||
><small>Full-screen notifications, from an automation</small></span
|
||
>
|
||
</button>
|
||
</nav>
|
||
</header>
|
||
<main>
|
||
<div class="intro">
|
||
<div>
|
||
<p class="eyebrow">HOME ASSISTANT</p>
|
||
<h1>Manage your remote screens here.</h1>
|
||
</div>
|
||
<span id="connection" role="status" class="badge">Connecting…</span>
|
||
</div>
|
||
<div class="workspace">
|
||
<aside>
|
||
<h2>My screens</h2>
|
||
<div id="screens"></div>
|
||
<div id="pending"></div>
|
||
<button class="quiet" id="refresh">↻ Refresh</button>
|
||
<div class="updates" id="updates" hidden>
|
||
<h2>Firmware updates</h2>
|
||
<p class="hint" id="updates-hint"></p>
|
||
<button id="update-all" hidden>Update all screens</button>
|
||
<label class="check">
|
||
<input type="checkbox" id="auto-update" />
|
||
<span
|
||
>Update automatically every night<small
|
||
>Between 03:00 and 06:00, one screen at a time. Stops on an
|
||
error and reports it in Home Assistant.</small
|
||
></span
|
||
>
|
||
</label>
|
||
</div>
|
||
</aside>
|
||
<section id="empty" class="empty">
|
||
<span class="empty-icon">▦</span>
|
||
<h2>Start with a screen</h2>
|
||
<p>
|
||
Connect a CYD or Guition via USB to your Home Assistant machine.
|
||
Profile, firmware, and the first flash happen in one window.
|
||
</p>
|
||
<button id="start">Install new screen</button>
|
||
</section>
|
||
<section id="editor" hidden>
|
||
<div class="editor-head">
|
||
<div>
|
||
<p class="eyebrow" id="screen-name"></p>
|
||
<h2>Make it your screen</h2>
|
||
</div>
|
||
<span id="delivery" class="badge"></span>
|
||
</div>
|
||
<section class="topbar-editor" id="topbar" aria-labelledby="topbar-title">
|
||
<div class="section-title">
|
||
<h3 id="topbar-title">Top bar</h3>
|
||
<span id="topbar-count"></span>
|
||
</div>
|
||
<div class="topbar-fields">
|
||
<label class="topbar-name"
|
||
>Left: name<input
|
||
id="title"
|
||
maxlength="60"
|
||
placeholder="For example: Living room"
|
||
/></label>
|
||
<div class="topbar-right">
|
||
<span class="topbar-caption" id="topbar-caption">Right: time, sensors, or a clock</span>
|
||
<div
|
||
id="topbar-chips"
|
||
class="topbar-chips"
|
||
role="list"
|
||
aria-labelledby="topbar-caption"
|
||
></div>
|
||
</div>
|
||
</div>
|
||
<p class="hint" id="topbar-hint"></p>
|
||
</section>
|
||
<div class="columns" id="tile-section">
|
||
<div class="tile-editor">
|
||
<div class="section-title">
|
||
<h3>Your tiles</h3>
|
||
<span id="count">0 / 10</span>
|
||
</div>
|
||
<div id="layout-preview" aria-label="Screen layout"></div>
|
||
<button id="add-page" class="quiet add-page" type="button">
|
||
+ Add page
|
||
</button>
|
||
<p id="no-tiles" class="hint">
|
||
Add your first light, scene, or device via the picker.
|
||
</p>
|
||
<p id="positions-hint" class="hint" hidden></p>
|
||
</div>
|
||
<div class="picker">
|
||
<label for="search">Add an entity</label
|
||
><input
|
||
id="search"
|
||
type="search"
|
||
placeholder="Search by name, device, or room…"
|
||
autocomplete="off"
|
||
/>
|
||
<div class="filters" id="filters"></div>
|
||
<div id="results" aria-live="polite"></div>
|
||
</div>
|
||
</div>
|
||
<details class="screen-settings" id="general-settings" open>
|
||
<summary>
|
||
Screen settings
|
||
<span class="hint">Brightness, quiet hours, and display</span>
|
||
</summary>
|
||
<p class="hint" id="settings-support"></p>
|
||
<div class="settings-grid" id="settings-fields"></div>
|
||
<p class="hint">
|
||
Night hours apply to the brightness during standby. The first
|
||
touch wakes the screen; the next one controls your home.
|
||
Matching start and end times turn the night window off.
|
||
</p>
|
||
</details>
|
||
<details class="screen-settings" id="inspector-section">
|
||
<summary>
|
||
Inspector
|
||
<span class="hint"
|
||
>Connection, status, and supported properties</span
|
||
>
|
||
</summary>
|
||
<button id="inspect" class="quiet">Read current data</button>
|
||
<div id="inspection-summary"></div>
|
||
<pre id="inspection">Select a screen and read the data.</pre>
|
||
</details>
|
||
<footer class="savebar">
|
||
<div>
|
||
<strong id="dirty">All saved</strong
|
||
><small id="save-detail"
|
||
>Changes apply without reflashing.</small
|
||
>
|
||
</div>
|
||
<button id="save">Save & send to screen</button>
|
||
</footer>
|
||
</section>
|
||
</div>
|
||
</main>
|
||
<dialog id="installer">
|
||
<form id="install-form">
|
||
<div class="dialog-head">
|
||
<div>
|
||
<p class="eyebrow">NEW SCREEN</p>
|
||
<h2 id="install-title">Connect and install.</h2>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
class="quiet"
|
||
id="close-install"
|
||
aria-label="Close"
|
||
>
|
||
✕
|
||
</button>
|
||
</div>
|
||
<div id="install-setup">
|
||
<p>
|
||
Connect the screen with a USB data cable to the machine running Home
|
||
Assistant. ESP Screens creates its own profile with unique
|
||
keys, builds the firmware, and puts it straight on the screen.
|
||
</p>
|
||
<fieldset>
|
||
<legend>Which screen do you have?</legend>
|
||
<label class="board"
|
||
><input type="radio" name="board" value="cyd" checked /><span
|
||
><b>CYD · 2.8 inch</b
|
||
><small>ESP32-2432S028 · 320 × 240</small></span
|
||
></label
|
||
><label class="board"
|
||
><input type="radio" name="board" value="guition" /><span
|
||
><b>Guition · 4 inch</b
|
||
><small>ESP32-S3-4848S040 · 480 × 480 · GT911</small></span
|
||
></label
|
||
>
|
||
</fieldset>
|
||
<label
|
||
>Name<input
|
||
name="friendly_name"
|
||
required
|
||
maxlength="60"
|
||
placeholder="Living room screen"
|
||
autocomplete="off" /><small class="node-line"
|
||
>Device name <code id="node-preview">screen</code
|
||
><button type="button" class="link" id="edit-node">
|
||
customize
|
||
</button></small
|
||
><small
|
||
>Every screen gets its own profile with its own keys. Four
|
||
screens? Go through this four times with a different name.</small
|
||
></label
|
||
>
|
||
<label id="node-label" hidden
|
||
>Device name<input
|
||
name="name"
|
||
pattern="[a-z][a-z0-9-]{0,29}"
|
||
maxlength="30"
|
||
autocomplete="off" /><small
|
||
>Lowercase letters, digits, and dashes. A different name for
|
||
each screen.</small
|
||
></label
|
||
>
|
||
<fieldset id="wifi-fields" class="wifi" hidden disabled>
|
||
<legend>Wi-Fi</legend>
|
||
<p class="hint" id="wifi-status"></p>
|
||
<label id="wifi-ssid-label"
|
||
>Wi-Fi name (2.4 GHz)<input name="wifi_ssid" autocomplete="off"
|
||
/></label>
|
||
<label id="wifi-password-label"
|
||
>Wi-Fi password<input
|
||
name="wifi_password"
|
||
type="password"
|
||
autocomplete="new-password"
|
||
/></label>
|
||
</fieldset>
|
||
<label
|
||
>Install via<select name="target" id="install-target"></select
|
||
><small id="target-hint"></small
|
||
></label>
|
||
<p class="hint" id="install-note"></p>
|
||
<div class="install-actions">
|
||
<button type="submit" id="install-go">Install</button>
|
||
<span id="install-status" role="status"></span>
|
||
</div>
|
||
</div>
|
||
<div id="install-progress" hidden>
|
||
<div class="progress-head">
|
||
<span class="spin" id="progress-spin"></span>
|
||
<span class="outcome" id="progress-mark" hidden></span>
|
||
<strong id="progress-title"></strong>
|
||
</div>
|
||
<p id="progress-detail"></p>
|
||
<div id="install-result" hidden>
|
||
<div class="key-box">
|
||
<span>API key</span><code id="api-key"></code>
|
||
<button type="button" class="mini" id="copy-key">Copy</button>
|
||
</div>
|
||
<ol class="steps" id="install-steps"></ol>
|
||
</div>
|
||
<details id="install-log-wrap">
|
||
<summary>ESPHome log</summary>
|
||
<pre id="install-log"></pre>
|
||
</details>
|
||
<div class="install-actions">
|
||
<button type="button" id="install-retry" hidden>
|
||
Retry
|
||
</button>
|
||
<button type="button" class="quiet" id="install-close">
|
||
Close
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</dialog>
|
||
<dialog id="firmware-dialog">
|
||
<div class="dialog-head">
|
||
<div>
|
||
<p class="eyebrow">FIRMWARE & USB</p>
|
||
<h2>Build and install screen firmware</h2>
|
||
</div>
|
||
<button id="close-firmware" class="quiet" aria-label="Close">✕</button>
|
||
</div>
|
||
<p>
|
||
The built-in ESPHome CLI uses your own profile (YAML with keys)
|
||
and secrets. Choose the screen's profile, then a USB port (screen
|
||
connected to the Home Assistant machine) or <b>Wi-Fi / OTA</b> with the hostname.
|
||
<b>Check</b> only validates, <b>Build only</b> compiles,
|
||
<b>Build & install</b> puts the firmware on the screen. Your tiles
|
||
stay as they are in ESP Screens. The first build takes a few minutes.
|
||
</p>
|
||
<label>ESPHome profile<select id="firmware-file"></select></label>
|
||
<label
|
||
>Install to<select id="firmware-port">
|
||
<option value="ota">Wi-Fi / OTA</option>
|
||
</select></label
|
||
>
|
||
<label id="firmware-host-label"
|
||
>IP address or hostname<input
|
||
id="firmware-host"
|
||
placeholder="screen-livingroom.local"
|
||
/></label>
|
||
<div class="yaml-actions">
|
||
<button id="firmware-validate" class="quiet">Check</button
|
||
><button id="firmware-build" class="quiet">Build only</button
|
||
><button id="firmware-install">Build & install</button>
|
||
</div>
|
||
<p id="firmware-status" role="status"></p>
|
||
<pre id="firmware-log"></pre>
|
||
</dialog>
|
||
<dialog id="tile-sheet" class="tile-sheet" aria-label="Configure tile"></dialog>
|
||
<dialog id="topbar-sheet" class="tile-sheet" aria-label="Top bar"></dialog>
|
||
<dialog id="alerts-dialog" class="alerts" aria-label="Alerts">
|
||
<div class="dialog-head">
|
||
<div>
|
||
<p class="eyebrow">ALERTS · CHEATSHEET</p>
|
||
<h2>A full-screen notification, from an automation.</h2>
|
||
</div>
|
||
<button id="close-alerts" class="quiet" aria-label="Close">✕</button>
|
||
</div>
|
||
<p>
|
||
Every screen has the <code>show_alert</code> action. It places a card over everything,
|
||
wakes the screen, and keeps the backlight at normal brightness until someone taps
|
||
the button or the timeout runs out. Home Assistant itself shows no explanation or
|
||
dropdowns for ESPHome actions; everything you can fill in is listed here.
|
||
</p>
|
||
<div class="alert-hero">
|
||
<div class="alert-mock" id="alert-mock" aria-hidden="true">
|
||
<div class="alert-mock-card">
|
||
<span class="mdi alert-mock-icon" id="alert-mock-icon"></span>
|
||
<strong id="alert-mock-title">Someone is at the door</strong>
|
||
<span id="alert-mock-subtitle">Door 3, back</span>
|
||
<button type="button" id="alert-mock-button" tabindex="-1">Coming</button>
|
||
</div>
|
||
</div>
|
||
<nav class="alerts-nav" aria-label="Sections">
|
||
<button type="button" data-jump="alerts-screens">Your screens</button>
|
||
<button type="button" data-jump="alerts-howto">In an automation</button>
|
||
<button type="button" data-jump="alerts-fields">The fields</button>
|
||
<button type="button" data-jump="alerts-icons">Icons</button>
|
||
<button type="button" data-jump="alerts-colors">Colors</button>
|
||
<button type="button" data-jump="alerts-behaviour">Behavior</button>
|
||
<button type="button" data-jump="alerts-events">Events</button>
|
||
<button type="button" data-jump="alerts-tips">Tips</button>
|
||
</nav>
|
||
</div>
|
||
<section id="alerts-screens">
|
||
<h3>Your screens</h3>
|
||
<p>
|
||
Every screen has its own action, with the device name in it. From firmware
|
||
<code id="alerts-min-firmware">0.2.31</code>; update an older screen under
|
||
Firmware updates.
|
||
</p>
|
||
<div id="alerts-screen-list"></div>
|
||
</section>
|
||
<section id="alerts-howto">
|
||
<h3>In an automation</h3>
|
||
<ol class="steps">
|
||
<li><b>Add action</b> and search for <b>ESPHome</b>. Choose your screen's action:
|
||
Home Assistant calls it "Performs the action show_alert of the node
|
||
<i>device name</i>".</li>
|
||
<li><b>Fill in the fields.</b> Home Assistant asks for all seven; leave what you don't
|
||
use empty (<code>""</code>, <code>0</code>, off).</li>
|
||
<li>Prefer YAML? Choose <b>Edit in YAML</b> on the action and paste the example
|
||
below.</li>
|
||
</ol>
|
||
<div class="alert-example">
|
||
<label
|
||
>Example for<select id="alerts-example-screen"></select
|
||
></label>
|
||
<div class="copy-line">
|
||
<pre id="alerts-example"></pre>
|
||
<button type="button" class="mini" id="alerts-example-copy">Copy YAML</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section id="alerts-fields">
|
||
<h3>The fields</h3>
|
||
<p>
|
||
Text is free-form; the limit is in bytes, and an accented letter counts as two.
|
||
What doesn't fit gets an ellipsis (title) or is dropped (subtitle).
|
||
</p>
|
||
<div class="table-scroll"><table id="alerts-field-table"></table></div>
|
||
</section>
|
||
<section id="alerts-icons">
|
||
<h3>Icons</h3>
|
||
<p>
|
||
Fill in the name in <code>icon</code>, for example <code>doorbell</code>.
|
||
<code>mdi:doorbell</code> and the hex codepoint (<code>F12E6</code>) also work.
|
||
Only these icons are included in the firmware; any other name shows the
|
||
warning triangle. Tap an icon to copy its name.
|
||
</p>
|
||
<p class="alerts-subhead">Handy for notifications</p>
|
||
<div class="chips" id="alerts-suggested"></div>
|
||
<label class="alerts-search"
|
||
>Search for an icon<input
|
||
id="alerts-icon-search"
|
||
type="search"
|
||
placeholder="Search by name or description…"
|
||
autocomplete="off"
|
||
/></label>
|
||
<div id="alerts-icon-groups"></div>
|
||
</section>
|
||
<section id="alerts-colors">
|
||
<h3>Colors</h3>
|
||
<p>
|
||
Fill in the name in <code>color</code>. These are the pastel shades of the tiles,
|
||
with dark text. Empty or unknown gives the white card. Tap to copy.
|
||
</p>
|
||
<div class="swatches" id="alerts-swatches"></div>
|
||
</section>
|
||
<section id="alerts-behaviour">
|
||
<h3>Behavior</h3>
|
||
<ul class="alerts-list">
|
||
<li><b>Wakes the screen.</b> An alert immediately wakes a dimmed or dark screen and
|
||
appears above every page, every open light card, and the standby layer.</li>
|
||
<li><b>On until the button.</b> As long as the card is showing, the backlight stays at
|
||
normal brightness. Standby and night hours wait. After the button, the regular
|
||
standby time starts again.</li>
|
||
<li><b>Timeout.</b> <code>timeout: 0</code> waits for the button, however long that
|
||
takes. With <code>timeout: 60</code> the card disappears after a minute, but the
|
||
button closes it sooner.</li>
|
||
<li><b>Blinking.</b> <code>flash: true</code> blinks the backlight four times
|
||
when the alert arrives (about a second) and leaves the screen on afterward.</li>
|
||
<li><b>Replacing.</b> A new alert replaces the current one; there's no stack.
|
||
The replaced alert reports itself as <code>replaced</code>.</li>
|
||
<li><b>Closing remotely.</b> <code>dismiss_alert</code> clears the card,
|
||
for example if the door is already open. Without an alert, that action does nothing.</li>
|
||
<li><b>Tapping beside the card</b> does nothing; only the button closes it. That way a
|
||
wake-up tap never accidentally dismisses the alert.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="alerts-events">
|
||
<h3>Events</h3>
|
||
<p>
|
||
Every end fires the event <code id="alerts-event-name">esphome.screen_alert</code>
|
||
with <code>action</code>, <code>title</code>, <code>screen</code> (the device name)
|
||
and the <code>device_id</code> that Home Assistant adds. Visible under
|
||
Settings → Tools → Events.
|
||
</p>
|
||
<div class="table-scroll"><table id="alerts-ending-table"></table></div>
|
||
<p class="alerts-subhead">Waiting until someone presses the button</p>
|
||
<div class="copy-line">
|
||
<pre id="alerts-wait-example"></pre>
|
||
<button type="button" class="mini" id="alerts-wait-copy">Copy YAML</button>
|
||
</div>
|
||
</section>
|
||
<section id="alerts-tips">
|
||
<h3>Tips</h3>
|
||
<ul class="alerts-list">
|
||
<li>Use a <b>color per kind of notification</b>: orange for the door, red for
|
||
smoke or water, green for "done". The text always stays dark and readable.</li>
|
||
<li>Keep the title <b>short</b>; the CYD shows about forty characters on one line.
|
||
Put details in the subtitle.</li>
|
||
<li>A <b>custom button text</b> turns the notification into a question: "Coming", "Seen",
|
||
"Turn off". The event then tells you who responded.</li>
|
||
<li>For an alert that resolves itself (the door opens, the laundry gets taken out)
|
||
close it in the same automation with <code>dismiss_alert</code>.</li>
|
||
<li>Multiple screens? Call each screen's action; they work independently and
|
||
each reports with its own <code>screen</code>.</li>
|
||
</ul>
|
||
</section>
|
||
</dialog>
|
||
<div id="toast" role="status" hidden></div>
|
||
</body>
|
||
</html>
|