Python library for controlling Airobot TE1 thermostats via local REST API.
This library is used by the Airobot Home Assistant integration.
- 🔌 Async/await support using asyncio with comprehensive error handling
- 🌡️ Temperature control for HOME and AWAY modes (5-35°C range)
- 🏠 Mode management (HOME/AWAY mode switching)
- ⚙️ Hysteresis control (0.0-0.5°C range)
- 💨 Boost mode for temporary heating (1 hour duration)
- 🔒 Child lock control for safety
- 💡 Sensor monitoring (temperature, humidity, CO2, AQI)
- 🛡️ Type hints for better IDE support and development experience
- ✅ Input validation with range checking for all settings
- 📊 Comprehensive data model with optional sensor support
pip install pyairobotrestBefore using this library, ensure your Airobot thermostat is properly configured:
- Network Connection: Connect the thermostat to your local WiFi/Ethernet network
- Initial Registration: Connect to the internet at least once to register with the server and obtain credentials (Device ID and password)
- Enable Local API: In thermostat settings, navigate to "Connectivity → Local API → Enable"
- Get Credentials: Find your Device ID (username) and password in the thermostat menu under "Mobile app" screen
Note: After initial setup, internet connectivity is not required for local API access.
import asyncio
from pyairobotrest import AirobotClient, AirobotConnectionError
async def main():
# Create client - replace with your thermostat's IP and credentials
client = AirobotClient(
host="192.168.1.100", # or "airobot-thermostat-t01xxxxxx.local"
username="T01XXXXXX", # Your thermostat Device ID
password="your_password" # Password from "Mobile app" screen
)
try:
# Read current status
status = await client.get_statuses()
print(f"Device: {status.device_id}")
print(f"Hardware: v{status.hw_version_string}")
print(f"Firmware: v{status.fw_version_string}")
print(f"Current temperature: {status.temp_air:.1f}°C")
print(f"Target temperature: {status.setpoint_temp:.1f}°C")
print(f"Humidity: {status.hum_air:.1f}%")
print(f"Heating: {'ON' if status.is_heating else 'OFF'}")
# Check optional sensors
if status.has_floor_sensor:
print(f"Floor temperature: {status.temp_floor:.1f}°C")
if status.has_co2_sensor:
print(f"CO2: {status.co2} ppm (AQI: {status.aqi})")
# Read current settings
settings = await client.get_settings()
print(f"Mode: {'HOME' if settings.mode == 1 else 'AWAY'}")
print(f"HOME temp: {settings.setpoint_temp}°C")
print(f"AWAY temp: {settings.setpoint_temp_away}°C")
# Control the thermostat
await client.set_home_temperature(23.0) # Set HOME temperature
await client.set_mode(1) # Switch to HOME mode
await client.set_boost_mode(True) # Enable boost for 1 hour
except AirobotConnectionError as err:
print(f"Connection error: {err}")
finally:
await client.close()
asyncio.run(main())import asyncio
from pyairobotrest import AirobotClient
async def main():
try:
# Using async context manager automatically handles connection cleanup
async with AirobotClient(
host="192.168.1.100",
username="T01XXXXXX",
password="your_password"
) as client:
# Read status
status = await client.get_statuses()
print(f"Temperature: {status.temp_air:.1f}°C")
print(f"Heating: {status.is_heating}")
# Configure thermostat
await client.set_home_temperature(22.5)
await client.set_hysteresis_band(0.3)
await client.set_child_lock(True)
except Exception as err:
print(f"Error: {err}")
asyncio.run(main())For explicit session initialization, use the factory method pattern:
import asyncio
from pyairobotrest import AirobotClient
async def main():
# Factory method ensures session is initialized before use
client = await AirobotClient.create(
host="192.168.1.100",
username="T01XXXXXX",
password="your_password"
)
try:
status = await client.get_statuses()
print(f"Temperature: {status.temp_air:.1f}°C")
finally:
await client.close()
asyncio.run(main())| Method | Description | Returns |
|---|---|---|
AirobotClient() |
Standard constructor | AirobotClient |
AirobotClient.create() |
Factory method with session pre-initialized | AirobotClient |
| Method | Description | Parameters |
|---|---|---|
get_statuses() |
Read all current measurements | None |
get_settings() |
Read all thermostat settings | None |
set_mode(mode) |
Set HOME/AWAY mode | mode: int (1=HOME, 2=AWAY) |
set_home_temperature(temp) |
Set HOME temperature | temp: float (5.0-35.0°C) |
set_away_temperature(temp) |
Set AWAY temperature | temp: float (5.0-35.0°C) |
set_hysteresis_band(hyst) |
Set temperature hysteresis | hyst: float (0.0-0.5°C) |
set_device_name(name) |
Set device name | name: str (1-20 chars) |
set_child_lock(enabled) |
Enable/disable child lock | enabled: bool |
set_boost_mode(enabled) |
Enable/disable boost mode | enabled: bool |
reboot_thermostat() |
Reboot the thermostat | None |
recalibrate_co2_sensor() |
Recalibrate CO2 sensor | None |
toggle_actuator_exercise(disabled) |
Enable/disable actuator test | disabled: bool |
@dataclass
class ThermostatStatus:
# Device identification
device_id: str # Unique device ID
hw_version: int # Hardware version (raw)
fw_version: int # Firmware version (raw)
# Temperature measurements
temp_air: float # Air temperature in °C
temp_floor: float | None # Floor temperature in °C (None if not attached)
setpoint_temp: float # Target temperature in °C
# Environmental sensors
hum_air: float # Relative humidity in %
co2: int | None # CO2 in ppm (None if not equipped)
aqi: int | None # Air quality index 0-5 (None if no CO2)
# Status
is_heating: bool # True if actively heating
has_error: bool # True if error present
error_code: int # Error code (0 = no error)
uptime: int # Device uptime in seconds
# Sensor availability
has_floor_sensor: bool # True if floor sensor attached
has_co2_sensor: bool # True if CO2 sensor equipped
# Version properties (human-readable)
hw_version_string: str # Hardware version (e.g., "1.11")
fw_version_string: str # Firmware version (e.g., "1.11")@dataclass
class ThermostatSettings:
device_id: str # Unique device ID
mode: int # 1=HOME, 2=AWAY
setpoint_temp: float # HOME temperature in °C
setpoint_temp_away: float # AWAY temperature in °C
hysteresis_band: float # Temperature hysteresis in °C
device_name: str # Device name (1-20 characters)
# Setting flags
setting_flags: SettingFlags
childlock_enabled: bool # Child lock status
boost_enabled: bool # Boost mode status (1 hour)
reboot: bool # Reboot flag
recalibrate_co2: bool # CO2 calibration flag
actuator_exercise_disabled: bool # Actuator exercise statusfrom pyairobotrest import (
AirobotConnectionError, # Connection issues
AirobotAuthError, # Authentication failures
AirobotTimeoutError, # Timeout errors
AirobotError, # General API errors
)
try:
async with AirobotClient("192.168.1.100", "T01XXXXXX", "password") as client:
status = await client.get_statuses()
except AirobotAuthError:
print("Authentication failed - check username/password")
except AirobotConnectionError:
print("Failed to connect to thermostat")
except AirobotTimeoutError:
print("Operation timed out")
except AirobotError as err:
print(f"API error: {err}")# Clone the repository
git clone https://github.com/mettolen/pyairobotrest.git
cd pyairobotrest
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install in development mode with dev dependencies
pip install -e ".[dev]"# Run all tests with coverage
pytest --cov=pyairobotrest --cov-report=term-missing
# Run type checking
mypy src/pyairobotrest
# Run linting and formatting
ruff check src/pyairobotrest
ruff format src/pyairobotrest
# Run pre-commit hooks (if installed)
pre-commit run --all-files- Python 3.11+
- Dependencies:
aiohttp>= 3.9.0 (HTTP client for REST API communication)asyncio(built-in, async/await support)
This library is tested and compatible with:
- Airobot TE1 thermostats with local REST API enabled
- Tested with hardware v1.1 (257) and firmware v1.11 (267)
- Home Assistant integration
- Python 3.11, 3.12, 3.13+
The thermostat reports firmware and hardware versions as encoded integers. The library automatically decodes these to human-readable strings:
status = await client.get_statuses()
# Raw version numbers (as stored in device)
print(f"Raw HW version: {status.hw_version}") # e.g., 267
print(f"Raw FW version: {status.fw_version}") # e.g., 257
# Human-readable version strings (automatically decoded)
print(f"HW version: v{status.hw_version_string}") # e.g., v1.11
print(f"FW version: v{status.fw_version_string}") # e.g., v1.1
# Version encoding: value = major * 256 + minor
# Example: 267 = 1 * 256 + 11 = v1.11
# 257 = 1 * 256 + 1 = v1.1All setter methods validate input values before sending to the API:
# These will raise AirobotError before making API calls
await client.set_home_temperature(40.0) # Error: outside 5-35°C range
await client.set_hysteresis_band(1.0) # Error: outside 0-0.5°C range
await client.set_device_name("x" * 25) # Error: name too long (max 20 chars)
await client.set_mode(3) # Error: invalid mode (must be 1 or 2)For testing or strict data validation scenarios, enable strict mode to raise exceptions for out-of-range sensor values:
from pyairobotrest.models import ThermostatStatus
# Normal mode (default): logs warnings for out-of-range values
status = ThermostatStatus.from_dict(api_response_data)
# Strict mode: raises ValueError for out-of-range values
try:
status = ThermostatStatus.from_dict(api_response_data, strict=True)
except ValueError as e:
print(f"Invalid sensor data: {e}")Use cases for strict mode:
- Unit testing with known good data
- Development and debugging
- Data quality validation pipelines
Default mode (strict=False) is recommended for production as it handles edge cases gracefully by logging warnings.
The thermostat measures air every 30 seconds, which is the minimum recommended polling interval:
import asyncio
async def monitor_temperature():
async with AirobotClient("192.168.1.100", "T01XXXXXX", "password") as client:
while True:
status = await client.get_statuses()
print(f"Temperature: {status.temp_air:.1f}°C")
await asyncio.sleep(30) # Poll every 30 seconds- Check IP address: Ensure the thermostat IP is correct
- Local API enabled: Verify "Connectivity - Local API – Enable" is turned on
- Network connectivity: Ensure the thermostat is on the same network
- Credentials: Check username (Device ID) and password from "Mobile app" screen
# Handle specific error types
try:
await client.get_statuses()
except AirobotAuthError:
print("Authentication failed - check credentials")
except AirobotTimeoutError:
print("Request timed out - thermostat may be offline")
except AirobotConnectionError as err:
if "refused" in str(err).lower():
print("Connection refused - check IP and ensure local API is enabled")
else:
print(f"Connection error: {err}")MIT License - see LICENSE file for details.
Contributions are welcome! Please see CONTRIBUTING.md for detailed guidelines on:
- Setting up your development environment
- Code style and testing requirements
- Submitting pull requests
- Reporting bugs and requesting features
For major changes, please open an issue first to discuss what you would like to change.