mirror of
https://github.com/home-assistant/core.git
synced 2025-12-20 19:09:45 +00:00
Handle variable number of channels for HmIPW-DRI16 and HmIPW-DRI32 in homematicip_cloud integration (#151201)
This commit is contained in:
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homematicip.base.enums import SmokeDetectorAlarmType, WindowState
|
from homematicip.base.enums import SmokeDetectorAlarmType, WindowState
|
||||||
|
from homematicip.base.functionalChannels import MultiModeInputChannel
|
||||||
from homematicip.device import (
|
from homematicip.device import (
|
||||||
AccelerationSensor,
|
AccelerationSensor,
|
||||||
ContactInterface,
|
ContactInterface,
|
||||||
@@ -87,8 +88,11 @@ async def async_setup_entry(
|
|||||||
entities.append(HomematicipTiltVibrationSensor(hap, device))
|
entities.append(HomematicipTiltVibrationSensor(hap, device))
|
||||||
if isinstance(device, WiredInput32):
|
if isinstance(device, WiredInput32):
|
||||||
entities.extend(
|
entities.extend(
|
||||||
HomematicipMultiContactInterface(hap, device, channel=channel)
|
HomematicipMultiContactInterface(
|
||||||
for channel in range(1, 33)
|
hap, device, channel_real_index=channel.index
|
||||||
|
)
|
||||||
|
for channel in device.functionalChannels
|
||||||
|
if isinstance(channel, MultiModeInputChannel)
|
||||||
)
|
)
|
||||||
elif isinstance(device, FullFlushContactInterface6):
|
elif isinstance(device, FullFlushContactInterface6):
|
||||||
entities.extend(
|
entities.extend(
|
||||||
@@ -227,21 +231,24 @@ class HomematicipMultiContactInterface(HomematicipGenericEntity, BinarySensorEnt
|
|||||||
device,
|
device,
|
||||||
channel=1,
|
channel=1,
|
||||||
is_multi_channel=True,
|
is_multi_channel=True,
|
||||||
|
channel_real_index=None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the multi contact entity."""
|
"""Initialize the multi contact entity."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hap, device, channel=channel, is_multi_channel=is_multi_channel
|
hap,
|
||||||
|
device,
|
||||||
|
channel=channel,
|
||||||
|
is_multi_channel=is_multi_channel,
|
||||||
|
channel_real_index=channel_real_index,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool | None:
|
def is_on(self) -> bool | None:
|
||||||
"""Return true if the contact interface is on/open."""
|
"""Return true if the contact interface is on/open."""
|
||||||
if self._device.functionalChannels[self._channel].windowState is None:
|
channel = self.get_channel_or_raise()
|
||||||
|
if channel.windowState is None:
|
||||||
return None
|
return None
|
||||||
return (
|
return channel.windowState != WindowState.CLOSED
|
||||||
self._device.functionalChannels[self._channel].windowState
|
|
||||||
!= WindowState.CLOSED
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class HomematicipContactInterface(HomematicipMultiContactInterface, BinarySensorEntity):
|
class HomematicipContactInterface(HomematicipMultiContactInterface, BinarySensorEntity):
|
||||||
|
|||||||
@@ -283,19 +283,23 @@ class HomematicipGarageDoorModule(HomematicipGenericEntity, CoverEntity):
|
|||||||
@property
|
@property
|
||||||
def is_closed(self) -> bool | None:
|
def is_closed(self) -> bool | None:
|
||||||
"""Return if the cover is closed."""
|
"""Return if the cover is closed."""
|
||||||
return self.functional_channel.doorState == DoorState.CLOSED
|
channel = self.get_channel_or_raise()
|
||||||
|
return channel.doorState == DoorState.CLOSED
|
||||||
|
|
||||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||||
"""Open the cover."""
|
"""Open the cover."""
|
||||||
await self.functional_channel.async_send_door_command(DoorCommand.OPEN)
|
channel = self.get_channel_or_raise()
|
||||||
|
await channel.async_send_door_command(DoorCommand.OPEN)
|
||||||
|
|
||||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||||
"""Close the cover."""
|
"""Close the cover."""
|
||||||
await self.functional_channel.async_send_door_command(DoorCommand.CLOSE)
|
channel = self.get_channel_or_raise()
|
||||||
|
await channel.async_send_door_command(DoorCommand.CLOSE)
|
||||||
|
|
||||||
async def async_stop_cover(self, **kwargs: Any) -> None:
|
async def async_stop_cover(self, **kwargs: Any) -> None:
|
||||||
"""Stop the cover."""
|
"""Stop the cover."""
|
||||||
await self.functional_channel.async_send_door_command(DoorCommand.STOP)
|
channel = self.get_channel_or_raise()
|
||||||
|
await channel.async_send_door_command(DoorCommand.STOP)
|
||||||
|
|
||||||
|
|
||||||
class HomematicipCoverShutterGroup(HomematicipGenericEntity, CoverEntity):
|
class HomematicipCoverShutterGroup(HomematicipGenericEntity, CoverEntity):
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -84,6 +85,7 @@ class HomematicipGenericEntity(Entity):
|
|||||||
post: str | None = None,
|
post: str | None = None,
|
||||||
channel: int | None = None,
|
channel: int | None = None,
|
||||||
is_multi_channel: bool | None = False,
|
is_multi_channel: bool | None = False,
|
||||||
|
channel_real_index: int | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the generic entity."""
|
"""Initialize the generic entity."""
|
||||||
self._hap = hap
|
self._hap = hap
|
||||||
@@ -91,8 +93,19 @@ class HomematicipGenericEntity(Entity):
|
|||||||
self._device = device
|
self._device = device
|
||||||
self._post = post
|
self._post = post
|
||||||
self._channel = channel
|
self._channel = channel
|
||||||
|
|
||||||
|
# channel_real_index represents the actual index of the devices channel.
|
||||||
|
# Accessing a functionalChannel by the channel parameter or array index is unreliable,
|
||||||
|
# because the functionalChannels array is sorted as strings, not numbers.
|
||||||
|
# For example, channels are ordered as: 1, 10, 11, 12, 2, 3, ...
|
||||||
|
# Using channel_real_index ensures you reference the correct channel.
|
||||||
|
self._channel_real_index: int | None = channel_real_index
|
||||||
|
|
||||||
self._is_multi_channel = is_multi_channel
|
self._is_multi_channel = is_multi_channel
|
||||||
|
self.functional_channel = None
|
||||||
|
with contextlib.suppress(ValueError):
|
||||||
self.functional_channel = self.get_current_channel()
|
self.functional_channel = self.get_current_channel()
|
||||||
|
|
||||||
# Marker showing that the HmIP device hase been removed.
|
# Marker showing that the HmIP device hase been removed.
|
||||||
self.hmip_device_removed = False
|
self.hmip_device_removed = False
|
||||||
|
|
||||||
@@ -101,17 +114,20 @@ class HomematicipGenericEntity(Entity):
|
|||||||
"""Return device specific attributes."""
|
"""Return device specific attributes."""
|
||||||
# Only physical devices should be HA devices.
|
# Only physical devices should be HA devices.
|
||||||
if isinstance(self._device, Device):
|
if isinstance(self._device, Device):
|
||||||
|
device_id = str(self._device.id)
|
||||||
|
home_id = str(self._device.homeId)
|
||||||
|
|
||||||
return DeviceInfo(
|
return DeviceInfo(
|
||||||
identifiers={
|
identifiers={
|
||||||
# Serial numbers of Homematic IP device
|
# Serial numbers of Homematic IP device
|
||||||
(DOMAIN, self._device.id)
|
(DOMAIN, device_id)
|
||||||
},
|
},
|
||||||
manufacturer=self._device.oem,
|
manufacturer=self._device.oem,
|
||||||
model=self._device.modelType,
|
model=self._device.modelType,
|
||||||
name=self._device.label,
|
name=self._device.label,
|
||||||
sw_version=self._device.firmwareVersion,
|
sw_version=self._device.firmwareVersion,
|
||||||
# Link to the homematic ip access point.
|
# Link to the homematic ip access point.
|
||||||
via_device=(DOMAIN, self._device.homeId),
|
via_device=(DOMAIN, home_id),
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -185,25 +201,31 @@ class HomematicipGenericEntity(Entity):
|
|||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""Return the name of the generic entity."""
|
"""Return the name of the generic entity."""
|
||||||
|
|
||||||
name = None
|
name = ""
|
||||||
# Try to get a label from a channel.
|
# Try to get a label from a channel.
|
||||||
if hasattr(self._device, "functionalChannels"):
|
functional_channels = getattr(self._device, "functionalChannels", None)
|
||||||
|
if functional_channels and self.functional_channel:
|
||||||
if self._is_multi_channel:
|
if self._is_multi_channel:
|
||||||
name = self._device.functionalChannels[self._channel].label
|
label = getattr(self.functional_channel, "label", None)
|
||||||
elif len(self._device.functionalChannels) > 1:
|
if label:
|
||||||
name = self._device.functionalChannels[1].label
|
name = str(label)
|
||||||
|
elif len(functional_channels) > 1:
|
||||||
|
label = getattr(functional_channels[1], "label", None)
|
||||||
|
if label:
|
||||||
|
name = str(label)
|
||||||
|
|
||||||
# Use device label, if name is not defined by channel label.
|
# Use device label, if name is not defined by channel label.
|
||||||
if not name:
|
if not name:
|
||||||
name = self._device.label
|
name = self._device.label or ""
|
||||||
if self._post:
|
if self._post:
|
||||||
name = f"{name} {self._post}"
|
name = f"{name} {self._post}"
|
||||||
elif self._is_multi_channel:
|
elif self._is_multi_channel:
|
||||||
name = f"{name} Channel{self._channel}"
|
name = f"{name} Channel{self.get_channel_index()}"
|
||||||
|
|
||||||
# Add a prefix to the name if the homematic ip home has a name.
|
# Add a prefix to the name if the homematic ip home has a name.
|
||||||
if name and self._home.name:
|
home_name = getattr(self._home, "name", None)
|
||||||
name = f"{self._home.name} {name}"
|
if name and home_name:
|
||||||
|
name = f"{home_name} {name}"
|
||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
@@ -217,9 +239,7 @@ class HomematicipGenericEntity(Entity):
|
|||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
unique_id = f"{self.__class__.__name__}_{self._device.id}"
|
unique_id = f"{self.__class__.__name__}_{self._device.id}"
|
||||||
if self._is_multi_channel:
|
if self._is_multi_channel:
|
||||||
unique_id = (
|
unique_id = f"{self.__class__.__name__}_Channel{self.get_channel_index()}_{self._device.id}"
|
||||||
f"{self.__class__.__name__}_Channel{self._channel}_{self._device.id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return unique_id
|
return unique_id
|
||||||
|
|
||||||
@@ -254,12 +274,65 @@ class HomematicipGenericEntity(Entity):
|
|||||||
return state_attr
|
return state_attr
|
||||||
|
|
||||||
def get_current_channel(self) -> FunctionalChannel:
|
def get_current_channel(self) -> FunctionalChannel:
|
||||||
"""Return the FunctionalChannel for device."""
|
"""Return the FunctionalChannel for the device.
|
||||||
if hasattr(self._device, "functionalChannels"):
|
|
||||||
|
Resolution priority:
|
||||||
|
1. For multi-channel entities with a real index, find channel by index match.
|
||||||
|
2. For multi-channel entities without a real index, use the provided channel position.
|
||||||
|
3. For non multi-channel entities with >1 channels, use channel at position 1
|
||||||
|
(index 0 is often a meta/service channel in HmIP).
|
||||||
|
Raises ValueError if no suitable channel can be resolved.
|
||||||
|
"""
|
||||||
|
functional_channels = getattr(self._device, "functionalChannels", None)
|
||||||
|
if not functional_channels:
|
||||||
|
raise ValueError(
|
||||||
|
f"Device {getattr(self._device, 'id', 'unknown')} has no functionalChannels"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Multi-channel handling
|
||||||
if self._is_multi_channel:
|
if self._is_multi_channel:
|
||||||
return self._device.functionalChannels[self._channel]
|
# Prefer real index mapping when provided to avoid ordering issues.
|
||||||
|
if self._channel_real_index is not None:
|
||||||
|
for channel in functional_channels:
|
||||||
|
if channel.index == self._channel_real_index:
|
||||||
|
return channel
|
||||||
|
raise ValueError(
|
||||||
|
f"Real channel index {self._channel_real_index} not found for device "
|
||||||
|
f"{getattr(self._device, 'id', 'unknown')}"
|
||||||
|
)
|
||||||
|
# Fallback: positional channel (already sorted as strings upstream).
|
||||||
|
if self._channel is not None and 0 <= self._channel < len(
|
||||||
|
functional_channels
|
||||||
|
):
|
||||||
|
return functional_channels[self._channel]
|
||||||
|
raise ValueError(
|
||||||
|
f"Channel position {self._channel} invalid for device "
|
||||||
|
f"{getattr(self._device, 'id', 'unknown')} (len={len(functional_channels)})"
|
||||||
|
)
|
||||||
|
|
||||||
if len(self._device.functionalChannels) > 1:
|
# Single-channel / non multi-channel entity: choose second element if available
|
||||||
return self._device.functionalChannels[1]
|
if len(functional_channels) > 1:
|
||||||
|
return functional_channels[1]
|
||||||
|
return functional_channels[0]
|
||||||
|
|
||||||
return None
|
def get_channel_index(self) -> int:
|
||||||
|
"""Return the correct channel index for this entity.
|
||||||
|
|
||||||
|
Prefers channel_real_index if set, otherwise returns channel.
|
||||||
|
This ensures the correct channel is used even if the functionalChannels list is not numerically ordered.
|
||||||
|
"""
|
||||||
|
if self._channel_real_index is not None:
|
||||||
|
return self._channel_real_index
|
||||||
|
|
||||||
|
if self._channel is not None:
|
||||||
|
return self._channel
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def get_channel_or_raise(self) -> FunctionalChannel:
|
||||||
|
"""Return the FunctionalChannel or raise an error if not found."""
|
||||||
|
if not self.functional_channel:
|
||||||
|
raise ValueError(
|
||||||
|
f"No functional channel found for device {getattr(self._device, 'id', 'unknown')}"
|
||||||
|
)
|
||||||
|
return self.functional_channel
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ class HomematicipDoorBellEvent(HomematicipGenericEntity, EventEntity):
|
|||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
self.functional_channel.add_on_channel_event_handler(self._async_handle_event)
|
|
||||||
|
channel = self.get_channel_or_raise()
|
||||||
|
channel.add_on_channel_event_handler(self._async_handle_event)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_handle_event(self, *args, **kwargs) -> None:
|
def _async_handle_event(self, *args, **kwargs) -> None:
|
||||||
|
|||||||
@@ -134,49 +134,49 @@ class HomematicipLightHS(HomematicipGenericEntity, LightEntity):
|
|||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return true if light is on."""
|
"""Return true if light is on."""
|
||||||
return self.functional_channel.on
|
channel = self.get_channel_or_raise()
|
||||||
|
return channel.on
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brightness(self) -> int | None:
|
def brightness(self) -> int | None:
|
||||||
"""Return the current brightness."""
|
"""Return the current brightness."""
|
||||||
return int(self.functional_channel.dimLevel * 255.0)
|
channel = self.get_channel_or_raise()
|
||||||
|
return int(channel.dimLevel * 255.0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hs_color(self) -> tuple[float, float] | None:
|
def hs_color(self) -> tuple[float, float] | None:
|
||||||
"""Return the hue and saturation color value [float, float]."""
|
"""Return the hue and saturation color value [float, float]."""
|
||||||
if (
|
channel = self.get_channel_or_raise()
|
||||||
self.functional_channel.hue is None
|
if channel.hue is None or channel.saturationLevel is None:
|
||||||
or self.functional_channel.saturationLevel is None
|
|
||||||
):
|
|
||||||
return None
|
return None
|
||||||
return (
|
return (
|
||||||
self.functional_channel.hue,
|
channel.hue,
|
||||||
self.functional_channel.saturationLevel * 100.0,
|
channel.saturationLevel * 100.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the light on."""
|
"""Turn the light on."""
|
||||||
|
channel = self.get_channel_or_raise()
|
||||||
hs_color = kwargs.get(ATTR_HS_COLOR, (0.0, 0.0))
|
hs_color = kwargs.get(ATTR_HS_COLOR, (0.0, 0.0))
|
||||||
hue = hs_color[0] % 360.0
|
hue = hs_color[0] % 360.0
|
||||||
saturation = hs_color[1] / 100.0
|
saturation = hs_color[1] / 100.0
|
||||||
dim_level = round(kwargs.get(ATTR_BRIGHTNESS, 255) / 255.0, 2)
|
dim_level = round(kwargs.get(ATTR_BRIGHTNESS, 255) / 255.0, 2)
|
||||||
|
|
||||||
if ATTR_HS_COLOR not in kwargs:
|
if ATTR_HS_COLOR not in kwargs:
|
||||||
hue = self.functional_channel.hue
|
hue = channel.hue
|
||||||
saturation = self.functional_channel.saturationLevel
|
saturation = channel.saturationLevel
|
||||||
|
|
||||||
if ATTR_BRIGHTNESS not in kwargs:
|
if ATTR_BRIGHTNESS not in kwargs:
|
||||||
# If no brightness is set, use the current brightness
|
# If no brightness is set, use the current brightness
|
||||||
dim_level = self.functional_channel.dimLevel or 1.0
|
dim_level = channel.dimLevel or 1.0
|
||||||
|
await channel.set_hue_saturation_dim_level_async(
|
||||||
await self.functional_channel.set_hue_saturation_dim_level_async(
|
|
||||||
hue=hue, saturation_level=saturation, dim_level=dim_level
|
hue=hue, saturation_level=saturation, dim_level=dim_level
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the light off."""
|
"""Turn the light off."""
|
||||||
await self.functional_channel.set_switch_state_async(on=False)
|
channel = self.get_channel_or_raise()
|
||||||
|
await channel.set_switch_state_async(on=False)
|
||||||
|
|
||||||
|
|
||||||
class HomematicipLightMeasuring(HomematicipLight):
|
class HomematicipLightMeasuring(HomematicipLight):
|
||||||
|
|||||||
@@ -307,7 +307,8 @@ class HomematicipWaterFlowSensor(HomematicipGenericEntity, SensorEntity):
|
|||||||
@property
|
@property
|
||||||
def native_value(self) -> float | None:
|
def native_value(self) -> float | None:
|
||||||
"""Return the state."""
|
"""Return the state."""
|
||||||
return self.functional_channel.waterFlow
|
channel = self.get_channel_or_raise()
|
||||||
|
return channel.waterFlow
|
||||||
|
|
||||||
|
|
||||||
class HomematicipWaterVolumeSensor(HomematicipGenericEntity, SensorEntity):
|
class HomematicipWaterVolumeSensor(HomematicipGenericEntity, SensorEntity):
|
||||||
|
|||||||
@@ -113,15 +113,18 @@ class HomematicipMultiSwitch(HomematicipGenericEntity, SwitchEntity):
|
|||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return true if switch is on."""
|
"""Return true if switch is on."""
|
||||||
return self.functional_channel.on
|
channel = self.get_channel_or_raise()
|
||||||
|
return channel.on
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch on."""
|
"""Turn the switch on."""
|
||||||
await self.functional_channel.async_turn_on()
|
channel = self.get_channel_or_raise()
|
||||||
|
await channel.async_turn_on()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch off."""
|
"""Turn the switch off."""
|
||||||
await self.functional_channel.async_turn_off()
|
channel = self.get_channel_or_raise()
|
||||||
|
await channel.async_turn_off()
|
||||||
|
|
||||||
|
|
||||||
class HomematicipSwitch(HomematicipMultiSwitch, SwitchEntity):
|
class HomematicipSwitch(HomematicipMultiSwitch, SwitchEntity):
|
||||||
|
|||||||
@@ -47,13 +47,16 @@ class HomematicipWateringValve(HomematicipGenericEntity, ValveEntity):
|
|||||||
|
|
||||||
async def async_open_valve(self) -> None:
|
async def async_open_valve(self) -> None:
|
||||||
"""Open the valve."""
|
"""Open the valve."""
|
||||||
await self.functional_channel.set_watering_switch_state_async(True)
|
channel = self.get_channel_or_raise()
|
||||||
|
await channel.set_watering_switch_state_async(True)
|
||||||
|
|
||||||
async def async_close_valve(self) -> None:
|
async def async_close_valve(self) -> None:
|
||||||
"""Close valve."""
|
"""Close valve."""
|
||||||
await self.functional_channel.set_watering_switch_state_async(False)
|
channel = self.get_channel_or_raise()
|
||||||
|
await channel.set_watering_switch_state_async(False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_closed(self) -> bool:
|
def is_closed(self) -> bool:
|
||||||
"""Return if the valve is closed."""
|
"""Return if the valve is closed."""
|
||||||
return self.functional_channel.wateringActive is False
|
channel = self.get_channel_or_raise()
|
||||||
|
return channel.wateringActive is False
|
||||||
|
|||||||
@@ -63,12 +63,25 @@ async def async_manipulate_test_data(
|
|||||||
new_value: Any,
|
new_value: Any,
|
||||||
channel: int = 1,
|
channel: int = 1,
|
||||||
fire_device: HomeMaticIPObject | None = None,
|
fire_device: HomeMaticIPObject | None = None,
|
||||||
|
channel_real_index: int | None = None,
|
||||||
):
|
):
|
||||||
"""Set new value on hmip device."""
|
"""Set new value on hmip device."""
|
||||||
if channel == 1:
|
if channel == 1:
|
||||||
setattr(hmip_device, attribute, new_value)
|
setattr(hmip_device, attribute, new_value)
|
||||||
if hasattr(hmip_device, "functionalChannels"):
|
|
||||||
functional_channel = hmip_device.functionalChannels[channel]
|
channels = getattr(hmip_device, "functionalChannels", None)
|
||||||
|
if channels:
|
||||||
|
if channel_real_index is not None:
|
||||||
|
functional_channel = next(
|
||||||
|
(ch for ch in channels if ch.index == channel_real_index),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
assert functional_channel is not None, (
|
||||||
|
f"No functional channel with index {channel_real_index} found in hmip_device.functionalChannels"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
functional_channel = channels[channel]
|
||||||
|
|
||||||
setattr(functional_channel, attribute, new_value)
|
setattr(functional_channel, attribute, new_value)
|
||||||
|
|
||||||
fire_target = hmip_device if fire_device is None else fire_device
|
fire_target = hmip_device if fire_device is None else fire_device
|
||||||
|
|||||||
@@ -565,8 +565,8 @@ async def test_hmip_multi_contact_interface(
|
|||||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test HomematicipMultiContactInterface."""
|
"""Test HomematicipMultiContactInterface."""
|
||||||
entity_id = "binary_sensor.wired_eingangsmodul_32_fach_channel5"
|
entity_id = "binary_sensor.wired_eingangsmodul_32_fach_channel10"
|
||||||
entity_name = "Wired Eingangsmodul – 32-fach Channel5"
|
entity_name = "Wired Eingangsmodul – 32-fach Channel10"
|
||||||
device_model = "HmIPW-DRI32"
|
device_model = "HmIPW-DRI32"
|
||||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||||
test_devices=["Wired Eingangsmodul – 32-fach", "Licht Flur"]
|
test_devices=["Wired Eingangsmodul – 32-fach", "Licht Flur"]
|
||||||
@@ -578,15 +578,25 @@ async def test_hmip_multi_contact_interface(
|
|||||||
|
|
||||||
assert ha_state.state == STATE_OFF
|
assert ha_state.state == STATE_OFF
|
||||||
await async_manipulate_test_data(
|
await async_manipulate_test_data(
|
||||||
hass, hmip_device, "windowState", WindowState.OPEN, channel=5
|
hass, hmip_device, "windowState", WindowState.OPEN, channel_real_index=10
|
||||||
)
|
)
|
||||||
ha_state = hass.states.get(entity_id)
|
ha_state = hass.states.get(entity_id)
|
||||||
assert ha_state.state == STATE_ON
|
assert ha_state.state == STATE_ON
|
||||||
|
|
||||||
await async_manipulate_test_data(hass, hmip_device, "windowState", None, channel=5)
|
await async_manipulate_test_data(
|
||||||
|
hass, hmip_device, "windowState", None, channel_real_index=10
|
||||||
|
)
|
||||||
ha_state = hass.states.get(entity_id)
|
ha_state = hass.states.get(entity_id)
|
||||||
assert ha_state.state == STATE_UNKNOWN
|
assert ha_state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
# Test channel 32 of device
|
||||||
|
entity_id = "binary_sensor.wired_eingangsmodul_32_fach_channel32"
|
||||||
|
entity_name = "Wired Eingangsmodul – 32-fach Channel32"
|
||||||
|
ha_state, hmip_device = get_and_check_entity_basics(
|
||||||
|
hass, mock_hap, entity_id, entity_name, device_model
|
||||||
|
)
|
||||||
|
assert ha_state.state == STATE_OFF
|
||||||
|
|
||||||
ha_state, hmip_device = get_and_check_entity_basics(
|
ha_state, hmip_device = get_and_check_entity_basics(
|
||||||
hass,
|
hass,
|
||||||
mock_hap,
|
mock_hap,
|
||||||
|
|||||||
Reference in New Issue
Block a user