Refactor ImageProcessingFaceEntity (#14296)

* Refactor ImageProcessingFaceEntity

* Replace STATE_UNKNOWN with None
This commit is contained in:
Robin 2018-05-05 15:57:53 +01:00 committed by Martin Hjelmare
parent 4d085882d5
commit b9e893184a
6 changed files with 110 additions and 114 deletions

View File

@ -10,6 +10,7 @@ import logging
import voluptuous as vol import voluptuous as vol
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, CONF_NAME, CONF_ENTITY_ID) ATTR_ENTITY_ID, CONF_NAME, CONF_ENTITY_ID)
@ -17,6 +18,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util.async_ import run_callback_threadsafe
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -33,7 +35,16 @@ DEVICE_CLASSES = [
SERVICE_SCAN = 'scan' SERVICE_SCAN = 'scan'
EVENT_DETECT_FACE = 'image_processing.detect_face'
ATTR_AGE = 'age'
ATTR_CONFIDENCE = 'confidence' ATTR_CONFIDENCE = 'confidence'
ATTR_FACES = 'faces'
ATTR_GENDER = 'gender'
ATTR_GLASSES = 'glasses'
ATTR_NAME = 'name'
ATTR_MOTION = 'motion'
ATTR_TOTAL_FACES = 'total_faces'
CONF_SOURCE = 'source' CONF_SOURCE = 'source'
CONF_CONFIDENCE = 'confidence' CONF_CONFIDENCE = 'confidence'
@ -133,3 +144,90 @@ class ImageProcessingEntity(Entity):
# process image data # process image data
yield from self.async_process_image(image.content) yield from self.async_process_image(image.content)
class ImageProcessingFaceEntity(ImageProcessingEntity):
"""Base entity class for face image processing."""
def __init__(self):
"""Initialize base face identify/verify entity."""
self.faces = []
self.total_faces = 0
@property
def state(self):
"""Return the state of the entity."""
confidence = 0
state = None
# No confidence support
if not self.confidence:
return self.total_faces
# Search high confidence
for face in self.faces:
if ATTR_CONFIDENCE not in face:
continue
f_co = face[ATTR_CONFIDENCE]
if f_co > confidence:
confidence = f_co
for attr in [ATTR_NAME, ATTR_MOTION]:
if attr in face:
state = face[attr]
break
return state
@property
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
return 'face'
@property
def state_attributes(self):
"""Return device specific state attributes."""
attr = {
ATTR_FACES: self.faces,
ATTR_TOTAL_FACES: self.total_faces,
}
return attr
def process_faces(self, faces, total):
"""Send event with detected faces and store data."""
run_callback_threadsafe(
self.hass.loop, self.async_process_faces, faces, total).result()
@callback
def async_process_faces(self, faces, total):
"""Send event with detected faces and store data.
known are a dict in follow format:
[
{
ATTR_CONFIDENCE: 80,
ATTR_NAME: 'Name',
ATTR_AGE: 12.0,
ATTR_GENDER: 'man',
ATTR_MOTION: 'smile',
ATTR_GLASSES: 'sunglasses'
},
]
This method must be run in the event loop.
"""
# Send events
for face in faces:
if ATTR_CONFIDENCE in face and self.confidence:
if face[ATTR_CONFIDENCE] < self.confidence:
continue
face.update({ATTR_ENTITY_ID: self.entity_id})
self.hass.async_add_job(
self.hass.bus.async_fire, EVENT_DETECT_FACE, face
)
# Update entity store
self.faces = faces
self.total_faces = total

View File

@ -4,11 +4,12 @@ Support for the demo image processing.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/demo/ https://home-assistant.io/components/demo/
""" """
from homeassistant.components.image_processing import ATTR_CONFIDENCE from homeassistant.components.image_processing import (
ImageProcessingFaceEntity, ATTR_CONFIDENCE, ATTR_NAME, ATTR_AGE,
ATTR_GENDER
)
from homeassistant.components.image_processing.openalpr_local import ( from homeassistant.components.image_processing.openalpr_local import (
ImageProcessingAlprEntity) ImageProcessingAlprEntity)
from homeassistant.components.image_processing.microsoft_face_identify import (
ImageProcessingFaceEntity, ATTR_NAME, ATTR_AGE, ATTR_GENDER)
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):

View File

@ -11,9 +11,7 @@ from homeassistant.core import split_entity_id
# pylint: disable=unused-import # pylint: disable=unused-import
from homeassistant.components.image_processing import PLATFORM_SCHEMA # noqa from homeassistant.components.image_processing import PLATFORM_SCHEMA # noqa
from homeassistant.components.image_processing import ( from homeassistant.components.image_processing import (
CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) ImageProcessingFaceEntity, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME)
from homeassistant.components.image_processing.microsoft_face_identify import (
ImageProcessingFaceEntity)
REQUIREMENTS = ['face_recognition==1.0.0'] REQUIREMENTS = ['face_recognition==1.0.0']

