Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ If a valve stops responding, NeverDry keeps trying — six attempts, with a grow
| `sensor.<zone>_last_duration` | s | Duration of the last session |
| `sensor.<zone>_session_water` | L | Water delivered in the current session |
| `sensor.<zone>_yearly_water` | L | Cumulative water delivered this year |
| `sensor.<zone>_rain` | mm | Cumulative rain accounted for the zone |
| `sensor.<zone>_rain_yearly` | L | Rain this zone received this calendar year: the yearly rain in mm times the zone's area |
| `sensor.<zone>_kc` | — | Current crop coefficient (seasonal) |
| `sensor.<zone>_flow_rate` | L/h | Design flow rate: what the zone was built to deliver |
| `sensor.<zone>_measured_flow_rate` | L/h | What the last supervised valve test measured, kept as a series so a slow decline reads as clogging emitters |
Expand All @@ -129,7 +129,7 @@ If a valve stops responding, NeverDry keeps trying — six attempts, with a grow
| `button.<zone>_stop` | — | Stop the zone's irrigation immediately |
| `button.<zone>_reset_valve` | — | Unlock a valve that NeverDry blocked after repeated failures |

All sensors that carry a physical unit declare the proper HA `device_class` (e.g. `precipitation`, `volume_storage`, `volume_flow_rate`). Home Assistant automatically converts the displayed unit to your system preference: go to **Settings → System → General → Unit system** to switch between metric and imperial. Deficit values appear in mm or inches; volumes in litres or gallons; flow rate in L/h or gal/h; ET rate in mm/h or in/h.
All sensors that carry a physical unit declare the proper HA `device_class` (e.g. `precipitation`, `volume_storage`, `volume_flow_rate`). Home Assistant automatically converts the displayed unit to your system preference: go to **Settings → System → General → Unit system** to switch between metric and imperial. Deficit values appear in mm or inches; volumes in litres or gallons; ET rate in mm/h or in/h. Flow rates appear in L/h or gal/h: Home Assistant does not convert flow rates between unit systems, so NeverDry picks the unit from the same setting.

## Dashboard — Zone Card

Expand Down Expand Up @@ -258,15 +258,15 @@ NeverDry is configured entirely through the UI — no YAML required.
|-------|----------|---------|-------------|
| Zone name | Yes | — | Display name |
| Valve | No | — | Entity controlling the valve — `switch.*` or `valve.*` (omit for monitoring-only mode) |
| Area | Yes | — | Irrigated area [m²] |
| Area | Yes | - | Irrigated area [m², ft² in imperial] |
| System type | Yes | — | Drip / micro-sprinkler / sprinkler / manual |
| Efficiency | No | (from type) | Override distribution efficiency [0.1–1.0] |
| Plant family | No | — | Sets seasonal Kc profile |
| Custom Kc | No | — | Override Kc [0.1–2.0] — use [NeverDry Planner](https://never-dry.github.io/neverdry-planner/) to estimate it |
| Site exposure | No | Full sun, open | Sun/wind exposure preset — multiplies the Kc [0.60–1.20] |
| Custom microclimate factor | Advanced only | — | Explicit exposure factor [0.1–1.5], used when exposure is *Advanced* |
| Guard flow rate | For timer mode | - | Valve flow rate [L/h, gal/h in imperial]. Required for timer-based zones; recommended for flow-meter and volume-dosing zones too, where it drives the expected duration and the safety-timeout scaling |
| Threshold | No | 20.0 | Mode A trigger [mm] |
| Threshold | No | 20.0 | Mode A trigger [mm, in in imperial] |
| Battery sensor | No | — | Valve battery sensor — mirrored in the zone device card |
| Flow meter sensor | No | — | Either a cumulative volume counter or an instantaneous flow rate — both work, and NeverDry tells them apart by the unit. See [valves tested](docs/valve-compatibility.md) for what your model exposes. Mirrored in the zone device card |

Expand Down
2 changes: 1 addition & 1 deletion docs/VISION.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ A timer knows one thing: the clock. NeverDry works across several domains at onc
|---|---|---|---|
| **Agronomy** | Evapotranspiration, crop coefficient, the soil's water reserve, the deficit, useful rain | Which plants, and where they stand | Manual crop coefficient, custom soil, soil moisture probe, evapotranspiration method |
| **Hydraulics** | Volume from flow and time, serial or parallel runs, the master valve or pump and its linger, shared water lines | The valve, and its flow rate (or how long you usually water) | A water network the zones draw from, a flow or line limit, a master, a water meter |
| **Electrical** (a little) | How many solenoids a board can hold open, confirmed commands, a valve stuck open | Nothing | A named board and its limit |
| **Electrical** (a little) | How many solenoids a board (transformer) can hold open, confirmed commands, a valve stuck open | Nothing | A valve coordinator the zones are counted on, and its limit |
| **Climate and place** | Outdoor, greenhouse or indoor; frost; sun exposure | Where the garden is | A second environment (a greenhouse with its own sensors), exposure, additional sensors |
| **Reliability** | Watchdog, learned latency, reachability, valve self-test | Nothing | Thresholds and timings |

Expand Down
2 changes: 1 addition & 1 deletion docs/assets/domain_model_uml.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 57 additions & 39 deletions docs/design/scheduler.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,16 @@ environments, outdoor and greenhouse, and the shared pump declined) and §8.4.7
(the automation a user built instead, read as a specification).

