Skip to content

Fix: diurnal constraints at startup + Alarmo child area propagation - #112

Open
tabascoz wants to merge 4 commits into
rhizomatics:mainfrom
tabascoz:fix/diurnal-constraints-startup
Open

Fix: diurnal constraints at startup + Alarmo child area propagation#112
tabascoz wants to merge 4 commits into
rhizomatics:mainfrom
tabascoz:fix/diurnal-constraints-startup

Conversation

@tabascoz

@tabascoz tabascoz commented Sep 3, 2026

Copy link
Copy Markdown

Two bugs fixed:

  1. Diurnal constraints ignored at startup / reload
    Symptom: Reloading the integration at 9:30 PM (before sunset_earliest=23:00) would immediately arm to armed_night, bypassing the configured earliest constraint.

Root cause: reset_armed_state(source=STARTUP) in initialize() armed the panel without checking sunset_earliest / sunrise_earliest.

Fix: Added constraint checks before the startup reset. If it's night and sunset_earliest hasn't been reached yet, the arm is deferred to that time. Same for sunrise. Two helpers (_has_sunset_passed_today / _has_sunrise_occurred_today) disambiguate "evening, waiting for earliest" from "pre-dawn, sunset was yesterday — arm now".

Also fixed _parse_time not converting YAML string fallbacks to datetime.time, and corrected two tests that set nonexistent autoarmer.sunrise_cutoff.

  1. Alarmo child areas not arming
    Symptom: When AutoArm is configured to manage an Alarmo master panel, only the master entity changed state — child areas remained in their previous state.

Root cause: arm() used hass.states.async_set() to change the alarm state, which bypasses Alarmo's master entity entirely. Alarmo's async_handle_arm_request → async_arm() (which iterates all child areas and calls item.async_arm() on each) was never invoked.

Fix: When Alarmo is detected ("alarmo" in hass.data), AutoArm calls the standard alarm_control_panel.alarm_arm_{mode} service instead. This triggers Alarmo's full internal arm flow including child area dispatch. A _arming_via_service flag prevents on_panel_change from misinterpreting the resulting state change as an external intervention. For non-Alarmo platforms (manual, built-in HA), the direct hass.states.async_set() is preserved.

tabascoz and others added 4 commits September 3, 2026 13:07
…import

Bug rhizomatics#1: reset_armed_state(source=startup) in initialize() immediately armed
the panel without checking sunset_earliest/sunrise_earliest constraints.
When the integration reloads at night before sunset_earliest (e.g. 9:30pm
with earliest=11pm), it now defers the arm to the earliest time.

Bug rhizomatics#2: _parse_time() returned raw YAML string fallback instead of
converting it to datetime.time, causing '<' comparison crashes.

Bug rhizomatics#3: Two tests set autoarmer.sunrise_cutoff (nonexistent attribute)
instead of sunrise_earliest — the cutoff logic was never actually tested.

Added tests: on_sunset with earliest constraint (deferred + already-met),
and startup deferral to sunset_earliest.
When AutoArm sets the alarm state via hass.states.async_set(), Alarmo's
master entity never sees the arm command. Its master->child area
propagation (async_arm iterating all areas) is skipped, so child areas
stay in their previous state.

Now, when alarmo is detected in hass.data, AutoArm calls the standard
alarm_control_panel.alarm_arm_{mode} service instead. This triggers
Alarmo's full arm flow including child area dispatch. For non-Alarmo
platforms (manual, built-in), the direct state set is preserved.

A _arming_via_service flag guards against the feedback loop where
on_panel_change would misinterpret our own service-triggered state
change as an external intervention.
…eam/main

Two tests broke after rebasing because the defer logic in initialize()
is sensitive to the wall-clock hour:

- test_startup_defers_to_sunset_earliest: _has_sunset_passed_today
  returns False at 5 AM (hour < 12). Mock it to return True so the
  defer branch is taken regardless of the actual clock hour.

- test_arm_fires_autoarming_event: sunrise_earliest is 06:30:00 but
  the test runs at 5:26 AM, so startup defers. With the panel still
  disarmed, two occupancy changes fire (one per person) instead of
  one. Set the initial panel state to armed_home to match the test's
  expected event (armed_home → armed_away).
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