refactor: 全面重构腾讯云人脸识别插件
- 拆分 tencent_cloud_client.py(1311行)为 api_operations.py / image_processor.py / retry.py - 更新 HA 废弃 API: SupportsResponse.OPTIONAL, async_unload_platforms, native_value, 移除 CONNECTION_CLASS - 新增动态人员传感器管理(自动增/删) - 新增 detect_face 服务定义(services.yaml) - 移除 secret_key 持久化存储,增强安全性 - 完善错误映射前缀匹配 + 异常类型安全退避 - 修复 Coordinator 双重人员 ID 跟踪死代码 - 添加 _sanitize_error 脱敏,ImageCache LRU 缓存 - manifest.json 版本 2.1.0 + Pillow 依赖
This commit is contained in:
@@ -258,22 +258,51 @@ class QuotaExceededError(TencentFaceRecognitionError):
|
||||
|
||||
|
||||
def handle_api_error(error_code: str, error_message: str) -> None:
|
||||
"""处理API错误"""
|
||||
"""处理API错误。
|
||||
|
||||
先尝试精确匹配,再用前缀匹配,最后回退到通用 ``ApiError``。
|
||||
"""
|
||||
error_mapping = {
|
||||
"FailedOperation.ImageDecodeFailed": (ImageNotFoundError, "image_decode_failed"),
|
||||
"FailedOperation.ImageSizeTooLarge": (ImageNotFoundError, "image_too_large"),
|
||||
"InvalidParameterValue.NoFaceInPhoto": (FaceNotDetectedError, "face_not_detected"),
|
||||
"FailedOperation.NoFaceInPhoto": (FaceNotDetectedError, "face_not_detected"),
|
||||
"FailedOperation.FaceSizeTooSmall": (FaceNotDetectedError, "face_too_small"),
|
||||
"FailedOperation.PersonNotFound": (PersonNotFoundError, "person_not_found"),
|
||||
"FailedOperation.GroupNotFound": (PersonGroupNotFoundError, "group_not_found"),
|
||||
"FailedOperation.FaceNotFound": (FaceNotFoundError, "face_not_found"),
|
||||
"AuthFailure": (AuthenticationError, "auth_failure"),
|
||||
"FailedOperation.ConflictInstance": (ApiError, "invalid_parameter"),
|
||||
"AuthFailure.SignatureFailure": (AuthenticationError, "auth_failure"),
|
||||
"AuthFailure.SecretIdNotFound": (AuthenticationError, "auth_failure"),
|
||||
"RequestLimitExceeded": (RateLimitError, "rate_limit_exceeded"),
|
||||
"RequestQuotaExceeded": (QuotaExceededError, "quota_exceeded"),
|
||||
"ResourceUnavailable.NetworkError": (NetworkError, "network_error"),
|
||||
}
|
||||
|
||||
error_info = error_mapping.get(error_code, (ApiError, "api_error"))
|
||||
error_class = error_info[0]
|
||||
error_key = error_info[1]
|
||||
error_class = None
|
||||
error_key = None
|
||||
|
||||
if error_code in error_mapping:
|
||||
error_class, error_key = error_mapping[error_code]
|
||||
else:
|
||||
for prefix in ("AuthFailure", "InvalidParameter", "ResourceNotFound",
|
||||
"LimitExceeded", "FailedOperation"):
|
||||
if error_code.startswith(prefix):
|
||||
_LOGGER.debug("按前缀匹配错误码: %s -> %s", error_code, prefix)
|
||||
if prefix == "AuthFailure":
|
||||
error_class, error_key = AuthenticationError, "auth_failure"
|
||||
elif prefix == "InvalidParameter":
|
||||
error_class, error_key = ApiError, "invalid_parameter"
|
||||
elif prefix == "ResourceNotFound":
|
||||
error_class, error_key = ApiError, "api_error"
|
||||
elif prefix == "LimitExceeded":
|
||||
error_class, error_key = RateLimitError, "rate_limit_exceeded"
|
||||
else:
|
||||
error_class, error_key = ApiError, "api_error"
|
||||
break
|
||||
|
||||
if error_class is None:
|
||||
error_class, error_key = ApiError, "api_error"
|
||||
|
||||
_LOGGER.error("腾讯云API错误: %s - %s", error_code, error_message)
|
||||
|
||||
@@ -290,17 +319,28 @@ def handle_api_error(error_code: str, error_message: str) -> None:
|
||||
|
||||
|
||||
def log_and_raise_error(error_class, message: str, details: Dict[str, Any] = None, error_key: Optional[str] = None) -> None:
|
||||
"""记录日志并抛出错误"""
|
||||
if details:
|
||||
_LOGGER.error("%s: %s", error_class.__name__, message)
|
||||
else:
|
||||
_LOGGER.error("%s: %s", error_class.__name__, message)
|
||||
"""记录日志并抛出错误。
|
||||
|
||||
raise error_class(
|
||||
message,
|
||||
error_key=error_key,
|
||||
error_details=details or {}
|
||||
)
|
||||
``error_class`` 必须是 ``TencentFaceRecognitionError``(或 ``HomeAssistantError``)
|
||||
的子类,以保证上层 ``except HomeAssistantError`` 能正确捕获。
|
||||
如果传入的 ``error_class`` 无法构造为带 ``error_key``/``error_details`` 的自定义错误
|
||||
(例如 ``ValueError``),则退化为抛出 ``ApiError``,避免异常类型不可控。
|
||||
"""
|
||||
_LOGGER.error("%s: %s", error_class.__name__, message)
|
||||
|
||||
try:
|
||||
raise error_class(
|
||||
message,
|
||||
error_key=error_key,
|
||||
error_details=details or {}
|
||||
)
|
||||
except TypeError:
|
||||
# error_class 不支持 error_key/error_details 关键字参数
|
||||
raise ApiError(
|
||||
message,
|
||||
error_key=error_key,
|
||||
error_details=details or {}
|
||||
) from None
|
||||
|
||||
|
||||
def validate_config(config: Dict[str, Any]) -> None:
|
||||
|
||||
Reference in New Issue
Block a user