**Added 2026-10-10**, and not yet reflected in section 14: section 8.5 (the
electrical limit of a controller board, GH #320, held by a valve coordinator
electrical limit of a controller board (transformer), GH #320, held by a valve coordinator
rather than by the scheduler), section 8.6 (the master stays on for the
whole sequence, GH #95), section 8.7 (an optional hydraulic limit, declared
from design figures, which revises the refusal in section 8 without undoing
the removal) and section 8.8 (two entry types, Environment and water network,
the network named by each zone, which closes Q9 and revises section 8.4.6; the
the removal) and section 8.8 (three entry types, Environment, water network
and valve coordinator, the network and the coordinator named by each zone, which closes Q9 and revises section 8.4.6; the
network was first a name on the entry, and moved to the zone the same day; on
2026-10-11 the entry types by place were withdrawn, place and hand-watering
being facts of the zone).
being facts of the zone, and the valve coordinator became a third entry type, the
network's sibling).

Nothing is binding while the note is `Draft`: a working answer is still a
proposal.
Expand Down Expand Up @@ -308,7 +309,7 @@ main. Admitting a zone to run concurrently requires two conditions, evaluated
more line (sections 8.7 and 8.8).

Both are hydraulic. Passing them makes a run *admissible*, not started: the
electrical limit of the board is a separate yes, and it is not the scheduler's
electrical limit of the board (transformer) is a separate yes, and it is not the scheduler's
to give (section 8.5).

**There was a third, and removing it is the substantive change here.** It read:
Expand Down Expand Up @@ -993,9 +994,13 @@ So concurrency has two owners:
optional limit (sections 8.7 and 8.8), both declared by the person who sees
the plumbing.
- **A valve coordinator** holds the electrical half: the zones wired to one
board and `max_active_zones`, how many of them that board may hold open.
A coordinator is optional and named on the instance, so one board can drive
zones of several entries (section 8.8).
board (transformer) and `max_active_zones`, how many of them that board may
hold open.
It is an **entry of its own**, the water network's
sibling, and each zone names the coordinator its valve is counted on, so one
coordinator can hold zones of several entries (section 8.8). It holds one
number, whatever imposes it (a transformer, a controller that runs one
station at a time, a cloud service), and models no particular controller.

The scheduler asks the coordinator to start the zones it has admitted; the
coordinator starts as many as its limit allows and **answers how many it
Expand All @@ -1009,9 +1014,12 @@ Three choices keep each side from having to know too much:
- **The master is not counted.** It stays hydraulic and keeps following zone
activity. The user declares the coordinator's limit net of it: a board rated
for one station plus master has a limit of 1.
- **The default coordinator is virtual.** An installation that configures none
gets one implicit coordinator holding every valve with a limit of 1: today's
behaviour, stated instead of emerging from the handlers.
- **The default coordinator is virtual.** With no valve coordinator declared, each
Environment entry has one implicit virtual coordinator holding every one of its
valves with a limit of 1: today's behaviour, stated instead of emerging from
the handlers. A zone naming no coordinator is counted there; a removed coordinator's
zones return there, named by the form first; a zone whose coordinator is not
loaded does not start until it answers.
- **An outside open is observed, never refused.** NeverDry does not own the
relays. A valve opened by hand or by another automation while another one on
the same board is open warns, naming both, and in timer mode marks both runs as
Expand Down Expand Up @@ -1084,37 +1092,47 @@ pair and every form that touches either side calls it:

