Refactor ZHA tests (#55844)

* Replace ZHA tests FakeDevice

* Refactor ZHA tests to use zigpy devices and endpoints

* Use common consts for zigpy device mocks

Use the same dict key names for device signature mocks as zha quirks.

* Use const for test device list

* Update tests/components/zha/common.py
This commit is contained in:
Alexei Chetroi 2021-09-06 19:00:06 -04:00 committed by GitHub
parent b1dbdec2ea
commit c6888e4faf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 2614 additions and 2592 deletions

View File

@ -503,7 +503,7 @@ class ZHADevice(LogMixin):
names.append( names.append(
{ {
ATTR_NAME: f"unknown {endpoint.device_type} device_type " ATTR_NAME: f"unknown {endpoint.device_type} device_type "
f"of 0x{endpoint.profile_id:04x} profile id" f"of 0x{(endpoint.profile_id or 0xFFFF):04x} profile id"
} }
) )
device_info[ATTR_ENDPOINT_NAMES] = names device_info[ATTR_ENDPOINT_NAMES] = names

View File

@ -1,75 +1,14 @@
"""Common test objects.""" """Common test objects."""
import asyncio import asyncio
import math import math
import time
from unittest.mock import AsyncMock, Mock from unittest.mock import AsyncMock, Mock
import zigpy.device as zigpy_dev
import zigpy.endpoint as zigpy_ep
import zigpy.profiles.zha
import zigpy.types
import zigpy.zcl
import zigpy.zcl.clusters.general
import zigpy.zcl.foundation as zcl_f import zigpy.zcl.foundation as zcl_f
import zigpy.zdo.types
import homeassistant.components.zha.core.const as zha_const import homeassistant.components.zha.core.const as zha_const
from homeassistant.util import slugify from homeassistant.util import slugify
class FakeEndpoint:
"""Fake endpoint for moking zigpy."""
def __init__(self, manufacturer, model, epid=1):
"""Init fake endpoint."""
self.device = None
self.endpoint_id = epid
self.in_clusters = {}
self.out_clusters = {}
self._cluster_attr = {}
self.member_of = {}
self.status = zigpy_ep.Status.ZDO_INIT
self.manufacturer = manufacturer
self.model = model
self.profile_id = zigpy.profiles.zha.PROFILE_ID
self.device_type = None
self.request = AsyncMock(return_value=[0])
def add_input_cluster(self, cluster_id, _patch_cluster=True):
"""Add an input cluster."""
cluster = zigpy.zcl.Cluster.from_id(self, cluster_id, is_server=True)
if _patch_cluster:
patch_cluster(cluster)
self.in_clusters[cluster_id] = cluster
ep_attribute = cluster.ep_attribute
if ep_attribute:
setattr(self, ep_attribute, cluster)
def add_output_cluster(self, cluster_id, _patch_cluster=True):
"""Add an output cluster."""
cluster = zigpy.zcl.Cluster.from_id(self, cluster_id, is_server=False)
if _patch_cluster:
patch_cluster(cluster)
self.out_clusters[cluster_id] = cluster
reply = AsyncMock(return_value=[0])
request = AsyncMock(return_value=[0])
@property
def __class__(self):
"""Fake being Zigpy endpoint."""
return zigpy_ep.Endpoint
@property
def unique_id(self):
"""Return the unique id for the endpoint."""
return self.device.ieee, self.endpoint_id
FakeEndpoint.add_to_group = zigpy_ep.Endpoint.add_to_group
FakeEndpoint.remove_from_group = zigpy_ep.Endpoint.remove_from_group
def patch_cluster(cluster): def patch_cluster(cluster):
"""Patch a cluster for testing.""" """Patch a cluster for testing."""
cluster.PLUGGED_ATTR_READS = {} cluster.PLUGGED_ATTR_READS = {}
@ -115,35 +54,6 @@ def patch_cluster(cluster):
cluster.add = AsyncMock(return_value=[0]) cluster.add = AsyncMock(return_value=[0])
class FakeDevice:
"""Fake device for mocking zigpy."""
def __init__(self, app, ieee, manufacturer, model, node_desc=None, nwk=0xB79C):
"""Init fake device."""
self._application = app
self.application = app
self.ieee = zigpy.types.EUI64.convert(ieee)
self.nwk = nwk
self.zdo = Mock()
self.endpoints = {0: self.zdo}
self.lqi = 255
self.rssi = 8
self.last_seen = time.time()
self.status = zigpy_dev.Status.ENDPOINTS_INIT
self.initializing = False
self.skip_configuration = False
self.manufacturer = manufacturer
self.model = model
self.remove_from_group = AsyncMock()
if node_desc is None:
node_desc = b"\x02@\x807\x10\x7fd\x00\x00*d\x00\x00"
self.node_desc = zigpy.zdo.types.NodeDescriptor.deserialize(node_desc)[0]
self.neighbors = []
FakeDevice.add_to_group = zigpy_dev.Device.add_to_group
def get_zha_gateway(hass): def get_zha_gateway(hass):
"""Return ZHA gateway from hass.data.""" """Return ZHA gateway from hass.data."""
try: try:

View File

@ -1,22 +1,26 @@
"""Test configuration for the ZHA component.""" """Test configuration for the ZHA component."""
import time
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
import pytest import pytest
import zigpy import zigpy
from zigpy.application import ControllerApplication from zigpy.application import ControllerApplication
import zigpy.config 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.group
import zigpy.profiles
import zigpy.types import zigpy.types
import zigpy.zdo.types as zdo_t
from homeassistant.components.zha import DOMAIN from homeassistant.components.zha import DOMAIN
import homeassistant.components.zha.core.const as zha_const import homeassistant.components.zha.core.const as zha_const
import homeassistant.components.zha.core.device as zha_core_device import homeassistant.components.zha.core.device as zha_core_device
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from .common import FakeDevice, FakeEndpoint, get_zha_gateway
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.components.light.conftest import mock_light_profiles # noqa: F401 from tests.components.light.conftest import mock_light_profiles # noqa: F401
from tests.components.zha import common
FIXTURE_GRP_ID = 0x1001 FIXTURE_GRP_ID = 0x1001
FIXTURE_GRP_NAME = "fixture group" FIXTURE_GRP_NAME = "fixture group"
@ -114,23 +118,29 @@ def zigpy_device_mock(zigpy_app_controller):
patch_cluster=True, patch_cluster=True,
): ):
"""Make a fake device using the specified cluster classes.""" """Make a fake device using the specified cluster classes."""
device = FakeDevice( device = zigpy.device.Device(
zigpy_app_controller, ieee, manufacturer, model, node_descriptor, nwk=nwk 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(): for epid, ep in endpoints.items():
endpoint = FakeEndpoint(manufacturer, model, epid) endpoint = device.add_endpoint(epid)
endpoint.device = device endpoint.device_type = ep[SIG_EP_TYPE]
device.endpoints[epid] = endpoint endpoint.profile_id = ep.get(SIG_EP_PROFILE)
endpoint.device_type = ep["device_type"] endpoint.request = AsyncMock(return_value=[0])
profile_id = ep.get("profile_id")
if profile_id:
endpoint.profile_id = profile_id
for cluster_id in ep.get("in_clusters", []): for cluster_id in ep.get(SIG_EP_INPUT, []):
endpoint.add_input_cluster(cluster_id, _patch_cluster=patch_cluster) cluster = endpoint.add_input_cluster(cluster_id)
if patch_cluster:
common.patch_cluster(cluster)
for cluster_id in ep.get("out_clusters", []): for cluster_id in ep.get(SIG_EP_OUTPUT, []):
endpoint.add_output_cluster(cluster_id, _patch_cluster=patch_cluster) cluster = endpoint.add_output_cluster(cluster_id)
if patch_cluster:
common.patch_cluster(cluster)
return device return device
@ -143,7 +153,7 @@ def zha_device_joined(hass, setup_zha):
async def _zha_device(zigpy_dev): async def _zha_device(zigpy_dev):
await setup_zha() await setup_zha()
zha_gateway = get_zha_gateway(hass) zha_gateway = common.get_zha_gateway(hass)
await zha_gateway.async_device_initialized(zigpy_dev) await zha_gateway.async_device_initialized(zigpy_dev)
await hass.async_block_till_done() await hass.async_block_till_done()
return zha_gateway.get_device(zigpy_dev.ieee) return zha_gateway.get_device(zigpy_dev.ieee)

View File

@ -18,6 +18,7 @@ from homeassistant.const import (
) )
from .common import async_enable_traffic, find_entity_id from .common import async_enable_traffic, find_entity_id
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
@pytest.fixture @pytest.fixture
@ -25,9 +26,10 @@ def zigpy_device(zigpy_device_mock):
"""Device tracker zigpy device.""" """Device tracker zigpy device."""
endpoints = { endpoints = {
1: { 1: {
"in_clusters": [security.IasAce.cluster_id], SIG_EP_INPUT: [security.IasAce.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.IAS_ANCILLARY_CONTROL, SIG_EP_TYPE: zha.DeviceType.IAS_ANCILLARY_CONTROL,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
} }
return zigpy_device_mock( return zigpy_device_mock(

View File

@ -40,7 +40,14 @@ from homeassistant.components.zha.core.const import (
from homeassistant.const import ATTR_NAME from homeassistant.const import ATTR_NAME
from homeassistant.core import Context from homeassistant.core import Context
from .conftest import FIXTURE_GRP_ID, FIXTURE_GRP_NAME from .conftest import (
FIXTURE_GRP_ID,
FIXTURE_GRP_NAME,
SIG_EP_INPUT,
SIG_EP_OUTPUT,
SIG_EP_PROFILE,
SIG_EP_TYPE,
)
IEEE_SWITCH_DEVICE = "01:2d:6f:00:0a:90:69:e7" IEEE_SWITCH_DEVICE = "01:2d:6f:00:0a:90:69:e7"
IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8" IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8"
@ -53,9 +60,10 @@ async def device_switch(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [general.OnOff.cluster_id, general.Basic.cluster_id], SIG_EP_INPUT: [general.OnOff.cluster_id, general.Basic.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
} }
}, },
ieee=IEEE_SWITCH_DEVICE, ieee=IEEE_SWITCH_DEVICE,
@ -72,13 +80,14 @@ async def device_groupable(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.OnOff.cluster_id, general.OnOff.cluster_id,
general.Basic.cluster_id, general.Basic.cluster_id,
general.Groups.cluster_id, general.Groups.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
} }
}, },
ieee=IEEE_GROUPABLE_DEVICE, ieee=IEEE_GROUPABLE_DEVICE,

View File

@ -13,21 +13,24 @@ from .common import (
find_entity_id, find_entity_id,
send_attributes_report, send_attributes_report,
) )
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
DEVICE_IAS = { DEVICE_IAS = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.IAS_ZONE, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [security.IasZone.cluster_id], SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.IAS_ZONE,
"out_clusters": [], SIG_EP_INPUT: [security.IasZone.cluster_id],
SIG_EP_OUTPUT: [],
} }
} }
DEVICE_OCCUPANCY = { DEVICE_OCCUPANCY = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.OCCUPANCY_SENSOR, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [measurement.OccupancySensing.cluster_id], SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.OCCUPANCY_SENSOR,
"out_clusters": [], SIG_EP_INPUT: [measurement.OccupancySensing.cluster_id],
SIG_EP_OUTPUT: [],
} }
} }

