From bc216a2394e00950599c01174cbd236f1f8f9cae Mon Sep 17 00:00:00 2001 From: Mattias Welponer Date: Sat, 7 Jul 2018 11:34:36 +0200 Subject: [PATCH 1/6] Add HomematicIP security zone --- .../alarm_control_panel/__init__.py | 13 ++- .../alarm_control_panel/homematicip_cloud.py | 86 +++++++++++++++++++ .../components/homematicip_cloud/const.py | 1 + 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/alarm_control_panel/homematicip_cloud.py diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index f81d2ef1037cd..84a72945a7e26 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -121,7 +121,7 @@ def alarm_arm_custom_bypass(hass, code=None, entity_id=None): @asyncio.coroutine def async_setup(hass, config): """Track states and offer events for sensors.""" - component = EntityComponent( + component = hass.data[DOMAIN] = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) yield from component.async_setup(config) @@ -154,6 +154,17 @@ def async_alarm_service_handler(service): return True +async def async_setup_entry(hass, entry): + """Setup a config entry.""" + return await hass.data[DOMAIN].async_setup_entry(entry) + + +async def async_unload_entry(hass, entry): + """Unload a config entry.""" + return await hass.data[DOMAIN].async_unload_entry(entry) + + +# pylint: disable=no-self-use class AlarmControlPanel(Entity): """An abstract class for alarm control devices.""" diff --git a/homeassistant/components/alarm_control_panel/homematicip_cloud.py b/homeassistant/components/alarm_control_panel/homematicip_cloud.py new file mode 100644 index 0000000000000..a9fdc819e17e1 --- /dev/null +++ b/homeassistant/components/alarm_control_panel/homematicip_cloud.py @@ -0,0 +1,86 @@ +""" +Support for HomematicIP alarm control panel. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/alarm_control_panel.homematicip_cloud/ +""" + +import logging + +from homeassistant.const import ( + STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, + STATE_ALARM_TRIGGERED) +from homeassistant.components.alarm_control_panel import AlarmControlPanel +from homeassistant.components.homematicip_cloud import ( + HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN, + HMIPC_HAPID) + + +DEPENDENCIES = ['homematicip_cloud'] + +_LOGGER = logging.getLogger(__name__) + +HMIP_OPEN = 'OPEN' +HMIP_ZONE_AWAY = 'EXTERNAL' +HMIP_ZONE_HOME = 'INTERNAL' + + +async def async_setup_platform(hass, config, async_add_devices, + discovery_info=None): + """Set up the HomematicIP alarm control devices.""" + pass + + +async def async_setup_entry(hass, config_entry, async_add_devices): + """Set up the HomematicIP climate from a config entry.""" + from homematicip.aio.group import AsyncSecurityZoneGroup + + home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home + devices = [] + for group in home.groups: + if isinstance(group, AsyncSecurityZoneGroup): + devices.append(HomematicipSecurityZone(home, group)) + + if devices: + async_add_devices(devices) + + +class HomematicipSecurityZone(HomematicipGenericDevice, AlarmControlPanel): + """Representation of an HomematicIP security zone group.""" + + def __init__(self, home, device): + """Initialize the security zone group.""" + device.modelType = 'Group-SecurityZone' + device.windowState = '' + super().__init__(home, device) + + @property + def state(self): + """Return the state of the device.""" + if (self._device.sabotage or self._device.motionDetected or + self._device.windowState == HMIP_OPEN): + return STATE_ALARM_TRIGGERED + + if self._device.active: + if self._device.label == HMIP_ZONE_HOME: + return STATE_ALARM_ARMED_HOME + return STATE_ALARM_ARMED_AWAY + + return STATE_ALARM_DISARMED + + async def async_alarm_disarm(self, code=None): + """Send disarm command.""" + await self._home.set_security_zones_activation(False, False) + + async def async_alarm_arm_home(self, code=None): + """Send arm home command.""" + await self._home.set_security_zones_activation(True, False) + + async def async_alarm_arm_away(self, code=None): + """Send arm away command.""" + await self._home.set_security_zones_activation(True, True) + + @property + def device_state_attributes(self): + """Return the state attributes of the alarm control device.""" + return {} diff --git a/homeassistant/components/homematicip_cloud/const.py b/homeassistant/components/homematicip_cloud/const.py index c40e577ae4a57..54b05c464b546 100644 --- a/homeassistant/components/homematicip_cloud/const.py +++ b/homeassistant/components/homematicip_cloud/const.py @@ -6,6 +6,7 @@ DOMAIN = 'homematicip_cloud' COMPONENTS = [ + 'alarm_control_panel', 'binary_sensor', 'climate', 'light', From e4e5f396ced6f190647c827b55c9ad55fcd1bebc Mon Sep 17 00:00:00 2001 From: Mattias Welponer Date: Sat, 7 Jul 2018 14:50:06 +0200 Subject: [PATCH 2/6] Update access point tests --- tests/components/homematicip_cloud/test_hap.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/components/homematicip_cloud/test_hap.py b/tests/components/homematicip_cloud/test_hap.py index 5344773fde659..476bed368d796 100644 --- a/tests/components/homematicip_cloud/test_hap.py +++ b/tests/components/homematicip_cloud/test_hap.py @@ -65,8 +65,10 @@ async def test_hap_setup_works(aioclient_mock): assert await hap.async_setup() is True assert hap.home is home - assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 5 + assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 6 assert hass.config_entries.async_forward_entry_setup.mock_calls[0][1] == \ + (entry, 'alarm_control_panel') + assert hass.config_entries.async_forward_entry_setup.mock_calls[1][1] == \ (entry, 'binary_sensor') @@ -104,10 +106,10 @@ async def test_hap_reset_unloads_entry_if_setup(): assert hap.home is home assert len(hass.services.async_register.mock_calls) == 0 - assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 5 + assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 6 hass.config_entries.async_forward_entry_unload.return_value = \ mock_coro(True) await hap.async_reset() - assert len(hass.config_entries.async_forward_entry_unload.mock_calls) == 5 + assert len(hass.config_entries.async_forward_entry_unload.mock_calls) == 6 From 5590ccffc2e4a5c07d493e39e23a31df0eaa7ac9 Mon Sep 17 00:00:00 2001 From: Mattias Welponer Date: Sun, 8 Jul 2018 22:13:11 +0200 Subject: [PATCH 3/6] Fix state if not armed and coments --- .../alarm_control_panel/homematicip_cloud.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/homematicip_cloud.py b/homeassistant/components/alarm_control_panel/homematicip_cloud.py index a9fdc819e17e1..52188798503d3 100644 --- a/homeassistant/components/alarm_control_panel/homematicip_cloud.py +++ b/homeassistant/components/alarm_control_panel/homematicip_cloud.py @@ -32,7 +32,7 @@ async def async_setup_platform(hass, config, async_add_devices, async def async_setup_entry(hass, config_entry, async_add_devices): - """Set up the HomematicIP climate from a config entry.""" + """Set up the HomematicIP alarm control panel from a config entry.""" from homematicip.aio.group import AsyncSecurityZoneGroup home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home @@ -57,11 +57,11 @@ def __init__(self, home, device): @property def state(self): """Return the state of the device.""" - if (self._device.sabotage or self._device.motionDetected or - self._device.windowState == HMIP_OPEN): - return STATE_ALARM_TRIGGERED - if self._device.active: + if (self._device.sabotage or self._device.motionDetected or + self._device.windowState == HMIP_OPEN): + return STATE_ALARM_TRIGGERED + if self._device.label == HMIP_ZONE_HOME: return STATE_ALARM_ARMED_HOME return STATE_ALARM_ARMED_AWAY @@ -83,4 +83,4 @@ async def async_alarm_arm_away(self, code=None): @property def device_state_attributes(self): """Return the state attributes of the alarm control device.""" - return {} + return None From 3f4aa7c1e3b80b33974072400e03970ca928badf Mon Sep 17 00:00:00 2001 From: Mattias Welponer Date: Mon, 9 Jul 2018 06:57:07 +0200 Subject: [PATCH 4/6] Add comment for the empty state_attributes --- .../components/alarm_control_panel/homematicip_cloud.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/alarm_control_panel/homematicip_cloud.py b/homeassistant/components/alarm_control_panel/homematicip_cloud.py index 52188798503d3..fd402b96ceedb 100644 --- a/homeassistant/components/alarm_control_panel/homematicip_cloud.py +++ b/homeassistant/components/alarm_control_panel/homematicip_cloud.py @@ -83,4 +83,6 @@ async def async_alarm_arm_away(self, code=None): @property def device_state_attributes(self): """Return the state attributes of the alarm control device.""" + "The base class is loading the battery proprety, but device doesnt" + "have this property - base class needs clean-up" return None From 94cd3823c25229db24e9e4f0aab3507a12ae1c07 Mon Sep 17 00:00:00 2001 From: Mattias Welponer Date: Mon, 9 Jul 2018 07:12:32 +0200 Subject: [PATCH 5/6] Fix comment --- .../components/alarm_control_panel/homematicip_cloud.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/homematicip_cloud.py b/homeassistant/components/alarm_control_panel/homematicip_cloud.py index fd402b96ceedb..03f0d368efa2c 100644 --- a/homeassistant/components/alarm_control_panel/homematicip_cloud.py +++ b/homeassistant/components/alarm_control_panel/homematicip_cloud.py @@ -83,6 +83,6 @@ async def async_alarm_arm_away(self, code=None): @property def device_state_attributes(self): """Return the state attributes of the alarm control device.""" - "The base class is loading the battery proprety, but device doesnt" - "have this property - base class needs clean-up" + # The base class is loading the battery proprety, but device doesnt + # have this property - base class needs clean-up. return None From 87e73e655a0f5efd3affd5f65b1038b126bc148e Mon Sep 17 00:00:00 2001 From: Mattias Welponer Date: Tue, 10 Jul 2018 21:52:20 +0200 Subject: [PATCH 6/6] Fix spelling --- .../components/alarm_control_panel/homematicip_cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/alarm_control_panel/homematicip_cloud.py b/homeassistant/components/alarm_control_panel/homematicip_cloud.py index 03f0d368efa2c..893fa76c44b4b 100644 --- a/homeassistant/components/alarm_control_panel/homematicip_cloud.py +++ b/homeassistant/components/alarm_control_panel/homematicip_cloud.py @@ -83,6 +83,6 @@ async def async_alarm_arm_away(self, code=None): @property def device_state_attributes(self): """Return the state attributes of the alarm control device.""" - # The base class is loading the battery proprety, but device doesnt + # The base class is loading the battery property, but device doesn't # have this property - base class needs clean-up. return None