Merge pull request #3067 from Mikhail5555/move-away-from-homeassistant-api

use normal async client for api calls
This commit is contained in:
vabene1111
2024-03-28 07:38:43 +01:00
committed by GitHub
2 changed files with 47 additions and 29 deletions

View File

@@ -1,26 +1,35 @@
import logging import logging
from logging import Logger from logging import Logger
from typing import Dict, Tuple
from urllib.parse import urljoin
from homeassistant_api import Client, HomeassistantAPIError, Domain from aiohttp import ClientError, request
from cookbook.connectors.connector import Connector from cookbook.connectors.connector import Connector
from cookbook.models import ShoppingListEntry, ConnectorConfig, Space from cookbook.models import ShoppingListEntry, ConnectorConfig, Space
class HomeAssistant(Connector): class HomeAssistant(Connector):
_domains_cache: dict[str, Domain]
_config: ConnectorConfig _config: ConnectorConfig
_logger: Logger _logger: Logger
_client: Client
def __init__(self, config: ConnectorConfig): def __init__(self, config: ConnectorConfig):
if not config.token or not config.url or not config.todo_entity: if not config.token or not config.url or not config.todo_entity:
raise ValueError("config for HomeAssistantConnector in incomplete") raise ValueError("config for HomeAssistantConnector in incomplete")
self._domains_cache = dict() if config.url[-1] != "/":
config.url += "/"
self._config = config self._config = config
self._logger = logging.getLogger("connector.HomeAssistant") self._logger = logging.getLogger("connector.HomeAssistant")
self._client = Client(self._config.url, self._config.token, async_cache_session=False, use_async=True)
async def homeassistant_api_call(self, method: str, path: str, data: Dict) -> str:
headers = {
"Authorization": f"Bearer {self._config.token}",
"Content-Type": "application/json"
}
async with request(method, urljoin(self._config.url, path), headers=headers, json=data) as response:
response.raise_for_status()
return await response.json()
async def on_shopping_list_entry_created(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None: async def on_shopping_list_entry_created(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
if not self._config.on_shopping_list_entry_created_enabled: if not self._config.on_shopping_list_entry_created_enabled:
@@ -28,15 +37,17 @@ class HomeAssistant(Connector):
item, description = _format_shopping_list_entry(shopping_list_entry) item, description = _format_shopping_list_entry(shopping_list_entry)
todo_domain = self._domains_cache.get('todo') logging.debug(f"adding {item=} to {self._config.name}")
try:
if todo_domain is None:
todo_domain = await self._client.async_get_domain('todo')
self._domains_cache['todo'] = todo_domain
logging.debug(f"pushing {item} to {self._config.name}") data = {
await todo_domain.add_item(entity_id=self._config.todo_entity, item=item) "entity_id": self._config.todo_entity,
except HomeassistantAPIError as err: "item": item,
"description": description,
}
try:
await self.homeassistant_api_call("POST", "services/todo/add_item", data)
except ClientError as err:
self._logger.warning(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}") self._logger.warning(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
async def on_shopping_list_entry_updated(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None: async def on_shopping_list_entry_updated(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
@@ -48,24 +59,31 @@ class HomeAssistant(Connector):
if not self._config.on_shopping_list_entry_deleted_enabled: if not self._config.on_shopping_list_entry_deleted_enabled:
return return
item, description = _format_shopping_list_entry(shopping_list_entry) if not hasattr(shopping_list_entry._state.fields_cache, "food"):
# Sometimes the food foreign key is not loaded, and we cant load it from an async process
self._logger.debug("required property was not present in ShoppingListEntry")
return
item, _ = _format_shopping_list_entry(shopping_list_entry)
logging.debug(f"removing {item=} from {self._config.name}")
data = {
"entity_id": self._config.todo_entity,
"item": item,
}
todo_domain = self._domains_cache.get('todo')
try: try:
if todo_domain is None: await self.homeassistant_api_call("POST", "services/todo/remove_item", data)
todo_domain = await self._client.async_get_domain('todo') except ClientError as err:
self._domains_cache['todo'] = todo_domain # This error will always trigger if the item is not present/found
self._logger.debug(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
logging.debug(f"deleting {item} from {self._config.name}")
await todo_domain.remove_item(entity_id=self._config.todo_entity, item=item)
except HomeassistantAPIError as err:
self._logger.warning(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
async def close(self) -> None: async def close(self) -> None:
await self._client.async_cache_session.close() pass
def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry): def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry) -> Tuple[str, str]:
item = shopping_list_entry.food.name item = shopping_list_entry.food.name
if shopping_list_entry.amount > 0: if shopping_list_entry.amount > 0:
item += f" ({shopping_list_entry.amount:.2f}".rstrip('0').rstrip('.') item += f" ({shopping_list_entry.amount:.2f}".rstrip('0').rstrip('.')
@@ -76,10 +94,10 @@ def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry):
else: else:
item += ")" item += ")"
description = "Imported by TandoorRecipes" description = "From TandoorRecipes"
if shopping_list_entry.created_by.first_name and len(shopping_list_entry.created_by.first_name) > 0: if shopping_list_entry.created_by.first_name and len(shopping_list_entry.created_by.first_name) > 0:
description += f", created by {shopping_list_entry.created_by.first_name}" description += f", by {shopping_list_entry.created_by.first_name}"
else: else:
description += f", created by {shopping_list_entry.created_by.username}" description += f", by {shopping_list_entry.created_by.username}"
return item, description return item, description

View File

@@ -43,7 +43,7 @@ django-auth-ldap==4.6.0
pyppeteer==2.0.0 pyppeteer==2.0.0
validators==0.20.0 validators==0.20.0
pytube==15.0.0 pytube==15.0.0
homeassistant-api==4.1.1.post2 aiohttp==3.9.3
# Development # Development
pytest==8.0.0 pytest==8.0.0