Skip to main content

A modern, fully asynchronous Python library for the Mitsubishi Electric MelCloudHome platform.

Project description

pymelcloudhome

A modern, fully asynchronous Python library for the Mitsubishi Electric "MelCloudHome" platform API, with persistent session handling.

Table of Contents

Supported Devices

Device Type Read Update
ATA
ATW
ERV

Installation

For developers working on pymelcloudhome, you'll need Poetry to manage dependencies.

  1. Clone the repository:

    git clone https://github.com/MHultman/pymelcloudhome.git
    cd pymelcloudhome
    
  2. Install all dependencies (production and development):

    poetry install
    

This command will create a virtual environment and install all necessary packages, including those required for testing, linting, and type checking.

If you are a user and only want to install the library as a dependency in your project, you can use pip:

pip install pymelcloudhome

Usage

The MelCloudHomeClient provides the following asynchronous methods to interact with the MelCloud Home API:

login(email: str, password: str)

Logs in to the MelCloud Home platform. This method uses a headless browser (Playwright) to handle the login process, including any JavaScript-based authentication.

await client.login("your-email@example.com", "your-password")

list_devices() -> List[Device]

Retrieves a list of all devices associated with the logged-in user. Each Device object contains details about the unit, including its type (ataunit for Air-to-Air or atwunit for Air-to-Water) and current settings.

devices = await client.list_devices()
for device in devices:
    print(f"Device ID: {device.id}, Name: {device.given_display_name}, Type: {device.device_type}")

get_device_state(device_id: str) -> Optional[Dict[str, Any]]

Retrieves the current operational state of a specific device from the cached data. This method does not make a new API call. It returns a dictionary of the device's settings or None if the device is not found.

device_id = "your-device-id" # e.g., "d3c4b5a6-f7e8-9012-cbad-876543210fed"
state = await client.get_device_state(device_id)
if state:
    print(f"Device state: {state}")

set_device_state(device_id: str, device_type: str, state_data: dict) -> dict

Updates the operational state of a specific device.

  • device_id: The ID of the device to update.
  • device_type: The type of the device, either "ataunit" or "atwunit".
  • state_data: A dictionary containing the settings to update and their new values.

For ATW (Air-to-Water) devices, you can send a dictionary with the following keys:

{
  "power": true, // or false
  "setTankWaterTemperature": 55
  "forcedHotWaterMode": true, // or false
  "operationModeZone1": "HeatRoomTemperature", // "HeatFlowTemperature", "HeatCurve"
  "setTemperatureZone1": 22,
  "setHeatFlowTemperatureZone1": 45,
  "setCoolFlowTemperatureZone1": 18,
  "operationModeZone2": "HeatRoomTemperature", // "HeatFlowTemperature", "HeatCurve"
  "setTemperatureZone2": 21,
  "setHeatFlowTemperatureZone2": 40,
  "setCoolFlowTemperatureZone2": 19
}

Sending value null will leave the setting unchanged.

Here is an example of how to use this method:

device_id = "your-device-id"
device_type = "atwunit"
new_state = {"power": True, "setTemperatureZone1": 23.5}
response = await client.set_device_state(device_id, device_type, new_state)
print(f"Set device state response: {response}")

close()

Closes the underlying aiohttp client session. This method is automatically called when using the client as an asynchronous context manager (async with).

await client.close()

Caching

To minimize API calls and improve performance, the MelCloudHomeClient caches the user profile data. By default, this cache lasts for 5 minutes. You can configure this duration by passing the cache_duration_minutes parameter when creating the client.

# Use a 10-minute cache
client = MelCloudHomeClient(cache_duration_minutes=10)

This means that subsequent calls to list_devices() and get_device_state() within this timeframe will use the cached data instead of making a new API request to fetch the user context.

Automatic Session Renewal

The client is designed to be resilient to session expiry. If an API call fails with a 401 Unauthorized status, the library will automatically attempt to re-authenticate using the credentials you provided during the initial login call. If the re-login is successful, the original request will be retried automatically.

This makes the client more robust for long-running applications, as you do not need to manually handle session expiry.

Error Handling

The library uses custom exceptions to indicate specific types of failures. It is best practice to wrap your client calls in a try...except block to handle these potential errors gracefully.

There are three main exceptions you should be prepared to handle:

  • LoginError: Raised when the initial authentication with MELCloud fails. This is typically caused by incorrect credentials (email or password) or a change in the MELCloud login page. It does not contain an HTTP status code, as it originates from the browser automation process.

  • ApiError: Raised for any failed API call that does not resolve after a potential re-login attempt. This can happen if the API endpoint is not found, the server returns an error, or if a re-login attempt also fails. This exception contains a .status attribute with the HTTP status code (e.g., 404, 500) and a .message attribute with the error details from the server.

  • DeviceNotFound: Raised when an operation is attempted on a device that does not exist or is not properly configured.

