Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add get_user_keyring_info service to UniFi Protect integration #133138

3 changes: 3 additions & 0 deletions homeassistant/components/unifiprotect/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
},
"remove_privacy_zone": {
"service": "mdi:eye-minus"
},
"get_user_keyring_info": {
"service": "mdi:key-chain"
}
}
}
52 changes: 49 additions & 3 deletions homeassistant/components/unifiprotect/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@

from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.const import ATTR_DEVICE_ID, ATTR_NAME, Platform
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.core import (
HomeAssistant,
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import (
config_validation as cv,
Expand All @@ -31,12 +37,14 @@
SERVICE_SET_PRIVACY_ZONE = "set_privacy_zone"
SERVICE_REMOVE_PRIVACY_ZONE = "remove_privacy_zone"
SERVICE_SET_CHIME_PAIRED = "set_chime_paired_doorbells"
SERVICE_GET_USER_KEYRING_INFO = "get_user_keyring_info"

ALL_GLOBAL_SERIVCES = [
SERVICE_ADD_DOORBELL_TEXT,
SERVICE_REMOVE_DOORBELL_TEXT,
SERVICE_SET_CHIME_PAIRED,
SERVICE_REMOVE_PRIVACY_ZONE,
SERVICE_GET_USER_KEYRING_INFO,
]

DOORBELL_TEXT_SCHEMA = vol.All(
Expand Down Expand Up @@ -69,6 +77,15 @@
cv.has_at_least_one_key(ATTR_DEVICE_ID),
)

GET_USER_KEYRING_INFO_SCHEMA = vol.All(
vol.Schema(
{
**cv.ENTITY_SERVICE_FIELDS,
},
),
cv.has_at_least_one_key(ATTR_DEVICE_ID),
)


@callback
def _async_get_ufp_instance(hass: HomeAssistant, device_id: str) -> ProtectApiClient:
Expand Down Expand Up @@ -205,32 +222,61 @@ async def set_chime_paired_doorbells(call: ServiceCall) -> None:
await chime.save_device(data_before_changed)


async def get_user_keyring_info(call: ServiceCall) -> ServiceResponse:
"""Get the user keyring info."""
camera = _async_get_ufp_camera(call)
ulp_users = camera.api.bootstrap.ulp_users
return {
str(index + 1): {
"full_name": user.full_name,
"user_status": user.status,
"key_type": key.registry_type,
**({"nfc_id": key.registry_id} if key.registry_type == "nfc" else {}),
"user_ulp_id": key.ulp_user,
}
RaHehl marked this conversation as resolved.
Show resolved Hide resolved
for index, key in enumerate(camera.api.bootstrap.keyrings.as_list())
if (user := ulp_users.by_ulp_id(key.ulp_user))
}


SERVICES = [
(
SERVICE_ADD_DOORBELL_TEXT,
add_doorbell_text,
DOORBELL_TEXT_SCHEMA,
SupportsResponse.NONE,
),
(
SERVICE_REMOVE_DOORBELL_TEXT,
remove_doorbell_text,
DOORBELL_TEXT_SCHEMA,
SupportsResponse.NONE,
),
(
SERVICE_SET_CHIME_PAIRED,
set_chime_paired_doorbells,
CHIME_PAIRED_SCHEMA,
SupportsResponse.NONE,
),
(
SERVICE_REMOVE_PRIVACY_ZONE,
remove_privacy_zone,
REMOVE_PRIVACY_ZONE_SCHEMA,
SupportsResponse.NONE,
),
(
SERVICE_GET_USER_KEYRING_INFO,
get_user_keyring_info,
GET_USER_KEYRING_INFO_SCHEMA,
SupportsResponse.ONLY,
),
]


def async_setup_services(hass: HomeAssistant) -> None:
"""Set up the global UniFi Protect services."""

for name, method, schema in SERVICES:
hass.services.async_register(DOMAIN, name, method, schema=schema)
for name, method, schema, supports_response in SERVICES:
hass.services.async_register(
DOMAIN, name, method, schema=schema, supports_response=supports_response
)
7 changes: 7 additions & 0 deletions homeassistant/components/unifiprotect/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,10 @@ remove_privacy_zone:
required: true
selector:
text:
get_user_keyring_info:
fields:
device_id:
required: true
selector:
device:
integration: unifiprotect
10 changes: 10 additions & 0 deletions homeassistant/components/unifiprotect/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@
"description": "The name of the zone to remove."
}
}
},
"get_user_keyring_info": {
"name": "Retrieve Keyring Details for Users",
"description": "Fetch a detailed list of users with NFC and fingerprint associations for automations.",
"fields": {
"device_id": {
"name": "UniFi Protect NVR",
"description": "Any device from the UniFi Protect instance you want to retrieve keyring details. This is useful for systems with multiple Protect instances."
}
}
}
}
}
42 changes: 42 additions & 0 deletions tests/components/unifiprotect/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from homeassistant.components.unifiprotect.const import ATTR_MESSAGE, DOMAIN
from homeassistant.components.unifiprotect.services import (
SERVICE_ADD_DOORBELL_TEXT,
SERVICE_GET_USER_KEYRING_INFO,
SERVICE_REMOVE_DOORBELL_TEXT,
SERVICE_REMOVE_PRIVACY_ZONE,
SERVICE_SET_CHIME_PAIRED,
Expand Down Expand Up @@ -249,3 +250,44 @@ async def test_remove_privacy_zone(
)
ufp.api.update_device.assert_called()
assert not doorbell.privacy_zones


@pytest.mark.asyncio
async def test_get_doorbell_user(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
ufp: MockUFPFixture,
doorbell: Camera,
) -> None:
"""Test get_doorbell_user service."""

ulp_user = Mock(full_name="Test User", status="active")
keyring = Mock(
registry_type="nfc",
registry_id="123456",
ulp_user="user_ulp_id",
)
ufp.api.bootstrap.ulp_users.by_ulp_id = Mock(return_value=ulp_user)
ufp.api.bootstrap.keyrings.as_list = Mock(return_value=[keyring])

await init_entry(hass, ufp, [doorbell])

camera_entry = entity_registry.async_get("binary_sensor.test_camera_doorbell")

response = await hass.services.async_call(
DOMAIN,
SERVICE_GET_USER_KEYRING_INFO,
{ATTR_DEVICE_ID: camera_entry.device_id},
blocking=True,
return_response=True,
)

assert response == {
"1": {
"full_name": "Test User",
"user_status": "active",
"key_type": "nfc",
"nfc_id": "123456",
"user_ulp_id": "user_ulp_id",
}
}
Loading