-
-
Notifications
You must be signed in to change notification settings - Fork 38.2k
Adds facebox #14307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Adds facebox #14307
Changes from 10 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
fd685ba
Adds facebox-detect
robmarkcole 4e6d676
Drop face_detect
robmarkcole e0608ef
Update and rename test_facebox_face_detect.py to test_facebox.py
robmarkcole 8286f09
Adds matched_faces attr
robmarkcole 38b9eed
Update test_facebox.py
robmarkcole 26128d2
Amend get_matched_faces to take faces
robmarkcole afdab60
Update test_facebox.py
robmarkcole 8e89ec3
Update test_facebox.py
robmarkcole faacce1
Update test_facebox.py
robmarkcole 2568d34
Make mock_req.post
robmarkcole a3ee098
Update test_facebox.py
robmarkcole d9ddc6e
Update test_facebox.py
robmarkcole 9bfe89a
Rename to facebox
robmarkcole 714cfac
Merge remote-tracking branch 'home-assistant/dev' into facebox-detect
robmarkcole 0288897
Merge remote-tracking branch 'origin/facebox-detect' into facebox-detect
robmarkcole 07d6b27
Working test
robmarkcole 3ce626d
Update test with matched_faces
robmarkcole 8d39604
Update hitron_coda.py to fix login for Shaw modems (#14306)
mikedm139 1c14d22
Add sensors for BMW electric cars (#14293)
gerard33 673a237
Add more homematicip cloud components (#14084)
worm-ee c782381
add 2 devices (#14321)
m4dmin a012b81
Gogogate2 0.1.1 (#14294)
dlbroadfoot 8145a8e
Snips: Added slot values for siteId and probability (#14315)
todschmidt 753419d
Fix Insteon PLM coverage
balloob 5537585
Add help for conversation/process service (#14323)
stephanerosi 107cf82
Fix BOM weather '-' value (#14042)
nickw444 3afd19b
Make sure zwave nodes/entities enter the registry is proper state. (#…
andrey-git 89d7896
Improving icloud device tracker (#14078)
evgeniy-khatko 2e002e1
Adds useful attributes to RainMachine programs and zones (#14087)
bachya 64b5739
Waze Travel Time: optional inclusive/exclusive filters (#14000)
mario-tux 146fc59
Bump frontend to 20180509.0
balloob c038ede
Ignore NaN values for influxdb (#14347)
amelchio 013d51e
Add zone 3 for Onkyo media player (#14295)
snikch 50806d3
Passing test
robmarkcole 39b7223
Merge remote-tracking branch 'home-assistant/dev' into facebox-detect
robmarkcole 3be6ae6
Fix lints
robmarkcole 6960ea1
Fix lints
robmarkcole File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| """ | ||
| Component that will perform facial detection and identification via a local | ||
| facebox classifier. | ||
|
|
||
| For more details about this platform, please refer to the documentation at | ||
| https://home-assistant.io/components/image_processing.facebox | ||
| """ | ||
| import base64 | ||
| import logging | ||
|
|
||
| import requests | ||
| import voluptuous as vol | ||
|
|
||
| from homeassistant.core import split_entity_id | ||
| import homeassistant.helpers.config_validation as cv | ||
| from homeassistant.components.image_processing import ( | ||
| PLATFORM_SCHEMA, ImageProcessingFaceEntity, CONF_SOURCE, CONF_ENTITY_ID, | ||
| CONF_NAME) | ||
| from homeassistant.const import (CONF_IP_ADDRESS, CONF_PORT) | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
| CLASSIFIER = 'facebox' | ||
|
|
||
| PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
| vol.Required(CONF_IP_ADDRESS): cv.string, | ||
| vol.Required(CONF_PORT): cv.port, | ||
| }) | ||
|
|
||
|
|
||
| def setup_platform(hass, config, add_devices, discovery_info=None): | ||
| """Set up the classifier.""" | ||
| entities = [] | ||
| for camera in config[CONF_SOURCE]: | ||
| entities.append(FaceClassifyEntity( | ||
| config[CONF_IP_ADDRESS], | ||
| config[CONF_PORT], | ||
| camera[CONF_ENTITY_ID], | ||
| camera.get(CONF_NAME) | ||
| )) | ||
| add_devices(entities) | ||
|
|
||
|
|
||
| class FaceClassifyEntity(ImageProcessingFaceEntity): | ||
| """Perform a face classification.""" | ||
|
|
||
| def __init__(self, ip, port, camera_entity, name=None): | ||
| """Init with the API key and model id""" | ||
| super().__init__() | ||
| self._url = "http://{}:{}/{}/check".format(ip, port, CLASSIFIER) | ||
| self._camera = camera_entity | ||
| if name: | ||
| self._name = name | ||
| else: | ||
| camera_name = split_entity_id(camera_entity)[1] | ||
| self._name = "{} {}".format( | ||
| CLASSIFIER, camera_name) | ||
| self._matched = {} | ||
|
|
||
| def process_image(self, image): | ||
| """Process an image.""" | ||
| response = {} | ||
| try: | ||
| response = requests.post( | ||
| self._url, | ||
| json=self.encode_image(image), | ||
| timeout=9 | ||
| ).json() | ||
| except requests.exceptions.ConnectionError: | ||
| _LOGGER.error("ConnectionError: Is {} running?".format(CLASSIFIER)) | ||
| response['success'] = False | ||
|
|
||
| if response['success']: | ||
| faces = response['faces'] | ||
| total = response['facesCount'] | ||
| self.process_faces(faces, total) | ||
| self._matched = self.get_matched_faces(faces) | ||
|
|
||
| else: | ||
| self.total_faces = None | ||
| self.faces = [] | ||
| self._matched = {} | ||
|
|
||
| def encode_image(self, image): | ||
| """base64 encode an image stream.""" | ||
| base64_img = base64.b64encode(image).decode('ascii') | ||
| return {"base64": base64_img} | ||
|
|
||
| def get_matched_faces(self, faces): | ||
| """Return the name and rounded confidence of matched faces.""" | ||
| return {face['name']: round(face['confidence'], 2) | ||
| for face in faces if face['matched']} | ||
|
|
||
| @property | ||
| def camera_entity(self): | ||
| """Return camera entity id from process pictures.""" | ||
| return self._camera | ||
|
|
||
| @property | ||
| def name(self): | ||
| """Return the name of the sensor.""" | ||
| return self._name | ||
|
|
||
| @property | ||
| def device_state_attributes(self): | ||
| """Return the classifier attributes.""" | ||
| return { | ||
| 'matched_faces': self._matched, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| """The tests for the facebox component.""" | ||
| import requests_mock | ||
|
|
||
| from homeassistant.const import (CONF_IP_ADDRESS, CONF_PORT) | ||
| from homeassistant.setup import async_setup_component | ||
| import homeassistant.components.image_processing as ip | ||
|
|
||
| MOCK_IP = '192.168.0.1' | ||
| MOCK_PORT = '8080' | ||
|
|
||
| MOCK_RESPONSE = """ | ||
| {"facesCount": 1, | ||
| "success": True, | ||
| "faces":[{'confidence': 0.5812028911604818, | ||
| 'id': 'john.jpg', | ||
| 'matched': True, | ||
| 'name': 'John Lennon', | ||
| 'rect': {'height': 75, 'left': 63, 'top': 262, 'width': 74} | ||
| }] | ||
| """ | ||
|
|
||
| VALID_ENTITY_ID = 'image_processing.facebox_demo_camera' | ||
| VALID_CONFIG = { | ||
| ip.DOMAIN: { | ||
| 'platform': 'facebox', | ||
| CONF_IP_ADDRESS: MOCK_IP, | ||
| CONF_PORT: MOCK_PORT, | ||
| ip.CONF_SOURCE: { | ||
| ip.CONF_ENTITY_ID: 'camera.demo_camera'} | ||
| }, | ||
| 'camera': { | ||
| 'platform': 'demo' | ||
| } | ||
| } | ||
|
|
||
|
|
||
| async def test_setup_platform(hass): | ||
| """Setup platform with one entity.""" | ||
|
|
||
| await async_setup_component(hass, ip.DOMAIN, VALID_CONFIG) | ||
| assert hass.states.get(VALID_ENTITY_ID) | ||
|
|
||
|
|
||
| async def test_process_image(hass): | ||
| """Test processing of an image.""" | ||
|
|
||
| await async_setup_component(hass, ip.DOMAIN, VALID_CONFIG) | ||
| assert hass.states.get(VALID_ENTITY_ID) | ||
|
|
||
| with requests_mock.Mocker() as mock_req: | ||
| url = "http://{}:{}/facebox/check".format(MOCK_IP, MOCK_PORT) | ||
| mock_req.post(url, text=MOCK_RESPONSE) | ||
| ip.scan(hass, entity_id=VALID_ENTITY_ID) | ||
| hass.block_till_done() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
|
|
||
| state = hass.states.get(VALID_ENTITY_ID) | ||
| assert state.state == '1' | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call the service directly with
await hass.services.async_call(...). Since we're in a coroutine here, we have to use the hass async api consistently.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok