Skip to content

Field fixes: Obstruction event, update stop, homing STOP speed, reboot position - #45

Draft
daniel-frenkel wants to merge 21 commits into
mainfrom
fix/field-hardening
Draft

daniel-frenkel wants to merge 21 commits into
mainfrom
fix/field-hardening

Conversation

@daniel-frenkel

Copy link
Copy Markdown
Member

Field fixes for Ropener v2.9.0. Draft: waiting for bench checks and review. No tag and no release from this PR.

What changes

  • Build: ESPHome pinned to 2026.6.5, the version v2.9.0 shipped with.
  • Position saved exactly where the motor stopped at any soft reboot (on_shutdown); the 200 ms refresh was already there.
  • The motor stops when a firmware update begins, however the update was started.
  • STOP during homing restores the run speed and cancels the homing timeout (it used to leave the curtain at the homing speed).
  • New entity: "Obstruction" event (obstructed), fired when a stall stops the curtain outside homing. It needs StallGuard Enabled on: with StallGuard off (the default) the Ropener cannot detect an obstruction at all, and the event never fires.
  • Tools: the regression gate now prints object IDs and no longer drops blocks that share an anchor; sites.py lists action call sites; bench-only builds with a "simulate stall" button (never built by CI).

Home Assistant entities

One addition on both boards, event | Obstruction | (none) | obstruction. No entity renamed or removed (gate run with object IDs against v2.9.0).

How it was checked

Every check was predicted, with a deliberate break that had to turn it red, and the predictions were committed before the code: tools/regression-gate/predictions/2026-10-field-hardening.md (later amendments appended, nothing edited). Each commit: esphome config and esphome compile exit 0 at 2026.6.5 for VAL3000 and VAL3100. Final gate against v2.9.0 matches the prediction exactly (2 changed, 4 new behaviour anchors). Bench checks with the hardware are pending.

🤖 Generated with Claude Code

daniel-frenkel and others added 21 commits October 30, 2026 09:55
Records the reviewer's rulings verbatim (one paragraph kept outside the
repo at its own instruction), what homing does at Speed Fast today
(follow-up 1), every check's predicted result and sabotage, the expected
final gate output, and the bench plan. No check had been run when this
was committed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions

The entity gate now prints each entity's object_id, computed with
ESPHome's own get_base_entity_object_id, so a diff shows the ID Home
Assistant's entity_id is built from, not only the name.

