šŸš€ Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more →
Socket
Book a DemoInstallSign in
Socket

asyncmiele

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

asyncmiele

Async Python client for Miele@Home devices

0.2.6
PyPI
Maintainers
1

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

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_id and group_key
  • Network connectivity: Verify the device's IP address in the host field 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_START capability and allow_remote_start=True is set
  • Program start fails: Ensure the device has PROGRAM_CONTROL capability 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

FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts