Release 0.2.73: the Vue editor with live values, Identify, a test alert, layout copying and ⌘K
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@ and screens for lookups and tests. If that file is missing you are not on his ma
|
||||
|
||||
- Keep base hardware and UI in `home-like-2432s028.yaml`; personal data belongs
|
||||
in the gitignored local profiles.
|
||||
- The editor is `web/` (Vue 3 + Vite, TypeScript; app 0.2.74+). `screen_manager/app/static` is its build
|
||||
- The editor is `web/` (Vue 3 + Vite, TypeScript; app 0.2.73+). `screen_manager/app/static` is its build
|
||||
output: change `web/src`, run `cd web && npm test && npm run build`, and commit both. Keep every URL the page asks for
|
||||
relative (`api/...`), so it works behind Home Assistant's ingress path. Tests read the source through
|
||||
`tests/editor_sources.py`; docs/RELEASING.md "Local development" has the dev-server recipe.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Test results 0.2.73 (firmware 0.2.61, unchanged)
|
||||
|
||||
Date: 2026-09-18. Branch `editor-vue`, rebased on 9f5694b (0.2.72). The firmware and the packages did not change;
|
||||
this release is the editor (web/, Vue 3 + Vite) and four additive add-on endpoints.
|
||||
|
||||
## Automated
|
||||
|
||||
- Python: `.venv-portal/bin/python -m unittest discover -s tests` → 398 tests OK. New: `tests/test_editor_live.py`
|
||||
(`/api/states`, `/api/screens/{inbox}/identify`, `/api/alerts/test`, the changelog in the update summary, and the
|
||||
page side of every new feature read from the Vue sources). The nine tests that used to read `static/app.js` now
|
||||
read the sources through `tests/editor_sources.py`; `tests/test_page_assets.py` checks the committed Vite build.
|
||||
- Editor: `cd web && npm test` → Vitest, 37 tests OK (grid rules, top bar rules, store, TileCard, Library,
|
||||
CommandPalette in jsdom). `npm run check` (vue-tsc) clean. `npm run build` → `screen_manager/app/static`.
|
||||
- `tools/generate_icons.py --check` → 150 pickable icons, 309 glyphs verified (the editor font now lives in
|
||||
`web/src/assets`).
|
||||
|
||||
## By hand, on the demo home (real add-on code, fake Home Assistant, port 8199)
|
||||
|
||||
- Choose a screen, tile drawer with every field, top bar drawer with the live sample, add to the top bar, Screen
|
||||
settings tab, add an entity from the library and Save & send (PUT arrives, chip says Saved), drag a tile onto
|
||||
another (swap), remove with Undo, Esc closes the drawer, ··· menu, Override YAML with the example, New screen,
|
||||
Firmware & USB, Alerts, Settings; dark mode; 375 px layout.
|
||||
- New in this release: live values on every tile (temperature with unit, On/Off with a lit icon, setpoint, volume and
|
||||
position fills, the playing title), Identify (show_alert with flash reaches the demo HA), Try it to one screen and
|
||||
to all (two calls logged), Copy layout from the other screen, Export (download + clipboard), room filter, Hide
|
||||
placed, lit and grey avatars, ⌘K search that adds an entity, What's new under the Update badge.
|
||||
|
||||
## On Max's own Home Assistant
|
||||
|
||||
- The first build of this editor ran as a local add-on (same code, before the six features) against his real screens
|
||||
and Home Assistant: "ik vind de rest van de editor ook echt geweldig, heb hem getest".
|
||||
|
||||
## Not exercised
|
||||
|
||||
- The progress bar of a running firmware update (the demo home cannot build firmware); only the mapping from phase
|
||||
and ESPHome stage to a percentage is unit-tested.
|
||||
- Drag & drop on a phone (touch), and the six new features inside the Home Assistant ingress iframe.
|
||||
@@ -5,7 +5,7 @@ ARG BUILD_ARCH
|
||||
LABEL io.hass.version="${BUILD_VERSION}" io.hass.type="app" io.hass.arch="${BUILD_ARCH}"
|
||||
RUN pip install --no-cache-dir 'aiohttp>=3.12,<4' PyYAML
|
||||
COPY app /app
|
||||
# The Update badge shows what's new from the changelog (app 0.2.74).
|
||||
# The Update badge shows what's new from the changelog (app 0.2.73).
|
||||
COPY CHANGELOG.md /app/CHANGELOG.md
|
||||
WORKDIR /app
|
||||
ENTRYPOINT []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""What's new for the Update badge (app 0.2.74): the CHANGELOG sections since a screen's firmware.
|
||||
"""What's new for the Update badge (app 0.2.73): the CHANGELOG sections since a screen's firmware.
|
||||
|
||||
The Dockerfile copies CHANGELOG.md next to this file; a development checkout has it one folder up.
|
||||
Each `## <app> (firmware <fw>)` section gives its bullet lines as plain text, newest first.
|
||||
|
||||
@@ -1624,7 +1624,7 @@ def create_app(manager, development=False):
|
||||
'attributes':state_message(i,t,manager.ha.states)['a'],
|
||||
'options':t.get('options',{})} for i,t in enumerate(layout['tiles'])]})
|
||||
async def states(request):
|
||||
"""Live values for the editor's mockup (app 0.2.74): the state, Home Assistant's word and the attributes a
|
||||
"""Live values for the editor's mockup (app 0.2.73): the state, Home Assistant's word and the attributes a
|
||||
card shows, for the tiles on the page, saved or not. At most sixty entities per request."""
|
||||
index = manager.registry_index()
|
||||
result = {}
|
||||
@@ -1647,14 +1647,14 @@ def create_app(manager, development=False):
|
||||
raise ValueError(f"{screen['name']} can't show an alert: {skipped[0][1] if skipped else 'not ready'}.")
|
||||
return ready[0]
|
||||
async def identify(request):
|
||||
"""Identify (app 0.2.74): the screen shows a short card and blinks its backlight, so you know which one it is."""
|
||||
"""Identify (app 0.2.73): the screen shows a short card and blinks its backlight, so you know which one it is."""
|
||||
screen = one_alert_target(request.match_info['inbox'])
|
||||
data, _ = alert_data({'title': f"This is {screen['name']}", 'subtitle': 'Identify, from ESP Screens', 'icon': 'bell-ring',
|
||||
'color': 'blue', 'button_text': 'OK', 'timeout': 8, 'flash': True})
|
||||
await manager.ha.call(alert_service(screen['node']), data)
|
||||
return web.json_response({'ok': True})
|
||||
async def test_alert(request):
|
||||
"""Alerts → Try it (app 0.2.74): one alert to one screen or to every screen, with the fields an automation sends."""
|
||||
"""Alerts → Try it (app 0.2.73): one alert to one screen or to every screen, with the fields an automation sends."""
|
||||
body = await request.json()
|
||||
if not isinstance(body, dict):
|
||||
raise ValueError('Invalid alert.')
|
||||
|
||||
@@ -32,7 +32,7 @@ class Updater:
|
||||
self.manager, self.path = manager, Path(path)
|
||||
self.auto, self.hosts, self.results, self.last_round = False, {}, {}, None
|
||||
self.task, self.current, self.queue, self.phase = None, None, [], None
|
||||
# What's new since a screen's firmware, for the Update badge (app 0.2.74).
|
||||
# What's new since a screen's firmware, for the Update badge (app 0.2.73).
|
||||
self.changelog = changelog.load()
|
||||
if self.path.exists():
|
||||
raw = json.loads(self.path.read_text())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""The editor's source, for the tests that read it.
|
||||
|
||||
The page is a Vue app in web/ (app 0.2.74); `npm run build` writes it to screen_manager/app/static. Tests look at
|
||||
The page is a Vue app in web/ (app 0.2.73); `npm run build` writes it to screen_manager/app/static. Tests look at
|
||||
the source, not the build: every .ts and .vue file under web/src as one text (SCRIPT, which is also the markup:
|
||||
a .vue file carries its template), and the stylesheets as another (CSS).
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Live values on the mockup, Identify, the test alert and the changelog for the Update badge (app 0.2.74).
|
||||
"""Live values on the mockup, Identify, the test alert and the changelog for the Update badge (app 0.2.73).
|
||||
|
||||
The editor asks /api/states for the tiles it shows and draws Home Assistant's values on the mockup; Identify and
|
||||
Try it call a screen's own show_alert action, with the same field rules as an alert event; the update summary
|
||||
|
||||
@@ -8,7 +8,7 @@ fills in what was missing.
|
||||
App 0.2.65: the page no longer opens the first screen by itself; the owner picks one. Until then a card asks for
|
||||
that, or offers the install when there are no screens yet.
|
||||
|
||||
App 0.2.74: the page is the Vue app in web/; these tests read its source (tests/editor_sources.py).
|
||||
App 0.2.73: the page is the Vue app in web/; these tests read its source (tests/editor_sources.py).
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""The page loads its own script and styles by content hash (app 0.2.58, Vite build since 0.2.74).
|
||||
"""The page loads its own script and styles by content hash (app 0.2.58, Vite build since 0.2.73).
|
||||
|
||||
After updating to 0.2.57, Safari showed an empty Screen settings panel: it kept a cached script from before
|
||||
the update. Vite names every built file after a hash of its content and index.html asks for those names, so a
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""Settings (app 0.2.45): New screen, Firmware & USB, firmware updates, Alerts and Claude moved off the main
|
||||
page into a view of their own. App 0.2.74: the sidebar carries the way in (New screen, Firmware & USB, Alerts,
|
||||
page into a view of their own. App 0.2.73: the sidebar carries the way in (New screen, Firmware & USB, Alerts,
|
||||
Settings), each once; the Settings page keeps the updates and Claude."""
|
||||
import re
|
||||
import sys
|
||||
|
||||
+4
-4
@@ -165,7 +165,7 @@ export async function loadEntityActions(entity: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Live values on the mockup (app 0.2.74): what the screen shows right now ----
|
||||
// ---- Live values on the mockup (app 0.2.73): what the screen shows right now ----
|
||||
let statesFlight = false;
|
||||
export async function loadStates() {
|
||||
const entities = [...new Set((state.layout?.tiles || []).map((t) => t.entity).filter((id) => !id.startsWith("screen.")))];
|
||||
@@ -347,7 +347,7 @@ export async function save() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Identify and the test alert (app 0.2.74): a screen's own show_alert action ----
|
||||
// ---- Identify and the test alert (app 0.2.73): a screen's own show_alert action ----
|
||||
export const canAlert = (screen: Screen | undefined) =>
|
||||
Boolean(screen && screen.alert_action && versionAtLeast(screen.firmware, state.inventory.alerts?.min_firmware || "0.2.31"));
|
||||
export async function identify(screen: Screen) {
|
||||
@@ -362,7 +362,7 @@ export async function sendTestAlert(target: string, data: Record<string, unknown
|
||||
return (await send("alerts/test", "POST", { screen: target, data })) as { sent: number; failed: number; skipped: number; unusable?: string[] };
|
||||
}
|
||||
|
||||
// ---- Copying and sharing a layout (app 0.2.74) ----
|
||||
// ---- Copying and sharing a layout (app 0.2.73) ----
|
||||
const LAYOUT_KEYS = ["title", "tiles", "header", "pages"] as const;
|
||||
function adopt(source: Partial<Layout>, what: string) {
|
||||
const layout = state.layout;
|
||||
@@ -418,7 +418,7 @@ export function importLayout(text: string) {
|
||||
adopt(data, "Layout imported");
|
||||
}
|
||||
|
||||
// ---- Updates with content (app 0.2.74): what a screen gets, and how far its update is ----
|
||||
// ---- Updates with content (app 0.2.73): what a screen gets, and how far its update is ----
|
||||
export function whatsNew(screen: Screen): string[] {
|
||||
const target = state.inventory.updates?.target;
|
||||
const sections = (state.inventory.updates as any)?.changelog as { app: string; firmware: string; lines: string[] }[] | undefined;
|
||||
|
||||
Reference in New Issue
Block a user