2019-04-03 15:40:03 +00:00
|
|
|
"""Support for Google Actions Smart Home Control."""
|
2017-10-18 05:00:59 +00:00
|
|
|
import logging
|
|
|
|
|
2018-06-25 17:05:07 +00:00
|
|
|
from aiohttp.web import Request, Response
|
2017-11-04 19:04:05 +00:00
|
|
|
|
2017-10-18 05:00:59 +00:00
|
|
|
# Typing imports
|
2017-11-04 19:04:05 +00:00
|
|
|
from homeassistant.components.http import HomeAssistantView
|
2018-07-01 15:57:01 +00:00
|
|
|
from homeassistant.core import callback
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
|
2017-10-18 05:00:59 +00:00
|
|
|
|
|
|
|
from .const import (
|
2017-11-13 16:32:23 +00:00
|
|
|
GOOGLE_ASSISTANT_API_ENDPOINT,
|
|
|
|
CONF_EXPOSE_BY_DEFAULT,
|
|
|
|
CONF_EXPOSED_DOMAINS,
|
2018-01-09 23:14:56 +00:00
|
|
|
CONF_ENTITY_CONFIG,
|
|
|
|
CONF_EXPOSE,
|
2019-04-19 21:50:21 +00:00
|
|
|
CONF_SECURE_DEVICES_PIN,
|
|
|
|
)
|
2018-03-08 22:39:10 +00:00
|
|
|
from .smart_home import async_handle_message
|
2019-06-21 09:17:21 +00:00
|
|
|
from .helpers import AbstractConfig
|
2017-10-18 05:00:59 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2019-06-21 09:17:21 +00:00
|
|
|
class GoogleConfig(AbstractConfig):
|
|
|
|
"""Config for manual setup of Google."""
|
|
|
|
|
|
|
|
def __init__(self, config):
|
|
|
|
"""Initialize the config."""
|
|
|
|
self._config = config
|
|
|
|
|
|
|
|
@property
|
|
|
|
def agent_user_id(self):
|
|
|
|
"""Return Agent User Id to use for query responses."""
|
|
|
|
return None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def entity_config(self):
|
|
|
|
"""Return entity config."""
|
2019-06-27 19:17:42 +00:00
|
|
|
return self._config.get(CONF_ENTITY_CONFIG) or {}
|
2019-06-21 09:17:21 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def secure_devices_pin(self):
|
|
|
|
"""Return entity config."""
|
|
|
|
return self._config.get(CONF_SECURE_DEVICES_PIN)
|
|
|
|
|
|
|
|
def should_expose(self, state) -> bool:
|
|
|
|
"""Return if entity should be exposed."""
|
|
|
|
expose_by_default = self._config.get(CONF_EXPOSE_BY_DEFAULT)
|
|
|
|
exposed_domains = self._config.get(CONF_EXPOSED_DOMAINS)
|
|
|
|
|
|
|
|
if state.attributes.get('view') is not None:
|
2017-10-18 05:00:59 +00:00
|
|
|
# Ignore entities that are views
|
|
|
|
return False
|
|
|
|
|
2019-06-21 09:17:21 +00:00
|
|
|
if state.entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
return False
|
|
|
|
|
2018-01-09 23:14:56 +00:00
|
|
|
explicit_expose = \
|
2019-06-21 09:17:21 +00:00
|
|
|
self.entity_config.get(state.entity_id, {}).get(CONF_EXPOSE)
|
2017-10-18 05:00:59 +00:00
|
|
|
|
|
|
|
domain_exposed_by_default = \
|
2019-06-21 09:17:21 +00:00
|
|
|
expose_by_default and state.domain in exposed_domains
|
2017-10-18 05:00:59 +00:00
|
|
|
|
|
|
|
# Expose an entity if the entity's domain is exposed by default and
|
|
|
|
# the configuration doesn't explicitly exclude it from being
|
|
|
|
# exposed, or if the entity is explicitly exposed
|
|
|
|
is_default_exposed = \
|
|
|
|
domain_exposed_by_default and explicit_expose is not False
|
|
|
|
|
|
|
|
return is_default_exposed or explicit_expose
|
|
|
|
|
2019-06-21 09:17:21 +00:00
|
|
|
def should_2fa(self, state):
|
|
|
|
"""If an entity should have 2FA checked."""
|
|
|
|
return True
|
2019-04-19 21:50:21 +00:00
|
|
|
|
2019-06-21 09:17:21 +00:00
|
|
|
|
|
|
|
@callback
|
|
|
|
def async_register_http(hass, cfg):
|
|
|
|
"""Register HTTP views for Google Assistant."""
|
|
|
|
hass.http.register_view(GoogleAssistantView(GoogleConfig(cfg)))
|
2017-10-18 05:00:59 +00:00
|
|
|
|
|
|
|
|
2017-12-31 23:04:49 +00:00
|
|
|
class GoogleAssistantView(HomeAssistantView):
|
|
|
|
"""Handle Google Assistant requests."""
|
2017-10-18 05:00:59 +00:00
|
|
|
|
2017-12-31 23:04:49 +00:00
|
|
|
url = GOOGLE_ASSISTANT_API_ENDPOINT
|
|
|
|
name = 'api:google_assistant'
|
2018-09-26 06:57:55 +00:00
|
|
|
requires_auth = True
|
2017-10-18 05:00:59 +00:00
|
|
|
|
2019-04-19 21:50:21 +00:00
|
|
|
def __init__(self, config):
|
2017-12-31 23:04:49 +00:00
|
|
|
"""Initialize the Google Assistant request handler."""
|
2019-04-19 21:50:21 +00:00
|
|
|
self.config = config
|
2017-10-18 05:00:59 +00:00
|
|
|
|
2018-04-28 23:26:20 +00:00
|
|
|
async def post(self, request: Request) -> Response:
|
2017-10-18 05:00:59 +00:00
|
|
|
"""Handle Google Assistant requests."""
|
2018-04-28 23:26:20 +00:00
|
|
|
message = await request.json() # type: dict
|
|
|
|
result = await async_handle_message(
|
2019-03-06 04:00:53 +00:00
|
|
|
request.app['hass'],
|
|
|
|
self.config,
|
|
|
|
request['hass_user'].id,
|
|
|
|
message)
|
2017-12-31 23:04:49 +00:00
|
|
|
return self.json(result)
|