LIEBHERR Integration (freezer, refrigerator)

Guys, did I understand correctly that the extension communicates with the refrigerator through the manufacturer’s servers, not directly, and if there is no Internet, then there is no control. Is that correct?

Yes, that should be correct.

Hi @benedikt.huebschen and thank you for this Integration! I first saw this integration and then decided to buy a Liebherr fridge with SmartDevice box.

I’m very happy your code works so well, but also a bit disappointed by Liebherr: they did not include switches to toggle a cooling zone on/off, or the whole fridge on/off. You can do the latter by touch display, but not by API.
The Energy Saver mode also is not accessible via API.

I sent them feedback via the beta test button in their Android app and requested to add these two controls to their API. Do you have good Liebherr contact to work on feature requests?

My goal is to let HA turn the fridge off under certain circumstances, e.g. when reading in the living room or watching TV, because the fridge is too loud. Their “night mode” unfortunately does not make it quiet.

Since I have the wine cellar, the light management was missing. If you want, I can send you the updated files that manage the light.

Hello, did someone manage to get the “local api” working somehow ?
Onboarding seems to be the key…

Hi Nicola,
i have a wine fridge too and not able to control the light. Would be pleased if you could share your solution.

Thanks and Regards.

(build for you by IA :smiley: )

Liebherr Wine Cooler Light Control Modification for Home Assistant

Overview

This guide explains how to modify the Liebherr custom component for Home Assistant to support PresentationLight control (the internal light of Liebherr wine coolers/fridges).

Prerequisites

  • Home Assistant with Liebherr custom component installed
  • A Liebherr appliance with PresentationLight feature (typically wine coolers)

Files to Modify

1. /custom_components/liebherr/switch.py

Add support for PresentationLightControl by modifying the following sections:

Section 1: Add PresentationLightControl to supported types

Around line 37-43, ensure "PresentationLightControl" is in the list:

if control["type"] in [
    "ToggleControl",
    "IceMaker",
    "BottleTimer",
    "AutoDoor",
    "PresentationLightControl",  # <-- Add this line
]:

Section 2: Modify the is_on property

Around line 94-104, add special handling for PresentationLight:

@property
def is_on(self) -> bool:
    """Return true if switch is on."""
    try:
        # Find the current control data
        if self.coordinator.data and "appliances" in self.coordinator.data:
            for appliance in self.coordinator.data["appliances"]:
                if appliance["deviceId"] == self._appliance["deviceId"]:
                    for control in appliance.get("controls", []):
                        if (control["name"] == self._control["name"] and
                            control.get("zoneId") == self._zoneId):

                            control_value = control.get("value", 0)

                            # For PresentationLightControl, any value > 0 means on
                            if self._control["type"] == "PresentationLightControl":
                                is_on = control_value > 0
                                return is_on
                            else:
                                # For other controls, use existing logic
                                return control_value in [True, "ON", 1]

        return False
    except Exception as e:
        _LOGGER.error(f"Error in is_on: {e}")
        return False

Section 3: Add turn_on handler for PresentationLight

In the async_turn_on method, add this elif block (around line 149):

elif self._control["type"] == "PresentationLightControl":
    # IMPORTANT: Use "target" instead of "value" as per Liebherr API docs
    _LOGGER.info(f"PresentationLight: Turning on")
    data = {"target": 1}  # Brightness level 1 (can be 0-5)

    result = await self._api.set_value(
        self._appliance["deviceId"], self._control["name"], data
    )

    # Update local value
    self.setControlValue(1)
    _LOGGER.info(f"PresentationLight: Successfully turned on")

Section 4: Add turn_off handler for PresentationLight

In the async_turn_off method, add this elif block (around line 207):

elif self._control["type"] == "PresentationLightControl":
    # IMPORTANT: Use "target" instead of "value" as per Liebherr API docs
    _LOGGER.info(f"PresentationLight: Turning off")
    data = {"target": 0}  # Brightness level 0 (off)

    result = await self._api.set_value(
        self._appliance["deviceId"], self._control["name"], data
    )

    # Update local value
    self.setControlValue(0)
    _LOGGER.info(f"PresentationLight: Successfully turned off")

