A Home Assistant custom integration that provides smooth light fading for brightness, colors, and color temperatures, with automatic brightness restoration, autoconfiguration via the UI, and support for native transitions.
- Compatibility
- Features
- Installation
- How it works
- State Transitions
- Usage:
fado.fade_lights - Usage:
fado.exclude_lights/fado.include_lights - Autoconfiguration Panel
- Troubleshooting
- Development
- License
- Home Assistant: 2024.1.0 or newer
- Python: 3.13 or newer
- Fade lights smoothly to any brightness level (0-100%) over a specified transition period, with easing
- Fade colors smoothly using HS, RGB, RGBW, RGBWW, XY, or color temperature (Kelvin)
- Hybrid transitions between color modes (e.g., color temperature to saturated color)
- Target lights by entity, device, area, floor, or label, or light groups
- Optionally specify starting values with the
from:parameter for precise control - Mostly drop-in replacement for the
light.turn_onaction - Capability-aware: skips lights that don't support requested color modes
- Uses native transitions (where available) to smooth out each step for flicker-free fading
- Plays nicely with manual adjustments from the wall switch
- Setting brightness to 1% automatically sets the minimum real brightness supported by the light
- Autoconfiguration UI to determine optimal configuration for individual lights
- Exclude/include lights from fades and brightness restoration via actions or the configuration panel
- Automatic restoration of original (pre-fade) brightness when turning light on
Fado is available in the default HACS repository.
Or install by searching in HACS:
- Open HACS in your Home Assistant instance
- Search for "Fado"
- Click "Download"
- Restart Home Assistant
-
Copy the
custom_components/fadofolder to your Home Assistant installation:<config_directory>/custom_components/fado/
-
Restart Home Assistant
After installation and restart, add the integration via the Home Assistant UI:
- Go to Settings → Devices & services
- Click + Add Integration
- Search for "Fado"
- Click to add it
Once configured, the Fado actions will be available in Settings > Developer Tools → Actions.
Before anything, you should open the Autoconfiguration Panel in the Home Assistant sidebar and autoconfigure your lights.
The principle in action is that fades (usually executed by automations) should be gradual and smooth, while manual actions by the user (using the switch or the app to turn the light on or off or to change the brightness or color), should be immediate, otherwise the user may think that something has gone wrong.
Additionally, Fado tries to do the right thing. The API should be straightforward and simple to use, while still allowing for maximum flexibility.
You don't need to understand all of the details explained below to use Fado. They are provided for interest only.
In Settings > Developer tools > Actions, or when
configuring an Action in an automation, use the
fado.fade_lights action to:
- select one or more target lights
- provide a target brightness (where zero means turn the light off), and/or a color or color temperature,
- specify a transition time, i.e. how long the fade should last,
- optionally specify an easing curve which by default tries to make the fade smoother during the lower brightness phase.
- optionally specify a from starting point in case you don't want to start from the current state of the light.
See Usage: fado.fade_lights for
parameter specifications.
Fado resolves the targets list to a list of unique light entities and dispatches a fade action for each entity, so the fade for each light begins from the state that that light is currently in.
It uses the transition time, minimum delay setting, and the distance between the beginning and end states (e.g. start- and end-brightness, or start- and end-color) to calculate the optimal number of steps and the size of each step that the fade should use.
If the light doesn't support the specified change (for instance changing color temperature on a light that only supports brightness), or if the light is already in the final state, then no fade is executed.
If there is an existing fade in progress then Fado cancels it and waits for it to be cleaned up before starting the new fade.
If the light is currently on then Fado stores the current
brightness level as the original brightness. This is used for
automatic brightness restoration.
If a from parameter is specified, then it immediately sets the
light to the specified from state, after which the fade loop
begins.
For each step in the fade loop, Fado determines the next
brightness and color/color temp values, sets them, and records
how long it took. If the elapsed time is less than the
minimum delay, then it sleeps for the
remaining time before continuing with the next step. This means
that the total transition time will be at least as long as the
specified transition time. (It may, however, be longer if Home
Assistant or the network or the light itself is responding
slowly.)
If the light supports
native transitions then a short
transition time is used to apply a fade step to use the
light's hardware to make the fade smoother.
Fado stores the details of each fade step that is issued because it expects to see a matching state change event which it will recognise as its own and so knows to ignore it.
When you fade a light down to off and then manually turn it back
on, the light turns on at the last brightness set by the fade
loop, which might be 1%. This is unlikely to be what you want.
Instead, the integration automatically restores the light to its
original brightness level before the fade started.
- Light is at 80% brightness.
- This value is stored as the
original brightness. - You fade it to 0% (off) over 5 seconds.
- Later, you turn the light on manually.
- The light turns on at the last brightness the hardware is aware of, e.g. 1%.
- Fado automatically restores the brightness to the
original brightnessvalue of 80%.
However, brightness restoration isn't always wanted. Imagine the user turns on the light from an off state and simultaneously changes the brightness, for instance by holding down the dimmer switch to fade the brightness up until the switch is released. In order to distinguish between this case and the previous case, Fado also stores the brightness at the moment the light was turned off.
- Light is at 80% brightness.
- You turn the light off.
- Fado stores the brightness before turning the light off as
previous brightness. - You turn the light on and hold the dimmer switch to change the brightness.
- Fado compares the current brightness to the
previous brightness. - If they are the same then it assumes the user has just turned
the light on and it should restore the
original brightness. - If they are different then it assumes the user has also
changed the brightness, and it stores the new brightness as
original brightness.
During the fade loop, if Fado sees any event that it doesn't expect, that means there has been a manual intervention (e.g. the user uses the switch or app to switch the light on or off, or to change the brightness or color). In this case Fado cancels the running fade and waits for any in-flight steps to finish. These in-flight steps might overwrite the user's intended change, so once the in-flight events have been cleared, Fado restores the intended state.
Example 1:
- Light is at 80% brightness.
- This value is stored as the
original brightness. - You fade it to 0% (off) over 5 seconds.
- When the fade reaches 30%, you turn the light off manually with the switch.
- The fade is cancelled but an in-flight step turns the light back on at 25%.
- Fado waits until the 25% event has been seen and no further events are expected.
- Then it restores your intended state by turning the light off.
- The stored
original brightnessremains at 80%
Example 2:
- Light is at 80% brightness.
- This value is stored as the
original brightness. - You fade it to 0% (off) over 5 seconds.
- When the fade reaches 30%, you turn the light off manually, and then back on again.
- The light turns off then comes back on at 30%.
- The fade is cancelled but an in-flight step turns the brightness to 25%.
- Fado waits until the 25% event has been seen and no further events are expected.
- Then it ignores the previous
offstate and restores your final intended state by turning the light on at the storedoriginal brightnessof 80%.
Lights that do not support brightness will turn off when brightness is set to 0, or turn on when brightness is greater than 0.
Colors and color temperatures overlap, but are not the same thing. Color temperatures consist of limited shades of white light, while colors can cover any color in the rainbow (but typically don't display white light accurately).
Fading from one color to another is straightforward, as is fading from one color temperature to another. Fado supports hybrid fading as well, for instance fading from a color to a color temperature or from a color temperature to a color. It does this by dividing the fade into two phases, where the color phase takes 70% of the transition time, and the color temperature phase takes 30% of the transition time.
- the color phase fades from the starting color to the closest color in the supported color temperature range
- the color temperature phase switches from color to color temperature at the crossover point and continues the fade to the target color temperature
- the color temperature phase fades from the starting color temperature to the last supported color temperature closest to the target color
- the color phase switches from color temperature to color at the crossover point and continues the fade to the target color
If the user specifies a color temperature but the light only supports RGB colors, then a best effort is made to use hue-saturation to approximate the specified color temperature.
This table details how the fade is executed depending on the
initial state of the light and the target state. If the
from parameter is
used, the specified values are used as the initial state.
| Initial State | Target | Action |
|---|---|---|
state:on, brightness:10 |
brightness:50 |
Brightness fades from 10 to 50 |
state:off |
brightness:50 |
Brightness fades from 0 to 50 |
state:on, hs:[10,10] |
hs:[50,50] |
Color fades hs:[10,10] to hs:[50,50] (similar for RGB, RGBW, etc.) |
state:off |
hs:[50,50] |
Color fades hs:[0,0] to hs:[50,50] (similar for RGB, RGBW, etc.) |
state:on, color_temp:2500 |
color_temp:4000 |
Color temperature fades from 2500 to 4000 |
state:off |
color_temp:4000 |
Fades from min- or max-color temp (whichever is closest) to 4000 |
state:on, color_temp:4000 |
hs:[0,100] |
Hybrid fade from color_temp:4000 to hs:[0,100] |
state:on, hs:[0,100] |
color_temp:4000 |
Hybrid fade from hs:[0,100] to color_temp:4000 |
This table details the changes applied when Fado detects a manual event (i.e. an event from the switch or the app):
Fado uses the previous brightness to distinguish between
turning a light on, and turning a light on while simultaneously
changing the brightness level:
| Old State | New State | Description |
|---|---|---|
state:on, brightness: 10 |
state:off, brightness: None |
Light turned off. Fado stores old brightness as previous brightness. |
state:on, brightness: 10 |
state:on, brightness: 20 |
Brightness changed while on. Fado stores new level as original brightness. |
state:off, brightness:None, previous brightness: 10 |
state:on, brightness:10 |
Brightness matches previous brightness, so Fado restores original brightness. |
state:off, brightness:None |
state:on, brightness:10, previous brightness: 20 |
Brightness differs from previous brightness, so Fado stores it as new original brightness. |
Fades one or more lights to a target brightness and/or color over a transition period.
Specify which lights to fade using any combination of:
-
entity_id: One or more light entities (e.g.,light.bedroom) -
device_id: One or more device IDs -
area_id: One or more area IDs (e.g.,living_room) -
floor_id: One or more floor IDs -
label_id: One or more label IDsLight groups are automatically expanded to their individual lights. Duplicate entities are automatically deduplicated.
How long the fade should take in seconds (supports decimals,
e.g., 0.5 for 500ms)
Either brightness_pct (0-100) or brightness (0-255).
A value of zero means off
Only one target color or color temperature parameter allowed.
Either:
color_temp_kelvin: Target color temperature in Kelvin (1000-40000)
or one of:
hs_color: Target color as[hue, saturation]where hue is 0-360 and saturation is 0-100rgb_color: Target color as[red, green, blue](0-255 each)rgbw_color: Target color as[red, green, blue, white](0-255 each)rgbww_color: Target color as[red, green, blue, cold_white, warm_white](0-255 each)xy_color: Target color as[x, y](0-1 each)
The color parameters are converted to hue-saturation which are
used internally, while the color_temp_kelvin parameter is
converted to color_temp_mireds internally.
You can specify starting values to override the current light state:
from.brightness_pct: Starting brightness percentagefrom.color_temp_kelvin: Starting color temperaturefrom.hs_color,from.rgb_color, etc.: Starting color (same formats as target colors)
Changing the brightness from 100 to 101 is a 1% change, but changing from 1 to 2 is a 100% change. This means that brightness changes are more jarring the lower the brightness level. Fado tries to make fading smoother by supporting easing curves:
auto(default): Usesease_in_quadwhen start brightness is less than end brightness, andease_out_quadwhen end brightness is less than start brightnesslinear: Fades in a straight lineease_in_quad: Starts slowease_in_cubic: Starts slowerease_out_quad: Ends slowease_out_cubic: Ends slowerease_in_out_sine: Smooth S curve
action: fado.fade_lights
target:
entity_id: light.bedroom
data:
brightness_pct: 50
transition: 5action: fado.fade_lights
target:
entity_id:
- light.bedroom_wall
- light.living_room_ceiling
- light.outside_lights # light group
area_id:
- kitchen
floor_id:
- upstairs
data:
brightness_pct: 80
transition: 10action: fado.fade_lights
target:
entity_id: light.bedroom
data:
color_temp_kelvin: 6500
transition: 30
from:
color_temp_kelvin: 2700action: fado.fade_lights
target:
entity_id: light.accent
data:
hs_color: [240, 100] # Blue
brightness_pct: 80
transition: 5automation:
- alias: "Sunset fade"
trigger:
- platform: sun
event: sunset
offset: "-00:30:00"
action:
- action: fado.fade_lights
target:
area_id: living_room
data:
brightness_pct: 20
transition: 1800 # 30 minutesExcludes one or more lights from Fado. Excluded lights are ignored by fade operations and state tracking.
Specify which lights to include or exclude using any combination of:
entity_id: One or more light entities (e.g.,light.bedroom)device_id: One or more device IDsarea_id: One or more area IDs (e.g.,living_room)floor_id: One or more floor IDslabel_id: One or more label IDs
Light groups are automatically expanded to their individual lights. Duplicate entities are automatically deduplicated.
action: fado.exclude_lights
target:
entity_id: light.bedroomaction: fado.include_lights
target:
area_id:
- kitchen
- livingroomAfter installation, Fado appears in your Home Assistant sidebar. Click it to access the configuration panel where you can autoconfigure each light for the smoothest fades with the minimum of overhead.
Run auto-configure to automatically measure optimal step timing, support for native transitions, and minimum real brightness for each light
| Setting | Description | Default | Range |
|---|---|---|---|
| Min delay | Minimum delay (ms) between fade-steps without overloading slower devices | Global min delay | global - 2000 |
| Min brightness | Minimum real brightness value that the light supports | 1 |
1 - 255 |
| Native transitions | Whether to use the device's native transitions to smooth fading | No |
No, Yes, Disable |
| Exclude | Exclude this light from management by Fado | No |
No, Yes |
| Log level | Controls logging verbosity | warning |
warning, info, debug |
| Global min delay | Absolute minimum delay (ms) for all lights. Per-light min delay cannot be lower | 100 |
50 - 2000 |
| Download diagnostics | Download diagnostic data for debugging | — | — |
Autoconfiguration measures how long it takes for a light to apply changes to brightness and to report back its new state to Home Assistant. This minimum delay is the amount of time (in milliseconds) that Fado will wait between each fade step.
The lower this number, the smoother the fade can be but the more events Home Assistant needs to process. However, there is no point in sending more frequent updates than the light can handle. While you can configure this setting manually, it is not recommended to set it to a lower value than that determined by autoconfiguration.
Accepts 50ms - 2000ms and defaults to the global minimum delay. The minimum delay for an individual light cannot be set lower than the global minimum delay.
Home Assistant allows setting a brightness value anywhere from 1 to 255, but internally lights often use a different scale, for instance 1 to 100. For these lights, setting a brightness value of 1 might result in the light being turned off instead.
Autoconfigure determines the minimum brightness value where light is still emitted. With Fado, setting a brightness percentage or brightness value lower than this setting will instead apply the minimum real brightness.
Some lights support native transitions, that is the light
hardware knows how to fade between two brightness levels. This
is triggered by passing a time value to the transition
parameter of light.turn_on. However, even if the light claims
to support transitions, in reality this may not be the case.
Also, the amount of time the transition takes may be very
different from the time passed to the transition parameter.
Autoconfiguration tests this out to determine (a) whether native transitions are actually supported, and (b) how this affects the minimum step delay.
By setting native transitions manually to Disable, Fado will
disable native transitions when autoconfiguring the minimum step
delay, and when applying fades to a light.
Checking the Exclude checkbox next to a light will prevent
Fado from fading a light and also from autorestoring the
original brightness level.
This is the absolute minimum delay for all lights. No light may have a custom minimum delay setting below this value. It defaults to 100ms and has a minimum value of 50ms.
See Troubleshooting
The Download diagnostics link will download a JSON file containing all of the data used by Fado for debugging purposes. Important when submitting bug reports.
By default, Fado adds a Fado Light Fader entry to the Home Assistant sidebar, visible to all users. If you prefer to control who can access the Fado UI, you can disable the sidebar panel and use a custom dashboard instead.
To disable the sidebar panel:
- Go to Settings → Devices & Services → Fado → Configure
- Uncheck Show sidebar panel
- Click Submit
The sidebar entry will be removed immediately. The Fado card and dashboard strategy remain available for use in any Lovelace dashboard.
Fado provides a custom Lovelace card (custom:fado-card) and a
dashboard strategy (custom:fado) that give you the same
autoconfiguration UI as the sidebar panel, but with full control
over dashboard visibility and placement.
You can add the Fado card to any Lovelace dashboard:
- Edit your dashboard and click Add Card
- Search for Fado Light Fader in the card picker
- Add the card
Or add it manually in YAML mode:
type: custom:fado-cardFor best results, use the card in a Panel view (single card filling the full page).
To create a standalone Fado dashboard using the built-in strategy:
- Go to Settings → Dashboards → Add Dashboard
- Set the URL to something like
lovelace-fado - Configure visibility (e.g. admin only, or specific users)
- Open the dashboard and switch to raw configuration editor (Edit Dashboard → Raw configuration editor)
- Replace the contents with:
strategy:
type: custom:fadoThis creates a full-page dashboard with the Fado card. You can then control which users see it via the dashboard visibility settings.
When Fado detects lights that haven't been autoconfigured yet, it shows a persistent notification to prompt you to configure them.
You can control this behaviour in Settings → Devices & Services → Fado → Configure:
- Enable notifications — Uncheck to disable unconfigured light notifications entirely.
- Dashboard URL — When the sidebar panel is disabled,
notification links point to this URL (e.g.
/lovelace-fado/0). Leave blank to omit the link from notifications.
Go to the Autoconfiguration Panel by clicking Fado in the Home Assistant sidebar, and adjust the Log level verbosity setting:
| Level | What it shows |
|---|---|
warning |
Default. Only logs exceptions. |
info |
Fade start/complete, manual interventions, brightness restoration, autoconfiguration |
debug |
Every brightness step, expected state tracking, task cancellation internals |
For most troubleshooting, info level is sufficient and easier
to follow.
This log level setting is persisted across restarts.
Different lights behave differently, and these differences can create problems.
The values set by Fado are not necessarily what the light
reports back. For instance, Fado sets a brightness of 50% but
the light reports a brightness of 51%. Fado uses rounding to
try to match these values regardless.
A light may compress several actions into a single event, so while applying a fade step the user turns the light off. This manual intervention may be ignored by the light and so the fade loop continues. Alternatively, maybe the light-off event is reported and the fade step never generates an event.
When using native transitions, the light
may emit state update events which are mid-range, e.g. a fade
step is intended to move the light from brightness 50 to
brightness 65, but the light may also report an intermediate
brightness state of 55. Intermediate steps are recognised but
are not removed from the list of expected states as the light
should later report a final state which matches the 50->65
change.
Fado maintains an expected-events list internally. These events are pruned after 3 seconds so that, even if things do occassionally go wrong, within 3 seconds the light should be functioning normally again.
If you encounter a bug, please open an issue with:
- Your Home Assistant version
- The integration version
- Debug logs showing the problem
- Diagnostic data (available from the Autoconfiguration Panel)
- Steps to reproduce
The integration includes a comprehensive test suite with 668 tests covering config flow, action handling, fade execution, color fading, manual interruption detection, and brightness restoration.
Install the test dependencies:
pip install pytest pytest-asyncio pytest-cov pytest-homeassistant-custom-component syrupyNote: Do not use
pip install -e .(editable install) as it conflicts withpytest-homeassistant-custom-component's custom component discovery mechanism.
Run all tests:
pytest tests/ -vRun tests with coverage report:
pytest tests/ --cov=custom_components.fado --cov-report=term-missing -vRun a specific test file:
pytest tests/test_fade_execution.py -vThe test suite achieves 100% code coverage and includes tests for:
- Config flow (
test_config_flow.py): User setup, import flow, options validation - Integration setup (
test_init.py): Action registration, storage loading, unload cleanup - Action handling (
test_actions.py): Entity ID formats, group expansion, default parameters - Fade execution (
test_fade_execution.py): Fade up/down, turn off at 0%, non-dimmable lights - Color parameters (
test_color_params.py): Color conversions, validation,from:parameter - Capability filtering (
test_capability_filtering.py): Light capability detection, unsupported mode handling - Step generation (
test_step_generation.py): Hue interpolation, hybrid transitions - Planckian locus (
test_planckian_locus.py): Color temperature to HS conversions - Manual interruption (
test_manual_interruption.py): Brightness/color change detection, fade cancellation - Brightness restoration
(
test_brightness_restoration.py): Restore on turn-on, storage persistence - Exclude/include actions (
test_exclude_action.py): Action registration, flag persistence, fade filtering, panel notification - Event waiting (
test_event_waiting.py): Condition-based event waiting, stale value pruning
Tests run automatically on push and pull requests via GitHub Actions. The workflow tests against Python 3.13.
MIT License - feel free to modify and redistribute