AsyncMiele
An asynchronous Python client for Miele@Home devices. It's a full rewrite of the home-assistant-miele-mobile project with a focus of decoupling the API from Home Assistant. This library also integrates some functionality from the MieleRESTServer project for those who can't afford running an extra server. It also integrates some convenience functions from the pymiele project, but doesn't require to communicate thru Miele cloud.
This library provides a simple, asynchronous interface to communicate with Miele appliances that support the Miele@Home protocol over local network (e.g., via XKM3100W module or built-in WiFi).
Why a new library?
The original home-assistant-miele-mobile project is a great project, but it's tightly coupled to Home Assistant. Those, who don't use Home Assistant, can't use this project. The original MieleRESTServer project is a great project, but it requires to run an extra REST server, which can be too much to run on a home automation controller. The original pymiele project is a great project, but it requires to communicate thru Miele cloud. It is a very strong limitation for countries, which aren't supported by Miele cloud and for users, who prefer their home automation to be fully isolated on the local network.
The library is a async merge of all 3 projects, mentioned above and is designed to integrate with any home automation system, which supports asyncio.
Features
- Asynchronous API using modern Python async/await syntax
- Connect to Miele devices over local network (no cloud dependency)
- Configuration-driven service architecture for production use
- Clean 3-step device setup process
- Enhanced capability detection and management
- Comprehensive device profile system with JSON storage
- Retrieve device information and status
- Proper error handling and type hints
- Clean, object-oriented design
Installation
pip install asyncmiele
Quick Start
Configuration-Driven Service Integration (Recommended)
For production services, use the configuration-driven approach with JSON profiles:
import asyncio
from asyncmiele import Appliance
async def service_integration():
# Load device from configuration file (includes credentials, capabilities, etc.)
appliance = await Appliance.from_config_file("kitchen_oven_config.json")
# Device is ready to use with full capability checking
if appliance.has_all_capabilities("WAKE_UP", "REMOTE_START"):
await appliance.wake_up()
await appliance.remote_start(allow_remote_start=True)
# Get current status
summary = await appliance.summary()
print(f"Progress: {summary.progress}")
asyncio.run(service_integration())
Manual Client Setup (Development/Testing)
For development or when you need manual control:
import asyncio
from asyncmiele import MieleClient, Appliance
async def manual_setup():
# Create client with stored credentials
client = MieleClient.from_hex(
host="192.168.1.100",
group_id_hex="your_group_id",
group_key_hex="your_group_key"
)
async with client:
# Create appliance instance
appliance = Appliance(client, "000123456789")
# Use appliance
summary = await appliance.summary()
print(f"Progress: {summary.progress}")
asyncio.run(manual_setup())
Device Setup Process
The library provides a streamlined 3-step configuration workflow for setting up new devices:
Step 1: Discover Devices in Setup Mode
Find devices that are ready for configuration:
python scripts/discover_setup_devices.py
Output shows devices with their information:
✅ Device found at 192.168.1.100
📋 Device ID: 000160829578 ← Save this ID for Step 3
🏷️ Device Type: Oven
🔧 Setup Mode: True
🔑 Temporary SSID: Miele_ABC123 ← Use this SSID for Step 2
Step 2: Configure Device WiFi
Important: Before running this script, manually connect your computer to the device's WiFi access point (e.g., "Miele_ABC123" from Step 1).
Configure the device to connect to your home WiFi network:
python scripts/configure_device_wifi.py \
--ssid "YourHomeWiFi" \
--password "your_wifi_password"
This script sends the WiFi configuration directly to the device using the same approach as MieleRESTServer. The device will disconnect from its access point and connect to your WiFi network, receiving a new IP address.
Note: The script assumes you are already connected to the device's temporary WiFi access point. This manual connection step ensures reliable communication during WiFi configuration.
Testing: Use --dry-run to see what configuration would be sent without actually sending it:
python scripts/configure_device_wifi.py --ssid "YourHomeWiFi" --password "your_wifi_password" --dry-run
Step 3: Create Device Profile
Generate the complete device configuration using the device ID from Step 1:
python scripts/create_device_profile.py \
--host 192.168.1.100 \
--device-id 000160829578 \
--device-name "Kitchen Oven" \
--output kitchen_oven_config.json
This script will:
- Generate and provision security credentials
- Detect device capabilities
- Extract the program catalog
- Create a complete JSON configuration file
Configuration File Format
The generated configuration file contains everything needed for service integration:
{
"device_id": "000160829578",
"device_type": "Oven",
"friendly_name": "Kitchen Oven",
"host": "192.168.1.100",
"timeout": 5.0,
"credentials": {
"group_id": "29dab98f50adf5b0",
"group_key": "244232e1c0abd062bf2a5f457834063f23baa8e44b1b7cfeba44c26560bc7ee901cc99d56e865729bbfbcd08fce4dba740cf6ca78dc9faba089b7d956b8bfcfc"
},
"capabilities": {
"supported": ["WAKE_UP", "REMOTE_START", "PROGRAM_CATALOG"],
"failed": ["LIGHT_CONTROL"],
"detection_date": "2024-01-15T10:30:00Z"
},
"program_catalog": {
"device_type": "Oven",
"extraction_method": "dop2_new",
"programs": {
"baking": {
"id": 1,
"name": "Baking",
"options": [
{"id": 101, "name": "Temperature", "min": 30, "max": 250}
]
}
}
}
}
Using Configuration Files in Services
Once you have a configuration file, using the device in your service is simple:
import asyncio
from asyncmiele import Appliance
async def my_service():
# Load complete device configuration
appliance = await Appliance.from_config_file("kitchen_oven_config.json")
# All capabilities are automatically available
if appliance.has_capability("REMOTE_START"):
await appliance.remote_start(allow_remote_start=True)
# Program catalog is automatically loaded
programs = await appliance.get_available_programs()
print(f"Available programs: {list(programs.keys())}")
asyncio.run(my_service())
Device Capabilities
The library provides an enhanced capability system using set-based operations for efficient capability checking:
Capability Checking with Configuration
When using configuration files, capability information is automatically loaded:
import asyncio
from asyncmiele import Appliance, DeviceCapability
async def capability_example():
appliance = await Appliance.from_config_file("device_config.json")
# Check single capability
if appliance.has_capability(DeviceCapability.WAKE_UP):
await appliance.wake_up()
# Check multiple capabilities (all required)
required_caps = [DeviceCapability.WAKE_UP, DeviceCapability.REMOTE_START]
if appliance.has_all_capabilities(*required_caps):
await appliance.wake_up()
await appliance.remote_start(allow_remote_start=True)
# Check multiple capabilities (any will do)
control_caps = [DeviceCapability.REMOTE_START, DeviceCapability.PROGRAM_CONTROL]
if appliance.has_any_capability(*control_caps):
print("Device supports some form of remote control")
# Get all supported capabilities
capabilities = appliance.get_supported_capabilities()
print(f"Supported: {[cap.name for cap in capabilities]}")
asyncio.run(capability_example())
Manual Capability Detection
For manual setup or testing, you can detect capabilities directly:
from asyncmiele import MieleClient
from asyncmiele.capabilities import detect_capabilities_as_sets
async def detect_capabilities():
client = MieleClient.from_hex(host, group_id, group_key)
async with client:
# Detect capabilities and get as sets
supported, failed = await detect_capabilities_as_sets(client, device_id)
print(f"Supported: {[cap.name for cap in supported]}")
print(f"Failed: {[cap.name for cap in failed]}")
asyncio.run(detect_capabilities())
Connection Management
The library includes built-in connection management and health monitoring when using the configuration-driven approach:
import asyncio
from asyncmiele import Appliance
async def connection_management_example():
# Configuration files include connection parameters and timeout settings
appliance = await Appliance.from_config_file("device_config.json")
# Built-in connection management handles:
# - Automatic retries
# - Connection pooling
# - Health monitoring
# - Network failure recovery
# Use appliance normally - connection management is automatic
summary = await appliance.summary()
print(f"Device status: {summary.status}")
asyncio.run(connection_management_example())
DOP2 Tree Visualization
The library includes tools for exploring and visualizing the DOP2 tree structure of Miele devices:
async def dop2_exploration():
appliance = await Appliance.from_config_file("device_config.json")
# Generate HTML visualization
await appliance.visualize_dop2_tree(
output_file="dop2_tree.html",
format_type="html",
known_only=True # Only explore known attributes
)
# Generate ASCII visualization for console
await appliance.visualize_dop2_tree(
output_file="dop2_tree.txt",
format_type="ascii"
)
asyncio.run(dop2_exploration())
You can also use the standalone script:
# Explore device DOP2 tree
python scripts/visualize_dop2_tree.py --config device_config.json --output-dir ./output
Migration from Manual Setup
If you have existing code using manual client setup, migration to the configuration-driven approach is straightforward:
Before (Manual Setup):
# Old manual approach
client = MieleClient.from_hex(host, group_id, group_key)
appliance = Appliance(client, device_id)
# Manual capability checking
if await client.has_capability(device_id, DeviceCapability.REMOTE_START):
await client.remote_start(device_id, allow_remote_start=True)
After (Configuration-Driven):
# New configuration-driven approach
appliance = await Appliance.from_config_file("device_config.json")
# Automatic capability checking
if appliance.has_capability(DeviceCapability.REMOTE_START):
await appliance.remote_start(allow_remote_start=True)
Creating Configuration from Existing Credentials
If you have existing credentials, create a configuration file:
from asyncmiele.models.device_profile import DeviceProfile
from asyncmiele.models.credentials import MieleCredentials
from asyncmiele.config.loader import save_device_profile
# Create profile from existing credentials
profile = DeviceProfile(
device_id="000123456789",
host="192.168.1.100",
timeout=5.0,
credentials=MieleCredentials(
group_id="your_group_id_hex",
group_key="your_group_key_hex"
)
)
# Save as configuration file
save_device_profile(profile, "device_config.json")
# Now use with configuration-driven approach
appliance = await Appliance.from_config_file("device_config.json")
Troubleshooting
Configuration Issues
- Invalid JSON: Use a JSON validator to check your configuration file format
- Missing credentials: Ensure the configuration file includes valid
group_idandgroup_key - Network connectivity: Verify the device's IP address in the
hostfield is correct and reachable
Device Setup Issues
- Device not found in Step 1: Make sure the device is in setup mode (usually by holding a button)
- WiFi configuration fails in Step 2:
- Ensure you are manually connected to the device's WiFi access point before running the script
- Verify the WiFi credentials and security type are correct
- Check that the device is reachable at 192.168.1.1 (or use --device-ip for different addresses)
- Profile creation fails in Step 3: Check network connectivity and device accessibility
Runtime Issues
- Device not responding: The device may be in sleep mode - capability checking will automatically wake it
- Remote start fails: Verify the device has
REMOTE_STARTcapability andallow_remote_start=Trueis set - Program start fails: Ensure the device has
PROGRAM_CONTROLcapability and a valid program catalog
For comprehensive troubleshooting, use the diagnostic script:
python scripts/device_diagnosis.py --config device_config.json
Acknowledgments
This project is based on reverse-engineering efforts of the Miele@Home protocol and inspired by the home-assistant-miele-mobile project. It has been refactored to be independent of Home Assistant and provide a clean, asynchronous API with configuration-driven service architecture.
License
MIT
Error Handling
The library provides detailed exception classes for proper error handling:
import asyncio
from asyncmiele import Appliance
from asyncmiele.exceptions.network import NetworkConnectionError, NetworkTimeoutError
from asyncmiele.exceptions.api import DeviceNotFoundError
from asyncmiele.exceptions.config import InvalidConfigurationError
async def handle_errors():
try:
# Configuration-driven approach handles most connection issues automatically
appliance = await Appliance.from_config_file("device_config.json")
await appliance.wake_up()
except InvalidConfigurationError as e:
print(f"Configuration error: {e}")
except NetworkConnectionError as e:
print(f"Connection failed: {e}")
except NetworkTimeoutError as e:
print(f"Request timed out: {e}")
except DeviceNotFoundError as e:
print(f"Device not found: {e}")
asyncio.run(handle_errors())
Waking Up a Device
If a Miele appliance has gone into power-saving "sleep" mode it returns invalid data.
Use the wake_up() helper to bring it online again:
# Configuration-driven approach
appliance = await Appliance.from_config_file("device_config.json")
await appliance.wake_up()
# Or with manual client
await client.wake_up("000123456789") # device route / ID
This sends {"DeviceAction": 2} to the device and usually completes with an empty 204-No-Content response.
Remote-Start (opt-in)
Starting a program remotely can be dangerous if the appliance is not prepared correctly.
For this reason remote-start is disabled by default. Enable it in one of two ways:
from asyncmiele.config import settings
settings.enable_remote_start = True # global once-per-process
or pass an explicit override flag per call:
await appliance.remote_start(allow_remote_start=True)
Before attempting a start you can check whether the appliance is ready:
if await appliance.can_remote_start():
await appliance.remote_start(allow_remote_start=True)
else:
print("Device not ready for remote start")
Remote-start issues {"ProcessAction": 1} to the /State resource. The device must already have a fully programmed cycle and expose the RemoteEnable flag (15).
Enhanced Appliance Class Features
The library provides an enhanced Appliance class that offers comprehensive device management:
Property Accessors
Convenient property accessors for common state information:
async def monitor_appliance():
appliance = await Appliance.from_config_file("device_config.json")
# Check if a program is running
if await appliance.is_running:
# Get the current program phase
phase = await appliance.program_phase
print(f"Current phase: {phase}")
# Get the remaining time in minutes
remaining = await appliance.remaining_time
print(f"Remaining time: {remaining} minutes")
# Get temperature information
current_temp = await appliance.current_temperature
target_temp = await appliance.target_temperature
print(f"Temperature: {current_temp}°C / Target: {target_temp}°C")
asyncio.run(monitor_appliance())
State Change Notifications
Monitor state changes with callbacks:
async def on_state_change(state):
print(f"State changed: {state}")
async def on_program_finished():
print("Program finished!")
# Send notification or trigger other actions
async def monitor_with_callbacks():
appliance = await Appliance.from_config_file("device_config.json")
# Register callbacks
await appliance.register_state_callback(on_state_change)
await appliance.register_program_finished_callback(on_program_finished)
# Later, unregister when no longer needed
await appliance.unregister_state_callback(on_state_change)
asyncio.run(monitor_with_callbacks())
Program Control with Configuration
With configuration files, program catalogs are automatically loaded:
async def program_control():
appliance = await Appliance.from_config_file("device_config.json")
# Get available programs (loaded from configuration)
programs = await appliance.get_available_programs()
print(f"Available programs: {list(programs.keys())}")
# Start a program with options
if appliance.has_capability("PROGRAM_CONTROL"):
await appliance.start_with_options(
"Normal",
temperature=60,
spin_speed=1200,
extra_rinse=True
)
print("Program started")
asyncio.run(program_control())
Configuration Validation
The library includes comprehensive configuration validation:
from asyncmiele.validation.config import ConfigurationValidator
async def validate_config():
validator = ConfigurationValidator()
# Load and validate a configuration file
profile = load_device_profile("device_config.json")
result = await validator.validate_profile(profile)
if result.success:
print("✅ Configuration is valid")
else:
print("❌ Configuration issues found:")
for issue in result.issues:
print(f" - {issue}")
asyncio.run(validate_config())
Utility Scripts
Configuration Management Scripts
The library includes streamlined scripts for device setup:
Discovery and Setup:
# Step 1: Find devices in setup mode
python scripts/discover_setup_devices.py
# Step 2: Configure WiFi (manual connection to device required)
python scripts/configure_device_wifi.py --ssid "YourWiFi" --password "password"
# Step 3: Create complete profile
python scripts/create_device_profile.py --host 192.168.1.100 --device-id 000123456789 --output config.json
Diagnostic and Maintenance:
# Device diagnosis and troubleshooting
python scripts/device_diagnosis.py --config device_config.json
# Factory reset (if needed)
python scripts/device_factory_reset.py --config device_config.json
# DOP2 tree exploration (development)
python scripts/dop2_explorer.py --config device_config.json
python scripts/visualize_dop2_tree.py --config device_config.json
Metadata
Release files for asyncmiele 0.2.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| asyncmiele-0.2.6.tar.gz | 143.2 kB | With provenance | Details for asyncmiele-0.2.6.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| asyncmiele-0.2.6-py3-none-any.whl | Python 3 | none | any | Details for asyncmiele-0.2.6-py3-none-any.whl |
Total release size: 307.9 kB
Release files / asyncmiele-0.2.6.tar.gz
| Download URL | asyncmiele-0.2.6.tar.gz |
|---|---|
| Size | 143.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
10d698acf579dfd44a5b49431ba2e0262cd8edb32702a692f6a9402290dcfde9
|
|
BLAKE2b-256 checksum How to use checksums |
5a9f9eaa4e043dd6d143580400f5851b1f93cf737c225cffcd6d04c86e639ddf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 8, 2025.
Transparency logRelease files / asyncmiele-0.2.6-py3-none-any.whl
| Download URL | asyncmiele-0.2.6-py3-none-any.whl |
|---|---|
| Size | 164.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5ecadc3869d6618fafa56f61f87b592fd3cfeee6241a301c3c2114c7d10dbade
|
|
BLAKE2b-256 checksum How to use checksums |
1b26a9deb87cd5094d0a29d0f3eeed175623a0efbfbe0517ce205fff87af05cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 8, 2025.
Transparency logSecurity for asyncmiele 0.2.6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for asyncmiele-0.2.6.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 8, 2025.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.