This document describes the WebSocket API for communicating with Pentair IntelliCenter pool controllers.
WebSocket Endpoint: ws://[IP_ADDRESS]:6680
Test Connection:
echo '{"command":"GetQuery","queryName":"GetConfiguration","arguments":"","messageID":"test-123"}' | websocat ws://[IP_ADDRESS]:6680All messages use JSON format with these structures:
{
"command": "CommandName",
"queryName": "QueryType",
"arguments": "",
"messageID": "unique-identifier"
}{
"command": "SendQuery",
"messageID": "unique-identifier",
"queryName": "QueryType",
"response": "200",
"answer": [...]
}{
"command": "Error",
"messageID": "generated-uuid",
"response": "404",
"description": "'CommandName' Unknown command!"
}The IntelliCenter WebSocket API supports both request/response queries and push notifications for real-time equipment state changes.
Standard polling uses GetParamList commands with unique messageIDs:
- Send request with
messageID - Receive response with matching
messageID - Response uses
objectListarray withobjnamandparams
UPDATED 2025-11-28: IntelliCenter sends unsolicited WriteParamList messages when equipment state changes.
Push Notification Format:
{
"command": "WriteParamList",
"messageID": "uuid-generated-by-intellicenter",
"response": "200",
"objectList": [
{
"changes": [
{
"objnam": "B1202",
"params": {
"SNAME": "Spa",
"TEMP": "80",
"SETPT": "97",
"HTMODE": "1",
"STATUS": "ON",
"OBJTYP": "BODY",
"SUBTYP": "SPA"
}
}
]
}
]
}Key Differences from Request/Response:
- Command:
WriteParamList(notGetParamList) - Structure:
objectList[].changes[].{objnam, params}(nestedchangesarray) - MessageID: UUID generated by IntelliCenter (not your request ID)
- Params: Same fields as polling responses (SNAME, STATUS, TEMP, etc.)
When reading from the WebSocket, you may receive:
- Your response -
messageIDmatches your request - Push notification -
messageIDis a UUID you didn't send
Implementation Pattern:
// Read message
var msg map[string]interface{}
conn.ReadJSON(&msg)
// Check if this is our response or a push notification
if msg["messageID"] == expectedMessageID {
// Process as response to our request
} else {
// Process as push notification (equipment change)
// Unwrap: objectList[0].changes[0].{objnam, params}
}Push notifications enable true real-time monitoring:
- No polling required - just listen for
WriteParamListmessages - Instant updates - changes arrive as they happen
- Same data format -
paramscontain the same fields as polling
Triggered By:
- Temperature setpoint changes
- Equipment on/off changes (circuits, features)
- Heater mode changes
- Body status changes
- Any user or scheduled equipment change
NOT Pushed (Requires Polling):
- Pump RPM/speed changes
- Pump wattage changes
For production monitoring:
- Initial poll - fetch complete state on startup
- Listen for push notifications - real-time updates
- Periodic poll - safety net to catch any missed updates (default 60s)
Why Hybrid is Required:
- IntelliCenter pushes body/setpoint/circuit changes instantly
- Pump RPM changes are NOT pushed - only discoverable via polling
- Polling provides a safety net for any missed push notifications
- Best of both worlds: real-time responsiveness + comprehensive coverage
What Gets Pushed vs What Requires Polling:
| Equipment Type | Push Notifications | Requires Polling |
|---|---|---|
| Body temperature/setpoint | ✓ Yes | - |
| Circuit on/off | ✓ Yes | - |
| Heater mode changes | ✓ Yes | - |
| Pump RPM changes | ✗ No | ✓ Yes |
| Feature status | ✓ Yes | - |
These commands retrieve static system configuration data.
Returns complete system setup including bodies, circuits, and features.
{
"command": "GetQuery",
"queryName": "GetConfiguration",
"arguments": "",
"messageID": "config-001"
}Response contains:
- Bodies (B#### format): Pool/Spa with temperature ranges and heater assignments
- Circuits (C#### format): Individual equipment (Pool, Spa, Lights, Cleaner, etc.)
- Features (FTR## format): Special functions (heating, jets, fountains)
Returns panel and module information with MAC addresses and firmware versions.
{
"command": "GetQuery",
"queryName": "GetHardwareDefinition",
"arguments": "",
"messageID": "hardware-001"
}Returns pump setup with speed settings for different circuits.
{
"command": "GetQuery",
"queryName": "GetPumpConfiguration",
"arguments": "",
"messageID": "pump-001"
}Returns heater setup and communication settings.
{
"command": "GetQuery",
"queryName": "GetHeaterConfiguration",
"arguments": "",
"messageID": "heater-001"
}The GetParamList command retrieves current operational data for monitoring.
Note on Parameter Lists: The examples below show both basic parameter sets for understanding key concepts and comprehensive parameter sets used in production implementations. Production systems typically request additional parameters for complete monitoring and correlation.
{
"messageID": "unique-id",
"command": "GetParamList",
"condition": "OBJTYP=OBJECTTYPE",
"objectList": [{"objnam": "INCR", "keys": ["param1", "param2"]}]
}Water Temperatures (Pool/Spa):
{
"messageID": "water-temp-001",
"command": "GetParamList",
"condition": "OBJTYP=BODY",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "TEMP", "STATUS", "SUBTYP", "HTMODE", "HTSRC", "LOTMP", "HITMP"]}]
}Response includes:
- SNAME: Display name (Pool, Spa)
- TEMP: Current water temperature (°F)
- STATUS: Body status
- SUBTYP: Body type (POOL, SPA)
- HTMODE: Heating demand status (see Heater Status section)
- HTSRC: Assigned heater object ID
- LOTMP: Low temperature setpoint (heating threshold)
- HITMP: High temperature setpoint (cooling threshold)
Air Temperature:
{
"messageID": "air-temp-001",
"command": "GetParamList",
"condition": "",
"objectList": [{"objnam": "_A135", "keys": ["SNAME", "PROBE", "STATUS", "SUBTYP"]}]
}All Temperature Sensors:
{
"messageID": "all-sensors-001",
"command": "GetParamList",
"condition": "OBJTYP=SENSE",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "PROBE", "SUBTYP"]}]
}Current Pump Data:
{
"messageID": "pump-001",
"command": "GetParamList",
"condition": "OBJTYP=PUMP",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "STATUS", "RPM", "GPM", "WATTS", "SPEED"]}]
}Response includes:
- STATUS: "10" = running, other values = stopped
- RPM: Current revolutions per minute
- GPM: Flow rate in gallons per minute
- WATTS: Power consumption (may have formatting issues)
Equipment On/Off Status:
{
"messageID": "circuit-001",
"command": "GetParamList",
"condition": "OBJTYP=CIRCUIT",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "STATUS", "SUBTYP", "OBJTYP"]}]
}Response includes:
- STATUS: "ON" or "OFF" for equipment state
- SUBTYP: Equipment type (POOL, SPA, LIGHT, GENERIC)
- Note: Returns many circuits; filter to C#### and FTR## objects for actual equipment
| Object Type | Format | Description | Example |
|---|---|---|---|
| Bodies | B#### | Pool/Spa with temperature ranges | B1101 (Pool), B1202 (Spa) |
| Circuits | C#### | Individual equipment controls | C0001 (Spa), C0003 (Pool Light) |
| Features | FTR## | Special pool features | FTR01 (Spa Heat), FTR03 (Spa Jets) |
| Heaters | H#### | Heating equipment | H0001 (UltraTemp), H0002 (Gas Heater) |
| Pumps | PMP## | Variable speed pumps | PMP01 (VS), PMP02 (pool) |
| Sensors | Various | Temperature sensors | _A135 (Air), SSS11 (Solar) |
| Circuit Groups | GRP## | Circuit group parent (OBJTYP=CIRCUIT) | GRP01 (AllOfTheLights) |
| Circuit Group Members | c#### | Individual circuits within a group | c0101, c0102 |
Bodies (OBJTYP=BODY):
- SNAME: Display name (Pool, Spa)
- TEMP: Current water temperature (°F)
- HTMODE: Heating status (0=off, 1=heating)
- HTSRC: Assigned heater object ID
Circuits (OBJTYP=CIRCUIT):
- SNAME: Display name
- STATUS: Equipment state ("ON"/"OFF")
- SUBTYP: Equipment type (POOL, SPA, LIGHT, GENERIC)
Pumps (OBJTYP=PUMP):
- SNAME: Display name
- STATUS: Running status ("10"=running)
- RPM: Current speed
- GPM: Current flow rate
Sensors (OBJTYP=SENSE):
- SNAME: Display name
- PROBE: Temperature reading (°F)
- SUBTYP: Sensor type (AIR, POOL, SOLAR)
Circuit Groups (OBJTYP=CIRCGRP):
- PARENT: Parent group ID (e.g., "GRP01")
- CIRCUIT: Referenced circuit ID (e.g., "C0003", "C0004")
- ACT: Active state ("ON"/"OFF")
- USE: Color/mode setting (e.g., "White", "Blue", "Red")
- DLY: Delay setting
- LISTORD: List order within group
- STATIC: Static mode flag ("ON"/"OFF")
- 200: Success
- 400: Bad Request (missing/invalid parameters)
- 404: Unknown command
- Each request must have a unique
messageID - Controller generates UUIDs for error responses
- Some queries require specific parameters (return 400 if missing)
- Raw TCP also available on port 6681 (same JSON format, no WebSocket framing)
- Many virtual controls and unused circuits are returned; filter to meaningful equipment
Discover All Equipment (No Filters):
Query all objects in the system without filtering by type - useful for equipment discovery, debugging, and monitoring unknown equipment types.
{
"messageID": "all-objects-001",
"command": "GetParamList",
"condition": "",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "STATUS", "OBJTYP", "SUBTYP"]}]
}Response includes:
- All equipment types: BODY, CIRCUIT, PUMP, HEATER, SENSE, VALVE, etc.
- OBJTYP: Object type classification (primary identifier)
- SUBTYP: Subtype within object class (POOL, SPA, LIGHT, ULTRA, etc.)
- SNAME: User-configured display name
- STATUS: Current operational status
Use Cases:
- Equipment discovery: Find all equipment in the system including unknown types
- Live monitoring: Track equipment changes across the entire system (listen mode)
- Debugging: Identify equipment types and naming conventions
- Universal compatibility: Works with any IntelliCenter configuration
Performance Considerations:
- Returns ~35+ objects including virtual controls and unused circuits
- Filter results by OBJTYP/SUBTYP or object naming patterns (C####, FTR##, etc.)
- Suitable for discovery and debugging, not continuous high-frequency polling
- For production monitoring, use targeted OBJTYP queries for better performance
Known Equipment Types:
- BODY: Pool/Spa water bodies
- CIRCUIT: Equipment controls (lights, valves, features)
- PUMP: Variable speed pumps
- HEATER: Heating/cooling equipment
- SENSE: Temperature sensors
- VALVE: Valve actuators
- CHEM: Chemical monitoring equipment
- REMOTE: Remote control devices
- CIRCGRP: Circuit group members (for light shows and synchronized circuits)
IntelliCenter supports circuit groups for synchronized control of multiple circuits. Circuit groups consist of:
- Parent Group (GRP##): Has
OBJTYP=CIRCUITand appears in circuit queries. Subtypes:LITSHO: Light Show groups (synchronized light control with color themes)CIRCGRP: Feature/Circuit groups (general circuit grouping without light-specific features)
- Member Circuits (c####): Have
OBJTYP=CIRCGRPand reference both the parent group and the actual circuit they control.
Key Insight: The parent group (e.g., GRP01 "AllOfTheLights") is returned in OBJTYP=CIRCUIT queries, while the member circuits (e.g., c0101, c0102) are returned in OBJTYP=CIRCGRP queries. Each member's CIRCUIT parameter references the actual equipment circuit (e.g., C0003 "Pool Light", C0004 "Spa Light").
Query Circuit Group Members:
{
"messageID": "circgrp-001",
"command": "GetParamList",
"condition": "OBJTYP=CIRCGRP",
"objectList": [{"objnam": "INCR", "keys": ["OBJTYP", "PARENT", "CIRCUIT", "ACT", "USE", "DLY", "LISTORD", "STATIC"]}]
}Response Example:
{
"command": "SendParamList",
"messageID": "circgrp-001",
"response": "200",
"objectList": [
{
"objnam": "c0101",
"params": {
"OBJTYP": "CIRCGRP",
"PARENT": "GRP01",
"CIRCUIT": "C0004",
"ACT": "ON",
"USE": "White",
"DLY": "0",
"LISTORD": "1",
"STATIC": "OFF"
}
},
{
"objnam": "c0102",
"params": {
"OBJTYP": "CIRCGRP",
"PARENT": "GRP01",
"CIRCUIT": "C0003",
"ACT": "ON",
"USE": "White",
"DLY": "0",
"LISTORD": "2",
"STATIC": "OFF"
}
}
]
}Query Parent Group:
{
"messageID": "grp-001",
"command": "GetParamList",
"condition": "",
"objectList": [{"objnam": "GRP01", "keys": ["SNAME", "STATUS", "OBJTYP", "SUBTYP", "ACT"]}]
}Parent Group Response:
{
"objnam": "GRP01",
"params": {
"SNAME": "AllOfTheLights",
"STATUS": "OFF",
"OBJTYP": "CIRCUIT",
"SUBTYP": "LITSHO",
"ACT": "WHITER"
}
}Key Parameters:
| Parameter | Description | Values |
|---|---|---|
| PARENT | Parent group ID | GRP01, GRP02, etc. |
| CIRCUIT | Referenced circuit ID | C0003, C0004, etc. |
| ACT | Active state in group | ON, OFF |
| USE | Color/mode setting | White, Blue, Red, Green, etc. |
| DLY | Delay between activations | 0, 1, 2, etc. (seconds) |
| LISTORD | Order within group | 1, 2, 3, etc. |
| STATIC | Static mode (no color cycling) | ON, OFF |
Push Notifications:
Circuit groups generate push notifications when activated/deactivated:
{
"command": "WriteParamList",
"messageID": "uuid-generated",
"response": "200",
"objectList": [
{
"changes": [
{
"objnam": "c0101",
"params": {
"ACT": "ON",
"CIRCUIT": "C0004",
"DLY": "0",
"LISTORD": "1",
"OBJNAM": "c0101",
"OBJTYP": "CIRCGRP",
"PARENT": "GRP01",
"STATIC": "OFF",
"USE": "White"
}
}
]
}
]
}Use Cases:
- Light Shows: Synchronized color patterns across multiple pool/spa lights
- Group Control: Turn multiple circuits on/off together
- Sequenced Activation: Delayed activation of circuits using DLY parameter
- Color Coordination: Same USE value across group members for uniform appearance
Discover All Sensors:
{
"messageID": "discover-001",
"command": "GetParamList",
"condition": "",
"objectList": [{"objnam": "INCR", "keys": ["OBJTYP", "SNAME", "TEMP", "PROBE", "SUBTYP"]}]
}Known Temperature Sensors:
| Object ID | Name | Type | Temperature Key | Notes |
|---|---|---|---|---|
| B1101 | Pool | BODY | TEMP | Pool water temperature |
| B1202 | Spa | BODY | TEMP | Spa water temperature |
| _A135 | Air Sensor | SENSE | PROBE | Outdoor air temperature |
| SSS11/SSS12 | Solar Sensors | SENSE | PROBE | May need different parameters |
Include (Meaningful Equipment):
- C0001-C0011: Core equipment (Pool, Spa, Lights, Cleaner)
- FTR01-FTR03: Features (Spa Heat, Fountain, Spa Jets)
Exclude (Virtual Controls):
- X-prefixed: Virtual buttons (Pump Speed +/-, Heat Enable)
- _A-prefixed: Action buttons (All Lights On/Off)
- AUX circuits: Often unused placeholder circuits
This filtering reduces ~35 total circuits to ~9 actual equipment items.
IntelliCenter tracks heating at multiple levels. For accurate monitoring, use both body-level HTMODE and heater-level status.
Body-Level Heater Status Query:
{
"messageID": "heating-001",
"command": "GetParamList",
"condition": "OBJTYP=BODY",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "HTMODE", "HTSRC", "TEMP", "LOTMP", "HITMP"]}]
}Equipment-Level Heater Status Query:
{
"messageID": "heater-equipment-001",
"command": "GetParamList",
"condition": "OBJTYP=HEATER",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "STATUS", "SUBTYP", "OBJTYP"]}]
}HTMODE Values:
- 0: No heating demand (idle when heater assigned, off when no heater assigned)
- 1: Actively calling for heat (traditional heater firing)
- 4: Heat pump heating mode (UltraTemp operation)
- 9: Heat pump cooling mode (UltraTemp operation)
Thermal Status Interpretation:
- Off (0): HTSRC="00000" (no heater assigned)
- Heating (1): HTMODE=1 or HTMODE=4 (actively heating)
- Idle (2): HTMODE=0 with assigned heater (enabled but setpoint satisfied)
- Cooling (3): HTMODE=9 (heat pump cooling mode)
Key Parameters:
- HTMODE: Actual heating demand (most important)
- HTSRC: Assigned heater object ID (H0001, H0002, etc.)
- Circuit features (FTR01="Spa Heat"): Enable/disable only, not active status
Best Practice: Use HTMODE >= 1 to detect active heating rather than relying on circuit feature STATUS.
For reliable on/off/idle detection, use the combination of HTMODE and HTSRC:
Active Heating/Cooling:
- HTMODE=1 + HTSRC="H0002" (or other heater ID): Traditional heater actively heating
- HTMODE=4 + HTSRC="H0001": Heat pump actively heating
- HTMODE=9 + HTSRC="H0001": Heat pump actively cooling
Idle (System Enabled, Setpoint Satisfied):
- HTMODE=0 + HTSRC="H0002" (or other heater ID): Heater assigned but not currently demanded
Off (System Disabled):
- HTSRC="00000" (regardless of HTMODE): No heater assigned, system off
Detection Logic:
- If HTSRC="00000" → System is OFF (no heater assigned)
- If HTSRC points to heater AND HTMODE≥1 → System is ACTIVE (heating/cooling)
- If HTSRC points to heater AND HTMODE=0 → System is IDLE (heater assigned but setpoint satisfied)
Important Distinction - Idle vs Off:
- Off: Heating system disabled (no heater assigned via HTSRC)
- Idle: Heating system enabled with assigned heater but no current demand (setpoint already reached)
Critical Notes:
- HTSRC="00000" definitively indicates the heating system is OFF (regardless of HTMODE value)
- HTSRC pointing to a heater (H0001, H0002, etc.) indicates the system is ON
- HTMODE determines operational demand when a heater is assigned
- Temperature comparison logic (LOTMP/HITMP vs TEMP) is not required for basic on/off/idle detection
Heat Pump Detection:
- Use
HTMODE == 4to detect heat pump heating operations - Use
HTMODE == 9to detect heat pump cooling operations
IntelliCenter systems with UltraTemp heat pumps support both heating and cooling operations with multiple operational modes. Heat pump monitoring requires tracking multiple object types and parameters.
Heater Configuration Query:
{
"command": "GetQuery",
"queryName": "GetHeaterConfiguration",
"arguments": "",
"messageID": "heater-config-001"
}Expected Response Structure:
{
"objnam": "H0001",
"params": {
"SUBTYP": "ULTRA",
"SNAME": "UltraTemp",
"DLY": "5",
"COMUART": "1",
"LISTORD": "1",
"ACT": "ACT"
}
}Heat Pump Identification:
- H0001: Primary heat pump (SUBTYP="ULTRA")
- H0002: Backup gas heater (SUBTYP="GENERIC")
- HXULT: Heat pump preference setting
Heat pumps operate in two primary modes affecting heating priority:
UltraTemp Only:
- Heat pump exclusive for heating
- No gas heater backup used
- Cooling operations available when enabled
UltraTemp Preferred:
- Heat pump priority for heating
- Gas heater as backup/supplement
- Heat pump used first, gas heater supplements as needed
Real-time Heater Status:
{
"messageID": "heater-status-001",
"command": "GetParamList",
"condition": "OBJTYP=HEATER",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "STATUS", "SUBTYP"]}]
}Heat Pump Response:
{
"objnam": "H0001",
"params": {
"SNAME": "UltraTemp",
"STATUS": "ON",
"SUBTYP": "ULTRA"
}
}Heat pumps with cooling capability require monitoring both heating and cooling setpoints:
Body Temperature Monitoring:
{
"messageID": "body-temps-001",
"command": "GetParamList",
"condition": "OBJTYP=BODY",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "TEMP", "LOTMP", "HITMP", "HTMODE"]}]
}Cooling Logic Parameters:
- TEMP: Current water temperature
- LOTMP: Low temperature setpoint (heating threshold)
- HITMP: High temperature setpoint (cooling threshold)
- HTMODE: Heating demand (0=off, 1=heating active)
Cooling Operation Detection:
- Heating: Triggered when TEMP < LOTMP
- Cooling: Triggered when TEMP > HITMP
- Idle: When LOTMP <= TEMP <= HITMP
Heat pump operations may involve multiple virtual circuits:
Circuit Discovery:
{
"messageID": "heatpump-circuits-001",
"command": "GetParamList",
"condition": "OBJTYP=CIRCUIT",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "STATUS", "SUBTYP"]}]
}Key Heat Pump Circuits:
- X0034: "Heat Pump" circuit
- X0035: "UltraTemp" circuit
- X0044: "Pool Heater" circuit
- X0051: "Heater" circuit
Heat pump detection requires monitoring multiple object types:
-
Body Level (OBJTYP=BODY):
- Temperature comparisons (TEMP vs LOTMP/HITMP)
- Heating demand status (HTMODE)
-
Heater Level (OBJTYP=HEATER):
- Equipment operational status
- Heat pump vs gas heater identification
-
Circuit Level (OBJTYP=CIRCUIT):
- Virtual control circuit status
- Heat pump specific controls
Important Notes:
- Cooling operations may not be reflected in traditional HTMODE parameters
- Heat pump operational status during cooling requires different parameter monitoring
- Multiple heater objects may be active simultaneously in "Preferred" mode
- Circuit status may lag behind actual equipment operation
Baseline State:
{
"Pool": {"TEMP": "92", "HTMODE": "0", "LOTMP": "86", "HITMP": "92"},
"Circuits": {"X0034": "OFF", "X0035": "OFF", "X0044": "OFF"}
}UltraTemp Only Heat Activated:
{
"Pool": {"TEMP": "92", "HTMODE": "4", "LOTMP": "101", "HITMP": "104"},
"Circuits": {"X0034": "OFF", "X0035": "OFF", "X0044": "OFF"}
}Key Findings:
- HTMODE=4: Indicates heat pump heating in "Only" mode
- Temperature Setpoints: Both LOTMP and HITMP increase when heat pump activates
- Circuit Status: Heat pump circuits (X0034, X0035, X0044) remain OFF during operation
- Heater Objects: H0001 status unchanged during activation
Detection Logic:
- Monitor body-level
HTMODE == 4for heat pump heating detection - Traditional gas heater uses
HTMODE == 1 - Circuit status unreliable for heat pump operational detection
UltraTemp Only vs Preferred Mode Detection:
{
"messageID": "mode-detection-001",
"command": "GetParamList",
"condition": "OBJTYP=BODY",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "HTSRC", "MODE", "HEATER", "HTMODE"]}]
}UltraTemp Only Mode:
{
"Pool": {
"HTSRC": "H0001",
"MODE": "5",
"HEATER": "H0001",
"HTMODE": "4"
}
}UltraTemp Preferred Mode:
{
"Pool": {
"HTSRC": "HXULT",
"MODE": "6",
"HEATER": "HXULT",
"HTMODE": "4"
}
}Mode Detection Logic:
- HTSRC="H0001" + MODE="5": UltraTemp Only (direct heat pump)
- HTSRC="HXULT" + MODE="6": UltraTemp Preferred (preference controller)
- HTMODE="4": Heat pump heating active (both modes)
- HTMODE="9": Heat pump cooling active (both modes)
- HEATER parameter mirrors HTSRC value for confirmation
UltraTemp Only Cooling Activated:
{
"Pool": {
"TEMP": "92",
"HTMODE": "9",
"LOTMP": "75",
"HITMP": "82",
"HTSRC": "H0001",
"MODE": "5",
"HEATER": "H0001"
}
}Cooling Detection Logic:
- HTMODE="9": Heat pump cooling mode active
- TEMP > HITMP: Cooling demand (92°F > 82°F setpoint)
- HTSRC="H0001": Confirms UltraTemp Only mode during cooling
- MODE="5": Consistent with Only mode operation
UltraTemp Preferred Cooling Activated:
{
"Pool": {
"TEMP": "92",
"HTMODE": "9",
"LOTMP": "75",
"HITMP": "82",
"HTSRC": "HXULT",
"MODE": "6",
"HEATER": "HXULT"
}
}Preferred Cooling Confirmation:
- HTMODE="9": Heat pump cooling mode (same as Only)
- HTSRC="HXULT": Confirms UltraTemp Preferred mode during cooling
- MODE="6": Consistent with Preferred mode operation
- Temperature setpoints: Identical to Only mode (75°F heat, 82°F cool)
All Operational Combinations:
| Mode | Operation | HTSRC | MODE | HTMODE | Detection Logic |
|---|---|---|---|---|---|
| Only | Heating | H0001 | 5 | 4 | Direct heat pump heating |
| Only | Cooling | H0001 | 5 | 9 | Direct heat pump cooling |
| Preferred | Heating | HXULT | 6 | 4 | Preference controller heating |
| Preferred | Cooling | HXULT | 6 | 9 | Preference controller cooling |
| Any | Idle/Off | * | * | 0 | No heat pump operation |
UltraTemp Preferred - Idle (Neutral Zone):
{
"Pool": {
"TEMP": "92",
"HTMODE": "0",
"LOTMP": "75",
"HITMP": "94",
"HTSRC": "HXULT",
"MODE": "6",
"HEATER": "HXULT"
}
}UltraTemp Only - Idle (Neutral Zone):
{
"Pool": {
"TEMP": "92",
"HTMODE": "0",
"LOTMP": "75",
"HITMP": "94",
"HTSRC": "H0001",
"MODE": "5",
"HEATER": "H0001"
}
}Neutral Zone Findings:
- HTMODE="0": System idle when LOTMP < TEMP < HITMP (75°F < 92°F < 94°F)
- Mode Detection Persistent: HTSRC/MODE values show current mode setting even when idle
- Real-time Mode Switching: HTSRC/MODE change immediately when switching between Only/Preferred
- Temperature Setpoints: Persist across mode changes
- Independent Operation: Mode configuration independent of operational status
Summary Detection Logic:
- HTMODE=0: Heat pump idle/off (neutral zone: LOTMP < TEMP < HITMP)
- HTMODE=4: Heat pump heating (TEMP < LOTMP)
- HTMODE=9: Heat pump cooling (TEMP > HITMP)
- Mode Detection: HTSRC + MODE values distinguish Only vs Preferred in all states
- Real-time Updates: Mode changes reflected immediately regardless of operational status
Problem: WebSocket connections can become "stale" - appearing connected but delivering cached data instead of real-time updates.
Solution: Use unique messageID correlation to detect stale connections.
Implementation:
messageID := fmt.Sprintf("request-%d-%d", time.Now().Unix(), time.Now().Nanosecond()%1000000)
// Send request with unique messageID
// Validate response messageID matches exactly
if resp.MessageID != messageID {
// Connection is stale - force reconnection
conn.Close()
reconnect()
}Why This Works:
- Unique messageIDs prevent cached responses
- Immediate detection of stale connections
- No false positives from network conditions
- Works regardless of equipment stability
Alternative methods (response timing, data variance tracking) are less reliable due to false positives.
IntelliCenter features have a "Show as Feature" setting in the web interface that controls whether they appear as user-accessible features. This setting is encoded in the SHOMNU parameter.
Discovery Method:
{
"command": "GetQuery",
"queryName": "GetConfiguration",
"arguments": "",
"messageID": "config-001"
}SHOMNU Encoding:
- Show as Feature: YES → SHOMNU ends with 'w' (e.g.,
"fcsrepvhzmtow") - Show as Feature: NO → SHOMNU without 'w' (e.g.,
"fcsrepvhzmto")
Real-time Verification:
{
"messageID": "feature-display-check",
"command": "GetParamList",
"condition": "OBJTYP=CIRCUIT",
"objectList": [{"objnam": "FTR01", "keys": ["SNAME", "SHOMNU"]}]
}Response Examples:
Feature enabled:
{"objnam": "FTR01", "params": {"SNAME": "Spa Heat", "SHOMNU": "fcsrepvhzmtow"}}Feature disabled:
{"objnam": "FTR01", "params": {"SNAME": "Spa Heat", "SHOMNU": "fcsrepvhzmto"}}Implementation Pattern:
// Respect IntelliCenter's "Show as Feature" setting
if !strings.HasSuffix(shomnu, "w") {
// User has disabled "Show as Feature" - skip this feature
return
}Use Cases:
- Eliminate duplicate controls (e.g., "Spa Heat" feature vs "Spa Heater" thermal equipment)
- Respect user configuration (users control what appears as features)
- Universal compatibility (works with any IntelliCenter setup)
Benefits:
- No hardcoded equipment names or logic
- User-controlled through IntelliCenter interface
- Automatically handles equipment/feature relationships
- Works across different pool configurations
IntelliCenter provides freeze protection settings per circuit. The FREEZE parameter indicates whether freeze protection is enabled for each circuit (not whether it's currently active).
Query Freeze Protection Settings:
{
"messageID": "freeze-check-001",
"command": "GetParamList",
"condition": "OBJTYP=CIRCUIT",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "STATUS", "FREEZE"]}]
}Response Example:
{
"objectList": [
{"objnam": "C0006", "params": {"SNAME": "Pool", "STATUS": "ON", "FREEZE": "ON"}},
{"objnam": "C0001", "params": {"SNAME": "Spa", "STATUS": "ON", "FREEZE": "ON"}},
{"objnam": "C0004", "params": {"SNAME": "Spa Light", "STATUS": "OFF", "FREEZE": "OFF"}},
{"objnam": "FTR02", "params": {"SNAME": "Fountain", "STATUS": "OFF", "FREEZE": "ON"}},
{"objnam": "X0046", "params": {"SNAME": "Freeze", "STATUS": "OFF", "FREEZE": "FREEZE"}}
]
}FREEZE Parameter Values:
- ON: Freeze protection is enabled for this circuit (will auto-activate if temp drops)
- OFF: Freeze protection is disabled for this circuit
- FREEZE: Virtual control circuit (X-prefixed) - not applicable
Key Observations:
- The
FREEZEparameter indicates configuration, not current activation status - Physical equipment circuits (C####, FTR##) have ON/OFF values
- Virtual circuits (X-prefixed) return "FREEZE" as a placeholder
- Typical freeze-protected equipment: Pool pump, Spa, Fountain (water circulation)
- Typically not freeze-protected: Lights, blower (no freeze damage risk)
Determining Active Freeze Protection:
To determine if freeze protection is currently active (circuits running due to freeze):
- Check air temperature via sensor query:
{
"messageID": "air-temp-001",
"command": "GetParamList",
"condition": "OBJTYP=SENSE",
"objectList": [{"objnam": "INCR", "keys": ["SNAME", "PROBE", "SUBTYP"]}]
}- Look for
_A135(Air Sensor) -PROBEvalue is temperature in °F - Freeze protection typically activates around 36-38°F
- If air temp is below threshold AND freeze-enabled circuits are running when not scheduled, freeze protection is likely active
Freeze Protection Active Indicator:
The _FEA2 object (named "Freeze") provides a direct indicator of active freeze protection:
{"objnam": "_FEA2", "params": {"SNAME": "Freeze", "STATUS": "ON", "FREEZE": "FREEZE"}}- STATUS="ON": Freeze protection is currently active (circuits running due to freeze)
- STATUS="OFF": Freeze protection is not active (normal operation)
Detection Query:
{
"messageID": "freeze-active-001",
"command": "GetParamList",
"condition": "OBJTYP=CIRCUIT",
"objectList": [{"objnam": "_FEA2", "keys": ["SNAME", "STATUS"]}]
}Test Results (2025-11-28, Freeze Protection Active):
Air Temperature: 35°F (at freeze threshold)
_FEA2 (Freeze): STATUS=ON ← Direct indicator of active freeze protection
Freeze-protected circuits running:
Pool (C0006): FREEZE=ON, STATUS=ON ✓
Spa (C0001): FREEZE=ON, STATUS=ON ✓
Fountain (FTR02): FREEZE=ON, STATUS=ON ✓
Non-freeze-protected circuits (not running):
Spa Light (C0004): FREEZE=OFF, STATUS=OFF
Air Blower (C0002): FREEZE=OFF, STATUS=OFF
Implementation Pattern:
// Check if freeze protection is currently active
func isFreezeProtectionActive(response Response) bool {
for _, obj := range response.ObjectList {
if obj.Objnam == "_FEA2" {
return obj.Params["STATUS"] == "ON"
}
}
return false
}Important: Heater Control During Freeze Protection
When freeze protection is active, heaters may not respond to user commands or API control requests. The IntelliCenter prioritizes freeze protection over manual/scheduled heating operations:
- Heater setpoints and mode changes may be ignored or delayed
- HTMODE values may not reflect user-requested heating states
- Circuits required for freeze protection cannot be turned off manually
- Normal heating schedules are overridden by freeze protection logic
Applications should check _FEA2 STATUS before attempting heater control operations and display appropriate warnings to users when freeze protection is active.
Pump Speed Priority During Freeze Protection
When freeze protection is active, pump speed is determined by freeze protection settings rather than other active circuits:
| Circuit | Configured Speed | Active | Actual Speed |
|---|---|---|---|
| Spa | 1800 RPM | Yes | - |
| Heater | 3000 RPM | Yes | - |
| Spa Jets | 3400 RPM | No | - |
| Freeze | 2000 RPM | Yes | 2000 RPM ✓ |
Even though the Heater circuit is active (normally requiring 3000 RPM), the pump runs at the Freeze speed (2000 RPM) because freeze protection takes priority over other circuit demands.
This behavior:
- Prioritizes freeze protection over normal heating/circulation demands
- Uses the freeze-configured speed (sufficient for preventing pipe damage)
- Is more energy-efficient than running at higher heating speeds
- Overrides pump speed settings from other active circuits
- Applies even to user-selected circuits: If a user manually turns on Spa Jets (3400 RPM) while freeze protection is active, the pump will still run at the freeze speed (2000 RPM), not the Jets speed
IntelliCenter supports IntelliBrite (SUBTYP=INTELLI) color-changing LED pool lights with fixed colors and animated light shows.
Query IntelliBrite Circuit:
{
"messageID": "intellibrite-001",
"command": "GetParamList",
"objectList": [{"objnam": "C0007", "keys": ["SNAME", "STATUS", "SUBTYP", "USE", "ACT"]}]
}Response:
{
"objnam": "C0007",
"params": {
"SNAME": "Pool Light",
"STATUS": "ON",
"SUBTYP": "INTELLI",
"USE": "BLUER",
"ACT": "65535"
}
}Key Parameters:
- ACT: Write-only trigger to change color/show (set this to change the light)
- USE: Read-only storage of current color or light show mode (see tables below)
- USAGE: Encoded capability mask indicating available options for this circuit type
- SUBTYP:
INTELLIfor IntelliBrite lights
| Parameter | Purpose | Behavior |
|---|---|---|
| ACT | Trigger color changes (write) | Set to desired color code; returns to "65535" after processing |
| USE | Stores selected color (read) | Persists the current selection; setting directly returns 404 |
- To change color: Set the
ACTparameter to the desired color/show code - To read current color: Read the
USEparameter - When
ACT= "65535", the light is in fixed color mode and the actual color is inUSE
Change Light Color Example:
{
"messageID": "set-color",
"command": "SetParamList",
"objectList": [{"objnam": "C0007", "params": {"ACT": "BLUER"}}]
}Response:
{"command": "SetParamList", "messageID": "set-color", "response": "200"}After this command, querying the circuit will show USE: "BLUER" and ACT: "65535".
| Code | Display Name |
|---|---|
WHITER |
White |
REDR |
Red |
GREENR |
Green |
BLUER |
Blue |
MAGNTAR |
Magenta |
| Code | Display Name |
|---|---|
SAMMOD |
Sam |
PARTY |
Party |
ROMAN |
Romance |
CARIB |
Caribbean |
AMERCA |
American |
SSET |
California Sunset |
ROYAL |
Royal |
Push Notification Example (Light Show Change):
{
"command": "WriteParamList",
"messageID": "uuid-generated",
"response": "200",
"objectList": [
{
"changes": [
{
"objnam": "GRP01",
"params": {
"ACT": "AMERCA",
"SNAME": "All Pool lights",
"STATUS": "ON",
"SUBTYP": "LITSHO",
"USE": "AMERCA"
}
}
]
}
]
}Light Show Groups (LITSHO):
IntelliBrite lights can be organized into light show groups (SUBTYP=LITSHO) for synchronized color control:
{
"objnam": "GRP01",
"params": {
"OBJTYP": "CIRCUIT",
"SUBTYP": "LITSHO",
"SNAME": "All Pool lights",
"USE": "AMERCA",
"ACT": "AMERCA",
"CHILD": "C0009 C0017 C0019"
}
}Key Light Show Group Parameters:
- ACT: Active color/show being displayed
- USE: Selected color/show mode
- CHILD: Space-separated list of member circuit IDs
- SYNC: Synchronization mode (ON/OFF)
- SWIM: Color Swim effect (ON/OFF)
Related Circuit Types:
INTELLI: IntelliBrite color-changing lightsGLOW: GloBrite color-changing lightsGLOWT: GloBrite White lightsMAGIC2: MagicStream laminar jetsCLRCASC: ColorCascade lightsLITSHO: Light Show groups
Critical Discovery: The "All Off" button in IntelliCenter's Light Management UI can cascade to turn off unrelated circuits, including body circuits (Spa/Pool).
Observed Behavior:
When using the web UI "All Off" button in the Lights management section:
- Command Sent (captured via WebSocket):
{
"command": "SetParamList",
"messageID": "SET_LIGHT_STATUS_MESSAGE_ID_ALL_OFF...",
"objectList": [
{"objnam": "<light1>", "params": {"STATUS": "OFF"}},
{"objnam": "<light2>", "params": {"STATUS": "OFF"}},
{"objnam": "<light3>", "params": {"STATUS": "OFF"}}
]
}-
Expected Result: Only the light circuits turn off.
-
Actual Result: IntelliCenter also sends NotifyList messages turning off:
- Body objects (e.g., Spa body)
- Body-associated circuits (e.g., Spa circuit)
- Pump status changes
Example NotifyList Cascade:
NotifyList: <light1> STATUS=OFF (expected)
NotifyList: <light2> STATUS=OFF (expected)
NotifyList: <light3> STATUS=OFF (expected)
NotifyList: <body> STATUS=OFF (UNEXPECTED - body turned off!)
NotifyList: <pump> CIRCUIT="00000" (UNEXPECTED - pump disassociated)
NotifyList: <spa-circuit> STATUS=OFF (UNEXPECTED - spa turned off!)
Cause:
IntelliCenter has internal logic linking certain lights to their associated bodies. When a body-associated light (e.g., Spa Light) is turned off via the bulk "All Lights Off" command, the system cascades to deactivate the entire body and its primary circuit.
This cascade is triggered by IntelliCenter firmware, not by the web UI - the UI only sends light-off commands.
Implications for API Users:
- Avoid bulk light-off commands if you want to preserve body (Spa/Pool) operation
- Turn off lights individually to prevent cascade effects
- Monitor NotifyList messages for unexpected circuit changes when controlling lights
- The web UI "All Off" button affects more than just lights
Safe Individual Light Control:
{
"command": "SetParamList",
"objectList": [{"objnam": "<light-circuit>", "params": {"STATUS": "OFF"}}]
}Individual circuit commands may still trigger cascade behavior in some cases - see "IntelliBrite Module Association Theory" below.
Observed Behavior:
Turning off an IntelliBrite circuit (SUBTYP=INTELLI) - either individually or via light group - can intermittently cascade to turn off an unrelated Spa circuit and body, even when:
- The IntelliBrite is the only member of its light group
- The Spa's actual light circuit is NOT in the light group
- The command is sent via the official IntelliCenter web app (not third-party)
- The Spa is not actively heating
Example Cascade (from pentameter --listen):
15:05:12 POLL: AUX 5 turned OFF (IntelliBrite circuit)
15:05:12 POLL: Spa turned OFF (UNEXPECTED cascade)
15:05:12 POLL: Light Group 1 turned OFF (group followed its member)
Module Configuration Observation:
| Object | Name | Type | Module |
|---|---|---|---|
| B1202 | Spa | BODY | M0102 |
| C0001 | Spa | SPA circuit | M0102 |
| C0007 | AUX 5 | INTELLI | M0102 |
| C0004 | Spa Light | LIGHT | M0101 |
The IntelliBrite circuit (C0007) shares the same parent module (M0102) as the Spa body (B1202). The actual Spa Light (C0004) is on a different module (M0101) and does NOT trigger cascade when turned off.
Theory: IntelliCenter firmware may implicitly associate IntelliBrite circuits with the body on their same expansion module, regardless of circuit naming or explicit configuration.
Alternative Theories:
-
No-Load Detection: The IntelliBrite circuit has no physical light connected. IntelliBrite uses digital communication over power lines. The controller may detect no response and interpret this as a fault, triggering body shutdown.
-
Freeze Mode Interaction: When freeze protection is active on Pool (causing freeze RPM on shared pump), turning off circuits on the Spa module may trigger unexpected firmware behavior.
Why Intermittent?
The cascade does not occur every time. This suggests either:
- A race condition in IntelliCenter firmware
- Additional state-dependent conditions not yet identified
- Timing-sensitive behavior
Implications:
- This behavior occurs with the official IntelliCenter web app - it is firmware behavior, not a third-party integration issue
- Configuring a circuit as IntelliBrite (SUBTYP=INTELLI) on a body's module may create implicit associations
- There is no known API method to query or modify these implicit associations