Add DHCP discovery flow to bosch_alarm (#142250)

* Add dhcp discovery

* Update homeassistant/components/bosch_alarm/config_flow.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* put mac address in entry instead of unique id

* Update host and mac via dhcp discovery

* add mac to connections

* Abort dhcp flow if there is already an ongoing flow

* apply changes from review

* apply change from review

* remove outdated test

* fix snapshots

* apply change from review

* clean definition for connections

* update quality scale

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Sanjay Govind 2025-05-16 20:21:41 +12:00 committed by GitHub
parent 270780ef5f
commit 9bbc49e842
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 1926 additions and 1703 deletions

View File

@ -7,10 +7,11 @@ from ssl import SSLError
from bosch_alarm_mode2 import Panel
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, Platform
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from .const import CONF_INSTALLER_CODE, CONF_USER_CODE, DOMAIN
@ -53,8 +54,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: BoschAlarmConfigEntry) -
device_registry = dr.async_get(hass)
mac = entry.data.get(CONF_MAC)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(CONNECTION_NETWORK_MAC, mac)} if mac else set(),
identifiers={(DOMAIN, entry.unique_id or entry.entry_id)},
name=f"Bosch {panel.model}",
manufacturer="Bosch Security Systems",

View File

@ -6,12 +6,13 @@ import asyncio
from collections.abc import Mapping
import logging
import ssl
from typing import Any
from typing import Any, Self
from bosch_alarm_mode2 import Panel
import voluptuous as vol
from homeassistant.config_entries import (
SOURCE_DHCP,
SOURCE_RECONFIGURE,
SOURCE_USER,
ConfigFlow,
@ -20,11 +21,14 @@ from homeassistant.config_entries import (
from homeassistant.const import (
CONF_CODE,
CONF_HOST,
CONF_MAC,
CONF_MODEL,
CONF_PASSWORD,
CONF_PORT,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import CONF_INSTALLER_CODE, CONF_USER_CODE, DOMAIN
@ -88,6 +92,12 @@ class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN):
"""Init config flow."""
self._data: dict[str, Any] = {}
self.mac: str | None = None
self.host: str | None = None
def is_matching(self, other_flow: Self) -> bool:
"""Return True if other_flow is matching this flow."""
return self.mac == other_flow.mac or self.host == other_flow.host
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@ -96,9 +106,12 @@ class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN):
errors: dict[str, str] = {}
if user_input is not None:
self.host = user_input[CONF_HOST]
if self.source == SOURCE_USER:
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
try:
# Use load_selector = 0 to fetch the panel model without authentication.
(model, serial) = await try_connect(user_input, 0)
(model, _) = await try_connect(user_input, 0)
except (
OSError,
ConnectionRefusedError,
@ -129,6 +142,55 @@ class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle DHCP discovery."""
self.mac = format_mac(discovery_info.macaddress)
self.host = discovery_info.ip
if self.hass.config_entries.flow.async_has_matching_flow(self):
return self.async_abort(reason="already_in_progress")
for entry in self.hass.config_entries.async_entries(DOMAIN):
if entry.data[CONF_MAC] == self.mac:
result = self.hass.config_entries.async_update_entry(
entry,
data={
**entry.data,
CONF_HOST: discovery_info.ip,
},
)
if result:
self.hass.config_entries.async_schedule_reload(entry.entry_id)
return self.async_abort(reason="already_configured")
try:
# Use load_selector = 0 to fetch the panel model without authentication.
(model, _) = await try_connect(
{CONF_HOST: discovery_info.ip, CONF_PORT: 7700}, 0
)
except (
OSError,
ConnectionRefusedError,
ssl.SSLError,
asyncio.exceptions.TimeoutError,
):
return self.async_abort(reason="cannot_connect")
except Exception:
_LOGGER.exception("Unexpected exception")
return self.async_abort(reason="unknown")
self.context["title_placeholders"] = {
"model": model,
"host": discovery_info.ip,
}
self._data = {
CONF_HOST: discovery_info.ip,
CONF_MAC: self.mac,
CONF_MODEL: model,
CONF_PORT: 7700,
}
return await self.async_step_auth()
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@ -172,7 +234,7 @@ class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN):
else:
if serial_number:
await self.async_set_unique_id(str(serial_number))
if self.source == SOURCE_USER:
if self.source in (SOURCE_USER, SOURCE_DHCP):
if serial_number:
self._abort_if_unique_id_configured()
else:
@ -184,6 +246,7 @@ class BoschAlarmConfigFlow(ConfigFlow, domain=DOMAIN):
)
if serial_number:
self._abort_if_unique_id_mismatch(reason="device_mismatch")
return self.async_update_reload_and_abort(
self._get_reconfigure_entry(),
data=self._data,

View File

@ -3,6 +3,11 @@
"name": "Bosch Alarm",
"codeowners": ["@mag1024", "@sanjay900"],
"config_flow": true,
"dhcp": [
{
"macaddress": "000463*"
}
],
"documentation": "https://www.home-assistant.io/integrations/bosch_alarm",
"integration_type": "device",
"iot_class": "local_push",

View File

@ -46,8 +46,8 @@ rules:
# Gold
devices: done
diagnostics: todo
discovery-update-info: todo
discovery: todo
discovery-update-info: done
discovery: done
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo

View File

@ -1,5 +1,6 @@
{
"config": {
"flow_title": "{model} ({host})",
"step": {
"user": {
"data": {
@ -42,6 +43,7 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",

View File

@ -108,6 +108,10 @@ DHCP: Final[list[dict[str, str | bool]]] = [
"hostname": "bond-*",
"macaddress": "F44E38*",
},
{
"domain": "bosch_alarm",
"macaddress": "000463*",
},
{
"domain": "broadlink",
"registered_devices": True,

View File

@ -13,7 +13,14 @@ from homeassistant.components.bosch_alarm.const import (
CONF_USER_CODE,
DOMAIN,
)
from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_PASSWORD, CONF_PORT
from homeassistant.const import (
CONF_HOST,
CONF_MAC,
CONF_MODEL,
CONF_PASSWORD,
CONF_PORT,
)
from homeassistant.helpers.device_registry import format_mac
from tests.common import MockConfigEntry
@ -38,6 +45,12 @@ def extra_config_entry_data(
return {CONF_MODEL: model_name} | config_flow_data
@pytest.fixture(params=[None])
def mac_address(request: pytest.FixtureRequest) -> str | None:
"""Return entity mac address."""
return request.param
@pytest.fixture
def config_flow_data(model: str) -> dict[str, Any]:
"""Return extra config entry data."""
@ -63,7 +76,7 @@ def model_name(model: str) -> str | None:
@pytest.fixture
def serial_number(model: str) -> str | None:
"""Return extra config entry data."""
if model == "solution_3000":
if model == "b5512":
return "1234567890"
return None
@ -183,7 +196,9 @@ def mock_panel(
@pytest.fixture
def mock_config_entry(
extra_config_entry_data: dict[str, Any], serial_number: str | None
extra_config_entry_data: dict[str, Any],
serial_number: str | None,
mac_address: str | None,
) -> MockConfigEntry:
"""Mock config entry for bosch alarm."""
return MockConfigEntry(
@ -194,6 +209,7 @@ def mock_config_entry(
CONF_HOST: "0.0.0.0",
CONF_PORT: 7700,
CONF_MODEL: "bosch_alarm_test_data.model",
CONF_MAC: mac_address and format_mac(mac_address),
}
| extra_config_entry_data,
)

View File

@ -1,5 +1,5 @@
# serializer version: 1
# name: test_alarm_control_panel[amax_3000][alarm_control_panel.area1-entry]
# name: test_alarm_control_panel[None-amax_3000][alarm_control_panel.area1-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -33,7 +33,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_alarm_control_panel[amax_3000][alarm_control_panel.area1-state]
# name: test_alarm_control_panel[None-amax_3000][alarm_control_panel.area1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'changed_by': None,
@ -50,58 +50,7 @@
'state': 'disarmed',
})
# ---
# name: test_alarm_control_panel[b5512][alarm_control_panel.area1-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'alarm_control_panel',
'entity_category': None,
'entity_id': 'alarm_control_panel.area1',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': <AlarmControlPanelEntityFeature: 3>,
'translation_key': None,
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1',
'unit_of_measurement': None,
})
# ---
# name: test_alarm_control_panel[b5512][alarm_control_panel.area1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'changed_by': None,
'code_arm_required': False,
'code_format': None,
'friendly_name': 'Area1',
'supported_features': <AlarmControlPanelEntityFeature: 3>,
}),
'context': <ANY>,
'entity_id': 'alarm_control_panel.area1',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'disarmed',
})
# ---
# name: test_alarm_control_panel[solution_3000][alarm_control_panel.area1-entry]
# name: test_alarm_control_panel[None-b5512][alarm_control_panel.area1-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -135,7 +84,58 @@
'unit_of_measurement': None,
})
# ---
# name: test_alarm_control_panel[solution_3000][alarm_control_panel.area1-state]
# name: test_alarm_control_panel[None-b5512][alarm_control_panel.area1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'changed_by': None,
'code_arm_required': False,
'code_format': None,
'friendly_name': 'Area1',
'supported_features': <AlarmControlPanelEntityFeature: 3>,
}),
'context': <ANY>,
'entity_id': 'alarm_control_panel.area1',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'disarmed',
})
# ---
# name: test_alarm_control_panel[None-solution_3000][alarm_control_panel.area1-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'alarm_control_panel',
'entity_category': None,
'entity_id': 'alarm_control_panel.area1',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': <AlarmControlPanelEntityFeature: 3>,
'translation_key': None,
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1',
'unit_of_measurement': None,
})
# ---
# name: test_alarm_control_panel[None-solution_3000][alarm_control_panel.area1-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'changed_by': None,

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
# serializer version: 1
# name: test_diagnostics[amax_3000]
# name: test_diagnostics[amax_3000-None]
dict({
'data': dict({
'areas': list([
@ -89,13 +89,14 @@
'entry_data': dict({
'host': '0.0.0.0',
'installer_code': '**REDACTED**',
'mac': None,
'model': 'AMAX 3000',
'password': '**REDACTED**',
'port': 7700,
}),
})
# ---
# name: test_diagnostics[b5512]
# name: test_diagnostics[b5512-None]
dict({
'data': dict({
'areas': list([
@ -180,105 +181,107 @@
}),
]),
'protocol_version': '1.0.0',
'serial_number': None,
}),
'entry_data': dict({
'host': '0.0.0.0',
'model': 'B5512 (US1B)',
'password': '**REDACTED**',
'port': 7700,
}),
})
# ---
# name: test_diagnostics[solution_3000]
dict({
'data': dict({
'areas': list([
dict({
'alarms': list([
]),
'all_armed': False,
'all_ready': True,
'armed': False,
'arming': False,
'disarmed': True,
'faults': 0,
'id': 1,
'name': 'Area1',
'part_armed': False,
'part_ready': True,
'pending': False,
'triggered': False,
}),
]),
'doors': list([
dict({
'id': 1,
'locked': True,
'name': 'Main Door',
'open': False,
}),
]),
'firmware_version': '1.0.0',
'history_events': list([
]),
'model': 'Solution 3000',
'outputs': list([
dict({
'active': False,
'id': 1,
'name': 'Output A',
}),
]),
'points': list([
dict({
'id': 0,
'name': 'Window',
'normal': True,
'open': False,
}),
dict({
'id': 1,
'name': 'Door',
'normal': True,
'open': False,
}),
dict({
'id': 2,
'name': 'Motion Detector',
'normal': True,
'open': False,
}),
dict({
'id': 3,
'name': 'CO Detector',
'normal': True,
'open': False,
}),
dict({
'id': 4,
'name': 'Smoke Detector',
'normal': True,
'open': False,
}),
dict({
'id': 5,
'name': 'Glassbreak Sensor',
'normal': True,
'open': False,
}),
dict({
'id': 6,
'name': 'Bedroom',
'normal': True,
'open': False,
}),
]),
'protocol_version': '1.0.0',
'serial_number': '1234567890',
}),
'entry_data': dict({
'host': '0.0.0.0',
'mac': None,
'model': 'B5512 (US1B)',
'password': '**REDACTED**',
'port': 7700,
}),
})
# ---
# name: test_diagnostics[solution_3000-None]
dict({
'data': dict({
'areas': list([
dict({
'alarms': list([
]),
'all_armed': False,
'all_ready': True,
'armed': False,
'arming': False,
'disarmed': True,
'faults': 0,
'id': 1,
'name': 'Area1',
'part_armed': False,
'part_ready': True,
'pending': False,
'triggered': False,
}),
]),
'doors': list([
dict({
'id': 1,
'locked': True,
'name': 'Main Door',
'open': False,
}),
]),
'firmware_version': '1.0.0',
'history_events': list([
]),
'model': 'Solution 3000',
'outputs': list([
dict({
'active': False,
'id': 1,
'name': 'Output A',
}),
]),
'points': list([
dict({
'id': 0,
'name': 'Window',
'normal': True,
'open': False,
}),
dict({
'id': 1,
'name': 'Door',
'normal': True,
'open': False,
}),
dict({
'id': 2,
'name': 'Motion Detector',
'normal': True,
'open': False,
}),
dict({
'id': 3,
'name': 'CO Detector',
'normal': True,
'open': False,
}),
dict({
'id': 4,
'name': 'Smoke Detector',
'normal': True,
'open': False,
}),
dict({
'id': 5,
'name': 'Glassbreak Sensor',
'normal': True,
'open': False,
}),
dict({
'id': 6,
'name': 'Bedroom',
'normal': True,
'open': False,
}),
]),
'protocol_version': '1.0.0',
'serial_number': None,
}),
'entry_data': dict({
'host': '0.0.0.0',
'mac': None,
'model': 'Solution 3000',
'port': 7700,
'user_code': '**REDACTED**',

View File

@ -1,5 +1,5 @@
# serializer version: 1
# name: test_sensor[amax_3000][sensor.area1_burglary_alarm_issues-entry]
# name: test_sensor[None-amax_3000][sensor.area1_burglary_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -33,7 +33,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_sensor[amax_3000][sensor.area1_burglary_alarm_issues-state]
# name: test_sensor[None-amax_3000][sensor.area1_burglary_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Burglary alarm issues',
@ -46,7 +46,7 @@
'state': 'no_issues',
})
# ---
# name: test_sensor[amax_3000][sensor.area1_faulting_points-entry]
# name: test_sensor[None-amax_3000][sensor.area1_faulting_points-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -80,7 +80,7 @@
'unit_of_measurement': 'points',
})
# ---
# name: test_sensor[amax_3000][sensor.area1_faulting_points-state]
# name: test_sensor[None-amax_3000][sensor.area1_faulting_points-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Faulting points',
@ -94,7 +94,7 @@
'state': '0',
})
# ---
# name: test_sensor[amax_3000][sensor.area1_fire_alarm_issues-entry]
# name: test_sensor[None-amax_3000][sensor.area1_fire_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -128,7 +128,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_sensor[amax_3000][sensor.area1_fire_alarm_issues-state]
# name: test_sensor[None-amax_3000][sensor.area1_fire_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Fire alarm issues',
@ -141,7 +141,7 @@
'state': 'no_issues',
})
# ---
# name: test_sensor[amax_3000][sensor.area1_gas_alarm_issues-entry]
# name: test_sensor[None-amax_3000][sensor.area1_gas_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -175,7 +175,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_sensor[amax_3000][sensor.area1_gas_alarm_issues-state]
# name: test_sensor[None-amax_3000][sensor.area1_gas_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Gas alarm issues',
@ -188,196 +188,7 @@
'state': 'no_issues',
})
# ---
# name: test_sensor[b5512][sensor.area1_burglary_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.area1_burglary_alarm_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Burglary alarm issues',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'alarms_burglary',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1_alarms_burglary',
'unit_of_measurement': None,
})
# ---
# name: test_sensor[b5512][sensor.area1_burglary_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Burglary alarm issues',
}),
'context': <ANY>,
'entity_id': 'sensor.area1_burglary_alarm_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'no_issues',
})
# ---
# name: test_sensor[b5512][sensor.area1_faulting_points-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.area1_faulting_points',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Faulting points',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'faulting_points',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1_faulting_points',
'unit_of_measurement': 'points',
})
# ---
# name: test_sensor[b5512][sensor.area1_faulting_points-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Faulting points',
'unit_of_measurement': 'points',
}),
'context': <ANY>,
'entity_id': 'sensor.area1_faulting_points',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensor[b5512][sensor.area1_fire_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.area1_fire_alarm_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Fire alarm issues',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'alarms_fire',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1_alarms_fire',
'unit_of_measurement': None,
})
# ---
# name: test_sensor[b5512][sensor.area1_fire_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Fire alarm issues',
}),
'context': <ANY>,
'entity_id': 'sensor.area1_fire_alarm_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'no_issues',
})
# ---
# name: test_sensor[b5512][sensor.area1_gas_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.area1_gas_alarm_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Gas alarm issues',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'alarms_gas',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1_alarms_gas',
'unit_of_measurement': None,
})
# ---
# name: test_sensor[b5512][sensor.area1_gas_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Gas alarm issues',
}),
'context': <ANY>,
'entity_id': 'sensor.area1_gas_alarm_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'no_issues',
})
# ---
# name: test_sensor[solution_3000][sensor.area1_burglary_alarm_issues-entry]
# name: test_sensor[None-b5512][sensor.area1_burglary_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -411,7 +222,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_sensor[solution_3000][sensor.area1_burglary_alarm_issues-state]
# name: test_sensor[None-b5512][sensor.area1_burglary_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Burglary alarm issues',
@ -424,7 +235,7 @@
'state': 'no_issues',
})
# ---
# name: test_sensor[solution_3000][sensor.area1_faulting_points-entry]
# name: test_sensor[None-b5512][sensor.area1_faulting_points-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -458,7 +269,7 @@
'unit_of_measurement': 'points',
})
# ---
# name: test_sensor[solution_3000][sensor.area1_faulting_points-state]
# name: test_sensor[None-b5512][sensor.area1_faulting_points-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Faulting points',
@ -472,7 +283,7 @@
'state': '0',
})
# ---
# name: test_sensor[solution_3000][sensor.area1_fire_alarm_issues-entry]
# name: test_sensor[None-b5512][sensor.area1_fire_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -506,7 +317,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_sensor[solution_3000][sensor.area1_fire_alarm_issues-state]
# name: test_sensor[None-b5512][sensor.area1_fire_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Fire alarm issues',
@ -519,7 +330,7 @@
'state': 'no_issues',
})
# ---
# name: test_sensor[solution_3000][sensor.area1_gas_alarm_issues-entry]
# name: test_sensor[None-b5512][sensor.area1_gas_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -553,7 +364,196 @@
'unit_of_measurement': None,
})
# ---
# name: test_sensor[solution_3000][sensor.area1_gas_alarm_issues-state]
# name: test_sensor[None-b5512][sensor.area1_gas_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Gas alarm issues',
}),
'context': <ANY>,
'entity_id': 'sensor.area1_gas_alarm_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'no_issues',
})
# ---
# name: test_sensor[None-solution_3000][sensor.area1_burglary_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.area1_burglary_alarm_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Burglary alarm issues',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'alarms_burglary',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1_alarms_burglary',
'unit_of_measurement': None,
})
# ---
# name: test_sensor[None-solution_3000][sensor.area1_burglary_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Burglary alarm issues',
}),
'context': <ANY>,
'entity_id': 'sensor.area1_burglary_alarm_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'no_issues',
})
# ---
# name: test_sensor[None-solution_3000][sensor.area1_faulting_points-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.area1_faulting_points',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Faulting points',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'faulting_points',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1_faulting_points',
'unit_of_measurement': 'points',
})
# ---
# name: test_sensor[None-solution_3000][sensor.area1_faulting_points-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Faulting points',
'unit_of_measurement': 'points',
}),
'context': <ANY>,
'entity_id': 'sensor.area1_faulting_points',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensor[None-solution_3000][sensor.area1_fire_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.area1_fire_alarm_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Fire alarm issues',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'alarms_fire',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1_alarms_fire',
'unit_of_measurement': None,
})
# ---
# name: test_sensor[None-solution_3000][sensor.area1_fire_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Fire alarm issues',
}),
'context': <ANY>,
'entity_id': 'sensor.area1_fire_alarm_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'no_issues',
})
# ---
# name: test_sensor[None-solution_3000][sensor.area1_gas_alarm_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.area1_gas_alarm_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Gas alarm issues',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'alarms_gas',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_area_1_alarms_gas',
'unit_of_measurement': None,
})
# ---
# name: test_sensor[None-solution_3000][sensor.area1_gas_alarm_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Area1 Gas alarm issues',

View File

@ -1,5 +1,5 @@
# serializer version: 1
# name: test_switch[amax_3000][switch.main_door_cycling-entry]
# name: test_switch[None-amax_3000][switch.main_door_cycling-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -33,7 +33,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_switch[amax_3000][switch.main_door_cycling-state]
# name: test_switch[None-amax_3000][switch.main_door_cycling-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Cycling',
@ -46,7 +46,7 @@
'state': 'off',
})
# ---
# name: test_switch[amax_3000][switch.main_door_locked-entry]
# name: test_switch[None-amax_3000][switch.main_door_locked-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -80,7 +80,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_switch[amax_3000][switch.main_door_locked-state]
# name: test_switch[None-amax_3000][switch.main_door_locked-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Locked',
@ -93,7 +93,7 @@
'state': 'on',
})
# ---
# name: test_switch[amax_3000][switch.main_door_secured-entry]
# name: test_switch[None-amax_3000][switch.main_door_secured-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -127,7 +127,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_switch[amax_3000][switch.main_door_secured-state]
# name: test_switch[None-amax_3000][switch.main_door_secured-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Secured',
@ -140,7 +140,7 @@
'state': 'off',
})
# ---
# name: test_switch[amax_3000][switch.output_a-entry]
# name: test_switch[None-amax_3000][switch.output_a-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -174,7 +174,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_switch[amax_3000][switch.output_a-state]
# name: test_switch[None-amax_3000][switch.output_a-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Output A',
@ -187,195 +187,7 @@
'state': 'off',
})
# ---
# name: test_switch[b5512][switch.main_door_cycling-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.main_door_cycling',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Cycling',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'cycling',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_door_1_cycling',
'unit_of_measurement': None,
})
# ---
# name: test_switch[b5512][switch.main_door_cycling-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Cycling',
}),
'context': <ANY>,
'entity_id': 'switch.main_door_cycling',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch[b5512][switch.main_door_locked-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.main_door_locked',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Locked',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'locked',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_door_1_locked',
'unit_of_measurement': None,
})
# ---
# name: test_switch[b5512][switch.main_door_locked-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Locked',
}),
'context': <ANY>,
'entity_id': 'switch.main_door_locked',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch[b5512][switch.main_door_secured-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.main_door_secured',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Secured',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'secured',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_door_1_secured',
'unit_of_measurement': None,
})
# ---
# name: test_switch[b5512][switch.main_door_secured-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Secured',
}),
'context': <ANY>,
'entity_id': 'switch.main_door_secured',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch[b5512][switch.output_a-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.output_a',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_output_1',
'unit_of_measurement': None,
})
# ---
# name: test_switch[b5512][switch.output_a-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Output A',
}),
'context': <ANY>,
'entity_id': 'switch.output_a',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch[solution_3000][switch.main_door_cycling-entry]
# name: test_switch[None-b5512][switch.main_door_cycling-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -409,7 +221,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_switch[solution_3000][switch.main_door_cycling-state]
# name: test_switch[None-b5512][switch.main_door_cycling-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Cycling',
@ -422,7 +234,7 @@
'state': 'off',
})
# ---
# name: test_switch[solution_3000][switch.main_door_locked-entry]
# name: test_switch[None-b5512][switch.main_door_locked-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -456,7 +268,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_switch[solution_3000][switch.main_door_locked-state]
# name: test_switch[None-b5512][switch.main_door_locked-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Locked',
@ -469,7 +281,7 @@
'state': 'on',
})
# ---
# name: test_switch[solution_3000][switch.main_door_secured-entry]
# name: test_switch[None-b5512][switch.main_door_secured-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -503,7 +315,7 @@
'unit_of_measurement': None,
})
# ---
# name: test_switch[solution_3000][switch.main_door_secured-state]
# name: test_switch[None-b5512][switch.main_door_secured-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Secured',
@ -516,7 +328,7 @@
'state': 'off',
})
# ---
# name: test_switch[solution_3000][switch.output_a-entry]
# name: test_switch[None-b5512][switch.output_a-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
@ -550,7 +362,195 @@
'unit_of_measurement': None,
})
# ---
# name: test_switch[solution_3000][switch.output_a-state]
# name: test_switch[None-b5512][switch.output_a-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Output A',
}),
'context': <ANY>,
'entity_id': 'switch.output_a',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch[None-solution_3000][switch.main_door_cycling-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.main_door_cycling',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Cycling',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'cycling',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_door_1_cycling',
'unit_of_measurement': None,
})
# ---
# name: test_switch[None-solution_3000][switch.main_door_cycling-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Cycling',
}),
'context': <ANY>,
'entity_id': 'switch.main_door_cycling',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch[None-solution_3000][switch.main_door_locked-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.main_door_locked',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Locked',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'locked',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_door_1_locked',
'unit_of_measurement': None,
})
# ---
# name: test_switch[None-solution_3000][switch.main_door_locked-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Locked',
}),
'context': <ANY>,
'entity_id': 'switch.main_door_locked',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch[None-solution_3000][switch.main_door_secured-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.main_door_secured',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Secured',
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'secured',
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_door_1_secured',
'unit_of_measurement': None,
})
# ---
# name: test_switch[None-solution_3000][switch.main_door_secured-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Main Door Secured',
}),
'context': <ANY>,
'entity_id': 'switch.main_door_secured',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch[None-solution_3000][switch.output_a-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.output_a',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'bosch_alarm',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '01JQ917ACKQ33HHM7YCFXYZX51_output_1',
'unit_of_measurement': None,
})
# ---
# name: test_switch[None-solution_3000][switch.output_a-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Output A',

View File

@ -6,12 +6,12 @@ from unittest.mock import AsyncMock
import pytest
from homeassistant import config_entries
from homeassistant.components.bosch_alarm.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_HOST, CONF_MODEL, CONF_PORT
from homeassistant.config_entries import SOURCE_DHCP, SOURCE_RECONFIGURE, SOURCE_USER
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_MODEL, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from . import setup_integration
@ -77,7 +77,7 @@ async def test_form_exceptions(
"""Test we handle exceptions correctly."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
@ -174,13 +174,6 @@ async def test_entry_already_configured_host(
result["flow_id"], {CONF_HOST: "0.0.0.0"}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "auth"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"], config_flow_data
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
@ -200,7 +193,7 @@ async def test_entry_already_configured_serial(
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_HOST: "0.0.0.0"}
result["flow_id"], {CONF_HOST: "1.1.1.1"}
)
assert result["type"] is FlowResultType.FORM
@ -214,6 +207,140 @@ async def test_entry_already_configured_serial(
assert result["reason"] == "already_configured"
async def test_dhcp_can_finish(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_panel: AsyncMock,
model_name: str,
serial_number: str,
config_flow_data: dict[str, Any],
) -> None:
"""Test DHCP discovery flow can finish right away."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_DHCP},
data=DhcpServiceInfo(
hostname="test",
ip="1.1.1.1",
macaddress="34ea34b43b5a",
),
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "auth"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
config_flow_data,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == f"Bosch {model_name}"
assert result["data"] == {
CONF_HOST: "1.1.1.1",
CONF_MAC: "34:ea:34:b4:3b:5a",
CONF_PORT: 7700,
CONF_MODEL: model_name,
**config_flow_data,
}
@pytest.mark.parametrize(
("exception", "message"),
[
(asyncio.exceptions.TimeoutError(), "cannot_connect"),
(Exception(), "unknown"),
],
)
async def test_dhcp_exceptions(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_panel: AsyncMock,
model_name: str,
serial_number: str,
config_flow_data: dict[str, Any],
exception: Exception,
message: str,
) -> None:
"""Test DHCP discovery flow that fails to connect."""
mock_panel.connect.side_effect = exception
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_DHCP},
data=DhcpServiceInfo(
hostname="test",
ip="1.1.1.1",
macaddress="34ea34b43b5a",
),
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == message
@pytest.mark.parametrize("mac_address", ["34ea34b43b5a"])
async def test_dhcp_updates_host(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
mock_panel: AsyncMock,
mac_address: str | None,
serial_number: str,
config_flow_data: dict[str, Any],
) -> None:
"""Test DHCP updates host."""
await setup_integration(hass, mock_config_entry)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_DHCP},
data=DhcpServiceInfo(
hostname="test",
ip="4.5.6.7",
macaddress=mac_address,
),
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert mock_config_entry.data[CONF_HOST] == "4.5.6.7"
@pytest.mark.parametrize("model", ["solution_3000", "amax_3000"])
async def test_dhcp_abort_ongoing_flow(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_panel: AsyncMock,
config_flow_data: dict[str, Any],
) -> None:
"""Test if a dhcp flow is aborted if there is already an ongoing flow."""
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: "0.0.0.0"}
)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_DHCP},
data=DhcpServiceInfo(
hostname="test",
ip="0.0.0.0",
macaddress="34ea34b43b5a",
),
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_in_progress"
async def test_reauth_flow_success(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
@ -274,7 +401,6 @@ async def test_reauth_flow_error(
)
assert result["step_id"] == "reauth_confirm"
assert result["errors"]["base"] == message
mock_panel.connect.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
@ -301,7 +427,7 @@ async def test_reconfig_flow(
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": config_entries.SOURCE_RECONFIGURE,
"source": SOURCE_RECONFIGURE,
"entry_id": mock_config_entry.entry_id,
},
)
@ -347,7 +473,7 @@ async def test_reconfig_flow_incorrect_model(
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": config_entries.SOURCE_RECONFIGURE,
"source": SOURCE_RECONFIGURE,
"entry_id": mock_config_entry.entry_id,
},
)