Update xknx to 3.1.0 and fix climate read only mode (#123776)

This commit is contained in:
Matthias Alphart 2024-08-13 13:28:37 +02:00 committed by GitHub
parent 71e23e7849
commit b3d1d79a49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 109 additions and 10 deletions

View file

@ -5,7 +5,11 @@ from __future__ import annotations
from typing import Any
from xknx import XKNX
from xknx.devices import Climate as XknxClimate, ClimateMode as XknxClimateMode
from xknx.devices import (
Climate as XknxClimate,
ClimateMode as XknxClimateMode,
Device as XknxDevice,
)
from xknx.dpt.dpt_20 import HVACControllerMode
from homeassistant import config_entries
@ -241,12 +245,9 @@ class KNXClimate(KnxYamlEntity, ClimateEntity):
if self._device.supports_on_off and not self._device.is_on:
return HVACMode.OFF
if self._device.mode is not None and self._device.mode.supports_controller_mode:
hvac_mode = CONTROLLER_MODES.get(
return CONTROLLER_MODES.get(
self._device.mode.controller_mode, self.default_hvac_mode
)
if hvac_mode is not HVACMode.OFF:
self._last_hvac_mode = hvac_mode
return hvac_mode
return self.default_hvac_mode
@property
@ -261,11 +262,15 @@ class KNXClimate(KnxYamlEntity, ClimateEntity):
if self._device.supports_on_off:
if not ha_controller_modes:
ha_controller_modes.append(self.default_hvac_mode)
ha_controller_modes.append(self._last_hvac_mode)
ha_controller_modes.append(HVACMode.OFF)
hvac_modes = list(set(filter(None, ha_controller_modes)))
return hvac_modes if hvac_modes else [self.default_hvac_mode]
return (
hvac_modes
if hvac_modes
else [self.hvac_mode] # mode read-only -> fall back to only current mode
)
@property
def hvac_action(self) -> HVACAction | None:
@ -354,3 +359,13 @@ class KNXClimate(KnxYamlEntity, ClimateEntity):
self._device.mode.unregister_device_updated_cb(self.after_update_callback)
self._device.mode.xknx.devices.async_remove(self._device.mode)
await super().async_will_remove_from_hass()
def after_update_callback(self, _device: XknxDevice) -> None:
"""Call after device was updated."""
if self._device.mode is not None and self._device.mode.supports_controller_mode:
hvac_mode = CONTROLLER_MODES.get(
self._device.mode.controller_mode, self.default_hvac_mode
)
if hvac_mode is not HVACMode.OFF:
self._last_hvac_mode = hvac_mode
super().after_update_callback(_device)

View file

@ -11,7 +11,7 @@
"loggers": ["xknx", "xknxproject"],
"quality_scale": "platinum",
"requirements": [
"xknx==3.0.0",
"xknx==3.1.0",
"xknxproject==3.7.1",
"knx-frontend==2024.8.9.225351"
],

View file

@ -2930,7 +2930,7 @@ xbox-webapi==2.0.11
xiaomi-ble==0.30.2
# homeassistant.components.knx
xknx==3.0.0
xknx==3.1.0
# homeassistant.components.knx
xknxproject==3.7.1

View file

@ -2316,7 +2316,7 @@ xbox-webapi==2.0.11
xiaomi-ble==0.30.2
# homeassistant.components.knx
xknx==3.0.0
xknx==3.1.0
# homeassistant.components.knx
xknxproject==3.7.1

View file

@ -231,6 +231,90 @@ async def test_climate_hvac_mode(
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: