mirror of
https://github.com/Electric-Special/ha-core.git
synced 2026-03-21 05:06:13 +01:00
Add Config Flow for Ness Alarm (#162414)
Co-authored-by: Joostlek <joostlek@outlook.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
104
tests/components/ness_alarm/conftest.py
Normal file
104
tests/components/ness_alarm/conftest.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""Test fixtures for ness_alarm."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.ness_alarm.const import DOMAIN
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
class MockClient:
|
||||
"""Mock nessclient.Client stub."""
|
||||
|
||||
async def panic(self, code):
|
||||
"""Handle panic."""
|
||||
|
||||
async def disarm(self, code):
|
||||
"""Handle disarm."""
|
||||
|
||||
async def arm_away(self, code):
|
||||
"""Handle arm_away."""
|
||||
|
||||
async def arm_home(self, code):
|
||||
"""Handle arm_home."""
|
||||
|
||||
async def aux(self, output_id, state):
|
||||
"""Handle auxiliary control."""
|
||||
|
||||
async def keepalive(self):
|
||||
"""Handle keepalive."""
|
||||
|
||||
async def update(self):
|
||||
"""Handle update."""
|
||||
|
||||
def on_zone_change(self):
|
||||
"""Handle on_zone_change."""
|
||||
|
||||
def on_state_change(self):
|
||||
"""Handle on_state_change."""
|
||||
|
||||
async def close(self):
|
||||
"""Handle close."""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_nessclient():
|
||||
"""Mock the nessclient Client constructor.
|
||||
|
||||
Replaces nessclient.Client with a Mock which always returns the same
|
||||
MagicMock() instance.
|
||||
"""
|
||||
_mock_instance = MagicMock(MockClient())
|
||||
_mock_factory = MagicMock()
|
||||
_mock_factory.return_value = _mock_instance
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.ness_alarm.Client", new=_mock_factory, create=True
|
||||
):
|
||||
yield _mock_instance
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return a mock config entry."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_client() -> Generator[AsyncMock]:
|
||||
"""Mock the nessclient Client for config flow tests."""
|
||||
with patch(
|
||||
"homeassistant.components.ness_alarm.config_flow.Client",
|
||||
return_value=AsyncMock(),
|
||||
) as mock:
|
||||
yield mock.return_value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Mock async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.ness_alarm.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def post_connection_delay() -> Generator[None]:
|
||||
"""Mock POST_CONNECTION_DELAY to 0 for faster tests."""
|
||||
with patch(
|
||||
"homeassistant.components.ness_alarm.config_flow.POST_CONNECTION_DELAY",
|
||||
0,
|
||||
):
|
||||
yield
|
||||
454
tests/components/ness_alarm/test_config_flow.py
Normal file
454
tests/components/ness_alarm/test_config_flow.py
Normal file
@@ -0,0 +1,454 @@
|
||||
"""Test the Ness Alarm config flow."""
|
||||
|
||||
from types import MappingProxyType
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
from homeassistant.components.ness_alarm.const import (
|
||||
CONF_INFER_ARMING_STATE,
|
||||
CONF_SHOW_HOME_MODE,
|
||||
CONF_ZONE_ID,
|
||||
CONF_ZONE_NAME,
|
||||
CONF_ZONE_NUMBER,
|
||||
CONF_ZONE_TYPE,
|
||||
CONF_ZONES,
|
||||
DOMAIN,
|
||||
SUBENTRY_TYPE_ZONE,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER, ConfigSubentry
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_user_flow(
|
||||
hass: HomeAssistant, mock_client: AsyncMock, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Test successful user config flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
CONF_INFER_ARMING_STATE: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Ness Alarm 192.168.1.100:1992"
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
CONF_INFER_ARMING_STATE: False,
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
mock_client.close.assert_awaited_once()
|
||||
|
||||
|
||||
async def test_user_flow_with_infer_arming_state(
|
||||
hass: HomeAssistant, mock_client: AsyncMock, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Test user flow with infer_arming_state enabled."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
CONF_INFER_ARMING_STATE: True,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"][CONF_INFER_ARMING_STATE] is True
|
||||
|
||||
|
||||
async def test_user_flow_already_configured(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test we abort if already configured."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
CONF_INFER_ARMING_STATE: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect", "expected_error"),
|
||||
[
|
||||
(OSError("Connection refused"), "cannot_connect"),
|
||||
(TimeoutError, "cannot_connect"),
|
||||
(RuntimeError("Unexpected"), "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_user_flow_connection_error_recovery(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_setup_entry: AsyncMock,
|
||||
side_effect: Exception,
|
||||
expected_error: str,
|
||||
) -> None:
|
||||
"""Test connection error handling and recovery."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
# First attempt fails
|
||||
mock_client.update.side_effect = side_effect
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
CONF_INFER_ARMING_STATE: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": expected_error}
|
||||
mock_client.close.assert_awaited_once()
|
||||
|
||||
# Second attempt succeeds
|
||||
mock_client.update.side_effect = None
|
||||
mock_client.close.reset_mock()
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
CONF_INFER_ARMING_STATE: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_import_yaml_config(
|
||||
hass: HomeAssistant, mock_client: AsyncMock, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
"""Test importing YAML configuration."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_HOST: "192.168.1.72",
|
||||
CONF_PORT: 4999,
|
||||
CONF_INFER_ARMING_STATE: False,
|
||||
CONF_ZONES: [
|
||||
{CONF_ZONE_NAME: "Garage", CONF_ZONE_ID: 1},
|
||||
{
|
||||
CONF_ZONE_NAME: "Front Door",
|
||||
CONF_ZONE_ID: 5,
|
||||
CONF_ZONE_TYPE: BinarySensorDeviceClass.DOOR,
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Ness Alarm 192.168.1.72:4999"
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "192.168.1.72",
|
||||
CONF_PORT: 4999,
|
||||
CONF_INFER_ARMING_STATE: False,
|
||||
}
|
||||
|
||||
# Check that subentries were created for zones with names preserved
|
||||
assert len(result["subentries"]) == 2
|
||||
assert result["subentries"][0]["title"] == "Zone 1"
|
||||
assert result["subentries"][0]["unique_id"] == "zone_1"
|
||||
assert result["subentries"][0]["data"][CONF_TYPE] == BinarySensorDeviceClass.MOTION
|
||||
assert result["subentries"][0]["data"][CONF_ZONE_NAME] == "Garage"
|
||||
assert result["subentries"][1]["title"] == "Zone 5"
|
||||
assert result["subentries"][1]["unique_id"] == "zone_5"
|
||||
assert result["subentries"][1]["data"][CONF_TYPE] == BinarySensorDeviceClass.DOOR
|
||||
assert result["subentries"][1]["data"][CONF_ZONE_NAME] == "Front Door"
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
mock_client.close.assert_awaited_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect", "expected_reason"),
|
||||
[
|
||||
(OSError("Connection refused"), "cannot_connect"),
|
||||
(TimeoutError, "cannot_connect"),
|
||||
(RuntimeError("Unexpected"), "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_import_yaml_config_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_setup_entry: AsyncMock,
|
||||
side_effect: Exception,
|
||||
expected_reason: str,
|
||||
) -> None:
|
||||
"""Test importing YAML configuration."""
|
||||
mock_client.update.side_effect = side_effect
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_HOST: "192.168.1.72",
|
||||
CONF_PORT: 4999,
|
||||
CONF_INFER_ARMING_STATE: False,
|
||||
CONF_ZONES: [
|
||||
{CONF_ZONE_NAME: "Garage", CONF_ZONE_ID: 1},
|
||||
{
|
||||
CONF_ZONE_NAME: "Front Door",
|
||||
CONF_ZONE_ID: 5,
|
||||
CONF_ZONE_TYPE: BinarySensorDeviceClass.DOOR,
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == expected_reason
|
||||
|
||||
|
||||
async def test_import_already_configured(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test we abort import if already configured."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 4999,
|
||||
CONF_ZONES: [],
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect", "expected_reason"),
|
||||
[
|
||||
(OSError("Connection refused"), "cannot_connect"),
|
||||
(TimeoutError, "cannot_connect"),
|
||||
(RuntimeError("Unexpected"), "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_import_connection_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
side_effect: Exception,
|
||||
expected_reason: str,
|
||||
) -> None:
|
||||
"""Test import aborts on connection errors."""
|
||||
mock_client.update.side_effect = side_effect
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_HOST: "192.168.1.72",
|
||||
CONF_PORT: 4999,
|
||||
CONF_ZONES: [],
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == expected_reason
|
||||
mock_client.close.assert_awaited_once()
|
||||
|
||||
|
||||
async def test_zone_subentry_flow(hass: HomeAssistant) -> None:
|
||||
"""Test adding a zone through subentry flow."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.subentries.async_init(
|
||||
(entry.entry_id, SUBENTRY_TYPE_ZONE),
|
||||
context={"source": SOURCE_USER},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.subentries.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_ZONE_NUMBER: 1,
|
||||
CONF_TYPE: BinarySensorDeviceClass.DOOR,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Zone 1"
|
||||
assert result["data"][CONF_ZONE_NUMBER] == 1
|
||||
assert result["data"][CONF_TYPE] == BinarySensorDeviceClass.DOOR
|
||||
|
||||
|
||||
async def test_zone_subentry_already_configured(hass: HomeAssistant) -> None:
|
||||
"""Test adding a zone that already exists."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
entry.subentries = {
|
||||
"zone_1_id": ConfigSubentry(
|
||||
subentry_type=SUBENTRY_TYPE_ZONE,
|
||||
subentry_id="zone_1_id",
|
||||
unique_id="zone_1",
|
||||
title="Zone 1",
|
||||
data=MappingProxyType(
|
||||
{
|
||||
CONF_ZONE_NUMBER: 1,
|
||||
CONF_TYPE: BinarySensorDeviceClass.MOTION,
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
result = await hass.config_entries.subentries.async_init(
|
||||
(entry.entry_id, SUBENTRY_TYPE_ZONE),
|
||||
context={"source": SOURCE_USER},
|
||||
)
|
||||
|
||||
result = await hass.config_entries.subentries.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_ZONE_NUMBER: 1,
|
||||
CONF_TYPE: BinarySensorDeviceClass.DOOR,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {CONF_ZONE_NUMBER: "already_configured"}
|
||||
|
||||
|
||||
async def test_zone_subentry_reconfigure(hass: HomeAssistant) -> None:
|
||||
"""Test reconfiguring an existing zone."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
zone_subentry = ConfigSubentry(
|
||||
subentry_type=SUBENTRY_TYPE_ZONE,
|
||||
subentry_id="zone_1_id",
|
||||
unique_id="zone_1",
|
||||
title="Zone 1",
|
||||
data=MappingProxyType(
|
||||
{
|
||||
CONF_ZONE_NUMBER: 1,
|
||||
CONF_TYPE: BinarySensorDeviceClass.MOTION,
|
||||
}
|
||||
),
|
||||
)
|
||||
entry.subentries = {"zone_1_id": zone_subentry}
|
||||
|
||||
result = await entry.start_subentry_reconfigure_flow(hass, "zone_1_id")
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reconfigure"
|
||||
assert result["description_placeholders"][CONF_ZONE_NUMBER] == "1"
|
||||
|
||||
result = await hass.config_entries.subentries.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_TYPE: BinarySensorDeviceClass.DOOR,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
|
||||
|
||||
async def test_options_flow(hass: HomeAssistant) -> None:
|
||||
"""Test options flow to configure alarm panel settings."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_SHOW_HOME_MODE: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert entry.options[CONF_SHOW_HOME_MODE] is False
|
||||
|
||||
|
||||
async def test_options_flow_enable_home_mode(hass: HomeAssistant) -> None:
|
||||
"""Test options flow to enable home mode."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
options={CONF_SHOW_HOME_MODE: False},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_SHOW_HOME_MODE: True,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert entry.options[CONF_SHOW_HOME_MODE] is True
|
||||
@@ -1,26 +1,33 @@
|
||||
"""Tests for the ness_alarm component."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
from types import MappingProxyType
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from nessclient import ArmingMode, ArmingState
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import alarm_control_panel
|
||||
from homeassistant.components.alarm_control_panel import AlarmControlPanelState
|
||||
from homeassistant.components.ness_alarm import (
|
||||
ATTR_CODE,
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanelEntityFeature,
|
||||
AlarmControlPanelState,
|
||||
)
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
from homeassistant.components.ness_alarm.const import (
|
||||
ATTR_OUTPUT_ID,
|
||||
CONF_DEVICE_PORT,
|
||||
CONF_ZONE_ID,
|
||||
CONF_ZONE_NAME,
|
||||
CONF_ZONES,
|
||||
CONF_SHOW_HOME_MODE,
|
||||
CONF_ZONE_NUMBER,
|
||||
DOMAIN,
|
||||
SERVICE_AUX,
|
||||
SERVICE_PANIC,
|
||||
SUBENTRY_TYPE_ZONE,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState, ConfigSubentry
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_STATE,
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
CONF_TYPE,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
SERVICE_ALARM_ARM_HOME,
|
||||
SERVICE_ALARM_DISARM,
|
||||
@@ -28,70 +35,234 @@ from homeassistant.const import (
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
VALID_CONFIG = {
|
||||
DOMAIN: {
|
||||
CONF_HOST: "alarm.local",
|
||||
CONF_DEVICE_PORT: 1234,
|
||||
CONF_ZONES: [
|
||||
{CONF_ZONE_NAME: "Zone 1", CONF_ZONE_ID: 1},
|
||||
{CONF_ZONE_NAME: "Zone 2", CONF_ZONE_ID: 2},
|
||||
],
|
||||
}
|
||||
}
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_setup_platform(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test platform setup."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
assert hass.services.has_service(DOMAIN, "panic")
|
||||
assert hass.services.has_service(DOMAIN, "aux")
|
||||
async def test_config_entry_setup(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test config entry setup."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get("alarm_control_panel.alarm_panel") is not None
|
||||
assert hass.states.get("binary_sensor.zone_1") is not None
|
||||
assert hass.states.get("binary_sensor.zone_2") is not None
|
||||
|
||||
# Services should be registered
|
||||
assert hass.services.has_service(DOMAIN, SERVICE_PANIC)
|
||||
assert hass.services.has_service(DOMAIN, SERVICE_AUX)
|
||||
|
||||
# Alarm panel should be created
|
||||
assert hass.states.get("alarm_control_panel.alarm_panel")
|
||||
|
||||
# Client keepalive and update should be called after startup
|
||||
assert mock_nessclient.keepalive.call_count == 1
|
||||
assert mock_nessclient.update.call_count == 1
|
||||
# update is called once during setup (connection test) and once after startup
|
||||
assert mock_nessclient.update.call_count == 2
|
||||
|
||||
|
||||
async def test_panic_service(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test calling panic service."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
async def test_config_entry_unload(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test config entry unload."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Client should be closed
|
||||
mock_nessclient.close.assert_called_once()
|
||||
|
||||
|
||||
async def test_config_entry_not_ready(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test config entry raises ConfigEntryNotReady on connection failure."""
|
||||
mock_nessclient.update.side_effect = OSError("Connection refused")
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
mock_nessclient.close.assert_called_once()
|
||||
|
||||
|
||||
async def test_config_entry_with_zones(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test config entry setup with zones as subentries."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
# Add zone subentries
|
||||
entry.subentries = {
|
||||
"zone_1_id": ConfigSubentry(
|
||||
subentry_type=SUBENTRY_TYPE_ZONE,
|
||||
subentry_id="zone_1_id",
|
||||
unique_id="zone_1",
|
||||
title="Zone 1",
|
||||
data={
|
||||
CONF_ZONE_NUMBER: 1,
|
||||
CONF_TYPE: BinarySensorDeviceClass.MOTION,
|
||||
},
|
||||
),
|
||||
"zone_2_id": ConfigSubentry(
|
||||
subentry_type=SUBENTRY_TYPE_ZONE,
|
||||
subentry_id="zone_2_id",
|
||||
unique_id="zone_2",
|
||||
title="Zone 2",
|
||||
data={
|
||||
CONF_ZONE_NUMBER: 2,
|
||||
CONF_TYPE: BinarySensorDeviceClass.DOOR,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Binary sensors should be created for each zone
|
||||
assert hass.states.get("binary_sensor.zone_1")
|
||||
assert hass.states.get("binary_sensor.zone_2")
|
||||
|
||||
|
||||
async def test_config_entry_reload_on_subentry_add(
|
||||
hass: HomeAssistant, mock_nessclient
|
||||
) -> None:
|
||||
"""Test config entry with subentries."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
# Add a zone subentry
|
||||
entry.subentries = {
|
||||
"zone_1_id": ConfigSubentry(
|
||||
subentry_type=SUBENTRY_TYPE_ZONE,
|
||||
subentry_id="zone_1_id",
|
||||
unique_id="zone_1",
|
||||
title="Zone 1",
|
||||
data={
|
||||
CONF_ZONE_NUMBER: 1,
|
||||
CONF_TYPE: BinarySensorDeviceClass.MOTION,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Zone entity should be created
|
||||
assert hass.states.get("binary_sensor.zone_1")
|
||||
|
||||
|
||||
async def test_panic_service_with_config_entry(
|
||||
hass: HomeAssistant, mock_nessclient
|
||||
) -> None:
|
||||
"""Test calling panic service with config entry."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_PANIC, blocking=True, service_data={ATTR_CODE: "1234"}
|
||||
)
|
||||
mock_nessclient.panic.assert_awaited_once_with("1234")
|
||||
|
||||
|
||||
async def test_aux_service(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test calling aux service."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
async def test_aux_service_with_config_entry(
|
||||
hass: HomeAssistant, mock_nessclient
|
||||
) -> None:
|
||||
"""Test calling aux service with config entry."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_AUX, blocking=True, service_data={ATTR_OUTPUT_ID: 1}
|
||||
)
|
||||
mock_nessclient.aux.assert_awaited_once_with(1, True)
|
||||
|
||||
|
||||
async def test_dispatch_state_change(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test calling aux service."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
on_state_change = mock_nessclient.on_state_change.call_args[0][0]
|
||||
on_state_change(ArmingState.ARMING, None)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.is_state(
|
||||
"alarm_control_panel.alarm_panel", AlarmControlPanelState.ARMING
|
||||
async def test_aux_service_with_state_false(
|
||||
hass: HomeAssistant, mock_nessclient
|
||||
) -> None:
|
||||
"""Test calling aux service with state=False."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_AUX,
|
||||
blocking=True,
|
||||
service_data={ATTR_OUTPUT_ID: 2, ATTR_STATE: False},
|
||||
)
|
||||
mock_nessclient.aux.assert_awaited_once_with(2, False)
|
||||
|
||||
|
||||
async def test_alarm_disarm(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test disarm."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
async def test_alarm_panel_disarm(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test alarm panel disarm."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
@@ -106,9 +277,17 @@ async def test_alarm_disarm(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
mock_nessclient.disarm.assert_called_once_with("1234")
|
||||
|
||||
|
||||
async def test_alarm_arm_away(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test disarm."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
async def test_alarm_panel_arm_away(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test alarm panel arm away."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
@@ -123,9 +302,17 @@ async def test_alarm_arm_away(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
mock_nessclient.arm_away.assert_called_once_with("1234")
|
||||
|
||||
|
||||
async def test_alarm_arm_home(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test disarm."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
async def test_alarm_panel_arm_home(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test alarm panel arm home."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
@@ -140,9 +327,17 @@ async def test_alarm_arm_home(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
mock_nessclient.arm_home.assert_called_once_with("1234")
|
||||
|
||||
|
||||
async def test_alarm_trigger(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test disarm."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
async def test_alarm_panel_trigger(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test alarm panel trigger."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
@@ -157,21 +352,79 @@ async def test_alarm_trigger(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
mock_nessclient.panic.assert_called_once_with("1234")
|
||||
|
||||
|
||||
async def test_dispatch_zone_change(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test zone change events dispatch a signal to subscribers."""
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
async def test_zone_state_change(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test zone state change events."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
# Add zone subentries
|
||||
entry.subentries = {
|
||||
"zone_1_id": ConfigSubentry(
|
||||
subentry_type=SUBENTRY_TYPE_ZONE,
|
||||
subentry_id="zone_1_id",
|
||||
unique_id="zone_1",
|
||||
title="Zone 1",
|
||||
data={
|
||||
CONF_ZONE_NUMBER: 1,
|
||||
CONF_TYPE: BinarySensorDeviceClass.MOTION,
|
||||
},
|
||||
),
|
||||
"zone_2_id": ConfigSubentry(
|
||||
subentry_type=SUBENTRY_TYPE_ZONE,
|
||||
subentry_id="zone_2_id",
|
||||
unique_id="zone_2",
|
||||
title="Zone 2",
|
||||
data={
|
||||
CONF_ZONE_NUMBER: 2,
|
||||
CONF_TYPE: BinarySensorDeviceClass.DOOR,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Get the zone change callback
|
||||
on_zone_change = mock_nessclient.on_zone_change.call_args[0][0]
|
||||
on_zone_change(1, True)
|
||||
|
||||
# Trigger zone 1
|
||||
on_zone_change(1, True)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.is_state("binary_sensor.zone_1", "on")
|
||||
assert hass.states.is_state("binary_sensor.zone_2", "off")
|
||||
|
||||
# Trigger zone 2
|
||||
on_zone_change(2, True)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.is_state("binary_sensor.zone_2", "on")
|
||||
|
||||
# Clear zone 1
|
||||
on_zone_change(1, False)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.is_state("binary_sensor.zone_1", "off")
|
||||
|
||||
|
||||
async def test_arming_state_change(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test arming state change handing."""
|
||||
async def test_arming_state_changes(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test all arming state changes."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Get the state change callback
|
||||
on_state_change = mock_nessclient.on_state_change.call_args[0][0]
|
||||
|
||||
states = [
|
||||
(ArmingState.UNKNOWN, None, STATE_UNKNOWN),
|
||||
(ArmingState.DISARMED, None, AlarmControlPanelState.DISARMED),
|
||||
@@ -193,67 +446,185 @@ async def test_arming_state_change(hass: HomeAssistant, mock_nessclient) -> None
|
||||
ArmingMode.ARMED_NIGHT,
|
||||
AlarmControlPanelState.ARMED_NIGHT,
|
||||
),
|
||||
(
|
||||
ArmingState.ARMED,
|
||||
ArmingMode.ARMED_VACATION,
|
||||
AlarmControlPanelState.ARMED_VACATION,
|
||||
),
|
||||
(
|
||||
ArmingState.ARMED,
|
||||
ArmingMode.ARMED_DAY,
|
||||
AlarmControlPanelState.ARMED_AWAY,
|
||||
),
|
||||
(
|
||||
ArmingState.ARMED,
|
||||
ArmingMode.ARMED_HIGHEST,
|
||||
AlarmControlPanelState.ARMED_AWAY,
|
||||
),
|
||||
(ArmingState.ENTRY_DELAY, None, AlarmControlPanelState.PENDING),
|
||||
(ArmingState.TRIGGERED, None, AlarmControlPanelState.TRIGGERED),
|
||||
]
|
||||
|
||||
await async_setup_component(hass, DOMAIN, VALID_CONFIG)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.is_state("alarm_control_panel.alarm_panel", STATE_UNKNOWN)
|
||||
on_state_change = mock_nessclient.on_state_change.call_args[0][0]
|
||||
|
||||
for arming_state, arming_mode, expected_state in states:
|
||||
on_state_change(arming_state, arming_mode)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.is_state("alarm_control_panel.alarm_panel", expected_state)
|
||||
|
||||
|
||||
class MockClient:
|
||||
"""Mock nessclient.Client stub."""
|
||||
async def test_arming_state_unknown_mode(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test arming state with unknown arming mode (for coverage)."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
async def panic(self, code):
|
||||
"""Handle panic."""
|
||||
# Get the state change callback
|
||||
on_state_change = mock_nessclient.on_state_change.call_args[0][0]
|
||||
|
||||
async def disarm(self, code):
|
||||
"""Handle disarm."""
|
||||
|
||||
async def arm_away(self, code):
|
||||
"""Handle arm_away."""
|
||||
|
||||
async def arm_home(self, code):
|
||||
"""Handle arm_home."""
|
||||
|
||||
async def aux(self, output_id, state):
|
||||
"""Handle auxiliary control."""
|
||||
|
||||
async def keepalive(self):
|
||||
"""Handle keepalive."""
|
||||
|
||||
async def update(self):
|
||||
"""Handle update."""
|
||||
|
||||
def on_zone_change(self):
|
||||
"""Handle on_zone_change."""
|
||||
|
||||
def on_state_change(self):
|
||||
"""Handle on_state_change."""
|
||||
|
||||
async def close(self):
|
||||
"""Handle close."""
|
||||
# Test with unhandled arming state (for coverage of warning log)
|
||||
on_state_change(999, None) # Invalid state
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_nessclient():
|
||||
"""Mock the nessclient Client constructor.
|
||||
async def test_homeassistant_stop_event(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test client is closed on homeassistant_stop event."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
Replaces nessclient.Client with a Mock which always returns the same
|
||||
MagicMock() instance.
|
||||
"""
|
||||
_mock_instance = MagicMock(MockClient())
|
||||
_mock_factory = MagicMock()
|
||||
_mock_factory.return_value = _mock_instance
|
||||
# Fire the homeassistant_stop event
|
||||
hass.bus.async_fire("homeassistant_stop")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Client should be closed
|
||||
mock_nessclient.close.assert_called()
|
||||
|
||||
|
||||
async def test_entry_reload_on_update(hass: HomeAssistant, mock_nessclient) -> None:
|
||||
"""Test config entry reload when update listener is triggered."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Add a zone subentry which should trigger the update listener and reload
|
||||
zone_subentry = ConfigSubentry(
|
||||
subentry_type=SUBENTRY_TYPE_ZONE,
|
||||
subentry_id="zone_1_id",
|
||||
unique_id="zone_1",
|
||||
title="Zone 1",
|
||||
data=MappingProxyType(
|
||||
{
|
||||
CONF_ZONE_NUMBER: 1,
|
||||
CONF_TYPE: BinarySensorDeviceClass.MOTION,
|
||||
}
|
||||
),
|
||||
)
|
||||
hass.config_entries.async_add_subentry(entry, zone_subentry)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Entry should have the new zone subentry
|
||||
assert len(entry.subentries) == 1
|
||||
|
||||
|
||||
async def test_alarm_panel_home_mode_disabled(
|
||||
hass: HomeAssistant, mock_nessclient
|
||||
) -> None:
|
||||
"""Test alarm panel with home mode disabled via options."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
options={CONF_SHOW_HOME_MODE: False},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("alarm_control_panel.alarm_panel")
|
||||
assert state is not None
|
||||
|
||||
# ARM_HOME should not be in supported features
|
||||
supported = state.attributes["supported_features"]
|
||||
assert not supported & AlarmControlPanelEntityFeature.ARM_HOME
|
||||
assert supported & AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
assert supported & AlarmControlPanelEntityFeature.TRIGGER
|
||||
|
||||
|
||||
async def test_alarm_panel_home_mode_enabled_by_default(
|
||||
hass: HomeAssistant, mock_nessclient
|
||||
) -> None:
|
||||
"""Test alarm panel has home mode enabled by default."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("alarm_control_panel.alarm_panel")
|
||||
assert state is not None
|
||||
|
||||
# ARM_HOME should be in supported features by default
|
||||
supported = state.attributes["supported_features"]
|
||||
assert supported & AlarmControlPanelEntityFeature.ARM_HOME
|
||||
assert supported & AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
assert supported & AlarmControlPanelEntityFeature.TRIGGER
|
||||
|
||||
|
||||
async def test_yaml_import_triggers_flow(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, issue_registry: ir.IssueRegistry
|
||||
) -> None:
|
||||
"""Test that YAML configuration triggers import flow."""
|
||||
with patch(
|
||||
"homeassistant.components.ness_alarm.Client", new=_mock_factory, create=True
|
||||
"homeassistant.components.ness_alarm.config_flow.Client",
|
||||
return_value=AsyncMock(),
|
||||
):
|
||||
yield _mock_instance
|
||||
config = {
|
||||
DOMAIN: {
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_PORT: 1992,
|
||||
}
|
||||
}
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check that a config entry was created from the import
|
||||
entries = hass.config_entries.async_entries(DOMAIN)
|
||||
assert len(entries) == 1
|
||||
assert entries[0].data[CONF_HOST] == "192.168.1.100"
|
||||
assert entries[0].data[CONF_PORT] == 1992
|
||||
|
||||
# Check that a deprecation repair issue was created
|
||||
issue = issue_registry.async_get_issue(
|
||||
"homeassistant", f"deprecated_yaml_{DOMAIN}"
|
||||
)
|
||||
assert issue is not None
|
||||
assert issue.severity == "warning"
|
||||
|
||||
Reference in New Issue
Block a user