Add full test coverage for Comelit climate platform (#140460)

* Add climate tests for Comelit

* fix climate and humidifier

* fix code and tests

* fix humidifier

* apply review comment

* align post merge

* add more tests

* typo

* apply review comment

* ruff
This commit is contained in:
Simone Chemelli 2025-03-30 15:15:26 +02:00 committed by GitHub
parent 4761207097
commit 5e1bbd8bff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 408 additions and 1 deletions

View File

@ -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,

View File

@ -0,0 +1,71 @@
# serializer version: 1
# name: test_all_entities[climate.climate0-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.AUTO: 'auto'>,
<HVACMode.COOL: 'cool'>,
<HVACMode.HEAT: 'heat'>,
<HVACMode.OFF: 'off'>,
]),
'max_temp': 30,
'min_temp': 5,
'target_temp_step': 0.1,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.climate0',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'comelit',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 385>,
'translation_key': None,
'unique_id': 'serial_bridge_config_entry_id-0',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[climate.climate0-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 22.1,
'friendly_name': 'Climate0',
'hvac_action': <HVACAction.HEATING: 'heating'>,
'hvac_modes': list([
<HVACMode.AUTO: 'auto'>,
<HVACMode.COOL: 'cool'>,
<HVACMode.HEAT: 'heat'>,
<HVACMode.OFF: 'off'>,
]),
'max_temp': 30,
'min_temp': 5,
'supported_features': <ClimateEntityFeature: 385>,
'target_temp_step': 0.1,
'temperature': 5.0,
}),
'context': <ANY>,
'entity_id': 'climate.climate0',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'heat',
})
# ---

View File

@ -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({

View File

@ -0,0 +1,282 @@
"""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 homeassistant.components.climate import (
ATTR_HVAC_MODE,
DOMAIN as CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_TEMPERATURE,
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(),
mock_serial_bridge_config_entry.entry_id,
)
@pytest.mark.parametrize(
("val", "mode", "temp"),
[
(
[
[100, 0, "U", "M", 210, 0, 0, "U"],
[650, 0, "O", "M", 500, 0, 0, "N"],
[0, 0],
],
HVACMode.HEAT,
21.0,
),
(
[
[100, 1, "U", "A", 210, 1, 0, "O"],
[650, 0, "O", "M", 500, 0, 0, "N"],
[0, 0],
],
HVACMode.HEAT,
21.0,
),
(
[
[100, 0, "O", "A", 210, 0, 0, "O"],
[650, 0, "O", "M", 500, 0, 0, "N"],
[0, 0],
],
HVACMode.OFF,
21.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],
mode: HVACMode,
temp: float,
) -> None:
"""Test climate data update."""
await setup_integration(hass, mock_serial_bridge_config_entry)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 5.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(wait_background_tasks=True)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == mode
assert state.attributes[ATTR_TEMPERATURE] == temp
async def test_climate_data_update_bad_data(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
mock_serial_bridge: AsyncMock,
mock_serial_bridge_config_entry: MockConfigEntry,
) -> None:
"""Test climate data update."""
await setup_integration(hass, mock_serial_bridge_config_entry)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 5.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(wait_background_tasks=True)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 5.0
async def test_climate_set_temperature(
hass: HomeAssistant,
mock_serial_bridge: AsyncMock,
mock_serial_bridge_config_entry: MockConfigEntry,
) -> None:
"""Test climate set temperature service."""
await setup_integration(hass, mock_serial_bridge_config_entry)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 5.0
# 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()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 23.0
async def test_climate_set_temperature_when_off(
hass: HomeAssistant,
mock_serial_bridge: AsyncMock,
mock_serial_bridge_config_entry: MockConfigEntry,
) -> None:
"""Test climate set temperature service when off."""
await setup_integration(hass, mock_serial_bridge_config_entry)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 5.0
# Switch climate off
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()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.OFF
# 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()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.OFF
async def test_climate_hvac_mode(
hass: HomeAssistant,
mock_serial_bridge: AsyncMock,
mock_serial_bridge_config_entry: MockConfigEntry,
) -> None:
"""Test climate hvac mode service."""
await setup_integration(hass, mock_serial_bridge_config_entry)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 5.0
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()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.OFF
async def test_climate_hvac_mode_when_off(
hass: HomeAssistant,
mock_serial_bridge: AsyncMock,
mock_serial_bridge_config_entry: MockConfigEntry,
) -> None:
"""Test climate hvac mode service when off."""
await setup_integration(hass, mock_serial_bridge_config_entry)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == 5.0
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()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.OFF
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.AUTO},
blocking=True,
)
mock_serial_bridge.set_clima_status.assert_called()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == HVACMode.AUTO