Skip to content
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

Accessing entities within other integrations could be improved #20

Open
3 tasks done
Big-Tree opened this issue Dec 13, 2023 · 6 comments
Open
3 tasks done

Accessing entities within other integrations could be improved #20

Big-Tree opened this issue Dec 13, 2023 · 6 comments
Labels
help wanted Extra attention is needed

Comments

@Big-Tree
Copy link
Owner

Checklist

  • I have filled out the template to the best of my ability.
  • This only contains 1 feature request (if you have multiple feature requests, open one feature request for each feature request).
  • This issue is not a duplicate feature request of previous feature requests.

Is your feature request related to a problem? Please describe.

The method by which entities are are accessed and controlled is not officially supported. They may be better alternative methods:

def get_entity(hass: HomeAssistant, entity_id: str):
"""Get the entity associated with entity_id. It could be owned by another integration.
Accessing entities of other integrations does not seem to be supported. The method we use
seems a bit dodgy.
"""
entity_reg: RegistryEntry = entity_registry.async_get(hass).async_get(entity_id)
domain: str = entity_reg.platform
entity_coordinator: DataUpdateCoordinator = hass.data[domain][entity_reg.config_entry_id]
# Get the entity via the entity coordinator
for update_callback, _ in entity_coordinator._listeners.values():
if update_callback.__self__.unique_id == entity_reg.unique_id:
return update_callback.__self__

Describe the solution you'd like

Describe alternatives you've considered

Additional context

@Big-Tree Big-Tree added the help wanted Extra attention is needed label Dec 13, 2023
@chappers00
Copy link
Collaborator

Logger: aiohttp.server
Source: /usr/local/lib/python3.11/site-packages/aiohttp/web_protocol.py:403
First occurred: 4:14:41 PM (2 occurrences)
Last logged: 4:15:06 PM