View File

@ -15,6 +15,7 @@ import homeassistant.components.zha.core.const as zha_const
import homeassistant.components.zha.core.registries as registries import homeassistant.components.zha.core.registries as registries
from .common import get_zha_gateway, make_zcl_header from .common import get_zha_gateway, make_zcl_header
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE
from tests.common import async_capture_events from tests.common import async_capture_events
@ -43,7 +44,7 @@ def zigpy_coordinator_device(zigpy_device_mock):
"""Coordinator device fixture.""" """Coordinator device fixture."""
coordinator = zigpy_device_mock( coordinator = zigpy_device_mock(
{1: {"in_clusters": [0x1000], "out_clusters": [], "device_type": 0x1234}}, {1: {SIG_EP_INPUT: [0x1000], SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x1234}},
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",
"test manufacturer", "test manufacturer",
"test model", "test model",
@ -69,7 +70,7 @@ def poll_control_ch(channel_pool, zigpy_device_mock):
"""Poll control channel fixture.""" """Poll control channel fixture."""
cluster_id = zigpy.zcl.clusters.general.PollControl.cluster_id cluster_id = zigpy.zcl.clusters.general.PollControl.cluster_id
zigpy_dev = zigpy_device_mock( zigpy_dev = zigpy_device_mock(
{1: {"in_clusters": [cluster_id], "out_clusters": [], "device_type": 0x1234}}, {1: {SIG_EP_INPUT: [cluster_id], SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x1234}},
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",
"test manufacturer", "test manufacturer",
"test model", "test model",
@ -85,7 +86,7 @@ async def poll_control_device(zha_device_restored, zigpy_device_mock):
"""Poll control device fixture.""" """Poll control device fixture."""
cluster_id = zigpy.zcl.clusters.general.PollControl.cluster_id cluster_id = zigpy.zcl.clusters.general.PollControl.cluster_id
zigpy_dev = zigpy_device_mock( zigpy_dev = zigpy_device_mock(
{1: {"in_clusters": [cluster_id], "out_clusters": [], "device_type": 0x1234}}, {1: {SIG_EP_INPUT: [cluster_id], SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x1234}},
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",
"test manufacturer", "test manufacturer",
"test model", "test model",
@ -159,7 +160,7 @@ async def test_in_channel_config(
): ):
"""Test ZHA core channel configuration for input clusters.""" """Test ZHA core channel configuration for input clusters."""
zigpy_dev = zigpy_device_mock( zigpy_dev = zigpy_device_mock(
{1: {"in_clusters": [cluster_id], "out_clusters": [], "device_type": 0x1234}}, {1: {SIG_EP_INPUT: [cluster_id], SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x1234}},
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",
"test manufacturer", "test manufacturer",
"test model", "test model",
@ -221,7 +222,7 @@ async def test_out_channel_config(
): ):
"""Test ZHA core channel configuration for output clusters.""" """Test ZHA core channel configuration for output clusters."""
zigpy_dev = zigpy_device_mock( zigpy_dev = zigpy_device_mock(
{1: {"out_clusters": [cluster_id], "in_clusters": [], "device_type": 0x1234}}, {1: {SIG_EP_OUTPUT: [cluster_id], SIG_EP_INPUT: [], SIG_EP_TYPE: 0x1234}},
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",
"test manufacturer", "test manufacturer",
"test model", "test model",
@ -328,14 +329,14 @@ def test_ep_channels_all_channels(m1, zha_device_mock):
zha_device = zha_device_mock( zha_device = zha_device_mock(
{ {
1: { 1: {
"in_clusters": [0, 1, 6, 8], SIG_EP_INPUT: [0, 1, 6, 8],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
}, },
2: { 2: {
"in_clusters": [0, 1, 6, 8, 768], SIG_EP_INPUT: [0, 1, 6, 8, 768],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": 0x0000, SIG_EP_TYPE: 0x0000,
}, },
} }
) )
@ -379,11 +380,11 @@ def test_channel_power_config(m1, zha_device_mock):
in_clusters = [0, 1, 6, 8] in_clusters = [0, 1, 6, 8]
zha_device = zha_device_mock( zha_device = zha_device_mock(
{ {
1: {"in_clusters": in_clusters, "out_clusters": [], "device_type": 0x0000}, 1: {SIG_EP_INPUT: in_clusters, SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x0000},
2: { 2: {
"in_clusters": [*in_clusters, 768], SIG_EP_INPUT: [*in_clusters, 768],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": 0x0000, SIG_EP_TYPE: 0x0000,
}, },
} }
) )
@ -402,8 +403,8 @@ def test_channel_power_config(m1, zha_device_mock):
zha_device = zha_device_mock( zha_device = zha_device_mock(
{ {
1: {"in_clusters": [], "out_clusters": [], "device_type": 0x0000}, 1: {SIG_EP_INPUT: [], SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x0000},
2: {"in_clusters": in_clusters, "out_clusters": [], "device_type": 0x0000}, 2: {SIG_EP_INPUT: in_clusters, SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x0000},
} }
) )
channels = zha_channels.Channels.new(zha_device) channels = zha_channels.Channels.new(zha_device)
@ -412,7 +413,7 @@ def test_channel_power_config(m1, zha_device_mock):
assert "2:0x0001" in pools[2].all_channels assert "2:0x0001" in pools[2].all_channels
zha_device = zha_device_mock( zha_device = zha_device_mock(
{2: {"in_clusters": in_clusters, "out_clusters": [], "device_type": 0x0000}} {2: {SIG_EP_INPUT: in_clusters, SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x0000}}
) )
channels = zha_channels.Channels.new(zha_device) channels = zha_channels.Channels.new(zha_device)
pools = {pool.id: pool for pool in channels.pools} pools = {pool.id: pool for pool in channels.pools}
@ -556,7 +557,7 @@ def zigpy_zll_device(zigpy_device_mock):
"""ZLL device fixture.""" """ZLL device fixture."""
return zigpy_device_mock( return zigpy_device_mock(
{1: {"in_clusters": [0x1000], "out_clusters": [], "device_type": 0x1234}}, {1: {SIG_EP_INPUT: [0x1000], SIG_EP_OUTPUT: [], SIG_EP_TYPE: 0x1234}},
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",
"test manufacturer", "test manufacturer",
"test model", "test model",

View File

@ -3,6 +3,7 @@
from unittest.mock import patch from unittest.mock import patch
import pytest import pytest
import zigpy.profiles
import zigpy.zcl.clusters import zigpy.zcl.clusters
from zigpy.zcl.clusters.hvac import Thermostat from zigpy.zcl.clusters.hvac import Thermostat
import zigpy.zcl.foundation as zcl_f import zigpy.zcl.foundation as zcl_f
@ -51,74 +52,79 @@ from homeassistant.components.zha.core.const import PRESET_COMPLEX, PRESET_SCHED
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNKNOWN from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNKNOWN
from .common import async_enable_traffic, find_entity_id, send_attributes_report from .common import async_enable_traffic, find_entity_id, send_attributes_report
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
CLIMATE = { CLIMATE = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.THERMOSTAT, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.THERMOSTAT,
SIG_EP_INPUT: [
zigpy.zcl.clusters.general.Basic.cluster_id, zigpy.zcl.clusters.general.Basic.cluster_id,
zigpy.zcl.clusters.general.Identify.cluster_id, zigpy.zcl.clusters.general.Identify.cluster_id,
zigpy.zcl.clusters.hvac.Thermostat.cluster_id, zigpy.zcl.clusters.hvac.Thermostat.cluster_id,
zigpy.zcl.clusters.hvac.UserInterface.cluster_id, zigpy.zcl.clusters.hvac.UserInterface.cluster_id,
], ],
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id], SIG_EP_OUTPUT: [zigpy.zcl.clusters.general.Ota.cluster_id],
} }
} }
CLIMATE_FAN = { CLIMATE_FAN = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.THERMOSTAT, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.THERMOSTAT,
SIG_EP_INPUT: [
zigpy.zcl.clusters.general.Basic.cluster_id, zigpy.zcl.clusters.general.Basic.cluster_id,
zigpy.zcl.clusters.general.Identify.cluster_id, zigpy.zcl.clusters.general.Identify.cluster_id,
zigpy.zcl.clusters.hvac.Fan.cluster_id, zigpy.zcl.clusters.hvac.Fan.cluster_id,
zigpy.zcl.clusters.hvac.Thermostat.cluster_id, zigpy.zcl.clusters.hvac.Thermostat.cluster_id,
zigpy.zcl.clusters.hvac.UserInterface.cluster_id, zigpy.zcl.clusters.hvac.UserInterface.cluster_id,
], ],
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id], SIG_EP_OUTPUT: [zigpy.zcl.clusters.general.Ota.cluster_id],
} }
} }
CLIMATE_SINOPE = { CLIMATE_SINOPE = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.THERMOSTAT, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.THERMOSTAT,
SIG_EP_INPUT: [
zigpy.zcl.clusters.general.Basic.cluster_id, zigpy.zcl.clusters.general.Basic.cluster_id,
zigpy.zcl.clusters.general.Identify.cluster_id, zigpy.zcl.clusters.general.Identify.cluster_id,
zigpy.zcl.clusters.hvac.Thermostat.cluster_id, zigpy.zcl.clusters.hvac.Thermostat.cluster_id,
zigpy.zcl.clusters.hvac.UserInterface.cluster_id, zigpy.zcl.clusters.hvac.UserInterface.cluster_id,
65281, 65281,
], ],
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id, 65281], SIG_EP_OUTPUT: [zigpy.zcl.clusters.general.Ota.cluster_id, 65281],
"profile_id": 260,
}, },
} }
CLIMATE_ZEN = { CLIMATE_ZEN = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.THERMOSTAT, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.THERMOSTAT,
SIG_EP_INPUT: [
zigpy.zcl.clusters.general.Basic.cluster_id, zigpy.zcl.clusters.general.Basic.cluster_id,
zigpy.zcl.clusters.general.Identify.cluster_id, zigpy.zcl.clusters.general.Identify.cluster_id,
zigpy.zcl.clusters.hvac.Fan.cluster_id, zigpy.zcl.clusters.hvac.Fan.cluster_id,
zigpy.zcl.clusters.hvac.Thermostat.cluster_id, zigpy.zcl.clusters.hvac.Thermostat.cluster_id,
zigpy.zcl.clusters.hvac.UserInterface.cluster_id, zigpy.zcl.clusters.hvac.UserInterface.cluster_id,
], ],
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id], SIG_EP_OUTPUT: [zigpy.zcl.clusters.general.Ota.cluster_id],
} }
} }
CLIMATE_MOES = { CLIMATE_MOES = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.THERMOSTAT, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.THERMOSTAT,
SIG_EP_INPUT: [
zigpy.zcl.clusters.general.Basic.cluster_id, zigpy.zcl.clusters.general.Basic.cluster_id,
zigpy.zcl.clusters.general.Identify.cluster_id, zigpy.zcl.clusters.general.Identify.cluster_id,
zigpy.zcl.clusters.hvac.Thermostat.cluster_id, zigpy.zcl.clusters.hvac.Thermostat.cluster_id,
zigpy.zcl.clusters.hvac.UserInterface.cluster_id, zigpy.zcl.clusters.hvac.UserInterface.cluster_id,
61148, 61148,
], ],
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id], SIG_EP_OUTPUT: [zigpy.zcl.clusters.general.Ota.cluster_id],
} }
} }
MANUF_SINOPE = "Sinope Technologies" MANUF_SINOPE = "Sinope Technologies"

