1 """Person detection using Sighthound cloud service."""
3 from __future__
import annotations
7 from pathlib
import Path
9 from PIL
import Image, ImageDraw, UnidentifiedImageError
10 import simplehound.core
as hound
11 import voluptuous
as vol
14 PLATFORM_SCHEMA
as IMAGE_PROCESSING_PLATFORM_SCHEMA,
15 ImageProcessingEntity,
31 _LOGGER = logging.getLogger(__name__)
33 EVENT_PERSON_DETECTED =
"sighthound.person_detected"
35 ATTR_BOUNDING_BOX =
"bounding_box"
36 ATTR_PEOPLE =
"people"
37 CONF_ACCOUNT_TYPE =
"account_type"
38 CONF_SAVE_FILE_FOLDER =
"save_file_folder"
39 CONF_SAVE_TIMESTAMPTED_FILE =
"save_timestamped_file"
40 DATETIME_FORMAT =
"%Y-%m-%d_%H:%M:%S"
44 PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA.extend(
46 vol.Required(CONF_API_KEY): cv.string,
47 vol.Optional(CONF_ACCOUNT_TYPE, default=DEV): vol.In([DEV, PROD]),
48 vol.Optional(CONF_SAVE_FILE_FOLDER): cv.isdir,
49 vol.Optional(CONF_SAVE_TIMESTAMPTED_FILE, default=
False): cv.boolean,
57 add_entities: AddEntitiesCallback,
58 discovery_info: DiscoveryInfoType |
None =
None,
60 """Set up the platform."""
62 api_key = config[CONF_API_KEY]
63 account_type = config[CONF_ACCOUNT_TYPE]
64 api = hound.cloud(api_key, account_type)
67 except hound.SimplehoundException
as exc:
68 _LOGGER.error(
"Sighthound error %s setup aborted", exc)
71 if save_file_folder := config.get(CONF_SAVE_FILE_FOLDER):
72 save_file_folder = Path(save_file_folder)
75 for camera
in config[CONF_SOURCE]:
78 camera[CONF_ENTITY_ID],
79 camera.get(CONF_NAME),
81 config[CONF_SAVE_TIMESTAMPTED_FILE],
83 entities.append(sighthound)
88 """Create a sighthound entity."""
90 _attr_should_poll =
False
91 _attr_unit_of_measurement = ATTR_PEOPLE
94 self, api, camera_entity, name, save_file_folder, save_timestamped_file
103 self.
_name_name = f
"sighthound_{camera_name}"
112 """Process an image."""
114 people = hound.get_people(detections)
115 self.
_state_state = len(people)
117 self.
_last_detection_last_detection = dt_util.now().strftime(DATETIME_FORMAT)
119 metadata = hound.get_metadata(detections)
122 for person
in people:
128 """Send event with detected total_persons."""
129 self.
hasshass.bus.fire(
130 EVENT_PERSON_DETECTED,
133 ATTR_BOUNDING_BOX: hound.bbox_to_tf_style(
140 """Save a timestamped image with bounding boxes around targets."""
142 img = Image.open(io.BytesIO(bytearray(image))).convert(
"RGB")
143 except UnidentifiedImageError:
144 _LOGGER.warning(
"Sighthound unable to process image, bad data")
146 draw = ImageDraw.Draw(img)
148 for person
in people:
149 box = hound.bbox_to_tf_style(
154 latest_save_path = directory / f
"{self._name}_latest.jpg"
155 img.save(latest_save_path)
158 timestamp_save_path = directory / f
"{self._name}_{self._last_detection}.jpg"
159 img.save(timestamp_save_path)
160 _LOGGER.debug(
"Sighthound saved file %s", timestamp_save_path)
164 """Return camera entity id from process pictures."""
169 """Return the name of the sensor."""
170 return self.
_name_name
174 """Return the state of the entity."""
179 """Return the attributes."""
def __init__(self, api, camera_entity, name, save_file_folder, save_timestamped_file)
def save_image(self, image, people, directory)
def extra_state_attributes(self)
def process_image(self, image)
def fire_person_detected_event(self, person)
None add_entities(AsusWrtRouter router, AddEntitiesCallback async_add_entities, set[str] tracked)
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
tuple[str, str] split_entity_id(str entity_id)
None draw_box(ImageDraw draw, tuple[float, float, float, float] box, int img_width, int img_height, str text="", tuple[int, int, int] color=(255, 255, 0))