Skip to main content

OpenSprinkler

GEM integrates with OpenSprinkler — an open-source Wi-Fi sprinkler controller — over its documented local HTTP API. No cloud account is required. Each physical valve station maps to a GEM zone; on/off start or stop a manual run, and the device also exposes rain-delay, global enable/disable, reboot, and program-trigger commands.

Prerequisites​

  • An OpenSprinkler v2.x or v3.x controller, OpenSprinkler Pi (OSPi), or OSBee running firmware 2.1.6 or newer.
  • Controller reachable on the LAN from the GEM server on port 80 (or the HTTP port you configured).
  • The OpenSprinkler device password. The factory default is opendoor — change it in the OpenSprinkler mobile app or web UI before deployment.

Setup​

  1. In the OpenSprinkler app, confirm the controller IP and that you can run a station manually.
  2. (Recommended) Change the device password from the default.
  3. In GEM, System → Devices → Add Device with driver opensprinkler.
  4. Set ip to the controller's LAN IP. Paste your device password into password (the driver MD5-hashes it before sending; GEM stores it encrypted).
  5. Save and reload. The driver logs opensprinkler connected: <id> <name> stations: <n> once it can read station names.
  6. Open the device's Setup tab and click Discover. It lists the controller's stations by name; tick the ones you want and click Create — each becomes a zone in Irrigation with the right address.
  7. To do it by hand instead: run get_stations and create one zone per station, with zone.address set to the zero-based station index (station S01 in the OpenSprinkler app → address 0).
  8. (Optional) Set default_duration on each zone to override the device-level default manual-run duration in seconds.

Attribute reference​

AttributeRequiredDefaultDescription
ipyes—OpenSprinkler controller LAN IP.
passwordyesopendoorPlaintext password. Driver hashes it with MD5 before sending on every request.
portno80HTTP port on the controller.
status_intervalno10000ms between /jc polls. A blank, zero, negative or non-numeric value uses the 10000 default.
default_durationno600Seconds for on when no level is provided and the zone has no default_duration.
request_timeoutno10000HTTP request timeout (ms).

Zone attributes​

AttributeDescription
default_durationPer-zone override for the manual-run duration used by on. Falls back to the device default.
stateDriver-managed: on while the station is running, off otherwise.
remaining_secondsDriver-managed: seconds left in the current manual run (0 when idle).

Zone address format​

zone.address is the 0-based station index. Station "S01" in the OpenSprinkler app is address 0, "S02" is 1, and so on. The driver does not change the GEM zone address representation — addresses are plain digits, no leading zeros required.

Commands​

CommandArgsEffect
onaddress, optional level (seconds)/cm?sid=<addr>&en=1&t=<seconds> — start a manual run. level overrides default_duration.
offaddress/cm?sid=<addr>&en=0&t=0 — stop the station.
stop_all—/cv?rsn=1 — stop every running station.
rain_delayhours (0 to clear)/cv?rd=<hours> — set rain-delay.
enable_controller—/cv?en=1 — clear global disable.
disable_controller—/cv?en=0 — globally suppress all irrigation.
reboot—/cv?rbt=1 — soft-reboot.
run_programprogram_id/mp?pid=<id> — manually start a configured program. program_id is the zero-based index into the program list get_programs returns: 0 is the first program.
get_status—/jc — controller status.
get_stations—/jn — station list with names.
get_programs—/jp — program list.
raw_requestpathGET an arbitrary path on the controller (e.g. /jo). Surrounding whitespace is trimmed, a leading / is added if missing, and the pw= query is appended automatically.

set_level is not an analog dimmer here — sprinkler valves are on/off only. To pick a duration, pass it as level to the on command (in seconds).

Status polling​

Every status_interval ms the driver fetches /jc and reads the ps (program-state) array, which holds [program_id, remaining_seconds, start_unix] per station. A station with remaining_seconds > 0 is reported as on with remaining_seconds set; everything else is off. The driver does not poll per-zone — one HTTP call covers the whole controller.

Polling starts on connect even when the controller cannot be reached yet, so a controller that comes up after GEM is picked up on a later poll without a reload. A reply that carries no ps array is treated as a failed poll and leaves zone states as they were. After three failed polls in a row the interval doubles per further failure, up to five minutes, and returns to status_interval on the first good poll.

Connection state​

The device shows connected whenever the controller answers a request, including firmware errors such as out of range. The exception is result code 2 (unauthorized): a controller that rejects the password is shown as disconnected, because GEM can neither read nor control it until the password is fixed.

Known limitations​

  • No remote authentication beyond the device password. The pw= parameter is MD5 of the password and is sent on every request. Use HTTPS via a reverse proxy or VPN if the controller is reachable beyond your LAN.
  • Result code 32 (not permitted). When the controller is in rain-delay or globally disabled, manual on requests are accepted but the station does not energize. Either clear the rain-delay or re-enable the controller first.
  • Station limit. OpenSprinkler v2/v3 supports up to 72 stations via expansion boards. Addresses beyond the configured count return result code 17 (out of range).
  • No event push. Status changes are visible on the next poll only. Lower status_interval to ~3000 ms for snappier UI feedback.

Troubleshooting​

SymptomLikely cause
unauthorized (bad password), device shown disconnectedPassword is wrong, or the controller still has the factory opendoor default while you configured something else in GEM.
out of rangeStation index is higher than the controller's configured station count. Run get_stations to confirm.
Status stuck at offController may be globally disabled or in rain-delay — run get_status and inspect en and rd.
Connection refusedOpenSprinkler is configured to a non-80 port. Set the port attribute.