| What changes | What is checked again |
|---|---|
| A zone, added or edited | its design flow rate against the maximum flow of its network, if one is declared; the network and the coordinator it names exist |
| A zone, added or edited | its design flow rate against the maximum flow of its network, if one is declared; the network and the valve coordinator it names exist |
| A network's maximum flow, set or lowered | every zone on that network, from every entry; any whose design flow rate exceeds it is named |
| A coordinator, added, edited or removed | the zones assigned to it; a removed coordinator's zones return to the virtual default, and the form says so |
| A valve coordinator entry, added, edited or removed | the zones that name it, from every entry; a removed coordinator's zones return to their entry's virtual coordinator, and the form names them before the removal is confirmed |
| A network entry removed | the zones that name it, from every entry; they return to their entry's implicit network, and the form names them before the removal is confirmed |

A zone above the maximum flow earns a warning with a confirmation, not a refusal:
it is most often a unit error (GH #318), and occasionally a zone that will only
ever run alone. A static test checks that every step editing a zone, the limit or
a coordinator calls the shared function, since two forms with two copies of one
a valve coordinator calls the shared function, since two forms with two copies of one
rule is how a field went missing from the zone edit form (GH #234).

### 8.8 Environments and water networks: the two entry types
### 8.8 Environments, water networks and valve coordinators: the three entry types

*Decided 2026-10-10, from discussion #239 and GH #320; revised the same day: the
network became an entry of its own, named by each zone; revised 2026-10-11:
entries are not typed by place.* "One valve at a time" has turned out to be
entries are not typed by place, and the valve coordinator became an entry of its own,
the network's sibling.* "One valve at a time" has turned out to be
three questions, and each has its own object:

| Object | Question | Level |
|---|---|---|
| **Scheduler** | *When*, and in what order, for the zones of one Environment | One per Environment entry |
| **Water network** | *Is there water* for one more line, and whose turn is it? | One per network entry, on the instance; one implicit per Environment entry when none is named |
| **Valve coordinator** | *Is there power* for one more solenoid? (section 8.5) | One per board, on the instance |
| **Valve coordinator** | *Is there power* for one more solenoid? (section 8.5) | One per valve coordinator entry, on the instance; one implicit virtual coordinator per Environment entry when none is named |

A zone starts only when its network and its coordinator both say yes. The
schedulers never talk to each other.

**Two entry types: Environment and water network.** An Environment entry is a
climate: its sensors, its latitude, its scheduler and its zones. A garden is
one; a greenhouse with its own sensors is a second. The zone is always the
sub-element of an Environment, never an entry type.
**Three entry types: Environment, water network and valve coordinator.** An
Environment entry is a climate: its sensors, its latitude, its scheduler and its
zones. A garden is one; a greenhouse with its own sensors is a second. A water
network holds the water side (sections 8.6 and 8.7), a valve coordinator the electrical
side (section 8.5). The zone is always the sub-element of an Environment, never
an entry type; it names the network it draws from and the coordinator its valve is
counted on, as a rule one per transformer.

*Revised 2026-10-11.* The coordinator was first a board name typed or created
in the zone form: the shape the network had just left, and for the same reason
it moved. A coordinator holds zones of several Environments, so its limit cannot
live in any one of them, and a name typed in two forms can split one in two. As an entry
it is declared once, with its limit, and the zones pick it from a list.

**The scheduler has no type of its own**; its behaviour follows facts it already
has. The freeze interlock (section 9.2) reads its Environment's temperature, so
Expand Down Expand Up @@ -1168,7 +1186,7 @@ Coordinating a shared pump across two environments was refused because it meant
part of NeverDry checking what another had already taken, with no object to hold
that state. The water network is that object.

**Simple by default: every new field is optional.** Network, board, hydraulic
**Simple by default: every new field is optional.** Network, valve coordinator, hydraulic
limit, master and actuation all default to today's behaviour. Two installations
are the test, and neither may meet a question about any of them:

Expand All @@ -1179,26 +1197,27 @@ How that holds in the forms:

- The **first entry** on an instance asks nothing new: it is an Environment, as
every installation today is.
- **Adding an entry** from the second on opens a menu of two: Environment and
water network.
- **Adding an entry** from the second on opens a menu of three: Environment,
water network and valve coordinator.
- **The zone form** gains three optional fields in a collapsed section: the
**network**, listing the network entries on the instance (empty: the entry's
implicit network, exactly as today), the **board** (empty: the virtual
coordinator), and **watered by hand** (off: a valve, as today). With watered by
hand on, the valve, network and board fields do not apply.
implicit network, exactly as today), the **valve coordinator**, listing the coordinator
entries on the instance (empty: the entry's virtual coordinator, limit 1, exactly as
today), and **watered by hand** (off: a valve, as today). With watered by
hand on, the valve, network and coordinator fields do not apply.
- **Existing entries are Environments**, and nothing changes in them. Zones whose
placement is greenhouse or indoor keep working as now; moving them to an
Environment of their own is a choice, not a migration.

| Installation | Entries | Network | Boards |
| Installation | Entries | Network | Coordinator |
|---|---|---|---|
| A terrace with seven pots | 1 Environment | none: implicit | none (virtual) |
| Four zones on one line | 1 Environment | none: implicit | none (virtual) |
| Four zones on one line, with a pump | 1 Environment + 1 network | the network, named by the four zones | none (virtual) |
| Two Netro controllers on one main | 1 Environment | none: implicit | 2 named, limit 1 each |
| Garden and greenhouse with its own sensors, on one main | 2 Environments + 1 network | the network, named by the zones of both | as wired |
| Garden on the mains, greenhouse with its own sensors on a tank | 2 Environments | none, or a network for the tank | as wired |
| Drip from the mains beside a lawn on a tank pump | 1 Environment + 1 network | the lawn names the pump's network; the drip names none | none (virtual) |
| A terrace with seven pots | 1 Environment | none: implicit | none: virtual |
| Four zones on one line | 1 Environment | none: implicit | none: virtual |
| Four zones on one line, with a pump | 1 Environment + 1 network | the network, named by the four zones | none: virtual |
| Two Netro controllers on one main | 1 Environment + 2 coordinators | none: implicit | each zone names its controller, limit 1 each |
| Garden and greenhouse with its own sensors, one controller on one main | 2 Environments + 1 network + 1 coordinator | the network, named by the zones of both | the coordinator, named by the zones of both |
| Garden on the mains, greenhouse with its own sensors on a tank | 2 Environments | none, or a network for the tank | none: virtual, one per Environment |
| Drip from the mains beside a lawn on a tank pump | 1 Environment + 1 network | the lawn names the pump's network; the drip names none | none: virtual |
| House plants beside the garden | the garden's Environment | none: watered by hand | none |

## 9. The irrigability envelope
Expand Down Expand Up @@ -1835,16 +1854,15 @@ anything wider waits for the installation that cannot be expressed that way.
**2026-10-10: the electrical limit narrows it further** (section 8.5, GH #320).
"One valve at a time" turns out to be two rules. The hydraulic one is the
subject of this question. The electrical one has its own owner, a valve
coordinator per board, with a virtual default of one valve at a time, so it no
coordinator per transformer, with a virtual default of one valve at a time, so it no
longer needs a boundary to be chosen here. What is left for Q9 is only where
*hydraulic* permission to overlap is declared.

**2026-10-10: closed by section 8.8.** The boundary is neither the entry nor the
zone but the **water network**, an entry of its own that each zone names, shared
by every zone that draws from the same line, whichever entry it belongs to. An
Environment entry is a climate, with its own scheduler; a board is a named
coordinator. The
failure the entry boundary allowed, two entries on one main, is now expressed by
Environment entry is a climate, with its own scheduler; a valve coordinator is an
entry of its own, the network's sibling. The failure the entry boundary allowed, two entries on one main, is now expressed by
their zones naming the same network.

## 15. Consequences for the domain model
Expand Down
Loading
Loading