Add support HmIP-BSL after firmware update to 2.0 to homematicip_cloud (#117657)
* Rebase * Fix number of loaded entities * Reduce redundant code * Remove unneccessary import in test_light
This commit is contained in:
parent
d2db25c7dd
commit
cf9e5ae5a0
5 changed files with 346 additions and 16 deletions
|
@ -14,12 +14,14 @@ from homematicip.aio.device import (
|
|||
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,
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -259,6 +261,66 @@ class HomematicipNotificationLight(HomematicipGenericEntity, LightEntity):
|
|||
)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -186,6 +186,10 @@ class HomeTemplate(Home):
|
|||
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]
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
"""Tests for HomematicIP Cloud light."""
|
||||
|
||||
from homematicip.base.enums import RGBColorState
|
||||
from homematicip.base.enums import OpticalSignalBehaviour, RGBColorState
|
||||
|
||||
from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_COLOR_MODE,
|
||||
ATTR_COLOR_NAME,
|
||||
ATTR_EFFECT,
|
||||
ATTR_HS_COLOR,
|
||||
ATTR_SUPPORTED_COLOR_MODES,
|
||||
DOMAIN as LIGHT_DOMAIN,
|
||||
ColorMode,
|
||||
|
@ -173,6 +175,101 @@ async def test_hmip_notification_light(
|
|||
assert not ha_state.attributes.get(ATTR_BRIGHTNESS)
|
||||
|
||||
|
||||
async def test_hmip_notification_light_2(
|
||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||
) -> None:
|
||||
"""Test HomematicipNotificationLight."""
|
||||
entity_id = "light.led_oben"
|
||||
entity_name = "Led Oben"
|
||||
device_model = "HmIP-BSL"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(test_devices=["BSL2"])
|
||||
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
assert ha_state.state == STATE_ON
|
||||
assert ha_state.attributes[ATTR_EFFECT] == "BLINKING_MIDDLE"
|
||||
|
||||
functional_channel = hmip_device.functionalChannels[3]
|
||||
service_call_counter = len(functional_channel.mock_calls)
|
||||
|
||||
# Send all color via service call.
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_on",
|
||||
{"entity_id": entity_id, ATTR_HS_COLOR: [240.0, 100.0], ATTR_BRIGHTNESS: 128},
|
||||
blocking=True,
|
||||
)
|
||||
assert functional_channel.mock_calls[-1][0] == "async_set_optical_signal"
|
||||
assert functional_channel.mock_calls[-1][2] == {
|
||||
"opticalSignalBehaviour": OpticalSignalBehaviour.BLINKING_MIDDLE,
|
||||
"rgb": RGBColorState.BLUE,
|
||||
"dimLevel": 0.5,
|
||||
}
|
||||
assert service_call_counter + 1 == len(functional_channel.mock_calls)
|
||||
|
||||
|
||||
async def test_hmip_notification_light_2_without_brightness_and_light(
|
||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||
) -> None:
|
||||
"""Test HomematicipNotificationLight."""
|
||||
entity_id = "light.led_oben"
|
||||
entity_name = "Led Oben"
|
||||
device_model = "HmIP-BSL"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(test_devices=["BSL2"])
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
color_before = ha_state.attributes["color_name"]
|
||||
|
||||
functional_channel = hmip_device.functionalChannels[3]
|
||||
service_call_counter = len(functional_channel.mock_calls)
|
||||
|
||||
# Send all color via service call.
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_on",
|
||||
{"entity_id": entity_id, ATTR_EFFECT: OpticalSignalBehaviour.FLASH_MIDDLE},
|
||||
blocking=True,
|
||||
)
|
||||
assert functional_channel.mock_calls[-1][0] == "async_set_optical_signal"
|
||||
assert functional_channel.mock_calls[-1][2] == {
|
||||
"opticalSignalBehaviour": OpticalSignalBehaviour.FLASH_MIDDLE,
|
||||
"rgb": color_before,
|
||||
"dimLevel": 1,
|
||||
}
|
||||
assert service_call_counter + 1 == len(functional_channel.mock_calls)
|
||||
|
||||
|
||||
async def test_hmip_notification_light_2_turn_off(
|
||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||
) -> None:
|
||||
"""Test HomematicipNotificationLight."""
|
||||
entity_id = "light.led_oben"
|
||||
entity_name = "Led Oben"
|
||||
device_model = "HmIP-BSL"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(test_devices=["BSL2"])
|
||||
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
functional_channel = hmip_device.functionalChannels[3]
|
||||
service_call_counter = len(functional_channel.mock_calls)
|
||||
|
||||
# Send all color via service call.
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_off",
|
||||
{"entity_id": entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
assert functional_channel.mock_calls[-1][0] == "async_turn_off"
|
||||
assert service_call_counter + 1 == len(functional_channel.mock_calls)
|
||||
|
||||
|
||||
async def test_hmip_dimmer(
|
||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||
) -> None:
|
||||
|
|
Loading…
Add table
Reference in a new issue