mirror of
https://github.com/home-assistant/core.git
synced 2026-05-08 17:49:37 +01:00
Add fabric index fields to Matter lock user and credential responses (#167875)
This commit is contained in:
@@ -71,6 +71,8 @@ class LockUserData(TypedDict):
|
||||
user_type: str
|
||||
credential_rule: str
|
||||
credentials: list[LockUserCredentialData]
|
||||
creator_fabric_index: int | None
|
||||
last_modified_fabric_index: int | None
|
||||
next_user_index: int | None
|
||||
|
||||
|
||||
@@ -115,6 +117,8 @@ class GetLockCredentialStatusResult(TypedDict):
|
||||
|
||||
credential_exists: bool
|
||||
user_index: int | None
|
||||
creator_fabric_index: int | None
|
||||
last_modified_fabric_index: int | None
|
||||
next_credential_index: int | None
|
||||
|
||||
|
||||
@@ -214,6 +218,8 @@ def _format_user_response(user_data: Any) -> LockUserData | None:
|
||||
_get_attr(user_data, "credentialRule"), "unknown"
|
||||
),
|
||||
credentials=credentials,
|
||||
creator_fabric_index=_get_attr(user_data, "creatorFabricIndex"),
|
||||
last_modified_fabric_index=_get_attr(user_data, "lastModifiedFabricIndex"),
|
||||
next_user_index=_get_attr(user_data, "nextUserIndex"),
|
||||
)
|
||||
|
||||
@@ -817,7 +823,8 @@ async def get_lock_credential_status(
|
||||
) -> GetLockCredentialStatusResult:
|
||||
"""Get the status of a credential slot on the lock.
|
||||
|
||||
Returns typed dict with credential_exists, user_index, next_credential_index.
|
||||
Returns typed dict with credential_exists, user_index, creator_fabric_index,
|
||||
last_modified_fabric_index, and next_credential_index.
|
||||
Raises HomeAssistantError on failure.
|
||||
"""
|
||||
lock_endpoint = _get_lock_endpoint_or_raise(node)
|
||||
@@ -839,5 +846,7 @@ async def get_lock_credential_status(
|
||||
return GetLockCredentialStatusResult(
|
||||
credential_exists=bool(_get_attr(response, "credentialExists")),
|
||||
user_index=_get_attr(response, "userIndex"),
|
||||
creator_fabric_index=_get_attr(response, "creatorFabricIndex"),
|
||||
last_modified_fabric_index=_get_attr(response, "lastModifiedFabricIndex"),
|
||||
next_credential_index=_get_attr(response, "nextCredentialIndex"),
|
||||
)
|
||||
|
||||
@@ -587,6 +587,8 @@ async def test_get_lock_users_service(
|
||||
"user_type": "unrestricted_user",
|
||||
"credential_rule": "single",
|
||||
"credentials": [],
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_user_index": None,
|
||||
}
|
||||
],
|
||||
@@ -745,6 +747,8 @@ async def test_get_lock_users_iterates_with_next_index(
|
||||
"user_type": "unrestricted_user",
|
||||
"credential_rule": "single",
|
||||
"credentials": [],
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_user_index": 5,
|
||||
},
|
||||
{
|
||||
@@ -755,6 +759,8 @@ async def test_get_lock_users_iterates_with_next_index(
|
||||
"user_type": "unrestricted_user",
|
||||
"credential_rule": "single",
|
||||
"credentials": [],
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_user_index": None,
|
||||
},
|
||||
],
|
||||
@@ -889,6 +895,8 @@ async def test_get_lock_users_with_credentials(
|
||||
{"type": "pin", "index": 1},
|
||||
{"type": "pin", "index": 2},
|
||||
],
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_user_index": None,
|
||||
}
|
||||
],
|
||||
@@ -942,6 +950,59 @@ async def test_get_lock_users_with_nullvalue_credentials(
|
||||
assert user["credentials"] == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_door_lock"])
|
||||
@pytest.mark.parametrize("attributes", [{"1/257/65532": _FEATURE_USR_PIN}])
|
||||
async def test_get_lock_users_with_fabric_indices(
|
||||
hass: HomeAssistant,
|
||||
matter_client: MagicMock,
|
||||
matter_node: MatterNode,
|
||||
) -> None:
|
||||
"""Test get_lock_users returns fabric indices and normalizes NullValue."""
|
||||
matter_client.send_device_command = AsyncMock(
|
||||
side_effect=[
|
||||
{
|
||||
"userIndex": 1,
|
||||
"userName": "HA User",
|
||||
"userUniqueID": None,
|
||||
"userStatus": 1,
|
||||
"userType": 0,
|
||||
"credentialRule": 0,
|
||||
"credentials": None,
|
||||
"creatorFabricIndex": 3,
|
||||
"lastModifiedFabricIndex": NullValue,
|
||||
"nextUserIndex": 2,
|
||||
},
|
||||
{
|
||||
"userIndex": 2,
|
||||
"userName": "External User",
|
||||
"userUniqueID": None,
|
||||
"userStatus": 1,
|
||||
"userType": 0,
|
||||
"credentialRule": 0,
|
||||
"credentials": None,
|
||||
"creatorFabricIndex": NullValue,
|
||||
"lastModifiedFabricIndex": 5,
|
||||
"nextUserIndex": None,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
result = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"get_lock_users",
|
||||
{ATTR_ENTITY_ID: "lock.mock_door_lock"},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
users = result["lock.mock_door_lock"]["users"]
|
||||
assert len(users) == 2
|
||||
assert users[0]["creator_fabric_index"] == 3
|
||||
assert users[0]["last_modified_fabric_index"] is None
|
||||
assert users[1]["creator_fabric_index"] is None
|
||||
assert users[1]["last_modified_fabric_index"] == 5
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_door_lock"])
|
||||
@pytest.mark.parametrize("attributes", [{"1/257/65532": _FEATURE_USR_PIN}])
|
||||
@pytest.mark.parametrize(
|
||||
@@ -1524,6 +1585,8 @@ async def test_get_lock_credential_status(
|
||||
assert result["lock.mock_door_lock"] == {
|
||||
"credential_exists": True,
|
||||
"user_index": 2,
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_credential_index": 3,
|
||||
}
|
||||
|
||||
@@ -1571,10 +1634,62 @@ async def test_get_lock_credential_status_empty_slot(
|
||||
assert result["lock.mock_door_lock"] == {
|
||||
"credential_exists": False,
|
||||
"user_index": None,
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_credential_index": None,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_door_lock"])
|
||||
@pytest.mark.parametrize("attributes", [{"1/257/65532": _FEATURE_USR_PIN}])
|
||||
@pytest.mark.parametrize(
|
||||
("creator", "last_modified", "expected_creator", "expected_last_modified"),
|
||||
[
|
||||
(3, NullValue, 3, None),
|
||||
(NullValue, 2, None, 2),
|
||||
],
|
||||
)
|
||||
async def test_get_lock_credential_status_with_fabric_indices(
|
||||
hass: HomeAssistant,
|
||||
matter_client: MagicMock,
|
||||
matter_node: MatterNode,
|
||||
creator: int,
|
||||
last_modified: int,
|
||||
expected_creator: int | None,
|
||||
expected_last_modified: int | None,
|
||||
) -> None:
|
||||
"""Test get_lock_credential_status returns fabric indices and normalizes NullValue."""
|
||||
matter_client.send_device_command = AsyncMock(
|
||||
return_value={
|
||||
"credentialExists": True,
|
||||
"userIndex": 2,
|
||||
"creatorFabricIndex": creator,
|
||||
"lastModifiedFabricIndex": last_modified,
|
||||
"nextCredentialIndex": 5,
|
||||
}
|
||||
)
|
||||
|
||||
result = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"get_lock_credential_status",
|
||||
{
|
||||
ATTR_ENTITY_ID: "lock.mock_door_lock",
|
||||
ATTR_CREDENTIAL_TYPE: "pin",
|
||||
ATTR_CREDENTIAL_INDEX: 1,
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
assert result["lock.mock_door_lock"] == {
|
||||
"credential_exists": True,
|
||||
"user_index": 2,
|
||||
"creator_fabric_index": expected_creator,
|
||||
"last_modified_fabric_index": expected_last_modified,
|
||||
"next_credential_index": 5,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_door_lock"])
|
||||
async def test_credential_services_without_usr_feature(
|
||||
hass: HomeAssistant,
|
||||
|
||||
Reference in New Issue
Block a user