View File

@ -32,6 +32,7 @@ from .common import (
make_zcl_header, make_zcl_header,
send_attributes_report, send_attributes_report,
) )
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
from tests.common import async_capture_events, mock_coro, mock_restore_cache from tests.common import async_capture_events, mock_coro, mock_restore_cache
@ -42,9 +43,10 @@ def zigpy_cover_device(zigpy_device_mock):
endpoints = { endpoints = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.IAS_ZONE, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [closures.WindowCovering.cluster_id], SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.IAS_ZONE,
"out_clusters": [], SIG_EP_INPUT: [closures.WindowCovering.cluster_id],
SIG_EP_OUTPUT: [],
} }
} }
return zigpy_device_mock(endpoints) return zigpy_device_mock(endpoints)
@ -56,9 +58,10 @@ def zigpy_cover_remote(zigpy_device_mock):
endpoints = { endpoints = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.WINDOW_COVERING_CONTROLLER, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [], SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.WINDOW_COVERING_CONTROLLER,
"out_clusters": [closures.WindowCovering.cluster_id], SIG_EP_INPUT: [],
SIG_EP_OUTPUT: [closures.WindowCovering.cluster_id],
} }
} }
return zigpy_device_mock(endpoints) return zigpy_device_mock(endpoints)
@ -70,13 +73,14 @@ def zigpy_shade_device(zigpy_device_mock):
endpoints = { endpoints = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.SHADE, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.SHADE,
SIG_EP_INPUT: [
closures.Shade.cluster_id, closures.Shade.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
general.OnOff.cluster_id, general.OnOff.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
} }
} }
return zigpy_device_mock(endpoints) return zigpy_device_mock(endpoints)
@ -88,9 +92,10 @@ def zigpy_keen_vent(zigpy_device_mock):
endpoints = { endpoints = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.LEVEL_CONTROLLABLE_OUTPUT, SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
"in_clusters": [general.LevelControl.cluster_id, general.OnOff.cluster_id], SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.LEVEL_CONTROLLABLE_OUTPUT,
"out_clusters": [], SIG_EP_INPUT: [general.LevelControl.cluster_id, general.OnOff.cluster_id],
SIG_EP_OUTPUT: [],
} }
} }
return zigpy_device_mock( return zigpy_device_mock(

View File

@ -17,6 +17,7 @@ import homeassistant.helpers.device_registry as dr
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from .common import async_enable_traffic, make_zcl_header from .common import async_enable_traffic, make_zcl_header
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE
from tests.common import async_fire_time_changed from tests.common import async_fire_time_changed
@ -32,9 +33,9 @@ def zigpy_device(zigpy_device_mock):
endpoints = { endpoints = {
3: { 3: {
"in_clusters": in_clusters, SIG_EP_INPUT: in_clusters,
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
} }
} }
return zigpy_device_mock(endpoints) return zigpy_device_mock(endpoints)
@ -53,9 +54,9 @@ def zigpy_device_mains(zigpy_device_mock):
endpoints = { endpoints = {
3: { 3: {
"in_clusters": in_clusters, SIG_EP_INPUT: in_clusters,
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
} }
} }
return zigpy_device_mock( return zigpy_device_mock(
@ -83,9 +84,9 @@ async def ota_zha_device(zha_device_restored, zigpy_device_mock):
zigpy_dev = zigpy_device_mock( zigpy_dev = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [general.Basic.cluster_id], SIG_EP_INPUT: [general.Basic.cluster_id],
"out_clusters": [general.Ota.cluster_id], SIG_EP_OUTPUT: [general.Ota.cluster_id],
"device_type": 0x1234, SIG_EP_TYPE: 0x1234,
} }
}, },
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",

View File

@ -12,6 +12,8 @@ from homeassistant.components.zha import DOMAIN
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE
from tests.common import async_get_device_automations, async_mock_service, mock_coro from tests.common import async_get_device_automations, async_mock_service, mock_coro
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa: F401 from tests.components.blueprint.conftest import stub_blueprint_populate # noqa: F401
@ -28,9 +30,9 @@ async def device_ias(hass, zigpy_device_mock, zha_device_joined_restored):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [c.cluster_id for c in clusters], SIG_EP_INPUT: [c.cluster_id for c in clusters],
"out_clusters": [general.OnOff.cluster_id], SIG_EP_OUTPUT: [general.OnOff.cluster_id],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
} }
}, },
) )

