From 34a31884b7353724db88fea8dfba1391cd86d270 Mon Sep 17 00:00:00 2001 From: SukramJ Date: Tue, 8 Dec 2020 00:16:22 +0100 Subject: [PATCH] Bump dependency to add more multi channel devices to HomematicIP Cloud (#43914) --- .../homematicip_cloud/binary_sensor.py | 45 +- .../components/homematicip_cloud/cover.py | 150 +++++- .../homematicip_cloud/generic_entity.py | 8 +- .../components/homematicip_cloud/light.py | 50 +- .../homematicip_cloud/manifest.json | 2 +- .../components/homematicip_cloud/switch.py | 33 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../homematicip_cloud/test_binary_sensor.py | 14 +- .../homematicip_cloud/test_cover.py | 101 +++- .../homematicip_cloud/test_device.py | 2 +- .../homematicip_cloud/test_light.py | 6 +- .../homematicip_cloud/test_switch.py | 23 +- tests/fixtures/homematicip_cloud.json | 502 ++++++++++++++++++ 14 files changed, 823 insertions(+), 117 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index 12bca8378c3..57aaa2b0b01 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -6,6 +6,7 @@ from homematicip.aio.device import ( AsyncContactInterface, AsyncDevice, AsyncFullFlushContactInterface, + AsyncFullFlushContactInterface6, AsyncMotionDetectorIndoor, AsyncMotionDetectorOutdoor, AsyncMotionDetectorPushButton, @@ -91,6 +92,11 @@ async def async_setup_entry( entities.append( HomematicipMultiContactInterface(hap, device, channel=channel) ) + elif isinstance(device, AsyncFullFlushContactInterface6): + for channel in range(1, 7): + entities.append( + HomematicipMultiContactInterface(hap, device, channel=channel) + ) elif isinstance( device, (AsyncContactInterface, AsyncFullFlushContactInterface) ): @@ -224,9 +230,17 @@ class HomematicipTiltVibrationSensor(HomematicipBaseActionSensor): class HomematicipMultiContactInterface(HomematicipGenericEntity, BinarySensorEntity): """Representation of the HomematicIP multi room/area contact interface.""" - def __init__(self, hap: HomematicipHAP, device, channel: int) -> None: + def __init__( + self, + hap: HomematicipHAP, + device, + channel=1, + is_multi_channel=True, + ) -> None: """Initialize the multi contact entity.""" - super().__init__(hap, device, channel=channel) + super().__init__( + hap, device, channel=channel, is_multi_channel=is_multi_channel + ) @property def device_class(self) -> str: @@ -244,30 +258,22 @@ class HomematicipMultiContactInterface(HomematicipGenericEntity, BinarySensorEnt ) -class HomematicipContactInterface(HomematicipGenericEntity, BinarySensorEntity): +class HomematicipContactInterface(HomematicipMultiContactInterface, BinarySensorEntity): """Representation of the HomematicIP contact interface.""" - @property - def device_class(self) -> str: - """Return the class of this sensor.""" - return DEVICE_CLASS_OPENING - - @property - def is_on(self) -> bool: - """Return true if the contact interface is on/open.""" - if self._device.windowState is None: - return None - return self._device.windowState != WindowState.CLOSED + def __init__(self, hap: HomematicipHAP, device) -> None: + """Initialize the multi contact entity.""" + super().__init__(hap, device, is_multi_channel=False) -class HomematicipShutterContact(HomematicipGenericEntity, BinarySensorEntity): +class HomematicipShutterContact(HomematicipMultiContactInterface, BinarySensorEntity): """Representation of the HomematicIP shutter contact.""" def __init__( self, hap: HomematicipHAP, device, has_additional_state: bool = False ) -> None: """Initialize the shutter contact.""" - super().__init__(hap, device) + super().__init__(hap, device, is_multi_channel=False) self.has_additional_state = has_additional_state @property @@ -275,13 +281,6 @@ class HomematicipShutterContact(HomematicipGenericEntity, BinarySensorEntity): """Return the class of this sensor.""" return DEVICE_CLASS_DOOR - @property - def is_on(self) -> bool: - """Return true if the shutter contact is on/open.""" - if self._device.windowState is None: - return None - return self._device.windowState != WindowState.CLOSED - @property def device_state_attributes(self) -> Dict[str, Any]: """Return the state attributes of the Shutter Contact.""" diff --git a/homeassistant/components/homematicip_cloud/cover.py b/homeassistant/components/homematicip_cloud/cover.py index 3d5af9b7c4d..29a06c558fe 100644 --- a/homeassistant/components/homematicip_cloud/cover.py +++ b/homeassistant/components/homematicip_cloud/cover.py @@ -3,6 +3,7 @@ from typing import Optional from homematicip.aio.device import ( AsyncBlindModule, + AsyncDinRailBlind4, AsyncFullFlushBlind, AsyncFullFlushShutter, AsyncGarageDoorModuleTormatic, @@ -37,6 +38,11 @@ async def async_setup_entry( for device in hap.home.devices: if isinstance(device, AsyncBlindModule): entities.append(HomematicipBlindModule(hap, device)) + elif isinstance(device, AsyncDinRailBlind4): + for channel in range(1, 5): + entities.append( + HomematicipMultiCoverSlats(hap, device, channel=channel) + ) elif isinstance(device, AsyncFullFlushBlind): entities.append(HomematicipCoverSlats(hap, device)) elif isinstance(device, AsyncFullFlushShutter): @@ -130,14 +136,28 @@ class HomematicipBlindModule(HomematicipGenericEntity, CoverEntity): await self._device.stop() -class HomematicipCoverShutter(HomematicipGenericEntity, CoverEntity): +class HomematicipMultiCoverShutter(HomematicipGenericEntity, CoverEntity): """Representation of the HomematicIP cover shutter.""" + def __init__( + self, + hap: HomematicipHAP, + device, + channel=1, + is_multi_channel=True, + ) -> None: + """Initialize the multi cover entity.""" + super().__init__( + hap, device, channel=channel, is_multi_channel=is_multi_channel + ) + @property def current_cover_position(self) -> int: """Return current position of cover.""" - if self._device.shutterLevel is not None: - return int((1 - self._device.shutterLevel) * 100) + if self._device.functionalChannels[self._channel].shutterLevel is not None: + return int( + (1 - self._device.functionalChannels[self._channel].shutterLevel) * 100 + ) return None async def async_set_cover_position(self, **kwargs) -> None: @@ -145,36 +165,61 @@ class HomematicipCoverShutter(HomematicipGenericEntity, CoverEntity): position = kwargs[ATTR_POSITION] # HmIP cover is closed:1 -> open:0 level = 1 - position / 100.0 - await self._device.set_shutter_level(level) + await self._device.set_shutter_level(level, self._channel) @property def is_closed(self) -> Optional[bool]: """Return if the cover is closed.""" - if self._device.shutterLevel is not None: - return self._device.shutterLevel == HMIP_COVER_CLOSED + if self._device.functionalChannels[self._channel].shutterLevel is not None: + return ( + self._device.functionalChannels[self._channel].shutterLevel + == HMIP_COVER_CLOSED + ) return None async def async_open_cover(self, **kwargs) -> None: """Open the cover.""" - await self._device.set_shutter_level(HMIP_COVER_OPEN) + await self._device.set_shutter_level(HMIP_COVER_OPEN, self._channel) async def async_close_cover(self, **kwargs) -> None: """Close the cover.""" - await self._device.set_shutter_level(HMIP_COVER_CLOSED) + await self._device.set_shutter_level(HMIP_COVER_CLOSED, self._channel) async def async_stop_cover(self, **kwargs) -> None: """Stop the device if in motion.""" - await self._device.set_shutter_stop() + await self._device.set_shutter_stop(self._channel) -class HomematicipCoverSlats(HomematicipCoverShutter, CoverEntity): - """Representation of the HomematicIP cover slats.""" +class HomematicipCoverShutter(HomematicipMultiCoverShutter, CoverEntity): + """Representation of the HomematicIP cover shutter.""" + + def __init__(self, hap: HomematicipHAP, device) -> None: + """Initialize the multi cover entity.""" + super().__init__(hap, device, is_multi_channel=False) + + +class HomematicipMultiCoverSlats(HomematicipMultiCoverShutter, CoverEntity): + """Representation of the HomematicIP multi cover slats.""" + + def __init__( + self, + hap: HomematicipHAP, + device, + channel=1, + is_multi_channel=True, + ) -> None: + """Initialize the multi slats entity.""" + super().__init__( + hap, device, channel=channel, is_multi_channel=is_multi_channel + ) @property def current_cover_tilt_position(self) -> int: """Return current tilt position of cover.""" - if self._device.slatsLevel is not None: - return int((1 - self._device.slatsLevel) * 100) + if self._device.functionalChannels[self._channel].slatsLevel is not None: + return int( + (1 - self._device.functionalChannels[self._channel].slatsLevel) * 100 + ) return None async def async_set_cover_tilt_position(self, **kwargs) -> None: @@ -182,19 +227,27 @@ class HomematicipCoverSlats(HomematicipCoverShutter, CoverEntity): position = kwargs[ATTR_TILT_POSITION] # HmIP slats is closed:1 -> open:0 level = 1 - position / 100.0 - await self._device.set_slats_level(level) + await self._device.set_slats_level(level, self._channel) async def async_open_cover_tilt(self, **kwargs) -> None: """Open the slats.""" - await self._device.set_slats_level(HMIP_SLATS_OPEN) + await self._device.set_slats_level(HMIP_SLATS_OPEN, self._channel) async def async_close_cover_tilt(self, **kwargs) -> None: """Close the slats.""" - await self._device.set_slats_level(HMIP_SLATS_CLOSED) + await self._device.set_slats_level(HMIP_SLATS_CLOSED, self._channel) async def async_stop_cover_tilt(self, **kwargs) -> None: """Stop the device if in motion.""" - await self._device.set_shutter_stop() + await self._device.set_shutter_stop(self._channel) + + +class HomematicipCoverSlats(HomematicipMultiCoverSlats, CoverEntity): + """Representation of the HomematicIP cover slats.""" + + def __init__(self, hap: HomematicipHAP, device) -> None: + """Initialize the multi slats entity.""" + super().__init__(hap, device, is_multi_channel=False) class HomematicipGarageDoorModule(HomematicipGenericEntity, CoverEntity): @@ -229,10 +282,69 @@ class HomematicipGarageDoorModule(HomematicipGenericEntity, CoverEntity): await self._device.send_door_command(DoorCommand.STOP) -class HomematicipCoverShutterGroup(HomematicipCoverSlats, CoverEntity): +class HomematicipCoverShutterGroup(HomematicipGenericEntity, CoverEntity): """Representation of the HomematicIP cover shutter group.""" def __init__(self, hap: HomematicipHAP, device, post: str = "ShutterGroup") -> None: """Initialize switching group.""" device.modelType = f"HmIP-{post}" - super().__init__(hap, device, post) + super().__init__(hap, device, post, is_multi_channel=False) + + @property + def current_cover_position(self) -> int: + """Return current position of cover.""" + if self._device.shutterLevel is not None: + return int((1 - self._device.shutterLevel) * 100) + return None + + @property + def current_cover_tilt_position(self) -> int: + """Return current tilt position of cover.""" + if self._device.slatsLevel is not None: + return int((1 - self._device.slatsLevel) * 100) + return None + + @property + def is_closed(self) -> Optional[bool]: + """Return if the cover is closed.""" + if self._device.shutterLevel is not None: + return self._device.shutterLevel == HMIP_COVER_CLOSED + return None + + async def async_set_cover_position(self, **kwargs) -> None: + """Move the cover to a specific position.""" + position = kwargs[ATTR_POSITION] + # HmIP cover is closed:1 -> open:0 + level = 1 - position / 100.0 + await self._device.set_shutter_level(level) + + async def async_set_cover_tilt_position(self, **kwargs) -> None: + """Move the cover to a specific tilt position.""" + position = kwargs[ATTR_TILT_POSITION] + # HmIP slats is closed:1 -> open:0 + level = 1 - position / 100.0 + await self._device.set_slats_level(level) + + async def async_open_cover(self, **kwargs) -> None: + """Open the cover.""" + await self._device.set_shutter_level(HMIP_COVER_OPEN) + + async def async_close_cover(self, **kwargs) -> None: + """Close the cover.""" + await self._device.set_shutter_level(HMIP_COVER_CLOSED) + + async def async_stop_cover(self, **kwargs) -> None: + """Stop the group if in motion.""" + await self._device.set_shutter_stop() + + async def async_open_cover_tilt(self, **kwargs) -> None: + """Open the slats.""" + await self._device.set_slats_level(HMIP_SLATS_OPEN) + + async def async_close_cover_tilt(self, **kwargs) -> None: + """Close the slats.""" + await self._device.set_slats_level(HMIP_SLATS_CLOSED) + + async def async_stop_cover_tilt(self, **kwargs) -> None: + """Stop the group if in motion.""" + await self._device.set_shutter_stop() diff --git a/homeassistant/components/homematicip_cloud/generic_entity.py b/homeassistant/components/homematicip_cloud/generic_entity.py index ce8b44f5702..a8df0107eeb 100644 --- a/homeassistant/components/homematicip_cloud/generic_entity.py +++ b/homeassistant/components/homematicip_cloud/generic_entity.py @@ -76,6 +76,7 @@ class HomematicipGenericEntity(Entity): device, post: Optional[str] = None, channel: Optional[int] = None, + is_multi_channel: Optional[bool] = False, ) -> None: """Initialize the generic entity.""" self._hap = hap @@ -83,6 +84,7 @@ class HomematicipGenericEntity(Entity): self._device = device self._post = post self._channel = channel + self._is_multi_channel = is_multi_channel # Marker showing that the HmIP device hase been removed. self.hmip_device_removed = False _LOGGER.info("Setting up %s (%s)", self.name, self._device.modelType) @@ -179,7 +181,7 @@ class HomematicipGenericEntity(Entity): name = None # Try to get a label from a channel. if hasattr(self._device, "functionalChannels"): - if self._channel: + if self._is_multi_channel: name = self._device.functionalChannels[self._channel].label else: if len(self._device.functionalChannels) > 1: @@ -190,7 +192,7 @@ class HomematicipGenericEntity(Entity): name = self._device.label if self._post: name = f"{name} {self._post}" - elif self._channel: + elif self._is_multi_channel: name = f"{name} Channel{self._channel}" # Add a prefix to the name if the homematic ip home has a name. @@ -213,7 +215,7 @@ class HomematicipGenericEntity(Entity): def unique_id(self) -> str: """Return a unique ID.""" unique_id = f"{self.__class__.__name__}_{self._device.id}" - if self._channel: + if self._is_multi_channel: unique_id = ( f"{self.__class__.__name__}_Channel{self._channel}_{self._device.id}" ) diff --git a/homeassistant/components/homematicip_cloud/light.py b/homeassistant/components/homematicip_cloud/light.py index f0c191ac1a9..1909ff818b9 100644 --- a/homeassistant/components/homematicip_cloud/light.py +++ b/homeassistant/components/homematicip_cloud/light.py @@ -106,9 +106,17 @@ class HomematicipLightMeasuring(HomematicipLight): class HomematicipMultiDimmer(HomematicipGenericEntity, LightEntity): """Representation of HomematicIP Cloud dimmer.""" - def __init__(self, hap: HomematicipHAP, device, channel: int) -> None: + def __init__( + self, + hap: HomematicipHAP, + device, + channel=1, + is_multi_channel=True, + ) -> None: """Initialize the dimmer light entity.""" - super().__init__(hap, device, channel=channel) + super().__init__( + hap, device, channel=channel, is_multi_channel=is_multi_channel + ) @property def is_on(self) -> bool: @@ -142,38 +150,12 @@ class HomematicipMultiDimmer(HomematicipGenericEntity, LightEntity): await self._device.set_dim_level(0, self._channel) -class HomematicipDimmer(HomematicipGenericEntity, LightEntity): +class HomematicipDimmer(HomematicipMultiDimmer, LightEntity): """Representation of HomematicIP Cloud dimmer.""" def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the dimmer light entity.""" - super().__init__(hap, device) - - @property - def is_on(self) -> bool: - """Return true if dimmer is on.""" - return self._device.dimLevel is not None and self._device.dimLevel > 0.0 - - @property - def brightness(self) -> int: - """Return the brightness of this light between 0..255.""" - return int((self._device.dimLevel or 0.0) * 255) - - @property - def supported_features(self) -> int: - """Flag supported features.""" - return SUPPORT_BRIGHTNESS - - async def async_turn_on(self, **kwargs) -> None: - """Turn the dimmer on.""" - if ATTR_BRIGHTNESS in kwargs: - await self._device.set_dim_level(kwargs[ATTR_BRIGHTNESS] / 255.0) - else: - await self._device.set_dim_level(1) - - async def async_turn_off(self, **kwargs) -> None: - """Turn the dimmer off.""" - await self._device.set_dim_level(0) + super().__init__(hap, device, is_multi_channel=False) class HomematicipNotificationLight(HomematicipGenericEntity, LightEntity): @@ -182,9 +164,13 @@ class HomematicipNotificationLight(HomematicipGenericEntity, LightEntity): def __init__(self, hap: HomematicipHAP, device, channel: int) -> None: """Initialize the notification light entity.""" if channel == 2: - super().__init__(hap, device, post="Top", channel=channel) + super().__init__( + hap, device, post="Top", channel=channel, is_multi_channel=True + ) else: - super().__init__(hap, device, post="Bottom", channel=channel) + super().__init__( + hap, device, post="Bottom", channel=channel, is_multi_channel=True + ) self._color_switcher = { RGBColorState.WHITE: [0.0, 0.0], diff --git a/homeassistant/components/homematicip_cloud/manifest.json b/homeassistant/components/homematicip_cloud/manifest.json index 30ca5165c85..9f045694460 100644 --- a/homeassistant/components/homematicip_cloud/manifest.json +++ b/homeassistant/components/homematicip_cloud/manifest.json @@ -3,7 +3,7 @@ "name": "HomematicIP Cloud", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homematicip_cloud", - "requirements": ["homematicip==0.12.1"], + "requirements": ["homematicip==0.13.0"], "codeowners": ["@SukramJ"], "quality_scale": "platinum" } diff --git a/homeassistant/components/homematicip_cloud/switch.py b/homeassistant/components/homematicip_cloud/switch.py index 72f9f94c210..9047ed9095f 100644 --- a/homeassistant/components/homematicip_cloud/switch.py +++ b/homeassistant/components/homematicip_cloud/switch.py @@ -3,6 +3,7 @@ from typing import Any, Dict from homematicip.aio.device import ( AsyncBrandSwitchMeasuring, + AsyncDinRailSwitch4, AsyncFullFlushInputSwitch, AsyncFullFlushSwitchMeasuring, AsyncHeatingSwitch2, @@ -44,6 +45,9 @@ async def async_setup_entry( elif isinstance(device, AsyncWiredSwitch8): for channel in range(1, 9): entities.append(HomematicipMultiSwitch(hap, device, channel=channel)) + elif isinstance(device, AsyncDinRailSwitch4): + for channel in range(1, 5): + entities.append(HomematicipMultiSwitch(hap, device, channel=channel)) elif isinstance( device, ( @@ -77,9 +81,17 @@ async def async_setup_entry( class HomematicipMultiSwitch(HomematicipGenericEntity, SwitchEntity): """Representation of the HomematicIP multi switch.""" - def __init__(self, hap: HomematicipHAP, device, channel: int) -> None: + def __init__( + self, + hap: HomematicipHAP, + device, + channel=1, + is_multi_channel=True, + ) -> None: """Initialize the multi switch device.""" - super().__init__(hap, device, channel=channel) + super().__init__( + hap, device, channel=channel, is_multi_channel=is_multi_channel + ) @property def is_on(self) -> bool: @@ -95,25 +107,12 @@ class HomematicipMultiSwitch(HomematicipGenericEntity, SwitchEntity): await self._device.turn_off(self._channel) -class HomematicipSwitch(HomematicipGenericEntity, SwitchEntity): +class HomematicipSwitch(HomematicipMultiSwitch, SwitchEntity): """Representation of the HomematicIP switch.""" def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the switch device.""" - super().__init__(hap, device) - - @property - def is_on(self) -> bool: - """Return true if device is on.""" - return self._device.on - - async def async_turn_on(self, **kwargs) -> None: - """Turn the device on.""" - await self._device.turn_on() - - async def async_turn_off(self, **kwargs) -> None: - """Turn the device off.""" - await self._device.turn_off() + super().__init__(hap, device, is_multi_channel=False) class HomematicipGroupSwitch(HomematicipGenericEntity, SwitchEntity): diff --git a/requirements_all.txt b/requirements_all.txt index e2d87b8b1a6..4eddfe6dc75 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -774,7 +774,7 @@ homeassistant-pyozw==0.1.10 homeconnect==0.6.3 # homeassistant.components.homematicip_cloud -homematicip==0.12.1 +homematicip==0.13.0 # homeassistant.components.horizon horimote==0.4.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bfb3abbaca6..92c40967366 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -403,7 +403,7 @@ homeassistant-pyozw==0.1.10 homeconnect==0.6.3 # homeassistant.components.homematicip_cloud -homematicip==0.12.1 +homematicip==0.13.0 # homeassistant.components.google # homeassistant.components.remember_the_milk diff --git a/tests/components/homematicip_cloud/test_binary_sensor.py b/tests/components/homematicip_cloud/test_binary_sensor.py index 420977cd40c..c3922666665 100644 --- a/tests/components/homematicip_cloud/test_binary_sensor.py +++ b/tests/components/homematicip_cloud/test_binary_sensor.py @@ -540,13 +540,13 @@ async def test_hmip_security_sensor_group(hass, default_mock_hap_factory): assert ha_state.state == STATE_ON -async def test_hmip_wired_multi_contact_interface(hass, default_mock_hap_factory): +async def test_hmip_multi_contact_interface(hass, default_mock_hap_factory): """Test HomematicipMultiContactInterface.""" entity_id = "binary_sensor.wired_eingangsmodul_32_fach_channel5" entity_name = "Wired Eingangsmodul – 32-fach Channel5" device_model = "HmIPW-DRI32" mock_hap = await default_mock_hap_factory.async_get_mock_hap( - test_devices=["Wired Eingangsmodul – 32-fach"] + test_devices=["Wired Eingangsmodul – 32-fach", "Licht Flur"] ) ha_state, hmip_device = get_and_check_entity_basics( @@ -563,3 +563,13 @@ async def test_hmip_wired_multi_contact_interface(hass, default_mock_hap_factory await async_manipulate_test_data(hass, hmip_device, "windowState", None, channel=5) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OFF + + ha_state, hmip_device = get_and_check_entity_basics( + hass, + mock_hap, + "binary_sensor.licht_flur_5", + "Licht Flur 5", + "HmIP-FCI6", + ) + + assert ha_state.state == STATE_OFF diff --git a/tests/components/homematicip_cloud/test_cover.py b/tests/components/homematicip_cloud/test_cover.py index 82d2f41de59..a35576ed353 100644 --- a/tests/components/homematicip_cloud/test_cover.py +++ b/tests/components/homematicip_cloud/test_cover.py @@ -43,7 +43,7 @@ async def test_hmip_cover_shutter(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 1 assert hmip_device.mock_calls[-1][0] == "set_shutter_level" - assert hmip_device.mock_calls[-1][1] == (0,) + assert hmip_device.mock_calls[-1][1] == (0, 1) await async_manipulate_test_data(hass, hmip_device, "shutterLevel", 0) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OPEN @@ -57,7 +57,7 @@ async def test_hmip_cover_shutter(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 3 assert hmip_device.mock_calls[-1][0] == "set_shutter_level" - assert hmip_device.mock_calls[-1][1] == (0.5,) + assert hmip_device.mock_calls[-1][1] == (0.5, 1) await async_manipulate_test_data(hass, hmip_device, "shutterLevel", 0.5) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OPEN @@ -68,7 +68,7 @@ async def test_hmip_cover_shutter(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 5 assert hmip_device.mock_calls[-1][0] == "set_shutter_level" - assert hmip_device.mock_calls[-1][1] == (1,) + assert hmip_device.mock_calls[-1][1] == (1, 1) await async_manipulate_test_data(hass, hmip_device, "shutterLevel", 1) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_CLOSED @@ -79,7 +79,7 @@ async def test_hmip_cover_shutter(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 7 assert hmip_device.mock_calls[-1][0] == "set_shutter_stop" - assert hmip_device.mock_calls[-1][1] == () + assert hmip_device.mock_calls[-1][1] == (1,) await async_manipulate_test_data(hass, hmip_device, "shutterLevel", None) ha_state = hass.states.get(entity_id) @@ -109,7 +109,7 @@ async def test_hmip_cover_slats(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 1 assert hmip_device.mock_calls[-1][0] == "set_slats_level" - assert hmip_device.mock_calls[-1][1] == (0,) + assert hmip_device.mock_calls[-1][1] == (0, 1) await async_manipulate_test_data(hass, hmip_device, "shutterLevel", 0) await async_manipulate_test_data(hass, hmip_device, "slatsLevel", 0) ha_state = hass.states.get(entity_id) @@ -125,7 +125,7 @@ async def test_hmip_cover_slats(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 4 assert hmip_device.mock_calls[-1][0] == "set_slats_level" - assert hmip_device.mock_calls[-1][1] == (0.5,) + assert hmip_device.mock_calls[-1][1] == (0.5, 1) await async_manipulate_test_data(hass, hmip_device, "slatsLevel", 0.5) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OPEN @@ -137,7 +137,7 @@ async def test_hmip_cover_slats(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 6 assert hmip_device.mock_calls[-1][0] == "set_slats_level" - assert hmip_device.mock_calls[-1][1] == (1,) + assert hmip_device.mock_calls[-1][1] == (1, 1) await async_manipulate_test_data(hass, hmip_device, "slatsLevel", 1) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OPEN @@ -149,7 +149,7 @@ async def test_hmip_cover_slats(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 8 assert hmip_device.mock_calls[-1][0] == "set_shutter_stop" - assert hmip_device.mock_calls[-1][1] == () + assert hmip_device.mock_calls[-1][1] == (1,) await async_manipulate_test_data(hass, hmip_device, "slatsLevel", None) ha_state = hass.states.get(entity_id) @@ -160,6 +160,84 @@ async def test_hmip_cover_slats(hass, default_mock_hap_factory): assert ha_state.state == STATE_UNKNOWN +async def test_hmip_multi_cover_slats(hass, default_mock_hap_factory): + """Test HomematicipCoverSlats.""" + entity_id = "cover.wohnzimmer_fenster" + entity_name = "Wohnzimmer Fenster" + device_model = "HmIP-DRBLI4" + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=["Jalousieaktor 1 für Hutschienenmontage – 4-fach"] + ) + + ha_state, hmip_device = get_and_check_entity_basics( + hass, mock_hap, entity_id, entity_name, device_model + ) + + await async_manipulate_test_data(hass, hmip_device, "shutterLevel", 1, channel=4) + await async_manipulate_test_data(hass, hmip_device, "slatsLevel", 1, channel=4) + ha_state = hass.states.get(entity_id) + + assert ha_state.state == STATE_CLOSED + assert ha_state.attributes[ATTR_CURRENT_POSITION] == 0 + assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 0 + service_call_counter = len(hmip_device.mock_calls) + + await hass.services.async_call( + "cover", "open_cover_tilt", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 1 + assert hmip_device.mock_calls[-1][0] == "set_slats_level" + assert hmip_device.mock_calls[-1][1] == (0, 4) + await async_manipulate_test_data(hass, hmip_device, "shutterLevel", 0, channel=4) + await async_manipulate_test_data(hass, hmip_device, "slatsLevel", 0, channel=4) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OPEN + assert ha_state.attributes[ATTR_CURRENT_POSITION] == 100 + assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 100 + + await hass.services.async_call( + "cover", + "set_cover_tilt_position", + {"entity_id": entity_id, "tilt_position": "50"}, + blocking=True, + ) + assert len(hmip_device.mock_calls) == service_call_counter + 4 + assert hmip_device.mock_calls[-1][0] == "set_slats_level" + assert hmip_device.mock_calls[-1][1] == (0.5, 4) + await async_manipulate_test_data(hass, hmip_device, "slatsLevel", 0.5, channel=4) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OPEN + assert ha_state.attributes[ATTR_CURRENT_POSITION] == 100 + assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 50 + + await hass.services.async_call( + "cover", "close_cover_tilt", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 6 + assert hmip_device.mock_calls[-1][0] == "set_slats_level" + assert hmip_device.mock_calls[-1][1] == (1, 4) + await async_manipulate_test_data(hass, hmip_device, "slatsLevel", 1, channel=4) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OPEN + assert ha_state.attributes[ATTR_CURRENT_POSITION] == 100 + assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 0 + + await hass.services.async_call( + "cover", "stop_cover_tilt", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 8 + assert hmip_device.mock_calls[-1][0] == "set_shutter_stop" + assert hmip_device.mock_calls[-1][1] == (4,) + + await async_manipulate_test_data(hass, hmip_device, "slatsLevel", None, channel=4) + ha_state = hass.states.get(entity_id) + assert not ha_state.attributes.get(ATTR_CURRENT_TILT_POSITION) + + await async_manipulate_test_data(hass, hmip_device, "shutterLevel", None, channel=4) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_UNKNOWN + + async def test_hmip_blind_module(hass, default_mock_hap_factory): """Test HomematicipBlindModule.""" entity_id = "cover.sonnenschutz_balkontur" @@ -254,6 +332,13 @@ async def test_hmip_blind_module(hass, default_mock_hap_factory): assert hmip_device.mock_calls[-1][0] == "stop" assert hmip_device.mock_calls[-1][1] == () + await hass.services.async_call( + "cover", "stop_cover_tilt", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 14 + assert hmip_device.mock_calls[-1][0] == "stop" + assert hmip_device.mock_calls[-1][1] == () + await async_manipulate_test_data(hass, hmip_device, "secondaryShadingLevel", None) ha_state = hass.states.get(entity_id) assert not ha_state.attributes.get(ATTR_CURRENT_TILT_POSITION) diff --git a/tests/components/homematicip_cloud/test_device.py b/tests/components/homematicip_cloud/test_device.py index 4047a8ef28c..0e69a67cdbf 100644 --- a/tests/components/homematicip_cloud/test_device.py +++ b/tests/components/homematicip_cloud/test_device.py @@ -22,7 +22,7 @@ async def test_hmip_load_all_supported_devices(hass, default_mock_hap_factory): test_devices=None, test_groups=None ) - assert len(mock_hap.hmip_device_by_entity_id) == 236 + assert len(mock_hap.hmip_device_by_entity_id) == 250 async def test_hmip_remove_device(hass, default_mock_hap_factory): diff --git a/tests/components/homematicip_cloud/test_light.py b/tests/components/homematicip_cloud/test_light.py index b62a98fd03f..b4dbd0d140e 100644 --- a/tests/components/homematicip_cloud/test_light.py +++ b/tests/components/homematicip_cloud/test_light.py @@ -175,7 +175,7 @@ async def test_hmip_dimmer(hass, default_mock_hap_factory): "light", "turn_on", {"entity_id": entity_id}, blocking=True ) assert hmip_device.mock_calls[-1][0] == "set_dim_level" - assert hmip_device.mock_calls[-1][1] == (1,) + assert hmip_device.mock_calls[-1][1] == (1, 1) await hass.services.async_call( "light", @@ -185,7 +185,7 @@ async def test_hmip_dimmer(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 2 assert hmip_device.mock_calls[-1][0] == "set_dim_level" - assert hmip_device.mock_calls[-1][1] == (1.0,) + assert hmip_device.mock_calls[-1][1] == (1.0, 1) await async_manipulate_test_data(hass, hmip_device, "dimLevel", 1) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_ON @@ -196,7 +196,7 @@ async def test_hmip_dimmer(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 4 assert hmip_device.mock_calls[-1][0] == "set_dim_level" - assert hmip_device.mock_calls[-1][1] == (0,) + assert hmip_device.mock_calls[-1][1] == (0, 1) await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OFF diff --git a/tests/components/homematicip_cloud/test_switch.py b/tests/components/homematicip_cloud/test_switch.py index 034ca33aece..f2b3dfba32c 100644 --- a/tests/components/homematicip_cloud/test_switch.py +++ b/tests/components/homematicip_cloud/test_switch.py @@ -43,7 +43,7 @@ async def test_hmip_switch(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 1 assert hmip_device.mock_calls[-1][0] == "turn_off" - assert hmip_device.mock_calls[-1][1] == () + assert hmip_device.mock_calls[-1][1] == (1,) await async_manipulate_test_data(hass, hmip_device, "on", False) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OFF @@ -53,7 +53,7 @@ async def test_hmip_switch(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 3 assert hmip_device.mock_calls[-1][0] == "turn_on" - assert hmip_device.mock_calls[-1][1] == () + assert hmip_device.mock_calls[-1][1] == (1,) await async_manipulate_test_data(hass, hmip_device, "on", True) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_ON @@ -80,7 +80,7 @@ async def test_hmip_switch_input(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 1 assert hmip_device.mock_calls[-1][0] == "turn_off" - assert hmip_device.mock_calls[-1][1] == () + assert hmip_device.mock_calls[-1][1] == (1,) await async_manipulate_test_data(hass, hmip_device, "on", False) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OFF @@ -90,7 +90,7 @@ async def test_hmip_switch_input(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 3 assert hmip_device.mock_calls[-1][0] == "turn_on" - assert hmip_device.mock_calls[-1][1] == () + assert hmip_device.mock_calls[-1][1] == (1,) await async_manipulate_test_data(hass, hmip_device, "on", True) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_ON @@ -117,7 +117,7 @@ async def test_hmip_switch_measuring(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 1 assert hmip_device.mock_calls[-1][0] == "turn_off" - assert hmip_device.mock_calls[-1][1] == () + assert hmip_device.mock_calls[-1][1] == (1,) await async_manipulate_test_data(hass, hmip_device, "on", False) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OFF @@ -127,7 +127,7 @@ async def test_hmip_switch_measuring(hass, default_mock_hap_factory): ) assert len(hmip_device.mock_calls) == service_call_counter + 3 assert hmip_device.mock_calls[-1][0] == "turn_on" - assert hmip_device.mock_calls[-1][1] == () + assert hmip_device.mock_calls[-1][1] == (1,) await async_manipulate_test_data(hass, hmip_device, "on", True) await async_manipulate_test_data(hass, hmip_device, "currentPowerConsumption", 50) ha_state = hass.states.get(entity_id) @@ -191,6 +191,7 @@ async def test_hmip_multi_switch(hass, default_mock_hap_factory): "Multi IO Box", "Heizungsaktor", "ioBroker", + "Schaltaktor Verteiler", ] ) @@ -221,6 +222,16 @@ async def test_hmip_multi_switch(hass, default_mock_hap_factory): ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OFF + ha_state, hmip_device = get_and_check_entity_basics( + hass, + mock_hap, + "switch.schaltaktor_verteiler_channel3", + "Schaltaktor Verteiler Channel3", + "HmIP-DRSI4", + ) + + assert ha_state.state == STATE_OFF + async def test_hmip_wired_multi_switch(hass, default_mock_hap_factory): """Test HomematicipMultiSwitch.""" diff --git a/tests/fixtures/homematicip_cloud.json b/tests/fixtures/homematicip_cloud.json index 7adf4b85b1a..beb7e42400f 100644 --- a/tests/fixtures/homematicip_cloud.json +++ b/tests/fixtures/homematicip_cloud.json @@ -6116,6 +6116,508 @@ "serializedGlobalTradeItemNumber": "3014F0000000000000FAF9B4", "type": "TORMATIC_MODULE", "updateState": "UP_TO_DATE" + }, + "3014F7110000000000005521": { + "availableFirmwareVersion": "1.4.2", + "connectionType": "HMIP_RF", + "firmwareVersion": "1.4.2", + "firmwareVersionInteger": 66562, + "functionalChannels": { + "0": { + "busConfigMismatch": null, + "coProFaulty": false, + "coProRestartNeeded": false, + "coProUpdateFailure": false, + "configPending": false, + "deviceId": "3014F7110000000000005521", + "deviceOverheated": false, + "deviceOverloaded": false, + "devicePowerFailureDetected": false, + "deviceUndervoltage": false, + "dutyCycle": false, + "functionalChannelType": "DEVICE_BASE", + "groupIndex": 0, + "groups": [ + "00000000-0000-0000-0000-000000000034" + ], + "index": 0, + "label": "", + "lowBat": null, + "multicastRoutingEnabled": false, + "powerShortCircuit": null, + "routerModuleEnabled": false, + "routerModuleSupported": false, + "rssiDeviceValue": -82, + "rssiPeerValue": -78, + "shortCircuitDataLine": null, + "supportedOptionalFeatures": { + "IFeatureBusConfigMismatch": false, + "IFeatureDeviceCoProError": false, + "IFeatureDeviceCoProRestart": false, + "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceIdentify": true, + "IFeatureDeviceOverheated": true, + "IFeatureDeviceOverloaded": false, + "IFeatureDevicePowerFailure": true, + "IFeatureDeviceTemperatureOutOfRange": false, + "IFeatureDeviceUndervoltage": false, + "IFeatureMulticastRouter": false, + "IFeaturePowerShortCircuit": false, + "IFeatureRssiValue": true, + "IFeatureShortCircuitDataLine": false, + "IOptionalFeatureDutyCycle": true, + "IOptionalFeatureLowBat": false + }, + "temperatureOutOfRange": false, + "unreach": false + }, + "1": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000005521", + "functionalChannelType": "MULTI_MODE_INPUT_SWITCH_CHANNEL", + "groupIndex": 1, + "groups": [ + "00000000-0000-0000-0000-000000000035" + ], + "index": 1, + "label": "Poolpumpe", + "multiModeInputMode": "KEY_BEHAVIOR", + "on": false, + "profileMode": "AUTOMATIC", + "userDesiredProfileMode": "AUTOMATIC" + }, + "2": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000005521", + "functionalChannelType": "MULTI_MODE_INPUT_SWITCH_CHANNEL", + "groupIndex": 2, + "groups": [ + "00000000-0000-0000-0000-000000000035" + ], + "index": 2, + "label": "Poollicht", + "multiModeInputMode": "KEY_BEHAVIOR", + "on": false, + "profileMode": "AUTOMATIC", + "userDesiredProfileMode": "AUTOMATIC" + }, + "3": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000005521", + "functionalChannelType": "MULTI_MODE_INPUT_SWITCH_CHANNEL", + "groupIndex": 3, + "groups": [], + "index": 3, + "label": "", + "multiModeInputMode": "KEY_BEHAVIOR", + "on": false, + "profileMode": "AUTOMATIC", + "userDesiredProfileMode": "AUTOMATIC" + }, + "4": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000005521", + "functionalChannelType": "MULTI_MODE_INPUT_SWITCH_CHANNEL", + "groupIndex": 4, + "groups": [], + "index": 4, + "label": "", + "multiModeInputMode": "KEY_BEHAVIOR", + "on": false, + "profileMode": "AUTOMATIC", + "userDesiredProfileMode": "AUTOMATIC" + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F7110000000000005521", + "label": "Schaltaktor Verteiler", + "lastStatusUpdate": 1605271783993, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 405, + "modelType": "HmIP-DRSI4", + "oem": "eQ-3", + "permanentlyReachable": true, + "serializedGlobalTradeItemNumber": "3014F7110000000000005521", + "type": "DIN_RAIL_SWITCH_4", + "updateState": "UP_TO_DATE" + }, + "3014F7110000000000022311": { + "availableFirmwareVersion": "1.6.0", + "connectionType": "HMIP_RF", + "firmwareVersion": "1.6.0", + "firmwareVersionInteger": 67072, + "functionalChannels": { + "0": { + "busConfigMismatch": null, + "coProFaulty": false, + "coProRestartNeeded": false, + "coProUpdateFailure": false, + "configPending": false, + "deviceId": "3014F7110000000000022311", + "deviceOverheated": false, + "deviceOverloaded": false, + "devicePowerFailureDetected": false, + "deviceUndervoltage": false, + "dutyCycle": false, + "functionalChannelType": "DEVICE_BASE", + "groupIndex": 0, + "groups": [ + "00000000-0000-0000-0000-000000000026" + ], + "index": 0, + "label": "", + "lowBat": null, + "multicastRoutingEnabled": false, + "powerShortCircuit": null, + "routerModuleEnabled": false, + "routerModuleSupported": false, + "rssiDeviceValue": -70, + "rssiPeerValue": -63, + "shortCircuitDataLine": null, + "supportedOptionalFeatures": { + "IFeatureBusConfigMismatch": false, + "IFeatureDeviceCoProError": false, + "IFeatureDeviceCoProRestart": false, + "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceIdentify": true, + "IFeatureDeviceOverheated": true, + "IFeatureDeviceOverloaded": false, + "IFeatureDevicePowerFailure": true, + "IFeatureDeviceTemperatureOutOfRange": false, + "IFeatureDeviceUndervoltage": false, + "IFeatureMulticastRouter": false, + "IFeaturePowerShortCircuit": false, + "IFeatureRssiValue": true, + "IFeatureShortCircuitDataLine": false, + "IOptionalFeatureDutyCycle": true, + "IOptionalFeatureLowBat": false + }, + "temperatureOutOfRange": false, + "unreach": false + }, + "1": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "blindModeActive": false, + "bottomToTopReferenceTime": 18.19999999999999, + "changeOverDelay": 0.5, + "delayCompensationValue": 0.0, + "deviceId": "3014F7110000000000022311", + "endpositionAutoDetectionEnabled": false, + "favoritePrimaryShadingPosition": 0.5, + "favoriteSecondaryShadingPosition": 0.5, + "functionalChannelType": "MULTI_MODE_INPUT_BLIND_CHANNEL", + "groupIndex": 1, + "groups": [ + "00000000-0000-0000-0000-000000000027" + ], + "index": 1, + "label": "Badezimmer ", + "multiModeInputMode": "KEY_BEHAVIOR", + "previousShutterLevel": null, + "previousSlatsLevel": null, + "processing": false, + "profileMode": "AUTOMATIC", + "selfCalibrationInProgress": null, + "shutterLevel": 0.0, + "slatsLevel": null, + "slatsReferenceTime": 0.0, + "supportedOptionalFeatures": { + "IOptionalFeatureSlatsState": false + }, + "supportingDelayCompensation": true, + "supportingEndpositionAutoDetection": false, + "supportingSelfCalibration": false, + "topToBottomReferenceTime": 17.49999999999998, + "userDesiredProfileMode": "AUTOMATIC" + }, + "2": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "blindModeActive": false, + "bottomToTopReferenceTime": 17.899999999999984, + "changeOverDelay": 0.5, + "delayCompensationValue": 0.0, + "deviceId": "3014F7110000000000022311", + "endpositionAutoDetectionEnabled": false, + "favoritePrimaryShadingPosition": 0.5, + "favoriteSecondaryShadingPosition": 0.5, + "functionalChannelType": "MULTI_MODE_INPUT_BLIND_CHANNEL", + "groupIndex": 2, + "groups": [ + "00000000-0000-0000-0000-000000000028" + ], + "index": 2, + "label": "Schlafzimmer ", + "multiModeInputMode": "KEY_BEHAVIOR", + "previousShutterLevel": null, + "previousSlatsLevel": null, + "processing": false, + "profileMode": "AUTOMATIC", + "selfCalibrationInProgress": null, + "shutterLevel": 0.0, + "slatsLevel": null, + "slatsReferenceTime": 0.0, + "supportedOptionalFeatures": { + "IOptionalFeatureSlatsState": false + }, + "supportingDelayCompensation": true, + "supportingEndpositionAutoDetection": false, + "supportingSelfCalibration": false, + "topToBottomReferenceTime": 17.399999999999977, + "userDesiredProfileMode": "AUTOMATIC" + }, + "3": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "blindModeActive": false, + "bottomToTopReferenceTime": 27.300000000000118, + "changeOverDelay": 0.5, + "delayCompensationValue": 0.0, + "deviceId": "3014F7110000000000022311", + "endpositionAutoDetectionEnabled": false, + "favoritePrimaryShadingPosition": 0.5, + "favoriteSecondaryShadingPosition": 0.5, + "functionalChannelType": "MULTI_MODE_INPUT_BLIND_CHANNEL", + "groupIndex": 3, + "groups": [ + "00000000-0000-0000-0000-000000000029" + ], + "index": 3, + "label": "Wohnzimmer T\u00fcr", + "multiModeInputMode": "KEY_BEHAVIOR", + "previousShutterLevel": null, + "previousSlatsLevel": null, + "processing": false, + "profileMode": "AUTOMATIC", + "selfCalibrationInProgress": null, + "shutterLevel": 0.0, + "slatsLevel": null, + "slatsReferenceTime": 0.0, + "supportedOptionalFeatures": { + "IOptionalFeatureSlatsState": false + }, + "supportingDelayCompensation": true, + "supportingEndpositionAutoDetection": false, + "supportingSelfCalibration": false, + "topToBottomReferenceTime": 24.400000000000077, + "userDesiredProfileMode": "AUTOMATIC" + }, + "4": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "blindModeActive": false, + "bottomToTopReferenceTime": 25.900000000000098, + "changeOverDelay": 0.5, + "delayCompensationValue": 0.0, + "deviceId": "3014F7110000000000022311", + "endpositionAutoDetectionEnabled": false, + "favoritePrimaryShadingPosition": 0.5, + "favoriteSecondaryShadingPosition": 0.5, + "functionalChannelType": "MULTI_MODE_INPUT_BLIND_CHANNEL", + "groupIndex": 4, + "groups": [ + "00000000-0000-0000-0000-000000000029" + ], + "index": 4, + "label": "Wohnzimmer Fenster", + "multiModeInputMode": "KEY_BEHAVIOR", + "previousShutterLevel": null, + "previousSlatsLevel": null, + "processing": false, + "profileMode": "AUTOMATIC", + "selfCalibrationInProgress": null, + "shutterLevel": 0.0, + "slatsLevel": null, + "slatsReferenceTime": 0.0, + "supportedOptionalFeatures": { + "IOptionalFeatureSlatsState": false + }, + "supportingDelayCompensation": true, + "supportingEndpositionAutoDetection": false, + "supportingSelfCalibration": false, + "topToBottomReferenceTime": 25.000000000000085, + "userDesiredProfileMode": "AUTOMATIC" + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F7110000000000022311", + "label": "Jalousieaktor 1 f\u00fcr Hutschienenmontage \u2013 4-fach", + "lastStatusUpdate": 1604414124509, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 406, + "modelType": "HmIP-DRBLI4", + "oem": "eQ-3", + "permanentlyReachable": true, + "serializedGlobalTradeItemNumber": "3014F7110000000000022311", + "type": "DIN_RAIL_BLIND_4", + "updateState": "UP_TO_DATE" + }, + "3014F7110000000000056775": { + "availableFirmwareVersion": "1.0.16", + "connectionType": "HMIP_RF", + "firmwareVersion": "1.0.16", + "firmwareVersionInteger": 65552, + "functionalChannels": { + "0": { + "busConfigMismatch": null, + "coProFaulty": false, + "coProRestartNeeded": false, + "coProUpdateFailure": false, + "configPending": false, + "deviceId": "3014F7110000000000056775", + "deviceOverheated": false, + "deviceOverloaded": false, + "devicePowerFailureDetected": false, + "deviceUndervoltage": false, + "dutyCycle": null, + "functionalChannelType": "DEVICE_BASE", + "groupIndex": 0, + "groups": [ + "00000000-0000-0000-0000-000000000043" + ], + "index": 0, + "label": "", + "lowBat": null, + "multicastRoutingEnabled": false, + "powerShortCircuit": null, + "routerModuleEnabled": false, + "routerModuleSupported": false, + "rssiDeviceValue": null, + "rssiPeerValue": null, + "shortCircuitDataLine": null, + "supportedOptionalFeatures": { + "IFeatureBusConfigMismatch": false, + "IFeatureDeviceCoProError": false, + "IFeatureDeviceCoProRestart": false, + "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceIdentify": false, + "IFeatureDeviceOverheated": false, + "IFeatureDeviceOverloaded": false, + "IFeatureDevicePowerFailure": false, + "IFeatureDeviceTemperatureOutOfRange": false, + "IFeatureDeviceUndervoltage": false, + "IFeatureMulticastRouter": false, + "IFeaturePowerShortCircuit": false, + "IFeatureRssiValue": true, + "IFeatureShortCircuitDataLine": false, + "IOptionalFeatureDutyCycle": true, + "IOptionalFeatureLowBat": true + }, + "temperatureOutOfRange": false, + "unreach": null + }, + "1": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000056775", + "functionalChannelType": "MULTI_MODE_INPUT_CHANNEL", + "groupIndex": 1, + "groups": [ + "00000000-0000-0000-0000-000000000044", + "00000000-0000-0000-0000-000000000045" + ], + "index": 1, + "label": "Licht Flur 1", + "multiModeInputMode": "KEY_BEHAVIOR", + "supportedOptionalFeatures": { + "IOptionalFeatureWindowState": false + }, + "windowState": null + }, + "2": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000056775", + "functionalChannelType": "MULTI_MODE_INPUT_CHANNEL", + "groupIndex": 2, + "groups": [ + "00000000-0000-0000-0000-000000000044", + "00000000-0000-0000-0000-000000000006", + "00000000-0000-0000-0000-000000000047" + ], + "index": 2, + "label": "Licht Flur 2", + "multiModeInputMode": "KEY_BEHAVIOR", + "supportedOptionalFeatures": { + "IOptionalFeatureWindowState": false + }, + "windowState": null + }, + "3": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000056775", + "functionalChannelType": "MULTI_MODE_INPUT_CHANNEL", + "groupIndex": 3, + "groups": [ + "00000000-0000-0000-0000-000000000044" + ], + "index": 3, + "label": "Tür", + "multiModeInputMode": "KEY_BEHAVIOR", + "supportedOptionalFeatures": { + "IOptionalFeatureWindowState": true + }, + "windowState": "OPEN" + }, + "4": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000056775", + "functionalChannelType": "MULTI_MODE_INPUT_CHANNEL", + "groupIndex": 4, + "groups": [ + "00000000-0000-0000-0000-000000000044" + ], + "index": 4, + "label": "Licht Flur 4", + "multiModeInputMode": "KEY_BEHAVIOR", + "supportedOptionalFeatures": { + "IOptionalFeatureWindowState": false + }, + "windowState": null + }, + "5": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000056775", + "functionalChannelType": "MULTI_MODE_INPUT_CHANNEL", + "groupIndex": 5, + "groups": [ + "00000000-0000-0000-0000-000000000044" + ], + "index": 5, + "label": "Licht Flur 5", + "multiModeInputMode": "KEY_BEHAVIOR", + "supportedOptionalFeatures": { + "IOptionalFeatureWindowState": false + }, + "windowState": null + }, + "6": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "deviceId": "3014F7110000000000056775", + "functionalChannelType": "MULTI_MODE_INPUT_CHANNEL", + "groupIndex": 6, + "groups": [ + "00000000-0000-0000-0000-000000000044" + ], + "index": 6, + "label": "Licht Flur 6", + "multiModeInputMode": "KEY_BEHAVIOR", + "supportedOptionalFeatures": { + "IOptionalFeatureWindowState": false + }, + "windowState": null + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F7110000000000056775", + "label": "Licht Flur", + "lastStatusUpdate": 0, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 379, + "modelType": "HmIP-FCI6", + "oem": "eQ-3", + "permanentlyReachable": false, + "serializedGlobalTradeItemNumber": "3014F7110000000000056775", + "type": "FULL_FLUSH_CONTACT_INTERFACE_6", + "updateState": "UP_TO_DATE" } }, "groups": {