v0.48.0 — Conditional schedules + rotations
v0.48.0 — Conditional schedules + rotations
The first meaningful new feature since the v0.47.x stabilisation series. Schedules and rotation steps can now declare conditions that the scheduler evaluates at fire time, so dashboards can react to what's actually happening at home.
Conditions
Each schedule and rotation step accepts a list of conditions. All conditions on a schedule or step are AND'd together; a schedule fires only when every condition passes. Three sources are supported in v1:
ha_entity— compare any Home Assistant entity's state via==/!=/>/</>=/<=/in/present_within_seconds. The evaluator subscribes to HA's state list and refreshes once per scheduler tick.time_window— wall-clock HH:MM window in the app's configured timezone, with an optional weekday mask. Overnight windows (22:00 → 06:00) are supported.sun—before_sunrise/after_sunset/is_day/is_nightwith an optional minute offset. Computed locally from yoursettings.app.latitude+longitude; no extra HA call.
When conditions fail:
- A schedule with a
fallback_page_idset routes to that page instead of skipping. The History row tags it asscheduler_fallbackso you can tell which path fired. - A schedule with no fallback skips silently — INFO-level log only, no History row, so the audit trail stays focused on actual pushes.
HA-unreachable falls open: every condition evaluates as if it passed, so dashboards keep refreshing on the existing cadence rather than pinning on a broken evaluator.
Rotation routing modes
Rotations gain a mode field with two values:
scheduled(default, backwards-compatible): the existing time-based cycle. If the current time-slot's step has failing conditions, the rotation advances to the next eligible step.priority: ignores step durations. Every tick, the rotation pushes the first step in declared order whose conditions match. A step with no conditions becomes the always-on fallback.
A new per-rotation min_hold_minutes (default 5) prevents flap when a HA sensor oscillates near a numeric threshold. Manual force-step bypasses the gate the same way it bypasses quiet hours.
Test conditions
A new POST /api/conditions/test endpoint accepts a JSON array of conditions, refreshes the HA state cache, and returns a per-condition {passed, observed, reason}. The schedule + rotation editors will surface this as a "Test" button in v0.48.1.
Editor UX
For v0.48.0 the schedule + rotation editors expose conditions as a raw JSON textarea. The full Bauhaus condition picker (entity autocomplete, operator dropdown, value type-shifting per entity domain) lands in v0.48.1. The JSON shape is the underlying contract, so anything a future picker produces will store identically.
Backwards compatibility
Every saved schedule and rotation defaults to empty conditions + mode=scheduled, so behaviour is unchanged until you opt in. The existing 34 scheduler tests stayed green without updates. 14 new tests cover the conditional axes (11 scheduler integration + 3 API endpoint).
Example
Show the energy dashboard when solar is producing, otherwise fall back to the calendar:
{
"page_id": "energy_dashboard",
"conditions": [
{"source_kind":"ha_entity","source_id":"sensor.solar_w","operator":">","value":1000}
],
"fallback_page_id": "calendar"
}Priority rotation: storm warning if active, else energy dashboard during daylight, else a clock at night:
{
"mode": "priority",
"steps": [
{"page_id":"storm_warning","dwell_minutes":10,
"conditions":[{"source_kind":"ha_entity","source_id":"binary_sensor.bom_warning","operator":"==","value":"on"}]},
{"page_id":"energy","dwell_minutes":10,
"conditions":[{"source_kind":"sun","operator":"is_day","value":{"offset_minutes":0}}]},
{"page_id":"clock","dwell_minutes":10}
]
}Upgrade: Docker users pull ghcr.io/dmellok/tesserae:0.48.0; HA add-on bumps land automatically.
941 tests, all green.