Skip to main content
PumpLink is a dispatch system. It’s not a dashboard, and it’s not pin binding. Commands are authenticated, sessions are timed and tracked, and everything that happens is logged and attributed to whoever caused it. Your firmware decides what to do with "on" — which relay closes, what interlocks apply, what the pump actually does. PumpLink decides who’s allowed to send it, for how long, and keeps the record.

The division of labor

The device is deliberately dumb about policy. It never receives a duration, a quota, or a user identity — it responds to "on" and "off" and acks both. Every decision about permission and time lives server-side, which is what makes the record trustworthy. See the device contract for the full handshake.

Sessions are a first-class concept

A session isn’t a side effect of two messages. It’s an object with a lifecycle:
1

Start

A request to POST /activate is authenticated and attributed to a user. The backend publishes "on" and starts its own timer.
2

Ack

The device confirms it received the command within 10 seconds. The session row isn’t written until that ack lands — if it never does, the backend publishes "off", returns a 504, and no session is recorded at all.
3

Run

The session is live and visible. The backend holds the duration; the device is quota-blind and just runs.
4

End

The session ends for exactly one recorded reason, and the reason is stored alongside it.
5

Confirm

The device must ack "off" too. Usage is credited once the outcome is known — acked or timed out — so a slow ack doesn’t undercount actual on-time.
Every session ends for exactly one recorded reason, and the reason is stored on the row: Sessions also record who ran them, whether the run was manual or scheduled, the duration requested, and the duration actually measured. GET /activations returns that history filtered by user, device, activation type, reason, or date range.
Quota is enforced before dispatch, not during a run. Each device has its own rolling 24-hour budget. The full requested duration is reserved up front, and if it doesn’t fit, the activation is rejected with a 429 and the device is never commanded. A running session is never cut short by quota — so a request either fits or it doesn’t, and nobody gets a pump that stops halfway through.
A device that doesn’t confirm "off" is recorded as UNKNOWN, not OFF. PumpLink won’t assume a device stopped just because it went quiet, and it refuses to re-activate a device in that state. It clears when the device publishes its real state on device/{id}/status, or when an admin physically checks the pump and calls POST /device/:id/confirm-off. An honest “I don’t know” is more useful than a wrong “off”.
Most comparison pages are slanted. This one tries not to be, because you’ll find out either way in about twenty minutes.

When Home Assistant is the better choice

Home Assistant is genuinely better than PumpLink in most of the situations people evaluate us for:
  • You already run it. A working HA instance is a large sunk investment in exactly the thing PumpLink would replace. Don’t tear it out.
  • You’re automating one home. HA is built for this and does it well.
  • You want local control. HA runs entirely on your hardware with no cloud dependency. PumpLink does not.
  • You need sensor-driven automation. Soil moisture triggers, tank levels, temperature thresholds, and conditional logic across devices — HA has a mature rule engine. PumpLink has none.
  • You want the integration ecosystem. Thousands of integrations, Zigbee and Z-Wave, an add-on store, and a large community. Nothing we could build in a year comes close.
Most people reading this page should probably use Home Assistant. PumpLink is narrower on purpose. It’s a better fit when:
  • There’s no server to run. A cabin, a farm, a remote pump site — nowhere to put a Pi, and nobody on-site to maintain it when it stops booting. PumpLink has no home server to keep alive.
  • Multiple people share the hardware. Each person gets their own login, an admin grants or revokes access per device, and every activation is attributed to whoever caused it. Not one shared password taped to the pump house.
  • You’re managing devices across locations. Not one home — many sites, from one place.
  • Accountability matters more than flexibility. When the question “who ran the pump for forty minutes on Tuesday” needs a real answer, the record has to be a first-class feature and not a log file you hope was enabled.
PumpLink also schedules runs itself — one-shot and recurring — and a scheduled run produces the same session record as a manual one, attributed to the person who created the schedule. That’s time-based automation, not sensor-based; the distinction matters and the next section is about it. Scheduling requires a paid plan, and the free tier is limited to one device. That’s the whole pitch. If none of those describe you, use Home Assistant.

The one thing HA handles poorly

Timed runs with a clean interrupt. There’s a recurring pattern in the r/homeassistant community: someone builds a timed pump run as an HA script — turn on, delay, turn off — and then discovers they can’t cleanly interrupt the wait. Stopping the script mid-delay means killing the script, and the turn-off step goes with it. You can work around it, and experienced HA users do: helper booleans, timer entities, wait_for_trigger instead of delay, a companion automation to catch the stop. It’s assemblable. It’s just assembled. Two things fall out of that:
  1. It’s fragile. The session lives across a script, a timer helper, and an automation or two. Any one of them failing leaves a pump running or a state flag stuck.
  2. Nothing records why it ended. You get state history, not session history. There’s no single record saying this run started at 14:02, was started by this person, ran for eleven minutes, and ended because an operator stopped it early.
