Repository navigation
ESPHome Replication Plan
Goal: reproduce the device's local behavior in ESPHome, using what is documented in Device Overview, Network Protocol, and Hardware GPIO Map.
-
Cloud registration/pairing (
/device-api/v2/register, token flow) — the unit also has an offline mode, so it is not required for local operation. - Captive-portal onboarding — ESPHome's own WiFi provisioning (or Improv/dashboard import) replaces this.
- TLS cert handling — the cloud uses a public CA bundle with no pinning, so there is nothing to replicate.
-
Valve control —
switchon GPIO9/GPIO10 (see Hardware GPIO Map) driven as a two-wire output, wrapped in ascriptthat holds the valve open for the dose time, then returns to idle. The latching valve's feedback and retry is left out; the draft is a simple timed pulse. -
Dispense triggers:
- a template
buttonfor a manual dose. The front button (GPIO6, pull-up, active low) is reported as a sensor only: on the factory unit it wakes the unit and keeps it awake rather than dispensing. - Scheduling (
SCHEDULE DISPENSE) runs on the device (4 daily time slots, each with a dose count), so it keeps working with HA down. HA only edits the slots and views history. -
REMOTE DISPENSE→ just a Home Assistant service call to the same script.
- a template
-
Battery monitoring —
sensor.adcon GPIO8 (ADC1 channel 7, 11dB atten — see Hardware GPIO Map), mapped from 2200–3000 mV to a percentage, withbinary_sensorthresholds at 20% (low) and 10% (critical) and dispensing blocked below 20%. -
Bottle/float sensor — if your unit has a float, a
binary_sensoron GPIO7. -
Counters — ESPHome
globalsornumberentities for the used-dose count and the dose limit, exposed to Home Assistant, replacing the cloud dashboard's role.
The unit must keep working with Home Assistant, and even WiFi, unavailable. HA is an optional controller and history/monitoring front end. In esphome/iflo.yaml this means:
- Schedule, dose limits, counters, last-dose time and the valve failsafe live on the device and are saved to flash.
- The clock comes from SNTP (HA also sets it when connected). Without any time source after a power loss, scheduled doses are skipped until the clock is valid again.
- ESPHome's default reboot timers for "no API client" and "no WiFi" are disabled, so an HA or router outage doesn't reboot-loop the unit.
- A local web UI (digest-auth) and the fallback AP allow control and WiFi changes with no HA.
- While HA is down it only sees the persisted counters and last-dose time on reconnect, not each individual dose (an on-device event log is an open task).
A first draft lives in the repo at esphome/iflo.yaml (core local function only: timed valve dose with a hard max-open failsafe, battery %, dose counters/limits, on-device 4-slot schedule, SNTP clock, local web UI). It passes esphome config and is not tested on hardware. Deliberately left out of this first build: deep sleep/wake (always-on WiFi drains the battery, so bench/USB power only), valve feedback/retry, and lost-dispense catch-up.
Safety gate: the config reads the hardware-model strap pins (GPIO18/19/20, see Hardware GPIO Map) and refuses to open the valve unless all three read low (the simple-valve model). See Flashing and Recovery before flashing a real unit.
The unit is battery powered and mostly asleep, so the target build needs deep sleep. The factory unit wakes on a timer, the front button (GPIO6), a bottle change (GPIO13), the float switch (GPIO7) and external power (GPIO16); see Power and Sleep. Plan: wake on the timer, the button (ext1) and the bottle eject (ext0, inverted level); the float switch and external power need a ULP program or timer polling, which ESPHome lacks. Wake on those plus a timer set to the earlier of the next schedule slot and a maximum sync interval, stay awake for a short window (about 60 s) so Home Assistant can connect, and offer a "stay awake" switch. While asleep the unit still runs its schedule on its own.
The repo ships a VS Code dev container and a docker-compose.yml with ESPHome and esptool pinned, so nothing has to be installed locally except Docker. See the repo README. The container builds only; flash from the host.
- GPIO40, 39, 41, 42 are probably the LEDs and GPIO15/GPIO2 the second driver IC; probe the PCB to confirm.
- The float switch is GPIO7 and the bottle eject switch GPIO13 (see Hardware GPIO Map); both are pull-up inputs (active low) in the draft; confirm on a unit.
- The latching/feedback valve (model 3, all straps high) is not implemented in the draft.
- The dose-time units (probably tenths of a second) and the battery divider ratio are unverified; the draft exposes them as adjustable values.
- How to get wired access for the first flash (see Flashing and Recovery).