Your whole desk, your whole room, on the bus
✦ Hypercolor as a first-class HA hub ✦
Overview • Features • Requirements • Install • Entities • Services • Recipes • Development • Contributing
Hypercolor is an open-source RGB lighting engine. One daemon, every RGB device on your desk, all painted by the same effect at 60fps. Effects are web pages, rendered headless and sampled onto your physical LED layout every frame.
This integration brings that engine into Home Assistant as a hub. Master light, scenes, layouts, live controls, audio-reactive primitives, and full device topology become first-class entities you can wire into automations, scripts, and dashboards.
The companion Lovelace card is hyper-light-card, built directly on this integration's catalog, live controls, and effect cover art.
| 💎 Master light | brightness + effect picker, with the daemon's stable id available as an attribute for templates |
| 🦋 Per-device lights | opt in any device to its own light entity, registered as a child of the hub |
| 🌊 Scenes | every named scene and every catalog effect becomes a scene entity, which is what puts them in reach of Alexa and Google Home; services cover activation and snapshotting too |
| 🎯 Layouts & presets | select from spatial layouts and per-effect presets, exposed as native select entities |
| 🪄 Live controls | brightness, speed, hue shift, and intensity as Home Assistant number sliders that patch the running effect |
| 🌙 Audio reactivity | binary sensor for beat events with configurable hold, sensor for energy, switch to toggle audio capture |
| 🧪 Diagnostics | a single run_diagnostics service returns daemon health, coordinator state, connection history, and redaction-safe metadata |
| 💜 Repair flows | reauth on dropped API keys, unavailable issue when the daemon disappears, both auto-clear when fixed |
| 🌸 mDNS discovery | daemons advertise _hypercolor._tcp.local.; HA offers a one-click setup as soon as one shows up |
| 🪐 Effect uploads | push new HTML effects from a service call (path or inline HTML), great for blueprints |
- Home Assistant 2026.4.4 or newer
- Python 3.14.2 through 3.14.x (HA's runtime range for this release)
- A reachable Hypercolor daemon (default port
9420) that serves the/api/v1/sceneand/api/v1/outputresources, which means version0.4.0or newer - Optional: an API key, if your daemon has auth turned on
The daemon is the actual lighting engine, hyperb1iss/hypercolor. Install it on the same network as Home Assistant, on Linux, macOS, or in a container. The integration only ever talks to it over HTTP and a single WebSocket.
- In HACS, open the three-dot menu → Custom repositories.
- Add
hyperb1iss/hypercolor-hasswith category Integration. - Search for Hypercolor, install, and restart Home Assistant.
- Go to Settings → Devices & services. Hypercolor will already be waiting if discovery found a daemon. Otherwise click Add integration and search for it.
git clone https://github.com/hyperb1iss/hypercolor-hass.git
cp -r hypercolor-hass/custom_components/hypercolor \
/path/to/your/homeassistant/config/custom_components/Restart Home Assistant and add the integration as above.
Once added, point the config flow at your daemon. Manual setup accepts:
- Host (default
127.0.0.1) - Port (default
9420) - API key (optional, only required if your daemon enforces auth)
Discovered daemons skip the host and port questions, just confirm and (optionally) drop in an API key.
Every entity attaches to a hub device (the daemon itself). Per-device entities attach
to child devices (your physical LED hardware), wired to the hub via via_device so
the device tree reads naturally.
A daemon named Hyperia appears as the device Hypercolor Hyperia. Home Assistant
uses that product and instance name for its entity namespace:
| Entity | Type | Purpose |
|---|---|---|
light.hypercolor_hyperia |
light | pause or resume output, set brightness, and pick effects |
binary_sensor.hypercolor_hyperia_connected |
binary_sensor | live connectivity to the daemon |
sensor.hypercolor_hyperia_active_effect |
sensor | display name of the running effect |
select.hypercolor_hyperia_scene |
select | activate a scene |
select.hypercolor_hyperia_layout |
select | switch spatial layouts |
select.hypercolor_hyperia_preset |
select | apply a preset to the current effect |
button.hypercolor_hyperia_previous_effect / next_effect / random_effect |
button | walk the catalog |
button.hypercolor_hyperia_stop_effect |
button | clear every renderable zone in the live scene |
button.hypercolor_hyperia_discover_devices |
button | re-run device discovery |
number.hypercolor_hyperia_brightness / speed / hue_shift / intensity |
number | live patches into the running effect |
scene.hypercolor_hyperia_<scene> |
scene | activate one named daemon scene |
scene.hypercolor_hyperia_effect_<effect> |
scene | apply one catalog effect to the live scene |
Turning the master light off pauses output without discarding the live scene, its preset, or its controls. Turning it back on resumes that exact state. The Stop button is the separate, destructive action that empties the live scene's zones.
Every named scene on the daemon gets a scene entity, and every catalog effect gets one
named Effect: <name>. Both track the daemon live: a scene added upstream shows up
without a reload, and a deleted one takes its entity with it. Unique ids are built from
the daemon's scene and effect ids, so renaming a scene keeps the entity, its history, and
any automation pointing at it.
The reason this is a platform rather than another service call is voice. Home Assistant
exposes the scene domain to Alexa and Google Home by default and never exposes
select, so the scene picker was invisible to both assistants. Alexa has no mapping for
light effects at all, and Google reaches them only as a mode setting on the light rather
than as something you can name. A scene entity per scene and per effect gives both
assistants one addressable thing per look.
Toggle these in the integration's options panel:
- 🌊 Audio entities (
channels.audio) addsbinary_sensor.hypercolor_hyperia_audio_beat,binary_sensor.hypercolor_hyperia_audio_reactive_active,sensor.hypercolor_hyperia_audio_energy,select.hypercolor_hyperia_audio_device, andswitch.hypercolor_hyperia_audio_reactive. - 🧪 Metrics entities (
channels.metrics): addssensor.hypercolor_hyperia_fpsandsensor.hypercolor_hyperia_render_time. - 🦋 Per-device entities (
per_device_entities) lets you opt specific device ids in to get their own light, identify button, and enabled switch.
light.hypercolor_hyperia carries a card-facing snapshot of the running effect so a dashboard
card (e.g. hyper-light-card) can render
rich effect info and a full control surface without walking every companion entity:
| Attribute | Meaning |
|---|---|
effect_description / effect_publisher |
catalog description and author of the running effect |
effect_tags / effect_category / effect_version |
catalog metadata for the running effect |
effect_audio_reactive |
whether the running effect reacts to audio |
effect_controls |
normalized control descriptors (id, label, kind, min/max/step, value, options) for every control the running effect exposes, with value read from the live layer |
effect_image / active_effect_cover_image_url |
cover art URL for palette extraction |
active_preset_id |
the preset the running layer was applied from, if any |
active_scene / active_scene_id / zone_count / scene_count / device_count |
scene and topology context |
The four number entities (brightness, speed, hue_shift, intensity) bind to the
matching control on the running effect. Min/max/step come from the effect's metadata, so
the slider always reflects what the active effect actually exposes. If the effect has no
matching control, the entity goes unavailable. Controls beyond these four are still
exposed to cards through the master light's effect_controls attribute and driven with
the hypercolor.set_control service.
Nineteen services cover Hypercolor's Home Assistant automation surface. All of them take
config_entry_id so multi-daemon setups stay unambiguous.
| Service | What it does |
|---|---|
hypercolor.apply_effect |
apply an effect by id, optionally with controls, transition, or an effect-scoped preset id |
hypercolor.set_color |
shortcut for the solid_color effect, takes hex or r/g/b |
hypercolor.set_control |
patch a single control on the running effect's live layer |
hypercolor.activate_scene / deactivate_scene / create_scene / snapshot_scene |
activate, deactivate, or create a scene, or capture the live scene as a saved one |
hypercolor.set_zone / list_zones / set_unassigned_behavior |
inspect and configure the live scene's zones |
hypercolor.apply_layout |
switch spatial layouts |
hypercolor.apply_preset |
apply a bundled or saved preset by effect_id and preset_id |
hypercolor.save_preset / delete_preset / list_presets |
manage saved presets and list the active effect's unified preset stack |
hypercolor.identify_device |
flash a specific device for duration_ms |
hypercolor.set_display_face |
composite an effect onto a display face with blend mode and opacity |
hypercolor.upload_effect |
push a new HTML effect from a path or inline content |
hypercolor.run_diagnostics |
redaction-safe daemon + integration health snapshot |
The services.yaml ships full schemas, so the dev tools UI shows proper selectors for
every field.
alias: Sunset warm Hypercolor
trigger:
- platform: sun
event: sunset
action:
- service: hypercolor.apply_effect
data:
config_entry_id: !input config_entry_id
effect_id: warm_sunsetalias: Bass dim main lights
trigger:
- platform: state
entity_id: binary_sensor.hypercolor_hyperia_audio_beat
to: "on"
condition:
- condition: numeric_state
entity_id: sensor.hypercolor_hyperia_audio_energy
above: 0.7
action:
- service: light.turn_on
target:
entity_id: light.living_room_main
data:
brightness_step_pct: -20
transition: 0.1The audio beat sensor uses a configurable hold (audio_beat_hold_ms, default 100ms) so
brief beats actually trigger automations instead of bouncing too fast for HA to see.
hyper-light-card is the dedicated Lovelace card if you want a polished UI. For a stock entities card:
type: entities
title: Hypercolor
entities:
- light.hypercolor_hyperia
- sensor.hypercolor_hyperia_active_effect
- select.hypercolor_hyperia_scene
- select.hypercolor_hyperia_preset
- number.hypercolor_hyperia_brightness
- number.hypercolor_hyperia_speed
- number.hypercolor_hyperia_hue_shift
- number.hypercolor_hyperia_intensityMore examples live in examples/.
Hypercolor daemons advertise on mDNS as _hypercolor._tcp.local. with an id property
that becomes the integration's unique id. That means the same daemon keeps the same config
entry across IP changes, container restarts, and network re-shuffles.
The integration also runs a background WebSocket session against the daemon. Events trigger immediate coordinator refreshes; aggregate metrics and audio spectrum are opt-in channels that ride the same socket. The integration subscribes before its first HTTP reconciliation, treats resync notifications as barriers, and negotiates optional channels against the daemon's advertised capabilities. If the WebSocket drops, the integration backs off exponentially and retries forever, so HA's connectivity sensor reflects reality without needing per-tick polling. Periodic reconciliation is disabled by default and remains available as an explicit fallback.
This project uses uv and just.
Python 3.14, ruff, ty, pytest. The dev tooling expects a sibling checkout of
hypercolor at ../hypercolor so the Python
client can be installed editable.
git clone https://github.com/hyperb1iss/hypercolor.git
git clone https://github.com/hyperb1iss/hypercolor-hass.git
cd hypercolor-hass
uv sync --all-groups
just hass-devjust hass-dev writes a throwaway HA config under .dev/hass/config, symlinks
custom_components/hypercolor into it, and boots a Home Assistant instance on
http://127.0.0.1:8123. No production HA config is touched.
| Recipe | What it runs |
|---|---|
just fmt |
ruff check --fix then ruff format |
just lint |
ruff check and ruff format --check |
just typecheck |
ty check against the integration |
just test |
full pytest suite with the coverage gate |
just e2e |
full integration lifecycle against the fake daemon |
just e2e-real |
smoke test against a running real daemon |
just metadata |
manifest, hacs.json, services.yaml, strings.json checks |
just hass-check |
Home Assistant config validation against the throwaway config |
just verify |
the whole pipeline: lint → typecheck → test → metadata → build |
just clean-hass |
reset transient HA state under .dev/ |
Pre-commit is wired up too:
uv run pre-commit installruff and ty run on every commit.
Unit tests live under tests/ and use pytest-homeassistant-custom-component to bring up
a real HA instance per test. The end-to-end scenarios in
tests/test_hass_control_surface.py and tests/test_hass_entity_lifecycle.py exercise
the full integration lifecycle against a fake daemon. Pass
HYPERCOLOR_HASS_REAL_E2E=1 and run just e2e-real to point the explicit smoke test at
a real daemon.
PRs welcome. The bar is:
just verifyis green- Tests cover anything you added or changed
- Conventional commits (
feat(hass):,fix(hass):, etc.) - No
SyncHypercolorClientimport because the integration is async only
For larger ideas, open an issue first so we can sketch the shape before you write the
code. Driver work, spatial topology, and effect authoring all live upstream in
hypercolor; this repo is just the bridge.
- 💜 Hypercolor, the engine and daemon
- 🌌 SignalRGB Home Assistant, the sister integration for SignalRGB on Windows
- 🪄 hyper-light-card, the companion Lovelace card for this integration
Apache-2.0. See LICENSE.
If Hypercolor lights up your home, give us a ⭐ or
support the project
✦ Built by Hyperbliss ✦