mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix Z-Wave device class endpoint discovery (#142171)
* Add test fixture and test for Glass 9 shutter * Fix zwave_js device class discovery matcher * Fall back to node device class * Fix test_special_meters modifying node state * Handle value added after node ready
This commit is contained in:
parent
341d9f15f0
commit
11564e3df5
@ -1334,21 +1334,49 @@ def async_discover_single_value(
|
||||
continue
|
||||
|
||||
# check device_class_generic
|
||||
# If the value has an endpoint but it is missing on the node
|
||||
# we can't match the endpoint device class to the schema device class.
|
||||
# This could happen if the value is discovered after the node is ready.
|
||||
if schema.device_class_generic and (
|
||||
not value.node.device_class
|
||||
or not any(
|
||||
value.node.device_class.generic.label == val
|
||||
for val in schema.device_class_generic
|
||||
(
|
||||
(endpoint := value.endpoint) is None
|
||||
or (node_endpoint := value.node.endpoints.get(endpoint)) is None
|
||||
or (device_class := node_endpoint.device_class) is None
|
||||
or not any(
|
||||
device_class.generic.label == val
|
||||
for val in schema.device_class_generic
|
||||
)
|
||||
)
|
||||
and (
|
||||
(device_class := value.node.device_class) is None
|
||||
or not any(
|
||||
device_class.generic.label == val
|
||||
for val in schema.device_class_generic
|
||||
)
|
||||
)
|
||||
):
|
||||
continue
|
||||
|
||||
# check device_class_specific
|
||||
# If the value has an endpoint but it is missing on the node
|
||||
# we can't match the endpoint device class to the schema device class.
|
||||
# This could happen if the value is discovered after the node is ready.
|
||||
if schema.device_class_specific and (
|
||||
not value.node.device_class
|
||||
or not any(
|
||||
value.node.device_class.specific.label == val
|
||||
for val in schema.device_class_specific
|
||||
(
|
||||
(endpoint := value.endpoint) is None
|
||||
or (node_endpoint := value.node.endpoints.get(endpoint)) is None
|
||||
or (device_class := node_endpoint.device_class) is None
|
||||
or not any(
|
||||
device_class.specific.label == val
|
||||
for val in schema.device_class_specific
|
||||
)
|
||||
)
|
||||
and (
|
||||
(device_class := value.node.device_class) is None
|
||||
or not any(
|
||||
device_class.specific.label == val
|
||||
for val in schema.device_class_specific
|
||||
)
|
||||
)
|
||||
):
|
||||
continue
|
||||
|
@ -301,6 +301,12 @@ def shelly_europe_ltd_qnsh_001p10_state_fixture() -> dict[str, Any]:
|
||||
return load_json_object_fixture("shelly_europe_ltd_qnsh_001p10_state.json", DOMAIN)
|
||||
|
||||
|
||||
@pytest.fixture(name="touchwand_glass9_state", scope="package")
|
||||
def touchwand_glass9_state_fixture() -> dict[str, Any]:
|
||||
"""Load the Touchwand Glass 9 shutter node state fixture data."""
|
||||
return load_json_object_fixture("touchwand_glass9_state.json", DOMAIN)
|
||||
|
||||
|
||||
@pytest.fixture(name="merten_507801_state", scope="package")
|
||||
def merten_507801_state_fixture() -> dict[str, Any]:
|
||||
"""Load the Merten 507801 Shutter node state fixture data."""
|
||||
@ -1040,6 +1046,14 @@ def shelly_qnsh_001P10_cover_shutter_fixture(
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="touchwand_glass9")
|
||||
def touchwand_glass9_fixture(client, touchwand_glass9_state) -> Node:
|
||||
"""Mock a Touchwand glass9 node."""
|
||||
node = Node(client, copy.deepcopy(touchwand_glass9_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="merten_507801")
|
||||
def merten_507801_cover_fixture(client, merten_507801_state) -> Node:
|
||||
"""Mock a Merten 507801 Shutter node."""
|
||||
|
File diff suppressed because it is too large
Load Diff
3467
tests/components/zwave_js/fixtures/touchwand_glass9_state.json
Normal file
3467
tests/components/zwave_js/fixtures/touchwand_glass9_state.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -56,6 +56,24 @@ async def test_iblinds_v2(hass: HomeAssistant, client, iblinds_v2, integration)
|
||||
assert state
|
||||
|
||||
|
||||
async def test_touchwand_glass9(
|
||||
hass: HomeAssistant,
|
||||
client: MagicMock,
|
||||
touchwand_glass9: Node,
|
||||
integration: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test a touchwand_glass9 is discovered as a cover."""
|
||||
node = touchwand_glass9
|
||||
node_device_class = node.device_class
|
||||
assert node_device_class
|
||||
assert node_device_class.specific.label == "Unused"
|
||||
|
||||
assert not hass.states.async_entity_ids_count("light")
|
||||
assert hass.states.async_entity_ids_count("cover") == 3
|
||||
state = hass.states.get("cover.gp9")
|
||||
assert state
|
||||
|
||||
|
||||
async def test_zvidar_state(hass: HomeAssistant, client, zvidar, integration) -> None:
|
||||
"""Test that an ZVIDAR Z-CM-V01 multilevel switch value is discovered as a cover."""
|
||||
node = zvidar
|
||||
|
@ -27,7 +27,7 @@ from homeassistant.components.persistent_notification import async_dismiss
|
||||
from homeassistant.components.zwave_js import DOMAIN
|
||||
from homeassistant.components.zwave_js.helpers import get_device_id, get_device_id_ext
|
||||
from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.const import STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import CoreState, HomeAssistant
|
||||
from homeassistant.helpers import (
|
||||
area_registry as ar,
|
||||
@ -366,6 +366,7 @@ async def test_listen_done_after_setup(
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("client")
|
||||
@pytest.mark.parametrize("platforms", [[Platform.SENSOR]])
|
||||
async def test_new_entity_on_value_added(
|
||||
hass: HomeAssistant,
|
||||
multisensor_6: Node,
|
||||
|
@ -655,6 +655,17 @@ async def test_special_meters(
|
||||
"value": 659.813,
|
||||
},
|
||||
)
|
||||
node_data["endpoints"].append(
|
||||
{
|
||||
"nodeId": 102,
|
||||
"index": 10,
|
||||
"installerIcon": 1792,
|
||||
"userIcon": 1792,
|
||||
"commandClasses": [
|
||||
{"id": 50, "name": "Meter", "version": 3, "isSecure": False}
|
||||
],
|
||||
}
|
||||
)
|
||||
# Add an ElectricScale.KILOVOLT_AMPERE_REACTIVE value to the state so we can test that
|
||||
# it is handled differently (no device class)
|
||||
node_data["values"].append(
|
||||
@ -678,6 +689,17 @@ async def test_special_meters(
|
||||
"value": 659.813,
|
||||
},
|
||||
)
|
||||
node_data["endpoints"].append(
|
||||
{
|
||||
"nodeId": 102,
|
||||
"index": 11,
|
||||
"installerIcon": 1792,
|
||||
"userIcon": 1792,
|
||||
"commandClasses": [
|
||||
{"id": 50, "name": "Meter", "version": 3, "isSecure": False}
|
||||
],
|
||||
}
|
||||
)
|
||||
node = Node(client, node_data)
|
||||
event = {"node": node}
|
||||
client.driver.controller.emit("node added", event)
|
||||
|
Loading…
x
Reference in New Issue
Block a user