View File

@ -3,6 +3,7 @@ from datetime import timedelta
import time import time
import pytest import pytest
import zigpy.profiles.zha
import zigpy.zcl.clusters.general as general import zigpy.zcl.clusters.general as general
from homeassistant.components.device_tracker import DOMAIN, SOURCE_TYPE_ROUTER from homeassistant.components.device_tracker import DOMAIN, SOURCE_TYPE_ROUTER
@ -18,6 +19,7 @@ from .common import (
find_entity_id, find_entity_id,
send_attributes_report, send_attributes_report,
) )
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
from tests.common import async_fire_time_changed from tests.common import async_fire_time_changed
@ -27,15 +29,16 @@ def zigpy_device_dt(zigpy_device_mock):
"""Device tracker zigpy device.""" """Device tracker zigpy device."""
endpoints = { endpoints = {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.Basic.cluster_id, general.Basic.cluster_id,
general.PowerConfiguration.cluster_id, general.PowerConfiguration.cluster_id,
general.Identify.cluster_id, general.Identify.cluster_id,
general.PollControl.cluster_id, general.PollControl.cluster_id,
general.BinaryInput.cluster_id, general.BinaryInput.cluster_id,
], ],
"out_clusters": [general.Identify.cluster_id, general.Ota.cluster_id], SIG_EP_OUTPUT: [general.Identify.cluster_id, general.Ota.cluster_id],
"device_type": SMARTTHINGS_ARRIVAL_SENSOR_DEVICE_TYPE, SIG_EP_TYPE: SMARTTHINGS_ARRIVAL_SENSOR_DEVICE_TYPE,
SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
} }
} }
return zigpy_device_mock(endpoints) return zigpy_device_mock(endpoints)

