Add Home Connect light entity for cooling appliances (#126090)

* Add Home Connect light entities for fridge

* Update homeassistant/components/home_connect/light.py

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Robert Contreras 2024-09-17 06:56:20 -07:00 committed by GitHub
parent 2ae4989031
commit 4d04402ad4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 135 additions and 12 deletions

View File

@ -32,6 +32,15 @@ COFFEE_EVENT_BEAN_CONTAINER_EMPTY = (
COFFEE_EVENT_WATER_TANK_EMPTY = "ConsumerProducts.CoffeeMaker.Event.WaterTankEmpty" COFFEE_EVENT_WATER_TANK_EMPTY = "ConsumerProducts.CoffeeMaker.Event.WaterTankEmpty"
COFFEE_EVENT_DRIP_TRAY_FULL = "ConsumerProducts.CoffeeMaker.Event.DripTrayFull" COFFEE_EVENT_DRIP_TRAY_FULL = "ConsumerProducts.CoffeeMaker.Event.DripTrayFull"
REFRIGERATION_INTERNAL_LIGHT_POWER = "Refrigeration.Common.Setting.Light.Internal.Power"
REFRIGERATION_INTERNAL_LIGHT_BRIGHTNESS = (
"Refrigeration.Common.Setting.Light.Internal.Brightness"
)
REFRIGERATION_EXTERNAL_LIGHT_POWER = "Refrigeration.Common.Setting.Light.External.Power"
REFRIGERATION_EXTERNAL_LIGHT_BRIGHTNESS = (
"Refrigeration.Common.Setting.Light.External.Brightness"
)
REFRIGERATION_SUPERMODEFREEZER = "Refrigeration.FridgeFreezer.Setting.SuperModeFreezer" REFRIGERATION_SUPERMODEFREEZER = "Refrigeration.FridgeFreezer.Setting.SuperModeFreezer"
REFRIGERATION_SUPERMODEREFRIGERATOR = ( REFRIGERATION_SUPERMODEREFRIGERATOR = (
"Refrigeration.FridgeFreezer.Setting.SuperModeRefrigerator" "Refrigeration.FridgeFreezer.Setting.SuperModeRefrigerator"

View File

@ -1,5 +1,6 @@
"""Provides a light for Home Connect.""" """Provides a light for Home Connect."""
from dataclasses import dataclass
import logging import logging
from math import ceil from math import ceil
from typing import Any from typing import Any
@ -11,13 +12,15 @@ from homeassistant.components.light import (
ATTR_HS_COLOR, ATTR_HS_COLOR,
ColorMode, ColorMode,
LightEntity, LightEntity,
LightEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ENTITIES from homeassistant.const import CONF_DEVICE, CONF_ENTITIES
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
import homeassistant.util.color as color_util import homeassistant.util.color as color_util
from .api import HomeConnectDevice
from .const import ( from .const import (
ATTR_VALUE, ATTR_VALUE,
BSH_AMBIENT_LIGHT_BRIGHTNESS, BSH_AMBIENT_LIGHT_BRIGHTNESS,
@ -28,12 +31,38 @@ from .const import (
COOKING_LIGHTING, COOKING_LIGHTING,
COOKING_LIGHTING_BRIGHTNESS, COOKING_LIGHTING_BRIGHTNESS,
DOMAIN, DOMAIN,
REFRIGERATION_EXTERNAL_LIGHT_BRIGHTNESS,
REFRIGERATION_EXTERNAL_LIGHT_POWER,
REFRIGERATION_INTERNAL_LIGHT_BRIGHTNESS,
REFRIGERATION_INTERNAL_LIGHT_POWER,
) )
from .entity import HomeConnectEntity from .entity import HomeConnectEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@dataclass(frozen=True, kw_only=True)
class HomeConnectLightEntityDescription(LightEntityDescription):
"""Light entity description."""
on_key: str
brightness_key: str | None
LIGHTS: tuple[HomeConnectLightEntityDescription, ...] = (
HomeConnectLightEntityDescription(
key="Internal Light",
on_key=REFRIGERATION_INTERNAL_LIGHT_POWER,
brightness_key=REFRIGERATION_INTERNAL_LIGHT_BRIGHTNESS,
),
HomeConnectLightEntityDescription(
key="External Light",
on_key=REFRIGERATION_EXTERNAL_LIGHT_POWER,
brightness_key=REFRIGERATION_EXTERNAL_LIGHT_BRIGHTNESS,
),
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ConfigEntry,
@ -48,7 +77,18 @@ async def async_setup_entry(
for device_dict in hc_api.devices: for device_dict in hc_api.devices:
entity_dicts = device_dict.get(CONF_ENTITIES, {}).get("light", []) entity_dicts = device_dict.get(CONF_ENTITIES, {}).get("light", [])
entity_list = [HomeConnectLight(**d) for d in entity_dicts] entity_list = [HomeConnectLight(**d) for d in entity_dicts]
entities += entity_list device: HomeConnectDevice = device_dict[CONF_DEVICE]
# Auto-discover entities
entities.extend(
HomeConnectCoolingLight(
device=device,
ambient=False,
entity_description=description,
)
for description in LIGHTS
if description.on_key in device.appliance.status
)
entities.extend(entity_list)
return entities return entities
async_add_entities(await hass.async_add_executor_job(get_entities), True) async_add_entities(await hass.async_add_executor_job(get_entities), True)
@ -57,10 +97,14 @@ async def async_setup_entry(
class HomeConnectLight(HomeConnectEntity, LightEntity): class HomeConnectLight(HomeConnectEntity, LightEntity):
"""Light for Home Connect.""" """Light for Home Connect."""
def __init__(self, device, desc, ambient): def __init__(self, device, desc, ambient) -> None:
"""Initialize the entity.""" """Initialize the entity."""
super().__init__(device, desc) super().__init__(device, desc)
self._ambient = ambient self._ambient = ambient
self._percentage_scale = (10, 100)
self._brightness_key: str | None
self._custom_color_key: str | None
self._color_key: str | None
if ambient: if ambient:
self._brightness_key = BSH_AMBIENT_LIGHT_BRIGHTNESS self._brightness_key = BSH_AMBIENT_LIGHT_BRIGHTNESS
self._key = BSH_AMBIENT_LIGHT_ENABLED self._key = BSH_AMBIENT_LIGHT_ENABLED
@ -97,10 +141,15 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
except HomeConnectError as err: except HomeConnectError as err:
_LOGGER.error("Error while trying selecting customcolor: %s", err) _LOGGER.error("Error while trying selecting customcolor: %s", err)
if self._attr_brightness is not None: if self._attr_brightness is not None:
brightness = 10 + ceil(self._attr_brightness / 255 * 90) brightness_arg = self._attr_brightness
if ATTR_BRIGHTNESS in kwargs: if ATTR_BRIGHTNESS in kwargs:
brightness = 10 + ceil(kwargs[ATTR_BRIGHTNESS] / 255 * 90) brightness_arg = kwargs[ATTR_BRIGHTNESS]
brightness = ceil(
color_util.brightness_to_value(
self._percentage_scale, brightness_arg
)
)
hs_color = kwargs.get(ATTR_HS_COLOR, self._attr_hs_color) hs_color = kwargs.get(ATTR_HS_COLOR, self._attr_hs_color)
if hs_color is not None: if hs_color is not None:
@ -120,8 +169,16 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
) )
elif ATTR_BRIGHTNESS in kwargs: elif ATTR_BRIGHTNESS in kwargs:
_LOGGER.debug("Changing brightness for: %s", self.name) _LOGGER.debug(
brightness = 10 + ceil(kwargs[ATTR_BRIGHTNESS] / 255 * 90) "Changing brightness for: %s, to: %s",
self.name,
kwargs[ATTR_BRIGHTNESS],
)
brightness = ceil(
color_util.brightness_to_value(
self._percentage_scale, kwargs[ATTR_BRIGHTNESS]
)
)
try: try:
await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(
self.device.appliance.set_setting, self._brightness_key, brightness self.device.appliance.set_setting, self._brightness_key, brightness
@ -172,7 +229,9 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
rgb = color_util.rgb_hex_to_rgb_list(colorvalue) rgb = color_util.rgb_hex_to_rgb_list(colorvalue)
hsv = color_util.color_RGB_to_hsv(rgb[0], rgb[1], rgb[2]) hsv = color_util.color_RGB_to_hsv(rgb[0], rgb[1], rgb[2])
self._attr_hs_color = (hsv[0], hsv[1]) self._attr_hs_color = (hsv[0], hsv[1])
self._attr_brightness = ceil((hsv[2] - 10) * 255 / 90) self._attr_brightness = color_util.value_to_brightness(
self._percentage_scale, hsv[2]
)
_LOGGER.debug("Updated, new brightness: %s", self._attr_brightness) _LOGGER.debug("Updated, new brightness: %s", self._attr_brightness)
else: else:
@ -180,7 +239,24 @@ class HomeConnectLight(HomeConnectEntity, LightEntity):
if brightness is None: if brightness is None:
self._attr_brightness = None self._attr_brightness = None
else: else:
self._attr_brightness = ceil( self._attr_brightness = color_util.value_to_brightness(
(brightness.get(ATTR_VALUE) - 10) * 255 / 90 self._percentage_scale, brightness[ATTR_VALUE]
) )
_LOGGER.debug("Updated, new brightness: %s", self._attr_brightness) _LOGGER.debug("Updated, new brightness: %s", self._attr_brightness)
class HomeConnectCoolingLight(HomeConnectLight):
"""Light entity for Cooling Appliances."""
def __init__(
self,
device: HomeConnectDevice,
ambient: bool,
entity_description: HomeConnectLightEntityDescription,
) -> None:
"""Initialize Cooling Light Entity."""
super().__init__(device, entity_description.key, ambient)
self.entity_description = entity_description
self._key = entity_description.on_key
self._brightness_key = entity_description.brightness_key
self._percentage_scale = (1, 100)

View File

@ -138,6 +138,22 @@
"constraints": { "constraints": {
"access": "readWrite" "access": "readWrite"
} }
},
{
"key": "Refrigeration.Common.Setting.Light.External.Power",
"value": true,
"type": "Boolean"
},
{
"key": "Refrigeration.Common.Setting.Light.External.Brightness",
"value": 70,
"unit": "%",
"type": "Double",
"constraints": {
"min": 0,
"max": 100,
"access": "readWrite"
}
} }
] ]
} }

View File

@ -3,7 +3,7 @@
from collections.abc import Awaitable, Callable, Generator from collections.abc import Awaitable, Callable, Generator
from unittest.mock import MagicMock, Mock from unittest.mock import MagicMock, Mock
from homeconnect.api import HomeConnectError from homeconnect.api import HomeConnectAppliance, HomeConnectError
import pytest import pytest
from homeassistant.components.home_connect.const import ( from homeassistant.components.home_connect.const import (
@ -12,6 +12,8 @@ from homeassistant.components.home_connect.const import (
BSH_AMBIENT_LIGHT_ENABLED, BSH_AMBIENT_LIGHT_ENABLED,
COOKING_LIGHTING, COOKING_LIGHTING,
COOKING_LIGHTING_BRIGHTNESS, COOKING_LIGHTING_BRIGHTNESS,
REFRIGERATION_EXTERNAL_LIGHT_BRIGHTNESS,
REFRIGERATION_EXTERNAL_LIGHT_POWER,
) )
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
@ -148,6 +150,19 @@ async def test_light(
STATE_ON, STATE_ON,
"Hood", "Hood",
), ),
(
"light.fridgefreezer_external_light",
{
REFRIGERATION_EXTERNAL_LIGHT_POWER: {
"value": True,
},
REFRIGERATION_EXTERNAL_LIGHT_BRIGHTNESS: {"value": 75},
},
SERVICE_TURN_ON,
{},
STATE_ON,
"FridgeFreezer",
),
], ],
indirect=["appliance"], indirect=["appliance"],
) )
@ -166,7 +181,14 @@ async def test_light_functionality(
get_appliances: MagicMock, get_appliances: MagicMock,
) -> None: ) -> None:
"""Test light functionality.""" """Test light functionality."""
appliance.status.update(SETTINGS_STATUS) appliance.status.update(
HomeConnectAppliance.json2dict(
load_json_object_fixture("home_connect/settings.json")
.get(appliance.name)
.get("data")
.get("settings")
)
)
get_appliances.return_value = [appliance] get_appliances.return_value = [appliance]
assert config_entry.state == ConfigEntryState.NOT_LOADED assert config_entry.state == ConfigEntryState.NOT_LOADED