Auto-level AirVisual API calls (#34903)

pull/36002/head
Aaron Bach 2020-05-21 02:49:18 -06:00 committed by GitHub
parent 1958d132a9
commit f5a326c51e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 41 additions and 2 deletions

View File

@ -1,6 +1,7 @@
"""The airvisual component.""" """The airvisual component."""
import asyncio import asyncio
from datetime import timedelta from datetime import timedelta
from math import ceil
from pyairvisual import Client from pyairvisual import Client
from pyairvisual.errors import AirVisualError, NodeProError from pyairvisual.errors import AirVisualError, NodeProError
@ -37,7 +38,6 @@ from .const import (
PLATFORMS = ["air_quality", "sensor"] PLATFORMS = ["air_quality", "sensor"]
DEFAULT_ATTRIBUTION = "Data provided by AirVisual" DEFAULT_ATTRIBUTION = "Data provided by AirVisual"
DEFAULT_GEOGRAPHY_SCAN_INTERVAL = timedelta(minutes=10)
DEFAULT_NODE_PRO_SCAN_INTERVAL = timedelta(minutes=1) DEFAULT_NODE_PRO_SCAN_INTERVAL = timedelta(minutes=1)
DEFAULT_OPTIONS = {CONF_SHOW_ON_MAP: True} DEFAULT_OPTIONS = {CONF_SHOW_ON_MAP: True}
@ -88,6 +88,37 @@ def async_get_geography_id(geography_dict):
) )
@callback
def async_get_cloud_api_update_interval(hass, api_key):
"""Get a leveled scan interval for a particular cloud API key.
This will shift based on the number of active consumers, thus keeping the user
under the monthly API limit.
"""
num_consumers = len(
{
config_entry
for config_entry in hass.config_entries.async_entries(DOMAIN)
if config_entry.data.get(CONF_API_KEY) == api_key
}
)
# Assuming 10,000 calls per month and a "smallest possible month" of 28 days; note
# that we give a buffer of 1500 API calls for any drift, restarts, etc.:
minutes_between_api_calls = ceil(1 / (8500 / 28 / 24 / 60 / num_consumers))
return timedelta(minutes=minutes_between_api_calls)
@callback
def async_reset_coordinator_update_intervals(hass, update_interval):
"""Update any existing data coordinators with a new update interval."""
if not hass.data[DOMAIN][DATA_COORDINATOR]:
return
for coordinator in hass.data[DOMAIN][DATA_COORDINATOR].values():
coordinator.update_interval = update_interval
async def async_setup(hass, config): async def async_setup(hass, config):
"""Set up the AirVisual component.""" """Set up the AirVisual component."""
hass.data[DOMAIN] = {DATA_COORDINATOR: {}} hass.data[DOMAIN] = {DATA_COORDINATOR: {}}
@ -163,6 +194,10 @@ async def async_setup_entry(hass, config_entry):
client = Client(api_key=config_entry.data[CONF_API_KEY], session=websession) client = Client(api_key=config_entry.data[CONF_API_KEY], session=websession)
update_interval = async_get_cloud_api_update_interval(
hass, config_entry.data[CONF_API_KEY]
)
async def async_update_data(): async def async_update_data():
"""Get new data from the API.""" """Get new data from the API."""
if CONF_CITY in config_entry.data: if CONF_CITY in config_entry.data:
@ -185,10 +220,14 @@ async def async_setup_entry(hass, config_entry):
hass, hass,
LOGGER, LOGGER,
name="geography data", name="geography data",
update_interval=DEFAULT_GEOGRAPHY_SCAN_INTERVAL, update_interval=update_interval,
update_method=async_update_data, update_method=async_update_data,
) )
# Ensure any other, existing config entries that use this API key are updated
# with the new scan interval:
async_reset_coordinator_update_intervals(hass, update_interval)
# Only geography-based entries have options: # Only geography-based entries have options:
config_entry.add_update_listener(async_update_options) config_entry.add_update_listener(async_update_options)
else: else: