Skip to main content

pyliebherrhomeapi

CI codecov PyPI version PyPI Downloads Python versions License: MIT

Python library for the Liebherr SmartDevice Home API.


This library is used by the Liebherr Home Assistant integration.

Dynamic Regex Badge Dynamic Regex Badge Static Badge

Features

  • 🔌 Async/await support using asyncio with comprehensive error handling
  • 🌡️ Temperature control for all zones in your Liebherr appliances
  • ❄️ SuperFrost/SuperCool control for quick cooling/freezing
  • 🎉 Special modes (Party Mode, Night Mode, Presentation Light)
  • 🧊 Ice maker control with Max Ice support
  • 💧 HydroBreeze and BioFreshPlus mode management
  • 🚪 Auto door control for supported appliances
  • 📱 Device management - list and query all connected appliances
  • 📡 Realtime updates via Server-Sent Events (SSE) (beta) - subscribe to live control state changes
  • 🛡️ Type hints for better IDE support and development experience
  • ✅ Input validation with proper error handling
  • 📊 Comprehensive data models for all control types
  • 📝 Configurable logging with privacy-focused debug output
  • 🧪 100% test coverage ensuring reliability and code quality

Requirements

  • Python 3.12+ (matches the typed codebase and test matrix)
  • Asyncio environment with aiohttp (installed automatically)
  • Network access to https://home-api.smartdevice.liebherr.com

Installation

  • From PyPI (when published):

    pip install pyliebherrhomeapi
    
  • From source (current repository):

    pip install .
    

Prerequisites

