Guest User

Untitled

a guest
Sep 29th, 2025
26
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.67 KB | None | 0 0
  1. esphome:
  2. name: cheap-yellow-display
  3.  
  4. esp32:
  5. board: esp32dev
  6. framework:
  7. type: arduino
  8.  
  9. logger:
  10.  
  11. wifi:
  12. ssid: "Nelley Kelson"
  13. password: "1234567890"
  14. power_save_mode: none
  15.  
  16. api:
  17.  
  18. ota:
  19. platform: esphome
  20.  
  21. # Backlight (GPIO21)
  22. output:
  23. - platform: ledc
  24. pin: 21
  25. id: backlight_pwm
  26.  
  27. light:
  28. - platform: monochromatic
  29. id: backlight
  30. name: "CYD Backlight"
  31. output: backlight_pwm
  32. restore_mode: ALWAYS_ON
  33.  
  34. # SPI — HSPI (write-only)
  35. spi:
  36. clk_pin: 14
  37. mosi_pin: 13
  38.  
  39. # ===== Music info from Home Assistant =====
  40. text_sensor:
  41. - platform: homeassistant
  42. id: song_artist
  43. entity_id: media_player.bedroom_homepod_mini_2
  44. attribute: media_artist
  45. on_value:
  46. - component.update: my_display
  47.  
  48. - platform: homeassistant
  49. id: song_title
  50. entity_id: media_player.bedroom_homepod_mini_2
  51. attribute: media_title
  52. on_value:
  53. - component.update: my_display
  54.  
  55. - platform: homeassistant
  56. id: album_name
  57. entity_id: media_player.bedroom_homepod_mini_2
  58. attribute: media_album_name
  59. on_value:
  60. - component.update: my_display
  61.  
  62. - platform: homeassistant
  63. id: player_state
  64. entity_id: media_player.bedroom_homepod_mini_2
  65. on_value:
  66. - component.update: my_display
  67.  
  68. - platform: homeassistant
  69. id: temp_unit
  70. entity_id: sensor.average_temperature
  71. attribute: unit_of_measurement
  72. on_value:
  73. - component.update: my_display
  74.  
  75. - platform: homeassistant
  76. id: hum_unit
  77. entity_id: sensor.average_humidity
  78. attribute: unit_of_measurement
  79. on_value:
  80. - component.update: my_display
  81.  
  82. sensor:
  83. - platform: homeassistant
  84. id: avg_temp
  85. entity_id: sensor.average_temperature
  86. on_value:
  87. - component.update: my_display
  88.  
  89. - platform: homeassistant
  90. id: avg_humidity
  91. entity_id: sensor.average_humidity
  92. on_value:
  93. - component.update: my_display
  94.  
  95. # ===== Time sync =====
  96. time:
  97. - platform: homeassistant
  98. id: ha_time
  99.  
  100. # ===== Fonts =====
  101. font:
  102. - file: "gfonts://Roboto"
  103. id: f_artist_big
  104. size: 28
  105. - file: "gfonts://Roboto"
  106. id: f_title
  107. size: 20
  108. - file: "gfonts://Roboto"
  109. id: f_album
  110. size: 16
  111. - file: "gfonts://Roboto"
  112. id: f_temp
  113. size: 16
  114. - file: "gfonts://Roboto"
  115. id: f_hum
  116. size: 16
  117. - file: "gfonts://Roboto"
  118. id: f_time
  119. size: 60 # Very large time
  120. - file: "gfonts://Roboto"
  121. id: f_date
  122. size: 18 # Smaller font for day/date
  123.  
  124. # ===== Display — ILI9341 on CYD =====
  125. display:
  126. - platform: ili9xxx
  127. id: my_display
  128. model: ILI9341
  129. cs_pin: 15
  130. dc_pin: 2
  131. reset_pin: 33
  132. spi_mode: MODE3
  133. data_rate: 10MHz
  134. color_order: RGB
  135. invert_colors: false
  136. rotation: 180
  137. color_palette: 8BIT
  138. update_interval: 1s
  139. dimensions:
  140. width: 320
  141. height: 240
  142. offset_width: 0
  143. offset_height: 0
  144.  
  145. lambda: |-
  146. using esphome::Color;
  147. using esphome::display::TextAlign;
  148.  
  149. const int W = 320, H = 240;
  150. it.fill(Color::BLACK);
  151.  
  152. std::string artist = id(song_artist).state.c_str();
  153. std::string title = id(song_title).state.c_str();
  154. std::string album = id(album_name).state.c_str();
  155.  
  156. if (artist.empty()) artist = "—";
  157. if (title.empty()) title = "—";
  158. if (album.empty()) album = "";
  159.  
  160. auto fit_to_width = [&](std::string s, esphome::display::BaseFont *fnt, int maxw) -> std::string {
  161. int bx, by, bw, bh;
  162. it.get_text_bounds(0, 0, s.c_str(), fnt, TextAlign::TOP_LEFT, &bx, &by, &bw, &bh);
  163. if (bw <= maxw) return s;
  164. std::string base = s;
  165. while (base.size() > 1) {
  166. base.pop_back();
  167. std::string cand = base + "…";
  168. it.get_text_bounds(0, 0, cand.c_str(), fnt, TextAlign::TOP_LEFT, &bx, &by, &bw, &bh);
  169. if (bw <= maxw) return cand;
  170. }
  171. return "…";
  172. };
  173.  
  174. const int MAX_W = W - 10;
  175. artist = fit_to_width(artist, id(f_artist_big), MAX_W);
  176. title = fit_to_width(title, id(f_title), MAX_W);
  177. if (!album.empty()) album = fit_to_width(album, id(f_album), MAX_W);
  178.  
  179. const int ARTIST_TOP = 8;
  180. const int TITLE_TOP = 36;
  181. const int ALBUM_TOP = 56;
  182.  
  183. it.printf(W/2, ARTIST_TOP, id(f_artist_big), TextAlign::TOP_CENTER, "%s", artist.c_str());
  184. it.printf(W/2, ARTIST_TOP + 1, id(f_artist_big), TextAlign::TOP_CENTER, "%s", artist.c_str());
  185. it.printf(W/2, TITLE_TOP, id(f_title), TextAlign::TOP_CENTER, "%s", title.c_str());
  186.  
  187. int redbar_top_y = 0;
  188. if (!album.empty()) {
  189. it.printf(W/2, ALBUM_TOP, id(f_album), TextAlign::TOP_CENTER, "%s", album.c_str());
  190. const int RBAR_MARGIN = 10;
  191. const int RBAR_W = W - RBAR_MARGIN*2;
  192. const int RBAR_H = 3;
  193. redbar_top_y = ALBUM_TOP + 18;
  194. it.filled_rectangle(RBAR_MARGIN, redbar_top_y, RBAR_W, RBAR_H, Color(255, 0, 0));
  195. }
  196.  
  197. // Bottom-left temperature
  198. float t = id(avg_temp).state;
  199. std::string tu = id(temp_unit).state.c_str();
  200. if (tu.empty()) tu = "°";
  201. if (isfinite(t)) {
  202. char tbuf[24];
  203. if (fabsf(t) < 100.0f) snprintf(tbuf, sizeof(tbuf), "%.1f%s", t, tu.c_str());
  204. else snprintf(tbuf, sizeof(tbuf), "%.0f%s", t, tu.c_str());
  205. it.printf(4, H - 4, id(f_temp), TextAlign::BOTTOM_LEFT, "%s", tbuf);
  206. }
  207.  
  208. // Bottom-right humidity
  209. float h = id(avg_humidity).state;
  210. std::string hu = id(hum_unit).state.c_str();
  211. if (hu.empty()) hu = "%";
  212. if (isfinite(h)) {
  213. char hbuf[24];
  214. snprintf(hbuf, sizeof(hbuf), "%.0f%s", h, hu.c_str());
  215. it.printf(W - 4, H - 4, id(f_hum), TextAlign::BOTTOM_RIGHT, "%s", hbuf);
  216. }
  217.  
  218. // Second red bar just above "Indoor Avg"
  219. const int RBAR_MARGIN = 10;
  220. const int RBAR_W = W - RBAR_MARGIN*2;
  221. const int RBAR_H = 3;
  222. const int RBAR_BOTTOM_Y = H - 30;
  223. it.filled_rectangle(RBAR_MARGIN, RBAR_BOTTOM_Y, RBAR_W, RBAR_H, Color(255, 0, 0));
  224.  
  225. // Bottom label
  226. it.printf(W/2, H - 4, id(f_album), TextAlign::BOTTOM_CENTER, "Indoor Avg");
  227.  
  228. // Time block (with day and date)
  229. auto now = id(ha_time).now();
  230. if (now.is_valid()) {
  231. int mid_y = (redbar_top_y + RBAR_BOTTOM_Y) / 2;
  232.  
  233. // Day of week above time
  234. it.strftime(W/2, mid_y - 40, id(f_date), TextAlign::BOTTOM_CENTER, "%A", now);
  235.  
  236. // Big time
  237. it.strftime(W/2, mid_y, id(f_time), TextAlign::CENTER, "%I:%M %p", now);
  238.  
  239. // Date below time
  240. it.strftime(W/2, mid_y + 40, id(f_date), TextAlign::TOP_CENTER, "%b %d, %Y", now);
  241. }
  242.  
Advertisement
Add Comment
Please, Sign In to add comment