haanim 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. haanim-0.2.0/.gitignore +113 -0
  2. haanim-0.2.0/LICENSE +21 -0
  3. haanim-0.2.0/PKG-INFO +220 -0
  4. haanim-0.2.0/README.md +185 -0
  5. haanim-0.2.0/pyproject.toml +199 -0
  6. haanim-0.2.0/src/haanim/__init__.py +166 -0
  7. haanim-0.2.0/src/haanim/const.py +141 -0
  8. haanim-0.2.0/src/haanim/engine/__init__.py +34 -0
  9. haanim-0.2.0/src/haanim/engine/action_dispatcher.py +590 -0
  10. haanim-0.2.0/src/haanim/engine/action_pool.py +319 -0
  11. haanim-0.2.0/src/haanim/engine/assets.py +143 -0
  12. haanim-0.2.0/src/haanim/engine/ast_evaluator.py +977 -0
  13. haanim-0.2.0/src/haanim/engine/automation_context.py +671 -0
  14. haanim-0.2.0/src/haanim/engine/automation_ids.py +91 -0
  15. haanim-0.2.0/src/haanim/engine/automation_module.py +130 -0
  16. haanim-0.2.0/src/haanim/engine/automation_status.py +185 -0
  17. haanim-0.2.0/src/haanim/engine/callables.py +112 -0
  18. haanim-0.2.0/src/haanim/engine/card.py +551 -0
  19. haanim-0.2.0/src/haanim/engine/card_checks.py +264 -0
  20. haanim-0.2.0/src/haanim/engine/card_elements.py +748 -0
  21. haanim-0.2.0/src/haanim/engine/card_layout.py +276 -0
  22. haanim-0.2.0/src/haanim/engine/constraints/__init__.py +5 -0
  23. haanim-0.2.0/src/haanim/engine/constraints/rules.py +235 -0
  24. haanim-0.2.0/src/haanim/engine/control.py +222 -0
  25. haanim-0.2.0/src/haanim/engine/cron_schedule.py +98 -0
  26. haanim-0.2.0/src/haanim/engine/decorators.py +584 -0
  27. haanim-0.2.0/src/haanim/engine/discovery.py +196 -0
  28. haanim-0.2.0/src/haanim/engine/durations.py +62 -0
  29. haanim-0.2.0/src/haanim/engine/errors.py +347 -0
  30. haanim-0.2.0/src/haanim/engine/eval_function.py +344 -0
  31. haanim-0.2.0/src/haanim/engine/expression_eval.py +545 -0
  32. haanim-0.2.0/src/haanim/engine/guards.py +115 -0
  33. haanim-0.2.0/src/haanim/engine/haanim_api.py +674 -0
  34. haanim-0.2.0/src/haanim/engine/haanim_module.py +153 -0
  35. haanim-0.2.0/src/haanim/engine/hot_reload.py +166 -0
  36. haanim-0.2.0/src/haanim/engine/import_controller.py +90 -0
  37. haanim-0.2.0/src/haanim/engine/lifecycle.py +601 -0
  38. haanim-0.2.0/src/haanim/engine/logging_wrapper.py +83 -0
  39. haanim-0.2.0/src/haanim/engine/metadata.py +126 -0
  40. haanim-0.2.0/src/haanim/engine/operators.py +48 -0
  41. haanim-0.2.0/src/haanim/engine/safe_builtins.py +48 -0
  42. haanim-0.2.0/src/haanim/engine/symbol_table.py +199 -0
  43. haanim-0.2.0/src/haanim/engine/time_expr.py +343 -0
  44. haanim-0.2.0/src/haanim/engine/time_schedule.py +190 -0
  45. haanim-0.2.0/src/haanim/engine/triggers/__init__.py +24 -0
  46. haanim-0.2.0/src/haanim/engine/triggers/base.py +122 -0
  47. haanim-0.2.0/src/haanim/engine/triggers/cron_trigger.py +57 -0
  48. haanim-0.2.0/src/haanim/engine/triggers/event_trigger.py +111 -0
  49. haanim-0.2.0/src/haanim/engine/triggers/interval_trigger.py +115 -0
  50. haanim-0.2.0/src/haanim/engine/triggers/manager.py +156 -0
  51. haanim-0.2.0/src/haanim/engine/triggers/scheduled.py +93 -0
  52. haanim-0.2.0/src/haanim/engine/triggers/state_trigger.py +173 -0
  53. haanim-0.2.0/src/haanim/engine/triggers/time_trigger.py +72 -0
  54. haanim-0.2.0/src/haanim/engine/validation.py +304 -0
  55. haanim-0.2.0/src/haanim/engine/variables.py +147 -0
  56. haanim-0.2.0/src/haanim/engine/waiting.py +82 -0
  57. haanim-0.2.0/src/haanim/entity.py +227 -0
  58. haanim-0.2.0/src/haanim/events.py +136 -0
  59. haanim-0.2.0/src/haanim/interfaces.py +447 -0
  60. haanim-0.2.0/src/haanim/py.typed +0 -0
  61. haanim-0.2.0/src/haanim/testing/__init__.py +24 -0
  62. haanim-0.2.0/src/haanim/testing/fakes.py +953 -0
  63. haanim-0.2.0/src/haanim/testing/harness.py +819 -0
  64. haanim-0.2.0/src/haanim/types.py +293 -0
@@ -0,0 +1,113 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ *.manifest
32
+ *.spec
33
+
34
+ # Unit test / coverage reports
35
+ htmlcov/
36
+ .tox/
37
+ .nox/
38
+ .coverage
39
+ .coverage.*
40
+ .cache
41
+ nosetests.xml
42
+ coverage.xml
43
+ *.cover
44
+ *.py,cover
45
+ .hypothesis/
46
+ .pytest_cache/
47
+
48
+
49
+ # pipenv
50
+ Pipfile.lock
51
+
52
+ # UV
53
+ .uv/
54
+ uv.lock
55
+
56
+ # PEP 582
57
+ __pypackages__/
58
+
59
+ # Environments
60
+ .env
61
+ .venv
62
+ env/
63
+ venv/
64
+ ENV/
65
+ env.bak/
66
+ venv.bak/
67
+
68
+ # mkdocs documentation
69
+ /site
70
+
71
+ # mypy
72
+ .mypy_cache/
73
+ .dmypy.json
74
+ dmypy.json
75
+
76
+ # Pyre type checker
77
+ .pyre/
78
+
79
+ # IDE
80
+ .idea/
81
+ *.swp
82
+ *.swo
83
+ *~
84
+ /out
85
+
86
+ # OS
87
+ .DS_Store
88
+ Thumbs.db
89
+
90
+ # Home Assistant
91
+ home-assistant.log
92
+ home-assistant_v2.db
93
+ *.db-shm
94
+ *.db-wal
95
+
96
+ # Development environment
97
+ dev/config/.storage/
98
+ dev/config/home-assistant.log
99
+ dev/config/home-assistant.log.*
100
+ dev/config/home-assistant_v2.db
101
+ dev/config/*.db-shm
102
+ dev/config/*.db-wal
103
+ dev/config/.cloud/
104
+ dev/config/.HA_VERSION
105
+ dev/config/deps/
106
+ dev/config/tts/
107
+ dev/config/blueprints/
108
+
109
+ # Misc
110
+ _*.md
111
+
112
+ # A copy of the engine inside the integration is only made for a release (scripts/build-integration.py)
113
+ custom_components/haanim/bundled/
haanim-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 HAAnim Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
haanim-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.5
2
+ Name: haanim
3
+ Version: 0.2.0
4
+ Summary: Python automations for Home Assistant: the HAAnim engine and its test harness
5
+ Project-URL: Homepage, https://github.com/valsr/haanim
6
+ Project-URL: Documentation, https://haanim.readthedocs.io/
7
+ Project-URL: Repository, https://github.com/valsr/haanim
8
+ Project-URL: Issues, https://github.com/valsr/haanim/issues
9
+ Project-URL: Changelog, https://github.com/valsr/haanim/blob/main/CHANGELOG.md
10
+ Author: valsr
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: automation,hacs,home-assistant,homeassistant
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Home Automation
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.14.2
21
+ Requires-Dist: cronsim>=2.6
22
+ Requires-Dist: python-slugify>=8.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: black>=24.0.0; extra == 'dev'
25
+ Requires-Dist: flake8>=7.0.0; extra == 'dev'
26
+ Requires-Dist: homeassistant<2026.10,>=2026.9.0; extra == 'dev'
27
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
28
+ Requires-Dist: pylint>=3.0.0; extra == 'dev'
29
+ Requires-Dist: pyright>=1.1.380; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
31
+ Requires-Dist: pytest-homeassistant-custom-component>=0.13.0; extra == 'dev'
32
+ Requires-Dist: pytest-xdist>=3.5.0; extra == 'dev'
33
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # HAAnim
37
+
38
+ [![CI](https://github.com/valsr/haanim/actions/workflows/ci.yml/badge.svg)](https://github.com/valsr/haanim/actions/workflows/ci.yml)
39
+ [![hacs_badge](https://img.shields.io/badge/HACS-Custom-orange.svg)](https://github.com/custom-components/hacs)
40
+ [![PyPI](https://img.shields.io/pypi/v/haanim.svg)](https://pypi.org/project/haanim/)
41
+ [![Documentation](https://readthedocs.org/projects/haanim/badge/?version=latest)](https://haanim.readthedocs.io/)
42
+ [![License](https://img.shields.io/github/license/valsr/haanim.svg)](https://github.com/valsr/haanim/blob/main/LICENSE)
43
+
44
+ Write Home Assistant automations in Python. An automation is a folder with a `main.py`; decorators say when
45
+ its functions run, and the `haa` object reaches entities, services, storage and the automation's own
46
+ dashboard card.
47
+
48
+ ```python
49
+ from haanim import StateEvent, TimeEvent, haa, on_state, on_time
50
+
51
+
52
+ @on_state("sensor.temperature > 30")
53
+ async def high_temperature(event: StateEvent):
54
+ await haa.service.notify.mobile_app(message=f"It is {float(haa.entity.sensor.temperature)}°C")
55
+
56
+
57
+ @on_time("09:00", day_of_week="weekdays", when="person.john == 'home'")
58
+ async def morning(event: TimeEvent):
59
+ await haa.service.light.turn_on(entity_id="light.bedroom", brightness=150)
60
+ ```
61
+
62
+ ## What you get
63
+
64
+ - **Triggers**: time (including sunrise and sunset), interval, cron, Home Assistant events and state
65
+ expressions, each with optional constraints (time of day, date range, day of week, state).
66
+ - **Actions** that can be run by hand, by a service, by another automation or by a trigger, with `DROP`,
67
+ `QUEUE` and `CANCEL` execution modes, timeouts and a concurrency limit.
68
+ - **`haa`**: entity access, service calls, `sleep` and `wait_for`, persistent variables, assets, calling and
69
+ controlling other automations.
70
+ - **A sensor per automation** (`sensor.haanim_<id>`: `on`, `off`, `error`) with status attributes.
71
+ - **A dashboard card** (`custom:haanim-card`) that the automation titles and fills with text, images,
72
+ values, live entities and buttons, plus a management panel in the sidebar with each automation's
73
+ controls, actions and log.
74
+ - **Hot reload**: edit a file and the automation is reloaded.
75
+ - **A test harness**: test an automation with `pytest`, with no Home Assistant running.
76
+
77
+ The interpreter keeps automations from blocking Home Assistant by accident (no blocking I/O, an import
78
+ allowlist, loops that yield). It is a guard rail, not a sandbox: an automation can do whatever Home Assistant
79
+ can. Only install automations you trust.
80
+
81
+ ## Installation
82
+
83
+ HAAnim needs Home Assistant 2026.9 or newer.
84
+
85
+ 1. Install the integration with HACS: **HACS → ⋮ → Custom repositories**, add
86
+ `https://github.com/valsr/haanim` as an **Integration**, then download HAAnim.
87
+ 2. Restart Home Assistant.
88
+ 3. **Settings → Devices & services → Add integration → HAAnim**.
89
+
90
+ That is all: a release of the integration has the HAAnim engine in it, so no Python package has to be
91
+ installed into Home Assistant.
92
+
93
+ To install by hand, download `haanim.zip` from a
94
+ [release](https://github.com/valsr/haanim/releases) and unpack it into
95
+ `<config>/custom_components/haanim`. Copying `custom_components/haanim` out of a checkout of the repository is
96
+ not enough, because the engine is not in that folder there; build the folder to copy with
97
+ `python scripts/build-integration.py --folder OUT`.
98
+
99
+ Automations live in `/config/haanim/automations/` by default. The folder, the rescan interval, the limits and
100
+ the import options are set under **Configure** on the integration.
101
+
102
+ ## Your first automation
103
+
104
+ Create `/config/haanim/automations/hello/main.py`:
105
+
106
+ ```python
107
+ from haanim import ActionEvent, action, haa, startup
108
+
109
+
110
+ @startup
111
+ def ready(event: ActionEvent):
112
+ haa.card.add_element(haa.card.create_text("hello", "## Hello\nPress the button."))
113
+ haa.card.add_element(haa.card.create_button("greet", label="Greet", action="greet"))
114
+
115
+
116
+ @action
117
+ async def greet(event: ActionEvent):
118
+ await haa.service.persistent_notification.create(message="Hello from HAAnim")
119
+ ```
120
+
121
+ Within the rescan interval the automation is running: `sensor.haanim_hello` is `on`, and the action can be run
122
+ from the HAAnim panel, with the `haanim.run_action` service, or from the automation's card:
123
+
124
+ ```yaml
125
+ type: custom:haanim-card
126
+ automation_id: hello
127
+ ```
128
+
129
+ The [automation guide](https://haanim.readthedocs.io/en/latest/AUTOMATIONS/) covers everything an automation can do, and
130
+ [`examples/`](https://github.com/valsr/haanim/tree/main/examples) has complete automations with tests, among them a demo for each part of
131
+ HAAnim. The same documentation is built for Read the Docs from `docs/` (`mkdocs.yml`).
132
+
133
+ ## Testing an automation
134
+
135
+ The engine is also a Python package, `haanim`, for your own machine: it gives your editor completion and
136
+ types for `from haanim import ...`, and the test harness. It is not needed in Home Assistant.
137
+
138
+ ```sh
139
+ pip install haanim pytest pytest-asyncio
140
+ ```
141
+
142
+ The package needs Python 3.14, the Python that Home Assistant 2026.9 runs on.
143
+
144
+ ```python
145
+ from haanim.testing import AutomationHarness
146
+
147
+
148
+ async def test_greeting():
149
+ async with AutomationHarness("automations/hello") as automation:
150
+ await automation.press("greet")
151
+ assert automation.service_calls("persistent_notification.create")[0].data == {
152
+ "message": "Hello from HAAnim"
153
+ }
154
+ ```
155
+
156
+ The harness runs the automation with the same interpreter and triggers as Home Assistant, against a fake Home
157
+ Assistant whose clock only moves when the test moves it. Installing the package does not install Home
158
+ Assistant.
159
+
160
+ ## Services
161
+
162
+ | Service | Data | What it does |
163
+ | ------------------------- | --------------------------------- | -------------------------------------------------------- |
164
+ | `haanim.run_action` | `automation_id`, `action`, `data` | Runs an action; returns its result as response data |
165
+ | `haanim.enable` | `automation_id` | Enables and starts the automation |
166
+ | `haanim.disable` | `automation_id` | Stops and disables the automation |
167
+ | `haanim.start` | `automation_id` | Starts the automation |
168
+ | `haanim.stop` | `automation_id` | Stops the automation |
169
+ | `haanim.restart` | `automation_id` | Restarts the automation |
170
+ | `haanim.reload` | `automation_id` (optional) | Rescans now and reloads one automation, or all |
171
+ | `haanim.list_automations` | - | Returns ID, name, state and enabled flag of every one |
172
+ | `haanim.list_actions` | `automation_id` | Returns the automation's actions |
173
+ | `haanim.clear_log` | `automation_id` (optional) | Empties the log HAAnim keeps for one automation, or all |
174
+ | `haanim.set_log_level` | `automation_id`, `level` | Sets the automation's log level; `default` takes it away |
175
+
176
+ ## Development
177
+
178
+ ```sh
179
+ git clone https://github.com/valsr/haanim.git
180
+ cd haanim
181
+ uv sync --all-extras
182
+
183
+ uv run pytest # Python tests, frontend tests and the coverage gate
184
+ uv run python scripts/check-public-api-coverage.py # the automation-facing API must be at 100%
185
+ scripts/test-frontend.sh # only the JavaScript tests (needs node 22+)
186
+ uv run black --check . && uv run pylint custom_components src
187
+ uv run mypy custom_components src && uv run pyright src custom_components examples
188
+ ```
189
+
190
+ A Home Assistant with the integration, the package and the examples, in a container:
191
+
192
+ ```sh
193
+ ./build-and-run.sh # http://localhost:8123, user admin, password admin
194
+ uv run python scripts/e2e-smoke.py # end-to-end checks against that container
195
+ ```
196
+
197
+ The engine has one source, `src/haanim`. In a checkout the integration imports it as the installed `haanim`
198
+ package (`uv sync` installs it in place). A release carries a copy of it inside the integration:
199
+
200
+ ```sh
201
+ uv run python scripts/build-integration.py # dist/haanim.zip: what a GitHub release attaches and HACS installs
202
+ ```
203
+
204
+ The repository:
205
+
206
+ | Path | What is there |
207
+ | --------------------------- | -------------------------------------------------------------------------- |
208
+ | `src/haanim/` | The engine: interpreter, triggers, dispatcher, `haa`. No Home Assistant imports |
209
+ | `src/haanim/testing/` | The test harness and the fakes it is built on |
210
+ | `custom_components/haanim/` | The integration: entity, services, options, websocket commands, frontend |
211
+ | `examples/` | Example automations and their harness tests |
212
+ | `tests/` | `engine/` (no Home Assistant), `integration/`, `frontend/` |
213
+ | `docs/` | The documentation (built with MkDocs) and notes on the development environment |
214
+
215
+ See [CONTRIBUTING.md](https://github.com/valsr/haanim/blob/main/CONTRIBUTING.md) before sending a pull request.
216
+
217
+ ## License
218
+
219
+ MIT, see [LICENSE](https://github.com/valsr/haanim/blob/main/LICENSE). HAAnim is a custom integration and is not supported by the Home Assistant
220
+ project.
haanim-0.2.0/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # HAAnim
2
+
3
+ [![CI](https://github.com/valsr/haanim/actions/workflows/ci.yml/badge.svg)](https://github.com/valsr/haanim/actions/workflows/ci.yml)
4
+ [![hacs_badge](https://img.shields.io/badge/HACS-Custom-orange.svg)](https://github.com/custom-components/hacs)
5
+ [![PyPI](https://img.shields.io/pypi/v/haanim.svg)](https://pypi.org/project/haanim/)
6
+ [![Documentation](https://readthedocs.org/projects/haanim/badge/?version=latest)](https://haanim.readthedocs.io/)
7
+ [![License](https://img.shields.io/github/license/valsr/haanim.svg)](https://github.com/valsr/haanim/blob/main/LICENSE)
8
+
9
+ Write Home Assistant automations in Python. An automation is a folder with a `main.py`; decorators say when
10
+ its functions run, and the `haa` object reaches entities, services, storage and the automation's own
11
+ dashboard card.
12
+
13
+ ```python
14
+ from haanim import StateEvent, TimeEvent, haa, on_state, on_time
15
+
16
+
17
+ @on_state("sensor.temperature > 30")
18
+ async def high_temperature(event: StateEvent):
19
+ await haa.service.notify.mobile_app(message=f"It is {float(haa.entity.sensor.temperature)}°C")
20
+
21
+
22
+ @on_time("09:00", day_of_week="weekdays", when="person.john == 'home'")
23
+ async def morning(event: TimeEvent):
24
+ await haa.service.light.turn_on(entity_id="light.bedroom", brightness=150)
25
+ ```
26
+
27
+ ## What you get
28
+
29
+ - **Triggers**: time (including sunrise and sunset), interval, cron, Home Assistant events and state
30
+ expressions, each with optional constraints (time of day, date range, day of week, state).
31
+ - **Actions** that can be run by hand, by a service, by another automation or by a trigger, with `DROP`,
32
+ `QUEUE` and `CANCEL` execution modes, timeouts and a concurrency limit.
33
+ - **`haa`**: entity access, service calls, `sleep` and `wait_for`, persistent variables, assets, calling and
34
+ controlling other automations.
35
+ - **A sensor per automation** (`sensor.haanim_<id>`: `on`, `off`, `error`) with status attributes.
36
+ - **A dashboard card** (`custom:haanim-card`) that the automation titles and fills with text, images,
37
+ values, live entities and buttons, plus a management panel in the sidebar with each automation's
38
+ controls, actions and log.
39
+ - **Hot reload**: edit a file and the automation is reloaded.
40
+ - **A test harness**: test an automation with `pytest`, with no Home Assistant running.
41
+
42
+ The interpreter keeps automations from blocking Home Assistant by accident (no blocking I/O, an import
43
+ allowlist, loops that yield). It is a guard rail, not a sandbox: an automation can do whatever Home Assistant
44
+ can. Only install automations you trust.
45
+
46
+ ## Installation
47
+
48
+ HAAnim needs Home Assistant 2026.9 or newer.
49
+
50
+ 1. Install the integration with HACS: **HACS → ⋮ → Custom repositories**, add
51
+ `https://github.com/valsr/haanim` as an **Integration**, then download HAAnim.
52
+ 2. Restart Home Assistant.
53
+ 3. **Settings → Devices & services → Add integration → HAAnim**.
54
+
55
+ That is all: a release of the integration has the HAAnim engine in it, so no Python package has to be
56
+ installed into Home Assistant.
57
+
58
+ To install by hand, download `haanim.zip` from a
59
+ [release](https://github.com/valsr/haanim/releases) and unpack it into
60
+ `<config>/custom_components/haanim`. Copying `custom_components/haanim` out of a checkout of the repository is
61
+ not enough, because the engine is not in that folder there; build the folder to copy with
62
+ `python scripts/build-integration.py --folder OUT`.
63
+
64
+ Automations live in `/config/haanim/automations/` by default. The folder, the rescan interval, the limits and
65
+ the import options are set under **Configure** on the integration.
66
+
67
+ ## Your first automation
68
+
69
+ Create `/config/haanim/automations/hello/main.py`:
70
+
71
+ ```python
72
+ from haanim import ActionEvent, action, haa, startup
73
+
74
+
75
+ @startup
76
+ def ready(event: ActionEvent):
77
+ haa.card.add_element(haa.card.create_text("hello", "## Hello\nPress the button."))
78
+ haa.card.add_element(haa.card.create_button("greet", label="Greet", action="greet"))
79
+
80
+
81
+ @action
82
+ async def greet(event: ActionEvent):
83
+ await haa.service.persistent_notification.create(message="Hello from HAAnim")
84
+ ```
85
+
86
+ Within the rescan interval the automation is running: `sensor.haanim_hello` is `on`, and the action can be run
87
+ from the HAAnim panel, with the `haanim.run_action` service, or from the automation's card:
88
+
89
+ ```yaml
90
+ type: custom:haanim-card
91
+ automation_id: hello
92
+ ```
93
+
94
+ The [automation guide](https://haanim.readthedocs.io/en/latest/AUTOMATIONS/) covers everything an automation can do, and
95
+ [`examples/`](https://github.com/valsr/haanim/tree/main/examples) has complete automations with tests, among them a demo for each part of
96
+ HAAnim. The same documentation is built for Read the Docs from `docs/` (`mkdocs.yml`).
97
+
98
+ ## Testing an automation
99
+
100
+ The engine is also a Python package, `haanim`, for your own machine: it gives your editor completion and
101
+ types for `from haanim import ...`, and the test harness. It is not needed in Home Assistant.
102
+
103
+ ```sh
104
+ pip install haanim pytest pytest-asyncio
105
+ ```
106
+
107
+ The package needs Python 3.14, the Python that Home Assistant 2026.9 runs on.
108
+
109
+ ```python
110
+ from haanim.testing import AutomationHarness
111
+
112
+
113
+ async def test_greeting():
114
+ async with AutomationHarness("automations/hello") as automation:
115
+ await automation.press("greet")
116
+ assert automation.service_calls("persistent_notification.create")[0].data == {
117
+ "message": "Hello from HAAnim"
118
+ }
119
+ ```
120
+
121
+ The harness runs the automation with the same interpreter and triggers as Home Assistant, against a fake Home
122
+ Assistant whose clock only moves when the test moves it. Installing the package does not install Home
123
+ Assistant.
124
+
125
+ ## Services
126
+
127
+ | Service | Data | What it does |
128
+ | ------------------------- | --------------------------------- | -------------------------------------------------------- |
129
+ | `haanim.run_action` | `automation_id`, `action`, `data` | Runs an action; returns its result as response data |
130
+ | `haanim.enable` | `automation_id` | Enables and starts the automation |
131
+ | `haanim.disable` | `automation_id` | Stops and disables the automation |
132
+ | `haanim.start` | `automation_id` | Starts the automation |
133
+ | `haanim.stop` | `automation_id` | Stops the automation |
134
+ | `haanim.restart` | `automation_id` | Restarts the automation |
135
+ | `haanim.reload` | `automation_id` (optional) | Rescans now and reloads one automation, or all |
136
+ | `haanim.list_automations` | - | Returns ID, name, state and enabled flag of every one |
137
+ | `haanim.list_actions` | `automation_id` | Returns the automation's actions |
138
+ | `haanim.clear_log` | `automation_id` (optional) | Empties the log HAAnim keeps for one automation, or all |
139
+ | `haanim.set_log_level` | `automation_id`, `level` | Sets the automation's log level; `default` takes it away |
140
+
141
+ ## Development
142
+
143
+ ```sh
144
+ git clone https://github.com/valsr/haanim.git
145
+ cd haanim
146
+ uv sync --all-extras
147
+
148
+ uv run pytest # Python tests, frontend tests and the coverage gate
149
+ uv run python scripts/check-public-api-coverage.py # the automation-facing API must be at 100%
150
+ scripts/test-frontend.sh # only the JavaScript tests (needs node 22+)
151
+ uv run black --check . && uv run pylint custom_components src
152
+ uv run mypy custom_components src && uv run pyright src custom_components examples
153
+ ```
154
+
155
+ A Home Assistant with the integration, the package and the examples, in a container:
156
+
157
+ ```sh
158
+ ./build-and-run.sh # http://localhost:8123, user admin, password admin
159
+ uv run python scripts/e2e-smoke.py # end-to-end checks against that container
160
+ ```
161
+
162
+ The engine has one source, `src/haanim`. In a checkout the integration imports it as the installed `haanim`
163
+ package (`uv sync` installs it in place). A release carries a copy of it inside the integration:
164
+
165
+ ```sh
166
+ uv run python scripts/build-integration.py # dist/haanim.zip: what a GitHub release attaches and HACS installs
167
+ ```
168
+
169
+ The repository:
170
+
171
+ | Path | What is there |
172
+ | --------------------------- | -------------------------------------------------------------------------- |
173
+ | `src/haanim/` | The engine: interpreter, triggers, dispatcher, `haa`. No Home Assistant imports |
174
+ | `src/haanim/testing/` | The test harness and the fakes it is built on |
175
+ | `custom_components/haanim/` | The integration: entity, services, options, websocket commands, frontend |
176
+ | `examples/` | Example automations and their harness tests |
177
+ | `tests/` | `engine/` (no Home Assistant), `integration/`, `frontend/` |
178
+ | `docs/` | The documentation (built with MkDocs) and notes on the development environment |
179
+
180
+ See [CONTRIBUTING.md](https://github.com/valsr/haanim/blob/main/CONTRIBUTING.md) before sending a pull request.
181
+
182
+ ## License
183
+
184
+ MIT, see [LICENSE](https://github.com/valsr/haanim/blob/main/LICENSE). HAAnim is a custom integration and is not supported by the Home Assistant
185
+ project.