In PumpLink, that’s not something you assemble. Start, duration, ack, auto-stop, force-stop, and end reason are all one object, recorded together. This is the design difference, and it’s most of the reason PumpLink exists. Naming these matters more than anything above it. If one of them is a dealbreaker, you should find out now, not after you’ve wired a relay.

No sensor-driven automation

There’s no rule engine and no conditions. PumpLink can run a pump at 6am; it can’t run one because a moisture reading dropped. Sensor logic lives in your firmware.

No telemetry ingestion

PumpLink doesn’t collect, store, or chart sensor readings. The status topic reports device state, not measurements.

No dashboard widgets or pin binding

You don’t drag a toggle onto a canvas and bind it to GPIO 26. The dashboard manages devices, users, and sessions.

No local-only operation

It’s cloud by design. If the device can’t reach mqtt.pumplink.dev, it can’t be commanded. There’s no LAN fallback.
Two more limits worth knowing before you start: the free tier covers one device, and scheduling requires a paid plan. Manual activation, quota, sessions, and the full activation log are on every tier. It also requires hardware you’re willing to build. Today that means an ESP32 you wire and flash yourself — see the ESP32 quickstart. Any MQTT-capable device that implements the contract works, but there’s no plug-in appliance to buy.
The cloud dependency is the real one to think about. A remote site with unreliable connectivity gets no commands during an outage, and PumpLink can’t reach a device to stop something it already started. Your firmware has to fail safe on its own — see what happens when the connection drops for what that means concretely.

What happens when the connection drops

Losing the broker is two different failure modes, and they are not equally serious.

Unreachable at activation

This one fails closed, and it’s the benign case. If the device isn’t connected when someone hits activate, the backend publishes "on" and then waits. No ack arrives within 10 seconds, so it aborts the session, publishes "off", and returns a 504 to the caller. Nothing is recorded as running, no quota is consumed, and the pump was never energized. The user sees a failure immediately rather than a session that silently did nothing.

Disconnected mid-session

This one is the scary one, and it deserves a straight answer: PumpLink cannot stop your pump if it can’t reach it. The broker detects the lost session — clean disconnect, keepalive timeout, or dropped TCP — and reports it to the backend. Two separate things then happen, and keeping them separate is the point:
  • Reachability flips to unreachable immediately. Your clients are told the device is off the network.
  • State is not touched by that signal. The backend force-ends the session, publishes "off" into the void, gets no ack, and records the device as UNKNOWN.
That’s the honest outcome. The session record is closed and the reason is device_broker_disconnect, but the relay is whatever it was, and nothing server-side can change that. If the pump was running, it is still running.
A device that drops while idle is a different case, and PumpLink deliberately leaves its state alone. Losing a broker session says nothing about whether a pump is spinning, so a device everyone agreed was OFF stays OFF and can be activated the moment it’s back. Only a session that was actually running resolves to UNKNOWN.
Everything PumpLink does here is bookkeeping. The stop has to come from your firmware.

What fail-safe firmware actually looks like

Four things, none of which depend on the network:
  • A local max-runtime cutoff. Keep an independent timer in firmware that opens the relay after a fixed maximum, regardless of MQTT. Set it comfortably above your longest legitimate session but below the point where a runaway does damage. This is the single most important one — it’s the only thing standing between a dropped connection and an unbounded run.
  • Relay opens on connection loss. If the MQTT client reports it’s no longer connected while a run is active, treat that as a stop rather than waiting for a command that can’t arrive.
  • Relay defaults to open on boot. Drive the pin low in setup() before anything else, so a brownout or reboot never comes back up with the pump energized. The ESP32 quickstart sketch already does this.
  • A hardware watchdog. A wedged firmware loop holds the relay in whatever state it was in. A watchdog reboots into the safe default instead.
Use a clean MQTT session. The backend doesn’t enforce this either way, but a persistent session lets the broker queue commands and replay them on reconnect — so a device that reconnects an hour later could receive an "on" from a session the backend already aborted and closed. The quickstart firmware connects with a clean session by default.
Broker-level disconnect detection means PumpLink always knows it lost the device, and never shows a stale ON as if it were current. Knowing is not the same as controlling. Build the cutoff into the firmware. If you’re running a single home setup on Home Assistant and it’s working, stay there. You’d be trading a mature rule engine and local control for a session log you probably don’t need at one site. Use PumpLink when there’s no server to run, when several people share the same hardware, or when you need to know who did what. Those are narrow conditions. They’re also the ones where nothing else fits well.

ESP32 quickstart

Wire and flash a device in under 30 minutes.

Device contract

The MQTT topics and handshake any device must implement.