The behaviour gate keyed bodies by anchor in a plain dict, so two blocks
with the same anchor silently overwrote each other. Glasscalibur has two
`interval: 200ms` blocks, which hid the motion-completion poll from the
gate entirely. Shared anchors now get an ordinal (interval:200ms#1, #2).

Adds sites.py, which lists where an action is called from, using the same
anchors.

Checked (predictions G1-G3): base-vs-base clean on all three targets; a
renamed entity shows exactly one REMOVED/ADDED pair with object IDs; a
change to the completion poll is reported by the fixed gate and missed by
the old one; call-site counts on base match, and an inserted call is found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build-release.yml ran a bare `pip install esphome`, so every release was
built with whatever ESPHome was newest that day. The shipped binaries
(Glasscalibur v2.1.0, Ropener v2.9.0 VAL3000 + VAL3100) all report
"ESPHome version 2026.6.5"; today the bare install would pull 2026.10.x.

The install step now pins 2026.6.5 and fails if `esphome version` reports
anything else.

Checked (P1.1, P1.2): the workflow has the pin and no bare install (base
had the bare install); the assertion passes in a 2026.6.5 venv and fails
against a 2026.5.3 install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
P2.1 predicted exactly one set_reboot_timeout( call in generated code;
every target has two, because the Wi-Fi component has its own (900000).
Appended, without editing anything above: the three readings, the
cause, the reviewer's ruling verbatim (one paragraph replaced by a
pointer, recorded outside this repository), the re-registered check
(exactly one api_apiserver_id->set_reboot_timeout( call, value 0; zero
matches fails), and its sabotage and prediction. Committed before the
re-registered check or its sabotage ran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d gate predictions

Appended, without editing anything above: the 2026-11-04 rulings
verbatim (some lines replaced by pointers, recorded outside this
repository), the P1.4 miss with its readings and diagnosed cause (the
hashed config carries build_path, written with an OS path separator),
the re-registered P1.4 with its control, F8 (position after a reboot
mid-travel) and its reboot paths, F8a and F8c predictions with the
ESPHome 2026.6.5 source lines relied on, and a predicted merge probe.
Committed before any of these checks ran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appended, without editing anything above: a pointer to the ruling of
2026-11-04 ~05:40Z (recorded outside this repository), the P1.4
control's miss and run 1's instrument failure, the re-registered
control (key-sorted comparison, with its own control and sabotage),
F8b's design (one OTA state listener per product, calling the cover's
normal STOP when an update begins), the re-worded F8-P3 with its
sabotage, and the gate rows. Committed before any of these checks ran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The behaviour gate only walked entities, scripts, intervals and on_boot,
so an esphome on_shutdown hook or an OTA platform's on_* trigger could be
added, dropped or rewritten without the gate noticing. Both are now
anchored: esphome:on_shutdown by priority, OTA triggers as ota:<platform>.

Checked (G4): base vs base still clean on all three targets; adding an
on_shutdown hook and an esphome-OTA on_begin is missed by the previous
gate and reported by this one as exactly those two NEW rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 200 ms poll keeps the saved position current between ticks; an
on_shutdown hook at priority 600 now also copies the driver's live count
at the moment of a soft reboot (Restart button, end of a firmware update,
any App.reboot). ESPHome runs shutdown hooks in reverse setup-priority
order, so it runs before the globals store (800) and the preference
syncer flushes (1000).

Checked (F8-P1): on both boards the resolved config has the hook below
priority 800 (moved to 900 it fails). P2.1 on these head builds: exactly
one api_apiserver_id->set_reboot_timeout(0). Gate: only NEW
esphome:on_shutdown :: 600.0. config and compile exit 0 at ESPHome
2026.6.5 for VAL3000 and VAL3100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appended, without editing anything above: a pointer to the ruling of
2026-11-04 ~17:05Z (recorded outside this repository), the third OTA
platform found by F8-M (web_server, the device web page upload) and the
lesson that every-platform claims are read from the resolved config,
the new F8-P6 check with its sabotage, the predictions for F8b's code,
and the updated F8-P5 bench path. Committed before F8b's code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Whatever starts an update (ESPHome, the device's own web page, or the
Update Firmware button), the motor now stops when it begins. Each OTA
platform reports the start of an update to ESPHome's global OTA
callback; one listener, registered at boot, answers it with the cover's
normal STOP, so stop_action runs exactly as if the user had pressed STOP.
The esphome OTA platform gets a log-only on_begin, which keeps the OTA
listener interface compiled in.

Checked (F8-P3, F8-P6) on every build target: the resolved config
registers the listener at on_boot 900 with the cover's STOP call, the
esphome OTA entry carries on_begin, the generated code defines
USE_OTA_STATE_LISTENER, and the OTA platforms are exactly esphome,
http_request and web_server. Dropping the registration, or adding a
fourth platform, turns them red. Gate: NEW esphome:on_boot :: 900.0 and
NEW ota:esphome :: on_begin only. config and compile exit 0 at ESPHome
2026.6.5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Homing drives at a fixed home_speed (3000) and only home_finish /
home_abort put the user's Speed back. A STOP from the web UI or Home
Assistant mid-homing stopped the motor but left it at the homing speed,
so every later move ran at 3000 until a reboot, the next homing or a
Speed change; homing's 60 s timeout was also left pending. stop_action
now also stops home_timeout and restores global_speed. Outside homing
both are no-ops.