2. /custom_components/liebherr/models.py (Optional)

If you want to add a proper data model for future enhancements:

@dataclass
class PresentationLightControlRequest:
    target: int  # Brightness level 0-5 (0=off, 1-5=various brightness levels)

Key Points

API Payload Format

The crucial discovery is that PresentationLightControl uses a different payload format:

  • Use "target" field, NOT "value"
  • Values range from 0-5:
    • 0 = Off
    • 1-5 = Different brightness levels
    • Currently implementation uses 1 for on, 0 for off

Entity Naming

The entity will appear as:

  • switch.[device_nickname]_presentationlight
  • Example: switch.winecooler_presentationlight

Testing

  1. Restart Home Assistant after making changes
  2. Check Developer Tools > States for the new switch entity
  3. Toggle the switch and verify the physical light responds
  4. Check logs for any errors:
    grep PresentationLight home-assistant.log
    

Dashboard Configuration Example

Add to your Lovelace UI:

type: entities
title: Wine Cooler
entities:
  - entity: switch.your_device_presentationlight
    name: Interior Light
  - entity: climate.your_device_temperature_top
    name: Top Zone Temperature
  - entity: climate.your_device_temperature_bottom
    name: Bottom Zone Temperature

Troubleshooting

Light not responding

  1. Check logs for API errors
  2. Verify the device actually has PresentationLight control
  3. Ensure you’re using "target" not "value" in the payload

Rate limiting

The Liebherr API has rate limits. If you get 429 errors:

  1. Reduce polling frequency in the integration config
  2. Wait a few minutes before retrying

Notes

  • The modification focuses on basic on/off functionality
  • Brightness levels 2-5 could be implemented with a light entity instead of switch
  • The API supports reading current brightness level in the control value

Credits

This modification enables PresentationLight control which is not included in the base Liebherr custom component. The key insight was discovering the correct API payload format from the Liebherr Swagger documentation.

FULL PACKAGE:

"""Switch platform for Liebherr integration with PresentationLight support."""

import logging
from typing import Any