Before using this library, you need:

  1. Connect your appliance: Connect your Liebherr appliance via the SmartDevice app to your home WiFi network

  2. Get your API Key (via the SmartDevice app):

    • Go to Settings in the SmartDevice app
    • Select "Beta features"
    • Activate the HomeAPI
    • Copy the API Key (⚠️ Important: The API key can only be copied once. Once you leave the screen, it cannot be copied again. If you forget your key, you'll need to create a new one via the app)
  3. Connected appliances only: Only appliances that are connected to the internet via the SmartDevice app can be accessed through the HomeAPI. Appliances that are only registered but not connected will not appear

Quick Start

import asyncio
from pyliebherrhomeapi import (
    LiebherrClient,
    TemperatureUnit,
    IceMakerMode,
)

async def main():
    # Create client with your API key
    async with LiebherrClient(api_key="your-api-key-here") as client:
        # Get all devices (only connected devices are returned)
        devices = await client.get_devices()
        print(f"Found {len(devices)} device(s)")

        for device in devices:
            # device_id is the serial number of the appliance
            print(f"Device: {device.nickname} ({device.device_id})")
            print(f"  Type: {device.device_type}")
            print(f"  Model: {device.device_name}")

            # Get all controls for this device
            controls = await client.get_controls(device.device_id)
            print(f"  Controls: {len(controls)}")

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

Important Notes

Device Zones

  • Each device has at least one zone (cooling zone, freezing zone, etc.)
  • Zone numbering: The top zone is zone 0, zone numbers ascend from top to bottom
  • Zone controls (like temperature, SuperFrost, SuperCool) always require a zone_id
  • Base controls (like Party Mode, Night Mode) apply to the whole device and don't need a zone

Polling and Device Refresh

The REST endpoints do not push updates. Control updates can be received through Realtime Updates (SSE), but device list and metadata changes still require an explicit REST request.

  • Controls: Prefer SSE for realtime updates. If polling is required, use /v1/devices/{deviceId}/controls to get all control states in one call
  • Device list: Call get_devices() (GET /v1/devices) again to discover added or removed appliances and appliance nickname changes. These changes are not included in SSE events
  • Rate limits: Avoid excessive polling. The API restricts the number of calls for security and performance reasons

Control Types

Base Controls (apply to entire device, no zone_id needed):

  • Party Mode
  • Night Mode

Zone Controls (require zone_id, even if device has only one zone):

  • Temperature
  • SuperFrost
  • SuperCool
  • Ice Maker
  • HydroBreeze
  • BioFreshPlus
  • Auto Door

Usage Examples

Temperature Control

from pyliebherrhomeapi import LiebherrClient, TemperatureUnit

async with LiebherrClient(api_key="your-api-key") as client:
    # Set temperature for zone 0 (top zone) to 4°C
    await client.set_temperature(
        device_id="12.345.678.9",
        zone_id=0,  # Zone 0 is the top zone
        target=4,
        unit=TemperatureUnit.CELSIUS
    )

    # Get temperature control info
    controls = await client.get_control(
        device_id="12.345.678.9",
        control_name="temperature",
        zone_id=0
    )

SuperCool and SuperFrost

# Enable SuperCool for zone 0
await client.set_super_cool(
    device_id="12.345.678.9",
    zone_id=0,
    value=True
)

# Enable SuperFrost for zone 1
await client.set_super_frost(
    device_id="12.345.678.9",
    zone_id=1,
    value=True
)

Special Modes

# Enable Party Mode
await client.set_party_mode(
    device_id="12.345.678.9",
    value=True
)

# Enable Night Mode
await client.set_night_mode(
    device_id="12.345.678.9",
    value=True
)

# Set presentation light intensity (0-5)
await client.set_presentation_light(
    device_id="12.345.678.9",
    target=3
)

Ice Maker Control

from pyliebherrhomeapi import IceMakerMode

# Turn on ice maker
await client.set_ice_maker(
    device_id="12.345.678.9",
    zone_id=0,
    mode=IceMakerMode.ON
)

# Enable Max Ice mode
await client.set_ice_maker(
    device_id="12.345.678.9",
    zone_id=0,
    mode=IceMakerMode.MAX_ICE
)

HydroBreeze Control

from pyliebherrhomeapi import HydroBreezeMode

# Set HydroBreeze to medium
await client.set_hydro_breeze(
    device_id="12.345.678.9",
    zone_id=0,
    mode=HydroBreezeMode.MEDIUM
)

BioFreshPlus Control

from pyliebherrhomeapi import BioFreshPlusMode

# Set BioFreshPlus mode
await client.set_bio_fresh_plus(
    device_id="12.345.678.9",
    zone_id=0,
    mode=BioFreshPlusMode.ZERO_ZERO
)

Auto Door Control

# Open the door
await client.trigger_auto_door(
    device_id="12.345.678.9",
    zone_id=0,
    value=True  # True to open, False to close
)

Query Device Controls

# Get all controls (recommended for polling - gets all states in one call)
all_controls = await client.get_controls(device_id="12.345.678.9")

# Get specific control by name
temp_controls = await client.get_control(
    device_id="12.345.678.9",
    control_name="temperature"
)

# Get control for specific zone
zone_temp = await client.get_control(
    device_id="12.345.678.9",
    control_name="temperature",
    zone_id=0  # Top zone
)

Polling Fallback Pattern

import asyncio
from pyliebherrhomeapi import LiebherrClient

async def poll_controls(
    client: LiebherrClient, device_id: str, interval: float
) -> None:
    """Poll all device controls at a caller-selected interval."""
    while True:
        try:
            # Get all controls in a single API call
            controls = await client.get_controls(device_id)

            # Process the controls
            for control in controls:
                print(f"{control.name}: {control}")

            await asyncio.sleep(interval)

        except Exception as e:
            print(f"Error polling device: {e}")
            await asyncio.sleep(interval)

async def main():
    async with LiebherrClient(api_key="your-api-key") as client:
        devices = await client.get_devices()
        if devices:
            # This is an example interval, not an API recommendation.
            await poll_controls(client, devices[0].device_id, interval=60)

Realtime Updates (SSE) (beta)

⚠️ Beta: SSE support in this library is beta. The endpoint and its control-list payload are documented in the official HomeAPI documentation and OpenAPI specification.

The client subscribes to /v1/sse/devices/{deviceId}/controls and yields a list of parsed controls on each update. The server keeps the connection open and sends an empty keep-alive about every 30 seconds. SSE events only contain appliance control updates; they do not report added or removed appliances or appliance nickname changes. Call get_devices() (GET /v1/devices) explicitly to refresh that information. Malformed events are logged and skipped.

Reconnects automatically with exponential backoff + jitter on recoverable errors (drops, timeouts, 5xx) and clean closures. Non-recoverable errors (LiebherrAuthenticationError, LiebherrNotFoundError, LiebherrPreconditionFailedError) are re-raised. Optional non-blocking on_connect / on_disconnect callbacks let you track availability (e.g. in a Home Assistant integration); an exception in a callback is logged and does not break the stream.

import asyncio
from pyliebherrhomeapi import LiebherrClient

async def main() -> None:
    async with LiebherrClient(api_key="your-api-key") as client:
        devices = await client.get_devices()
        if not devices:
            return

        async for controls in client.stream_controls_forever(
            devices[0].device_id,
            on_connect=lambda: print("SSE connected"),
            on_disconnect=lambda: print("SSE disconnected, reconnecting..."),
        ):
            for control in controls:
                zone_id = getattr(control, "zone_id", None)
                print(f"Update: {control.name} (zone={zone_id}) -> {control}")

asyncio.run(main())

Low-level: stream_controls()

Opens a single connection and yields events until it ends. Use it only if you want to manage reconnection yourself; otherwise prefer stream_controls_forever().

SSE vs. polling: prefer SSE for low-latency, self-healing subscriptions (e.g. a long-running Home Assistant integration); prefer polling for simple scripts or environments where long-lived connections are problematic.

Logging

The library uses Python's standard logging module for diagnostics. By default, it uses a NullHandler, so no logs are emitted unless you configure logging in your application.

Enable Debug Logging

import logging

# Enable debug logging for the library
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Or configure just for pyliebherrhomeapi
logger = logging.getLogger('pyliebherrhomeapi')
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)

