From ae21ab29457ec876b52f1b68fecebec3eef14d74 Mon Sep 17 00:00:00 2001 From: Denis Shulyaka Date: Wed, 28 Jun 2023 14:21:17 +0300 Subject: [PATCH] Add `action` attribute to Humidifier entity (#95131) * Add HumidifierAction StrEnum * Add action attribute to HumidifierEntity * Update strings.json * Add action to demo humidifier * Add tests * Fix imports * Add 'off' humidifier action * Set action to 'off' when state is 'off' * Add 'off' action to strings.json * Test that action sets to "off" when state is "off" * Use is_on instead of state * Fix typo * black --- homeassistant/components/demo/humidifier.py | 5 +++++ homeassistant/components/humidifier/__init__.py | 11 +++++++++++ homeassistant/components/humidifier/const.py | 13 +++++++++++++ homeassistant/components/humidifier/strings.json | 9 +++++++++ tests/components/demo/test_humidifier.py | 6 ++++++ 5 files changed, 44 insertions(+) diff --git a/homeassistant/components/demo/humidifier.py b/homeassistant/components/demo/humidifier.py index 75c2cf3120a..2e16a04e171 100644 --- a/homeassistant/components/demo/humidifier.py +++ b/homeassistant/components/demo/humidifier.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Any from homeassistant.components.humidifier import ( + HumidifierAction, HumidifierDeviceClass, HumidifierEntity, HumidifierEntityFeature, @@ -30,6 +31,7 @@ async def async_setup_platform( mode=None, target_humidity=68, current_humidity=45, + action=HumidifierAction.HUMIDIFYING, device_class=HumidifierDeviceClass.HUMIDIFIER, ), DemoHumidifier( @@ -37,6 +39,7 @@ async def async_setup_platform( mode=None, target_humidity=54, current_humidity=59, + action=HumidifierAction.DRYING, device_class=HumidifierDeviceClass.DEHUMIDIFIER, ), DemoHumidifier( @@ -71,11 +74,13 @@ class DemoHumidifier(HumidifierEntity): current_humidity: int | None = None, available_modes: list[str] | None = None, is_on: bool = True, + action: HumidifierAction | None = None, device_class: HumidifierDeviceClass | None = None, ) -> None: """Initialize the humidifier device.""" self._attr_name = name self._attr_is_on = is_on + self._attr_action = action self._attr_supported_features = SUPPORT_FLAGS if mode is not None: self._attr_supported_features |= HumidifierEntityFeature.MODES diff --git a/homeassistant/components/humidifier/__init__.py b/homeassistant/components/humidifier/__init__.py index 49c3f4681a8..79effa6f0c2 100644 --- a/homeassistant/components/humidifier/__init__.py +++ b/homeassistant/components/humidifier/__init__.py @@ -29,6 +29,7 @@ from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass from .const import ( # noqa: F401 + ATTR_ACTION, ATTR_AVAILABLE_MODES, ATTR_CURRENT_HUMIDITY, ATTR_HUMIDITY, @@ -45,6 +46,7 @@ from .const import ( # noqa: F401 SERVICE_SET_HUMIDITY, SERVICE_SET_MODE, SUPPORT_MODES, + HumidifierAction, HumidifierEntityFeature, ) @@ -133,6 +135,7 @@ class HumidifierEntity(ToggleEntity): """Base class for humidifier entities.""" entity_description: HumidifierEntityDescription + _attr_action: HumidifierAction | None = None _attr_available_modes: list[str] | None _attr_current_humidity: int | None = None _attr_device_class: HumidifierDeviceClass | None @@ -170,6 +173,9 @@ class HumidifierEntity(ToggleEntity): """Return the optional state attributes.""" data: dict[str, int | str | None] = {} + if self.action is not None: + data[ATTR_ACTION] = self.action if self.is_on else HumidifierAction.OFF + if self.current_humidity is not None: data[ATTR_CURRENT_HUMIDITY] = self.current_humidity @@ -181,6 +187,11 @@ class HumidifierEntity(ToggleEntity): return data + @property + def action(self) -> HumidifierAction | None: + """Return the current action.""" + return self._attr_action + @property def current_humidity(self) -> int | None: """Return the current humidity.""" diff --git a/homeassistant/components/humidifier/const.py b/homeassistant/components/humidifier/const.py index 27e181fe63c..35601cf2b1f 100644 --- a/homeassistant/components/humidifier/const.py +++ b/homeassistant/components/humidifier/const.py @@ -1,6 +1,8 @@ """Provides the constants needed for component.""" from enum import IntFlag +from homeassistant.backports.enum import StrEnum + MODE_NORMAL = "normal" MODE_ECO = "eco" MODE_AWAY = "away" @@ -11,6 +13,17 @@ MODE_SLEEP = "sleep" MODE_AUTO = "auto" MODE_BABY = "baby" + +class HumidifierAction(StrEnum): + """Actions for humidifier devices.""" + + HUMIDIFYING = "humidifying" + DRYING = "drying" + IDLE = "idle" + OFF = "off" + + +ATTR_ACTION = "action" ATTR_AVAILABLE_MODES = "available_modes" ATTR_CURRENT_HUMIDITY = "current_humidity" ATTR_HUMIDITY = "humidity" diff --git a/homeassistant/components/humidifier/strings.json b/homeassistant/components/humidifier/strings.json index e4f5961a3cf..3b4c0bf2dab 100644 --- a/homeassistant/components/humidifier/strings.json +++ b/homeassistant/components/humidifier/strings.json @@ -28,6 +28,15 @@ "on": "[%key:common::state::on%]" }, "state_attributes": { + "action": { + "name": "Action", + "state": { + "humidifying": "Humidifying", + "drying": "Drying", + "idle": "Idle", + "off": "Off" + } + }, "available_modes": { "name": "Available modes" }, diff --git a/tests/components/demo/test_humidifier.py b/tests/components/demo/test_humidifier.py index 501362d6533..13aeb5724b9 100644 --- a/tests/components/demo/test_humidifier.py +++ b/tests/components/demo/test_humidifier.py @@ -4,6 +4,7 @@ import pytest import voluptuous as vol from homeassistant.components.humidifier import ( + ATTR_ACTION, ATTR_CURRENT_HUMIDITY, ATTR_HUMIDITY, ATTR_MAX_HUMIDITY, @@ -45,6 +46,7 @@ def test_setup_params(hass: HomeAssistant) -> None: assert state.state == STATE_ON assert state.attributes.get(ATTR_HUMIDITY) == 54 assert state.attributes.get(ATTR_CURRENT_HUMIDITY) == 59 + assert state.attributes.get(ATTR_ACTION) == "drying" def test_default_setup_params(hass: HomeAssistant) -> None: @@ -124,12 +126,14 @@ async def test_turn_on(hass: HomeAssistant) -> None: ) state = hass.states.get(ENTITY_DEHUMIDIFIER) assert state.state == STATE_OFF + assert state.attributes.get(ATTR_ACTION) == "off" await hass.services.async_call( DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_DEHUMIDIFIER}, blocking=True ) state = hass.states.get(ENTITY_DEHUMIDIFIER) assert state.state == STATE_ON + assert state.attributes.get(ATTR_ACTION) == "drying" async def test_turn_off(hass: HomeAssistant) -> None: @@ -139,12 +143,14 @@ async def test_turn_off(hass: HomeAssistant) -> None: ) state = hass.states.get(ENTITY_DEHUMIDIFIER) assert state.state == STATE_ON + assert state.attributes.get(ATTR_ACTION) == "drying" await hass.services.async_call( DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_DEHUMIDIFIER}, blocking=True ) state = hass.states.get(ENTITY_DEHUMIDIFIER) assert state.state == STATE_OFF + assert state.attributes.get(ATTR_ACTION) == "off" async def test_toggle(hass: HomeAssistant) -> None: