mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 19:57:07 +00:00
Add evaporative humidifier for switchbot integration (#146235)
* add support for evaporative humidifier * add evaporative humidifier unit test * clear the humidifier action in pyswitchbot * fix ruff * fix Sentence-casing issue * add icon translation * remove last run success * use icon translations for water level * remove the translation for last run success
This commit is contained in:
parent
85e9919bbd
commit
d0b2d1dc92
@ -92,6 +92,7 @@ PLATFORMS_BY_TYPE = {
|
|||||||
],
|
],
|
||||||
SupportedModels.AIR_PURIFIER.value: [Platform.FAN, Platform.SENSOR],
|
SupportedModels.AIR_PURIFIER.value: [Platform.FAN, Platform.SENSOR],
|
||||||
SupportedModels.AIR_PURIFIER_TABLE.value: [Platform.FAN, Platform.SENSOR],
|
SupportedModels.AIR_PURIFIER_TABLE.value: [Platform.FAN, Platform.SENSOR],
|
||||||
|
SupportedModels.EVAPORATIVE_HUMIDIFIER: [Platform.HUMIDIFIER, Platform.SENSOR],
|
||||||
}
|
}
|
||||||
CLASS_BY_DEVICE = {
|
CLASS_BY_DEVICE = {
|
||||||
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
|
SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
|
||||||
@ -117,6 +118,7 @@ CLASS_BY_DEVICE = {
|
|||||||
SupportedModels.LOCK_ULTRA.value: switchbot.SwitchbotLock,
|
SupportedModels.LOCK_ULTRA.value: switchbot.SwitchbotLock,
|
||||||
SupportedModels.AIR_PURIFIER.value: switchbot.SwitchbotAirPurifier,
|
SupportedModels.AIR_PURIFIER.value: switchbot.SwitchbotAirPurifier,
|
||||||
SupportedModels.AIR_PURIFIER_TABLE.value: switchbot.SwitchbotAirPurifier,
|
SupportedModels.AIR_PURIFIER_TABLE.value: switchbot.SwitchbotAirPurifier,
|
||||||
|
SupportedModels.EVAPORATIVE_HUMIDIFIER: switchbot.SwitchbotEvaporativeHumidifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ class SupportedModels(StrEnum):
|
|||||||
LOCK_ULTRA = "lock_ultra"
|
LOCK_ULTRA = "lock_ultra"
|
||||||
AIR_PURIFIER = "air_purifier"
|
AIR_PURIFIER = "air_purifier"
|
||||||
AIR_PURIFIER_TABLE = "air_purifier_table"
|
AIR_PURIFIER_TABLE = "air_purifier_table"
|
||||||
|
EVAPORATIVE_HUMIDIFIER = "evaporative_humidifier"
|
||||||
|
|
||||||
|
|
||||||
CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||||
@ -75,6 +76,7 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
|||||||
SwitchbotModel.LOCK_ULTRA: SupportedModels.LOCK_ULTRA,
|
SwitchbotModel.LOCK_ULTRA: SupportedModels.LOCK_ULTRA,
|
||||||
SwitchbotModel.AIR_PURIFIER: SupportedModels.AIR_PURIFIER,
|
SwitchbotModel.AIR_PURIFIER: SupportedModels.AIR_PURIFIER,
|
||||||
SwitchbotModel.AIR_PURIFIER_TABLE: SupportedModels.AIR_PURIFIER_TABLE,
|
SwitchbotModel.AIR_PURIFIER_TABLE: SupportedModels.AIR_PURIFIER_TABLE,
|
||||||
|
SwitchbotModel.EVAPORATIVE_HUMIDIFIER: SupportedModels.EVAPORATIVE_HUMIDIFIER,
|
||||||
}
|
}
|
||||||
|
|
||||||
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
|
||||||
@ -103,6 +105,7 @@ ENCRYPTED_MODELS = {
|
|||||||
SwitchbotModel.LOCK_ULTRA,
|
SwitchbotModel.LOCK_ULTRA,
|
||||||
SwitchbotModel.AIR_PURIFIER,
|
SwitchbotModel.AIR_PURIFIER,
|
||||||
SwitchbotModel.AIR_PURIFIER_TABLE,
|
SwitchbotModel.AIR_PURIFIER_TABLE,
|
||||||
|
SwitchbotModel.EVAPORATIVE_HUMIDIFIER,
|
||||||
}
|
}
|
||||||
|
|
||||||
ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
|
ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
|
||||||
@ -116,6 +119,7 @@ ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[
|
|||||||
SwitchbotModel.LOCK_ULTRA: switchbot.SwitchbotLock,
|
SwitchbotModel.LOCK_ULTRA: switchbot.SwitchbotLock,
|
||||||
SwitchbotModel.AIR_PURIFIER: switchbot.SwitchbotAirPurifier,
|
SwitchbotModel.AIR_PURIFIER: switchbot.SwitchbotAirPurifier,
|
||||||
SwitchbotModel.AIR_PURIFIER_TABLE: switchbot.SwitchbotAirPurifier,
|
SwitchbotModel.AIR_PURIFIER_TABLE: switchbot.SwitchbotAirPurifier,
|
||||||
|
SwitchbotModel.EVAPORATIVE_HUMIDIFIER: switchbot.SwitchbotEvaporativeHumidifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = {
|
HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = {
|
||||||
|
@ -2,11 +2,16 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import switchbot
|
import switchbot
|
||||||
|
from switchbot import HumidifierAction as SwitchbotHumidifierAction, HumidifierMode
|
||||||
|
|
||||||
from homeassistant.components.humidifier import (
|
from homeassistant.components.humidifier import (
|
||||||
MODE_AUTO,
|
MODE_AUTO,
|
||||||
MODE_NORMAL,
|
MODE_NORMAL,
|
||||||
|
HumidifierAction,
|
||||||
HumidifierDeviceClass,
|
HumidifierDeviceClass,
|
||||||
HumidifierEntity,
|
HumidifierEntity,
|
||||||
HumidifierEntityFeature,
|
HumidifierEntityFeature,
|
||||||
@ -17,7 +22,13 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|||||||
from .coordinator import SwitchbotConfigEntry
|
from .coordinator import SwitchbotConfigEntry
|
||||||
from .entity import SwitchbotSwitchedEntity, exception_handler
|
from .entity import SwitchbotSwitchedEntity, exception_handler
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
PARALLEL_UPDATES = 0
|
PARALLEL_UPDATES = 0
|
||||||
|
EVAPORATIVE_HUMIDIFIER_ACTION_MAP: dict[int, HumidifierAction] = {
|
||||||
|
SwitchbotHumidifierAction.OFF: HumidifierAction.OFF,
|
||||||
|
SwitchbotHumidifierAction.HUMIDIFYING: HumidifierAction.HUMIDIFYING,
|
||||||
|
SwitchbotHumidifierAction.DRYING: HumidifierAction.DRYING,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@ -26,7 +37,11 @@ async def async_setup_entry(
|
|||||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Switchbot based on a config entry."""
|
"""Set up Switchbot based on a config entry."""
|
||||||
async_add_entities([SwitchBotHumidifier(entry.runtime_data)])
|
coordinator = entry.runtime_data
|
||||||
|
if isinstance(coordinator.device, switchbot.SwitchbotEvaporativeHumidifier):
|
||||||
|
async_add_entities([SwitchBotEvaporativeHumidifier(coordinator)])
|
||||||
|
else:
|
||||||
|
async_add_entities([SwitchBotHumidifier(coordinator)])
|
||||||
|
|
||||||
|
|
||||||
class SwitchBotHumidifier(SwitchbotSwitchedEntity, HumidifierEntity):
|
class SwitchBotHumidifier(SwitchbotSwitchedEntity, HumidifierEntity):
|
||||||
@ -69,3 +84,71 @@ class SwitchBotHumidifier(SwitchbotSwitchedEntity, HumidifierEntity):
|
|||||||
else:
|
else:
|
||||||
self._last_run_success = await self._device.async_set_manual()
|
self._last_run_success = await self._device.async_set_manual()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
class SwitchBotEvaporativeHumidifier(SwitchbotSwitchedEntity, HumidifierEntity):
|
||||||
|
"""Representation of a Switchbot evaporative humidifier."""
|
||||||
|
|
||||||
|
_device: switchbot.SwitchbotEvaporativeHumidifier
|
||||||
|
_attr_device_class = HumidifierDeviceClass.HUMIDIFIER
|
||||||
|
_attr_supported_features = HumidifierEntityFeature.MODES
|
||||||
|
_attr_available_modes = HumidifierMode.get_modes()
|
||||||
|
_attr_min_humidity = 1
|
||||||
|
_attr_max_humidity = 99
|
||||||
|
_attr_translation_key = "evaporative_humidifier"
|
||||||
|
_attr_name = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool | None:
|
||||||
|
"""Return true if device is on."""
|
||||||
|
return self._device.is_on()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode(self) -> str:
|
||||||
|
"""Return the evaporative humidifier current mode."""
|
||||||
|
return self._device.get_mode().name.lower()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_humidity(self) -> int | None:
|
||||||
|
"""Return the current humidity."""
|
||||||
|
return self._device.get_humidity()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_humidity(self) -> int | None:
|
||||||
|
"""Return the humidity we try to reach."""
|
||||||
|
return self._device.get_target_humidity()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def action(self) -> HumidifierAction | None:
|
||||||
|
"""Return the current action."""
|
||||||
|
return EVAPORATIVE_HUMIDIFIER_ACTION_MAP.get(
|
||||||
|
self._device.get_action(), HumidifierAction.IDLE
|
||||||
|
)
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
|
async def async_set_humidity(self, humidity: int) -> None:
|
||||||
|
"""Set new target humidity."""
|
||||||
|
_LOGGER.debug("Setting target humidity to: %s %s", humidity, self._address)
|
||||||
|
await self._device.set_target_humidity(humidity)
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
|
async def async_set_mode(self, mode: str) -> None:
|
||||||
|
"""Set new evaporative humidifier mode."""
|
||||||
|
_LOGGER.debug("Setting mode to: %s %s", mode, self._address)
|
||||||
|
await self._device.set_mode(HumidifierMode[mode.upper()])
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn on the humidifier."""
|
||||||
|
_LOGGER.debug("Turning on the humidifier %s", self._address)
|
||||||
|
await self._device.turn_on()
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn off the humidifier."""
|
||||||
|
_LOGGER.debug("Turning off the humidifier %s", self._address)
|
||||||
|
await self._device.turn_off()
|
||||||
|
self.async_write_ha_state()
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
{
|
{
|
||||||
"entity": {
|
"entity": {
|
||||||
|
"sensor": {
|
||||||
|
"water_level": {
|
||||||
|
"default": "mdi:water-percent",
|
||||||
|
"state": {
|
||||||
|
"empty": "mdi:water-off",
|
||||||
|
"low": "mdi:water-outline",
|
||||||
|
"medium": "mdi:water",
|
||||||
|
"high": "mdi:water-check"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"fan": {
|
"fan": {
|
||||||
"fan": {
|
"fan": {
|
||||||
"state_attributes": {
|
"state_attributes": {
|
||||||
@ -31,6 +42,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"humidifier": {
|
||||||
|
"evaporative_humidifier": {
|
||||||
|
"state_attributes": {
|
||||||
|
"mode": {
|
||||||
|
"state": {
|
||||||
|
"high": "mdi:water-plus",
|
||||||
|
"medium": "mdi:water",
|
||||||
|
"low": "mdi:water-outline",
|
||||||
|
"quiet": "mdi:volume-off",
|
||||||
|
"target_humidity": "mdi:target",
|
||||||
|
"sleep": "mdi:weather-night",
|
||||||
|
"auto": "mdi:autorenew",
|
||||||
|
"drying_filter": "mdi:water-remove"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from switchbot import HumidifierWaterLevel
|
||||||
from switchbot.const.air_purifier import AirQualityLevel
|
from switchbot.const.air_purifier import AirQualityLevel
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import async_last_service_info
|
from homeassistant.components.bluetooth import async_last_service_info
|
||||||
@ -117,6 +118,12 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = {
|
|||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
device_class=SensorDeviceClass.ENERGY,
|
device_class=SensorDeviceClass.ENERGY,
|
||||||
),
|
),
|
||||||
|
"water_level": SensorEntityDescription(
|
||||||
|
key="water_level",
|
||||||
|
translation_key="water_level",
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
options=HumidifierWaterLevel.get_levels(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,6 +114,15 @@
|
|||||||
"moderate": "Moderate",
|
"moderate": "Moderate",
|
||||||
"unhealthy": "Unhealthy"
|
"unhealthy": "Unhealthy"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"water_level": {
|
||||||
|
"name": "Water level",
|
||||||
|
"state": {
|
||||||
|
"empty": "Empty",
|
||||||
|
"low": "[%key:common::state::low%]",
|
||||||
|
"medium": "[%key:common::state::medium%]",
|
||||||
|
"high": "[%key:common::state::high%]"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cover": {
|
"cover": {
|
||||||
@ -138,6 +147,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"evaporative_humidifier": {
|
||||||
|
"state_attributes": {
|
||||||
|
"mode": {
|
||||||
|
"state": {
|
||||||
|
"high": "[%key:common::state::high%]",
|
||||||
|
"medium": "[%key:common::state::medium%]",
|
||||||
|
"low": "[%key:common::state::low%]",
|
||||||
|
"quiet": "Quiet",
|
||||||
|
"target_humidity": "Target humidity",
|
||||||
|
"sleep": "Sleep",
|
||||||
|
"auto": "[%key:common::state::auto%]",
|
||||||
|
"drying_filter": "Drying filter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lock": {
|
"lock": {
|
||||||
|
@ -859,3 +859,27 @@ AIR_PURIFIER_TABLE_VOC_SERVICE_INFO = BluetoothServiceInfoBleak(
|
|||||||
connectable=True,
|
connectable=True,
|
||||||
tx_power=-127,
|
tx_power=-127,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
EVAPORATIVE_HUMIDIFIER_SERVICE_INFO = BluetoothServiceInfoBleak(
|
||||||
|
name="Evaporative Humidifier",
|
||||||
|
manufacturer_data={
|
||||||
|
2409: b"\xa0\xa3\xb3,\x9c\xe68\x86\x88\xb5\x99\x12\x10\x1b\x00\x85]",
|
||||||
|
},
|
||||||
|
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"#\x00\x00\x15\x1c\x00"},
|
||||||
|
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||||
|
address="AA:BB:CC:DD:EE:FF",
|
||||||
|
rssi=-60,
|
||||||
|
source="local",
|
||||||
|
advertisement=generate_advertisement_data(
|
||||||
|
local_name="Evaporative Humidifier",
|
||||||
|
manufacturer_data={
|
||||||
|
2409: b"\xa0\xa3\xb3,\x9c\xe68\x86\x88\xb5\x99\x12\x10\x1b\x00\x85]",
|
||||||
|
},
|
||||||
|
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"#\x00\x00\x15\x1c\x00"},
|
||||||
|
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||||
|
),
|
||||||
|
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Evaporative Humidifier"),
|
||||||
|
time=0,
|
||||||
|
connectable=True,
|
||||||
|
tx_power=-127,
|
||||||
|
)
|
||||||
|
@ -21,7 +21,7 @@ from homeassistant.const import ATTR_ENTITY_ID
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from . import HUMIDIFIER_SERVICE_INFO
|
from . import EVAPORATIVE_HUMIDIFIER_SERVICE_INFO, HUMIDIFIER_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
from tests.components.bluetooth import inject_bluetooth_service_info
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
@ -173,3 +173,89 @@ async def test_exception_handling_humidifier_service(
|
|||||||
{**service_data, ATTR_ENTITY_ID: entity_id},
|
{**service_data, ATTR_ENTITY_ID: entity_id},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("service", "service_data", "mock_method"),
|
||||||
|
[
|
||||||
|
(SERVICE_TURN_ON, {}, "turn_on"),
|
||||||
|
(SERVICE_TURN_OFF, {}, "turn_off"),
|
||||||
|
(SERVICE_SET_HUMIDITY, {ATTR_HUMIDITY: 60}, "set_target_humidity"),
|
||||||
|
(SERVICE_SET_MODE, {ATTR_MODE: "sleep"}, "set_mode"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_evaporative_humidifier_services(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
|
||||||
|
service: str,
|
||||||
|
service_data: dict,
|
||||||
|
mock_method: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test evaporative humidifier services with proper parameters."""
|
||||||
|
inject_bluetooth_service_info(hass, EVAPORATIVE_HUMIDIFIER_SERVICE_INFO)
|
||||||
|
|
||||||
|
entry = mock_entry_encrypted_factory(sensor_type="evaporative_humidifier")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
entity_id = "humidifier.test_name"
|
||||||
|
|
||||||
|
mocked_instance = AsyncMock(return_value=True)
|
||||||
|
with patch.multiple(
|
||||||
|
"homeassistant.components.switchbot.humidifier.switchbot.SwitchbotEvaporativeHumidifier",
|
||||||
|
update=AsyncMock(return_value=None),
|
||||||
|
**{mock_method: mocked_instance},
|
||||||
|
):
|
||||||
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
HUMIDIFIER_DOMAIN,
|
||||||
|
service,
|
||||||
|
{**service_data, ATTR_ENTITY_ID: entity_id},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
mocked_instance.assert_awaited_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("service", "service_data", "mock_method"),
|
||||||
|
[
|
||||||
|
(SERVICE_TURN_ON, {}, "turn_on"),
|
||||||
|
(SERVICE_TURN_OFF, {}, "turn_off"),
|
||||||
|
(SERVICE_SET_HUMIDITY, {ATTR_HUMIDITY: 60}, "set_target_humidity"),
|
||||||
|
(SERVICE_SET_MODE, {ATTR_MODE: "sleep"}, "set_mode"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_evaporative_humidifier_services_with_exception(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
|
||||||
|
service: str,
|
||||||
|
service_data: dict,
|
||||||
|
mock_method: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test exception handling for evaporative humidifier services."""
|
||||||
|
inject_bluetooth_service_info(hass, EVAPORATIVE_HUMIDIFIER_SERVICE_INFO)
|
||||||
|
|
||||||
|
entry = mock_entry_encrypted_factory(sensor_type="evaporative_humidifier")
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
entity_id = "humidifier.test_name"
|
||||||
|
|
||||||
|
patch_target = f"homeassistant.components.switchbot.humidifier.switchbot.SwitchbotEvaporativeHumidifier.{mock_method}"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
patch_target,
|
||||||
|
new=AsyncMock(side_effect=SwitchbotOperationError("Operation failed")),
|
||||||
|
):
|
||||||
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
HomeAssistantError,
|
||||||
|
match="An error occurred while performing the action: Operation failed",
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
HUMIDIFIER_DOMAIN,
|
||||||
|
service,
|
||||||
|
{**service_data, ATTR_ENTITY_ID: entity_id},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
@ -11,6 +11,7 @@ from homeassistant.components.switchbot.const import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
CONF_ADDRESS,
|
CONF_ADDRESS,
|
||||||
@ -23,6 +24,7 @@ from homeassistant.setup import async_setup_component
|
|||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
CIRCULATOR_FAN_SERVICE_INFO,
|
CIRCULATOR_FAN_SERVICE_INFO,
|
||||||
|
EVAPORATIVE_HUMIDIFIER_SERVICE_INFO,
|
||||||
HUB3_SERVICE_INFO,
|
HUB3_SERVICE_INFO,
|
||||||
HUBMINI_MATTER_SERVICE_INFO,
|
HUBMINI_MATTER_SERVICE_INFO,
|
||||||
LEAK_SERVICE_INFO,
|
LEAK_SERVICE_INFO,
|
||||||
@ -484,3 +486,61 @@ async def test_hub3_sensor(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_evaporative_humidifier_sensor(hass: HomeAssistant) -> None:
|
||||||
|
"""Test setting up creates the sensor for evaporative humidifier."""
|
||||||
|
await async_setup_component(hass, DOMAIN, {})
|
||||||
|
inject_bluetooth_service_info(hass, EVAPORATIVE_HUMIDIFIER_SERVICE_INFO)
|
||||||
|
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
|
||||||
|
CONF_NAME: "test-name",
|
||||||
|
CONF_SENSOR_TYPE: "evaporative_humidifier",
|
||||||
|
CONF_KEY_ID: "ff",
|
||||||
|
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||||
|
},
|
||||||
|
unique_id="aabbccddeeff",
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.switchbot.humidifier.switchbot.SwitchbotEvaporativeHumidifier.update",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(hass.states.async_all("sensor")) == 4
|
||||||
|
|
||||||
|
rssi_sensor = hass.states.get("sensor.test_name_bluetooth_signal")
|
||||||
|
rssi_sensor_attrs = rssi_sensor.attributes
|
||||||
|
assert rssi_sensor.state == "-60"
|
||||||
|
assert rssi_sensor_attrs[ATTR_FRIENDLY_NAME] == "test-name Bluetooth signal"
|
||||||
|
assert rssi_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "dBm"
|
||||||
|
|
||||||
|
humidity_sensor = hass.states.get("sensor.test_name_humidity")
|
||||||
|
humidity_sensor_attrs = humidity_sensor.attributes
|
||||||
|
assert humidity_sensor.state == "53"
|
||||||
|
assert humidity_sensor_attrs[ATTR_FRIENDLY_NAME] == "test-name Humidity"
|
||||||
|
assert humidity_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "%"
|
||||||
|
assert humidity_sensor_attrs[ATTR_STATE_CLASS] == "measurement"
|
||||||
|
|
||||||
|
temperature_sensor = hass.states.get("sensor.test_name_temperature")
|
||||||
|
temperature_sensor_attrs = temperature_sensor.attributes
|
||||||
|
assert temperature_sensor.state == "25.1"
|
||||||
|
assert temperature_sensor_attrs[ATTR_FRIENDLY_NAME] == "test-name Temperature"
|
||||||
|
assert temperature_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "°C"
|
||||||
|
assert temperature_sensor_attrs[ATTR_STATE_CLASS] == "measurement"
|
||||||
|
|
||||||
|
water_level_sensor = hass.states.get("sensor.test_name_water_level")
|
||||||
|
water_level_sensor_attrs = water_level_sensor.attributes
|
||||||
|
assert water_level_sensor.state == "medium"
|
||||||
|
assert water_level_sensor_attrs[ATTR_FRIENDLY_NAME] == "test-name Water level"
|
||||||
|
assert water_level_sensor_attrs[ATTR_DEVICE_CLASS] == "enum"
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user