I’m trying to write my first custom integration to control my home thermostats.
I’m looking for some guidance (any similar examples would be great) on how I’m supposed to structure my integration to model my scenario
My house has multiple thermostats.
Each thermostat has a mode it can be in (e.g. Day, Night, Vacation), and multiple values representing its current state (e.g. Current Temp, Target Temp, Is heating currently on or off)
I have an API which I can call, and it returns all of the above data for all thermostats in one go.
Changing the mode for 1 or more thermostats can be done with a single (or multiple) calls to the API.
How should I model this? My understanding so far is that I should have a single component (platform?). This then calls the API on installation to discover the devices, where it adds a device for each thermostat. Each thermostat/device should implement a ClimateEntity which exposes the various properties and services.
(Climate)Entity can implement async def async_update(self):, which implies each entity is called separately to update it’s value. I dont want to hit the API loads of times when I can just call it once - is this supported in some implicit way or do I have to wrangle this myself?
I’ve read those but as a complete HA beginner its quite difficult to comprehend.
Assuming I do understand what a Platform, Integration, Component, Device and Entity are, my question is still one of general best practice and an implementation specific detail. It would be great if I could get some insight on them.
@koying Two questions about that Fetching Data article
in the example, how does MyEntity's state/data get updated? if async_turn_on is invoked, then it signalls the coordinator to update iteself, and then subsequent calls to is_on have new data to work from. Fine. However this example seems a bit naive in that it seems to assume the LightEntity is the only thing able to modify its own state. If the Lights are only able to update themselves, why would you need the coordinator? Surely the point in the coordinator is to manage multiple entities state where they can change outside of their own awareness? And therefore fetching new data may bring changes for entities other than yourself…
In my case, the state (the temperature in the room) can change remotely. So the coordinator needs to poll the API for the new data. How is this then fed back into my CoordinatorEntity?
I’ve got the following code which works great for initial setup, but is currently missing handling updates
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
apiData = await loadData() # make API request, returning thermostat data
for thermostat in apiData.thermostats:
t = MyTermostatEntity(thermostat.id, thermostat.name, thermostat.other_data)
async_add_entities([t])
class MyThermostatEntity(ClimateEntity):
def __init__(self, id, name, other_data):
self._id = id
self._attr_unique_id = "my-thermostat-" + id
self._name = name
self._other_data = other_data
In changing this to make MyThermostatEntity extend CoordinatorEntity I’m stuck with (1) from above. Do i just override def update(self) on MyThermostat and read from coordinator.data? Surely not because the coordinator hasn’t been updated? Or has it? does HA call CoordinatorEntity.coordinator.async_request_refresh() or something? What am I missing?
My configuration.yaml looks like this:
my_thermostats:
apiUser: foo
apiPass: bar
I expect each Thermostat/entity to be discovered from the API, I have no need to do anything with individual thermostats in the configuration.
The concept behind DataCoordinator is that it (and only it) manage the data communication between HA and the devices/APIs. It is still polling, though, and every update_interval, it does this polling from the devices/APIs.
The coordinator centralizes the received data, and the entities read from the coordinator (rather than going directly to the API) to get their data.
So the entities’s update() is noop, and the properties are read from the coordinator, as in
@property
def is_on(self):
"""Return entity state.
Example to show how we fetch data from coordinator.
"""
self.coordinator.data[self.idx]["state"]
You can still force an out-of-band update with
# Update the data
await self.coordinator.async_request_refresh()
but it should be exceptional, as in the example refreshing the data immediately after doing an action.