1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Add switch controls for dishwashers in SmartThings (#160266)

Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
Eduardo Tsen
2026-02-12 22:57:54 +01:00
committed by GitHub
parent 0a79d84f9a
commit 40ec6d3793
8 changed files with 1107 additions and 14 deletions

View File

@@ -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": {

View File

@@ -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,
)

View File

@@ -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"
}

View File

@@ -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"]

View File

@@ -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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'select.dishwasher_selected_zone',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'select.dishwasher_selected_zone',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'select.dishwasher_zone_booster',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'select.dishwasher_zone_booster',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'select.dishwasher_selected_zone',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'select.dishwasher_selected_zone',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'all',
})
# ---
# name: test_all_entities[da_wm_sc_000001][select.airdresser-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@@ -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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.dishwasher_high_temp_wash',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.dishwasher_high_temp_wash',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.dishwasher_sanitize',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.dishwasher_sanitize',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.dishwasher_speed_booster',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.dishwasher_speed_booster',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.dishwasher_sanitize',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.dishwasher_sanitize',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.dishwasher_speed_booster',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'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': <ANY>,
'entity_id': 'switch.dishwasher_speed_booster',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_all_entities[da_wm_sc_000001][switch.airdresser_auto_cycle_link-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@@ -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,
},
),
]
)

View File

@@ -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()