Skip to content

Zone holds: pause_zone action hold with constraints - #23

Draft
Cloudore wants to merge 1 commit into
cgonfer:mainfrom
Cloudore:feat/zone-hold
Draft

Zone holds: pause_zone action hold with constraints#23
Cloudore wants to merge 1 commit into
cgonfer:mainfrom
Cloudore:feat/zone-hold

Conversation

@Cloudore

@Cloudore Cloudore commented Sep 4, 2026

Copy link
Copy Markdown

Summary

  • velair.pause_zone gains action: hold: a pause reason that keeps delivering a temperature target instead of freezing the climate, through the normal resilient delivery path. Fields: temperature (or a target_temp_low/target_temp_high range), optional hvac_mode and fan_mode, constraint (absolute | raise_only | lower_only) and a short label.
  • A hold starts from the block the Default schedule or active Profile would apply now; several identified holds fold in start order, each applying its constraint. Reusing a pause_id updates the hold in place (start time kept); velair.resume_zone releases only that hold and immediately delivers the remaining holds or the schedule; timed holds expire like other reasons.
  • Precedence stays as today for everything existing: turn_off beats everything and a plain pause, including Manual adjustment, freezes the climate above any hold. Manual adjustment can start while a hold is active (Velair is still in control); the hold is delivered again when automatic control resumes. Boost remains unavailable while any reason is active.
  • The override sensor reports hold with the composed effective_temperature, the latest hold's constraint and label and every active hold; zone runtime reports hold; pause lifecycle events carry the hold fields; overview badge and diagnostics label the state; docs and all translations updated.
  • Why: this keeps presence and occupancy decisions in Home Assistant, as you described in [Feature]: Home/Away Adjustment #5, while giving automations a primitive that does not edit schedules: "hold this room at 27° while it is empty, but never below what the schedule wants" is one service call with a pause_id, exactly like the window pause. The same primitive covers vacation parking, sleep setbacks and cooking boosts.

Checks

  • Backend tests pass with python -m unittest discover -s tests (adds tests/backend/test_zone_hold.py, 18 tests: constraints, fold order, in-place update, freeze/turn-off precedence, Manual adjustment interplay, expiry, events, normalization)
  • Frontend TypeScript check passes with npx tsc --noEmit
  • Frontend build passes with npm run build (bundle committed)
  • Documentation was updated when behavior changed (docs/user/manual-control.md, docs/user/automation-events.md, docs/user/usage.md, README)

Manual testing

Draft: installed on Home Assistant 2026.8.2 with six managed climates (five Midea CCM15 zones and one LocalTuya unit) and configured; the live hold walkthrough on real hardware (hold → update → manual adjustment → resume → expiry) is scheduled next and this PR will be marked ready with the observed behaviour. Opening it now for design feedback.

🤖 Generated with Claude Code

https://claude.ai/code/session_01F2KBugsLZkL6yR7BB95pKK

A zone pause can now hold a temperature instead of freezing the climate.
`velair.pause_zone` with `action: hold` keeps delivering a target through
the normal resilient delivery path, starting from the block the schedule or
active Profile would apply now and folding every active hold in start order
with a per-hold constraint (`absolute`, `raise_only`, `lower_only`).
Holds carry optional hvac_mode, fan_mode and a short label, compose by
pause_id like other reasons, expire like other reasons and are re-delivered
when a freeze or another hold is removed.

Precedence is unchanged for existing behavior: `turn_off` beats everything
and a plain pause (including Manual adjustment) freezes the climate above
any hold. Manual adjustment can start while a hold is active because Velair
is still in control; the hold is delivered again when automatic control
resumes.

The override sensor gains a `hold` state with the composed effective target,
the latest hold's constraint and label, and every active hold; zone runtime
reports `hold`; pause lifecycle events carry the hold fields; the overview
badge and diagnostics label the state; docs and translations updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJhD4sYeWQvoikjYP2CxUr
@cgonfer

cgonfer commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Thank you for opening this as a draft and for documenting the intended behaviour in detail.

I wanted to provide feedback now, before you spend more time on the hardware walkthrough, because there is a design question that needs to be resolved before this implementation can move forward.

The Home/Away use case referenced in #5 was intentionally addressed through Profiles and Modes. Presence logic remains in Home Assistant, while Velair applies the selected climate routine. That issue was closed as completed with that design.

This proposal goes significantly further: it turns pause_zone into another persistent control authority that can compose several temperature targets and interact with schedules, Profiles, Modes, Manual adjustment and Boost. That changes the meaning of a pause and introduces a new precedence model in the scheduler.

Before adding that complexity to Velair's core, we would need to establish which specific use case is still not covered by Profiles, Modes or Boost, and agree on the expected behaviour independently of the implementation.

A preliminary code review has also found several important issues that confirm the behaviour is not yet sufficiently defined:

  • Holds are excluded from future scheduled events, so they are not recomputed when the active schedule block changes. For example, a raise_only hold calculated as 22 °C can remain at 22 °C even after the schedule changes to 24 °C.
  • A Profile or Mode change also skips zones with an active hold, so the physical target is not recomputed against the new Profile.
  • A hold can be delivered while a Profile requests turn_off, despite turn_off being described as taking precedence.
  • Hold temperatures stored in the canonical pauses collection are not included in Celsius/Fahrenheit migration.
  • Holds can be persisted while a temperature-unit migration is pending.
  • The service selectors are fixed to 5–35 with a 0.5 step, which prevents normal Fahrenheit targets and does not follow the climate entity's actual step.
  • Scalar and range targets can be combined without a defined safe meaning. Switching between them discards the previously accumulated target, so raise_only and lower_only may not honour the schedule as documented.
  • Target shape and HVAC mode compatibility are not fully validated before persistence. A hold can therefore be saved and only fail when Velair attempts to deliver it.
  • Restart, global resume and Profile transitions do not reliably reapply an active hold.
  • The documentation says that the override sensor exposes every active hold, but the sensor currently filters that collection out.

The added tests cover the main scalar examples, but not these interactions, nor range targets, Fahrenheit migration, restart recovery, Profile/Mode changes, global pause/resume or frontend presentation. The PR also currently has no GitHub check runs.

For now, I will leave the draft open for a few days in case you would like to clarify a concrete workflow that Profiles, Modes and Boost cannot represent. However, please do not invest additional time in implementation or hardware testing yet, as the product scope and architecture would need to be agreed first.

If there is a concrete workflow that the existing features cannot represent, you are very welcome to describe it here or open a Discussion with a few real examples. We can then evaluate the smallest appropriate abstraction and define its interactions, precedence, persistence, units and recovery behaviour before considering another implementation.

If no distinct unmet use case emerges, I will close the PR because the current proposal would add substantial complexity to the scheduler without a previously agreed product requirement.

Thank you again for taking the time to explore the idea and for opening it as a draft for early feedback.

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.

2 participants