Error handling request
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/aiohttp/web_protocol.py", line 433, in _handle_request
resp = await request_handler(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/web_app.py", line 504, in _handle
resp = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/web_middlewares.py", line 117, in impl
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/security_filter.py", line 85, in security_filter_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/forwarded.py", line 100, in forwarded_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/request_context.py", line 28, in request_context_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/ban.py", line 80, in ban_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/auth.py", line 236, in auth_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/headers.py", line 31, in headers_middleware
response = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/view.py", line 148, in handle
result = await handler(request, **request.match_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/decorators.py", line 63, in with_admin
return await func(self, request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/config/config_entries.py", line 177, in post
return await super().post(request, flow_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/data_validator.py", line 72, in wrapper
result = await method(view, request, data, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/data_entry_flow.py", line 110, in post
result = await self._flow_mgr.async_configure(flow_id, data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/data_entry_flow.py", line 293, in async_configure
result = await self._async_handle_step(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/data_entry_flow.py", line 389, in _async_handle_step
result: FlowResult = await getattr(flow, method)(user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/optispark/config_flow.py", line 71, in async_step_init
postcode = await self._test_credentials(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/optispark/config_flow.py", line 144, in _test_credentials
power_entity = get_entity(self.hass, heat_pump_power_entity_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/optispark/init.py", line 70, in get_entity
entity_coordinator: DataUpdateCoordinator = hass.data[domain][entity_reg.config_entry_id]
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'e35ea7b6bea2b0c6d7ed57830f3effc6'

@Big-Tree
Copy link
Owner Author

It's trying to get the entity which measures the power of the heat pump. It looks like the domain is 'e35ea7b6bea2b0c6d7ed57830f3effc6' and that doesn't exist. However, 'e35ea7b6bea2b0c6d7ed57830f3effc6' doesn't look like a typical domain name so I think the method by which I get the domain name is flawed.

@Big-Tree
Copy link
Owner Author

I've changed the way domain is fetched and added some more logging.

def get_entity(hass: HomeAssistant, entity_id: str):
"""Get the entity associated with entity_id. It could be owned by another integration.
Accessing entities of other integrations does not seem to be supported. The method we use
seems a bit dodgy.
"""
entity_reg: RegistryEntry = entity_registry.async_get(hass).async_get(entity_id)
# Lets get the domain name via the device id
from homeassistant.helpers.device_registry import DeviceRegistry
from homeassistant.helpers import device_registry
device_id = entity_reg.device_id
device_reg: DeviceRegistry = device_registry.async_get(hass).async_get(device_id)
domain: str = list(device_reg.identifiers)[0][0]
#domain: str = entity_reg.platform
LOGGER.info(f'entity_id: {entity_id}')
LOGGER.info(f'domain: {domain}')
LOGGER.info(f'dir(hass.data): {dir(hass.data)}')
entity_coordinator: DataUpdateCoordinator = hass.data[domain][entity_reg.config_entry_id]
# Get the entity via the entity coordinator
for update_callback, _ in entity_coordinator._listeners.values():
if update_callback.__self__.unique_id == entity_reg.unique_id:
return update_callback.__self__

@chappers00
Copy link
Collaborator

Logs with latest version:
2024-01-08 17:38:16.255 ERROR (MainThread) [custom_components.optispark] Traceback (most recent call last):
File "/config/custom_components/optispark/init.py", line 90, in get_entity
entity_coordinator: DataUpdateCoordinator = hass.data[domain][entity_reg.config_entry_id]

KeyError: 'e35ea7b6bea2b0c6d7ed57830f3effc6'
2024-01-08 17:38:16.255 ERROR (MainThread) [custom_components.optispark] 'e35ea7b6bea2b0c6d7ed57830f3effc6'
2024-01-08 17:38:16.255 ERROR (MainThread) [custom_components.optispark] 'e35ea7b6bea2b0c6d7ed57830f3effc6'

@Big-Tree
Copy link
Owner Author

Big-Tree commented Jan 9, 2024

I've updated how entities from other integrations are fetched again. 3rd time lucky? It's similar to how HA automation get entities.

This has been tested with Daikin's heat pump integration

def get_entity(hass, entity_id):
"""Get entity instance from entity_id.
All integrations have their data stored in hass.data[domain]
HA integrations such as climate, switch, binary sensor have get_entity() methods that fetch
any entities that belong to that domain, or platforms that have implemented that domain. This
includes 3rd party integrations that have implemented HA integrations (as a platform).
Scan for entity_id with all integrations that implement get_method(). We assume that only one
entity instance will be found.
"""
entities_found = []
successful_domains = []
for domain in hass.data:
if hasattr(hass.data[domain], 'get_entity'):
entity = hass.data[domain].get_entity(entity_id)
if entity is not None:
entities_found.append(entity)
successful_domains.append(domain)
if len(entities_found) != 1:
raise OptisparkGetEntityError(f'({len(entities_found)}) entities found instead of 1')
return entities_found[0]

@chappers00
Copy link
Collaborator

2024-01-10 16:11:27.086 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/aiohttp/web_protocol.py", line 452, in _handle_request
resp = await request_handler(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/web_app.py", line 543, in _handle
resp = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/web_middlewares.py", line 114, in impl
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/security_filter.py", line 85, in security_filter_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/forwarded.py", line 100, in forwarded_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/request_context.py", line 28, in request_context_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/ban.py", line 80, in ban_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/auth.py", line 233, in auth_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/headers.py", line 31, in headers_middleware
response = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/view.py", line 149, in handle
result = await handler(request, **request.match_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/decorators.py", line 63, in with_admin
return await func(self, request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/config/config_entries.py", line 177, in post
return await super().post(request, flow_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/http/data_validator.py", line 72, in wrapper
result = await method(view, request, data, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/data_entry_flow.py", line 110, in post
result = await self._flow_mgr.async_configure(flow_id, data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/data_entry_flow.py", line 320, in async_configure
result = await self._async_handle_step(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/data_entry_flow.py", line 416, in _async_handle_step
result: FlowResult = await getattr(flow, method)(user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/optispark/config_flow.py", line 159, in async_step_accept
dynamo_data = await get_history(
^^^^^^^^^^^^^^^^^^
File "/config/custom_components/optispark/history.py", line 240, in get_history
state_changes = await get_state_changes(hass, entity_id, history_days)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/optispark/history.py", line 212, in get_state_changes
state_changes = await get_instance(hass).async_add_executor_job(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/recorder/history/init.py", line 174, in state_changes_during_period
return _target(
^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/recorder/history/modern.py", line 373, in state_changes_during_period
raise ValueError("entity_id must be provided")
ValueError: entity_id must be provided

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants