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 typing import Any
from xknx import XKNX 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 xknx.dpt.dpt_20 import HVACControllerMode
from homeassistant import config_entries 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: if self._device.supports_on_off and not self._device.is_on:
return HVACMode.OFF return HVACMode.OFF
if self._device.mode is not None and self._device.mode.supports_controller_mode: 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 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 return self.default_hvac_mode
@property @property
@ -261,11 +262,15 @@ class KNXClimate(KnxYamlEntity, ClimateEntity):
if self._device.supports_on_off: if self._device.supports_on_off:
if not ha_controller_modes: 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) ha_controller_modes.append(HVACMode.OFF)
hvac_modes = list(set(filter(None, ha_controller_modes))) 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 @property
def hvac_action(self) -> HVACAction | None: 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.unregister_device_updated_cb(self.after_update_callback)
self._device.mode.xknx.devices.async_remove(self._device.mode) self._device.mode.xknx.devices.async_remove(self._device.mode)
await super().async_will_remove_from_hass() 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"], "loggers": ["xknx", "xknxproject"],
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": [ "requirements": [
"xknx==3.0.0", "xknx==3.1.0",
"xknxproject==3.7.1", "xknxproject==3.7.1",
"knx-frontend==2024.8.9.225351" "knx-frontend==2024.8.9.225351"
], ],

View File

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

View File

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

View File

@ -231,6 +231,90 @@ async def test_climate_hvac_mode(
assert hass.states.get("climate.test").state == "cool" 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( async def test_climate_preset_mode(
hass: HomeAssistant, knx: KNXTestKit, entity_registry: er.EntityRegistry hass: HomeAssistant, knx: KNXTestKit, entity_registry: er.EntityRegistry
) -> None: ) -> None: