Skip to content

Latest commit

 

History

History
1450 lines (1185 loc) · 43.6 KB

File metadata and controls

1450 lines (1185 loc) · 43.6 KB

Pentair IntelliCenter WebSocket API

This document describes the WebSocket API for communicating with Pentair IntelliCenter pool controllers.

Quick Start

WebSocket Endpoint: ws://[IP_ADDRESS]:6680

Test Connection:

echo '{"command":"GetQuery","queryName":"GetConfiguration","arguments":"","messageID":"test-123"}' | websocat ws://[IP_ADDRESS]:6680

Message Protocol

All messages use JSON format with these structures:

Request

{
  "command": "CommandName",
  "queryName": "QueryType", 
  "arguments": "",
  "messageID": "unique-identifier"
}

Success Response

{
  "command": "SendQuery",
  "messageID": "unique-identifier",
  "queryName": "QueryType", 
  "response": "200",
  "answer": [...]
}

Error Response

{
  "command": "Error",
  "messageID": "generated-uuid",
  "response": "404", 
  "description": "'CommandName' Unknown command!"
}

API Architecture: Request/Response + Push Notifications

The IntelliCenter WebSocket API supports both request/response queries and push notifications for real-time equipment state changes.

Request/Response

Standard polling uses GetParamList commands with unique messageIDs:

  • Send request with messageID
  • Receive response with matching messageID
  • Response uses objectList array with objnam and params

Push Notifications

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 (not GetParamList)
  • Structure: objectList[].changes[].{objnam, params} (nested changes array)
  • MessageID: UUID generated by IntelliCenter (not your request ID)
  • Params: Same fields as polling responses (SNAME, STATUS, TEMP, etc.)

Handling Push Notifications

When reading from the WebSocket, you may receive:

  1. Your response - messageID matches your request
  2. Push notification - messageID is 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}
}

Real-Time Event Monitoring

Push notifications enable true real-time monitoring:

  • No polling required - just listen for WriteParamList messages
  • Instant updates - changes arrive as they happen
  • Same data format - params contain 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

Hybrid Approach (Recommended)

For production monitoring:

  1. Initial poll - fetch complete state on startup
  2. Listen for push notifications - real-time updates
  3. 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 -

Basic Configuration Queries

These commands retrieve static system configuration data.

GetConfiguration

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)

GetHardwareDefinition

Returns panel and module information with MAC addresses and firmware versions.

{
  "command": "GetQuery", 
  "queryName": "GetHardwareDefinition",
  "arguments": "",
  "messageID": "hardware-001"
}

GetPumpConfiguration

Returns pump setup with speed settings for different circuits.

{
  "command": "GetQuery",
  "queryName": "GetPumpConfiguration", 
  "arguments": "",
  "messageID": "pump-001"
}

GetHeaterConfiguration

Returns heater setup and communication settings.

{
  "command": "GetQuery",
  "queryName": "GetHeaterConfiguration",
  "arguments": "",
  "messageID": "heater-001"
}

Real-Time Monitoring

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.

Request Format

{
  "messageID": "unique-id",
  "command": "GetParamList",
  "condition": "OBJTYP=OBJECTTYPE", 
  "objectList": [{"objnam": "INCR", "keys": ["param1", "param2"]}]
}

Temperature Monitoring

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"]}]
}

Pump Monitoring

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)

Circuit and Feature Status

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

System Object Reference

Object Naming Conventions

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

Key Parameters by Object Type

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")

Response Codes

  • 200: Success
  • 400: Bad Request (missing/invalid parameters)
  • 404: Unknown command

Important Notes

  • 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

Advanced Topics

Universal Equipment Discovery

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)

Circuit Groups

IntelliCenter supports circuit groups for synchronized control of multiple circuits. Circuit groups consist of:

  1. Parent Group (GRP##): Has OBJTYP=CIRCUIT and 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)
  2. Member Circuits (c####): Have OBJTYP=CIRCGRP and 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

Temperature Sensor Discovery

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

Circuit Filtering for Monitoring

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.

Heater Status Monitoring

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.

Heater Status Detection Logic

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:

  1. If HTSRC="00000" → System is OFF (no heater assigned)
  2. If HTSRC points to heater AND HTMODE≥1 → System is ACTIVE (heating/cooling)
  3. 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 == 4 to detect heat pump heating operations
  • Use HTMODE == 9 to detect heat pump cooling operations

Heat Pump Detection and Monitoring

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.

Heat Pump Equipment Discovery

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

Operational Modes

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

Heat Pump Status Monitoring

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"
  }
}

Cooling Detection

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 Control Circuits

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

Monitoring Complexity

Heat pump detection requires monitoring multiple object types:

  1. Body Level (OBJTYP=BODY):

    • Temperature comparisons (TEMP vs LOTMP/HITMP)
    • Heating demand status (HTMODE)
  2. Heater Level (OBJTYP=HEATER):

    • Equipment operational status
    • Heat pump vs gas heater identification
  3. 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

Test Results - UltraTemp Only Heating Mode

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 == 4 for heat pump heating detection
  • Traditional gas heater uses HTMODE == 1
  • Circuit status unreliable for heat pump operational detection

Heat Pump Mode 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

Test Results - UltraTemp Only Cooling Mode

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

Test Results - UltraTemp Preferred Cooling Mode

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)

Complete Heat Pump Detection Matrix

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

Test Results - Idle/Neutral Zone Behavior

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

Connection Staleness Detection

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.

Feature Display Control (SHOMNU Parameter)

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

Freeze Protection Monitoring

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 FREEZE parameter 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):

  1. Check air temperature via sensor query:
{
  "messageID": "air-temp-001",
  "command": "GetParamList",
  "condition": "OBJTYP=SENSE",
  "objectList": [{"objnam": "INCR", "keys": ["SNAME", "PROBE", "SUBTYP"]}]
}
  1. Look for _A135 (Air Sensor) - PROBE value is temperature in °F
  2. Freeze protection typically activates around 36-38°F
  3. 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

IntelliBrite Color-Changing Lights

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: INTELLI for IntelliBrite lights

Setting vs Reading Colors

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 ACT parameter to the desired color/show code
  • To read current color: Read the USE parameter
  • When ACT = "65535", the light is in fixed color mode and the actual color is in USE

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".

Fixed Colors (5)

Code Display Name
WHITER White
REDR Red
GREENR Green
BLUER Blue
MAGNTAR Magenta

Light Shows (7)

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 lights
  • GLOW: GloBrite color-changing lights
  • GLOWT: GloBrite White lights
  • MAGIC2: MagicStream laminar jets
  • CLRCASC: ColorCascade lights
  • LITSHO: Light Show groups

⚠️ Light Control Cascading Behavior (Gotcha)

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:

  1. 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"}}
  ]
}
  1. Expected Result: Only the light circuits turn off.

  2. 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.

IntelliBrite Module Association Theory (Unconfirmed)

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:

  1. 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.

  2. 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