Skip to main content

Python library for Pentair IntelliCenter pool control systems

Reason this release was yanked:

Pre-release, use v0.1.0 or later

Project description

pyintellicenter

PyPI version Python Versions Tests License: MIT

Python library for communicating with Pentair IntelliCenter pool control systems over local network.

Alpha Release: This library is in early development. API may change between versions.

Features

  • Dual Transport Support: TCP (port 6681) and WebSocket (port 6680) connections
  • Local Communication: Direct connection to IntelliCenter (no cloud required)
  • Real-time Updates: Push-based notifications via NotifyList protocol
  • mDNS Discovery: Automatically find IntelliCenter units on your network
  • Async/Await: Built on Python asyncio for efficient I/O
  • Type Annotations: Full type hints for IDE support and static analysis
  • Robust Connection Handling: Automatic reconnection with exponential backoff
  • Circuit Breaker Pattern: Prevents connection storms during outages
  • Home Assistant Ready: Convenience helpers for integration development

Installation

pip install pyintellicenter

Requirements

  • Python 3.13+
  • Pentair IntelliCenter controller (i5P, i8P, i10P, or similar)
  • Local network access to IntelliCenter

Quick Start

Basic Connection (TCP)

import asyncio
from pyintellicenter import ICModelController, PoolModel, ICConnectionHandler

async def main():
    # Create a model to hold equipment state
    model = PoolModel()

    # Create controller connected to your IntelliCenter
    controller = ICModelController("192.168.1.100", model)

    # Use ICConnectionHandler for automatic reconnection
    handler = ICConnectionHandler(controller)
    await handler.start()

    # Access system information
    print(f"Connected to: {controller.system_info.prop_name}")
    print(f"Software version: {controller.system_info.sw_version}")

    # List all equipment
    for obj in model:
        print(f"{obj.sname} ({obj.objtype}): {obj.status}")

    # Control equipment - turn on pool
    await controller.set_circuit_state("POOL", True)

    # Clean shutdown
    await handler.stop()

asyncio.run(main())

WebSocket Connection

from pyintellicenter import ICConnection

async def main():
    # Use WebSocket transport instead of TCP
    async with ICConnection("192.168.1.100", transport="websocket") as conn:
        response = await conn.send_request(
            "GetParamList",
            condition="",
            objectList=[{"objnam": "INCR", "keys": ["VER", "SNAME"]}]
        )
        print(response)

asyncio.run(main())

Auto-Discovery

from pyintellicenter import discover_intellicenter_units

async def main():
    # Find all IntelliCenter units on the network
    units = await discover_intellicenter_units(timeout=5.0)

    for unit in units:
        print(f"Found: {unit.name} at {unit.host}:{unit.port}")
        print(f"  Model: {unit.model}")
        print(f"  WebSocket port: {unit.ws_port}")

asyncio.run(main())

Architecture

┌─────────────────────────────────────────────────────────┐
│                    ICConnectionHandler                   │
│              (Auto-reconnection, callbacks)              │
├─────────────────────────────────────────────────────────┤
│                    ICModelController                     │
│           (State management, helper methods)             │
├─────────────────────────────────────────────────────────┤
│                     ICBaseController                     │
│              (Command handling, metrics)                 │
├─────────────────────────────────────────────────────────┤
│                      ICConnection                        │
│               (Transport selection, flow control)        │
├──────────────────────┬──────────────────────────────────┤
│      ICProtocol      │       ICWebSocketTransport       │
│    (TCP transport)   │      (WebSocket transport)       │
└──────────────────────┴──────────────────────────────────┘

Layer Overview

Layer Class Purpose
Handler ICConnectionHandler Auto-reconnection, lifecycle callbacks
Controller ICModelController State management, convenience methods
Controller ICBaseController Basic command handling, metrics
Connection ICConnection Transport selection, request flow control
Transport ICProtocol TCP communication (port 6681)
Transport ICWebSocketTransport WebSocket communication (port 6680)
Model PoolModel Equipment collection
Model PoolObject Individual equipment item

API Reference

ICConnection

Low-level connection wrapper supporting both TCP and WebSocket transports.

from pyintellicenter import ICConnection