from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .models import PresentationLightControlRequest

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up Liebherr switch platform."""
    _LOGGER.debug("Setting up Liebherr switch platform")

    coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"]
    api = hass.data[DOMAIN][config_entry.entry_id]["api"]

    entities = []
    if coordinator.data and "appliances" in coordinator.data:
        for appliance in coordinator.data["appliances"]:
            _LOGGER.debug(f"Processing appliance {appliance.get('deviceId')}")

            for control in appliance.get("controls", []):
                _LOGGER.debug(f"Found control {control.get('name')} of type {control.get('type')}")

                if control["type"] in [
                    "ToggleControl",
                    "IceMaker",
                    "BottleTimer",
                    "AutoDoor",
                    "PresentationLightControl",  # Added support for light control
                ]:
                    zone_id = control.get("zoneId")
                    entities.append(
                        LiebherrSwitch(api, coordinator, appliance, control, zone_id)
                    )
                    _LOGGER.debug(f"Created switch for {control.get('name')}")

    async_add_entities(entities)
    _LOGGER.debug(f"Added {len(entities)} switch entities")


class LiebherrSwitch(CoordinatorEntity, SwitchEntity):
    """Representation of a Liebherr switch."""

    def __init__(self, api, coordinator, appliance, control, zoneId) -> None:
        """Initialize the switch."""
        super().__init__(coordinator)
        self._api = api
        self._appliance = appliance
        self._control = control
        self._zoneId = zoneId

        # Create unique ID
        if zoneId is not None:
            self._attr_unique_id = f"{appliance['deviceId']}_{control['name']}_{zoneId}"
            self._attr_name = f"{appliance.get('nickname', appliance['deviceId'])} {control['name']} Zone {zoneId}"
        else:
            self._attr_unique_id = f"{appliance['deviceId']}_{control['name']}"
            self._attr_name = f"{appliance.get('nickname', appliance['deviceId'])} {control['name']}"

        self._attr_device_info = {
            "identifiers": {(DOMAIN, appliance["deviceId"])},
            "name": appliance.get("nickname", appliance["deviceId"]),
            "manufacturer": "Liebherr",
            "model": appliance.get("model", "Unknown"),
        }

        _LOGGER.debug(f"Created switch for {control.get('name')} with type {control.get('type')}")

    @property
    def is_on(self) -> bool:
        """Return true if switch is on."""
        try:
            # Find the current control data
            if self.coordinator.data and "appliances" in self.coordinator.data:
                for appliance in self.coordinator.data["appliances"]:
                    if appliance["deviceId"] == self._appliance["deviceId"]:
                        for control in appliance.get("controls", []):
                            if (control["name"] == self._control["name"] and
                                control.get("zoneId") == self._zoneId):

                                control_value = control.get("value", 0)
                                _LOGGER.debug(f"Control {control['name']} has value {control_value}")

                                # For PresentationLightControl, any value > 0 means on
                                if self._control["type"] == "PresentationLightControl":
                                    is_on = control_value > 0
                                    _LOGGER.debug(f"PresentationLight is_on = {is_on}")
                                    return is_on
                                else:
                                    # For other controls, use existing logic
                                    return control_value in [True, "ON", 1]

            return False
        except Exception as e:
            _LOGGER.error(f"Error in is_on: {e}")
            return False

    def setControlValue(self, value):
        """Update the control value locally."""
        _LOGGER.debug(f"Setting {self._control['name']} to {value}")
        self._control["value"] = value

    async def async_turn_on(self, **kwargs):
        """Turn the switch on."""
        _LOGGER.info(f"Turning ON control {self._control['name']} of type {self._control['type']}")

        try:
            if self._control["type"] == "ToggleControl":
                data = {"value": True}
                await self._api.set_value(
                    self._appliance["deviceId"], self._control["name"], data
                )
                self.setControlValue(True)

            elif self._control["type"] == "IceMaker":
                data = {"iceMakerMode": "ON"}
                await self._api.set_value(
                    self._appliance["deviceId"] + "/" + self._control["name"], data
                )
                self.setControlValue("ON")

            elif self._control["type"] == "BottleTimer":
                data = {"bottleTimer": "ON"}
                await self._api.set_value(
                    self._appliance["deviceId"] + "/" + self._control["name"], data
                )
                self.setControlValue("ON")

            elif self._control["type"] == "AutoDoor":
                data = {"zoneId": self._zoneId, "value": True}
                await self._api.set_value(
                    self._appliance["deviceId"], self._control["name"], data
                )
                self.setControlValue(True)

            elif self._control["type"] == "PresentationLightControl":
                # IMPORTANT: Use "target" instead of "value" as per API documentation!
                _LOGGER.info(f"PresentationLight: Using 'target' field for API payload")
                data = {"target": 1}  # Brightness level 1 (can be 0-5)
                _LOGGER.info(f"PresentationLight: Sending data {data} to device {self._appliance['deviceId']}")

                result = await self._api.set_value(
                    self._appliance["deviceId"], self._control["name"], data
                )
                _LOGGER.info(f"PresentationLight: API result: {result}")

                # Update local value
                self.setControlValue(1)
                _LOGGER.info(f"PresentationLight: Successfully turned on with target=1")

        except Exception as e:
            _LOGGER.error(f"Exception during turn_on: {e}")
            _LOGGER.error(f"Exception type: {type(e)}")
            import traceback
            _LOGGER.error(f"Traceback: {traceback.format_exc()}")
            raise

        # Force coordinator update
        await self.coordinator.async_request_refresh()

    async def async_turn_off(self, **kwargs):
        """Turn the switch off."""
        _LOGGER.info(f"Turning OFF control {self._control['name']} of type {self._control['type']}")

        try:
            if self._control["type"] == "ToggleControl":
                data = {"value": False}
                await self._api.set_value(
                    self._appliance["deviceId"], self._control["name"], data
                )
                self.setControlValue(False)

            elif self._control["type"] == "IceMaker":
                data = {"iceMakerMode": "OFF"}
                await self._api.set_value(
                    self._appliance["deviceId"] + "/" + self._control["name"], data
                )
                self.setControlValue("OFF")

            elif self._control["type"] == "BottleTimer":
                data = {"bottleTimer": "OFF"}
                await self._api.set_value(
                    self._appliance["deviceId"] + "/" + self._control["name"], data
                )
                self.setControlValue("OFF")

            elif self._control["type"] == "AutoDoor":
                data = {"zoneId": self._zoneId, "value": False}
                await self._api.set_value(
                    self._appliance["deviceId"], self._control["name"], data
                )
                self.setControlValue(False)

            elif self._control["type"] == "PresentationLightControl":
                # IMPORTANT: Use "target" instead of "value" as per API documentation!
                _LOGGER.info(f"PresentationLight: Using 'target' field for API payload")
                data = {"target": 0}  # Brightness level 0 (off)
                _LOGGER.info(f"PresentationLight: Sending data {data} to device {self._appliance['deviceId']}")

                result = await self._api.set_value(
                    self._appliance["deviceId"], self._control["name"], data
                )
                _LOGGER.info(f"PresentationLight: API result: {result}")

                # Update local value
                self.setControlValue(0)
                _LOGGER.info(f"PresentationLight: Successfully turned off with target=0")

        except Exception as e:
            _LOGGER.error(f"Exception during turn_off: {e}")
            raise

        # Force coordinator update
        await self.coordinator.async_request_refresh()```

Hi all,

I forked @benedikt.huebschen’s integration in October and have been maintaining it here that fully implements all the endpoints/functionality currently available in the beta Liebherr Smart Home API:

This integration will map the appliance controls as follows:

Liebherr Control Homeassistant Domain
Auto Door Cover
Ice Maker, BioFreshPlus Select
Presentation Light Light or Number*
SuperCool, SuperFreeze, PartyMode, NightMode Switch
HydroBreeze Fan
Temperature Climate
image_url (Device) Image
Bottle Timer, Night Mode Not available in API

There is also currently not support for notifications via the API.

Contributions are welcome!

New Core Integration

There is a new Liebherr core intergration from @mettolen that was recently accepted (Pull Request) that will be included in an upcoming release of Home Assistant. The code quality appears to be fantastic but lacks a lot of functionality and translations of the custom integration. I’m looking forward to contributing.

Hello,

Liebherr core integration will be available with the HA 2026.3 release. I intentionally decided not to include it in 2026.2 to allow more time for implementing the remaining functionality and address quality scale items.

Building the core integration requires time, as pull requests need to be reasonably scoped, focus on a single platform or quality scale item, and complete code review, which can sometimes take a week or more. That said, I still expect to implement most of the functionality by the 2026.3 release.

Translations are provided by HA and managed through Localise, so there is no need for the integration itself to supply them. How and whether items are translated depends on the specific language coverage. Contributing translation | Home Assistant Developer Docs

Control-to-HA entity mapping will differ slightly from HACS. This is the current plan:

Liebherr Control HA Entity
Current temperature Sensor
Setpoint temperature Number
SuperCool, SuperFrost, Party Mode, Night Mode Switch
Ice Maker, HydroBreeze, BioFreshPlus Select
Presentation Light Light
Auto Door cover Cover

But, I would appreciate some help during the next beta period to test all the functionality, as my fridge does not have zones, Presentation Light, or Auto Door. I can implement these based on the API spec, but some additional testing with actual device would be good.

Going forward, I hope both HACS and core integration can complement each other, with one offering more flexibility and the other a more standardized approach.

3 Likes