Skip to main content

Jandy AquaLink RS

Driver: jandy

Control Jandy AquaLink RS pool/spa controllers over their RS485 bus via a serial-to-Ethernet bridge (e.g. USR-TCP232). The driver speaks the raw #-command protocol, supports circuit on/off and heater setpoints, and monitors per-zone state, temperature, and setpoint. On connect it subscribes to asynchronous change pushes (#COSMSGS=1), so most updates arrive unsolicited rather than from polling.

Prerequisites

  • A Jandy AquaLink RS system wired to a serial-to-Ethernet bridge on the RS485 bus.
  • The bridge reachable on the LAN from the GEM server (typically TCP port 23).

Setup

  1. Connect the serial-to-Ethernet bridge to the Jandy AquaLink RS485 bus.
  2. In GEM, System → Devices → Add Device with driver jandy. Enter the bridge ip and port.
  3. Let the controller run for a minute so registers populate the device discovered_addresses list, then run the sync_zones command to auto-create one zone per register.
  4. (Optional) For a composite zone whose state, temperature, and setpoint live at different registers, create it by hand and set jandy_state_address / jandy_temperature_address / jandy_setpoint_address instead of relying on the zone address.

Addressing

A zone can be addressed two ways:

  1. Single register (auto-create path). Put the AquaLink register directly in the zone address field (e.g. S01, POOLTMP, POOLHTR). The driver classifies it by value/name as on/off, temperature, or setpoint. This is what sync_zones creates — one register per zone.
  2. Composite zone (hand-built). Set one or more of jandy_state_address / jandy_temperature_address / jandy_setpoint_address to route state, temperature, and setpoint to different registers. Each explicit attribute overrides just that one role; any role left unset falls back to the address field.

If a jandy_*_address attribute is set, that register wins for its role; otherwise the zone address is treated as the register.

sync_zones

sync_zones reads device.discovered_addresses and creates one zone per register, then:

  • classifies each register as on/off (water_onoff control), setpoint (water_onoff_setpoint), or temperature (water_temperature);
  • places every register under the water subsystem — it does not split pool vs. spa or create subsystems. The water subsystem must already exist; any register is skipped with a warning if it doesn't;
  • is idempotent — registers already represented by an existing zone (by address or any jandy_*_address attribute) are skipped, never modified.

Run it any time after discovered_addresses has populated. If it returns "no discovered addresses yet", let the controller run longer so registers appear on the wire, then re-run.

Attributes

Device

AttributeRequiredDefaultDescription
ipyesSerial bridge IP.
portyesSerial bridge TCP port.
polling_rateno3000Delay (ms) between individual register reads in the heartbeat sweep. RS485 is single-master — don't set below the panel's own poll cadence.
heartbeat_intervalno60000How often (ms) to re-sweep configured zone registers as a sanity check against the COSMSGS push subscription.
command_throttleno1000Minimum spacing (ms) between commands.
discovered_addresses— (read-only)Comma-separated registers seen on the wire. Auto-populated; the menu for hand-wiring zones and the source list sync_zones builds from.

Zone

AttributeDescription
jandy_state_addressAquaLink register for on/off state (composite override).
jandy_temperature_addressAquaLink register for temperature (composite override).
jandy_setpoint_addressAquaLink register for the heater/temperature setpoint (composite override).

Commands

CommandArgsEffect
getaddressQuery the value at an AquaLink register.
onaddressTurn on the circuit at the register (or the zone's state register).
offaddressTurn off the circuit at the register (or the zone's state register).
setpointaddress, setpointSet the heater/temperature setpoint (or the zone's setpoint register).
setaddress, valueSet an arbitrary register to a value.
passthroughcommandSend a raw AquaLink command string.
resetReset the AquaLink controller.
sync_zonesAuto-create one zone per discovered register (idempotent — see above).

Known behavior

  • On every (re)connect the driver sends #COSMSGS=1 so the bridge pushes register changes asynchronously; most state updates come in unsolicited. A slow heartbeat sweep re-reads each configured register as a fallback in case the push subscription silently dies.
  • Temperatures are reported as 75 F or 23 C; the driver normalizes both and converts Celsius to Fahrenheit for storage.
  • on/off/setpoint commands schedule a delayed re-poll of the matching register as a belt-and-suspenders confirmation.

Troubleshooting

SymptomCheck
jandy error response in logs / no zone updatesVerify the register address — invalid AquaLink registers are echoed back with a leading ?.
Status updates lag minutes behind realityLower polling_rate (default 3000 ms) — but watch for bus contention with the AquaLink panel itself.
Setpoint command silently does nothingConfirm a setpoint register resolves for the zone (its address classifies as a setpoint, or jandy_setpoint_address is set) and that the register supports writes — some are read-only.
no discovered addresses yet from sync_zonesLet the controller run so registers appear in discovered_addresses, then run sync_zones again.
sync_zones skips every register / creates no zonesConfirm a water subsystem exists — sync_zones places all Jandy zones under it and skips registers (logging could not resolve subsystem) when it's missing.