Files
tencent_face_recognition/services.py
T

503 lines
17 KiB
Python
Raw Normal View History

2025-09-07 21:48:21 +08:00
"""服务定义"""
import logging
from typing import Dict, Any, List, Optional
import voluptuous as vol
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import HomeAssistantError
2025-09-07 21:48:21 +08:00
from homeassistant.helpers import config_validation as cv
from .const import (
DOMAIN,
SERVICE_FACE_SEARCH,
SERVICE_DETECT_FACE,
SERVICE_GET_FACE_ATTRIBUTES,
SERVICE_CREATE_PERSON,
SERVICE_DELETE_PERSON,
SERVICE_CREATE_FACE,
SERVICE_DELETE_FACE,
2025-09-07 21:48:21 +08:00
ATTR_IMAGE_URL,
ATTR_IMAGE_FILE,
ATTR_IMAGE_PATH,
ATTR_CAMERA_ENTITY_ID,
ATTR_CONFIG_ENTRY_ID,
ATTR_GROUP_ID,
2025-09-07 21:48:21 +08:00
ATTR_MAX_FACE_NUM,
ATTR_MIN_FACE_SIZE,
ATTR_MAX_USER_NUM,
ATTR_QUALITY_CONTROL,
ATTR_NEED_ROTATE_CHECK,
ATTR_FACE_MATCH_THRESHOLD,
ATTR_PERSON_ID,
ATTR_PERSON_NAME,
ATTR_GENDER,
ATTR_FACE_ID,
ATTR_PERSON_TAG,
EVENT_FACE_DETECTED,
2025-09-07 21:48:21 +08:00
)
_LOGGER = logging.getLogger(__name__)
FACE_SEARCH_SCHEMA = vol.Schema({
vol.Required(ATTR_GROUP_ID): cv.string,
2025-09-07 21:48:21 +08:00
vol.Optional(ATTR_IMAGE_URL): cv.string,
vol.Optional(ATTR_IMAGE_PATH): cv.string,
vol.Optional(ATTR_IMAGE_FILE): cv.string,
vol.Optional(ATTR_CAMERA_ENTITY_ID): cv.string,
vol.Optional(ATTR_CONFIG_ENTRY_ID): cv.string,
2025-09-07 21:48:21 +08:00
vol.Optional(ATTR_MAX_FACE_NUM, default=1): cv.positive_int,
vol.Optional(ATTR_MIN_FACE_SIZE, default=34): cv.positive_int,
vol.Optional(ATTR_MAX_USER_NUM, default=5): cv.positive_int,
vol.Optional(ATTR_QUALITY_CONTROL, default=1): vol.In([0, 1]),
vol.Optional(ATTR_NEED_ROTATE_CHECK, default=1): vol.In([0, 1]),
vol.Optional(ATTR_FACE_MATCH_THRESHOLD, default=60.0): vol.Range(min=0, max=100),
})
DETECT_FACE_SCHEMA = vol.Schema({
vol.Optional(ATTR_IMAGE_URL): cv.string,
vol.Optional(ATTR_IMAGE_PATH): cv.string,
vol.Optional(ATTR_IMAGE_FILE): cv.string,
vol.Optional(ATTR_CAMERA_ENTITY_ID): cv.string,
vol.Optional(ATTR_CONFIG_ENTRY_ID): cv.string,
2025-09-07 21:48:21 +08:00
vol.Optional(ATTR_MAX_FACE_NUM, default=1): cv.positive_int,
vol.Optional(ATTR_MIN_FACE_SIZE, default=34): cv.positive_int,
vol.Optional(ATTR_NEED_ROTATE_CHECK, default=1): vol.In([0, 1]),
})
GET_FACE_ATTRIBUTES_SCHEMA = vol.Schema({
vol.Optional(ATTR_IMAGE_URL): cv.string,
vol.Optional(ATTR_IMAGE_PATH): cv.string,
vol.Optional(ATTR_IMAGE_FILE): cv.string,
vol.Optional(ATTR_CAMERA_ENTITY_ID): cv.string,
vol.Optional(ATTR_CONFIG_ENTRY_ID): cv.string,
2025-09-07 21:48:21 +08:00
vol.Optional(ATTR_MAX_FACE_NUM, default=1): cv.positive_int,
vol.Optional(ATTR_NEED_ROTATE_CHECK, default=1): vol.In([0, 1]),
})
CREATE_PERSON_SCHEMA = vol.Schema({
vol.Required(ATTR_PERSON_ID): cv.string,
vol.Required(ATTR_PERSON_NAME): cv.string,
vol.Required(ATTR_GROUP_ID): cv.string,
vol.Optional(ATTR_IMAGE_URL): cv.string,
vol.Optional(ATTR_IMAGE_PATH): cv.string,
vol.Optional(ATTR_IMAGE_FILE): cv.string,
vol.Optional(ATTR_CAMERA_ENTITY_ID): cv.string,
vol.Optional(ATTR_CONFIG_ENTRY_ID): cv.string,
vol.Optional(ATTR_GENDER): vol.In([0, 1]),
vol.Optional(ATTR_PERSON_TAG): cv.string,
vol.Optional(ATTR_QUALITY_CONTROL, default=1): vol.In([0, 1]),
vol.Optional(ATTR_NEED_ROTATE_CHECK, default=1): vol.In([0, 1]),
})
DELETE_PERSON_SCHEMA = vol.Schema({
vol.Required(ATTR_PERSON_ID): cv.string,
vol.Optional(ATTR_CONFIG_ENTRY_ID): cv.string,
})
CREATE_FACE_SCHEMA = vol.Schema({
vol.Required(ATTR_PERSON_ID): cv.string,
vol.Optional(ATTR_IMAGE_URL): cv.string,
vol.Optional(ATTR_IMAGE_PATH): cv.string,
vol.Optional(ATTR_IMAGE_FILE): cv.string,
vol.Optional(ATTR_CAMERA_ENTITY_ID): cv.string,
vol.Optional(ATTR_CONFIG_ENTRY_ID): cv.string,
vol.Optional(ATTR_QUALITY_CONTROL, default=1): vol.In([0, 1]),
vol.Optional(ATTR_NEED_ROTATE_CHECK, default=1): vol.In([0, 1]),
})
DELETE_FACE_SCHEMA = vol.Schema({
vol.Required(ATTR_PERSON_ID): cv.string,
vol.Required(ATTR_FACE_ID): cv.string,
vol.Optional(ATTR_CONFIG_ENTRY_ID): cv.string,
})
2025-09-07 21:48:21 +08:00
async def async_setup_services(hass: HomeAssistant) -> None:
"""设置服务"""
if hass.services.has_service(DOMAIN, SERVICE_FACE_SEARCH):
return
2025-09-07 21:48:21 +08:00
_LOGGER.info("设置腾讯云人脸识别服务")
hass.services.async_register(
DOMAIN,
SERVICE_FACE_SEARCH,
async_face_search_service,
schema=FACE_SEARCH_SCHEMA,
supports_response=True
)
hass.services.async_register(
DOMAIN,
SERVICE_DETECT_FACE,
async_detect_face_service,
schema=DETECT_FACE_SCHEMA,
supports_response=True
2025-09-07 21:48:21 +08:00
)
hass.services.async_register(
DOMAIN,
SERVICE_GET_FACE_ATTRIBUTES,
async_get_face_attributes_service,
schema=GET_FACE_ATTRIBUTES_SCHEMA,
supports_response=True
)
hass.services.async_register(
DOMAIN,
SERVICE_CREATE_PERSON,
async_create_person_service,
schema=CREATE_PERSON_SCHEMA,
supports_response=True
)
hass.services.async_register(
DOMAIN,
SERVICE_DELETE_PERSON,
async_delete_person_service,
schema=DELETE_PERSON_SCHEMA,
supports_response=True
)
hass.services.async_register(
DOMAIN,
SERVICE_CREATE_FACE,
async_create_face_service,
schema=CREATE_FACE_SCHEMA,
supports_response=True
)
hass.services.async_register(
DOMAIN,
SERVICE_DELETE_FACE,
async_delete_face_service,
schema=DELETE_FACE_SCHEMA,
supports_response=True
2025-09-07 21:48:21 +08:00
)
async def async_unload_services(hass: HomeAssistant) -> None:
"""卸载服务"""
if any(hass.config_entries.async_entries(DOMAIN)):
return
2025-09-07 21:48:21 +08:00
_LOGGER.info("卸载腾讯云人脸识别服务")
for service_name in [
SERVICE_FACE_SEARCH,
SERVICE_DETECT_FACE,
SERVICE_GET_FACE_ATTRIBUTES,
SERVICE_CREATE_PERSON,
SERVICE_DELETE_PERSON,
SERVICE_CREATE_FACE,
SERVICE_DELETE_FACE,
]:
hass.services.async_remove(DOMAIN, service_name)
def _get_entry_data(hass: HomeAssistant, config_entry_id: str = None):
"""获取配置项数据,支持多配置条目"""
domain_data = hass.data.get(DOMAIN, {})
if config_entry_id:
data = domain_data.get(config_entry_id)
if data and isinstance(data, dict) and "face_recognition" in data:
return data
return None
for entry_id, data in domain_data.items():
if isinstance(data, dict) and "face_recognition" in data:
return data
return None
2025-09-07 21:48:21 +08:00
async def async_face_search_service(call: ServiceCall) -> Dict[str, Any]:
"""人脸搜索服务"""
hass = call.hass
data = call.data
entry_data = _get_entry_data(hass, data.get(ATTR_CONFIG_ENTRY_ID))
if not entry_data:
2025-09-07 21:48:21 +08:00
raise HomeAssistantError("未找到腾讯云人脸识别配置")
face_recognition = entry_data.get("face_recognition")
if not face_recognition:
raise HomeAssistantError("腾讯云人脸识别插件未正确初始化")
try:
group_id = data.get(ATTR_GROUP_ID)
2025-09-07 21:48:21 +08:00
image_url = data.get(ATTR_IMAGE_URL)
image_path = data.get(ATTR_IMAGE_PATH)
image_file = data.get(ATTR_IMAGE_FILE)
camera_entity_id = data.get(ATTR_CAMERA_ENTITY_ID)
2025-09-07 21:48:21 +08:00
max_face_num = data.get(ATTR_MAX_FACE_NUM, 1)
min_face_size = data.get(ATTR_MIN_FACE_SIZE, 34)
max_user_num = data.get(ATTR_MAX_USER_NUM, 5)
quality_control = data.get(ATTR_QUALITY_CONTROL, 1)
need_rotate_check = data.get(ATTR_NEED_ROTATE_CHECK, 1)
face_match_threshold = data.get(ATTR_FACE_MATCH_THRESHOLD, 60.0)
2025-09-07 23:21:24 +08:00
result = await face_recognition.async_search_faces(
2025-09-07 21:48:21 +08:00
image_url=image_url,
image_path=image_path,
image_file=image_file,
camera_entity_id=camera_entity_id,
2025-09-07 21:48:21 +08:00
group_ids=[group_id],
max_face_num=max_face_num,
min_face_size=min_face_size,
max_user_num=max_user_num,
quality_control=quality_control,
need_rotate_check=need_rotate_check,
face_match_threshold=face_match_threshold
)
if result.get("success", False):
for face in result.get("faces", []):
for candidate in face.get("candidates", []):
hass.bus.async_fire(EVENT_FACE_DETECTED, {
ATTR_PERSON_ID: candidate.get("person_id"),
ATTR_PERSON_NAME: candidate.get("person_name"),
"score": candidate.get("score"),
ATTR_PERSON_TAG: candidate.get("person_tag"),
ATTR_GROUP_ID: group_id,
ATTR_CAMERA_ENTITY_ID: camera_entity_id,
})
2025-09-07 23:21:24 +08:00
return result
2025-09-07 21:48:21 +08:00
except HomeAssistantError:
raise
2025-09-07 21:48:21 +08:00
except Exception as ex:
_LOGGER.error("人脸搜索服务调用失败: %s", ex)
raise HomeAssistantError(f"人脸搜索服务调用失败: {ex}")
2025-09-07 21:48:21 +08:00
async def async_detect_face_service(call: ServiceCall) -> Dict[str, Any]:
"""人脸检测服务"""
hass = call.hass
data = call.data
entry_data = _get_entry_data(hass, data.get(ATTR_CONFIG_ENTRY_ID))
if not entry_data:
2025-09-07 21:48:21 +08:00
raise HomeAssistantError("未找到腾讯云人脸识别配置")
face_recognition = entry_data.get("face_recognition")
if not face_recognition:
raise HomeAssistantError("腾讯云人脸识别插件未正确初始化")
try:
image_url = data.get(ATTR_IMAGE_URL)
image_path = data.get(ATTR_IMAGE_PATH)
image_file = data.get(ATTR_IMAGE_FILE)
camera_entity_id = data.get(ATTR_CAMERA_ENTITY_ID)
2025-09-07 21:48:21 +08:00
max_face_num = data.get(ATTR_MAX_FACE_NUM, 1)
min_face_size = data.get(ATTR_MIN_FACE_SIZE, 34)
need_rotate_check = data.get(ATTR_NEED_ROTATE_CHECK, 1)
2025-09-07 23:21:24 +08:00
result = await face_recognition.async_detect_faces(
2025-09-07 21:48:21 +08:00
image_url=image_url,
image_path=image_path,
image_file=image_file,
camera_entity_id=camera_entity_id,
2025-09-07 21:48:21 +08:00
max_face_num=max_face_num,
min_face_size=min_face_size,
need_rotate_check=need_rotate_check
)
if not result.get("success", False):
error_msg = result.get("error_message", result.get("error", "未知错误"))
raise HomeAssistantError(f"人脸检测失败: {error_msg}")
2025-09-07 23:21:24 +08:00
return result
2025-09-07 21:48:21 +08:00
except HomeAssistantError:
raise
2025-09-07 21:48:21 +08:00
except Exception as ex:
_LOGGER.error("人脸检测服务调用失败: %s", ex)
raise HomeAssistantError(f"人脸检测服务调用失败: {ex}")
2025-09-07 21:48:21 +08:00
async def async_get_face_attributes_service(call: ServiceCall) -> Dict[str, Any]:
"""获取人脸属性服务"""
hass = call.hass
data = call.data
entry_data = _get_entry_data(hass, data.get(ATTR_CONFIG_ENTRY_ID))
if not entry_data:
2025-09-07 21:48:21 +08:00
raise HomeAssistantError("未找到腾讯云人脸识别配置")
face_recognition = entry_data.get("face_recognition")
if not face_recognition:
raise HomeAssistantError("腾讯云人脸识别插件未正确初始化")
try:
image_url = data.get(ATTR_IMAGE_URL)
image_path = data.get(ATTR_IMAGE_PATH)
image_file = data.get(ATTR_IMAGE_FILE)
camera_entity_id = data.get(ATTR_CAMERA_ENTITY_ID)
2025-09-07 21:48:21 +08:00
max_face_num = data.get(ATTR_MAX_FACE_NUM, 1)
need_rotate_check = data.get(ATTR_NEED_ROTATE_CHECK, 1)
2025-09-07 23:21:24 +08:00
result = await face_recognition.async_get_face_attributes(
2025-09-07 21:48:21 +08:00
image_url=image_url,
image_path=image_path,
image_file=image_file,
camera_entity_id=camera_entity_id,
2025-09-07 21:48:21 +08:00
max_face_num=max_face_num,
need_rotate_check=need_rotate_check
)
if not result.get("success", False):
error_msg = result.get("error_message", result.get("error", "未知错误"))
raise HomeAssistantError(f"获取人脸属性失败: {error_msg}")
2025-09-07 23:21:24 +08:00
return result
2025-09-07 21:48:21 +08:00
except HomeAssistantError:
raise
2025-09-07 21:48:21 +08:00
except Exception as ex:
_LOGGER.error("获取人脸属性服务调用失败: %s", ex)
raise HomeAssistantError(f"获取人脸属性服务调用失败: {ex}")
2025-09-07 21:48:21 +08:00
async def async_create_person_service(call: ServiceCall) -> Dict[str, Any]:
"""创建人员服务"""
hass = call.hass
data = call.data
2025-09-07 21:48:21 +08:00
entry_data = _get_entry_data(hass, data.get(ATTR_CONFIG_ENTRY_ID))
if not entry_data:
raise HomeAssistantError("未找到腾讯云人脸识别配置")
face_recognition = entry_data.get("face_recognition")
if not face_recognition:
raise HomeAssistantError("腾讯云人脸识别插件未正确初始化")
try:
result = await face_recognition.async_create_person(
person_id=data.get(ATTR_PERSON_ID),
person_name=data.get(ATTR_PERSON_NAME),
group_id=data.get(ATTR_GROUP_ID),
image_url=data.get(ATTR_IMAGE_URL),
image_path=data.get(ATTR_IMAGE_PATH),
image_file=data.get(ATTR_IMAGE_FILE),
camera_entity_id=data.get(ATTR_CAMERA_ENTITY_ID),
gender=data.get(ATTR_GENDER),
person_tag=data.get(ATTR_PERSON_TAG),
quality_control=data.get(ATTR_QUALITY_CONTROL, 1),
need_rotate_check=data.get(ATTR_NEED_ROTATE_CHECK, 1),
)
if not result.get("success", False):
error_msg = result.get("error_message", result.get("error", "未知错误"))
raise HomeAssistantError(f"创建人员失败: {error_msg}")
return result
except HomeAssistantError:
raise
except Exception as ex:
_LOGGER.error("创建人员服务调用失败: %s", ex)
raise HomeAssistantError(f"创建人员服务调用失败: {ex}")
async def async_delete_person_service(call: ServiceCall) -> Dict[str, Any]:
"""删除人员服务"""
hass = call.hass
data = call.data
entry_data = _get_entry_data(hass, data.get(ATTR_CONFIG_ENTRY_ID))
if not entry_data:
raise HomeAssistantError("未找到腾讯云人脸识别配置")
face_recognition = entry_data.get("face_recognition")
if not face_recognition:
raise HomeAssistantError("腾讯云人脸识别插件未正确初始化")
try:
result = await face_recognition.async_delete_person(
person_id=data.get(ATTR_PERSON_ID),
)
if not result.get("success", False):
error_msg = result.get("error_message", result.get("error", "未知错误"))
raise HomeAssistantError(f"删除人员失败: {error_msg}")
return result
except HomeAssistantError:
raise
except Exception as ex:
_LOGGER.error("删除人员服务调用失败: %s", ex)
raise HomeAssistantError(f"删除人员服务调用失败: {ex}")
async def async_create_face_service(call: ServiceCall) -> Dict[str, Any]:
"""注册人脸服务"""
hass = call.hass
data = call.data
entry_data = _get_entry_data(hass, data.get(ATTR_CONFIG_ENTRY_ID))
if not entry_data:
raise HomeAssistantError("未找到腾讯云人脸识别配置")
face_recognition = entry_data.get("face_recognition")
if not face_recognition:
raise HomeAssistantError("腾讯云人脸识别插件未正确初始化")
try:
result = await face_recognition.async_create_face(
person_id=data.get(ATTR_PERSON_ID),
image_url=data.get(ATTR_IMAGE_URL),
image_path=data.get(ATTR_IMAGE_PATH),
image_file=data.get(ATTR_IMAGE_FILE),
camera_entity_id=data.get(ATTR_CAMERA_ENTITY_ID),
quality_control=data.get(ATTR_QUALITY_CONTROL, 1),
need_rotate_check=data.get(ATTR_NEED_ROTATE_CHECK, 1),
)
if not result.get("success", False):
error_msg = result.get("error_message", result.get("error", "未知错误"))
raise HomeAssistantError(f"注册人脸失败: {error_msg}")
return result
except HomeAssistantError:
raise
except Exception as ex:
_LOGGER.error("注册人脸服务调用失败: %s", ex)
raise HomeAssistantError(f"注册人脸服务调用失败: {ex}")
async def async_delete_face_service(call: ServiceCall) -> Dict[str, Any]:
"""删除人脸服务"""
hass = call.hass
data = call.data
entry_data = _get_entry_data(hass, data.get(ATTR_CONFIG_ENTRY_ID))
if not entry_data:
raise HomeAssistantError("未找到腾讯云人脸识别配置")
face_recognition = entry_data.get("face_recognition")
if not face_recognition:
raise HomeAssistantError("腾讯云人脸识别插件未正确初始化")
try:
result = await face_recognition.async_delete_face(
person_id=data.get(ATTR_PERSON_ID),
face_id=data.get(ATTR_FACE_ID),
)
if not result.get("success", False):
error_msg = result.get("error_message", result.get("error", "未知错误"))
raise HomeAssistantError(f"删除人脸失败: {error_msg}")
return result
except HomeAssistantError:
raise
except Exception as ex:
_LOGGER.error("删除人脸服务调用失败: %s", ex)
raise HomeAssistantError(f"删除人脸服务调用失败: {ex}")