Example of Handling Errors

import asyncio
from pymelcloudhome import MelCloudHomeClient
from pymelcloudhome.errors import LoginError, ApiError, DeviceNotFound

async def main():
    async with MelCloudHomeClient() as client:
        try:
            # Attempt to log in
            await client.login("your-email@example.com", "your-password")
            print("Login successful!")

            # Perform operations
            devices = await client.list_devices()
            if not devices:
                print("No devices found.")
                return

            # ... your code to interact with devices ...

        except LoginError:
            print("Login failed. Please check your email and password.")
        except ApiError as e:
            print(f"An API error occurred: Status {e.status} - {e.message}")
        except DeviceNotFound:
            print("The specified device could not be found.")
        except Exception as e:
            print(f"An unexpected error occurred: {e}")

if __name__ == "__main__":
    asyncio.run(main())

Example Usage

import asyncio
from pymelcloudhome import MelCloudHomeClient

async def main():
    async with MelCloudHomeClient() as client:
        await client.login("your-email@example.com", "your-password")

        # List all devices
        devices = await client.list_devices()
        print("Discovered Devices:")
        for device in devices:
            print(f"  - ID: {device.id}, Name: {device.given_display_name}, Type: {device.device_type}")

        if devices:
            # Get state of the first device
            first_device_id = devices[0].id
            current_state = await client.get_device_state(first_device_id)
            print(f"Current state of {devices[0].given_display_name}: {current_state}")

            # Example: Set power and temperature for an ATW unit
            if devices[0].device_type == "atwunit":
                print(f"Attempting to set state for ATW unit: {devices[0].given_display_name}")
                update_data = {"power": True, "setTemperatureZone1": 22.0}
                set_response = await client.set_device_state(first_device_id, "atwunit", update_data)
                print(f"Set state response: {set_response}")

if __name__ == "__main__":
    asyncio.run(main())

Running Tests

To run the test suite, first install the development dependencies:

poetry install

Then, run pytest:

poetry run pytest

Development Workflow

Managing Dependencies

When you modify pyproject.toml (add/remove/update dependencies), you need to update the poetry.lock file:

# Update poetry.lock file
poetry lock

# Or use the provided scripts:
# Windows:
.\scripts\update-lock.bat

# Unix/Linux/macOS:
./scripts/update-lock.sh

# Or use Make (if available):
make lock-update

Automated Checks

This project includes several automation tools to prevent common issues:

  1. Pre-commit hooks: Automatically run linting, formatting, and dependency checks before commits
  2. CI/CD validation: GitHub Actions will check that poetry.lock is up-to-date
  3. VSCode tasks: Use Ctrl+Shift+P → "Tasks: Run Task" → "Poetry: Update Lock File"

Available Make Commands

make help          # Show all available commands
make install       # Install dependencies
make lock-check    # Check if poetry.lock is up-to-date
make lock-update   # Update poetry.lock
make test          # Run tests
make lint          # Run linting and type checking
make format        # Format code
make check         # Run all checks (lock, lint, test)
make update        # Update lock and run tests

Linting and Type Checking

This project uses black and ruff for linting and mypy for type checking. To run them, ensure you have installed the development dependencies:

poetry install

Then, execute the following commands:

poetry run black .
poetry run ruff .
poetry run mypy .

Contributing

Contributions are welcome! Please read the contributing guidelines before submitting a pull request.

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

pymelcloudhome-0.2.0.tar.gz (16.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pymelcloudhome-0.2.0-py3-none-any.whl (17.2 kB view details)

Uploaded Python 3

File details

Details for the file pymelcloudhome-0.2.0.tar.gz.

File metadata

  • Download URL: pymelcloudhome-0.2.0.tar.gz
  • Upload date:
  • Size: 16.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.10.18 Linux/6.11.0-1018-azure

File hashes

Hashes for pymelcloudhome-0.2.0.tar.gz
Algorithm Hash digest
SHA256 d4ca6aac8e2160c80dee905cddfb300b5a6952674814ce422023b0230d5cb926
MD5 5d7b7dd069a9fbf890ae6cc53a2e8970
BLAKE2b-256 b17e85f28def99db8d8a4fc9ed7481a8679ba0133b89dffc4d3186b3cbb1e4a0

See more details on using hashes here.

File details

Details for the file pymelcloudhome-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pymelcloudhome-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 17.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.10.18 Linux/6.11.0-1018-azure

File hashes

Hashes for pymelcloudhome-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 77a8f1b0311d124a79d06f259dda154f104aec06b93c0b6e099fe6b3df786c60
MD5 8a1355a434692d4ecb7fbed614e505d4
BLAKE2b-256 b4e247cc8bc0888b37a9f5816e336dd09fa7733b04ceab34284ec603ccd23625

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page