pymelcloudhome


Namepymelcloudhome JSON
Version 0.1.1 PyPI version JSON
download
home_pagehttps://github.com/MHultman/pymelcloudhome
SummaryA modern, fully asynchronous Python library for the Mitsubishi Electric MelCloudHome platform.
upload_time2025-07-22 15:27:03
maintainerNone
docs_urlNone
authorYour Name
requires_python<4.0,>=3.10
licenseMIT
keywords melcloudhome mitsubishi hvac api asyncio
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
# pymelcloudhome

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

## Table of Contents

- [Installation](#installation)
- [Usage](#usage)
  - [`login(email: str, password: str)`](#loginemail-str-password-str)
  - [`list_devices() -> List[Device]`](#list_devices---listdevice)
  - [`get_device_state(device_id: str) -> Optional[Dict[str, Any]]`](#get_device_statedevice_id-str---optionaldictstr-any)
  - [`set_device_state(device_id: str, device_type: str, state_data: dict) -> dict`](#set_device_statedevice_id-str-device_type-str-state_data-dict---dict)
  - [`close()`](#close)
  - [Caching](#caching)
- [Automatic Session Renewal](#automatic-session-renewal)
- [Error Handling](#error-handling)
  - [Example of Handling Errors](#example-of-handling-errors)
- [Example Usage](#example-usage)
- [Running Tests](#running-tests)
- [Contributing](#contributing)

## Installation

For developers working on `pymelcloudhome`, you'll need [Poetry](https://python-poetry.org/docs/#installation) to manage dependencies.

1.  **Clone the repository:**

    ```bash
    git clone https://github.com/your-username/pymelcloudhome.git
    cd pymelcloudhome
    ```

2.  **Install all dependencies (production and development):**

    ```bash
    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:

```bash
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.

```python
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.

```python
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.

```python
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, common `state_data` values you might send include:
- `"power"`: `True` or `False` (to turn the device on or off)
- `"setTemperatureZone1"`: A float representing the target temperature for Zone 1 (e.g., `22.0`)
- Other values may be available depending on your specific device model and its capabilities. You can inspect the output of `get_device_state` to discover more controllable parameters.

```python
device_id = "your-device-id"
device_type = "atwunit" # or "ataunit"
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`).

```python
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.

```python
# 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

```python
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

```python
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:

```bash
poetry install
```

Then, run pytest:

```bash
poetry run pytest
```

## Contributing

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

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/MHultman/pymelcloudhome",
    "name": "pymelcloudhome",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.10",
    "maintainer_email": null,
    "keywords": "melcloudhome, mitsubishi, hvac, api, asyncio",
    "author": "Your Name",
    "author_email": "you@example.com",
    "download_url": "https://files.pythonhosted.org/packages/8e/90/6e3d2a2df960c50f5642256ee13998a59ba565ce025980c73bacaba3c978/pymelcloudhome-0.1.1.tar.gz",
    "platform": null,
    "description": "\n# pymelcloudhome\n\nA modern, fully asynchronous Python library for the Mitsubishi Electric \"MelCloudHome\" platform API, with persistent session handling.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [`login(email: str, password: str)`](#loginemail-str-password-str)\n  - [`list_devices() -> List[Device]`](#list_devices---listdevice)\n  - [`get_device_state(device_id: str) -> Optional[Dict[str, Any]]`](#get_device_statedevice_id-str---optionaldictstr-any)\n  - [`set_device_state(device_id: str, device_type: str, state_data: dict) -> dict`](#set_device_statedevice_id-str-device_type-str-state_data-dict---dict)\n  - [`close()`](#close)\n  - [Caching](#caching)\n- [Automatic Session Renewal](#automatic-session-renewal)\n- [Error Handling](#error-handling)\n  - [Example of Handling Errors](#example-of-handling-errors)\n- [Example Usage](#example-usage)\n- [Running Tests](#running-tests)\n- [Contributing](#contributing)\n\n## Installation\n\nFor developers working on `pymelcloudhome`, you'll need [Poetry](https://python-poetry.org/docs/#installation) to manage dependencies.\n\n1.  **Clone the repository:**\n\n    ```bash\n    git clone https://github.com/your-username/pymelcloudhome.git\n    cd pymelcloudhome\n    ```\n\n2.  **Install all dependencies (production and development):**\n\n    ```bash\n    poetry install\n    ```\n\nThis command will create a virtual environment and install all necessary packages, including those required for testing, linting, and type checking.\n\nIf you are a user and only want to install the library as a dependency in your project, you can use pip:\n\n```bash\npip install pymelcloudhome\n```\n\n## Usage\n\nThe `MelCloudHomeClient` provides the following asynchronous methods to interact with the MelCloud Home API:\n\n### `login(email: str, password: str)`\nLogs in to the MelCloud Home platform. This method uses a headless browser (Playwright) to handle the login process, including any JavaScript-based authentication.\n\n```python\nawait client.login(\"your-email@example.com\", \"your-password\")\n```\n\n### `list_devices() -> List[Device]`\nRetrieves 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.\n\n```python\ndevices = await client.list_devices()\nfor device in devices:\n    print(f\"Device ID: {device.id}, Name: {device.given_display_name}, Type: {device.device_type}\")\n```\n\n### `get_device_state(device_id: str) -> Optional[Dict[str, Any]]`\nRetrieves 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.\n\n```python\ndevice_id = \"your-device-id\" # e.g., \"d3c4b5a6-f7e8-9012-cbad-876543210fed\"\nstate = await client.get_device_state(device_id)\nif state:\n    print(f\"Device state: {state}\")\n```\n\n### `set_device_state(device_id: str, device_type: str, state_data: dict) -> dict`\nUpdates the operational state of a specific device.\n- `device_id`: The ID of the device to update.\n- `device_type`: The type of the device, either \"ataunit\" or \"atwunit\".\n- `state_data`: A dictionary containing the settings to update and their new values.\n\nFor ATW (Air-to-Water) devices, common `state_data` values you might send include:\n- `\"power\"`: `True` or `False` (to turn the device on or off)\n- `\"setTemperatureZone1\"`: A float representing the target temperature for Zone 1 (e.g., `22.0`)\n- Other values may be available depending on your specific device model and its capabilities. You can inspect the output of `get_device_state` to discover more controllable parameters.\n\n```python\ndevice_id = \"your-device-id\"\ndevice_type = \"atwunit\" # or \"ataunit\"\nnew_state = {\"power\": True, \"setTemperatureZone1\": 23.5}\nresponse = await client.set_device_state(device_id, device_type, new_state)\nprint(f\"Set device state response: {response}\")\n```\n\n### `close()`\nCloses the underlying aiohttp client session. This method is automatically called when using the client as an asynchronous context manager (`async with`).\n\n```python\nawait client.close()\n```\n\n### Caching\n\nTo 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.\n\n```python\n# Use a 10-minute cache\nclient = MelCloudHomeClient(cache_duration_minutes=10)\n```\n\nThis 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.\n\n## Automatic Session Renewal\n\nThe 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.\n\nThis makes the client more robust for long-running applications, as you do not need to manually handle session expiry.\n\n## Error Handling\n\nThe 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.\n\nThere are three main exceptions you should be prepared to handle:\n\n- **`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.\n\n- **`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.\n\n- **`DeviceNotFound`**: Raised when an operation is attempted on a device that does not exist or is not properly configured.\n\n### Example of Handling Errors\n\n```python\nimport asyncio\nfrom pymelcloudhome import MelCloudHomeClient\nfrom pymelcloudhome.errors import LoginError, ApiError, DeviceNotFound\n\nasync def main():\n    async with MelCloudHomeClient() as client:\n        try:\n            # Attempt to log in\n            await client.login(\"your-email@example.com\", \"your-password\")\n            print(\"Login successful!\")\n\n            # Perform operations\n            devices = await client.list_devices()\n            if not devices:\n                print(\"No devices found.\")\n                return\n\n            # ... your code to interact with devices ...\n\n        except LoginError:\n            print(\"Login failed. Please check your email and password.\")\n        except ApiError as e:\n            print(f\"An API error occurred: Status {e.status} - {e.message}\")\n        except DeviceNotFound:\n            print(\"The specified device could not be found.\")\n        except Exception as e:\n            print(f\"An unexpected error occurred: {e}\")\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n## Example Usage\n\n```python\nimport asyncio\nfrom pymelcloudhome import MelCloudHomeClient\n\nasync def main():\n    async with MelCloudHomeClient() as client:\n        await client.login(\"your-email@example.com\", \"your-password\")\n\n        # List all devices\n        devices = await client.list_devices()\n        print(\"Discovered Devices:\")\n        for device in devices:\n            print(f\"  - ID: {device.id}, Name: {device.given_display_name}, Type: {device.device_type}\")\n\n        if devices:\n            # Get state of the first device\n            first_device_id = devices[0].id\n            current_state = await client.get_device_state(first_device_id)\n            print(f\"Current state of {devices[0].given_display_name}: {current_state}\")\n\n            # Example: Set power and temperature for an ATW unit\n            if devices[0].device_type == \"atwunit\":\n                print(f\"Attempting to set state for ATW unit: {devices[0].given_display_name}\")\n                update_data = {\"power\": True, \"setTemperatureZone1\": 22.0}\n                set_response = await client.set_device_state(first_device_id, \"atwunit\", update_data)\n                print(f\"Set state response: {set_response}\")\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n## Running Tests\n\nTo run the test suite, first install the development dependencies:\n\n```bash\npoetry install\n```\n\nThen, run pytest:\n\n```bash\npoetry run pytest\n```\n\n## Contributing\n\nContributions are welcome! Please read the contributing guidelines before submitting a pull request.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A modern, fully asynchronous Python library for the Mitsubishi Electric MelCloudHome platform.",
    "version": "0.1.1",
    "project_urls": {
        "Homepage": "https://github.com/MHultman/pymelcloudhome",
        "Repository": "https://github.com/MHultman/pymelcloudhome"
    },
    "split_keywords": [
        "melcloudhome",
        " mitsubishi",
        " hvac",
        " api",
        " asyncio"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "244bcf37a45daeb359b945cff6be2c9f77b41bb896387c1eeeedac6b0643d078",
                "md5": "71c774e767c27c507f601a50335778bf",
                "sha256": "a590e509df3716c86b9df08e9c2c2e74e009d479470d9eefd91cc99fbb406e2d"
            },
            "downloads": -1,
            "filename": "pymelcloudhome-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "71c774e767c27c507f601a50335778bf",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.10",
            "size": 9352,
            "upload_time": "2025-07-22T15:27:02",
            "upload_time_iso_8601": "2025-07-22T15:27:02.061233Z",
            "url": "https://files.pythonhosted.org/packages/24/4b/cf37a45daeb359b945cff6be2c9f77b41bb896387c1eeeedac6b0643d078/pymelcloudhome-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8e906e3d2a2df960c50f5642256ee13998a59ba565ce025980c73bacaba3c978",
                "md5": "7d14428f6b76f3027ba34ed3e5e0942a",
                "sha256": "347e792a8f57cda806f691227f66cc3e4b750193d987d00924a24cc46cf3992d"
            },
            "downloads": -1,
            "filename": "pymelcloudhome-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "7d14428f6b76f3027ba34ed3e5e0942a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.10",
            "size": 7976,
            "upload_time": "2025-07-22T15:27:03",
            "upload_time_iso_8601": "2025-07-22T15:27:03.218206Z",
            "url": "https://files.pythonhosted.org/packages/8e/90/6e3d2a2df960c50f5642256ee13998a59ba565ce025980c73bacaba3c978/pymelcloudhome-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-22 15:27:03",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "MHultman",
    "github_project": "pymelcloudhome",
    "github_not_found": true,
    "lcname": "pymelcloudhome"
}
        
Elapsed time: 1.54136s