Repository navigation
fix(cover): settle a full run at the actuator's own stop up to 0.5 s before the travel time (#770 end-stop follow-up) - #847
Conversation
…before the travel time (OpenWebNet-HA#770) Discussion OpenWebNet-HA#770 (comment 19245565) asks that open_cover / close_cover on timed covers settle at the end like set_cover_position 0 / 100; OpenWebNet-HA#834 settles them on travel_time + 2 s, but the actuator's own stop status that arrives a little before the stored travel time still froze the timed estimate short of the end: _freeze_position took a stop as the end stop only at or after run_started_at + travel. Measured on the live F461 2.0.11 / F411/2 plant (report 20, 2026-10-26, travel calibrated at 5.82 s down / 5.85 s up): close from 100 %, actuator *2*0*05## 5.76 s after the motor start -> "open 1", last_run 5.76; open with the stop at 5.74 s (report 18) -> "open 98"; the run that matched 5.82 s -> 100. The shutter sat on its limit stop every time. With a whole-second yaml travel_time (6 for that shutter) the same run ends at 4 %. - cover_motion.py: END_STOP_TOLERANCE = 0.5 s (60-80 ms measured early stop plus up to 0.5 s of yaml travel_time rounding; cited in the comment). - cover.py: _freeze_position(at, *, end_stop_tolerance=0.0) counts a stop as the end stop when at + tolerance >= run_started_at + travel; a new _full_run_to_end flag marks runs to an end (own open / close and set 0 / 100, scope runs that move the member, wall full runs) and handle_event passes the tolerance for a plain *2*0*WHERE## on such a run only. Translated keypad stops (*2*1000#0*WHERE##), HA's own stops, stops during an HA partial run and stops more than 0.5 s before the travel time still freeze the estimate; last_run_seconds keeps the measured run; a run from unknown keeps settling on its timer (OpenWebNet-HA#795 / OpenWebNet-HA#820); the OpenWebNet-HA#834 timer still settles a run whose stop never comes. - tests/test_discussion_770_cover_end_stop_tolerance.py: 14 tests on the F461 fixtures with the report-20 timings, the OpenWebNet-HA#770 F411 shutters (60 s / 86 s cutoff just before the stored time) and a known scope member. - docs/configuration/covers.md, .github/release-notes/2.0.0b17.md. Verified with OWNd master (fb0acd7) on PYTHONPATH: pytest tests: 5202 passed; cover.py / cover_motion.py 100 % statements, no partial branch on a changed line; ruff clean; mypy clean on the two modules; scripts/verify_ha_standards.py and scripts/sync_documentation.py --check pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the manual-stop ambiguity Review of fix/cover-settle-from-known-position: - _full_run_to_end is set only by _settle_run_to_end (own open / close and set_cover_position 0 / 100, scope runs that move the member) and by _arm_wall_settle (wall full runs); _begin_run clears it, so a set_cover_position run HA times itself never gets the tolerance. _begin_stop now clears it as well: a stop HA asked for is never the end stop, whenever its status arrives (before this the write of the stop frame cleared it). - Tests: set_cover_position 95 from 0 on a 6 s travel ends at 95 after HA's own stop at 5.7 s (within 0.5 s of the travel time), and a plain *2*0*05## at 5.7 s of that run freezes at 95, not 100; the HA stop_cover test asserts the flag is cleared before the stop status arrives. 16 tests in the module. - cover_motion.py END_STOP_TOLERANCE comment: a wall-switch / scenario stop in the last 0.5 s of a full run on a gateway without WHO 2 command translation (MH200N and the like) is the same plain *2*0*WHERE## as the actuator's limit stop and is reported as arrived; worst case 100 % / 0 % instead of 92..99 % on a 6 s shutter, 99 % on a 60 s one. 0.5 s kept: it covers the measured 60-80 ms early stop (report 20 / 18) and the whole-second travel_time rounding, and stays inside the 1 s early window of the OpenWebNet-HA#843 calibration cutoff rule without treating any configurator limit as a stop. Verified with OWNd master (fb0acd7) on PYTHONPATH: the cover modules (test_cover_*.py, test_discussion_770_*.py, test_issue_302.py, test_issue_433_cover_scope.py, test_component_impulse_cover.py): 257 passed; ruff clean; mypy clean on the two modules; scripts/verify_ha_standards.py and scripts/sync_documentation.py --check pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Following up on your report in Discussion #770 regarding your MH200N + F411 (M=0 / M=1) shutters: While #834 added the full-run settle timer ( This PR introduces an Could you confirm:
(If you would like to test this branch directly in Home Assistant via SSH/Terminal:) cd /config/custom_components
git clone -b fix/cover-settle-from-known-position --depth 1 https://github.com/gdluck/HomeAssistant-MyHOME.git /tmp/myhome_test \
&& rm -rf myhome.bak \
&& mv myhome myhome.bak \
&& cp -r /tmp/myhome_test/custom_components/myhome ./ \
&& rm -rf /tmp/myhome_test(Restart Home Assistant after updating; revert anytime with Thank you! |
|
Independent confirmation on a physical F461 2.0.11 + F411 test plant (one shutter, WHERE Setup: Bus measurement (actuator cutoff, undisturbed close): The curtain physically reaches its end in ~5 s; the gateway only reports the stop at the cutoff (120 s, then 30 s), i.e. 25–115 s late. Home Assistant full runs ( Both settle at Conclusion: the full-run settle behaviour the reporter needs is confirmed on independent F461 + F411 hardware. Their 60 s / 86 s blinds on the mandatory 2-minute F411 cutoff will reach Scope note, to be precise: because the F411 cutoff here is longer than |
|
We are merging this fix now so that it is included directly in the upcoming v2.0.0b17 beta release. Once v2.0.0b17 is released, you can update directly through Home Assistant without needing any manual git checkout commands. Please test it on your MH200N + F411 shutters ("Essen Fixglas" and "Stiege oben") and let us know in Discussion #770 if it behaves as expected or if you run into any issues! |
41989f0
into
OpenWebNet-HA:v2-phase1-architecture
Requested by A
What is the problem
Timed covers (F411/2 and the like, no position report) settle a full run on
travel_time+ 2 s since #834, as asked in discussion #770, comment 19245565 ("open/close buttons should also settle after travel time + 2 s … With the 1 minute setting the Fixglas stopped right at the end and was not reported as closed"). What was still missing is the actuator's own stop status that arrives a little before the stored travel time:cover.py::_freeze_positiontook a stop as the end stop only whenat >= run_started_at + travel, so the F411's limit stop 60–80 ms early cancelled the settle timer and froze the timed estimate one or two percent short of the end.Measured on the live F461 2.0.11 / F411/2 plant (internal report 20, 2026-10-26; travel calibrated on the bus at 5.82 s down / 5.85 s up):
close_coverfrom 100 % → actuator*2*0*05##5.76 s after the motor start → entityopen 1,last_run_seconds 5.76;open_cover→ stop at 5.74 s (report 18) →open 98; the one run that matched 5.82 s →open 100. The shutter was physically on its end stop every time. With a yamltravel_time: 6(whole seconds) the same run ends at 4 %.Affects WHO 2 timed covers on every gateway; the 60 s / 86 s F411 shutters of #770 (MH200N) are the reported case.
Known ambiguity, stated up front: on a gateway that relays no WHO 2 command translation (MH200N and the like) a wall-switch or scenario stop in the last 0.5 s of a full run is the same plain
*2*0*WHERE##as the actuator's limit stop, so it is reported as arrived too. Worst case a manual stop there shows 100 % / 0 % instead of 92..99 % on a 6 s shutter (0.5 s = 8 % of the travel) and instead of 99 % on a 60 s one. That is weighed against what the tolerance fixes on every full run: the measured 60–80 ms early stop (reports 20 / 18) plus the whole-secondtravel_timerounding (up to 0.5 s). Stops HA sends (stop_cover, the auto-stop of aset_cover_positionrun) and translated keypad stops (*2*1000#0*WHERE##, F461 / MyHomeServer1) are never snapped. 0.5 s is kept: it stays inside the 1 s early window of the #843 calibration cutoff rule (CALIBRATION_CUTOFF_EARLY, DECISIONS_REGISTER D45), the fix for #837, and treats no configurator limit as a stop. This PR does not touchcalibrate_cover: #837 is settled by #843, and the settle tolerance here only applies to runs, never to calibration.Fixes the end-stop follow-up of discussion #770 (comment 19245565); no issue number.
What is the solution
cover_motion.py:END_STOP_TOLERANCE = 0.5s (measurement cited in the comment: 60 / 80 ms early stops on a 5.82 s calibrated travel; the stop-status relay plus the limit stop ≈ 0.1 s; a whole-second yamltravel_timecan be up to 0.5 s above the real travel).cover.py:_freeze_position(at, *, end_stop_tolerance=0.0): end stop whenat + end_stop_tolerance >= run_started_at + travel; every existing caller keeps 0._full_run_to_endflag, set by_settle_run_to_end(ownopen_cover/close_cover/set_cover_position: 0|100, scope runs that move the member) and by_arm_wall_settle(wall / scenario full runs), cleared by_begin_run,_freeze_position,_finish_end_of_run_settle,_abort_motion.handle_event: a plain*2*0*WHERE##(no command translation: nobody sent a stop) on such a run passesEND_STOP_TOLERANCE; a translated stop (*2*1000#0*WHERE##), an own HA stop and a stop more than 0.5 s before the travel time still freeze the estimate.last_run_secondskeeps the measured run; no stop frame is sent; the fix(cover): settle full open/close, scope, and wall runs on travel_time + 2 s (#770) #834 timer still settles a run whose stop never comes; a run fromunknownkeeps settling on its timer as in fix(cover): a timed cover with no restored state starts unknown instead of "open 50 %" #795 / fix(climate,cover): restore the setpoint with its knob offset; scope cover members settle from unknown (#791 / #795 follow-up) #820.docs/configuration/covers.md(full-run bullet) and.github/release-notes/2.0.0b17.md.State Diagram
stateDiagram-v2 [*] --> Known : position 0..100 Known --> FullRun : open_cover or close_cover or set 0|100 or scope or wall direction FullRun --> FullRun : *2*0*W## before travel - 0.5 s (plain) -> freeze estimate (mid-run stop) FullRun --> End : *2*0*W## at travel - 0.5 s .. travel (plain, actuator's own) -> 0 % or 100 % FullRun --> End : *2*0*W## at or after travel -> 0 % or 100 % FullRun --> End : travel + 2 s timer, no stop seen (#834) FullRun --> Frozen : *2*1000#0*W## (translated keypad or HA stop) or own stop_cover -> estimate Known --> PartialRun : set_cover_position (HA auto-stop) PartialRun --> Frozen : any stop -> estimate (tolerance never applies) [*] --> Unknown : no restored position Unknown --> SettlingFromUnknown : open or close SettlingFromUnknown --> SettlingFromUnknown : *2*0*W## before travel -> ignored, timer continues (#795) SettlingFromUnknown --> End : timer travel + 2 s, or stop at/after travelHardware Verification
open 1/open 98. The branch itself has not been run on the plant yet (no gateway contact in this session); the test file replays the report's timings.closed.How is it tested
tests/test_discussion_770_cover_end_stop_tolerance.py(new, 16 tests, F461 fixtures with travel 5.82 / 5.85 s, direction echo 0.12 s):test_close_from_100_with_the_actuator_stop_60_ms_early_is_closed(report 20: 5.76 s →closed 0,last_run_seconds 5.76, only*2*2*05##sent, settle task gone)test_open_from_0_with_the_actuator_stop_80_ms_early_is_open_100(report 18: 5.74 s → 100)test_a_stop_at_the_travel_time_is_still_the_end_stoptest_a_stop_further_than_the_tolerance_from_the_end_freezes_the_estimate(0.51 s early → 9 %),test_a_stop_exactly_at_the_tolerance_is_the_end_stoptest_a_translated_keypad_stop_near_the_end_freezes_the_estimate,test_an_ha_stop_near_the_end_freezes_the_estimate(asserts_full_run_to_endis cleared bystop_coverbefore the stop status),test_a_plain_stop_during_a_partial_run_near_the_end_freezes_the_estimatetest_set_position_0_to_95_ends_at_95_not_at_the_end(travel 6 s, HA's own stop at 5.7 s → 95 %),test_a_plain_stop_at_the_target_of_a_0_to_95_run_is_not_an_end_stop(plain stop at 5.7 s of that run → 95 %, not 100 %)test_a_wall_switch_close_from_100_settles_at_the_early_actuator_stopis the same frame sequence whether the stop was the actuator's or a keypad's.test_a_wall_switch_close_from_100_settles_at_the_early_actuator_stoptest_a_run_from_unknown_keeps_settling_on_the_timer,test_the_timer_still_settles_a_run_whose_actuator_never_stopstest_discussion_770_f411_cutoff_just_before_the_stored_travel_time_is_closed[Essen Fixglas / Stiege oben]test_a_known_scope_member_settles_at_its_early_actuator_stoptests/test_cover_unknown_start.py,tests/test_discussion_770_cover_full_run_settle.py,tests/test_cover_*.py,tests/test_issue_302.py,tests/test_issue_433_cover_scope.py(198 passed).PYTHONPATH: 5202 passed in 442.70 s (5 snapshots passed; the base's 5188 + 14 new); branch coverage ofcover.py/cover_motion.py:cover.py1815 statements, 0 missed, 698 branches, 24 partial (99.0 %);cover_motion.py44 statements, 0 missed, 16 branches, 1 partial (98.3 %). None of the partial branches (cover.py194->196, 519->524, 756->758, 900->exit, 1007->exit, 1053->1080, 1055->1080, 1082->1087, 1118->exit, 1295->1297, 1322->exit, 1324->exit, 1446->1449, 1477->1480, 1533->1535, 2098->2104, 2236->2238, 2338->2340, 2368->2390, 2383->2385, 3038->exit, 3041->3045, 3109->3111, 3228->3262; cover_motion.py137->139) is on a changed line; they are unchanged base code (measured with a plain--cov-branch, not the repository's CI coverage configuration).ruff check .clean;mypyon the two modules clean;scripts/verify_ha_standards.pyandscripts/sync_documentation.py --checkpass.Where are the traces
*2*2*05##19:09:06.541 → echoes*2*1000#0*05##,*2*0*05##,*2*1000#2*05##,*2*2*05##→ actuator*2*0*05##19:09:12.462) and report 18; they are cited frame by frame in the test file. No new fixture is added undertests/fixtures/traces/: the raw capture of report 20 is not in the repository, and the existing F461 fixtures (tests/fixtures/traces/f461_live_2026-10-23, with the 5.825 s cover reply of its stopwatch run,f461_live_2026-10-24,f461_actuators_live_2026-10-25) do not contain a full run from a known position.tests/fixtures/traces/discussion_770_f454_f401) are position-reporting covers and do not exercise this path.What does the Firmware Oracle / Encyclopedia say
WHO = 2):*2*0*WHERE##is the automation status "stop"; the specification gives no way to tell the actuator's own limit stop from a keypad stop without the WHO 2 command translation (*2*1000#0*WHERE##, relayed by F461 / MyHomeServer1 for commands, never for the actuator's own stop). That is why the tolerance applies to untranslated stops only.Checklist
MyHOMECover.handle_event()is the only event path touched.pytest,ruff, andscripts/verify_ha_standards.pypass (with OWNd master on the path).🤖 Generated with Claude Code