Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for IQVIA."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from collections.abc import Callable, Coroutine
7 from datetime import timedelta
8 from functools import partial
9 from typing import Any
10 
11 from pyiqvia import Client
12 from pyiqvia.errors import IQVIAError
13 
14 from homeassistant.config_entries import ConfigEntry
15 from homeassistant.const import Platform
16 from homeassistant.core import HomeAssistant
17 from homeassistant.exceptions import ConfigEntryNotReady
18 from homeassistant.helpers import aiohttp_client
19 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
20 
21 from .const import (
22  CONF_ZIP_CODE,
23  DOMAIN,
24  LOGGER,
25  TYPE_ALLERGY_FORECAST,
26  TYPE_ALLERGY_INDEX,
27  TYPE_ALLERGY_OUTLOOK,
28  TYPE_ASTHMA_FORECAST,
29  TYPE_ASTHMA_INDEX,
30  TYPE_DISEASE_FORECAST,
31  TYPE_DISEASE_INDEX,
32 )
33 
34 DEFAULT_ATTRIBUTION = "Data provided by IQVIA™"
35 DEFAULT_SCAN_INTERVAL = timedelta(minutes=30)
36 
37 PLATFORMS = [Platform.SENSOR]
38 
39 
40 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
41  """Set up IQVIA as config entry."""
42  if not entry.unique_id:
43  # If the config entry doesn't already have a unique ID, set one:
44  hass.config_entries.async_update_entry(
45  entry, unique_id=entry.data[CONF_ZIP_CODE]
46  )
47 
48  websession = aiohttp_client.async_get_clientsession(hass)
49  client = Client(entry.data[CONF_ZIP_CODE], session=websession)
50 
51  # We disable the client's request retry abilities here to avoid a lengthy (and
52  # blocking) startup:
53  client.disable_request_retries()
54 
55  async def async_get_data_from_api(
56  api_coro: Callable[[], Coroutine[Any, Any, dict[str, Any]]],
57  ) -> dict[str, Any]:
58  """Get data from a particular API coroutine."""
59  try:
60  return await api_coro()
61  except IQVIAError as err:
62  raise UpdateFailed from err
63 
64  coordinators = {}
65  init_data_update_tasks = []
66 
67  for sensor_type, api_coro in (
68  (TYPE_ALLERGY_FORECAST, client.allergens.extended),
69  (TYPE_ALLERGY_INDEX, client.allergens.current),
70  (TYPE_ALLERGY_OUTLOOK, client.allergens.outlook),
71  (TYPE_ASTHMA_FORECAST, client.asthma.extended),
72  (TYPE_ASTHMA_INDEX, client.asthma.current),
73  (TYPE_DISEASE_FORECAST, client.disease.extended),
74  (TYPE_DISEASE_INDEX, client.disease.current),
75  ):
76  coordinator = coordinators[sensor_type] = DataUpdateCoordinator(
77  hass,
78  LOGGER,
79  config_entry=entry,
80  name=f"{entry.data[CONF_ZIP_CODE]} {sensor_type}",
81  update_interval=DEFAULT_SCAN_INTERVAL,
82  update_method=partial(async_get_data_from_api, api_coro),
83  )
84  init_data_update_tasks.append(coordinator.async_refresh())
85 
86  results = await asyncio.gather(*init_data_update_tasks, return_exceptions=True)
87  if all(isinstance(result, Exception) for result in results):
88  # The IQVIA API can be selectively flaky, meaning that any number of the setup
89  # API calls could fail. We only retry integration setup if *all* of the initial
90  # API calls fail:
91  raise ConfigEntryNotReady
92 
93  # Once we've successfully authenticated, we re-enable client request retries:
94  client.enable_request_retries()
95 
96  hass.data.setdefault(DOMAIN, {})
97  hass.data[DOMAIN][entry.entry_id] = coordinators
98 
99  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
100 
101  return True
102 
103 
104 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
105  """Unload an OpenUV config entry."""
106  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
107  if unload_ok:
108  hass.data[DOMAIN].pop(entry.entry_id)
109 
110  return unload_ok
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:104
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:40