Checked (P3.1, Ropener): stop_action contains script.stop home_timeout
and stepper.set_speed on both boards (base: neither). Gate: only CHANGED
cover:Ropener :: stop_action. config and compile exit 0 at ESPHome
2026.6.5 for VAL3000 and VAL3100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ange)

on_stall's non-homing branch (settle to IDLE, keep the position) moves
into a script, stall_stop, so the next commit can add the Obstruction
event in one place. No behaviour change.

Checked: with stall_stop inlined back, on_stall is identical to the
previous commit's on both boards. Gate: CHANGED stepper:driver ::
on_stall, NEW script:stall_stop. config and compile exit 0 at ESPHome
2026.6.5 for VAL3000 and VAL3100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Until now a stall that stopped the curtain outside homing left no signal
at all. A new event entity, "Obstruction" (event type "obstructed"),
fires once each time it happens. Homing stalls are excluded (they mean
"closed end reached"). Existing entities are unchanged.

It can only fire with StallGuard Enabled on. With StallGuard off (the
default) the Ropener cannot detect an obstruction at all: the motor
keeps pulling, slipping, until its travel is counted out, and this event
never fires.

Checked (P5.1-P5.3, P4.1): the entity gate against v2.9.0 shows exactly
one addition, event | Obstruction | (none) | obstruction, and no
removals, on both boards; event.trigger is only in stall_stop, which is
reached only from on_stall's StallGuard-enabled, non-homing branch;
on_stall has no else branch; stall_stop never re-targets the motor.
Each of these turns red when broken on purpose. config and compile exit
0 at ESPHome 2026.6.5 for VAL3000 and VAL3100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bench-only wrapper around the release firmware that adds one button,
"BENCH: simulate stall", which runs the stall response on demand while
the cover is moving (StallGuard does not fire on cue). It exists for the
bench checks in tools/regression-gate/predictions. It is never built by
CI or a release, and must never be flashed to a customer unit.

Checked (R1g): the bench build compiles at ESPHome 2026.6.5 and its
entity list adds the BENCH button; no release build contains it, and
the release workflow does not reference tools/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appended, without editing anything above: a pointer to the ruling of
2026-11-04 ~17:50Z (recorded outside this repository), the P6.1 miss
(the only match was this predictions record itself), and P6.1
re-registered over the product docs (docs/, README.md, firmware/,
flash/). Committed before the re-registered check ran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appended, without editing anything above: a pointer to the approving
ruling of 2026-11-04 ~18:15Z (recorded outside this repository), the
session ground rules verbatim, and the re-registered bench steps:
everything at the Slow preset, driven from the PC over REST, away from
the ends; three yes/no questions for Daniel at the start; B3 step 5
dropped (covered by P4.2); F8-P4 and F8-P5 on both products with a
move of at least 20 % of travel so they cannot pass vacuously. Committed
before the session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appended, without editing anything above: Daniel's directions from the
session verbatim (start now, Glasscalibur first, firmware loads allowed,
HA disabled, Button 1 presses yes, no old-firmware comparison, run at
Medium or Fast to limit motor heating), the bench facts so far, and the
one speed-dependent expectation re-registered (B3 back-off ~9-10 s at
Medium). Committed before the session's motion steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
B6 step 2's expected TSTEP re-registered for Medium (374000 / 5000 ~ 75), and
Home Assistant connected for B4, per Daniel's answers in the working session.
Committed before any Ropener motion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Results of the 2026-11-04 bench session as measured, with the reruns, the
instrument failures, the window overrun and the Ropener F8-P4 miss kept. The
ruling on them is recorded outside the repository; this is its pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Registered before the next bench session: the stop point is computed from the
last reading plus the recorded gap to the restart (the device reports no
position at a restart), with a measured error bound under 1 % of travel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a Restart the motor stops in the shutdown pass just after the position
save, about 40 ms after "Rebooting safely", not at that line or at the reset.
The re-run's stop time is set to that event, with the remainder in the error
bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant