diff --git a/tests/components/comelit/const.py b/tests/components/comelit/const.py index d06e6cfd8cb..0cbdaf56bbe 100644 --- a/tests/components/comelit/const.py +++ b/tests/components/comelit/const.py @@ -38,7 +38,7 @@ BRIDGE_DEVICE_QUERY = { type="climate", val=[ [221, 0, "U", "M", 50, 0, 0, "U"], - [650, 0, "O", "M", 500, 0, 0, "N"], + [650, 0, "U", "M", 500, 0, 0, "U"], [0, 0], ], protected=0, diff --git a/tests/components/comelit/snapshots/test_diagnostics.ambr b/tests/components/comelit/snapshots/test_diagnostics.ambr index 51ea646df9f..c9ebf635353 100644 --- a/tests/components/comelit/snapshots/test_diagnostics.ambr +++ b/tests/components/comelit/snapshots/test_diagnostics.ambr @@ -27,12 +27,12 @@ list([ 650, 0, - 'O', + 'U', 'M', 500, 0, 0, - 'N', + 'U', ]), list([ 0, diff --git a/tests/components/comelit/snapshots/test_humidifier.ambr b/tests/components/comelit/snapshots/test_humidifier.ambr new file mode 100644 index 00000000000..ffe53d09c5d --- /dev/null +++ b/tests/components/comelit/snapshots/test_humidifier.ambr @@ -0,0 +1,133 @@ +# serializer version: 1 +# name: test_all_entities[humidifier.climate0_dehumidifier-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'available_modes': list([ + 'normal', + 'auto', + ]), + 'max_humidity': 90, + 'min_humidity': 10, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'humidifier', + 'entity_category': None, + 'entity_id': 'humidifier.climate0_dehumidifier', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Dehumidifier', + 'platform': 'comelit', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': 'dehumidifier', + 'unique_id': 'serial_bridge_config_entry_id-0-dehumidifier', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[humidifier.climate0_dehumidifier-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'action': , + 'available_modes': list([ + 'normal', + 'auto', + ]), + 'current_humidity': 65.0, + 'device_class': 'dehumidifier', + 'friendly_name': 'Climate0 Dehumidifier', + 'humidity': 50.0, + 'max_humidity': 90, + 'min_humidity': 10, + 'mode': 'normal', + 'supported_features': , + }), + 'context': , + 'entity_id': 'humidifier.climate0_dehumidifier', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[humidifier.climate0_humidifier-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'available_modes': list([ + 'normal', + 'auto', + ]), + 'max_humidity': 90, + 'min_humidity': 10, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'humidifier', + 'entity_category': None, + 'entity_id': 'humidifier.climate0_humidifier', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Humidifier', + 'platform': 'comelit', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': 'humidifier', + 'unique_id': 'serial_bridge_config_entry_id-0-humidifier', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[humidifier.climate0_humidifier-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'action': , + 'available_modes': list([ + 'normal', + 'auto', + ]), + 'current_humidity': 65.0, + 'device_class': 'humidifier', + 'friendly_name': 'Climate0 Humidifier', + 'humidity': 50.0, + 'max_humidity': 90, + 'min_humidity': 10, + 'mode': 'normal', + 'supported_features': , + }), + 'context': , + 'entity_id': 'humidifier.climate0_humidifier', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- diff --git a/tests/components/comelit/test_humidifier.py b/tests/components/comelit/test_humidifier.py new file mode 100644 index 00000000000..448453aadef --- /dev/null +++ b/tests/components/comelit/test_humidifier.py @@ -0,0 +1,292 @@ +"""Tests for Comelit SimpleHome humidifier platform.""" + +from typing import Any +from unittest.mock import AsyncMock, patch + +from aiocomelit.api import ComelitSerialBridgeObject +from aiocomelit.const import CLIMATE, WATT +from freezegun.api import FrozenDateTimeFactory +import pytest +from syrupy import SnapshotAssertion + +from homeassistant.components.comelit.const import DOMAIN, SCAN_INTERVAL +from homeassistant.components.humidifier import ( + ATTR_HUMIDITY, + ATTR_MODE, + DOMAIN as HUMIDIFIER_DOMAIN, + MODE_AUTO, + MODE_NORMAL, + SERVICE_SET_HUMIDITY, + SERVICE_SET_MODE, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, +) +from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er + +from . import setup_integration + +from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform + +ENTITY_ID = "humidifier.climate0_humidifier" + + +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test all entities.""" + with patch( + "homeassistant.components.comelit.BRIDGE_PLATFORMS", [Platform.HUMIDIFIER] + ): + await setup_integration(hass, mock_serial_bridge_config_entry) + + await snapshot_platform( + hass, + entity_registry, + snapshot, + mock_serial_bridge_config_entry.entry_id, + ) + + +@pytest.mark.parametrize( + ("val", "mode", "humidity"), + [ + ( + [ + [100, 0, "U", "M", 210, 0, 0, "U"], + [650, 0, "U", "M", 500, 0, 0, "U"], + [0, 0], + ], + STATE_ON, + 50.0, + ), + ( + [ + [100, 1, "U", "A", 210, 1, 0, "O"], + [650, 1, "U", "A", 500, 1, 0, "O"], + [0, 0], + ], + STATE_ON, + 50.0, + ), + ( + [ + [100, 0, "O", "A", 210, 0, 0, "O"], + [650, 0, "O", "A", 500, 0, 0, "O"], + [0, 0], + ], + STATE_OFF, + 50.0, + ), + ], +) +async def test_humidifier_data_update( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, + val: list[Any, Any], + mode: str, + humidity: float, +) -> None: + """Test humidifier data update.""" + await setup_integration(hass, mock_serial_bridge_config_entry) + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_HUMIDITY] == 50.0 + + mock_serial_bridge.get_all_devices.return_value[CLIMATE] = { + 0: ComelitSerialBridgeObject( + index=0, + name="Climate0", + status=0, + human_status="off", + type="climate", + val=val, + protected=0, + zone="Living room", + power=0.0, + power_unit=WATT, + ), + } + + freezer.tick(SCAN_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == mode + assert state.attributes[ATTR_HUMIDITY] == humidity + + +async def test_humidifier_data_update_bad_data( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, +) -> None: + """Test humidifier data update.""" + await setup_integration(hass, mock_serial_bridge_config_entry) + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_HUMIDITY] == 50.0 + + mock_serial_bridge.get_all_devices.return_value[CLIMATE] = { + 0: ComelitSerialBridgeObject( + index=0, + name="Climate0", + status=0, + human_status="off", + type="climate", + val="bad_data", + protected=0, + zone="Living room", + power=0.0, + power_unit=WATT, + ), + } + + freezer.tick(SCAN_INTERVAL) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_HUMIDITY] == 50.0 + + +async def test_humidifier_set_humidity( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, +) -> None: + """Test humidifier set humidity service.""" + + await setup_integration(hass, mock_serial_bridge_config_entry) + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_HUMIDITY] == 50.0 + + # Test set humidity + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_SET_HUMIDITY, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HUMIDITY: 23}, + blocking=True, + ) + mock_serial_bridge.set_humidity_status.assert_called() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_HUMIDITY] == 23.0 + + +async def test_humidifier_set_humidity_while_off( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, +) -> None: + """Test humidifier set humidity service while off.""" + + await setup_integration(hass, mock_serial_bridge_config_entry) + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_HUMIDITY] == 50.0 + + # Switch humidifier off + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: ENTITY_ID}, + blocking=True, + ) + mock_serial_bridge.set_humidity_status.assert_called() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_OFF + + # Try setting humidity + with pytest.raises(HomeAssistantError) as exc_info: + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_SET_HUMIDITY, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HUMIDITY: 23}, + blocking=True, + ) + assert exc_info.value.translation_domain == DOMAIN + assert exc_info.value.translation_key == "humidity_while_off" + + +async def test_humidifier_set_mode( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, +) -> None: + """Test humidifier set mode service.""" + + await setup_integration(hass, mock_serial_bridge_config_entry) + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_HUMIDITY] == 50.0 + assert state.attributes[ATTR_MODE] == MODE_NORMAL + + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_SET_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_MODE: MODE_AUTO}, + blocking=True, + ) + mock_serial_bridge.set_humidity_status.assert_called() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_MODE] == MODE_AUTO + + +async def test_humidifier_set_status( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, +) -> None: + """Test humidifier set status service.""" + + await setup_integration(hass, mock_serial_bridge_config_entry) + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON + assert state.attributes[ATTR_HUMIDITY] == 50.0 + + # Test turn off + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: ENTITY_ID}, + blocking=True, + ) + mock_serial_bridge.set_humidity_status.assert_called() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_OFF + + # Test turn on + await hass.services.async_call( + HUMIDIFIER_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: ENTITY_ID}, + blocking=True, + ) + mock_serial_bridge.set_humidity_status.assert_called() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == STATE_ON