diff --git a/homeassistant/components/airobot/manifest.json b/homeassistant/components/airobot/manifest.json index 861f220b169fc8..14d7e311acc715 100644 --- a/homeassistant/components/airobot/manifest.json +++ b/homeassistant/components/airobot/manifest.json @@ -13,5 +13,5 @@ "iot_class": "local_polling", "loggers": ["pyairobotrest"], "quality_scale": "silver", - "requirements": ["pyairobotrest==0.1.0"] + "requirements": ["pyairobotrest==0.2.0"] } diff --git a/homeassistant/components/bsblan/icons.json b/homeassistant/components/bsblan/icons.json index 8846d4e153c3af..f58cebd1651e11 100644 --- a/homeassistant/components/bsblan/icons.json +++ b/homeassistant/components/bsblan/icons.json @@ -2,6 +2,9 @@ "services": { "set_hot_water_schedule": { "service": "mdi:calendar-clock" + }, + "sync_time": { + "service": "mdi:timer-sync-outline" } } } diff --git a/homeassistant/components/bsblan/services.py b/homeassistant/components/bsblan/services.py index bd0d876c710cd9..7768c790041b8c 100644 --- a/homeassistant/components/bsblan/services.py +++ b/homeassistant/components/bsblan/services.py @@ -13,6 +13,7 @@ from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import config_validation as cv, device_registry as dr +from homeassistant.util import dt as dt_util from .const import DOMAIN @@ -30,8 +31,9 @@ ATTR_SATURDAY_SLOTS = "saturday_slots" ATTR_SUNDAY_SLOTS = "sunday_slots" -# Service name +# Service names SERVICE_SET_HOT_WATER_SCHEDULE = "set_hot_water_schedule" +SERVICE_SYNC_TIME = "sync_time" # Schema for a single time slot @@ -203,6 +205,74 @@ async def set_hot_water_schedule(service_call: ServiceCall) -> None: await entry.runtime_data.slow_coordinator.async_request_refresh() +async def async_sync_time(service_call: ServiceCall) -> None: + """Synchronize BSB-LAN device time with Home Assistant.""" + device_id: str = service_call.data[ATTR_DEVICE_ID] + + # Get the device and config entry + device_registry = dr.async_get(service_call.hass) + device_entry = device_registry.async_get(device_id) + + if device_entry is None: + raise ServiceValidationError( + translation_domain=DOMAIN, + translation_key="invalid_device_id", + translation_placeholders={"device_id": device_id}, + ) + + # Find the config entry for this device + matching_entries: list[BSBLanConfigEntry] = [ + entry + for entry in service_call.hass.config_entries.async_entries(DOMAIN) + if entry.entry_id in device_entry.config_entries + ] + + if not matching_entries: + raise ServiceValidationError( + translation_domain=DOMAIN, + translation_key="no_config_entry_for_device", + translation_placeholders={"device_id": device_entry.name or device_id}, + ) + + entry = matching_entries[0] + + # Verify the config entry is loaded + if entry.state is not ConfigEntryState.LOADED: + raise ServiceValidationError( + translation_domain=DOMAIN, + translation_key="config_entry_not_loaded", + translation_placeholders={"device_name": device_entry.name or device_id}, + ) + + client = entry.runtime_data.client + + try: + # Get current device time + device_time = await client.time() + current_time = dt_util.now() + current_time_str = current_time.strftime("%d.%m.%Y %H:%M:%S") + + # Only sync if device time differs from HA time + if device_time.time.value != current_time_str: + await client.set_time(current_time_str) + except BSBLANError as err: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="sync_time_failed", + translation_placeholders={ + "device_name": device_entry.name or device_id, + "error": str(err), + }, + ) from err + + +SYNC_TIME_SCHEMA = vol.Schema( + { + vol.Required(ATTR_DEVICE_ID): cv.string, + } +) + + @callback def async_setup_services(hass: HomeAssistant) -> None: """Register the BSB-Lan services.""" @@ -212,3 +282,10 @@ def async_setup_services(hass: HomeAssistant) -> None: set_hot_water_schedule, schema=SERVICE_SET_HOT_WATER_SCHEDULE_SCHEMA, ) + + hass.services.async_register( + DOMAIN, + SERVICE_SYNC_TIME, + async_sync_time, + schema=SYNC_TIME_SCHEMA, + ) diff --git a/homeassistant/components/bsblan/services.yaml b/homeassistant/components/bsblan/services.yaml index 959cf398c65bfc..0844aa35feaaf9 100644 --- a/homeassistant/components/bsblan/services.yaml +++ b/homeassistant/components/bsblan/services.yaml @@ -1,3 +1,12 @@ +sync_time: + fields: + device_id: + required: true + example: "abc123device456" + selector: + device: + integration: bsblan + set_hot_water_schedule: fields: device_id: diff --git a/homeassistant/components/bsblan/strings.json b/homeassistant/components/bsblan/strings.json index 128aa053c620af..f7a53654ab3c3f 100644 --- a/homeassistant/components/bsblan/strings.json +++ b/homeassistant/components/bsblan/strings.json @@ -79,9 +79,6 @@ "invalid_device_id": { "message": "Invalid device ID: {device_id}" }, - "invalid_time_format": { - "message": "Invalid time format provided" - }, "no_config_entry_for_device": { "message": "No configuration entry found for device: {device_id}" }, @@ -108,6 +105,9 @@ }, "setup_general_error": { "message": "An unknown error occurred while retrieving static device data" + }, + "sync_time_failed": { + "message": "Failed to sync time for {device_name}: {error}" } }, "services": { @@ -148,6 +148,16 @@ } }, "name": "Set hot water schedule" + }, + "sync_time": { + "description": "Synchronize Home Assistant time to the BSB-Lan device. Only updates if device time differs from Home Assistant time.", + "fields": { + "device_id": { + "description": "The BSB-LAN device to sync time for.", + "name": "Device" + } + }, + "name": "Sync time" } } } diff --git a/homeassistant/components/miele/__init__.py b/homeassistant/components/miele/__init__.py index f26c6c5f4c5521..4758a947188cb4 100644 --- a/homeassistant/components/miele/__init__.py +++ b/homeassistant/components/miele/__init__.py @@ -19,7 +19,12 @@ from .api import AsyncConfigEntryAuth from .const import DOMAIN -from .coordinator import MieleConfigEntry, MieleDataUpdateCoordinator +from .coordinator import ( + MieleAuxDataUpdateCoordinator, + MieleConfigEntry, + MieleDataUpdateCoordinator, + MieleRuntimeData, +) from .services import async_setup_services PLATFORMS: list[Platform] = [ @@ -75,19 +80,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: MieleConfigEntry) -> boo ) from err # Setup MieleAPI and coordinator for data fetch - api = MieleAPI(auth) - coordinator = MieleDataUpdateCoordinator(hass, entry, api) - await coordinator.async_config_entry_first_refresh() - entry.runtime_data = coordinator + _api = MieleAPI(auth) + _coordinator = MieleDataUpdateCoordinator(hass, entry, _api) + await _coordinator.async_config_entry_first_refresh() + _aux_coordinator = MieleAuxDataUpdateCoordinator(hass, entry, _api) + await _aux_coordinator.async_config_entry_first_refresh() + + entry.runtime_data = MieleRuntimeData(_api, _coordinator, _aux_coordinator) entry.async_create_background_task( hass, - coordinator.api.listen_events( - data_callback=coordinator.callback_update_data, - actions_callback=coordinator.callback_update_actions, + entry.runtime_data.api.listen_events( + data_callback=_coordinator.callback_update_data, + actions_callback=_coordinator.callback_update_actions, ), "pymiele event listener", ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True @@ -107,5 +116,5 @@ async def async_remove_config_entry_device( identifier for identifier in device_entry.identifiers if identifier[0] == DOMAIN - and identifier[1] in config_entry.runtime_data.data.devices + and identifier[1] in config_entry.runtime_data.coordinator.data.devices ) diff --git a/homeassistant/components/miele/binary_sensor.py b/homeassistant/components/miele/binary_sensor.py index b43bd86010e9d5..1e713cd68df5bb 100644 --- a/homeassistant/components/miele/binary_sensor.py +++ b/homeassistant/components/miele/binary_sensor.py @@ -264,7 +264,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the binary sensor platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator added_devices: set[str] = set() def _async_add_new_devices() -> None: diff --git a/homeassistant/components/miele/button.py b/homeassistant/components/miele/button.py index dda2dc7ef30329..a9140c855b3639 100644 --- a/homeassistant/components/miele/button.py +++ b/homeassistant/components/miele/button.py @@ -112,7 +112,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the button platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator added_devices: set[str] = set() def _async_add_new_devices() -> None: diff --git a/homeassistant/components/miele/climate.py b/homeassistant/components/miele/climate.py index d682a7111b1d94..09d16cb9e52c6e 100644 --- a/homeassistant/components/miele/climate.py +++ b/homeassistant/components/miele/climate.py @@ -138,7 +138,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the climate platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator added_devices: set[str] = set() def _async_add_new_devices() -> None: diff --git a/homeassistant/components/miele/coordinator.py b/homeassistant/components/miele/coordinator.py index 5a94f58477c3e8..dde6efedd5a4d0 100644 --- a/homeassistant/components/miele/coordinator.py +++ b/homeassistant/components/miele/coordinator.py @@ -9,7 +9,13 @@ import logging from aiohttp import ClientResponseError -from pymiele import MieleAction, MieleAPI, MieleDevice +from pymiele import ( + MieleAction, + MieleAPI, + MieleDevice, + MieleFillingLevel, + MieleFillingLevels, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -20,7 +26,16 @@ _LOGGER = logging.getLogger(__name__) -type MieleConfigEntry = ConfigEntry[MieleDataUpdateCoordinator] +@dataclass +class MieleRuntimeData: + """Runtime data for the Miele integration.""" + + api: MieleAPI + coordinator: MieleDataUpdateCoordinator + aux_coordinator: MieleAuxDataUpdateCoordinator + + +type MieleConfigEntry = ConfigEntry[MieleRuntimeData] @dataclass @@ -31,8 +46,15 @@ class MieleCoordinatorData: actions: dict[str, MieleAction] +@dataclass +class MieleAuxCoordinatorData: + """Data class for storing auxiliary coordinator data.""" + + filling_levels: dict[str, MieleFillingLevel] + + class MieleDataUpdateCoordinator(DataUpdateCoordinator[MieleCoordinatorData]): - """Coordinator for Miele data.""" + """Main coordinator for Miele data.""" config_entry: MieleConfigEntry new_device_callbacks: list[Callable[[dict[str, MieleDevice]], None]] = [] @@ -66,6 +88,7 @@ async def _async_update_data(self) -> MieleCoordinatorData: } self.devices = devices actions = {} + for device_id in devices: try: actions_json = await self.api.get_actions(device_id) @@ -99,10 +122,7 @@ async def callback_update_data(self, devices_json: dict[str, dict]) -> None: device_id: MieleDevice(device) for device_id, device in devices_json.items() } self.async_set_updated_data( - MieleCoordinatorData( - devices=devices, - actions=self.data.actions, - ) + MieleCoordinatorData(devices=devices, actions=self.data.actions) ) async def callback_update_actions(self, actions_json: dict[str, dict]) -> None: @@ -111,8 +131,34 @@ async def callback_update_actions(self, actions_json: dict[str, dict]) -> None: device_id: MieleAction(action) for device_id, action in actions_json.items() } self.async_set_updated_data( - MieleCoordinatorData( - devices=self.data.devices, - actions=actions, - ) + MieleCoordinatorData(devices=self.data.devices, actions=actions) + ) + + +class MieleAuxDataUpdateCoordinator(DataUpdateCoordinator[MieleAuxCoordinatorData]): + """Coordinator for Miele data for slowly polled endpoints.""" + + config_entry: MieleConfigEntry + + def __init__( + self, + hass: HomeAssistant, + config_entry: MieleConfigEntry, + api: MieleAPI, + ) -> None: + """Initialize the Miele data coordinator.""" + super().__init__( + hass, + _LOGGER, + config_entry=config_entry, + name=DOMAIN, + update_interval=timedelta(seconds=60), + ) + self.api = api + + async def _async_update_data(self) -> MieleAuxCoordinatorData: + """Fetch data from the Miele API.""" + filling_levels_json = await self.api.get_filling_levels() + return MieleAuxCoordinatorData( + filling_levels=MieleFillingLevels(filling_levels_json).filling_levels ) diff --git a/homeassistant/components/miele/diagnostics.py b/homeassistant/components/miele/diagnostics.py index debb1334706e90..4d7d629139a432 100644 --- a/homeassistant/components/miele/diagnostics.py +++ b/homeassistant/components/miele/diagnostics.py @@ -38,13 +38,19 @@ async def async_get_config_entry_diagnostics( "devices": redact_identifiers( { device_id: device_data.raw - for device_id, device_data in config_entry.runtime_data.data.devices.items() + for device_id, device_data in config_entry.runtime_data.coordinator.data.devices.items() + } + ), + "filling_levels": redact_identifiers( + { + device_id: filling_level_data.raw + for device_id, filling_level_data in config_entry.runtime_data.aux_coordinator.data.filling_levels.items() } ), "actions": redact_identifiers( { device_id: action_data.raw - for device_id, action_data in config_entry.runtime_data.data.actions.items() + for device_id, action_data in config_entry.runtime_data.coordinator.data.actions.items() } ), } @@ -68,13 +74,19 @@ async def async_get_device_diagnostics( "model_id": device.model_id, } - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator + aux_coordinator = config_entry.runtime_data.aux_coordinator device_id = cast(str, device.serial_number) miele_data: dict[str, Any] = { "devices": { hash_identifier(device_id): coordinator.data.devices[device_id].raw }, + "filling_levels": { + hash_identifier(device_id): aux_coordinator.data.filling_levels[ + device_id + ].raw + }, "actions": { hash_identifier(device_id): coordinator.data.actions[device_id].raw }, diff --git a/homeassistant/components/miele/entity.py b/homeassistant/components/miele/entity.py index 93e109d3500bc7..c6d9496b5ad6b8 100644 --- a/homeassistant/components/miele/entity.py +++ b/homeassistant/components/miele/entity.py @@ -1,16 +1,18 @@ """Entity base class for the Miele integration.""" -from pymiele import MieleAction, MieleAPI, MieleDevice +from pymiele import MieleAction, MieleAPI, MieleDevice, MieleFillingLevel from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DEVICE_TYPE_TAGS, DOMAIN, MANUFACTURER, MieleAppliance, StateStatus -from .coordinator import MieleDataUpdateCoordinator +from .coordinator import MieleAuxDataUpdateCoordinator, MieleDataUpdateCoordinator -class MieleEntity(CoordinatorEntity[MieleDataUpdateCoordinator]): +class MieleBaseEntity[ + _MieleCoordinatorT: MieleDataUpdateCoordinator | MieleAuxDataUpdateCoordinator +](CoordinatorEntity[_MieleCoordinatorT]): """Base class for Miele entities.""" _attr_has_entity_name = True @@ -22,7 +24,7 @@ def get_unique_id(device_id: str, description: EntityDescription) -> str: def __init__( self, - coordinator: MieleDataUpdateCoordinator, + coordinator: _MieleCoordinatorT, device_id: str, description: EntityDescription, ) -> None: @@ -30,7 +32,26 @@ def __init__( super().__init__(coordinator) self._device_id = device_id self.entity_description = description - self._attr_unique_id = MieleEntity.get_unique_id(device_id, description) + self._attr_unique_id = MieleBaseEntity.get_unique_id(device_id, description) + self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, device_id)}) + + @property + def api(self) -> MieleAPI: + """Return the api object.""" + return self.coordinator.api + + +class MieleEntity(MieleBaseEntity[MieleDataUpdateCoordinator]): + """Base class for Miele entities that use the main data coordinator.""" + + def __init__( + self, + coordinator: MieleDataUpdateCoordinator, + device_id: str, + description: EntityDescription, + ) -> None: + """Initialize the entity.""" + super().__init__(coordinator, device_id, description) device = self.device appliance_type = DEVICE_TYPE_TAGS.get(MieleAppliance(device.device_type)) @@ -61,11 +82,6 @@ def action(self) -> MieleAction: """Return the actions object.""" return self.coordinator.data.actions[self._device_id] - @property - def api(self) -> MieleAPI: - """Return the api object.""" - return self.coordinator.api - @property def available(self) -> bool: """Return the availability of the entity.""" @@ -75,3 +91,12 @@ def available(self) -> bool: and self._device_id in self.coordinator.data.devices and (self.device.state_status is not StateStatus.not_connected) ) + + +class MieleAuxEntity(MieleBaseEntity[MieleAuxDataUpdateCoordinator]): + """Base class for Miele entities that use the auxiliary data coordinator.""" + + @property + def levels(self) -> MieleFillingLevel: + """Return the filling levels object.""" + return self.coordinator.data.filling_levels[self._device_id] diff --git a/homeassistant/components/miele/fan.py b/homeassistant/components/miele/fan.py index 9561c0dc17ae26..ae500898b4e92e 100644 --- a/homeassistant/components/miele/fan.py +++ b/homeassistant/components/miele/fan.py @@ -66,7 +66,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the fan platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator added_devices: set[str] = set() def _async_add_new_devices() -> None: diff --git a/homeassistant/components/miele/icons.json b/homeassistant/components/miele/icons.json index 59a9e211f06b5b..364c741b6cc03f 100644 --- a/homeassistant/components/miele/icons.json +++ b/homeassistant/components/miele/icons.json @@ -71,6 +71,9 @@ "plate_step_warming": "mdi:alpha-w-circle-outline" } }, + "power_disk_level": { + "default": "mdi:car-coolant-level" + }, "program_id": { "default": "mdi:selection-ellipse-arrow-inside" }, @@ -83,6 +86,12 @@ "remaining_time": { "default": "mdi:clock-end" }, + "rinse_aid_level": { + "default": "mdi:water-opacity" + }, + "salt_level": { + "default": "mdi:shaker-outline" + }, "spin_speed": { "default": "mdi:sync" }, @@ -95,6 +104,12 @@ "target_temperature": { "default": "mdi:thermometer-check" }, + "twin_dos_1_level": { + "default": "mdi:car-coolant-level" + }, + "twin_dos_2_level": { + "default": "mdi:car-coolant-level" + }, "water_forecast": { "default": "mdi:water-outline" } diff --git a/homeassistant/components/miele/light.py b/homeassistant/components/miele/light.py index bcdaf920ef9a82..93856b8429ce97 100644 --- a/homeassistant/components/miele/light.py +++ b/homeassistant/components/miele/light.py @@ -86,7 +86,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the light platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator added_devices: set[str] = set() def _async_add_new_devices() -> None: diff --git a/homeassistant/components/miele/select.py b/homeassistant/components/miele/select.py index ee2b8b6758d8b8..7c756b129eaa5f 100644 --- a/homeassistant/components/miele/select.py +++ b/homeassistant/components/miele/select.py @@ -71,7 +71,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the select platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator added_devices: set[str] = set() def _async_add_new_devices() -> None: diff --git a/homeassistant/components/miele/sensor.py b/homeassistant/components/miele/sensor.py index 6ed7940c807c7e..e75fea0078e04d 100644 --- a/homeassistant/components/miele/sensor.py +++ b/homeassistant/components/miele/sensor.py @@ -8,7 +8,7 @@ import logging from typing import Any, Final, cast -from pymiele import MieleDevice, MieleTemperature +from pymiele import MieleDevice, MieleFillingLevel, MieleTemperature from homeassistant.components.sensor import ( RestoreSensor, @@ -44,8 +44,12 @@ StateProgramType, StateStatus, ) -from .coordinator import MieleConfigEntry, MieleDataUpdateCoordinator -from .entity import MieleEntity +from .coordinator import ( + MieleAuxDataUpdateCoordinator, + MieleConfigEntry, + MieleDataUpdateCoordinator, +) +from .entity import MieleAuxEntity, MieleEntity PARALLEL_UPDATES = 0 @@ -139,10 +143,13 @@ def _convert_finish_timestamp( @dataclass(frozen=True, kw_only=True) -class MieleSensorDescription(SensorEntityDescription): +class MieleSensorDescription[T: (MieleDevice, MieleFillingLevel)]( + SensorEntityDescription +): """Class describing Miele sensor entities.""" - value_fn: Callable[[MieleDevice], StateType | datetime] + value_fn: Callable[[T], StateType | datetime] + end_value_fn: Callable[[StateType | datetime], StateType | datetime] | None = None extra_attributes: dict[str, Callable[[MieleDevice], StateType]] | None = None zone: int | None = None @@ -150,14 +157,14 @@ class MieleSensorDescription(SensorEntityDescription): @dataclass -class MieleSensorDefinition: +class MieleSensorDefinition[T: (MieleDevice, MieleFillingLevel)]: """Class for defining sensor entities.""" types: tuple[MieleAppliance, ...] - description: MieleSensorDescription + description: MieleSensorDescription[T] -SENSOR_TYPES: Final[tuple[MieleSensorDefinition, ...]] = ( +SENSOR_TYPES: Final[tuple[MieleSensorDefinition[MieleDevice], ...]] = ( MieleSensorDefinition( types=( MieleAppliance.WASHING_MACHINE, @@ -689,6 +696,59 @@ class MieleSensorDefinition: ), ) +POLLED_SENSOR_TYPES: Final[tuple[MieleSensorDefinition[MieleFillingLevel], ...]] = ( + MieleSensorDefinition( + types=(MieleAppliance.WASHING_MACHINE,), + description=MieleSensorDescription[MieleFillingLevel]( + key="twin_dos_1_level", + translation_key="twin_dos_1_level", + value_fn=lambda value: value.twin_dos_container_1_filling_level, + native_unit_of_measurement=PERCENTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + ), + MieleSensorDefinition( + types=(MieleAppliance.WASHING_MACHINE,), + description=MieleSensorDescription[MieleFillingLevel]( + key="twin_dos_2_level", + translation_key="twin_dos_2_level", + value_fn=lambda value: value.twin_dos_container_2_filling_level, + native_unit_of_measurement=PERCENTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + ), + MieleSensorDefinition( + types=(MieleAppliance.DISHWASHER,), + description=MieleSensorDescription[MieleFillingLevel]( + key="power_disk_level", + translation_key="power_disk_level", + value_fn=lambda value: None, + native_unit_of_measurement=PERCENTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + ), + MieleSensorDefinition( + types=(MieleAppliance.DISHWASHER,), + description=MieleSensorDescription[MieleFillingLevel]( + key="salt_level", + translation_key="salt_level", + value_fn=lambda value: value.salt_filling_level, + native_unit_of_measurement=PERCENTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + ), + MieleSensorDefinition( + types=(MieleAppliance.DISHWASHER,), + description=MieleSensorDescription[MieleFillingLevel]( + key="rinse_aid_level", + translation_key="rinse_aid_level", + value_fn=lambda value: value.rinse_aid_filling_level, + native_unit_of_measurement=PERCENTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + ), +) + async def async_setup_entry( hass: HomeAssistant, @@ -696,11 +756,14 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the sensor platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator + aux_coordinator = config_entry.runtime_data.aux_coordinator added_devices: set[str] = set() # device_id added_entities: set[str] = set() # unique_id - def _get_entity_class(definition: MieleSensorDefinition) -> type[MieleSensor]: + def _get_entity_class( + definition: MieleSensorDefinition[MieleDevice], + ) -> type[MieleSensor]: """Get the entity class for the sensor.""" return { "state_status": MieleStatusSensor, @@ -725,7 +788,7 @@ def _is_entity_registered(unique_id: str) -> bool: ) def _is_sensor_enabled( - definition: MieleSensorDefinition, + definition: MieleSensorDefinition[MieleDevice], device: MieleDevice, unique_id: str, ) -> bool: @@ -748,6 +811,15 @@ def _is_sensor_enabled( return False return True + def _enabled_aux_sensor( + definition: MieleSensorDefinition[MieleFillingLevel], level: MieleFillingLevel + ) -> bool: + """Check if aux sensors are enabled.""" + return not ( + definition.description.value_fn is not None + and definition.description.value_fn(level) is None + ) + def _async_add_devices() -> None: nonlocal added_devices, added_entities entities: list = [] @@ -775,7 +847,11 @@ def _async_add_devices() -> None: continue # sensors is not enabled, skip - if not _is_sensor_enabled(definition, device, unique_id): + if not _is_sensor_enabled( + definition, + device, + unique_id, + ): continue added_entities.add(unique_id) @@ -787,6 +863,15 @@ def _async_add_devices() -> None: config_entry.async_on_unload(coordinator.async_add_listener(_async_add_devices)) _async_add_devices() + async_add_entities( + MieleAuxSensor(aux_coordinator, device_id, definition.description) + for device_id in aux_coordinator.data.filling_levels + for definition in POLLED_SENSOR_TYPES + if _enabled_aux_sensor( + definition, aux_coordinator.data.filling_levels[device_id] + ) + ) + APPLIANCE_ICONS = { MieleAppliance.WASHING_MACHINE: "mdi:washing-machine", @@ -885,6 +970,32 @@ def _handle_coordinator_update(self) -> None: super()._handle_coordinator_update() +class MieleAuxSensor(MieleAuxEntity, SensorEntity): + """Representation of a filling level Sensor.""" + + entity_description: MieleSensorDescription + + def __init__( + self, + coordinator: MieleAuxDataUpdateCoordinator, + device_id: str, + description: MieleSensorDescription, + ) -> None: + """Initialize the sensor.""" + super().__init__(coordinator, device_id, description) + if description.unique_id_fn is not None: + self._attr_unique_id = description.unique_id_fn(device_id, description) + + @property + def native_value(self) -> StateType | datetime: + """Return the state of the level sensor.""" + return ( + self.entity_description.value_fn(self.levels) + if self.entity_description.value_fn is not None + else None + ) + + class MielePlateSensor(MieleSensor): """Representation of a Sensor.""" diff --git a/homeassistant/components/miele/strings.json b/homeassistant/components/miele/strings.json index dcebedd60f097b..7bc02374c6f6e7 100644 --- a/homeassistant/components/miele/strings.json +++ b/homeassistant/components/miele/strings.json @@ -257,6 +257,9 @@ "plate_step_warm": "Warming" } }, + "power_disk_level": { + "name": "PowerDisk level" + }, "program_id": { "name": "Program", "state": { @@ -1038,6 +1041,12 @@ "remaining_time": { "name": "Remaining time" }, + "rinse_aid_level": { + "name": "Rinse aid level" + }, + "salt_level": { + "name": "Salt level" + }, "spin_speed": { "name": "Spin speed" }, @@ -1080,6 +1089,12 @@ "temperature_zone_3": { "name": "Temperature zone 3" }, + "twin_dos_1_level": { + "name": "TwinDos 1 level" + }, + "twin_dos_2_level": { + "name": "TwinDos 2 level" + }, "water_consumption": { "name": "Water consumption" }, diff --git a/homeassistant/components/miele/switch.py b/homeassistant/components/miele/switch.py index f44e8d74deb598..9940304bd8c32a 100644 --- a/homeassistant/components/miele/switch.py +++ b/homeassistant/components/miele/switch.py @@ -117,7 +117,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the switch platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator added_devices: set[str] = set() def _async_add_new_devices() -> None: diff --git a/homeassistant/components/miele/vacuum.py b/homeassistant/components/miele/vacuum.py index fd52ddf7425527..a47bcdb1c32f21 100644 --- a/homeassistant/components/miele/vacuum.py +++ b/homeassistant/components/miele/vacuum.py @@ -128,7 +128,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the vacuum platform.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.coordinator async_add_entities( MieleVacuum(coordinator, device_id, definition.description) diff --git a/homeassistant/components/nibe_heatpump/manifest.json b/homeassistant/components/nibe_heatpump/manifest.json index 973e8fac74feb2..ae116172e810f9 100644 --- a/homeassistant/components/nibe_heatpump/manifest.json +++ b/homeassistant/components/nibe_heatpump/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/nibe_heatpump", "integration_type": "device", "iot_class": "local_polling", - "requirements": ["nibe==2.20.0"] + "requirements": ["nibe==2.21.0"] } diff --git a/homeassistant/components/portainer/manifest.json b/homeassistant/components/portainer/manifest.json index eb28e4521cd9ed..ad7c2b76619a34 100644 --- a/homeassistant/components/portainer/manifest.json +++ b/homeassistant/components/portainer/manifest.json @@ -7,5 +7,5 @@ "integration_type": "hub", "iot_class": "local_polling", "quality_scale": "bronze", - "requirements": ["pyportainer==1.0.19"] + "requirements": ["pyportainer==1.0.21"] } diff --git a/homeassistant/components/shopping_list/__init__.py b/homeassistant/components/shopping_list/__init__.py index 97c6ed135c361b..e60acf4b377853 100644 --- a/homeassistant/components/shopping_list/__init__.py +++ b/homeassistant/components/shopping_list/__init__.py @@ -325,8 +325,7 @@ async def async_update_list( ) return self.items - @callback - def async_reorder( + async def async_reorder( self, item_ids: list[str], context: Context | None = None ) -> None: """Reorder items.""" @@ -351,7 +350,7 @@ def async_reorder( ) new_items.append(value) self.items = new_items - self.hass.async_add_executor_job(self.save) + await self.hass.async_add_executor_job(self.save) self._async_notify() self.hass.bus.async_fire( EVENT_SHOPPING_LIST_UPDATED, @@ -388,7 +387,7 @@ async def async_sort( ) -> None: """Sort items by name.""" self.items = sorted(self.items, key=lambda item: item["name"], reverse=reverse) # type: ignore[arg-type,return-value] - self.hass.async_add_executor_job(self.save) + await self.hass.async_add_executor_job(self.save) self._async_notify() self.hass.bus.async_fire( EVENT_SHOPPING_LIST_UPDATED, @@ -591,7 +590,8 @@ async def websocket_handle_clear( vol.Required("item_ids"): [str], } ) -def websocket_handle_reorder( +@websocket_api.async_response +async def websocket_handle_reorder( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], @@ -599,7 +599,9 @@ def websocket_handle_reorder( """Handle reordering shopping_list items.""" msg_id = msg.pop("id") try: - hass.data[DOMAIN].async_reorder(msg.pop("item_ids"), connection.context(msg)) + await hass.data[DOMAIN].async_reorder( + msg.pop("item_ids"), connection.context(msg) + ) except NoMatchingShoppingListItem: connection.send_error( msg_id, diff --git a/homeassistant/components/sonos/const.py b/homeassistant/components/sonos/const.py index 49b823d4c6b639..82416bd1965dfe 100644 --- a/homeassistant/components/sonos/const.py +++ b/homeassistant/components/sonos/const.py @@ -212,3 +212,4 @@ SCAN_INTERVAL = datetime.timedelta(seconds=10) DISCOVERY_INTERVAL = datetime.timedelta(seconds=60) SUBSCRIPTION_TIMEOUT = 1200 +LONG_SERVICE_TIMEOUT = 30.0 diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index cb48037524e402..f7a4420704b09f 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -54,6 +54,7 @@ from .const import ( ATTR_QUEUE_POSITION, DOMAIN, + LONG_SERVICE_TIMEOUT, MEDIA_TYPE_DIRECTORY, MEDIA_TYPES_TO_SONOS, MODELS_LINEIN_AND_TV, @@ -76,7 +77,6 @@ _LOGGER = logging.getLogger(__name__) -LONG_SERVICE_TIMEOUT = 30.0 UNJOIN_SERVICE_TIMEOUT = 0.1 VOLUME_INCREMENT = 2 diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index dd1ecca42279c5..317ffbc13d99e6 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -40,6 +40,7 @@ AVAILABILITY_TIMEOUT, BATTERY_SCAN_INTERVAL, DOMAIN, + LONG_SERVICE_TIMEOUT, SCAN_INTERVAL, SONOS_CHECK_ACTIVITY, SONOS_CREATE_ALARM, @@ -1067,7 +1068,7 @@ def unjoin(self) -> None: """Unjoin the player from a group.""" if self.sonos_group == [self]: return - self.soco.unjoin() + self.soco.unjoin(timeout=LONG_SERVICE_TIMEOUT) @staticmethod async def unjoin_multi( diff --git a/homeassistant/components/vesync/sensor.py b/homeassistant/components/vesync/sensor.py index d5538c2879a500..5c441aa55fcb0c 100644 --- a/homeassistant/components/vesync/sensor.py +++ b/homeassistant/components/vesync/sensor.py @@ -64,6 +64,22 @@ class VeSyncSensorEntityDescription(SensorEntityDescription): lambda device: rgetattr(device, "state.air_quality_string") is not None ), ), + VeSyncSensorEntityDescription( + key="pm1", + device_class=SensorDeviceClass.PM1, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda device: device.state.pm1, + exists_fn=lambda device: rgetattr(device, "state.pm1") is not None, + ), + VeSyncSensorEntityDescription( + key="pm10", + device_class=SensorDeviceClass.PM10, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda device: device.state.pm10, + exists_fn=lambda device: rgetattr(device, "state.pm10") is not None, + ), VeSyncSensorEntityDescription( key="pm25", device_class=SensorDeviceClass.PM25, diff --git a/requirements_all.txt b/requirements_all.txt index fceb1ea8d93ac6..3a3f0ebf63abbd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1578,7 +1578,7 @@ nextdns==4.1.0 nhc==0.7.0 # homeassistant.components.nibe_heatpump -nibe==2.20.0 +nibe==2.21.0 # homeassistant.components.nice_go nice-go==1.0.1 @@ -1891,7 +1891,7 @@ pyaftership==21.11.0 pyairnow==1.3.1 # homeassistant.components.airobot -pyairobotrest==0.1.0 +pyairobotrest==0.2.0 # homeassistant.components.airvisual # homeassistant.components.airvisual_pro @@ -2318,7 +2318,7 @@ pyplaato==0.0.19 pypoint==3.0.0 # homeassistant.components.portainer -pyportainer==1.0.19 +pyportainer==1.0.21 # homeassistant.components.probe_plus pyprobeplus==1.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 79c40579dd8efd..3fc867258b357f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1373,7 +1373,7 @@ nextdns==4.1.0 nhc==0.7.0 # homeassistant.components.nibe_heatpump -nibe==2.20.0 +nibe==2.21.0 # homeassistant.components.nice_go nice-go==1.0.1 @@ -1613,7 +1613,7 @@ pyaftership==21.11.0 pyairnow==1.3.1 # homeassistant.components.airobot -pyairobotrest==0.1.0 +pyairobotrest==0.2.0 # homeassistant.components.airvisual # homeassistant.components.airvisual_pro @@ -1959,7 +1959,7 @@ pyplaato==0.0.19 pypoint==3.0.0 # homeassistant.components.portainer -pyportainer==1.0.19 +pyportainer==1.0.21 # homeassistant.components.probe_plus pyprobeplus==1.1.2 diff --git a/tests/components/bsblan/fixtures/time.json b/tests/components/bsblan/fixtures/time.json new file mode 100644 index 00000000000000..fdbdfb69927cfa --- /dev/null +++ b/tests/components/bsblan/fixtures/time.json @@ -0,0 +1,11 @@ +{ + "time": { + "name": "Time", + "value": "14.11.2025 10:30:00", + "unit": "", + "desc": "", + "dataType": 0, + "readonly": 0, + "error": 0 + } +} diff --git a/tests/components/bsblan/test_services.py b/tests/components/bsblan/test_services.py index f0955a7ac7a8d6..6d1807b2f1db82 100644 --- a/tests/components/bsblan/test_services.py +++ b/tests/components/bsblan/test_services.py @@ -4,7 +4,8 @@ from typing import Any from unittest.mock import MagicMock -from bsblan import BSBLANError, DaySchedule, TimeSlot +from bsblan import BSBLANError, DaySchedule, DeviceTime, TimeSlot +from freezegun.api import FrozenDateTimeFactory import pytest import voluptuous as vol @@ -16,6 +17,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import device_registry as dr +from homeassistant.util import dt as dt_util from tests.common import MockConfigEntry @@ -174,9 +176,22 @@ async def test_invalid_device_id( assert exc_info.value.translation_key == "invalid_device_id" +@pytest.mark.parametrize( + ("service_name", "service_data"), + [ + ( + SERVICE_SET_HOT_WATER_SCHEDULE, + {"monday_slots": [{"start_time": time(6, 0), "end_time": time(8, 0)}]}, + ), + ("sync_time", {}), + ], + ids=["set_hot_water_schedule", "sync_time"], +) async def test_no_config_entry_for_device( hass: HomeAssistant, device_registry: dr.DeviceRegistry, + service_name: str, + service_data: dict[str, Any], ) -> None: """Test error when device has no matching BSB-LAN config entry.""" # Create a different config entry (not for bsblan) @@ -196,11 +211,8 @@ async def test_no_config_entry_for_device( with pytest.raises(ServiceValidationError) as exc_info: await hass.services.async_call( DOMAIN, - SERVICE_SET_HOT_WATER_SCHEDULE, - { - "device_id": device_entry.id, - "monday_slots": [{"start_time": time(6, 0), "end_time": time(8, 0)}], - }, + service_name, + {"device_id": device_entry.id, **service_data}, blocking=True, ) @@ -421,3 +433,198 @@ async def test_async_setup_services( # Verify service is now registered assert hass.services.has_service(DOMAIN, SERVICE_SET_HOT_WATER_SCHEDULE) + + +async def test_sync_time_service( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_bsblan: MagicMock, + device_registry: dr.DeviceRegistry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test the sync_time service.""" + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Get the device + device = device_registry.async_get_device(identifiers={(DOMAIN, TEST_DEVICE_MAC)}) + assert device is not None + + # Mock device time that differs from HA time + mock_bsblan.time.return_value = DeviceTime.from_json( + '{"time": {"name": "Time", "value": "01.01.2020 00:00:00", "unit": "", "desc": "", "dataType": 0, "readonly": 0, "error": 0}}' + ) + + # Call the service + await hass.services.async_call( + DOMAIN, + "sync_time", + {"device_id": device.id}, + blocking=True, + ) + + # Verify time() was called to check current device time + assert mock_bsblan.time.called + + # Verify set_time() was called with current HA time + current_time_str = dt_util.now().strftime("%d.%m.%Y %H:%M:%S") + mock_bsblan.set_time.assert_called_once_with(current_time_str) + + +async def test_sync_time_service_no_update_when_same( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_bsblan: MagicMock, + device_registry: dr.DeviceRegistry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test the sync_time service doesn't update when time matches.""" + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Get the device + device = device_registry.async_get_device(identifiers={(DOMAIN, TEST_DEVICE_MAC)}) + assert device is not None + + # Mock device time that matches HA time + current_time_str = dt_util.now().strftime("%d.%m.%Y %H:%M:%S") + mock_bsblan.time.return_value = DeviceTime.from_json( + f'{{"time": {{"name": "Time", "value": "{current_time_str}", "unit": "", "desc": "", "dataType": 0, "readonly": 0, "error": 0}}}}' + ) + + # Call the service + await hass.services.async_call( + DOMAIN, + "sync_time", + {"device_id": device.id}, + blocking=True, + ) + + # Verify time() was called + assert mock_bsblan.time.called + + # Verify set_time() was NOT called since times match + assert not mock_bsblan.set_time.called + + +async def test_sync_time_service_error_handling( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_bsblan: MagicMock, + device_registry: dr.DeviceRegistry, +) -> None: + """Test the sync_time service handles errors gracefully.""" + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Get the device + device = device_registry.async_get_device(identifiers={(DOMAIN, TEST_DEVICE_MAC)}) + assert device is not None + + # Mock time() to raise an error + mock_bsblan.time.side_effect = BSBLANError("Connection failed") + + # Call the service - should raise HomeAssistantError + with pytest.raises(HomeAssistantError, match="Failed to sync time"): + await hass.services.async_call( + DOMAIN, + "sync_time", + {"device_id": device.id}, + blocking=True, + ) + + +async def test_sync_time_service_set_time_error( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_bsblan: MagicMock, + device_registry: dr.DeviceRegistry, +) -> None: + """Test the sync_time service handles set_time errors.""" + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Get the device + device = device_registry.async_get_device(identifiers={(DOMAIN, TEST_DEVICE_MAC)}) + assert device is not None + + # Mock device time that differs + mock_bsblan.time.return_value = DeviceTime.from_json( + '{"time": {"name": "Time", "value": "01.01.2020 00:00:00", "unit": "", "desc": "", "dataType": 0, "readonly": 0, "error": 0}}' + ) + + # Mock set_time() to raise an error + mock_bsblan.set_time.side_effect = BSBLANError("Write failed") + + # Call the service - should raise HomeAssistantError + with pytest.raises(HomeAssistantError, match="Failed to sync time"): + await hass.services.async_call( + DOMAIN, + "sync_time", + {"device_id": device.id}, + blocking=True, + ) + + +async def test_sync_time_service_entry_not_found( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_bsblan: MagicMock, +) -> None: + """Test the sync_time service raises error for non-existent device.""" + # Set up the entry (this registers the service) + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Call the service with a non-existent device ID + with pytest.raises(ServiceValidationError): + await hass.services.async_call( + DOMAIN, + "sync_time", + {"device_id": "non_existent_device_id"}, + blocking=True, + ) + + +async def test_sync_time_service_entry_not_loaded( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_bsblan: MagicMock, + device_registry: dr.DeviceRegistry, +) -> None: + """Test the sync_time service raises error for unloaded entry.""" + # Set up the first entry (this registers the service) + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Create a second unloaded entry + unloaded_entry = MockConfigEntry( + domain=DOMAIN, + title="Unloaded BSBLAN", + data=mock_config_entry.data.copy(), + unique_id="unloaded_unique_id", + ) + unloaded_entry.add_to_hass(hass) + # Don't call async_setup on this entry, so it stays NOT_LOADED + + # Manually register a device for this unloaded entry + unloaded_device = device_registry.async_get_or_create( + config_entry_id=unloaded_entry.entry_id, + identifiers={(DOMAIN, "unloaded_device_mac")}, + name="Unloaded Device", + ) + + # Call the service with the device from the unloaded entry - should raise error + with pytest.raises(ServiceValidationError): + await hass.services.async_call( + DOMAIN, + "sync_time", + {"device_id": unloaded_device.id}, + blocking=True, + ) diff --git a/tests/components/miele/conftest.py b/tests/components/miele/conftest.py index 13c467fe54a3b0..d7ac1f5d69bd60 100644 --- a/tests/components/miele/conftest.py +++ b/tests/components/miele/conftest.py @@ -111,11 +111,26 @@ async def programs_fixture( return load_json_value_fixture(load_programs_file, DOMAIN) +@pytest.fixture(scope="package") +def load_filling_levels_file() -> str: + """Fixture for loading filling levels file.""" + return "filling_levels.json" + + +@pytest.fixture +async def filling_levels_fixture( + hass: HomeAssistant, load_filling_levels_file: str +) -> JsonValueType: + """Fixture for filling levels.""" + return load_json_value_fixture(load_filling_levels_file, DOMAIN) + + @pytest.fixture def mock_miele_client( device_fixture, action_fixture, programs_fixture, + filling_levels_fixture, ) -> Generator[MagicMock]: """Mock a Miele client.""" @@ -127,6 +142,7 @@ def mock_miele_client( client.get_devices.return_value = device_fixture client.get_actions.return_value = action_fixture + client.get_filling_levels.return_value = filling_levels_fixture client.get_programs.return_value = programs_fixture client.set_program.return_value = None diff --git a/tests/components/miele/fixtures/filling_levels.json b/tests/components/miele/fixtures/filling_levels.json new file mode 100644 index 00000000000000..21132358aa49af --- /dev/null +++ b/tests/components/miele/fixtures/filling_levels.json @@ -0,0 +1,257 @@ +[ + { + "deviceId": "Dummy_Appliance_3", + "fillingLevels": { + "twinDosContainer1FillingLevel": 63, + "twinDosContainer2FillingLevel": 20, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyWasher", + "fillingLevels": { + "twinDosContainer1FillingLevel": 63, + "twinDosContainer2FillingLevel": 20, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "Dummy_Appliance_5", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": 80.5, + "saltFillingLevel": 75, + "rinseAidFillingLevel": 25, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyAppliance_18", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": 33, + "fatFilterSaturation": 66, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "Dummy_Appliance_4", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyAppliance_12", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyAppliance_74", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyAppliance_74_off", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "Dummy_Appliance_1", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "Dummy_Appliance_2", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyDryer", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyOven", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "Dummy_Vacuum_1", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyAppliance_Fridge_Freezer", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyAppliance_hob_w_extr", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyAppliance_Fridge_Freezer_Offline", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": null, + "degreasingCounter": null, + "milkCleaningCounter": null + } + }, + { + "deviceId": "DummyAppliance_CoffeeSystem", + "fillingLevels": { + "twinDosContainer1FillingLevel": null, + "twinDosContainer2FillingLevel": null, + "powerDiscFillingLevel": null, + "saltFillingLevel": null, + "rinseAidFillingLevel": null, + "coalFilterSaturation": null, + "fatFilterSaturation": null, + "descalingCounter": 1, + "degreasingCounter": 2, + "milkCleaningCounter": 3 + } + } +] diff --git a/tests/components/miele/snapshots/test_diagnostics.ambr b/tests/components/miele/snapshots/test_diagnostics.ambr index 4f5004f25de834..4fabeef69fd128 100644 --- a/tests/components/miele/snapshots/test_diagnostics.ambr +++ b/tests/components/miele/snapshots/test_diagnostics.ambr @@ -830,6 +830,212 @@ }), }), }), + 'filling_levels': dict({ + '**REDACTED_019aa577ad1c330d': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_10582eaebd067ad3': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_262ba14529681b23': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': 80.5, + 'rinseAidFillingLevel': 25, + 'saltFillingLevel': 75, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_43eee10f37037b43': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_4b870e84d3e80013': dict({ + 'coalFilterSaturation': 33, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': 66, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_4b9caddd6c8a75c4': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_4c9255e388336dab': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_53a963893a01dc04': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_57d53e72806e88b4': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_5aed38e9def460e6': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_9c332e79e3cb1202': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_a367c0b42fc12284': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_afeb069d963cd921': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': 63, + 'twinDosContainer2FillingLevel': 20, + }), + '**REDACTED_bd62a0d31ec35172': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': 2, + 'descalingCounter': 1, + 'fatFilterSaturation': None, + 'milkCleaningCounter': 3, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_c9fe55cdf70786ca': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': 63, + 'twinDosContainer2FillingLevel': 20, + }), + '**REDACTED_e7bc6793e305bf53': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + '**REDACTED_ecf06220046f162f': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + }), 'missing_code_warnings': list([ 'None', ]), @@ -1014,6 +1220,20 @@ }), }), }), + 'filling_levels': dict({ + '**REDACTED_57d53e72806e88b4': dict({ + 'coalFilterSaturation': None, + 'degreasingCounter': None, + 'descalingCounter': None, + 'fatFilterSaturation': None, + 'milkCleaningCounter': None, + 'powerDiscFillingLevel': None, + 'rinseAidFillingLevel': None, + 'saltFillingLevel': None, + 'twinDosContainer1FillingLevel': None, + 'twinDosContainer2FillingLevel': None, + }), + }), 'missing_code_warnings': list([ 'None', ]), diff --git a/tests/components/miele/snapshots/test_sensor.ambr b/tests/components/miele/snapshots/test_sensor.ambr index 94dfa420ee4634..53929f59848c43 100644 --- a/tests/components/miele/snapshots/test_sensor.ambr +++ b/tests/components/miele/snapshots/test_sensor.ambr @@ -351,6 +351,300 @@ 'state': 'own_program', }) # --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_rinse_aid_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Rinse aid level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rinse_aid_level', + 'unique_id': 'Dummy_Appliance_5-rinse_aid_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_rinse_aid_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Rinse aid level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_salt_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_salt_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Salt level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'salt_level', + 'unique_id': 'Dummy_Appliance_5-salt_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_salt_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Salt level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_salt_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_twindos_1_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_twindos_1_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_twindos_1_level_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'DummyWasher-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_twindos_1_level_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_twindos_2_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_twindos_2_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_twindos_2_level_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'DummyWasher-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_coffee_system_sensor_states[platforms0-coffee_system.json][sensor.none_twindos_2_level_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- # name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.hob_with_extraction-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -1512,26 +1806,320 @@ 'state': 'off', }) # --- -# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.fridge_freezer-entry] +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_rinse_aid_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, - 'capabilities': dict({ - 'options': list([ - 'autocleaning', - 'failure', - 'idle', - 'in_use', - 'not_connected', - 'off', - 'on', - 'pause', - 'program_ended', - 'program_interrupted', - 'programmed', - 'reserved', - 'rinse_hold', + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Rinse aid level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rinse_aid_level', + 'unique_id': 'Dummy_Appliance_5-rinse_aid_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_rinse_aid_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Rinse aid level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_salt_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_salt_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Salt level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'salt_level', + 'unique_id': 'Dummy_Appliance_5-salt_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_salt_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Salt level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_salt_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_twindos_1_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_twindos_1_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_twindos_1_level_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'DummyWasher-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_twindos_1_level_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_twindos_2_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_twindos_2_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_twindos_2_level_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'DummyWasher-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fan_hob_sensor_states[platforms0-fan_devices.json][sensor.none_twindos_2_level_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.fridge_freezer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'autocleaning', + 'failure', + 'idle', + 'in_use', + 'not_connected', + 'off', + 'on', + 'pause', + 'program_ended', + 'program_interrupted', + 'programmed', + 'reserved', + 'rinse_hold', 'service', 'supercooling', 'supercooling_superfreezing', @@ -1866,6 +2454,300 @@ 'state': '-18.0', }) # --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_rinse_aid_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Rinse aid level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rinse_aid_level', + 'unique_id': 'Dummy_Appliance_5-rinse_aid_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_rinse_aid_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Rinse aid level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_salt_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_salt_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Salt level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'salt_level', + 'unique_id': 'Dummy_Appliance_5-salt_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_salt_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Salt level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_salt_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_twindos_1_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_twindos_1_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_twindos_1_level_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'DummyWasher-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_twindos_1_level_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_twindos_2_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_twindos_2_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_twindos_2_level_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'DummyWasher-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_fridge_freezer_sensor_states[platforms0-fridge_freezer.json][sensor.none_twindos_2_level_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- # name: test_hob_sensor_states[platforms0-hob.json][sensor.kdma7774_app2_2-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -2449,26 +3331,320 @@ 'state': 'plate_step_boost', }) # --- -# name: test_sensor_states[platforms0][sensor.freezer-entry] +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_rinse_aid_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, - 'capabilities': dict({ - 'options': list([ - 'autocleaning', - 'failure', - 'idle', - 'in_use', - 'not_connected', - 'off', - 'on', - 'pause', - 'program_ended', - 'program_interrupted', - 'programmed', - 'reserved', - 'rinse_hold', + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Rinse aid level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rinse_aid_level', + 'unique_id': 'Dummy_Appliance_5-rinse_aid_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_rinse_aid_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Rinse aid level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_salt_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_salt_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Salt level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'salt_level', + 'unique_id': 'Dummy_Appliance_5-salt_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_salt_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Salt level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_salt_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_twindos_1_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_twindos_1_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_twindos_1_level_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'DummyWasher-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_twindos_1_level_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_twindos_2_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_twindos_2_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_twindos_2_level_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level_2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'DummyWasher-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_hob_sensor_states[platforms0-hob.json][sensor.none_twindos_2_level_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_sensor_states[platforms0][sensor.freezer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'autocleaning', + 'failure', + 'idle', + 'in_use', + 'not_connected', + 'off', + 'on', + 'pause', + 'program_ended', + 'program_interrupted', + 'programmed', + 'reserved', + 'rinse_hold', 'service', 'supercooling', 'supercooling_superfreezing', @@ -2691,6 +3867,202 @@ 'state': 'off', }) # --- +# name: test_sensor_states[platforms0][sensor.none_rinse_aid_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Rinse aid level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rinse_aid_level', + 'unique_id': 'Dummy_Appliance_5-rinse_aid_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[platforms0][sensor.none_rinse_aid_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Rinse aid level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25', + }) +# --- +# name: test_sensor_states[platforms0][sensor.none_salt_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_salt_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Salt level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'salt_level', + 'unique_id': 'Dummy_Appliance_5-salt_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[platforms0][sensor.none_salt_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Salt level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_salt_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_sensor_states[platforms0][sensor.none_twindos_1_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'DummyWasher-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[platforms0][sensor.none_twindos_1_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_sensor_states[platforms0][sensor.none_twindos_2_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'DummyWasher-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[platforms0][sensor.none_twindos_2_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- # name: test_sensor_states[platforms0][sensor.oven-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -4724,6 +6096,104 @@ 'state': 'unknown', }) # --- +# name: test_sensor_states[platforms0][sensor.washing_machine_twindos_1_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_twindos_1_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[platforms0][sensor.washing_machine_twindos_1_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Washing machine TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.washing_machine_twindos_1_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_sensor_states[platforms0][sensor.washing_machine_twindos_2_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_twindos_2_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states[platforms0][sensor.washing_machine_twindos_2_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Washing machine TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.washing_machine_twindos_2_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- # name: test_sensor_states[platforms0][sensor.washing_machine_water_consumption-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -5071,26 +6541,222 @@ 'state': 'off', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.oven-entry] +# name: test_sensor_states_api_push[platforms0][sensor.none_rinse_aid_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, - 'capabilities': dict({ - 'options': list([ - 'autocleaning', - 'failure', - 'idle', - 'in_use', - 'not_connected', - 'off', - 'on', - 'pause', - 'program_ended', - 'program_interrupted', - 'programmed', - 'reserved', - 'rinse_hold', + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Rinse aid level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rinse_aid_level', + 'unique_id': 'Dummy_Appliance_5-rinse_aid_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.none_rinse_aid_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Rinse aid level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_rinse_aid_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '25', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.none_salt_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_salt_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Salt level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'salt_level', + 'unique_id': 'Dummy_Appliance_5-salt_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.none_salt_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Salt level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_salt_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '75', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.none_twindos_1_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'DummyWasher-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.none_twindos_1_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_1_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '63', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.none_twindos_2_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_2_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 2 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'DummyWasher-twin_dos_2_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.none_twindos_2_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.none_twindos_2_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.oven-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'autocleaning', + 'failure', + 'idle', + 'in_use', + 'not_connected', + 'off', + 'on', + 'pause', + 'program_ended', + 'program_interrupted', + 'programmed', + 'reserved', + 'rinse_hold', 'service', 'supercooling', 'supercooling_superfreezing', @@ -6674,51 +8340,499 @@ ]), }), 'context': , - 'entity_id': 'sensor.washing_machine_program', + 'entity_id': 'sensor.washing_machine_program', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'no_program', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program_phase-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'anti_crease', + 'automatic_start', + 'cleaning', + 'cooling_down', + 'disinfecting', + 'drain', + 'drying', + 'finished', + 'flex_load_active', + 'freshen_up_and_moisten', + 'hygiene', + 'main_wash', + 'not_running', + 'pre_wash', + 'rinse', + 'rinse_hold', + 'soak', + 'spin', + 'starch_stop', + 'steam_smoothing', + 'venting', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.washing_machine_program_phase', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Program phase', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'program_phase', + 'unique_id': 'Dummy_Appliance_3-state_program_phase', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program_phase-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Washing machine Program phase', + 'options': list([ + 'anti_crease', + 'automatic_start', + 'cleaning', + 'cooling_down', + 'disinfecting', + 'drain', + 'drying', + 'finished', + 'flex_load_active', + 'freshen_up_and_moisten', + 'hygiene', + 'main_wash', + 'not_running', + 'pre_wash', + 'rinse', + 'rinse_hold', + 'soak', + 'spin', + 'starch_stop', + 'steam_smoothing', + 'venting', + ]), + }), + 'context': , + 'entity_id': 'sensor.washing_machine_program_phase', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'not_running', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program_type-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'automatic_program', + 'cleaning_care_program', + 'maintenance_program', + 'normal_operation_mode', + 'own_program', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_program_type', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Program type', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'program_type', + 'unique_id': 'Dummy_Appliance_3-state_program_type', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program_type-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'Washing machine Program type', + 'options': list([ + 'automatic_program', + 'cleaning_care_program', + 'maintenance_program', + 'normal_operation_mode', + 'own_program', + ]), + }), + 'context': , + 'entity_id': 'sensor.washing_machine_program_type', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'normal_operation_mode', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_remaining_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_remaining_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Remaining time', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'remaining_time', + 'unique_id': 'Dummy_Appliance_3-state_remaining_time', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_remaining_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Washing machine Remaining time', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.washing_machine_remaining_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_spin_speed-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_spin_speed', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Spin speed', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'spin_speed', + 'unique_id': 'Dummy_Appliance_3-state_spinning_speed', + 'unit_of_measurement': 'rpm', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_spin_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Washing machine Spin speed', + 'unit_of_measurement': 'rpm', + }), + 'context': , + 'entity_id': 'sensor.washing_machine_spin_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_start', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Start', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'start', + 'unique_id': 'Dummy_Appliance_3-state_start_timestamp', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'timestamp', + 'friendly_name': 'Washing machine Start', + }), + 'context': , + 'entity_id': 'sensor.washing_machine_start', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start_in-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_start_in', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Start in', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'start_time', + 'unique_id': 'Dummy_Appliance_3-state_start_time', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start_in-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'duration', + 'friendly_name': 'Washing machine Start in', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.washing_machine_start_in', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_target_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.washing_machine_target_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Target temperature', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'target_temperature', + 'unique_id': 'Dummy_Appliance_3-state_target_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_target_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'Washing machine Target temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.washing_machine_target_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_twindos_1_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_twindos_1_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'TwinDos 1 level', + 'platform': 'miele', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_1_level', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_twindos_1_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Washing machine TwinDos 1 level', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.washing_machine_twindos_1_level', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'no_program', + 'state': '63', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program_phase-entry] +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_twindos_2_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, - 'capabilities': dict({ - 'options': list([ - 'anti_crease', - 'automatic_start', - 'cleaning', - 'cooling_down', - 'disinfecting', - 'drain', - 'drying', - 'finished', - 'flex_load_active', - 'freshen_up_and_moisten', - 'hygiene', - 'main_wash', - 'not_running', - 'pre_wash', - 'rinse', - 'rinse_hold', - 'soak', - 'spin', - 'starch_stop', - 'steam_smoothing', - 'venting', - ]), - }), + 'capabilities': None, 'config_entry_id': , 'config_subentry_id': , 'device_class': None, 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.washing_machine_program_phase', + 'entity_category': , + 'entity_id': 'sensor.washing_machine_twindos_2_level', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -6728,68 +8842,39 @@ 'name': None, 'options': dict({ }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Program phase', + 'original_name': 'TwinDos 2 level', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'program_phase', - 'unique_id': 'Dummy_Appliance_3-state_program_phase', - 'unit_of_measurement': None, + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_2_level', + 'unit_of_measurement': '%', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program_phase-state] +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_twindos_2_level-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'enum', - 'friendly_name': 'Washing machine Program phase', - 'options': list([ - 'anti_crease', - 'automatic_start', - 'cleaning', - 'cooling_down', - 'disinfecting', - 'drain', - 'drying', - 'finished', - 'flex_load_active', - 'freshen_up_and_moisten', - 'hygiene', - 'main_wash', - 'not_running', - 'pre_wash', - 'rinse', - 'rinse_hold', - 'soak', - 'spin', - 'starch_stop', - 'steam_smoothing', - 'venting', - ]), + 'friendly_name': 'Washing machine TwinDos 2 level', + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.washing_machine_program_phase', + 'entity_id': 'sensor.washing_machine_twindos_2_level', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'not_running', + 'state': '20', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program_type-entry] +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_water_consumption-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'options': list([ - 'automatic_program', - 'cleaning_care_program', - 'maintenance_program', - 'normal_operation_mode', - 'own_program', - ]), + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -6798,7 +8883,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.washing_machine_program_type', + 'entity_id': 'sensor.washing_machine_water_consumption', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -6807,41 +8892,39 @@ }), 'name': None, 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), }), - 'original_device_class': , + 'original_device_class': , 'original_icon': None, - 'original_name': 'Program type', + 'original_name': 'Water consumption', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'program_type', - 'unique_id': 'Dummy_Appliance_3-state_program_type', - 'unit_of_measurement': None, + 'translation_key': 'water_consumption', + 'unique_id': 'Dummy_Appliance_3-current_water_consumption', + 'unit_of_measurement': , }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program_type-state] +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_water_consumption-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'enum', - 'friendly_name': 'Washing machine Program type', - 'options': list([ - 'automatic_program', - 'cleaning_care_program', - 'maintenance_program', - 'normal_operation_mode', - 'own_program', - ]), + 'device_class': 'water', + 'friendly_name': 'Washing machine Water consumption', + 'state_class': , + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.washing_machine_program_type', + 'entity_id': 'sensor.washing_machine_water_consumption', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'normal_operation_mode', + 'state': 'unknown', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_remaining_time-entry] +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_water_forecast-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -6854,7 +8937,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.washing_machine_remaining_time', + 'entity_id': 'sensor.washing_machine_water_forecast', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -6863,38 +8946,34 @@ }), 'name': None, 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Remaining time', + 'original_name': 'Water forecast', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'remaining_time', - 'unique_id': 'Dummy_Appliance_3-state_remaining_time', - 'unit_of_measurement': , + 'translation_key': 'water_forecast', + 'unique_id': 'Dummy_Appliance_3-water_forecast', + 'unit_of_measurement': '%', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_remaining_time-state] +# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_water_forecast-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'duration', - 'friendly_name': 'Washing machine Remaining time', - 'unit_of_measurement': , + 'friendly_name': 'Washing machine Water forecast', + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.washing_machine_remaining_time', + 'entity_id': 'sensor.washing_machine_water_forecast', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': '0.0', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_spin_speed-entry] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_rinse_aid_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -6907,7 +8986,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.washing_machine_spin_speed', + 'entity_id': 'sensor.none_rinse_aid_level', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -6919,31 +8998,31 @@ }), 'original_device_class': None, 'original_icon': None, - 'original_name': 'Spin speed', + 'original_name': 'Rinse aid level', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'spin_speed', - 'unique_id': 'Dummy_Appliance_3-state_spinning_speed', - 'unit_of_measurement': 'rpm', + 'translation_key': 'rinse_aid_level', + 'unique_id': 'Dummy_Appliance_5-rinse_aid_level', + 'unit_of_measurement': '%', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_spin_speed-state] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_rinse_aid_level-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'Washing machine Spin speed', - 'unit_of_measurement': 'rpm', + 'friendly_name': 'Rinse aid level', + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.washing_machine_spin_speed', + 'entity_id': 'sensor.none_rinse_aid_level', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': '25', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start-entry] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_salt_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -6956,7 +9035,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.washing_machine_start', + 'entity_id': 'sensor.none_salt_level', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -6966,33 +9045,33 @@ 'name': None, 'options': dict({ }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Start', + 'original_name': 'Salt level', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'start', - 'unique_id': 'Dummy_Appliance_3-state_start_timestamp', - 'unit_of_measurement': None, + 'translation_key': 'salt_level', + 'unique_id': 'Dummy_Appliance_5-salt_level', + 'unit_of_measurement': '%', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start-state] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_salt_level-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'timestamp', - 'friendly_name': 'Washing machine Start', + 'friendly_name': 'Salt level', + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.washing_machine_start', + 'entity_id': 'sensor.none_salt_level', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': '75', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start_in-entry] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_twindos_1_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -7005,7 +9084,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.washing_machine_start_in', + 'entity_id': 'sensor.none_twindos_1_level', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -7014,56 +9093,47 @@ }), 'name': None, 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - 'sensor.private': dict({ - 'suggested_unit_of_measurement': , - }), }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Start in', + 'original_name': 'TwinDos 1 level', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'start_time', - 'unique_id': 'Dummy_Appliance_3-state_start_time', - 'unit_of_measurement': , + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_1_level', + 'unit_of_measurement': '%', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start_in-state] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_twindos_1_level-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'duration', - 'friendly_name': 'Washing machine Start in', - 'unit_of_measurement': , + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.washing_machine_start_in', + 'entity_id': 'sensor.none_twindos_1_level', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': '63', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_target_temperature-entry] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_twindos_1_level_2-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), + 'capabilities': None, 'config_entry_id': , 'config_subentry_id': , 'device_class': None, 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.washing_machine_target_temperature', + 'entity_category': , + 'entity_id': 'sensor.none_twindos_1_level_2', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -7072,46 +9142,39 @@ }), 'name': None, 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 1, - }), }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Target temperature', + 'original_name': 'TwinDos 1 level', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'target_temperature', - 'unique_id': 'Dummy_Appliance_3-state_target_temperature', - 'unit_of_measurement': , + 'translation_key': 'twin_dos_1_level', + 'unique_id': 'DummyWasher-twin_dos_1_level', + 'unit_of_measurement': '%', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_target_temperature-state] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_twindos_1_level_2-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'Washing machine Target temperature', - 'state_class': , - 'unit_of_measurement': , + 'friendly_name': 'TwinDos 1 level', + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.washing_machine_target_temperature', + 'entity_id': 'sensor.none_twindos_1_level_2', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': '63', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_water_consumption-entry] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_twindos_2_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), + 'capabilities': None, 'config_entry_id': , 'config_subentry_id': , 'device_class': None, @@ -7119,7 +9182,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.washing_machine_water_consumption', + 'entity_id': 'sensor.none_twindos_2_level', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -7128,39 +9191,34 @@ }), 'name': None, 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Water consumption', + 'original_name': 'TwinDos 2 level', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'water_consumption', - 'unique_id': 'Dummy_Appliance_3-current_water_consumption', - 'unit_of_measurement': , + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'Dummy_Appliance_3-twin_dos_2_level', + 'unit_of_measurement': '%', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_water_consumption-state] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_twindos_2_level-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'water', - 'friendly_name': 'Washing machine Water consumption', - 'state_class': , - 'unit_of_measurement': , + 'friendly_name': 'TwinDos 2 level', + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.washing_machine_water_consumption', + 'entity_id': 'sensor.none_twindos_2_level', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': '20', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_water_forecast-entry] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_twindos_2_level_2-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -7173,7 +9231,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.washing_machine_water_forecast', + 'entity_id': 'sensor.none_twindos_2_level_2', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -7185,28 +9243,28 @@ }), 'original_device_class': None, 'original_icon': None, - 'original_name': 'Water forecast', + 'original_name': 'TwinDos 2 level', 'platform': 'miele', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'water_forecast', - 'unique_id': 'Dummy_Appliance_3-water_forecast', + 'translation_key': 'twin_dos_2_level', + 'unique_id': 'DummyWasher-twin_dos_2_level', 'unit_of_measurement': '%', }) # --- -# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_water_forecast-state] +# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.none_twindos_2_level_2-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'Washing machine Water forecast', + 'friendly_name': 'TwinDos 2 level', 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.washing_machine_water_forecast', + 'entity_id': 'sensor.none_twindos_2_level_2', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '0.0', + 'state': '20', }) # --- # name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.robot_vacuum_cleaner-entry] diff --git a/tests/components/miele/test_init.py b/tests/components/miele/test_init.py index 4c8e31b2d1133f..6d5f025be463a7 100644 --- a/tests/components/miele/test_init.py +++ b/tests/components/miele/test_init.py @@ -109,7 +109,7 @@ async def test_devices_multiple_created_count( """Test that multiple devices are created.""" await setup_integration(hass, mock_config_entry) - assert len(device_registry.devices) == 5 + assert len(device_registry.devices) == 7 async def test_device_info( @@ -205,7 +205,7 @@ async def test_setup_all_platforms( async_fire_time_changed(hass) await hass.async_block_till_done() - assert len(device_registry.devices) == prev_devices + 2 + assert len(device_registry.devices) == prev_devices + 1 # Check a sample sensor for each new device assert hass.states.get("sensor.dishwasher").state == "in_use" diff --git a/tests/components/netatmo/test_camera.py b/tests/components/netatmo/test_camera.py index 75dfe3aa62b6e2..f301200e26bee3 100644 --- a/tests/components/netatmo/test_camera.py +++ b/tests/components/netatmo/test_camera.py @@ -53,8 +53,20 @@ async def test_entity( ) +@pytest.mark.parametrize( + ("camera_type", "camera_id", "camera_entity"), + [ + ("NACamera", "12:34:56:00:f1:62", "camera.hall"), + ("NOC", "12:34:56:10:b9:0e", "camera.front"), + ], +) async def test_setup_component_with_webhook( - hass: HomeAssistant, config_entry: MockConfigEntry, netatmo_auth: AsyncMock + hass: HomeAssistant, + config_entry: MockConfigEntry, + netatmo_auth: AsyncMock, + camera_type: str, + camera_id: str, + camera_entity: str, ) -> None: """Test setup with webhook.""" with selected_platforms([Platform.CAMERA]): @@ -65,104 +77,41 @@ async def test_setup_component_with_webhook( webhook_id = config_entry.data[CONF_WEBHOOK_ID] await hass.async_block_till_done() - camera_entity_indoor = "camera.hall" - camera_entity_outdoor = "camera.front" - - # Test indoor camera events - assert hass.states.get(camera_entity_indoor).state == "streaming" - response = { - "event_type": "off", - "device_id": "12:34:56:00:f1:62", - "camera_id": "12:34:56:00:f1:62", - "event_id": "601dce1560abca1ebad9b723", - "push_type": "NACamera-off", - } - await simulate_webhook(hass, webhook_id, response) - - assert hass.states.get(camera_entity_indoor).state == "idle" - - response = { - "event_type": "on", - "device_id": "12:34:56:00:f1:62", - "camera_id": "12:34:56:00:f1:62", - "event_id": "646227f1dc0dfa000ec5f350", - "push_type": "NACamera-on", - } - await simulate_webhook(hass, webhook_id, response) - - assert hass.states.get(camera_entity_indoor).state == "streaming" - - # Test outdoor camera events - assert hass.states.get(camera_entity_outdoor).state == "streaming" + # Test on/off camera events + assert hass.states.get(camera_entity).state == "streaming" response = { "event_type": "off", - "device_id": "12:34:56:10:b9:0e", - "camera_id": "12:34:56:10:b9:0e", + "device_id": camera_id, + "camera_id": camera_id, "event_id": "601dce1560abca1ebad9b723", - "push_type": "NOC-off", + "push_type": f"{camera_type}-off", } await simulate_webhook(hass, webhook_id, response) - assert hass.states.get(camera_entity_outdoor).state == "idle" + assert hass.states.get(camera_entity).state == "idle" response = { "event_type": "on", - "device_id": "12:34:56:10:b9:0e", - "camera_id": "12:34:56:10:b9:0e", + "device_id": camera_id, + "camera_id": camera_id, "event_id": "646227f1dc0dfa000ec5f350", - "push_type": "NOC-on", - } - await simulate_webhook(hass, webhook_id, response) - - assert hass.states.get(camera_entity_outdoor).state == "streaming" - - response = { - "event_type": "light_mode", - "device_id": "12:34:56:10:b9:0e", - "camera_id": "12:34:56:10:b9:0e", - "event_id": "601dce1560abca1ebad9b723", - "push_type": "NOC-light_mode", - "sub_type": "on", - } - await simulate_webhook(hass, webhook_id, response) - - assert hass.states.get(camera_entity_outdoor).state == "streaming" - assert hass.states.get(camera_entity_outdoor).attributes["light_state"] == "on" - - response = { - "event_type": "light_mode", - "device_id": "12:34:56:10:b9:0e", - "camera_id": "12:34:56:10:b9:0e", - "event_id": "601dce1560abca1ebad9b723", - "push_type": "NOC-light_mode", - "sub_type": "auto", - } - await simulate_webhook(hass, webhook_id, response) - - assert hass.states.get(camera_entity_outdoor).attributes["light_state"] == "auto" - - response = { - "event_type": "light_mode", - "device_id": "12:34:56:10:b9:0e", - "camera_id": "12:34:56:10:b9:0e", - "event_id": "601dce1560abca1ebad9b723", - "push_type": "NOC-light_mode", + "push_type": f"{camera_type}-on", } await simulate_webhook(hass, webhook_id, response) - assert hass.states.get(camera_entity_indoor).state == "streaming" - assert hass.states.get(camera_entity_outdoor).attributes["light_state"] == "auto" + assert hass.states.get(camera_entity).state == "streaming" + # Test turn_on/turn_off services with patch("pyatmo.home.Home.async_set_state") as mock_set_state: await hass.services.async_call( - "camera", "turn_off", service_data={"entity_id": "camera.hall"} + "camera", "turn_off", service_data={"entity_id": camera_entity} ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( { "modules": [ { - "id": "12:34:56:00:f1:62", + "id": camera_id, "monitoring": "off", } ] @@ -171,14 +120,14 @@ async def test_setup_component_with_webhook( with patch("pyatmo.home.Home.async_set_state") as mock_set_state: await hass.services.async_call( - "camera", "turn_on", service_data={"entity_id": "camera.hall"} + "camera", "turn_on", service_data={"entity_id": camera_entity} ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( { "modules": [ { - "id": "12:34:56:00:f1:62", + "id": camera_id, "monitoring": "on", } ] @@ -364,6 +313,69 @@ async def test_service_set_persons_home( ) +@pytest.mark.parametrize( + ("camera_type", "camera_id", "camera_entity"), + [ + ("NOC", "12:34:56:10:b9:0e", "camera.front"), + ], +) +async def test_light_component_with_webhook( + hass: HomeAssistant, + config_entry: MockConfigEntry, + netatmo_auth: AsyncMock, + camera_type: str, + camera_id: str, + camera_entity: str, +) -> None: + """Test setup with webhook.""" + with selected_platforms([Platform.CAMERA]): + assert await hass.config_entries.async_setup(config_entry.entry_id) + + await hass.async_block_till_done() + + webhook_id = config_entry.data[CONF_WEBHOOK_ID] + await hass.async_block_till_done() + + assert hass.states.get(camera_entity).state == "streaming" + + response = { + "event_type": "light_mode", + "device_id": camera_id, + "camera_id": camera_id, + "event_id": "601dce1560abca1ebad9b723", + "push_type": f"{camera_type}-light_mode", + "sub_type": "on", + } + await simulate_webhook(hass, webhook_id, response) + + assert hass.states.get(camera_entity).state == "streaming" + assert hass.states.get(camera_entity).attributes["light_state"] == "on" + + response = { + "event_type": "light_mode", + "device_id": camera_id, + "camera_id": camera_id, + "event_id": "601dce1560abca1ebad9b723", + "push_type": f"{camera_type}-light_mode", + "sub_type": "auto", + } + await simulate_webhook(hass, webhook_id, response) + + assert hass.states.get(camera_entity).attributes["light_state"] == "auto" + + response = { + "event_type": "light_mode", + "device_id": camera_id, + "camera_id": camera_id, + "event_id": "601dce1560abca1ebad9b723", + "push_type": f"{camera_type}-light_mode", + } + await simulate_webhook(hass, webhook_id, response) + + assert hass.states.get(camera_entity).state == "streaming" + assert hass.states.get(camera_entity).attributes["light_state"] == "auto" + + async def test_service_set_camera_light( hass: HomeAssistant, config_entry: MockConfigEntry, netatmo_auth: AsyncMock ) -> None: diff --git a/tests/components/shopping_list/conftest.py b/tests/components/shopping_list/conftest.py index dd1b690e1e3e1b..69d214efe18c70 100644 --- a/tests/components/shopping_list/conftest.py +++ b/tests/components/shopping_list/conftest.py @@ -1,6 +1,8 @@ """Shopping list test helpers.""" -from unittest.mock import patch +from collections.abc import Generator +from contextlib import suppress +import os import pytest @@ -11,13 +13,13 @@ @pytest.fixture(autouse=True) -def mock_shopping_list_io(): - """Stub out the persistence.""" - with ( - patch("homeassistant.components.shopping_list.ShoppingData.save"), - patch("homeassistant.components.shopping_list.ShoppingData.async_load"), - ): +def wipe_shopping_list_store(hass: HomeAssistant) -> Generator[None]: + """Wipe shopping list store after test.""" + try: yield + finally: + with suppress(FileNotFoundError): + os.remove(hass.config.path(".shopping_list.json")) @pytest.fixture @@ -27,7 +29,7 @@ def mock_config_entry() -> MockConfigEntry: @pytest.fixture -async def sl_setup(hass: HomeAssistant, mock_config_entry: MockConfigEntry): +async def sl_setup(hass: HomeAssistant, mock_config_entry: MockConfigEntry) -> None: """Set up the shopping list.""" mock_config_entry.add_to_hass(hass) diff --git a/tests/components/shopping_list/snapshots/test_init.ambr b/tests/components/shopping_list/snapshots/test_init.ambr new file mode 100644 index 00000000000000..0e30e9bf0590d7 --- /dev/null +++ b/tests/components/shopping_list/snapshots/test_init.ambr @@ -0,0 +1,717 @@ +# serializer version: 1 +# name: test_add_item + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_add_item_service + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_api_update_fails + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_api_update_fails.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_api_update_fails.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_clear_completed_items + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_clear_completed_items.1 + list([ + dict({ + 'complete': True, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': True, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_clear_completed_items.2 + list([ + ]) +# --- +# name: test_clear_completed_items_service + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_clear_completed_items_service.1 + list([ + dict({ + 'complete': True, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_clear_completed_items_service.2 + list([ + ]) +# --- +# name: test_deprecated_api_clear_completed + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_deprecated_api_clear_completed.1 + list([ + dict({ + 'complete': True, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_deprecated_api_clear_completed.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_deprecated_api_create + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + ]) +# --- +# name: test_deprecated_api_create_fail + '' +# --- +# name: test_deprecated_api_get_all + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_deprecated_api_get_all.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_deprecated_api_update + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_deprecated_api_update.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_deprecated_api_update.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + dict({ + 'complete': True, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_recent_items_intent + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + ]) +# --- +# name: test_recent_items_intent.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + ]) +# --- +# name: test_remove_item + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_remove_item.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_remove_item.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_remove_item_service + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_remove_item_service.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_sort_list_service + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'zzz', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'ddd', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'aaa', + }), + ]) +# --- +# name: test_sort_list_service.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'aaa', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'ddd', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'zzz', + }), + ]) +# --- +# name: test_sort_list_service.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'zzz', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'ddd', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'aaa', + }), + ]) +# --- +# name: test_update_list + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_update_list.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'dupe', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'dupe', + }), + ]) +# --- +# name: test_ws_add_item + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + ]) +# --- +# name: test_ws_add_item_fail + '' +# --- +# name: test_ws_clear_items + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_clear_items.1 + list([ + dict({ + 'complete': True, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_clear_items.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_get_items + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_get_items.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_remove_item + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_ws_remove_item.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'cheese', + }), + ]) +# --- +# name: test_ws_remove_item_fail + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + ]) +# --- +# name: test_ws_remove_item_fail.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + ]) +# --- +# name: test_ws_reorder_items + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'apple', + }), + ]) +# --- +# name: test_ws_reorder_items.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'apple', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_ws_reorder_items.2 + list([ + dict({ + 'complete': True, + 'id': '', + 'name': 'wine', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'apple', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_ws_reorder_items.3 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'apple', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': True, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_reorder_items_failure + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'apple', + }), + ]) +# --- +# name: test_ws_reorder_items_failure.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'apple', + }), + ]) +# --- +# name: test_ws_reorder_items_failure.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'apple', + }), + ]) +# --- +# name: test_ws_update_item + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_update_item.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + dict({ + 'complete': False, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_update_item.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'soda', + }), + dict({ + 'complete': True, + 'id': '', + 'name': 'wine', + }), + ]) +# --- +# name: test_ws_update_item_fail + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_ws_update_item_fail.1 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- +# name: test_ws_update_item_fail.2 + list([ + dict({ + 'complete': False, + 'id': '', + 'name': 'beer', + }), + ]) +# --- diff --git a/tests/components/shopping_list/test_init.py b/tests/components/shopping_list/test_init.py index 276602f794eab4..644f28c64d2015 100644 --- a/tests/components/shopping_list/test_init.py +++ b/tests/components/shopping_list/test_init.py @@ -1,8 +1,11 @@ """Test shopping list component.""" from http import HTTPStatus +import json +from pathlib import Path import pytest +from syrupy.assertion import SnapshotAssertion from homeassistant.components.shopping_list import NoMatchingShoppingListItem from homeassistant.components.shopping_list.const import ( @@ -28,7 +31,23 @@ from tests.typing import ClientSessionGenerator, WebSocketGenerator -async def test_add_item(hass: HomeAssistant, sl_setup) -> None: +def assert_shopping_list_data(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None: + """Assert shopping list data matches snapshot.""" + path = Path(hass.config.path(".shopping_list.json")) + if not path.exists(): + assert snapshot == "" + else: + shopping_list_data = json.loads(path.read_text(encoding="utf-8")) + for item in shopping_list_data: + if "id" not in item: + continue + item["id"] = "" + assert shopping_list_data == snapshot + + +async def test_add_item( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: """Test adding an item intent.""" response = await intent.async_handle( @@ -39,10 +58,13 @@ async def test_add_item(hass: HomeAssistant, sl_setup) -> None: # Response text is now handled by default conversation agent assert response.response_type == intent.IntentResponseType.ACTION_DONE + assert_shopping_list_data(hass, snapshot) -async def test_remove_item(hass: HomeAssistant, sl_setup) -> None: - """Test removiung list items.""" +async def test_remove_item( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: + """Test removing list items.""" await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}} ) @@ -50,12 +72,14 @@ async def test_remove_item(hass: HomeAssistant, sl_setup) -> None: await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}} ) + assert_shopping_list_data(hass, snapshot) assert len(hass.data[DOMAIN].items) == 2 # Remove a single item item_id = hass.data[DOMAIN].items[0]["id"] await hass.data[DOMAIN].async_remove(item_id) + assert_shopping_list_data(hass, snapshot) assert len(hass.data[DOMAIN].items) == 1 @@ -65,9 +89,12 @@ async def test_remove_item(hass: HomeAssistant, sl_setup) -> None: # Trying to remove the same item twice should fail with pytest.raises(NoMatchingShoppingListItem): await hass.data[DOMAIN].async_remove(item_id) + assert_shopping_list_data(hass, snapshot) -async def test_update_list(hass: HomeAssistant, sl_setup) -> None: +async def test_update_list( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: """Test updating all list items.""" await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}} @@ -76,6 +103,7 @@ async def test_update_list(hass: HomeAssistant, sl_setup) -> None: await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}} ) + assert_shopping_list_data(hass, snapshot) # Update a single attribute, other attributes shouldn't change await hass.data[DOMAIN].async_update_list({"complete": True}) @@ -90,6 +118,7 @@ async def test_update_list(hass: HomeAssistant, sl_setup) -> None: # Update multiple attributes await hass.data[DOMAIN].async_update_list({"name": "dupe", "complete": False}) + assert_shopping_list_data(hass, snapshot) beer = hass.data[DOMAIN].items[0] assert beer["name"] == "dupe" @@ -100,7 +129,9 @@ async def test_update_list(hass: HomeAssistant, sl_setup) -> None: assert cheese["complete"] is False -async def test_clear_completed_items(hass: HomeAssistant, sl_setup) -> None: +async def test_clear_completed_items( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: """Test clear completed list items.""" await intent.async_handle( hass, @@ -112,18 +143,23 @@ async def test_clear_completed_items(hass: HomeAssistant, sl_setup) -> None: await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}} ) + assert_shopping_list_data(hass, snapshot) assert len(hass.data[DOMAIN].items) == 2 # Update a single attribute, other attributes shouldn't change await hass.data[DOMAIN].async_update_list({"complete": True}) + assert_shopping_list_data(hass, snapshot) await hass.data[DOMAIN].async_clear_completed() + assert_shopping_list_data(hass, snapshot) assert len(hass.data[DOMAIN].items) == 0 -async def test_recent_items_intent(hass: HomeAssistant, sl_setup) -> None: +async def test_recent_items_intent( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: """Test recent items.""" await intent.async_handle( @@ -135,8 +171,10 @@ async def test_recent_items_intent(hass: HomeAssistant, sl_setup) -> None: await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "soda"}} ) + assert_shopping_list_data(hass, snapshot) response = await intent.async_handle(hass, "test", "HassShoppingListLastItems") + assert_shopping_list_data(hass, snapshot) assert ( response.speech["plain"]["speech"] @@ -145,7 +183,10 @@ async def test_recent_items_intent(hass: HomeAssistant, sl_setup) -> None: async def test_deprecated_api_get_all( - hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test the API.""" @@ -155,9 +196,11 @@ async def test_deprecated_api_get_all( await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}} ) + assert_shopping_list_data(hass, snapshot) client = await hass_client() resp = await client.get("/api/shopping_list") + assert_shopping_list_data(hass, snapshot) assert resp.status == HTTPStatus.OK data = await resp.json() @@ -169,7 +212,10 @@ async def test_deprecated_api_get_all( async def test_ws_get_items( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test get shopping_list items websocket command.""" @@ -179,6 +225,7 @@ async def test_ws_get_items( await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}} ) + assert_shopping_list_data(hass, snapshot) client = await hass_ws_client(hass) events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) @@ -187,6 +234,7 @@ async def test_ws_get_items( msg = await client.receive_json() assert msg["success"] is True assert len(events) == 0 + assert_shopping_list_data(hass, snapshot) assert msg["id"] == 5 assert msg["type"] == TYPE_RESULT @@ -200,7 +248,10 @@ async def test_ws_get_items( async def test_deprecated_api_update( - hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test the API.""" @@ -210,6 +261,7 @@ async def test_deprecated_api_update( await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}} ) + assert_shopping_list_data(hass, snapshot) beer_id = hass.data["shopping_list"].items[0]["id"] wine_id = hass.data["shopping_list"].items[1]["id"] @@ -219,6 +271,7 @@ async def test_deprecated_api_update( resp = await client.post( f"/api/shopping_list/item/{beer_id}", json={"name": "soda"} ) + assert_shopping_list_data(hass, snapshot) assert resp.status == HTTPStatus.OK assert len(events) == 1 @@ -228,6 +281,7 @@ async def test_deprecated_api_update( resp = await client.post( f"/api/shopping_list/item/{wine_id}", json={"complete": True} ) + assert_shopping_list_data(hass, snapshot) assert resp.status == HTTPStatus.OK assert len(events) == 2 @@ -240,7 +294,10 @@ async def test_deprecated_api_update( async def test_ws_update_item( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test update shopping_list item websocket command.""" await intent.async_handle( @@ -249,6 +306,7 @@ async def test_ws_update_item( await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}} ) + assert_shopping_list_data(hass, snapshot) beer_id = hass.data["shopping_list"].items[0]["id"] wine_id = hass.data["shopping_list"].items[1]["id"] @@ -267,6 +325,7 @@ async def test_ws_update_item( data = msg["result"] assert data == {"id": beer_id, "name": "soda", "complete": False} assert len(events) == 1 + assert_shopping_list_data(hass, snapshot) await client.send_json( { @@ -281,6 +340,7 @@ async def test_ws_update_item( data = msg["result"] assert data == {"id": wine_id, "name": "wine", "complete": True} assert len(events) == 2 + assert_shopping_list_data(hass, snapshot) beer, wine = hass.data["shopping_list"].items assert beer == {"id": beer_id, "name": "soda", "complete": False} @@ -288,34 +348,44 @@ async def test_ws_update_item( async def test_api_update_fails( - hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test the API.""" await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}} ) + assert_shopping_list_data(hass, snapshot) client = await hass_client() events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) resp = await client.post("/api/shopping_list/non_existing", json={"name": "soda"}) + assert_shopping_list_data(hass, snapshot) assert resp.status == HTTPStatus.NOT_FOUND assert len(events) == 0 beer_id = hass.data["shopping_list"].items[0]["id"] resp = await client.post(f"/api/shopping_list/item/{beer_id}", json={"name": 123}) + assert_shopping_list_data(hass, snapshot) assert resp.status == HTTPStatus.BAD_REQUEST async def test_ws_update_item_fail( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test failure of update shopping_list item websocket command.""" await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}} ) + assert_shopping_list_data(hass, snapshot) client = await hass_ws_client(hass) events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) await client.send_json( @@ -331,15 +401,20 @@ async def test_ws_update_item_fail( data = msg["error"] assert data == {"code": "item_not_found", "message": "Item not found"} assert len(events) == 0 + assert_shopping_list_data(hass, snapshot) await client.send_json({"id": 6, "type": "shopping_list/items/update", "name": 123}) msg = await client.receive_json() assert msg["success"] is False assert len(events) == 0 + assert_shopping_list_data(hass, snapshot) async def test_deprecated_api_clear_completed( - hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test the API.""" @@ -349,6 +424,7 @@ async def test_deprecated_api_clear_completed( await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}} ) + assert_shopping_list_data(hass, snapshot) beer_id = hass.data["shopping_list"].items[0]["id"] wine_id = hass.data["shopping_list"].items[1]["id"] @@ -362,10 +438,12 @@ async def test_deprecated_api_clear_completed( ) assert resp.status == HTTPStatus.OK assert len(events) == 1 + assert_shopping_list_data(hass, snapshot) resp = await client.post("/api/shopping_list/clear_completed") assert resp.status == HTTPStatus.OK assert len(events) == 2 + assert_shopping_list_data(hass, snapshot) items = hass.data["shopping_list"].items assert len(items) == 1 @@ -374,7 +452,10 @@ async def test_deprecated_api_clear_completed( async def test_ws_clear_items( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test clearing shopping_list items websocket command.""" await intent.async_handle( @@ -383,6 +464,7 @@ async def test_ws_clear_items( await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}} ) + assert_shopping_list_data(hass, snapshot) beer_id = hass.data["shopping_list"].items[0]["id"] wine_id = hass.data["shopping_list"].items[1]["id"] client = await hass_ws_client(hass) @@ -398,6 +480,7 @@ async def test_ws_clear_items( msg = await client.receive_json() assert msg["success"] is True assert len(events) == 1 + assert_shopping_list_data(hass, snapshot) await client.send_json({"id": 6, "type": "shopping_list/items/clear"}) msg = await client.receive_json() @@ -406,16 +489,21 @@ async def test_ws_clear_items( assert len(items) == 1 assert items[0] == {"id": wine_id, "name": "wine", "complete": False} assert len(events) == 2 + assert_shopping_list_data(hass, snapshot) async def test_deprecated_api_create( - hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test the API.""" client = await hass_client() events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) resp = await client.post("/api/shopping_list/item", json={"name": "soda"}) + assert_shopping_list_data(hass, snapshot) assert resp.status == HTTPStatus.OK data = await resp.json() @@ -430,13 +518,17 @@ async def test_deprecated_api_create( async def test_deprecated_api_create_fail( - hass: HomeAssistant, hass_client: ClientSessionGenerator, sl_setup + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test the API.""" client = await hass_client() events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) resp = await client.post("/api/shopping_list/item", json={"name": 1234}) + assert_shopping_list_data(hass, snapshot) assert resp.status == HTTPStatus.BAD_REQUEST assert len(hass.data["shopping_list"].items) == 0 @@ -444,7 +536,10 @@ async def test_deprecated_api_create_fail( async def test_ws_add_item( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test adding shopping_list item websocket command.""" client = await hass_ws_client(hass) @@ -456,6 +551,7 @@ async def test_ws_add_item( assert data["name"] == "soda" assert data["complete"] is False assert len(events) == 1 + assert_shopping_list_data(hass, snapshot) items = hass.data["shopping_list"].items assert len(items) == 1 @@ -464,7 +560,10 @@ async def test_ws_add_item( async def test_ws_add_item_fail( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test adding shopping_list item failure websocket command.""" client = await hass_ws_client(hass) @@ -474,10 +573,14 @@ async def test_ws_add_item_fail( assert msg["success"] is False assert len(events) == 0 assert len(hass.data["shopping_list"].items) == 0 + assert_shopping_list_data(hass, snapshot) async def test_ws_remove_item( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test removing shopping_list item websocket command.""" client = await hass_ws_client(hass) @@ -490,6 +593,7 @@ async def test_ws_remove_item( ) msg = await client.receive_json() assert len(events) == 2 + assert_shopping_list_data(hass, snapshot) items = hass.data["shopping_list"].items assert len(items) == 2 @@ -500,6 +604,7 @@ async def test_ws_remove_item( msg = await client.receive_json() assert len(events) == 3 assert msg["success"] is True + assert_shopping_list_data(hass, snapshot) items = hass.data["shopping_list"].items assert len(items) == 1 @@ -507,22 +612,30 @@ async def test_ws_remove_item( async def test_ws_remove_item_fail( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test removing shopping_list item failure websocket command.""" client = await hass_ws_client(hass) events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) await client.send_json({"id": 5, "type": "shopping_list/items/add", "name": "soda"}) msg = await client.receive_json() + assert_shopping_list_data(hass, snapshot) await client.send_json({"id": 6, "type": "shopping_list/items/remove"}) msg = await client.receive_json() assert msg["success"] is False assert len(events) == 1 assert len(hass.data["shopping_list"].items) == 1 + assert_shopping_list_data(hass, snapshot) async def test_ws_reorder_items( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test reordering shopping_list items websocket command.""" await intent.async_handle( @@ -534,6 +647,7 @@ async def test_ws_reorder_items( await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "apple"}} ) + assert_shopping_list_data(hass, snapshot) beer_id = hass.data["shopping_list"].items[0]["id"] wine_id = hass.data["shopping_list"].items[1]["id"] @@ -566,6 +680,7 @@ async def test_ws_reorder_items( "name": "beer", "complete": False, } + assert_shopping_list_data(hass, snapshot) # Mark wine as completed. await client.send_json( @@ -578,6 +693,7 @@ async def test_ws_reorder_items( ) _ = await client.receive_json() assert len(events) == 2 + assert_shopping_list_data(hass, snapshot) await client.send_json( { @@ -604,10 +720,14 @@ async def test_ws_reorder_items( "name": "wine", "complete": True, } + assert_shopping_list_data(hass, snapshot) async def test_ws_reorder_items_failure( - hass: HomeAssistant, hass_ws_client: WebSocketGenerator, sl_setup + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + sl_setup: None, + snapshot: SnapshotAssertion, ) -> None: """Test reordering shopping_list items websocket command.""" await intent.async_handle( @@ -619,6 +739,7 @@ async def test_ws_reorder_items_failure( await intent.async_handle( hass, "test", "HassShoppingListAddItem", {"item": {"value": "apple"}} ) + assert_shopping_list_data(hass, snapshot) beer_id = hass.data["shopping_list"].items[0]["id"] wine_id = hass.data["shopping_list"].items[1]["id"] @@ -639,6 +760,7 @@ async def test_ws_reorder_items_failure( assert msg["success"] is False assert msg["error"]["code"] == ERR_NOT_FOUND assert len(events) == 0 + assert_shopping_list_data(hass, snapshot) # Testing not sending all unchecked item ids. await client.send_json( @@ -652,9 +774,12 @@ async def test_ws_reorder_items_failure( assert msg["success"] is False assert msg["error"]["code"] == ERR_INVALID_FORMAT assert len(events) == 0 + assert_shopping_list_data(hass, snapshot) -async def test_add_item_service(hass: HomeAssistant, sl_setup) -> None: +async def test_add_item_service( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: """Test adding shopping_list item service.""" events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) await hass.services.async_call( @@ -665,9 +790,12 @@ async def test_add_item_service(hass: HomeAssistant, sl_setup) -> None: ) assert len(hass.data[DOMAIN].items) == 1 assert len(events) == 1 + assert_shopping_list_data(hass, snapshot) -async def test_remove_item_service(hass: HomeAssistant, sl_setup) -> None: +async def test_remove_item_service( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: """Test removing shopping_list item service.""" events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) await hass.services.async_call( @@ -684,6 +812,7 @@ async def test_remove_item_service(hass: HomeAssistant, sl_setup) -> None: ) assert len(hass.data[DOMAIN].items) == 2 assert len(events) == 2 + assert_shopping_list_data(hass, snapshot) await hass.services.async_call( DOMAIN, @@ -694,9 +823,12 @@ async def test_remove_item_service(hass: HomeAssistant, sl_setup) -> None: assert len(hass.data[DOMAIN].items) == 1 assert hass.data[DOMAIN].items[0]["name"] == "cheese" assert len(events) == 3 + assert_shopping_list_data(hass, snapshot) -async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> None: +async def test_clear_completed_items_service( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: """Test clearing completed shopping_list items service.""" events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) await hass.services.async_call( @@ -707,6 +839,7 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N ) assert len(hass.data[DOMAIN].items) == 1 assert len(events) == 1 + assert_shopping_list_data(hass, snapshot) events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) await hass.services.async_call( @@ -717,6 +850,7 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N ) assert len(hass.data[DOMAIN].items) == 1 assert len(events) == 1 + assert_shopping_list_data(hass, snapshot) events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) await hass.services.async_call( @@ -727,9 +861,12 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N ) assert len(hass.data[DOMAIN].items) == 0 assert len(events) == 1 + assert_shopping_list_data(hass, snapshot) -async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None: +async def test_sort_list_service( + hass: HomeAssistant, sl_setup: None, snapshot: SnapshotAssertion +) -> None: """Test sort_all service.""" for name in ("zzz", "ddd", "aaa"): @@ -739,6 +876,7 @@ async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None: {ATTR_NAME: name}, blocking=True, ) + assert_shopping_list_data(hass, snapshot) # sort ascending events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) @@ -748,6 +886,7 @@ async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None: {ATTR_REVERSE: False}, blocking=True, ) + assert_shopping_list_data(hass, snapshot) assert hass.data[DOMAIN].items[0][ATTR_NAME] == "aaa" assert hass.data[DOMAIN].items[1][ATTR_NAME] == "ddd" @@ -761,6 +900,7 @@ async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None: {ATTR_REVERSE: True}, blocking=True, ) + assert_shopping_list_data(hass, snapshot) assert hass.data[DOMAIN].items[0][ATTR_NAME] == "zzz" assert hass.data[DOMAIN].items[1][ATTR_NAME] == "ddd" diff --git a/tests/components/sonos/test_services.py b/tests/components/sonos/test_services.py index 7e30e1e2125d53..af8177a901cb3c 100644 --- a/tests/components/sonos/test_services.py +++ b/tests/components/sonos/test_services.py @@ -13,6 +13,7 @@ SERVICE_JOIN, SERVICE_UNJOIN, ) +from homeassistant.components.sonos.const import LONG_SERVICE_TIMEOUT from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError @@ -222,7 +223,7 @@ def mock_unjoin(*args, **kwargs): await hass.async_block_till_done(wait_background_tasks=True) assert len(caplog.records) == 0 - assert soco_bedroom.unjoin.call_count == 1 + soco_bedroom.unjoin.assert_called_with(timeout=LONG_SERVICE_TIMEOUT) assert soco_living_room.unjoin.call_count == 0 diff --git a/tests/components/vesync/snapshots/test_diagnostics.ambr b/tests/components/vesync/snapshots/test_diagnostics.ambr index 290bb0dcdb183a..e6eb53ac54ec0e 100644 --- a/tests/components/vesync/snapshots/test_diagnostics.ambr +++ b/tests/components/vesync/snapshots/test_diagnostics.ambr @@ -314,6 +314,60 @@ }), 'unit_of_measurement': '%', }), + dict({ + 'device_class': None, + 'disabled': False, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_fan_pm1', + 'icon': None, + 'name': None, + 'original_device_class': 'pm1', + 'original_icon': None, + 'original_name': 'PM1', + 'state': dict({ + 'attributes': dict({ + 'device_class': 'pm1', + 'friendly_name': 'Test Fan PM1', + 'state_class': 'measurement', + 'unit_of_measurement': 'μg/m³', + }), + 'entity_id': 'sensor.test_fan_pm1', + 'last_changed': str, + 'last_reported': str, + 'last_updated': str, + 'state': 'unavailable', + }), + 'unit_of_measurement': 'μg/m³', + }), + dict({ + 'device_class': None, + 'disabled': False, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_fan_pm10', + 'icon': None, + 'name': None, + 'original_device_class': 'pm10', + 'original_icon': None, + 'original_name': 'PM10', + 'state': dict({ + 'attributes': dict({ + 'device_class': 'pm10', + 'friendly_name': 'Test Fan PM10', + 'state_class': 'measurement', + 'unit_of_measurement': 'μg/m³', + }), + 'entity_id': 'sensor.test_fan_pm10', + 'last_changed': str, + 'last_reported': str, + 'last_updated': str, + 'state': 'unavailable', + }), + 'unit_of_measurement': 'μg/m³', + }), dict({ 'device_class': None, 'disabled': False,