ir-canonical
A canonical media-control vocabulary and a Flipper-IRDB name → canonical
mapping, published as a small Python library so both the ESPHome codegen
(esphome-ir-codegen) and Concerto can resolve remote buttons semantically
instead of matching raw strings.
Why
Flipper-IRDB has no enforced button
naming — 54,415 distinct names over 301,921 occurrences. "Volume up" alone
appears as VOL+, Vol_up, VOLUME_UP, VOL_^, TV_Vol+, AMP_VOL+, … So you
cannot match a control by raw name.
Use it
pip install homeops-ir-canonical
from ir_canonical import resolve, canonical, MAP
resolve("VOL+") # -> "volume_up" (curated lookup, baked at build time)
resolve("TV_Power") # -> "power_toggle"
canonical("Ch_prev") # -> "channel_down" (rule engine; maps unseen spellings)
canonical("HDMI_1") # -> "input_hdmi_1"
MAP["vol+"] # the flat {alias: canonical} dict, lower-cased keys
resolve(name)— fast curated lookup via the bundled, human-maintainedcontrols/tree (compiled tocontrol_map.jsonat build time).canonical(name)— the broad rule-based engine; resolves spellings the curated tree hasn't enumerated yet, and the full vocabulary (not just the core).
Both are case-insensitive.
The curated controls tree (edit this by hand)
src/ir_canonical/controls/ is the human-owned source of truth for the core
controls that matter for driving activities (power / volume / channel / transport
/ navigation / digits / source — a curated subset):
src/ir_canonical/controls/
Volume_Up/aliases.json ["VOL+", "Vol_up", "VOLUME_UP", "AMP_VOL+", ...]
Power_Toggle/aliases.json ["POWER", "STANDBY", "TV_POWER", "PWR", ...]
...
Edit any aliases.json to prune or extend a control. The build bakes the tree
into control_map.json (so installs get an instant lookup); CI fails the build if
two controls claim the same alias.
Beyond IR: app and gamepad families
Two families extend the vocabulary past classic IR remotes so one control language spans IR, BLE, and native transports:
app_*— video-app launch hotkeys (app_netflix,app_youtube,app_prime_video,app_disney_plus, …). These appear on real IR remotes, so bothresolve()andcanonical()map their Flipper spellings.pad_*— gamepad controller inputs (pad_a,pad_up,pad_l1,pad_start, …). These have no IR source — they exist purely as cross-transport canonical vocabulary, soresolve()/canonical()only match the namespacedpad_*spellings, not arbitrary remote names.
Layout
| Path | What |
|---|---|
src/ir_canonical/normalize.py |
the rule-based canonical() engine + vocabulary |
src/ir_canonical/controls/ |
the curated tree (edit by hand) |
src/ir_canonical/compile.py |
tree → {alias: canonical} compiler |
hatch_build.py |
build hook that bakes control_map.json into the wheel |
seed.py |
dev tool: seed the tree + data/ reports from a Flipper-IRDB clone |
data/ |
full auto-derived map + unmapped tail (reference, not shipped) |
Develop
python -m build --wheel --no-isolation # build (bakes the map)
pip install dist/*.whl && pytest -q # test as a consumer
python seed.py /path/to/Flipper-IRDB-clone # re-seed the tree (won't clobber edits)
Release & publish
Versioning is managed by release-please
(Conventional Commits). Merging the release PR tags a GitHub Release, which triggers
publish.yaml to upload the wheel to PyPI via trusted publishing.
One-time PyPI setup: create the
homeops-ir-canonicalproject and add a trusted publisher → ownerHomeOps, repoir-canonical, workflowpublish.yaml, environmentpypi.
Metadata
Release files for homeops-ir-canonical 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| homeops_ir_canonical-0.1.1.tar.gz | 244.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| homeops_ir_canonical-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 286.9 kB
Release files / homeops_ir_canonical-0.1.1.tar.gz
| Download URL | homeops_ir_canonical-0.1.1.tar.gz |
|---|---|
| Size | 244.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
38ccb555d92e53b9a3dbec0ececa8a09f86dd8df2bddfefb6a1c99f73fcff1b1
|
|
BLAKE2b-256 checksum How to use checksums |
cf34bb03b9ef8fb34fe9fd47bd281166d126a0a72f260d38df0e9214e18b6229
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jun 19, 2026.
Transparency logRelease files / homeops_ir_canonical-0.1.1-py3-none-any.whl
| Download URL | homeops_ir_canonical-0.1.1-py3-none-any.whl |
|---|---|
| Size | 42.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
32a91901f77802bb493bfa5bbb1f08ade869a2f823a6c4338e9eb8fb2379e8e2
|
|
BLAKE2b-256 checksum How to use checksums |
530cb8cb1d94c5b9320f3cbf761c872794dcef60dad96e3d40593b0239ff82ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jun 19, 2026.
Transparency log