"""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