diff --git a/changelog.md b/changelog.md index 9294f277..790a4c4c 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,7 @@ - Add option to un ignore mechanism to ignore the automatic creation of custom entities by device type - Remove incomplete/wrong custom mapping for HBW-LC-RGBWW-IN6-DR +- Fix mapping of HmIP-HDM # Version 2024.2.1 (2024-02-02) diff --git a/hahomematic/platforms/custom/const.py b/hahomematic/platforms/custom/const.py index 14e8e19d..d68aec54 100644 --- a/hahomematic/platforms/custom/const.py +++ b/hahomematic/platforms/custom/const.py @@ -11,6 +11,7 @@ class DeviceProfile(StrEnum): IP_DIMMER = "IPDimmer" IP_FIXED_COLOR_LIGHT = "IPFixedColorLight" IP_GARAGE = "IPGarage" + IP_HDM = "IPHdm" IP_LOCK = "IPLock" IP_RGBW_LIGHT = "IPRGBW" IP_SIMPLE_FIXED_COLOR_LIGHT = "IPSimpleFixedColorLight" diff --git a/hahomematic/platforms/custom/cover.py b/hahomematic/platforms/custom/cover.py index 9b864175..91ad0e24 100644 --- a/hahomematic/platforms/custom/cover.py +++ b/hahomematic/platforms/custom/cover.py @@ -574,6 +574,21 @@ def make_ip_garage( ) +def make_ip_hdm( + device: hmd.HmDevice, + group_base_channels: tuple[int, ...], + extended: ExtendedConfig | None = None, +) -> tuple[CustomEntity, ...]: + """Create HomematicIP cover entities.""" + return hmed.make_custom_entity( + device=device, + entity_class=CeIpBlind, + device_profile=DeviceProfile.IP_HDM, + group_base_channels=group_base_channels, + extended=extended, + ) + + def make_rf_blind( device: hmd.HmDevice, group_base_channels: tuple[int, ...], @@ -650,7 +665,7 @@ def make_rf_window_drive( ), "HmIP-FBL": CustomConfig(func=make_ip_blind, channels=(3,)), "HmIP-FROLL": CustomConfig(func=make_ip_cover, channels=(3,)), - "HmIP-HDM": CustomConfig(func=make_ip_blind, channels=(0,)), + "HmIP-HDM": CustomConfig(func=make_ip_hdm, channels=(0,)), "HmIP-MOD-HO": CustomConfig(func=make_ip_garage, channels=(1,)), "HmIP-MOD-TM": CustomConfig(func=make_ip_garage, channels=(1,)), "HmIPW-DRBL4": CustomConfig( diff --git a/hahomematic/platforms/custom/definition.py b/hahomematic/platforms/custom/definition.py index d8d21326..fb1be35f 100644 --- a/hahomematic/platforms/custom/definition.py +++ b/hahomematic/platforms/custom/definition.py @@ -130,6 +130,22 @@ 1: (Parameter.STATE,), }, }, + DeviceProfile.IP_HDM: { + ED.DEVICE_GROUP: { + ED.PRIMARY_CHANNEL: 1, + ED.FIELDS: { + 1: { + Field.DIRECTION: Parameter.ACTIVITY_STATE, + Field.LEVEL: Parameter.LEVEL, + Field.LEVEL_2: Parameter.LEVEL_2, + Field.STOP: Parameter.STOP, + }, + 2: { + Field.COMBINED_PARAMETER: Parameter.COMBINED_PARAMETER, + }, + }, + }, + }, DeviceProfile.IP_FIXED_COLOR_LIGHT: { ED.DEVICE_GROUP: { ED.PRIMARY_CHANNEL: 1, diff --git a/requirements_test.txt b/requirements_test.txt index 5f5419d8..2aece529 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -11,10 +11,10 @@ pylint-per-file-ignores==1.3.2 pylint-strict-informational==0.1 pylint==3.0.3 pytest-aiohttp==1.0.5 -pytest-asyncio==0.23.4 +pytest-asyncio==0.23.5 pytest-cov==4.1.0 pytest-rerunfailures==13.0 pytest-socket==0.7.0 pytest-timeout==2.2.0 -pytest==7.4.4 +pytest==8.0.0 types-python-slugify==8.0.2.20240127 diff --git a/tests/test_central_pydevccu.py b/tests/test_central_pydevccu.py index 5802ec6a..78fc22dd 100644 --- a/tests/test_central_pydevccu.py +++ b/tests/test_central_pydevccu.py @@ -117,7 +117,7 @@ async def test_central_full(central_unit_full) -> None: assert usage_types[EntityUsage.CE_PRIMARY] == 181 assert usage_types[EntityUsage.ENTITY] == 3745 assert usage_types[EntityUsage.CE_VISIBLE] == 102 - assert usage_types[EntityUsage.CE_SECONDARY] == 148 + assert usage_types[EntityUsage.CE_SECONDARY] == 146 assert len(ce_channels) == 115 assert len(entity_types) == 6 diff --git a/tests/test_cover.py b/tests/test_cover.py index 9e1b942c..2e9c212e 100644 --- a/tests/test_cover.py +++ b/tests/test_cover.py @@ -22,14 +22,15 @@ from tests import const, helper TEST_DEVICES: dict[str, str] = { - "VCU8537918": "HmIP-BROLL.json", - "VCU7807849": "HmIPW-DRBL4.json", - "VCU1223813": "HmIP-FBL.json", "VCU0000045": "HM-LC-Bl1-FM.json", - "VCU3574044": "HmIP-MOD-HO.json", - "VCU6166407": "HmIP-MOD-TM.json", "VCU0000145": "HM-LC-JaX.json", "VCU0000350": "HM-Sec-Win.json", + "VCU1223813": "HmIP-FBL.json", + "VCU3560967": "HmIP-HDM1.json", + "VCU3574044": "HmIP-MOD-HO.json", + "VCU6166407": "HmIP-MOD-TM.json", + "VCU7807849": "HmIPW-DRBL4.json", + "VCU8537918": "HmIP-BROLL.json", } # pylint: disable=protected-access @@ -429,6 +430,127 @@ async def test_ceipblind(factory: helper.Factory) -> None: assert cover._channel_tilt_level == _CLOSED_LEVEL assert cover.current_tilt_position == 0 + central.event(const.INTERFACE_ID, "VCU1223813:3", "ACTIVITY_STATE", 1) + assert cover.is_opening + + central.event(const.INTERFACE_ID, "VCU1223813:3", "ACTIVITY_STATE", 2) + assert cover.is_closing + + central.event(const.INTERFACE_ID, "VCU1223813:3", "ACTIVITY_STATE", 3) + assert cover.is_opening is False + assert cover.is_closing is False + + await cover.stop() + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU1223813:4", paramset_key="VALUES", parameter="STOP", value=True + ) + + +@pytest.mark.asyncio +async def test_ceipblind_hdm(factory: helper.Factory) -> None: + """Test CeIpBlind HDM.""" + central, mock_client = await factory.get_default_central(TEST_DEVICES) + cover: CeIpBlind = cast(CeIpBlind, helper.get_prepared_custom_entity(central, "VCU3560967", 1)) + assert cover.usage == EntityUsage.CE_PRIMARY + + assert cover.current_position == 0 + assert cover.current_tilt_position == 0 + await cover.set_position(position=81) + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU3560967:2", + paramset_key="VALUES", + parameter="COMBINED_PARAMETER", + value="L2=0,L=81", + ) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL", 0.81) + assert cover.current_position == 81 + assert cover.current_tilt_position == 0 + + await cover.open() + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU3560967:2", + paramset_key="VALUES", + parameter="COMBINED_PARAMETER", + value="L2=100,L=100", + ) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL_2", 1.0) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL", 1.0) + assert cover.current_position == 100 + assert cover.current_tilt_position == 100 + + await cover.close() + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU3560967:2", + paramset_key="VALUES", + parameter="COMBINED_PARAMETER", + value="L2=0,L=0", + ) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL_2", 0.0) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL", 0.0) + assert cover.current_position == 0 + assert cover.current_tilt_position == 0 + + await cover.open_tilt() + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU3560967:2", + paramset_key="VALUES", + parameter="COMBINED_PARAMETER", + value="L2=100,L=0", + ) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL_2", 1.0) + assert cover.current_position == 0 + assert cover.current_tilt_position == 100 + + await cover.set_position(tilt_position=45) + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU3560967:2", + paramset_key="VALUES", + parameter="COMBINED_PARAMETER", + value="L2=45,L=0", + ) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL_2", 0.45) + assert cover.current_position == 0 + assert cover.current_tilt_position == 45 + + await cover.close_tilt() + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU3560967:2", + paramset_key="VALUES", + parameter="COMBINED_PARAMETER", + value="L2=0,L=0", + ) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL_2", 0.0) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL", 0.0) + assert cover.current_position == 0 + assert cover.current_tilt_position == 0 + + await cover.set_position(position=10, tilt_position=20) + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU3560967:2", + paramset_key="VALUES", + parameter="COMBINED_PARAMETER", + value="L2=20,L=10", + ) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL", 0.1) + central.event(const.INTERFACE_ID, "VCU3560967:1", "LEVEL_2", 0.2) + assert cover.current_position == 10 + assert cover.current_tilt_position == 20 + + central.event(const.INTERFACE_ID, "VCU3560967:1", "ACTIVITY_STATE", 1) + assert cover.is_opening + + central.event(const.INTERFACE_ID, "VCU3560967:1", "ACTIVITY_STATE", 2) + assert cover.is_closing + + central.event(const.INTERFACE_ID, "VCU3560967:1", "ACTIVITY_STATE", 3) + assert cover.is_opening is False + assert cover.is_closing is False + + await cover.stop() + assert mock_client.method_calls[-1] == call.set_value( + channel_address="VCU3560967:1", paramset_key="VALUES", parameter="STOP", value=True + ) + @pytest.mark.asyncio async def test_cegarageho(factory: helper.Factory) -> None: