diff --git a/homeassistant/brands/microsoft.json b/homeassistant/brands/microsoft.json index bfdf62cc2769..8ba8735e5980 100644 --- a/homeassistant/brands/microsoft.json +++ b/homeassistant/brands/microsoft.json @@ -7,9 +7,6 @@ "azure_event_hub", "azure_service_bus", "azure_storage", - "microsoft_face_detect", - "microsoft_face_identify", - "microsoft_face", "microsoft", "onedrive", "onedrive_for_business", diff --git a/homeassistant/components/microsoft_face/__init__.py b/homeassistant/components/microsoft_face/__init__.py deleted file mode 100644 index 2b5864ef4756..000000000000 --- a/homeassistant/components/microsoft_face/__init__.py +++ /dev/null @@ -1,344 +0,0 @@ -"""Support for Microsoft face recognition.""" - -import asyncio -from collections.abc import Coroutine -import json -import logging -from typing import Any, override - -import aiohttp -from aiohttp.hdrs import CONTENT_TYPE -import voluptuous as vol - -from homeassistant.components import camera -from homeassistant.const import ATTR_NAME, CONF_API_KEY, CONF_TIMEOUT, CONTENT_TYPE_JSON -from homeassistant.core import HomeAssistant, ServiceCall -from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.typing import ConfigType -from homeassistant.util import slugify -from homeassistant.util.hass_dict import HassKey - -_LOGGER = logging.getLogger(__name__) - -ATTR_CAMERA_ENTITY = "camera_entity" -ATTR_GROUP = "group" -ATTR_PERSON = "person" - -CONF_AZURE_REGION = "azure_region" - -DEFAULT_TIMEOUT = 10 -DOMAIN = "microsoft_face" -DATA_MICROSOFT_FACE: HassKey[MicrosoftFace] = HassKey(DOMAIN) - -FACE_API_URL = "api.cognitive.microsoft.com/face/v1.0/{0}" - -SERVICE_CREATE_GROUP = "create_group" -SERVICE_CREATE_PERSON = "create_person" -SERVICE_DELETE_GROUP = "delete_group" -SERVICE_DELETE_PERSON = "delete_person" -SERVICE_FACE_PERSON = "face_person" -SERVICE_TRAIN_GROUP = "train_group" - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_API_KEY): cv.string, - vol.Optional(CONF_AZURE_REGION, default="westus"): cv.string, - vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, - } - ) - }, - extra=vol.ALLOW_EXTRA, -) - -SCHEMA_GROUP_SERVICE = vol.Schema({vol.Required(ATTR_NAME): cv.string}) - -SCHEMA_PERSON_SERVICE = SCHEMA_GROUP_SERVICE.extend( - {vol.Required(ATTR_GROUP): cv.slugify} -) - -SCHEMA_FACE_SERVICE = vol.Schema( - { - vol.Required(ATTR_PERSON): cv.string, - vol.Required(ATTR_GROUP): cv.slugify, - vol.Required(ATTR_CAMERA_ENTITY): cv.entity_id, - } -) - -SCHEMA_TRAIN_SERVICE = vol.Schema({vol.Required(ATTR_GROUP): cv.slugify}) - - -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up Microsoft Face.""" - component = EntityComponent[MicrosoftFaceGroupEntity]( - logging.getLogger(__name__), DOMAIN, hass - ) - entities: dict[str, MicrosoftFaceGroupEntity] = {} - domain_config: dict[str, Any] = config[DOMAIN] - azure_region: str = domain_config[CONF_AZURE_REGION] - api_key: str = domain_config[CONF_API_KEY] - timeout: int = domain_config[CONF_TIMEOUT] - face = MicrosoftFace( - hass, - azure_region, - api_key, - timeout, - component, - entities, - ) - - try: - # read exists group/person from cloud and create entities - await face.update_store() - except HomeAssistantError as err: - _LOGGER.error("Can't load data from face api: %s", err) - return False - - hass.data[DATA_MICROSOFT_FACE] = face - - async def async_create_group(service: ServiceCall) -> None: - """Create a new person group.""" - name = service.data[ATTR_NAME] - g_id = slugify(name) - - try: - await face.call_api("put", f"persongroups/{g_id}", {"name": name}) - face.store[g_id] = {} - old_entity = entities.pop(g_id, None) - if old_entity: - await component.async_remove_entity(old_entity.entity_id) - - entities[g_id] = MicrosoftFaceGroupEntity(face, g_id, name) - await component.async_add_entities([entities[g_id]]) - # pylint: disable-next=home-assistant-action-swallowed-exception - except HomeAssistantError as err: - _LOGGER.error("Can't create group '%s' with error: %s", g_id, err) - - hass.services.async_register( - DOMAIN, SERVICE_CREATE_GROUP, async_create_group, schema=SCHEMA_GROUP_SERVICE - ) - - async def async_delete_group(service: ServiceCall) -> None: - """Delete a person group.""" - g_id = slugify(service.data[ATTR_NAME]) - - try: - await face.call_api("delete", f"persongroups/{g_id}") - face.store.pop(g_id) - - entity = entities.pop(g_id) - await component.async_remove_entity(entity.entity_id) - # pylint: disable-next=home-assistant-action-swallowed-exception - except HomeAssistantError as err: - _LOGGER.error("Can't delete group '%s' with error: %s", g_id, err) - - hass.services.async_register( - DOMAIN, SERVICE_DELETE_GROUP, async_delete_group, schema=SCHEMA_GROUP_SERVICE - ) - - async def async_train_group(service: ServiceCall) -> None: - """Train a person group.""" - g_id = service.data[ATTR_GROUP] - - try: - await face.call_api("post", f"persongroups/{g_id}/train") - # pylint: disable-next=home-assistant-action-swallowed-exception - except HomeAssistantError as err: - _LOGGER.error("Can't train group '%s' with error: %s", g_id, err) - - hass.services.async_register( - DOMAIN, SERVICE_TRAIN_GROUP, async_train_group, schema=SCHEMA_TRAIN_SERVICE - ) - - async def async_create_person(service: ServiceCall) -> None: - """Create a person in a group.""" - name = service.data[ATTR_NAME] - g_id = service.data[ATTR_GROUP] - - try: - user_data = await face.call_api( - "post", f"persongroups/{g_id}/persons", {"name": name} - ) - - face.store[g_id][name] = user_data["personId"] - entities[g_id].async_write_ha_state() - # pylint: disable-next=home-assistant-action-swallowed-exception - except HomeAssistantError as err: - _LOGGER.error("Can't create person '%s' with error: %s", name, err) - - hass.services.async_register( - DOMAIN, SERVICE_CREATE_PERSON, async_create_person, schema=SCHEMA_PERSON_SERVICE - ) - - async def async_delete_person(service: ServiceCall) -> None: - """Delete a person in a group.""" - name = service.data[ATTR_NAME] - g_id = service.data[ATTR_GROUP] - p_id = face.store[g_id].get(name) - - try: - await face.call_api("delete", f"persongroups/{g_id}/persons/{p_id}") - - face.store[g_id].pop(name) - entities[g_id].async_write_ha_state() - # pylint: disable-next=home-assistant-action-swallowed-exception - except HomeAssistantError as err: - _LOGGER.error("Can't delete person '%s' with error: %s", p_id, err) - - hass.services.async_register( - DOMAIN, SERVICE_DELETE_PERSON, async_delete_person, schema=SCHEMA_PERSON_SERVICE - ) - - async def async_face_person(service: ServiceCall) -> None: - """Add a new face picture to a person.""" - g_id = service.data[ATTR_GROUP] - p_id = face.store[g_id].get(service.data[ATTR_PERSON]) - - camera_entity = service.data[ATTR_CAMERA_ENTITY] - - try: - image = await camera.async_get_image(hass, camera_entity) - - await face.call_api( - "post", - f"persongroups/{g_id}/persons/{p_id}/persistedFaces", - image.content, - binary=True, - ) - # pylint: disable-next=home-assistant-action-swallowed-exception - except HomeAssistantError as err: - _LOGGER.error( - "Can't add an image of a person '%s' with error: %s", p_id, err - ) - - hass.services.async_register( - DOMAIN, SERVICE_FACE_PERSON, async_face_person, schema=SCHEMA_FACE_SERVICE - ) - - return True - - -class MicrosoftFaceGroupEntity(Entity): - """Person-Group state/data Entity.""" - - _attr_should_poll = False - - def __init__(self, api: MicrosoftFace, g_id: str, name: str) -> None: - """Initialize person/group entity.""" - self.entity_id = f"{DOMAIN}.{g_id}" - self._api = api - self._id = g_id - self._attr_name = name - - @property - @override - def state(self) -> int: - """Return the state of the entity.""" - return len(self._api.store[self._id]) - - @property - @override - def extra_state_attributes(self) -> dict[str, Any]: - """Return device specific state attributes.""" - return dict(self._api.store[self._id]) - - -class MicrosoftFace: - """Microsoft Face api for Home Assistant.""" - - def __init__( - self, - hass: HomeAssistant, - server_loc: str, - api_key: str, - timeout: int, - component: EntityComponent[MicrosoftFaceGroupEntity], - entities: dict[str, MicrosoftFaceGroupEntity], - ) -> None: - """Initialize Microsoft Face api.""" - self.hass = hass - self.websession = async_get_clientsession(hass) - self.timeout = timeout - self._api_key = api_key - self._server_url = f"https://{server_loc}.{FACE_API_URL}" - self._store: dict[str, dict[str, Any]] = {} - self._component = component - self._entities = entities - - @property - def store(self) -> dict[str, dict[str, Any]]: - """Store group/person data and IDs.""" - return self._store - - async def update_store(self) -> None: - """Load all group/person data into local store.""" - groups = await self.call_api("get", "persongroups") - - remove_tasks: list[Coroutine[Any, Any, None]] = [] - new_entities = [] - for group in groups: - g_id = group["personGroupId"] - self._store[g_id] = {} - old_entity = self._entities.pop(g_id, None) - if old_entity: - remove_tasks.append( - self._component.async_remove_entity(old_entity.entity_id) - ) - - self._entities[g_id] = MicrosoftFaceGroupEntity(self, g_id, group["name"]) - new_entities.append(self._entities[g_id]) - - persons = await self.call_api("get", f"persongroups/{g_id}/persons") - - for person in persons: - self._store[g_id][person["name"]] = person["personId"] - - if remove_tasks: - await asyncio.gather(*remove_tasks) - await self._component.async_add_entities(new_entities) - - async def call_api(self, method, function, data=None, binary=False, params=None): - """Make an api call.""" - headers = {"Ocp-Apim-Subscription-Key": self._api_key} - url = self._server_url.format(function) - - payload = None - if binary: - headers[CONTENT_TYPE] = "application/octet-stream" - payload = data - else: - headers[CONTENT_TYPE] = CONTENT_TYPE_JSON - if data is not None: - payload = json.dumps(data).encode() - else: - payload = None - - try: - async with asyncio.timeout(self.timeout): - response = await self.websession.request( - method, url, data=payload, headers=headers, params=params - ) - - answer = await response.json() - - _LOGGER.debug("Read from microsoft face api: %s", answer) - if response.status < 300: - return answer - - _LOGGER.warning( - "Error %d microsoft face api %s", response.status, response.url - ) - raise HomeAssistantError(answer["error"]["message"]) - - except aiohttp.ClientError: - _LOGGER.warning("Can't connect to microsoft face api") - - except TimeoutError: - _LOGGER.warning("Timeout from microsoft face api %s", response.url) - - raise HomeAssistantError("Network error on microsoft face api.") diff --git a/homeassistant/components/microsoft_face/icons.json b/homeassistant/components/microsoft_face/icons.json deleted file mode 100644 index 6e61676224d2..000000000000 --- a/homeassistant/components/microsoft_face/icons.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "services": { - "create_group": { - "service": "mdi:account-multiple-plus" - }, - "create_person": { - "service": "mdi:account-plus" - }, - "delete_group": { - "service": "mdi:account-multiple-remove" - }, - "delete_person": { - "service": "mdi:account-remove" - }, - "face_person": { - "service": "mdi:face-man" - }, - "train_group": { - "service": "mdi:account-multiple-check" - } - } -} diff --git a/homeassistant/components/microsoft_face/manifest.json b/homeassistant/components/microsoft_face/manifest.json deleted file mode 100644 index e13d1c76ccbc..000000000000 --- a/homeassistant/components/microsoft_face/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "domain": "microsoft_face", - "name": "Microsoft Face", - "codeowners": [], - "dependencies": ["camera"], - "documentation": "https://www.home-assistant.io/integrations/microsoft_face", - "iot_class": "cloud_push", - "quality_scale": "legacy" -} diff --git a/homeassistant/components/microsoft_face/services.yaml b/homeassistant/components/microsoft_face/services.yaml deleted file mode 100644 index 13078495b43b..000000000000 --- a/homeassistant/components/microsoft_face/services.yaml +++ /dev/null @@ -1,62 +0,0 @@ -create_group: - fields: - name: - required: true - example: family - selector: - text: -create_person: - fields: - group: - required: true - example: family - selector: - text: - name: - required: true - example: Hans - selector: - text: -delete_group: - fields: - name: - required: true - example: family - selector: - text: -delete_person: - fields: - group: - required: true - example: family - selector: - text: - name: - required: true - example: Hans - selector: - text: -face_person: - fields: - camera_entity: - required: true - example: camera.door - selector: - text: - group: - required: true - example: family - selector: - text: - person: - required: true - example: Hans - selector: - text: -train_group: - fields: - group: - required: true - example: family - selector: - text: diff --git a/homeassistant/components/microsoft_face/strings.json b/homeassistant/components/microsoft_face/strings.json deleted file mode 100644 index b7eac0271c7d..000000000000 --- a/homeassistant/components/microsoft_face/strings.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "services": { - "create_group": { - "description": "Creates a new person group.", - "fields": { - "name": { - "description": "Name of the group.", - "name": "[%key:common::config_flow::data::name%]" - } - }, - "name": "Create group" - }, - "create_person": { - "description": "Creates a new person in the group.", - "fields": { - "group": { - "description": "Name of the group.", - "name": "Group" - }, - "name": { - "description": "Name of the person.", - "name": "[%key:common::config_flow::data::name%]" - } - }, - "name": "Create person" - }, - "delete_group": { - "description": "Deletes a new person group.", - "fields": { - "name": { - "description": "Name of the group.", - "name": "[%key:common::config_flow::data::name%]" - } - }, - "name": "Delete group" - }, - "delete_person": { - "description": "Deletes a person in the group.", - "fields": { - "group": { - "description": "Name of the group.", - "name": "Group" - }, - "name": { - "description": "[%key:component::microsoft_face::services::create_person::fields::name::description%]", - "name": "[%key:common::config_flow::data::name%]" - } - }, - "name": "Delete person" - }, - "face_person": { - "description": "Adds a new picture to a person.", - "fields": { - "camera_entity": { - "description": "Camera to take a picture.", - "name": "Camera entity" - }, - "group": { - "description": "Name of the group.", - "name": "Group" - }, - "person": { - "description": "[%key:component::microsoft_face::services::create_person::fields::name::description%]", - "name": "Person" - } - }, - "name": "Face person" - }, - "train_group": { - "description": "Trains a person group.", - "fields": { - "group": { - "description": "Name of the group.", - "name": "Group" - } - }, - "name": "Train group" - } - } -} diff --git a/homeassistant/components/microsoft_face_detect/__init__.py b/homeassistant/components/microsoft_face_detect/__init__.py deleted file mode 100644 index 897a367a101a..000000000000 --- a/homeassistant/components/microsoft_face_detect/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The microsoft_face_detect component.""" diff --git a/homeassistant/components/microsoft_face_detect/image_processing.py b/homeassistant/components/microsoft_face_detect/image_processing.py deleted file mode 100644 index b9c5385f57c5..000000000000 --- a/homeassistant/components/microsoft_face_detect/image_processing.py +++ /dev/null @@ -1,125 +0,0 @@ -"""Component that will help set the Microsoft face detect processing.""" - -import logging -from typing import TYPE_CHECKING, override - -import voluptuous as vol - -from homeassistant.components.image_processing import ( - ATTR_AGE, - ATTR_GENDER, - ATTR_GLASSES, - PLATFORM_SCHEMA as IMAGE_PROCESSING_PLATFORM_SCHEMA, - FaceInformation, - ImageProcessingFaceEntity, -) -from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE, MicrosoftFace -from homeassistant.const import CONF_ENTITY_ID, CONF_NAME, CONF_SOURCE -from homeassistant.core import HomeAssistant, split_entity_id -from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -_LOGGER = logging.getLogger(__name__) - -SUPPORTED_ATTRIBUTES = [ATTR_AGE, ATTR_GENDER, ATTR_GLASSES] - -CONF_ATTRIBUTES = "attributes" -DEFAULT_ATTRIBUTES = [ATTR_AGE, ATTR_GENDER] - - -def validate_attributes(list_attributes): - """Validate face attributes.""" - for attr in list_attributes: - if attr not in SUPPORTED_ATTRIBUTES: - raise vol.Invalid(f"Invalid attribute {attr}") - return list_attributes - - -PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA.extend( - { - vol.Optional(CONF_ATTRIBUTES, default=DEFAULT_ATTRIBUTES): vol.All( - cv.ensure_list, validate_attributes - ) - } -) - - -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Microsoft Face detection platform.""" - api = hass.data[DATA_MICROSOFT_FACE] - attributes: list[str] = config[CONF_ATTRIBUTES] - source: list[dict[str, str]] = config[CONF_SOURCE] - - async_add_entities( - MicrosoftFaceDetectEntity( - camera[CONF_ENTITY_ID], api, attributes, camera.get(CONF_NAME) - ) - for camera in source - ) - - -class MicrosoftFaceDetectEntity(ImageProcessingFaceEntity): - """Microsoft Face API entity for identify.""" - - def __init__( - self, - camera_entity: str, - api: MicrosoftFace, - attributes: list[str], - name: str | None, - ) -> None: - """Initialize Microsoft Face.""" - super().__init__() - - self._api = api - self._attr_camera_entity = camera_entity - self._attributes = attributes - - if name: - self._attr_name = name - else: - self._attr_name = f"MicrosoftFace {split_entity_id(camera_entity)[1]}" - - @override - async def async_process_image(self, image: bytes) -> None: - """Process image. - - This method is a coroutine. - """ - face_data = None - try: - face_data = await self._api.call_api( - "post", - "detect", - image, - binary=True, - params={"returnFaceAttributes": ",".join(self._attributes)}, - ) - - except HomeAssistantError as err: - _LOGGER.error("Can't process image on microsoft face: %s", err) - return - - if not face_data: - face_data = [] - - faces: list[FaceInformation] = [] - for face in face_data: - face_attr = FaceInformation() - for attr in self._attributes: - if TYPE_CHECKING: - assert attr in SUPPORTED_ATTRIBUTES - if attr in face["faceAttributes"]: - face_attr[attr] = face["faceAttributes"][attr] # type: ignore[literal-required] - - if face_attr: - faces.append(face_attr) - - self.async_process_faces(faces, len(face_data)) diff --git a/homeassistant/components/microsoft_face_detect/manifest.json b/homeassistant/components/microsoft_face_detect/manifest.json deleted file mode 100644 index f3f9f0fa0957..000000000000 --- a/homeassistant/components/microsoft_face_detect/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "domain": "microsoft_face_detect", - "name": "Microsoft Face Detect", - "codeowners": [], - "dependencies": ["microsoft_face"], - "documentation": "https://www.home-assistant.io/integrations/microsoft_face_detect", - "iot_class": "cloud_push", - "quality_scale": "legacy" -} diff --git a/homeassistant/components/microsoft_face_identify/__init__.py b/homeassistant/components/microsoft_face_identify/__init__.py deleted file mode 100644 index cd402d0fef17..000000000000 --- a/homeassistant/components/microsoft_face_identify/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The microsoft_face_identify component.""" diff --git a/homeassistant/components/microsoft_face_identify/image_processing.py b/homeassistant/components/microsoft_face_identify/image_processing.py deleted file mode 100644 index 3a73f076040c..000000000000 --- a/homeassistant/components/microsoft_face_identify/image_processing.py +++ /dev/null @@ -1,121 +0,0 @@ -"""Component that will help set the Microsoft face for verify processing.""" - -import logging -from typing import override - -import voluptuous as vol - -from homeassistant.components.image_processing import ( - ATTR_CONFIDENCE, - CONF_CONFIDENCE, - PLATFORM_SCHEMA as IMAGE_PROCESSING_PLATFORM_SCHEMA, - FaceInformation, - ImageProcessingFaceEntity, -) -from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE, MicrosoftFace -from homeassistant.const import ATTR_NAME, CONF_ENTITY_ID, CONF_NAME, CONF_SOURCE -from homeassistant.core import HomeAssistant, split_entity_id -from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -_LOGGER = logging.getLogger(__name__) - -CONF_GROUP = "group" - -PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA.extend( - {vol.Required(CONF_GROUP): cv.slugify} -) - - -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Microsoft Face identify platform.""" - api = hass.data[DATA_MICROSOFT_FACE] - face_group: str = config[CONF_GROUP] - confidence: float = config[CONF_CONFIDENCE] - source: list[dict[str, str]] = config[CONF_SOURCE] - - async_add_entities( - MicrosoftFaceIdentifyEntity( - camera[CONF_ENTITY_ID], - api, - face_group, - confidence, - camera.get(CONF_NAME), - ) - for camera in source - ) - - -class MicrosoftFaceIdentifyEntity(ImageProcessingFaceEntity): - """Representation of the Microsoft Face API entity for identify.""" - - def __init__( - self, - camera_entity: str, - api: MicrosoftFace, - face_group: str, - confidence: float, - name: str | None, - ) -> None: - """Initialize the Microsoft Face API.""" - super().__init__() - - self._api = api - self._attr_camera_entity = camera_entity - self._attr_confidence = confidence - self._face_group = face_group - - if name: - self._attr_name = name - else: - self._attr_name = f"MicrosoftFace {split_entity_id(camera_entity)[1]}" - - @override - async def async_process_image(self, image: bytes) -> None: - """Process image. - - This method is a coroutine. - """ - detect = [] - try: - face_data = await self._api.call_api("post", "detect", image, binary=True) - - if face_data: - face_ids = [data["faceId"] for data in face_data] - detect = await self._api.call_api( - "post", - "identify", - {"faceIds": face_ids, "personGroupId": self._face_group}, - ) - - except HomeAssistantError as err: - _LOGGER.error("Can't process image on Microsoft face: %s", err) - return - - # Parse data - known_faces: list[FaceInformation] = [] - total = 0 - for face in detect: - total += 1 - if not face["candidates"]: - continue - - data = face["candidates"][0] - name = "" - for s_name, s_id in self._api.store[self._face_group].items(): - if data["personId"] == s_id: - name = s_name - break - - known_faces.append( - {ATTR_NAME: name, ATTR_CONFIDENCE: data["confidence"] * 100} - ) - - self.async_process_faces(known_faces, total) diff --git a/homeassistant/components/microsoft_face_identify/manifest.json b/homeassistant/components/microsoft_face_identify/manifest.json deleted file mode 100644 index b3964ee12545..000000000000 --- a/homeassistant/components/microsoft_face_identify/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "domain": "microsoft_face_identify", - "name": "Microsoft Face Identify", - "codeowners": [], - "dependencies": ["microsoft_face"], - "documentation": "https://www.home-assistant.io/integrations/microsoft_face_identify", - "iot_class": "cloud_push", - "quality_scale": "legacy" -} diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 614a6f72fb32..32277115e0db 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -4263,24 +4263,6 @@ "iot_class": "cloud_polling", "name": "Azure Storage" }, - "microsoft_face_detect": { - "integration_type": "hub", - "config_flow": false, - "iot_class": "cloud_push", - "name": "Microsoft Face Detect" - }, - "microsoft_face_identify": { - "integration_type": "hub", - "config_flow": false, - "iot_class": "cloud_push", - "name": "Microsoft Face Identify" - }, - "microsoft_face": { - "integration_type": "hub", - "config_flow": false, - "iot_class": "cloud_push", - "name": "Microsoft Face" - }, "microsoft": { "integration_type": "hub", "config_flow": false, diff --git a/pylint/plugins/pylint_home_assistant/checkers/class_module.py b/pylint/plugins/pylint_home_assistant/checkers/class_module.py index 2d5481c80041..5962b2a5d83e 100644 --- a/pylint/plugins/pylint_home_assistant/checkers/class_module.py +++ b/pylint/plugins/pylint_home_assistant/checkers/class_module.py @@ -93,7 +93,6 @@ _ENTITY_COMPONENTS: set[str] = set(ENTITY_COMPONENTS).union( "input_number", "input_select", "input_text", - "microsoft_face", "person", "plant", "remember_the_milk", diff --git a/script/hassfest/quality_scale.py b/script/hassfest/quality_scale.py index 6c8fcca50fd7..7f88c046e8eb 100644 --- a/script/hassfest/quality_scale.py +++ b/script/hassfest/quality_scale.py @@ -585,9 +585,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [ "mfi", "microbees", "microsoft", - "microsoft_face", - "microsoft_face_detect", - "microsoft_face_identify", "mikrotik", "mill", "min_max", @@ -1543,9 +1540,6 @@ INTEGRATIONS_WITHOUT_SCALE = [ "mfi", "microbees", "microsoft", - "microsoft_face", - "microsoft_face_detect", - "microsoft_face_identify", "mikrotik", "mill", "min_max", diff --git a/tests/components/microsoft_face/__init__.py b/tests/components/microsoft_face/__init__.py deleted file mode 100644 index 0b07c35b5156..000000000000 --- a/tests/components/microsoft_face/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the microsoft_face component.""" diff --git a/tests/components/microsoft_face/fixtures/create_person.json b/tests/components/microsoft_face/fixtures/create_person.json deleted file mode 100644 index 876524dfbc96..000000000000 --- a/tests/components/microsoft_face/fixtures/create_person.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "personId": "25985303-c537-4467-b41d-bdb45cd95ca1" -} diff --git a/tests/components/microsoft_face/fixtures/persongroups.json b/tests/components/microsoft_face/fixtures/persongroups.json deleted file mode 100644 index 5bbb8ceea290..000000000000 --- a/tests/components/microsoft_face/fixtures/persongroups.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "personGroupId": "test_group1", - "name": "test group1", - "userData": "test" - }, - { - "personGroupId": "test_group2", - "name": "test group2", - "userData": "test" - } -] diff --git a/tests/components/microsoft_face/fixtures/persons.json b/tests/components/microsoft_face/fixtures/persons.json deleted file mode 100644 index e604bcc2672c..000000000000 --- a/tests/components/microsoft_face/fixtures/persons.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "personId": "25985303-c537-4467-b41d-bdb45cd95ca1", - "name": "Ryan", - "userData": "User-provided data attached to the person", - "persistedFaceIds": [ - "015839fb-fbd9-4f79-ace9-7675fc2f1dd9", - "fce92aed-d578-4d2e-8114-068f8af4492e", - "b64d5e15-8257-4af2-b20a-5a750f8940e7" - ] - }, - { - "personId": "2ae4935b-9659-44c3-977f-61fac20d0538", - "name": "David", - "userData": "User-provided data attached to the person", - "persistedFaceIds": [ - "30ea1073-cc9e-4652-b1e3-d08fb7b95315", - "fbd2a038-dbff-452c-8e79-2ee81b1aa84e" - ] - } -] diff --git a/tests/components/microsoft_face/test_init.py b/tests/components/microsoft_face/test_init.py deleted file mode 100644 index a343c633fc76..000000000000 --- a/tests/components/microsoft_face/test_init.py +++ /dev/null @@ -1,355 +0,0 @@ -"""The tests for the microsoft face platform.""" - -from unittest.mock import patch - -import pytest - -from homeassistant.components import camera, microsoft_face as mf -from homeassistant.components.microsoft_face import ( - ATTR_CAMERA_ENTITY, - ATTR_GROUP, - ATTR_PERSON, - DOMAIN, - SERVICE_CREATE_GROUP, - SERVICE_CREATE_PERSON, - SERVICE_DELETE_GROUP, - SERVICE_DELETE_PERSON, - SERVICE_FACE_PERSON, - SERVICE_TRAIN_GROUP, -) -from homeassistant.const import ATTR_NAME -from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component - -from tests.common import assert_setup_component, async_load_fixture -from tests.test_util.aiohttp import AiohttpClientMocker - - -@pytest.fixture(autouse=True) -async def setup_homeassistant(hass: HomeAssistant): - """Set up the homeassistant integration.""" - await async_setup_component(hass, "homeassistant", {}) - - -def create_group(hass: HomeAssistant, name: str) -> None: - """Create a new person group. - - This is a legacy helper method. Do not use it for new tests. - """ - data = {ATTR_NAME: name} - hass.async_create_task(hass.services.async_call(DOMAIN, SERVICE_CREATE_GROUP, data)) - - -def delete_group(hass: HomeAssistant, name: str) -> None: - """Delete a person group. - - This is a legacy helper method. Do not use it for new tests. - """ - data = {ATTR_NAME: name} - hass.async_create_task(hass.services.async_call(DOMAIN, SERVICE_DELETE_GROUP, data)) - - -def train_group(hass: HomeAssistant, group: str) -> None: - """Train a person group. - - This is a legacy helper method. Do not use it for new tests. - """ - data = {ATTR_GROUP: group} - hass.async_create_task(hass.services.async_call(DOMAIN, SERVICE_TRAIN_GROUP, data)) - - -def create_person(hass: HomeAssistant, group: str, name: str) -> None: - """Create a person in a group. - - This is a legacy helper method. Do not use it for new tests. - """ - data = {ATTR_GROUP: group, ATTR_NAME: name} - hass.async_create_task( - hass.services.async_call(DOMAIN, SERVICE_CREATE_PERSON, data) - ) - - -def delete_person(hass: HomeAssistant, group: str, name: str) -> None: - """Delete a person in a group. - - This is a legacy helper method. Do not use it for new tests. - """ - data = {ATTR_GROUP: group, ATTR_NAME: name} - hass.async_create_task( - hass.services.async_call(DOMAIN, SERVICE_DELETE_PERSON, data) - ) - - -def face_person( - hass: HomeAssistant, group: str, person: str, camera_entity: str -) -> None: - """Add a new face picture to a person. - - This is a legacy helper method. Do not use it for new tests. - """ - data = {ATTR_GROUP: group, ATTR_PERSON: person, ATTR_CAMERA_ENTITY: camera_entity} - hass.async_create_task(hass.services.async_call(DOMAIN, SERVICE_FACE_PERSON, data)) - - -CONFIG = {mf.DOMAIN: {"api_key": "12345678abcdef"}} -ENDPOINT_URL = f"https://westus.{mf.FACE_API_URL}" - - -@pytest.fixture -def mock_update(): - """Mock update store.""" - with patch( - "homeassistant.components.microsoft_face.MicrosoftFace.update_store", - return_value=None, - ) as mock_update_store: - yield mock_update_store - - -async def test_setup_component(hass: HomeAssistant, mock_update) -> None: - """Set up component.""" - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - -async def test_setup_component_wrong_api_key(hass: HomeAssistant, mock_update) -> None: - """Set up component without api key.""" - with assert_setup_component(0, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, {mf.DOMAIN: {}}) - - -async def test_setup_component_test_service(hass: HomeAssistant, mock_update) -> None: - """Set up component.""" - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - assert hass.services.has_service(mf.DOMAIN, "create_group") - assert hass.services.has_service(mf.DOMAIN, "delete_group") - assert hass.services.has_service(mf.DOMAIN, "train_group") - assert hass.services.has_service(mf.DOMAIN, "create_person") - assert hass.services.has_service(mf.DOMAIN, "delete_person") - assert hass.services.has_service(mf.DOMAIN, "face_person") - - -async def test_setup_component_test_entities( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up component.""" - aioclient_mock.get( - ENDPOINT_URL.format("persongroups"), - text=await async_load_fixture(hass, "persongroups.json", DOMAIN), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group1/persons"), - text=await async_load_fixture(hass, "persons.json", DOMAIN), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group2/persons"), - text=await async_load_fixture(hass, "persons.json", DOMAIN), - ) - - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - assert len(aioclient_mock.mock_calls) == 3 - - entity_group1 = hass.states.get("microsoft_face.test_group1") - entity_group2 = hass.states.get("microsoft_face.test_group2") - - assert entity_group1 is not None - assert entity_group2 is not None - - assert entity_group1.attributes["Ryan"] == "25985303-c537-4467-b41d-bdb45cd95ca1" - assert entity_group1.attributes["David"] == "2ae4935b-9659-44c3-977f-61fac20d0538" - - assert entity_group2.attributes["Ryan"] == "25985303-c537-4467-b41d-bdb45cd95ca1" - assert entity_group2.attributes["David"] == "2ae4935b-9659-44c3-977f-61fac20d0538" - - -async def test_service_groups( - hass: HomeAssistant, mock_update, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up component, test groups services.""" - aioclient_mock.put( - ENDPOINT_URL.format("persongroups/service_group"), - status=200, - text="{}", - ) - aioclient_mock.delete( - ENDPOINT_URL.format("persongroups/service_group"), - status=200, - text="{}", - ) - - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - create_group(hass, "Service Group") - await hass.async_block_till_done() - - entity = hass.states.get("microsoft_face.service_group") - assert entity is not None - assert len(aioclient_mock.mock_calls) == 1 - - delete_group(hass, "Service Group") - await hass.async_block_till_done() - - entity = hass.states.get("microsoft_face.service_group") - assert entity is None - assert len(aioclient_mock.mock_calls) == 2 - - -async def test_service_person( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up component, test person services.""" - aioclient_mock.get( - ENDPOINT_URL.format("persongroups"), - text=await async_load_fixture(hass, "persongroups.json", DOMAIN), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group1/persons"), - text=await async_load_fixture(hass, "persons.json", DOMAIN), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group2/persons"), - text=await async_load_fixture(hass, "persons.json", DOMAIN), - ) - - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - assert len(aioclient_mock.mock_calls) == 3 - - aioclient_mock.post( - ENDPOINT_URL.format("persongroups/test_group1/persons"), - text=await async_load_fixture(hass, "create_person.json", DOMAIN), - ) - aioclient_mock.delete( - ENDPOINT_URL.format( - "persongroups/test_group1/persons/25985303-c537-4467-b41d-bdb45cd95ca1" - ), - status=200, - text="{}", - ) - - create_person(hass, "test group1", "Hans") - await hass.async_block_till_done() - - entity_group1 = hass.states.get("microsoft_face.test_group1") - - assert len(aioclient_mock.mock_calls) == 4 - assert entity_group1 is not None - assert entity_group1.attributes["Hans"] == "25985303-c537-4467-b41d-bdb45cd95ca1" - - delete_person(hass, "test group1", "Hans") - await hass.async_block_till_done() - - entity_group1 = hass.states.get("microsoft_face.test_group1") - - assert len(aioclient_mock.mock_calls) == 5 - assert entity_group1 is not None - assert "Hans" not in entity_group1.attributes - - -async def test_service_train( - hass: HomeAssistant, mock_update, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up component, test train groups services.""" - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - aioclient_mock.post( - ENDPOINT_URL.format("persongroups/service_group/train"), - status=200, - text="{}", - ) - - train_group(hass, "Service Group") - await hass.async_block_till_done() - - assert len(aioclient_mock.mock_calls) == 1 - - -async def test_service_face( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up component, test person face services.""" - aioclient_mock.get( - ENDPOINT_URL.format("persongroups"), - text=await async_load_fixture(hass, "persongroups.json", DOMAIN), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group1/persons"), - text=await async_load_fixture(hass, "persons.json", DOMAIN), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group2/persons"), - text=await async_load_fixture(hass, "persons.json", DOMAIN), - ) - - CONFIG["camera"] = {"platform": "demo"} - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - assert len(aioclient_mock.mock_calls) == 3 - - aioclient_mock.post( - ENDPOINT_URL.format( - "persongroups/test_group2/persons/" - "2ae4935b-9659-44c3-977f-61fac20d0538/persistedFaces" - ), - status=200, - text="{}", - ) - - with patch( - "homeassistant.components.camera.async_get_image", - return_value=camera.Image("image/jpeg", b"Test"), - ): - face_person(hass, "test_group2", "David", "camera.demo_camera") - await hass.async_block_till_done() - - assert len(aioclient_mock.mock_calls) == 4 - assert aioclient_mock.mock_calls[3][2] == b"Test" - - -async def test_service_status_400( - hass: HomeAssistant, mock_update, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up component, test groups services with error.""" - aioclient_mock.put( - ENDPOINT_URL.format("persongroups/service_group"), - status=400, - text="{'error': {'message': 'Error'}}", - ) - - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - create_group(hass, "Service Group") - await hass.async_block_till_done() - - entity = hass.states.get("microsoft_face.service_group") - assert entity is None - assert len(aioclient_mock.mock_calls) == 1 - - -async def test_service_status_timeout( - hass: HomeAssistant, mock_update, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up component, test groups services with timeout.""" - aioclient_mock.put( - ENDPOINT_URL.format("persongroups/service_group"), - status=400, - exc=TimeoutError(), - ) - - with assert_setup_component(3, mf.DOMAIN): - await async_setup_component(hass, mf.DOMAIN, CONFIG) - - create_group(hass, "Service Group") - await hass.async_block_till_done() - - entity = hass.states.get("microsoft_face.service_group") - assert entity is None - assert len(aioclient_mock.mock_calls) == 1 diff --git a/tests/components/microsoft_face_detect/__init__.py b/tests/components/microsoft_face_detect/__init__.py deleted file mode 100644 index df079d019ebb..000000000000 --- a/tests/components/microsoft_face_detect/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the microsoft_face_detect component.""" diff --git a/tests/components/microsoft_face_detect/fixtures/detect.json b/tests/components/microsoft_face_detect/fixtures/detect.json deleted file mode 100644 index c43f4ec494eb..000000000000 --- a/tests/components/microsoft_face_detect/fixtures/detect.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "faceId": "c5c24a82-6845-4031-9d5d-978df9175426", - "faceRectangle": { - "width": 78, - "height": 78, - "left": 394, - "top": 54 - }, - "faceAttributes": { - "age": 71.0, - "gender": "male", - "smile": 0.88, - "facialHair": { - "mustache": 0.8, - "beard": 0.1, - "sideburns": 0.02 - }, - "glasses": "sunglasses", - "headPose": { - "roll": 2.1, - "yaw": 3, - "pitch": 0 - } - } - } -] diff --git a/tests/components/microsoft_face_detect/fixtures/persongroups.json b/tests/components/microsoft_face_detect/fixtures/persongroups.json deleted file mode 100644 index 5bbb8ceea290..000000000000 --- a/tests/components/microsoft_face_detect/fixtures/persongroups.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "personGroupId": "test_group1", - "name": "test group1", - "userData": "test" - }, - { - "personGroupId": "test_group2", - "name": "test group2", - "userData": "test" - } -] diff --git a/tests/components/microsoft_face_detect/fixtures/persons.json b/tests/components/microsoft_face_detect/fixtures/persons.json deleted file mode 100644 index e604bcc2672c..000000000000 --- a/tests/components/microsoft_face_detect/fixtures/persons.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "personId": "25985303-c537-4467-b41d-bdb45cd95ca1", - "name": "Ryan", - "userData": "User-provided data attached to the person", - "persistedFaceIds": [ - "015839fb-fbd9-4f79-ace9-7675fc2f1dd9", - "fce92aed-d578-4d2e-8114-068f8af4492e", - "b64d5e15-8257-4af2-b20a-5a750f8940e7" - ] - }, - { - "personId": "2ae4935b-9659-44c3-977f-61fac20d0538", - "name": "David", - "userData": "User-provided data attached to the person", - "persistedFaceIds": [ - "30ea1073-cc9e-4652-b1e3-d08fb7b95315", - "fbd2a038-dbff-452c-8e79-2ee81b1aa84e" - ] - } -] diff --git a/tests/components/microsoft_face_detect/test_image_processing.py b/tests/components/microsoft_face_detect/test_image_processing.py deleted file mode 100644 index 98d61b55c192..000000000000 --- a/tests/components/microsoft_face_detect/test_image_processing.py +++ /dev/null @@ -1,167 +0,0 @@ -"""The tests for the microsoft face detect platform.""" - -from unittest.mock import PropertyMock, patch - -import pytest - -from homeassistant.components.image_processing import DOMAIN as IP_DOMAIN -from homeassistant.components.microsoft_face import DOMAIN as MF_DOMAIN, FACE_API_URL -from homeassistant.const import ATTR_ENTITY_PICTURE -from homeassistant.core import HomeAssistant, callback -from homeassistant.setup import async_setup_component - -from tests.common import assert_setup_component, async_load_fixture -from tests.components.image_processing import common -from tests.test_util.aiohttp import AiohttpClientMocker - -CONFIG = { - IP_DOMAIN: { - "platform": "microsoft_face_detect", - "source": {"entity_id": "camera.demo_camera", "name": "test local"}, - "attributes": ["age", "gender"], - }, - "camera": {"platform": "demo"}, - MF_DOMAIN: {"api_key": "12345678abcdef6"}, -} - -ENDPOINT_URL = f"https://westus.{FACE_API_URL}" - - -@pytest.fixture(autouse=True) -async def setup_homeassistant(hass: HomeAssistant): - """Set up the homeassistant integration.""" - await async_setup_component(hass, "homeassistant", {}) - - -@pytest.fixture -def store_mock(): - """Mock update store.""" - with patch( - "homeassistant.components.microsoft_face.MicrosoftFace.update_store", - return_value=None, - ) as mock_update_store: - yield mock_update_store - - -@pytest.fixture -def poll_mock(): - """Disable polling.""" - with patch( - "homeassistant.components.microsoft_face_detect.image_processing." - "MicrosoftFaceDetectEntity.should_poll", - new_callable=PropertyMock(return_value=False), - ): - yield - - -async def test_setup_platform(hass: HomeAssistant, store_mock) -> None: - """Set up platform with one entity.""" - config = { - IP_DOMAIN: { - "platform": "microsoft_face_detect", - "source": {"entity_id": "camera.demo_camera"}, - "attributes": ["age", "gender"], - }, - "camera": {"platform": "demo"}, - MF_DOMAIN: {"api_key": "12345678abcdef6"}, - } - - with assert_setup_component(1, IP_DOMAIN): - await async_setup_component(hass, IP_DOMAIN, config) - await hass.async_block_till_done() - - assert hass.states.get("image_processing.microsoftface_demo_camera") - - -async def test_setup_platform_name(hass: HomeAssistant, store_mock) -> None: - """Set up platform with one entity and set name.""" - config = { - IP_DOMAIN: { - "platform": "microsoft_face_detect", - "source": {"entity_id": "camera.demo_camera", "name": "test local"}, - }, - "camera": {"platform": "demo"}, - MF_DOMAIN: {"api_key": "12345678abcdef6"}, - } - - with assert_setup_component(1, IP_DOMAIN): - await async_setup_component(hass, IP_DOMAIN, config) - await hass.async_block_till_done() - - assert hass.states.get("image_processing.test_local") - - -async def test_ms_detect_process_image( - hass: HomeAssistant, poll_mock, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up and scan a picture and test plates from event.""" - aioclient_mock.get( - ENDPOINT_URL.format("persongroups"), - text=await async_load_fixture( - hass, "persongroups.json", "microsoft_face_detect" - ), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group1/persons"), - text=await async_load_fixture(hass, "persons.json", "microsoft_face_detect"), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group2/persons"), - text=await async_load_fixture(hass, "persons.json", "microsoft_face_detect"), - ) - - await async_setup_component(hass, IP_DOMAIN, CONFIG) - await hass.async_block_till_done() - - state = hass.states.get("camera.demo_camera") - url = f"{hass.config.internal_url}{state.attributes.get(ATTR_ENTITY_PICTURE)}" - - face_events = [] - - @callback - def mock_face_event(event): - """Mock event.""" - face_events.append(event) - - hass.bus.async_listen("image_processing.detect_face", mock_face_event) - - aioclient_mock.get(url, content=b"image") - - aioclient_mock.post( - ENDPOINT_URL.format("detect"), - text=await async_load_fixture(hass, "detect.json", "microsoft_face_detect"), - params={"returnFaceAttributes": "age,gender"}, - ) - - common.async_scan(hass, entity_id="image_processing.test_local") - await hass.async_block_till_done() - - state = hass.states.get("image_processing.test_local") - - assert len(face_events) == 1 - assert state.attributes.get("total_faces") == 1 - assert state.state == "1" - - assert face_events[0].data["age"] == 71.0 - assert face_events[0].data["gender"] == "male" - assert face_events[0].data["entity_id"] == "image_processing.test_local" - - # Test that later, if a request is made that results in no face - # being detected, that this is reflected in the state object - aioclient_mock.clear_requests() - aioclient_mock.post( - ENDPOINT_URL.format("detect"), - text="[]", - params={"returnFaceAttributes": "age,gender"}, - ) - - common.async_scan(hass, entity_id="image_processing.test_local") - await hass.async_block_till_done() - - state = hass.states.get("image_processing.test_local") - - # No more face events were fired - assert len(face_events) == 1 - # Total faces and actual qualified number of faces reset to zero - assert state.attributes.get("total_faces") == 0 - assert state.state == "0" diff --git a/tests/components/microsoft_face_identify/__init__.py b/tests/components/microsoft_face_identify/__init__.py deleted file mode 100644 index 0b31c83b9427..000000000000 --- a/tests/components/microsoft_face_identify/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the microsoft_face_identify component.""" diff --git a/tests/components/microsoft_face_identify/fixtures/detect.json b/tests/components/microsoft_face_identify/fixtures/detect.json deleted file mode 100644 index c43f4ec494eb..000000000000 --- a/tests/components/microsoft_face_identify/fixtures/detect.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "faceId": "c5c24a82-6845-4031-9d5d-978df9175426", - "faceRectangle": { - "width": 78, - "height": 78, - "left": 394, - "top": 54 - }, - "faceAttributes": { - "age": 71.0, - "gender": "male", - "smile": 0.88, - "facialHair": { - "mustache": 0.8, - "beard": 0.1, - "sideburns": 0.02 - }, - "glasses": "sunglasses", - "headPose": { - "roll": 2.1, - "yaw": 3, - "pitch": 0 - } - } - } -] diff --git a/tests/components/microsoft_face_identify/fixtures/identify.json b/tests/components/microsoft_face_identify/fixtures/identify.json deleted file mode 100644 index f7fb1930b656..000000000000 --- a/tests/components/microsoft_face_identify/fixtures/identify.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "faceId": "c5c24a82-6845-4031-9d5d-978df9175426", - "candidates": [ - { - "personId": "2ae4935b-9659-44c3-977f-61fac20d0538", - "confidence": 0.92 - } - ] - }, - { - "faceId": "c5c24a82-6825-4031-9d5d-978df0175426", - "candidates": [ - { - "personId": "25985303-c537-4467-b41d-bdb45cd95ca1", - "confidence": 0.32 - } - ] - } -] diff --git a/tests/components/microsoft_face_identify/fixtures/persongroups.json b/tests/components/microsoft_face_identify/fixtures/persongroups.json deleted file mode 100644 index 5bbb8ceea290..000000000000 --- a/tests/components/microsoft_face_identify/fixtures/persongroups.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "personGroupId": "test_group1", - "name": "test group1", - "userData": "test" - }, - { - "personGroupId": "test_group2", - "name": "test group2", - "userData": "test" - } -] diff --git a/tests/components/microsoft_face_identify/fixtures/persons.json b/tests/components/microsoft_face_identify/fixtures/persons.json deleted file mode 100644 index e604bcc2672c..000000000000 --- a/tests/components/microsoft_face_identify/fixtures/persons.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "personId": "25985303-c537-4467-b41d-bdb45cd95ca1", - "name": "Ryan", - "userData": "User-provided data attached to the person", - "persistedFaceIds": [ - "015839fb-fbd9-4f79-ace9-7675fc2f1dd9", - "fce92aed-d578-4d2e-8114-068f8af4492e", - "b64d5e15-8257-4af2-b20a-5a750f8940e7" - ] - }, - { - "personId": "2ae4935b-9659-44c3-977f-61fac20d0538", - "name": "David", - "userData": "User-provided data attached to the person", - "persistedFaceIds": [ - "30ea1073-cc9e-4652-b1e3-d08fb7b95315", - "fbd2a038-dbff-452c-8e79-2ee81b1aa84e" - ] - } -] diff --git a/tests/components/microsoft_face_identify/test_image_processing.py b/tests/components/microsoft_face_identify/test_image_processing.py deleted file mode 100644 index 6bd4df3b94b9..000000000000 --- a/tests/components/microsoft_face_identify/test_image_processing.py +++ /dev/null @@ -1,168 +0,0 @@ -"""The tests for the microsoft face identify platform.""" - -from unittest.mock import PropertyMock, patch - -import pytest - -from homeassistant.components.image_processing import DOMAIN as IP_DOMAIN -from homeassistant.components.microsoft_face import DOMAIN as MF_DOMAIN, FACE_API_URL -from homeassistant.const import ATTR_ENTITY_PICTURE, STATE_UNKNOWN -from homeassistant.core import HomeAssistant, callback -from homeassistant.setup import async_setup_component - -from tests.common import assert_setup_component, async_load_fixture -from tests.components.image_processing import common -from tests.test_util.aiohttp import AiohttpClientMocker - - -@pytest.fixture(autouse=True) -async def setup_homeassistant(hass: HomeAssistant): - """Set up the homeassistant integration.""" - await async_setup_component(hass, "homeassistant", {}) - - -@pytest.fixture -def store_mock(): - """Mock update store.""" - with patch( - "homeassistant.components.microsoft_face.MicrosoftFace.update_store", - return_value=None, - ) as mock_update_store: - yield mock_update_store - - -@pytest.fixture -def poll_mock(): - """Disable polling.""" - with patch( - "homeassistant.components.microsoft_face_identify.image_processing." - "MicrosoftFaceIdentifyEntity.should_poll", - new_callable=PropertyMock(return_value=False), - ): - yield - - -CONFIG = { - IP_DOMAIN: { - "platform": "microsoft_face_identify", - "source": {"entity_id": "camera.demo_camera", "name": "test local"}, - "group": "Test Group1", - }, - "camera": {"platform": "demo"}, - MF_DOMAIN: {"api_key": "12345678abcdef6"}, -} - -ENDPOINT_URL = f"https://westus.{FACE_API_URL}" - - -async def test_setup_platform(hass: HomeAssistant, store_mock) -> None: - """Set up platform with one entity.""" - config = { - IP_DOMAIN: { - "platform": "microsoft_face_identify", - "source": {"entity_id": "camera.demo_camera"}, - "group": "Test Group1", - }, - "camera": {"platform": "demo"}, - MF_DOMAIN: {"api_key": "12345678abcdef6"}, - } - - with assert_setup_component(1, IP_DOMAIN): - await async_setup_component(hass, IP_DOMAIN, config) - await hass.async_block_till_done() - - assert hass.states.get("image_processing.microsoftface_demo_camera") - - -async def test_setup_platform_name(hass: HomeAssistant, store_mock) -> None: - """Set up platform with one entity and set name.""" - config = { - IP_DOMAIN: { - "platform": "microsoft_face_identify", - "source": {"entity_id": "camera.demo_camera", "name": "test local"}, - "group": "Test Group1", - }, - "camera": {"platform": "demo"}, - MF_DOMAIN: {"api_key": "12345678abcdef6"}, - } - - with assert_setup_component(1, IP_DOMAIN): - await async_setup_component(hass, IP_DOMAIN, config) - await hass.async_block_till_done() - - assert hass.states.get("image_processing.test_local") - - -async def test_ms_identify_process_image( - hass: HomeAssistant, poll_mock, aioclient_mock: AiohttpClientMocker -) -> None: - """Set up and scan a picture and test plates from event.""" - aioclient_mock.get( - ENDPOINT_URL.format("persongroups"), - text=await async_load_fixture( - hass, "persongroups.json", "microsoft_face_identify" - ), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group1/persons"), - text=await async_load_fixture(hass, "persons.json", "microsoft_face_identify"), - ) - aioclient_mock.get( - ENDPOINT_URL.format("persongroups/test_group2/persons"), - text=await async_load_fixture(hass, "persons.json", "microsoft_face_identify"), - ) - - await async_setup_component(hass, IP_DOMAIN, CONFIG) - await hass.async_block_till_done() - - state = hass.states.get("camera.demo_camera") - url = f"{hass.config.internal_url}{state.attributes.get(ATTR_ENTITY_PICTURE)}" - - face_events = [] - - @callback - def mock_face_event(event): - """Mock event.""" - face_events.append(event) - - hass.bus.async_listen("image_processing.detect_face", mock_face_event) - - aioclient_mock.get(url, content=b"image") - - aioclient_mock.post( - ENDPOINT_URL.format("detect"), - text=await async_load_fixture(hass, "detect.json", "microsoft_face_identify"), - ) - aioclient_mock.post( - ENDPOINT_URL.format("identify"), - text=await async_load_fixture(hass, "identify.json", "microsoft_face_identify"), - ) - - common.async_scan(hass, entity_id="image_processing.test_local") - await hass.async_block_till_done() - - state = hass.states.get("image_processing.test_local") - - assert len(face_events) == 1 - assert state.attributes.get("total_faces") == 2 - assert state.state == "David" - - assert face_events[0].data["name"] == "David" - assert face_events[0].data["confidence"] == float(92) - assert face_events[0].data["entity_id"] == "image_processing.test_local" - - # Test that later, if a request is made that results in no face - # being detected, that this is reflected in the state object - aioclient_mock.clear_requests() - aioclient_mock.post(ENDPOINT_URL.format("detect"), text="[]") - - common.async_scan(hass, entity_id="image_processing.test_local") - await hass.async_block_till_done() - - state = hass.states.get("image_processing.test_local") - - # No more face events were fired - assert len(face_events) == 1 - # Total faces and actual qualified number of faces reset to zero - assert state.attributes.get("total_faces") == 0 - assert state.state == STATE_UNKNOWN