Awesome Spotify Touch Control via ILI9341-Screen

Hey guys,

I’m happy to share one of my highlight ESPHome projects:
A Spotify player control via touch screen using an ESP32 and ILI9341

The screen has the following features:

  • Display current song/artist/album
  • Song progress bar
  • Play/pause
  • Next track
  • Volume Control
  • Shuffle on/off
  • Playlist selection (by touching the spotify icon)

Here are some screenshots:

Volume Control:

Playlist control (press Spotify button)

Things you need to create first in HA:

Add the following spotify toggles (helpers) to your HA config:

  • input_boolean.esp32_spotify_playlist_menu (controls the playlist menu)
  • input_boolean.esp32_wetter_screen (controls the empty start control screen. Fill it with your dashboard. :slight_smile: )
  • input_boolean.esp32_spotify_volume (controls the volume screen)

Add the following spotify sensors to your HA config:

  • volume_level
  • media_duration
  • media_position
  • media_title
  • media_artist
  • media_album_name
  • media_playlist

Finally add a binary_sensor for the shuffle on/off.

Create scripts for “start spotify”, “play/pause”, each playlist and volume control up and down.
Start Spotify:

alias: Esp32-Spotify Open / Start
sequence:
  - service: input_boolean.turn_off
    data: {}
    target:
      entity_id:
        - input_boolean.esp32_wetter_screen
  - service: media_player.select_source
    data:
      source: HomeAssistant
    target:
      entity_id: media_player.spotify_xxx
  - delay:
      hours: 0
      minutes: 0
      seconds: 0
      milliseconds: 500
  - choose:
      - conditions:
          - condition: not
            conditions:
              - condition: state
                entity_id: media_player.spotify_xxx
                state: playing
        sequence:
          - service: media_player.volume_set
            data:
              volume_level: 0.5
            target:
              entity_id: media_player.spotify_xxx
    default: []
  - service: media_player.media_play
    data: {}
    target:
      entity_id: media_player.spotify_xxx
  - service: homeassistant.update_entity
    data: {}
    target:
      entity_id: media_player.spotify_xxx
  - delay:
      hours: 0
      minutes: 0
      seconds: 0
      milliseconds: 500
  - service: homeassistant.update_entity
    data: {}
    target:
      entity_id: media_player.spotify_xxx
mode: restart
icon: mdi:spotify

Here’s an example for playlists:

alias: Esp32-Spotify-Playlist-B4
sequence:
  - service: media_player.play_media
    target:
      entity_id: media_player.spotify_xxx
    data:
      media_content_id: spotify:playlist:37i9dQZF1DX4jP4eebSWR9
      media_content_type: spotify://playlist
    metadata:
      title: Hot Hits Deutschland
      thumbnail: https://i.scdn.co/image/ab67706f000000032675fbb1d358045ce76f422a
      media_class: playlist
      children_media_class: track
      navigateIds:
        - {}
        - media_content_type: spotify://categories
          media_content_id: categories
        - media_content_type: spotify://category_playlists
          media_content_id: toplists
  - service: input_boolean.toggle
    data: {}
    target:
      entity_id: input_boolean.esp32_spotify_playlist_menu
mode: single
icon: mdi:playlist-play

Example script for volume control:

alias: Esp32-Spotify Volume up
sequence:
  - service: input_boolean.turn_on
    data: {}
    target:
      entity_id: input_boolean.esp32_spotify_volume
  - service: media_player.volume_up
    data: {}
    target:
      entity_id:
        - media_player.spotify_xxx
  - delay:
      hours: 0
      minutes: 0
      seconds: 5
      milliseconds: 0
  - service: input_boolean.turn_off
    data: {}
    target:
      entity_id: input_boolean.esp32_spotify_volume
mode: restart
icon: mdi:spotify

Automations:

  • toggle off the playlist screen after 7 seconds
  • IMPORTANT: toogle off “wetter screen” (start screen), when spotify is playing
  • toogle on “wetter screen”, when spotify is off or paused for longer than 20 seconds

I recommend to create an automation, which is updating Spotify at least every 2 seconds to keep the progress bar updated.

Big thanks for the inspiration goes to @MrChristian
https://community.home-assistant.io/t/az-touch-esp-example/383573/18

logger:

ota:

api:

captive_portal:

time:
    platform: homeassistant
    id: esptime

spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19

