Skip to main content

· One min read

As part of an effort to improve performance and simplify the core job API, async_add_hass_job is deprecated and will be removed from Home Assistant in 2025.5.

Calls should be replaced with async_run_hass_job instead.

· 2 min read

By replacing pip with uv in our production images, our build pipeline (and therefore releasing a new version) is a lot faster. Uv is an extremely fast Python package installer and resolver written in Rust. It is developed by Astral and it's open source. Check it out on GitHub.

In the following table, you can see that we can save around 5 hours of execution time on each build.

ArchPipUVSavings
aarch641h 24m 53s5m 18s~1h 20m
armhf1h 52m 20s6m 2s~1h 46m
armv71h 26m 43s5m 28s~1h 21m
amd6422m 10s3m 20s~19m
i38617m 37s3m 11s~14m

On average, we run the build pipeline 43 times as we create

  • 31 nightlies (one nightly per day)
  • 7 beta releases
  • 5 stable releases (including patch ones)

In total, we save around 215 hours per month. With this massive improvement, we can now ship hotfixes even faster, as the pipeline to ship a new version now takes around 20 minutes instead of 2.5 hours.

The 215 monthly saved execution hours can be used by other jobs and make the CI experience for all developers and our community better. By replacing pip with uv, we improve our sustainability by using fewer resources to build our images.

A big thank you to Astral for developing this amazing tool. Please check out their website and products as they offer, for example, a "lightning" fast linter/formatter for Python too.

· One min read

As of Home Assistant 2024.5, we deprecate the use of hass.helpers. Using hass.helpers will issue a warning in the logs. Authors of custom integrations are encouraged to update their code to prevent any issues before Home Assistant 2024.11.

Starting from Home Assistant 2024.11, hass.helpers will be removed and will no longer work.

Integrations that use hass.helpers should be updated to import the functions and classes directly from the integration package and pass the hass object as first parameter.

New example

from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession

async def async_setup(hass: HomeAssistant, config):
"""Set up the component."""
client = async_get_clientsession(hass)

Old example

from homeassistant.core import HomeAssistant

async def async_setup(hass: HomeAssistant, config):
"""Set up the component."""
client = hass.helpers.aiohttp_client.async_get_clientsession()

· 2 min read

As of Home Assistant Core 2024.4, config entries can now be reconfigured by adding a reconfigure step in their config flows.

This is not to replace the optional configuration (OptionsFlow) but instead to allow the user to change the setup configuration after a config entry has been created.

Reconfiguration vs. Reauthentication

The reconfigure step does not replace a reauth step and they have different purposes.

Reauthentication should be started automatically by the integration in the case of a login/token/etc. is invalidated, so the user has an option to adjust those settings.

Reconfiguration is started by the user from the config entry options menu and should be implemented to update config entry data which are not optional for the integration to work. Authentication issues are handled with a re-authentication flow. (See reauthentication).

Example

Examples could be changing the latitude and longitude of a WeatherEntity when moving between homes or having a mobile home, changing the communication port of a local device, etc.

To implement the reconfigure step, include it in your config flow as:

import voluptuous as vol

class ExampleConfigFlow(ConfigFlow, domain=DOMAIN):
"""Config flow for Example integration."""

async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None):
"""Add reconfigure step to allow to reconfigure a config entry."""
if user_input is not None:
pass # TODO: process user input

return self.async_show_form(
step_id="reconfigure",
data_schema=vol.Schema({vol.Required("password"): str}),
)

· 2 min read

The State object is now always updated and an event is always fired when an integration sets the state of an entity, regardless of any change to the state or a state attribute. This is implemented by adding a new timestamp, State.last_reported and a new event state_reported.

The state_reported event is fired in large volume, and must be used with care to avoid detrimental effects on system load:

  • Event listeners which subscribe to all events are not called when state_reported is fired.
  • It's not allowed to listen to state_reported events for all entities.
  • When listening to state_reported, an event_filter must be used to screen out events for other entities.
  • When listening to state_reported, the run_immediately flag must be set.

