Help creating a custom Thermostat integration

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?

Any guidance on getting started much appreciated

Thanks

Be sure to understand:

Then go on to

Thanks,

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.

Thanks

At first you have to create python library implementing your api usage. What device do you have? probably it already exists?

Specifically for this, see

So use the helpers from homeassistant.helpers.update_coordinator

Thanks! Had somehow missed that :confused:

@koying Two questions about that Fetching Data article

  1. 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?

  2. 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.

Am I going about this wrong?

Thanks

No and Yes

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.

OK thanks

so after the coordinator has polled and fetched new data into itself, how does it get into the entities?

Do I have to keep a track of my entities myself and push the new data onto them, perhaps via coordinator.async_add_listener?

Doesn’t matter if so, just trying to understand if theres some implicit functionality here or if i need to roll it myself

Thanks

Yes and No :wink:

Entities are created as usual, and the data is fetched from the coordinator via self.coordinator.data, as in the example