# TCP connection (default)
conn = ICConnection("192.168.1.100")
conn = ICConnection("192.168.1.100", transport="tcp")
conn = ICConnection("192.168.1.100", port=6681)

# WebSocket connection
conn = ICConnection("192.168.1.100", transport="websocket")
conn = ICConnection("192.168.1.100", port=6680, transport="websocket")

# Full configuration
conn = ICConnection(
    host="192.168.1.100",
    port=6681,                    # Default: 6681 (TCP), 6680 (WebSocket)
    transport="tcp",              # "tcp" or "websocket"
    response_timeout=30.0,        # Request timeout in seconds
    keepalive_interval=90.0,      # Keepalive interval in seconds
    notification_queue_size=100,  # Max queued notifications
)

# Usage as context manager
async with ICConnection("192.168.1.100") as conn:
    response = await conn.send_request("GetParamList", ...)

# Manual connection management
await conn.connect()
response = await conn.send_request("GetParamList", ...)
await conn.disconnect()

# Callbacks
conn.set_notification_callback(lambda msg: print(msg))
conn.set_disconnect_callback(lambda exc: print(f"Disconnected: {exc}"))

ICModelController

Controller that maintains equipment state in a PoolModel.

from pyintellicenter import ICModelController, PoolModel

model = PoolModel()
controller = ICModelController(
    host="192.168.1.100",
    model=model,
    port=6681,
    keepalive_interval=90.0,
)

await controller.start()

# System information
info = controller.system_info
print(f"Name: {info.prop_name}")
print(f"Version: {info.sw_version}")
print(f"Unique ID: {info.unique_id}")
print(f"Uses Metric: {info.uses_metric}")

# Equipment control
await controller.set_circuit_state("POOL", True)
await controller.set_circuit_state("SPA", False)
await controller.set_heat_mode("B1101", HeaterType.HEATER)
await controller.set_setpoint("B1101", 84)
await controller.set_super_chlorinate("C0001", True)
await controller.set_light_effect("C0003", "PARTY")

# Batch operations
await controller.set_multiple_circuit_states(["AUX1", "AUX2"], True)

# Entity getters
bodies = controller.get_bodies()
circuits = controller.get_circuits()
pumps = controller.get_pumps()
heaters = controller.get_heaters()
sensors = controller.get_sensors()
schedules = controller.get_schedules()
lights = controller.get_lights()
color_lights = controller.get_color_lights()
chem_controllers = controller.get_chem_controllers()
valves = controller.get_valves()

# All entities grouped by type (for Home Assistant discovery)
entities = controller.get_all_entities()
# Returns: {"bodies": [...], "circuits": [...], "lights": [...], ...}

# Temperature helpers
unit = controller.get_temperature_unit()  # "F" or "C"
temp = controller.get_body_temperature("B1101")
setpoint = controller.get_body_setpoint("B1101")
heat_mode = controller.get_body_heat_mode("B1101")
is_heating = controller.is_body_heating("B1101")

# Chemistry helpers
ph = controller.get_chem_reading("C0001", "PH")
orp = controller.get_chem_reading("C0001", "ORP")
salt = controller.get_chem_reading("C0001", "SALT")
alerts = controller.get_chem_alerts("C0001")

# Pump helpers
is_running = controller.is_pump_running("P0001")
rpm = controller.get_pump_rpm("P0001")
gpm = controller.get_pump_gpm("P0001")
watts = controller.get_pump_watts("P0001")
metrics = controller.get_pump_metrics("P0001")  # {"rpm": ..., "gpm": ..., "watts": ...}

# Sensor helpers
air_sensors = controller.get_air_sensors()
solar_sensors = controller.get_solar_sensors()
reading = controller.get_sensor_reading("S0001")

# Light helpers
effect = controller.get_light_effect("C0003")
effect_name = controller.get_light_effect_name("C0003")
available = controller.get_available_light_effects("C0003")

# Update callback
def on_update(controller, changes):
    for objnam, attrs in changes.items():
        print(f"{objnam} changed: {attrs}")

controller.set_updated_callback(on_update)

await controller.stop()

ICConnectionHandler

Wraps a controller with automatic reconnection and lifecycle callbacks.