View File

@ -12,6 +12,7 @@ from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from .common import async_enable_traffic from .common import async_enable_traffic
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
from tests.common import ( from tests.common import (
async_fire_time_changed, async_fire_time_changed,
@ -57,9 +58,10 @@ async def mock_devices(hass, zigpy_device_mock, zha_device_joined_restored):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [general.Basic.cluster_id], SIG_EP_INPUT: [general.Basic.cluster_id],
"out_clusters": [general.OnOff.cluster_id], SIG_EP_OUTPUT: [general.OnOff.cluster_id],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
} }
} }
) )

View File

@ -5,6 +5,7 @@ from unittest import mock
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
import pytest import pytest
from zigpy.const import SIG_ENDPOINTS, SIG_MANUFACTURER, SIG_MODEL, SIG_NODE_DESC
import zigpy.profiles.zha import zigpy.profiles.zha
import zigpy.quirks import zigpy.quirks
import zigpy.types import zigpy.types
@ -29,7 +30,15 @@ import homeassistant.components.zha.switch
import homeassistant.helpers.entity_registry import homeassistant.helpers.entity_registry
from .common import get_zha_gateway from .common import get_zha_gateway
from .zha_devices_list import DEVICES from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
from .zha_devices_list import (
DEV_SIG_CHANNELS,
DEV_SIG_ENT_MAP,
DEV_SIG_ENT_MAP_CLASS,
DEV_SIG_ENT_MAP_ID,
DEV_SIG_EVT_CHANNELS,
DEVICES,
)
NO_TAIL_ID = re.compile("_\\d$") NO_TAIL_ID = re.compile("_\\d$")
@ -72,11 +81,11 @@ async def test_devices(
) )
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
device["endpoints"], device[SIG_ENDPOINTS],
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",
device["manufacturer"], device[SIG_MANUFACTURER],
device["model"], device[SIG_MODEL],
node_descriptor=device["node_descriptor"], node_descriptor=device[SIG_NODE_DESC],
patch_cluster=False, patch_cluster=False,
) )
@ -120,11 +129,13 @@ async def test_devices(
ch.id for pool in zha_dev.channels.pools for ch in pool.client_channels.values() ch.id for pool in zha_dev.channels.pools for ch in pool.client_channels.values()
} }
entity_map = device["entity_map"] entity_map = device[DEV_SIG_ENT_MAP]
assert zha_entity_ids == { assert zha_entity_ids == {
e["entity_id"] for e in entity_map.values() if not e.get("default_match", False) e[DEV_SIG_ENT_MAP_ID]
for e in entity_map.values()
if not e.get("default_match", False)
} }
assert event_channels == set(device["event_channels"]) assert event_channels == set(device[DEV_SIG_EVT_CHANNELS])
for call in _dispatch.call_args_list: for call in _dispatch.call_args_list:
_, component, entity_cls, unique_id, channels = call[0] _, component, entity_cls, unique_id, channels = call[0]
@ -133,10 +144,10 @@ async def test_devices(
assert key in entity_map assert key in entity_map
assert entity_id is not None assert entity_id is not None
no_tail_id = NO_TAIL_ID.sub("", entity_map[key]["entity_id"]) no_tail_id = NO_TAIL_ID.sub("", entity_map[key][DEV_SIG_ENT_MAP_ID])
assert entity_id.startswith(no_tail_id) assert entity_id.startswith(no_tail_id)
assert {ch.name for ch in channels} == set(entity_map[key]["channels"]) assert {ch.name for ch in channels} == set(entity_map[key][DEV_SIG_CHANNELS])
assert entity_cls.__name__ == entity_map[key]["entity_class"] assert entity_cls.__name__ == entity_map[key][DEV_SIG_ENT_MAP_CLASS]
def _get_first_identify_cluster(zigpy_device): def _get_first_identify_cluster(zigpy_device):
@ -258,20 +269,20 @@ async def test_discover_endpoint(device_info, channels_mock, hass):
"homeassistant.components.zha.core.channels.Channels.async_new_entity" "homeassistant.components.zha.core.channels.Channels.async_new_entity"
) as new_ent: ) as new_ent:
channels = channels_mock( channels = channels_mock(
device_info["endpoints"], device_info[SIG_ENDPOINTS],
manufacturer=device_info["manufacturer"], manufacturer=device_info[SIG_MANUFACTURER],
model=device_info["model"], model=device_info[SIG_MODEL],
node_desc=device_info["node_descriptor"], node_desc=device_info[SIG_NODE_DESC],
patch_cluster=False, patch_cluster=False,
) )
assert device_info["event_channels"] == sorted( assert device_info[DEV_SIG_EVT_CHANNELS] == sorted(
ch.id for pool in channels.pools for ch in pool.client_channels.values() ch.id for pool in channels.pools for ch in pool.client_channels.values()
) )
assert new_ent.call_count == len( assert new_ent.call_count == len(
[ [
device_info device_info
for device_info in device_info["entity_map"].values() for device_info in device_info[DEV_SIG_ENT_MAP].values()
if not device_info.get("default_match", False) if not device_info.get("default_match", False)
] ]
) )
@ -279,10 +290,10 @@ async def test_discover_endpoint(device_info, channels_mock, hass):
for call_args in new_ent.call_args_list: for call_args in new_ent.call_args_list:
comp, ent_cls, unique_id, channels = call_args[0] comp, ent_cls, unique_id, channels = call_args[0]
map_id = (comp, unique_id) map_id = (comp, unique_id)
assert map_id in device_info["entity_map"] assert map_id in device_info[DEV_SIG_ENT_MAP]
entity_info = device_info["entity_map"][map_id] entity_info = device_info[DEV_SIG_ENT_MAP][map_id]
assert {ch.name for ch in channels} == set(entity_info["channels"]) assert {ch.name for ch in channels} == set(entity_info[DEV_SIG_CHANNELS])
assert ent_cls.__name__ == entity_info["entity_class"] assert ent_cls.__name__ == entity_info[DEV_SIG_ENT_MAP_CLASS]
def _ch_mock(cluster): def _ch_mock(cluster):
@ -377,11 +388,11 @@ async def test_device_override(
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.COLOR_DIMMABLE_LIGHT,
"endpoint_id": 1, "endpoint_id": 1,
"in_clusters": [0, 3, 4, 5, 6, 8, 768, 2821, 64513], SIG_EP_INPUT: [0, 3, 4, 5, 6, 8, 768, 2821, 64513],
"out_clusters": [25], SIG_EP_OUTPUT: [25],
"profile_id": 260, SIG_EP_PROFILE: 260,
} }
}, },
"00:11:22:33:44:55:66:77", "00:11:22:33:44:55:66:77",

