Skip to content

Commit

Permalink
Add support HmIP-BSL after firmware update to 2.0 to homematicip_cloud (
Browse files Browse the repository at this point in the history
#117657)

* Rebase

* Fix number of loaded entities

* Reduce redundant code

* Remove unneccessary import in test_light
  • Loading branch information
hahn-th authored Oct 15, 2024
1 parent d2db25c commit cf9e5ae
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 16 deletions.
90 changes: 76 additions & 14 deletions homeassistant/components/homematicip_cloud/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
AsyncPluggableDimmer,
AsyncWiredDimmer3,
)
from homematicip.base.enums import RGBColorState
from homematicip.base.enums import OpticalSignalBehaviour, RGBColorState
from homematicip.base.functionalChannels import NotificationLightChannel
from packaging.version import Version

from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_NAME,
ATTR_EFFECT,
ATTR_HS_COLOR,
ATTR_TRANSITION,
ColorMode,
Expand Down Expand Up @@ -47,15 +49,22 @@ async def async_setup_entry(
if isinstance(device, AsyncBrandSwitchMeasuring):
entities.append(HomematicipLightMeasuring(hap, device))
elif isinstance(device, AsyncBrandSwitchNotificationLight):
device_version = Version(device.firmwareVersion)
entities.append(HomematicipLight(hap, device))

entity_class = (
HomematicipNotificationLightV2
if device_version > Version("2.0.0")
else HomematicipNotificationLight
)

entities.append(
HomematicipNotificationLight(hap, device, device.topLightChannelIndex)
entity_class(hap, device, device.topLightChannelIndex, "Top")
)
entities.append(
HomematicipNotificationLight(
hap, device, device.bottomLightChannelIndex
)
entity_class(hap, device, device.bottomLightChannelIndex, "Bottom")
)

elif isinstance(device, (AsyncWiredDimmer3, AsyncDinRailDimmer3)):
entities.extend(
HomematicipMultiDimmer(hap, device, channel=channel)
Expand Down Expand Up @@ -158,16 +167,9 @@ class HomematicipNotificationLight(HomematicipGenericEntity, LightEntity):
_attr_supported_color_modes = {ColorMode.HS}
_attr_supported_features = LightEntityFeature.TRANSITION

def __init__(self, hap: HomematicipHAP, device, channel: int) -> None:
def __init__(self, hap: HomematicipHAP, device, channel: int, post: str) -> None:
"""Initialize the notification light entity."""
if channel == 2:
super().__init__(
hap, device, post="Top", channel=channel, is_multi_channel=True
)
else:
super().__init__(
hap, device, post="Bottom", channel=channel, is_multi_channel=True
)
super().__init__(hap, device, post=post, channel=channel, is_multi_channel=True)

self._color_switcher: dict[str, tuple[float, float]] = {
RGBColorState.WHITE: (0.0, 0.0),
Expand Down Expand Up @@ -259,6 +261,66 @@ async def async_turn_off(self, **kwargs: Any) -> None:
)


class HomematicipNotificationLightV2(HomematicipNotificationLight, LightEntity):
"""Representation of HomematicIP Cloud notification light."""

_effect_list = [
OpticalSignalBehaviour.BILLOW_MIDDLE,
OpticalSignalBehaviour.BLINKING_MIDDLE,
OpticalSignalBehaviour.FLASH_MIDDLE,
OpticalSignalBehaviour.OFF,
OpticalSignalBehaviour.ON,
]

def __init__(self, hap: HomematicipHAP, device, channel: int, post: str) -> None:
"""Initialize the notification light entity."""
super().__init__(hap, device, post=post, channel=channel)
self._attr_supported_features |= LightEntityFeature.EFFECT

@property
def effect_list(self) -> list[str] | None:
"""Return the list of supported effects."""
return self._effect_list

@property
def effect(self) -> str | None:
"""Return the current effect."""
return self._func_channel.opticalSignalBehaviour

@property
def is_on(self) -> bool:
"""Return true if light is on."""
return self._func_channel.on

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
# Use hs_color from kwargs,
# if not applicable use current hs_color.
hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_color)
simple_rgb_color = _convert_color(hs_color)

# If no kwargs, use default value.
brightness = 255
if ATTR_BRIGHTNESS in kwargs:
brightness = kwargs[ATTR_BRIGHTNESS]

# Minimum brightness is 10, otherwise the led is disabled
brightness = max(10, brightness)
dim_level = round(brightness / 255.0, 2)

effect = self.effect
if ATTR_EFFECT in kwargs:
effect = kwargs[ATTR_EFFECT]

await self._func_channel.async_set_optical_signal(
opticalSignalBehaviour=effect, rgb=simple_rgb_color, dimLevel=dim_level
)

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
await self._func_channel.async_turn_off()


def _convert_color(color: tuple) -> RGBColorState:
"""Convert the given color to the reduced RGBColorState color.
Expand Down
167 changes: 167 additions & 0 deletions tests/components/homematicip_cloud/fixtures/homematicip_cloud.json
Original file line number Diff line number Diff line change
Expand Up @@ -3237,6 +3237,173 @@
"type": "BRAND_SWITCH_NOTIFICATION_LIGHT",
"updateState": "UP_TO_DATE"
},
"3014F711000000000000BSL2": {
"availableFirmwareVersion": "2.0.2",
"connectionType": "HMIP_RF",
"deviceArchetype": "HMIP",
"firmwareVersion": "2.0.2",
"firmwareVersionInteger": 131074,
"functionalChannels": {
"0": {
"busConfigMismatch": null,
"coProFaulty": false,
"coProRestartNeeded": false,
"coProUpdateFailure": false,
"configPending": false,
"controlsMountingOrientation": null,
"daliBusState": null,
"defaultLinkedGroup": [],
"deviceCommunicationError": null,
"deviceDriveError": null,
"deviceDriveModeError": null,
"deviceId": "3014F711000000000000BSL2",
"deviceOperationMode": null,
"deviceOverheated": false,
"deviceOverloaded": false,
"devicePowerFailureDetected": false,
"deviceUndervoltage": false,
"displayContrast": null,
"dutyCycle": false,
"functionalChannelType": "DEVICE_BASE",
"groupIndex": 0,
"groups": ["00000000-0000-0000-0000-000000000007"],
"index": 0,
"label": "",
"lockJammed": null,
"lowBat": null,
"mountingOrientation": null,
"multicastRoutingEnabled": false,
"particulateMatterSensorCommunicationError": null,
"particulateMatterSensorError": null,
"powerShortCircuit": null,
"profilePeriodLimitReached": null,
"routerModuleEnabled": false,
"routerModuleSupported": false,
"rssiDeviceValue": -74,
"rssiPeerValue": -75,
"sensorCommunicationError": null,
"sensorError": null,
"shortCircuitDataLine": null,
"supportedOptionalFeatures": {
"IFeatureBusConfigMismatch": false,
"IFeatureDeviceCoProError": false,
"IFeatureDeviceCoProRestart": false,
"IFeatureDeviceCoProUpdate": false,
"IFeatureDeviceCommunicationError": false,
"IFeatureDeviceDaliBusError": false,
"IFeatureDeviceDriveError": false,
"IFeatureDeviceDriveModeError": false,
"IFeatureDeviceIdentify": true,
"IFeatureDeviceOverheated": true,
"IFeatureDeviceOverloaded": false,
"IFeatureDeviceParticulateMatterSensorCommunicationError": false,
"IFeatureDeviceParticulateMatterSensorError": false,
"IFeatureDevicePowerFailure": false,
"IFeatureDeviceSensorCommunicationError": false,
"IFeatureDeviceSensorError": false,
"IFeatureDeviceTemperatureHumiditySensorCommunicationError": false,
"IFeatureDeviceTemperatureHumiditySensorError": false,
"IFeatureDeviceTemperatureOutOfRange": false,
"IFeatureDeviceUndervoltage": false,
"IFeatureMulticastRouter": false,
"IFeaturePowerShortCircuit": false,
"IFeatureProfilePeriodLimit": true,
"IFeatureRssiValue": true,
"IFeatureShortCircuitDataLine": false,
"IOptionalFeatureDefaultLinkedGroup": false,
"IOptionalFeatureDeviceErrorLockJammed": false,
"IOptionalFeatureDeviceOperationMode": false,
"IOptionalFeatureDisplayContrast": false,
"IOptionalFeatureDutyCycle": true,
"IOptionalFeatureLowBat": false,
"IOptionalFeatureMountingOrientation": false
},
"temperatureHumiditySensorCommunicationError": null,
"temperatureHumiditySensorError": null,
"temperatureOutOfRange": false,
"unreach": false
},
"1": {
"channelRole": null,
"deviceId": "3014F711000000000000BSL2",
"functionalChannelType": "SWITCH_CHANNEL",
"groupIndex": 1,
"groups": [],
"index": 1,
"internalLinkConfiguration": {
"firstInputAction": "OFF",
"internalLinkConfigurationType": "DOUBLE_INPUT_SWITCH",
"longPressOnTimeEnabled": false,
"onTime": 111600.0,
"secondInputAction": "ON"
},
"label": "",
"on": false,
"powerUpSwitchState": "PERMANENT_OFF",
"profileMode": "AUTOMATIC",
"supportedOptionalFeatures": {
"IFeatureAccessAuthorizationActuatorChannel": false,
"IFeatureGarageGroupActuatorChannel": false,
"IFeatureLightGroupActuatorChannel": false,
"IFeatureLightProfileActuatorChannel": false,
"IOptionalFeatureInternalLinkConfiguration": true,
"IOptionalFeaturePowerUpSwitchState": true
},
"userDesiredProfileMode": "AUTOMATIC"
},
"2": {
"channelRole": "NOTIFICATION_LIGHT_DIMMING_ACTUATOR",
"deviceId": "3014F711000000000000BSL2",
"dimLevel": 0.0,
"functionalChannelType": "NOTIFICATION_LIGHT_CHANNEL",
"groupIndex": 2,
"groups": ["00000000-0000-0000-0000-000000000021"],
"index": 2,
"label": "Led Unten",
"on": false,
"opticalSignalBehaviour": "BLINKING_MIDDLE",
"profileMode": "AUTOMATIC",
"simpleRGBColorState": "TURQUOISE",
"supportedOptionalFeatures": {
"IFeatureOpticalSignalBehaviourState": true
},
"userDesiredProfileMode": "AUTOMATIC"
},
"3": {
"channelRole": "NOTIFICATION_LIGHT_DIMMING_ACTUATOR",
"deviceId": "3014F711000000000000BSL2",
"dimLevel": 0.25,
"functionalChannelType": "NOTIFICATION_LIGHT_CHANNEL",
"groupIndex": 3,
"groups": ["00000000-0000-0000-0000-000000000021"],
"index": 3,
"label": "Led Oben",
"on": true,
"opticalSignalBehaviour": "BLINKING_MIDDLE",
"profileMode": "AUTOMATIC",
"simpleRGBColorState": "GREEN",
"supportedOptionalFeatures": {
"IFeatureOpticalSignalBehaviourState": true
},
"userDesiredProfileMode": "AUTOMATIC"
}
},
"homeId": "00000000-0000-0000-0000-000000000001",
"id": "3014F711000000000000BSL2",
"label": "BSL2",
"lastStatusUpdate": 1714910246419,
"liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED",
"manuallyUpdateForced": false,
"manufacturerCode": 1,
"measuredAttributes": {},
"modelId": 360,
"modelType": "HmIP-BSL",
"oem": "eQ-3",
"permanentlyReachable": true,
"serializedGlobalTradeItemNumber": "3014F711000000000000BSL2",
"type": "BRAND_SWITCH_NOTIFICATION_LIGHT",
"updateState": "UP_TO_DATE"
},
"3014F711SLO0000000000026": {
"availableFirmwareVersion": "0.0.0",
"connectionType": "HMIP_RF",
Expand Down
4 changes: 4 additions & 0 deletions tests/components/homematicip_cloud/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ def update_home(self, json_state, clearConfig: bool = False):
def _generate_mocks(self):
"""Generate mocks for groups and devices."""
self.devices = [_get_mock(device) for device in self.devices]
for device in self.devices:
device.functionalChannels = [
_get_mock(ch) for ch in device.functionalChannels
]

self.groups = [_get_mock(group) for group in self.groups]

Expand Down
2 changes: 1 addition & 1 deletion tests/components/homematicip_cloud/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async def test_hmip_load_all_supported_devices(
test_devices=None, test_groups=None
)

assert len(mock_hap.hmip_device_by_entity_id) == 293
assert len(mock_hap.hmip_device_by_entity_id) == 296


async def test_hmip_remove_device(
Expand Down
Loading

0 comments on commit cf9e5ae

Please sign in to comment.