Rewrite with UI, calibration, and syncing with physical switches#51
Conversation
- Add pyproject.toml with pytest, ruff, and pyright configuration - Add GitHub Actions workflow for tests and linting - Update .gitignore with .coverage and .iml entries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract the core cover logic from the monolithic cover.py into cover_base.py (CoverTimeBased base class) with abstract methods for relay control. Add concrete subclasses for each input mode: - cover_switch_mode.py: separate open/close switches - cover_toggle_mode.py: single toggle switch with directional state - cover_pulse_mode.py: momentary pulse switch control - cover_wrapped.py: wraps an existing HA cover entity - cover_switch.py: helper for switch entity operations Slim cover.py to a thin factory that creates the appropriate subclass based on the input_mode configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a standalone travel_calculator.py that follows HA conventions, removing the xknx library dependency. Handles position tracking, movement timing, and travel state management. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace tilt mode strings with a strategy pattern. Each strategy encapsulates movement planning and command generation: - base.py: TiltStrategy ABC with MovementStep dataclasses - sequential.py: SequentialTilt with phased movement model - dual_motor.py: DualMotorTilt with boundary lock and safety - inline.py: InlineTilt using the main motor for tilt control Factory function creates the appropriate strategy from config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add __init__.py with async_setup_entry for config entry support - Add config_flow.py with UI-based setup wizard - Update manifest.json with config_flow dependency and version bump Enables UI-first setup, deprecating YAML-only configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add calibration.py with automated timing calibration services: - start_calibration/stop_calibration for travel time measurement - Motor overhead calibration with two-phase step test - Min movement time calibration with incremental pulses - Tilt time calibration with travel_moves_with_tilt validation - CalibrationState dataclass for tracking active calibration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add websocket_api.py providing real-time configuration management: - get_config/save_config for cover timing parameters - get_all_covers for listing configured covers with state - start/stop_calibration commands with progress reporting - set_known_position/set_known_tilt_position for position reset - raw_command for direct relay control bypassing position tracker Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update services.yaml with calibration and position reset services using target selectors for UI mode - Update strings.json with config flow, calibration, and service descriptions - Update translations for en, pt; add pl translation - Add translatable strings for all calibration attributes and frontend card labels Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add cover-time-based-card.js (1599 lines) providing a full configuration UI: - Device tab: entity pickers, timing table with save/discard, input mode and tilt mode selectors - Calibration tab: automated calibration with progress feedback, position reset, and per-attribute hints - Tilt Motor tab: strategy-specific entity pickers and config - All strings are translatable via HA's localization system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 19 test files with ~10,000 lines of tests covering: - Base movement orchestration and position tracking - All input mode subclasses (pulse, switch, toggle, wrapped) - Cover factory and service dispatching - Calibration system with all calibration types - WebSocket API endpoints and config management - Tilt strategy behavior for all strategy types - State monitoring with echo filtering - Travel calculator position/timing logic - Config flow and integration setup - Relay command sequences and motor overhead Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite README for UI-first setup, deprecate YAML-only docs - Add sections for calibration, tilt strategies, state monitoring - Update CHANGELOG with all new features and breaking changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 20 design and plan documents covering: - UI config flow design and implementation plan - Input mode subclasses design and refactor plan - State monitoring design and implementation plan - Calibration APIs design and implementation plan - Configuration card design and plan - Manual position reset design and plan - Tilt strategies design, refactor plan, and config UI plan - Inline tilt design and implementation plan Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When travel or tilt position is None (e.g. newly created entity with no restored state), None != 0 evaluates to True, causing set_position(100) to silently force tilt to 100% on unknown state. Add the same None guard that DualMotorTilt already has. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move CONF_*, DOMAIN, and DEFAULT_* constants that were duplicated across cover_base.py, cover.py, websocket_api.py and __init__.py into a single const.py module. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move _resolve_entity from cover.py and websocket_api.py into a shared helpers.py module as resolve_entity (raises) and resolve_entity_or_none (returns None). Update all call sites and test mock paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move 10 calibration methods (337 lines) from CoverTimeBased into CalibrationMixin in cover_calibration.py. CoverTimeBased now inherits from CalibrationMixin, keeping all behavior identical. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract _extract_coupled_tilt, _extract_coupled_travel, and _calculate_pre_step_delay from CoverTimeBased into module-level functions in tilt_strategies/planning.py. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract the ~45-line tilt coupling block duplicated between _async_move_to_endpoint and set_position into _plan_tilt_for_travel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reorganize definitions across 5 files for top-to-bottom readability: - cover_base.py: group by lifecycle/properties/public API/internals - cover_toggle_mode.py: public overrides → state handlers → relay cmds - dual_motor.py: move can_calibrate_tilt after __init__ - test_websocket_api.py: group unwraps at top, classes match source order - cover.py: group constants, move _register_services after setup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pulse/toggle send methods now return immediately after the ON edge, deferring the sleep+turn_off to a background task. This fixes calibration overhead measuring as zero and improves position tracking accuracy. Also adds _send_tilt_open/close/stop overrides to PulseModeCover and ToggleModeCover so tilt buttons use the same pulse/toggle pattern as travel buttons. Fixes ToggleModeCover.async_stop_cover missing the _send_tilt_stop call when tilt restore/pre-step was active. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sections Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ration _async_handle_command dispatches to _send_open/_send_close but never set _last_command. The higher-level methods (async_open/close_cover) set it, but calibration calls _async_handle_command directly. Toggle mode's _send_stop needs _last_command to know which button to re-pulse, so it silently skipped every stop during calibration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… switch for pulse mode Replace the two-field device_type + input_mode system with a single control_mode field (wrapped/switch/pulse/toggle). No migration needed as this code hasn't been released yet. Pulse mode now requires a stop switch entity — the entity shows as unavailable in HA and the calibration tab is disabled until configured. Tilt stop switch is also required for pulse mode with dual_motor tilt. Tilt entity visibility now follows control mode: - wrapped: no tilt entity pickers - switch/toggle: tilt open + close - pulse: tilt open + close + stop (required) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change min=0 to min=0.1 for travel_time_close, travel_time_open, tilt_time_close, and tilt_time_open. Matches the YAML schema's cv.positive_float behavior and prevents division-by-zero in the position calculator and calibration logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix _async_move_tilt_to_endpoint and set_tilt_position to use tilt motor relays instead of main motor in dual_motor mode - Add tilt motor stops at intermediate cleanup points for safety - Unify _has_tilt_motor() to check tilt_strategy.uses_tilt_motor consistently between base and wrapped classes - Map legacy travel_moves_with_tilt YAML config to inline tilt mode - Fix frontend auto-save entity_id override order - Add assertion for _start_pending_travel command narrowing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Declare host class attributes and methods under TYPE_CHECKING so pyright can resolve them without runtime circular imports. Adds assertions for str | None narrowing where values are guaranteed set by callers. Resolves all 39 reportAttributeAccessIssue pyright errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HACS rejects the non-standard "card" key in strings.json. Move all card UI translations into the JS bundle and look up locale via hass.language. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The pytest_homeassistant_custom_component verify_cleanup fixture detects pending _complete_pulse() tasks after tests that intentionally skip draining. Add _cancel_tasks() cleanup to the 6 affected tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ON→OFF transitions are just relay releases and should be ignored. Only the rising edge (OFF→ON) indicates a real button press. Applies to both travel and tilt external state handlers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover services (open/close/stop) now behave identically regardless of control mode. No special-casing where same-direction = stop in toggle mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pressing open/close when tracker already shows the endpoint still sends the relay command plus run-on time. This allows physical covers that are out of sync to resync. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tilt calibration now derives step size from num_steps (3 steps → 20% each) instead of hardcoding 10%. Travel calibration (8 steps → 10%) is unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses pytest-homeassistant-custom-component for a real HA instance. input_boolean entities simulate physical switches. MockConfigEntry loads the integration through the real config entry lifecycle. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests open/track/auto-stop, stop-during-movement, set_position, and endpoint resync through real HA service calls, event bus, and switch state changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Feedback: echo filtering and external button press detection - Modes: toggle stop-before-reverse and pulse relay pulsing - Lifecycle: correct entity features from config, position restore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests tilt-before-travel pre-step and tilt rejection when cover is not at an endpoint position. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidates the homeassistant component setup (turn_on/turn_off services) into the shared fixture so individual tests don't need to do it separately. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
No worries. I'm going to keep pushing changes to this branch. |
test_close_while_closing_reissues and test_open_while_opening_reissues called async_close/open_cover which creates _complete_pulse tasks, but never cancelled them before the test ended, causing "Task was destroyed but it is pending" errors in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests that leave the cover moving (opening/closing) caused "Lingering timer" CI failures because the auto_updater_hook (0.1s interval) was still scheduled when the test ended. Fix: setup_cover fixture now yields and unloads the entry on teardown. Tests creating their own entries also unload them at the end. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tFound - Add stop_auto_updater() to async_will_remove_from_hass() so the 0.1s interval timer is cancelled when the entity is removed (fixes lingering timer errors in test teardown) - Force homeassistant component setup in test fixture by clearing pre-loaded state, ensuring turn_on/turn_off services are registered (fixes ServiceNotFound on CI Python 3.13) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HA 2026.2.3 requires Python >=3.13.2, so 3.12 installs an incompatible version. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrapped covers produce 2 state transitions on direction change (e.g. closing→open→opening). Mark expected transition count correctly and move same-state check before echo filter so attribute-only updates don't consume echo counts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move step_count increment to before the step movement so the step number appears immediately when each step begins. Show "Final step" during the continuous phase of startup delay calibration. Also restructure the calibration active box layout (name, step, buttons on separate lines). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Have you had a moment to look at this? thanks |
Sese-Schneider
left a comment
There was a problem hiding this comment.
@clintongormley thank you so much for the complete rewrite! This will be such a game-changer.
The code does generally LGTM, however its impossible for me to spot bugs in such a large rewrite.
After addressing my changes, I would like to release this as 4.0.0-beta.1 so the community can give feedback on this!
Can you please let me know all the issues/feature-requests this also fixes so we can close them with the PR?
Thanks again!
| entity_id: | ||
| example: cover.blinds | ||
| position: | ||
| tilt_position: |
Co-authored-by: Sebastian Schneider <sese.tailor@gmx.net>
Co-authored-by: Sebastian Schneider <sese.tailor@gmx.net>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a ha-version matrix so the test suite runs against both the current stable release and the latest dev/beta pre-release of pytest-homeassistant-custom-component. The dev job is continue-on-error so failures are an early warning without blocking merges. Also switches all pip calls to python -m pip for interpreter consistency, and scopes --pre to pytest-homeassistant-custom-component only so that pytest/pytest-asyncio stay on stable releases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
thanks for the review and approval @Sese-Schneider !
You didn't review every line??? Shock! I tried to do it in smaller steps but in the end it was pretty much a rewrite where lots of moving parts have to be added together. Thanks for the trust I'm sure there will be plenty of bugs that will come out of the woodwork as soon as this is released.
I've marked all the issues to be closed when this is merged, and I've deleted the docs/plans/. I've also added a commit to test against stable as well as dev.
And to you :) I think we're good to go! |
|
@tykarol have you had a chance to try out the 4.0 beta release? I'm keen to get your feedback on how your blinds work with the tilt mechanism |
|
@clintongormley no, I have this on the different hardware and not connected to the real devices atm. I will try to find some time to try it on HA UI with the relays at lest. |
Summary
Major rewrite that transforms Cover Time Based from a YAML-only integration into a modern UI-first Home Assistant helper with comprehensive configuration, calibration, and monitoring capabilities.
Architecture
control_modesetting replacesdevice_type/input_mode— supports wrapped, switch, pulse, and toggle modesCoverTimeBasedbase class withSwitchModeCover,PulseModeCover,ToggleModeCover, andWrappedCoverTimeBasedsubclassesxknxdependency with a local HA-convention copy (zero external dependencies)UI & Configuration
Tilt Strategies
State Monitoring & Position Tracking
Test plan
python -m pytest tests/ -v— 667 tests passingpython -m pytest tests/ --cov— 99.95% coverageruff check . && ruff format --check .— cleanpyright— 0 errorspylint— 9.84/10🤖 Generated with Claude Code
Closes #53
Closes #47
Closes #43
Closes #42
Closes #39
Closes #29
Closes #31
Closes #14
Closes #6
Closes #2