Python SDK for controlling Alnor ventilation devices via Modbus TCP and cloud API
Project description
🤖 AI-Generated Code: This entire SDK was generated by Claude Code. All code, documentation, tests, and CI/CD infrastructure were created through AI assistance.
Alnor SDK
Python SDK for controlling Alnor ventilation devices via Modbus TCP (local) or Cloud API (remote).
⚠️ Development Status: This SDK is in active development and requires Python 3.13+ for modern type system features.
⚠️ Testing Status: Currently tested only with HRU-PremAir-450 heat recovery unit via Cloud API. Modbus TCP functionality is completely untested and may not work correctly. If you test with Modbus or other devices, please report your results!
🚀 Quick Start
New to the SDK? Check out the Quick Start Guide for a 5-minute tutorial!
from alnor_sdk import AlnorClient, ProductType
async with AlnorClient() as client:
device = await client.connect("my_hru", "192.168.1.100",
ProductType.HEAT_RECOVERY_UNIT)
state = await client.get_state(device.device_id)
print(f"Temperature: {state.indoor_temperature}°C, Speed: {state.speed}%")
Features
- Two connection methods: Local Modbus TCP or cloud API
- Command-line interface: Control devices from your terminal
- Async/await API for non-blocking I/O
- Tested devices:
- HRU-PremAir-450 (Heat Recovery Unit) via Cloud API
- Device discovery and connection management
- Type-safe models with Pydantic
- Comprehensive error handling
- Protocol-based architecture for flexibility
Installation
SDK Only
pip install alnor-sdk
With CLI Tool
# Install with command-line interface
pip install alnor-sdk[cli]
# Or with interactive TUI (Terminal UI)
pip install alnor-sdk[cli,tui]
For Development
pip install alnor-sdk[dev]
CLI Tool
The SDK includes a powerful command-line interface for controlling devices:
# Connect to a device
alnor device connect 192.168.1.100 living_room --type HRU
# Check device status
alnor device status living_room
# Control device speed
alnor control speed living_room 75
# Set ventilation mode
alnor control mode living_room auto
# Monitor device in real-time
alnor monitor living_room
# Zone management
alnor zone create upstairs
alnor zone add upstairs living_room
alnor zone control upstairs 60
Features:
- Beautiful terminal output with colors and tables
- JSON output for scripting and automation
- Real-time monitoring with live updates
- Zone management for controlling multiple devices
- Configuration persistence across sessions
See the CLI Documentation for complete usage guide.
Connection Methods
Local Modbus TCP (Direct Connection)
Connect directly to Alnor devices on your local network using the Modbus TCP protocol on port 502. This method:
- Requires devices to have local Modbus enabled
- Works without internet connection
- Provides fastest response times
- Best for home automation systems
Cloud API (Remote Connection)
Connect to devices through the Alnor cloud gateway at api.connect2myhome.eu. This method:
- Requires Alnor cloud account (same as mobile app)
- Works from anywhere with internet
- Goes through internet gateway device
- No special firewall configuration needed
Note: Many Alnor internet gateways only support cloud mode by default. If port 502 is closed on your device, use the cloud API method.
Quick Start
Local Modbus TCP Connection
import asyncio
from alnor_sdk import AlnorClient, ProductType
async def main():
async with AlnorClient() as client:
# Connect to a device
device = await client.connect(
device_id="living_room_hrv",
host="192.168.1.100",
product_type=ProductType.HEAT_RECOVERY_UNIT
)
# Set ventilation speed to 75%
await client.set_speed(device.device_id, 75)
# Get current state
state = await client.get_state(device.device_id)
print(f"Speed: {state.speed}%")
print(f"Indoor temperature: {state.indoor_temperature}°C")
print(f"Filter days remaining: {state.filter_days_remaining}")
asyncio.run(main())
Cloud API Connection
Option 1: Using Environment Variables (Recommended)
Set environment variables:
export ALNOR_CLOUD_USERNAME=your_email@example.com
export ALNOR_CLOUD_PASSWORD=your_password
Then use the client without passing credentials:
import asyncio
from alnor_sdk.communication import AlnorCloudApi, CloudClient
from alnor_sdk.controllers import HeatRecoveryUnitController
from alnor_sdk.models.enums import VentilationMode
async def main():
# Credentials loaded automatically from environment variables
async with AlnorCloudApi() as api:
await api.connect()
# Get your bridges (gateways)
bridges = await api.get_bridges()
bridge_id = bridges[0]["bridgeId"]
# Get devices
devices = await api.get_devices(bridge_id)
device_id = devices[0]["deviceId"]
product_id = devices[0]["productId"]
# Create register-based client and controller
client = CloudClient(api, device_id)
controller = HeatRecoveryUnitController(client, device_id, product_id)
# Set ventilation mode
await controller.set_mode(VentilationMode.HOME)
# Get current state
state = await controller.get_state()
print(f"Speed: {state.speed}%")
print(f"Indoor temperature: {state.indoor_temperature}°C")
print(f"Filter: {state.filter_days_remaining} days remaining")
asyncio.run(main())
Option 2: Passing Credentials Directly
async with AlnorCloudApi("your_email@example.com", "your_password") as api:
await api.connect()
# ... rest of code
See examples/cloud_control.py for a complete cloud API example.
Supported Devices
⚠️ Testing Status: Only the HRU-PremAir-450 has been tested and verified to work. All other devices listed below are based on product IDs found in the SDK but remain completely untested. If you test any of these devices, please report your results!
Exhaust Fans
- ⚠️ VMC-02VJ04 (Product ID: 0001c844) - UNTESTED
Heat Recovery Units
- ✅ HRU-PremAir-450 - TESTED & WORKING (via Cloud API)
- ⚠️ VMD-02RMS37-2 (Product ID: 0001c89f) - UNTESTED
- ⚠️ VMD-02RPS54 (Product ID: 0001c84f) - UNTESTED
- ⚠️ VMD-02RPS66 (Product ID: 0001c88e) - UNTESTED
- ⚠️ VMD-02RPS78 (Product ID: 0001c8a0) - UNTESTED
CO2 Sensors
- ⚠️ VMS-02C05 (Product ID: 0001c845) - UNTESTED
- ⚠️ VMI-02MC02 (Product ID: 0001c86c) - UNTESTED
Humidity Sensors
- ⚠️ VMS-02HB04 (Product ID: 0001c846) - UNTESTED
- ⚠️ VMI-02MPH04 (Product ID: 0001c86a) - UNTESTED
Usage Examples
Device Discovery
async with AlnorClient() as client:
# Scan network for devices
devices = await client.discover_devices([
"192.168.1.100",
"192.168.1.101",
"192.168.1.102"
])
for device in devices:
print(f"Found: {device.device_id} at {device.host}")
Reading Sensors
async with AlnorClient() as client:
# Connect to CO2 sensor
device = await client.connect(
device_id="bedroom_co2",
host="192.168.1.101",
product_type=ProductType.CO2_SENSOR_VMS
)
# Read CO2 level
co2_level = await client.read_co2(device.device_id)
print(f"CO2: {co2_level} ppm")
# Connect to humidity sensor
device = await client.connect(
device_id="bathroom_humidity",
host="192.168.1.102",
product_type=ProductType.HUMIDITY_SENSOR_VMS
)
# Read temperature and humidity
temperature, humidity = await client.read_humidity(device.device_id)
print(f"Temperature: {temperature}°C, Humidity: {humidity}%")
Zone Management
async with AlnorClient() as client:
# Connect devices
await client.connect("living_room", "192.168.1.100", ProductType.HEAT_RECOVERY_UNIT)
await client.connect("bedroom", "192.168.1.101", ProductType.EXHAUST_FAN)
# Create zone
zone = await client.create_zone("upstairs", "Upstairs Zone")
# Add devices to zone
await client.add_device_to_zone("upstairs", "living_room")
await client.add_device_to_zone("upstairs", "bedroom")
# Control all devices in zone
await client.control_zone("upstairs", speed=60)
Ventilation Modes
from alnor_sdk import VentilationMode
async with AlnorClient() as client:
device = await client.connect("hrv", "192.168.1.100", ProductType.HEAT_RECOVERY_UNIT)
# Set mode
await client.set_mode(device.device_id, VentilationMode.AUTO)
# Available modes:
# - VentilationMode.AWAY (low speed)
# - VentilationMode.HOME (medium speed)
# - VentilationMode.AUTO (automatic)
# - VentilationMode.PARTY (high speed)
Architecture
Protocol
- Modbus TCP: Industry-standard protocol for device communication
- Port: 502 (default Modbus TCP port)
- Registers: Holding registers (40001-49999 range)
Models
- Device: Physical device representation
- DeviceState: Current state including sensor readings
- Product: Product catalog information
- Zone: Logical grouping of devices
Controllers
- ExhaustFanController: Controls exhaust fan devices
- HeatRecoveryUnitController: Controls heat recovery units
- SensorController: Reads sensor data
Development
Setup
git clone https://github.com/nashant/alnor-sdk
cd alnor-sdk
python -m venv venv
source venv/bin/activate
pip install -e .[dev]
Testing
# Run tests
pytest
# Run with coverage
pytest --cov=alnor_sdk --cov-report=html
# Type checking
mypy src/alnor_sdk
# Linting
pylint src/alnor_sdk
# Formatting
black src/ tests/
isort src/ tests/
Building
python -m build
Protocol Details
Modbus Register Mapping
Exhaust Fan (VMC-02VJ04)
41001: Fan speed (read)41003: Ventilation speed (read)41051: Ventilation speed set (write)
Heat Recovery Unit (VMD-02RMS37-2, etc.)
41000: Ventilation speed (read)41001-41002: Fan speeds (read)41005-41011: Temperatures (read, value/10)41040: Filter days remaining (read)41500: Ventilation speed set (write)
Sensors
- CO2:
41000(read, ppm) - Humidity:
41000(temperature, value/10),41001(humidity %)
Troubleshooting
Local Modbus Connection Issues
- Ensure device is powered and connected to network
- Verify IP address and port 502 is accessible
- Check firewall settings
- Port 502 closed? Your gateway may only support cloud mode - use Cloud API instead
Cloud Authentication Issues
- Error 10103 (Invalid credentials):
- Verify username/password in the Alnor mobile app
- Check for verification emails from
alnor.euorconnect2myhome.eu - Try resetting password in the mobile app
- Ensure account is verified (click email verification link)
- Error 10100 (User not found):
- Create account in the Alnor mobile app first
- Error 10102 (Account locked):
- Contact Alnor support to unlock account
Timeout Errors
- Increase timeout:
AlnorClient(timeout=10.0)orAlnorCloudApi(username, password, timeout=15.0) - Check network latency
- For cloud API: Verify gateway is online and connected
Register Read/Write Errors
- Verify device type matches ProductType
- Check Modbus register addresses for your device model
- For cloud API: Check device is connected to gateway
Testing
The SDK includes comprehensive unit and integration tests.
Running Unit Tests
Unit tests require no external dependencies (devices or network):
# Run all unit tests
pytest tests/unit -m unit -v
# Run with coverage report
pytest tests/unit --cov=alnor_sdk --cov-report=html
# View coverage report
open htmlcov/index.html
Running Integration Tests
Integration tests require real devices and credentials:
# Set up environment variables
export ALNOR_DEVICE_HOST=192.168.1.100
export ALNOR_CLOUD_USERNAME=your_email@example.com
export ALNOR_CLOUD_PASSWORD=your_password
# Run integration tests
pytest tests/integration -m integration -v
# Run all tests
pytest -v
Required environment variables for integration tests:
ALNOR_DEVICE_HOST: IP address of your Modbus deviceALNOR_CLOUD_USERNAME: Your Alnor cloud emailALNOR_CLOUD_PASSWORD: Your Alnor cloud password
Test Markers
Tests are organized using pytest markers:
@pytest.mark.unit: Fast tests with no external dependencies@pytest.mark.integration: Tests requiring real devices/network@pytest.mark.slow: Long-running tests
# Run only fast unit tests
pytest -m unit
# Run everything except slow tests
pytest -m "not slow"
License
MIT License - see LICENSE file for details
Project Structure
alnor-sdk/
├── src/alnor_sdk/ # Main SDK source code
│ ├── communication/ # Modbus and Cloud API clients
│ ├── controllers/ # Device controllers
│ ├── models/ # Pydantic data models
│ └── utils/ # Utility functions and constants
├── tests/ # Test suite
│ ├── unit/ # Unit tests (no external dependencies)
│ └── integration/ # Integration tests (requires devices)
├── examples/ # Usage examples
│ ├── basic_connection.py
│ ├── cloud_control.py
│ └── ...
├── scripts/ # Development/debug scripts (NOT part of SDK)
│ ├── test_*.py # Debug/integration scripts
│ └── README.md # ⚠️ See warning about credentials
└── docs/ # Documentation
Note: The scripts/ directory contains development and debugging scripts that may have hardcoded credentials. These are not part of the official SDK API. Use the examples/ directory for proper usage examples.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Submit a pull request
Disclaimer
This SDK is an unofficial implementation based on reverse engineering the Alnor Android app. Use at your own risk. The author is not affiliated with Alnor.
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 alnor_sdk-0.1.0.tar.gz.
File metadata
- Download URL: alnor_sdk-0.1.0.tar.gz
- Upload date:
- Size: 46.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8bf5b715a668eb9bb031f08dec46406fce98cf20c7d8bf3fe3cfba3d8b9a4224
|
|
| MD5 |
e306d08619cd79e1936eeffbddb40492
|
|
| BLAKE2b-256 |
4fca838d1c94fc8c5915a98c68cbf5902e63db10e927cdd37f5664ff9543e196
|
File details
Details for the file alnor_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: alnor_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 55.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c95adaeb5a26b64325e6d3c4905a5c78e3ffa987fe55d710a3b75d3d2df8407
|
|
| MD5 |
ba57b4b690efafe5128086dac7f7a6b1
|
|
| BLAKE2b-256 |
d0d37a4e5ab565cb9d8beb6c3ebffc3203f9691f53c856dab818cfc1bb25c3f3
|