mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add get_user_keyring_info service to UniFi Protect integration (#133138)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
0f1835139f
commit
6c70586f7e
@ -83,3 +83,10 @@ EVENT_TYPE_FINGERPRINT_IDENTIFIED: Final = "identified"
|
|||||||
EVENT_TYPE_FINGERPRINT_NOT_IDENTIFIED: Final = "not_identified"
|
EVENT_TYPE_FINGERPRINT_NOT_IDENTIFIED: Final = "not_identified"
|
||||||
EVENT_TYPE_NFC_SCANNED: Final = "scanned"
|
EVENT_TYPE_NFC_SCANNED: Final = "scanned"
|
||||||
EVENT_TYPE_DOORBELL_RING: Final = "ring"
|
EVENT_TYPE_DOORBELL_RING: Final = "ring"
|
||||||
|
|
||||||
|
KEYRINGS_ULP_ID: Final = "ulp_id"
|
||||||
|
KEYRINGS_USER_STATUS: Final = "user_status"
|
||||||
|
KEYRINGS_USER_FULL_NAME: Final = "full_name"
|
||||||
|
KEYRINGS_KEY_TYPE: Final = "key_type"
|
||||||
|
KEYRINGS_KEY_TYPE_ID_FINGERPRINT: Final = "fingerprint_id"
|
||||||
|
KEYRINGS_KEY_TYPE_ID_NFC: Final = "nfc_id"
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
},
|
},
|
||||||
"remove_privacy_zone": {
|
"remove_privacy_zone": {
|
||||||
"service": "mdi:eye-minus"
|
"service": "mdi:eye-minus"
|
||||||
|
},
|
||||||
|
"get_user_keyring_info": {
|
||||||
|
"service": "mdi:key-chain"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,13 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||||
from homeassistant.const import ATTR_DEVICE_ID, ATTR_NAME, Platform
|
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.exceptions import HomeAssistantError, ServiceValidationError
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import (
|
||||||
config_validation as cv,
|
config_validation as cv,
|
||||||
@ -21,9 +27,19 @@ from homeassistant.helpers import (
|
|||||||
entity_registry as er,
|
entity_registry as er,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.service import async_extract_referenced_entity_ids
|
from homeassistant.helpers.service import async_extract_referenced_entity_ids
|
||||||
|
from homeassistant.util.json import JsonValueType
|
||||||
from homeassistant.util.read_only_dict import ReadOnlyDict
|
from homeassistant.util.read_only_dict import ReadOnlyDict
|
||||||
|
|
||||||
from .const import ATTR_MESSAGE, DOMAIN
|
from .const import (
|
||||||
|
ATTR_MESSAGE,
|
||||||
|
DOMAIN,
|
||||||
|
KEYRINGS_KEY_TYPE,
|
||||||
|
KEYRINGS_KEY_TYPE_ID_FINGERPRINT,
|
||||||
|
KEYRINGS_KEY_TYPE_ID_NFC,
|
||||||
|
KEYRINGS_ULP_ID,
|
||||||
|
KEYRINGS_USER_FULL_NAME,
|
||||||
|
KEYRINGS_USER_STATUS,
|
||||||
|
)
|
||||||
from .data import async_ufp_instance_for_config_entry_ids
|
from .data import async_ufp_instance_for_config_entry_ids
|
||||||
|
|
||||||
SERVICE_ADD_DOORBELL_TEXT = "add_doorbell_text"
|
SERVICE_ADD_DOORBELL_TEXT = "add_doorbell_text"
|
||||||
@ -31,12 +47,14 @@ SERVICE_REMOVE_DOORBELL_TEXT = "remove_doorbell_text"
|
|||||||
SERVICE_SET_PRIVACY_ZONE = "set_privacy_zone"
|
SERVICE_SET_PRIVACY_ZONE = "set_privacy_zone"
|
||||||
SERVICE_REMOVE_PRIVACY_ZONE = "remove_privacy_zone"
|
SERVICE_REMOVE_PRIVACY_ZONE = "remove_privacy_zone"
|
||||||
SERVICE_SET_CHIME_PAIRED = "set_chime_paired_doorbells"
|
SERVICE_SET_CHIME_PAIRED = "set_chime_paired_doorbells"
|
||||||
|
SERVICE_GET_USER_KEYRING_INFO = "get_user_keyring_info"
|
||||||
|
|
||||||
ALL_GLOBAL_SERIVCES = [
|
ALL_GLOBAL_SERIVCES = [
|
||||||
SERVICE_ADD_DOORBELL_TEXT,
|
SERVICE_ADD_DOORBELL_TEXT,
|
||||||
SERVICE_REMOVE_DOORBELL_TEXT,
|
SERVICE_REMOVE_DOORBELL_TEXT,
|
||||||
SERVICE_SET_CHIME_PAIRED,
|
SERVICE_SET_CHIME_PAIRED,
|
||||||
SERVICE_REMOVE_PRIVACY_ZONE,
|
SERVICE_REMOVE_PRIVACY_ZONE,
|
||||||
|
SERVICE_GET_USER_KEYRING_INFO,
|
||||||
]
|
]
|
||||||
|
|
||||||
DOORBELL_TEXT_SCHEMA = vol.All(
|
DOORBELL_TEXT_SCHEMA = vol.All(
|
||||||
@ -69,6 +87,15 @@ REMOVE_PRIVACY_ZONE_SCHEMA = vol.All(
|
|||||||
cv.has_at_least_one_key(ATTR_DEVICE_ID),
|
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
|
@callback
|
||||||
def _async_get_ufp_instance(hass: HomeAssistant, device_id: str) -> ProtectApiClient:
|
def _async_get_ufp_instance(hass: HomeAssistant, device_id: str) -> ProtectApiClient:
|
||||||
@ -205,26 +232,70 @@ async def set_chime_paired_doorbells(call: ServiceCall) -> None:
|
|||||||
await chime.save_device(data_before_changed)
|
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.as_list()
|
||||||
|
user_keyrings: list[JsonValueType] = [
|
||||||
|
{
|
||||||
|
KEYRINGS_USER_FULL_NAME: user.full_name,
|
||||||
|
KEYRINGS_USER_STATUS: user.status,
|
||||||
|
KEYRINGS_ULP_ID: user.ulp_id,
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
KEYRINGS_KEY_TYPE: key.registry_type,
|
||||||
|
**(
|
||||||
|
{KEYRINGS_KEY_TYPE_ID_FINGERPRINT: key.registry_id}
|
||||||
|
if key.registry_type == "fingerprint"
|
||||||
|
else {}
|
||||||
|
),
|
||||||
|
**(
|
||||||
|
{KEYRINGS_KEY_TYPE_ID_NFC: key.registry_id}
|
||||||
|
if key.registry_type == "nfc"
|
||||||
|
else {}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for key in camera.api.bootstrap.keyrings.as_list()
|
||||||
|
if key.ulp_user == user.ulp_id
|
||||||
|
],
|
||||||
|
}
|
||||||
|
for user in ulp_users
|
||||||
|
]
|
||||||
|
|
||||||
|
response: ServiceResponse = {"users": user_keyrings}
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
SERVICES = [
|
SERVICES = [
|
||||||
(
|
(
|
||||||
SERVICE_ADD_DOORBELL_TEXT,
|
SERVICE_ADD_DOORBELL_TEXT,
|
||||||
add_doorbell_text,
|
add_doorbell_text,
|
||||||
DOORBELL_TEXT_SCHEMA,
|
DOORBELL_TEXT_SCHEMA,
|
||||||
|
SupportsResponse.NONE,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
SERVICE_REMOVE_DOORBELL_TEXT,
|
SERVICE_REMOVE_DOORBELL_TEXT,
|
||||||
remove_doorbell_text,
|
remove_doorbell_text,
|
||||||
DOORBELL_TEXT_SCHEMA,
|
DOORBELL_TEXT_SCHEMA,
|
||||||
|
SupportsResponse.NONE,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
SERVICE_SET_CHIME_PAIRED,
|
SERVICE_SET_CHIME_PAIRED,
|
||||||
set_chime_paired_doorbells,
|
set_chime_paired_doorbells,
|
||||||
CHIME_PAIRED_SCHEMA,
|
CHIME_PAIRED_SCHEMA,
|
||||||
|
SupportsResponse.NONE,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
SERVICE_REMOVE_PRIVACY_ZONE,
|
SERVICE_REMOVE_PRIVACY_ZONE,
|
||||||
remove_privacy_zone,
|
remove_privacy_zone,
|
||||||
REMOVE_PRIVACY_ZONE_SCHEMA,
|
REMOVE_PRIVACY_ZONE_SCHEMA,
|
||||||
|
SupportsResponse.NONE,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SERVICE_GET_USER_KEYRING_INFO,
|
||||||
|
get_user_keyring_info,
|
||||||
|
GET_USER_KEYRING_INFO_SCHEMA,
|
||||||
|
SupportsResponse.ONLY,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -232,5 +303,7 @@ SERVICES = [
|
|||||||
def async_setup_services(hass: HomeAssistant) -> None:
|
def async_setup_services(hass: HomeAssistant) -> None:
|
||||||
"""Set up the global UniFi Protect services."""
|
"""Set up the global UniFi Protect services."""
|
||||||
|
|
||||||
for name, method, schema in SERVICES:
|
for name, method, schema, supports_response in SERVICES:
|
||||||
hass.services.async_register(DOMAIN, name, method, schema=schema)
|
hass.services.async_register(
|
||||||
|
DOMAIN, name, method, schema=schema, supports_response=supports_response
|
||||||
|
)
|
||||||
|
@ -53,3 +53,10 @@ remove_privacy_zone:
|
|||||||
required: true
|
required: true
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
get_user_keyring_info:
|
||||||
|
fields:
|
||||||
|
device_id:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
device:
|
||||||
|
integration: unifiprotect
|
||||||
|
@ -225,6 +225,16 @@
|
|||||||
"description": "The name of the zone to remove."
|
"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."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,19 @@ from uiprotect.data import Camera, Chime, Color, Light, ModelType
|
|||||||
from uiprotect.data.devices import CameraZone
|
from uiprotect.data.devices import CameraZone
|
||||||
from uiprotect.exceptions import BadRequest
|
from uiprotect.exceptions import BadRequest
|
||||||
|
|
||||||
from homeassistant.components.unifiprotect.const import ATTR_MESSAGE, DOMAIN
|
from homeassistant.components.unifiprotect.const import (
|
||||||
|
ATTR_MESSAGE,
|
||||||
|
DOMAIN,
|
||||||
|
KEYRINGS_KEY_TYPE,
|
||||||
|
KEYRINGS_KEY_TYPE_ID_FINGERPRINT,
|
||||||
|
KEYRINGS_KEY_TYPE_ID_NFC,
|
||||||
|
KEYRINGS_ULP_ID,
|
||||||
|
KEYRINGS_USER_FULL_NAME,
|
||||||
|
KEYRINGS_USER_STATUS,
|
||||||
|
)
|
||||||
from homeassistant.components.unifiprotect.services import (
|
from homeassistant.components.unifiprotect.services import (
|
||||||
SERVICE_ADD_DOORBELL_TEXT,
|
SERVICE_ADD_DOORBELL_TEXT,
|
||||||
|
SERVICE_GET_USER_KEYRING_INFO,
|
||||||
SERVICE_REMOVE_DOORBELL_TEXT,
|
SERVICE_REMOVE_DOORBELL_TEXT,
|
||||||
SERVICE_REMOVE_PRIVACY_ZONE,
|
SERVICE_REMOVE_PRIVACY_ZONE,
|
||||||
SERVICE_SET_CHIME_PAIRED,
|
SERVICE_SET_CHIME_PAIRED,
|
||||||
@ -249,3 +259,59 @@ async def test_remove_privacy_zone(
|
|||||||
)
|
)
|
||||||
ufp.api.update_device.assert_called()
|
ufp.api.update_device.assert_called()
|
||||||
assert not doorbell.privacy_zones
|
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", ulp_id="user_ulp_id")
|
||||||
|
keyring = Mock(
|
||||||
|
registry_type="nfc",
|
||||||
|
registry_id="123456",
|
||||||
|
ulp_user="user_ulp_id",
|
||||||
|
)
|
||||||
|
keyring_2 = Mock(
|
||||||
|
registry_type="fingerprint",
|
||||||
|
registry_id="2",
|
||||||
|
ulp_user="user_ulp_id",
|
||||||
|
)
|
||||||
|
ufp.api.bootstrap.ulp_users.as_list = Mock(return_value=[ulp_user])
|
||||||
|
ufp.api.bootstrap.keyrings.as_list = Mock(return_value=[keyring, keyring_2])
|
||||||
|
|
||||||
|
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 == {
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
KEYRINGS_USER_FULL_NAME: "Test User",
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
KEYRINGS_KEY_TYPE: "nfc",
|
||||||
|
KEYRINGS_KEY_TYPE_ID_NFC: "123456",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KEYRINGS_KEY_TYPE_ID_FINGERPRINT: "2",
|
||||||
|
KEYRINGS_KEY_TYPE: "fingerprint",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
KEYRINGS_USER_STATUS: "active",
|
||||||
|
KEYRINGS_ULP_ID: "user_ulp_id",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user