diff --git a/tests/conftest.py b/tests/conftest.py index 00d1e88e..f4626ebb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,19 +25,14 @@ import zigpy.zdo.types as zdo_t from tests import common -from zha.application.const import ( - CONF_ALARM_ARM_REQUIRES_CODE, - CONF_ALARM_FAILED_TRIES, - CONF_ALARM_MASTER_CODE, - CONF_ENABLE_ENHANCED_LIGHT_TRANSITION, - CONF_GROUP_MEMBERS_ASSUME_STATE, - CONF_RADIO_TYPE, - CUSTOM_CONFIGURATION, - ZHA_ALARM_OPTIONS, - ZHA_OPTIONS, -) from zha.application.gateway import Gateway -from zha.application.helpers import ZHAData +from zha.application.helpers import ( + AlarmControlPanelOptions, + CoordinatorConfiguration, + LightOptions, + ZHAConfiguration, + ZHAData, +) from zha.zigbee.device import Device FIXTURE_GRP_ID = 0x1001 @@ -185,29 +180,23 @@ def caplog_fixture(caplog: pytest.LogCaptureFixture) -> pytest.LogCaptureFixture @pytest.fixture def zha_data() -> ZHAData: """Fixture representing zha configuration data.""" + return ZHAData( - yaml_config={}, - config_entry_data={ - "data": { - zigpy.config.CONF_DEVICE: { - zigpy.config.CONF_DEVICE_PATH: "/dev/ttyUSB0" - }, - CONF_RADIO_TYPE: "ezsp", - }, - "options": { - CUSTOM_CONFIGURATION: { - ZHA_OPTIONS: { - CONF_ENABLE_ENHANCED_LIGHT_TRANSITION: True, - CONF_GROUP_MEMBERS_ASSUME_STATE: False, - }, - ZHA_ALARM_OPTIONS: { - CONF_ALARM_ARM_REQUIRES_CODE: False, - CONF_ALARM_MASTER_CODE: "4321", - CONF_ALARM_FAILED_TRIES: 2, - }, - } - }, - }, + config=ZHAConfiguration( + coordinator_configuration=CoordinatorConfiguration( + radio_type="ezsp", + path="/dev/ttyUSB0", + ), + light_options=LightOptions( + enable_enhanced_light_transition=True, + group_members_assume_state=False, + ), + alarm_control_panel_options=AlarmControlPanelOptions( + arm_requires_code=False, + master_code="4321", + failed_tries=2, + ), + ) ) diff --git a/tests/test_discover.py b/tests/test_discover.py index 131e05c8..9b3cc28a 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -44,6 +44,7 @@ from zha.application import Platform, discovery from zha.application.discovery import ENDPOINT_PROBE, PLATFORMS, EndpointProbe from zha.application.gateway import Gateway +from zha.application.helpers import DeviceOverridesConfiguration from zha.application.platforms import PlatformEntity from zha.application.registries import ( PLATFORM_ENTITIES, @@ -191,9 +192,7 @@ async def test_devices( no_tail_id = NO_TAIL_ID.sub("", ent_info[DEV_SIG_ENT_MAP_ID]) message1 = f"No entity found for platform[{platform}] unique_id[{unique_id}] no_tail_id[{no_tail_id}]" - if not contains_ignored_suffix( - unique_id - ): # TODO remove this when update is fixed + if not contains_ignored_suffix(unique_id): assert unique_id in created_entities, message1 entity = created_entities[unique_id] unhandled_entities.remove(unique_id) @@ -275,7 +274,7 @@ def test_discover_by_device_type_override() -> None: ep_mock.return_value.device_type = 0x0100 type(endpoint).zigpy_endpoint = ep_mock - overrides = {endpoint.unique_id: {"type": Platform.SWITCH}} + overrides = {endpoint.unique_id: DeviceOverridesConfiguration(type=Platform.SWITCH)} get_entity_mock = mock.MagicMock( return_value=(mock.sentinel.entity_cls, mock.sentinel.claimed) ) @@ -328,8 +327,8 @@ def test_discover_probe_single_cluster() -> None: @pytest.mark.parametrize("device_info", DEVICES) async def test_discover_endpoint( device_info: dict[str, Any], - zha_device_mock: Callable[..., Device], - zha_gateway: Gateway, + zha_device_mock: Callable[..., Device], # pylint: disable=redefined-outer-name + zha_gateway: Gateway, # pylint: disable=unused-argument ) -> None: """Test device discovery.""" @@ -367,20 +366,17 @@ async def test_discover_endpoint( test_ent_class = ent_info[DEV_SIG_ENT_MAP_CLASS] test_unique_id_head = UNIQUE_ID_HD.match(unique_id).group(0) - if ( - test_ent_class != "FirmwareUpdateEntity" - ): # TODO remove this when update is fixed - assert (test_unique_id_head, test_ent_class) in ha_ent_info - - entity_platform, entity_unique_id, entity_cluster_handlers = ha_ent_info[ - (test_unique_id_head, test_ent_class) - ] - assert platform is entity_platform.value - # unique_id used for discover is the same for "multi entities" - assert unique_id.startswith(entity_unique_id) - assert {ch.name for ch in entity_cluster_handlers} == set( - ent_info[DEV_SIG_CLUSTER_HANDLERS] - ) + assert (test_unique_id_head, test_ent_class) in ha_ent_info + + entity_platform, entity_unique_id, entity_cluster_handlers = ha_ent_info[ + (test_unique_id_head, test_ent_class) + ] + assert platform is entity_platform.value + # unique_id used for discover is the same for "multi entities" + assert unique_id.startswith(entity_unique_id) + assert {ch.name for ch in entity_cluster_handlers} == set( + ent_info[DEV_SIG_CLUSTER_HANDLERS] + ) def _ch_mock(cluster): @@ -410,8 +406,6 @@ def _test_single_input_cluster_device_class(probe_mock): class QuirkedIAS(zigpy.quirks.CustomCluster, zigpy.zcl.clusters.security.IasZone): """Quirked IAS Zone cluster.""" - pass - ias_ch = _ch_mock(QuirkedIAS) class _Analog(zigpy.quirks.CustomCluster, zigpy.zcl.clusters.general.AnalogInput): @@ -485,8 +479,10 @@ async def test_device_override( ) if override is not None: - override = {"device_config": {"00:11:22:33:44:55:66:77-1": {"type": override}}} - zha_gateway.config.yaml_config = override + overrides = { + "00:11:22:33:44:55:66:77-1": DeviceOverridesConfiguration(type=override) + } + zha_gateway.config.config.device_overrides = overrides discovery.ENDPOINT_PROBE.initialize(zha_gateway) await zha_gateway.async_device_initialized(zigpy_device) @@ -502,7 +498,7 @@ async def test_device_override( async def test_quirks_v2_entity_discovery( - zha_gateway: Gateway, + zha_gateway: Gateway, # pylint: disable=unused-argument zigpy_device_mock, device_joined, ) -> None: @@ -566,7 +562,7 @@ async def test_quirks_v2_entity_discovery( async def test_quirks_v2_entity_discovery_e1_curtain( - zha_gateway: Gateway, + zha_gateway: Gateway, # pylint: disable=unused-argument zigpy_device_mock, device_joined, ) -> None: @@ -778,7 +774,7 @@ def _get_test_device( async def test_quirks_v2_entity_no_metadata( - zha_gateway: Gateway, + zha_gateway: Gateway, # pylint: disable=unused-argument zigpy_device_mock, device_joined, caplog: pytest.LogCaptureFixture, @@ -797,7 +793,7 @@ async def test_quirks_v2_entity_no_metadata( async def test_quirks_v2_entity_discovery_errors( - zha_gateway: Gateway, + zha_gateway: Gateway, # pylint: disable=unused-argument zigpy_device_mock, device_joined, caplog: pytest.LogCaptureFixture, @@ -846,7 +842,7 @@ def validate_device_class_unit( quirk: QuirksV2RegistryEntry, entity_metadata: EntityMetadata, platform: Platform, - translations: dict, + translations: dict, # pylint: disable=unused-argument ) -> None: """Ensure device class and unit are used correctly.""" if ( @@ -887,7 +883,7 @@ def validate_translation_keys_device_class( quirk: QuirksV2RegistryEntry, entity_metadata: EntityMetadata, platform: Platform, - translations: dict, + translations: dict, # pylint: disable=unused-argument ) -> None: """Validate translation keys and device class usage.""" if isinstance(entity_metadata, ZCLCommandButtonMetadata): @@ -960,7 +956,7 @@ def validate_metadata(validator: Callable) -> None: ], ) async def test_quirks_v2_metadata_errors( - zha_gateway: Gateway, + zha_gateway: Gateway, # pylint: disable=unused-argument zigpy_device_mock, device_joined, augment_method: Callable[[QuirksV2RegistryEntry], QuirksV2RegistryEntry], @@ -1063,7 +1059,7 @@ def bad_number_device_class( ], ) async def test_quirks_v2_metadata_bad_device_classes( - zha_gateway: Gateway, + zha_gateway: Gateway, # pylint: disable=unused-argument zigpy_device_mock, device_joined, caplog: pytest.LogCaptureFixture, diff --git a/tests/test_gateway.py b/tests/test_gateway.py index a1ea9ae7..b739ab17 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -17,7 +17,7 @@ from tests.common import async_find_group_entity_id, find_entity_id from tests.conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE from zha.application import Platform -from zha.application.const import RadioType +from zha.application.const import CONF_USE_THREAD, RadioType from zha.application.gateway import ( DeviceJoinedDeviceInfo, DeviceJoinedEvent, @@ -353,8 +353,8 @@ async def test_gateway_initialize_bellows_thread( zha_data: ZHAData, ) -> None: """Test ZHA disabling the UART thread when connecting to a TCP coordinator.""" - zha_data.config_entry_data["data"]["device"]["path"] = device_path - zha_data.yaml_config["zigpy_config"] = config_override + zha_data.config.coordinator_configuration.path = device_path + zha_data.zigpy_config = config_override with patch( "bellows.zigbee.application.ControllerApplication.new", @@ -362,7 +362,7 @@ async def test_gateway_initialize_bellows_thread( ) as mock_new: zha_gw = Gateway(zha_data) await zha_gw.async_initialize() - assert mock_new.mock_calls[-1].kwargs["config"]["use_thread"] is thread_state + assert mock_new.mock_calls[-1].kwargs["config"][CONF_USE_THREAD] is thread_state await zha_gw.shutdown() diff --git a/tests/test_light.py b/tests/test_light.py index eb48bc94..06646355 100644 --- a/tests/test_light.py +++ b/tests/test_light.py @@ -18,12 +18,7 @@ import zigpy.zcl.foundation as zcl_f from zha.application import Platform -from zha.application.const import ( - CONF_ALWAYS_PREFER_XY_COLOR_MODE, - CONF_GROUP_MEMBERS_ASSUME_STATE, - CUSTOM_CONFIGURATION, - ZHA_OPTIONS, -) +from zha.application.const import CONF_ALWAYS_PREFER_XY_COLOR_MODE from zha.application.gateway import Gateway from zha.application.platforms import GroupEntity, PlatformEntity from zha.application.platforms.light.const import ( @@ -1015,10 +1010,9 @@ async def test_light_initialization( # mock attribute reads zigpy_device.endpoints[1].light_color.PLUGGED_ATTR_READS = plugged_attr_reads + light_options = zha_gateway.config.config.light_options for key in config_override: - zha_gateway.config.config_entry_data["options"][CUSTOM_CONFIGURATION][ - ZHA_OPTIONS - ][key] = config_override[key] + setattr(light_options, key, config_override[key]) zha_device = await device_joined(zigpy_device) entity_id = find_entity_id(Platform.LIGHT, zha_device) assert entity_id is not None @@ -1865,9 +1859,7 @@ async def test_group_member_assume_state( ) -> None: """Test the group members assume state function.""" - zha_gateway.config.config_entry_data["options"][CUSTOM_CONFIGURATION][ZHA_OPTIONS][ - CONF_GROUP_MEMBERS_ASSUME_STATE - ] = True + zha_gateway.config.config.light_options.group_members_assume_state = True zha_gateway.coordinator_zha_device = coordinator coordinator._zha_gateway = zha_gateway diff --git a/zha/application/const.py b/zha/application/const.py index cd270067..4ef0686a 100644 --- a/zha/application/const.py +++ b/zha/application/const.py @@ -73,56 +73,12 @@ CLUSTER_TYPE_IN = "in" CLUSTER_TYPE_OUT = "out" -CONF_ALARM_MASTER_CODE = "alarm_master_code" -CONF_ALARM_FAILED_TRIES = "alarm_failed_tries" -CONF_ALARM_ARM_REQUIRES_CODE = "alarm_arm_requires_code" - -CONF_BAUDRATE = "baudrate" -CONF_FLOW_CONTROL = "flow_control" -CONF_CUSTOM_QUIRKS_PATH = "custom_quirks_path" -CONF_DEFAULT_LIGHT_TRANSITION = "default_light_transition" -CONF_DEVICE_CONFIG = "device_config" -CONF_ENABLE_ENHANCED_LIGHT_TRANSITION = "enhanced_light_transition" -CONF_ENABLE_LIGHT_TRANSITIONING_FLAG = "light_transitioning_flag" CONF_ALWAYS_PREFER_XY_COLOR_MODE = "always_prefer_xy_color_mode" -CONF_GROUP_MEMBERS_ASSUME_STATE = "group_members_assume_state" -CONF_ENABLE_IDENTIFY_ON_JOIN = "enable_identify_on_join" -CONF_ENABLE_QUIRKS = "enable_quirks" -CONF_RADIO_TYPE = "radio_type" -CONF_USB_PATH = "usb_path" CONF_USE_THREAD = "use_thread" -CONF_ZIGPY = "zigpy_config" -CONF_CONSIDER_UNAVAILABLE_MAINS = "consider_unavailable_mains" CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS = 60 * 60 * 2 # 2 hours -CONF_CONSIDER_UNAVAILABLE_BATTERY = "consider_unavailable_battery" CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY = 60 * 60 * 6 # 6 hours -CUSTOM_CONFIGURATION = "custom_configuration" - -DATA_DEVICE_CONFIG = "zha_device_config" -DATA_ZHA = "zha" -DATA_ZHA_CONFIG = "config" -DATA_ZHA_CORE_EVENTS = "zha_core_events" -DATA_ZHA_DEVICE_TRIGGER_CACHE = "zha_device_trigger_cache" -DATA_ZHA_GATEWAY = "zha_gateway" - -DEFAULT_RADIO_TYPE = "ezsp" -DEFAULT_BAUDRATE = 57600 -DEFAULT_DATABASE_NAME = "zigbee.db" - -DEVICE_PAIRING_STATUS = "pairing_status" - -DISCOVERY_KEY = "zha_discovery_info" - -DOMAIN = "zha" - -GROUP_ID = "group_id" -GROUP_IDS = "group_ids" -GROUP_NAME = "group_name" - -MFG_CLUSTER_ID_START = 0xFC00 - POWER_MAINS_POWERED = "Mains" POWER_BATTERY_OR_UNKNOWN = "Battery or Unknown" @@ -140,9 +96,6 @@ ZCL_INIT_ATTRS = "ZCL_INIT_ATTRS" -ZHA_ALARM_OPTIONS = "zha_alarm_options" -ZHA_OPTIONS = "zha_options" - _ControllerClsType = type[zigpy.application.ControllerApplication] @@ -227,7 +180,6 @@ def description(self) -> str: WARNING_DEVICE_SQUAWK_MODE_ARMED = 0 WARNING_DEVICE_SQUAWK_MODE_DISARMED = 1 -ZHA_DISCOVERY_NEW = "zha_discovery_new_{}" ZHA_CLUSTER_HANDLER_MSG = "zha_channel_message" ZHA_CLUSTER_HANDLER_MSG_BIND = "zha_channel_bind" ZHA_CLUSTER_HANDLER_MSG_CFG_RPT = "zha_channel_configure_reporting" diff --git a/zha/application/discovery.py b/zha/application/discovery.py index ead1dfd6..d8f18c5a 100644 --- a/zha/application/discovery.py +++ b/zha/application/discovery.py @@ -4,7 +4,7 @@ from collections import Counter import logging -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, cast from slugify import slugify from zigpy.quirks.v2 import ( @@ -23,6 +23,7 @@ from zigpy.zcl.clusters.general import Ota from zha.application import Platform, const as zha_const +from zha.application.helpers import DeviceOverridesConfiguration from zha.application.platforms import ( # noqa: F401 pylint: disable=unused-import alarm_control_panel, binary_sensor, @@ -348,7 +349,7 @@ class EndpointProbe: def __init__(self) -> None: """Initialize instance.""" - self._device_configs: dict[str, Any] = {} + self._device_configs: dict[str, DeviceOverridesConfiguration] = {} def discover_entities(self, endpoint: Endpoint) -> None: """Process an endpoint on a zigpy device.""" @@ -368,7 +369,9 @@ def discover_by_device_type(self, endpoint: Endpoint) -> None: unique_id = endpoint.unique_id - platform: str | None = self._device_configs.get(unique_id, {}).get("type") + platform: str | None = None + if unique_id in self._device_configs: + platform = self._device_configs.get(unique_id).type if platform is None: ep_profile_id = endpoint.zigpy_endpoint.profile_id ep_device_type = endpoint.zigpy_endpoint.device_type @@ -538,8 +541,7 @@ def discover_multi_entities( def initialize(self, gateway: Gateway) -> None: """Update device overrides config.""" - zha_config = gateway.config.yaml_config - if overrides := zha_config.get(zha_const.CONF_DEVICE_CONFIG): + if overrides := gateway.config.config.device_overrides: self._device_configs.update(overrides) diff --git a/zha/application/gateway.py b/zha/application/gateway.py index 35137da4..36e6d2be 100644 --- a/zha/application/gateway.py +++ b/zha/application/gateway.py @@ -14,7 +14,13 @@ from zhaquirks import setup as setup_quirks from zigpy.application import ControllerApplication -from zigpy.config import CONF_DEVICE, CONF_DEVICE_PATH, CONF_NWK_VALIDATE_SETTINGS +from zigpy.config import ( + CONF_DEVICE, + CONF_DEVICE_BAUDRATE, + CONF_DEVICE_FLOW_CONTROL, + CONF_DEVICE_PATH, + CONF_NWK_VALIDATE_SETTINGS, +) import zigpy.device import zigpy.endpoint import zigpy.group @@ -23,11 +29,7 @@ from zha.application import discovery from zha.application.const import ( - CONF_CUSTOM_QUIRKS_PATH, - CONF_ENABLE_QUIRKS, - CONF_RADIO_TYPE, CONF_USE_THREAD, - CONF_ZIGPY, UNKNOWN_MANUFACTURER, UNKNOWN_MODEL, ZHA_GW_MSG, @@ -177,9 +179,13 @@ def __init__(self, config: ZHAData) -> None: def get_application_controller_data(self) -> tuple[ControllerApplication, dict]: """Get an uninitialized instance of a zigpy `ControllerApplication`.""" - radio_type = RadioType[self.config.config_entry_data["data"][CONF_RADIO_TYPE]] - app_config = self.config.yaml_config.get(CONF_ZIGPY, {}) - app_config[CONF_DEVICE] = self.config.config_entry_data["data"][CONF_DEVICE] + radio_type = RadioType[self.config.config.coordinator_configuration.radio_type] + app_config = self.config.zigpy_config + app_config[CONF_DEVICE] = { + CONF_DEVICE_PATH: self.config.config.coordinator_configuration.path, + CONF_DEVICE_BAUDRATE: self.config.config.coordinator_configuration.baudrate, + CONF_DEVICE_FLOW_CONTROL: self.config.config.coordinator_configuration.flow_control, + } if CONF_NWK_VALIDATE_SETTINGS not in app_config: app_config[CONF_NWK_VALIDATE_SETTINGS] = True @@ -208,9 +214,9 @@ async def async_initialize(self) -> None: discovery.ENDPOINT_PROBE.initialize(self) discovery.GROUP_PROBE.initialize(self) - if self.config.yaml_config.get(CONF_ENABLE_QUIRKS, True): + if self.config.config.quirks_configuration.enabled: await self.async_add_import_executor_job( - setup_quirks, self.config.yaml_config.get(CONF_CUSTOM_QUIRKS_PATH) + setup_quirks, self.config.config.quirks_configuration.custom_quirks_path ) self.shutting_down = False diff --git a/zha/application/helpers.py b/zha/application/helpers.py index 91e1e565..815d5e9c 100644 --- a/zha/application/helpers.py +++ b/zha/application/helpers.py @@ -25,7 +25,8 @@ from zha.application.const import ( CLUSTER_TYPE_IN, CLUSTER_TYPE_OUT, - CUSTOM_CONFIGURATION, + CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY, + CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS, ) from zha.async_ import gather_with_limited_concurrency from zha.decorators import SetRegistry, callback, periodic @@ -178,18 +179,6 @@ def async_is_bindable_target(source_zha_device: Device, target_zha_device: Devic return False -def async_get_zha_config_value( - zha_data: ZHAData, section: str, config_key: str, default: _T -) -> _T: - """Get the value for the specified configuration from the ZHA config entry.""" - return ( - zha_data.config_entry_data.get("options", {}) - .get(CUSTOM_CONFIGURATION, {}) - .get(section, {}) - .get(config_key, default) - ) - - def convert_install_code(value: str) -> zigpy.types.KeyData: """Convert string to install code bytes and validate length.""" @@ -266,12 +255,90 @@ def qr_to_install_code(qr_code: str) -> tuple[zigpy.types.EUI64, zigpy.types.Key raise vol.Invalid(f"couldn't convert qr code: {qr_code}") +@dataclass(kw_only=True, slots=True) +class LightOptions: + """ZHA light options.""" + + default_light_transition: float = dataclasses.field(default=0) + enable_enhanced_light_transition: bool = dataclasses.field(default=False) + enable_light_transitioning_flag: bool = dataclasses.field(default=True) + always_prefer_xy_color_mode: bool = dataclasses.field(default=True) + group_members_assume_state: bool = dataclasses.field(default=True) + + +@dataclass(kw_only=True, slots=True) +class DeviceOptions: + """ZHA device options.""" + + enable_identify_on_join: bool = dataclasses.field(default=True) + consider_unavailable_mains: int = dataclasses.field( + default=CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS + ) + consider_unavailable_battery: int = dataclasses.field( + default=CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY + ) + + +@dataclass(kw_only=True, slots=True) +class AlarmControlPanelOptions: + """ZHA alarm control panel options.""" + + master_code: str = dataclasses.field(default="1234") + failed_tries: int = dataclasses.field(default=3) + arm_requires_code: bool = dataclasses.field(default=False) + + +@dataclass(kw_only=True, slots=True) +class CoordinatorConfiguration: + """ZHA coordinator configuration.""" + + path: str + baudrate: int = dataclasses.field(default=115200) + flow_control: str = dataclasses.field(default="hardware") + radio_type: str = dataclasses.field(default="ezsp") + + +@dataclass(kw_only=True, slots=True) +class QuirksConfiguration: + """ZHA quirks configuration.""" + + enabled: bool = dataclasses.field(default=True) + custom_quirks_path: str | None = dataclasses.field(default=None) + + +@dataclass(kw_only=True, slots=True) +class DeviceOverridesConfiguration: + """ZHA device overrides configuration.""" + + type: Platform + + +@dataclass(kw_only=True, slots=True) +class ZHAConfiguration: + """ZHA configuration.""" + + coordinator_configuration: CoordinatorConfiguration = dataclasses.field( + default_factory=CoordinatorConfiguration + ) + quirks_configuration: QuirksConfiguration = dataclasses.field( + default_factory=QuirksConfiguration + ) + device_overrides: dict[str, DeviceOverridesConfiguration] = dataclasses.field( + default_factory=dict + ) + light_options: LightOptions = dataclasses.field(default_factory=LightOptions) + device_options: DeviceOptions = dataclasses.field(default_factory=DeviceOptions) + alarm_control_panel_options: AlarmControlPanelOptions = dataclasses.field( + default_factory=AlarmControlPanelOptions + ) + + @dataclasses.dataclass(kw_only=True, slots=True) class ZHAData: """ZHA data stored in `gateway.data`.""" - yaml_config: dict[str, Any] = dataclasses.field(default_factory=dict) - config_entry_data: dict[str, Any] = dataclasses.field(default_factory=dict) + config: ZHAConfiguration + zigpy_config: dict[str, Any] = dataclasses.field(default_factory=dict) platforms: collections.defaultdict[Platform, list] = dataclasses.field( default_factory=lambda: collections.defaultdict(list) ) diff --git a/zha/application/platforms/alarm_control_panel/__init__.py b/zha/application/platforms/alarm_control_panel/__init__.py index 4c2e293c..0213d811 100644 --- a/zha/application/platforms/alarm_control_panel/__init__.py +++ b/zha/application/platforms/alarm_control_panel/__init__.py @@ -10,13 +10,6 @@ from zigpy.zcl.clusters.security import IasAce from zha.application import Platform -from zha.application.const import ( - CONF_ALARM_ARM_REQUIRES_CODE, - CONF_ALARM_FAILED_TRIES, - CONF_ALARM_MASTER_CODE, - ZHA_ALARM_OPTIONS, -) -from zha.application.helpers import async_get_zha_config_value from zha.application.platforms import PlatformEntity, PlatformEntityInfo from zha.application.platforms.alarm_control_panel.const import ( IAS_ACE_STATE_MAP, @@ -76,17 +69,13 @@ def __init__( ) -> None: """Initialize the ZHA alarm control device.""" super().__init__(unique_id, cluster_handlers, endpoint, device, **kwargs) - config = device.gateway.config + alarm_options = device.gateway.config.config.alarm_control_panel_options self._cluster_handler: IasAceClusterHandler = cluster_handlers[0] - self._cluster_handler.panel_code = async_get_zha_config_value( - config, ZHA_ALARM_OPTIONS, CONF_ALARM_MASTER_CODE, "1234" - ) - self._cluster_handler.code_required_arm_actions = async_get_zha_config_value( - config, ZHA_ALARM_OPTIONS, CONF_ALARM_ARM_REQUIRES_CODE, False - ) - self._cluster_handler.max_invalid_tries = async_get_zha_config_value( - config, ZHA_ALARM_OPTIONS, CONF_ALARM_FAILED_TRIES, 3 + self._cluster_handler.panel_code = alarm_options.master_code + self._cluster_handler.code_required_arm_actions = ( + alarm_options.arm_requires_code ) + self._cluster_handler.max_invalid_tries = alarm_options.failed_tries self._cluster_handler.on_event( CLUSTER_HANDLER_STATE_CHANGED, self._handle_event_protocol ) diff --git a/zha/application/platforms/light/__init__.py b/zha/application/platforms/light/__init__.py index d11135e1..39afe3e3 100644 --- a/zha/application/platforms/light/__init__.py +++ b/zha/application/platforms/light/__init__.py @@ -21,15 +21,6 @@ from zigpy.zcl.foundation import Status from zha.application import Platform -from zha.application.const import ( - CONF_ALWAYS_PREFER_XY_COLOR_MODE, - CONF_DEFAULT_LIGHT_TRANSITION, - CONF_ENABLE_ENHANCED_LIGHT_TRANSITION, - CONF_ENABLE_LIGHT_TRANSITIONING_FLAG, - CONF_GROUP_MEMBERS_ASSUME_STATE, - ZHA_OPTIONS, -) -from zha.application.helpers import async_get_zha_config_value from zha.application.platforms import ( BaseEntity, BaseEntityInfo, @@ -726,11 +717,9 @@ def __init__( self._cancel_refresh_handle: Callable | None = None effect_list = [] - self._zha_config_always_prefer_xy_color_mode = async_get_zha_config_value( - device.gateway.config, - ZHA_OPTIONS, - CONF_ALWAYS_PREFER_XY_COLOR_MODE, - True, + light_options = device.gateway.config.config.light_options + self._zha_config_always_prefer_xy_color_mode = ( + light_options.always_prefer_xy_color_mode ) self._supported_color_modes = {ColorMode.ONOFF} @@ -809,23 +798,12 @@ def __init__( if effect_list: self._effect_list = effect_list - self._zha_config_transition = async_get_zha_config_value( - device.gateway.config, - ZHA_OPTIONS, - CONF_DEFAULT_LIGHT_TRANSITION, - 0, - ) - self._zha_config_enhanced_light_transition = async_get_zha_config_value( - device.gateway.config, - ZHA_OPTIONS, - CONF_ENABLE_ENHANCED_LIGHT_TRANSITION, - False, + self._zha_config_transition = light_options.default_light_transition + self._zha_config_enhanced_light_transition = ( + light_options.enable_enhanced_light_transition ) - self._zha_config_enable_light_transitioning_flag = async_get_zha_config_value( - device.gateway.config, - ZHA_OPTIONS, - CONF_ENABLE_LIGHT_TRANSITIONING_FLAG, - True, + self._zha_config_enable_light_transitioning_flag = ( + light_options.enable_light_transitioning_flag ) self._on_off_cluster_handler.on_event( @@ -1155,29 +1133,16 @@ def __init__(self, group: Group): ClusterHandler ) = group.zigpy_group.endpoint[Identify.cluster_id] self._debounced_member_refresh: Debouncer | None = None - self._zha_config_transition = async_get_zha_config_value( - group.gateway.config, - ZHA_OPTIONS, - CONF_DEFAULT_LIGHT_TRANSITION, - 0, - ) - self._zha_config_enable_light_transitioning_flag = async_get_zha_config_value( - group.gateway.config, - ZHA_OPTIONS, - CONF_ENABLE_LIGHT_TRANSITIONING_FLAG, - True, + light_options = group.gateway.config.config.light_options + self._zha_config_transition = light_options.default_light_transition + self._zha_config_enable_light_transitioning_flag = ( + light_options.enable_light_transitioning_flag ) - self._zha_config_always_prefer_xy_color_mode = async_get_zha_config_value( - group.gateway.config, - ZHA_OPTIONS, - CONF_ALWAYS_PREFER_XY_COLOR_MODE, - True, + self._zha_config_always_prefer_xy_color_mode = ( + light_options.always_prefer_xy_color_mode ) - self._zha_config_group_members_assume_state = async_get_zha_config_value( - group.gateway.config, - ZHA_OPTIONS, - CONF_GROUP_MEMBERS_ASSUME_STATE, - True, + self._zha_config_group_members_assume_state = ( + light_options.group_members_assume_state ) if self._zha_config_group_members_assume_state: self._update_group_from_child_delay = ASSUME_UPDATE_GROUP_FROM_CHILD_DELAY diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index 4ab9165d..c110755b 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -50,11 +50,6 @@ CLUSTER_COMMANDS_SERVER, CLUSTER_TYPE_IN, CLUSTER_TYPE_OUT, - CONF_CONSIDER_UNAVAILABLE_BATTERY, - CONF_CONSIDER_UNAVAILABLE_MAINS, - CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY, - CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS, - CONF_ENABLE_IDENTIFY_ON_JOIN, POWER_BATTERY_OR_UNKNOWN, POWER_MAINS_POWERED, UNKNOWN, @@ -63,9 +58,8 @@ ZHA_CLUSTER_HANDLER_CFG_DONE, ZHA_CLUSTER_HANDLER_MSG, ZHA_EVENT, - ZHA_OPTIONS, ) -from zha.application.helpers import async_get_zha_config_value, convert_to_zcl_values +from zha.application.helpers import convert_to_zcl_values from zha.application.platforms import PlatformEntity, PlatformEntityInfo from zha.event import EventBase from zha.exceptions import ZHAException @@ -220,20 +214,13 @@ def __init__( self._basic_ch: ClusterHandler | None = None self._sw_build_id: int | None = None + device_options = _gateway.config.config.device_options if self.is_mains_powered: - self.consider_unavailable_time: int = async_get_zha_config_value( - self._gateway.config, - ZHA_OPTIONS, - CONF_CONSIDER_UNAVAILABLE_MAINS, - CONF_DEFAULT_CONSIDER_UNAVAILABLE_MAINS, + self.consider_unavailable_time: int = ( + device_options.consider_unavailable_mains ) else: - self.consider_unavailable_time = async_get_zha_config_value( - self._gateway.config, - ZHA_OPTIONS, - CONF_CONSIDER_UNAVAILABLE_BATTERY, - CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY, - ) + self.consider_unavailable_time = device_options.consider_unavailable_battery self._available: bool = self.is_active_coordinator or ( self.last_seen is not None and time.time() - self.last_seen < self.consider_unavailable_time @@ -697,11 +684,8 @@ def extended_device_info(self) -> ExtendedDeviceInfo: async def async_configure(self) -> None: """Configure the device.""" - should_identify = async_get_zha_config_value( - self._gateway.config, - ZHA_OPTIONS, - CONF_ENABLE_IDENTIFY_ON_JOIN, - True, + should_identify = ( + self.gateway.config.config.device_options.enable_identify_on_join ) self.debug("started configuration") await self._zdo_handler.async_configure()