Skip to content

flz/iaqualink-py

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

282 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🏊 iaqualink-py

Asynchronous Python library for Jandy iAqualink pool control systems

License Python

πŸ“– Overview

iaqualink-py is a modern, fully asynchronous Python library for interacting with Jandy iAqualink pool and spa control systems. It provides a clean, Pythonic interface to monitor and control your pool equipment from your Python applications.

✨ Features

  • πŸ”„ Fully Asynchronous - Built with asyncio and httpx for efficient, non-blocking I/O
  • πŸ” 401 Replay for Auth-Bearing Requests - Rebuilds and replays systems discovery and iaqua/exo system requests after auth refresh
  • πŸ—οΈ Multi-System Support
    • iAqua systems (iaqualink.net API)
    • eXO systems (zodiac-io.com API)
    • i2d systems β€” iQPump variable-speed pumps (r-api.iaqualink.net API)
  • 🌑️ Comprehensive Device Support
    • Temperature sensors (pool, spa, air)
    • Thermostats with adjustable set points
    • Pumps and heaters, including paired HPM heat pumps
    • Lights with toggle control
    • ICL (IntelliCenter) lights with color control, custom RGBW, and dimming
    • Auxiliary switches
    • Water chemistry sensors (pH, ORP, salinity)
    • Freeze protection monitoring
  • πŸ”Œ Context Manager Support - Automatic resource cleanup
  • πŸ›‘οΈ Type Safe - Full type hints for modern Python development

πŸ“¦ Installation

pip install iaqualink

Or using uv:

uv add iaqualink

To install the optional CLI as well:

pip install 'iaqualink[cli]'

Or with uv:

uv add 'iaqualink[cli]'

πŸ’» CLI

The optional cli extra installs an iaqualink command for common discovery and control tasks.

Credentials can be provided in this order:

  • command-line options such as --username and --password
  • environment variables IAQUALINK_USERNAME and IAQUALINK_PASSWORD
  • a YAML config file, defaulting to typer.get_app_dir("iaqualink") / "config.yaml"

Example config:

username: user@example.com
password: super-secret-password

Example commands:

# Enable verbose debug logging
iaqualink --debug list-systems

# List the systems on the account
iaqualink list-systems

# Show devices for a system
iaqualink list-devices --system YOUR-SERIAL

# Show a tree view of all systems and devices
iaqualink status

# Turn on a device by key or label
iaqualink turn-on pool_pump --system YOUR-SERIAL

# Change a thermostat set point
iaqualink set-temperature spa_set_point 102 --system YOUR-SERIAL

Session Persistence

The CLI can persist login state across runs so repeated commands do not need to authenticate every time.

By default, session state is stored at typer.get_app_dir("iaqualink") / "session.json", which resolves to an app-specific config directory on macOS, Linux, and Windows.

All commands accept --cookie-jar to override that location:

iaqualink list-systems --cookie-jar ~/.cache/iaqualink/session.json

Session lifecycle:

  1. First run logs in normally and writes the current auth state to the jar.
  2. Later runs restore that state when the saved username matches the requested username.
  3. If a restored session is stale during systems discovery, the CLI reauthenticates and updates the jar automatically.

Security notes:

  • The cookie jar stores authentication tokens in plain text.
  • On shared systems, use a location with appropriate file permissions.
  • If you change credentials or want to force a fresh login, delete the jar file.
  • Use --debug on any command to enable verbose logging while troubleshooting CLI or API behavior.

πŸš€ Quick Start

Basic Usage

from iaqualink import AqualinkClient

async with AqualinkClient('user@example.com', 'password') as client:
    # Discover your pool systems
    systems = await client.get_systems()

    # Get the first system
    system = list(systems.values())[0]
    print(f"Found system: {system.name}")

    # Get all devices
    devices = await system.get_devices()

    # Access specific devices
    pool_temp = devices.get('pool_temp')
    if pool_temp:
        print(f"Pool temperature: {pool_temp.state}Β°F")

    spa_heater = devices.get('spa_heater')
    if spa_heater:
        print(f"Spa heater: {'ON' if spa_heater.is_on else 'OFF'}")

Controlling Devices

# Turn on pool pump
pool_pump = devices.get('pool_pump')
if pool_pump:
    await pool_pump.turn_on()

# Set spa temperature
spa_thermostat = devices.get('spa_set_point')
if spa_thermostat:
    await spa_thermostat.set_temperature(102)

# Toggle pool light
pool_light = devices.get('aux_3')
if pool_light:
    await pool_light.toggle()

Controlling ICL (IntelliCenter) Lights

# Get ICL light zone
icl_light = devices.get('icl_zone_1')
if icl_light:
    # Turn on/off
    await icl_light.turn_on()
    await icl_light.turn_off()

    # Set preset color by name
    await icl_light.set_effect("Emerald Green")

    # Set custom RGBW color (0-255 each)
    await icl_light.set_rgbw(255, 0, 128)
    await icl_light.set_rgbw(100, 200, 255, white=50)

    # Set brightness (0-100)
    await icl_light.set_brightness_percentage(75)

    # Read current state
    print(f"Status: {'ON' if icl_light.is_on else 'OFF'}")
    print(f"Color: {icl_light.effect}")
    print(f"Brightness: {icl_light.brightness_percentage}%")
    print(f"RGBW: {icl_light.rgbw}")

Monitoring System Status

# Update system state
await system.update()

# Check if system is online
if system.online:
    print(f"System {system.name} is online")

    # Get all temperature readings
    for device_name, device in devices.items():
        if 'temp' in device_name and device.state:
            print(f"{device.label}: {device.state}Β°")

πŸ”§ Advanced Usage

Working with Multiple Systems

async with AqualinkClient('user@example.com', 'password') as client:
    systems = await client.get_systems()

    for serial, system in systems.items():
        print(f"System: {system.name} ({serial})")
        print(f"Type: {system.data.get('device_type')}")

        devices = await system.get_devices()
        print(f"Devices: {len(devices)}")

πŸ—οΈ Architecture

The library uses a plugin-style architecture with base classes and system-specific implementations:

  • AqualinkClient - Authentication and system discovery
  • AqualinkSystem - Base class with iAqua, eXO, and i2d implementations
  • AqualinkDevice - Device hierarchy with type-specific subclasses

See CLAUDE.md for detailed architecture documentation.

πŸ§ͺ Development

Setup Development Environment

# Clone the repository
git clone https://github.com/flz/iaqualink-py.git
cd iaqualink-py

# Install dependencies
uv sync --group dev --group test

Running Tests

# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov-report=xml --cov=iaqualink

# Run specific test file
uv run pytest tests/test_client.py

# Run CLI tests
uv run pytest tests/test_cli.py

Code Quality

# Run all pre-commit hooks (ruff, mypy)
uv run pre-commit run --all-files

# Auto-fix linting issues
uv run ruff check --fix .

# Format code
uv run ruff format .

# Type checking
uv run mypy src/

πŸ“‹ Requirements

  • Python 3.14 or higher
  • httpx with HTTP/2 support

πŸ“„ License

This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ”— Links

⚠️ Disclaimer

This is an unofficial library and is not affiliated with or endorsed by Jandy, Zodiac Pool Systems, or Fluidra. Use at your own risk.


Made with ❀️ by Florent Thoumie

About

Asynchronous library for Jandy iAqualink

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors