UX-13 phase P5 (design §6). Base release/1.0 = 8c34647.
1. Dutch text.app/ui_text/nl.json has the same keys as en.json. app/uitext.py loads it and falls back to English key by key. The page picks Dutch when the browser language is nl, as the classic page already does. Option labels come from translations/nl.yaml through config/model?lang=. Home's sentences moved from homeview.py into home_words, so /status?lang=nl builds them in Dutch; plain /status is unchanged. The setup-only banner and pill now come from ui_text, and a test checks them against messages.banner_setup_only(). Still English: status rows, banners, hints and validator sentences (messages.py, validate.py), because notifications, the log and the start refusal share them. The NL screenshot shows this.
2. STYLE check. A new test_ui_text.py checks:
EN/NL key parity and matching placeholders.
A name has at most 6 words. A description is 1 sentence of at most 25 words. Other lines have at most 2 sentences.
No semicolons, and no jargon from the EN and NL lists.
No inline details: no link, section number or file name.
Home rendered in both languages.
Any Dutch string left in English.
Each failure names the file, the key and the string, and every rule has a mutation check. The shared rules moved unchanged from test_messages.py to tools/style_rules.py. test_messages part 8 now also renders Dutch.
3. Screenshots.tools/ux13_screenshots.sh serves the ux13_demo.py fixtures (running, shadow, new, and a new setup state) in the CI image. It then runs headless Chromium in the Playwright image (v1.55.0), and any page error fails the run. The output is 11 PNGs in docs/img/ux13/, with a README. DOCS.md "Language and screenshots" points to them. Battery modes (P3) have no screenshot, only a placeholder line.
4. Period editor. "Other time windows" is now an editable list (ui.periods, control period_list) with rows for from, to, days and price. uimodel._periods_json() builds static_profile and names the first bad time, zero-length window, empty or out-of-range price, overlap (both windows, day and times) or gap (day and times), in EN and NL. A stored profile whose days the editor cannot express stays as stored, for editing in the Configuration tab. Stored window names are kept.
Tested
All test_*.py in the CI image (goodwe-addon-checks:1): all pass (run without GOODWE_REQUIRE_HWSIM locally).
New checks in test_uimodel part 7, test_setup, test_home (Dutch /status) and test_ui_text.
Supervisor 2026.08.0 AppOptions, offline via tools/ux13_wizard_blobs.py --supervisor: 133 of 133 wizard blobs accepted, 17 of them with time windows, 78 keys, none dropped.
Screenshot run: no page errors, and I checked the images by eye.
Not tested on TEST-23; the post-merge test agent does that.
UX-13 phase P5 (design §6). Base release/1.0 = 8c34647.
**1. Dutch text.** `app/ui_text/nl.json` has the same keys as `en.json`. `app/uitext.py` loads it and falls back to English key by key. The page picks Dutch when the browser language is `nl`, as the classic page already does. Option labels come from `translations/nl.yaml` through `config/model?lang=`. Home's sentences moved from `homeview.py` into `home_words`, so `/status?lang=nl` builds them in Dutch; plain `/status` is unchanged. The setup-only banner and pill now come from ui_text, and a test checks them against `messages.banner_setup_only()`. **Still English:** status rows, banners, hints and validator sentences (`messages.py`, `validate.py`), because notifications, the log and the start refusal share them. The NL screenshot shows this.
**2. STYLE check.** A new `test_ui_text.py` checks:
- EN/NL key parity and matching placeholders.
- A name has at most 6 words. A description is 1 sentence of at most 25 words. Other lines have at most 2 sentences.
- No semicolons, and no jargon from the EN and NL lists.
- No inline details: no link, section number or file name.
- Home rendered in both languages.
- Any Dutch string left in English.
Each failure names the file, the key and the string, and every rule has a mutation check. The shared rules moved unchanged from test_messages.py to `tools/style_rules.py`. test_messages part 8 now also renders Dutch.
**3. Screenshots.** `tools/ux13_screenshots.sh` serves the `ux13_demo.py` fixtures (running, shadow, new, and a new `setup` state) in the CI image. It then runs headless Chromium in the Playwright image (v1.55.0), and any page error fails the run. The output is 11 PNGs in `docs/img/ux13/`, with a README. DOCS.md "Language and screenshots" points to them. Battery modes (P3) have no screenshot, only a placeholder line.
**4. Period editor.** "Other time windows" is now an editable list (`ui.periods`, control `period_list`) with rows for from, to, days and price. `uimodel._periods_json()` builds `static_profile` and names the first bad time, zero-length window, empty or out-of-range price, overlap (both windows, day and times) or gap (day and times), in EN and NL. A stored profile whose days the editor cannot express stays as stored, for editing in the Configuration tab. Stored window names are kept.
**Tested**
- All `test_*.py` in the CI image (goodwe-addon-checks:1): all pass (run without GOODWE_REQUIRE_HWSIM locally).
- New checks in test_uimodel part 7, test_setup, test_home (Dutch `/status`) and test_ui_text.
- Supervisor 2026.08.0 `AppOptions`, offline via `tools/ux13_wizard_blobs.py --supervisor`: 133 of 133 wizard blobs accepted, 17 of them with time windows, 78 keys, none dropped.
- Screenshot run: no page errors, and I checked the images by eye.
Not tested on TEST-23; the post-merge test agent does that.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01UBtqPrG1mDLXzTEBnhEkkV
- app/ui_text/nl.json and app/uitext.py: every page word in Dutch, English
fallback per key; the page picks by browser language. Home's sentences
move into ui_text `home_words`, so /status?lang=nl builds them in Dutch.
- test_ui_text.py: EN/NL key and placeholder parity, STYLE limits, jargon
lists for both languages, no inline details, untranslated Dutch caught.
Shared rules moved unchanged to tools/style_rules.py.
- tools/ux13_screenshots.sh/.py: DOCS screenshots from the demo fixtures in
headless Chromium; docs/img/ux13/. Battery modes (P3): no screenshot.
- "Other time windows": a list editor (ui.periods) that builds
static_profile and names bad times, empty prices, overlaps and gaps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- test_ui_text.py: key parity, placeholder parity, English-left-in-Dutch,
semicolon, 25-word description and 2-sentence line each now fail on a
spoiled input (the English-left check was not a real mutation).
- Jargon lists now cover the whole 'Not' column of STYLE.md section 4, in
English, with the Dutch equivalents.
- nl.json: 'gewijzigd worden' where 'wijzigen' read as intransitive,
'Bezig met bewaren' for the saving state, 'Metingen kloppen niet'.
- test_uimodel.py: an overlap that changes partners is named piece by piece
(a surviving mutant of _tiling's partner check).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1. STYLE rules move: pass.tools/style_rules.py is byte-identical to the block removed from test_messages.py. Mutations in the shared module (SNAKE never matches; sentences() always 1) fail both test_messages.py and test_ui_text.py.
2. STYLE test bites: fixed in ede01b8. The PR said every rule had a mutation check. That was true for name length, 2 sentences, jargon, links and snake_case, but not for key parity, placeholder parity, semicolons, the 25-word description limit or the 2-sentence line. The "English left in Dutch" check was not a real mutation: it only asserted "Save" != NL[...]. Each of these now fails on a spoiled input, and I mutated each detector to confirm it. The jargon lists were not tuned to the strings (a probe with the rest of STYLE §4 found no hits), but they were incomplete. They now cover the whole "Not" column (command, charge level, ceiling, quarter, billed quarter, ESP32, master, rail, ...) plus Dutch equivalents (laadtoestand, setpunt, commando, geparkeerd, controller). All strings still pass.
Dutch quality is good and plain. I fixed three strings where "kan/kunnen alleen ... wijzigen" read as intransitive (now "gewijzigd worden"), changed savebar.saving "Bewaren" (identical to the Save button) to "Bezig met bewaren", and changed "Metingen kloppen niet samen" to "Metingen kloppen niet". Left as is: "Support" and "Timing" (SAME list), and "feed"/"topic", which mirror the English.
3. Home English unchanged: pass. On the same ux13_demo fixtures (running, shadow, alarm), base 8c34647 and head produce identical status_payload() output apart from timestamps. The only change in text_samples() is one extra sample ("Held at 0 W" title).
4. Period editor: pass, plus one test added.
Round trip: stored profiles (named and indented, unnamed with timezone and "00:00" ends, start/end keys, bare list) load and save byte-equal, untouched, after an unrelated edit, and after re-sending the rows. "mon,wed" profiles show no rows and are kept byte-equal.
Every named error has a test. Mutating the gap and overlap detection fails the tests. One mutant survived: dropping the here != rows partner-change check. test_uimodel part 7 now has a case for that (1 overlaps 2 then 3, reported as 1 and 2, 06:00 to 08:00).
Supervisor 2026.08.0 AppOptions (offline, --network none): 136 of 136 accepted, which is the 133 wizard blobs (17 with windows) plus 3 of my own (a wrapping named window, seven single days, a negative price).
The error texts pass test_ui_text as descriptions (1 sentence, ≤25 words).
5. Screenshots: pass. From a clean clone the script produced all 11 PNGs in about 2 minutes (it pulls the Playwright image and PyPI). They total 1.8 MB (≈170 KB each), which is acceptable. Each regeneration adds ~1.8 MB to history, so it should not become routine. They contain only fixture data: no real IPs, tokens or entity names beyond sensor.grid. The P3 placeholder is honest (README and DOCS say there is no screenshot). Cosmetic: the sticky save bar covers part of the time-window shots, and settings-installer shows a "Stored settings are not running" banner from the fixture.
6. Safety: pass. Outside UI and text, the only code change is status_payload(lang=), passed through to homeview.build(), which only reads. There is no controller, command or service-call change. homeview._LANG is module state set for a synchronous build, which is safe on the single asyncio thread but fragile if a thread ever calls it (follow-up).
7. Records: pass. DECISIONS and DOCS are concise, and I checked each claim (the _check_tiling reference exists, and the 133/17 Supervisor numbers match). The PR does not claim a TEST-23 run.
## Review of PR #148 (head 667b8e9 -> ede01b8)
**1. STYLE rules move: pass.** `tools/style_rules.py` is byte-identical to the block removed from `test_messages.py`. Mutations in the shared module (SNAKE never matches; `sentences()` always 1) fail **both** test_messages.py and test_ui_text.py.
**2. STYLE test bites: fixed in ede01b8.** The PR said every rule had a mutation check. That was true for name length, 2 sentences, jargon, links and snake_case, but **not** for key parity, placeholder parity, semicolons, the 25-word description limit or the 2-sentence line. The "English left in Dutch" check was not a real mutation: it only asserted `"Save" != NL[...]`. Each of these now fails on a spoiled input, and I mutated each detector to confirm it. The jargon lists were not tuned to the strings (a probe with the rest of STYLE §4 found no hits), but they were incomplete. They now cover the whole "Not" column (command, charge level, ceiling, quarter, billed quarter, ESP32, master, rail, ...) plus Dutch equivalents (laadtoestand, setpunt, commando, geparkeerd, controller). All strings still pass.
Dutch quality is good and plain. I fixed three strings where "kan/kunnen alleen ... wijzigen" read as intransitive (now "gewijzigd worden"), changed `savebar.saving` "Bewaren" (identical to the Save button) to "Bezig met bewaren", and changed "Metingen kloppen niet samen" to "Metingen kloppen niet". Left as is: "Support" and "Timing" (SAME list), and "feed"/"topic", which mirror the English.
**3. Home English unchanged: pass.** On the same `ux13_demo` fixtures (running, shadow, alarm), base 8c34647 and head produce identical `status_payload()` output apart from timestamps. The only change in `text_samples()` is one extra sample ("Held at 0 W" title).
**4. Period editor: pass, plus one test added.**
- Round trip: stored profiles (named and indented, unnamed with `timezone` and "00:00" ends, `start`/`end` keys, bare list) load and save **byte-equal**, untouched, after an unrelated edit, and after re-sending the rows. "mon,wed" profiles show no rows and are kept byte-equal.
- Every named error has a test. Mutating the gap and overlap detection fails the tests. One mutant survived: dropping the `here != rows` partner-change check. `test_uimodel` part 7 now has a case for that (1 overlaps 2 then 3, reported as 1 and 2, 06:00 to 08:00).
- Supervisor 2026.08.0 `AppOptions` (offline, `--network none`): 136 of 136 accepted, which is the 133 wizard blobs (17 with windows) plus 3 of my own (a wrapping named window, seven single days, a negative price).
- The error texts pass test_ui_text as descriptions (1 sentence, ≤25 words).
**5. Screenshots: pass.** From a clean clone the script produced all 11 PNGs in about 2 minutes (it pulls the Playwright image and PyPI). They total 1.8 MB (≈170 KB each), which is acceptable. Each regeneration adds ~1.8 MB to history, so it should not become routine. They contain only fixture data: no real IPs, tokens or entity names beyond `sensor.grid`. The P3 placeholder is honest (README and DOCS say there is no screenshot). Cosmetic: the sticky save bar covers part of the time-window shots, and `settings-installer` shows a "Stored settings are not running" banner from the fixture.
**6. Safety: pass.** Outside UI and text, the only code change is `status_payload(lang=)`, passed through to `homeview.build()`, which only reads. There is no controller, command or service-call change. `homeview._LANG` is module state set for a synchronous build, which is safe on the single asyncio thread but fragile if a thread ever calls it (follow-up).
**7. Records: pass.** DECISIONS and DOCS are concise, and I checked each claim (the `_check_tiling` reference exists, and the 133/17 Supervisor numbers match). The PR does not claim a TEST-23 run.
admin
merged commit a5dddadf45 into release/1.02026-09-28 17:06:19 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
UX-13 phase P5 (design §6). Base release/1.0 =
8c34647.1. Dutch text.
app/ui_text/nl.jsonhas the same keys asen.json.app/uitext.pyloads it and falls back to English key by key. The page picks Dutch when the browser language isnl, as the classic page already does. Option labels come fromtranslations/nl.yamlthroughconfig/model?lang=. Home's sentences moved fromhomeview.pyintohome_words, so/status?lang=nlbuilds them in Dutch; plain/statusis unchanged. The setup-only banner and pill now come from ui_text, and a test checks them againstmessages.banner_setup_only(). Still English: status rows, banners, hints and validator sentences (messages.py,validate.py), because notifications, the log and the start refusal share them. The NL screenshot shows this.2. STYLE check. A new
test_ui_text.pychecks:Each failure names the file, the key and the string, and every rule has a mutation check. The shared rules moved unchanged from test_messages.py to
tools/style_rules.py. test_messages part 8 now also renders Dutch.3. Screenshots.
tools/ux13_screenshots.shserves theux13_demo.pyfixtures (running, shadow, new, and a newsetupstate) in the CI image. It then runs headless Chromium in the Playwright image (v1.55.0), and any page error fails the run. The output is 11 PNGs indocs/img/ux13/, with a README. DOCS.md "Language and screenshots" points to them. Battery modes (P3) have no screenshot, only a placeholder line.4. Period editor. "Other time windows" is now an editable list (
ui.periods, controlperiod_list) with rows for from, to, days and price.uimodel._periods_json()buildsstatic_profileand names the first bad time, zero-length window, empty or out-of-range price, overlap (both windows, day and times) or gap (day and times), in EN and NL. A stored profile whose days the editor cannot express stays as stored, for editing in the Configuration tab. Stored window names are kept.Tested
test_*.pyin the CI image (goodwe-addon-checks:1): all pass (run without GOODWE_REQUIRE_HWSIM locally)./status) and test_ui_text.AppOptions, offline viatools/ux13_wizard_blobs.py --supervisor: 133 of 133 wizard blobs accepted, 17 of them with time windows, 78 keys, none dropped.Not tested on TEST-23; the post-merge test agent does that.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UBtqPrG1mDLXzTEBnhEkkV
Review of PR #148 (head
667b8e9->ede01b8)1. STYLE rules move: pass.
tools/style_rules.pyis byte-identical to the block removed fromtest_messages.py. Mutations in the shared module (SNAKE never matches;sentences()always 1) fail both test_messages.py and test_ui_text.py.2. STYLE test bites: fixed in
ede01b8. The PR said every rule had a mutation check. That was true for name length, 2 sentences, jargon, links and snake_case, but not for key parity, placeholder parity, semicolons, the 25-word description limit or the 2-sentence line. The "English left in Dutch" check was not a real mutation: it only asserted"Save" != NL[...]. Each of these now fails on a spoiled input, and I mutated each detector to confirm it. The jargon lists were not tuned to the strings (a probe with the rest of STYLE §4 found no hits), but they were incomplete. They now cover the whole "Not" column (command, charge level, ceiling, quarter, billed quarter, ESP32, master, rail, ...) plus Dutch equivalents (laadtoestand, setpunt, commando, geparkeerd, controller). All strings still pass.Dutch quality is good and plain. I fixed three strings where "kan/kunnen alleen ... wijzigen" read as intransitive (now "gewijzigd worden"), changed
savebar.saving"Bewaren" (identical to the Save button) to "Bezig met bewaren", and changed "Metingen kloppen niet samen" to "Metingen kloppen niet". Left as is: "Support" and "Timing" (SAME list), and "feed"/"topic", which mirror the English.3. Home English unchanged: pass. On the same
ux13_demofixtures (running, shadow, alarm), base8c34647and head produce identicalstatus_payload()output apart from timestamps. The only change intext_samples()is one extra sample ("Held at 0 W" title).4. Period editor: pass, plus one test added.
timezoneand "00:00" ends,start/endkeys, bare list) load and save byte-equal, untouched, after an unrelated edit, and after re-sending the rows. "mon,wed" profiles show no rows and are kept byte-equal.here != rowspartner-change check.test_uimodelpart 7 now has a case for that (1 overlaps 2 then 3, reported as 1 and 2, 06:00 to 08:00).AppOptions(offline,--network none): 136 of 136 accepted, which is the 133 wizard blobs (17 with windows) plus 3 of my own (a wrapping named window, seven single days, a negative price).5. Screenshots: pass. From a clean clone the script produced all 11 PNGs in about 2 minutes (it pulls the Playwright image and PyPI). They total 1.8 MB (≈170 KB each), which is acceptable. Each regeneration adds ~1.8 MB to history, so it should not become routine. They contain only fixture data: no real IPs, tokens or entity names beyond
sensor.grid. The P3 placeholder is honest (README and DOCS say there is no screenshot). Cosmetic: the sticky save bar covers part of the time-window shots, andsettings-installershows a "Stored settings are not running" banner from the fixture.6. Safety: pass. Outside UI and text, the only code change is
status_payload(lang=), passed through tohomeview.build(), which only reads. There is no controller, command or service-call change.homeview._LANGis module state set for a synchronous build, which is safe on the single asyncio thread but fragile if a thread ever calls it (follow-up).7. Records: pass. DECISIONS and DOCS are concise, and I checked each claim (the
_check_tilingreference exists, and the 133/17 Supervisor numbers match). The PR does not claim a TEST-23 run.