mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add Dryer Wrinkle Prevent switch to SmartThings (#141085)
* Add Dryer Wrinkle Prevent switch to SmartThings * Fix
This commit is contained in:
parent
4b4d75063c
commit
c56b087d0c
@ -27,6 +27,14 @@
|
|||||||
"stop": "mdi:stop"
|
"stop": "mdi:stop"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"wrinkle_prevent": {
|
||||||
|
"default": "mdi:tumble-dryer",
|
||||||
|
"state": {
|
||||||
|
"off": "mdi:tumble-dryer-off"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,6 +442,11 @@
|
|||||||
"freeze_protection": "Freeze protection"
|
"freeze_protection": "Freeze protection"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"wrinkle_prevent": {
|
||||||
|
"name": "Wrinkle prevent"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"issues": {
|
"issues": {
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pysmartthings import Attribute, Capability, Command
|
from pysmartthings import Attribute, Capability, Command, SmartThings
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity
|
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
|
|
||||||
from . import SmartThingsConfigEntry
|
from . import FullDevice, SmartThingsConfigEntry
|
||||||
from .const import MAIN
|
from .const import MAIN
|
||||||
from .entity import SmartThingsEntity
|
from .entity import SmartThingsEntity
|
||||||
|
|
||||||
@ -29,6 +30,37 @@ AC_CAPABILITIES = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class SmartThingsSwitchEntityDescription(SwitchEntityDescription):
|
||||||
|
"""Describe a SmartThings switch entity."""
|
||||||
|
|
||||||
|
status_attribute: Attribute
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class SmartThingsCommandSwitchEntityDescription(SmartThingsSwitchEntityDescription):
|
||||||
|
"""Describe a SmartThings switch entity."""
|
||||||
|
|
||||||
|
command: Command
|
||||||
|
|
||||||
|
|
||||||
|
SWITCH = SmartThingsSwitchEntityDescription(
|
||||||
|
key=Capability.SWITCH,
|
||||||
|
status_attribute=Attribute.SWITCH,
|
||||||
|
name=None,
|
||||||
|
)
|
||||||
|
CAPABILITY_TO_COMMAND_SWITCHES: dict[
|
||||||
|
Capability | str, SmartThingsCommandSwitchEntityDescription
|
||||||
|
] = {
|
||||||
|
Capability.CUSTOM_DRYER_WRINKLE_PREVENT: SmartThingsCommandSwitchEntityDescription(
|
||||||
|
key=Capability.CUSTOM_DRYER_WRINKLE_PREVENT,
|
||||||
|
translation_key="wrinkle_prevent",
|
||||||
|
status_attribute=Attribute.DRYER_WRINKLE_PREVENT,
|
||||||
|
command=Command.SET_DRYER_WRINKLE_PREVENT,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: SmartThingsConfigEntry,
|
entry: SmartThingsConfigEntry,
|
||||||
@ -36,35 +68,89 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Add switches for a config entry."""
|
"""Add switches for a config entry."""
|
||||||
entry_data = entry.runtime_data
|
entry_data = entry.runtime_data
|
||||||
async_add_entities(
|
entities: list[SmartThingsEntity] = [
|
||||||
SmartThingsSwitch(entry_data.client, device, {Capability.SWITCH})
|
SmartThingsSwitch(entry_data.client, device, SWITCH, Capability.SWITCH)
|
||||||
for device in entry_data.devices.values()
|
for device in entry_data.devices.values()
|
||||||
if Capability.SWITCH in device.status[MAIN]
|
if Capability.SWITCH in device.status[MAIN]
|
||||||
and not any(capability in device.status[MAIN] for capability in CAPABILITIES)
|
and not any(capability in device.status[MAIN] for capability in CAPABILITIES)
|
||||||
and not all(capability in device.status[MAIN] for capability in AC_CAPABILITIES)
|
and not all(capability in device.status[MAIN] for capability in AC_CAPABILITIES)
|
||||||
|
]
|
||||||
|
entities.extend(
|
||||||
|
SmartThingsCommandSwitch(
|
||||||
|
entry_data.client,
|
||||||
|
device,
|
||||||
|
description,
|
||||||
|
Capability(capability),
|
||||||
|
)
|
||||||
|
for device in entry_data.devices.values()
|
||||||
|
for capability, description in CAPABILITY_TO_COMMAND_SWITCHES.items()
|
||||||
|
if capability in device.status[MAIN]
|
||||||
)
|
)
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class SmartThingsSwitch(SmartThingsEntity, SwitchEntity):
|
class SmartThingsSwitch(SmartThingsEntity, SwitchEntity):
|
||||||
"""Define a SmartThings switch."""
|
"""Define a SmartThings switch."""
|
||||||
|
|
||||||
_attr_name = None
|
entity_description: SmartThingsSwitchEntityDescription
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
client: SmartThings,
|
||||||
|
device: FullDevice,
|
||||||
|
entity_description: SmartThingsSwitchEntityDescription,
|
||||||
|
capability: Capability,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the switch."""
|
||||||
|
super().__init__(client, device, {capability})
|
||||||
|
self.entity_description = entity_description
|
||||||
|
self.switch_capability = capability
|
||||||
|
self._attr_unique_id = device.device.device_id
|
||||||
|
if capability is not Capability.SWITCH:
|
||||||
|
self._attr_unique_id = f"{device.device.device_id}_{MAIN}_{capability}"
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch off."""
|
"""Turn the switch off."""
|
||||||
await self.execute_device_command(
|
await self.execute_device_command(
|
||||||
Capability.SWITCH,
|
self.switch_capability,
|
||||||
Command.OFF,
|
Command.OFF,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch on."""
|
"""Turn the switch on."""
|
||||||
await self.execute_device_command(
|
await self.execute_device_command(
|
||||||
Capability.SWITCH,
|
self.switch_capability,
|
||||||
Command.ON,
|
Command.ON,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return true if light is on."""
|
"""Return true if switch is on."""
|
||||||
return self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "on"
|
return (
|
||||||
|
self.get_attribute_value(
|
||||||
|
self.switch_capability, self.entity_description.status_attribute
|
||||||
|
)
|
||||||
|
== "on"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SmartThingsCommandSwitch(SmartThingsSwitch):
|
||||||
|
"""Define a SmartThings command switch."""
|
||||||
|
|
||||||
|
entity_description: SmartThingsCommandSwitchEntityDescription
|
||||||
|
|
||||||
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn the switch off."""
|
||||||
|
await self.execute_device_command(
|
||||||
|
self.switch_capability,
|
||||||
|
self.entity_description.command,
|
||||||
|
"off",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn the switch on."""
|
||||||
|
await self.execute_device_command(
|
||||||
|
self.switch_capability,
|
||||||
|
self.entity_description.command,
|
||||||
|
"on",
|
||||||
|
)
|
||||||
|
@ -281,6 +281,53 @@
|
|||||||
'state': 'off',
|
'state': 'off',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_all_entities[da_wm_wd_000001][switch.dryer_wrinkle_prevent-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': None,
|
||||||
|
'entity_id': 'switch.dryer_wrinkle_prevent',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Wrinkle prevent',
|
||||||
|
'platform': 'smartthings',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'wrinkle_prevent',
|
||||||
|
'unique_id': '02f7256e-8353-5bdd-547f-bd5b1647e01b_main_custom.dryerWrinklePrevent',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[da_wm_wd_000001][switch.dryer_wrinkle_prevent-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Dryer Wrinkle prevent',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.dryer_wrinkle_prevent',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_all_entities[da_wm_wd_000001_1][switch.seca_roupa-entry]
|
# name: test_all_entities[da_wm_wd_000001_1][switch.seca_roupa-entry]
|
||||||
EntityRegistryEntrySnapshot({
|
EntityRegistryEntrySnapshot({
|
||||||
'aliases': set({
|
'aliases': set({
|
||||||
@ -328,6 +375,53 @@
|
|||||||
'state': 'off',
|
'state': 'off',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_all_entities[da_wm_wd_000001_1][switch.seca_roupa_wrinkle_prevent-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': None,
|
||||||
|
'entity_id': 'switch.seca_roupa_wrinkle_prevent',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Wrinkle prevent',
|
||||||
|
'platform': 'smartthings',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'wrinkle_prevent',
|
||||||
|
'unique_id': '3a6c4e05-811d-5041-e956-3d04c424cbcd_main_custom.dryerWrinklePrevent',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[da_wm_wd_000001_1][switch.seca_roupa_wrinkle_prevent-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Seca-Roupa Wrinkle prevent',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.seca_roupa_wrinkle_prevent',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_all_entities[da_wm_wm_000001][switch.washer-entry]
|
# name: test_all_entities[da_wm_wm_000001][switch.washer-entry]
|
||||||
EntityRegistryEntrySnapshot({
|
EntityRegistryEntrySnapshot({
|
||||||
'aliases': set({
|
'aliases': set({
|
||||||
|
@ -66,6 +66,39 @@ async def test_switch_turn_on_off(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("device_fixture", ["da_wm_wd_000001"])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("action", "argument"),
|
||||||
|
[
|
||||||
|
(SERVICE_TURN_ON, "on"),
|
||||||
|
(SERVICE_TURN_OFF, "off"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_command_switch_turn_on_off(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
devices: AsyncMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
action: str,
|
||||||
|
argument: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test switch turn on and off command."""
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
action,
|
||||||
|
{ATTR_ENTITY_ID: "switch.dryer_wrinkle_prevent"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
devices.execute_device_command.assert_called_once_with(
|
||||||
|
"02f7256e-8353-5bdd-547f-bd5b1647e01b",
|
||||||
|
Capability.CUSTOM_DRYER_WRINKLE_PREVENT,
|
||||||
|
Command.SET_DRYER_WRINKLE_PREVENT,
|
||||||
|
MAIN,
|
||||||
|
argument,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("device_fixture", ["c2c_arlo_pro_3_switch"])
|
@pytest.mark.parametrize("device_fixture", ["c2c_arlo_pro_3_switch"])
|
||||||
async def test_state_update(
|
async def test_state_update(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user