Improve type hints in doods (#145426)

This commit is contained in:
epenet 2025-05-22 09:18:28 +02:00 committed by GitHub
parent 1db5c514e6
commit 8758a086c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -6,6 +6,7 @@ import io
import logging import logging
import os import os
import time import time
from typing import Any
from PIL import Image, ImageDraw, UnidentifiedImageError from PIL import Image, ImageDraw, UnidentifiedImageError
from pydoods import PyDOODS from pydoods import PyDOODS
@ -88,10 +89,11 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the Doods client.""" """Set up the Doods client."""
url = config[CONF_URL] url: str = config[CONF_URL]
auth_key = config[CONF_AUTH_KEY] auth_key: str = config[CONF_AUTH_KEY]
detector_name = config[CONF_DETECTOR] detector_name: str = config[CONF_DETECTOR]
timeout = config[CONF_TIMEOUT] source: list[dict[str, str]] = config[CONF_SOURCE]
timeout: int = config[CONF_TIMEOUT]
doods = PyDOODS(url, auth_key, timeout) doods = PyDOODS(url, auth_key, timeout)
response = doods.get_detectors() response = doods.get_detectors()
@ -113,31 +115,35 @@ def setup_platform(
add_entities( add_entities(
Doods( Doods(
hass,
camera[CONF_ENTITY_ID], camera[CONF_ENTITY_ID],
camera.get(CONF_NAME), camera.get(CONF_NAME),
doods, doods,
detector, detector,
config, config,
) )
for camera in config[CONF_SOURCE] for camera in source
) )
class Doods(ImageProcessingEntity): class Doods(ImageProcessingEntity):
"""Doods image processing service client.""" """Doods image processing service client."""
def __init__(self, hass, camera_entity, name, doods, detector, config): def __init__(
self,
camera_entity: str,
name: str | None,
doods: PyDOODS,
detector: dict[str, Any],
config: dict[str, Any],
) -> None:
"""Initialize the DOODS entity.""" """Initialize the DOODS entity."""
self.hass = hass self._attr_camera_entity = camera_entity
self._camera_entity = camera_entity
if name: if name:
self._name = name self._attr_name = name
else: else:
name = split_entity_id(camera_entity)[1] self._attr_name = f"Doods {split_entity_id(camera_entity)[1]}"
self._name = f"Doods {name}"
self._doods = doods self._doods = doods
self._file_out = config[CONF_FILE_OUT] self._file_out: list[template.Template] = config[CONF_FILE_OUT]
self._detector_name = detector["name"] self._detector_name = detector["name"]
# detector config and aspect ratio # detector config and aspect ratio
@ -150,16 +156,16 @@ class Doods(ImageProcessingEntity):
self._aspect = self._width / self._height self._aspect = self._width / self._height
# the base confidence # the base confidence
dconfig = {} dconfig: dict[str, float] = {}
confidence = config[CONF_CONFIDENCE] confidence: float = config[CONF_CONFIDENCE]
# handle labels and specific detection areas # handle labels and specific detection areas
labels = config[CONF_LABELS] labels: list[str | dict[str, Any]] = config[CONF_LABELS]
self._label_areas = {} self._label_areas = {}
self._label_covers = {} self._label_covers = {}
for label in labels: for label in labels:
if isinstance(label, dict): if isinstance(label, dict):
label_name = label[CONF_NAME] label_name: str = label[CONF_NAME]
if label_name not in detector["labels"] and label_name != "*": if label_name not in detector["labels"] and label_name != "*":
_LOGGER.warning("Detector does not support label %s", label_name) _LOGGER.warning("Detector does not support label %s", label_name)
continue continue
@ -207,28 +213,18 @@ class Doods(ImageProcessingEntity):
self._covers = area_config[CONF_COVERS] self._covers = area_config[CONF_COVERS]
self._dconfig = dconfig self._dconfig = dconfig
self._matches = {} self._matches: dict[str, list[dict[str, Any]]] = {}
self._total_matches = 0 self._total_matches = 0
self._last_image = None self._last_image = None
self._process_time = 0 self._process_time = 0.0
@property @property
def camera_entity(self): def state(self) -> int:
"""Return camera entity id from process pictures."""
return self._camera_entity
@property
def name(self):
"""Return the name of the image processor."""
return self._name
@property
def state(self):
"""Return the state of the entity.""" """Return the state of the entity."""
return self._total_matches return self._total_matches
@property @property
def extra_state_attributes(self): def extra_state_attributes(self) -> dict[str, Any]:
"""Return device specific state attributes.""" """Return device specific state attributes."""
return { return {
ATTR_MATCHES: self._matches, ATTR_MATCHES: self._matches,
@ -281,7 +277,7 @@ class Doods(ImageProcessingEntity):
os.makedirs(os.path.dirname(path), exist_ok=True) os.makedirs(os.path.dirname(path), exist_ok=True)
img.save(path) img.save(path)
def process_image(self, image): def process_image(self, image: bytes) -> None:
"""Process the image.""" """Process the image."""
try: try:
img = Image.open(io.BytesIO(bytearray(image))).convert("RGB") img = Image.open(io.BytesIO(bytearray(image))).convert("RGB")
@ -312,7 +308,7 @@ class Doods(ImageProcessingEntity):
time.monotonic() - start, time.monotonic() - start,
) )
matches = {} matches: dict[str, list[dict[str, Any]]] = {}
total_matches = 0 total_matches = 0
if not response or "error" in response: if not response or "error" in response:
@ -382,9 +378,7 @@ class Doods(ImageProcessingEntity):
paths = [] paths = []
for path_template in self._file_out: for path_template in self._file_out:
if isinstance(path_template, template.Template): if isinstance(path_template, template.Template):
paths.append( paths.append(path_template.render(camera_entity=self.camera_entity))
path_template.render(camera_entity=self._camera_entity)
)
else: else:
paths.append(path_template) paths.append(path_template)
self._save_image(image, matches, paths) self._save_image(image, matches, paths)