From 8dc616fa8bf2f25fb2c9f4b8a3df6671e2365980 Mon Sep 17 00:00:00 2001 From: xyzm Date: Sun, 7 Sep 2025 23:21:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96API=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=B8=8E=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- face_recognition.py | 103 +++++++++++++++++++++++++++++++++++----- services.py | 42 ++++++++++++---- services.yaml | 7 ++- tencent_cloud_client.py | 84 +++++++++++++++++++++++++++----- 4 files changed, 200 insertions(+), 36 deletions(-) diff --git a/face_recognition.py b/face_recognition.py index b448f0c..1239057 100644 --- a/face_recognition.py +++ b/face_recognition.py @@ -44,14 +44,14 @@ class FaceRecognition: quality_control: int = 1, need_rotate_check: int = 1, face_match_threshold: float = 60.0 - ) -> List[Dict[str, Any]]: + ) -> Dict[str, Any]: """搜索人脸""" try: # 验证参数 self._validate_image_params(image_url, image_path, image_file) # 调用腾讯云API搜索人脸 - results = await self.hass.async_add_executor_job( + result = await self.hass.async_add_executor_job( self.client.search_faces, image_url, image_path, @@ -66,14 +66,43 @@ class FaceRecognition: ) # 处理结果 - return self._process_search_results(results) + if result.get("success", False): + processed_faces = self._process_search_results(result.get("faces", [])) + return { + "success": True, + "faces": processed_faces, + "error": None, + "error_code": None, + "error_message": None + } + else: + # 返回错误信息 + return { + "success": False, + "faces": [], + "error": result.get("error"), + "error_code": result.get("error_code"), + "error_message": result.get("error_message") + } except ValueError as ex: _LOGGER.error("人脸搜索参数错误: %s", ex) - raise HomeAssistantError(str(ex)) + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": "invalid_parameter", + "error_message": str(ex) + } except Exception as ex: _LOGGER.error("人脸搜索失败: %s", ex) - raise HomeAssistantError(f"人脸搜索失败: {str(ex)}") + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": "unknown_error", + "error_message": str(ex) + } def _validate_image_params(self, image_url: str, image_path: str, image_file: str) -> None: """验证图片参数""" @@ -152,14 +181,14 @@ class FaceRecognition: image_file: str = None, max_face_num: int = 1, need_rotate_check: int = 1 - ) -> List[Dict[str, Any]]: + ) -> Dict[str, Any]: """获取人脸属性""" try: # 验证参数 self._validate_image_params(image_url, image_path, image_file) # 调用腾讯云API获取人脸属性 - results = await self.hass.async_add_executor_job( + result = await self.hass.async_add_executor_job( self.client.get_face_attributes, image_url, image_path, @@ -168,10 +197,34 @@ class FaceRecognition: need_rotate_check ) - return results + # 处理结果 + if result.get("success", False): + return { + "success": True, + "faces": result.get("faces", []), + "error": None, + "error_code": None, + "error_message": None + } + else: + # 返回错误信息 + return { + "success": False, + "faces": [], + "error": result.get("error"), + "error_code": result.get("error_code"), + "error_message": result.get("error_message") + } except Exception as ex: - self._handle_api_error("获取人脸属性", ex) + _LOGGER.error("获取人脸属性失败: %s", ex) + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": "unknown_error", + "error_message": str(ex) + } # 移除 _get_face_attributes_sync 方法,因为现在直接使用客户端的 get_face_attributes 方法 @@ -183,14 +236,14 @@ class FaceRecognition: max_face_num: int = 1, min_face_size: int = 34, need_rotate_check: int = 1 - ) -> List[Dict[str, Any]]: + ) -> Dict[str, Any]: """检测人脸""" try: # 验证参数 self._validate_image_params(image_url, image_path, image_file) # 调用腾讯云API检测人脸 - results = await self.hass.async_add_executor_job( + result = await self.hass.async_add_executor_job( self.client.detect_faces, image_url, image_path, @@ -200,9 +253,33 @@ class FaceRecognition: need_rotate_check ) - return results + # 处理结果 + if result.get("success", False): + return { + "success": True, + "faces": result.get("faces", []), + "error": None, + "error_code": None, + "error_message": None + } + else: + # 返回错误信息 + return { + "success": False, + "faces": [], + "error": result.get("error"), + "error_code": result.get("error_code"), + "error_message": result.get("error_message") + } except Exception as ex: - self._handle_api_error("人脸检测", ex) + _LOGGER.error("人脸检测失败: %s", ex) + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": "unknown_error", + "error_message": str(ex) + } # 移除 _detect_faces_sync 方法,因为现在直接使用客户端的 detect_faces 方法 \ No newline at end of file diff --git a/services.py b/services.py index c8cd9e4..b97d87a 100644 --- a/services.py +++ b/services.py @@ -134,7 +134,7 @@ async def async_face_search_service(call: ServiceCall) -> Dict[str, Any]: need_rotate_check = data.get(ATTR_NEED_ROTATE_CHECK, 1) face_match_threshold = data.get(ATTR_FACE_MATCH_THRESHOLD, 60.0) - results = await face_recognition.async_search_faces( + result = await face_recognition.async_search_faces( image_url=image_url, image_path=image_path, image_file=image_file, @@ -147,11 +147,19 @@ async def async_face_search_service(call: ServiceCall) -> Dict[str, Any]: face_match_threshold=face_match_threshold ) - return {"results": results} + # 直接返回结果,包含状态和错误信息 + return result except Exception as ex: _LOGGER.error("人脸搜索服务调用失败: %s", ex) - raise HomeAssistantError(f"人脸搜索服务调用失败: {str(ex)}") + # 返回错误信息,而不是抛出异常 + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": "service_error", + "error_message": str(ex) + } async def async_detect_face_service(call: ServiceCall) -> Dict[str, Any]: @@ -184,7 +192,7 @@ async def async_detect_face_service(call: ServiceCall) -> Dict[str, Any]: min_face_size = data.get(ATTR_MIN_FACE_SIZE, 34) need_rotate_check = data.get(ATTR_NEED_ROTATE_CHECK, 1) - results = await face_recognition.async_detect_faces( + result = await face_recognition.async_detect_faces( image_url=image_url, image_path=image_path, image_file=image_file, @@ -193,11 +201,19 @@ async def async_detect_face_service(call: ServiceCall) -> Dict[str, Any]: need_rotate_check=need_rotate_check ) - return {"results": results} + # 直接返回结果,包含状态和错误信息 + return result except Exception as ex: _LOGGER.error("人脸检测服务调用失败: %s", ex) - raise HomeAssistantError(f"人脸检测服务调用失败: {str(ex)}") + # 返回错误信息,而不是抛出异常 + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": "service_error", + "error_message": str(ex) + } async def async_get_face_attributes_service(call: ServiceCall) -> Dict[str, Any]: @@ -229,7 +245,7 @@ async def async_get_face_attributes_service(call: ServiceCall) -> Dict[str, Any] max_face_num = data.get(ATTR_MAX_FACE_NUM, 1) need_rotate_check = data.get(ATTR_NEED_ROTATE_CHECK, 1) - results = await face_recognition.async_get_face_attributes( + result = await face_recognition.async_get_face_attributes( image_url=image_url, image_path=image_path, image_file=image_file, @@ -237,11 +253,19 @@ async def async_get_face_attributes_service(call: ServiceCall) -> Dict[str, Any] need_rotate_check=need_rotate_check ) - return {"results": results} + # 直接返回结果,包含状态和错误信息 + return result except Exception as ex: _LOGGER.error("获取人脸属性服务调用失败: %s", ex) - raise HomeAssistantError(f"获取人脸属性服务调用失败: {str(ex)}") + # 返回错误信息,而不是抛出异常 + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": "service_error", + "error_message": str(ex) + } def _get_config_entry(hass: HomeAssistant): diff --git a/services.yaml b/services.yaml index 8974f1f..27e31ac 100644 --- a/services.yaml +++ b/services.yaml @@ -64,9 +64,12 @@ face_search: name: 质量控制 description: 是否进行质量控制。 default: true - example: true + example: 1 selector: - boolean: + number: + min: 0 + max: 1 + mode: box need_rotate_check: name: 旋转检查 description: 是否进行旋转检查。 diff --git a/tencent_cloud_client.py b/tencent_cloud_client.py index 2e34cdf..16fa535 100644 --- a/tencent_cloud_client.py +++ b/tencent_cloud_client.py @@ -383,7 +383,7 @@ class TencentCloudClient: quality_control: int = 1, need_rotate_check: int = 1, face_match_threshold: float = 60.0 - ) -> List[Dict[str, Any]]: + ) -> Dict[str, Any]: """搜索人脸""" _LOGGER.info( "开始搜索人脸: image_url=%s, image_path=%s, image_file is not None: %s, max_face_num=%d, min_face_size=%d, max_user_num=%d, quality_control=%d, need_rotate_check=%d, face_match_threshold=%.2f", @@ -463,7 +463,13 @@ class TencentCloudClient: else: _LOGGER.info("未检测到人脸") - return results + return { + "success": True, + "faces": results, + "error": None, + "error_code": None, + "error_message": None + } except Exception as ex: # 计算请求耗时 @@ -472,8 +478,22 @@ class TencentCloudClient: # 记录错误日志 self._log_error("人脸搜索", request_id, ex, duration) - # 重新抛出异常 - raise + # 不抛出异常,而是返回包含错误信息的结构化结果 + error_code = None + error_message = str(ex) + + # 如果是腾讯云API异常,提取错误码和消息 + if isinstance(ex, TencentCloudSDKException): + error_code = getattr(ex, "code", None) + error_message = getattr(ex, "message", str(ex)) + + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": error_code, + "error_message": error_message + } # 执行带重试的API调用 return self._execute_with_retry(api_call, "人脸搜索") @@ -486,7 +506,7 @@ class TencentCloudClient: max_face_num: int = 1, min_face_size: int = 34, need_rotate_check: int = 1 - ) -> List[Dict[str, Any]]: + ) -> Dict[str, Any]: """检测人脸""" _LOGGER.info( "开始检测人脸: max_face_num=%d, min_face_size=%d, need_rotate_check=%d", @@ -544,7 +564,13 @@ class TencentCloudClient: else: _LOGGER.info("未检测到人脸") - return results + return { + "success": True, + "faces": results, + "error": None, + "error_code": None, + "error_message": None + } except Exception as ex: # 计算请求耗时 @@ -553,8 +579,22 @@ class TencentCloudClient: # 记录错误日志 self._log_error("人脸检测", request_id, ex, duration) - # 重新抛出异常 - raise + # 不抛出异常,而是返回包含错误信息的结构化结果 + error_code = None + error_message = str(ex) + + # 如果是腾讯云API异常,提取错误码和消息 + if isinstance(ex, TencentCloudSDKException): + error_code = getattr(ex, "code", None) + error_message = getattr(ex, "message", str(ex)) + + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": error_code, + "error_message": error_message + } # 执行带重试的API调用 return self._execute_with_retry(api_call, "人脸检测") @@ -566,7 +606,7 @@ class TencentCloudClient: image_file: str = None, max_face_num: int = 1, need_rotate_check: int = 1 - ) -> List[Dict[str, Any]]: + ) -> Dict[str, Any]: """获取人脸属性""" _LOGGER.info( "开始获取人脸属性: max_face_num=%d, need_rotate_check=%d", @@ -627,7 +667,13 @@ class TencentCloudClient: else: _LOGGER.info("未检测到人脸") - return results + return { + "success": True, + "faces": results, + "error": None, + "error_code": None, + "error_message": None + } except Exception as ex: # 计算请求耗时 @@ -636,8 +682,22 @@ class TencentCloudClient: # 记录错误日志 self._log_error("获取人脸属性", request_id, ex, duration) - # 重新抛出异常 - raise + # 不抛出异常,而是返回包含错误信息的结构化结果 + error_code = None + error_message = str(ex) + + # 如果是腾讯云API异常,提取错误码和消息 + if isinstance(ex, TencentCloudSDKException): + error_code = getattr(ex, "code", None) + error_message = getattr(ex, "message", str(ex)) + + return { + "success": False, + "faces": [], + "error": str(ex), + "error_code": error_code, + "error_message": error_message + } # 执行带重试的API调用 return self._execute_with_retry(api_call, "获取人脸属性")