Last active
February 6, 2026 22:13
-
-
Save widget/32588e0da3c24db6a3442a6b931e7402 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Enables display with a few pages | |
| # starting - shows friendly_name | |
| # clock - shows time | |
| # off - blank | |
| # play - scrolls title and artist | |
| # | |
| # Something in the display lambda is very expensive and really slows down | |
| # the main loop. To combat that, this runs the CPU faster | |
| defaults: | |
| font_small: 14 | |
| api: | |
| on_client_connected: | |
| then: | |
| - display.page.show: page_clock | |
| - component.update: spi_display | |
| esp32: | |
| # override CPU freq | |
| cpu_frequency: 240MHz | |
| logger: | |
| logs: | |
| # don't log missing glyph errors | |
| font: ERROR | |
| spi: | |
| clk_pin: GPIO18 | |
| mosi_pin: GPIO23 | |
| miso_pin: GPIO19 | |
| id: spi_bus_display | |
| time: | |
| - platform: homeassistant | |
| id: esptime | |
| font: | |
| - file: 'gfonts://Material+Symbols+Outlined' | |
| id: icons_small | |
| size: ${font_small} | |
| glyphs: | |
| - "\ue019" # album | |
| - "\ue01a" # artist | |
| - "\U000fffd8" # music notes | |
| - file: | |
| type: gfonts | |
| family: Ubuntu | |
| weight: regular | |
| id: text_small | |
| size: ${font_small} | |
| glyphsets: | |
| - GF_Latin_Core # European alphabets too | |
| # For the clock page | |
| - file: | |
| type: gfonts | |
| family: Roboto | |
| weight: regular | |
| id: text_big | |
| size: 40 | |
| glyphs: [' ', ':', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] | |
| text_sensor: | |
| # These don't have names, they don't need sharing back to HA (MA knows) | |
| - platform: sendspin | |
| type: title | |
| id: ss_title | |
| - platform: sendspin | |
| type: artist | |
| id: ss_artist | |
| - platform: sendspin | |
| type: album | |
| id: ss_album | |
| switch: | |
| - platform: template | |
| name: "Screen" | |
| id: switch_screen | |
| optimistic: true | |
| restore_mode: ALWAYS_ON | |
| turn_on_action: | |
| - if: | |
| condition: | |
| api.connected: | |
| then: | |
| - if: | |
| condition: | |
| media_player.is_idle: ${speaker_id} | |
| then: | |
| - display.page.show: page_clock | |
| else: | |
| - display.page.show: page_play | |
| - component.update: spi_display | |
| turn_off_action: | |
| - display.page.show: page_off | |
| - component.update: spi_display | |
| media_player: | |
| - platform: speaker_source | |
| id: !extend ${speaker_id} | |
| on_play: | |
| - display.page.show: page_play | |
| - component.update: spi_display | |
| - switch.template.publish: | |
| id: switch_screen | |
| state: ON | |
| on_idle: | |
| - display.page.show: page_clock | |
| - component.update: spi_display | |
| display: | |
| - platform: ssd1306_spi | |
| model: SH1106 128x64 | |
| id: spi_display | |
| spi_id: spi_bus_display | |
| cs_pin: | |
| number: ${display_cs} | |
| ignore_strapping_warning: true | |
| dc_pin: ${display_dc} | |
| reset_pin: ${display_reset} | |
| update_interval: 200ms | |
| data_rate: 20MHz | |
| pages: | |
| - id: page_startup | |
| lambda: |- | |
| it.printf(it.get_width() / 2, 20, | |
| id(text_small), TextAlign::CENTER, "${friendly_name}"); | |
| it.printf(it.get_width() / 2, 40, | |
| id(text_small), TextAlign::CENTER, "connecting..."); | |
| # Selected by api.on_connection | |
| - id: page_clock | |
| lambda: |- | |
| // display clock | |
| auto hours = id(esptime).now().hour; | |
| auto minutes = id(esptime).now().minute; | |
| auto seconds = id(esptime).now().second; | |
| auto dot = seconds % 2 == 0 ? " " : ":"; | |
| it.printf(it.get_width() / 2, | |
| it.get_height() / 2, | |
| id(text_big), | |
| TextAlign::CENTER, | |
| "%d%s%02d", hours, dot, minutes); | |
| - id: page_off | |
| lambda: return; | |
| # Selected by the speaker on_play | |
| - id: page_play | |
| lambda: |- | |
| const int PADDING_LEFT = 18, PADDING_RIGHT = 28; | |
| const int LINE_HEIGHTS[] = {10, 32, 54}; | |
| int x1, y1, new_h, new_w, | |
| title_width_offset = PADDING_LEFT, | |
| artist_width_offset = PADDING_LEFT, | |
| album_width_offset = PADDING_LEFT; | |
| bool increment = false; | |
| static int iteration = 0; | |
| // Draw some icons | |
| it.print(0, LINE_HEIGHTS[0], | |
| id(icons_small), | |
| TextAlign::CENTER_LEFT, | |
| "\U000fffd8"); // music icon | |
| it.print(0, LINE_HEIGHTS[1], | |
| id(icons_small), | |
| TextAlign::CENTER_LEFT, | |
| "\ue01a"); // artist icon | |
| it.print(0, LINE_HEIGHTS[2], | |
| id(icons_small), | |
| TextAlign::CENTER_LEFT, | |
| "\ue019"); // album icon | |
| it.start_clipping( Rect(PADDING_LEFT, | |
| 0, | |
| it.get_width(), | |
| it.get_height())); | |
| it.get_text_bounds(PADDING_LEFT, 0, | |
| id(ss_title).state.c_str(), | |
| id(text_small), | |
| TextAlign::CENTER_LEFT, | |
| &x1, &y1, &new_w, &new_h); | |
| if (new_w > it.get_width()) { | |
| auto missing_artist_text = new_w - it.get_width(); | |
| increment = true; | |
| title_width_offset = PADDING_LEFT - (iteration % (missing_artist_text + PADDING_RIGHT)); | |
| } | |
| it.print(title_width_offset, LINE_HEIGHTS[0], | |
| id(text_small), | |
| TextAlign::CENTER_LEFT, | |
| id(ss_title).state.c_str()); | |
| // Do this again for the artist | |
| it.get_text_bounds(PADDING_LEFT, 0, | |
| id(ss_artist).state.c_str(), | |
| id(text_small), | |
| TextAlign::CENTER_LEFT, | |
| &x1, &y1, &new_w, &new_h); | |
| if (new_w > it.get_width()) { | |
| auto missing_title_text = new_w - it.get_width(); | |
| increment = true; | |
| artist_width_offset = PADDING_LEFT - (iteration % (missing_title_text + PADDING_RIGHT)); | |
| } | |
| it.print(artist_width_offset, LINE_HEIGHTS[1], | |
| id(text_small), | |
| TextAlign::CENTER_LEFT, | |
| id(ss_artist).state.c_str()); | |
| // And the album | |
| it.get_text_bounds(PADDING_LEFT, 0, | |
| id(ss_album).state.c_str(), | |
| id(text_small), | |
| TextAlign::CENTER_LEFT, | |
| &x1, &y1, &new_w, &new_h); | |
| if (new_w > it.get_width()) { | |
| auto missing_album_text = new_w - it.get_width(); | |
| increment = true; | |
| album_width_offset = PADDING_LEFT - (iteration % (missing_album_text + PADDING_RIGHT)); | |
| } | |
| it.print(album_width_offset, LINE_HEIGHTS[2], | |
| id(text_small), | |
| TextAlign::CENTER_LEFT, | |
| id(ss_album).state.c_str()); | |
| it.end_clipping(); | |
| if (increment) { | |
| iteration++; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment