core/tests/components/knx/test_climate.py

853 lines
29 KiB
Python

"""Test KNX climate."""
import pytest
from homeassistant.components.climate import HVACMode
from homeassistant.components.knx.schema import ClimateSchema
from homeassistant.const import CONF_NAME, STATE_IDLE
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from .conftest import KNXTestKit
from tests.common import async_capture_events
RAW_FLOAT_20_0 = (0x07, 0xD0)
RAW_FLOAT_21_0 = (0x0C, 0x1A)
RAW_FLOAT_22_0 = (0x0C, 0x4C)
async def test_climate_basic_temperature_set(
hass: HomeAssistant, knx: KNXTestKit
) -> None:
"""Test KNX climate basic."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
}
}
)
events = async_capture_events(hass, "state_changed")
# read temperature
await knx.assert_read("1/2/3")
# read target temperature
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
events.clear()
# set new temperature
await hass.services.async_call(
"climate",
"set_temperature",
{"entity_id": "climate.test", "temperature": 20},
blocking=True,
)
await knx.assert_write("1/2/4", RAW_FLOAT_20_0)
assert len(events) == 1
@pytest.mark.parametrize("heat_cool_ga", [None, "4/4/4"])
async def test_climate_on_off(
hass: HomeAssistant, knx: KNXTestKit, heat_cool_ga: str | None
) -> None:
"""Test KNX climate on/off."""
on_off_ga = "3/3/3"
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_ON_OFF_ADDRESS: on_off_ga,
ClimateSchema.CONF_ON_OFF_STATE_ADDRESS: "1/2/9",
}
| (
{
ClimateSchema.CONF_HEAT_COOL_ADDRESS: heat_cool_ga,
ClimateSchema.CONF_HEAT_COOL_STATE_ADDRESS: "1/2/11",
}
if heat_cool_ga
else {}
)
}
)
# read temperature state
await knx.assert_read("1/2/3")
await knx.receive_response("1/2/3", RAW_FLOAT_20_0)
# read target temperature state
await knx.assert_read("1/2/5")
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
# read on/off state
await knx.assert_read("1/2/9")
await knx.receive_response("1/2/9", 1)
# read heat/cool state
if heat_cool_ga:
await knx.assert_read("1/2/11")
await knx.receive_response("1/2/11", 0) # cool
# turn off
await hass.services.async_call(
"climate",
"turn_off",
{"entity_id": "climate.test"},
blocking=True,
)
await knx.assert_write(on_off_ga, 0)
assert hass.states.get("climate.test").state == "off"
# turn on
await hass.services.async_call(
"climate",
"turn_on",
{"entity_id": "climate.test"},
blocking=True,
)
await knx.assert_write(on_off_ga, 1)
if heat_cool_ga:
# does not fall back to default hvac mode after turn_on
assert hass.states.get("climate.test").state == "cool"
else:
assert hass.states.get("climate.test").state == "heat"
# set hvac mode to off triggers turn_off if no controller_mode is available
await hass.services.async_call(
"climate",
"set_hvac_mode",
{"entity_id": "climate.test", "hvac_mode": HVACMode.OFF},
blocking=True,
)
await knx.assert_write(on_off_ga, 0)
assert hass.states.get("climate.test").state == "off"
# set hvac mode to heat
await hass.services.async_call(
"climate",
"set_hvac_mode",
{"entity_id": "climate.test", "hvac_mode": HVACMode.HEAT},
blocking=True,
)
if heat_cool_ga:
await knx.assert_write(heat_cool_ga, 1)
await knx.assert_write(on_off_ga, 1)
else:
await knx.assert_write(on_off_ga, 1)
assert hass.states.get("climate.test").state == "heat"
@pytest.mark.parametrize("on_off_ga", [None, "4/4/4"])
async def test_climate_hvac_mode(
hass: HomeAssistant, knx: KNXTestKit, on_off_ga: str | None
) -> None:
"""Test KNX climate hvac mode."""
controller_mode_ga = "3/3/3"
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_CONTROLLER_MODE_ADDRESS: controller_mode_ga,
ClimateSchema.CONF_CONTROLLER_MODE_STATE_ADDRESS: "1/2/7",
ClimateSchema.CONF_OPERATION_MODES: ["Auto"],
}
| (
{
ClimateSchema.CONF_ON_OFF_ADDRESS: on_off_ga,
}
if on_off_ga
else {}
)
}
)
# read states state updater
# StateUpdater semaphore allows 2 concurrent requests
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/3", RAW_FLOAT_20_0)
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (0x01,))
# turn hvac mode to off - set_hvac_mode() doesn't send to on_off if dedicated hvac mode is available
await hass.services.async_call(
"climate",
"set_hvac_mode",
{"entity_id": "climate.test", "hvac_mode": HVACMode.OFF},
blocking=True,
)
await knx.assert_write(controller_mode_ga, (0x06,))
if on_off_ga:
await knx.assert_write(on_off_ga, 0)
assert hass.states.get("climate.test").state == "off"
# set hvac to non default mode
await hass.services.async_call(
"climate",
"set_hvac_mode",
{"entity_id": "climate.test", "hvac_mode": HVACMode.COOL},
blocking=True,
)
await knx.assert_write(controller_mode_ga, (0x03,))
if on_off_ga:
await knx.assert_write(on_off_ga, 1)
assert hass.states.get("climate.test").state == "cool"
# turn off
await hass.services.async_call(
"climate",
"turn_off",
{"entity_id": "climate.test"},
blocking=True,
)
if on_off_ga:
await knx.assert_write(on_off_ga, 0)
else:
await knx.assert_write(controller_mode_ga, (0x06,))
assert hass.states.get("climate.test").state == "off"
# turn on
await hass.services.async_call(
"climate",
"turn_on",
{"entity_id": "climate.test"},
blocking=True,
)
if on_off_ga:
await knx.assert_write(on_off_ga, 1)
else:
# restore last hvac mode
await knx.assert_write(controller_mode_ga, (0x03,))
assert hass.states.get("climate.test").state == "cool"
async def test_climate_heat_cool_read_only(
hass: HomeAssistant, knx: KNXTestKit
) -> None:
"""Test KNX climate hvac mode."""
heat_cool_state_ga = "3/3/3"
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_HEAT_COOL_STATE_ADDRESS: heat_cool_state_ga,
}
}
)
# read states state updater
# StateUpdater semaphore allows 2 concurrent requests
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/3", RAW_FLOAT_20_0)
await knx.receive_response("1/2/5", RAW_FLOAT_20_0)
await knx.assert_read(heat_cool_state_ga)
await knx.receive_response(heat_cool_state_ga, True) # heat
state = hass.states.get("climate.test")
assert state.state == "heat"
assert state.attributes["hvac_modes"] == ["heat"]
assert state.attributes["hvac_action"] == "heating"
await knx.receive_write(heat_cool_state_ga, False) # cool
state = hass.states.get("climate.test")
assert state.state == "cool"
assert state.attributes["hvac_modes"] == ["cool"]
assert state.attributes["hvac_action"] == "cooling"
async def test_climate_heat_cool_read_only_on_off(
hass: HomeAssistant, knx: KNXTestKit
) -> None:
"""Test KNX climate hvac mode."""
on_off_ga = "2/2/2"
heat_cool_state_ga = "3/3/3"
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_ON_OFF_ADDRESS: on_off_ga,
ClimateSchema.CONF_HEAT_COOL_STATE_ADDRESS: heat_cool_state_ga,
}
}
)
# read states state updater
# StateUpdater semaphore allows 2 concurrent requests
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/3", RAW_FLOAT_20_0)
await knx.receive_response("1/2/5", RAW_FLOAT_20_0)
await knx.assert_read(heat_cool_state_ga)
await knx.receive_response(heat_cool_state_ga, True) # heat
state = hass.states.get("climate.test")
assert state.state == "off"
assert set(state.attributes["hvac_modes"]) == {"off", "heat"}
assert state.attributes["hvac_action"] == "off"
await knx.receive_write(heat_cool_state_ga, False) # cool
state = hass.states.get("climate.test")
assert state.state == "off"
assert set(state.attributes["hvac_modes"]) == {"off", "cool"}
assert state.attributes["hvac_action"] == "off"
await knx.receive_write(on_off_ga, True)
state = hass.states.get("climate.test")
assert state.state == "cool"
assert set(state.attributes["hvac_modes"]) == {"off", "cool"}
assert state.attributes["hvac_action"] == "cooling"
async def test_climate_preset_mode(
hass: HomeAssistant, knx: KNXTestKit, entity_registry: er.EntityRegistry
) -> None:
"""Test KNX climate preset mode."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_OPERATION_MODE_ADDRESS: "1/2/6",
ClimateSchema.CONF_OPERATION_MODE_STATE_ADDRESS: "1/2/7",
}
}
)
# StateUpdater initialize state
# StateUpdater semaphore allows 2 concurrent requests
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (0x01,)) # comfort
knx.assert_state("climate.test", HVACMode.HEAT, preset_mode="comfort")
# set preset mode
await hass.services.async_call(
"climate",
"set_preset_mode",
{"entity_id": "climate.test", "preset_mode": "building_protection"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x04,))
knx.assert_state("climate.test", HVACMode.HEAT, preset_mode="building_protection")
# set preset mode
await hass.services.async_call(
"climate",
"set_preset_mode",
{"entity_id": "climate.test", "preset_mode": "economy"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x03,))
knx.assert_state("climate.test", HVACMode.HEAT, preset_mode="economy")
assert len(knx.xknx.devices) == 2
assert len(knx.xknx.devices[0].device_updated_cbs) == 2
assert len(knx.xknx.devices[1].device_updated_cbs) == 2
# test removing also removes hooks
entity_registry.async_remove("climate.test")
# If we remove the entity the underlying devices should disappear too
assert len(knx.xknx.devices) == 0
async def test_update_entity(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test update climate entity for KNX."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_OPERATION_MODE_ADDRESS: "1/2/6",
ClimateSchema.CONF_OPERATION_MODE_STATE_ADDRESS: "1/2/7",
}
}
)
assert await async_setup_component(hass, "homeassistant", {})
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (0x01,))
# verify update entity retriggers group value reads to the bus
await hass.services.async_call(
"homeassistant",
"update_entity",
target={"entity_id": "climate.test"},
blocking=True,
)
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
await knx.assert_read("1/2/7")
async def test_command_value_idle_mode(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX climate command_value."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_COMMAND_VALUE_STATE_ADDRESS: "1/2/6",
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# StateUpdater semaphore allows 2 concurrent requests
await knx.assert_read("1/2/6")
await knx.receive_response("1/2/6", (0x32,))
knx.assert_state("climate.test", HVACMode.HEAT, command_value=20)
await knx.receive_write("1/2/6", (0x00,))
knx.assert_state(
"climate.test", HVACMode.HEAT, command_value=0, hvac_action=STATE_IDLE
)
async def test_fan_speed_3_steps(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX climate fan speed 3 steps."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
ClimateSchema.CONF_FAN_MAX_STEP: 3,
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# Query status
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (0x01,))
knx.assert_state(
"climate.test",
HVACMode.HEAT,
fan_mode="low",
fan_modes=["off", "low", "medium", "high"],
)
# set fan mode
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "medium"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x02,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="medium")
# turn off
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "off"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x0,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="off")
async def test_fan_speed_2_steps(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX climate fan speed 2 steps."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
ClimateSchema.CONF_FAN_MAX_STEP: 2,
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# Query status
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (0x01,))
knx.assert_state(
"climate.test", HVACMode.HEAT, fan_mode="low", fan_modes=["off", "low", "high"]
)
# set fan mode
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "high"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x02,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="high")
# turn off
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "off"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x0,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="off")
async def test_fan_speed_1_step(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX climate fan speed 1 step."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
ClimateSchema.CONF_FAN_MAX_STEP: 1,
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# Query status
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (0x01,))
knx.assert_state(
"climate.test", HVACMode.HEAT, fan_mode="on", fan_modes=["off", "on"]
)
# turn off
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "off"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x0,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="off")
async def test_fan_speed_5_steps(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX climate fan speed 5 steps."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
ClimateSchema.CONF_FAN_MAX_STEP: 5,
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# Query status
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (0x01,))
knx.assert_state(
"climate.test",
HVACMode.HEAT,
fan_mode="1",
fan_modes=["off", "1", "2", "3", "4", "5"],
)
# set fan mode
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "4"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x04,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="4")
# turn off
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "off"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x0,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="off")
async def test_fan_speed_percentage(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX climate fan speed percentage."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
ClimateSchema.CONF_FAN_SPEED_MODE: "percent",
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# Query status
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (84,)) # 84 / 255 = 33%
knx.assert_state(
"climate.test",
HVACMode.HEAT,
fan_mode="low",
fan_modes=["off", "low", "medium", "high"],
)
# set fan mode
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "medium"},
blocking=True,
)
await knx.assert_write("1/2/6", (168,)) # 168 / 255 = 66%
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="medium")
# turn off
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "off"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x0,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="off")
# check fan mode that is not in the fan modes list
await knx.receive_write("1/2/6", (127,)) # 127 / 255 = 50%
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="medium")
# check FAN_OFF is not picked when fan_speed is closest to zero
await knx.receive_write("1/2/6", (3,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="low")
async def test_fan_speed_percentage_4_steps(
hass: HomeAssistant, knx: KNXTestKit
) -> None:
"""Test KNX climate fan speed percentage with 4 steps."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
ClimateSchema.CONF_FAN_SPEED_MODE: "percent",
ClimateSchema.CONF_FAN_MAX_STEP: 4,
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# Query status
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (64,)) # 64 / 255 = 25%
knx.assert_state(
"climate.test",
HVACMode.HEAT,
fan_mode="25%",
fan_modes=["off", "25%", "50%", "75%", "100%"],
)
# set fan mode
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "50%"},
blocking=True,
)
await knx.assert_write("1/2/6", (128,)) # 128 / 255 = 50%
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="50%")
# turn off
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "off"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x0,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="off")
# check fan mode that is not in the fan modes list
await knx.receive_write("1/2/6", (168,)) # 168 / 255 = 66%
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="75%")
async def test_fan_speed_zero_mode_auto(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX climate fan speed 3 steps."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
ClimateSchema.CONF_FAN_MAX_STEP: 3,
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
ClimateSchema.CONF_FAN_ZERO_MODE: "auto",
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# Query status
await knx.assert_read("1/2/7")
await knx.receive_response("1/2/7", (0x01,))
knx.assert_state(
"climate.test",
HVACMode.HEAT,
fan_mode="low",
fan_modes=["auto", "low", "medium", "high"],
)
# set auto
await hass.services.async_call(
"climate",
"set_fan_mode",
{"entity_id": "climate.test", "fan_mode": "auto"},
blocking=True,
)
await knx.assert_write("1/2/6", (0x0,))
knx.assert_state("climate.test", HVACMode.HEAT, fan_mode="auto")
async def test_climate_humidity(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX climate humidity."""
await knx.setup_integration(
{
ClimateSchema.PLATFORM: {
CONF_NAME: "test",
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
ClimateSchema.CONF_HUMIDITY_STATE_ADDRESS: "1/2/16",
}
}
)
# read states state updater
await knx.assert_read("1/2/3")
await knx.assert_read("1/2/5")
# StateUpdater initialize state
await knx.receive_response("1/2/5", RAW_FLOAT_22_0)
await knx.receive_response("1/2/3", RAW_FLOAT_21_0)
# Query status
await knx.assert_read("1/2/16")
await knx.receive_response("1/2/16", (0x14, 0x74))
knx.assert_state(
"climate.test",
HVACMode.HEAT,
current_humidity=45.6,
)