* Start gateway using new zigpy init. Update config entry data import. Use new zigpy startup. Fix config entry import without zha config section. Auto form Zigbee network. * Migrate config entry. * New ZHA config entry flow. Use lightweight probe() method for ZHA config entry validation when available. Failback to old behavior of setting up Zigpy app if radio lib does not provide probing. * Clean ZHA_GW_RADIO * Don't import ZHA device settings. * Update config flow tests. * Filter out empty manufacturer. * Replace port path with an by-id device name. * Rebase cleanup * Use correct mock. * Make lint happy again * Update tests/components/zha/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/zha/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/zha/test_config_flow.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Use executor pool for IO * Address comments. Use AsyncMock from tests. * Use core interface to test config flow. * Use core interface to test config_flow. * Address comments. Use core interface. * Update ZHA dependencies. * Schema guard * Use async_update_entry for migration. * Don't allow schema extra keys. Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
207 lines
6.4 KiB
Python
207 lines
6.4 KiB
Python
"""Test configuration for the ZHA component."""
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
import zigpy
|
|
from zigpy.application import ControllerApplication
|
|
import zigpy.config
|
|
import zigpy.group
|
|
import zigpy.types
|
|
|
|
import homeassistant.components.zha.core.const as zha_const
|
|
import homeassistant.components.zha.core.device as zha_core_device
|
|
import homeassistant.components.zha.core.registries as zha_regs
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from .common import FakeDevice, FakeEndpoint, get_zha_gateway
|
|
|
|
import tests.async_mock
|
|
from tests.common import MockConfigEntry
|
|
|
|
FIXTURE_GRP_ID = 0x1001
|
|
FIXTURE_GRP_NAME = "fixture group"
|
|
|
|
|
|
@pytest.fixture
|
|
def zigpy_app_controller():
|
|
"""Zigpy ApplicationController fixture."""
|
|
app = mock.MagicMock(spec_set=ControllerApplication)
|
|
app.startup = tests.async_mock.AsyncMock()
|
|
app.shutdown = tests.async_mock.AsyncMock()
|
|
groups = zigpy.group.Groups(app)
|
|
groups.add_group(FIXTURE_GRP_ID, FIXTURE_GRP_NAME, suppress_event=True)
|
|
app.configure_mock(groups=groups)
|
|
type(app).ieee = mock.PropertyMock()
|
|
app.ieee.return_value = zigpy.types.EUI64.convert("00:15:8d:00:02:32:4f:32")
|
|
type(app).nwk = mock.PropertyMock(return_value=zigpy.types.NWK(0x0000))
|
|
type(app).devices = mock.PropertyMock(return_value={})
|
|
return app
|
|
|
|
|
|
@pytest.fixture
|
|
def zigpy_radio():
|
|
"""Zigpy radio mock."""
|
|
radio = mock.MagicMock()
|
|
radio.connect = tests.async_mock.AsyncMock()
|
|
return radio
|
|
|
|
|
|
@pytest.fixture(name="config_entry")
|
|
async def config_entry_fixture(hass):
|
|
"""Fixture representing a config entry."""
|
|
entry = MockConfigEntry(
|
|
version=2,
|
|
domain=zha_const.DOMAIN,
|
|
data={
|
|
zigpy.config.CONF_DEVICE: {zigpy.config.CONF_DEVICE_PATH: "/dev/ttyUSB0"},
|
|
zha_const.CONF_RADIO_TYPE: "MockRadio",
|
|
},
|
|
)
|
|
entry.add_to_hass(hass)
|
|
return entry
|
|
|
|
|
|
@pytest.fixture
|
|
def setup_zha(hass, config_entry, zigpy_app_controller, zigpy_radio):
|
|
"""Set up ZHA component."""
|
|
zha_config = {zha_const.CONF_ENABLE_QUIRKS: False}
|
|
app_ctrl = mock.MagicMock()
|
|
app_ctrl.new = tests.async_mock.AsyncMock(return_value=zigpy_app_controller)
|
|
app_ctrl.SCHEMA = zigpy.config.CONFIG_SCHEMA
|
|
app_ctrl.SCHEMA_DEVICE = zigpy.config.SCHEMA_DEVICE
|
|
|
|
radio_details = {
|
|
zha_const.CONTROLLER: app_ctrl,
|
|
zha_const.ZHA_GW_RADIO_DESCRIPTION: "mock radio",
|
|
}
|
|
|
|
async def _setup(config=None):
|
|
config = config or {}
|
|
with mock.patch.dict(zha_regs.RADIO_TYPES, {"MockRadio": radio_details}):
|
|
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 channel():
|
|
"""Channel mock factory fixture."""
|
|
|
|
def channel(name: str, cluster_id: int, endpoint_id: int = 1):
|
|
ch = mock.MagicMock()
|
|
ch.name = name
|
|
ch.generic_id = f"channel_0x{cluster_id:04x}"
|
|
ch.id = f"{endpoint_id}:0x{cluster_id:04x}"
|
|
ch.async_configure = tests.async_mock.AsyncMock()
|
|
ch.async_initialize = tests.async_mock.AsyncMock()
|
|
return ch
|
|
|
|
return channel
|
|
|
|
|
|
@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,
|
|
):
|
|
"""Make a fake device using the specified cluster classes."""
|
|
device = FakeDevice(
|
|
zigpy_app_controller, ieee, manufacturer, model, node_descriptor, nwk=nwk
|
|
)
|
|
for epid, ep in endpoints.items():
|
|
endpoint = FakeEndpoint(manufacturer, model, epid)
|
|
endpoint.device = device
|
|
device.endpoints[epid] = endpoint
|
|
endpoint.device_type = ep["device_type"]
|
|
profile_id = ep.get("profile_id")
|
|
if profile_id:
|
|
endpoint.profile_id = profile_id
|
|
|
|
for cluster_id in ep.get("in_clusters", []):
|
|
endpoint.add_input_cluster(cluster_id)
|
|
|
|
for cluster_id in ep.get("out_clusters", []):
|
|
endpoint.add_output_cluster(cluster_id)
|
|
|
|
return device
|
|
|
|
return _mock_dev
|
|
|
|
|
|
@pytest.fixture
|
|
def zha_device_joined(hass, setup_zha):
|
|
"""Return a newly joined ZHA device."""
|
|
|
|
async def _zha_device(zigpy_dev):
|
|
await setup_zha()
|
|
zha_gateway = get_zha_gateway(hass)
|
|
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
|
|
|
|
|
|
@pytest.fixture
|
|
def zha_device_restored(hass, zigpy_app_controller, setup_zha):
|
|
"""Return a restored ZHA device."""
|
|
|
|
async def _zha_device(zigpy_dev):
|
|
zigpy_app_controller.devices[zigpy_dev.ieee] = zigpy_dev
|
|
await setup_zha()
|
|
zha_gateway = hass.data[zha_const.DATA_ZHA][zha_const.DATA_ZHA_GATEWAY]
|
|
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):
|
|
"""Join or restore ZHA device."""
|
|
named_method = request.getfixturevalue(request.param)
|
|
named_method.name = request.param
|
|
return named_method
|
|
|
|
|
|
@pytest.fixture
|
|
def zha_device_mock(hass, zigpy_device_mock):
|
|
"""Return a zha Device factory."""
|
|
|
|
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",
|
|
):
|
|
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
|
|
)
|
|
zha_device = zha_core_device.ZHADevice(hass, zigpy_device, mock.MagicMock())
|
|
return zha_device
|
|
|
|
return _zha_device
|