mirror of
https://github.com/home-assistant/core.git
synced 2026-03-03 08:10:36 +00:00
TotalConnect major test updates (#139672)
Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
@@ -172,9 +172,9 @@ class TotalConnectZoneBinarySensor(TotalConnectZoneEntity, BinarySensorEntity):
|
||||
super().__init__(coordinator, zone, location_id, entity_description.key)
|
||||
self.entity_description = entity_description
|
||||
self._attr_extra_state_attributes = {
|
||||
"zone_id": zone.zoneid,
|
||||
"zone_id": str(zone.zoneid),
|
||||
"location_id": location_id,
|
||||
"partition": zone.partition,
|
||||
"partition": str(zone.partition),
|
||||
}
|
||||
|
||||
@property
|
||||
|
||||
@@ -105,11 +105,7 @@ class TotalConnectConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
},
|
||||
)
|
||||
else:
|
||||
# Force the loading of locations using I/O
|
||||
number_locations = await self.hass.async_add_executor_job(
|
||||
self.client.get_number_locations,
|
||||
)
|
||||
if number_locations < 1:
|
||||
if self.client.get_number_locations() < 1:
|
||||
return self.async_abort(reason="no_locations")
|
||||
for location_id in self.client.locations:
|
||||
self.usercodes[location_id] = None
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/totalconnect",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["total_connect_client"],
|
||||
"requirements": ["total-connect-client==2025.1.4"]
|
||||
"requirements": ["total-connect-client==2025.5"]
|
||||
}
|
||||
|
||||
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@@ -2965,7 +2965,7 @@ tololib==1.2.2
|
||||
toonapi==0.3.0
|
||||
|
||||
# homeassistant.components.totalconnect
|
||||
total-connect-client==2025.1.4
|
||||
total-connect-client==2025.5
|
||||
|
||||
# homeassistant.components.tplink_lte
|
||||
tp-connected==0.0.4
|
||||
|
||||
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@@ -2442,7 +2442,7 @@ tololib==1.2.2
|
||||
toonapi==0.3.0
|
||||
|
||||
# homeassistant.components.totalconnect
|
||||
total-connect-client==2025.1.4
|
||||
total-connect-client==2025.5
|
||||
|
||||
# homeassistant.components.tplink_omada
|
||||
tplink-omada-client==1.4.4
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
"""Tests for the totalconnect component."""
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
|
||||
"""Fixture for setting up the component."""
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -1,473 +0,0 @@
|
||||
"""Common methods used across tests for TotalConnect."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from total_connect_client import ArmingState, ResultCode, ZoneStatus, ZoneType
|
||||
|
||||
from homeassistant.components.totalconnect.const import (
|
||||
AUTO_BYPASS,
|
||||
CODE_REQUIRED,
|
||||
CONF_USERCODES,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
LOCATION_ID = 123456
|
||||
|
||||
DEVICE_INFO_BASIC_1 = {
|
||||
"DeviceID": "987654",
|
||||
"DeviceName": "test",
|
||||
"DeviceClassID": 1,
|
||||
"DeviceSerialNumber": "987654321ABC",
|
||||
"DeviceFlags": "PromptForUserCode=0,PromptForInstallerCode=0,PromptForImportSecuritySettings=0,AllowUserSlotEditing=0,CalCapable=1,CanBeSentToPanel=0,CanArmNightStay=0,CanSupportMultiPartition=0,PartitionCount=0,MaxPartitionCount=0,OnBoardingSupport=0,PartitionAdded=0,DuplicateUserSyncStatus=0,PanelType=8,PanelVariant=1,BLEDisarmCapable=0,ArmHomeSupported=0,DuplicateUserCodeCheck=1,CanSupportRapid=0,IsKeypadSupported=1,WifiEnrollmentSupported=0,IsConnectedPanel=0,ArmNightInSceneSupported=0,BuiltInCameraSettingsSupported=0,ZWaveThermostatScheduleDisabled=0,MultipleAuthorityLevelSupported=0,VideoOnPanelSupported=0,EnableBLEMode=0,IsPanelWiFiResetSupported=0,IsCompetitorClearBypass=0,IsNotReadyStateSupported=0,isArmStatusWithoutExitDelayNotSupported=0",
|
||||
"SecurityPanelTypeID": None,
|
||||
"DeviceSerialText": None,
|
||||
}
|
||||
DEVICE_LIST = [DEVICE_INFO_BASIC_1]
|
||||
|
||||
LOCATION_INFO_BASIC_NORMAL = {
|
||||
"LocationID": LOCATION_ID,
|
||||
"LocationName": "test",
|
||||
"SecurityDeviceID": "987654",
|
||||
"PhotoURL": "http://www.example.com/some/path/to/file.jpg",
|
||||
"LocationModuleFlags": "Security=1,Video=0,Automation=0,GPS=0,VideoPIR=0",
|
||||
"DeviceList": {"DeviceInfoBasic": DEVICE_LIST},
|
||||
}
|
||||
|
||||
LOCATIONS = {"LocationInfoBasic": [LOCATION_INFO_BASIC_NORMAL]}
|
||||
|
||||
MODULE_FLAGS = "Some=0,Fake=1,Flags=2"
|
||||
|
||||
USER = {
|
||||
"UserID": "1234567",
|
||||
"Username": "username",
|
||||
"UserFeatureList": "Master=0,User Administration=0,Configuration Administration=0",
|
||||
}
|
||||
|
||||
RESPONSE_SESSION_DETAILS = {
|
||||
"ResultCode": ResultCode.SUCCESS.value,
|
||||
"ResultData": "Success",
|
||||
"SessionID": "12345",
|
||||
"Locations": LOCATIONS,
|
||||
"ModuleFlags": MODULE_FLAGS,
|
||||
"UserInfo": USER,
|
||||
}
|
||||
|
||||
PARTITION_DISARMED = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.DISARMED,
|
||||
}
|
||||
|
||||
PARTITION_DISARMED2 = {
|
||||
"PartitionID": "2",
|
||||
"ArmingState": ArmingState.DISARMED,
|
||||
}
|
||||
|
||||
PARTITION_ARMED_STAY = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.ARMED_STAY,
|
||||
}
|
||||
|
||||
PARTITION_ARMED_STAY2 = {
|
||||
"PartitionID": "2",
|
||||
"ArmingState": ArmingState.DISARMED,
|
||||
}
|
||||
|
||||
PARTITION_ARMED_AWAY = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.ARMED_AWAY,
|
||||
}
|
||||
|
||||
PARTITION_ARMED_CUSTOM = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.ARMED_CUSTOM_BYPASS,
|
||||
}
|
||||
|
||||
PARTITION_ARMED_NIGHT = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.ARMED_STAY_NIGHT,
|
||||
}
|
||||
|
||||
PARTITION_ARMING = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.ARMING,
|
||||
}
|
||||
PARTITION_DISARMING = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.DISARMING,
|
||||
}
|
||||
|
||||
PARTITION_TRIGGERED_POLICE = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.ALARMING,
|
||||
}
|
||||
|
||||
PARTITION_TRIGGERED_FIRE = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.ALARMING_FIRE_SMOKE,
|
||||
}
|
||||
|
||||
PARTITION_TRIGGERED_CARBON_MONOXIDE = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.ALARMING_CARBON_MONOXIDE,
|
||||
}
|
||||
|
||||
PARTITION_UNKNOWN = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": "99999",
|
||||
}
|
||||
|
||||
|
||||
PARTITION_INFO_DISARMED = [PARTITION_DISARMED, PARTITION_DISARMED2]
|
||||
PARTITION_INFO_ARMED_STAY = [PARTITION_ARMED_STAY, PARTITION_ARMED_STAY2]
|
||||
PARTITION_INFO_ARMED_AWAY = [PARTITION_ARMED_AWAY]
|
||||
PARTITION_INFO_ARMED_CUSTOM = [PARTITION_ARMED_CUSTOM]
|
||||
PARTITION_INFO_ARMED_NIGHT = [PARTITION_ARMED_NIGHT]
|
||||
PARTITION_INFO_ARMING = [PARTITION_ARMING]
|
||||
PARTITION_INFO_DISARMING = [PARTITION_DISARMING]
|
||||
PARTITION_INFO_TRIGGERED_POLICE = [PARTITION_TRIGGERED_POLICE]
|
||||
PARTITION_INFO_TRIGGERED_FIRE = [PARTITION_TRIGGERED_FIRE]
|
||||
PARTITION_INFO_TRIGGERED_CARBON_MONOXIDE = [PARTITION_TRIGGERED_CARBON_MONOXIDE]
|
||||
PARTITION_INFO_UNKNOWN = [PARTITION_UNKNOWN]
|
||||
|
||||
PARTITIONS_DISARMED = {"PartitionInfo": PARTITION_INFO_DISARMED}
|
||||
PARTITIONS_ARMED_STAY = {"PartitionInfo": PARTITION_INFO_ARMED_STAY}
|
||||
PARTITIONS_ARMED_AWAY = {"PartitionInfo": PARTITION_INFO_ARMED_AWAY}
|
||||
PARTITIONS_ARMED_CUSTOM = {"PartitionInfo": PARTITION_INFO_ARMED_CUSTOM}
|
||||
PARTITIONS_ARMED_NIGHT = {"PartitionInfo": PARTITION_INFO_ARMED_NIGHT}
|
||||
PARTITIONS_ARMING = {"PartitionInfo": PARTITION_INFO_ARMING}
|
||||
PARTITIONS_DISARMING = {"PartitionInfo": PARTITION_INFO_DISARMING}
|
||||
PARTITIONS_TRIGGERED_POLICE = {"PartitionInfo": PARTITION_INFO_TRIGGERED_POLICE}
|
||||
PARTITIONS_TRIGGERED_FIRE = {"PartitionInfo": PARTITION_INFO_TRIGGERED_FIRE}
|
||||
PARTITIONS_TRIGGERED_CARBON_MONOXIDE = {
|
||||
"PartitionInfo": PARTITION_INFO_TRIGGERED_CARBON_MONOXIDE
|
||||
}
|
||||
PARTITIONS_UNKNOWN = {"PartitionInfo": PARTITION_INFO_UNKNOWN}
|
||||
|
||||
ZONE_NORMAL = {
|
||||
"ZoneID": "1",
|
||||
"ZoneDescription": "Security",
|
||||
"ZoneStatus": ZoneStatus.FAULT,
|
||||
"ZoneTypeId": ZoneType.SECURITY,
|
||||
"PartitionId": "1",
|
||||
"CanBeBypassed": 1,
|
||||
}
|
||||
ZONE_2 = {
|
||||
"ZoneID": "2",
|
||||
"ZoneDescription": "Fire",
|
||||
"ZoneStatus": ZoneStatus.LOW_BATTERY,
|
||||
"ZoneTypeId": ZoneType.FIRE_SMOKE,
|
||||
"PartitionId": "1",
|
||||
"CanBeBypassed": 1,
|
||||
}
|
||||
ZONE_3 = {
|
||||
"ZoneID": "3",
|
||||
"ZoneDescription": "Gas",
|
||||
"ZoneStatus": ZoneStatus.TAMPER,
|
||||
"ZoneTypeId": ZoneType.CARBON_MONOXIDE,
|
||||
"PartitionId": "1",
|
||||
"CanBeBypassed": 1,
|
||||
}
|
||||
ZONE_4 = {
|
||||
"ZoneID": "4",
|
||||
"ZoneDescription": "Motion",
|
||||
"ZoneStatus": ZoneStatus.NORMAL,
|
||||
"ZoneTypeId": ZoneType.INTERIOR_FOLLOWER,
|
||||
"PartitionId": "1",
|
||||
"CanBeBypassed": 1,
|
||||
}
|
||||
ZONE_5 = {
|
||||
"ZoneID": "5",
|
||||
"ZoneDescription": "Medical",
|
||||
"ZoneStatus": ZoneStatus.NORMAL,
|
||||
"ZoneTypeId": ZoneType.PROA7_MEDICAL,
|
||||
"PartitionId": "1",
|
||||
"CanBeBypassed": 0,
|
||||
}
|
||||
# 99 is an unknown ZoneType
|
||||
ZONE_6 = {
|
||||
"ZoneID": "6",
|
||||
"ZoneDescription": "Unknown",
|
||||
"ZoneStatus": ZoneStatus.NORMAL,
|
||||
"ZoneTypeId": 99,
|
||||
"PartitionId": "1",
|
||||
"CanBeBypassed": 0,
|
||||
}
|
||||
|
||||
ZONE_7 = {
|
||||
"ZoneID": 7,
|
||||
"ZoneDescription": "Temperature",
|
||||
"ZoneStatus": ZoneStatus.NORMAL,
|
||||
"ZoneTypeId": ZoneType.MONITOR,
|
||||
"PartitionId": "1",
|
||||
"CanBeBypassed": 0,
|
||||
}
|
||||
|
||||
# ZoneType security that cannot be bypassed is a Button on the alarm panel
|
||||
ZONE_8 = {
|
||||
"ZoneID": 8,
|
||||
"ZoneDescription": "Button",
|
||||
"ZoneStatus": ZoneStatus.FAULT,
|
||||
"ZoneTypeId": ZoneType.SECURITY,
|
||||
"PartitionId": "1",
|
||||
"CanBeBypassed": 0,
|
||||
}
|
||||
|
||||
|
||||
ZONE_INFO = [ZONE_NORMAL, ZONE_2, ZONE_3, ZONE_4, ZONE_5, ZONE_6, ZONE_7]
|
||||
ZONES = {"ZoneInfo": ZONE_INFO}
|
||||
|
||||
METADATA_DISARMED = {
|
||||
"Partitions": PARTITIONS_DISARMED,
|
||||
"Zones": ZONES,
|
||||
"PromptForImportSecuritySettings": False,
|
||||
"IsInACLoss": False,
|
||||
"IsCoverTampered": False,
|
||||
"Bell1SupervisionFailure": False,
|
||||
"Bell2SupervisionFailure": False,
|
||||
"IsInLowBattery": False,
|
||||
}
|
||||
|
||||
METADATA_ARMED_STAY = METADATA_DISARMED.copy()
|
||||
METADATA_ARMED_STAY["Partitions"] = PARTITIONS_ARMED_STAY
|
||||
|
||||
METADATA_ARMED_AWAY = METADATA_DISARMED.copy()
|
||||
METADATA_ARMED_AWAY["Partitions"] = PARTITIONS_ARMED_AWAY
|
||||
|
||||
METADATA_ARMED_CUSTOM = METADATA_DISARMED.copy()
|
||||
METADATA_ARMED_CUSTOM["Partitions"] = PARTITIONS_ARMED_CUSTOM
|
||||
|
||||
METADATA_ARMED_NIGHT = METADATA_DISARMED.copy()
|
||||
METADATA_ARMED_NIGHT["Partitions"] = PARTITIONS_ARMED_NIGHT
|
||||
|
||||
METADATA_ARMING = METADATA_DISARMED.copy()
|
||||
METADATA_ARMING["Partitions"] = PARTITIONS_ARMING
|
||||
|
||||
METADATA_DISARMING = METADATA_DISARMED.copy()
|
||||
METADATA_DISARMING["Partitions"] = PARTITIONS_DISARMING
|
||||
|
||||
METADATA_TRIGGERED_POLICE = METADATA_DISARMED.copy()
|
||||
METADATA_TRIGGERED_POLICE["Partitions"] = PARTITIONS_TRIGGERED_POLICE
|
||||
|
||||
METADATA_TRIGGERED_FIRE = METADATA_DISARMED.copy()
|
||||
METADATA_TRIGGERED_FIRE["Partitions"] = PARTITIONS_TRIGGERED_FIRE
|
||||
|
||||
METADATA_TRIGGERED_CARBON_MONOXIDE = METADATA_DISARMED.copy()
|
||||
METADATA_TRIGGERED_CARBON_MONOXIDE["Partitions"] = PARTITIONS_TRIGGERED_CARBON_MONOXIDE
|
||||
|
||||
METADATA_UNKNOWN = METADATA_DISARMED.copy()
|
||||
METADATA_UNKNOWN["Partitions"] = PARTITIONS_UNKNOWN
|
||||
|
||||
RESPONSE_DISARMED = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_DISARMED,
|
||||
"ArmingState": ArmingState.DISARMED,
|
||||
}
|
||||
RESPONSE_ARMED_STAY = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_ARMED_STAY,
|
||||
"ArmingState": ArmingState.ARMED_STAY,
|
||||
}
|
||||
RESPONSE_ARMED_AWAY = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_ARMED_AWAY,
|
||||
"ArmingState": ArmingState.ARMED_AWAY,
|
||||
}
|
||||
RESPONSE_ARMED_CUSTOM = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_ARMED_CUSTOM,
|
||||
"ArmingState": ArmingState.ARMED_CUSTOM_BYPASS,
|
||||
}
|
||||
RESPONSE_ARMED_NIGHT = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_ARMED_NIGHT,
|
||||
"ArmingState": ArmingState.ARMED_STAY_NIGHT,
|
||||
}
|
||||
RESPONSE_ARMING = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_ARMING,
|
||||
"ArmingState": ArmingState.ARMING,
|
||||
}
|
||||
RESPONSE_DISARMING = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_DISARMING,
|
||||
"ArmingState": ArmingState.DISARMING,
|
||||
}
|
||||
RESPONSE_TRIGGERED_POLICE = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_TRIGGERED_POLICE,
|
||||
"ArmingState": ArmingState.ALARMING,
|
||||
}
|
||||
RESPONSE_TRIGGERED_FIRE = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_TRIGGERED_FIRE,
|
||||
"ArmingState": ArmingState.ALARMING_FIRE_SMOKE,
|
||||
}
|
||||
RESPONSE_TRIGGERED_CARBON_MONOXIDE = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_TRIGGERED_CARBON_MONOXIDE,
|
||||
"ArmingState": ArmingState.ALARMING_CARBON_MONOXIDE,
|
||||
}
|
||||
RESPONSE_UNKNOWN = {
|
||||
"ResultCode": 0,
|
||||
"PanelMetadataAndStatus": METADATA_UNKNOWN,
|
||||
"ArmingState": ArmingState.DISARMED,
|
||||
}
|
||||
|
||||
RESPONSE_ARM_SUCCESS = {"ResultCode": ResultCode.ARM_SUCCESS.value}
|
||||
RESPONSE_ARM_FAILURE = {"ResultCode": ResultCode.COMMAND_FAILED.value}
|
||||
RESPONSE_DISARM_SUCCESS = {"ResultCode": ResultCode.DISARM_SUCCESS.value}
|
||||
RESPONSE_DISARM_FAILURE = {
|
||||
"ResultCode": ResultCode.COMMAND_FAILED.value,
|
||||
"ResultData": "Command Failed",
|
||||
}
|
||||
RESPONSE_USER_CODE_INVALID = {
|
||||
"ResultCode": ResultCode.USER_CODE_INVALID.value,
|
||||
"ResultData": "testing user code invalid",
|
||||
}
|
||||
RESPONSE_SUCCESS = {"ResultCode": ResultCode.SUCCESS.value}
|
||||
RESPONSE_ZONE_BYPASS_SUCCESS = {
|
||||
"ResultCode": ResultCode.SUCCESS.value,
|
||||
"ResultData": "None",
|
||||
}
|
||||
RESPONSE_ZONE_BYPASS_FAILURE = {
|
||||
"ResultCode": ResultCode.FAILED_TO_BYPASS_ZONE.value,
|
||||
"ResultData": "None",
|
||||
}
|
||||
|
||||
USERNAME = "username@me.com"
|
||||
PASSWORD = "password"
|
||||
USERCODES = {LOCATION_ID: "7890"}
|
||||
CONFIG_DATA = {
|
||||
CONF_USERNAME: USERNAME,
|
||||
CONF_PASSWORD: PASSWORD,
|
||||
CONF_USERCODES: USERCODES,
|
||||
}
|
||||
CONFIG_DATA_NO_USERCODES = {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
||||
|
||||
OPTIONS_DATA = {AUTO_BYPASS: False, CODE_REQUIRED: False}
|
||||
OPTIONS_DATA_CODE_REQUIRED = {AUTO_BYPASS: False, CODE_REQUIRED: True}
|
||||
|
||||
PARTITION_DETAILS_1 = {
|
||||
"PartitionID": "1",
|
||||
"ArmingState": ArmingState.DISARMED.value,
|
||||
"PartitionName": "Test1",
|
||||
}
|
||||
|
||||
PARTITION_DETAILS_2 = {
|
||||
"PartitionID": "2",
|
||||
"ArmingState": ArmingState.DISARMED.value,
|
||||
"PartitionName": "Test2",
|
||||
}
|
||||
|
||||
PARTITION_DETAILS = {"PartitionDetails": [PARTITION_DETAILS_1, PARTITION_DETAILS_2]}
|
||||
RESPONSE_PARTITION_DETAILS = {
|
||||
"ResultCode": ResultCode.SUCCESS.value,
|
||||
"ResultData": "testing partition details",
|
||||
"PartitionsInfoList": PARTITION_DETAILS,
|
||||
}
|
||||
|
||||
ZONE_DETAILS_NORMAL = {
|
||||
"PartitionId": "1",
|
||||
"Batterylevel": "-1",
|
||||
"Signalstrength": "-1",
|
||||
"zoneAdditionalInfo": None,
|
||||
"ZoneID": "1",
|
||||
"ZoneStatus": ZoneStatus.NORMAL,
|
||||
"ZoneTypeId": ZoneType.SECURITY,
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": None,
|
||||
}
|
||||
|
||||
ZONE_STATUS_INFO = [ZONE_DETAILS_NORMAL]
|
||||
ZONE_DETAILS = {"ZoneStatusInfoWithPartitionId": ZONE_STATUS_INFO}
|
||||
ZONE_DETAIL_STATUS = {"Zones": ZONE_DETAILS}
|
||||
|
||||
RESPONSE_GET_ZONE_DETAILS_SUCCESS = {
|
||||
"ResultCode": 0,
|
||||
"ResultData": "Success",
|
||||
"ZoneStatus": ZONE_DETAIL_STATUS,
|
||||
}
|
||||
|
||||
TOTALCONNECT_REQUEST = (
|
||||
"homeassistant.components.totalconnect.TotalConnectClient.request"
|
||||
)
|
||||
TOTALCONNECT_GET_CONFIG = (
|
||||
"homeassistant.components.totalconnect.TotalConnectClient._get_configuration"
|
||||
)
|
||||
TOTALCONNECT_REQUEST_TOKEN = (
|
||||
"homeassistant.components.totalconnect.TotalConnectClient._request_token"
|
||||
)
|
||||
|
||||
|
||||
async def setup_platform(
|
||||
hass: HomeAssistant, platform: Any, code_required: bool = False
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the TotalConnect platform."""
|
||||
# first set up a config entry and add it to hass
|
||||
if code_required:
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=CONFIG_DATA, options=OPTIONS_DATA_CODE_REQUIRED
|
||||
)
|
||||
else:
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=CONFIG_DATA, options=OPTIONS_DATA
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
responses = [
|
||||
RESPONSE_SESSION_DETAILS,
|
||||
RESPONSE_PARTITION_DETAILS,
|
||||
RESPONSE_GET_ZONE_DETAILS_SUCCESS,
|
||||
RESPONSE_DISARMED,
|
||||
RESPONSE_DISARMED,
|
||||
]
|
||||
|
||||
with (
|
||||
patch("homeassistant.components.totalconnect.PLATFORMS", [platform]),
|
||||
patch(
|
||||
TOTALCONNECT_REQUEST,
|
||||
side_effect=responses,
|
||||
) as mock_request,
|
||||
patch(TOTALCONNECT_GET_CONFIG, side_effect=None),
|
||||
patch(TOTALCONNECT_REQUEST_TOKEN, side_effect=None),
|
||||
):
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
assert mock_request.call_count == 5
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return mock_entry
|
||||
|
||||
|
||||
async def init_integration(hass: HomeAssistant) -> MockConfigEntry:
|
||||
"""Set up the TotalConnect integration."""
|
||||
# first set up a config entry and add it to hass
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_DATA, options=OPTIONS_DATA)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
responses = [
|
||||
RESPONSE_SESSION_DETAILS,
|
||||
RESPONSE_PARTITION_DETAILS,
|
||||
RESPONSE_GET_ZONE_DETAILS_SUCCESS,
|
||||
RESPONSE_DISARMED,
|
||||
RESPONSE_DISARMED,
|
||||
]
|
||||
|
||||
with (
|
||||
patch(
|
||||
TOTALCONNECT_REQUEST,
|
||||
side_effect=responses,
|
||||
) as mock_request,
|
||||
patch(TOTALCONNECT_GET_CONFIG, side_effect=None),
|
||||
patch(TOTALCONNECT_REQUEST_TOKEN, side_effect=None),
|
||||
):
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
assert mock_request.call_count == 5
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return mock_entry
|
||||
249
tests/components/totalconnect/conftest.py
Normal file
249
tests/components/totalconnect/conftest.py
Normal file
@@ -0,0 +1,249 @@
|
||||
"""Configure py.test."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from total_connect_client import ArmingState, TotalConnectClient
|
||||
from total_connect_client.device import TotalConnectDevice
|
||||
from total_connect_client.location import TotalConnectLocation
|
||||
from total_connect_client.partition import TotalConnectPartition
|
||||
from total_connect_client.user import TotalConnectUser
|
||||
from total_connect_client.zone import TotalConnectZone, ZoneStatus, ZoneType
|
||||
|
||||
from homeassistant.components.totalconnect.const import (
|
||||
AUTO_BYPASS,
|
||||
CODE_REQUIRED,
|
||||
CONF_USERCODES,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
|
||||
from .const import CODE, LOCATION_ID, PASSWORD, USERCODES, USERNAME
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
load_json_array_fixture,
|
||||
load_json_object_fixture,
|
||||
)
|
||||
|
||||
|
||||
def create_mock_zone(
|
||||
identifier: int,
|
||||
partition: str,
|
||||
description: str,
|
||||
status: ZoneStatus,
|
||||
zone_type_id: int,
|
||||
can_be_bypassed: bool,
|
||||
battery_level: int,
|
||||
signal_strength: int,
|
||||
sensor_serial_number: str | None,
|
||||
loop_number: int | None,
|
||||
response_type: str | None,
|
||||
alarm_report_state: str | None,
|
||||
supervision_type: str | None,
|
||||
chime_state: str | None,
|
||||
device_type: str | None,
|
||||
) -> AsyncMock:
|
||||
"""Create a mock TotalConnectZone."""
|
||||
zone = AsyncMock(spec=TotalConnectZone, autospec=True)
|
||||
zone.zoneid = identifier
|
||||
zone.partition = partition
|
||||
zone.description = description
|
||||
zone.status = status
|
||||
zone.zone_type_id = zone_type_id
|
||||
zone.can_be_bypassed = can_be_bypassed
|
||||
zone.battery_level = battery_level
|
||||
zone.signal_strength = signal_strength
|
||||
zone.sensor_serial_number = sensor_serial_number
|
||||
zone.loop_number = loop_number
|
||||
zone.response_type = response_type
|
||||
zone.alarm_report_state = alarm_report_state
|
||||
zone.supervision_type = supervision_type
|
||||
zone.chime_state = chime_state
|
||||
zone.device_type = device_type
|
||||
zone.is_type_security.return_value = zone_type_id in (
|
||||
ZoneType.SECURITY,
|
||||
ZoneType.ENTRY_EXIT1,
|
||||
ZoneType.ENTRY_EXIT2,
|
||||
ZoneType.PERIMETER,
|
||||
ZoneType.INTERIOR_FOLLOWER,
|
||||
ZoneType.TROUBLE_ALARM,
|
||||
ZoneType.SILENT_24HR,
|
||||
ZoneType.AUDIBLE_24HR,
|
||||
ZoneType.INTERIOR_DELAY,
|
||||
ZoneType.LYRIC_LOCAL_ALARM,
|
||||
ZoneType.PROA7_GARAGE_MONITOR,
|
||||
)
|
||||
zone.is_type_button.return_value = (
|
||||
zone.is_type_security.return_value and not can_be_bypassed
|
||||
) or zone_type_id in (
|
||||
ZoneType.PROA7_MEDICAL,
|
||||
ZoneType.AUDIBLE_24HR,
|
||||
ZoneType.SILENT_24HR,
|
||||
ZoneType.RF_ARM_STAY,
|
||||
ZoneType.RF_ARM_AWAY,
|
||||
ZoneType.RF_DISARM,
|
||||
)
|
||||
return zone
|
||||
|
||||
|
||||
def create_mock_zone_from_dict(
|
||||
zone_data: dict[str, Any],
|
||||
) -> AsyncMock:
|
||||
"""Create a mock TotalConnectZone from a dictionary."""
|
||||
return create_mock_zone(
|
||||
zone_data["ZoneID"],
|
||||
zone_data["PartitionId"],
|
||||
zone_data["ZoneDescription"],
|
||||
ZoneStatus(zone_data["ZoneStatus"]),
|
||||
zone_data["ZoneTypeId"],
|
||||
zone_data["CanBeBypassed"],
|
||||
zone_data.get("Batterylevel"),
|
||||
zone_data.get("Signalstrength"),
|
||||
(zone_data["zoneAdditionalInfo"] or {}).get("SensorSerialNumber"),
|
||||
(zone_data["zoneAdditionalInfo"] or {}).get("LoopNumber"),
|
||||
(zone_data["zoneAdditionalInfo"] or {}).get("ResponseType"),
|
||||
(zone_data["zoneAdditionalInfo"] or {}).get("AlarmReportState"),
|
||||
(zone_data["zoneAdditionalInfo"] or {}).get("ZoneSupervisionType"),
|
||||
(zone_data["zoneAdditionalInfo"] or {}).get("ChimeState"),
|
||||
(zone_data["zoneAdditionalInfo"] or {}).get("DeviceType"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_partition() -> TotalConnectPartition:
|
||||
"""Create a mock TotalConnectPartition."""
|
||||
partition = AsyncMock(spec=TotalConnectPartition, autospec=True)
|
||||
partition.partitionid = 1
|
||||
partition.name = "Test1"
|
||||
partition.is_stay_armed = False
|
||||
partition.is_fire_armed = False
|
||||
partition.is_fire_enabled = False
|
||||
partition.is_common_armed = False
|
||||
partition.is_common_enabled = False
|
||||
partition.is_locked = False
|
||||
partition.is_new_partition = False
|
||||
partition.is_night_stay_enabled = 0
|
||||
partition.exit_delay_timer = 0
|
||||
partition.arming_state = ArmingState.DISARMED
|
||||
return partition
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_partition_2() -> TotalConnectPartition:
|
||||
"""Create a mock TotalConnectPartition."""
|
||||
partition = AsyncMock(spec=TotalConnectPartition, autospec=True)
|
||||
partition.partitionid = 2
|
||||
partition.name = "Test2"
|
||||
partition.is_stay_armed = False
|
||||
partition.is_fire_armed = False
|
||||
partition.is_fire_enabled = False
|
||||
partition.is_common_armed = False
|
||||
partition.is_common_enabled = False
|
||||
partition.is_locked = False
|
||||
partition.is_new_partition = False
|
||||
partition.is_night_stay_enabled = 0
|
||||
partition.exit_delay_timer = 0
|
||||
partition.arming_state = ArmingState.DISARMED
|
||||
return partition
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_location(
|
||||
mock_partition: AsyncMock, mock_partition_2: AsyncMock
|
||||
) -> TotalConnectLocation:
|
||||
"""Create a mock TotalConnectLocation."""
|
||||
location = AsyncMock(spec=TotalConnectLocation, autospec=True)
|
||||
location.location_id = LOCATION_ID
|
||||
location.location_name = "Test Location"
|
||||
location.security_device_id = 7654321
|
||||
location.set_usercode.return_value = True
|
||||
location.partitions = {1: mock_partition, 2: mock_partition_2}
|
||||
location.devices = {
|
||||
7654321: TotalConnectDevice(load_json_object_fixture("device_1.json", DOMAIN))
|
||||
}
|
||||
location.zones = {
|
||||
z["ZoneID"]: create_mock_zone_from_dict(z)
|
||||
for z in load_json_array_fixture("zones.json", DOMAIN)
|
||||
}
|
||||
location.is_low_battery.return_value = False
|
||||
location.is_cover_tampered.return_value = False
|
||||
location.is_ac_loss.return_value = False
|
||||
location.arming_state = ArmingState.DISARMED
|
||||
location._module_flags = {
|
||||
"can_bypass_zones": True,
|
||||
"can_clear_bypass": True,
|
||||
"can_set_usercodes": True,
|
||||
}
|
||||
location.ac_loss = False
|
||||
location.low_battery = False
|
||||
location.auto_bypass_low_battery = False
|
||||
location.cover_tampered = False
|
||||
return location
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_client(mock_location: TotalConnectLocation) -> Generator[TotalConnectClient]:
|
||||
"""Mock a TotalConnectClient for testing."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient",
|
||||
autospec=True,
|
||||
) as mock_client,
|
||||
patch(
|
||||
"homeassistant.components.totalconnect.TotalConnectClient", new=mock_client
|
||||
),
|
||||
):
|
||||
client = mock_client.return_value
|
||||
client.get_number_locations.return_value = 1
|
||||
client.locations = {mock_location.location_id: mock_location}
|
||||
client.usercodes = {mock_location.location_id: CODE}
|
||||
client.auto_bypass_low_battery = False
|
||||
client._module_flags = {}
|
||||
client.retry_delay = 0
|
||||
client._invalid_credentials = False
|
||||
user_mock = AsyncMock(spec=TotalConnectUser, autospec=True)
|
||||
user_mock._master_user = True
|
||||
user_mock._user_admin = True
|
||||
user_mock._config_admin = True
|
||||
user_mock.security_problem.return_value = False
|
||||
user_mock._features = {
|
||||
"can_set_usercodes": True,
|
||||
"can_bypass_zones": True,
|
||||
"can_clear_bypass": True,
|
||||
}
|
||||
setattr(client, "_user", user_mock)
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def code_required() -> bool:
|
||||
"""Return whether a code is required."""
|
||||
return False
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry(code_required: bool) -> MockConfigEntry:
|
||||
"""Create a mock config entry for testing."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_USERNAME: USERNAME,
|
||||
CONF_PASSWORD: PASSWORD,
|
||||
CONF_USERCODES: USERCODES,
|
||||
},
|
||||
options={AUTO_BYPASS: False, CODE_REQUIRED: code_required},
|
||||
unique_id=USERNAME,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Mock the setup entry for TotalConnect."""
|
||||
with patch(
|
||||
"homeassistant.components.totalconnect.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup:
|
||||
yield mock_setup
|
||||
8
tests/components/totalconnect/const.py
Normal file
8
tests/components/totalconnect/const.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""Constants for the Total Connect tests."""
|
||||
|
||||
LOCATION_ID = 1234567
|
||||
CODE = "7890"
|
||||
|
||||
USERNAME = "username@me.com"
|
||||
PASSWORD = "password"
|
||||
USERCODES = {LOCATION_ID: "7890"}
|
||||
12
tests/components/totalconnect/fixtures/device_1.json
Normal file
12
tests/components/totalconnect/fixtures/device_1.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"DeviceID": 7654321,
|
||||
"DeviceName": "test",
|
||||
"DeviceClassID": 1,
|
||||
"DeviceSerialNumber": "1234567890AB",
|
||||
"DeviceFlags": "PromptForUserCode=0,PromptForInstallerCode=0,PromptForImportSecuritySettings=0,AllowUserSlotEditing=0,CalCapable=1,CanBeSentToPanel=1,CanArmNightStay=0,CanSupportMultiPartition=0,PartitionCount=0,MaxPartitionCount=4,OnBoardingSupport=0,PartitionAdded=0,DuplicateUserSyncStatus=0,PanelType=12,PanelVariant=1,BLEDisarmCapable=0,ArmHomeSupported=1,DuplicateUserCodeCheck=1,CanSupportRapid=0,IsKeypadSupported=0,WifiEnrollmentSupported=1,IsConnectedPanel=1,ArmNightInSceneSupported=1,BuiltInCameraSettingsSupported=0,ZWaveThermostatScheduleDisabled=0,MultipleAuthorityLevelSupported=1,VideoOnPanelSupported=1,EnableBLEMode=0,IsPanelWiFiResetSupported=0,IsCompetitorClearBypass=0,IsNotReadyStateSupported=0,isArmStatusWithoutExitDelayNotSupported=0,UserCodeLength=4,UserCodeLengthChanged=0,DoubleDisarmRequired=0,TMSCloudSupported=0,IsAVCEnabled=0",
|
||||
"SecurityPanelTypeID": 12,
|
||||
"DeviceSerialText": null,
|
||||
"DeviceType": null,
|
||||
"DeviceVariants": null,
|
||||
"RestrictedPanel": 0
|
||||
}
|
||||
658
tests/components/totalconnect/fixtures/zones.json
Normal file
658
tests/components/totalconnect/fixtures/zones.json
Normal file
@@ -0,0 +1,658 @@
|
||||
[
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "020000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "1",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 0
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 2,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Security",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-12-11T09:00:13",
|
||||
"ZoneTypeId": 1
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "030000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "4",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 2
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 3,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Fire",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-06-02T15:41:05",
|
||||
"ZoneTypeId": 9
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "040000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "4",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 2
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 4,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Gas",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-12-11T09:00:13",
|
||||
"ZoneTypeId": 14
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "050000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "4",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 2
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 5,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Unknown",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-06-02T15:40:59",
|
||||
"ZoneTypeId": 99
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "060000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "1",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 1,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 0
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 6,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Temperature",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 12
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": 5,
|
||||
"Signalstrength": 2,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "070000000000000A",
|
||||
"LoopNumber": 2,
|
||||
"ResponseType": "53",
|
||||
"AlarmReportState": 0,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 15
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 7,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Doorbell Other",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 53
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "080000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "3",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 0
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 8,
|
||||
"ZoneStatus": 1,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Office Side Door",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 3
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "090000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "3",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 0
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 9,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Office Back Door",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 3
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "100000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "1",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 0
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 10,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Master Bedroom Door",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-06-02T15:40:57",
|
||||
"ZoneTypeId": 1
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "120000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "3",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 0
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 12,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Dining Room Two Door",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 3
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "130000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "3",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 0
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 13,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Patio Door",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 3
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "140000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "3",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 1
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 14,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Living Room Window",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 3
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "150000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "3",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 1
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 15,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Living Room Two Window",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 3
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "160000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "9",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 4
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 16,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Apartment SmokeDetector",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-04-28T09:42:29",
|
||||
"ZoneTypeId": 9
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "170000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "9",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 4
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 17,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Upstairs Hallway SmokeDetector",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-04-28T09:53:57",
|
||||
"ZoneTypeId": 9
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "180000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "9",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 4
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 18,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Downstairs Hallway SmokeDetector",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-04-28T09:47:10",
|
||||
"ZoneTypeId": 9
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "190000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "9",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 4
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 19,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Kid Bedroom SmokeDetector",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-04-28T09:49:07",
|
||||
"ZoneTypeId": 9
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "200000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "9",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 4
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 20,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Guest Bedroom SmokeDetector",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-04-28T09:50:20",
|
||||
"ZoneTypeId": 9
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "210000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "14",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 6
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 21,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Apartment CarbonMonoxideDetecto",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-04-28T09:41:18",
|
||||
"ZoneTypeId": 14
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "220000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "14",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 6
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 22,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Downstairs Hallway CarbonMonoxid",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-04-28T09:45:39",
|
||||
"ZoneTypeId": 14
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "230000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "14",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 6
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 23,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Upstairs Hallway CarbonMonoxideD",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-04-28T09:52:37",
|
||||
"ZoneTypeId": 14
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "240000",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "9",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 4
|
||||
},
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 24,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Master Bedroom SmokeDetector",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 9
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": 5,
|
||||
"Signalstrength": 3,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "250000000000000A",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "23",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 0,
|
||||
"DeviceType": 15
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 25,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Garage Side Other",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": "2024-12-15T15:14:39",
|
||||
"ZoneTypeId": 23
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": 5,
|
||||
"Signalstrength": 5,
|
||||
"zoneAdditionalInfo": {
|
||||
"SensorSerialNumber": "260000000000000A",
|
||||
"LoopNumber": 1,
|
||||
"ResponseType": "1",
|
||||
"AlarmReportState": 1,
|
||||
"ZoneSupervisionType": 0,
|
||||
"ChimeState": 1,
|
||||
"DeviceType": 0
|
||||
},
|
||||
"CanBeBypassed": 1,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 26,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 1,
|
||||
"ZoneDescription": "Front Door Door",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 1
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": null,
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 800,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 0,
|
||||
"ZoneDescription": "Master Bedroom Keypad",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 50
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": null,
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 1995,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 0,
|
||||
"ZoneDescription": "Zone 995 Fire",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 9
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": null,
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 1996,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 0,
|
||||
"ZoneDescription": "Zone 996 Medical",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 15
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": null,
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 1998,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 0,
|
||||
"ZoneDescription": "Zone 998 Other",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 6
|
||||
},
|
||||
{
|
||||
"PartitionId": 1,
|
||||
"Batterylevel": -1,
|
||||
"Signalstrength": -1,
|
||||
"zoneAdditionalInfo": null,
|
||||
"CanBeBypassed": 0,
|
||||
"ZoneFlags": null,
|
||||
"ZoneID": 1999,
|
||||
"ZoneStatus": 0,
|
||||
"IsBypassableZone": 0,
|
||||
"IsSensingZone": 0,
|
||||
"ZoneDescription": "Zone 999 Police",
|
||||
"AlarmTriggerTime": null,
|
||||
"AlarmTriggerTimeLocalized": null,
|
||||
"ZoneTypeId": 7
|
||||
}
|
||||
]
|
||||
@@ -1,5 +1,5 @@
|
||||
# serializer version: 1
|
||||
# name: test_attributes[alarm_control_panel.test-entry]
|
||||
# name: test_entities[alarm_control_panel.test-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -30,11 +30,11 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <AlarmControlPanelEntityFeature: 7>,
|
||||
'translation_key': None,
|
||||
'unique_id': '123456',
|
||||
'unique_id': '1234567',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_attributes[alarm_control_panel.test-state]
|
||||
# name: test_entities[alarm_control_panel.test-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'changed_by': None,
|
||||
@@ -51,7 +51,7 @@
|
||||
'state': 'disarmed',
|
||||
})
|
||||
# ---
|
||||
# name: test_attributes[alarm_control_panel.test_partition_2-entry]
|
||||
# name: test_entities[alarm_control_panel.test_partition_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -82,11 +82,11 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': <AlarmControlPanelEntityFeature: 7>,
|
||||
'translation_key': 'partition',
|
||||
'unique_id': '123456_2',
|
||||
'unique_id': '1234567_2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_attributes[alarm_control_panel.test_partition_2-state]
|
||||
# name: test_entities[alarm_control_panel.test_partition_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'changed_by': None,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,100 @@
|
||||
# serializer version: 1
|
||||
# name: test_entity_registry[button.dining_room_two_door_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.dining_room_two_door_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_12_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.dining_room_two_door_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Dining Room Two Door Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.dining_room_two_door_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.doorbell_other_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.doorbell_other_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_7_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.doorbell_other_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Doorbell Other Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.doorbell_other_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.fire_bypass-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -30,7 +126,7 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '123456_2_bypass',
|
||||
'unique_id': '1234567_3_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
@@ -47,6 +143,102 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.front_door_door_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.front_door_door_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_26_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.front_door_door_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Front Door Door Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.front_door_door_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.garage_side_other_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.garage_side_other_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_25_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.garage_side_other_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Garage Side Other Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.garage_side_other_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.gas_bypass-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -78,7 +270,7 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '123456_3_bypass',
|
||||
'unique_id': '1234567_4_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
@@ -95,7 +287,7 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.motion_bypass-entry]
|
||||
# name: test_entity_registry[button.living_room_two_window_bypass-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@@ -108,7 +300,7 @@
|
||||
'disabled_by': None,
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.motion_bypass',
|
||||
'entity_id': 'button.living_room_two_window_bypass',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
@@ -126,17 +318,257 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '123456_4_bypass',
|
||||
'unique_id': '1234567_15_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.motion_bypass-state]
|
||||
# name: test_entity_registry[button.living_room_two_window_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Motion Bypass',
|
||||
'friendly_name': 'Living Room Two Window Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.motion_bypass',
|
||||
'entity_id': 'button.living_room_two_window_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.living_room_window_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.living_room_window_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_14_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.living_room_window_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Living Room Window Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.living_room_window_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.master_bedroom_door_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.master_bedroom_door_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_10_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.master_bedroom_door_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Master Bedroom Door Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.master_bedroom_door_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.office_back_door_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.office_back_door_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_9_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.office_back_door_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Office Back Door Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.office_back_door_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.office_side_door_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.office_side_door_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_8_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.office_side_door_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Office Side Door Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.office_side_door_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.patio_door_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.patio_door_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_13_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.patio_door_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Patio Door Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.patio_door_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
@@ -174,7 +606,7 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '123456_1_bypass',
|
||||
'unique_id': '1234567_2_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
@@ -191,6 +623,54 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.temperature_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.temperature_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_6_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.temperature_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Temperature Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.temperature_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.test_bypass_all-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -222,7 +702,7 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass_all',
|
||||
'unique_id': '123456_bypass_all',
|
||||
'unique_id': '1234567_bypass_all',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
@@ -270,7 +750,7 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'clear_bypass',
|
||||
'unique_id': '123456_clear_bypass',
|
||||
'unique_id': '1234567_clear_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
@@ -287,3 +767,51 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.unknown_bypass-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': 'button',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'button.unknown_bypass',
|
||||
'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': 'Bypass',
|
||||
'platform': 'totalconnect',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'bypass',
|
||||
'unique_id': '1234567_5_bypass',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entity_registry[button.unknown_bypass-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Unknown Bypass',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'button.unknown_bypass',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
|
||||
619
tests/components/totalconnect/snapshots/test_diagnostics.ambr
Normal file
619
tests/components/totalconnect/snapshots/test_diagnostics.ambr
Normal file
@@ -0,0 +1,619 @@
|
||||
# serializer version: 1
|
||||
# name: test_entry_diagnostics
|
||||
dict({
|
||||
'client': dict({
|
||||
'auto_bypass_low_battery': False,
|
||||
'invalid_credentials': False,
|
||||
'module_flags': dict({
|
||||
}),
|
||||
'retry_delay': 0,
|
||||
}),
|
||||
'locations': list([
|
||||
dict({
|
||||
'ac_loss': False,
|
||||
'arming_state': dict({
|
||||
'__type': "<enum 'ArmingState'>",
|
||||
'repr': '<ArmingState.DISARMED: 10200>',
|
||||
}),
|
||||
'auto_bypass_low_battery': False,
|
||||
'cover_tampered': False,
|
||||
'devices': list([
|
||||
dict({
|
||||
'class_id': 1,
|
||||
'device_id': 7654321,
|
||||
'flags': dict({
|
||||
'AllowUserSlotEditing': '0',
|
||||
'ArmHomeSupported': '1',
|
||||
'ArmNightInSceneSupported': '1',
|
||||
'BLEDisarmCapable': '0',
|
||||
'BuiltInCameraSettingsSupported': '0',
|
||||
'CalCapable': '1',
|
||||
'CanArmNightStay': '0',
|
||||
'CanBeSentToPanel': '1',
|
||||
'CanSupportMultiPartition': '0',
|
||||
'CanSupportRapid': '0',
|
||||
'DoubleDisarmRequired': '0',
|
||||
'DuplicateUserCodeCheck': '1',
|
||||
'DuplicateUserSyncStatus': '0',
|
||||
'EnableBLEMode': '0',
|
||||
'IsAVCEnabled': '0',
|
||||
'IsCompetitorClearBypass': '0',
|
||||
'IsConnectedPanel': '1',
|
||||
'IsKeypadSupported': '0',
|
||||
'IsNotReadyStateSupported': '0',
|
||||
'IsPanelWiFiResetSupported': '0',
|
||||
'MaxPartitionCount': '4',
|
||||
'MultipleAuthorityLevelSupported': '1',
|
||||
'OnBoardingSupport': '0',
|
||||
'PanelType': '12',
|
||||
'PanelVariant': '1',
|
||||
'PartitionAdded': '0',
|
||||
'PartitionCount': '0',
|
||||
'PromptForImportSecuritySettings': '0',
|
||||
'PromptForInstallerCode': '0',
|
||||
'PromptForUserCode': '0',
|
||||
'TMSCloudSupported': '0',
|
||||
'UserCodeLength': '4',
|
||||
'UserCodeLengthChanged': '0',
|
||||
'VideoOnPanelSupported': '1',
|
||||
'WifiEnrollmentSupported': '1',
|
||||
'ZWaveThermostatScheduleDisabled': '0',
|
||||
'isArmStatusWithoutExitDelayNotSupported': '0',
|
||||
}),
|
||||
'name': 'test',
|
||||
'security_panel_type_id': 12,
|
||||
'serial_number': '**REDACTED**',
|
||||
'serial_text': None,
|
||||
}),
|
||||
]),
|
||||
'location_id': 1234567,
|
||||
'low_battery': False,
|
||||
'module_flags': dict({
|
||||
'can_bypass_zones': True,
|
||||
'can_clear_bypass': True,
|
||||
'can_set_usercodes': True,
|
||||
}),
|
||||
'name': 'Test Location',
|
||||
'partitions': list([
|
||||
dict({
|
||||
'arming_state': dict({
|
||||
'__type': "<enum 'ArmingState'>",
|
||||
'repr': '<ArmingState.DISARMED: 10200>',
|
||||
}),
|
||||
'exit_delay_timer': 0,
|
||||
'is_common_enabled': False,
|
||||
'is_fire_enabled': False,
|
||||
'is_locked': False,
|
||||
'is_new_partition': False,
|
||||
'is_night_stay_enabled': 0,
|
||||
'is_stay_armed': False,
|
||||
'name': 'Test1',
|
||||
'partition_id': 1,
|
||||
}),
|
||||
dict({
|
||||
'arming_state': dict({
|
||||
'__type': "<enum 'ArmingState'>",
|
||||
'repr': '<ArmingState.DISARMED: 10200>',
|
||||
}),
|
||||
'exit_delay_timer': 0,
|
||||
'is_common_enabled': False,
|
||||
'is_fire_enabled': False,
|
||||
'is_locked': False,
|
||||
'is_new_partition': False,
|
||||
'is_night_stay_enabled': 0,
|
||||
'is_stay_armed': False,
|
||||
'name': 'Test2',
|
||||
'partition_id': 2,
|
||||
}),
|
||||
]),
|
||||
'security_device_id': 7654321,
|
||||
'zones': list([
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Security',
|
||||
'device_type': 0,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '1',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 2,
|
||||
'zone_type_id': 1,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 0,
|
||||
'description': 'Fire',
|
||||
'device_type': 2,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '4',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 3,
|
||||
'zone_type_id': 9,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 0,
|
||||
'description': 'Gas',
|
||||
'device_type': 2,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '4',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 4,
|
||||
'zone_type_id': 14,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 0,
|
||||
'description': 'Unknown',
|
||||
'device_type': 2,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '4',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 5,
|
||||
'zone_type_id': 99,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Temperature',
|
||||
'device_type': 0,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '1',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 1,
|
||||
'zone_id': 6,
|
||||
'zone_type_id': 12,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 0,
|
||||
'battery_level': 5,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Doorbell Other',
|
||||
'device_type': 15,
|
||||
'loop_number': 2,
|
||||
'partition': 1,
|
||||
'response_type': '53',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': 2,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 7,
|
||||
'zone_type_id': 53,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Office Side Door',
|
||||
'device_type': 0,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '3',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 1,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 8,
|
||||
'zone_type_id': 3,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Office Back Door',
|
||||
'device_type': 0,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '3',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 9,
|
||||
'zone_type_id': 3,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Master Bedroom Door',
|
||||
'device_type': 0,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '1',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 10,
|
||||
'zone_type_id': 1,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Dining Room Two Door',
|
||||
'device_type': 0,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '3',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 12,
|
||||
'zone_type_id': 3,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Patio Door',
|
||||
'device_type': 0,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '3',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 13,
|
||||
'zone_type_id': 3,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Living Room Window',
|
||||
'device_type': 1,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '3',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 14,
|
||||
'zone_type_id': 3,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Living Room Two Window',
|
||||
'device_type': 1,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '3',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 15,
|
||||
'zone_type_id': 3,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Apartment SmokeDetector',
|
||||
'device_type': 4,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '9',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 16,
|
||||
'zone_type_id': 9,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Upstairs Hallway SmokeDetector',
|
||||
'device_type': 4,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '9',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 17,
|
||||
'zone_type_id': 9,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Downstairs Hallway SmokeDetector',
|
||||
'device_type': 4,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '9',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 18,
|
||||
'zone_type_id': 9,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Kid Bedroom SmokeDetector',
|
||||
'device_type': 4,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '9',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 19,
|
||||
'zone_type_id': 9,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Guest Bedroom SmokeDetector',
|
||||
'device_type': 4,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '9',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 20,
|
||||
'zone_type_id': 9,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Apartment CarbonMonoxideDetecto',
|
||||
'device_type': 6,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '14',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 21,
|
||||
'zone_type_id': 14,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Downstairs Hallway CarbonMonoxid',
|
||||
'device_type': 6,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '14',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 22,
|
||||
'zone_type_id': 14,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Upstairs Hallway CarbonMonoxideD',
|
||||
'device_type': 6,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '14',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 23,
|
||||
'zone_type_id': 14,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': 0,
|
||||
'description': 'Master Bedroom SmokeDetector',
|
||||
'device_type': 4,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '9',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 24,
|
||||
'zone_type_id': 9,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': 5,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 0,
|
||||
'description': 'Garage Side Other',
|
||||
'device_type': 15,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '23',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': 3,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 25,
|
||||
'zone_type_id': 23,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': 1,
|
||||
'battery_level': 5,
|
||||
'can_be_bypassed': 1,
|
||||
'chime_state': 1,
|
||||
'description': 'Front Door Door',
|
||||
'device_type': 0,
|
||||
'loop_number': 1,
|
||||
'partition': 1,
|
||||
'response_type': '1',
|
||||
'sensor_serial_number': '**REDACTED**',
|
||||
'signal_strength': 5,
|
||||
'status': 0,
|
||||
'supervision_type': 0,
|
||||
'zone_id': 26,
|
||||
'zone_type_id': 1,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': None,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': None,
|
||||
'description': 'Master Bedroom Keypad',
|
||||
'device_type': None,
|
||||
'loop_number': None,
|
||||
'partition': 1,
|
||||
'response_type': None,
|
||||
'sensor_serial_number': None,
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': None,
|
||||
'zone_id': 800,
|
||||
'zone_type_id': 50,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': None,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': None,
|
||||
'description': 'Zone 995 Fire',
|
||||
'device_type': None,
|
||||
'loop_number': None,
|
||||
'partition': 1,
|
||||
'response_type': None,
|
||||
'sensor_serial_number': None,
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': None,
|
||||
'zone_id': 1995,
|
||||
'zone_type_id': 9,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': None,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': None,
|
||||
'description': 'Zone 996 Medical',
|
||||
'device_type': None,
|
||||
'loop_number': None,
|
||||
'partition': 1,
|
||||
'response_type': None,
|
||||
'sensor_serial_number': None,
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': None,
|
||||
'zone_id': 1996,
|
||||
'zone_type_id': 15,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': None,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': None,
|
||||
'description': 'Zone 998 Other',
|
||||
'device_type': None,
|
||||
'loop_number': None,
|
||||
'partition': 1,
|
||||
'response_type': None,
|
||||
'sensor_serial_number': None,
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': None,
|
||||
'zone_id': 1998,
|
||||
'zone_type_id': 6,
|
||||
}),
|
||||
dict({
|
||||
'alarm_report_state': None,
|
||||
'battery_level': -1,
|
||||
'can_be_bypassed': 0,
|
||||
'chime_state': None,
|
||||
'description': 'Zone 999 Police',
|
||||
'device_type': None,
|
||||
'loop_number': None,
|
||||
'partition': 1,
|
||||
'response_type': None,
|
||||
'sensor_serial_number': None,
|
||||
'signal_strength': -1,
|
||||
'status': 0,
|
||||
'supervision_type': None,
|
||||
'zone_id': 1999,
|
||||
'zone_type_id': 7,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
'user': dict({
|
||||
'config_admin': True,
|
||||
'features': dict({
|
||||
'can_bypass_zones': True,
|
||||
'can_clear_bypass': True,
|
||||
'can_set_usercodes': True,
|
||||
}),
|
||||
'master': True,
|
||||
'security_problem': False,
|
||||
'user_admin': True,
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
@@ -1,19 +1,16 @@
|
||||
"""Tests for the TotalConnect alarm control panel device."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
from total_connect_client.exceptions import (
|
||||
AuthenticationError,
|
||||
ServiceUnavailable,
|
||||
TotalConnectError,
|
||||
)
|
||||
from total_connect_client import ArmingState, ArmType
|
||||
from total_connect_client.exceptions import BadResultCodeError, UsercodeInvalid
|
||||
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
DOMAIN as ALARM_DOMAIN,
|
||||
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
|
||||
AlarmControlPanelState,
|
||||
)
|
||||
from homeassistant.components.totalconnect.alarm_control_panel import (
|
||||
@@ -21,593 +18,375 @@ from homeassistant.components.totalconnect.alarm_control_panel import (
|
||||
SERVICE_ALARM_ARM_HOME_INSTANT,
|
||||
)
|
||||
from homeassistant.components.totalconnect.const import DOMAIN
|
||||
from homeassistant.components.totalconnect.coordinator import SCAN_INTERVAL
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
SERVICE_ALARM_ARM_HOME,
|
||||
SERVICE_ALARM_ARM_NIGHT,
|
||||
SERVICE_ALARM_DISARM,
|
||||
STATE_UNAVAILABLE,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_component import async_update_entity
|
||||
|
||||
from .common import (
|
||||
LOCATION_ID,
|
||||
RESPONSE_ARM_FAILURE,
|
||||
RESPONSE_ARM_SUCCESS,
|
||||
RESPONSE_ARMED_AWAY,
|
||||
RESPONSE_ARMED_CUSTOM,
|
||||
RESPONSE_ARMED_NIGHT,
|
||||
RESPONSE_ARMED_STAY,
|
||||
RESPONSE_ARMING,
|
||||
RESPONSE_DISARM_FAILURE,
|
||||
RESPONSE_DISARM_SUCCESS,
|
||||
RESPONSE_DISARMED,
|
||||
RESPONSE_DISARMING,
|
||||
RESPONSE_SUCCESS,
|
||||
RESPONSE_UNKNOWN,
|
||||
RESPONSE_USER_CODE_INVALID,
|
||||
TOTALCONNECT_REQUEST,
|
||||
USERCODES,
|
||||
setup_platform,
|
||||
)
|
||||
from . import setup_integration
|
||||
from .const import CODE
|
||||
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
|
||||
ENTITY_ID = "alarm_control_panel.test"
|
||||
ENTITY_ID_2 = "alarm_control_panel.test_partition_2"
|
||||
CODE = "-1"
|
||||
DATA = {ATTR_ENTITY_ID: ENTITY_ID}
|
||||
DELAY = timedelta(seconds=10)
|
||||
|
||||
ARMING_HELPER = "homeassistant.components.totalconnect.alarm_control_panel.ArmingHelper"
|
||||
|
||||
async def test_attributes(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion
|
||||
|
||||
async def test_entities(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the alarm control panel attributes are correct."""
|
||||
entry = await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(
|
||||
"homeassistant.components.totalconnect.TotalConnectClient.request",
|
||||
return_value=RESPONSE_DISARMED,
|
||||
) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
mock_request.assert_called_once()
|
||||
"homeassistant.components.totalconnect.PLATFORMS",
|
||||
[Platform.ALARM_CONTROL_PANEL],
|
||||
):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
assert mock_request.call_count == 1
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_arm_home_success(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
@pytest.mark.parametrize("code_required", [False, True])
|
||||
@pytest.mark.parametrize(
|
||||
("service", "arm_type"),
|
||||
[
|
||||
(SERVICE_ALARM_ARM_HOME, ArmType.STAY),
|
||||
(SERVICE_ALARM_ARM_NIGHT, ArmType.STAY_NIGHT),
|
||||
(SERVICE_ALARM_ARM_AWAY, ArmType.AWAY),
|
||||
],
|
||||
)
|
||||
async def test_arming(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_partition: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
service: str,
|
||||
arm_type: ArmType,
|
||||
) -> None:
|
||||
"""Test arm home method success."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_SUCCESS, RESPONSE_ARMED_STAY]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert hass.states.get(ENTITY_ID_2).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
"""Test arming method success."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_HOME, DATA, blocking=True
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
entity_id = "alarm_control_panel.test"
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_HOME
|
||||
# second partition should not be armed
|
||||
assert hass.states.get(ENTITY_ID_2).state == AlarmControlPanelState.DISARMED
|
||||
mock_partition.arming_state = ArmingState.ARMING
|
||||
|
||||
await hass.services.async_call(
|
||||
ALARM_CONTROL_PANEL_DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_CODE: CODE},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_partition.arm.call_args[1] == {"arm_type": arm_type, "usercode": ""}
|
||||
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.ARMING
|
||||
|
||||
|
||||
async def test_arm_home_failure(hass: HomeAssistant) -> None:
|
||||
"""Test arm home method failure."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_FAILURE, RESPONSE_USER_CODE_INVALID]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_HOME, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Failed to arm home test"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
# config entry usercode is invalid
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_HOME, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Usercode is invalid, did not arm home"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
# should have started a re-auth flow
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 1
|
||||
assert mock_request.call_count == 3
|
||||
|
||||
|
||||
async def test_arm_home_instant_success(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
@pytest.mark.parametrize("code_required", [True])
|
||||
@pytest.mark.parametrize(
|
||||
("service", "arm_type"),
|
||||
[
|
||||
(SERVICE_ALARM_ARM_HOME, ArmType.STAY),
|
||||
(SERVICE_ALARM_ARM_NIGHT, ArmType.STAY_NIGHT),
|
||||
(SERVICE_ALARM_ARM_AWAY, ArmType.AWAY),
|
||||
],
|
||||
)
|
||||
async def test_arming_invalid_usercode(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_partition: AsyncMock,
|
||||
mock_location: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
service: str,
|
||||
arm_type: ArmType,
|
||||
) -> None:
|
||||
"""Test arm home instant method success."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_SUCCESS, RESPONSE_ARMED_STAY]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert hass.states.get(ENTITY_ID_2).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
"""Test arming method with invalid usercode."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "alarm_control_panel.test"
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_location.get_panel_meta_data.call_count == 1
|
||||
|
||||
mock_partition.arming_state = ArmingState.ARMING
|
||||
|
||||
with pytest.raises(ServiceValidationError, match="Incorrect code entered"):
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_ALARM_ARM_HOME_INSTANT, DATA, blocking=True
|
||||
ALARM_CONTROL_PANEL_DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_CODE: "invalid_code"},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_HOME
|
||||
assert mock_partition.arm.call_count == 0
|
||||
assert mock_location.get_panel_meta_data.call_count == 1
|
||||
|
||||
|
||||
async def test_arm_home_instant_failure(hass: HomeAssistant) -> None:
|
||||
"""Test arm home instant method failure."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_FAILURE, RESPONSE_USER_CODE_INVALID]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_ALARM_ARM_HOME_INSTANT, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Failed to arm home instant test"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
# usercode is invalid
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_ALARM_ARM_HOME_INSTANT, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert str(err.value) == "Usercode is invalid, did not arm home instant"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
# should have started a re-auth flow
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 1
|
||||
assert mock_request.call_count == 3
|
||||
|
||||
|
||||
async def test_arm_away_instant_success(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
@pytest.mark.parametrize("code_required", [False, True])
|
||||
async def test_disarming(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_partition: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test arm home instant method success."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_SUCCESS, RESPONSE_ARMED_AWAY]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert hass.states.get(ENTITY_ID_2).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
"""Test disarming method success."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_ALARM_ARM_AWAY_INSTANT, DATA, blocking=True
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
entity_id = "alarm_control_panel.test"
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
mock_partition.arming_state = ArmingState.ARMING
|
||||
|
||||
await hass.services.async_call(
|
||||
ALARM_CONTROL_PANEL_DOMAIN,
|
||||
SERVICE_ALARM_DISARM,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_CODE: CODE},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_partition.disarm.call_args[1] == {"usercode": ""}
|
||||
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.ARMING
|
||||
|
||||
|
||||
async def test_arm_away_instant_failure(hass: HomeAssistant) -> None:
|
||||
"""Test arm home instant method failure."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_FAILURE, RESPONSE_USER_CODE_INVALID]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_ALARM_ARM_AWAY_INSTANT, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Failed to arm away instant test"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
# usercode is invalid
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_ALARM_ARM_AWAY_INSTANT, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Usercode is invalid, did not arm away instant"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
# should have started a re-auth flow
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 1
|
||||
assert mock_request.call_count == 3
|
||||
|
||||
|
||||
async def test_arm_away_success(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
@pytest.mark.parametrize("code_required", [True])
|
||||
async def test_disarming_invalid_usercode(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_partition: AsyncMock,
|
||||
mock_location: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test arm away method success."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_SUCCESS, RESPONSE_ARMED_AWAY]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
"""Test disarming method with invalid usercode."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "alarm_control_panel.test"
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_location.get_panel_meta_data.call_count == 1
|
||||
|
||||
mock_partition.arming_state = ArmingState.ARMING
|
||||
|
||||
with pytest.raises(ServiceValidationError, match="Incorrect code entered"):
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_AWAY, DATA, blocking=True
|
||||
ALARM_CONTROL_PANEL_DOMAIN,
|
||||
SERVICE_ALARM_DISARM,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_CODE: "invalid_code"},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
assert mock_partition.disarm.call_count == 0
|
||||
assert mock_location.get_panel_meta_data.call_count == 1
|
||||
|
||||
|
||||
async def test_arm_away_failure(hass: HomeAssistant) -> None:
|
||||
"""Test arm away method failure."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_FAILURE, RESPONSE_USER_CODE_INVALID]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_AWAY, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Failed to arm away test"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
# usercode is invalid
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_AWAY, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Usercode is invalid, did not arm away"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
# should have started a re-auth flow
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 1
|
||||
assert mock_request.call_count == 3
|
||||
|
||||
|
||||
async def test_disarm_success(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
@pytest.mark.parametrize(
|
||||
("service", "arm_type"),
|
||||
[
|
||||
(SERVICE_ALARM_ARM_HOME_INSTANT, ArmType.STAY_INSTANT),
|
||||
(SERVICE_ALARM_ARM_AWAY_INSTANT, ArmType.AWAY_INSTANT),
|
||||
],
|
||||
)
|
||||
async def test_instant_arming(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_partition: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
service: str,
|
||||
arm_type: ArmType,
|
||||
) -> None:
|
||||
"""Test disarm method success."""
|
||||
responses = [RESPONSE_ARMED_AWAY, RESPONSE_DISARM_SUCCESS, RESPONSE_DISARMED]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
assert mock_request.call_count == 1
|
||||
"""Test instant arming method success."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_DISARM, DATA, blocking=True
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
entity_id = "alarm_control_panel.test"
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
mock_partition.arming_state = ArmingState.ARMING
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_partition.arm.call_args[1] == {"arm_type": arm_type, "usercode": ""}
|
||||
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.ARMING
|
||||
|
||||
|
||||
async def test_disarm_failure(hass: HomeAssistant) -> None:
|
||||
"""Test disarm method failure."""
|
||||
responses = [
|
||||
RESPONSE_ARMED_AWAY,
|
||||
RESPONSE_DISARM_FAILURE,
|
||||
RESPONSE_USER_CODE_INVALID,
|
||||
]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_DISARM, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Failed to disarm test"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
# usercode is invalid
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_DISARM, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Usercode is invalid, did not disarm"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
# should have started a re-auth flow
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 1
|
||||
assert mock_request.call_count == 3
|
||||
|
||||
|
||||
async def test_disarm_code_required(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "suffix", "flows"),
|
||||
[(UsercodeInvalid, "invalid_code", 1), (BadResultCodeError, "failed", 0)],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("service", "prefix"),
|
||||
[
|
||||
(SERVICE_ALARM_ARM_HOME, "arm_home"),
|
||||
(SERVICE_ALARM_ARM_NIGHT, "arm_night"),
|
||||
(SERVICE_ALARM_ARM_AWAY, "arm_away"),
|
||||
],
|
||||
)
|
||||
async def test_arming_exceptions(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_partition: AsyncMock,
|
||||
mock_location: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
service: str,
|
||||
prefix: str,
|
||||
exception: Exception,
|
||||
suffix: str,
|
||||
flows: int,
|
||||
) -> None:
|
||||
"""Test disarm with code."""
|
||||
responses = [RESPONSE_ARMED_AWAY, RESPONSE_DISARM_SUCCESS, RESPONSE_DISARMED]
|
||||
await setup_platform(hass, ALARM_DOMAIN, code_required=True)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
assert mock_request.call_count == 1
|
||||
"""Test arming method exceptions."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
# runtime user entered code is bad
|
||||
DATA_WITH_CODE = DATA.copy()
|
||||
DATA_WITH_CODE["code"] = "666"
|
||||
with pytest.raises(ServiceValidationError, match="Incorrect code entered"):
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_DISARM, DATA_WITH_CODE, blocking=True
|
||||
)
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
# code check means the call to total_connect never happens
|
||||
assert mock_request.call_count == 1
|
||||
entity_id = "alarm_control_panel.test"
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_location.get_panel_meta_data.call_count == 1
|
||||
|
||||
# runtime user entered code that is in config
|
||||
DATA_WITH_CODE["code"] = USERCODES[LOCATION_ID]
|
||||
mock_partition.arm.side_effect = exception
|
||||
|
||||
mock_partition.arming_state = ArmingState.ARMING
|
||||
|
||||
with pytest.raises(HomeAssistantError) as exc:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_DISARM, DATA_WITH_CODE, blocking=True
|
||||
ALARM_CONTROL_PANEL_DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_CODE: CODE},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 2
|
||||
assert mock_partition.arm.call_count == 1
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert exc.value.translation_key == f"{prefix}_{suffix}"
|
||||
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_location.get_panel_meta_data.call_count == 1
|
||||
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == flows
|
||||
|
||||
|
||||
async def test_arm_night_success(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "suffix", "flows"),
|
||||
[(UsercodeInvalid, "invalid_code", 1), (BadResultCodeError, "failed", 0)],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("service", "prefix"),
|
||||
[
|
||||
(SERVICE_ALARM_ARM_HOME_INSTANT, "arm_home_instant"),
|
||||
(SERVICE_ALARM_ARM_AWAY_INSTANT, "arm_away_instant"),
|
||||
],
|
||||
)
|
||||
async def test_instant_arming_exceptions(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_partition: AsyncMock,
|
||||
mock_location: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
service: str,
|
||||
prefix: str,
|
||||
exception: Exception,
|
||||
suffix: str,
|
||||
flows: int,
|
||||
) -> None:
|
||||
"""Test arm night method success."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_SUCCESS, RESPONSE_ARMED_NIGHT]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
"""Test arming method exceptions."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "alarm_control_panel.test"
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_location.get_panel_meta_data.call_count == 1
|
||||
|
||||
mock_partition.arm.side_effect = exception
|
||||
|
||||
mock_partition.arming_state = ArmingState.ARMING
|
||||
|
||||
with pytest.raises(HomeAssistantError) as exc:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_NIGHT, DATA, blocking=True
|
||||
DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
assert mock_partition.arm.call_count == 1
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_NIGHT
|
||||
assert exc.value.translation_key == f"{prefix}_{suffix}"
|
||||
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_location.get_panel_meta_data.call_count == 1
|
||||
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == flows
|
||||
|
||||
|
||||
async def test_arm_night_failure(hass: HomeAssistant) -> None:
|
||||
"""Test arm night method failure."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_ARM_FAILURE, RESPONSE_USER_CODE_INVALID]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_NIGHT, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Failed to arm night test"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
# usercode is invalid
|
||||
with pytest.raises(HomeAssistantError) as err:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_NIGHT, DATA, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert f"{err.value}" == "Usercode is invalid, did not arm night"
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
# should have started a re-auth flow
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 1
|
||||
assert mock_request.call_count == 3
|
||||
|
||||
|
||||
async def test_arming(hass: HomeAssistant, freezer: FrozenDateTimeFactory) -> None:
|
||||
"""Test arming."""
|
||||
responses = [RESPONSE_DISARMED, RESPONSE_SUCCESS, RESPONSE_ARMING]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_ARM_NIGHT, DATA, blocking=True
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMING
|
||||
|
||||
|
||||
async def test_disarming(hass: HomeAssistant, freezer: FrozenDateTimeFactory) -> None:
|
||||
"""Test disarming."""
|
||||
responses = [RESPONSE_ARMED_AWAY, RESPONSE_SUCCESS, RESPONSE_DISARMING]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.ARMED_AWAY
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, SERVICE_ALARM_DISARM, DATA, blocking=True
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
freezer.tick(DELAY)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_request.call_count == 3
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMING
|
||||
|
||||
|
||||
async def test_armed_custom(hass: HomeAssistant) -> None:
|
||||
"""Test armed custom."""
|
||||
responses = [RESPONSE_ARMED_CUSTOM]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert (
|
||||
hass.states.get(ENTITY_ID).state
|
||||
== AlarmControlPanelState.ARMED_CUSTOM_BYPASS
|
||||
)
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
|
||||
async def test_unknown(hass: HomeAssistant) -> None:
|
||||
"""Test unknown arm status."""
|
||||
responses = [RESPONSE_UNKNOWN]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
|
||||
async def test_other_update_failures(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
@pytest.mark.parametrize(
|
||||
("arming_state", "state"),
|
||||
[
|
||||
(ArmingState.DISARMED, AlarmControlPanelState.DISARMED),
|
||||
(ArmingState.DISARMED_BYPASS, AlarmControlPanelState.DISARMED),
|
||||
(ArmingState.DISARMED_ZONE_FAULTED, AlarmControlPanelState.DISARMED),
|
||||
(ArmingState.ARMED_STAY_NIGHT, AlarmControlPanelState.ARMED_NIGHT),
|
||||
(ArmingState.ARMED_STAY_NIGHT_BYPASS_PROA7, AlarmControlPanelState.ARMED_NIGHT),
|
||||
(
|
||||
ArmingState.ARMED_STAY_NIGHT_INSTANT_PROA7,
|
||||
AlarmControlPanelState.ARMED_NIGHT,
|
||||
),
|
||||
(
|
||||
ArmingState.ARMED_STAY_NIGHT_INSTANT_BYPASS_PROA7,
|
||||
AlarmControlPanelState.ARMED_NIGHT,
|
||||
),
|
||||
(ArmingState.ARMED_STAY, AlarmControlPanelState.ARMED_HOME),
|
||||
(ArmingState.ARMED_STAY_PROA7, AlarmControlPanelState.ARMED_HOME),
|
||||
(ArmingState.ARMED_STAY_BYPASS, AlarmControlPanelState.ARMED_HOME),
|
||||
(ArmingState.ARMED_STAY_BYPASS_PROA7, AlarmControlPanelState.ARMED_HOME),
|
||||
(ArmingState.ARMED_STAY_INSTANT, AlarmControlPanelState.ARMED_HOME),
|
||||
(ArmingState.ARMED_STAY_INSTANT_PROA7, AlarmControlPanelState.ARMED_HOME),
|
||||
(ArmingState.ARMED_STAY_INSTANT_BYPASS, AlarmControlPanelState.ARMED_HOME),
|
||||
(
|
||||
ArmingState.ARMED_STAY_INSTANT_BYPASS_PROA7,
|
||||
AlarmControlPanelState.ARMED_HOME,
|
||||
),
|
||||
(ArmingState.ARMED_STAY_OTHER, AlarmControlPanelState.ARMED_HOME),
|
||||
(ArmingState.ARMED_AWAY, AlarmControlPanelState.ARMED_AWAY),
|
||||
(ArmingState.ARMED_AWAY_BYPASS, AlarmControlPanelState.ARMED_AWAY),
|
||||
(ArmingState.ARMED_AWAY_INSTANT, AlarmControlPanelState.ARMED_AWAY),
|
||||
(ArmingState.ARMED_AWAY_INSTANT_BYPASS, AlarmControlPanelState.ARMED_AWAY),
|
||||
(ArmingState.ARMED_CUSTOM_BYPASS, AlarmControlPanelState.ARMED_CUSTOM_BYPASS),
|
||||
(ArmingState.ARMING, AlarmControlPanelState.ARMING),
|
||||
(ArmingState.DISARMING, AlarmControlPanelState.DISARMING),
|
||||
(ArmingState.ALARMING, AlarmControlPanelState.TRIGGERED),
|
||||
(ArmingState.ALARMING_FIRE_SMOKE, AlarmControlPanelState.TRIGGERED),
|
||||
(ArmingState.ALARMING_CARBON_MONOXIDE, AlarmControlPanelState.TRIGGERED),
|
||||
(ArmingState.ALARMING_CARBON_MONOXIDE_PROA7, AlarmControlPanelState.TRIGGERED),
|
||||
],
|
||||
)
|
||||
async def test_arming_state(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_partition: AsyncMock,
|
||||
mock_location: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
arming_state: ArmingState,
|
||||
state: AlarmControlPanelState,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test other failures seen during updates."""
|
||||
responses = [
|
||||
RESPONSE_DISARMED,
|
||||
ServiceUnavailable,
|
||||
RESPONSE_DISARMED,
|
||||
TotalConnectError,
|
||||
RESPONSE_DISARMED,
|
||||
ValueError,
|
||||
]
|
||||
await setup_platform(hass, ALARM_DOMAIN)
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||
# first things work as planned
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 1
|
||||
"""Test arming state transitions."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
# then an error: ServiceUnavailable --> UpdateFailed
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
|
||||
assert mock_request.call_count == 2
|
||||
entity_id = "alarm_control_panel.test"
|
||||
assert hass.states.get(entity_id).state == AlarmControlPanelState.DISARMED
|
||||
|
||||
# works again
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 3
|
||||
mock_partition.arming_state = arming_state
|
||||
|
||||
# then an error: TotalConnectError --> UpdateFailed
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
|
||||
assert mock_request.call_count == 4
|
||||
freezer.tick(timedelta(seconds=30))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
# works again
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
assert hass.states.get(ENTITY_ID).state == AlarmControlPanelState.DISARMED
|
||||
assert mock_request.call_count == 5
|
||||
|
||||
# unknown TotalConnect status via ValueError
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
|
||||
assert mock_request.call_count == 6
|
||||
|
||||
|
||||
async def test_authentication_error(hass: HomeAssistant) -> None:
|
||||
"""Test other failures seen during updates."""
|
||||
entry = await setup_platform(hass, ALARM_DOMAIN)
|
||||
|
||||
with patch(TOTALCONNECT_REQUEST, side_effect=AuthenticationError):
|
||||
await async_update_entity(hass, ENTITY_ID)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
flows = hass.config_entries.flow.async_progress()
|
||||
assert len(flows) == 1
|
||||
|
||||
flow = flows[0]
|
||||
assert flow.get("step_id") == "reauth_confirm"
|
||||
assert flow.get("handler") == DOMAIN
|
||||
|
||||
assert "context" in flow
|
||||
assert flow["context"].get("source") == SOURCE_REAUTH
|
||||
assert flow["context"].get("entry_id") == entry.entry_id
|
||||
assert hass.states.get(entity_id).state == state
|
||||
|
||||
@@ -1,91 +1,29 @@
|
||||
"""Tests for the TotalConnect binary sensor."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DOMAIN as BINARY_SENSOR,
|
||||
BinarySensorDeviceClass,
|
||||
)
|
||||
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_OFF, STATE_ON
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import RESPONSE_DISARMED, ZONE_NORMAL, setup_platform
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
|
||||
ZONE_ENTITY_ID = "binary_sensor.security"
|
||||
ZONE_LOW_BATTERY_ID = "binary_sensor.security_battery"
|
||||
ZONE_TAMPER_ID = "binary_sensor.security_tamper"
|
||||
PANEL_BATTERY_ID = "binary_sensor.test_battery"
|
||||
PANEL_TAMPER_ID = "binary_sensor.test_tamper"
|
||||
PANEL_POWER_ID = "binary_sensor.test_power"
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
async def test_entity_registry(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the binary sensor is registered in entity registry."""
|
||||
entry = await setup_platform(hass, BINARY_SENSOR)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
|
||||
|
||||
async def test_state_and_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test the binary sensor attributes are correct."""
|
||||
|
||||
"""Test the alarm control panel attributes are correct."""
|
||||
with patch(
|
||||
"homeassistant.components.totalconnect.TotalConnectClient.request",
|
||||
return_value=RESPONSE_DISARMED,
|
||||
"homeassistant.components.totalconnect.PLATFORMS", [Platform.BINARY_SENSOR]
|
||||
):
|
||||
await setup_platform(hass, BINARY_SENSOR)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get(ZONE_ENTITY_ID)
|
||||
assert state.state == STATE_ON
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME) == ZONE_NORMAL["ZoneDescription"]
|
||||
)
|
||||
assert state.attributes.get("device_class") == BinarySensorDeviceClass.DOOR
|
||||
|
||||
state = hass.states.get(f"{ZONE_ENTITY_ID}_battery")
|
||||
assert state.state == STATE_OFF
|
||||
state = hass.states.get(f"{ZONE_ENTITY_ID}_tamper")
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
# Zone 2 is fire with low battery
|
||||
state = hass.states.get("binary_sensor.fire")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes.get("device_class") == BinarySensorDeviceClass.SMOKE
|
||||
state = hass.states.get("binary_sensor.fire_battery")
|
||||
assert state.state == STATE_ON
|
||||
state = hass.states.get("binary_sensor.fire_tamper")
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
# Zone 3 is gas with tamper
|
||||
state = hass.states.get("binary_sensor.gas")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes.get("device_class") == BinarySensorDeviceClass.GAS
|
||||
state = hass.states.get("binary_sensor.gas_battery")
|
||||
assert state.state == STATE_OFF
|
||||
state = hass.states.get("binary_sensor.gas_tamper")
|
||||
assert state.state == STATE_ON
|
||||
|
||||
# Zone 6 is unknown type, assume it is a security (door) sensor
|
||||
state = hass.states.get("binary_sensor.unknown")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes.get("device_class") == BinarySensorDeviceClass.DOOR
|
||||
state = hass.states.get("binary_sensor.unknown_battery")
|
||||
assert state.state == STATE_OFF
|
||||
state = hass.states.get("binary_sensor.unknown_tamper")
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
# Zone 7 is temperature
|
||||
state = hass.states.get("binary_sensor.temperature")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes.get("device_class") == BinarySensorDeviceClass.PROBLEM
|
||||
state = hass.states.get("binary_sensor.temperature_battery")
|
||||
assert state.state == STATE_OFF
|
||||
state = hass.states.get("binary_sensor.temperature_tamper")
|
||||
assert state.state == STATE_OFF
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
@@ -1,84 +1,64 @@
|
||||
"""Tests for the TotalConnect buttons."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
from total_connect_client.exceptions import FailedToBypassZone
|
||||
|
||||
from homeassistant.components.button import DOMAIN as BUTTON, SERVICE_PRESS
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
|
||||
from homeassistant.const import ATTR_ENTITY_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import setup_platform
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
|
||||
ZONE_BYPASS_ID = "button.security_bypass"
|
||||
PANEL_CLEAR_ID = "button.test_clear_bypass"
|
||||
PANEL_BYPASS_ID = "button.test_bypass_all"
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
async def test_entity_registry(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the button is registered in entity registry."""
|
||||
entry = await setup_platform(hass, BUTTON)
|
||||
with patch("homeassistant.components.totalconnect.PLATFORMS", [Platform.BUTTON]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_id", "tcc_request"),
|
||||
[
|
||||
(ZONE_BYPASS_ID, "total_connect_client.zone.TotalConnectZone.bypass"),
|
||||
(
|
||||
PANEL_BYPASS_ID,
|
||||
"total_connect_client.location.TotalConnectLocation.zone_bypass_all",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_bypass_button(
|
||||
hass: HomeAssistant, entity_id: str, tcc_request: str
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_location: AsyncMock,
|
||||
) -> None:
|
||||
"""Test pushing a bypass button."""
|
||||
responses = [FailedToBypassZone, None]
|
||||
await setup_platform(hass, BUTTON)
|
||||
with patch(tcc_request, side_effect=responses) as mock_request:
|
||||
# try to bypass, but fails
|
||||
with pytest.raises(FailedToBypassZone):
|
||||
await hass.services.async_call(
|
||||
domain=BUTTON,
|
||||
service=SERVICE_PRESS,
|
||||
service_data={ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
# try to bypass, works this time
|
||||
await hass.services.async_call(
|
||||
domain=BUTTON,
|
||||
service=SERVICE_PRESS,
|
||||
service_data={ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_request.call_count == 2
|
||||
|
||||
|
||||
async def test_clear_button(hass: HomeAssistant) -> None:
|
||||
"""Test pushing the clear bypass button."""
|
||||
data = {ATTR_ENTITY_ID: PANEL_CLEAR_ID}
|
||||
await setup_platform(hass, BUTTON)
|
||||
TOTALCONNECT_REQUEST = (
|
||||
"total_connect_client.location.TotalConnectLocation.clear_bypass"
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: "button.security_bypass"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
with patch(TOTALCONNECT_REQUEST) as mock_request:
|
||||
await hass.services.async_call(
|
||||
domain=BUTTON,
|
||||
service=SERVICE_PRESS,
|
||||
service_data=data,
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_request.call_count == 1
|
||||
assert mock_location.zones[2].bypass.call_count == 1
|
||||
|
||||
|
||||
async def test_clear_button(
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_location: AsyncMock,
|
||||
) -> None:
|
||||
"""Test pushing the clear bypass button."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{ATTR_ENTITY_ID: "button.test_clear_bypass"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert mock_location.clear_bypass.call_count == 1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Tests for the TotalConnect config flow."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from total_connect_client.exceptions import AuthenticationError
|
||||
|
||||
@@ -11,217 +11,235 @@ from homeassistant.components.totalconnect.const import (
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.const import CONF_PASSWORD
|
||||
from homeassistant.const import CONF_LOCATION, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from .common import (
|
||||
CONFIG_DATA,
|
||||
CONFIG_DATA_NO_USERCODES,
|
||||
RESPONSE_DISARMED,
|
||||
RESPONSE_GET_ZONE_DETAILS_SUCCESS,
|
||||
RESPONSE_PARTITION_DETAILS,
|
||||
RESPONSE_SESSION_DETAILS,
|
||||
RESPONSE_SUCCESS,
|
||||
RESPONSE_USER_CODE_INVALID,
|
||||
TOTALCONNECT_GET_CONFIG,
|
||||
TOTALCONNECT_REQUEST,
|
||||
TOTALCONNECT_REQUEST_TOKEN,
|
||||
USERNAME,
|
||||
init_integration,
|
||||
)
|
||||
from . import setup_integration
|
||||
from .const import LOCATION_ID, PASSWORD, USERNAME
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_user(hass: HomeAssistant) -> None:
|
||||
async def test_full_flow(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test user step."""
|
||||
# user starts with no data entered, so show the user form
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=None,
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
|
||||
async def test_user_show_locations(hass: HomeAssistant) -> None:
|
||||
"""Test user locations form."""
|
||||
# user/pass provided, so check if valid then ask for usercodes on locations form
|
||||
responses = [
|
||||
RESPONSE_SESSION_DETAILS,
|
||||
RESPONSE_PARTITION_DETAILS,
|
||||
RESPONSE_GET_ZONE_DETAILS_SUCCESS,
|
||||
RESPONSE_DISARMED,
|
||||
RESPONSE_USER_CODE_INVALID,
|
||||
RESPONSE_SUCCESS,
|
||||
]
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "locations"
|
||||
|
||||
with (
|
||||
patch(
|
||||
TOTALCONNECT_REQUEST,
|
||||
side_effect=responses,
|
||||
) as mock_request,
|
||||
patch(TOTALCONNECT_GET_CONFIG, side_effect=None),
|
||||
patch(TOTALCONNECT_REQUEST_TOKEN, side_effect=None),
|
||||
patch(
|
||||
"homeassistant.components.totalconnect.async_setup_entry", return_value=True
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=CONFIG_DATA_NO_USERCODES,
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERCODES: "7890"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
CONF_PASSWORD: PASSWORD,
|
||||
CONF_USERNAME: USERNAME,
|
||||
CONF_USERCODES: {LOCATION_ID: "7890"},
|
||||
}
|
||||
assert result["title"] == "Total Connect"
|
||||
assert result["options"] == {}
|
||||
assert result["result"].unique_id == USERNAME
|
||||
|
||||
|
||||
async def test_login_errors(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test login errors."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient",
|
||||
) as client:
|
||||
client.side_effect = AuthenticationError()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
|
||||
# first it should show the locations form
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "locations"
|
||||
# client should have sent four requests for init
|
||||
assert mock_request.call_count == 4
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
# user enters an invalid usercode
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_USERCODES: "bad"},
|
||||
)
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["step_id"] == "locations"
|
||||
# client should have sent 5th request to validate usercode
|
||||
assert mock_request.call_count == 5
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
|
||||
# user enters a valid usercode
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
user_input={CONF_USERCODES: "7890"},
|
||||
)
|
||||
assert result3["type"] is FlowResultType.CREATE_ENTRY
|
||||
# client should have sent another request to validate usercode
|
||||
assert mock_request.call_count == 6
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "locations"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERCODES: "7890"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_abort_if_already_setup(hass: HomeAssistant) -> None:
|
||||
async def test_usercode_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_client: AsyncMock,
|
||||
mock_location: AsyncMock,
|
||||
) -> None:
|
||||
"""Test user step with usercode errors."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "locations"
|
||||
|
||||
mock_location.set_usercode.return_value = False
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERCODES: "7890"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "locations"
|
||||
assert result["errors"] == {CONF_LOCATION: "usercode"}
|
||||
|
||||
mock_location.set_usercode.return_value = True
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERCODES: "7890"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_no_locations(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_client: AsyncMock,
|
||||
mock_location: AsyncMock,
|
||||
) -> None:
|
||||
"""Test no locations found."""
|
||||
|
||||
mock_client.get_number_locations.return_value = 0
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "no_locations"
|
||||
|
||||
|
||||
async def test_abort_if_already_setup(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test abort if the account is already setup."""
|
||||
MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=CONFIG_DATA,
|
||||
unique_id=USERNAME,
|
||||
).add_to_hass(hass)
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
# Should fail, same USERNAME (flow)
|
||||
with patch("homeassistant.components.totalconnect.config_flow.TotalConnectClient"):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=CONFIG_DATA,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_login_failed(hass: HomeAssistant) -> None:
|
||||
"""Test when we have errors during login."""
|
||||
with patch(
|
||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
||||
) as client_mock:
|
||||
client_mock.side_effect = AuthenticationError()
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=CONFIG_DATA,
|
||||
)
|
||||
async def test_reauth(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test login errors."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
result = await mock_config_entry.start_reauth_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_reauth(hass: HomeAssistant) -> None:
|
||||
"""Test reauth."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=CONFIG_DATA,
|
||||
unique_id=USERNAME,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await entry.start_reauth_flow(hass)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
||||
) as client_mock,
|
||||
patch(
|
||||
"homeassistant.components.totalconnect.async_setup_entry", return_value=True
|
||||
),
|
||||
):
|
||||
# first test with an invalid password
|
||||
client_mock.side_effect = AuthenticationError()
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_PASSWORD: "abc"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
|
||||
assert mock_config_entry.data[CONF_PASSWORD] == "abc"
|
||||
|
||||
|
||||
async def test_reauth_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test login errors."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
result = await mock_config_entry.start_reauth_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient",
|
||||
) as client:
|
||||
client.side_effect = AuthenticationError()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
||||
result["flow_id"], {CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
# now test with the password valid
|
||||
client_mock.side_effect = None
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
await hass.async_block_till_done()
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
|
||||
assert len(hass.config_entries.async_entries()) == 1
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
|
||||
|
||||
async def test_no_locations(hass: HomeAssistant) -> None:
|
||||
"""Test with no user locations."""
|
||||
responses = [
|
||||
RESPONSE_SESSION_DETAILS,
|
||||
RESPONSE_PARTITION_DETAILS,
|
||||
RESPONSE_GET_ZONE_DETAILS_SUCCESS,
|
||||
RESPONSE_DISARMED,
|
||||
]
|
||||
|
||||
with (
|
||||
patch(
|
||||
TOTALCONNECT_REQUEST,
|
||||
side_effect=responses,
|
||||
) as mock_request,
|
||||
patch(TOTALCONNECT_GET_CONFIG, side_effect=None),
|
||||
patch(TOTALCONNECT_REQUEST_TOKEN, side_effect=None),
|
||||
patch(
|
||||
"homeassistant.components.totalconnect.async_setup_entry", return_value=True
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.totalconnect.TotalConnectClient.get_number_locations",
|
||||
return_value=0,
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=CONFIG_DATA_NO_USERCODES,
|
||||
)
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "no_locations"
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_request.call_count == 1
|
||||
|
||||
|
||||
async def test_options_flow(hass: HomeAssistant) -> None:
|
||||
async def test_options_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test config flow options."""
|
||||
config_entry = await init_integration(hass)
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "init"
|
||||
@@ -231,8 +249,4 @@ async def test_options_flow(hass: HomeAssistant) -> None:
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert config_entry.options == {AUTO_BYPASS: True, CODE_REQUIRED: False}
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_config_entry.options == {AUTO_BYPASS: True, CODE_REQUIRED: False}
|
||||
|
||||
@@ -1,36 +1,29 @@
|
||||
"""Test TotalConnect diagnostics."""
|
||||
|
||||
from homeassistant.components.diagnostics import REDACTED
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import LOCATION_ID, init_integration
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
async def test_entry_diagnostics(
|
||||
hass: HomeAssistant, hass_client: ClientSessionGenerator
|
||||
hass: HomeAssistant,
|
||||
mock_client: AsyncMock,
|
||||
hass_client: ClientSessionGenerator,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test config entry diagnostics."""
|
||||
entry = await init_integration(hass)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
|
||||
|
||||
client = result["client"]
|
||||
assert client["invalid_credentials"] is False
|
||||
|
||||
user = result["user"]
|
||||
assert user["master"] is False
|
||||
|
||||
location = result["locations"][0]
|
||||
assert location["location_id"] == LOCATION_ID
|
||||
|
||||
device = location["devices"][0]
|
||||
assert device["serial_number"] == REDACTED
|
||||
|
||||
partition = location["partitions"][0]
|
||||
assert partition["name"] == "Test1"
|
||||
|
||||
zone = location["zones"][0]
|
||||
assert zone["zone_id"] == "1"
|
||||
assert (
|
||||
await get_diagnostics_for_config_entry(hass, hass_client, mock_config_entry)
|
||||
== snapshot
|
||||
)
|
||||
|
||||
@@ -4,29 +4,23 @@ from unittest.mock import patch
|
||||
|
||||
from total_connect_client.exceptions import AuthenticationError
|
||||
|
||||
from homeassistant.components.totalconnect.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import CONFIG_DATA
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_reauth_started(hass: HomeAssistant) -> None:
|
||||
async def test_reauth_start(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that reauth is started when we have login errors."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=CONFIG_DATA,
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.totalconnect.TotalConnectClient",
|
||||
) as mock_client:
|
||||
mock_client.side_effect = AuthenticationError()
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert mock_entry.state is ConfigEntryState.SETUP_ERROR
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
|
||||
|
||||
Reference in New Issue
Block a user