196 lines
6.4 KiB
Python
196 lines
6.4 KiB
Python
"""Google config for Cloud."""
|
|
import asyncio
|
|
import logging
|
|
|
|
from hass_nabucasa import cloud_api
|
|
from hass_nabucasa.google_report_state import ErrorResponse
|
|
|
|
from homeassistant.components.google_assistant.helpers import AbstractConfig
|
|
from homeassistant.const import (
|
|
CLOUD_NEVER_EXPOSED_ENTITIES,
|
|
EVENT_HOMEASSISTANT_STARTED,
|
|
HTTP_OK,
|
|
)
|
|
from homeassistant.core import CoreState, callback
|
|
from homeassistant.helpers import entity_registry
|
|
|
|
from .const import (
|
|
CONF_ENTITY_CONFIG,
|
|
DEFAULT_DISABLE_2FA,
|
|
DEFAULT_SHOULD_EXPOSE,
|
|
PREF_DISABLE_2FA,
|
|
PREF_SHOULD_EXPOSE,
|
|
)
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class CloudGoogleConfig(AbstractConfig):
|
|
"""HA Cloud Configuration for Google Assistant."""
|
|
|
|
def __init__(self, hass, config, cloud_user, prefs, cloud):
|
|
"""Initialize the Google config."""
|
|
super().__init__(hass)
|
|
self._config = config
|
|
self._user = cloud_user
|
|
self._prefs = prefs
|
|
self._cloud = cloud
|
|
self._cur_entity_prefs = self._prefs.google_entity_configs
|
|
self._sync_entities_lock = asyncio.Lock()
|
|
self._sync_on_started = False
|
|
|
|
@property
|
|
def enabled(self):
|
|
"""Return if Google is enabled."""
|
|
return self._cloud.is_logged_in and self._prefs.google_enabled
|
|
|
|
@property
|
|
def entity_config(self):
|
|
"""Return entity config."""
|
|
return self._config.get(CONF_ENTITY_CONFIG) or {}
|
|
|
|
@property
|
|
def secure_devices_pin(self):
|
|
"""Return entity config."""
|
|
return self._prefs.google_secure_devices_pin
|
|
|
|
@property
|
|
def should_report_state(self):
|
|
"""Return if states should be proactively reported."""
|
|
return self._cloud.is_logged_in and self._prefs.google_report_state
|
|
|
|
@property
|
|
def local_sdk_webhook_id(self):
|
|
"""Return the local SDK webhook.
|
|
|
|
Return None to disable the local SDK.
|
|
"""
|
|
return self._prefs.google_local_webhook_id
|
|
|
|
@property
|
|
def local_sdk_user_id(self):
|
|
"""Return the user ID to be used for actions received via the local SDK."""
|
|
return self._user
|
|
|
|
@property
|
|
def cloud_user(self):
|
|
"""Return Cloud User account."""
|
|
return self._user
|
|
|
|
async def async_initialize(self):
|
|
"""Perform async initialization of config."""
|
|
await super().async_initialize()
|
|
# Remove bad data that was there until 0.103.6 - Jan 6, 2020
|
|
self._store.pop_agent_user_id(self._user)
|
|
|
|
self._prefs.async_listen_updates(self._async_prefs_updated)
|
|
|
|
self.hass.bus.async_listen(
|
|
entity_registry.EVENT_ENTITY_REGISTRY_UPDATED,
|
|
self._handle_entity_registry_updated,
|
|
)
|
|
|
|
def should_expose(self, state):
|
|
"""If a state object should be exposed."""
|
|
return self._should_expose_entity_id(state.entity_id)
|
|
|
|
def _should_expose_entity_id(self, entity_id):
|
|
"""If an entity ID should be exposed."""
|
|
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
|
return False
|
|
|
|
if not self._config["filter"].empty_filter:
|
|
return self._config["filter"](entity_id)
|
|
|
|
entity_configs = self._prefs.google_entity_configs
|
|
entity_config = entity_configs.get(entity_id, {})
|
|
return entity_config.get(PREF_SHOULD_EXPOSE, DEFAULT_SHOULD_EXPOSE)
|
|
|
|
@property
|
|
def agent_user_id(self):
|
|
"""Return Agent User Id to use for query responses."""
|
|
return self._cloud.username
|
|
|
|
def get_agent_user_id(self, context):
|
|
"""Get agent user ID making request."""
|
|
return self.agent_user_id
|
|
|
|
def should_2fa(self, state):
|
|
"""If an entity should be checked for 2FA."""
|
|
entity_configs = self._prefs.google_entity_configs
|
|
entity_config = entity_configs.get(state.entity_id, {})
|
|
return not entity_config.get(PREF_DISABLE_2FA, DEFAULT_DISABLE_2FA)
|
|
|
|
async def async_report_state(self, message, agent_user_id: str):
|
|
"""Send a state report to Google."""
|
|
try:
|
|
await self._cloud.google_report_state.async_send_message(message)
|
|
except ErrorResponse as err:
|
|
_LOGGER.warning("Error reporting state - %s: %s", err.code, err.message)
|
|
|
|
async def _async_request_sync_devices(self, agent_user_id: str):
|
|
"""Trigger a sync with Google."""
|
|
if self._sync_entities_lock.locked():
|
|
return HTTP_OK
|
|
|
|
async with self._sync_entities_lock:
|
|
resp = await cloud_api.async_google_actions_request_sync(self._cloud)
|
|
return resp.status
|
|
|
|
async def _async_prefs_updated(self, prefs):
|
|
"""Handle updated preferences."""
|
|
if self.should_report_state != self.is_reporting_state:
|
|
if self.should_report_state:
|
|
self.async_enable_report_state()
|
|
else:
|
|
self.async_disable_report_state()
|
|
|
|
# State reporting is reported as a property on entities.
|
|
# So when we change it, we need to sync all entities.
|
|
await self.async_sync_entities_all()
|
|
|
|
# If entity prefs are the same or we have filter in config.yaml,
|
|
# don't sync.
|
|
elif (
|
|
self._cur_entity_prefs is not prefs.google_entity_configs
|
|
and self._config["filter"].empty_filter
|
|
):
|
|
self.async_schedule_google_sync_all()
|
|
|
|
if self.enabled and not self.is_local_sdk_active:
|
|
self.async_enable_local_sdk()
|
|
elif not self.enabled and self.is_local_sdk_active:
|
|
self.async_disable_local_sdk()
|
|
|
|
async def _handle_entity_registry_updated(self, event):
|
|
"""Handle when entity registry updated."""
|
|
if not self.enabled or not self._cloud.is_logged_in:
|
|
return
|
|
|
|
# Only consider entity registry updates if info relevant for Google has changed
|
|
if event.data["action"] == "update" and not bool(
|
|
set(event.data["changes"]) & entity_registry.ENTITY_DESCRIBING_ATTRIBUTES
|
|
):
|
|
return
|
|
|
|
entity_id = event.data["entity_id"]
|
|
|
|
if not self._should_expose_entity_id(entity_id):
|
|
return
|
|
|
|
if self.hass.state == CoreState.running:
|
|
self.async_schedule_google_sync_all()
|
|
return
|
|
|
|
if self._sync_on_started:
|
|
return
|
|
|
|
self._sync_on_started = True
|
|
|
|
@callback
|
|
async def sync_google(_):
|
|
"""Sync entities to Google."""
|
|
await self.async_sync_entities_all()
|
|
|
|
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, sync_google)
|