diff --git a/homeassistant/components/comelit/climate.py b/homeassistant/components/comelit/climate.py index de9b0ce7ea0..83dc78e9151 100644 --- a/homeassistant/components/comelit/climate.py +++ b/homeassistant/components/comelit/climate.py @@ -101,6 +101,7 @@ class ComelitClimateEntity(CoordinatorEntity[ComelitSerialBridge], ClimateEntity _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_has_entity_name = True _attr_name = None + _attr_hvac_mode: HVACMode | None = None def __init__( self, diff --git a/homeassistant/components/comelit/const.py b/homeassistant/components/comelit/const.py index 84d8fbd6315..f52f33fd6da 100644 --- a/homeassistant/components/comelit/const.py +++ b/homeassistant/components/comelit/const.py @@ -9,3 +9,5 @@ _LOGGER = logging.getLogger(__package__) DOMAIN = "comelit" DEFAULT_PORT = 80 DEVICE_TYPE_LIST = [BRIDGE, VEDO] + +SCAN_INTERVAL = 5 diff --git a/homeassistant/components/comelit/coordinator.py b/homeassistant/components/comelit/coordinator.py index b3be3a47825..df4965d9945 100644 --- a/homeassistant/components/comelit/coordinator.py +++ b/homeassistant/components/comelit/coordinator.py @@ -22,7 +22,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers import device_registry as dr from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import _LOGGER, DOMAIN +from .const import _LOGGER, DOMAIN, SCAN_INTERVAL type ComelitConfigEntry = ConfigEntry[ComelitBaseCoordinator] @@ -53,7 +53,7 @@ class ComelitBaseCoordinator(DataUpdateCoordinator[T]): logger=_LOGGER, config_entry=entry, name=f"{DOMAIN}-{host}-coordinator", - update_interval=timedelta(seconds=5), + update_interval=timedelta(seconds=SCAN_INTERVAL), ) device_registry = dr.async_get(self.hass) device_registry.async_get_or_create( diff --git a/homeassistant/components/comelit/humidifier.py b/homeassistant/components/comelit/humidifier.py index c61c27a5348..de0615ffc62 100644 --- a/homeassistant/components/comelit/humidifier.py +++ b/homeassistant/components/comelit/humidifier.py @@ -97,6 +97,7 @@ class ComelitHumidifierEntity(CoordinatorEntity[ComelitSerialBridge], Humidifier _attr_min_humidity = 10 _attr_max_humidity = 90 _attr_has_entity_name = True + _attr_mode: str | None = None def __init__( self, diff --git a/tests/components/comelit/const.py b/tests/components/comelit/const.py index f353ec97628..0161616b9b3 100644 --- a/tests/components/comelit/const.py +++ b/tests/components/comelit/const.py @@ -29,7 +29,24 @@ VEDO_PIN = 5678 FAKE_PIN = 0000 BRIDGE_DEVICE_QUERY = { - CLIMATE: {}, + CLIMATE: { + 0: ComelitSerialBridgeObject( + index=0, + name="Climate0", + status=0, + human_status="off", + type="climate", + val=[ + [221, 0, "U", "M", 50, 0, 0, "U"], + [650, 0, "O", "M", 500, 0, 0, "N"], + [0, 0], + ], + protected=0, + zone="Living room", + power=0.0, + power_unit=WATT, + ), + }, COVER: { 0: ComelitSerialBridgeObject( index=0, diff --git a/tests/components/comelit/snapshots/test_climate.ambr b/tests/components/comelit/snapshots/test_climate.ambr new file mode 100644 index 00000000000..7951c32eebb --- /dev/null +++ b/tests/components/comelit/snapshots/test_climate.ambr @@ -0,0 +1,69 @@ +# serializer version: 1 +# name: test_all_entities[climate.climate0-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'hvac_modes': list([ + , + , + , + , + ]), + 'max_temp': 30, + 'min_temp': 5, + 'target_temp_step': 0.1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'climate', + 'entity_category': None, + 'entity_id': 'climate.climate0', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'comelit', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[climate.climate0-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'current_temperature': None, + 'friendly_name': 'Climate0', + 'hvac_modes': list([ + , + , + , + , + ]), + 'max_temp': 30, + 'min_temp': 5, + 'supported_features': , + 'target_temp_step': 0.1, + 'temperature': None, + }), + 'context': , + 'entity_id': 'climate.climate0', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/comelit/snapshots/test_diagnostics.ambr b/tests/components/comelit/snapshots/test_diagnostics.ambr index c4544f38f52..0249655c5d9 100644 --- a/tests/components/comelit/snapshots/test_diagnostics.ambr +++ b/tests/components/comelit/snapshots/test_diagnostics.ambr @@ -5,6 +5,43 @@ 'devices': list([ dict({ 'clima': list([ + dict({ + '0': dict({ + 'human_status': 'off', + 'name': 'Climate0', + 'power': 0.0, + 'power_unit': 'W', + 'protected': 0, + 'status': 0, + 'val': list([ + list([ + 221, + 0, + 'U', + 'M', + 50, + 0, + 0, + 'U', + ]), + list([ + 650, + 0, + 'O', + 'M', + 500, + 0, + 0, + 'N', + ]), + list([ + 0, + 0, + ]), + ]), + 'zone': 'Living room', + }), + }), ]), }), dict({ diff --git a/tests/components/comelit/test_climate.py b/tests/components/comelit/test_climate.py new file mode 100644 index 00000000000..e4d2a0fa1ad --- /dev/null +++ b/tests/components/comelit/test_climate.py @@ -0,0 +1,162 @@ +"""Tests for Comelit SimpleHome climate 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 syrupy.filters import props + +from homeassistant.components.climate import ( + ATTR_HVAC_MODE, + DOMAIN as CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + SERVICE_SET_TEMPERATURE, + HVACAction, + HVACMode, +) +from homeassistant.components.comelit.const import SCAN_INTERVAL +from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, Platform +from homeassistant.core import HomeAssistant +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 = "climate.climate0" + + +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.CLIMATE]): + await setup_integration(hass, mock_serial_bridge_config_entry) + + await snapshot_platform( + hass, + entity_registry, + snapshot(exclude=props("unique_id")), + mock_serial_bridge_config_entry.entry_id, + ) + + +@pytest.mark.parametrize( + "val", + [ + [ + [270, 0, "U", "M", 50, 0, 0, "U"], + [650, 0, "O", "M", 500, 0, 0, "N"], + [0, 0], + ], + [ + [270, 1, "O", "A", 50, 1, 0, "O"], + [650, 1, "O", "A", 500, 1, 0, "N"], + [0, 0], + ], + ], +) +async def test_climate_data_update( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, + val: list[Any, Any], +) -> None: + """Test climate data update.""" + await setup_integration(hass, mock_serial_bridge_config_entry) + + entity = hass.states.get(ENTITY_ID) + assert entity + assert entity.state == HVACAction.HEATING + assert entity.attributes[ATTR_TEMPERATURE] == 21.1 + + 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 + 1) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) + + entity = hass.states.get(ENTITY_ID) + assert entity + assert entity.state == HVACAction.HEATING + assert entity.attributes[ATTR_TEMPERATURE] == 27 + + +async def test_climate_set_temperature( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test climate set temperature service.""" + + await setup_integration(hass, mock_serial_bridge_config_entry) + + entity = hass.states.get(ENTITY_ID) + assert entity + assert entity.state == HVACMode.HEAT + assert entity.attributes[ATTR_TEMPERATURE] == 21.1 + + # Test set temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_TEMPERATURE: 23}, + blocking=True, + ) + mock_serial_bridge.set_clima_status.assert_called() + + entity = hass.states.get(ENTITY_ID) + assert entity + assert entity.state == HVACMode.HEAT + assert entity.attributes[ATTR_TEMPERATURE] == 23 + + +async def test_climate_hvac_mode( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test climate hvac mode service.""" + + await setup_integration(hass, mock_serial_bridge_config_entry) + + entity = hass.states.get(ENTITY_ID) + assert entity + assert entity.state == HVACMode.HEAT + assert entity.attributes[ATTR_TEMPERATURE] == 21.1 + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + mock_serial_bridge.set_clima_status.assert_called() + + entity = hass.states.get(ENTITY_ID) + assert entity + assert entity.state == HVACMode.OFF