Your integration will need to fetch data from an API to be able to provide this to Home Assistant. This API can be available over the web (local or cloud), sockets, serial ports exposed via USB sticks, etc.
Push vs Poll
APIs come in many different shapes and forms but at its core they fall in two categories: push and poll.
With push, we subscribe to an API and we get notified by the API when new data is available. It pushes the changes to us. Push APIs are great because they consume less resources. When a change happens, we can get notified of a change and don't have to re-fetch all the data and find changes. Because entities can be disabled, you should make sure that your entity subscribes inside the
async_added_to_hass callback and unsubscribes inside
With polling, we will fetch the latest data from the API at a specified interval. Your integration will then supply this data to its entity, which is written to Home Assistant.
Because polling is so common, Home Assistant by default assumes that your entity is based on polling. If this is not the case, return
False from the
Entity.should_poll property. When you disable polling, your integration will be responsible for calling one of the methods to indicate to Home Assistant that it's time to write the entity state to Home Assistant:
- If you are executing from within an async function and don't need your entity update method called, call
Entity.async_write_ha_state(). This is an async callback that will write the state to the state machine within yielding to the event loop.
Entity.async_schedule_update_ha_state(force_refresh=False)will schedule an update of the entity. If
force_refreshis set to
True, Home Assistant will call your entities update method (
async_update()) prior to writing the state.
Polling API endpoints
We're going to explain a few different API types here and the best way to integrate them in Home Assistant. Note that some integrations will encounter a combination of the ones below.
Coordinated, single API poll for data for all entities
This API will have a single method to fetch data for all the entities that you have in Home Assistant. In this case we will want to have a single periodical poll on this endpoint, and then let entities know as soon as new data is available for them.
Home Assistant provides a DataUpdateCoordinator class to help you manage this as efficiently as possible.
from datetime import timedeltaimport loggingfrom homeassistant.helpers import entityfrom homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailedfrom .const import DOMAIN_LOGGER = logging.getLogger(__name__)async def async_setup_entry(hass, entry, async_add_entities):# assuming API object stored here by __init__.pyapi = hass.data[DOMAIN][entry.entry_id]async def async_update_data():"""Fetch data from API endpoint.This is the place to pre-process the data to lookup tablesso entities can quickly look up their data."""try:return await api.fetch_data()except ApiError:raise UpdateFailedcoordinator = DataUpdateCoordinator(hass,_LOGGER,# Name of the data. For logging purposes.name="sensor",update_method=async_update_data,# Polling interval. Will only be polled if there are subscribers.update_interval=timedelta(seconds=30),)# Fetch initial data so we have data when entities subscribeawait coordinator.async_refresh()async_add_entities(MyEntity(coordinator, idx) for idx, entin enumerate(coordinator.data))class MyEntity(entity.Entity):def __init__(self, coordinator, idx):self.coordinator = coordinatorself.idx = idx@propertydef is_on(self):"""Return entity state.Example to show how we fetch data from coordinator."""self.coordinator.data[self.idx]['state']@propertydef should_poll(self):"""No need to poll. Coordinator notifies entity of updates."""return False@propertydef available(self):"""Return if entity is available."""return self.coordinator.last_update_successasync def async_added_to_hass(self):"""When entity is added to hass."""self.coordinator.async_add_listener(self.async_write_ha_state)async def async_will_remove_from_hass(self):"""When entity will be removed from hass."""self.coordinator.async_remove_listener(self.async_write_ha_state)async def async_turn_on(self, **kwargs):"""Turn the light on.Example method how to request data updates."""# Do the turning on.# ...# Update the dataawait self.coordinator.async_request_refresh()async def async_update(self):"""Update the entity.Only used by the generic entity update service."""await self.coordinator.async_request_refresh()
Separate polling for each individual entity
Some APIs will offer an endpoint per device. It sometimes won't be possible to map a device from your API to a single entity. If you create multiple entities from a single API device endpoint, please see the preivous section.
If you can map exactly one device endpoint to a single entity, you can fetch the data for this entity inside the
async_update() methods. Make sure polling is set to
True and Home Assistant will call this method regularly.
If your entities need to fetch data before being written to Home Assistant for the first time, pass
True to the
You can control the polling interval for your integration by defining a
SCAN_INTERVAL constant in your platform. Careful with setting this too low. It will take up resources in Home Assistant, can overwelm the device hosting the API or can get you blocked from cloud APIs.
from datetime import timedeltaSCAN_INTERVAL = timedelta(seconds=5)
This is an advanced topic.
Home Assistant has built-in logic to make sure that integrations do not hammer APIs and consume all available resources in Home Assistant. This logic is built around limiting the number of parallel requests. This logic is automatically used during service calls and entity updates.
Home Assistant controls the number of parallel updates (calls to
update()) by maintaining a semaphore per integration. For example, if the semaphore allows 1 parallel connection, updates and service calls will wait if one is in progress. If the value is 0, the integration is itself responsible for limiting the number of parallel requests if necessary.
The default value for parallel requests for a platform is decided based on the first entity that is added to Home Assistant. It's 0 if the entity defines the
async_update method, else it's 1. (this is a legacy decision)
Platforms can override the default by defining the
PARALLEL_UPDATES constant in their platform (ie