View File

@ -49,6 +49,7 @@ from .common import (
get_zha_gateway, get_zha_gateway,
send_attributes_report, send_attributes_report,
) )
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
from tests.components.zha.common import async_wait_for_updates from tests.components.zha.common import async_wait_for_updates
@ -61,9 +62,10 @@ def zigpy_device(zigpy_device_mock):
"""Device tracker zigpy device.""" """Device tracker zigpy device."""
endpoints = { endpoints = {
1: { 1: {
"in_clusters": [hvac.Fan.cluster_id], SIG_EP_INPUT: [hvac.Fan.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
} }
return zigpy_device_mock( return zigpy_device_mock(
@ -78,9 +80,10 @@ async def coordinator(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [general.Groups.cluster_id], SIG_EP_INPUT: [general.Groups.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
ieee="00:15:8d:00:02:32:4f:32", ieee="00:15:8d:00:02:32:4f:32",
@ -99,13 +102,14 @@ async def device_fan_1(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.Groups.cluster_id, general.Groups.cluster_id,
general.OnOff.cluster_id, general.OnOff.cluster_id,
hvac.Fan.cluster_id, hvac.Fan.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.ON_OFF_LIGHT, SIG_EP_TYPE: zha.DeviceType.ON_OFF_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
}, },
}, },
ieee=IEEE_GROUPABLE_DEVICE, ieee=IEEE_GROUPABLE_DEVICE,
@ -123,14 +127,15 @@ async def device_fan_2(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.Groups.cluster_id, general.Groups.cluster_id,
general.OnOff.cluster_id, general.OnOff.cluster_id,
hvac.Fan.cluster_id, hvac.Fan.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.ON_OFF_LIGHT, SIG_EP_TYPE: zha.DeviceType.ON_OFF_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
}, },
}, },
ieee=IEEE_GROUPABLE_DEVICE2, ieee=IEEE_GROUPABLE_DEVICE2,

View File

@ -13,6 +13,7 @@ from homeassistant.components.zha.core.group import GroupMember
from homeassistant.components.zha.core.store import TOMBSTONE_LIFETIME from homeassistant.components.zha.core.store import TOMBSTONE_LIFETIME
from .common import async_enable_traffic, async_find_group_entity_id, get_zha_gateway from .common import async_enable_traffic, async_find_group_entity_id, get_zha_gateway
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8" IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8"
IEEE_GROUPABLE_DEVICE2 = "02:2d:6f:00:0a:90:69:e8" IEEE_GROUPABLE_DEVICE2 = "02:2d:6f:00:0a:90:69:e8"
@ -24,9 +25,10 @@ def zigpy_dev_basic(zigpy_device_mock):
return zigpy_device_mock( return zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [general.Basic.cluster_id], SIG_EP_INPUT: [general.Basic.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
} }
) )
@ -47,9 +49,10 @@ async def coordinator(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [], SIG_EP_INPUT: [],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
ieee="00:15:8d:00:02:32:4f:32", ieee="00:15:8d:00:02:32:4f:32",
@ -68,14 +71,15 @@ async def device_light_1(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.OnOff.cluster_id, general.OnOff.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
lighting.Color.cluster_id, lighting.Color.cluster_id,
general.Groups.cluster_id, general.Groups.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
ieee=IEEE_GROUPABLE_DEVICE, ieee=IEEE_GROUPABLE_DEVICE,
@ -92,14 +96,15 @@ async def device_light_2(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.OnOff.cluster_id, general.OnOff.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
lighting.Color.cluster_id, lighting.Color.cluster_id,
general.Groups.cluster_id, general.Groups.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
ieee=IEEE_GROUPABLE_DEVICE2, ieee=IEEE_GROUPABLE_DEVICE2,

View File

@ -1,6 +1,6 @@
"""Test zha light.""" """Test zha light."""
from datetime import timedelta from datetime import timedelta
from unittest.mock import AsyncMock, MagicMock, call, patch, sentinel from unittest.mock import AsyncMock, call, patch, sentinel
import pytest import pytest
import zigpy.profiles.zha as zha import zigpy.profiles.zha as zha
@ -23,6 +23,7 @@ from .common import (
get_zha_gateway, get_zha_gateway,
send_attributes_report, send_attributes_report,
) )
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
from tests.common import async_fire_time_changed from tests.common import async_fire_time_changed
from tests.components.zha.common import async_wait_for_updates from tests.components.zha.common import async_wait_for_updates
@ -35,39 +36,42 @@ IEEE_GROUPABLE_DEVICE3 = "03:2d:6f:00:0a:90:69:e7"
LIGHT_ON_OFF = { LIGHT_ON_OFF = {
1: { 1: {
"device_type": zha.DeviceType.ON_OFF_LIGHT, SIG_EP_PROFILE: zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zha.DeviceType.ON_OFF_LIGHT,
SIG_EP_INPUT: [
general.Basic.cluster_id, general.Basic.cluster_id,
general.Identify.cluster_id, general.Identify.cluster_id,
general.OnOff.cluster_id, general.OnOff.cluster_id,
], ],
"out_clusters": [general.Ota.cluster_id], SIG_EP_OUTPUT: [general.Ota.cluster_id],
} }
} }
LIGHT_LEVEL = { LIGHT_LEVEL = {
1: { 1: {
"device_type": zha.DeviceType.DIMMABLE_LIGHT, SIG_EP_PROFILE: zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zha.DeviceType.DIMMABLE_LIGHT,
SIG_EP_INPUT: [
general.Basic.cluster_id, general.Basic.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
general.OnOff.cluster_id, general.OnOff.cluster_id,
], ],
"out_clusters": [general.Ota.cluster_id], SIG_EP_OUTPUT: [general.Ota.cluster_id],
} }
} }
LIGHT_COLOR = { LIGHT_COLOR = {
1: { 1: {
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_PROFILE: zha.PROFILE_ID,
"in_clusters": [ SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_INPUT: [
general.Basic.cluster_id, general.Basic.cluster_id,
general.Identify.cluster_id, general.Identify.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
general.OnOff.cluster_id, general.OnOff.cluster_id,
lighting.Color.cluster_id, lighting.Color.cluster_id,
], ],
"out_clusters": [general.Ota.cluster_id], SIG_EP_OUTPUT: [general.Ota.cluster_id],
} }
} }
@ -79,9 +83,10 @@ async def coordinator(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [general.Groups.cluster_id], SIG_EP_INPUT: [general.Groups.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
ieee="00:15:8d:00:02:32:4f:32", ieee="00:15:8d:00:02:32:4f:32",
@ -100,15 +105,16 @@ async def device_light_1(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.OnOff.cluster_id, general.OnOff.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
lighting.Color.cluster_id, lighting.Color.cluster_id,
general.Groups.cluster_id, general.Groups.cluster_id,
general.Identify.cluster_id, general.Identify.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
ieee=IEEE_GROUPABLE_DEVICE, ieee=IEEE_GROUPABLE_DEVICE,
@ -126,15 +132,16 @@ async def device_light_2(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.OnOff.cluster_id, general.OnOff.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
lighting.Color.cluster_id, lighting.Color.cluster_id,
general.Groups.cluster_id, general.Groups.cluster_id,
general.Identify.cluster_id, general.Identify.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
ieee=IEEE_GROUPABLE_DEVICE2, ieee=IEEE_GROUPABLE_DEVICE2,
@ -152,15 +159,16 @@ async def device_light_3(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
general.OnOff.cluster_id, general.OnOff.cluster_id,
general.LevelControl.cluster_id, general.LevelControl.cluster_id,
lighting.Color.cluster_id, lighting.Color.cluster_id,
general.Groups.cluster_id, general.Groups.cluster_id,
general.Identify.cluster_id, general.Identify.cluster_id,
], ],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
SIG_EP_PROFILE: zha.PROFILE_ID,
} }
}, },
ieee=IEEE_GROUPABLE_DEVICE3, ieee=IEEE_GROUPABLE_DEVICE3,
@ -171,14 +179,14 @@ async def device_light_3(hass, zigpy_device_mock, zha_device_joined):
return zha_device return zha_device
@patch("zigpy.zcl.clusters.general.OnOff.read_attributes", new=MagicMock())
async def test_light_refresh(hass, zigpy_device_mock, zha_device_joined_restored): async def test_light_refresh(hass, zigpy_device_mock, zha_device_joined_restored):
"""Test zha light platform refresh.""" """Test zha light platform refresh."""
# create zigpy devices # create zigpy devices
zigpy_device = zigpy_device_mock(LIGHT_ON_OFF) zigpy_device = zigpy_device_mock(LIGHT_ON_OFF)
zha_device = await zha_device_joined_restored(zigpy_device)
on_off_cluster = zigpy_device.endpoints[1].on_off on_off_cluster = zigpy_device.endpoints[1].on_off
on_off_cluster.PLUGGED_ATTR_READS = {"on_off": 0}
zha_device = await zha_device_joined_restored(zigpy_device)
entity_id = await find_entity_id(DOMAIN, zha_device, hass) entity_id = await find_entity_id(DOMAIN, zha_device, hass)
# allow traffic to flow through the gateway and device # allow traffic to flow through the gateway and device
@ -193,7 +201,7 @@ async def test_light_refresh(hass, zigpy_device_mock, zha_device_joined_restored
assert hass.states.get(entity_id).state == STATE_OFF assert hass.states.get(entity_id).state == STATE_OFF
# 1 interval - 1 call # 1 interval - 1 call
on_off_cluster.read_attributes.return_value = [{"on_off": 1}, {}] on_off_cluster.PLUGGED_ATTR_READS = {"on_off": 1}
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=80)) async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=80))
await hass.async_block_till_done() await hass.async_block_till_done()
assert on_off_cluster.read_attributes.call_count == 1 assert on_off_cluster.read_attributes.call_count == 1
@ -201,7 +209,7 @@ async def test_light_refresh(hass, zigpy_device_mock, zha_device_joined_restored
assert hass.states.get(entity_id).state == STATE_ON assert hass.states.get(entity_id).state == STATE_ON
# 2 intervals - 2 calls # 2 intervals - 2 calls
on_off_cluster.read_attributes.return_value = [{"on_off": 0}, {}] on_off_cluster.PLUGGED_ATTR_READS = {"on_off": 0}
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=80)) async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=80))
await hass.async_block_till_done() await hass.async_block_till_done()
assert on_off_cluster.read_attributes.call_count == 2 assert on_off_cluster.read_attributes.call_count == 2

View File

@ -11,6 +11,7 @@ from homeassistant.components.lock import DOMAIN
from homeassistant.const import STATE_LOCKED, STATE_UNAVAILABLE, STATE_UNLOCKED from homeassistant.const import STATE_LOCKED, STATE_UNAVAILABLE, STATE_UNLOCKED
from .common import async_enable_traffic, find_entity_id, send_attributes_report from .common import async_enable_traffic, find_entity_id, send_attributes_report
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE
from tests.common import mock_coro from tests.common import mock_coro
@ -28,9 +29,9 @@ async def lock(hass, zigpy_device_mock, zha_device_joined_restored):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [closures.DoorLock.cluster_id, general.Basic.cluster_id], SIG_EP_INPUT: [closures.DoorLock.cluster_id, general.Basic.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.DOOR_LOCK, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.DOOR_LOCK,
} }
}, },
) )

View File

@ -17,6 +17,7 @@ from .common import (
find_entity_id, find_entity_id,
send_attributes_report, send_attributes_report,
) )
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE
from tests.common import mock_coro from tests.common import mock_coro
@ -27,9 +28,9 @@ def zigpy_analog_output_device(zigpy_device_mock):
endpoints = { endpoints = {
1: { 1: {
"device_type": zigpy.profiles.zha.DeviceType.LEVEL_CONTROL_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.LEVEL_CONTROL_SWITCH,
"in_clusters": [general.AnalogOutput.cluster_id, general.Basic.cluster_id], SIG_EP_INPUT: [general.AnalogOutput.cluster_id, general.Basic.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
} }
} }
return zigpy_device_mock(endpoints) return zigpy_device_mock(endpoints)

View File

@ -34,6 +34,7 @@ from .common import (
send_attribute_report, send_attribute_report,
send_attributes_report, send_attributes_report,
) )
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE
async def async_test_humidity(hass, cluster, entity_id): async def async_test_humidity(hass, cluster, entity_id):
@ -163,9 +164,9 @@ async def test_sensor(
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [cluster_id, general.Basic.cluster_id], SIG_EP_INPUT: [cluster_id, general.Basic.cluster_id],
"out_cluster": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
} }
} }
) )
@ -284,12 +285,12 @@ async def test_temp_uom(
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [ SIG_EP_INPUT: [
measurement.TemperatureMeasurement.cluster_id, measurement.TemperatureMeasurement.cluster_id,
general.Basic.cluster_id, general.Basic.cluster_id,
], ],
"out_cluster": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
} }
} }
) )
@ -327,9 +328,9 @@ async def test_electrical_measurement_init(
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [cluster_id, general.Basic.cluster_id], SIG_EP_INPUT: [cluster_id, general.Basic.cluster_id],
"out_cluster": [], SIG_EP_OUTPUT: [],
"device_type": zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SWITCH,
} }
} }
) )

