Skip to content

State and keys

GET/api-gateway/v2/iot-shadow/device/{deviceId}/state

Returns the most recent merged state without contacting the controller.

{
"status": "SUCCESS",
"data": {
"v2_capable": true,
"state": {
"frame_version": 1,
"counter": 327685,
"seen_at": 1758400000,
"telemetry_at": 1758400000,
"config_at": 1758399000,
"telemetry": { … },
"config": { … },
"aux_timers": []
}
}
}
Field Meaning
v2_capable A verified frame arrived in the last 10 minutes
seen_at, telemetry_at, config_at Unix seconds of the newest frame of any kind, of telemetry, and of configuration
counter The controller’s envelope counter on the newest frame
telemetry Live readings and equipment modes. Omitted until the first telemetry frame
config Numeric configuration and equipment types. Omitted until the first configuration frame
aux_timers Running auto-off countdowns, [] when none, null before the first telemetry frame

A frame replaces only the sections it carries, so telemetry and config can have different timestamps.

{
"flags": { "heater": false, "solar": false, "spa_heater": false, "ph_switch": false, "orp_switch": true,
"chem_stable": true, "is_naked": false, "pump_calib": true, "need_sen_cfg": false, "has_no_aux": false,
"exp1_conn": true, "exp2_conn": false, "backwash": false, "dry_run": 3, "flow_ok": true },
"water_temp": 26.5,
"ambient_temp": 24.5,
"ph": 7.4,
"orp": 652,
"tds": 3368,
"flow": 180,
"pressure": 12,
"prime_press": 0,
"pump_speed": 60,
"pump_power": 420,
"pump_current": 2652,
"drive_temp": 38,
"main_pump": 99,
"san": { "salt_chl": 3, "ph_doser": 1, "mineral_chl": 0, "liquid_chl": 3, "naked_ion": 3, "ozone": 3, "uv": 3 },
"cost": [0.12, 35, 0.08, 14.2],
"has_gap": true,
"flags2": { "pump_cooling": false, "pump_priming": false, "wifi_up": true, "eth_up": false, "mqtt_up": true,
"internet_ok": true, "has_spa": false, "exp3_conn": false, "exp4_conn": false },
"aux_count": 37,
"aux_mode": [99, 0, 99, 0, 0, …],
"aux_trump": [0, 2],
"pump_cooldown_remain": 0,
"pump_prime_remain": 0
}
Field Meaning
flags.heater, solar, spa_heater Heat source relays on
flags.ph_switch, orp_switch Acid and chlorine dosing outputs energised
flags.chem_stable Readings settled enough to dose on
flags.pump_calib Flow calibration done
flags.need_sen_cfg A sensor input needs configuration
flags.exp1_conn … flags2.exp4_conn Wireless expanders 1 to 4 online
flags.backwash Backwash in progress
flags.dry_run Dry-run protection state, 0 to 3. 3 is normal
flags.flow_ok Flow established
water_temp, ambient_temp °C. ambient_temp is 0 without an air sensor
ph, orp pH and mV
tds Reserved; carries a placeholder value
flow, pressure, prime_press L/min and kPa
pump_speed, pump_power, pump_current, drive_temp Percent, W, mA and °C of the main pump drive. pump_speed is 0 unless the motor is running
main_pump 0 off, 1 to 20 running under that schedule, 99 manual
san.* State of each sanitiser, 3 when not fitted
cost Savings per hour, reduction percent, current running cost, accumulated savings, in currency units
flags2.wifi_up, eth_up, mqtt_up, internet_ok Connectivity
flags2.has_spa Spa actuators configured
aux_count Number of equipment slots, 37 on current firmware
aux_mode[i] Slot i: 0 off, 1 to 20 under that schedule, 99 manual. Slot 0 is the main pump; use main_pump for its real mode
aux_trump Slots under manual override
pump_cooldown_remain, pump_prime_remain Seconds

Slot layout: 0 to 11 are outputs on the controller, 12 to 16 expander 1, 17 to 21 expander 2, 22 to 25 reserved, 26 the chlorinator’s ozone virtual slot, 27 to 31 expander 3, 32 to 36 expander 4.

{
"fw_version": "2.5.46",
"pool_size": 50,
"electricity_cost": 30,
"max_run_current": 12,
"pump_prime_time100": 240,
"pump_start_speed": 40,
"min_allow_pump_speed": 50,
"spa_pump_speed": 100,
"heater_pump_speed": 100,
"solar_pump_speed": 100,
"cleaner_pump_speed": 100,
"backwash_pressure": 150,
"flow_retry_time": 60,
"flow_retry_cycles": 3,
"min_flow": 30,
"min_pressure": 30,
"desired_water_temp": 28,
"spa_desired_temp": 38,
"cfg_flags": 2,
"heater_cooldown": 180,
"spa_heater_cooldown": 180,
"solar_hyst_temp": 5,
"solar_hyst_time": 120,
"ambient_hyst_time": 60,
"solar_collector_flush": 840,
"ph_dosing": true,
"ph_dosing_time": 60,
"desired_ph_level": 74,
"acid_drum_volume": 20000,
"remain_acid_volume": 12000,
"ph_max_dose_per_day": 10,
"ph_dose_intervals": 30,
"ph_dose_rate": 100,
"orp_controlled": true,
"liquid_dosing": false,
"desired_orp_mineral": 650,
"mineral_max_duration_preday": 0,
"desired_orp_liquid": 650,
"liquid_dose_time": 60,
"chlorine_drum_volume": 20000,
"remain_chlorine_volume": 15000,
"liquid_dose_rate": 100,
"chlsave_sp": 0,
"ozonesave_sp": 0,
"pool_light_mode": 5,
"spa_light_mode": 1,
"water_temp_offset": 0,
"ambient_temp_offset": 0,
"sched_enabled": 5,
"aux_count": 37,
"aux_type": [31, 6, 12, 0, …]
}

Values are the controller’s raw settings, unscaled: pH set points are pH × 10, ORP in mV, volumes in mL, temperatures in whole °C, speeds in percent, times in seconds. cfg_flags bits: 0 solar heater selected, 1 pool heater selected, 2 spa heater selected, 3 tropical solar mode, 4 to 5 solar mode. sched_enabled bit i is set when schedule i is enabled. aux_type[i] is the equipment type in slot i.

[ { "slot": 3, "remain_sec": 1740, "duration_sec": 3600 } ]

POST/api-gateway/v2/iot-shadow/device/{deviceId}/state/refresh

No body. Asks the controller for a full frame, waits up to 8 seconds, and returns { "state": … } with the same state document as GET …/state (without v2_capable). Both telemetry and config are present in the result. Use it when you need to be sure the data is current, for example right after the user changes something in another app; ordinary polling should use GET …/state.

GET/api-gateway/v2/iot-shadow/device/{deviceId}/envelope-key

{
"status": "SUCCESS",
"data": {
"device_id": "02_53_4D_00_00_01",
"key_hex": "00112233445566778899aabbccddeeff",
"counter": "unix_seconds"
}
}

The 16-byte key that signs everything this controller sends and accepts. Fetch it once, store it securely, and use it to talk to the controller on the LAN. The response is sent with Cache-Control: no-store. The key is unique to the controller; it cannot be used to derive any other controller’s key.

counter tells you which counter convention the cloud uses when signing outbound frames; see Envelope and keys for why that matters when several parties send actions to one controller.