Fix ZHA configuration APIs (#81874)
* Fix ZHA configuration loading and saving issues * add tests
This commit is contained in:
parent
e4ecaa433a
commit
ebffe0f33b
4 changed files with 239 additions and 3 deletions
|
@ -1090,11 +1090,17 @@ async def websocket_update_zha_configuration(
|
|||
):
|
||||
data_to_save[CUSTOM_CONFIGURATION][section].pop(entry)
|
||||
# remove entire section block if empty
|
||||
if not data_to_save[CUSTOM_CONFIGURATION][section]:
|
||||
if (
|
||||
not data_to_save[CUSTOM_CONFIGURATION].get(section)
|
||||
and section in data_to_save[CUSTOM_CONFIGURATION]
|
||||
):
|
||||
data_to_save[CUSTOM_CONFIGURATION].pop(section)
|
||||
|
||||
# remove entire custom_configuration block if empty
|
||||
if not data_to_save[CUSTOM_CONFIGURATION]:
|
||||
if (
|
||||
not data_to_save.get(CUSTOM_CONFIGURATION)
|
||||
and CUSTOM_CONFIGURATION in data_to_save
|
||||
):
|
||||
data_to_save.pop(CUSTOM_CONFIGURATION)
|
||||
|
||||
_LOGGER.info(
|
||||
|
|
|
@ -221,11 +221,13 @@ def async_get_zha_config_value(
|
|||
)
|
||||
|
||||
|
||||
def async_cluster_exists(hass, cluster_id):
|
||||
def async_cluster_exists(hass, cluster_id, skip_coordinator=True):
|
||||
"""Determine if a device containing the specified in cluster is paired."""
|
||||
zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
|
||||
zha_devices = zha_gateway.devices.values()
|
||||
for zha_device in zha_devices:
|
||||
if skip_coordinator and zha_device.is_coordinator:
|
||||
continue
|
||||
clusters_by_endpoint = zha_device.async_get_clusters()
|
||||
for clusters in clusters_by_endpoint.values():
|
||||
if (
|
||||
|
|
153
tests/components/zha/data.py
Normal file
153
tests/components/zha/data.py
Normal file
|
@ -0,0 +1,153 @@
|
|||
"""Test data for ZHA API tests."""
|
||||
|
||||
BASE_CUSTOM_CONFIGURATION = {
|
||||
"schemas": {
|
||||
"zha_options": [
|
||||
{
|
||||
"type": "integer",
|
||||
"valueMin": 0,
|
||||
"name": "default_light_transition",
|
||||
"optional": True,
|
||||
"default": 0,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "enhanced_light_transition",
|
||||
"required": True,
|
||||
"default": False,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "light_transitioning_flag",
|
||||
"required": True,
|
||||
"default": True,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "always_prefer_xy_color_mode",
|
||||
"required": True,
|
||||
"default": True,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "enable_identify_on_join",
|
||||
"required": True,
|
||||
"default": True,
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"valueMin": 0,
|
||||
"name": "consider_unavailable_mains",
|
||||
"optional": True,
|
||||
"default": 7200,
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"valueMin": 0,
|
||||
"name": "consider_unavailable_battery",
|
||||
"optional": True,
|
||||
"default": 21600,
|
||||
},
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"zha_options": {
|
||||
"enhanced_light_transition": True,
|
||||
"default_light_transition": 0,
|
||||
"light_transitioning_flag": True,
|
||||
"always_prefer_xy_color_mode": True,
|
||||
"enable_identify_on_join": True,
|
||||
"consider_unavailable_mains": 7200,
|
||||
"consider_unavailable_battery": 21600,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
CONFIG_WITH_ALARM_OPTIONS = {
|
||||
"schemas": {
|
||||
"zha_options": [
|
||||
{
|
||||
"type": "integer",
|
||||
"valueMin": 0,
|
||||
"name": "default_light_transition",
|
||||
"optional": True,
|
||||
"default": 0,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "enhanced_light_transition",
|
||||
"required": True,
|
||||
"default": False,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "light_transitioning_flag",
|
||||
"required": True,
|
||||
"default": True,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "always_prefer_xy_color_mode",
|
||||
"required": True,
|
||||
"default": True,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "enable_identify_on_join",
|
||||
"required": True,
|
||||
"default": True,
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"valueMin": 0,
|
||||
"name": "consider_unavailable_mains",
|
||||
"optional": True,
|
||||
"default": 7200,
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"valueMin": 0,
|
||||
"name": "consider_unavailable_battery",
|
||||
"optional": True,
|
||||
"default": 21600,
|
||||
},
|
||||
],
|
||||
"zha_alarm_options": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "alarm_master_code",
|
||||
"required": True,
|
||||
"default": "1234",
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"valueMin": 0,
|
||||
"name": "alarm_failed_tries",
|
||||
"required": True,
|
||||
"default": 3,
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "alarm_arm_requires_code",
|
||||
"required": True,
|
||||
"default": False,
|
||||
},
|
||||
],
|
||||
},
|
||||
"data": {
|
||||
"zha_options": {
|
||||
"enhanced_light_transition": True,
|
||||
"default_light_transition": 0,
|
||||
"light_transitioning_flag": True,
|
||||
"always_prefer_xy_color_mode": True,
|
||||
"enable_identify_on_join": True,
|
||||
"consider_unavailable_mains": 7200,
|
||||
"consider_unavailable_battery": 21600,
|
||||
},
|
||||
"zha_alarm_options": {
|
||||
"alarm_arm_requires_code": False,
|
||||
"alarm_master_code": "4321",
|
||||
"alarm_failed_tries": 2,
|
||||
},
|
||||
},
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
"""Test ZHA API."""
|
||||
from binascii import unhexlify
|
||||
from copy import deepcopy
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
@ -8,6 +9,7 @@ import zigpy.backups
|
|||
import zigpy.profiles.zha
|
||||
import zigpy.types
|
||||
import zigpy.zcl.clusters.general as general
|
||||
import zigpy.zcl.clusters.security as security
|
||||
|
||||
from homeassistant.components.websocket_api import const
|
||||
from homeassistant.components.zha import DOMAIN
|
||||
|
@ -50,6 +52,7 @@ from .conftest import (
|
|||
SIG_EP_PROFILE,
|
||||
SIG_EP_TYPE,
|
||||
)
|
||||
from .data import BASE_CUSTOM_CONFIGURATION, CONFIG_WITH_ALARM_OPTIONS
|
||||
|
||||
IEEE_SWITCH_DEVICE = "01:2d:6f:00:0a:90:69:e7"
|
||||
IEEE_GROUPABLE_DEVICE = "01:2d:6f:00:0a:90:69:e8"
|
||||
|
@ -61,6 +64,7 @@ def required_platform_only():
|
|||
with patch(
|
||||
"homeassistant.components.zha.PLATFORMS",
|
||||
(
|
||||
Platform.ALARM_CONTROL_PANEL,
|
||||
Platform.SELECT,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
|
@ -89,6 +93,25 @@ async def device_switch(hass, zigpy_device_mock, zha_device_joined):
|
|||
return zha_device
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def device_ias_ace(hass, zigpy_device_mock, zha_device_joined):
|
||||
"""Test alarm control panel device."""
|
||||
|
||||
zigpy_device = zigpy_device_mock(
|
||||
{
|
||||
1: {
|
||||
SIG_EP_INPUT: [security.IasAce.cluster_id],
|
||||
SIG_EP_OUTPUT: [],
|
||||
SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.IAS_ANCILLARY_CONTROL,
|
||||
SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
|
||||
}
|
||||
},
|
||||
)
|
||||
zha_device = await zha_device_joined(zigpy_device)
|
||||
zha_device.available = True
|
||||
return zha_device
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def device_groupable(hass, zigpy_device_mock, zha_device_joined):
|
||||
"""Test zha light platform."""
|
||||
|
@ -225,6 +248,58 @@ async def test_list_devices(zha_client):
|
|||
assert device == device2
|
||||
|
||||
|
||||
async def test_get_zha_config(zha_client):
|
||||
"""Test getting zha custom configuration."""
|
||||
await zha_client.send_json({ID: 5, TYPE: "zha/configuration"})
|
||||
|
||||
msg = await zha_client.receive_json()
|
||||
|
||||
configuration = msg["result"]
|
||||
assert configuration == BASE_CUSTOM_CONFIGURATION
|
||||
|
||||
|
||||
async def test_get_zha_config_with_alarm(hass, zha_client, device_ias_ace):
|
||||
"""Test getting zha custom configuration."""
|
||||
await zha_client.send_json({ID: 5, TYPE: "zha/configuration"})
|
||||
|
||||
msg = await zha_client.receive_json()
|
||||
|
||||
configuration = msg["result"]
|
||||
assert configuration == CONFIG_WITH_ALARM_OPTIONS
|
||||
|
||||
# test that the alarm options are not in the config when we remove the device
|
||||
device_ias_ace.gateway.device_removed(device_ias_ace.device)
|
||||
await hass.async_block_till_done()
|
||||
await zha_client.send_json({ID: 6, TYPE: "zha/configuration"})
|
||||
|
||||
msg = await zha_client.receive_json()
|
||||
|
||||
configuration = msg["result"]
|
||||
assert configuration == BASE_CUSTOM_CONFIGURATION
|
||||
|
||||
|
||||
async def test_update_zha_config(zha_client, zigpy_app_controller):
|
||||
"""Test updating zha custom configuration."""
|
||||
|
||||
configuration = deepcopy(CONFIG_WITH_ALARM_OPTIONS)
|
||||
configuration["data"]["zha_options"]["default_light_transition"] = 10
|
||||
|
||||
with patch(
|
||||
"bellows.zigbee.application.ControllerApplication.new",
|
||||
return_value=zigpy_app_controller,
|
||||
):
|
||||
await zha_client.send_json(
|
||||
{ID: 5, TYPE: "zha/configuration/update", "data": configuration["data"]}
|
||||
)
|
||||
msg = await zha_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
await zha_client.send_json({ID: 6, TYPE: "zha/configuration"})
|
||||
msg = await zha_client.receive_json()
|
||||
configuration = msg["result"]
|
||||
assert configuration == configuration
|
||||
|
||||
|
||||
async def test_device_not_found(zha_client):
|
||||
"""Test not found response from get device API."""
|
||||
await zha_client.send_json(
|
||||
|
|
Loading…
Add table
Reference in a new issue