Background

Home Assistant previously discarded state writes where neither the state nor the state attributes were changed, unless the integration set the force_update flag. This behavior made it very difficult for integrations to correctly do time series analysis of numerical sensor state. It also meant the user didn't know if an integration is updating a sensor or not.

The new timestamp and associated event were discussed in architecure discussion #1062.

Custom integration impact

The following APIs have breaking changes:

  • The time_fired argument for hass.bus.async_fire now accepts a float timestamp instead of a datetime object.
  • The signature of event filters which can be passed to hass.bus.async_listen has changed.
  • The time_fired: datetime | None argument for Event() has been replaced with time_fired_timestamp: float | None.
  • A new argument last_reported: datetime has been added to State() in the middle of the argument list.

For more details, see core PR #113511 and core PR #113798.

· One min read

Logging exceptions with translation support

Translation support applies to HomeAssistantError and subclasses like ServiceValidationError. When a translation_domain and translation_key is set and the error message is added in strings.json, it is no longer needed to add the error message string for the local logging. Home Assistant will automatically fetch the English error message from the translation cache.

When raising HomeAssistantError or a subclass with translation support we should remove the log message from the argument list to enable it to be fetched from the translation cache.

For example:

async def async_select_index(hass: HomeAssistant, index: int) -> None:
"""Setup the config entry for my device."""
try:
check_index(index)
except ValueError as exc:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_index",
translation_placeholders={
"index": index,
"expected": expected,
},
) from exc

The error message is placed in strings.json:

{
...
"exceptions": {
"invalid_index": {
"message": "An invalid index is selected, expected: {expected}, got: {index}"
}
}
}

· One min read

async_run_job and async_add_job are deprecated and will be removed in Home Assistant 2025.4. This deprecation does not apply to the sync API add_job method, which is not planned to be removed.

Instead, it's more efficient to use one of the other job methods, as the method of calling the job does not ever have to be worked out:

If the callable is a coroutine function running from a config entry: entry.async_create_background_task, entry.async_create_task

If the callable is a coroutine function running from another place: hass.async_create_background_task, hass.async_create_task

If the callable should run in the executor: hass.async_add_executor_job

· One min read

As of Home Assistant Core 2024.4 we have deprecated the auxiliary heater functionality in ClimateEntity.

Integrations that are currently implementing the is_aux_heat property and the turn_aux_heat_on/turn_aux_heat_off methods need to remove these and alternatively implement other entities to accommodate the necessary functionality such as a SwitchEntity or in the case of a read-only property a BinarySensorEntity.

You can read more about this decision here.

· One min read

Starting from Home Assistant 2024.4, all integrations are imported in the executor to avoid blocking the event loop while the code is imported and executed.

In the rare event that an integration creates loop-bound objects at import time, this may fail because there is no running loop in the executor.

Custom integrations needing more time to adjust their code to avoid creating such objects can opt-out and continue to have their code imported in the event loop, understanding that system stability will be impacted.

To opt-out of importing in the executor, an integration can add:

"import_executor": false to their manifest.json

· One min read

The method for integrations to provide translations for states of its entities under other integrations, for example to translate an integration's sensors was changed in November 2022, and support for the old method will be removed in Home Assistant Core 2024.5.0.

Once Home Assistant Core 2024.5.0 is released, integrations can no longer use custom device classes together with a strings.<platform name>.json file. Instead, entities must set the translation_key property on an entity and include that translation_key in the integration's strings.json.

The following example strings.json is for a Moon domain sensor entity with its translation_key property set to phase:

{
"entity": {
"sensor": {
"phase": {
"state": {
"new_moon": "New moon",
"first_quarter": "First quarter",
"full_moon": "Full moon",
"last_quarter": "Last quarter"
}
}
}
}
}

For more details, see the translation and entity documentation.