font:
  - file: 'fonts/verdana.ttf'
    id: font40
    size: 40
    glyphs: °.0123456789-%d
  - file: 'fonts/verdana.ttf'
    id: font_spot_time
    size: 12
    glyphs: :0123456789
  - file: 'fonts/verdana.ttf'
    id: font21
    size: 21
    glyphs: ['&', '@', '<', '>', '$', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
       '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
       'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
       'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
       'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/', '\', '\xab', '\xc3', '\xaf', '''', 'ß' ]
  - file: 'fonts/verdana.ttf'
    id: font18
    size: 18   
    glyphs: ['&', '@', '<', '>', '$', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
       '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
       'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
       'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
       'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/', '\', '''', 'ß' ]
  - file: 'fonts/verdana.ttf'
    id: font16
    size: 16
    glyphs: ['&', '@', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
       '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
       'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
       'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
       'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/', '\', '''', 'ß' ]
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_icon_spotify
    size: 75
    glyphs:
      - "\U000F040C" # play-circle
      - "\U000F03E5" # pause-circle
      - "\U000F0661" # skip-next-circle
      - "\U000F0376" # minus-circle
      - "\U000F0417" # plus-circle
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_icon_spotify_big
    size: 80
    glyphs:
      - "\U000F04C7" # Spotify Icon
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_icon_spotify_infobar
    size: 30
    glyphs:
      - "\U000F04C7" # Spotify Icon 
      - "\U000F0595" # Wetter Icon 
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_icon_spotify_infobar_s
    size: 24
    glyphs:
      - "\U000F049D" # Shuffle on Icon 
      - "\U000F049E" # Shuffle off Icon 

color:
  - id: my_red
    red: 100%
    green: 0%
  - id: my_green
    red:   0%
    green: 100%
    blue:  30%
  - id: my_white
    red:   1.0000
    green: 1.0000
    blue:  1.0000
  - id: my_grey
    red:   0.6000
    green: 0.6000
    blue:  0.6000
  - id: my_black
    red:   0.0000
    green: 0.0000
    blue:  0.0000
output:
  # backlight
  - platform: ledc
    pin: 15
    id: gpio_15_backlight_pwm_touch
    inverted: false

light:
  - platform: monochromatic
    output: gpio_15_backlight_pwm_touch
    name: "ILI9341 Display Backlight Touch"
    id: back_light_touch
    restore_mode: RESTORE_DEFAULT_ON

text_sensor:
  - platform: homeassistant
    id: current_title
    entity_id: sensor.esp32_media_player_current_title
  - platform: homeassistant
    id: current_artist
    entity_id: sensor.esp32_media_player_current_artist
  - platform: homeassistant
    id: current_playlist
    entity_id: sensor.esp32_media_player_current_playlist
  - platform: homeassistant
    id: spotify
    entity_id: sensor.esp32_media_player_status
  - platform: homeassistant
    id: spotpostime
    entity_id: sensor.esp32_spotify_position_time
  - platform: homeassistant
    id: spotdur
    entity_id: sensor.esp32_spotify_duration
sensor:
  - platform: homeassistant
    id: spotvol
    entity_id: sensor.esp32_spotify_volume
    filters:
    - multiply: 10
  - platform: homeassistant
    id: spotpos
    entity_id: sensor.esp32_spotify_position
  - platform: homeassistant
    id: spotpos2
    entity_id: sensor.esp32_spotify_position
    filters:
    - offset: 78
binary_sensor:
  - platform: homeassistant
    entity_id: input_boolean.esp32_wetter_screen
    id: display_wetter
  - platform: homeassistant
    entity_id: input_boolean.esp32_spotify_volume
    id: display_volume
  - platform: homeassistant
    entity_id: binary_sensor.esp32_spotify_shuffle
    id: spotify_shuffle
  - platform: homeassistant
    entity_id: input_boolean.esp32_spotify_playlist_menu
    id: playlist
  - platform: xpt2046
    xpt2046_id: touchscreen                                             # Start Play/Pause
    id: touch_play
    x_min: 35
    x_max: 110
    y_min: 2
    y_max: 77
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state == "playing";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
            - and:
              - lambda: 'return id(spotify).state == "paused";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
        then:
        - homeassistant.service:
            service: script.esp32_spotify_play_pause
  - platform: xpt2046
    xpt2046_id: touchscreen                                             # Next Track
    id: touch_next
    x_min: 35
    x_max: 110
    y_min: 78
    y_max: 154
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state == "playing";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
            - and:
              - lambda: 'return id(spotify).state == "paused";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
        then:
        - homeassistant.service:
            service: media_player.media_next_track
            data:
              entity_id: media_player.spotify_xxx
  - platform: xpt2046
    xpt2046_id: touchscreen                                             # Volume - 
    id: touch_minus
    x_min: 35
    x_max: 110
    y_min: 155
    y_max: 230
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state == "playing";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
            - and:
              - lambda: 'return id(spotify).state == "paused";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
        then:
        - homeassistant.service:
            service: script.esp32_spotify_volume_down
  - platform: xpt2046
    xpt2046_id: touchscreen                                             # Volume +
    id: touch_plus
    x_min: 35
    x_max: 110
    y_min: 232
    y_max: 295
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state == "playing";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
            - and:
              - lambda: 'return id(spotify).state == "paused";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
        then:
        - homeassistant.service:
            service: script.esp32_spotify_volume_up
  - platform: xpt2046
    xpt2046_id: touchscreen                                             # Start Spotify
    id: touch_info_spot
    x_min: 195
    x_max: 239
    y_min: 275
    y_max: 319
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state != "playing";'
              - lambda: 'return id(spotify).state != "paused";'
            - and:
              - lambda: 'return id(display_wetter).state;'
        then:
        - homeassistant.service:
            service: script.esp32_open_spotify
  - platform: xpt2046
    xpt2046_id: touchscreen                                             # Turn on Display Wetter 
    id: touch_info_wetter
    x_min: 195
    x_max: 239
    y_min: 275
    y_max: 319
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state == "playing";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
            - and:
              - lambda: 'return id(spotify).state == "paused";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
        then:
        - homeassistant.service:
            service: script.esp32_open_display_wetter
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_info_spot_shuffle_off                                      # Touch Shuffle off
    x_min: 205
    x_max: 239
    y_min: 75
    y_max: 120
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state == "playing";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
              - lambda: 'return id(spotify_shuffle).state == false;'
            - and:
              - lambda: 'return id(spotify).state == "paused";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
              - lambda: 'return id(spotify_shuffle).state == false;'
        then:
        - homeassistant.service:
            service: media_player.shuffle_set
            data:
              shuffle: 'true'
              entity_id: media_player.spotify_xxx            
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_info_spot_shuffle_on                                      # Touch Shuffle on
    x_min: 205
    x_max: 239
    y_min: 75
    y_max: 120
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state == "playing";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
              - lambda: 'return id(spotify_shuffle).state;'
            - and:
              - lambda: 'return id(spotify).state == "paused";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
              - lambda: 'return id(spotify_shuffle).state;'
        then:
        - homeassistant.service:
            service: media_player.shuffle_set
            data:
              shuffle: 'false'
              entity_id: media_player.spotify_xxx
#START SPOTIFY PLAYLIST TOUCH
  - platform: xpt2046
    xpt2046_id: touchscreen                                             # Spotify Icon Playlist
    id: touch_playlist
    x_max: 180
    y_min: 3
    x_min: 105
    y_max: 75
    on_press:
      if: 
        condition:
          or:
            - and: 
              - lambda: 'return id(spotify).state == "playing";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
            - and:
              - lambda: 'return id(spotify).state == "paused";'
              - lambda: 'return id(playlist).state;'
              - lambda: 'return id(display_wetter).state == false;'
        then:
        - homeassistant.service:
            service: input_boolean.toggle
            data: 
              entity_id: input_boolean.esp32_spotify_playlist_menu
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_B1
    x_max: 239
    y_min: 200
    x_min: 201
    y_max: 319
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_b1
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_A1
    x_max: 239
    y_min: 75
    x_min: 201
    y_max: 199
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_a1
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_B2
    x_max: 200
    y_min: 200
    x_min: 161
    y_max: 319
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_b2
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_A2
    x_max: 200
    y_min: 75
    x_min: 161
    y_max: 199
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_a2
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_B3
    x_max: 160
    y_min: 200
    x_min: 121
    y_max: 319
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_b3
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_A3
    x_max: 160
    y_min: 75
    x_min: 121
    y_max: 199
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_a3
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_B4
    x_max: 120
    y_min: 200
    x_min: 81
    y_max: 319
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_b4
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_A4
    x_max: 120
    y_min: 75
    x_min: 81
    y_max: 199
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_a4
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_B5
    x_max: 80
    y_min: 200
    x_min: 41
    y_max: 319
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_b5
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_A5
    x_max: 80
    y_min: 75
    x_min: 41
    y_max: 199
    on_press:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_a5
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_B6
    x_max: 40
    y_min: 200
    x_min: 2
    y_max: 319
    on_state:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_b6
  - platform: xpt2046
    xpt2046_id: touchscreen
    id: touch_A6
    x_max: 40
    y_min: 75
    x_min: 2
    y_max: 199
    on_state:
      if: 
        condition:
          binary_sensor.is_off: playlist
        then:
        - homeassistant.service:
            service: script.esp32_spotify_playlist_a6
xpt2046:
  id: touchscreen
  cs_pin: 14
  irq_pin: 27
  update_interval: 50ms
  report_interval: 1s
  threshold: 400
  dimension_x: 240
  dimension_y: 320
  calibration_x_min: 3860
  calibration_x_max: 280
  calibration_y_min: 340
  calibration_y_max: 3860
  swap_x_y: false

display:
  - platform: ili9341
    model: TFT 2.4
    id: touch_display
    cs_pin: GPIO5
    dc_pin: GPIO4
    reset_pin: GPIO22
    rotation: 270
    lambda: |-
      auto black = Color(0, 0, 0);
      it.fill(black);
      // WENN SPOTIFY SPIELT BUTTONS
      if ((id(spotify).state == "playing" or id(spotify).state == "paused") and id(display_wetter).state == false) {
          if (id(spotify).state == "playing") 
          { it.print(0, 155, id(font_icon_spotify), id(my_green), TextAlign::TOP_LEFT, "\U000F03E5"); }            // Pause Icon
          else 
          { it.print(0, 155, id(font_icon_spotify), id(my_white), TextAlign::TOP_LEFT, "\U000F040C"); }            // Play Icon
      it.print(-5, 43, id(font_icon_spotify_big), id(my_green), TextAlign::TOP_LEFT, "\U000F04C7");                // Spotify Icon gross       
      // WENN SPOTIFY SPIELT UND PLAYLIST GESCHLOSSEN
         if (id(playlist).state) {
             if (id(spotify_shuffle).state) { 
                it.print(98, 3, id(font_icon_spotify_infobar_s), id(my_green), TextAlign::TOP_RIGHT, "\U000F049D");  // Shuffle on
             } 
             else {
                it.print(98, 3, id(font_icon_spotify_infobar_s), id(my_white), TextAlign::TOP_RIGHT, "\U000F049E");  // Shuffle off
             }  
      it.strftime(4, 0, id(font21), id(my_white), TextAlign::TOP_LEFT, "%H:%M", id(esptime).now());
      it.filled_circle(319, 0, 40, id(my_white));
      it.print(319, 1, id(font_icon_spotify_infobar), id(my_black), TextAlign::TOP_RIGHT, "\U000F0595");            // Wetter Display
      it.print(78, 66, id(font21), id(my_white), TextAlign::TOP_LEFT, id(current_title).state.c_str());             // Track Infos
      it.print(78, 91, id(font21), id(my_white), TextAlign::TOP_LEFT, id(current_artist).state.c_str());
      it.print(78, 46, id(font18), id(my_green), TextAlign::TOP_LEFT, id(current_playlist).state.c_str());
      // it.printf(78, 35, id(font18), id(my_green), TextAlign::TOP_LEFT, "%.0f", id(spotpos).state);
      
      it.filled_rectangle(78, 130, 222, 2, id(my_grey));                                                             // Progress Back
      it.filled_rectangle(78, 130, id(spotpos).state, 2, id(my_green));                                              // Progress Bar
      it.filled_circle(id(spotpos2).state, 130, 4, id(my_green));                                                    // Progress Circle
      it.print(78, 133, id(font_spot_time), id(my_green), TextAlign::TOP_LEFT, id(spotpostime).state.c_str());       // Position Song 
      it.print(300, 133, id(font_spot_time), id(my_green), TextAlign::TOP_RIGHT, id(spotdur).state.c_str());         // Duration Song
      
      it.print(78, 155, id(font_icon_spotify), id(my_white), TextAlign::TOP_LEFT, "\U000F0661");                    // Next Track   
      it.print(155, 155, id(font_icon_spotify), id(my_white), TextAlign::TOP_LEFT, "\U000F0376");                   // Volume-
      it.print(232, 155, id(font_icon_spotify), id(my_white), TextAlign::TOP_LEFT, "\U000F0417");                   // Volume+
          if (id(display_volume).state) {                                                                           // Volume State
          it.filled_circle(230, 122, 35, id(my_green));
          it.printf(230, 145, id(font40), id(my_black), TextAlign::BOTTOM_CENTER, "%.0f", id(spotvol).state);
          }
       } 
      // WENN SPOTIFY SPIELT UND PLAYLIST OFFEN
      else if ((id(spotify).state == "playing" or id(spotify).state == "paused") and id(playlist).state == false) {
      it.line(76, 0, 76, 240);
      it.line(200, 0, 200, 240);
      it.line(319, 0, 319, 240);
      it.line(76, 0, 319, 0);
      it.line(76, 40, 319, 40);
      it.line(76, 80, 319, 80);
      it.line(76, 120, 319, 120);
      it.line(76, 160, 319, 160);
      it.line(76, 200, 319, 200);
      it.line(76, 239, 319, 239);
      it.print(85, 20, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Mix d.Woche");
      it.print(85, 60, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Playlist W");
      it.print(85, 100, id(font18), id(my_white), TextAlign::CENTER_LEFT, "me right no");
      it.print(85, 140, id(font18), id(my_white), TextAlign::CENTER_LEFT, "House Party");
      it.print(85, 180, id(font18), id(my_white), TextAlign::CENTER_LEFT, "News");
      it.print(85, 220, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Playlist Z");
      it.print(209, 20, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Playlist X");
      it.print(209, 60, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Playlist Y");
      it.print(209, 100, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Pool Electro");
      it.print(209, 140, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Hot Hits");
      it.print(209, 180, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Kaffeehaus");
      it.print(209, 220, id(font18), id(my_white), TextAlign::CENTER_LEFT, "Chilled Dan");
      }} 
      else {
      // STATUSLEISTE ZEIT ODER WETTERWARNUNG
      it.strftime(4, 3, id(font21), id(my_white), TextAlign::TOP_LEFT, "%H:%M", id(esptime).now());
      it.filled_circle(319, 0, 40, id(my_white));
      it.print(319, 1, id(font_icon_spotify_infobar), id(my_black), TextAlign::TOP_RIGHT, "\U000F04C7");            // Spotify Icon Infobar 
      }
5 Likes

Very nice project. Can you share the wiring diagram? how to connect esp32 to ILI9341?
How many components do I need?

Thank you. You just need the esp32 and the ILI9341. No additional components needed.

Regarding the wiring you can follow @MrChristian’s post:

It worked well for me after I found out, that my first display had no touch. :wink:

Be aware, that SCK/T_CLK, MOSI/T_DIN and MISO/T_DO are shared pins. It took me a while to find out.

1 Like

2 Likes

Hello @brix29

Nice work! thanks for sharing

How do you select the media_player to play the music on? dont understand. Thanks

Hi @Olivier974,

I use scripts to run the media player. This makes it easy to maintain in case you like to change the playlist for example:

script.esp32_spotify_playlist_a4

The scripts can be found next to automations:

alias: Esp32-Spotify-Playlist-A4
sequence:
  - service: media_player.play_media
    target:
      entity_id: media_player.spotify_lt12b
    data:
      media_content_id: spotify:playlist:5OhjR3QIvNYUdsQVuJKWmu
      media_content_type: spotify://playlist
    metadata:
      title: >-
        House Party 2022 || Dance Music | EDM Hits | Club Beats | Deep | Slap |
        Rave | #electronicmusic
      thumbnail: https://i.scdn.co/image/ab67706c0000bebbe3b5369febeaf8313b2a18c4
      media_class: playlist
      children_media_class: track
      navigateIds:
        - {}
        - media_content_type: spotify://current_user_playlists
          media_content_id: current_user_playlists
  - service: input_boolean.toggle
    data: {}
    target:
      entity_id: input_boolean.esp32_spotify_playlist_menu
mode: single
icon: mdi:playlist-play

1 Like

hello @brix29

i try to integrate your work…seems at least 1 script is missing :slight_smile:

what is “script.esp32_open_display_wetter”

i have only a black screen with hour and spotify logo…

1- can you share this script please?

2- what do you mean please with “input_boolean.esp32_wetter_screen (controls the empty start control screen. Fill it with your dashboard. :slight_smile: )”

///////////////////////////////////////////////////////////////////////////////

for those are interested with your work, you need:

in sensor.yaml

esp32_media_player_current_title:
      value_template: "{{ state_attr('media_player.spotify_xxx','media_title') }}"
    esp32_media_player_current_artist:
      value_template: "{{ state_attr('media_player.spotify_xxx','media_artist') }}"
    esp32_media_player_current_playlist:
      value_template: "{{ state_attr('media_player.spotify_xxx','media_playlist') }}"
    esp32_media_player_status:
      value_template: "{{ state_attr('media_player.spotify_xxx','status') }}"
    esp32_spotify_position_time:
      value_template: "{{ state_attr('media_player.spotify_xxx','media_position_time') }}"
    esp32_spotify_duration:
      value_template: "{{ state_attr('media_player.spotify_xxx','media_duration') }}"
    esp32_media_player_playlist:
      value_template: "{{ state_attr('media_player.spotify_xxx','media_playlist') }}"
    esp32_spotify_volume:
      value_template: "{{ state_attr('media_player.spotify_xxx','volume_level') }}"
    esp32_spotify_position:
      value_template: "{{ state_attr('media_player.spotify_xxx','media_position') }}"

in binary_sensors.yaml:

    esp32_spotify_shuffle:
      value_template: "{{ state_attr('media_player.spotify_xxx','shuffle') }}"

in script.yaml :

start_spotify:
  alias: Esp32-Spotify Open / Start
  sequence:
    - service: input_boolean.turn_off
      data: {}
      target:
        entity_id:
          - input_boolean.esp32_wetter_screen
    - service: media_player.select_source
      data:
        source: HomeAssistant
      target:
        entity_id: media_player.spotify_xxx
    - delay:
        hours: 0
        minutes: 0
        seconds: 0
        milliseconds: 500
    - choose:
        - conditions:
            - condition: not
              conditions:
                - condition: state
                  entity_id: media_player.spotify_xxx
                  state: playing
          sequence:
            - service: media_player.volume_set
              data:
                volume_level: 0.5
              target:
                entity_id: media_player.spotify_xxx
      default: []
    - service: media_player.media_play
      data: {}
      target:
        entity_id: media_player.spotify_xxx
    - service: homeassistant.update_entity
      data: {}
      target:
        entity_id: media_player.spotify_xxx
    - delay:
        hours: 0
        minutes: 0
        seconds: 0
        milliseconds: 500
    - service: homeassistant.update_entity
      data: {}
      target:
        entity_id: media_player.spotify_xxx
  mode: restart
  icon: mdi:spotify

######### Spotify Playlist ############
esp32_spotify_playlist_a4:
  alias: Esp32-Spotify-Playlist-A4
  sequence:
    - service: media_player.play_media
      target:
        entity_id: media_player.spotify_lt12b
      data:
        media_content_id: spotify:playlist:5OhjR3QIvNYUdsQVuJKWmu
        media_content_type: spotify://playlist
      metadata:
        title: >-
          House Party 2022 || Dance Music | EDM Hits | Club Beats | Deep | Slap |
          Rave | #electronicmusic
        thumbnail: https://i.scdn.co/image/ab67706c0000bebbe3b5369febeaf8313b2a18c4
        media_class: playlist
        children_media_class: track
        navigateIds:
          - {}
          - media_content_type: spotify://current_user_playlists
            media_content_id: current_user_playlists
    - service: input_boolean.toggle
      data: {}
      target:
        entity_id: input_boolean.esp32_spotify_playlist_menu
  mode: single

######### Spotify volume ############
esp32_spotify_volume_up:
  alias: Esp32-Spotify Volume up
  sequence:
    - service: input_boolean.turn_on
      data: {}
      target:
        entity_id: input_boolean.esp32_spotify_volume
    - service: media_player.volume_up
      data: {}
      target:
        entity_id:
          - media_player.spotify_xxxx
    - delay:
        hours: 0
        minutes: 0
        seconds: 5
        milliseconds: 0
    - service: input_boolean.turn_off
      data: {}
      target:
        entity_id: input_boolean.esp32_spotify_volume
  mode: restart
  icon: mdi:spotify

esp32_spotify_volume_down:
  alias: Esp32-Spotify Volume down
  sequence:
    - service: input_boolean.turn_on
      data: {}
      target:
        entity_id: input_boolean.esp32_spotify_volume
    - service: media_player.volume_down
      data: {}
      target:
        entity_id:
          - media_player.spotify_xxx
    - delay:
        hours: 0
        minutes: 0
        seconds: 5
        milliseconds: 0
    - service: input_boolean.turn_off
      data: {}
      target:
        entity_id: input_boolean.esp32_spotify_volume
  mode: restart
  icon: mdi:spotify

i can share my work too :slight_smile:

the most interesting part is to control each of my 5 cover position with a return of the value of the real position and a the picto update too :

its just white bar between 0 to 100%, by step of 15%, but you can open with the arrow up and close with the arrow down.

the font image is fixed and make with photoshop.

If someone want the code, i can post it with 3d print files.

And the code is up to date because the xpt4086 has changed in ESPHome.

The case contained 2 ESP32 and 2 ILI 2.4" and a 1500mA Lipo Battery with usb charging port. The battery last between 3 and 4 hours at full backlight, and there is a switch to Poweroff. Poweron takes 20 seconds to be operationnal. (5 seconds if no API encryption).

Each time you press on a name of light, powerplug or any item, it display in green to be sure you have press right and indicate the state on, off is white.

i have more than 1500 line of code to control all my light (x16), power plug (x10), cover (x5)

and with your “Music” part i have 1736 lines of code and 89% of flash is full :slight_smile: (on the down ESP32, the up is 84% full).

You have left arrow and right arrow to navigate into menus, 10 pages for the moment.

1 Like

Hi Olivier,

you are right. Sorry, I missed some code.

This is my code for the sensors (In the meanwhile I added the VLC Player for webradio in some sensors. You can remove it of course.):

      esp32_media_player_current_title:
        friendly_name: "ESP32-Touch Media Player Current Title"
        value_template: >
          {% if states.media_player.vlc_telnet.state == "playing" %}
            {{ state_attr('media_player.vlc_telnet', 'media_title') }}
          {% else %}
            {{ state_attr('media_player.spotify_xxx', 'media_title') }}
          {% endif %}
      esp32_media_player_current_playlist:
        friendly_name: "ESP32-Touch Media Player Current Playlist"
        value_template: >
          {% if states.media_player.vlc_telnet.state == "playing" %}
            Radio playing
          {% else %}
            {{ state_attr('media_player.spotify_xxx', 'media_playlist') }}
          {% endif %}
      esp32_media_player_current_artist:
        friendly_name: "ESP32-Touch Media Player Current Artist"
        value_template: >
          {% if states.media_player.vlc_telnet.state == "playing" %}
          {% else %}
            {{ state_attr('media_player.spotify_xxx', 'media_artist') }}
          {% endif %}
      esp32_media_player_status:
        friendly_name: "ESP32-Touch Media Player Status"
        value_template: >
          {% if states.media_player.spotify_xxx.state == "playing" or states.media_player.vlc_telnet.state == "playing" %}
            playing 
          {% elif states.media_player.spotify_xxx.state == "paused" or states.media_player.vlc_telnet.state == "paused"  %}
            paused
          {% else %}
            off
          {% endif %}
      esp32_spotify_volume:
        friendly_name: "ESP32-Touch Spotify Volume"
        value_template: "{{state_attr('media_player.spotify_xxx', 'volume_level') }}"
      esp32_spotify_position:
        friendly_name: "ESP32-Touch Spotify Position"
        value_template: "{{ ((state_attr('media_player.spotify_xxx', 'media_position') | int) / (state_attr('media_player.spotify_xxx', 'media_duration') | int) * 222 | round(0) ) }}"
      esp32_spotify_position_time:
        friendly_name: "ESP32-Touch Spotify Position Time"
        value_template: "{{ (state_attr('media_player.spotify_xxx', 'media_position') | float | timestamp_custom('%-M:%S')) }}"
      esp32_spotify_duration:
        friendly_name: "ESP32-Touch Spotify Duration"
        value_template: "{{ (state_attr('media_player.spotify_xxx', 'media_duration') | float | timestamp_custom('%-M:%S')) }}"

Maybe the time format was not ok in your version…

binary sensors:

      esp32_spotify_shuffle:
        friendly_name: "ESP32-Touch Spotify Shuffle Status"
        value_template: "{{ state_attr('media_player.spotify_xxx', 'shuffle') }}"

Regarding 2: Missing scripts

esp32_open_spotify:
  alias: Esp32-Spotify Open / Start
  sequence:
  - service: input_boolean.turn_off
    data: {}
    target:
      entity_id:
      - input_boolean.esp32_wetter_screen
esp32_open_display_wetter:
  alias: Esp32-Open Display Wetter
  sequence:
  - service: input_boolean.turn_on
    data: {}
    target:
      entity_id: input_boolean.esp32_wetter_screen
  - delay:
      hours: 0
      minutes: 0
      seconds: 15
      milliseconds: 0
  - service: input_boolean.turn_off
    data: {}
    target:
      entity_id: input_boolean.esp32_wetter_screen
  mode: single
  icon: mdi:weather-partly-cloudy

Regarding 2:

I have 2 screens:

  1. Wetter-Screen: (this screen is empty in this posted code)
  • always active
  • showing some sensor data like weather, temperature,
  • has a spotify icon on the top right. touch it to start spotify
    grafik
  1. Spotify Screen:
  • only active, if spotify is playing (or VLC player)
  • icon on the top right: touch switches to the “Wetter Screen” for some seconds

I hope this helps a bit… pls let me know, if you need anything else.

Best Axel

1 Like

Oh wow. Very nice work. I love the cover functionality. Unfortunately, I don’t have any shades or covers. :slight_smile:
I’m also curious how your “magic mirror” looks like.

How did you manage to get 1500 lines? My ESP32 stops working after 1000 lines. The flash is not full, but it fails booting. What’s your trick?

Best,
Axel

Thanks a lot for all the informations, explanations and codes! sure it will help!
i will try tomorow! and let you know if it works :wink:

i have 2 magic mirrors lol, one is pretty big, both have a amplifier sound with speakers stereo and a usb mic on a RPI4 4G, and it works well! i ll give you some photos tomorow if you are curious how it look like! :wink:

i doesnt do anything special lol…its an ESP-WROOM32 and never add issue even if the flash is almost full…sorry to not give some helpfull tips…

see you

1 Like

hello @brix29

seems missing “script.esp32_spotify_play_pause” and “spotpos” :

'spotpos': Sending state nan  with 1 decimals of accuracy

and in the first post you give the script of “start_spotify” but i see nowhere this script used in your code.

Little better i have now the screen with play the song :wink: but still missed something.

here is the “big” magic Mirror (1000mm high and 600mm wide) :wink:

1 Like

Strange. Can you pls check, if these two sensors are working fine? (Spotify has to play a song)

“spotpos” and “spotpos2” is sending the position and creates the progressbar (“green line with the green dot”):

  - platform: homeassistant
    id: spotpos
    entity_id: sensor.esp32_spotify_position
  - platform: homeassistant
    id: spotpos2
    entity_id: sensor.esp32_spotify_position
    filters:
    - offset: 78

Thank you! Impressive work! :star_struck: Can you even talk with it? “Mirror, mirror on the wall, who is the fairest one of all?” :slight_smile:

thanks! yu’re right! problem with “sensor.esp32_spotify_position_time” …my bad… lol. its ok.

but what about the “start_spotify” script, cant find anywhere in yur code : this one :

start_spotify:
  alias: Esp32-Spotify Open / Start
  sequence:
    - service: input_boolean.turn_off
      data: {}
      target:
        entity_id:
          - input_boolean.esp32_wetter_screen
    - service: media_player.select_source
      data:
        source: HomeAssistant
      target:
        entity_id: media_player.spotify_xxx
    - delay:
        hours: 0
        minutes: 0
        seconds: 0
        milliseconds: 500
    - choose:
        - conditions:
            - condition: not
              conditions:
                - condition: state
                  entity_id: media_player.spotify_xxx
                  state: playing
          sequence:
            - service: media_player.volume_set
              data:
                volume_level: 0.5
              target:
                entity_id: media_player.spotify_xxx
      default: []
    - service: media_player.media_play
      data: {}
      target:
        entity_id: media_player.spotify_xxx
    - service: homeassistant.update_entity
      data: {}
      target:
        entity_id: media_player.spotify_1182670249
    - delay:
        hours: 0
        minutes: 0
        seconds: 0
        milliseconds: 500
    - service: homeassistant.update_entity
      data: {}
      target:
        entity_id: media_player.spotify_xxx
  mode: restart
  icon: mdi:spotify

About the MagicMirror yes i can tell “ok google” and it act like a google home : turn_on/off light, power plug, covers…ect :wink:

last question : i think i understand why nether “-” “+” “play/pause” doesnt work…i have a free version of spotify…i think you have a premium account? right? thats why i cant control anything instead playlist perhaps…

EDIT : yes, confirmed : “media_player.volume_down : Error handling message: Entity media_player.spotify_xxx does not support this service.”

same for all functions…play/pause/volume…so bad :frowning:

The Start Spotify Script ist the “esp32_open_spotify”. It’s started by pressing the spotify icon on the top right.

BTW. Maybe intersting for you. Here are the automations, I use:

	- id: '1647473667059'
	  alias: Esp32-Touch-Spotify Playlist Menü schließen
	  description: ''
	  trigger:
	  - platform: state
		entity_id: input_boolean.esp32_spotify_playlist_menu
		to: 'off'
		for:
		  hours: 0
		  minutes: 0
		  seconds: 8
	  condition: []
	  action:
	  - service: input_boolean.turn_on
		data: {}
		target:
		  entity_id: input_boolean.esp32_spotify_playlist_menu
	  mode: single
	- id: '1647716520887'
	  alias: Esp32-Spotify Update Last Update Sensor
	  description: ''
	  trigger:
	  - platform: time_pattern
		seconds: /1
	  condition:
	  - condition: state
		entity_id: media_player.spotify_xxx
		state: playing
	  action:
	  - service: homeassistant.update_entity
		data: {}
		target:
		  entity_id: media_player.spotify_xxx
	  mode: single
	- id: '1647716767391'
	  alias: Esp32-Spotify Update Last Update Sensor (On)
	  description: ''
	  trigger:
	  - platform: state
		to: playing
		entity_id: media_player.spotify_xxx
	  condition: []
	  action:
	  - service: automation.turn_on
		data: {}
		target:
		  entity_id: automation.esp32_spotify_update_last_update_sensor
	  mode: single
	- id: '1647716865819'
	  alias: Esp32-Spotify Update Last Update Sensor (Off)
	  description: ''
	  trigger:
	  - platform: state
		entity_id: media_player.spotify_xxx
		from: playing
	  condition: []
	  action:
	  - service: automation.turn_off
		data: {}
		target:
		  entity_id: automation.esp32_spotify_update_last_update_sensor
	  mode: single
	- id: '1647797376061'
	  alias: Esp32-Spotify-Garten und Wetter Menü öffnen, wenn Spotify pausiert
	  description: ''
	  trigger:
	  - platform: state
		entity_id: sensor.esp32_media_player_status
		to: paused
		for:
		  hours: 0
		  minutes: 0
		  seconds: 25
	  condition: []
	  action:
	  - service: input_boolean.turn_on
		data: {}
		target:
		  entity_id:
		  - input_boolean.esp32_wetter_screen
	  mode: single
	- id: '1647813090667'
	  alias: Esp32-Spotify-Garten und Wetter  Menü schliessen, wenn Spotify startet
	  description: ''
	  trigger:
	  - platform: state
		entity_id: sensor.esp32_media_player_status
		to: playing
		for:
		  hours: 0
		  minutes: 0
		  seconds: 0
	  condition:
	  - condition: state
		entity_id: media_player.spotify_xxx
		attribute: source
		state: HomeAssistant
	  action:
	  - service: input_boolean.turn_off
		data: {}
		target:
		  entity_id:
		  - input_boolean.esp32_wetter_screen
	  mode: single

Ahhh… haven’t you replaced the xxx with your Spotify Player? The xxx stands for “your spotify player”. :wink:

1 Like

yes lol

i missed the automation, my bad…thanks!!!

have you a premium account?

because i have not and it doesnt work without

:laughing:
Yes, I do. I don’t know, if the Spotify integration is working without premium.

1 Like

yes…confirmed…premium is needed for play/pause, next, previous, start…options from “service”…

superb! how do long song/album titles look like?