Log Levels

  • DEBUG: Detailed information about API requests, responses, and session lifecycle
  • INFO: General information about operations (currently not used)
  • WARNING: HTTP errors and connection issues
  • ERROR: Severe errors like server failures

Privacy

Device IDs are automatically masked in debug logs (showing only last 4 characters) to protect sensitive information.

Error Handling

from pyliebherrhomeapi import (
    LiebherrClient,
    LiebherrAuthenticationError,
    LiebherrBadRequestError,
    LiebherrNotFoundError,
    LiebherrPreconditionFailedError,
    LiebherrUnsupportedError,
    LiebherrConnectionError,
    LiebherrTimeoutError,
)

async with LiebherrClient(api_key="your-api-key") as client:
    try:
        await client.set_temperature(
            device_id="12.345.678.9",
            zone_id=0,
            target=4
        )
    except LiebherrAuthenticationError:
        print("Invalid API key")
    except LiebherrBadRequestError as e:
        print(f"Invalid request: {e}")
    except LiebherrNotFoundError:
        print("Device not reachable")
    except LiebherrPreconditionFailedError:
        print("Device not onboarded to your household")
    except LiebherrUnsupportedError:
        print("Feature not supported on this device")
    except (LiebherrConnectionError, LiebherrTimeoutError) as e:
        print(f"Connection error: {e}")

Development

Setup

# Clone the repository
git clone https://github.com/mettolen/pyliebherrhomeapi.git
cd pyliebherrhomeapi

# Install development dependencies
pip install -e ".[dev]"

Testing

# Run tests
pytest

# Run tests with coverage
pytest --cov=pyliebherrhomeapi --cov-report=html

Code Quality

# Format code
ruff format .

# Lint code
ruff check .

# Type checking
mypy src

API Documentation

For detailed API documentation, visit:

API Base URL: https://home-api.smartdevice.liebherr.com

Implementation Notes

This handwritten client is implemented against the checked-in official openapi.json specification and the HomeAPI documentation from the Liebherr Developer Portal.

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for details.

Metadata

Release files for pyliebherrhomeapi 0.5.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyliebherrhomeapi 0.5.2
File Size Uploaded
pyliebherrhomeapi-0.5.2.tar.gz 42.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyliebherrhomeapi 0.5.2
File Interpreter ABI Platform
pyliebherrhomeapi-0.5.2-py3-none-any.whl Python 3 none any Details

Total release size: 63.9 kB

Release files / pyliebherrhomeapi-0.5.2.tar.gz

Download URL pyliebherrhomeapi-0.5.2.tar.gz
Size 42.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b1b14692316862ec53ff302f2d3fa7db86544dc69f0da047e6b07b6bde45a5f7
BLAKE2b-256 checksum
How to use checksums
ac8e2b31238768f12c60a6c84a26ac200a020eaa4ad022021e16d111dd3be9ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 27, 2026.

Transparency log

Release files / pyliebherrhomeapi-0.5.2-py3-none-any.whl

Download URL pyliebherrhomeapi-0.5.2-py3-none-any.whl
Size 21.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d4e256e85d1777b4acd3325328641d22e5ce093f3917fe8c07bdad8f01b51237
BLAKE2b-256 checksum
How to use checksums
46e3be76370838e5373184f3aaa92deded7b16fbe0f2af52be68c4de155b5e39
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.2 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page