Files
hassos_config/esphome/widgets/media_player/media_player.yaml
2026-03-26 12:10:21 +01:00

561 lines
17 KiB
YAML

substitutions:
ha_server: "http://192.168.1.44:8123"
media_player_entity: "media_player.yandex_station_m00bv9j00dc7sg"
power_icon: "\U0000e909"
play_icon: "\U0000e90a"
pause_icon: "\U0000e90b"
back_step_icon: "\U0000e90c"
forward_step_icon: "\U0000e90d"
volume_off_icon: "\U0000e90e"
volume_on_icon: "\U0000e90f"
volume_minus_icon: "\U0000e910"
volume_plus_icon: "\U0000e911"
repeat_all_icon: "\U0000e912"
repeat_off_icon: "\U0000e913"
repeat_one_icon: "\U0000e914"
globals:
- id: repeat_modes
type: std::vector<std::string>
initial_value: '{"all", "one", "off"}'
- id: repeat_icons
type: std::vector<std::string>
initial_value: '{"${repeat_all_icon}", "${repeat_one_icon}", "${repeat_off_icon}"}'
- id: current_repeat_mode_idx
type: int
initial_value: '0'
- id: tmp_repeat_mode
type: std::string
restore_value: no
initial_value: '"all"'
- id: tmp_repeat_icon
type: std::string
restore_value: no
initial_value: '"${repeat_all_icon}"'
- id: cover_url
type: std::string
restore_value: no
initial_value: '""'
- id: duration_slider_user_interaction
type: bool
restore_value: no
initial_value: 'false'
- id: slider_position
type: float
restore_value: no
initial_value: '0.0'
- id: pending_cover_url
type: std::string
restore_value: no
initial_value: '""'
- id: pending_repeat_value
type: std::string
restore_value: no
initial_value: '""'
script:
- id: update_cover_script
mode: restart
then:
- lambda: |-
std::string url = id(pending_cover_url);
id(cover_url) = url;
- homeassistant.action:
action: display_tools.save_media_cover
data:
entity_id: "${media_player_entity}"
size: small
- delay: 1s
- online_image.release: media_image_jpeg
- online_image.set_url:
id: media_image_jpeg
url: "${ha_server}/local/display_tools/cover.jpeg"
- id: update_repeat_mode_script
mode: restart
then:
- lambda: |-
std::string x = id(pending_repeat_value);
auto modes = id(repeat_modes);
auto icons = id(repeat_icons);
for (int i = 0; i < modes.size(); i++) {
if (modes[i] == x) {
id(current_repeat_mode_idx) = i;
id(tmp_repeat_mode) = modes[i];
id(tmp_repeat_icon) = icons[i];
break;
}
}
- lvgl.label.update:
id: media_player_repeat_label
text: !lambda 'return id(tmp_repeat_icon);'
- id: handle_repeat_press_script
mode: restart
then:
- lambda: |-
int idx = id(current_repeat_mode_idx);
auto modes = id(repeat_modes);
auto icons = id(repeat_icons);
idx = (idx + 1) % modes.size();
id(current_repeat_mode_idx) = idx;
std::string new_mode = modes[idx];
std::string new_icon = icons[idx];
id(tmp_repeat_mode) = new_mode;
id(tmp_repeat_icon) = new_icon;
- lvgl.label.update:
id: media_player_repeat_label
text: !lambda 'return id(tmp_repeat_icon);'
- homeassistant.action:
action: media_player.repeat_set
data:
entity_id: "${media_player_entity}"
repeat: !lambda 'return id(tmp_repeat_mode);'
- id: handle_duration_slider_release_script
mode: restart
then:
- homeassistant.service:
service: media_player.media_seek
data:
entity_id: "${media_player_entity}"
seek_position: !lambda 'return std::to_string(int(id(slider_position)));'
- delay: 2s
- lambda: |-
id(duration_slider_user_interaction) = false;
lvgl:
pages:
- id: media_player_page
bg_color: color_slate_blue_gray
widgets:
# Cover
- obj:
id: media_cover
x: 10
y: 10
width: 140
height: 140
align: TOP_LEFT
bg_color: color_steel_blue
bg_image_src: media_image_jpeg
border_color: color_slate_blue_gray
border_width: 10
shadow_opa: TRANSP
radius: 20
# Title/Artist/Duration
- obj:
x: -20
y: 20
width: 300
height: 120
align: TOP_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
border_width: 0
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: media_player_title_label
height: 40
width: 260
long_mode: DOT
y: -25
align: CENTER
text_color: color_misty_blue
text_font: nunito_20
text: "Title"
- label:
id: media_player_artist_label
height: 30
width: 260
long_mode: DOT
y: 3
align: CENTER
text_color: color_misty_blue
text_font: nunito_16
text: "Artist"
- slider:
id: media_player_duration_pos_slider
radius: 2
y: 35
bg_color: color_slate_blue_gray
align: CENTER
width: 260
height: 5
min_value: 0
max_value: 100
indicator:
bg_color: color_misty_blue
radius: 2
knob:
bg_opa: TRANSP
on_press:
- lambda: |-
id(duration_slider_user_interaction) = true;
on_release:
- lambda: |-
id(slider_position) = x;
- script.execute: handle_duration_slider_release_script
# Controls
- obj:
y: 160
width: 440
height: 120
align: TOP_MID
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
# Off
- label:
x: 10
align: LEFT_MID
text_color: color_steel_blue
text_font: icons_38
text: "${power_icon}"
on_press:
- homeassistant.action:
action: media_player.turn_off
data:
entity_id: "${media_player_entity}"
# back
- label:
x: -80
align: CENTER
text_color: color_misty_blue
text_font: icons_38
text: "${back_step_icon}"
on_press:
- homeassistant.action:
action: media_player.media_previous_track
data:
entity_id: "${media_player_entity}"
# play / pause
- obj:
id: media_player_control_play_bg
height: 90
width: 90
bg_color: color_steel_blue
bg_opa: 80%
border_opa: TRANSP
radius: 50
align: CENTER
widgets:
- label:
id: media_player_state_label
align: CENTER
text_color: color_misty_blue
text_font: icons_48
text: "${play_icon}"
on_press:
- if:
condition:
lambda: 'return id(media_player_state).state == "playing";'
then:
- homeassistant.action:
action: media_player.media_pause
data:
entity_id: "${media_player_entity}"
else:
- homeassistant.action:
action: media_player.media_play
data:
entity_id: "${media_player_entity}"
# forward
- label:
x: 80
align: CENTER
text_color: color_misty_blue
text_font: icons_38
text: "${forward_step_icon}"
on_press:
- homeassistant.action:
action: media_player.media_next_track
data:
entity_id: "${media_player_entity}"
# repeat
- label:
id: media_player_repeat_label
x: -10
align: RIGHT_MID
text_color: color_steel_blue
text_font: icons_38
text: "${repeat_all_icon}"
on_press:
- script.execute: handle_repeat_press_script
# Volume
- obj:
y: 300
width: 440
height: 80
align: TOP_MID
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
# mute
- label:
id: media_player_mute_label
x: 10
align: LEFT_MID
text_color: color_steel_blue
text_font: icons_32
text: "${volume_on_icon}"
on_press:
- if:
condition:
lambda: 'return id(media_player_is_volume_muted).state;'
then:
- homeassistant.action:
action: media_player.volume_mute
data:
entity_id: "${media_player_entity}"
is_volume_muted: "false"
else:
- homeassistant.action:
action: media_player.volume_mute
data:
entity_id: "${media_player_entity}"
is_volume_muted: "true"
# vol down
- label:
id: media_player_vol_down_label
x: -280
align: RIGHT_MID
text_color: color_steel_blue
text_font: icons_32
text: "${volume_minus_icon}"
on_press:
- homeassistant.action:
action: media_player.volume_down
data:
entity_id: "${media_player_entity}"
# set vol
- slider:
id: media_player_volume_slider
bg_color: color_slate_blue_gray
bg_opa: 100%
align: RIGHT_MID
x: -60
width: 200
height: 10
min_value: 0
max_value: 100
indicator:
bg_color: color_misty_blue
knob:
bg_color: color_misty_blue
on_release:
- homeassistant.action:
action: media_player.volume_set
data:
entity_id: "${media_player_entity}"
volume_level: !lambda 'return float(x) / 100.0;'
# vol up
- label:
id: media_player_vol_up_label
x: -10
align: RIGHT_MID
text_color: color_steel_blue
text_font: icons_32
text: "${volume_plus_icon}"
on_press:
- homeassistant.action:
action: media_player.volume_up
data:
entity_id: "${media_player_entity}"
# Media player name
- obj:
x: -20
y: 400
width: 360
height: 60
align: TOP_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: media_player_name_label
align: CENTER
long_mode: DOT
text_color: color_misty_blue
text_font: nunito_16
text: "Friendly name"
# Return
- button:
x: 20
y: 400
width: 60
height: 60
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- label:
align: CENTER
text_font: icons_28
text_color: color_misty_blue
text: "${exit_icon}"
on_press:
- lvgl.page.show: devices_page
- lvgl.widget.show: menu_controls_main
text_sensor:
- platform: homeassistant
id: media_player_state
entity_id: "${media_player_entity}"
on_value:
- if:
condition:
lambda: 'return x == "paused";'
then:
- lvgl.label.update:
id: media_player_state_label
x: 0
text: "${play_icon}"
else:
- lvgl.label.update:
id: media_player_state_label
x: 0
text: "${pause_icon}"
- platform: homeassistant
id: media_player_cover
entity_id: "${media_player_entity}"
attribute: entity_picture
on_value:
- lambda: |-
id(pending_cover_url) = x;
- script.execute: update_cover_script
- platform: homeassistant
id: media_player_title_sensor
entity_id: "${media_player_entity}"
attribute: media_title
on_value:
- delay: 1s
- lvgl.label.update:
id: media_player_title_label
text: !lambda return x;
- platform: homeassistant
id: media_player_artist_sensor
entity_id: "${media_player_entity}"
attribute: media_artist
on_value:
- delay: 1s
- lvgl.label.update:
id: media_player_artist_label
text: !lambda return x;
- platform: homeassistant
id: media_player_repeat
entity_id: "${media_player_entity}"
attribute: repeat
on_value:
- lambda: |-
id(pending_repeat_value) = x;
- script.execute: update_repeat_mode_script
- platform: homeassistant
id: media_player_friendly_name
entity_id: "${media_player_entity}"
attribute: friendly_name
on_value:
- lvgl.label.update:
id: media_player_name_label
text: !lambda return x;
sensor:
- platform: homeassistant
id: media_player_volume_level
entity_id: "${media_player_entity}"
attribute: volume_level
on_value:
- lvgl.slider.update:
id: media_player_volume_slider
value: !lambda 'return int(x * 100);'
- platform: homeassistant
id: media_player_media_duration
entity_id: "${media_player_entity}"
attribute: media_duration
on_value:
- lambda: |-
lv_slider_set_range(id(media_player_duration_pos_slider), 0, x);
- platform: homeassistant
id: media_player_media_position
entity_id: "${media_player_entity}"
attribute: media_position
on_value:
- if:
condition:
lambda: 'return !id(duration_slider_user_interaction);'
then:
- lvgl.slider.update:
id: media_player_duration_pos_slider
value: !lambda return x;
binary_sensor:
- platform: homeassistant
id: media_player_is_volume_muted
entity_id: "${media_player_entity}"
attribute: is_volume_muted
on_state:
- if:
condition:
lambda: 'return x;'
then:
- lvgl.label.update:
id: media_player_mute_label
text: "${volume_off_icon}"
else:
- lvgl.label.update:
id: media_player_mute_label
text: "${volume_on_icon}"
online_image:
- url: "https://www.example.com/example.jpeg"
format: JPEG
type: RGB565
resize: 120x120
id: media_image_jpeg
on_download_finished:
- lvgl.obj.update:
id: media_cover
bg_image_src: media_image_jpeg