Group by endpoints and not by devices for ZHA Zigbee groups (#34583)

* start implementation
* handle members correctly
* fix group member info
* align groupable devices with group members
* handle group endpoint adding and removing
* update add group
* update group and group member
* update create group
* remove domain check
* update test
* remove temporary 2nd groupable device api
* update test
* rename validator - review comment
* fix test that was never running
* additional testing
* fix coordinator descriptors
* remove check that was done twice
* update test
* Use AsyncMock()
Co-authored-by: Alexei Chetroi <lexoid@gmail.com>
This commit is contained in:
David F. Mulcahey 2020-05-04 15:19:53 -04:00 committed by GitHub
parent e54e9279e3
commit 8279efc164
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 438 additions and 167 deletions

View file

@ -53,6 +53,7 @@ from .core.const import (
WARNING_DEVICE_STROBE_HIGH,
WARNING_DEVICE_STROBE_YES,
)
from .core.group import GroupMember
from .core.helpers import async_is_bindable_target, get_matched_clusters
_LOGGER = logging.getLogger(__name__)
@ -209,7 +210,7 @@ async def websocket_get_devices(hass, connection, msg):
"""Get ZHA devices."""
zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
devices = [device.async_get_info() for device in zha_gateway.devices.values()]
devices = [device.zha_device_info for device in zha_gateway.devices.values()]
connection.send_result(msg[ID], devices)
@ -221,13 +222,35 @@ async def websocket_get_groupable_devices(hass, connection, msg):
"""Get ZHA devices that can be grouped."""
zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
devices = [
device.async_get_info()
for device in zha_gateway.devices.values()
if device.is_groupable or device.is_coordinator
]
devices = [device for device in zha_gateway.devices.values() if device.is_groupable]
groupable_devices = []
connection.send_result(msg[ID], devices)
for device in devices:
entity_refs = zha_gateway.device_registry.get(device.ieee)
for ep_id in device.async_get_groupable_endpoints():
groupable_devices.append(
{
"endpoint_id": ep_id,
"entities": [
{
"name": zha_gateway.ha_entity_registry.async_get(
entity_ref.reference_id
).name,
"original_name": zha_gateway.ha_entity_registry.async_get(
entity_ref.reference_id
).original_name,
}
for entity_ref in entity_refs
if list(entity_ref.cluster_channels.values())[
0
].cluster.endpoint.endpoint_id
== ep_id
],
"device": device.zha_device_info,
}
)
connection.send_result(msg[ID], groupable_devices)
@websocket_api.require_admin
@ -236,7 +259,7 @@ async def websocket_get_groupable_devices(hass, connection, msg):
async def websocket_get_groups(hass, connection, msg):
"""Get ZHA groups."""
zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
groups = [group.async_get_info() for group in zha_gateway.groups.values()]
groups = [group.group_info for group in zha_gateway.groups.values()]
connection.send_result(msg[ID], groups)
@ -251,7 +274,7 @@ async def websocket_get_device(hass, connection, msg):
ieee = msg[ATTR_IEEE]
device = None
if ieee in zha_gateway.devices:
device = zha_gateway.devices[ieee].async_get_info()
device = zha_gateway.devices[ieee].zha_device_info
if not device:
connection.send_message(
websocket_api.error_message(
@ -274,7 +297,7 @@ async def websocket_get_group(hass, connection, msg):
group = None
if group_id in zha_gateway.groups:
group = zha_gateway.groups.get(group_id).async_get_info()
group = zha_gateway.groups.get(group_id).group_info
if not group:
connection.send_message(
websocket_api.error_message(
@ -285,13 +308,27 @@ async def websocket_get_group(hass, connection, msg):
connection.send_result(msg[ID], group)
def cv_group_member(value: Any) -> GroupMember:
"""Validate and transform a group member."""
if not isinstance(value, Mapping):
raise vol.Invalid("Not a group member")
try:
group_member = GroupMember(
ieee=EUI64.convert(value["ieee"]), endpoint_id=value["endpoint_id"]
)
except KeyError:
raise vol.Invalid("Not a group member")
return group_member
@websocket_api.require_admin
@websocket_api.async_response
@websocket_api.websocket_command(
{
vol.Required(TYPE): "zha/group/add",
vol.Required(GROUP_NAME): cv.string,
vol.Optional(ATTR_MEMBERS): vol.All(cv.ensure_list, [EUI64.convert]),
vol.Optional(ATTR_MEMBERS): vol.All(cv.ensure_list, [cv_group_member]),
}
)
async def websocket_add_group(hass, connection, msg):
@ -300,7 +337,7 @@ async def websocket_add_group(hass, connection, msg):
group_name = msg[GROUP_NAME]
members = msg.get(ATTR_MEMBERS)
group = await zha_gateway.async_create_zigpy_group(group_name, members)
connection.send_result(msg[ID], group.async_get_info())
connection.send_result(msg[ID], group.group_info)
@websocket_api.require_admin
@ -323,7 +360,7 @@ async def websocket_remove_groups(hass, connection, msg):
await asyncio.gather(*tasks)
else:
await zha_gateway.async_remove_zigpy_group(group_ids[0])
ret_groups = [group.async_get_info() for group in zha_gateway.groups.values()]
ret_groups = [group.group_info for group in zha_gateway.groups.values()]
connection.send_result(msg[ID], ret_groups)
@ -333,7 +370,7 @@ async def websocket_remove_groups(hass, connection, msg):
{
vol.Required(TYPE): "zha/group/members/add",
vol.Required(GROUP_ID): cv.positive_int,
vol.Required(ATTR_MEMBERS): vol.All(cv.ensure_list, [EUI64.convert]),
vol.Required(ATTR_MEMBERS): vol.All(cv.ensure_list, [cv_group_member]),
}
)
async def websocket_add_group_members(hass, connection, msg):
@ -353,7 +390,7 @@ async def websocket_add_group_members(hass, connection, msg):
)
)
return
ret_group = zha_group.async_get_info()
ret_group = zha_group.group_info
connection.send_result(msg[ID], ret_group)
@ -363,7 +400,7 @@ async def websocket_add_group_members(hass, connection, msg):
{
vol.Required(TYPE): "zha/group/members/remove",
vol.Required(GROUP_ID): cv.positive_int,
vol.Required(ATTR_MEMBERS): vol.All(cv.ensure_list, [EUI64.convert]),
vol.Required(ATTR_MEMBERS): vol.All(cv.ensure_list, [cv_group_member]),
}
)
async def websocket_remove_group_members(hass, connection, msg):
@ -383,7 +420,7 @@ async def websocket_remove_group_members(hass, connection, msg):
)
)
return
ret_group = zha_group.async_get_info()
ret_group = zha_group.group_info
connection.send_result(msg[ID], ret_group)
@ -608,7 +645,7 @@ async def websocket_get_bindable_devices(hass, connection, msg):
source_device = zha_gateway.get_device(source_ieee)
devices = [
device.async_get_info()
device.zha_device_info
for device in zha_gateway.devices.values()
if async_is_bindable_target(source_device, device)
]