from pyintellicenter import ICConnectionHandler, ICConnectionHandlerCallbacks

callbacks = ICConnectionHandlerCallbacks(
    on_started=lambda: print("Connected!"),
    on_stopped=lambda: print("Stopped"),
    on_disconnected=lambda: print("Disconnected"),
    on_reconnected=lambda: print("Reconnected!"),
    on_retrying=lambda attempt, delay: print(f"Retry {attempt} in {delay}s"),
)

handler = ICConnectionHandler(
    controller,
    callbacks=callbacks,
    time_between_reconnects=30.0,    # Initial reconnect delay
    disconnect_debounce_time=15.0,   # Grace period before disconnect callback
)

# Start connection (waits for first connection)
await handler.start()

# Access the underlying controller
print(handler.controller.system_info.prop_name)

# Check connection state
print(f"Connected: {handler.connected}")

# Stop with cleanup
await handler.stop()

PoolModel

Collection of pool equipment objects.

from pyintellicenter import PoolModel

model = PoolModel()

# Iteration
for obj in model:
    print(f"{obj.objnam}: {obj.sname}")

# Access by object name
pump = model["PUMP1"]

# Get objects by type
bodies = model.get_by_type("BODY")
pools = model.get_by_type("BODY", "POOL")
pumps = model.get_by_type("PUMP")

# Get children of an object
children = model.get_children(panel)

# Object count
print(f"Total objects: {model.num_objects}")

PoolObject

Individual equipment item.

obj = model["PUMP1"]

# Core properties
obj.objnam     # Object name: "PUMP1"
obj.sname      # Friendly name: "Pool Pump"
obj.objtype    # Type: "PUMP"
obj.subtype    # Subtype: "VSF"
obj.status     # Status: "ON" or "OFF"
obj.parent     # Parent object name

# Type checks
obj.is_a_light              # Is this a light?
obj.is_a_light_show         # Is this a light show circuit?
obj.is_featured             # Is this marked as featured?
obj.supports_color_effects  # Supports IntelliBrite effects?
obj.is_on                   # Is status ON?

# Attribute access
rpm = obj["RPM"]
power = obj["PWR"]
temp = obj["TEMP"]

# All attributes
for key in obj.attribute_keys:
    print(f"{key}: {obj[key]}")

Discovery

Find IntelliCenter units on your local network using mDNS/Zeroconf.

from pyintellicenter import (
    discover_intellicenter_units,
    find_unit_by_name,
    find_unit_by_host,
    ICUnit,
)

# Discover all units
units = await discover_intellicenter_units(timeout=5.0)

# With existing Zeroconf instance (for Home Assistant)
from zeroconf import Zeroconf
zc = Zeroconf()
units = await discover_intellicenter_units(timeout=5.0, zeroconf=zc)

# Find specific unit
unit = await find_unit_by_name("My Pool", timeout=5.0)
unit = await find_unit_by_host("192.168.1.100", timeout=5.0)

# ICUnit properties
unit.name      # Friendly name
unit.host      # IP address
unit.port      # TCP port (6681)
unit.ws_port   # WebSocket port (6680)
unit.model     # Model info (if available)

Equipment Types

Type Constant Description Common Subtypes
Body BODY_TYPE Body of water POOL, SPA
Pump PUMP_TYPE Variable speed pump SPEED, FLOW, VSF
Circuit CIRCUIT_TYPE Circuit/Feature GENERIC, LIGHT, INTELLI, GLOW, DIMMER
Heater HEATER_TYPE Heater GENERIC, SOLAR, ULTRA, HYBRID
Chem CHEM_TYPE Chemistry controller ICHLOR, ICHEM
Sensor SENSE_TYPE Temperature sensor POOL, AIR, SOLAR
Schedule SCHED_TYPE Schedule -
Valve VALVE_TYPE Valve -

Heater Modes

from pyintellicenter import HeaterType

HeaterType.OFF              # Heater off
HeaterType.HEATER           # Gas/electric heater only
HeaterType.SOLAR_PREF       # Solar preferred, heater backup
HeaterType.SOLAR_ONLY       # Solar only
HeaterType.ULTRA_TEMP       # UltraTemp heat pump only
HeaterType.ULTRA_TEMP_PREF  # UltraTemp preferred
HeaterType.HYBRID           # Hybrid mode
# ... and more