View File

@ -11,9 +11,8 @@ import voluptuous as vol
from homeassistant.core import split_entity_id from homeassistant.core import split_entity_id
from homeassistant.components.image_processing import ( from homeassistant.components.image_processing import (
PLATFORM_SCHEMA, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) ImageProcessingFaceEntity, PLATFORM_SCHEMA, CONF_SOURCE, CONF_ENTITY_ID,
from homeassistant.components.image_processing.microsoft_face_identify import ( CONF_NAME)
ImageProcessingFaceEntity)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['face_recognition==1.0.0'] REQUIREMENTS = ['face_recognition==1.0.0']

View File

@ -13,9 +13,8 @@ from homeassistant.core import split_entity_id
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE
from homeassistant.components.image_processing import ( from homeassistant.components.image_processing import (
PLATFORM_SCHEMA, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME) PLATFORM_SCHEMA, ImageProcessingFaceEntity, ATTR_AGE, ATTR_GENDER,
from homeassistant.components.image_processing.microsoft_face_identify import ( ATTR_GLASSES, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME)
ImageProcessingFaceEntity, ATTR_GENDER, ATTR_AGE, ATTR_GLASSES)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
DEPENDENCIES = ['microsoft_face'] DEPENDENCIES = ['microsoft_face']

View File

@ -9,30 +9,18 @@ import logging
import voluptuous as vol import voluptuous as vol
from homeassistant.core import split_entity_id, callback from homeassistant.core import split_entity_id
from homeassistant.const import STATE_UNKNOWN
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE
from homeassistant.components.image_processing import ( from homeassistant.components.image_processing import (
PLATFORM_SCHEMA, ImageProcessingEntity, CONF_CONFIDENCE, CONF_SOURCE, PLATFORM_SCHEMA, ImageProcessingFaceEntity, ATTR_NAME,
CONF_ENTITY_ID, CONF_NAME, ATTR_ENTITY_ID, ATTR_CONFIDENCE) CONF_CONFIDENCE, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME, ATTR_CONFIDENCE)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util.async_ import run_callback_threadsafe
DEPENDENCIES = ['microsoft_face'] DEPENDENCIES = ['microsoft_face']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
EVENT_DETECT_FACE = 'image_processing.detect_face'
ATTR_NAME = 'name'
ATTR_TOTAL_FACES = 'total_faces'
ATTR_AGE = 'age'
ATTR_GENDER = 'gender'
ATTR_MOTION = 'motion'
ATTR_GLASSES = 'glasses'
ATTR_FACES = 'faces'
CONF_GROUP = 'group' CONF_GROUP = 'group'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@ -57,93 +45,6 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
async_add_devices(entities) async_add_devices(entities)
class ImageProcessingFaceEntity(ImageProcessingEntity):
"""Base entity class for face image processing."""
def __init__(self):
"""Initialize base face identify/verify entity."""
self.faces = []
self.total_faces = 0
@property
def state(self):
"""Return the state of the entity."""
confidence = 0
state = STATE_UNKNOWN
# No confidence support
if not self.confidence:
return self.total_faces
# Search high confidence
for face in self.faces:
if ATTR_CONFIDENCE not in face:
continue
f_co = face[ATTR_CONFIDENCE]
if f_co > confidence:
confidence = f_co
for attr in [ATTR_NAME, ATTR_MOTION]:
if attr in face:
state = face[attr]
break
return state
@property
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
return 'face'
@property
def state_attributes(self):
"""Return device specific state attributes."""
attr = {
ATTR_FACES: self.faces,
ATTR_TOTAL_FACES: self.total_faces,
}
return attr
def process_faces(self, faces, total):
"""Send event with detected faces and store data."""
run_callback_threadsafe(
self.hass.loop, self.async_process_faces, faces, total).result()
@callback
def async_process_faces(self, faces, total):
"""Send event with detected faces and store data.
known are a dict in follow format:
[
{
ATTR_CONFIDENCE: 80,
ATTR_NAME: 'Name',
ATTR_AGE: 12.0,
ATTR_GENDER: 'man',
ATTR_MOTION: 'smile',
ATTR_GLASSES: 'sunglasses'
},
]
This method must be run in the event loop.
"""
# Send events
for face in faces:
if ATTR_CONFIDENCE in face and self.confidence:
if face[ATTR_CONFIDENCE] < self.confidence:
continue
face.update({ATTR_ENTITY_ID: self.entity_id})
self.hass.async_add_job(
self.hass.bus.async_fire, EVENT_DETECT_FACE, face
)
# Update entity store
self.faces = faces
self.total_faces = total
class MicrosoftFaceIdentifyEntity(ImageProcessingFaceEntity): class MicrosoftFaceIdentifyEntity(ImageProcessingFaceEntity):
"""Representation of the Microsoft Face API entity for identify.""" """Representation of the Microsoft Face API entity for identify."""