View File

@ -18,6 +18,7 @@ from .common import (
get_zha_gateway, get_zha_gateway,
send_attributes_report, send_attributes_report,
) )
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE
from tests.common import mock_coro from tests.common import mock_coro
from tests.components.zha.common import async_wait_for_updates from tests.components.zha.common import async_wait_for_updates
@ -33,9 +34,9 @@ def zigpy_device(zigpy_device_mock):
"""Device tracker zigpy device.""" """Device tracker zigpy device."""
endpoints = { endpoints = {
1: { 1: {
"in_clusters": [general.Basic.cluster_id, general.OnOff.cluster_id], SIG_EP_INPUT: [general.Basic.cluster_id, general.OnOff.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH,
} }
} }
return zigpy_device_mock(endpoints) return zigpy_device_mock(endpoints)
@ -48,9 +49,9 @@ async def coordinator(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [], SIG_EP_INPUT: [],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.COLOR_DIMMABLE_LIGHT, SIG_EP_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
} }
}, },
ieee="00:15:8d:00:02:32:4f:32", ieee="00:15:8d:00:02:32:4f:32",
@ -69,9 +70,9 @@ async def device_switch_1(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [general.OnOff.cluster_id, general.Groups.cluster_id], SIG_EP_INPUT: [general.OnOff.cluster_id, general.Groups.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH,
} }
}, },
ieee=IEEE_GROUPABLE_DEVICE, ieee=IEEE_GROUPABLE_DEVICE,
@ -89,9 +90,9 @@ async def device_switch_2(hass, zigpy_device_mock, zha_device_joined):
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
{ {
1: { 1: {
"in_clusters": [general.OnOff.cluster_id, general.Groups.cluster_id], SIG_EP_INPUT: [general.OnOff.cluster_id, general.Groups.cluster_id],
"out_clusters": [], SIG_EP_OUTPUT: [],
"device_type": zha.DeviceType.ON_OFF_SWITCH, SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH,
} }
}, },
ieee=IEEE_GROUPABLE_DEVICE2, ieee=IEEE_GROUPABLE_DEVICE2,

File diff suppressed because it is too large Load Diff