1
0
mirror of https://github.com/home-assistant/core.git synced 2025-05-07 23:49:17 +00:00
core/tests/components/qbus/test_climate.py
Thomas D 2208650fde
Add climate platform to qbus ()
* Add climate platform

* Add unit tests for climate platform

* Use setup_integration fixture

* Apply new import order

* Undo import order

* Code review

* Throw an exception on invalid preset mode

* Let device response determine state

* Remove hvac mode OFF

* Remove hvac mode OFF

* Setup debouncer when being added to hass

* Fix typo
2025-03-26 00:25:05 +00:00

229 lines
7.5 KiB
Python

"""Test Qbus light entities."""
from datetime import timedelta
from unittest.mock import MagicMock, call
import pytest
from homeassistant.components.climate import (
ATTR_CURRENT_TEMPERATURE,
ATTR_HVAC_ACTION,
ATTR_PRESET_MODE,
DOMAIN as CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_TEMPERATURE,
ClimateEntity,
HVACAction,
HVACMode,
)
from homeassistant.components.qbus.climate import STATE_REQUEST_DELAY
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import EntityPlatform
from homeassistant.util import dt as dt_util
from tests.common import async_fire_mqtt_message, async_fire_time_changed
from tests.typing import MqttMockHAClient
_CURRENT_TEMPERATURE = 21.5
_SET_TEMPERATURE = 20.5
_REGIME = "COMFORT"
_PAYLOAD_CLIMATE_STATE_TEMP = (
f'{{"id":"UL20","properties":{{"setTemp":{_SET_TEMPERATURE}}},"type":"event"}}'
)
_PAYLOAD_CLIMATE_STATE_TEMP_FULL = f'{{"id":"UL20","properties":{{"currRegime":"MANUEEL","currTemp":{_CURRENT_TEMPERATURE},"setTemp":{_SET_TEMPERATURE}}},"type":"state"}}'
_PAYLOAD_CLIMATE_STATE_PRESET = (
f'{{"id":"UL20","properties":{{"currRegime":"{_REGIME}"}},"type":"event"}}'
)
_PAYLOAD_CLIMATE_STATE_PRESET_FULL = f'{{"id":"UL20","properties":{{"currRegime":"{_REGIME}","currTemp":{_CURRENT_TEMPERATURE},"setTemp":22.0}},"type":"state"}}'
_PAYLOAD_CLIMATE_SET_TEMP = f'{{"id": "UL20", "type": "state", "properties": {{"setTemp": {_SET_TEMPERATURE}}}}}'
_PAYLOAD_CLIMATE_SET_PRESET = (
'{"id": "UL20", "type": "state", "properties": {"currRegime": "COMFORT"}}'
)
_TOPIC_CLIMATE_STATE = "cloudapp/QBUSMQTTGW/UL1/UL20/state"
_TOPIC_CLIMATE_SET_STATE = "cloudapp/QBUSMQTTGW/UL1/UL20/setState"
_TOPIC_GET_STATE = "cloudapp/QBUSMQTTGW/getState"
_CLIMATE_ENTITY_ID = "climate.living_th"
async def test_climate(
hass: HomeAssistant,
mqtt_mock: MqttMockHAClient,
setup_integration: None,
) -> None:
"""Test climate temperature & preset."""
# Set temperature
mqtt_mock.reset_mock()
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: _CLIMATE_ENTITY_ID,
ATTR_TEMPERATURE: _SET_TEMPERATURE,
},
blocking=True,
)
mqtt_mock.async_publish.assert_called_once_with(
_TOPIC_CLIMATE_SET_STATE, _PAYLOAD_CLIMATE_SET_TEMP, 0, False
)
# Simulate a partial state response
async_fire_mqtt_message(hass, _TOPIC_CLIMATE_STATE, _PAYLOAD_CLIMATE_STATE_TEMP)
await hass.async_block_till_done()
# Check state
entity = hass.states.get(_CLIMATE_ENTITY_ID)
assert entity
assert entity.attributes[ATTR_TEMPERATURE] == _SET_TEMPERATURE
assert entity.attributes[ATTR_CURRENT_TEMPERATURE] is None
assert entity.attributes[ATTR_PRESET_MODE] == "MANUEEL"
assert entity.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
assert entity.state == HVACMode.HEAT
# After a delay, a full state request should've been sent
_wait_and_assert_state_request(hass, mqtt_mock)
# Simulate a full state response
async_fire_mqtt_message(
hass, _TOPIC_CLIMATE_STATE, _PAYLOAD_CLIMATE_STATE_TEMP_FULL
)
await hass.async_block_till_done()
# Check state after full state response
entity = hass.states.get(_CLIMATE_ENTITY_ID)
assert entity
assert entity.attributes[ATTR_TEMPERATURE] == _SET_TEMPERATURE
assert entity.attributes[ATTR_CURRENT_TEMPERATURE] == _CURRENT_TEMPERATURE
assert entity.attributes[ATTR_PRESET_MODE] == "MANUEEL"
assert entity.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
assert entity.state == HVACMode.HEAT
# Set preset
mqtt_mock.reset_mock()
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{
ATTR_ENTITY_ID: _CLIMATE_ENTITY_ID,
ATTR_PRESET_MODE: _REGIME,
},
blocking=True,
)
mqtt_mock.async_publish.assert_called_once_with(
_TOPIC_CLIMATE_SET_STATE, _PAYLOAD_CLIMATE_SET_PRESET, 0, False
)
# Simulate a partial state response
async_fire_mqtt_message(hass, _TOPIC_CLIMATE_STATE, _PAYLOAD_CLIMATE_STATE_PRESET)
await hass.async_block_till_done()
# Check state
entity = hass.states.get(_CLIMATE_ENTITY_ID)
assert entity
assert entity.attributes[ATTR_TEMPERATURE] == _SET_TEMPERATURE
assert entity.attributes[ATTR_CURRENT_TEMPERATURE] == _CURRENT_TEMPERATURE
assert entity.attributes[ATTR_PRESET_MODE] == _REGIME
assert entity.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
assert entity.state == HVACMode.HEAT
# After a delay, a full state request should've been sent
_wait_and_assert_state_request(hass, mqtt_mock)
# Simulate a full state response
async_fire_mqtt_message(
hass, _TOPIC_CLIMATE_STATE, _PAYLOAD_CLIMATE_STATE_PRESET_FULL
)
await hass.async_block_till_done()
# Check state after full state response
entity = hass.states.get(_CLIMATE_ENTITY_ID)
assert entity
assert entity.attributes[ATTR_TEMPERATURE] == 22.0
assert entity.attributes[ATTR_CURRENT_TEMPERATURE] == _CURRENT_TEMPERATURE
assert entity.attributes[ATTR_PRESET_MODE] == _REGIME
assert entity.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING
assert entity.state == HVACMode.HEAT
async def test_climate_when_invalid_state_received(
hass: HomeAssistant,
mqtt_mock: MqttMockHAClient,
setup_integration: None,
) -> None:
"""Test climate when no valid state is received."""
platform: EntityPlatform = hass.data["entity_components"][CLIMATE_DOMAIN]
entity: ClimateEntity = next(
(
entity
for entity in platform.entities
if entity.entity_id == _CLIMATE_ENTITY_ID
),
None,
)
assert entity
entity.async_schedule_update_ha_state = MagicMock()
# Simulate state response
async_fire_mqtt_message(hass, _TOPIC_CLIMATE_STATE, "")
await hass.async_block_till_done()
entity.async_schedule_update_ha_state.assert_not_called()
async def test_climate_with_fast_subsequent_changes(
hass: HomeAssistant,
mqtt_mock: MqttMockHAClient,
setup_integration: None,
) -> None:
"""Test climate with fast subsequent changes."""
# Simulate two subsequent partial state responses
async_fire_mqtt_message(hass, _TOPIC_CLIMATE_STATE, _PAYLOAD_CLIMATE_STATE_TEMP)
await hass.async_block_till_done()
async_fire_mqtt_message(hass, _TOPIC_CLIMATE_STATE, _PAYLOAD_CLIMATE_STATE_TEMP)
await hass.async_block_till_done()
# State request should be requested only once
_wait_and_assert_state_request(hass, mqtt_mock)
async def test_climate_with_unknown_preset(
hass: HomeAssistant,
mqtt_mock: MqttMockHAClient,
setup_integration: None,
) -> None:
"""Test climate with passing an unknown preset value."""
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{
ATTR_ENTITY_ID: _CLIMATE_ENTITY_ID,
ATTR_PRESET_MODE: "What is cooler than being cool?",
},
blocking=True,
)
def _wait_and_assert_state_request(
hass: HomeAssistant, mqtt_mock: MqttMockHAClient
) -> None:
mqtt_mock.reset_mock()
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(STATE_REQUEST_DELAY))
mqtt_mock.async_publish.assert_has_calls(
[call(_TOPIC_GET_STATE, '["UL20"]', 0, False)],
any_order=True,
)