Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add AWAY state configuration #107

Merged
merged 3 commits into from
Nov 8, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
feat: add ARM AWAY configuration
palazzem committed Nov 8, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 4ce3343540e67f7f58a9f28776f6237fced4d5e7
2 changes: 1 addition & 1 deletion custom_components/econnect_metronet/alarm_control_panel.py
Original file line number Diff line number Diff line change
@@ -100,7 +100,7 @@ async def async_alarm_disarm(self, code=None):
@set_device_state(STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMING)
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
await self.hass.async_add_executor_job(self._device.arm, code)
await self.hass.async_add_executor_job(self._device.arm, code, self._device._sectors_away)

@set_device_state(STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMING)
async def async_alarm_arm_home(self, code=None):
8 changes: 7 additions & 1 deletion custom_components/econnect_metronet/config_flow.py
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
from requests.exceptions import ConnectionError, HTTPError

from .const import (
CONF_AREAS_ARM_AWAY,
CONF_AREAS_ARM_HOME,
CONF_AREAS_ARM_NIGHT,
CONF_AREAS_ARM_VACATION,
@@ -104,7 +105,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
"""Reconfigure integration options.

Available options are:
* Areas armed in Arm Away state
* Areas armed in Arm Away state. If not set all sectors are armed.
* Areas armed in Arm Home state
* Areas armed in Arm Night state
* Areas armed in Arm Vacation state
"""
@@ -131,6 +133,10 @@ async def async_step_init(self, user_input=None):
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_AREAS_ARM_AWAY,
default=self.config_entry.options.get(CONF_AREAS_ARM_AWAY, []),
): select(sectors),
vol.Optional(
CONF_AREAS_ARM_HOME,
default=self.config_entry.options.get(CONF_AREAS_ARM_HOME, []),
1 change: 1 addition & 0 deletions custom_components/econnect_metronet/const.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
CONF_DOMAIN = "domain"
CONF_SYSTEM_URL = "system_base_url"
CONF_SYSTEM_NAME = "system_name"
CONF_AREAS_ARM_AWAY = "areas_arm_away"
CONF_AREAS_ARM_HOME = "areas_arm_home"
CONF_AREAS_ARM_NIGHT = "areas_arm_night"
CONF_AREAS_ARM_VACATION = "areas_arm_vacation"
8 changes: 7 additions & 1 deletion custom_components/econnect_metronet/devices.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,12 @@
)
from requests.exceptions import HTTPError

from .const import CONF_AREAS_ARM_HOME, CONF_AREAS_ARM_NIGHT, CONF_AREAS_ARM_VACATION
from .const import (
CONF_AREAS_ARM_AWAY,
CONF_AREAS_ARM_HOME,
CONF_AREAS_ARM_NIGHT,
CONF_AREAS_ARM_VACATION,
)

_LOGGER = logging.getLogger(__name__)

@@ -45,6 +50,7 @@ def __init__(self, connection, config=None):

# Load user configuration
config = config or {}
self._sectors_away = config.get(CONF_AREAS_ARM_AWAY) or []
self._sectors_home = config.get(CONF_AREAS_ARM_HOME) or []
self._sectors_night = config.get(CONF_AREAS_ARM_NIGHT) or []
self._sectors_vacation = config.get(CONF_AREAS_ARM_VACATION) or []
22 changes: 22 additions & 0 deletions tests/test_alarm_panel.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

import pytest
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from custom_components.econnect_metronet.alarm_control_panel import EconnectAlarm
@@ -38,3 +39,24 @@ def test_alarm_panel_entity_id_with_system_name(client, hass, config_entry):
coordinator = DataUpdateCoordinator(hass, logging.getLogger(__name__), name="econnect_metronet")
entity = EconnectAlarm("test_id", config_entry, device, coordinator)
assert entity.entity_id == "econnect_metronet.econnect_metronet_home"


@pytest.mark.asyncio
async def test_alarm_panel_arm_away(mocker, panel):
# Ensure an empty AWAY config arms all sectors
arm = mocker.patch.object(panel._device._connection, "arm", autopsec=True)
# Test
await panel.async_alarm_arm_away(code=42)
assert arm.call_count == 1
assert arm.call_args.kwargs["sectors"] == []


@pytest.mark.asyncio
async def test_alarm_panel_arm_away_with_options(mocker, panel):
# Ensure an empty AWAY config arms all sectors
arm = mocker.patch.object(panel._device._connection, "arm", autopsec=True)
panel._device._sectors_away = [1, 2]
# Test
await panel.async_alarm_arm_away(code=42)
assert arm.call_count == 1
assert arm.call_args.kwargs["sectors"] == [1, 2]
25 changes: 25 additions & 0 deletions tests/test_devices.py
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
from requests.models import Response

from custom_components.econnect_metronet.const import (
CONF_AREAS_ARM_AWAY,
CONF_AREAS_ARM_HOME,
CONF_AREAS_ARM_NIGHT,
CONF_AREAS_ARM_VACATION,
@@ -27,6 +28,7 @@ def test_device_constructor(client):
assert device._connection == client
assert device._inventory == {}
assert device._last_ids == {10: 0, 9: 0, 11: 0}
assert device._sectors_away == []
assert device._sectors_home == []
assert device._sectors_night == []
assert device._sectors_vacation == []
@@ -36,6 +38,7 @@ def test_device_constructor(client):
def test_device_constructor_with_config(client):
"""Should initialize defaults attributes to run properly."""
config = {
CONF_AREAS_ARM_AWAY: [1, 2, 3, 4, 5],
CONF_AREAS_ARM_HOME: [3, 4],
CONF_AREAS_ARM_NIGHT: [1, 2, 3],
CONF_AREAS_ARM_VACATION: [5, 3],
@@ -45,6 +48,7 @@ def test_device_constructor_with_config(client):
assert device._connection == client
assert device._inventory == {}
assert device._last_ids == {10: 0, 9: 0, 11: 0}
assert device._sectors_away == [1, 2, 3, 4, 5]
assert device._sectors_home == [3, 4]
assert device._sectors_night == [1, 2, 3]
assert device._sectors_vacation == [5, 3]
@@ -54,6 +58,7 @@ def test_device_constructor_with_config(client):
def test_device_constructor_with_config_empty(client):
"""Should initialize defaults attributes to run properly."""
config = {
CONF_AREAS_ARM_AWAY: None,
CONF_AREAS_ARM_HOME: None,
CONF_AREAS_ARM_NIGHT: None,
CONF_AREAS_ARM_VACATION: None,
@@ -63,6 +68,7 @@ def test_device_constructor_with_config_empty(client):
assert device._connection == client
assert device._inventory == {}
assert device._last_ids == {10: 0, 9: 0, 11: 0}
assert device._sectors_away == []
assert device._sectors_home == []
assert device._sectors_night == []
assert device._sectors_vacation == []
@@ -871,6 +877,7 @@ def test_get_state_armed_vacation_out_of_order(alarm_device):

def test_get_state_armed_away(alarm_device):
"""Test when sectors are armed but don't match home or night."""
alarm_device._sectors_away = []
alarm_device._sectors_home = [1, 2, 3]
alarm_device._sectors_night = [4, 5, 6]
alarm_device._sectors_vacation = [4, 2]
@@ -887,6 +894,7 @@ def test_get_state_armed_away(alarm_device):

def test_get_state_armed_mixed(alarm_device):
"""Test when some sectors from home and night are armed."""
alarm_device._sectors_away = []
alarm_device._sectors_home = [1, 2, 3]
alarm_device._sectors_night = [4, 5, 6]
alarm_device._inventory = {
@@ -899,3 +907,20 @@ def test_get_state_armed_mixed(alarm_device):
}
# Test
assert alarm_device.get_state() == STATE_ALARM_ARMED_AWAY


def test_get_state_armed_away_with_config(alarm_device):
# Ensure arm AWAY is set when it matches the config value
alarm_device._sectors_away = [4]
alarm_device._sectors_home = [1, 2, 3]
alarm_device._sectors_night = [4, 5, 6]
alarm_device._sectors_vacation = [4, 2]
alarm_device._inventory = {
9: {
0: {"id": 1, "index": 0, "element": 1, "excluded": False, "status": False, "name": "S1 Living Room"},
1: {"id": 2, "index": 1, "element": 2, "excluded": False, "status": False, "name": "S2 Bedroom"},
2: {"id": 3, "index": 2, "element": 4, "excluded": False, "status": True, "name": "S3 Outdoor"},
}
}
# Test
assert alarm_device.get_state() == STATE_ALARM_ARMED_AWAY
15 changes: 14 additions & 1 deletion tests/test_options_flow.py
Original file line number Diff line number Diff line change
@@ -25,11 +25,13 @@ async def test_form_fields(self, hass, config_entry):
assert form["step_id"] == "init"
assert form["errors"] == {}
assert list(form["data_schema"].schema.keys()) == [
"areas_arm_away",
"areas_arm_home",
"areas_arm_night",
"areas_arm_vacation",
"scan_interval",
]
assert isinstance(form["data_schema"].schema["areas_arm_away"], select)
assert isinstance(form["data_schema"].schema["areas_arm_home"], select)
assert isinstance(form["data_schema"].schema["areas_arm_night"], select)
assert isinstance(form["data_schema"].schema["areas_arm_vacation"], select)
@@ -49,7 +51,12 @@ async def test_form_submit_successful_empty(self, hass, config_entry):
# Check HA config
assert result["type"] == "create_entry"
assert result["title"] == "e-Connect/Metronet Alarm"
assert result["data"] == {"areas_arm_vacation": [], "areas_arm_home": [], "areas_arm_night": []}
assert result["data"] == {
"areas_arm_vacation": [],
"areas_arm_home": [],
"areas_arm_night": [],
"areas_arm_away": [],
}

async def test_form_submit_invalid_type(self, hass, config_entry):
# Ensure it fails if a user submits an option with an invalid type
@@ -102,6 +109,7 @@ async def test_form_submit_successful_with_identifier(self, hass, config_entry):
assert result["type"] == "create_entry"
assert result["title"] == "e-Connect/Metronet Alarm"
assert result["data"] == {
"areas_arm_away": [],
"areas_arm_home": [1],
"areas_arm_night": [],
"areas_arm_vacation": [],
@@ -127,6 +135,7 @@ async def test_form_submit_successful_with_input(self, hass, config_entry):
assert result["type"] == "create_entry"
assert result["title"] == "e-Connect/Metronet Alarm"
assert result["data"] == {
"areas_arm_away": [],
"areas_arm_home": [(1, "S1 Living Room")],
"areas_arm_night": [],
"areas_arm_vacation": [],
@@ -142,6 +151,9 @@ async def test_form_submit_successful_with_multiple_inputs(self, hass, config_en
result = await hass.config_entries.options.async_configure(
form["flow_id"],
user_input={
"areas_arm_away": [
(2, "S2 Bedroom"),
],
"areas_arm_home": [
(1, "S1 Living Room"),
],
@@ -156,6 +168,7 @@ async def test_form_submit_successful_with_multiple_inputs(self, hass, config_en
assert result["type"] == "create_entry"
assert result["title"] == "e-Connect/Metronet Alarm"
assert result["data"] == {
"areas_arm_away": [(2, "S2 Bedroom")],
"areas_arm_home": [(1, "S1 Living Room")],
"areas_arm_night": [(1, "S1 Living Room")],
"areas_arm_vacation": [(1, "S1 Living Room"), (2, "S2 Bedroom")],