Skip to content

Commit

Permalink
Allow exposing entities not in the entity registry to assistants (hom…
Browse files Browse the repository at this point in the history
  • Loading branch information
emontnemery authored and frenck committed May 2, 2023
1 parent 0db28dc commit 5b1278d
Show file tree
Hide file tree
Showing 52 changed files with 562 additions and 223 deletions.
3 changes: 1 addition & 2 deletions homeassistant/components/alexa/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ async def async_disable_proactive_mode(self):
unsub_func()
self._unsub_proactive_report = None

@callback
def should_expose(self, entity_id):
async def should_expose(self, entity_id):
"""If an entity should be exposed."""
return False

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/alexa/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ async def async_api_discovery(
discovery_endpoints = [
alexa_entity.serialize_discovery()
for alexa_entity in async_get_entities(hass, config)
if config.should_expose(alexa_entity.entity_id)
if await config.should_expose(alexa_entity.entity_id)
]

return directive.response(
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/alexa/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self, request):

self.entity = self.entity_id = self.endpoint = self.instance = None

def load_entity(self, hass, config):
async def load_entity(self, hass, config):
"""Set attributes related to the entity for this request.
Sets these attributes when self.has_endpoint is True:
Expand All @@ -49,7 +49,7 @@ def load_entity(self, hass, config):
self.entity_id = _endpoint_id.replace("#", ".")

self.entity = hass.states.get(self.entity_id)
if not self.entity or not config.should_expose(self.entity_id):
if not self.entity or not await config.should_expose(self.entity_id):
raise AlexaInvalidEndpointError(_endpoint_id)

self.endpoint = ENTITY_ADAPTERS[self.entity.domain](hass, config, self.entity)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/alexa/smart_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async def async_handle_message(hass, config, request, context=None, enabled=True
await config.set_authorized(True)

if directive.has_endpoint:
directive.load_entity(hass, config)
await directive.load_entity(hass, config)

funct_ref = HANDLERS.get((directive.namespace, directive.name))
if funct_ref:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/alexa/smart_home_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def user_identifier(self):
"""Return an identifier for the user that represents this config."""
return ""

def should_expose(self, entity_id):
async def should_expose(self, entity_id):
"""If an entity should be exposed."""
if not self._config[CONF_FILTER].empty_filter:
return self._config[CONF_FILTER](entity_id)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/alexa/state_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ async def async_entity_state_listener(
if new_state.domain not in ENTITY_ADAPTERS:
return

if not smart_home_config.should_expose(changed_entity):
if not await smart_home_config.should_expose(changed_entity):
_LOGGER.debug("Not exposing %s because filtered by config", changed_entity)
return

Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/cloud/alexa_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,14 @@ def _should_expose_legacy(self, entity_id):
and entity_supported(self.hass, entity_id)
)

def should_expose(self, entity_id):
async def should_expose(self, entity_id):
"""If an entity should be exposed."""
if not self._config[CONF_FILTER].empty_filter:
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
return False
return self._config[CONF_FILTER](entity_id)

return async_should_expose(self.hass, CLOUD_ALEXA, entity_id)
return await async_should_expose(self.hass, CLOUD_ALEXA, entity_id)

@callback
def async_invalidate_access_token(self):
Expand Down Expand Up @@ -423,7 +423,7 @@ async def async_sync_entities(self):
is_enabled = self.enabled

for entity in alexa_entities.async_get_entities(self.hass, self):
if is_enabled and self.should_expose(entity.entity_id):
if is_enabled and await self.should_expose(entity.entity_id):
to_update.append(entity.entity_id)
else:
to_remove.append(entity.entity_id)
Expand Down Expand Up @@ -482,7 +482,7 @@ async def _handle_entity_registry_updated(self, event):

entity_id = event.data["entity_id"]

if not self.should_expose(entity_id):
if not await self.should_expose(entity_id):
return

action = event.data["action"]
Expand Down
30 changes: 15 additions & 15 deletions homeassistant/components/cloud/google_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,9 @@ async def hass_started(hass):
self._handle_device_registry_updated,
)

def should_expose(self, state):
async def should_expose(self, state):
"""If a state object should be exposed."""
return self._should_expose_entity_id(state.entity_id)
return await self._should_expose_entity_id(state.entity_id)

def _should_expose_legacy(self, entity_id):
"""If an entity ID should be exposed."""
Expand Down Expand Up @@ -258,14 +258,14 @@ def _should_expose_legacy(self, entity_id):
and _supported_legacy(self.hass, entity_id)
)

def _should_expose_entity_id(self, entity_id):
async def _should_expose_entity_id(self, entity_id):
"""If an entity should be exposed."""
if not self._config[CONF_FILTER].empty_filter:
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
return False
return self._config[CONF_FILTER](entity_id)

return async_should_expose(self.hass, CLOUD_GOOGLE, entity_id)
return await async_should_expose(self.hass, CLOUD_GOOGLE, entity_id)

@property
def agent_user_id(self):
Expand Down Expand Up @@ -358,8 +358,7 @@ def _async_exposed_entities_updated(self) -> None:
"""Handle updated preferences."""
self.async_schedule_google_sync_all()

@callback
def _handle_entity_registry_updated(self, event: Event) -> None:
async def _handle_entity_registry_updated(self, event: Event) -> None:
"""Handle when entity registry updated."""
if (
not self.enabled
Expand All @@ -376,13 +375,12 @@ def _handle_entity_registry_updated(self, event: Event) -> None:

entity_id = event.data["entity_id"]

if not self._should_expose_entity_id(entity_id):
if not await self._should_expose_entity_id(entity_id):
return

self.async_schedule_google_sync_all()

@callback
def _handle_device_registry_updated(self, event: Event) -> None:
async def _handle_device_registry_updated(self, event: Event) -> None:
"""Handle when device registry updated."""
if (
not self.enabled
Expand All @@ -396,13 +394,15 @@ def _handle_device_registry_updated(self, event: Event) -> None:
return

# Check if any exposed entity uses the device area
if not any(
entity_entry.area_id is None
and self._should_expose_entity_id(entity_entry.entity_id)
for entity_entry in er.async_entries_for_device(
er.async_get(self.hass), event.data["device_id"]
)
used = False
for entity_entry in er.async_entries_for_device(
er.async_get(self.hass), event.data["device_id"]
):
if entity_entry.area_id is None and await self._should_expose_entity_id(
entity_entry.entity_id
):
used = True
if not used:
return

self.async_schedule_google_sync_all()
6 changes: 3 additions & 3 deletions homeassistant/components/conversation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def agent_id_validator(value: Any) -> str:
def _get_agent_manager(hass: HomeAssistant) -> AgentManager:
"""Get the active agent."""
manager = AgentManager(hass)
manager.async_setup()
hass.async_create_task(manager.async_setup())
return manager


Expand Down Expand Up @@ -393,9 +393,9 @@ def __init__(self, hass: HomeAssistant) -> None:
self._agents: dict[str, AbstractConversationAgent] = {}
self._builtin_agent_init_lock = asyncio.Lock()

def async_setup(self) -> None:
async def async_setup(self) -> None:
"""Set up the conversation agents."""
async_setup_default_agent(self.hass)
await async_setup_default_agent(self.hass)

async def async_get_agent(
self, agent_id: str | None = None
Expand Down
17 changes: 7 additions & 10 deletions homeassistant/components/conversation/default_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,20 @@ def _get_language_variations(language: str) -> Iterable[str]:
yield lang


@core.callback
def async_setup(hass: core.HomeAssistant) -> None:
async def async_setup(hass: core.HomeAssistant) -> None:
"""Set up entity registry listener for the default agent."""
entity_registry = er.async_get(hass)
for entity_id in entity_registry.entities:
async_should_expose(hass, DOMAIN, entity_id)
await async_should_expose(hass, DOMAIN, entity_id)

@core.callback
def async_handle_entity_registry_changed(event: core.Event) -> None:
async def async_handle_entity_registry_changed(event: core.Event) -> None:
"""Set expose flag on newly created entities."""
if event.data["action"] == "create":
async_should_expose(hass, DOMAIN, event.data["entity_id"])
await async_should_expose(hass, DOMAIN, event.data["entity_id"])

hass.bus.async_listen(
er.EVENT_ENTITY_REGISTRY_UPDATED,
async_handle_entity_registry_changed,
run_immediately=True,
)


Expand Down Expand Up @@ -157,7 +154,7 @@ async def async_process(self, user_input: ConversationInput) -> ConversationResu
conversation_id,
)

slot_lists = self._make_slot_lists()
slot_lists = await self._make_slot_lists()

result = await self.hass.async_add_executor_job(
self._recognize,
Expand Down Expand Up @@ -486,7 +483,7 @@ def _async_exposed_entities_updated(self) -> None:
"""Handle updated preferences."""
self._slot_lists = None

def _make_slot_lists(self) -> dict[str, SlotList]:
async def _make_slot_lists(self) -> dict[str, SlotList]:
"""Create slot lists with areas and entity names/aliases."""
if self._slot_lists is not None:
return self._slot_lists
Expand All @@ -496,7 +493,7 @@ def _make_slot_lists(self) -> dict[str, SlotList]:
entities = [
entity
for entity in entity_registry.entities.values()
if async_should_expose(self.hass, DOMAIN, entity.entity_id)
if await async_should_expose(self.hass, DOMAIN, entity.entity_id)
]
devices = dr.async_get(self.hass)

Expand Down
16 changes: 7 additions & 9 deletions homeassistant/components/google_assistant/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def get_agent_user_id(self, context):
"""Get agent user ID from context."""

@abstractmethod
def should_expose(self, state) -> bool:
async def should_expose(self, state) -> bool:
"""Return if entity should be exposed."""

def should_2fa(self, state):
Expand Down Expand Up @@ -535,16 +535,14 @@ def traits(self):
]
return self._traits

@callback
def should_expose(self):
async def should_expose(self):
"""If entity should be exposed."""
return self.config.should_expose(self.state)
return await self.config.should_expose(self.state)

@callback
def should_expose_local(self) -> bool:
async def should_expose_local(self) -> bool:
"""Return if the entity should be exposed locally."""
return (
self.should_expose()
await self.should_expose()
and get_google_type(
self.state.domain, self.state.attributes.get(ATTR_DEVICE_CLASS)
)
Expand Down Expand Up @@ -587,7 +585,7 @@ def might_2fa_traits(self) -> bool:
trait.might_2fa(domain, features, device_class) for trait in self.traits()
)

def sync_serialize(self, agent_user_id, instance_uuid):
async def sync_serialize(self, agent_user_id, instance_uuid):
"""Serialize entity for a SYNC response.
https://developers.google.com/actions/smarthome/create-app#actiondevicessync
Expand Down Expand Up @@ -623,7 +621,7 @@ def sync_serialize(self, agent_user_id, instance_uuid):
device["name"]["nicknames"].extend(entity_entry.aliases)

# Add local SDK info if enabled
if self.config.is_local_sdk_active and self.should_expose_local():
if self.config.is_local_sdk_active and await self.should_expose_local():
device["otherDeviceIds"] = [{"deviceId": self.entity_id}]
device["customData"] = {
"webhookId": self.config.get_local_webhook_id(agent_user_id),
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/google_assistant/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def should_report_state(self):
"""Return if states should be proactively reported."""
return self._config.get(CONF_REPORT_STATE)

def should_expose(self, state) -> bool:
async 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)
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/google_assistant/report_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async def async_entity_state_listener(changed_entity, old_state, new_state):
if not new_state:
return

if not google_config.should_expose(new_state):
if not await google_config.should_expose(new_state):
return

entity = GoogleEntity(hass, google_config, new_state)
Expand Down Expand Up @@ -115,7 +115,7 @@ async def initial_report(_now):
checker = await create_checker(hass, DOMAIN, extra_significant_check)

for entity in async_get_entities(hass, google_config):
if not entity.should_expose():
if not await entity.should_expose():
continue

try:
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/google_assistant/smart_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ async def async_devices_sync_response(hass, config, agent_user_id):
devices = []

for entity in entities:
if not entity.should_expose():
if not await entity.should_expose():
continue

try:
devices.append(entity.sync_serialize(agent_user_id, instance_uuid))
devices.append(await entity.sync_serialize(agent_user_id, instance_uuid))
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Error serializing %s", entity.entity_id)

Expand Down Expand Up @@ -318,7 +318,7 @@ async def async_devices_reachable(
"devices": [
entity.reachable_device_serialize()
for entity in async_get_entities(hass, data.config)
if entity.entity_id in google_ids and entity.should_expose_local()
if entity.entity_id in google_ids and await entity.should_expose_local()
]
}

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/homeassistant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ async def async_handle_reload_all(call: ha.ServiceCall) -> None:
)

exposed_entities = ExposedEntities(hass)
await exposed_entities.async_initialize()
await exposed_entities.async_load()
hass.data[DATA_EXPOSED_ENTITIES] = exposed_entities

return True
Loading

0 comments on commit 5b1278d

Please sign in to comment.