You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi sebr, first off the v4.1.0 work is great. PR #414's next_watering was a clean piece of architecture, and the data path it relies on made it natural to extend. This issue proposes building on top of that, and I'd appreciate your read before I open a PR.
Worth noting up front: the previously-shipped bhyve_next_watering.py python_script (retired in #400) was clearly tracking the same itch. It had a working Smart Watering path, with TODO placeholders for the manual programs. The work here picks up where that left off, fills in the manual program math, and surfaces it as first-class entities rather than a python_script.
The use case
I wanted a sensor that tells me when each individual zone is going to fire so I can move my lawn furniture out of the way before the wrong sprinkler hits it. I'm a Smart Watering user, so the controller's reported start time would have been good enough for my own use. But once I started exploring the data path, I wanted the per-zone resolution to be correct for everyone, including users on manual programs and one-off zone test runs from the Orbit app.
The gap
What I wanted was per-zone resolution: "when does Front - Street fire?" Not "when does the controller next start?" The two answers can differ by 30+ minutes for a mid-sequence zone, and the data to compute the per-zone time is already at program.watering_plan[*].run_times in the coordinator state.
Manual programs are similar. Without a per-zone sensor, there's no predictive valve open time at all for any zone past the first. The user is left to figure out the offset themselves from the program's start time and the run durations.
What we built
Per-zone calculation across all four manual program types plus Smart Watering:
Smart Watering (program e): zone's open time = cycle start + cumulative duration of preceding stations from watering_plan[*].run_times.
Manual days program: combines start_times[0] with the next configured weekday from frequency.days (B-hyve numbering: 0=Sunday, 1=Monday).
Manual interval program: advances from frequency.interval_start_time by frequency.interval days until the next future occurrence.
Manual odd / even programs: matches the next calendar date with the configured parity.
For multi-zone manual programs, the same per-station offset applies. Zones with multiple programs assigned return the earliest future run, with deterministic tie-break on program letter so the result doesn't depend on dict iteration order.
What we noticed during testing
While verifying the per-zone sensor on dev HA, I noticed something that surprised me: when I toggled a manual program switch directly in HA (visible in the video below) or within the Orbit App, the per-zone sensors updated within a second. But next_watering stayed stale until the next coordinator polling cycle or an integration reload. I have been using an automation to reload the integration every hour to make sure I had the latest start datetime.
The integration's websocket already updates coordinator.data["programs"] immediately on program_changed events, which is why the per-zone sensors are realtime. next_watering reads from device.status.next_start_time, which is on the polled refresh cadence. Different data path, different freshness.
Rather than touching next_watering, we built a sibling sensor that aggregates the new per-zone calculations and inherits their realtime freshness, coexisting with PR #414's sensor.
zone_bhyve.MP4
In the first part of the video, I'm toggling interval, odd, and even program switches directly in HA. The per-zone sensors and the new device-level summary update immediately, while next_watering stays stale. The toggles round-trip through Orbit's API and come back via websocket, so the same path that catches Orbit-app changes also catches HA-side changes.
In the last few seconds, I trigger a manual zone start from the Orbit app on Front - West. Watch Zone Now Watering flip from "All Valves Idle" to "Front - West" and the pump icon switch. The same data path catches both scheduled runs and ad-hoc test starts, which is what makes Zone Now Watering useful for automations.
That insight pulled in two more entities, both useful for automations:
Zone Now Watering (visible toggling in the video): friendly name of the zone currently watering, sourced from device.status.watering_status. Returns idle (translated to "All Valves Idle") when no zone is running. Catches scheduled runs and ad-hoc test starts.
Zone Next Watering Name: companion to Zone Next Watering, exposes the zone name as state. Lets users build single-tile dashboard views combining "Front - Street, in 2 minutes" without template logic.
The four sensors
Sensor
Scope
State
Zone Watering: {zone_name}
Per zone
TIMESTAMP, when this zone next fires
Zone Next Watering
Per device
TIMESTAMP, earliest future watering across all zones
Zone Next Watering Name
Per device
STRING, zone name responsible for Zone Next Watering
Zone Now Watering
Per device
STRING, currently watering zone or idle
All four are disabled by default. Users opt in via the entity registry, which avoids dropping six-plus new entities on existing installs.
Scope
Touches sensor.py, the two translation files, tests/test_sensor.py, and README.md. No changes to coordinator.py, valve.py, switch.py, config_flow.py, or the API client. Tests cover all five frequency types, multi-program priority, edge cases, entity registration, and the dynamic icon.
Side notes
Program letter behavior. B-hyve devices support up to four manual programs (A through D) plus one Smart program (E). The Orbit app surfaces the manual letters as user-facing labels in the Program tab. While testing, a manual program landed at letter d after a delete-and-recreate cycle. Orbit assigns letters by lowest available slot and recycles them. The README's existing line that "Slot d does not have a known use at this stage" is slightly outdated; slot d is the fourth user-fillable manual program slot. The PR currently includes a small one-paragraph correction. Happy to keep bundled or split, your preference.
Smart classification. The orchestrator uses program.is_smart_program is True as the canonical Smart marker (matching valve.py). Doesn't infer Smart from program == "e", since the letter is reusable.
Open questions
Does the per-zone use case land for you? We were able to get fairly close predictions (as actual smart watering times can be last minute) to automate against.
Are four new sensor types coexisting with 'next_watering' OK, or would you prefer different scoping? Curious whether you'd be open to 'next_watering' itself adopting the realtime data path internally, while keeping its current name and entity_id stable for backward compatibility?
Status
Built and verified on dev HA against a 6-zone Brookdale Timer. Ruff clean, pytest passing. Branch is local; not pushed to a fork yet, waiting on your read before opening any PR.
Hi sebr, first off the v4.1.0 work is great. PR #414's
next_wateringwas a clean piece of architecture, and the data path it relies on made it natural to extend. This issue proposes building on top of that, and I'd appreciate your read before I open a PR.Worth noting up front: the previously-shipped
bhyve_next_watering.pypython_script (retired in #400) was clearly tracking the same itch. It had a working Smart Watering path, with TODO placeholders for the manual programs. The work here picks up where that left off, fills in the manual program math, and surfaces it as first-class entities rather than a python_script.The use case
I wanted a sensor that tells me when each individual zone is going to fire so I can move my lawn furniture out of the way before the wrong sprinkler hits it. I'm a Smart Watering user, so the controller's reported start time would have been good enough for my own use. But once I started exploring the data path, I wanted the per-zone resolution to be correct for everyone, including users on manual programs and one-off zone test runs from the Orbit app.
The gap
What I wanted was per-zone resolution: "when does Front - Street fire?" Not "when does the controller next start?" The two answers can differ by 30+ minutes for a mid-sequence zone, and the data to compute the per-zone time is already at
program.watering_plan[*].run_timesin the coordinator state.Manual programs are similar. Without a per-zone sensor, there's no predictive valve open time at all for any zone past the first. The user is left to figure out the offset themselves from the program's start time and the run durations.
What we built
Per-zone calculation across all four manual program types plus Smart Watering:
e): zone's open time = cycle start + cumulative duration of preceding stations fromwatering_plan[*].run_times.daysprogram: combinesstart_times[0]with the next configured weekday fromfrequency.days(B-hyve numbering: 0=Sunday, 1=Monday).intervalprogram: advances fromfrequency.interval_start_timebyfrequency.intervaldays until the next future occurrence.odd/evenprograms: matches the next calendar date with the configured parity.For multi-zone manual programs, the same per-station offset applies. Zones with multiple programs assigned return the earliest future run, with deterministic tie-break on program letter so the result doesn't depend on dict iteration order.
What we noticed during testing
While verifying the per-zone sensor on dev HA, I noticed something that surprised me: when I toggled a manual program switch directly in HA (visible in the video below) or within the Orbit App, the per-zone sensors updated within a second. But
next_wateringstayed stale until the next coordinator polling cycle or an integration reload. I have been using an automation to reload the integration every hour to make sure I had the latest start datetime.The integration's websocket already updates
coordinator.data["programs"]immediately onprogram_changedevents, which is why the per-zone sensors are realtime.next_wateringreads fromdevice.status.next_start_time, which is on the polled refresh cadence. Different data path, different freshness.Rather than touching
next_watering, we built a sibling sensor that aggregates the new per-zone calculations and inherits their realtime freshness, coexisting with PR #414's sensor.zone_bhyve.MP4
In the first part of the video, I'm toggling interval, odd, and even program switches directly in HA. The per-zone sensors and the new device-level summary update immediately, while
next_wateringstays stale. The toggles round-trip through Orbit's API and come back via websocket, so the same path that catches Orbit-app changes also catches HA-side changes.In the last few seconds, I trigger a manual zone start from the Orbit app on Front - West. Watch
Zone Now Wateringflip from "All Valves Idle" to "Front - West" and the pump icon switch. The same data path catches both scheduled runs and ad-hoc test starts, which is what makesZone Now Wateringuseful for automations.That insight pulled in two more entities, both useful for automations:
Zone Now Watering(visible toggling in the video): friendly name of the zone currently watering, sourced fromdevice.status.watering_status. Returnsidle(translated to "All Valves Idle") when no zone is running. Catches scheduled runs and ad-hoc test starts.Zone Next Watering Name: companion toZone Next Watering, exposes the zone name as state. Lets users build single-tile dashboard views combining "Front - Street, in 2 minutes" without template logic.The four sensors
Zone Watering: {zone_name}Zone Next WateringZone Next Watering NameZone Next WateringZone Now WateringidleAll four are disabled by default. Users opt in via the entity registry, which avoids dropping six-plus new entities on existing installs.
Scope
Touches
sensor.py, the two translation files,tests/test_sensor.py, andREADME.md. No changes tocoordinator.py,valve.py,switch.py,config_flow.py, or the API client. Tests cover all five frequency types, multi-program priority, edge cases, entity registration, and the dynamic icon.Side notes
Program letter behavior. B-hyve devices support up to four manual programs (A through D) plus one Smart program (E). The Orbit app surfaces the manual letters as user-facing labels in the Program tab. While testing, a manual program landed at letter
dafter a delete-and-recreate cycle. Orbit assigns letters by lowest available slot and recycles them. The README's existing line that "Slotddoes not have a known use at this stage" is slightly outdated; slotdis the fourth user-fillable manual program slot. The PR currently includes a small one-paragraph correction. Happy to keep bundled or split, your preference.Smart classification. The orchestrator uses
program.is_smart_program is Trueas the canonical Smart marker (matchingvalve.py). Doesn't infer Smart fromprogram == "e", since the letter is reusable.Open questions
Status
Built and verified on dev HA against a 6-zone Brookdale Timer. Ruff clean, pytest passing. Branch is local; not pushed to a fork yet, waiting on your read before opening any PR.
Thanks for taking a look. 🕵️♂️
~David