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 initial_value: '{"all", "one", "off"}' - id: repeat_icons type: std::vector 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