diff --git a/homeassistant/components/smartthings/icons.json b/homeassistant/components/smartthings/icons.json index dfdd689ea0c..72995be6f69 100644 --- a/homeassistant/components/smartthings/icons.json +++ b/homeassistant/components/smartthings/icons.json @@ -93,6 +93,14 @@ "stop": "mdi:stop" } }, + "selected_zone": { + "state": { + "all": "mdi:card", + "lower": "mdi:dock-bottom", + "none": "mdi:card-outline", + "upper": "mdi:dock-top" + } + }, "soil_level": { "default": "mdi:liquid-spot" }, @@ -101,6 +109,14 @@ }, "water_temperature": { "default": "mdi:water-thermometer" + }, + "zone_booster": { + "state": { + "all": "mdi:card", + "left": "mdi:dock-left", + "none": "mdi:card-outline", + "right": "mdi:dock-right" + } } }, "sensor": { @@ -139,6 +155,9 @@ } }, "switch": { + "add_rinse": { + "default": "mdi:water-plus" + }, "auto_cycle_link": { "default": "mdi:link-off", "state": { @@ -158,21 +177,42 @@ "on": "mdi:lightbulb-on" } }, + "dry_plus": { + "default": "mdi:heat-wave" + }, + "heated_dry": { + "default": "mdi:heat-wave" + }, + "high_temp_wash": { + "default": "mdi:thermometer-plus" + }, + "hot_air_dry": { + "default": "mdi:heat-wave" + }, "ice_maker": { "default": "mdi:delete-variant" }, "keep_fresh_mode": { "default": "mdi:creation" }, + "multi_tab": { + "default": "mdi:pill-multiple" + }, "power_cool": { "default": "mdi:snowflake-alert" }, "power_freeze": { "default": "mdi:snowflake" }, + "rinse_plus": { + "default": "mdi:water-plus" + }, "sanitize": { "default": "mdi:lotion" }, + "sanitizing_wash": { + "default": "mdi:lotion" + }, "sound_effect": { "default": "mdi:volume-high", "state": { @@ -180,6 +220,15 @@ "on": "mdi:volume-high" } }, + "speed_booster": { + "default": "mdi:fast-forward" + }, + "steam_soak": { + "default": "mdi:weather-windy" + }, + "storm_wash": { + "default": "mdi:weather-dust" + }, "wrinkle_prevent": { "default": "mdi:tumble-dryer", "state": { diff --git a/homeassistant/components/smartthings/select.py b/homeassistant/components/smartthings/select.py index 2f1ec0c982b..063c6b591ac 100644 --- a/homeassistant/components/smartthings/select.py +++ b/homeassistant/components/smartthings/select.py @@ -3,6 +3,7 @@ from __future__ import annotations from dataclasses import dataclass +from typing import cast from pysmartthings import Attribute, Capability, Command, SmartThings @@ -196,6 +197,28 @@ CAPABILITIES_TO_SELECT: dict[Capability | str, SmartThingsSelectDescription] = { value_is_integer=True, ), } +DISHWASHER_WASHING_OPTIONS_TO_SELECT: dict[ + Attribute | str, SmartThingsSelectDescription +] = { + Attribute.SELECTED_ZONE: SmartThingsSelectDescription( + key=Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS, + translation_key="selected_zone", + options_attribute=Attribute.SELECTED_ZONE, + status_attribute=Attribute.SELECTED_ZONE, + command=Command.SET_SELECTED_ZONE, + entity_category=EntityCategory.CONFIG, + requires_remote_control_status=True, + ), + Attribute.ZONE_BOOSTER: SmartThingsSelectDescription( + key=Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS, + translation_key="zone_booster", + options_attribute=Attribute.ZONE_BOOSTER, + status_attribute=Attribute.ZONE_BOOSTER, + command=Command.SET_ZONE_BOOSTER, + entity_category=EntityCategory.CONFIG, + requires_remote_control_status=True, + ), +} async def async_setup_entry( @@ -226,6 +249,24 @@ async def async_setup_entry( ) ) ) + async_add_entities( + SmartThingsDishwasherWashingOptionSelectEntity( + entry_data.client, + device, + DISHWASHER_WASHING_OPTIONS_TO_SELECT[attribute], + ) + for device in entry_data.devices.values() + for component in device.status + if component == MAIN + and Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS in device.status[component] + for attribute in cast( + list[str], + device.status[component][Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS][ + Attribute.SUPPORTED_LIST + ].value, + ) + if attribute in DISHWASHER_WASHING_OPTIONS_TO_SELECT + ) class SmartThingsSelectEntity(SmartThingsEntity, SelectEntity): @@ -239,11 +280,14 @@ class SmartThingsSelectEntity(SmartThingsEntity, SelectEntity): device: FullDevice, entity_description: SmartThingsSelectDescription, component: str, + extra_capabilities: set[Capability] | None = None, ) -> None: """Initialize the instance.""" capabilities = {entity_description.key} if entity_description.requires_remote_control_status: capabilities.add(Capability.REMOTE_CONTROL_STATUS) + if extra_capabilities is not None: + capabilities.update(extra_capabilities) super().__init__(client, device, capabilities, component=component) self.entity_description = entity_description self._attr_unique_id = f"{device.device.device_id}_{component}_{entity_description.key}_{entity_description.status_attribute}_{entity_description.status_attribute}" @@ -279,8 +323,8 @@ class SmartThingsSelectEntity(SmartThingsEntity, SelectEntity): option = str(option) return option - async def async_select_option(self, option: str) -> None: - """Select an option.""" + def _validate_before_select(self) -> None: + """Validate that the select can be used.""" if ( self.entity_description.requires_remote_control_status and self.get_attribute_value( @@ -291,6 +335,10 @@ class SmartThingsSelectEntity(SmartThingsEntity, SelectEntity): raise ServiceValidationError( "Can only be updated when remote control is enabled" ) + + async def async_select_option(self, option: str) -> None: + """Select an option.""" + self._validate_before_select() new_option: str | int = option if self.entity_description.options_map: new_option = next( @@ -308,3 +356,105 @@ class SmartThingsSelectEntity(SmartThingsEntity, SelectEntity): self.entity_description.command, new_option, ) + + +class SmartThingsDishwasherWashingOptionSelectEntity(SmartThingsSelectEntity): + """Define a SmartThings select for a dishwasher washing option.""" + + def __init__( + self, + client: SmartThings, + device: FullDevice, + entity_description: SmartThingsSelectDescription, + ) -> None: + """Initialize the switch.""" + super().__init__( + client, + device, + entity_description, + MAIN, + { + Capability.DISHWASHER_OPERATING_STATE, + Capability.SAMSUNG_CE_DISHWASHER_OPERATION, + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE, + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE_DETAILS, + }, + ) + + @property + def options(self) -> list[str]: + """Return the list of options.""" + device_options = self.get_attribute_value( + self.entity_description.key, self.entity_description.options_attribute + )["settable"] + selected_course = self.get_attribute_value( + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE, Attribute.WASHING_COURSE + ) + course_details = self.get_attribute_value( + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE_DETAILS, + Attribute.PREDEFINED_COURSES, + ) + course_options = set( + next( + ( + detail["options"][self.entity_description.options_attribute][ + "settable" + ] + for detail in course_details + if detail["courseName"] == selected_course + ), + [], + ) + ) + return [option for option in device_options if option in course_options] + + @property + def current_option(self) -> str | None: + """Return the current option.""" + return self.get_attribute_value( + self.entity_description.key, self.entity_description.status_attribute + )["value"] + + def _validate_before_select(self) -> None: + """Validate that the select can be used.""" + super()._validate_before_select() + if ( + self.get_attribute_value( + Capability.DISHWASHER_OPERATING_STATE, Attribute.MACHINE_STATE + ) + != "stop" + ): + raise ServiceValidationError( + "Can only be updated when dishwasher machine state is stop" + ) + + async def async_select_option(self, option: str) -> None: + """Select an option.""" + self._validate_before_select() + selected_course = self.get_attribute_value( + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE, Attribute.WASHING_COURSE + ) + options = { + option: self.get_attribute_value(self.entity_description.key, option)[ + "value" + ] + for option in self.get_attribute_value( + self.entity_description.key, Attribute.SUPPORTED_LIST + ) + } + options[self.entity_description.options_attribute] = option + await self.execute_device_command( + Capability.SAMSUNG_CE_DISHWASHER_OPERATION, + Command.CANCEL, + False, + ) + await self.execute_device_command( + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE, + Command.SET_WASHING_COURSE, + selected_course, + ) + await self.execute_device_command( + self.entity_description.key, + Command.SET_OPTIONS, + options, + ) diff --git a/homeassistant/components/smartthings/strings.json b/homeassistant/components/smartthings/strings.json index 59d76971db8..6a1d69ec866 100644 --- a/homeassistant/components/smartthings/strings.json +++ b/homeassistant/components/smartthings/strings.json @@ -220,6 +220,15 @@ "stop": "[%key:common::state::stopped%]" } }, + "selected_zone": { + "name": "Selected zone", + "state": { + "all": "All", + "lower": "Lower", + "none": "None", + "upper": "Upper" + } + }, "soil_level": { "name": "Soil level", "state": { @@ -284,6 +293,15 @@ "tap_cold": "Tap Cold", "warm": "Warm" } + }, + "zone_booster": { + "name": "Zone booster", + "state": { + "all": "All", + "left": "Left", + "none": "None", + "right": "Right" + } } }, "sensor": { @@ -825,6 +843,9 @@ } }, "switch": { + "add_rinse": { + "name": "Add rinse" + }, "auto_cycle_link": { "name": "Auto Cycle Link" }, @@ -834,6 +855,18 @@ "display_lighting": { "name": "Display lighting" }, + "dry_plus": { + "name": "Dry plus" + }, + "heated_dry": { + "name": "Heated dry" + }, + "high_temp_wash": { + "name": "High temp wash" + }, + "hot_air_dry": { + "name": "Hot air dry" + }, "ice_maker": { "name": "Cubed ice" }, @@ -843,21 +876,39 @@ "keep_fresh_mode": { "name": "Keep fresh mode" }, + "multi_tab": { + "name": "Multi-tab" + }, "power_cool": { "name": "Power cool" }, "power_freeze": { "name": "Power freeze" }, + "rinse_plus": { + "name": "Rinse plus" + }, "sabbath_mode": { "name": "Sabbath mode" }, "sanitize": { "name": "Sanitize" }, + "sanitizing_wash": { + "name": "Sanitizing wash" + }, "sound_effect": { "name": "Sound effect" }, + "speed_booster": { + "name": "Speed booster" + }, + "steam_soak": { + "name": "Steam soak" + }, + "storm_wash": { + "name": "Storm wash" + }, "wrinkle_prevent": { "name": "Wrinkle prevent" } diff --git a/homeassistant/components/smartthings/switch.py b/homeassistant/components/smartthings/switch.py index bc2f2db9110..682d6f80493 100644 --- a/homeassistant/components/smartthings/switch.py +++ b/homeassistant/components/smartthings/switch.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any +from typing import Any, cast from pysmartthings import Attribute, Capability, Command, SmartThings @@ -14,6 +14,7 @@ from homeassistant.components.switch import ( ) from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -48,7 +49,7 @@ class SmartThingsSwitchEntityDescription(SwitchEntityDescription): status_attribute: Attribute component_translation_key: dict[str, str] | None = None - on_key: str = "on" + on_key: str | bool = "on" on_command: Command = Command.ON off_command: Command = Command.OFF @@ -58,6 +59,17 @@ class SmartThingsCommandSwitchEntityDescription(SmartThingsSwitchEntityDescripti """Describe a SmartThings switch entity.""" command: Command + off_key: str | bool = "off" + + +@dataclass(frozen=True, kw_only=True) +class SmartThingsDishwasherWashingOptionSwitchEntityDescription( + SmartThingsCommandSwitchEntityDescription +): + """Describe a SmartThings switch entity for a dishwasher washing option.""" + + on_key: str | bool = True + off_key: str | bool = False SWITCH = SmartThingsSwitchEntityDescription( @@ -151,6 +163,94 @@ CAPABILITY_TO_SWITCHES: dict[Capability | str, SmartThingsSwitchEntityDescriptio entity_category=EntityCategory.CONFIG, ), } +DISHWASHER_WASHING_OPTIONS_TO_SWITCHES: dict[ + Attribute | str, SmartThingsDishwasherWashingOptionSwitchEntityDescription +] = { + Attribute.ADD_RINSE: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.ADD_RINSE, + translation_key="add_rinse", + status_attribute=Attribute.ADD_RINSE, + command=Command.SET_ADD_RINSE, + entity_category=EntityCategory.CONFIG, + ), + Attribute.DRY_PLUS: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.DRY_PLUS, + translation_key="dry_plus", + status_attribute=Attribute.DRY_PLUS, + command=Command.SET_DRY_PLUS, + entity_category=EntityCategory.CONFIG, + ), + Attribute.HEATED_DRY: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.HEATED_DRY, + translation_key="heated_dry", + status_attribute=Attribute.HEATED_DRY, + command=Command.SET_HEATED_DRY, + entity_category=EntityCategory.CONFIG, + ), + Attribute.HIGH_TEMP_WASH: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.HIGH_TEMP_WASH, + translation_key="high_temp_wash", + status_attribute=Attribute.HIGH_TEMP_WASH, + command=Command.SET_HIGH_TEMP_WASH, + entity_category=EntityCategory.CONFIG, + ), + Attribute.HOT_AIR_DRY: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.HOT_AIR_DRY, + translation_key="hot_air_dry", + status_attribute=Attribute.HOT_AIR_DRY, + command=Command.SET_HOT_AIR_DRY, + entity_category=EntityCategory.CONFIG, + ), + Attribute.MULTI_TAB: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.MULTI_TAB, + translation_key="multi_tab", + status_attribute=Attribute.MULTI_TAB, + command=Command.SET_MULTI_TAB, + entity_category=EntityCategory.CONFIG, + ), + Attribute.RINSE_PLUS: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.RINSE_PLUS, + translation_key="rinse_plus", + status_attribute=Attribute.RINSE_PLUS, + command=Command.SET_RINSE_PLUS, + entity_category=EntityCategory.CONFIG, + ), + Attribute.SANITIZE: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.SANITIZE, + translation_key="sanitize", + status_attribute=Attribute.SANITIZE, + command=Command.SET_SANITIZE, + entity_category=EntityCategory.CONFIG, + ), + Attribute.SANITIZING_WASH: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.SANITIZING_WASH, + translation_key="sanitizing_wash", + status_attribute=Attribute.SANITIZING_WASH, + command=Command.SET_SANITIZING_WASH, + entity_category=EntityCategory.CONFIG, + ), + Attribute.SPEED_BOOSTER: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.SPEED_BOOSTER, + translation_key="speed_booster", + status_attribute=Attribute.SPEED_BOOSTER, + command=Command.SET_SPEED_BOOSTER, + entity_category=EntityCategory.CONFIG, + ), + Attribute.STEAM_SOAK: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.STEAM_SOAK, + translation_key="steam_soak", + status_attribute=Attribute.STEAM_SOAK, + command=Command.SET_STEAM_SOAK, + entity_category=EntityCategory.CONFIG, + ), + Attribute.STORM_WASH: SmartThingsDishwasherWashingOptionSwitchEntityDescription( + key=Attribute.STORM_WASH, + translation_key="storm_wash", + status_attribute=Attribute.STORM_WASH, + command=Command.SET_STORM_WASH, + entity_category=EntityCategory.CONFIG, + ), +} async def async_setup_entry( @@ -191,6 +291,24 @@ async def async_setup_entry( ) ) ) + entities.extend( + SmartThingsDishwasherWashingOptionSwitch( + entry_data.client, + device, + DISHWASHER_WASHING_OPTIONS_TO_SWITCHES[attribute], + ) + for device in entry_data.devices.values() + for component in device.status + if component == MAIN + and Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS in device.status[component] + for attribute in cast( + list[str], + device.status[component][Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS][ + Attribute.SUPPORTED_LIST + ].value, + ) + if attribute in DISHWASHER_WASHING_OPTIONS_TO_SWITCHES + ) entity_registry = er.async_get(hass) for device in entry_data.devices.values(): if ( @@ -261,9 +379,13 @@ class SmartThingsSwitch(SmartThingsEntity, SwitchEntity): entity_description: SmartThingsSwitchEntityDescription, capability: Capability, component: str = MAIN, + extra_capabilities: set[Capability] | None = None, ) -> None: """Initialize the switch.""" - super().__init__(client, device, {capability}, component=component) + extra_capabilities = set() if extra_capabilities is None else extra_capabilities + super().__init__( + client, device, {capability} | extra_capabilities, component=component + ) self.entity_description = entity_description self.switch_capability = capability self._attr_unique_id = f"{device.device.device_id}_{component}_{capability}_{entity_description.status_attribute}_{entity_description.status_attribute}" @@ -288,15 +410,15 @@ class SmartThingsSwitch(SmartThingsEntity, SwitchEntity): self.entity_description.on_command, ) + def _current_state(self) -> Any: + return self.get_attribute_value( + self.switch_capability, self.entity_description.status_attribute + ) + @property def is_on(self) -> bool: """Return true if switch is on.""" - return ( - self.get_attribute_value( - self.switch_capability, self.entity_description.status_attribute - ) - == self.entity_description.on_key - ) + return self._current_state() == self.entity_description.on_key class SmartThingsCommandSwitch(SmartThingsSwitch): @@ -309,7 +431,7 @@ class SmartThingsCommandSwitch(SmartThingsSwitch): await self.execute_device_command( self.switch_capability, self.entity_description.command, - "off", + self.entity_description.off_key, ) async def async_turn_on(self, **kwargs: Any) -> None: @@ -317,5 +439,81 @@ class SmartThingsCommandSwitch(SmartThingsSwitch): await self.execute_device_command( self.switch_capability, self.entity_description.command, - "on", + self.entity_description.on_key, ) + + +class SmartThingsDishwasherWashingOptionSwitch(SmartThingsCommandSwitch): + """Define a SmartThings dishwasher washing option switch.""" + + def __init__( + self, + client: SmartThings, + device: FullDevice, + entity_description: SmartThingsSwitchEntityDescription, + ) -> None: + """Initialize the switch.""" + super().__init__( + client, + device, + entity_description, + Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS, + MAIN, + { + Capability.REMOTE_CONTROL_STATUS, + Capability.DISHWASHER_OPERATING_STATE, + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE, + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE_DETAILS, + }, + ) + + def _validate_before_execute(self) -> None: + """Validate that the switch command can be executed.""" + if ( + self.get_attribute_value( + Capability.REMOTE_CONTROL_STATUS, Attribute.REMOTE_CONTROL_ENABLED + ) + == "false" + ): + raise ServiceValidationError( + "Can only be updated when remote control is enabled" + ) + if ( + self.get_attribute_value( + Capability.DISHWASHER_OPERATING_STATE, Attribute.MACHINE_STATE + ) + != "stop" + ): + raise ServiceValidationError( + "Can only be updated when dishwasher machine state is stop" + ) + selected_course = self.get_attribute_value( + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE, Attribute.WASHING_COURSE + ) + course_details = self.get_attribute_value( + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE_DETAILS, + Attribute.PREDEFINED_COURSES, + ) + course_settable: list[bool] = next( + ( + detail["options"][self.entity_description.status_attribute]["settable"] + for detail in course_details + if detail["courseName"] == selected_course + ), + [], + ) + if not course_settable: + raise ServiceValidationError("Option is not supported by selected cycle") + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the switch off.""" + self._validate_before_execute() + await super().async_turn_off(**kwargs) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the switch on.""" + self._validate_before_execute() + await super().async_turn_on(**kwargs) + + def _current_state(self) -> Any: + return super()._current_state()["value"] diff --git a/tests/components/smartthings/snapshots/test_select.ambr b/tests/components/smartthings/snapshots/test_select.ambr index e5ac4227f5f..da6b983af71 100644 --- a/tests/components/smartthings/snapshots/test_select.ambr +++ b/tests/components/smartthings/snapshots/test_select.ambr @@ -533,6 +533,124 @@ 'state': 'stop', }) # --- +# name: test_all_entities[da_wm_dw_000001][select.dishwasher_selected_zone-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'none', + 'lower', + 'all', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.dishwasher_selected_zone', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Selected zone', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Selected zone', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'selected_zone', + 'unique_id': 'f36dc7ce-cac0-0667-dc14-a3704eb5e676_main_samsungce.dishwasherWashingOptions_selectedZone_selectedZone', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[da_wm_dw_000001][select.dishwasher_selected_zone-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Dishwasher Selected zone', + 'options': list([ + 'none', + 'lower', + 'all', + ]), + }), + 'context': , + 'entity_id': 'select.dishwasher_selected_zone', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'all', + }) +# --- +# name: test_all_entities[da_wm_dw_000001][select.dishwasher_zone_booster-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'none', + 'left', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.dishwasher_zone_booster', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Zone booster', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Zone booster', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'zone_booster', + 'unique_id': 'f36dc7ce-cac0-0667-dc14-a3704eb5e676_main_samsungce.dishwasherWashingOptions_zoneBooster_zoneBooster', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[da_wm_dw_000001][select.dishwasher_zone_booster-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Dishwasher Zone booster', + 'options': list([ + 'none', + 'left', + ]), + }), + 'context': , + 'entity_id': 'select.dishwasher_zone_booster', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'none', + }) +# --- # name: test_all_entities[da_wm_dw_01011][select.dishwasher-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -593,6 +711,64 @@ 'state': 'stop', }) # --- +# name: test_all_entities[da_wm_dw_01011][select.dishwasher_selected_zone-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'lower', + 'all', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.dishwasher_selected_zone', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Selected zone', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Selected zone', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'selected_zone', + 'unique_id': '7ff318f3-3772-524d-3c9f-72fcd26413ed_main_samsungce.dishwasherWashingOptions_selectedZone_selectedZone', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[da_wm_dw_01011][select.dishwasher_selected_zone-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Dishwasher Selected zone', + 'options': list([ + 'lower', + 'all', + ]), + }), + 'context': , + 'entity_id': 'select.dishwasher_selected_zone', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'all', + }) +# --- # name: test_all_entities[da_wm_sc_000001][select.airdresser-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/smartthings/snapshots/test_switch.ambr b/tests/components/smartthings/snapshots/test_switch.ambr index 73fb7dd542b..d9ccafd5556 100644 --- a/tests/components/smartthings/snapshots/test_switch.ambr +++ b/tests/components/smartthings/snapshots/test_switch.ambr @@ -1028,6 +1028,251 @@ 'state': 'off', }) # --- +# name: test_all_entities[da_wm_dw_000001][switch.dishwasher_high_temp_wash-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.dishwasher_high_temp_wash', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'High temp wash', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'High temp wash', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'high_temp_wash', + 'unique_id': 'f36dc7ce-cac0-0667-dc14-a3704eb5e676_main_samsungce.dishwasherWashingOptions_highTempWash_highTempWash', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[da_wm_dw_000001][switch.dishwasher_high_temp_wash-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Dishwasher High temp wash', + }), + 'context': , + 'entity_id': 'switch.dishwasher_high_temp_wash', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[da_wm_dw_000001][switch.dishwasher_sanitize-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.dishwasher_sanitize', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Sanitize', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Sanitize', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'sanitize', + 'unique_id': 'f36dc7ce-cac0-0667-dc14-a3704eb5e676_main_samsungce.dishwasherWashingOptions_sanitize_sanitize', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[da_wm_dw_000001][switch.dishwasher_sanitize-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Dishwasher Sanitize', + }), + 'context': , + 'entity_id': 'switch.dishwasher_sanitize', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[da_wm_dw_000001][switch.dishwasher_speed_booster-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.dishwasher_speed_booster', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Speed booster', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Speed booster', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'speed_booster', + 'unique_id': 'f36dc7ce-cac0-0667-dc14-a3704eb5e676_main_samsungce.dishwasherWashingOptions_speedBooster_speedBooster', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[da_wm_dw_000001][switch.dishwasher_speed_booster-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Dishwasher Speed booster', + }), + 'context': , + 'entity_id': 'switch.dishwasher_speed_booster', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[da_wm_dw_01011][switch.dishwasher_sanitize-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.dishwasher_sanitize', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Sanitize', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Sanitize', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'sanitize', + 'unique_id': '7ff318f3-3772-524d-3c9f-72fcd26413ed_main_samsungce.dishwasherWashingOptions_sanitize_sanitize', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[da_wm_dw_01011][switch.dishwasher_sanitize-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Dishwasher Sanitize', + }), + 'context': , + 'entity_id': 'switch.dishwasher_sanitize', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[da_wm_dw_01011][switch.dishwasher_speed_booster-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.dishwasher_speed_booster', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Speed booster', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Speed booster', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'speed_booster', + 'unique_id': '7ff318f3-3772-524d-3c9f-72fcd26413ed_main_samsungce.dishwasherWashingOptions_speedBooster_speedBooster', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[da_wm_dw_01011][switch.dishwasher_speed_booster-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Dishwasher Speed booster', + }), + 'context': , + 'entity_id': 'switch.dishwasher_speed_booster', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- # name: test_all_entities[da_wm_sc_000001][switch.airdresser_auto_cycle_link-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/smartthings/test_select.py b/tests/components/smartthings/test_select.py index 0095edc786c..65af53a216b 100644 --- a/tests/components/smartthings/test_select.py +++ b/tests/components/smartthings/test_select.py @@ -1,6 +1,6 @@ """Test for the SmartThings select platform.""" -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, call from pysmartthings import Attribute, Capability, Command from pysmartthings.models import HealthStatus @@ -220,3 +220,90 @@ async def test_select_option_as_integer( MAIN, argument=300, ) + + +@pytest.mark.parametrize("device_fixture", ["da_wm_dw_01011"]) +async def test_select_option_with_wrong_dishwasher_machine_state( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test state update.""" + set_attribute_value( + devices, + Capability.REMOTE_CONTROL_STATUS, + Attribute.REMOTE_CONTROL_ENABLED, + "true", + ) + set_attribute_value( + devices, + Capability.DISHWASHER_OPERATING_STATE, + Attribute.MACHINE_STATE, + "run", + ) + await setup_integration(hass, mock_config_entry) + + with pytest.raises( + ServiceValidationError, + match="Can only be updated when dishwasher machine state is stop", + ): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + {ATTR_ENTITY_ID: "select.dishwasher_selected_zone", ATTR_OPTION: "lower"}, + blocking=True, + ) + devices.execute_device_command.assert_not_called() + + +@pytest.mark.parametrize("device_fixture", ["da_wm_dw_01011"]) +async def test_select_dishwasher_washing_option( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test state update.""" + set_attribute_value( + devices, + Capability.REMOTE_CONTROL_STATUS, + Attribute.REMOTE_CONTROL_ENABLED, + "true", + ) + await setup_integration(hass, mock_config_entry) + + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + {ATTR_ENTITY_ID: "select.dishwasher_selected_zone", ATTR_OPTION: "lower"}, + blocking=True, + ) + device_id = "7ff318f3-3772-524d-3c9f-72fcd26413ed" + devices.execute_device_command.assert_has_calls( + [ + call( + device_id, + Capability.SAMSUNG_CE_DISHWASHER_OPERATION, + Command.CANCEL, + MAIN, + argument=False, + ), + call( + device_id, + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE, + Command.SET_WASHING_COURSE, + MAIN, + argument="eco", + ), + call( + device_id, + Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS, + Command.SET_OPTIONS, + MAIN, + argument={ + "selectedZone": "lower", + "speedBooster": False, + "sanitize": False, + }, + ), + ] + ) diff --git a/tests/components/smartthings/test_switch.py b/tests/components/smartthings/test_switch.py index 524e5988de6..8a54b0961f9 100644 --- a/tests/components/smartthings/test_switch.py +++ b/tests/components/smartthings/test_switch.py @@ -22,10 +22,12 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers import entity_registry as er, issue_registry as ir from homeassistant.setup import async_setup_component from . import ( + set_attribute_value, setup_integration, snapshot_smartthings_entities, trigger_health_update, @@ -110,6 +112,45 @@ async def test_command_switch_turn_on_off( ) +@pytest.mark.parametrize("device_fixture", ["da_wm_dw_01011"]) +@pytest.mark.parametrize( + ("action", "argument"), + [ + (SERVICE_TURN_ON, True), + (SERVICE_TURN_OFF, False), + ], +) +async def test_dishwasher_washing_option_switch_turn_on_off( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, + action: str, + argument: str, +) -> None: + """Test switch turn on and off command.""" + set_attribute_value( + devices, + Capability.REMOTE_CONTROL_STATUS, + Attribute.REMOTE_CONTROL_ENABLED, + "true", + ) + await setup_integration(hass, mock_config_entry) + + await hass.services.async_call( + SWITCH_DOMAIN, + action, + {ATTR_ENTITY_ID: "switch.dishwasher_speed_booster"}, + blocking=True, + ) + devices.execute_device_command.assert_called_once_with( + "7ff318f3-3772-524d-3c9f-72fcd26413ed", + Capability.SAMSUNG_CE_DISHWASHER_WASHING_OPTIONS, + Command.SET_SPEED_BOOSTER, + MAIN, + argument, + ) + + @pytest.mark.parametrize("device_fixture", ["da_ref_normal_000001"]) @pytest.mark.parametrize( ("action", "command"), @@ -465,3 +506,99 @@ async def test_availability_at_start( """Test unavailable at boot.""" await setup_integration(hass, mock_config_entry) assert hass.states.get("switch.2nd_floor_hallway").state == STATE_UNAVAILABLE + + +@pytest.mark.parametrize("device_fixture", ["da_wm_dw_01011"]) +async def test_turn_on_without_remote_control( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test state update.""" + set_attribute_value( + devices, + Capability.REMOTE_CONTROL_STATUS, + Attribute.REMOTE_CONTROL_ENABLED, + "false", + ) + await setup_integration(hass, mock_config_entry) + + with pytest.raises( + ServiceValidationError, + match="Can only be updated when remote control is enabled", + ): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.dishwasher_speed_booster"}, + blocking=True, + ) + devices.execute_device_command.assert_not_called() + + +@pytest.mark.parametrize("device_fixture", ["da_wm_dw_01011"]) +async def test_turn_on_with_wrong_dishwasher_machine_state( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test state update.""" + set_attribute_value( + devices, + Capability.REMOTE_CONTROL_STATUS, + Attribute.REMOTE_CONTROL_ENABLED, + "true", + ) + set_attribute_value( + devices, + Capability.DISHWASHER_OPERATING_STATE, + Attribute.MACHINE_STATE, + "run", + ) + await setup_integration(hass, mock_config_entry) + + with pytest.raises( + ServiceValidationError, + match="Can only be updated when dishwasher machine state is stop", + ): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.dishwasher_speed_booster"}, + blocking=True, + ) + devices.execute_device_command.assert_not_called() + + +@pytest.mark.parametrize("device_fixture", ["da_wm_dw_01011"]) +async def test_turn_on_with_wrong_dishwasher_cycle( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test state update.""" + set_attribute_value( + devices, + Capability.REMOTE_CONTROL_STATUS, + Attribute.REMOTE_CONTROL_ENABLED, + "true", + ) + set_attribute_value( + devices, + Capability.SAMSUNG_CE_DISHWASHER_WASHING_COURSE, + Attribute.WASHING_COURSE, + "preWash", + ) + await setup_integration(hass, mock_config_entry) + + with pytest.raises( + ServiceValidationError, + match="Option is not supported by selected cycle", + ): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.dishwasher_speed_booster"}, + blocking=True, + ) + devices.execute_device_command.assert_not_called()