diff --git a/homeassistant/components/vesync/__init__.py b/homeassistant/components/vesync/__init__.py index 3eedaff15d086..600876c309623 100644 --- a/homeassistant/components/vesync/__init__.py +++ b/homeassistant/components/vesync/__init__.py @@ -32,6 +32,7 @@ Platform.SELECT, Platform.SENSOR, Platform.SWITCH, + Platform.TIME, Platform.UPDATE, ] diff --git a/homeassistant/components/vesync/strings.json b/homeassistant/components/vesync/strings.json index 115a760876ad8..cc9418cc6ee53 100644 --- a/homeassistant/components/vesync/strings.json +++ b/homeassistant/components/vesync/strings.json @@ -148,6 +148,11 @@ "display": { "name": "Display" } + }, + "time": { + "remaining_time": { + "name": "Remaining time" + } } }, "services": { diff --git a/homeassistant/components/vesync/time.py b/homeassistant/components/vesync/time.py new file mode 100644 index 0000000000000..6cd07853fd441 --- /dev/null +++ b/homeassistant/components/vesync/time.py @@ -0,0 +1,101 @@ +"""Support for VeSync time entities.""" + +from collections.abc import Callable +from dataclasses import dataclass +import logging + +from pyvesync.base_devices.vesyncbasedevice import VeSyncBaseDevice +from pyvesync.device_container import DeviceContainer + +from homeassistant.components.time import TimeEntity, TimeEntityDescription, time +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from .common import is_air_fryer +from .const import VS_DEVICES, VS_DISCOVERY +from .coordinator import VesyncConfigEntry, VeSyncDataCoordinator +from .entity import VeSyncBaseEntity + +_LOGGER = logging.getLogger(__name__) + +PARALLEL_UPDATES = 0 + + +@dataclass(frozen=True, kw_only=True) +class VeSyncTimeEntityDescription(TimeEntityDescription): + """Class to describe a Vesync number entity.""" + + exists_fn: Callable[[VeSyncBaseDevice], bool] = lambda _: True + value_fn: Callable[[VeSyncBaseDevice], float] + + +TIME_DESCRIPTIONS: list[VeSyncTimeEntityDescription] = [ + VeSyncTimeEntityDescription( + key="remaining_time", + translation_key="remaining_time", + exists_fn=is_air_fryer, + value_fn=lambda device: device.state.remaining_time, + ) +] + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: VesyncConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up time entities.""" + + coordinator = config_entry.runtime_data + + @callback + def discover(devices: list[VeSyncBaseDevice]) -> None: + """Add new devices to platform.""" + _setup_entities(devices, async_add_entities, coordinator) + + config_entry.async_on_unload( + async_dispatcher_connect(hass, VS_DISCOVERY.format(VS_DEVICES), discover) + ) + + _setup_entities( + config_entry.runtime_data.manager.devices, async_add_entities, coordinator + ) + + +@callback +def _setup_entities( + devices: DeviceContainer | list[VeSyncBaseDevice], + async_add_entities: AddConfigEntryEntitiesCallback, + coordinator: VeSyncDataCoordinator, +) -> None: + """Add time entities.""" + + async_add_entities( + VeSyncTimeEntity(dev, description, coordinator) + for dev in devices + for description in TIME_DESCRIPTIONS + if description.exists_fn(dev) + ) + + +class VeSyncTimeEntity(VeSyncBaseEntity, TimeEntity): + """A class to set numeric options on Vesync device.""" + + entity_description: VeSyncTimeEntityDescription + + def __init__( + self, + device: VeSyncBaseDevice, + description: VeSyncTimeEntityDescription, + coordinator: VeSyncDataCoordinator, + ) -> None: + """Initialize the VeSync time device.""" + super().__init__(device, coordinator) + self.entity_description = description + self._attr_unique_id = f"{super().unique_id}-{description.key}" + + @property + def native_value(self) -> time: + """Return the value reported by the number.""" + return self.entity_description.value_fn(self.device) diff --git a/tests/components/vesync/snapshots/test_sensor.ambr b/tests/components/vesync/snapshots/test_sensor.ambr index a0052ba6076f5..7d78967d3b283 100644 --- a/tests/components/vesync/snapshots/test_sensor.ambr +++ b/tests/components/vesync/snapshots/test_sensor.ambr @@ -783,51 +783,6 @@ 'unique_id': 'CS158Cooking-preheat_set_time', 'unit_of_measurement': , }), - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'options': list([ - 'cooking_end', - 'cooking', - 'cooking_stop', - 'heating', - 'preheat_end', - 'preheat_stop', - 'pull_out', - 'standby', - ]), - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.cs158_af_air_fryer_cooking_cooking_status', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'object_id_base': 'Cooking status', - 'options': dict({ - }), - 'original_device_class': , - 'original_icon': None, - 'original_name': 'Cooking status', - 'platform': 'vesync', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'cook_status', - 'unique_id': 'CS158Cooking-cook_status', - 'unit_of_measurement': None, - }), ]) # --- # name: test_sensor_state[CS158-AF Air Fryer Cooking][sensor.cs158_af_air_fryer_cooking_cooking_set_temperature] @@ -861,30 +816,6 @@ 'state': '15', }) # --- -# name: test_sensor_state[CS158-AF Air Fryer Cooking][sensor.cs158_af_air_fryer_cooking_cooking_status] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'enum', - 'friendly_name': 'CS158-AF Air Fryer Cooking Cooking status', - 'options': list([ - 'cooking_end', - 'cooking', - 'cooking_stop', - 'heating', - 'preheat_end', - 'preheat_stop', - 'pull_out', - 'standby', - ]), - }), - 'context': , - 'entity_id': 'sensor.cs158_af_air_fryer_cooking_cooking_status', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'cooking', - }) -# --- # name: test_sensor_state[CS158-AF Air Fryer Cooking][sensor.cs158_af_air_fryer_cooking_current_temperature] StateSnapshot({ 'attributes': ReadOnlyDict({ @@ -1097,51 +1028,6 @@ 'unique_id': 'CS158Standby-preheat_set_time', 'unit_of_measurement': , }), - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'options': list([ - 'cooking_end', - 'cooking', - 'cooking_stop', - 'heating', - 'preheat_end', - 'preheat_stop', - 'pull_out', - 'standby', - ]), - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.cs158_af_air_fryer_standby_cooking_status', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'object_id_base': 'Cooking status', - 'options': dict({ - }), - 'original_device_class': , - 'original_icon': None, - 'original_name': 'Cooking status', - 'platform': 'vesync', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'cook_status', - 'unique_id': 'CS158Standby-cook_status', - 'unit_of_measurement': None, - }), ]) # --- # name: test_sensor_state[CS158-AF Air Fryer Standby][sensor.cs158_af_air_fryer_standby_cooking_set_temperature] @@ -1174,30 +1060,6 @@ 'state': 'unknown', }) # --- -# name: test_sensor_state[CS158-AF Air Fryer Standby][sensor.cs158_af_air_fryer_standby_cooking_status] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'enum', - 'friendly_name': 'CS158-AF Air Fryer Standby Cooking status', - 'options': list([ - 'cooking_end', - 'cooking', - 'cooking_stop', - 'heating', - 'preheat_end', - 'preheat_stop', - 'pull_out', - 'standby', - ]), - }), - 'context': , - 'entity_id': 'sensor.cs158_af_air_fryer_standby_cooking_status', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'standby', - }) -# --- # name: test_sensor_state[CS158-AF Air Fryer Standby][sensor.cs158_af_air_fryer_standby_current_temperature] StateSnapshot({ 'attributes': ReadOnlyDict({ diff --git a/tests/components/vesync/snapshots/test_time.ambr b/tests/components/vesync/snapshots/test_time.ambr new file mode 100644 index 0000000000000..82a08ad89e9d7 --- /dev/null +++ b/tests/components/vesync/snapshots/test_time.ambr @@ -0,0 +1,630 @@ +# serializer version: 1 +# name: test_update_state[Air Purifier 131s][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'air-purifier', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LV-PUR131S', + 'model_id': None, + 'name': 'Air Purifier 131s', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Air Purifier 131s][entities] + list([ + ]) +# --- +# name: test_update_state[Air Purifier 200s][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'asd_sdfKIHG7IJHGwJGJ7GJ_ag5h3G55', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'Core200S', + 'model_id': None, + 'name': 'Air Purifier 200s', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Air Purifier 200s][entities] + list([ + ]) +# --- +# name: test_update_state[Air Purifier 400s][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + '400s-purifier', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LAP-C401S-WJP', + 'model_id': None, + 'name': 'Air Purifier 400s', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Air Purifier 400s][entities] + list([ + ]) +# --- +# name: test_update_state[Air Purifier 600s][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + '600s-purifier', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LAP-C601S-WUS', + 'model_id': None, + 'name': 'Air Purifier 600s', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Air Purifier 600s][entities] + list([ + ]) +# --- +# name: test_update_state[CS158-AF Air Fryer Cooking][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'CS158Cooking', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'CS158-AF', + 'model_id': None, + 'name': 'CS158-AF Air Fryer Cooking', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': None, + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[CS158-AF Air Fryer Cooking][entities] + list([ + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'time', + 'entity_category': None, + 'entity_id': 'time.cs158_af_air_fryer_cooking_remaining_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Remaining time', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Remaining time', + 'platform': 'vesync', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'remaining_time', + 'unique_id': 'CS158Cooking-remaining_time', + 'unit_of_measurement': None, + }), + ]) +# --- +# name: test_update_state[CS158-AF Air Fryer Cooking][time.cs158_af_air_fryer_cooking_remaining_time] + None +# --- +# name: test_update_state[CS158-AF Air Fryer Standby][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'CS158Standby', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'CS158-AF', + 'model_id': None, + 'name': 'CS158-AF Air Fryer Standby', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': None, + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[CS158-AF Air Fryer Standby][entities] + list([ + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'time', + 'entity_category': None, + 'entity_id': 'time.cs158_af_air_fryer_standby_remaining_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Remaining time', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Remaining time', + 'platform': 'vesync', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'remaining_time', + 'unique_id': 'CS158Standby-remaining_time', + 'unit_of_measurement': None, + }), + ]) +# --- +# name: test_update_state[CS158-AF Air Fryer Standby][time.cs158_af_air_fryer_standby_remaining_time] + None +# --- +# name: test_update_state[Dimmable Light][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'dimmable-bulb', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'ESL100', + 'model_id': None, + 'name': 'Dimmable Light', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Dimmable Light][entities] + list([ + ]) +# --- +# name: test_update_state[Dimmer Switch][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'dimmable-switch', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'ESWD16', + 'model_id': None, + 'name': 'Dimmer Switch', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Dimmer Switch][entities] + list([ + ]) +# --- +# name: test_update_state[Humidifier 200s][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + '200s-humidifier4321', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'Classic200S', + 'model_id': None, + 'name': 'Humidifier 200s', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Humidifier 200s][entities] + list([ + ]) +# --- +# name: test_update_state[Humidifier 6000s][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + '6000s', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LEH-S601S-WUS', + 'model_id': None, + 'name': 'Humidifier 6000s', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': None, + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Humidifier 6000s][entities] + list([ + ]) +# --- +# name: test_update_state[Humidifier 600S][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + '600s-humidifier', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LUH-A602S-WUS', + 'model_id': None, + 'name': 'Humidifier 600S', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Humidifier 600S][entities] + list([ + ]) +# --- +# name: test_update_state[Outlet][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'outlet', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'wifi-switch-1.3', + 'model_id': None, + 'name': 'Outlet', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Outlet][entities] + list([ + ]) +# --- +# name: test_update_state[SmartTowerFan][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'smarttowerfan', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LTF-F422S-KEU', + 'model_id': None, + 'name': 'SmartTowerFan', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[SmartTowerFan][entities] + list([ + ]) +# --- +# name: test_update_state[Temperature Light][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'tunable-bulb', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'ESL100CW', + 'model_id': None, + 'name': 'Temperature Light', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Temperature Light][entities] + list([ + ]) +# --- +# name: test_update_state[Wall Switch][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'switch', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'ESWL01', + 'model_id': None, + 'name': 'Wall Switch', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '1.0.0', + 'via_device_id': None, + }), + ]) +# --- +# name: test_update_state[Wall Switch][entities] + list([ + ]) +# --- diff --git a/tests/components/vesync/test_init.py b/tests/components/vesync/test_init.py index 5886f19dd2b8e..0985b426b3728 100644 --- a/tests/components/vesync/test_init.py +++ b/tests/components/vesync/test_init.py @@ -68,6 +68,7 @@ async def test_async_setup_entry__no_devices( Platform.SELECT, Platform.SENSOR, Platform.SWITCH, + Platform.TIME, Platform.UPDATE, ] @@ -95,6 +96,7 @@ async def test_async_setup_entry__loads_fans( Platform.SELECT, Platform.SENSOR, Platform.SWITCH, + Platform.TIME, Platform.UPDATE, ] assert manager.login.call_count == 1 diff --git a/tests/components/vesync/test_time.py b/tests/components/vesync/test_time.py new file mode 100644 index 0000000000000..aa8f4dbc7f957 --- /dev/null +++ b/tests/components/vesync/test_time.py @@ -0,0 +1,51 @@ +"""Tests for the time module.""" + +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.components.time import DOMAIN as UPDATE_DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr, entity_registry as er + +from .common import ALL_DEVICE_NAMES, mock_devices_response + +from tests.common import MockConfigEntry +from tests.test_util.aiohttp import AiohttpClientMocker + + +@pytest.mark.parametrize("device_name", ALL_DEVICE_NAMES) +async def test_update_state( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + config_entry: MockConfigEntry, + device_registry: dr.DeviceRegistry, + entity_registry: er.EntityRegistry, + aioclient_mock: AiohttpClientMocker, + device_name: str, +) -> None: + """Test the resulting setup state is as expected for the platform.""" + + # Configure the API devices call for device_name + mock_devices_response(aioclient_mock, device_name) + + # setup platform - only including the named device + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + # Check device registry + devices = dr.async_entries_for_config_entry(device_registry, config_entry.entry_id) + assert devices == snapshot(name="devices") + + # Check entity registry + entities = [ + entity + for entity in er.async_entries_for_config_entry( + entity_registry, config_entry.entry_id + ) + if entity.domain == UPDATE_DOMAIN + ] + assert entities == snapshot(name="entities") + + # Check states + for entity in entities: + assert hass.states.get(entity.entity_id) == snapshot(name=entity.entity_id)