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
This commit is contained in:
Denis Shulyaka 2023-06-28 14:21:17 +03:00 committed by GitHub
parent 8b6ed9c6b9
commit ae21ab2945
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 0 deletions

View file

@ -4,6 +4,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from homeassistant.components.humidifier import ( from homeassistant.components.humidifier import (
HumidifierAction,
HumidifierDeviceClass, HumidifierDeviceClass,
HumidifierEntity, HumidifierEntity,
HumidifierEntityFeature, HumidifierEntityFeature,
@ -30,6 +31,7 @@ async def async_setup_platform(
mode=None, mode=None,
target_humidity=68, target_humidity=68,
current_humidity=45, current_humidity=45,
action=HumidifierAction.HUMIDIFYING,
device_class=HumidifierDeviceClass.HUMIDIFIER, device_class=HumidifierDeviceClass.HUMIDIFIER,
), ),
DemoHumidifier( DemoHumidifier(
@ -37,6 +39,7 @@ async def async_setup_platform(
mode=None, mode=None,
target_humidity=54, target_humidity=54,
current_humidity=59, current_humidity=59,
action=HumidifierAction.DRYING,
device_class=HumidifierDeviceClass.DEHUMIDIFIER, device_class=HumidifierDeviceClass.DEHUMIDIFIER,
), ),
DemoHumidifier( DemoHumidifier(
@ -71,11 +74,13 @@ class DemoHumidifier(HumidifierEntity):
current_humidity: int | None = None, current_humidity: int | None = None,
available_modes: list[str] | None = None, available_modes: list[str] | None = None,
is_on: bool = True, is_on: bool = True,
action: HumidifierAction | None = None,
device_class: HumidifierDeviceClass | None = None, device_class: HumidifierDeviceClass | None = None,
) -> None: ) -> None:
"""Initialize the humidifier device.""" """Initialize the humidifier device."""
self._attr_name = name self._attr_name = name
self._attr_is_on = is_on self._attr_is_on = is_on
self._attr_action = action
self._attr_supported_features = SUPPORT_FLAGS self._attr_supported_features = SUPPORT_FLAGS
if mode is not None: if mode is not None:
self._attr_supported_features |= HumidifierEntityFeature.MODES self._attr_supported_features |= HumidifierEntityFeature.MODES

View file

@ -29,6 +29,7 @@ from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
from .const import ( # noqa: F401 from .const import ( # noqa: F401
ATTR_ACTION,
ATTR_AVAILABLE_MODES, ATTR_AVAILABLE_MODES,
ATTR_CURRENT_HUMIDITY, ATTR_CURRENT_HUMIDITY,
ATTR_HUMIDITY, ATTR_HUMIDITY,
@ -45,6 +46,7 @@ from .const import ( # noqa: F401
SERVICE_SET_HUMIDITY, SERVICE_SET_HUMIDITY,
SERVICE_SET_MODE, SERVICE_SET_MODE,
SUPPORT_MODES, SUPPORT_MODES,
HumidifierAction,
HumidifierEntityFeature, HumidifierEntityFeature,
) )
@ -133,6 +135,7 @@ class HumidifierEntity(ToggleEntity):
"""Base class for humidifier entities.""" """Base class for humidifier entities."""
entity_description: HumidifierEntityDescription entity_description: HumidifierEntityDescription
_attr_action: HumidifierAction | None = None
_attr_available_modes: list[str] | None _attr_available_modes: list[str] | None
_attr_current_humidity: int | None = None _attr_current_humidity: int | None = None
_attr_device_class: HumidifierDeviceClass | None _attr_device_class: HumidifierDeviceClass | None
@ -170,6 +173,9 @@ class HumidifierEntity(ToggleEntity):
"""Return the optional state attributes.""" """Return the optional state attributes."""
data: dict[str, int | str | None] = {} 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: if self.current_humidity is not None:
data[ATTR_CURRENT_HUMIDITY] = self.current_humidity data[ATTR_CURRENT_HUMIDITY] = self.current_humidity
@ -181,6 +187,11 @@ class HumidifierEntity(ToggleEntity):
return data return data
@property
def action(self) -> HumidifierAction | None:
"""Return the current action."""
return self._attr_action
@property @property
def current_humidity(self) -> int | None: def current_humidity(self) -> int | None:
"""Return the current humidity.""" """Return the current humidity."""

View file

@ -1,6 +1,8 @@
"""Provides the constants needed for component.""" """Provides the constants needed for component."""
from enum import IntFlag from enum import IntFlag
from homeassistant.backports.enum import StrEnum
MODE_NORMAL = "normal" MODE_NORMAL = "normal"
MODE_ECO = "eco" MODE_ECO = "eco"
MODE_AWAY = "away" MODE_AWAY = "away"
@ -11,6 +13,17 @@ MODE_SLEEP = "sleep"
MODE_AUTO = "auto" MODE_AUTO = "auto"
MODE_BABY = "baby" 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_AVAILABLE_MODES = "available_modes"
ATTR_CURRENT_HUMIDITY = "current_humidity" ATTR_CURRENT_HUMIDITY = "current_humidity"
ATTR_HUMIDITY = "humidity" ATTR_HUMIDITY = "humidity"

View file

@ -28,6 +28,15 @@
"on": "[%key:common::state::on%]" "on": "[%key:common::state::on%]"
}, },
"state_attributes": { "state_attributes": {
"action": {
"name": "Action",
"state": {
"humidifying": "Humidifying",
"drying": "Drying",
"idle": "Idle",
"off": "Off"
}
},
"available_modes": { "available_modes": {
"name": "Available modes" "name": "Available modes"
}, },

View file

@ -4,6 +4,7 @@ import pytest
import voluptuous as vol import voluptuous as vol
from homeassistant.components.humidifier import ( from homeassistant.components.humidifier import (
ATTR_ACTION,
ATTR_CURRENT_HUMIDITY, ATTR_CURRENT_HUMIDITY,
ATTR_HUMIDITY, ATTR_HUMIDITY,
ATTR_MAX_HUMIDITY, ATTR_MAX_HUMIDITY,
@ -45,6 +46,7 @@ def test_setup_params(hass: HomeAssistant) -> None:
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes.get(ATTR_HUMIDITY) == 54 assert state.attributes.get(ATTR_HUMIDITY) == 54
assert state.attributes.get(ATTR_CURRENT_HUMIDITY) == 59 assert state.attributes.get(ATTR_CURRENT_HUMIDITY) == 59
assert state.attributes.get(ATTR_ACTION) == "drying"
def test_default_setup_params(hass: HomeAssistant) -> None: 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) state = hass.states.get(ENTITY_DEHUMIDIFIER)
assert state.state == STATE_OFF assert state.state == STATE_OFF
assert state.attributes.get(ATTR_ACTION) == "off"
await hass.services.async_call( await hass.services.async_call(
DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_DEHUMIDIFIER}, blocking=True DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_DEHUMIDIFIER}, blocking=True
) )
state = hass.states.get(ENTITY_DEHUMIDIFIER) state = hass.states.get(ENTITY_DEHUMIDIFIER)
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes.get(ATTR_ACTION) == "drying"
async def test_turn_off(hass: HomeAssistant) -> None: 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) state = hass.states.get(ENTITY_DEHUMIDIFIER)
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes.get(ATTR_ACTION) == "drying"
await hass.services.async_call( await hass.services.async_call(
DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_DEHUMIDIFIER}, blocking=True DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_DEHUMIDIFIER}, blocking=True
) )
state = hass.states.get(ENTITY_DEHUMIDIFIER) state = hass.states.get(ENTITY_DEHUMIDIFIER)
assert state.state == STATE_OFF assert state.state == STATE_OFF
assert state.attributes.get(ATTR_ACTION) == "off"
async def test_toggle(hass: HomeAssistant) -> None: async def test_toggle(hass: HomeAssistant) -> None: