Skip to main content

Calendar best practices

· One min read

Home Assistant has improved best practices for Calendar entities to ensure that calendar triggers/automations and the UI work correctly in all cases.

In particular, there are now more documented expectations and enforcement of invariants including:

  • A CalendarEvent property end is exclusive. e.g. An all day event that lasts one day should have an end date with 1 day after the start date.
  • A CalendarEvent can accept a datetime in any timezone. Floating dates without a timezone are not allowed.
  • The CalendarEvent invariants are now enforced when created.
  • Events returned by async_get_events are expected to be returned in order.
  • All Day events returned by async_get_events must be evaluated in the Home Assistant local timezone. That is, an all day event should be ordered as if it starts at midnight in the local time.

The Calendar Entity developer documentation has been updated with additional detail.

Translating the name and attributes of entities

· 2 min read

It's now possible to translate the name of entities, and this is preferred over hard coding a name in natural language in the Python implementation. Also, entity components provide shared translations, for example, for binary sensor device classes, which should be used to avoid translating the same thing multiple times.

Also, the frontend now has full support for translated entity state attributes for both the names and their values.

warning

Pointing to translations via the translation_key property is currently only supported for entities with a unique_id.

Additionally, translating entity names requires that the has_entity_name property is set to True.

Translating entity name

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

{
"entity": {
"sensor": {
"thermostat_mode": {
"name": "Thermostat mode"
}
}
}
}

The following example strings.json is for a sensor entity with its translation_key property set to temperature_sensor where a shared translation provided by the sensor integration is used:

{
"entity": {
"sensor": {
"temperature_sensor": {
"name": "[%key:component::sensor::entity_component::temperature::name%]"
}
}
}
}

Translating entity attributes

The following example strings.json is for a demo domain climate entity with its translation_key property set to ubercool, which has custom fan_mode and swing_mode settings:

{
"entity": {
"climate": {
"ubercool": {
"state_attributes": {
"fan_mode": {
"state": {
"auto_high": "Auto High",
"auto_low": "Auto Low",
"on_high": "On High",
"on_low": "On Low"
}
},
"swing_mode": {
"state": {
"1": "1",
"2": "2",
"3": "3",
"auto": "Auto",
"off": "Off"
}
}
}
}
}
}
}

For more details, see the entity name translation entity attribute translation and entity documentation.

Custom tile features

· One min read

In the Home Assistant Core 2022.3 release, we add custom features for the tile card. If you are a developer of custom cards, you can now build your own features to the tile card instead of building a whole card.

Screenshot showing example of custom tile feature

type: tile
entity: button.push
features:
- type: custom:button-press-tile-feature

Custom tile features can even be added to the tile card editor like any other built-in tile feature using similar syntax as custom cards.

window.customTileFeatures = window.customTileFeatures || [];
window.customTileFeatures.push({
type: "button-press-tile-feature",
name: "Button press",
});

For more details, see the custom tile features documentation.

Deprecated callback signatures for MQTT subscribe removed

· One min read

Home Assistant's MQTT integration no longer supports deprecated callback signatures for MQTT subscribe.

Custome integrations that still used the deprecated callback signature for the callback function on MQTT subscribe will break unless updated. An exception will be raised if a not supported callback type is detected.

Examples of deprecated callback functions that will no longer work:

async def async_deprecated_callback1(topic: str, payload: ReceivePayloadType, qos: int) -> None:
"""Deprecated async callback example 1."""
...


@callback
def async_deprecated_callback2(topic: str, payload: ReceivePayloadType, qos: int) -> None:
"""Deprecated async callback example 2."""
...

Example of a correct callback signature:

@callback
def async_correct_callback(msg: ReceiveMessage) -> None:
"""Callback example 1."""
...

Added support for snapshot testing

· 2 min read

Home Assistant now supports snapshot testing for our Python codebase.

Snapshot testing (also known as approval tests) are tests that assert values against a stored reference value (the snapshot); ensuring the output of code remains the same over time.

Snapshot tests are different from regular (functional) tests and do not replace functional tests, but they can be very useful for testing larger test outputs. Within Home Assistant they could, for example, be used to test entity states, device or entity registry items, or the output of a diagnostic dump.

Take, for example, this diagnostic test, which uses a snapshot to assert the output of a diagnostic dump:

