HAAnim
Write Home Assistant automations in Python. An automation is a folder with a main.py; decorators say when
its functions run, and the haa object reaches entities, services, storage and the automation's own
dashboard card.
from haanim import StateEvent, TimeEvent, haa, on_state, on_time
@on_state("sensor.temperature > 30")
async def high_temperature(event: StateEvent):
await haa.service.notify.mobile_app(message=f"It is {float(haa.entity.sensor.temperature)}°C")
@on_time("09:00", day_of_week="weekdays", when="person.john == 'home'")
async def morning(event: TimeEvent):
await haa.service.light.turn_on(entity_id="light.bedroom", brightness=150)
What you get
- Triggers: time (including sunrise and sunset), interval, cron, Home Assistant events and state expressions, each with optional constraints (time of day, date range, day of week, state).
- Actions that can be run by hand, by a service, by another automation or by a trigger, with
DROP,QUEUEandCANCELexecution modes, timeouts and a concurrency limit. haa: entity access, service calls,sleepandwait_for, persistent variables, assets, calling and controlling other automations.- A sensor per automation (
sensor.haanim_<id>:on,off,error) with status attributes. - A dashboard card (
custom:haanim-card) that the automation titles and fills with text, images, values, live entities and buttons, plus a management panel in the sidebar with each automation's controls, actions and log. - Hot reload: edit a file and the automation is reloaded.
- A test harness: test an automation with
pytest, with no Home Assistant running.
The interpreter keeps automations from blocking Home Assistant by accident (no blocking I/O, an import allowlist, loops that yield). It is a guard rail, not a sandbox: an automation can do whatever Home Assistant can. Only install automations you trust.
Installation
HAAnim needs Home Assistant 2026.9 or newer.
- Install the integration with HACS: HACS → ⋮ → Custom repositories, add
https://github.com/valsr/haanimas an Integration, then download HAAnim. - Restart Home Assistant.
- Settings → Devices & services → Add integration → HAAnim.
That is all: a release of the integration has the HAAnim engine in it, so no Python package has to be installed into Home Assistant.
To install by hand, download haanim.zip from a
release and unpack it into
<config>/custom_components/haanim. Copying custom_components/haanim out of a checkout of the repository is
not enough, because the engine is not in that folder there; build the folder to copy with
python scripts/build-integration.py --folder OUT.
Automations live in /config/haanim/automations/ by default. The folder, the rescan interval, the limits and
the import options are set under Configure on the integration.
Your first automation
Create /config/haanim/automations/hello/main.py:
from haanim import ActionEvent, action, haa, startup
@startup
def ready(event: ActionEvent):
haa.card.add_element(haa.card.create_text("hello", "## Hello\nPress the button."))
haa.card.add_element(haa.card.create_button("greet", label="Greet", action="greet"))
@action
async def greet(event: ActionEvent):
await haa.service.persistent_notification.create(message="Hello from HAAnim")
Within the rescan interval the automation is running: sensor.haanim_hello is on, and the action can be run
from the HAAnim panel, with the haanim.run_action service, or from the automation's card:
type: custom:haanim-card
automation_id: hello
The automation guide covers everything an automation can do, and
examples/ has complete automations with tests, among them a demo for each part of
HAAnim. The same documentation is built for Read the Docs from docs/ (mkdocs.yml).
Testing an automation
The engine is also a Python package, haanim, for your own machine: it gives your editor completion and
types for from haanim import ..., and the test harness. It is not needed in Home Assistant.
pip install haanim pytest pytest-asyncio
The package needs Python 3.14, the Python that Home Assistant 2026.9 runs on.
from haanim.testing import AutomationHarness
async def test_greeting():
async with AutomationHarness("automations/hello") as automation:
await automation.press("greet")
assert automation.service_calls("persistent_notification.create")[0].data == {
"message": "Hello from HAAnim"
}
The harness runs the automation with the same interpreter and triggers as Home Assistant, against a fake Home Assistant whose clock only moves when the test moves it. Installing the package does not install Home Assistant.
Services
| Service | Data | What it does |
|---|---|---|
haanim.run_action |
automation_id, action, data |
Runs an action; returns its result as response data |
haanim.enable |
automation_id |
Enables and starts the automation |
haanim.disable |
automation_id |
Stops and disables the automation |
haanim.start |
automation_id |
Starts the automation |
haanim.stop |
automation_id |
Stops the automation |
haanim.restart |
automation_id |
Restarts the automation |
haanim.reload |
automation_id (optional) |
Rescans now and reloads one automation, or all |
haanim.list_automations |
- | Returns ID, name, state and enabled flag of every one |
haanim.list_actions |
automation_id |
Returns the automation's actions |
haanim.clear_log |
automation_id (optional) |
Empties the log HAAnim keeps for one automation, or all |
haanim.set_log_level |
automation_id, level |
Sets the automation's log level; default takes it away |
Development
git clone https://github.com/valsr/haanim.git
cd haanim
uv sync --all-extras
uv run pytest # Python tests, frontend tests and the coverage gate
uv run python scripts/check-public-api-coverage.py # the automation-facing API must be at 100%
scripts/test-frontend.sh # only the JavaScript tests (needs node 22+)
uv run black --check . && uv run pylint custom_components src
uv run mypy custom_components src && uv run pyright src custom_components examples
A Home Assistant with the integration, the package and the examples, in a container:
./build-and-run.sh # http://localhost:8123, user admin, password admin
uv run python scripts/e2e-smoke.py # end-to-end checks against that container
The engine has one source, src/haanim. In a checkout the integration imports it as the installed haanim
package (uv sync installs it in place). A release carries a copy of it inside the integration:
uv run python scripts/build-integration.py # dist/haanim.zip: what a GitHub release attaches and HACS installs
The repository:
| Path | What is there |
|---|---|
src/haanim/ |
The engine: interpreter, triggers, dispatcher, haa. No Home Assistant imports |
src/haanim/testing/ |
The test harness and the fakes it is built on |
custom_components/haanim/ |
The integration: entity, services, options, websocket commands, frontend |
examples/ |
Example automations and their harness tests |
tests/ |
engine/ (no Home Assistant), integration/, frontend/ |
docs/ |
The documentation (built with MkDocs) and notes on the development environment |
See CONTRIBUTING.md before sending a pull request.
License
MIT, see LICENSE. HAAnim is a custom integration and is not supported by the Home Assistant project.
Metadata
Release files for haanim 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| haanim-0.2.0.tar.gz | 138.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| haanim-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 307.2 kB
Release files / haanim-0.2.0.tar.gz
| Download URL | haanim-0.2.0.tar.gz |
|---|---|
| Size | 138.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e7891f36f99b3341a63b84979d8efc9c6ba9df7ff6c50a117de18daec07dcd7c
|
|
BLAKE2b-256 checksum How to use checksums |
325b32285b8a1ca329af459b16de980eb2e97c974bacadde88f23305b19157be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency logRelease files / haanim-0.2.0-py3-none-any.whl
| Download URL | haanim-0.2.0-py3-none-any.whl |
|---|---|
| Size | 169.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b8db1c8034aef6de7bba3f8bfbddba1ac93a22dc06a1431fd33454c96ae94378
|
|
BLAKE2b-256 checksum How to use checksums |
865503874b43f43c3823b7e0dfce2d4d03191da3b526a8734fc2788159ce309d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency log