core/tests/components/zha/conftest.py

550 lines
17 KiB
Python
Raw Normal View History

"""Test configuration for the ZHA component."""
from collections.abc import Callable, Generator
import itertools
import time
from typing import Any
from unittest.mock import AsyncMock, MagicMock, create_autospec, patch
import warnings
2021-01-01 21:31:56 +00:00
import pytest
import zigpy
from zigpy.application import ControllerApplication
import zigpy.backups
import zigpy.config
from zigpy.const import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
import zigpy.device
import zigpy.group
import zigpy.profiles
2023-04-21 06:24:39 +00:00
import zigpy.quirks
import zigpy.state
import zigpy.types
import zigpy.util
from zigpy.zcl.clusters.general import Basic, Groups
from zigpy.zcl.foundation import Status
import zigpy.zdo.types as zdo_t
import homeassistant.components.zha.core.const as zha_const
ZHA device channel refactoring (#31971) * Add ZHA core typing helper. * Add aux_channels to ZHA rule matching. * Add match rule claim_channels() method. * Expose underlying zigpy device. * Not sure we need this one. * Move "base" channels. * Framework for channel discovery. * Make DEVICE_CLASS and REMOTE_DEVICE_TYPE default dicts. * Remove attribute reporting configuration registry. * Refactor channels. - Refactor zha events - Use compound IDs and unique_ids - Refactor signal dispatching on attribute updates * Use unique id compatible with entities unique ids. * Refactor ZHA Entity registry. Let match rule to check for the match. * Refactor discovery to use new channels. * Cleanup ZDO channel. Remove unused zha store call. * Handle channel configuration and initialization. * Refactor ZHA Device to use new channels. * Refactor ZHA Gateway to use new discovery framework. Use hass.data for entity info intermediate store. * Don't keep entities in hass.data. * ZHA gateway new discovery framework. * Refactor ZHA platform loading. * Don't update ZHA entities, when restoring from zigpy. * ZHA entity discover tests. * Add AnalogInput sensor. * Remove 0xFC02 based entity from Keen smart vents. * Clean up IAS channels. * Refactor entity restoration. * Fix lumi.router entities name. * Rename EndpointsChannel to ChannelPool. * Make Channels.pools a list. * Fix cover test. * Fix FakeDevice class. * Fix device actions. * Fix channels typing. * Revert update_before_add=False * Refactor channel class matching. * Use a helper function for adding entities. * Make Pylint happy. * Rebase cleanup. * Update coverage for ZHA device type overrides. * Use cluster_id for single output cluster registry. * Remove ZHA typing from coverage. * Fix tests. * Address comments. * Address comments.
2020-02-21 23:06:57 +00:00
import homeassistant.components.zha.core.device as zha_core_device
from homeassistant.components.zha.core.gateway import ZHAGateway
from homeassistant.components.zha.core.helpers import get_zha_gateway
from homeassistant.helpers import restore_state
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from .common import patch_cluster as common_patch_cluster
from tests.common import MockConfigEntry
from tests.components.light.conftest import mock_light_profiles # noqa: F401
FIXTURE_GRP_ID = 0x1001
FIXTURE_GRP_NAME = "fixture group"
COUNTER_NAMES = ["counter_1", "counter_2", "counter_3"]
@pytest.fixture(scope="module", autouse=True)
def disable_request_retry_delay():
"""Disable ZHA request retrying delay to speed up failures."""
with patch(
"homeassistant.components.zha.core.cluster_handlers.RETRYABLE_REQUEST_DECORATOR",
zigpy.util.retryable_request(tries=3, delay=0),
):
yield
@pytest.fixture(scope="module", autouse=True)
def globally_load_quirks():
"""Load quirks automatically so that ZHA tests run deterministically in isolation.
If portions of the ZHA test suite that do not happen to load quirks are run
independently, bugs can emerge that will show up only when more of the test suite is
run.
"""
import zhaquirks
zhaquirks.setup()
2023-04-21 06:24:39 +00:00
class _FakeApp(ControllerApplication):
async def add_endpoint(self, descriptor: zdo_t.SimpleDescriptor):
pass
async def connect(self):
pass
async def disconnect(self):
pass
async def force_remove(self, dev: zigpy.device.Device):
pass
async def load_network_info(self, *, load_devices: bool = False):
pass
async def permit_ncp(self, time_s: int = 60):
pass
2023-11-29 10:30:15 +00:00
async def permit_with_link_key(
self, node: zigpy.types.EUI64, link_key: zigpy.types.KeyData, time_s: int = 60
2023-04-21 06:24:39 +00:00
):
pass
async def reset_network_info(self):
pass
async def send_packet(self, packet: zigpy.types.ZigbeePacket):
pass
async def start_network(self):
pass
async def write_network_info(
self, *, network_info: zigpy.state.NetworkInfo, node_info: zigpy.state.NodeInfo
) -> None:
2023-04-21 06:24:39 +00:00
pass
async def request(
self,
device: zigpy.device.Device,
profile: zigpy.types.uint16_t,
cluster: zigpy.types.uint16_t,
src_ep: zigpy.types.uint8_t,
dst_ep: zigpy.types.uint8_t,
sequence: zigpy.types.uint8_t,
data: bytes,
*,
expect_reply: bool = True,
use_ieee: bool = False,
extended_timeout: bool = False,
):
pass
async def move_network_to_channel(
self, new_channel: int, *, num_broadcasts: int = 5
) -> None:
pass
def _wrap_mock_instance(obj: Any) -> MagicMock:
"""Auto-mock every attribute and method in an object."""
mock = create_autospec(obj, spec_set=True, instance=True)
for attr_name in dir(obj):
if attr_name.startswith("__") and attr_name not in {"__getitem__"}:
continue
real_attr = getattr(obj, attr_name)
mock_attr = getattr(mock, attr_name)
if callable(real_attr) and not hasattr(real_attr, "__aenter__"):
mock_attr.side_effect = real_attr
else:
setattr(mock, attr_name, real_attr)
return mock
2023-04-21 06:24:39 +00:00
@pytest.fixture
async def zigpy_app_controller():
"""Zigpy ApplicationController fixture."""
2023-04-21 06:24:39 +00:00
app = _FakeApp(
{
zigpy.config.CONF_DATABASE: None,
zigpy.config.CONF_DEVICE: {zigpy.config.CONF_DEVICE_PATH: "/dev/null"},
zigpy.config.CONF_STARTUP_ENERGY_SCAN: False,
zigpy.config.CONF_NWK_BACKUP_ENABLED: False,
zigpy.config.CONF_TOPO_SCAN_ENABLED: False,
zigpy.config.CONF_OTA: {
Use new zigpy OTA providers for ZHA (#111159) * Use `None` instead of `"unknown"` when the current version is unknown * Only use the current file version from the OTA notification * Use `sw_version`, if available, and update `current_file_version` * Assume the current version is the latest version * Fix lint errors * Use `image` instead of `firmware` * Include a changelog if updates expose it * Clear latest firmware only after updating the installed version * Bump minimum zigpy version to 0.63.0 * Create a data update coordinator to consolidate updates * Fix overridden `async_update` * Fix most unit tests * Simplify `test_devices` to fix current tests * Use a dict comprehension for creating mocked entities * Fix unit tests (thanks @dmulcahey!) * Update the currently installed version on cluster attribute update * Drop `PARALLEL_UPDATES` now that we use an update coordinator * Drop `_reset_progress`, it is already handled by the update component * Do not update the progress if we are not supposed to be updating * Ignore latest version (e.g. if device attrs changed) if zigpy rejects it * Clean up handling of command id in `Ota.cluster_command` * Start progress at 1%: 0 and False are considered equal and are filtered! Use `ceil` instead of remapping 1-100 * The installed version will be auto-updated when the upgrade succeeds * Avoid 1 as well, it collides with `True` * Bump zigpy to (unreleased) 0.63.2 * Fix unit tests * Fix existing unit tests Send both event types Globally enable sending both event types * Remove unnecessary branches * Test ignoring invalid progress callbacks * Test updating a device with a no longer compatible firmware
2024-02-28 19:38:04 +00:00
zigpy.config.CONF_OTA_ENABLED: False,
},
2023-04-21 06:24:39 +00:00
}
)
app.groups.add_group(FIXTURE_GRP_ID, FIXTURE_GRP_NAME, suppress_event=True)
app.state.node_info.nwk = 0x0000
app.state.node_info.ieee = zigpy.types.EUI64.convert("00:15:8d:00:02:32:4f:32")
app.state.network_info.pan_id = 0x1234
app.state.network_info.extended_pan_id = app.state.node_info.ieee
app.state.network_info.channel = 15
app.state.network_info.network_key.key = zigpy.types.KeyData(range(16))
app.state.counters = zigpy.state.CounterGroups()
app.state.counters["ezsp_counters"] = zigpy.state.CounterGroup("ezsp_counters")
for name in COUNTER_NAMES:
app.state.counters["ezsp_counters"][name].increment()
2023-04-21 06:24:39 +00:00
# Create a fake coordinator device
dev = app.add_device(nwk=app.state.node_info.nwk, ieee=app.state.node_info.ieee)
dev.node_desc = zdo_t.NodeDescriptor()
dev.node_desc.logical_type = zdo_t.LogicalType.Coordinator
dev.manufacturer = "Coordinator Manufacturer"
dev.model = "Coordinator Model"
ep = dev.add_endpoint(1)
ep.add_input_cluster(Basic.cluster_id)
ep.add_input_cluster(Groups.cluster_id)
with patch("zigpy.device.Device.request", return_value=[Status.SUCCESS]):
# The mock wrapping accesses deprecated attributes, so we suppress the warnings
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
mock_app = _wrap_mock_instance(app)
mock_app.backups = _wrap_mock_instance(app.backups)
yield mock_app
2019-07-31 19:25:30 +00:00
@pytest.fixture(name="config_entry")
async def config_entry_fixture(hass) -> MockConfigEntry:
"""Fixture representing a config entry."""
return MockConfigEntry(
version=3,
domain=zha_const.DOMAIN,
data={
zigpy.config.CONF_DEVICE: {zigpy.config.CONF_DEVICE_PATH: "/dev/ttyUSB0"},
zha_const.CONF_RADIO_TYPE: "ezsp",
},
options={
zha_const.CUSTOM_CONFIGURATION: {
zha_const.ZHA_OPTIONS: {
zha_const.CONF_ENABLE_ENHANCED_LIGHT_TRANSITION: True,
Implement "group members assume state" option for ZHA (#84938) * Initial "group members assume state" implementation for ZHA * Remove left-over debug flag (where polling was disabled) * Implement _send_member_assume_state_event() method and also use after turn_off * Only assume updated arguments from service call to group * Make code more readable and change checks slightly * Move "send member assume state" events to LightGroup on/off calls * Include new config option in tests * Check that member is available before updating to assumed state * Lower "update group from child delay" for debouncer to basically 0 when using assumed member state * Allow "child to group" updates regardless of config option This is not needed, as group members will not update their state, as long as they're transitioning. (If a group transitions, it also sets its members to transitioning mode) This fixes multiple issues. Previously, the state of a group was completely wrong when: - turn on group with 10 second transition - turn on members individually - turn off members individually - group state would not update correctly * Move "default update group from child delay" constant * Change to new constant name in test * Also update fan test to new constant name * Decrease "update group from child delay" to 10ms In my testing, 0.0 also works without any issues and correctly de-bounces child updates when using the "assume state option". This is just for avoiding multiple state changes when changing the group -> children issue individual updates. With 2 children in a group and delay 0, both child updates only cause one group re-calculation and state change. 0.01 (10ms) should be plenty for very slow systems to de-bounce the update (and in the worst case, it'll cause just another state change but nothing breaks) * Also implement "assuming state" for effect Not sure if anybody even uses this, but this one is a bit special because the effect is always deactivated if it's not provided in the light.turn_on call. * Move shortened delay for "assuming members" to a constant * Add basic test to verify that group members assume on/off state * Move _assume_group_state function declaration out of async_added_to_hass * Fix rare edge-case when rapidly toggling lights and light groups at the same time This prevents an issue where either the group transition would unset the transition flag or the single light would unset the group transition status midst-transition. Note: When a new individual transition is started, we want to unset the group flag, as we actually cancel that transition. * Check that effect list exists, add return type * Re-trigger CI due to timeout * Increase ASSUME_UPDATE_GROUP_FROM_CHILD_DELAY slightly The debouncer is used when updating group member states either by assuming them (in which case we want to barely have any delay), or between the time we get the results back from polling (where we want a slightly longer time). As it's not easily possible to distinguish if a group member was updated via assuming the state of the group or by the polling that follows, 50 ms seems to be a good middle point. * Add debug print for when updating group state * Fix issues with "off brightness" when switching between group/members This fixes a bunch of issues with "off brightness" and passes it down to the members correctly. For example, if a light group is turned off with a transition (so bulbs get their level set to 1), this will also set the "off brightness" of all individual bulbs to the last level that they were at. (It really fixes a lot of issues when using the "member assume group state" option. It's not really possible to fix them without that.) Furthermore, issues where polling was previously needed to get the correct state after "playing with transitions", should now get be resolved and get correct state when using the "members assume group state" option. Note: The only case which still can't be fixed is the following: If individual lights have off_with_transition set, but not the group, and the group is then turned on without a level, individual lights might fall back to brightness level 1 (<- at least now shows correctly in UI even before polling). Since all lights might need different brightness levels to be turned on, we can't use one group call. But making individual calls when turning on a ZHA group would cause a lot of traffic and thereby be counter-productive. In this case, light.turn_on should just be called with a level (or individual calls to the lights should be made). Another thing that was changed is to reset off_with_transition/off_brightness for a LightGroup when a member is turned on (even if the LightGroup wasn't turned on using its turn_on method). off_with_transition/off_brightness for individual bulbs is now also turned off when a light is detected to be on during polling. Lastly, the waiting for polled attributes could previously cause "invalid state" to be set (so mid-transition levels). This could happen when group and members are repeatedly toggled at similar times. These "invalid states" could cause wrong "off brightness" levels if transitions are also used. To fix this, we check after waiting for the polled attributes in async_get_state to see if a transition has started in the meanwhile. If so, the values can be discarded. A new poll will happen later and if using the "members assume group state" config option, the values should already be correct before the polling. * Enable "group members assume state" config option by default The config tests are also updated to expect the config option be enabled by default. For all tests, the config option is generally disabled though: There are only two group related tests. The one that tests this new feature overrides the config option to be enabled anyway. The other tests works in a similar way but also "sends" attribute reports, so we want to disable the feature for that test. (It would also run with it enabled (if the correct CHILD_UPDATE value is patched), but then it would test the same stuff as the other test, hence we're disabling the config option for that test.)
2023-01-16 15:48:18 +00:00
zha_const.CONF_GROUP_MEMBERS_ASSUME_STATE: False,
},
zha_const.ZHA_ALARM_OPTIONS: {
zha_const.CONF_ALARM_ARM_REQUIRES_CODE: False,
zha_const.CONF_ALARM_MASTER_CODE: "4321",
zha_const.CONF_ALARM_FAILED_TRIES: 2,
},
}
},
)
@pytest.fixture
def mock_zigpy_connect(
zigpy_app_controller: ControllerApplication,
) -> Generator[ControllerApplication, None, None]:
"""Patch the zigpy radio connection with our mock application."""
with (
patch(
"bellows.zigbee.application.ControllerApplication.new",
return_value=zigpy_app_controller,
),
patch(
"bellows.zigbee.application.ControllerApplication",
return_value=zigpy_app_controller,
),
):
yield zigpy_app_controller
@pytest.fixture
def setup_zha(
hass, config_entry: MockConfigEntry, mock_zigpy_connect: ControllerApplication
):
"""Set up ZHA component."""
zha_config = {zha_const.CONF_ENABLE_QUIRKS: False}
ZHA device channel refactoring (#31971) * Add ZHA core typing helper. * Add aux_channels to ZHA rule matching. * Add match rule claim_channels() method. * Expose underlying zigpy device. * Not sure we need this one. * Move "base" channels. * Framework for channel discovery. * Make DEVICE_CLASS and REMOTE_DEVICE_TYPE default dicts. * Remove attribute reporting configuration registry. * Refactor channels. - Refactor zha events - Use compound IDs and unique_ids - Refactor signal dispatching on attribute updates * Use unique id compatible with entities unique ids. * Refactor ZHA Entity registry. Let match rule to check for the match. * Refactor discovery to use new channels. * Cleanup ZDO channel. Remove unused zha store call. * Handle channel configuration and initialization. * Refactor ZHA Device to use new channels. * Refactor ZHA Gateway to use new discovery framework. Use hass.data for entity info intermediate store. * Don't keep entities in hass.data. * ZHA gateway new discovery framework. * Refactor ZHA platform loading. * Don't update ZHA entities, when restoring from zigpy. * ZHA entity discover tests. * Add AnalogInput sensor. * Remove 0xFC02 based entity from Keen smart vents. * Clean up IAS channels. * Refactor entity restoration. * Fix lumi.router entities name. * Rename EndpointsChannel to ChannelPool. * Make Channels.pools a list. * Fix cover test. * Fix FakeDevice class. * Fix device actions. * Fix channels typing. * Revert update_before_add=False * Refactor channel class matching. * Use a helper function for adding entities. * Make Pylint happy. * Rebase cleanup. * Update coverage for ZHA device type overrides. * Use cluster_id for single output cluster registry. * Remove ZHA typing from coverage. * Fix tests. * Address comments. * Address comments.
2020-02-21 23:06:57 +00:00
async def _setup(config=None):
config_entry.add_to_hass(hass)
ZHA device channel refactoring (#31971) * Add ZHA core typing helper. * Add aux_channels to ZHA rule matching. * Add match rule claim_channels() method. * Expose underlying zigpy device. * Not sure we need this one. * Move "base" channels. * Framework for channel discovery. * Make DEVICE_CLASS and REMOTE_DEVICE_TYPE default dicts. * Remove attribute reporting configuration registry. * Refactor channels. - Refactor zha events - Use compound IDs and unique_ids - Refactor signal dispatching on attribute updates * Use unique id compatible with entities unique ids. * Refactor ZHA Entity registry. Let match rule to check for the match. * Refactor discovery to use new channels. * Cleanup ZDO channel. Remove unused zha store call. * Handle channel configuration and initialization. * Refactor ZHA Device to use new channels. * Refactor ZHA Gateway to use new discovery framework. Use hass.data for entity info intermediate store. * Don't keep entities in hass.data. * ZHA gateway new discovery framework. * Refactor ZHA platform loading. * Don't update ZHA entities, when restoring from zigpy. * ZHA entity discover tests. * Add AnalogInput sensor. * Remove 0xFC02 based entity from Keen smart vents. * Clean up IAS channels. * Refactor entity restoration. * Fix lumi.router entities name. * Rename EndpointsChannel to ChannelPool. * Make Channels.pools a list. * Fix cover test. * Fix FakeDevice class. * Fix device actions. * Fix channels typing. * Revert update_before_add=False * Refactor channel class matching. * Use a helper function for adding entities. * Make Pylint happy. * Rebase cleanup. * Update coverage for ZHA device type overrides. * Use cluster_id for single output cluster registry. * Remove ZHA typing from coverage. * Fix tests. * Address comments. * Address comments.
2020-02-21 23:06:57 +00:00
config = config or {}
status = await async_setup_component(
hass, zha_const.DOMAIN, {zha_const.DOMAIN: {**zha_config, **config}}
)
assert status is True
await hass.async_block_till_done()
return _setup
@pytest.fixture
def cluster_handler():
"""ClusterHandler mock factory fixture."""
def cluster_handler(name: str, cluster_id: int, endpoint_id: int = 1):
ch = MagicMock()
ch.name = name
ch.generic_id = f"cluster_handler_0x{cluster_id:04x}"
ch.id = f"{endpoint_id}:0x{cluster_id:04x}"
ch.async_configure = AsyncMock()
ch.async_initialize = AsyncMock()
return ch
return cluster_handler
@pytest.fixture
def zigpy_device_mock(zigpy_app_controller):
"""Make a fake device using the specified cluster classes."""
def _mock_dev(
endpoints,
ieee="00:0d:6f:00:0a:90:69:e7",
manufacturer="FakeManufacturer",
model="FakeModel",
node_descriptor=b"\x02@\x807\x10\x7fd\x00\x00*d\x00\x00",
nwk=0xB79C,
patch_cluster=True,
quirk=None,
attributes=None,
):
"""Make a fake device using the specified cluster classes."""
device = zigpy.device.Device(
zigpy_app_controller, zigpy.types.EUI64.convert(ieee), nwk
)
device.manufacturer = manufacturer
device.model = model
device.node_desc = zdo_t.NodeDescriptor.deserialize(node_descriptor)[0]
device.last_seen = time.time()
for epid, ep in endpoints.items():
endpoint = device.add_endpoint(epid)
endpoint.device_type = ep[SIG_EP_TYPE]
endpoint.profile_id = ep.get(SIG_EP_PROFILE, 0x0104)
2023-04-21 06:24:39 +00:00
endpoint.request = AsyncMock()
for cluster_id in ep.get(SIG_EP_INPUT, []):
endpoint.add_input_cluster(cluster_id)
for cluster_id in ep.get(SIG_EP_OUTPUT, []):
endpoint.add_output_cluster(cluster_id)
device.status = zigpy.device.Status.ENDPOINTS_INIT
if quirk:
device = quirk(zigpy_app_controller, device.ieee, device.nwk, device)
2023-04-21 06:24:39 +00:00
else:
# Allow zigpy to apply quirks if we don't pass one explicitly
device = zigpy.quirks.get_device(device)
if patch_cluster:
for endpoint in (ep for epid, ep in device.endpoints.items() if epid):
endpoint.request = AsyncMock(return_value=[0])
for cluster in itertools.chain(
endpoint.in_clusters.values(), endpoint.out_clusters.values()
):
common_patch_cluster(cluster)
if attributes is not None:
for ep_id, clusters in attributes.items():
for cluster_name, attrs in clusters.items():
cluster = getattr(device.endpoints[ep_id], cluster_name)
for name, value in attrs.items():
attr_id = cluster.find_attribute(name).id
cluster._attr_cache[attr_id] = value
return device
return _mock_dev
@patch("homeassistant.components.zha.setup_quirks", MagicMock(return_value=True))
@pytest.fixture
def zha_device_joined(hass, setup_zha):
"""Return a newly joined ZHA device."""
setup_zha_fixture = setup_zha
async def _zha_device(zigpy_dev, *, setup_zha: bool = True):
zigpy_dev.last_seen = time.time()
if setup_zha:
await setup_zha_fixture()
zha_gateway = get_zha_gateway(hass)
zha_gateway.application_controller.devices[zigpy_dev.ieee] = zigpy_dev
await zha_gateway.async_device_initialized(zigpy_dev)
await hass.async_block_till_done()
return zha_gateway.get_device(zigpy_dev.ieee)
return _zha_device
@patch("homeassistant.components.zha.setup_quirks", MagicMock(return_value=True))
@pytest.fixture
def zha_device_restored(hass, zigpy_app_controller, setup_zha):
"""Return a restored ZHA device."""
setup_zha_fixture = setup_zha
async def _zha_device(zigpy_dev, *, last_seen=None, setup_zha: bool = True):
zigpy_app_controller.devices[zigpy_dev.ieee] = zigpy_dev
if last_seen is not None:
zigpy_dev.last_seen = last_seen
if setup_zha:
await setup_zha_fixture()
zha_gateway = get_zha_gateway(hass)
return zha_gateway.get_device(zigpy_dev.ieee)
return _zha_device
@pytest.fixture(params=["zha_device_joined", "zha_device_restored"])
def zha_device_joined_restored(request: pytest.FixtureRequest):
"""Join or restore ZHA device."""
named_method = request.getfixturevalue(request.param)
named_method.name = request.param
return named_method
ZHA device channel refactoring (#31971) * Add ZHA core typing helper. * Add aux_channels to ZHA rule matching. * Add match rule claim_channels() method. * Expose underlying zigpy device. * Not sure we need this one. * Move "base" channels. * Framework for channel discovery. * Make DEVICE_CLASS and REMOTE_DEVICE_TYPE default dicts. * Remove attribute reporting configuration registry. * Refactor channels. - Refactor zha events - Use compound IDs and unique_ids - Refactor signal dispatching on attribute updates * Use unique id compatible with entities unique ids. * Refactor ZHA Entity registry. Let match rule to check for the match. * Refactor discovery to use new channels. * Cleanup ZDO channel. Remove unused zha store call. * Handle channel configuration and initialization. * Refactor ZHA Device to use new channels. * Refactor ZHA Gateway to use new discovery framework. Use hass.data for entity info intermediate store. * Don't keep entities in hass.data. * ZHA gateway new discovery framework. * Refactor ZHA platform loading. * Don't update ZHA entities, when restoring from zigpy. * ZHA entity discover tests. * Add AnalogInput sensor. * Remove 0xFC02 based entity from Keen smart vents. * Clean up IAS channels. * Refactor entity restoration. * Fix lumi.router entities name. * Rename EndpointsChannel to ChannelPool. * Make Channels.pools a list. * Fix cover test. * Fix FakeDevice class. * Fix device actions. * Fix channels typing. * Revert update_before_add=False * Refactor channel class matching. * Use a helper function for adding entities. * Make Pylint happy. * Rebase cleanup. * Update coverage for ZHA device type overrides. * Use cluster_id for single output cluster registry. * Remove ZHA typing from coverage. * Fix tests. * Address comments. * Address comments.
2020-02-21 23:06:57 +00:00
@pytest.fixture
def zha_device_mock(
hass, config_entry, zigpy_device_mock
) -> Callable[..., zha_core_device.ZHADevice]:
"""Return a ZHA Device factory."""
ZHA device channel refactoring (#31971) * Add ZHA core typing helper. * Add aux_channels to ZHA rule matching. * Add match rule claim_channels() method. * Expose underlying zigpy device. * Not sure we need this one. * Move "base" channels. * Framework for channel discovery. * Make DEVICE_CLASS and REMOTE_DEVICE_TYPE default dicts. * Remove attribute reporting configuration registry. * Refactor channels. - Refactor zha events - Use compound IDs and unique_ids - Refactor signal dispatching on attribute updates * Use unique id compatible with entities unique ids. * Refactor ZHA Entity registry. Let match rule to check for the match. * Refactor discovery to use new channels. * Cleanup ZDO channel. Remove unused zha store call. * Handle channel configuration and initialization. * Refactor ZHA Device to use new channels. * Refactor ZHA Gateway to use new discovery framework. Use hass.data for entity info intermediate store. * Don't keep entities in hass.data. * ZHA gateway new discovery framework. * Refactor ZHA platform loading. * Don't update ZHA entities, when restoring from zigpy. * ZHA entity discover tests. * Add AnalogInput sensor. * Remove 0xFC02 based entity from Keen smart vents. * Clean up IAS channels. * Refactor entity restoration. * Fix lumi.router entities name. * Rename EndpointsChannel to ChannelPool. * Make Channels.pools a list. * Fix cover test. * Fix FakeDevice class. * Fix device actions. * Fix channels typing. * Revert update_before_add=False * Refactor channel class matching. * Use a helper function for adding entities. * Make Pylint happy. * Rebase cleanup. * Update coverage for ZHA device type overrides. * Use cluster_id for single output cluster registry. * Remove ZHA typing from coverage. * Fix tests. * Address comments. * Address comments.
2020-02-21 23:06:57 +00:00
def _zha_device(
endpoints=None,
ieee="00:11:22:33:44:55:66:77",
manufacturer="mock manufacturer",
model="mock model",
node_desc=b"\x02@\x807\x10\x7fd\x00\x00*d\x00\x00",
patch_cluster=True,
) -> zha_core_device.ZHADevice:
ZHA device channel refactoring (#31971) * Add ZHA core typing helper. * Add aux_channels to ZHA rule matching. * Add match rule claim_channels() method. * Expose underlying zigpy device. * Not sure we need this one. * Move "base" channels. * Framework for channel discovery. * Make DEVICE_CLASS and REMOTE_DEVICE_TYPE default dicts. * Remove attribute reporting configuration registry. * Refactor channels. - Refactor zha events - Use compound IDs and unique_ids - Refactor signal dispatching on attribute updates * Use unique id compatible with entities unique ids. * Refactor ZHA Entity registry. Let match rule to check for the match. * Refactor discovery to use new channels. * Cleanup ZDO channel. Remove unused zha store call. * Handle channel configuration and initialization. * Refactor ZHA Device to use new channels. * Refactor ZHA Gateway to use new discovery framework. Use hass.data for entity info intermediate store. * Don't keep entities in hass.data. * ZHA gateway new discovery framework. * Refactor ZHA platform loading. * Don't update ZHA entities, when restoring from zigpy. * ZHA entity discover tests. * Add AnalogInput sensor. * Remove 0xFC02 based entity from Keen smart vents. * Clean up IAS channels. * Refactor entity restoration. * Fix lumi.router entities name. * Rename EndpointsChannel to ChannelPool. * Make Channels.pools a list. * Fix cover test. * Fix FakeDevice class. * Fix device actions. * Fix channels typing. * Revert update_before_add=False * Refactor channel class matching. * Use a helper function for adding entities. * Make Pylint happy. * Rebase cleanup. * Update coverage for ZHA device type overrides. * Use cluster_id for single output cluster registry. * Remove ZHA typing from coverage. * Fix tests. * Address comments. * Address comments.
2020-02-21 23:06:57 +00:00
if endpoints is None:
endpoints = {
1: {
"in_clusters": [0, 1, 8, 768],
"out_clusters": [0x19],
"device_type": 0x0105,
},
2: {
"in_clusters": [0],
"out_clusters": [6, 8, 0x19, 768],
"device_type": 0x0810,
},
}
zigpy_device = zigpy_device_mock(
endpoints, ieee, manufacturer, model, node_desc, patch_cluster=patch_cluster
ZHA device channel refactoring (#31971) * Add ZHA core typing helper. * Add aux_channels to ZHA rule matching. * Add match rule claim_channels() method. * Expose underlying zigpy device. * Not sure we need this one. * Move "base" channels. * Framework for channel discovery. * Make DEVICE_CLASS and REMOTE_DEVICE_TYPE default dicts. * Remove attribute reporting configuration registry. * Refactor channels. - Refactor zha events - Use compound IDs and unique_ids - Refactor signal dispatching on attribute updates * Use unique id compatible with entities unique ids. * Refactor ZHA Entity registry. Let match rule to check for the match. * Refactor discovery to use new channels. * Cleanup ZDO channel. Remove unused zha store call. * Handle channel configuration and initialization. * Refactor ZHA Device to use new channels. * Refactor ZHA Gateway to use new discovery framework. Use hass.data for entity info intermediate store. * Don't keep entities in hass.data. * ZHA gateway new discovery framework. * Refactor ZHA platform loading. * Don't update ZHA entities, when restoring from zigpy. * ZHA entity discover tests. * Add AnalogInput sensor. * Remove 0xFC02 based entity from Keen smart vents. * Clean up IAS channels. * Refactor entity restoration. * Fix lumi.router entities name. * Rename EndpointsChannel to ChannelPool. * Make Channels.pools a list. * Fix cover test. * Fix FakeDevice class. * Fix device actions. * Fix channels typing. * Revert update_before_add=False * Refactor channel class matching. * Use a helper function for adding entities. * Make Pylint happy. * Rebase cleanup. * Update coverage for ZHA device type overrides. * Use cluster_id for single output cluster registry. * Remove ZHA typing from coverage. * Fix tests. * Address comments. * Address comments.
2020-02-21 23:06:57 +00:00
)
return zha_core_device.ZHADevice(
hass,
zigpy_device,
ZHAGateway(hass, {}, config_entry),
)
ZHA device channel refactoring (#31971) * Add ZHA core typing helper. * Add aux_channels to ZHA rule matching. * Add match rule claim_channels() method. * Expose underlying zigpy device. * Not sure we need this one. * Move "base" channels. * Framework for channel discovery. * Make DEVICE_CLASS and REMOTE_DEVICE_TYPE default dicts. * Remove attribute reporting configuration registry. * Refactor channels. - Refactor zha events - Use compound IDs and unique_ids - Refactor signal dispatching on attribute updates * Use unique id compatible with entities unique ids. * Refactor ZHA Entity registry. Let match rule to check for the match. * Refactor discovery to use new channels. * Cleanup ZDO channel. Remove unused zha store call. * Handle channel configuration and initialization. * Refactor ZHA Device to use new channels. * Refactor ZHA Gateway to use new discovery framework. Use hass.data for entity info intermediate store. * Don't keep entities in hass.data. * ZHA gateway new discovery framework. * Refactor ZHA platform loading. * Don't update ZHA entities, when restoring from zigpy. * ZHA entity discover tests. * Add AnalogInput sensor. * Remove 0xFC02 based entity from Keen smart vents. * Clean up IAS channels. * Refactor entity restoration. * Fix lumi.router entities name. * Rename EndpointsChannel to ChannelPool. * Make Channels.pools a list. * Fix cover test. * Fix FakeDevice class. * Fix device actions. * Fix channels typing. * Revert update_before_add=False * Refactor channel class matching. * Use a helper function for adding entities. * Make Pylint happy. * Rebase cleanup. * Update coverage for ZHA device type overrides. * Use cluster_id for single output cluster registry. * Remove ZHA typing from coverage. * Fix tests. * Address comments. * Address comments.
2020-02-21 23:06:57 +00:00
return _zha_device
@pytest.fixture
def hass_disable_services(hass):
"""Mock services."""
with patch.object(
hass, "services", MagicMock(has_service=MagicMock(return_value=True))
):
yield hass
@pytest.fixture(autouse=True)
def speed_up_radio_mgr():
"""Speed up the radio manager connection time by removing delays."""
with patch("homeassistant.components.zha.radio_manager.CONNECT_DELAY_S", 0.00001):
yield
@pytest.fixture
def network_backup() -> zigpy.backups.NetworkBackup:
"""Real ZHA network backup taken from an active instance."""
return zigpy.backups.NetworkBackup.from_dict(
{
"backup_time": "2022-11-16T03:16:49.427675+00:00",
"network_info": {
"extended_pan_id": "2f:73:58:bd:fe:78:91:11",
"pan_id": "2DB4",
"nwk_update_id": 0,
"nwk_manager_id": "0000",
"channel": 15,
"channel_mask": [
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
],
"security_level": 5,
"network_key": {
"key": "4a:c7:9d:50:51:09:16:37:2e:34:66:c6:ed:9b:23:85",
"tx_counter": 14131,
"rx_counter": 0,
"seq": 0,
"partner_ieee": "ff:ff:ff:ff:ff:ff:ff:ff",
},
"tc_link_key": {
"key": "5a:69:67:42:65:65:41:6c:6c:69:61:6e:63:65:30:39",
"tx_counter": 0,
"rx_counter": 0,
"seq": 0,
"partner_ieee": "84:ba:20:ff:fe:59:f5:ff",
},
"key_table": [],
"children": [],
"nwk_addresses": {"cc:cc:cc:ff:fe:e6:8e:ca": "1431"},
"stack_specific": {
"ezsp": {"hashed_tclk": "e9bd3ac165233d95923613c608beb147"}
},
"metadata": {
"ezsp": {
"manufacturer": "",
"board": "",
"version": "7.1.3.0 build 0",
"stack_version": 9,
"can_write_custom_eui64": False,
}
},
"source": "bellows@0.34.2",
},
"node_info": {
"nwk": "0000",
"ieee": "84:ba:20:ff:fe:59:f5:ff",
"logical_type": "coordinator",
},
}
)
@pytest.fixture
def core_rs(hass_storage: dict[str, Any]):
"""Core.restore_state fixture."""
def _storage(entity_id, state, attributes={}):
now = dt_util.utcnow().isoformat()
hass_storage[restore_state.STORAGE_KEY] = {
"version": restore_state.STORAGE_VERSION,
"key": restore_state.STORAGE_KEY,
"data": [
{
"state": {
"entity_id": entity_id,
"state": str(state),
"attributes": attributes,
"last_changed": now,
"last_updated": now,
"context": {
"id": "3c2243ff5f30447eb12e7348cfd5b8ff",
"user_id": None,
},
},
"last_seen": now,
}
],
}
return _storage