# tests/components/example/test_diagnostics.py
async def test_diagnostics(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
init_integration: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Test diagnostics."""
assert (
await get_diagnostics_for_config_entry(hass, hass_client, init_integration)
== snapshot
)

When this test is run for the first time, it will fail, as no snapshot exists. To create (or update) a snapshot, run pytest with the --snapshot-update flag, which will create a snapshot file in the snapshots directory of this component.

The snapshot file is named after the test file, in this case: tests/components/example/snapshots/test_diagnostics.ambr. The snapshot files are human-readable and must be committed to the repository.

Any sequential runs of the tests will then compare the results against the snapshot. If the results are different, the test will fail.

Snapshots are an easy way to ensure the output of code remains the same over time and can greatly reduce the amount of testing code needed (while providing) a full assert against a complete output.

Snapshot testing in Home Assistant is build on top of Syrupy, which has been extended to handle Home Assistant-specific data structures.

More information on testing integrations, can be found in our documentation.

Refactor json helper and utility

· One min read

As of Home Assistant Core 2023.3, some constants and functions have been moved between homeassistant.helpers.json and homeassistant.util.json :

  • save_json and find_paths_unserializable_data functions should now be imported from homeassistant.helpers.json
  • json_loads function should now be imported from homeassistant.util.json
  • JSON_ENCODE_EXCEPTIONS and JSON_DECODE_EXCEPTIONS constants should now be imported from homeassistant.util.json

The HassGetState intent

· One min read

We've added a new built-in intent: HassGetState

This intent will enable users to ask questions to Assist once we've added translations to the intents repository. You can try it out now by adding custom sentences:

# Example <config>/custom_sentences/en/get_state.yaml

language: en
intents:
HassGetState:
data:
- sentences:
- what is <name> [in <area>]
- is <name> {state} [in <area>]

responses:
intents:
HassGetState:
default: "{{ slots.name }} is {{ state.state_with_unit }}"

lists:
state:
values:
- "on"
- "off"
- open
- closed
- locked
- unlocked
- wet
- dry

With these sentences, you can now ask Assist things like "what is the outside temperature?", "is the front door locked?", or "is the floor in the basement wet?" This relies on having entity names (or aliases) set up just right, of course. For example, a sensor named "outside temperature" and a binary moisture sensor named "floor" in the "basement" area.

As we add translations, more types of questions will be possible such as "which lights are in the living room?" and "are any doors unlocked?"

The number of decimals used when displaying a sensor state is now configurable

· One min read

The number of decimal digits shown when displaying a sensor state is now configurable by the user. Integrations can suggest the number of decimal digits by setting the property suggested_display_precision. Integrations are encouraged to remove rounding for display and instead set property suggested_display_precision.

Round for presentation is done by the frontend, as well as by new template functions introduced in core PR #87619.

The number of displayed decimal digits is influenced by unit conversion:

  • Converting from a smaller to a larger unit increases the display precision
  • Converting from a larger to a smaller unit decreases the display precision if the integration has set suggested_display_precision
  • Minimum precision when converting from a larger to a smaller unit is 0, i.e. there's no rounding to tens, hundreds etc.

The number of displayed decimal digits is not influenced by unit conversion if the user has set the display precision themselves.

Note: A similar concept where the sensor's state was rounded, detailed in an earlier blog post, has been reverted.

Introducing drafting of PRs in our review process

· 3 min read

Home Assistant gets lots of contributions, which is absolutely amazing! But when having lots of PRs, it becomes harder to keep track of the state of those.

To help with this, we are introducing a new process to our review process that automatically drafts PRs when they require more work before they can be reviewed again (or merged).

We have adjusted our bot to automatically draft PRs if a review has requested changes to be made. Once the changes have been made, the author of the PR can click the "Ready for review" button to un-draft the PR and make it ready for review again.

The ready for review button in the bottom of a PR in draft mode

Before you click the "Ready for review" button, make sure you have addressed all requested changes, and all our CI jobs and checks are passing successfully.

What is a draft PR?

A draft PR is a PR that is not ready for review yet. It is a way to let others know that you are working on something, but it is not ready for review and merging yet.

Draft PRs are recognizable by the "Draft" label in the top right of the PR and show up with a grey merge icon eveywhere in the GitHub UI.

This is what a PR in draft looks like

This doesn't mean you should open a PR to start working on something; please only open a PR if you think it is ready for review and merging. However, after opening a PR, there may be a reason to put it back into draft state.

For example, opening a PR will automatically trigger our CI jobs and checks. These checks can reveal issues in your code that need adjustments, or of course, an actual review took place that requested changes.

You can put any of your PRs back into draft at any moment, by clicking the "Convert to draft" link in the top right of your PR.

Putting a PR in draft is something you can do too

Why are we doing this?

As a reviewer, you are presented with a lot of PRs. Some of them are ready for review, and some of them are not. Typically, the only way to find out, is to open the PR and look at it, to discover it is still in progress.

This is not only a waste of time but also a waste of energy. Especially considering this happens to multiple reviewers on the same PR multiple times a day.

The draft state of a PR is visible in all places in GitHub. In notifications, searches, and just the overview of PRs. Above all, it is easily filterable.

This gives reviewers a better focus on what can actually use their attention right now.

More background information can be found in this Google Document.

Or, read all about our review process on this page.

Translation files removed from Core repository

· 2 min read

We have removed all translation files from the Home Assistant Core repository and put in place a helper script to compile English translations from the translation strings files (strings.json) for development purposes.

Previously, all translation files (including all languages) were part of the Home Assistant Core repository. Every night we would update the translations by downloading them from Lokalise and committing them into the Core repository.

Instead, we have moved this process @ build time. We now download the latest translations from Lokalise when we ship a new release (including betas & nightly builds).

This approach comes with some benefits:

  • We no longer have to commit translation files into the Core repository. This means as a developer, this is also no longer a confusing burden.
  • People will no longer (incorrectly) try to contribute language translations via GitHub.
  • Each release, including patch, beta, and nightly versions, will now also have the latest translations.

Local development

For local development, we have our translations development helper script. This always has been in place already, except now, it can compile the English translations for all integrations at once.

python3 -m script.translations develop --all

This script is automatically run when a dev environment is set up, and each time you run running Home Assistant in your VSCode (as a pre-launch task).