mirror of https://github.com/home-assistant/core
853 lines
29 KiB
Python
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,
|
|
)
|