Home Assistant custom integration for Trimlight EDGE lights.
This integration uses the Trimlight EDGE cloud API and adds Home Assistant entities for power, brightness, built-in presets, saved custom presets, custom effect modes, speed control, and current preset tracking.
This is a custom integration, not an official Home Assistant integration.
- UI-based setup through Home Assistant config flow
light.trimlightfor power and brightnessselect.trimlight_built_in_presetfor built-in animationsselect.trimlight_custom_presetfor saved custom presetsselect.trimlight_custom_effect_modefor custom effect modesnumber.trimlight_effect_speedfor effect speed as a 0-100% slidersensor.trimlight_current_presetwith effect detail attributesbutton.trimlight_refresh_presetsto refresh preset caches- Local preset cache written to your Home Assistant config directory
- Optional structured debug logging for troubleshooting
You need:
- A Trimlight EDGE controller
- A Home Assistant installation that supports custom integrations
client_idclient_secretdevice_id
The integration validates the credentials during setup by calling the Trimlight device-detail endpoint.
client_idThe Trimlight EDGE account identifier used by the API. In your setup, this is the same value you have already been using in your Trimlight EDGE scripts.client_secretThe Trimlight EDGE API secret. This is not generated by Home Assistant. It must come from Trimlight or from the credentials already used by your existing Trimlight EDGE tooling.device_idThe unique device identifier for your Trimlight EDGE controller.
If you already have working Trimlight EDGE scripts, reuse the same client_id, client_secret, and device_id values here.
If you do not know your device_id, use your existing Trimlight scripts or other Trimlight EDGE tooling to fetch it before adding the integration.
If you use HACS:
- Open
HACS. - Add this repository as a custom integration repository if needed.
- Download
Trimlight. - Restart Home Assistant.
- Open your Home Assistant config directory.
- Create
custom_components/trimlightif it does not already exist. - Copy the contents of
custom_components/trimlightinto your Home Assistant config atcustom_components/trimlight. - Restart Home Assistant.
For local development, direct copy into the Home Assistant config folder is the fastest workflow. You do not need HACS Redownload if you are copying files directly.
After installation:
- Go to
Settings->Devices & Services. - Select
Add Integration. - Search for
Trimlight. - Enter:
client_idclient_secretdevice_id
The integration creates a config entry titled Trimlight <last 6 chars of device_id>.
The integration exposes these options in the options flow:
Commit custom presetsWhen enabled, updates to a saved custom preset are written back to that saved preset and then re-run by ID. This is most useful for custom speed and brightness changes that you want to persist.Enable debug loggingWrites structured JSONL debug events totrimlight_debug_ENTRY_ID.jsonlin your Home Assistant config directory.
The integration creates these entities:
light.trimlightPower and brightness controlselect.trimlight_built_in_presetBuilt-in Trimlight animationsselect.trimlight_custom_presetSaved custom presets from the controllerselect.trimlight_custom_effect_modeEffect mode for the active custom presetnumber.trimlight_effect_speedEffect speed displayed as 0-100%sensor.trimlight_current_presetCurrent preset name plus effect detail attributesbutton.trimlight_refresh_presetsRefresh preset lists from the controller
sensor.trimlight_current_preset exposes:
current_effect_idcurrent_effect_categorycurrent_effect_modecurrent_effect_speedcurrent_effect_brightnesscurrent_effect_pixel_lencurrent_effect_reversecurrent_effect_pixels
For saved custom presets, the integration prefers the saved preset definition over stale controller data when it can safely identify the active preset.
The select entities expose extra lookup attributes:
select.trimlight_custom_presetcurrent_idpresetsname_to_idoption_to_id
select.trimlight_built_in_presetcurrent_idbuiltins
select.trimlight_custom_effect_modecurrent_mode_idmodes
- The integration is
cloud_polling. - Standard polling interval is
600seconds. - After control actions, the integration schedules a verification refresh after
5seconds. - Power transitions use a
20second grace window to reduce UI flicker while the controller settles.
light.turn_onsets the controller switch state to1.light.turn_offsets the controller switch state to0.- Brightness updates are applied to the active effect.
- Built-in presets are selected with
select.trimlight_built_in_preset. - If the controller is off, the integration powers it on first.
- The integration tries the built-in preview path first.
- If the controller rejects the built-in preview shape, the integration falls back to the saved built-in effect by ID.
- If the first selection does not stick, the integration may reapply the built-in automatically during verification.
- Saved custom presets are selected with
select.trimlight_custom_preset. - The integration applies saved custom presets by ID.
- If the controller is off, the integration powers it on first and then applies the preset.
- If the first selection does not stick, the integration may re-run the preset automatically during verification.
- Duplicate custom preset names are disambiguated in the selector, for example
Name (id 12).
- Custom effect modes are selected with
select.trimlight_custom_effect_mode. - The mode list comes from the built-in
CUSTOM_EFFECT_MODESmap in the integration. - Mode changes apply to the active custom effect.
number.trimlight_effect_speeddisplays speed as0-100%.- The integration converts that to the device's
0-255speed range. - Built-in speed changes are applied to the active built-in effect.
- Custom speed changes are applied to the active custom effect.
- When
Commit custom presetsis enabled, custom speed changes are persisted back to the saved custom preset before the preset is re-run.
- After powering on from an off state,
Custom presetandCustom effect modemay briefly showunknownor remain blank while the controller reports its active effect. - This short delay is expected.
- The
Current presetsensor, custom select, and custom effect mode should settle once the controller state catches up.
Preset cache data is stored in Home Assistant storage and also written as a readable file in your Home Assistant config directory:
trimlight_presets_<entry_id>.json
If built-in presets are not returned by the controller, the integration falls back to the static built-in preset list bundled with the integration.
action:
- service: light.turn_on
target:
entity_id: light.trimlight
- service: select.select_option
target:
entity_id: select.trimlight_custom_preset
data:
option: "Seahawks"action:
- service: select.select_option
target:
entity_id: select.trimlight_built_in_preset
data:
option: "Rainbow Spin"action:
- service: number.set_value
target:
entity_id: number.trimlight_effect_speed
data:
value: 75- If custom presets do not appear, press
button.trimlight_refresh_presets. - If HACS appears to show an older commit, use
Update informationbeforeRedownload. - If the current preset is briefly
UnknownorOffduring a transition, wait for the verification refresh to complete. - If you are testing local code by copying directly into Home Assistant, restart Home Assistant after each integration change.
- If you enable debug logging, review
trimlight_debug_ENTRY_ID.jsonlin your Home Assistant config directory. - The integration expects valid Trimlight EDGE API credentials for every request. Invalid credentials will cause setup or refresh failures.
The integration works with standard Home Assistant cards. Common pairings are:
light.trimlightwith a Tile cardselect.trimlight_built_in_presetwith a Select cardselect.trimlight_custom_presetwith a Select cardselect.trimlight_custom_effect_modewith a Select cardnumber.trimlight_effect_speedwith a Number cardsensor.trimlight_current_presetwith an Entity, Template, or Markdown card
If you use Mushroom cards, they work well with these entities, but they are optional and not required for the integration.
These examples are optional. They are not required for the integration to work, but they can be useful for building a dashboard around the Trimlight entities.
type: entity
entity: sensor.trimlight_current_preset
name: Current Presettype: entities
entities:
- entity: select.trimlight_built_in_preset
name: Built-In Presettype: entities
entities:
- entity: select.trimlight_custom_preset
name: Custom Presettype: conditional
conditions:
- entity: select.trimlight_custom_preset
state_not: unknown
- entity: select.trimlight_custom_preset
state_not: unavailable
card:
type: entities
entities:
- entity: select.trimlight_custom_effect_mode
name: Custom Effect Modetype: conditional
conditions:
- entity: light.trimlight
state: "on"
card:
type: entities
entities:
- entity: number.trimlight_effect_speed
name: Speed ControlThis markdown card renders a simple 6x5 preview grid using current_effect_pixels from sensor.trimlight_current_preset.
type: markdown
title: Preview
content: >
{% set pixels =
state_attr('sensor.trimlight_current_preset','current_effect_pixels') or []
-%} {% if pixels -%} <table
style="border-collapse:collapse;border-spacing:0;line-height:0;"> {%- for row
in range(0,6) %} <tr style="line-height:0;"> {%- for col in range(0,5) %} {%-
set i = row*5 + col %} {%- set p = (pixels | selectattr('index','equalto', i)
| first) %} {%- if p is none %} {%- set color = 0 %} {%- set count = 0 %} {%-
else %} {%- set color = p.color | int %} {%- set count = p.count | int %} {%-
endif %} {%- set r = color // 65536 %} {%- set g = (color // 256) % 256 %} {%-
set b = color % 256 %} {%- set hex = '%02X%02X%02X' % (r,g,b) %} <td
style="width:28px;height:28px;padding:0;text-align:center;vertical-align:middle;">
<font color="#{{ hex }}" style="line-height:28px;display:inline-block;"><b>{{ count }}</b></font>
</td> {%- endfor %} </tr> {%- endfor %} </table> {%- else %} No pixel data {%-
endif %}type: markdown
content: >
{% set pixels =
state_attr('sensor.trimlight_current_preset','current_effect_pixels') %} {% if
pixels %} <div style="font-family: monospace; white-space: pre-wrap;
word-break: break-word;"> {% for p in pixels %} {% set color = p.color | int
%} {% set r = (color // 65536) %} {% set g = (color // 256) % 256 %} {% set b
= color % 256 %} {% set hex = '%02X%02X%02X' % (r, g, b) %} <div>idx {{
p.index }} | count {{ p.count }} | disable {{ p.disable }} | <span
style="color: rgb({{ r }}, {{ g }}, {{ b }}); font-weight: 600;">
<font color="#{{ hex }}">{{ color }} (#{{ hex }})</font>
</span> </div> {% endfor %} </div> {% else %} No pixel data {% endif %}This repository includes a Windows-friendly test runner for state-based validation:
tools/trimlight_test_runner.pytools/run_trimlight_tests.ps1tools/trimlight_test_runner.example.json
Recommended setup:
- Copy
tools/trimlight_test_runner.example.jsontotools/trimlight_test_runner.local.json. - Create a token file such as
tools/ha_trimlight.token. - Paste your Home Assistant long-lived token into that file.
- Edit the local config file with your Home Assistant URL, share path, and preset names.
- Run the suite:
.\tools\run_trimlight_tests.ps1Useful commands:
.\tools\run_trimlight_tests.ps1 -Scenario custom_off_to_on
python .\tools\trimlight_test_runner.py --list-scenariosRunner output is written to the local debug/ folder. When available, the runner also copies the latest trimlight_debug_*.jsonl file from your Home Assistant share into debug/.
- Runtime integration state is stored in
custom_components/trimlight/data.py. - Effect lookups and normalization live in
custom_components/trimlight/effects.py. - Effect update logic lives in
custom_components/trimlight/controller.py. - Built-in preset names live in
custom_components/trimlight/presets.py. - Preset cache persistence lives in
custom_components/trimlight/storage.py.
The repository includes Trimlight EDGE API reference material in docs.
MIT License. See LICENSE.