Fix HomematicIP Cloud Alarm Control Panel support for basic mode (#28778)
This commit is contained in:
parent
b4ccc0202a
commit
60e7440ec1
4 changed files with 31 additions and 113 deletions
|
@ -1,8 +1,7 @@
|
|||
"""Support for HomematicIP Cloud alarm control panel."""
|
||||
import logging
|
||||
|
||||
from homematicip.aio.group import AsyncSecurityZoneGroup
|
||||
from homematicip.base.enums import WindowState
|
||||
from homematicip.functionalHomes import SecurityAndAlarmHome
|
||||
|
||||
from homeassistant.components.alarm_control_panel import AlarmControlPanel
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
@ -32,34 +31,15 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up the HomematicIP alrm control panel from a config entry."""
|
||||
hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]]
|
||||
devices = []
|
||||
security_zones = []
|
||||
for group in hap.home.groups:
|
||||
if isinstance(group, AsyncSecurityZoneGroup):
|
||||
security_zones.append(group)
|
||||
|
||||
if security_zones:
|
||||
devices.append(HomematicipAlarmControlPanel(hap, security_zones))
|
||||
|
||||
if devices:
|
||||
async_add_entities(devices)
|
||||
async_add_entities([HomematicipAlarmControlPanel(hap)])
|
||||
|
||||
|
||||
class HomematicipAlarmControlPanel(AlarmControlPanel):
|
||||
"""Representation of an alarm control panel."""
|
||||
|
||||
def __init__(self, hap: HomematicipHAP, security_zones) -> None:
|
||||
def __init__(self, hap: HomematicipHAP) -> None:
|
||||
"""Initialize the alarm control panel."""
|
||||
self._home = hap.home
|
||||
self.alarm_state = STATE_ALARM_DISARMED
|
||||
self._internal_alarm_zone = None
|
||||
self._external_alarm_zone = None
|
||||
|
||||
for security_zone in security_zones:
|
||||
if security_zone.label == "INTERNAL":
|
||||
self._internal_alarm_zone = security_zone
|
||||
elif security_zone.label == "EXTERNAL":
|
||||
self._external_alarm_zone = security_zone
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
|
@ -75,28 +55,23 @@ class HomematicipAlarmControlPanel(AlarmControlPanel):
|
|||
@property
|
||||
def state(self) -> str:
|
||||
"""Return the state of the device."""
|
||||
# check for triggered alarm
|
||||
if self._security_and_alarm.alarmActive:
|
||||
return STATE_ALARM_TRIGGERED
|
||||
|
||||
activation_state = self._home.get_security_zones_activation()
|
||||
# check arm_away
|
||||
if activation_state == (True, True):
|
||||
if self._internal_alarm_zone_state or self._external_alarm_zone_state:
|
||||
return STATE_ALARM_TRIGGERED
|
||||
return STATE_ALARM_ARMED_AWAY
|
||||
# check arm_home
|
||||
if activation_state == (False, True):
|
||||
if self._external_alarm_zone_state:
|
||||
return STATE_ALARM_TRIGGERED
|
||||
return STATE_ALARM_ARMED_HOME
|
||||
|
||||
return STATE_ALARM_DISARMED
|
||||
|
||||
@property
|
||||
def _internal_alarm_zone_state(self) -> bool:
|
||||
return _get_zone_alarm_state(self._internal_alarm_zone)
|
||||
|
||||
@property
|
||||
def _external_alarm_zone_state(self) -> bool:
|
||||
"""Return the state of the device."""
|
||||
return _get_zone_alarm_state(self._external_alarm_zone)
|
||||
def _security_and_alarm(self):
|
||||
return self._home.get_functionalHome(SecurityAndAlarmHome)
|
||||
|
||||
async def async_alarm_disarm(self, code=None):
|
||||
"""Send disarm command."""
|
||||
|
@ -112,10 +87,7 @@ class HomematicipAlarmControlPanel(AlarmControlPanel):
|
|||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
if self._internal_alarm_zone:
|
||||
self._internal_alarm_zone.on_update(self._async_device_changed)
|
||||
if self._external_alarm_zone:
|
||||
self._external_alarm_zone.on_update(self._async_device_changed)
|
||||
self._home.on_update(self._async_device_changed)
|
||||
|
||||
def _async_device_changed(self, *args, **kwargs):
|
||||
"""Handle device state changes."""
|
||||
|
@ -138,26 +110,9 @@ class HomematicipAlarmControlPanel(AlarmControlPanel):
|
|||
@property
|
||||
def available(self) -> bool:
|
||||
"""Device available."""
|
||||
return (
|
||||
not self._internal_alarm_zone.unreach
|
||||
or not self._external_alarm_zone.unreach
|
||||
)
|
||||
return self._home.connected
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique ID."""
|
||||
return f"{self.__class__.__name__}_{self._home.id}"
|
||||
|
||||
|
||||
def _get_zone_alarm_state(security_zone) -> bool:
|
||||
if security_zone and security_zone.active:
|
||||
if (
|
||||
security_zone.sabotage
|
||||
or security_zone.motionDetected
|
||||
or security_zone.presenceDetected
|
||||
or security_zone.windowState == WindowState.OPEN
|
||||
or security_zone.windowState == WindowState.TILTED
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
"""Tests for HomematicIP Cloud alarm control panel."""
|
||||
from homematicip.base.enums import WindowState
|
||||
from homematicip.group import SecurityZoneGroup
|
||||
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
|
||||
)
|
||||
|
@ -17,29 +14,24 @@ from homeassistant.setup import async_setup_component
|
|||
from .helper import get_and_check_entity_basics
|
||||
|
||||
|
||||
def _get_security_zones(groups): # pylint: disable=W0221
|
||||
"""Get the security zones."""
|
||||
for group in groups:
|
||||
if isinstance(group, SecurityZoneGroup):
|
||||
if group.label == "EXTERNAL":
|
||||
external = group
|
||||
elif group.label == "INTERNAL":
|
||||
internal = group
|
||||
return internal, external
|
||||
|
||||
|
||||
async def _async_manipulate_security_zones(
|
||||
hass, home, internal_active, external_active, window_state
|
||||
hass, home, internal_active=False, external_active=False, alarm_triggered=False
|
||||
):
|
||||
"""Set new values on hmip security zones."""
|
||||
internal_zone, external_zone = _get_security_zones(home.groups)
|
||||
json = home._rawJSONData # pylint: disable=W0212
|
||||
json["functionalHomes"]["SECURITY_AND_ALARM"]["alarmActive"] = alarm_triggered
|
||||
external_zone_id = json["functionalHomes"]["SECURITY_AND_ALARM"]["securityZones"][
|
||||
"EXTERNAL"
|
||||
]
|
||||
internal_zone_id = json["functionalHomes"]["SECURITY_AND_ALARM"]["securityZones"][
|
||||
"INTERNAL"
|
||||
]
|
||||
external_zone = home.search_group_by_id(external_zone_id)
|
||||
external_zone.active = external_active
|
||||
external_zone.windowState = window_state
|
||||
internal_zone = home.search_group_by_id(internal_zone_id)
|
||||
internal_zone.active = internal_active
|
||||
|
||||
# Just one call to a security zone is required to refresh the ACP.
|
||||
internal_zone.fire_update_event()
|
||||
|
||||
home.fire_update_event(json)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
|
@ -70,79 +62,49 @@ async def test_hmip_alarm_control_panel(hass, default_mock_hap):
|
|||
assert not hmip_device
|
||||
|
||||
home = default_mock_hap.home
|
||||
service_call_counter = len(home.mock_calls)
|
||||
|
||||
await hass.services.async_call(
|
||||
"alarm_control_panel", "alarm_arm_away", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
assert len(home.mock_calls) == service_call_counter + 1
|
||||
assert home.mock_calls[-1][0] == "set_security_zones_activation"
|
||||
assert home.mock_calls[-1][1] == (True, True)
|
||||
await _async_manipulate_security_zones(
|
||||
hass,
|
||||
home,
|
||||
internal_active=True,
|
||||
external_active=True,
|
||||
window_state=WindowState.CLOSED,
|
||||
hass, home, internal_active=True, external_active=True
|
||||
)
|
||||
assert hass.states.get(entity_id).state is STATE_ALARM_ARMED_AWAY
|
||||
|
||||
await hass.services.async_call(
|
||||
"alarm_control_panel", "alarm_arm_home", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
assert len(home.mock_calls) == service_call_counter + 3
|
||||
assert home.mock_calls[-1][0] == "set_security_zones_activation"
|
||||
assert home.mock_calls[-1][1] == (False, True)
|
||||
await _async_manipulate_security_zones(
|
||||
hass,
|
||||
home,
|
||||
internal_active=False,
|
||||
external_active=True,
|
||||
window_state=WindowState.CLOSED,
|
||||
)
|
||||
await _async_manipulate_security_zones(hass, home, external_active=True)
|
||||
assert hass.states.get(entity_id).state is STATE_ALARM_ARMED_HOME
|
||||
|
||||
await hass.services.async_call(
|
||||
"alarm_control_panel", "alarm_disarm", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
assert len(home.mock_calls) == service_call_counter + 5
|
||||
assert home.mock_calls[-1][0] == "set_security_zones_activation"
|
||||
assert home.mock_calls[-1][1] == (False, False)
|
||||
await _async_manipulate_security_zones(
|
||||
hass,
|
||||
home,
|
||||
internal_active=False,
|
||||
external_active=False,
|
||||
window_state=WindowState.CLOSED,
|
||||
)
|
||||
await _async_manipulate_security_zones(hass, home)
|
||||
assert hass.states.get(entity_id).state is STATE_ALARM_DISARMED
|
||||
|
||||
await hass.services.async_call(
|
||||
"alarm_control_panel", "alarm_arm_away", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
assert len(home.mock_calls) == service_call_counter + 7
|
||||
assert home.mock_calls[-1][0] == "set_security_zones_activation"
|
||||
assert home.mock_calls[-1][1] == (True, True)
|
||||
await _async_manipulate_security_zones(
|
||||
hass,
|
||||
home,
|
||||
internal_active=True,
|
||||
external_active=True,
|
||||
window_state=WindowState.OPEN,
|
||||
hass, home, internal_active=True, external_active=True, alarm_triggered=True
|
||||
)
|
||||
assert hass.states.get(entity_id).state is STATE_ALARM_TRIGGERED
|
||||
|
||||
await hass.services.async_call(
|
||||
"alarm_control_panel", "alarm_arm_home", {"entity_id": entity_id}, blocking=True
|
||||
)
|
||||
assert len(home.mock_calls) == service_call_counter + 9
|
||||
assert home.mock_calls[-1][0] == "set_security_zones_activation"
|
||||
assert home.mock_calls[-1][1] == (False, True)
|
||||
await _async_manipulate_security_zones(
|
||||
hass,
|
||||
home,
|
||||
internal_active=False,
|
||||
external_active=True,
|
||||
window_state=WindowState.OPEN,
|
||||
hass, home, external_active=True, alarm_triggered=True
|
||||
)
|
||||
assert hass.states.get(entity_id).state is STATE_ALARM_TRIGGERED
|
||||
|
|
|
@ -343,6 +343,7 @@ async def test_hmip_heating_group_heat_with_switch(hass, default_mock_hap):
|
|||
hass, default_mock_hap, entity_id, entity_name, device_model
|
||||
)
|
||||
|
||||
assert hmip_device
|
||||
assert ha_state.state == HVAC_MODE_AUTO
|
||||
assert ha_state.attributes["current_temperature"] == 24.7
|
||||
assert ha_state.attributes["min_temp"] == 5.0
|
||||
|
|
|
@ -161,5 +161,5 @@ async def test_hmip_dump_hap_config_services(hass, mock_hap_with_service):
|
|||
)
|
||||
home = mock_hap_with_service.home
|
||||
assert home.mock_calls[-1][0] == "download_configuration"
|
||||
assert len(home.mock_calls) == 8 # pylint: disable=W0212
|
||||
assert len(write_mock.mock_calls) > 0
|
||||
assert home.mock_calls
|
||||
assert write_mock.mock_calls
|
||||
|
|
Loading…
Add table
Reference in a new issue