mirror of
https://github.com/home-assistant/core.git
synced 2026-04-17 23:53:49 +01:00
Add HmIP-FLC support to HomematicIP Cloud (#165827)
This commit is contained in:
@@ -4,7 +4,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 LockState, SmokeDetectorAlarmType, WindowState
|
||||||
from homematicip.base.functionalChannels import MultiModeInputChannel
|
from homematicip.base.functionalChannels import MultiModeInputChannel
|
||||||
from homematicip.device import (
|
from homematicip.device import (
|
||||||
AccelerationSensor,
|
AccelerationSensor,
|
||||||
@@ -74,6 +74,30 @@ SAM_DEVICE_ATTRIBUTES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _is_full_flush_lock_controller(device: object) -> bool:
|
||||||
|
"""Return whether the device is an HmIP-FLC."""
|
||||||
|
return getattr(device, "modelType", None) == "HmIP-FLC" and hasattr(
|
||||||
|
device, "functionalChannels"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_channel_by_role(
|
||||||
|
device: object,
|
||||||
|
functional_channel_type: str,
|
||||||
|
channel_role: str,
|
||||||
|
) -> object | None:
|
||||||
|
"""Return the matching functional channel for the device."""
|
||||||
|
for channel in getattr(device, "functionalChannels", []):
|
||||||
|
channel_type = getattr(channel, "functionalChannelType", None)
|
||||||
|
channel_type_name = getattr(channel_type, "name", channel_type)
|
||||||
|
if channel_type_name != functional_channel_type:
|
||||||
|
continue
|
||||||
|
if getattr(channel, "channelRole", None) != channel_role:
|
||||||
|
continue
|
||||||
|
return channel
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: HomematicIPConfigEntry,
|
config_entry: HomematicIPConfigEntry,
|
||||||
@@ -122,6 +146,9 @@ async def async_setup_entry(
|
|||||||
entities.append(
|
entities.append(
|
||||||
HomematicipPluggableMainsFailureSurveillanceSensor(hap, device)
|
HomematicipPluggableMainsFailureSurveillanceSensor(hap, device)
|
||||||
)
|
)
|
||||||
|
if _is_full_flush_lock_controller(device):
|
||||||
|
entities.append(HomematicipFullFlushLockControllerLocked(hap, device))
|
||||||
|
entities.append(HomematicipFullFlushLockControllerGlassBreak(hap, device))
|
||||||
if isinstance(device, PresenceDetectorIndoor):
|
if isinstance(device, PresenceDetectorIndoor):
|
||||||
entities.append(HomematicipPresenceDetector(hap, device))
|
entities.append(HomematicipPresenceDetector(hap, device))
|
||||||
if isinstance(device, SmokeDetector):
|
if isinstance(device, SmokeDetector):
|
||||||
@@ -298,6 +325,55 @@ class HomematicipMotionDetector(HomematicipGenericEntity, BinarySensorEntity):
|
|||||||
return self._device.motionDetected
|
return self._device.motionDetected
|
||||||
|
|
||||||
|
|
||||||
|
class HomematicipFullFlushLockControllerLocked(
|
||||||
|
HomematicipGenericEntity, BinarySensorEntity
|
||||||
|
):
|
||||||
|
"""Representation of the HomematicIP full flush lock controller lock state."""
|
||||||
|
|
||||||
|
_attr_device_class = BinarySensorDeviceClass.LOCK
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the full flush lock controller lock sensor."""
|
||||||
|
super().__init__(hap, device, post="Locked")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return true if the controlled lock is locked."""
|
||||||
|
channel = _get_channel_by_role(
|
||||||
|
self._device,
|
||||||
|
"MULTI_MODE_LOCK_INPUT_CHANNEL",
|
||||||
|
"DOOR_LOCK_SENSOR",
|
||||||
|
)
|
||||||
|
if channel is None:
|
||||||
|
return False
|
||||||
|
lock_state = getattr(channel, "lockState", None)
|
||||||
|
return getattr(lock_state, "name", lock_state) == LockState.LOCKED.name
|
||||||
|
|
||||||
|
|
||||||
|
class HomematicipFullFlushLockControllerGlassBreak(
|
||||||
|
HomematicipGenericEntity, BinarySensorEntity
|
||||||
|
):
|
||||||
|
"""Representation of the HomematicIP full flush lock controller glass state."""
|
||||||
|
|
||||||
|
_attr_device_class = BinarySensorDeviceClass.PROBLEM
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the full flush lock controller glass break sensor."""
|
||||||
|
super().__init__(hap, device, post="Glass break")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return true if glass break has been detected."""
|
||||||
|
channel = _get_channel_by_role(
|
||||||
|
self._device,
|
||||||
|
"MULTI_MODE_LOCK_INPUT_CHANNEL",
|
||||||
|
"DOOR_LOCK_SENSOR",
|
||||||
|
)
|
||||||
|
if channel is None:
|
||||||
|
return False
|
||||||
|
return bool(getattr(channel, "glassBroken", False))
|
||||||
|
|
||||||
|
|
||||||
class HomematicipPresenceDetector(HomematicipGenericEntity, BinarySensorEntity):
|
class HomematicipPresenceDetector(HomematicipGenericEntity, BinarySensorEntity):
|
||||||
"""Representation of the HomematicIP presence detector."""
|
"""Representation of the HomematicIP presence detector."""
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ from .entity import HomematicipGenericEntity
|
|||||||
from .hap import HomematicIPConfigEntry, HomematicipHAP
|
from .hap import HomematicIPConfigEntry, HomematicipHAP
|
||||||
|
|
||||||
|
|
||||||
|
def _is_full_flush_lock_controller(device: object) -> bool:
|
||||||
|
"""Return whether the device is an HmIP-FLC."""
|
||||||
|
return getattr(device, "modelType", None) == "HmIP-FLC" and hasattr(
|
||||||
|
device, "send_start_impulse_async"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: HomematicIPConfigEntry,
|
config_entry: HomematicIPConfigEntry,
|
||||||
@@ -20,11 +27,17 @@ async def async_setup_entry(
|
|||||||
"""Set up the HomematicIP button from a config entry."""
|
"""Set up the HomematicIP button from a config entry."""
|
||||||
hap = config_entry.runtime_data
|
hap = config_entry.runtime_data
|
||||||
|
|
||||||
async_add_entities(
|
entities: list[ButtonEntity] = [
|
||||||
HomematicipGarageDoorControllerButton(hap, device)
|
HomematicipGarageDoorControllerButton(hap, device)
|
||||||
for device in hap.home.devices
|
for device in hap.home.devices
|
||||||
if isinstance(device, WallMountedGarageDoorController)
|
if isinstance(device, WallMountedGarageDoorController)
|
||||||
|
]
|
||||||
|
entities.extend(
|
||||||
|
HomematicipFullFlushLockControllerButton(hap, device)
|
||||||
|
for device in hap.home.devices
|
||||||
|
if _is_full_flush_lock_controller(device)
|
||||||
)
|
)
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class HomematicipGarageDoorControllerButton(HomematicipGenericEntity, ButtonEntity):
|
class HomematicipGarageDoorControllerButton(HomematicipGenericEntity, ButtonEntity):
|
||||||
@@ -38,3 +51,16 @@ class HomematicipGarageDoorControllerButton(HomematicipGenericEntity, ButtonEnti
|
|||||||
async def async_press(self) -> None:
|
async def async_press(self) -> None:
|
||||||
"""Handle the button press."""
|
"""Handle the button press."""
|
||||||
await self._device.send_start_impulse_async()
|
await self._device.send_start_impulse_async()
|
||||||
|
|
||||||
|
|
||||||
|
class HomematicipFullFlushLockControllerButton(HomematicipGenericEntity, ButtonEntity):
|
||||||
|
"""Representation of the HomematicIP full flush lock controller opener."""
|
||||||
|
|
||||||
|
def __init__(self, hap: HomematicipHAP, device) -> None:
|
||||||
|
"""Initialize the full flush lock controller opener button."""
|
||||||
|
super().__init__(hap, device, post="Door opener")
|
||||||
|
self._attr_icon = "mdi:door-open"
|
||||||
|
|
||||||
|
async def async_press(self) -> None:
|
||||||
|
"""Handle the button press."""
|
||||||
|
await self._device.send_start_impulse_async()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Initializer helpers for HomematicIP fake server."""
|
"""Initializer helpers for HomematicIP fake server."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
from homematicip.async_home import AsyncHome
|
from homematicip.async_home import AsyncHome
|
||||||
@@ -69,6 +70,124 @@ async def default_mock_hap_factory_fixture(
|
|||||||
return HomeFactory(hass, mock_connection, hmip_config_entry)
|
return HomeFactory(hass, mock_connection, hmip_config_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="full_flush_lock_controller_device_data")
|
||||||
|
def full_flush_lock_controller_device_data_fixture() -> dict[str, Any]:
|
||||||
|
"""Return fixture data for an HmIP-FLC device."""
|
||||||
|
return {
|
||||||
|
"availableFirmwareVersion": "0.0.0",
|
||||||
|
"connectionType": "HMIP_RF",
|
||||||
|
"deviceArchetype": "HMIP",
|
||||||
|
"firmwareVersion": "1.0.10",
|
||||||
|
"firmwareVersionInteger": 65546,
|
||||||
|
"functionalChannels": {
|
||||||
|
"0": {
|
||||||
|
"configPending": False,
|
||||||
|
"deviceId": "3014F7110000000000000026",
|
||||||
|
"dutyCycle": False,
|
||||||
|
"functionalChannelType": "DEVICE_BASE",
|
||||||
|
"groupIndex": 0,
|
||||||
|
"groups": [],
|
||||||
|
"index": 0,
|
||||||
|
"label": "",
|
||||||
|
"lowBat": None,
|
||||||
|
"routerModuleEnabled": False,
|
||||||
|
"routerModuleSupported": False,
|
||||||
|
"rssiDeviceValue": -82,
|
||||||
|
"rssiPeerValue": -97,
|
||||||
|
"supportedOptionalFeatures": {
|
||||||
|
"IFeatureRssiValue": True,
|
||||||
|
"IOptionalFeatureDutyCycle": True,
|
||||||
|
"IOptionalFeatureLowBat": False,
|
||||||
|
},
|
||||||
|
"unreach": False,
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"actionParameter": "NOT_CUSTOMISABLE",
|
||||||
|
"binaryBehaviorType": "NORMALLY_OPEN",
|
||||||
|
"channelRole": "DOOR_LOCK_SENSOR",
|
||||||
|
"corrosionPreventionActive": False,
|
||||||
|
"deviceId": "3014F7110000000000000026",
|
||||||
|
"doorBellSensorEventTimestamp": None,
|
||||||
|
"eventDelay": 0,
|
||||||
|
"functionalChannelType": "MULTI_MODE_LOCK_INPUT_CHANNEL",
|
||||||
|
"glassBroken": True,
|
||||||
|
"groupIndex": 1,
|
||||||
|
"groups": [],
|
||||||
|
"index": 1,
|
||||||
|
"label": "",
|
||||||
|
"lockState": "LOCKED",
|
||||||
|
"multiModeInputMode": "BINARY_BEHAVIOR",
|
||||||
|
"supportedOptionalFeatures": {},
|
||||||
|
"windowState": "OPEN",
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"channelRole": "DOOR_LOCK_ACTUATOR",
|
||||||
|
"deviceId": "3014F7110000000000000026",
|
||||||
|
"doorLockActive": False,
|
||||||
|
"functionalChannelType": "DOOR_SWITCH_CHANNEL",
|
||||||
|
"groupIndex": 3,
|
||||||
|
"groups": [],
|
||||||
|
"impulseDuration": 111600.0,
|
||||||
|
"index": 3,
|
||||||
|
"internalLinkConfiguration": {
|
||||||
|
"firstInputAction": "TOGGLE",
|
||||||
|
"internalLinkConfigurationType": "SINGLE_INPUT_DOOR_SWITCH",
|
||||||
|
},
|
||||||
|
"label": "",
|
||||||
|
"multiModeInputMode": "KEY_BEHAVIOR",
|
||||||
|
"processing": False,
|
||||||
|
"profileMode": "AUTOMATIC",
|
||||||
|
"supportedOptionalFeatures": {},
|
||||||
|
"userDesiredProfileMode": "AUTOMATIC",
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"channelRole": "DOOR_OPENER_ACTUATOR",
|
||||||
|
"deviceId": "3014F7110000000000000026",
|
||||||
|
"doorLockActive": False,
|
||||||
|
"functionalChannelType": "DOOR_SWITCH_CHANNEL",
|
||||||
|
"groupIndex": 4,
|
||||||
|
"groups": [],
|
||||||
|
"impulseDuration": 0.9,
|
||||||
|
"index": 4,
|
||||||
|
"internalLinkConfiguration": {
|
||||||
|
"firstInputAction": "LOCK_OPEN",
|
||||||
|
"internalLinkConfigurationType": "SINGLE_INPUT_DOOR_SWITCH",
|
||||||
|
},
|
||||||
|
"label": "",
|
||||||
|
"multiModeInputMode": "SWITCH_BEHAVIOR",
|
||||||
|
"processing": False,
|
||||||
|
"profileMode": "AUTOMATIC",
|
||||||
|
"supportedOptionalFeatures": {},
|
||||||
|
"userDesiredProfileMode": "AUTOMATIC",
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"authorized": True,
|
||||||
|
"channelRole": "DOOR_LOCK_ACTUATOR",
|
||||||
|
"deviceId": "3014F7110000000000000026",
|
||||||
|
"functionalChannelType": "ACCESS_AUTHORIZATION_CHANNEL",
|
||||||
|
"groupIndex": 3,
|
||||||
|
"groups": [],
|
||||||
|
"index": 5,
|
||||||
|
"label": "",
|
||||||
|
"supportedOptionalFeatures": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"homeId": "00000000-0000-0000-0000-000000000001",
|
||||||
|
"id": "3014F7110000000000000026",
|
||||||
|
"label": "Universal Motorschloss Controller",
|
||||||
|
"lastStatusUpdate": 1760619002144,
|
||||||
|
"liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED",
|
||||||
|
"manufacturerCode": 1,
|
||||||
|
"modelId": 546,
|
||||||
|
"modelType": "HmIP-FLC",
|
||||||
|
"oem": "eQ-3",
|
||||||
|
"permanentlyReachable": True,
|
||||||
|
"serializedGlobalTradeItemNumber": "3014F7110000000000000026",
|
||||||
|
"type": "FULL_FLUSH_LOCK_CONTROLLER",
|
||||||
|
"updateState": "UP_TO_DATE",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="hmip_config")
|
@pytest.fixture(name="hmip_config")
|
||||||
def hmip_config_fixture() -> ConfigType:
|
def hmip_config_fixture() -> ConfigType:
|
||||||
"""Create a config for homematic ip cloud."""
|
"""Create a config for homematic ip cloud."""
|
||||||
|
|||||||
@@ -109,7 +109,10 @@ class HomeFactory:
|
|||||||
self.hmip_config_entry = hmip_config_entry
|
self.hmip_config_entry = hmip_config_entry
|
||||||
|
|
||||||
async def async_get_mock_hap(
|
async def async_get_mock_hap(
|
||||||
self, test_devices=None, test_groups=None
|
self,
|
||||||
|
test_devices=None,
|
||||||
|
test_groups=None,
|
||||||
|
extra_devices: list[dict[str, Any]] | None = None,
|
||||||
) -> HomematicipHAP:
|
) -> HomematicipHAP:
|
||||||
"""Create a mocked homematic access point."""
|
"""Create a mocked homematic access point."""
|
||||||
home_name = self.hmip_config_entry.data["name"]
|
home_name = self.hmip_config_entry.data["name"]
|
||||||
@@ -119,6 +122,7 @@ class HomeFactory:
|
|||||||
home_name=home_name,
|
home_name=home_name,
|
||||||
test_devices=test_devices,
|
test_devices=test_devices,
|
||||||
test_groups=test_groups,
|
test_groups=test_groups,
|
||||||
|
extra_devices=extra_devices,
|
||||||
)
|
)
|
||||||
.init_home()
|
.init_home()
|
||||||
.get_async_home_mock()
|
.get_async_home_mock()
|
||||||
@@ -156,7 +160,12 @@ class HomeTemplate(Home):
|
|||||||
_typeSecurityEventMap = TYPE_SECURITY_EVENT_MAP
|
_typeSecurityEventMap = TYPE_SECURITY_EVENT_MAP
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, connection=None, home_name="", test_devices=None, test_groups=None
|
self,
|
||||||
|
connection=None,
|
||||||
|
home_name="",
|
||||||
|
test_devices=None,
|
||||||
|
test_groups=None,
|
||||||
|
extra_devices: list[dict[str, Any]] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Init template with connection."""
|
"""Init template with connection."""
|
||||||
super().__init__(connection=connection)
|
super().__init__(connection=connection)
|
||||||
@@ -166,8 +175,12 @@ class HomeTemplate(Home):
|
|||||||
self.init_json_state = None
|
self.init_json_state = None
|
||||||
self.test_devices = test_devices
|
self.test_devices = test_devices
|
||||||
self.test_groups = test_groups
|
self.test_groups = test_groups
|
||||||
|
self.extra_devices = extra_devices or []
|
||||||
|
|
||||||
def _cleanup_json(self, json):
|
def _cleanup_json(self, json):
|
||||||
|
for extra_device in self.extra_devices:
|
||||||
|
json["devices"][extra_device["id"]] = extra_device
|
||||||
|
|
||||||
if self.test_devices is not None:
|
if self.test_devices is not None:
|
||||||
new_devices = {}
|
new_devices = {}
|
||||||
for json_device in json["devices"].items():
|
for json_device in json["devices"].items():
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"""Tests for HomematicIP Cloud binary sensor."""
|
"""Tests for HomematicIP Cloud binary sensor."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from homematicip.base.enums import SmokeDetectorAlarmType, WindowState
|
from homematicip.base.enums import SmokeDetectorAlarmType, WindowState
|
||||||
|
|
||||||
from homeassistant.components.homematicip_cloud.binary_sensor import (
|
from homeassistant.components.homematicip_cloud.binary_sensor import (
|
||||||
@@ -27,6 +29,49 @@ from homeassistant.core import HomeAssistant
|
|||||||
from .helper import HomeFactory, async_manipulate_test_data, get_and_check_entity_basics
|
from .helper import HomeFactory, async_manipulate_test_data, get_and_check_entity_basics
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hmip_full_flush_lock_controller_binary_sensors(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
default_mock_hap_factory: HomeFactory,
|
||||||
|
full_flush_lock_controller_device_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
"""Test HomematicIP full flush lock controller binary sensors."""
|
||||||
|
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||||
|
test_devices=["Universal Motorschloss Controller"],
|
||||||
|
extra_devices=[full_flush_lock_controller_device_data],
|
||||||
|
)
|
||||||
|
|
||||||
|
lock_entity_id = "binary_sensor.universal_motorschloss_controller_locked"
|
||||||
|
lock_state, hmip_device = get_and_check_entity_basics(
|
||||||
|
hass,
|
||||||
|
mock_hap,
|
||||||
|
lock_entity_id,
|
||||||
|
"Universal Motorschloss Controller Locked",
|
||||||
|
"HmIP-FLC",
|
||||||
|
)
|
||||||
|
assert lock_state.state == STATE_ON
|
||||||
|
|
||||||
|
glass_entity_id = "binary_sensor.universal_motorschloss_controller_glass_break"
|
||||||
|
glass_state, _ = get_and_check_entity_basics(
|
||||||
|
hass,
|
||||||
|
mock_hap,
|
||||||
|
glass_entity_id,
|
||||||
|
"Universal Motorschloss Controller Glass break",
|
||||||
|
"HmIP-FLC",
|
||||||
|
)
|
||||||
|
assert glass_state.state == STATE_ON
|
||||||
|
|
||||||
|
assert hmip_device is not None
|
||||||
|
await async_manipulate_test_data(hass, hmip_device, "lockState", "UNLOCKED")
|
||||||
|
lock_state = hass.states.get(lock_entity_id)
|
||||||
|
assert lock_state
|
||||||
|
assert lock_state.state == STATE_OFF
|
||||||
|
|
||||||
|
await async_manipulate_test_data(hass, hmip_device, "glassBroken", False)
|
||||||
|
glass_state = hass.states.get(glass_entity_id)
|
||||||
|
assert glass_state
|
||||||
|
assert glass_state.state == STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
async def test_hmip_home_cloud_connection_sensor(
|
async def test_hmip_home_cloud_connection_sensor(
|
||||||
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
hass: HomeAssistant, default_mock_hap_factory: HomeFactory
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"""Tests for HomematicIP Cloud button."""
|
"""Tests for HomematicIP Cloud button."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
|
||||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
|
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
|
||||||
@@ -41,3 +43,41 @@ async def test_hmip_garage_door_controller_button(
|
|||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state
|
assert state
|
||||||
assert state.state == now.isoformat()
|
assert state.state == now.isoformat()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hmip_full_flush_lock_controller_button(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
default_mock_hap_factory: HomeFactory,
|
||||||
|
full_flush_lock_controller_device_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
"""Test HomematicIP full flush lock controller opener button."""
|
||||||
|
entity_id = "button.universal_motorschloss_controller_door_opener"
|
||||||
|
entity_name = "Universal Motorschloss Controller Door opener"
|
||||||
|
device_model = "HmIP-FLC"
|
||||||
|
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
|
||||||
|
test_devices=["Universal Motorschloss Controller"],
|
||||||
|
extra_devices=[full_flush_lock_controller_device_data],
|
||||||
|
)
|
||||||
|
|
||||||
|
get_and_check_entity_basics(hass, mock_hap, entity_id, entity_name, device_model)
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
now = dt_util.parse_datetime("2021-01-09 12:00:00+00:00")
|
||||||
|
freezer.move_to(now)
|
||||||
|
await hass.services.async_call(
|
||||||
|
BUTTON_DOMAIN,
|
||||||
|
SERVICE_PRESS,
|
||||||
|
{ATTR_ENTITY_ID: entity_id},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
hmip_device = mock_hap.hmip_device_by_entity_id[entity_id]
|
||||||
|
assert hmip_device.mock_calls[-1][0] == "send_start_impulse_async"
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == now.isoformat()
|
||||||
|
|||||||
Reference in New Issue
Block a user