From 85aa32338e0fa31dc346d2d497dc48c20decc22b Mon Sep 17 00:00:00 2001 From: Robert Contreras Date: Fri, 13 Sep 2024 10:31:35 -0700 Subject: [PATCH] Add Home Connect sensors for fridge door states and alarms (#125490) * New sensors for Fridge door states and alarms * Move 2 option entities to binary_sensor, tests * Change state translations * Fix stale docstring --- .../components/home_connect/binary_sensor.py | 87 ++++++++++++- .../components/home_connect/const.py | 26 ++++ .../components/home_connect/icons.json | 44 +++++++ .../components/home_connect/sensor.py | 114 +++++++++++++++++- .../components/home_connect/strings.json | 44 +++++++ .../home_connect/fixtures/status.json | 4 + .../home_connect/test_binary_sensor.py | 64 +++++++++- tests/components/home_connect/test_sensor.py | 101 +++++++++++++++- 8 files changed, 478 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/home_connect/binary_sensor.py b/homeassistant/components/home_connect/binary_sensor.py index 84b02be1cc4..758759c135b 100644 --- a/homeassistant/components/home_connect/binary_sensor.py +++ b/homeassistant/components/home_connect/binary_sensor.py @@ -1,14 +1,21 @@ """Provides a binary sensor for Home Connect.""" +from dataclasses import dataclass, field import logging -from homeassistant.components.binary_sensor import BinarySensorEntity +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, + BinarySensorEntityDescription, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ENTITIES from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback +from .api import HomeConnectDevice from .const import ( + ATTR_DEVICE, ATTR_VALUE, BSH_DOOR_STATE, BSH_DOOR_STATE_CLOSED, @@ -17,12 +24,47 @@ from .const import ( BSH_REMOTE_CONTROL_ACTIVATION_STATE, BSH_REMOTE_START_ALLOWANCE_STATE, DOMAIN, + REFRIGERATION_STATUS_DOOR_CHILLER, + REFRIGERATION_STATUS_DOOR_CLOSED, + REFRIGERATION_STATUS_DOOR_FREEZER, + REFRIGERATION_STATUS_DOOR_OPEN, + REFRIGERATION_STATUS_DOOR_REFRIGERATOR, ) from .entity import HomeConnectEntity _LOGGER = logging.getLogger(__name__) +@dataclass(frozen=True, kw_only=True) +class HomeConnectBinarySensorEntityDescription(BinarySensorEntityDescription): + """Entity Description class for binary sensors.""" + + state_key: str | None + device_class: BinarySensorDeviceClass | None = BinarySensorDeviceClass.DOOR + boolean_map: dict[str, bool] = field( + default_factory=lambda: { + REFRIGERATION_STATUS_DOOR_CLOSED: False, + REFRIGERATION_STATUS_DOOR_OPEN: True, + } + ) + + +BINARY_SENSORS: tuple[HomeConnectBinarySensorEntityDescription, ...] = ( + HomeConnectBinarySensorEntityDescription( + key="Chiller Door", + state_key=REFRIGERATION_STATUS_DOOR_CHILLER, + ), + HomeConnectBinarySensorEntityDescription( + key="Freezer Door", + state_key=REFRIGERATION_STATUS_DOOR_FREEZER, + ), + HomeConnectBinarySensorEntityDescription( + key="Refrigerator Door", + state_key=REFRIGERATION_STATUS_DOOR_REFRIGERATOR, + ), +) + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -36,6 +78,15 @@ async def async_setup_entry( for device_dict in hc_api.devices: entity_dicts = device_dict.get(CONF_ENTITIES, {}).get("binary_sensor", []) entities += [HomeConnectBinarySensor(**d) for d in entity_dicts] + device: HomeConnectDevice = device_dict[ATTR_DEVICE] + # Auto-discover entities + entities.extend( + HomeConnectFridgeDoorBinarySensor( + device=device, entity_description=description + ) + for description in BINARY_SENSORS + if description.state_key in device.appliance.status + ) return entities async_add_entities(await hass.async_add_executor_job(get_entities), True) @@ -93,3 +144,37 @@ class HomeConnectBinarySensor(HomeConnectEntity, BinarySensorEntity): def device_class(self): """Return the device class.""" return self._device_class + + +class HomeConnectFridgeDoorBinarySensor(HomeConnectEntity, BinarySensorEntity): + """Binary sensor for Home Connect Fridge Doors.""" + + entity_description: HomeConnectBinarySensorEntityDescription + + def __init__( + self, + device: HomeConnectDevice, + entity_description: HomeConnectBinarySensorEntityDescription, + ) -> None: + """Initialize the entity.""" + self.entity_description = entity_description + super().__init__(device, entity_description.key) + + async def async_update(self) -> None: + """Update the binary sensor's status.""" + _LOGGER.debug( + "Updating: %s, cur state: %s", + self._attr_unique_id, + self.state, + ) + self._attr_is_on = self.entity_description.boolean_map.get( + self.device.appliance.status.get(self.entity_description.state_key, {}).get( + ATTR_VALUE + ) + ) + self._attr_available = self._attr_is_on is not None + _LOGGER.debug( + "Updated: %s, new state: %s", + self._attr_unique_id, + self.state, + ) diff --git a/homeassistant/components/home_connect/const.py b/homeassistant/components/home_connect/const.py index 4c21201c37a..68bad33ec50 100644 --- a/homeassistant/components/home_connect/const.py +++ b/homeassistant/components/home_connect/const.py @@ -14,6 +14,9 @@ BSH_REMOTE_CONTROL_ACTIVATION_STATE = "BSH.Common.Status.RemoteControlActive" BSH_REMOTE_START_ALLOWANCE_STATE = "BSH.Common.Status.RemoteControlStartAllowed" BSH_CHILD_LOCK_STATE = "BSH.Common.Setting.ChildLock" +BSH_EVENT_PRESENT_STATE_PRESENT = "BSH.Common.EnumType.EventPresentState.Present" +BSH_EVENT_PRESENT_STATE_CONFIRMED = "BSH.Common.EnumType.EventPresentState.Confirmed" +BSH_EVENT_PRESENT_STATE_OFF = "BSH.Common.EnumType.EventPresentState.Off" BSH_OPERATION_STATE = "BSH.Common.Status.OperationState" BSH_OPERATION_STATE_RUN = "BSH.Common.EnumType.OperationState.Run" @@ -23,6 +26,11 @@ BSH_OPERATION_STATE_FINISHED = "BSH.Common.EnumType.OperationState.Finished" COOKING_LIGHTING = "Cooking.Common.Setting.Lighting" COOKING_LIGHTING_BRIGHTNESS = "Cooking.Common.Setting.LightingBrightness" +COFFEE_EVENT_BEAN_CONTAINER_EMPTY = ( + "ConsumerProducts.CoffeeMaker.Event.BeanContainerEmpty" +) +COFFEE_EVENT_WATER_TANK_EMPTY = "ConsumerProducts.CoffeeMaker.Event.WaterTankEmpty" +COFFEE_EVENT_DRIP_TRAY_FULL = "ConsumerProducts.CoffeeMaker.Event.DripTrayFull" REFRIGERATION_SUPERMODEFREEZER = "Refrigeration.FridgeFreezer.Setting.SuperModeFreezer" REFRIGERATION_SUPERMODEREFRIGERATOR = ( @@ -30,6 +38,24 @@ REFRIGERATION_SUPERMODEREFRIGERATOR = ( ) REFRIGERATION_DISPENSER = "Refrigeration.Common.Setting.Dispenser.Enabled" +REFRIGERATION_STATUS_DOOR_CHILLER = "Refrigeration.Common.Status.Door.ChillerCommon" +REFRIGERATION_STATUS_DOOR_FREEZER = "Refrigeration.Common.Status.Door.Freezer" +REFRIGERATION_STATUS_DOOR_REFRIGERATOR = "Refrigeration.Common.Status.Door.Refrigerator" + +REFRIGERATION_STATUS_DOOR_CLOSED = "Refrigeration.Common.EnumType.Door.States.Closed" +REFRIGERATION_STATUS_DOOR_OPEN = "Refrigeration.Common.EnumType.Door.States.Open" + +REFRIGERATION_EVENT_DOOR_ALARM_REFRIGERATOR = ( + "Refrigeration.FridgeFreezer.Event.DoorAlarmRefrigerator" +) +REFRIGERATION_EVENT_DOOR_ALARM_FREEZER = ( + "Refrigeration.FridgeFreezer.Event.DoorAlarmFreezer" +) +REFRIGERATION_EVENT_TEMP_ALARM_FREEZER = ( + "Refrigeration.FridgeFreezer.Event.TemperatureAlarmFreezer" +) + + BSH_AMBIENT_LIGHT_ENABLED = "BSH.Common.Setting.AmbientLightEnabled" BSH_AMBIENT_LIGHT_BRIGHTNESS = "BSH.Common.Setting.AmbientLightBrightness" BSH_AMBIENT_LIGHT_COLOR = "BSH.Common.Setting.AmbientLightColor" diff --git a/homeassistant/components/home_connect/icons.json b/homeassistant/components/home_connect/icons.json index 163c03b297c..949b30919b5 100644 --- a/homeassistant/components/home_connect/icons.json +++ b/homeassistant/components/home_connect/icons.json @@ -23,6 +23,50 @@ } }, "entity": { + "sensor": { + "alarm_sensor_fridge": { + "default": "mdi:fridge", + "state": { + "confirmed": "mdi:fridge-alert-outline", + "present": "mdi:fridge-alert" + } + }, + "alarm_sensor_freezer": { + "default": "mdi:snowflake", + "state": { + "confirmed": "mdi:snowflake-check", + "present": "mdi:snowflake-alert" + } + }, + "alarm_sensor_temp": { + "default": "mdi:thermometer", + "state": { + "confirmed": "mdi:thermometer-check", + "present": "mdi:thermometer-alert" + } + }, + "alarm_sensor_coffee_bean_container": { + "default": "mdi:coffee-maker", + "state": { + "confirmed": "mdi:coffee-maker-check", + "present": "mdi:coffee-maker-outline" + } + }, + "alarm_sensor_coffee_water_tank": { + "default": "mdi:water", + "state": { + "confirmed": "mdi:water-check", + "present": "mdi:water-alert" + } + }, + "alarm_sensor_coffee_drip_tray": { + "default": "mdi:tray", + "state": { + "confirmed": "mdi:tray-full", + "present": "mdi:tray-alert" + } + } + }, "switch": { "refrigeration_dispenser": { "default": "mdi:snowflake", diff --git a/homeassistant/components/home_connect/sensor.py b/homeassistant/components/home_connect/sensor.py index 9bd48617fb3..c91864c2680 100644 --- a/homeassistant/components/home_connect/sensor.py +++ b/homeassistant/components/home_connect/sensor.py @@ -1,29 +1,95 @@ """Provides a sensor for Home Connect.""" +from dataclasses import dataclass, field from datetime import datetime, timedelta import logging from typing import cast -from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ENTITIES from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.util.dt as dt_util +from .api import ConfigEntryAuth, HomeConnectDevice from .const import ( + ATTR_DEVICE, ATTR_VALUE, + BSH_EVENT_PRESENT_STATE_OFF, BSH_OPERATION_STATE, BSH_OPERATION_STATE_FINISHED, BSH_OPERATION_STATE_PAUSE, BSH_OPERATION_STATE_RUN, + COFFEE_EVENT_BEAN_CONTAINER_EMPTY, + COFFEE_EVENT_DRIP_TRAY_FULL, + COFFEE_EVENT_WATER_TANK_EMPTY, DOMAIN, + REFRIGERATION_EVENT_DOOR_ALARM_FREEZER, + REFRIGERATION_EVENT_DOOR_ALARM_REFRIGERATOR, + REFRIGERATION_EVENT_TEMP_ALARM_FREEZER, ) from .entity import HomeConnectEntity _LOGGER = logging.getLogger(__name__) +@dataclass(frozen=True, kw_only=True) +class HomeConnectSensorEntityDescription(SensorEntityDescription): + """Entity Description class for sensors.""" + + device_class: SensorDeviceClass | None = SensorDeviceClass.ENUM + options: list[str] | None = field( + default_factory=lambda: ["confirmed", "off", "present"] + ) + state_key: str + appliance_types: tuple[str, ...] + + +SENSORS: tuple[HomeConnectSensorEntityDescription, ...] = ( + HomeConnectSensorEntityDescription( + key="Door Alarm Freezer", + translation_key="alarm_sensor_freezer", + state_key=REFRIGERATION_EVENT_DOOR_ALARM_FREEZER, + appliance_types=("FridgeFreezer", "Freezer"), + ), + HomeConnectSensorEntityDescription( + key="Door Alarm Refrigerator", + translation_key="alarm_sensor_fridge", + state_key=REFRIGERATION_EVENT_DOOR_ALARM_REFRIGERATOR, + appliance_types=("FridgeFreezer", "Refrigerator"), + ), + HomeConnectSensorEntityDescription( + key="Temperature Alarm Freezer", + translation_key="alarm_sensor_temp", + state_key=REFRIGERATION_EVENT_TEMP_ALARM_FREEZER, + appliance_types=("FridgeFreezer", "Freezer"), + ), + HomeConnectSensorEntityDescription( + key="Bean Container Empty", + translation_key="alarm_sensor_coffee_bean_container", + state_key=COFFEE_EVENT_BEAN_CONTAINER_EMPTY, + appliance_types=("CoffeeMaker",), + ), + HomeConnectSensorEntityDescription( + key="Water Tank Empty", + translation_key="alarm_sensor_coffee_water_tank", + state_key=COFFEE_EVENT_WATER_TANK_EMPTY, + appliance_types=("CoffeeMaker",), + ), + HomeConnectSensorEntityDescription( + key="Drip Tray Full", + translation_key="alarm_sensor_coffee_drip_tray", + state_key=COFFEE_EVENT_DRIP_TRAY_FULL, + appliance_types=("CoffeeMaker",), + ), +) + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -34,10 +100,20 @@ async def async_setup_entry( def get_entities(): """Get a list of entities.""" entities = [] - hc_api = hass.data[DOMAIN][config_entry.entry_id] + hc_api: ConfigEntryAuth = hass.data[DOMAIN][config_entry.entry_id] for device_dict in hc_api.devices: entity_dicts = device_dict.get(CONF_ENTITIES, {}).get("sensor", []) entities += [HomeConnectSensor(**d) for d in entity_dicts] + device: HomeConnectDevice = device_dict[ATTR_DEVICE] + # Auto-discover entities + entities.extend( + HomeConnectAlarmSensor( + device, + entity_description=description, + ) + for description in SENSORS + if device.appliance.type in description.appliance_types + ) return entities async_add_entities(await hass.async_add_executor_job(get_entities), True) @@ -101,3 +177,37 @@ class HomeConnectSensor(HomeConnectEntity, SensorEntity): -1 ] _LOGGER.debug("Updated, new state: %s", self._attr_native_value) + + +class HomeConnectAlarmSensor(HomeConnectEntity, SensorEntity): + """Sensor entity setup using SensorEntityDescription.""" + + entity_description: HomeConnectSensorEntityDescription + + def __init__( + self, + device: HomeConnectDevice, + entity_description: HomeConnectSensorEntityDescription, + ) -> None: + """Initialize the entity.""" + self.entity_description = entity_description + super().__init__(device, self.entity_description.key) + + @property + def available(self) -> bool: + """Return true if the sensor is available.""" + return self._attr_native_value is not None + + async def async_update(self) -> None: + """Update the sensor's status.""" + self._attr_native_value = ( + self.device.appliance.status.get(self.entity_description.state_key, {}) + .get(ATTR_VALUE, BSH_EVENT_PRESENT_STATE_OFF) + .rsplit(".", maxsplit=1)[-1] + .lower() + ) + _LOGGER.debug( + "Updated: %s, new state: %s", + self._attr_unique_id, + self._attr_native_value, + ) diff --git a/homeassistant/components/home_connect/strings.json b/homeassistant/components/home_connect/strings.json index 8afd3aaf8ce..1fcd95e9cb2 100644 --- a/homeassistant/components/home_connect/strings.json +++ b/homeassistant/components/home_connect/strings.json @@ -1,4 +1,8 @@ { + "common": { + "confirmed": "Confirmed", + "present": "Present" + }, "config": { "step": { "pick_implementation": { @@ -129,5 +133,45 @@ "value": { "name": "Value", "description": "Value of the setting." } } } + }, + "entity": { + "sensor": { + "alarm_sensor_fridge": { + "state": { + "confirmed": "[%key:component::home_connect::common::confirmed%]", + "present": "[%key:component::home_connect::common::present%]" + } + }, + "alarm_sensor_freezer": { + "state": { + "confirmed": "[%key:component::home_connect::common::confirmed%]", + "present": "[%key:component::home_connect::common::present%]" + } + }, + "alarm_sensor_temp": { + "state": { + "confirmed": "[%key:component::home_connect::common::confirmed%]", + "present": "[%key:component::home_connect::common::present%]" + } + }, + "alarm_sensor_coffee_bean_container": { + "state": { + "confirmed": "[%key:component::home_connect::common::confirmed%]", + "present": "[%key:component::home_connect::common::present%]" + } + }, + "alarm_sensor_coffee_water_tank": { + "state": { + "confirmed": "[%key:component::home_connect::common::confirmed%]", + "present": "[%key:component::home_connect::common::present%]" + } + }, + "alarm_sensor_coffee_drip_tray": { + "state": { + "confirmed": "[%key:component::home_connect::common::confirmed%]", + "present": "[%key:component::home_connect::common::present%]" + } + } + } } } diff --git a/tests/components/home_connect/fixtures/status.json b/tests/components/home_connect/fixtures/status.json index 8eac586a308..efdbde6cd97 100644 --- a/tests/components/home_connect/fixtures/status.json +++ b/tests/components/home_connect/fixtures/status.json @@ -10,6 +10,10 @@ { "key": "BSH.Common.Status.DoorState", "value": "BSH.Common.EnumType.DoorState.Closed" + }, + { + "key": "Refrigeration.Common.Status.Door.Refrigerator", + "value": "BSH.Common.EnumType.DoorState.Open" } ] } diff --git a/tests/components/home_connect/test_binary_sensor.py b/tests/components/home_connect/test_binary_sensor.py index 39502507439..de4263f6345 100644 --- a/tests/components/home_connect/test_binary_sensor.py +++ b/tests/components/home_connect/test_binary_sensor.py @@ -3,6 +3,7 @@ from collections.abc import Awaitable, Callable from unittest.mock import MagicMock, Mock +from homeconnect.api import HomeConnectAPI import pytest from homeassistant.components.home_connect.const import ( @@ -10,13 +11,16 @@ from homeassistant.components.home_connect.const import ( BSH_DOOR_STATE_CLOSED, BSH_DOOR_STATE_LOCKED, BSH_DOOR_STATE_OPEN, + REFRIGERATION_STATUS_DOOR_CLOSED, + REFRIGERATION_STATUS_DOOR_OPEN, + REFRIGERATION_STATUS_DOOR_REFRIGERATOR, ) from homeassistant.config_entries import ConfigEntryState -from homeassistant.const import Platform +from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_component import async_update_entity -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, load_json_object_fixture @pytest.fixture @@ -70,3 +74,59 @@ async def test_binary_sensors_door_states( await async_update_entity(hass, entity_id) await hass.async_block_till_done() assert hass.states.is_state(entity_id, expected) + + +@pytest.mark.parametrize( + ("entity_id", "status_key", "event_value_update", "expected", "appliance"), + [ + ( + "binary_sensor.fridgefreezer_refrigerator_door", + REFRIGERATION_STATUS_DOOR_REFRIGERATOR, + REFRIGERATION_STATUS_DOOR_CLOSED, + STATE_OFF, + "FridgeFreezer", + ), + ( + "binary_sensor.fridgefreezer_refrigerator_door", + REFRIGERATION_STATUS_DOOR_REFRIGERATOR, + REFRIGERATION_STATUS_DOOR_OPEN, + STATE_ON, + "FridgeFreezer", + ), + ( + "binary_sensor.fridgefreezer_refrigerator_door", + REFRIGERATION_STATUS_DOOR_REFRIGERATOR, + "", + STATE_UNAVAILABLE, + "FridgeFreezer", + ), + ], + indirect=["appliance"], +) +@pytest.mark.usefixtures("bypass_throttle") +async def test_bianry_sensors_fridge_door_states( + entity_id: str, + status_key: str, + event_value_update: str, + appliance: Mock, + expected: str, + hass: HomeAssistant, + config_entry: MockConfigEntry, + integration_setup: Callable[[], Awaitable[bool]], + setup_credentials: None, + get_appliances: MagicMock, +) -> None: + """Tests for Home Connect Fridge appliance door states.""" + appliance.status.update( + HomeConnectAPI.json2dict( + load_json_object_fixture("home_connect/status.json")["data"]["status"] + ) + ) + get_appliances.return_value = [appliance] + assert config_entry.state == ConfigEntryState.NOT_LOADED + assert await integration_setup() + assert config_entry.state == ConfigEntryState.LOADED + appliance.status.update({status_key: {"value": event_value_update}}) + await async_update_entity(hass, entity_id) + await hass.async_block_till_done() + assert hass.states.is_state(entity_id, expected) diff --git a/tests/components/home_connect/test_sensor.py b/tests/components/home_connect/test_sensor.py index 661ac62403f..f0565c178fe 100644 --- a/tests/components/home_connect/test_sensor.py +++ b/tests/components/home_connect/test_sensor.py @@ -4,14 +4,22 @@ from collections.abc import Awaitable, Callable from unittest.mock import MagicMock, Mock from freezegun.api import FrozenDateTimeFactory +from homeconnect.api import HomeConnectAPI import pytest +from homeassistant.components.home_connect.const import ( + BSH_EVENT_PRESENT_STATE_CONFIRMED, + BSH_EVENT_PRESENT_STATE_OFF, + BSH_EVENT_PRESENT_STATE_PRESENT, + COFFEE_EVENT_BEAN_CONTAINER_EMPTY, + REFRIGERATION_EVENT_DOOR_ALARM_FREEZER, +) from homeassistant.config_entries import ConfigEntryState from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_component import async_update_entity -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, load_json_object_fixture TEST_HC_APP = "Dishwasher" @@ -207,3 +215,94 @@ async def test_remaining_prog_time_edge_cases( await hass.async_block_till_done() freezer.tick() assert hass.states.is_state(entity_id, expected_state) + + +@pytest.mark.parametrize( + ("entity_id", "status_key", "event_value_update", "expected", "appliance"), + [ + ( + "sensor.fridgefreezer_door_alarm_freezer", + "EVENT_NOT_IN_STATUS_YET_SO_SET_TO_OFF", + "", + "off", + "FridgeFreezer", + ), + ( + "sensor.fridgefreezer_door_alarm_freezer", + REFRIGERATION_EVENT_DOOR_ALARM_FREEZER, + BSH_EVENT_PRESENT_STATE_OFF, + "off", + "FridgeFreezer", + ), + ( + "sensor.fridgefreezer_door_alarm_freezer", + REFRIGERATION_EVENT_DOOR_ALARM_FREEZER, + BSH_EVENT_PRESENT_STATE_PRESENT, + "present", + "FridgeFreezer", + ), + ( + "sensor.fridgefreezer_door_alarm_freezer", + REFRIGERATION_EVENT_DOOR_ALARM_FREEZER, + BSH_EVENT_PRESENT_STATE_CONFIRMED, + "confirmed", + "FridgeFreezer", + ), + ( + "sensor.coffeemaker_bean_container_empty", + "EVENT_NOT_IN_STATUS_YET_SO_SET_TO_OFF", + "", + "off", + "CoffeeMaker", + ), + ( + "sensor.coffeemaker_bean_container_empty", + COFFEE_EVENT_BEAN_CONTAINER_EMPTY, + BSH_EVENT_PRESENT_STATE_OFF, + "off", + "CoffeeMaker", + ), + ( + "sensor.coffeemaker_bean_container_empty", + COFFEE_EVENT_BEAN_CONTAINER_EMPTY, + BSH_EVENT_PRESENT_STATE_PRESENT, + "present", + "CoffeeMaker", + ), + ( + "sensor.coffeemaker_bean_container_empty", + COFFEE_EVENT_BEAN_CONTAINER_EMPTY, + BSH_EVENT_PRESENT_STATE_CONFIRMED, + "confirmed", + "CoffeeMaker", + ), + ], + indirect=["appliance"], +) +@pytest.mark.usefixtures("bypass_throttle") +async def test_sensors_states( + entity_id: str, + status_key: str, + event_value_update: str, + appliance: Mock, + expected: str, + hass: HomeAssistant, + config_entry: MockConfigEntry, + integration_setup: Callable[[], Awaitable[bool]], + setup_credentials: None, + get_appliances: MagicMock, +) -> None: + """Tests for Appliance alarm sensors.""" + appliance.status.update( + HomeConnectAPI.json2dict( + load_json_object_fixture("home_connect/status.json")["data"]["status"] + ) + ) + get_appliances.return_value = [appliance] + assert config_entry.state == ConfigEntryState.NOT_LOADED + assert await integration_setup() + assert config_entry.state == ConfigEntryState.LOADED + appliance.status.update({status_key: {"value": event_value_update}}) + await async_update_entity(hass, entity_id) + await hass.async_block_till_done() + assert hass.states.is_state(entity_id, expected)