Light Effects

from pyintellicenter import LIGHT_EFFECTS

# Available effects for IntelliBrite/MagicStream lights
LIGHT_EFFECTS = {
    "PARTY": "Party",
    "ROMAN": "Romance",
    "CARIB": "Caribbean",
    "AMERCA": "American",
    "SSET": "Sunset",
    "ROYAL": "Royalty",
    "BLUER": "Blue",
    "GREENR": "Green",
    "REDR": "Red",
    "WHITER": "White",
    "MAGNTAR": "Magenta",
}

# Set light effect
await controller.set_light_effect("C0003", "PARTY")

Connection Behavior

Connection Flow

  1. Connect: Establishes TCP or WebSocket connection
  2. Initialize: Fetches system info and all equipment objects
  3. Monitor: Receives real-time NotifyList push updates
  4. Keepalive: Sends queries every 90 seconds (configurable)

Reconnection Strategy

  1. Debounce: 15-second grace period before marking disconnected
  2. Exponential Backoff: Starts at 30s, doubles each attempt (max 5 min)
  3. Circuit Breaker: After 5 consecutive failures, pauses for 5 minutes
  4. Reset: Successful connection resets failure counters

Notification Processing

  • Push notifications are queued (default: 100 items max)
  • Queue prevents slow callbacks from blocking I/O
  • When full, oldest notifications are dropped (prefers fresh state)
  • Both sync and async callbacks are supported

Error Handling

from pyintellicenter import (
    ICError,           # Base exception
    ICConnectionError, # Connection failures
    ICResponseError,   # Bad response from IntelliCenter
    ICCommandError,    # Command execution error
    ICTimeoutError,    # Request timeout
)

try:
    await controller.start()
except ICConnectionError as e:
    print(f"Connection failed: {e}")
except ICTimeoutError as e:
    print(f"Request timed out: {e}")

Development

# Clone repository
git clone https://github.com/joyfulhouse/pyintellicenter.git
cd pyintellicenter

# Install with uv (recommended)
uv sync --extra dev

# Run tests
uv run pytest tests/

# Run tests with coverage
uv run pytest tests/ --cov=src/pyintellicenter --cov-report=term-missing

# Linting and formatting
uv run ruff check .
uv run ruff format .

# Type checking
uv run mypy src/pyintellicenter

# Full validation
uv run ruff check --fix . && uv run ruff format . && uv run mypy src/pyintellicenter && uv run pytest tests/

License

MIT License - see LICENSE for details.

Related Projects

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyintellicenter-0.0.5a12.tar.gz (38.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyintellicenter-0.0.5a12-py3-none-any.whl (44.3 kB view details)

Uploaded Python 3

File details

Details for the file pyintellicenter-0.0.5a12.tar.gz.

File metadata

  • Download URL: pyintellicenter-0.0.5a12.tar.gz
  • Upload date:
  • Size: 38.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyintellicenter-0.0.5a12.tar.gz
Algorithm Hash digest
SHA256 12b3f26e08b0b37e6545669bce80fd241385fcce02f5f1bee5abdbba973f3b59
MD5 bd7ee991dda72a935c148a3b56cb6d96
BLAKE2b-256 882e00169da5f2c01f35b75eb3e60e24cb871dc2b783486bef8a253e7faa9065

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyintellicenter-0.0.5a12.tar.gz:

Publisher: publish.yml on joyfulhouse/pyintellicenter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyintellicenter-0.0.5a12-py3-none-any.whl.

File metadata

File hashes

Hashes for pyintellicenter-0.0.5a12-py3-none-any.whl
Algorithm Hash digest
SHA256 87a67d718e8a18ce7ca54381e93f377be3c0b87698cb228f8d80723e00b2e9ad
MD5 b0755a7e208befc1fd3bc00a54c5c77b
BLAKE2b-256 ad90cea33333517f90937b4635a3a37cd176ef4977e33c74b747a5ac45d50282

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyintellicenter-0.0.5a12-py3-none-any.whl:

Publisher: publish.yml on joyfulhouse/pyintellicenter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page