Add sighthound timestamped file (#32202)
* Update image_processing.py Adds save timestamp file and adds last_detection attribute * Update test_image_processing.py Adds test * Adds assert pil_img.save.call_args * Test timestamp filename * Add test bad data * Update test_image_processing.py * Fix bad image data test * Update homeassistant/components/sighthound/image_processing.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
e416f17e4d
commit
3ca97a0517
2 changed files with 111 additions and 9 deletions
|
@ -3,7 +3,7 @@ import io
|
|||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
from PIL import Image, ImageDraw, UnidentifiedImageError
|
||||
import simplehound.core as hound
|
||||
import voluptuous as vol
|
||||
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.components.image_processing import (
|
|||
from homeassistant.const import ATTR_ENTITY_ID, CONF_API_KEY
|
||||
from homeassistant.core import split_entity_id
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.util.pil import draw_box
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -27,6 +28,8 @@ ATTR_BOUNDING_BOX = "bounding_box"
|
|||
ATTR_PEOPLE = "people"
|
||||
CONF_ACCOUNT_TYPE = "account_type"
|
||||
CONF_SAVE_FILE_FOLDER = "save_file_folder"
|
||||
CONF_SAVE_TIMESTAMPTED_FILE = "save_timestamped_file"
|
||||
DATETIME_FORMAT = "%Y-%m-%d_%H:%M:%S"
|
||||
DEV = "dev"
|
||||
PROD = "prod"
|
||||
|
||||
|
@ -35,6 +38,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Optional(CONF_ACCOUNT_TYPE, default=DEV): vol.In([DEV, PROD]),
|
||||
vol.Optional(CONF_SAVE_FILE_FOLDER): cv.isdir,
|
||||
vol.Optional(CONF_SAVE_TIMESTAMPTED_FILE, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -58,7 +62,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
entities = []
|
||||
for camera in config[CONF_SOURCE]:
|
||||
sighthound = SighthoundEntity(
|
||||
api, camera[CONF_ENTITY_ID], camera.get(CONF_NAME), save_file_folder
|
||||
api,
|
||||
camera[CONF_ENTITY_ID],
|
||||
camera.get(CONF_NAME),
|
||||
save_file_folder,
|
||||
config[CONF_SAVE_TIMESTAMPTED_FILE],
|
||||
)
|
||||
entities.append(sighthound)
|
||||
add_entities(entities)
|
||||
|
@ -67,7 +75,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
class SighthoundEntity(ImageProcessingEntity):
|
||||
"""Create a sighthound entity."""
|
||||
|
||||
def __init__(self, api, camera_entity, name, save_file_folder):
|
||||
def __init__(
|
||||
self, api, camera_entity, name, save_file_folder, save_timestamped_file
|
||||
):
|
||||
"""Init."""
|
||||
self._api = api
|
||||
self._camera = camera_entity
|
||||
|
@ -77,15 +87,19 @@ class SighthoundEntity(ImageProcessingEntity):
|
|||
camera_name = split_entity_id(camera_entity)[1]
|
||||
self._name = f"sighthound_{camera_name}"
|
||||
self._state = None
|
||||
self._last_detection = None
|
||||
self._image_width = None
|
||||
self._image_height = None
|
||||
self._save_file_folder = save_file_folder
|
||||
self._save_timestamped_file = save_timestamped_file
|
||||
|
||||
def process_image(self, image):
|
||||
"""Process an image."""
|
||||
detections = self._api.detect(image)
|
||||
people = hound.get_people(detections)
|
||||
self._state = len(people)
|
||||
if self._state > 0:
|
||||
self._last_detection = dt_util.now().strftime(DATETIME_FORMAT)
|
||||
|
||||
metadata = hound.get_metadata(detections)
|
||||
self._image_width = metadata["image_width"]
|
||||
|
@ -109,7 +123,11 @@ class SighthoundEntity(ImageProcessingEntity):
|
|||
|
||||
def save_image(self, image, people, directory):
|
||||
"""Save a timestamped image with bounding boxes around targets."""
|
||||
img = Image.open(io.BytesIO(bytearray(image))).convert("RGB")
|
||||
try:
|
||||
img = Image.open(io.BytesIO(bytearray(image))).convert("RGB")
|
||||
except UnidentifiedImageError:
|
||||
_LOGGER.warning("Sighthound unable to process image, bad data")
|
||||
return
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
for person in people:
|
||||
|
@ -117,9 +135,15 @@ class SighthoundEntity(ImageProcessingEntity):
|
|||
person["boundingBox"], self._image_width, self._image_height
|
||||
)
|
||||
draw_box(draw, box, self._image_width, self._image_height)
|
||||
|
||||
latest_save_path = directory / f"{self._name}_latest.jpg"
|
||||
img.save(latest_save_path)
|
||||
|
||||
if self._save_timestamped_file:
|
||||
timestamp_save_path = directory / f"{self._name}_{self._last_detection}.jpg"
|
||||
img.save(timestamp_save_path)
|
||||
_LOGGER.info("Sighthound saved file %s", timestamp_save_path)
|
||||
|
||||
@property
|
||||
def camera_entity(self):
|
||||
"""Return camera entity id from process pictures."""
|
||||
|
@ -144,3 +168,11 @@ class SighthoundEntity(ImageProcessingEntity):
|
|||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement."""
|
||||
return ATTR_PEOPLE
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the attributes."""
|
||||
attr = {}
|
||||
if self._last_detection:
|
||||
attr["last_person"] = self._last_detection
|
||||
return attr
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue