Recently I installed a WLED strip on my media cabinet, which sits below my TV. Initially, I set up a basic automation to power the LED strip on and off with the power state of my Apple TV. One day, while pondering my next unnecessary and over-engineered automation project, I thought:
What if I could use the LED strip as a media progress indicator for content playing on my Apple TV?
After some quick research, it seemed other users had used WLED as a progress bar for different use cases, but nothing perfectly fit my goal.
Goals and limitations
- The LED strip should update at a reasonable rate to match the content playing on my Apple TV.
- A known limitation of the Home Assistant Apple TV integration is its slow polling and update frequency.
- The automation needs to calculate a precise progress value because updates from the Apple TV attributes may not always be timely or reliable.
- The Apple TV attributes in Home Assistant do not appear to reflect application state; they only update when media starts or stops (see this post).
- If I exit a movie, for example, the entity reflects a
pausedstate and its attributes still describe the movie I exited. This does not change until new media plays. - Since I do not want to stare at the progress bar when nothing is playing, I treat the
pausedstate—with a slight delay—as a trigger to return to my default color.
- If I exit a movie, for example, the entity reflects a
⚠️ Full disclaimer: I make no claims of being an expert at writing code or automations. My talented French colleague Claude lent a heavy hand on this project. Please feel free to tell Claude how you feel about the code.
Components
- Apple TV integration: Home Assistant's built-in integration monitors the Apple TV state.
- The integration provides several states that the automations monitor:
playing: Media is currently playing.paused: Media is paused.idle: Apple TV is on but not playing media.standby: Apple TV is in sleep mode.
- It also provides media attributes used to calculate the progress bar percentage:
media_position: Current position in the media, in seconds.media_duration: Total duration of the media, in seconds.media_position_updated_at: Timestamp when the position was last updated.
- The integration provides several states that the automations monitor:
- WLED REST API: A custom REST API endpoint provides direct JSON control of WLED.
WLED API
After some research, I decided the WLED JSON API was the best approach. It lets us send a POST request with a JSON body containing instructions for the LED strip.
My LED strip contains 108 addressable LEDs. Sending this basic POST request turns the entire strip orange:
{
"seg": {
"i": [0, 108, "FFA500"]
}
}
Here are two additional examples:
-
Single segment control (used in the idle state):
{ "on": true, "bri": 255, "seg": { "i": [0, 108, "FF8C00"] } } -
Multi-segment control (used in the progress bar state):
{ "on": true, "bri": 255, "seg": { "i": [0, 42, "00FFFF", 42, 108, "FF0080"] } }
The i array represents segments with their start position, end position, and color. Its syntax is [start_position1, end_position1, "color1", start_position2, end_position2, "color2", ...].
Home Assistant
Configuration.yaml
To control the WLED strip from Home Assistant, add this REST command to configuration.yaml:
rest_command:
wled_progress:
url: http://123.456.789.0/json
method: POST
content_type: "application/json"
payload: "{{ progress }}"
Both automations use this command to control the strip. The progress placeholder lets each automation dynamically construct its own JSON payload.
Automations.yaml
1. Apple TV power and paused state
This consolidated automation handles the power and paused scenarios based on Apple TV state. I chose orange (FF8C00) as the idle color, but you can change it.
- id: 'XXXXXXXXXXXXXXXX'
alias: Apple TV LED Power and Color Control
description: Controls LED strip power and color based on Apple TV state
triggers:
- entity_id: media_player.apple_tv
trigger: state
- event: start
trigger: homeassistant
actions:
- variables:
apple_tv_state: '{{ states(''media_player.apple_tv'') }}'
- choose:
# When Apple TV is playing, the progress bar automation handles it
- conditions:
- condition: state
entity_id: media_player.apple_tv
state: playing
sequence:
- delay:
milliseconds: 100
# When Apple TV is paused or idle, turn on the LED and set it to orange
- conditions:
- condition: or
conditions:
- condition: state
entity_id: media_player.apple_tv
state: paused
- condition: state
entity_id: media_player.apple_tv
state: idle
sequence:
- target:
entity_id: light.media_cabinet_led_strip
data: {}
action: light.turn_on
- delay:
seconds: 3
- data:
progress: "{% set json_data = {\n \"on\": true,\n \"bri\": 255,\n \"seg\": {\n \"i\": [0, 108, \"FF8C00\"]\n }\n} %} {{ json_data|tojson }}\n"
action: rest_command.wled_progress
# When Apple TV is off or in standby, turn off the LED strip
- conditions:
- condition: or
conditions:
- condition: state
entity_id: media_player.apple_tv
state: standby
- condition: state
entity_id: media_player.apple_tv
state: 'off'
sequence:
- target:
entity_id: light.media_cabinet_led_strip
data: {}
action: light.turn_off
- data:
progress: "{% set json_data = {\n \"on\": false,\n \"seg\": {\n \"i\": [0, 0, \"000000\", 0, 108, \"000000\"]\n }\n} %} {{ json_data|tojson }}\n"
action: rest_command.wled_progress
mode: restart
max_exceeded: silent
Key details:
- When playing, it does nothing and lets the progress bar automation take over.
- When paused or idle, it turns on the strip and sets it to orange after a three-second delay.
- When off or in standby, it turns off the strip completely.
mode: restartensures only one instance runs at a time.- The Home Assistant start trigger restores the correct state after a restart.
2. Apple TV progress bar while playing
This automation creates the progress bar effect while media is playing:
- id: 'XXXXXXXXXXXXXXXXXXXX'
alias: Apple TV Progress Bar When Playing
description: Shows progress bar on WLED when Apple TV is playing
trigger:
- platform: state
entity_id: media_player.apple_tv
to: 'playing'
- platform: state
entity_id: media_player.apple_tv
attribute: media_position
- platform: time_pattern
seconds: '/1'
condition:
- condition: state
entity_id: media_player.apple_tv
state: 'playing'
- condition: template
value_template: '{{ state_attr("media_player.apple_tv", "media_duration") is not none and state_attr("media_player.apple_tv", "media_duration") > 0 }}'
action:
- service: rest_command.wled_progress
data:
progress: >
{% set last_position = state_attr('media_player.apple_tv', 'media_position')|float(0) %}
{% set last_updated = state_attr('media_player.apple_tv', 'media_position_updated_at') %}
{% set duration = state_attr('media_player.apple_tv', 'media_duration')|float(1) %}
{% set position = last_position %}
{% if last_updated is not none %}
{% set time_diff = (now().timestamp() - as_timestamp(last_updated))|float(0) %}
{% set position = last_position + time_diff %}
{% if position > duration %}
{% set position = duration %}
{% endif %}
{% endif %}
{% set progress_percent = position / duration if duration > 0 else 0 %}
{% set led_count = 108 %}
{% set progress_leds = (progress_percent * led_count)|int %}
{% set json_data = {
"on": true,
"bri": 255,
"seg": {
"i": [0, progress_leds, "00FFFF", progress_leds, 108, "FF0080"]
}
} %}
{{ json_data|tojson }}
mode: restart
max_exceeded: silent
Key details:
- It updates every second and whenever the media position changes.
- It calculates the current playback position, including time elapsed since the last update.
- It converts the percentage played into an LED position.
- Cyan (
00FFFF) represents the completed portion; pink (FF0080) represents the remainder. - It uses WLED's segment array notation to create the split-color effect.
Future enhancement ideas
- Add color customization through Home Assistant
input_selectentities. - Control brightness based on time of day or ambient light sensors.
- Add visual effects for specific media types, such as movies or music.
Closing
I hope you find this useful. Please share your adaptations, and feel free to ask questions.