From 90dbacb90903eca221be6eed75a2c5a7bf260d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Kn=C3=B6bel?= Date: Sun, 20 Oct 2024 11:54:58 +0200 Subject: [PATCH] Climate: Added humidity support (#1586) --- docs/changelog.md | 4 ++++ docs/climate.md | 2 ++ test/devices_tests/climate_test.py | 28 ++++++++++++++++++++++++++++ xknx/devices/climate.py | 13 +++++++++++++ 4 files changed, 47 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 7414990fc..e53f90997 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,10 @@ nav_order: 2 # Changelog +### Devices + +- Climate: Added humidity support + # 3.2.0 Climate Fan speed 2024-09-23 ### Devices diff --git a/docs/climate.md b/docs/climate.md index 5fd0111df..6a4655bcb 100644 --- a/docs/climate.md +++ b/docs/climate.md @@ -31,6 +31,7 @@ Climate are representations of KNX HVAC/Climate controls. - `on_off_invert` Invert on/off. Default: `False` - `group_address_active_state` KNX address for reading if the climate device is currently active. *DPT 1* - `group_address_command_value_state` KNX address for reading the current command value / valve position in %. *DPT 5.001* +- `group_address_humidity_state` KNX address of current room humidity. *DPT 9.007* - `sync_state` defines if and how often the value should be actively read from the bus. If `False` no GroupValueRead telegrams will be sent to its group address. Defaults to `True` - `max_temp` Maximum value for target temperature. - `min_temp` Minimum value for target temperature. @@ -94,6 +95,7 @@ climate = Climate( mode=climate_mode, device_updated_cb=None, fan_speed_mode=FanSpeedMode.STEP, + group_address_humidity_state='', ) xknx.devices.async_add(climate) xknx.devices.async_add(climate_mode) diff --git a/test/devices_tests/climate_test.py b/test/devices_tests/climate_test.py index 63fef737d..8a2dddee4 100644 --- a/test/devices_tests/climate_test.py +++ b/test/devices_tests/climate_test.py @@ -11,6 +11,7 @@ DPT2ByteFloat, DPTArray, DPTBinary, + DPTHumidity, DPTHVACContrMode, DPTHVACMode, DPTHVACStatus, @@ -84,6 +85,7 @@ def test_has_group_address(self): group_address_setpoint_shift_state="1/2/4", group_address_on_off="1/2/11", group_address_on_off_state="1/2/12", + group_address_humidity_state="1/2/16", ) assert climate.has_group_address(GroupAddress("1/2/1")) @@ -91,6 +93,7 @@ def test_has_group_address(self): assert climate.has_group_address(GroupAddress("1/2/4")) assert climate.has_group_address(GroupAddress("1/2/11")) assert climate.has_group_address(GroupAddress("1/2/12")) + assert climate.has_group_address(GroupAddress("1/2/16")) assert not climate.has_group_address(GroupAddress("1/2/99")) # @@ -1073,6 +1076,15 @@ async def test_sync_mode_from_climate(self): telegram1 = xknx.telegrams.get_nowait() assert telegram1 == Telegram(GroupAddress("1/2/4"), payload=GroupValueRead()) + async def test_sync_humidity(self): + """Test sync function / sending group reads to KNX bus for humidity.""" + xknx = XKNX() + climate = Climate(xknx, "TestClimate", group_address_humidity_state="1/2/16") + await climate.sync() + assert xknx.telegrams.qsize() == 1 + telegram1 = xknx.telegrams.get_nowait() + assert telegram1 == Telegram(GroupAddress("1/2/16"), payload=GroupValueRead()) + # # TEST PROCESS # @@ -1258,6 +1270,22 @@ async def test_process_heat_cool(self): climate_mode.process(telegram) assert climate_mode.controller_mode == HVACControllerMode.HEAT + async def test_process_humidity(self): + """Test process / reading telegrams from telegram queue. Test if humidity is processed correctly.""" + xknx = XKNX() + climate = Climate(xknx, "TestClimate", group_address_humidity_state="1/2/16") + after_update_callback = Mock() + climate.register_device_updated_cb(after_update_callback) + + telegram = Telegram( + destination_address=GroupAddress("1/2/16"), + payload=GroupValueWrite(DPTHumidity.to_knx(45.6)), + ) + climate.process(telegram) + + assert climate.humidity.value == 45.6 + after_update_callback.assert_called_with(climate) + # # SUPPORTED OPERATION MODES # diff --git a/xknx/devices/climate.py b/xknx/devices/climate.py index dc54c75b5..aa57eb0c2 100644 --- a/xknx/devices/climate.py +++ b/xknx/devices/climate.py @@ -16,6 +16,7 @@ GroupAddressesType, RemoteValue, RemoteValueDptValue1Ucount, + RemoteValueNumeric, RemoteValueScaling, RemoteValueSetpointShift, RemoteValueSwitch, @@ -68,6 +69,7 @@ def __init__( group_address_fan_speed: GroupAddressesType = None, group_address_fan_speed_state: GroupAddressesType = None, fan_speed_mode: FanSpeedMode = FanSpeedMode.PERCENT, + group_address_humidity_state: GroupAddressesType = None, ): """Initialize Climate class.""" super().__init__(xknx, name, device_updated_cb) @@ -168,6 +170,16 @@ def __init__( self.mode = mode + self.humidity = RemoteValueNumeric( + xknx, + group_address_state=group_address_humidity_state, + sync_state=sync_state, + value_type="humidity", + device_name=self.name, + feature_name="Current humidity", + after_update_cb=self.after_update, + ) + def _iter_remote_values(self) -> Iterator[RemoteValue[Any]]: """Iterate the devices RemoteValue classes.""" yield self.temperature @@ -177,6 +189,7 @@ def _iter_remote_values(self) -> Iterator[RemoteValue[Any]]: yield self.active yield self.command_value yield self.fan_speed + yield self.humidity def has_group_address(self, group_address: DeviceGroupAddress) -> bool: """Test if device has given group address."""