Python library for interacting with SenzWifi (Pentair Thermal WiFi) API to control thermostats
Project description
python-senz-wifi-module
Python library for interacting with the SenzWifi (Pentair Thermal WiFi) API to control thermostats.
Features
- Async-first — built on
httpxfor efficient async operations - Authentication & session management — automatic retry on session expiry
- Thermostat control — list, read, and update thermostat settings
- Convenience methods — set temperature, turn off, boost mode
- Real-time notifications — long-polling for thermostat state changes
- CLI tool — built-in command-line interface for quick operations
- Fully typed — type hints throughout with
py.typedmarker
Installation
pip install senzwifi
Or install from source:
git clone https://github.com/sockless-coding/python-senz-wifi-module.git
cd python-senz-wifi-module
pip install -e .
Quick Start
Using the Library
import asyncio
from senzwifi import AsyncSenzWifi, RegulationMode
async def main():
# Create client with credentials
async with AsyncSenzWifi("your@email.com", "your-password") as client:
# Authenticate
await client.authenticate()
# List all thermostats
response = await client.get_thermostats()
for group in response.groups:
print(f"Group: {group.group_name}")
for t in group.thermostats:
print(f" {t.room}: {t.temperature_celsius:.1f}°C (mode: {RegulationMode(t.regulation_mode).name})")
# Set a thermostat to 21°C manually
await client.set_manual_temperature("SN123456789", 21.0)
# Turn off a thermostat
await client.turn_off("SN123456789")
# Start boost mode (3 hours by default)
await client.start_boost("SN123456789")
asyncio.run(main())
Using the CLI
# List all thermostats
senzwifi --username "your@email.com" --password "your-password" list
# Get a specific thermostat
senzwifi --username "your@email.com" --password "your-password" get SN123456789
# Set temperature to 21°C
senzwifi --username "your@email.com" --password "your-password" set SN123456789 21.0
# Turn off a thermostat
senzwifi --username "your@email.com" --password "your-password" off SN123456789
# Start boost mode
senzwifi --username "your@email.com" --password "your-password" boost SN123456789
API Reference
AsyncSenzWifi
The main client class. Supports use as an async context manager.
async with AsyncSenzWifi(email, password) as client:
await client.authenticate()
# ... use the client
Constructor
| Parameter | Type | Default | Description |
|---|---|---|---|
email |
str |
— | Account email address |
password |
str |
— | Account password |
max_retries |
int |
3 |
Maximum retry attempts for failed requests |
timeout |
float |
30.0 |
Request timeout in seconds |
Methods
authenticate() -> AuthResponse
Authenticate and obtain a session ID.
auth = await client.authenticate()
if auth.is_success:
print(f"Logged in as {auth.email}")
get_thermostats() -> ThermostatsResponse
Fetch all thermostats grouped by their group.
response = await client.get_thermostats()
for group in response.groups:
print(f"{group.group_name}: {len(group.thermostats)} devices")
# Get all thermostats flat
all_thermostats = response.get_all_thermostats()
# Find a specific thermostat
t = response.get_thermostat("SN123456789")
get_thermostat(serial_number: str) -> Thermostat
Get a specific thermostat by serial number.
t = await client.get_thermostat("SN123456789")
print(f"{t.room}: {t.temperature_celsius:.1f}°C")
set_manual_temperature(serial_number: str, temperature_celsius: float) -> UpdateThermostatResponse
Set a thermostat to a specific temperature in manual mode.
result = await client.set_manual_temperature("SN123456789", 21.5)
print(f"Success: {result.success}")
turn_off(serial_number: str) -> UpdateThermostatResponse
Turn off a thermostat.
result = await client.turn_off("SN123456789")
start_boost(serial_number: str, end_time: datetime | None = None) -> UpdateThermostatResponse
Start boost (comfort) mode. Defaults to 3 hours from now if no end time is specified.
from datetime import datetime, timedelta, timezone
# Boost for 3 hours (default)
await client.start_boost("SN123456789")
# Boost until a specific time
end = datetime.now(timezone.utc) + timedelta(hours=1)
await client.start_boost("SN123456789", end_time=end)
update_thermostat(serial_number: str, thermostat: Thermostat) -> UpdateThermostatResponse
Update a thermostat with custom settings.
t = await client.get_thermostat("SN123456789")
t.regulation_mode = RegulationMode.SCHEDULE
t.manual_temperature = 2000 # 20.0°C in raw format
result = await client.update_thermostat("SN123456789", t)
wait_for_notification(timeout: float = 300.0) -> Notification | None
Long-poll for thermostat state change notifications.
notification = await client.wait_for_notification(timeout=60.0)
if notification:
print(f"Change detected: {notification.action} on {notification.thermostat.room}")
Models
RegulationMode
RegulationMode.SCHEDULE # 1 — Use the configured schedule
RegulationMode.BOOST # 2 — Boost / comfort temp mode
RegulationMode.MANUAL # 3 — Constant temp mode
RegulationMode.OFF # 5 — Off
Thermostat
Key properties (all temperatures available in both raw and Celsius):
| Property | Type | Description |
|---|---|---|
serial_number |
str |
Device serial number |
room |
str |
Room name |
group_name |
str |
Group name |
temperature_celsius |
float |
Current temperature |
regulation_mode |
int |
Current regulation mode |
online |
bool |
Whether the device is online |
heating |
bool |
Whether heating is active |
manual_temperature_celsius |
float |
Manual setpoint temperature |
comfort_temperature_celsius |
float |
Comfort/boost temperature |
min_temp_celsius |
float |
Minimum temperature |
max_temp_celsius |
float |
Maximum temperature |
schedules |
list[Schedule] |
Available schedules |
Temperature Conversion Utilities
from senzwifi import temp_to_celsius, celsius_to_temp
# Raw API value (1/100 °C) → Celsius
temp_to_celsius(2100) # 21.0
# Celsius → raw API value
celsius_to_temp(21.0) # 2100
Exceptions
| Exception | Description |
|---|---|
SenzWifiError |
Base exception for all errors |
AuthenticationError |
Authentication failed |
SessionExpiredError |
Session has expired |
DeviceNotFoundError |
Thermostat not found |
APIError |
API request failed |
Example: Monitoring Thermostats
See examples/monitor.py for a complete example that monitors thermostat changes in real time.
import asyncio
from senzwifi import AsyncSenzWifi, RegulationMode
async def monitor():
async with AsyncSenzWifi("your@email.com", "your-password") as client:
await client.authenticate()
# Show initial state
response = await client.get_thermostats()
for t in response.get_all_thermostats():
mode = RegulationMode(t.regulation_mode).name
print(f"[INIT] {t.room}: {t.temperature_celsius:.1f}°C ({mode})")
# Watch for changes
while True:
notification = await client.wait_for_notification()
if notification:
t = notification.thermostat
mode = RegulationMode(t.regulation_mode).name
print(f"[CHANGE] {t.room}: {t.temperature_celsius:.1f}°C ({mode})")
asyncio.run(monitor())
Development
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run linter
ruff check .
# Run type checker
mypy senzwifi
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file senzwifi-2026.6.1.tar.gz.
File metadata
- Download URL: senzwifi-2026.6.1.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4d29dadf0fdf6f72a80df2001fbac3c3171f11bf9f3da5bb32cab11809e1ab1
|
|
| MD5 |
a49469081127fb85c8fd222c4472e046
|
|
| BLAKE2b-256 |
5de85ef0ba4da7092d0369b7d722399f3a12f206d7bd929bd4f96e40a1510f84
|
Provenance
The following attestation bundles were made for senzwifi-2026.6.1.tar.gz:
Publisher:
publish.yml on sockless-coding/python-senz-wifi-module
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
senzwifi-2026.6.1.tar.gz -
Subject digest:
e4d29dadf0fdf6f72a80df2001fbac3c3171f11bf9f3da5bb32cab11809e1ab1 - Sigstore transparency entry: 1884560643
- Sigstore integration time:
-
Permalink:
sockless-coding/python-senz-wifi-module@44e4771f7ab722c006a27c52df9e406f4d57f00a -
Branch / Tag:
refs/tags/2026.6.1 - Owner: https://github.com/sockless-coding
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@44e4771f7ab722c006a27c52df9e406f4d57f00a -
Trigger Event:
release
-
Statement type:
File details
Details for the file senzwifi-2026.6.1-py3-none-any.whl.
File metadata
- Download URL: senzwifi-2026.6.1-py3-none-any.whl
- Upload date:
- Size: 17.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eab9f6283cf7925f2b385ef9ed26403b1f2def5c7bf0b0126c7ae133698170a0
|
|
| MD5 |
96dfed44cc583a5a915891fe8193e0da
|
|
| BLAKE2b-256 |
0d5ec4505907747f9a88d143ff146734032d1fb39d18f11e539c6953a424268d
|
Provenance
The following attestation bundles were made for senzwifi-2026.6.1-py3-none-any.whl:
Publisher:
publish.yml on sockless-coding/python-senz-wifi-module
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
senzwifi-2026.6.1-py3-none-any.whl -
Subject digest:
eab9f6283cf7925f2b385ef9ed26403b1f2def5c7bf0b0126c7ae133698170a0 - Sigstore transparency entry: 1884560828
- Sigstore integration time:
-
Permalink:
sockless-coding/python-senz-wifi-module@44e4771f7ab722c006a27c52df9e406f4d57f00a -
Branch / Tag:
refs/tags/2026.6.1 - Owner: https://github.com/sockless-coding
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@44e4771f7ab722c006a27c52df9e406f4d57f00a -
Trigger Event:
release
-
Statement type: