mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add HmIP-HDM1 and HmIPW-DRD3 to Homematic IP Cloud (#43132)
* cleanup const.py * Add wired multi dimmer HMIPW-DRD3 to Homematic IP Cloud * Add HmIP-HDM1 to Homematic IP Cloud
This commit is contained in:
parent
69a438e2fc
commit
40408eb0eb
@ -1,19 +1,30 @@
|
||||
"""Constants for the HomematicIP Cloud component."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
|
||||
)
|
||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.components.cover import DOMAIN as COVER_DOMAIN
|
||||
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.components.weather import DOMAIN as WEATHER_DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(".")
|
||||
|
||||
DOMAIN = "homematicip_cloud"
|
||||
|
||||
COMPONENTS = [
|
||||
"alarm_control_panel",
|
||||
"binary_sensor",
|
||||
"climate",
|
||||
"cover",
|
||||
"light",
|
||||
"sensor",
|
||||
"switch",
|
||||
"weather",
|
||||
ALARM_CONTROL_PANEL_DOMAIN,
|
||||
BINARY_SENSOR_DOMAIN,
|
||||
CLIMATE_DOMAIN,
|
||||
COVER_DOMAIN,
|
||||
LIGHT_DOMAIN,
|
||||
SENSOR_DOMAIN,
|
||||
SWITCH_DOMAIN,
|
||||
WEATHER_DOMAIN,
|
||||
]
|
||||
|
||||
CONF_ACCESSPOINT = "accesspoint"
|
||||
|
@ -2,6 +2,7 @@
|
||||
from typing import Optional
|
||||
|
||||
from homematicip.aio.device import (
|
||||
AsyncBlindModule,
|
||||
AsyncFullFlushBlind,
|
||||
AsyncFullFlushShutter,
|
||||
AsyncGarageDoorModuleTormatic,
|
||||
@ -34,7 +35,9 @@ async def async_setup_entry(
|
||||
hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id]
|
||||
entities = []
|
||||
for device in hap.home.devices:
|
||||
if isinstance(device, AsyncFullFlushBlind):
|
||||
if isinstance(device, AsyncBlindModule):
|
||||
entities.append(HomematicipBlindModule(hap, device))
|
||||
elif isinstance(device, AsyncFullFlushBlind):
|
||||
entities.append(HomematicipCoverSlats(hap, device))
|
||||
elif isinstance(device, AsyncFullFlushShutter):
|
||||
entities.append(HomematicipCoverShutter(hap, device))
|
||||
@ -51,6 +54,82 @@ async def async_setup_entry(
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class HomematicipBlindModule(HomematicipGenericEntity, CoverEntity):
|
||||
"""Representation of the HomematicIP blind module."""
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int:
|
||||
"""Return current position of cover."""
|
||||
if self._device.primaryShadingLevel is not None:
|
||||
return int((1 - self._device.primaryShadingLevel) * 100)
|
||||
return None
|
||||
|
||||
@property
|
||||
def current_cover_tilt_position(self) -> int:
|
||||
"""Return current tilt position of cover."""
|
||||
if self._device.secondaryShadingLevel is not None:
|
||||
return int((1 - self._device.secondaryShadingLevel) * 100)
|
||||
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_primary_shading_level(primaryShadingLevel=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_secondary_shading_level(
|
||||
primaryShadingLevel=self._device.primaryShadingLevel,
|
||||
secondaryShadingLevel=level,
|
||||
)
|
||||
|
||||
@property
|
||||
def is_closed(self) -> Optional[bool]:
|
||||
"""Return if the cover is closed."""
|
||||
if self._device.primaryShadingLevel is not None:
|
||||
return self._device.primaryShadingLevel == HMIP_COVER_CLOSED
|
||||
return None
|
||||
|
||||
async def async_open_cover(self, **kwargs) -> None:
|
||||
"""Open the cover."""
|
||||
await self._device.set_primary_shading_level(
|
||||
primaryShadingLevel=HMIP_COVER_OPEN
|
||||
)
|
||||
|
||||
async def async_close_cover(self, **kwargs) -> None:
|
||||
"""Close the cover."""
|
||||
await self._device.set_primary_shading_level(
|
||||
primaryShadingLevel=HMIP_COVER_CLOSED
|
||||
)
|
||||
|
||||
async def async_stop_cover(self, **kwargs) -> None:
|
||||
"""Stop the device if in motion."""
|
||||
await self._device.stop()
|
||||
|
||||
async def async_open_cover_tilt(self, **kwargs) -> None:
|
||||
"""Open the slats."""
|
||||
await self._device.set_secondary_shading_level(
|
||||
primaryShadingLevel=self._device.primaryShadingLevel,
|
||||
secondaryShadingLevel=HMIP_SLATS_OPEN,
|
||||
)
|
||||
|
||||
async def async_close_cover_tilt(self, **kwargs) -> None:
|
||||
"""Close the slats."""
|
||||
await self._device.set_secondary_shading_level(
|
||||
primaryShadingLevel=self._device.primaryShadingLevel,
|
||||
secondaryShadingLevel=HMIP_SLATS_CLOSED,
|
||||
)
|
||||
|
||||
async def async_stop_cover_tilt(self, **kwargs) -> None:
|
||||
"""Stop the device if in motion."""
|
||||
await self._device.stop()
|
||||
|
||||
|
||||
class HomematicipCoverShutter(HomematicipGenericEntity, CoverEntity):
|
||||
"""Representation of the HomematicIP cover shutter."""
|
||||
|
||||
|
@ -8,6 +8,7 @@ from homematicip.aio.device import (
|
||||
AsyncDimmer,
|
||||
AsyncFullFlushDimmer,
|
||||
AsyncPluggableDimmer,
|
||||
AsyncWiredDimmer3,
|
||||
)
|
||||
from homematicip.base.enums import RGBColorState
|
||||
from homematicip.base.functionalChannels import NotificationLightChannel
|
||||
@ -51,6 +52,9 @@ async def async_setup_entry(
|
||||
hap, device, device.bottomLightChannelIndex
|
||||
)
|
||||
)
|
||||
elif isinstance(device, AsyncWiredDimmer3):
|
||||
for channel in range(1, 4):
|
||||
entities.append(HomematicipMultiDimmer(hap, device, channel=channel))
|
||||
elif isinstance(
|
||||
device,
|
||||
(AsyncDimmer, AsyncPluggableDimmer, AsyncBrandDimmer, AsyncFullFlushDimmer),
|
||||
@ -99,6 +103,45 @@ class HomematicipLightMeasuring(HomematicipLight):
|
||||
return state_attr
|
||||
|
||||
|
||||
class HomematicipMultiDimmer(HomematicipGenericEntity, LightEntity):
|
||||
"""Representation of HomematicIP Cloud dimmer."""
|
||||
|
||||
def __init__(self, hap: HomematicipHAP, device, channel: int) -> None:
|
||||
"""Initialize the dimmer light entity."""
|
||||
super().__init__(hap, device, channel=channel)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if dimmer is on."""
|
||||
func_channel = self._device.functionalChannels[self._channel]
|
||||
return func_channel.dimLevel is not None and func_channel.dimLevel > 0.0
|
||||
|
||||
@property
|
||||
def brightness(self) -> int:
|
||||
"""Return the brightness of this light between 0..255."""
|
||||
return int(
|
||||
(self._device.functionalChannels[self._channel].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, self._channel
|
||||
)
|
||||
else:
|
||||
await self._device.set_dim_level(1, self._channel)
|
||||
|
||||
async def async_turn_off(self, **kwargs) -> None:
|
||||
"""Turn the dimmer off."""
|
||||
await self._device.set_dim_level(0, self._channel)
|
||||
|
||||
|
||||
class HomematicipDimmer(HomematicipGenericEntity, LightEntity):
|
||||
"""Representation of HomematicIP Cloud dimmer."""
|
||||
|
||||
|
@ -160,6 +160,109 @@ async def test_hmip_cover_slats(hass, default_mock_hap_factory):
|
||||
assert ha_state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_hmip_blind_module(hass, default_mock_hap_factory):
|
||||
"""Test HomematicipBlindModule."""
|
||||
entity_id = "cover.sonnenschutz_balkontur"
|
||||
entity_name = "Sonnenschutz Balkontür"
|
||||
device_model = "HmIP-HDM1"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||
test_devices=[entity_name]
|
||||
)
|
||||
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
assert ha_state.state == STATE_OPEN
|
||||
assert ha_state.attributes[ATTR_CURRENT_POSITION] == 5
|
||||
assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 100
|
||||
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_secondary_shading_level"
|
||||
assert hmip_device.mock_calls[-1][2] == {
|
||||
"primaryShadingLevel": 0.94956,
|
||||
"secondaryShadingLevel": 0,
|
||||
}
|
||||
|
||||
await async_manipulate_test_data(hass, hmip_device, "primaryShadingLevel", 0)
|
||||
await async_manipulate_test_data(hass, hmip_device, "secondaryShadingLevel", 0)
|
||||
await hass.services.async_call(
|
||||
"cover", "open_cover", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
assert len(hmip_device.mock_calls) == service_call_counter + 4
|
||||
|
||||
assert hmip_device.mock_calls[-1][0] == "set_primary_shading_level"
|
||||
assert hmip_device.mock_calls[-1][2] == {"primaryShadingLevel": 0}
|
||||
|
||||
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 async_manipulate_test_data(hass, hmip_device, "primaryShadingLevel", 0.5)
|
||||
await async_manipulate_test_data(hass, hmip_device, "secondaryShadingLevel", 0.5)
|
||||
await hass.services.async_call(
|
||||
"cover",
|
||||
"set_cover_tilt_position",
|
||||
{"entity_id": entity_id, "tilt_position": "50"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"cover",
|
||||
"set_cover_position",
|
||||
{"entity_id": entity_id, "position": "50"},
|
||||
blocking=True,
|
||||
)
|
||||
assert len(hmip_device.mock_calls) == service_call_counter + 8
|
||||
|
||||
assert hmip_device.mock_calls[-1][0] == "set_primary_shading_level"
|
||||
assert hmip_device.mock_calls[-1][2] == {"primaryShadingLevel": 0.5}
|
||||
ha_state = hass.states.get(entity_id)
|
||||
assert ha_state.state == STATE_OPEN
|
||||
assert ha_state.attributes[ATTR_CURRENT_POSITION] == 50
|
||||
assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 50
|
||||
|
||||
await async_manipulate_test_data(hass, hmip_device, "primaryShadingLevel", 1)
|
||||
await async_manipulate_test_data(hass, hmip_device, "secondaryShadingLevel", 1)
|
||||
await hass.services.async_call(
|
||||
"cover", "close_cover", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"cover", "close_cover_tilt", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
assert len(hmip_device.mock_calls) == service_call_counter + 12
|
||||
|
||||
assert hmip_device.mock_calls[-1][0] == "set_secondary_shading_level"
|
||||
assert hmip_device.mock_calls[-1][2] == {
|
||||
"primaryShadingLevel": 1,
|
||||
"secondaryShadingLevel": 1,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
await hass.services.async_call(
|
||||
"cover", "stop_cover", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
assert len(hmip_device.mock_calls) == service_call_counter + 13
|
||||
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)
|
||||
|
||||
await async_manipulate_test_data(hass, hmip_device, "primaryShadingLevel", None)
|
||||
ha_state = hass.states.get(entity_id)
|
||||
assert ha_state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_hmip_garage_door_tormatic(hass, default_mock_hap_factory):
|
||||
"""Test HomematicipCoverShutte."""
|
||||
entity_id = "cover.garage_door_module"
|
||||
|
@ -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) == 233
|
||||
assert len(mock_hap.hmip_device_by_entity_id) == 236
|
||||
|
||||
|
||||
async def test_hmip_remove_device(hass, default_mock_hap_factory):
|
||||
|
@ -245,3 +245,55 @@ async def test_hmip_light_measuring(hass, default_mock_hap_factory):
|
||||
await async_manipulate_test_data(hass, hmip_device, "on", False)
|
||||
ha_state = hass.states.get(entity_id)
|
||||
assert ha_state.state == STATE_OFF
|
||||
|
||||
|
||||
async def test_hmip_wired_multi_dimmer(hass, default_mock_hap_factory):
|
||||
"""Test HomematicipMultiDimmer."""
|
||||
entity_id = "light.raumlich_kuche"
|
||||
entity_name = "Raumlich (Küche)"
|
||||
device_model = "HmIPW-DRD3"
|
||||
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||
test_devices=["Wired Dimmaktor – 3-fach (Küche)"]
|
||||
)
|
||||
|
||||
ha_state, hmip_device = get_and_check_entity_basics(
|
||||
hass, mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
assert ha_state.state == STATE_OFF
|
||||
service_call_counter = len(hmip_device.mock_calls)
|
||||
|
||||
await hass.services.async_call(
|
||||
"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, 1)
|
||||
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_on",
|
||||
{"entity_id": entity_id, "brightness": "100"},
|
||||
blocking=True,
|
||||
)
|
||||
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] == (0.39215686274509803, 1)
|
||||
await async_manipulate_test_data(hass, hmip_device, "dimLevel", 1, channel=1)
|
||||
ha_state = hass.states.get(entity_id)
|
||||
assert ha_state.state == STATE_ON
|
||||
assert ha_state.attributes[ATTR_BRIGHTNESS] == 255
|
||||
|
||||
await hass.services.async_call(
|
||||
"light", "turn_off", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
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, 1)
|
||||
await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0, channel=1)
|
||||
ha_state = hass.states.get(entity_id)
|
||||
assert ha_state.state == STATE_OFF
|
||||
|
||||
await async_manipulate_test_data(hass, hmip_device, "dimLevel", None, channel=1)
|
||||
ha_state = hass.states.get(entity_id)
|
||||
assert ha_state.state == STATE_OFF
|
||||
assert not ha_state.attributes.get(ATTR_BRIGHTNESS)
|
||||
|
2
tests/fixtures/homematicip_cloud.json
vendored
2
tests/fixtures/homematicip_cloud.json
vendored
@ -233,7 +233,7 @@
|
||||
"profileMode": "AUTOMATIC",
|
||||
"secondaryCloseAdjustable": false,
|
||||
"secondaryOpenAdjustable": false,
|
||||
"secondaryShadingLevel": null,
|
||||
"secondaryShadingLevel": 0,
|
||||
"secondaryShadingStateType": "NOT_EXISTENT",
|
||||
"shadingDriveVersion": null,
|
||||
"shadingPackagePosition": "TOP",
|
||||
|
Loading…
x
Reference in New Issue
Block a user