esphome 2024.10.3__py3-none-any.whl → 2024.11.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. esphome/__main__.py +22 -4
  2. esphome/automation.py +29 -2
  3. esphome/components/animation/__init__.py +5 -8
  4. esphome/components/animation/animation.cpp +1 -1
  5. esphome/components/audio/__init__.py +9 -0
  6. esphome/components/audio/audio.h +21 -0
  7. esphome/components/axs15231/__init__.py +6 -0
  8. esphome/components/axs15231/touchscreen/__init__.py +36 -0
  9. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +64 -0
  10. esphome/components/axs15231/touchscreen/axs15231_touchscreen.h +27 -0
  11. esphome/components/bme68x_bsec2/__init__.py +1 -1
  12. esphome/components/bytebuffer/__init__.py +5 -0
  13. esphome/components/bytebuffer/bytebuffer.h +421 -0
  14. esphome/components/climate/__init__.py +14 -13
  15. esphome/components/datetime/__init__.py +3 -3
  16. esphome/components/debug/debug_esp32.cpp +16 -8
  17. esphome/components/dfplayer/dfplayer.cpp +132 -6
  18. esphome/components/dfplayer/dfplayer.h +19 -53
  19. esphome/components/display/display.cpp +142 -0
  20. esphome/components/display/display.h +7 -0
  21. esphome/components/es8311/__init__.py +0 -0
  22. esphome/components/es8311/audio_dac.py +70 -0
  23. esphome/components/es8311/es8311.cpp +227 -0
  24. esphome/components/es8311/es8311.h +135 -0
  25. esphome/components/es8311/es8311_const.h +195 -0
  26. esphome/components/esp32/boards.py +199 -1
  27. esphome/components/esp32/gpio.py +3 -1
  28. esphome/components/esp32_ble/const_esp32c6.h +7 -0
  29. esphome/components/esp32_ble_client/ble_client_base.h +1 -1
  30. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -1
  31. esphome/components/esp32_rmt_led_strip/led_strip.cpp +2 -2
  32. esphome/components/esp32_rmt_led_strip/led_strip.h +2 -0
  33. esphome/components/esp32_rmt_led_strip/light.py +3 -1
  34. esphome/components/esp8266/gpio.py +7 -5
  35. esphome/components/ethernet/__init__.py +55 -1
  36. esphome/components/ethernet/ethernet_component.cpp +14 -1
  37. esphome/components/ethernet/ethernet_component.h +7 -1
  38. esphome/components/font/__init__.py +213 -108
  39. esphome/components/gp8403/output/__init__.py +1 -1
  40. esphome/components/host/gpio.py +6 -4
  41. esphome/components/http_request/__init__.py +12 -0
  42. esphome/components/http_request/http_request.h +65 -3
  43. esphome/components/http_request/http_request_arduino.cpp +2 -3
  44. esphome/components/http_request/http_request_idf.cpp +6 -14
  45. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  46. esphome/components/http_request/update/http_request_update.cpp +1 -1
  47. esphome/components/i2c_device/__init__.py +26 -0
  48. esphome/components/i2c_device/i2c_device.cpp +17 -0
  49. esphome/components/i2c_device/i2c_device.h +18 -0
  50. esphome/components/i2s_audio/__init__.py +1 -3
  51. esphome/components/i2s_audio/speaker/__init__.py +12 -4
  52. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +432 -197
  53. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +91 -32
  54. esphome/components/ili9xxx/display.py +5 -1
  55. esphome/components/image/__init__.py +5 -8
  56. esphome/components/image/image.cpp +14 -14
  57. esphome/components/image/image.h +20 -24
  58. esphome/components/internal_temperature/internal_temperature.cpp +51 -2
  59. esphome/components/internal_temperature/internal_temperature.h +1 -0
  60. esphome/components/libretiny/gpio.py +4 -2
  61. esphome/components/light/__init__.py +32 -1
  62. esphome/components/light/automation.py +39 -32
  63. esphome/components/light/effects.py +36 -36
  64. esphome/components/light/light_state.cpp +6 -16
  65. esphome/components/light/light_state.h +34 -0
  66. esphome/components/light/types.py +3 -1
  67. esphome/components/logger/logger_esp32.cpp +15 -0
  68. esphome/components/lvgl/__init__.py +202 -95
  69. esphome/components/lvgl/automation.py +42 -40
  70. esphome/components/lvgl/binary_sensor/__init__.py +8 -15
  71. esphome/components/lvgl/defines.py +14 -8
  72. esphome/components/lvgl/encoders.py +11 -8
  73. esphome/components/lvgl/keypads.py +77 -0
  74. esphome/components/lvgl/light/__init__.py +6 -8
  75. esphome/components/lvgl/lv_validation.py +2 -4
  76. esphome/components/lvgl/lvcode.py +3 -9
  77. esphome/components/lvgl/lvgl_esphome.cpp +210 -89
  78. esphome/components/lvgl/lvgl_esphome.h +113 -30
  79. esphome/components/lvgl/lvgl_proxy.h +17 -0
  80. esphome/components/lvgl/number/__init__.py +10 -15
  81. esphome/components/lvgl/schemas.py +4 -2
  82. esphome/components/lvgl/select/__init__.py +12 -37
  83. esphome/components/lvgl/select/lvgl_select.h +27 -33
  84. esphome/components/lvgl/sensor/__init__.py +8 -14
  85. esphome/components/lvgl/styles.py +3 -4
  86. esphome/components/lvgl/switch/__init__.py +8 -13
  87. esphome/components/lvgl/text/__init__.py +5 -6
  88. esphome/components/lvgl/text_sensor/__init__.py +15 -15
  89. esphome/components/lvgl/touchscreens.py +2 -3
  90. esphome/components/lvgl/trigger.py +7 -9
  91. esphome/components/lvgl/types.py +9 -3
  92. esphome/components/lvgl/widgets/__init__.py +32 -21
  93. esphome/components/lvgl/widgets/dropdown.py +22 -10
  94. esphome/components/lvgl/widgets/msgbox.py +6 -5
  95. esphome/components/lvgl/widgets/obj.py +4 -2
  96. esphome/components/lvgl/widgets/page.py +3 -2
  97. esphome/components/lvgl/widgets/qrcode.py +54 -0
  98. esphome/components/lvgl/widgets/roller.py +21 -14
  99. esphome/components/lvgl/widgets/tileview.py +2 -1
  100. esphome/components/max17043/__init__.py +1 -0
  101. esphome/components/max17043/automation.h +20 -0
  102. esphome/components/max17043/max17043.cpp +98 -0
  103. esphome/components/max17043/max17043.h +29 -0
  104. esphome/components/max17043/sensor.py +77 -0
  105. esphome/components/media_player/__init__.py +11 -0
  106. esphome/components/media_player/automation.h +10 -0
  107. esphome/components/media_player/media_player.cpp +4 -0
  108. esphome/components/midea/air_conditioner.cpp +17 -1
  109. esphome/components/mlx90393/sensor.py +1 -1
  110. esphome/components/modbus_controller/__init__.py +31 -1
  111. esphome/components/modbus_controller/automation.h +16 -0
  112. esphome/components/modbus_controller/const.py +2 -0
  113. esphome/components/modbus_controller/modbus_controller.cpp +14 -2
  114. esphome/components/modbus_controller/modbus_controller.h +9 -0
  115. esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +40 -21
  116. esphome/components/mopeka_pro_check/mopeka_pro_check.h +9 -2
  117. esphome/components/mopeka_pro_check/sensor.py +41 -0
  118. esphome/components/mqtt/__init__.py +36 -0
  119. esphome/components/mqtt/mqtt_client.cpp +27 -3
  120. esphome/components/mqtt/mqtt_client.h +27 -2
  121. esphome/components/mqtt/mqtt_climate.cpp +4 -2
  122. esphome/components/mqtt/mqtt_component.cpp +6 -0
  123. esphome/components/mqtt/mqtt_component.h +4 -0
  124. esphome/components/mqtt/mqtt_const.h +6 -0
  125. esphome/components/online_image/online_image.cpp +2 -8
  126. esphome/components/online_image/online_image.h +2 -6
  127. esphome/components/opentherm/__init__.py +35 -9
  128. esphome/components/opentherm/binary_sensor/__init__.py +33 -0
  129. esphome/components/opentherm/const.py +11 -0
  130. esphome/components/opentherm/generate.py +142 -0
  131. esphome/components/opentherm/hub.cpp +130 -24
  132. esphome/components/opentherm/hub.h +62 -9
  133. esphome/components/opentherm/input.h +18 -0
  134. esphome/components/opentherm/input.py +51 -0
  135. esphome/components/opentherm/number/__init__.py +74 -0
  136. esphome/components/opentherm/number/number.cpp +40 -0
  137. esphome/components/opentherm/number/number.h +31 -0
  138. esphome/components/opentherm/opentherm.cpp +30 -0
  139. esphome/components/opentherm/opentherm.h +34 -2
  140. esphome/components/opentherm/opentherm_macros.h +151 -0
  141. esphome/components/opentherm/output/__init__.py +47 -0
  142. esphome/components/opentherm/output/output.cpp +18 -0
  143. esphome/components/opentherm/output/output.h +33 -0
  144. esphome/components/opentherm/schema.py +814 -0
  145. esphome/components/opentherm/sensor/__init__.py +51 -0
  146. esphome/components/opentherm/switch/__init__.py +43 -0
  147. esphome/components/opentherm/switch/switch.cpp +28 -0
  148. esphome/components/opentherm/switch/switch.h +20 -0
  149. esphome/components/opentherm/validate.py +31 -0
  150. esphome/components/pcd8544/display.py +8 -4
  151. esphome/components/prometheus/prometheus_handler.cpp +176 -14
  152. esphome/components/prometheus/prometheus_handler.h +25 -7
  153. esphome/components/qspi_amoled/display.py +1 -141
  154. esphome/components/qspi_dbi/display.py +185 -0
  155. esphome/components/qspi_dbi/models.py +64 -0
  156. esphome/components/{qspi_amoled/qspi_amoled.cpp → qspi_dbi/qspi_dbi.cpp} +95 -46
  157. esphome/components/{qspi_amoled/qspi_amoled.h → qspi_dbi/qspi_dbi.h} +26 -15
  158. esphome/components/rp2040/__init__.py +6 -3
  159. esphome/components/rp2040/gpio.py +5 -3
  160. esphome/components/rtttl/rtttl.cpp +4 -1
  161. esphome/components/rtttl/rtttl.h +1 -0
  162. esphome/components/sdl/sdl_esphome.cpp +22 -5
  163. esphome/components/sdl/sdl_esphome.h +1 -0
  164. esphome/components/sensor/__init__.py +18 -8
  165. esphome/components/sensor/filter.cpp +19 -18
  166. esphome/components/sensor/filter.h +9 -10
  167. esphome/components/sgp4x/sgp4x.cpp +40 -74
  168. esphome/components/sgp4x/sgp4x.h +5 -3
  169. esphome/components/speaker/__init__.py +51 -5
  170. esphome/components/speaker/automation.h +25 -0
  171. esphome/components/speaker/speaker.h +72 -1
  172. esphome/components/spi/__init__.py +15 -14
  173. esphome/components/spi_device/__init__.py +4 -15
  174. esphome/components/ssd1306_spi/display.py +6 -2
  175. esphome/components/ssd1322_spi/display.py +6 -2
  176. esphome/components/ssd1325_spi/display.py +6 -2
  177. esphome/components/ssd1327_spi/display.py +6 -2
  178. esphome/components/ssd1331_spi/display.py +6 -2
  179. esphome/components/ssd1351_spi/display.py +6 -2
  180. esphome/components/st7567_spi/display.py +6 -2
  181. esphome/components/st7701s/display.py +5 -1
  182. esphome/components/st7735/display.py +10 -5
  183. esphome/components/st7789v/display.py +12 -7
  184. esphome/components/statsd/statsd.cpp +2 -0
  185. esphome/components/statsd/statsd.h +2 -0
  186. esphome/components/sun/sun.h +3 -0
  187. esphome/components/tc74/__init__.py +1 -0
  188. esphome/components/tc74/sensor.py +32 -0
  189. esphome/components/tc74/tc74.cpp +68 -0
  190. esphome/components/tc74/tc74.h +28 -0
  191. esphome/components/touchscreen/__init__.py +41 -50
  192. esphome/components/touchscreen/touchscreen.h +4 -8
  193. esphome/components/udp/udp_component.cpp +6 -3
  194. esphome/components/udp/udp_component.h +4 -2
  195. esphome/components/waveshare_epaper/display.py +6 -2
  196. esphome/components/web_server/web_server.cpp +22 -0
  197. esphome/components/web_server/web_server.h +3 -0
  198. esphome/components/weikai/weikai.h +2 -2
  199. esphome/components/wifi/wifi_component.cpp +2 -2
  200. esphome/components/wifi/wifi_component_esp32_arduino.cpp +4 -4
  201. esphome/components/wifi/wifi_component_esp8266.cpp +4 -4
  202. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -2
  203. esphome/components/xpt2046/touchscreen/__init__.py +7 -32
  204. esphome/config_validation.py +3 -1
  205. esphome/const.py +8 -1
  206. esphome/core/defines.h +8 -2
  207. esphome/core/helpers.cpp +32 -17
  208. esphome/core/helpers.h +32 -16
  209. esphome/core/ring_buffer.cpp +2 -2
  210. esphome/core/ring_buffer.h +2 -2
  211. esphome/dashboard/core.py +25 -0
  212. esphome/dashboard/status/mdns.py +3 -4
  213. esphome/dashboard/web_server.py +54 -19
  214. esphome/espota2.py +36 -35
  215. esphome/helpers.py +68 -16
  216. esphome/mqtt.py +9 -2
  217. esphome/storage_json.py +4 -0
  218. esphome/writer.py +7 -18
  219. esphome/zeroconf.py +8 -6
  220. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/METADATA +7 -5
  221. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/RECORD +226 -180
  222. esphome/core/bytebuffer.cpp +0 -167
  223. esphome/core/bytebuffer.h +0 -144
  224. /esphome/components/{qspi_amoled → qspi_dbi}/__init__.py +0 -0
  225. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/LICENSE +0 -0
  226. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/WHEEL +0 -0
  227. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/entry_points.txt +0 -0
  228. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/top_level.txt +0 -0
@@ -5,16 +5,12 @@
5
5
  #include "lvgl_hal.h"
6
6
  #include "lvgl_esphome.h"
7
7
 
8
+ #include <numeric>
9
+
8
10
  namespace esphome {
9
11
  namespace lvgl {
10
12
  static const char *const TAG = "lvgl";
11
13
 
12
- #if LV_USE_LOG
13
- static void log_cb(const char *buf) {
14
- esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf);
15
- }
16
- #endif // LV_USE_LOG
17
-
18
14
  static const char *const EVENT_NAMES[] = {
19
15
  "NONE",
20
16
  "PRESSED",
@@ -69,43 +65,57 @@ std::string lv_event_code_name_for(uint8_t event_code) {
69
65
  }
70
66
  return str_sprintf("%2d", event_code);
71
67
  }
68
+
72
69
  static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
73
- // make sure all coordinates are even
74
- if (area->x1 & 1)
75
- area->x1--;
76
- if (!(area->x2 & 1))
77
- area->x2++;
78
- if (area->y1 & 1)
79
- area->y1--;
80
- if (!(area->y2 & 1))
81
- area->y2++;
70
+ // cater for display driver chips with special requirements for bounds of partial
71
+ // draw areas. Extend the draw area to satisfy:
72
+ // * Coordinates must be a multiple of draw_rounding
73
+ auto *comp = static_cast<LvglComponent *>(disp_drv->user_data);
74
+ auto draw_rounding = comp->draw_rounding;
75
+ // round down the start coordinates
76
+ area->x1 = area->x1 / draw_rounding * draw_rounding;
77
+ area->y1 = area->y1 / draw_rounding * draw_rounding;
78
+ // round up the end coordinates
79
+ area->x2 = (area->x2 + draw_rounding) / draw_rounding * draw_rounding - 1;
80
+ area->y2 = (area->y2 + draw_rounding) / draw_rounding * draw_rounding - 1;
82
81
  }
83
82
 
84
83
  lv_event_code_t lv_api_event; // NOLINT
85
84
  lv_event_code_t lv_update_event; // NOLINT
86
- void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); }
85
+ void LvglComponent::dump_config() {
86
+ ESP_LOGCONFIG(TAG, "LVGL:");
87
+ ESP_LOGCONFIG(TAG, " Display width/height: %d x %d", this->disp_drv_.hor_res, this->disp_drv_.ver_res);
88
+ ESP_LOGCONFIG(TAG, " Rotation: %d", this->rotation);
89
+ ESP_LOGCONFIG(TAG, " Draw rounding: %d", (int) this->draw_rounding);
90
+ }
87
91
  void LvglComponent::set_paused(bool paused, bool show_snow) {
88
92
  this->paused_ = paused;
89
93
  this->show_snow_ = show_snow;
90
- this->snow_line_ = 0;
91
94
  if (!paused && lv_scr_act() != nullptr) {
92
95
  lv_disp_trig_activity(this->disp_); // resets the inactivity time
93
96
  lv_obj_invalidate(lv_scr_act());
94
97
  }
98
+ this->pause_callbacks_.call(paused);
99
+ }
100
+
101
+ void LvglComponent::esphome_lvgl_init() {
102
+ lv_init();
103
+ lv_update_event = static_cast<lv_event_code_t>(lv_event_register_id());
104
+ lv_api_event = static_cast<lv_event_code_t>(lv_event_register_id());
95
105
  }
96
106
  void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
97
- lv_obj_add_event_cb(obj, callback, event, this);
107
+ lv_obj_add_event_cb(obj, callback, event, nullptr);
98
108
  }
99
109
  void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
100
110
  lv_event_code_t event2) {
101
- this->add_event_cb(obj, callback, event1);
102
- this->add_event_cb(obj, callback, event2);
111
+ add_event_cb(obj, callback, event1);
112
+ add_event_cb(obj, callback, event2);
103
113
  }
104
114
  void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
105
115
  lv_event_code_t event2, lv_event_code_t event3) {
106
- this->add_event_cb(obj, callback, event1);
107
- this->add_event_cb(obj, callback, event2);
108
- this->add_event_cb(obj, callback, event3);
116
+ add_event_cb(obj, callback, event1);
117
+ add_event_cb(obj, callback, event2);
118
+ add_event_cb(obj, callback, event3);
109
119
  }
110
120
  void LvglComponent::add_page(LvPageType *page) {
111
121
  this->pages_.push_back(page);
@@ -133,19 +143,64 @@ void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) {
133
143
  } while (this->pages_[this->current_page_]->skip); // skip empty pages()
134
144
  this->show_page(this->current_page_, anim, time);
135
145
  }
136
- void LvglComponent::draw_buffer_(const lv_area_t *area, const uint8_t *ptr) {
146
+ void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) {
147
+ auto width = lv_area_get_width(area);
148
+ auto height = lv_area_get_height(area);
149
+ auto x1 = area->x1;
150
+ auto y1 = area->y1;
151
+ lv_color_t *dst = this->rotate_buf_;
152
+ switch (this->rotation) {
153
+ case display::DISPLAY_ROTATION_90_DEGREES:
154
+ for (lv_coord_t x = height; x-- != 0;) {
155
+ for (lv_coord_t y = 0; y != width; y++) {
156
+ dst[y * height + x] = *ptr++;
157
+ }
158
+ }
159
+ y1 = x1;
160
+ x1 = this->disp_drv_.ver_res - area->y1 - height;
161
+ width = height;
162
+ height = lv_area_get_width(area);
163
+ break;
164
+
165
+ case display::DISPLAY_ROTATION_180_DEGREES:
166
+ for (lv_coord_t y = height; y-- != 0;) {
167
+ for (lv_coord_t x = width; x-- != 0;) {
168
+ dst[y * width + x] = *ptr++;
169
+ }
170
+ }
171
+ x1 = this->disp_drv_.hor_res - x1 - width;
172
+ y1 = this->disp_drv_.ver_res - y1 - height;
173
+ break;
174
+
175
+ case display::DISPLAY_ROTATION_270_DEGREES:
176
+ for (lv_coord_t x = 0; x != height; x++) {
177
+ for (lv_coord_t y = width; y-- != 0;) {
178
+ dst[y * height + x] = *ptr++;
179
+ }
180
+ }
181
+ x1 = y1;
182
+ y1 = this->disp_drv_.hor_res - area->x1 - width;
183
+ width = height;
184
+ height = lv_area_get_width(area);
185
+ break;
186
+
187
+ default:
188
+ dst = ptr;
189
+ break;
190
+ }
137
191
  for (auto *display : this->displays_) {
138
- display->draw_pixels_at(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area), ptr,
139
- display::COLOR_ORDER_RGB, LV_BITNESS, LV_COLOR_16_SWAP);
192
+ ESP_LOGV(TAG, "draw buffer x1=%d, y1=%d, width=%d, height=%d", x1, y1, width, height);
193
+ display->draw_pixels_at(x1, y1, width, height, (const uint8_t *) dst, display::COLOR_ORDER_RGB, LV_BITNESS,
194
+ LV_COLOR_16_SWAP);
140
195
  }
141
196
  }
142
197
 
143
198
  void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
144
199
  if (!this->paused_) {
145
200
  auto now = millis();
146
- this->draw_buffer_(area, (const uint8_t *) color_p);
147
- ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area),
148
- lv_area_get_height(area), (int) (millis() - now));
201
+ this->draw_buffer_(area, color_p);
202
+ ESP_LOGVV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area),
203
+ lv_area_get_height(area), (int) (millis() - now));
149
204
  }
150
205
  lv_disp_flush_ready(disp_drv);
151
206
  }
@@ -160,9 +215,18 @@ IdleTrigger::IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeo
160
215
  });
161
216
  }
162
217
 
218
+ PauseTrigger::PauseTrigger(LvglComponent *parent, TemplatableValue<bool> paused) : paused_(std::move(paused)) {
219
+ parent->add_on_pause_callback([this](bool pausing) {
220
+ if (this->paused_.value() == pausing)
221
+ this->trigger();
222
+ });
223
+ }
224
+
163
225
  #ifdef USE_LVGL_TOUCHSCREEN
164
- LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) {
226
+ LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent) {
227
+ this->set_parent(parent);
165
228
  lv_indev_drv_init(&this->drv_);
229
+ this->drv_.disp = parent->get_disp();
166
230
  this->drv_.long_press_repeat_time = long_press_repeat_time;
167
231
  this->drv_.long_press_time = long_press_time;
168
232
  this->drv_.type = LV_INDEV_TYPE_POINTER;
@@ -178,6 +242,7 @@ LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_r
178
242
  }
179
243
  };
180
244
  }
245
+
181
246
  void LVTouchListener::update(const touchscreen::TouchPoints_t &tpoints) {
182
247
  this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty();
183
248
  if (this->touch_pressed_)
@@ -203,6 +268,39 @@ LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_
203
268
  }
204
269
  #endif // USE_LVGL_KEY_LISTENER
205
270
 
271
+ #if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
272
+ std::string LvSelectable::get_selected_text() {
273
+ auto selected = this->get_selected_index();
274
+ if (selected >= this->options_.size())
275
+ return "";
276
+ return this->options_[selected];
277
+ }
278
+
279
+ static std::string join_string(std::vector<std::string> options) {
280
+ return std::accumulate(
281
+ options.begin(), options.end(), std::string(),
282
+ [](const std::string &a, const std::string &b) -> std::string { return a + (a.length() > 0 ? "\n" : "") + b; });
283
+ }
284
+
285
+ void LvSelectable::set_selected_text(const std::string &text, lv_anim_enable_t anim) {
286
+ auto index = std::find(this->options_.begin(), this->options_.end(), text);
287
+ if (index != this->options_.end()) {
288
+ this->set_selected_index(index - this->options_.begin(), anim);
289
+ lv_event_send(this->obj, lv_api_event, nullptr);
290
+ }
291
+ }
292
+
293
+ void LvSelectable::set_options(std::vector<std::string> options) {
294
+ auto index = this->get_selected_index();
295
+ if (index >= options.size())
296
+ index = options.size() - 1;
297
+ this->options_ = std::move(options);
298
+ this->set_option_string(join_string(this->options_).c_str());
299
+ lv_event_send(this->obj, LV_EVENT_REFRESH, nullptr);
300
+ this->set_selected_index(index, LV_ANIM_OFF);
301
+ }
302
+ #endif // USE_LVGL_DROPDOWN || LV_USE_ROLLER
303
+
206
304
  #ifdef USE_LVGL_BUTTONMATRIX
207
305
  void LvButtonMatrixType::set_obj(lv_obj_t *lv_obj) {
208
306
  LvCompound::set_obj(lv_obj);
@@ -261,45 +359,72 @@ void LvKeyboardType::set_obj(lv_obj_t *lv_obj) {
261
359
  #endif // USE_LVGL_KEYBOARD
262
360
 
263
361
  void LvglComponent::write_random_() {
264
- // length of 2 lines in 32 bit units
265
- // we write 2 lines for the benefit of displays that won't write one line at a time.
266
- size_t line_len = this->disp_drv_.hor_res * LV_COLOR_DEPTH / 8 / 4 * 2;
267
- for (size_t i = 0; i != line_len; i++) {
268
- ((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32();
269
- }
270
- lv_area_t area;
271
- area.x1 = 0;
272
- area.x2 = this->disp_drv_.hor_res - 1;
273
- if (this->snow_line_ == this->disp_drv_.ver_res / 2) {
274
- area.y1 = static_cast<lv_coord_t>(random_uint32() % (this->disp_drv_.ver_res / 2) * 2);
275
- } else {
276
- area.y1 = this->snow_line_++ * 2;
362
+ int iterations = 6 - lv_disp_get_inactive_time(this->disp_) / 60000;
363
+ if (iterations <= 0)
364
+ iterations = 1;
365
+ while (iterations-- != 0) {
366
+ auto col = random_uint32() % this->disp_drv_.hor_res;
367
+ col = col / this->draw_rounding * this->draw_rounding;
368
+ auto row = random_uint32() % this->disp_drv_.ver_res;
369
+ row = row / this->draw_rounding * this->draw_rounding;
370
+ auto size = (random_uint32() % 32) / this->draw_rounding * this->draw_rounding - 1;
371
+ lv_area_t area;
372
+ area.x1 = col;
373
+ area.y1 = row;
374
+ area.x2 = col + size;
375
+ area.y2 = row + size;
376
+ if (area.x2 >= this->disp_drv_.hor_res)
377
+ area.x2 = this->disp_drv_.hor_res - 1;
378
+ if (area.y2 >= this->disp_drv_.ver_res)
379
+ area.y2 = this->disp_drv_.ver_res - 1;
380
+
381
+ size_t line_len = lv_area_get_width(&area) * lv_area_get_height(&area) / 2;
382
+ for (size_t i = 0; i != line_len; i++) {
383
+ ((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32();
384
+ }
385
+ this->draw_buffer_(&area, (lv_color_t *) this->draw_buf_.buf1);
277
386
  }
278
- // write 2 lines
279
- area.y2 = area.y1 + 1;
280
- this->draw_buffer_(&area, (const uint8_t *) this->draw_buf_.buf1);
281
387
  }
282
388
 
283
- void LvglComponent::setup() {
284
- ESP_LOGCONFIG(TAG, "LVGL Setup starts");
285
- #if LV_USE_LOG
286
- lv_log_register_print_cb(log_cb);
287
- #endif
288
- lv_init();
289
- lv_update_event = static_cast<lv_event_code_t>(lv_event_register_id());
290
- lv_api_event = static_cast<lv_event_code_t>(lv_event_register_id());
389
+ /**
390
+ * @class LvglComponent
391
+ * @brief Component for rendering LVGL.
392
+ *
393
+ * This component renders LVGL widgets on a display. Some initialisation must be done in the constructor
394
+ * since LVGL needs to be initialised before any widgets can be created.
395
+ *
396
+ * @param displays a list of displays to render onto. All displays must have the same
397
+ * resolution.
398
+ * @param buffer_frac the fraction of the display resolution to use for the LVGL
399
+ * draw buffer. A higher value will make animations smoother but
400
+ * also increase memory usage.
401
+ * @param full_refresh if true, the display will be fully refreshed on every frame.
402
+ * If false, only changed areas will be updated.
403
+ * @param draw_rounding the rounding to use when drawing. A value of 1 will draw
404
+ * without any rounding, a value of 2 will round to the nearest
405
+ * multiple of 2, and so on.
406
+ * @param resume_on_input if true, this component will resume rendering when the user
407
+ * presses a key or clicks on the screen.
408
+ */
409
+ LvglComponent::LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh,
410
+ int draw_rounding, bool resume_on_input)
411
+ : draw_rounding(draw_rounding),
412
+ displays_(std::move(displays)),
413
+ buffer_frac_(buffer_frac),
414
+ full_refresh_(full_refresh),
415
+ resume_on_input_(resume_on_input) {
291
416
  auto *display = this->displays_[0];
292
417
  size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_;
293
418
  auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
294
- auto *buf = lv_custom_mem_alloc(buf_bytes);
295
- if (buf == nullptr) {
296
- #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
297
- ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
298
- #endif
299
- this->mark_failed();
300
- this->status_set_error("Memory allocation failure");
301
- return;
419
+ this->rotation = display->get_rotation();
420
+ if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) {
421
+ this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes)); // NOLINT
422
+ if (this->rotate_buf_ == nullptr)
423
+ return;
302
424
  }
425
+ auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT
426
+ if (buf == nullptr)
427
+ return;
303
428
  lv_disp_draw_buf_init(&this->draw_buf_, buf, nullptr, buffer_pixels);
304
429
  lv_disp_drv_init(&this->disp_drv_);
305
430
  this->disp_drv_.draw_buf = &this->draw_buf_;
@@ -307,33 +432,36 @@ void LvglComponent::setup() {
307
432
  this->disp_drv_.full_refresh = this->full_refresh_;
308
433
  this->disp_drv_.flush_cb = static_flush_cb;
309
434
  this->disp_drv_.rounder_cb = rounder_cb;
310
- switch (display->get_rotation()) {
311
- case display::DISPLAY_ROTATION_0_DEGREES:
312
- break;
313
- case display::DISPLAY_ROTATION_90_DEGREES:
314
- this->disp_drv_.sw_rotate = true;
315
- this->disp_drv_.rotated = LV_DISP_ROT_90;
316
- break;
317
- case display::DISPLAY_ROTATION_180_DEGREES:
318
- this->disp_drv_.sw_rotate = true;
319
- this->disp_drv_.rotated = LV_DISP_ROT_180;
320
- break;
321
- case display::DISPLAY_ROTATION_270_DEGREES:
322
- this->disp_drv_.sw_rotate = true;
323
- this->disp_drv_.rotated = LV_DISP_ROT_270;
324
- break;
325
- }
326
- display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);
327
435
  this->disp_drv_.hor_res = (lv_coord_t) display->get_width();
328
436
  this->disp_drv_.ver_res = (lv_coord_t) display->get_height();
329
- ESP_LOGV(TAG, "sw_rotate = %d, rotated=%d", this->disp_drv_.sw_rotate, this->disp_drv_.rotated);
330
437
  this->disp_ = lv_disp_drv_register(&this->disp_drv_);
331
- for (const auto &v : this->init_lambdas_)
332
- v(this);
438
+ }
439
+
440
+ void LvglComponent::setup() {
441
+ if (this->draw_buf_.buf1 == nullptr) {
442
+ this->mark_failed();
443
+ this->status_set_error("Memory allocation failure");
444
+ return;
445
+ }
446
+ ESP_LOGCONFIG(TAG, "LVGL Setup starts");
447
+ #if LV_USE_LOG
448
+ lv_log_register_print_cb([](const char *buf) {
449
+ auto next = strchr(buf, ')');
450
+ if (next != nullptr)
451
+ buf = next + 1;
452
+ while (isspace(*buf))
453
+ buf++;
454
+ esp_log_printf_(LVGL_LOG_LEVEL, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf);
455
+ });
456
+ #endif
457
+ // Rotation will be handled by our drawing function, so reset the display rotation.
458
+ for (auto *display : this->displays_)
459
+ display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);
333
460
  this->show_page(0, LV_SCR_LOAD_ANIM_NONE, 0);
334
461
  lv_disp_trig_activity(this->disp_);
335
462
  ESP_LOGCONFIG(TAG, "LVGL Setup complete");
336
463
  }
464
+
337
465
  void LvglComponent::update() {
338
466
  // update indicators
339
467
  if (this->paused_) {
@@ -348,13 +476,6 @@ void LvglComponent::loop() {
348
476
  }
349
477
  lv_timer_handler_run_in_period(5);
350
478
  }
351
- bool lv_is_pre_initialise() {
352
- if (!lv_is_initialized()) {
353
- ESP_LOGE(TAG, "LVGL call before component is initialised");
354
- return true;
355
- }
356
- return false;
357
- }
358
479
 
359
480
  #ifdef USE_LVGL_ANIMIMG
360
481
  void lv_animimg_stop(lv_obj_t *obj) {
@@ -4,6 +4,9 @@
4
4
  #ifdef USE_BINARY_SENSOR
5
5
  #include "esphome/components/binary_sensor/binary_sensor.h"
6
6
  #endif // USE_BINARY_SENSOR
7
+ #ifdef USE_LVGL_IMAGE
8
+ #include "esphome/components/image/image.h"
9
+ #endif // USE_LVGL_IMAGE
7
10
  #ifdef USE_LVGL_ROTARY_ENCODER
8
11
  #include "esphome/components/rotary_encoder/rotary_encoder.h"
9
12
  #endif // USE_LVGL_ROTARY_ENCODER
@@ -18,11 +21,9 @@
18
21
  #include "esphome/core/component.h"
19
22
  #include "esphome/core/log.h"
20
23
  #include <lvgl.h>
21
- #include <vector>
22
24
  #include <map>
23
- #ifdef USE_LVGL_IMAGE
24
- #include "esphome/components/image/image.h"
25
- #endif // USE_LVGL_IMAGE
25
+ #include <utility>
26
+ #include <vector>
26
27
 
27
28
  #ifdef USE_LVGL_FONT
28
29
  #include "esphome/components/font/font.h"
@@ -41,7 +42,6 @@ namespace lvgl {
41
42
  extern lv_event_code_t lv_api_event; // NOLINT
42
43
  extern lv_event_code_t lv_update_event; // NOLINT
43
44
  extern std::string lv_event_code_name_for(uint8_t event_code);
44
- extern bool lv_is_pre_initialise();
45
45
  #if LV_COLOR_DEPTH == 16
46
46
  static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565;
47
47
  #elif LV_COLOR_DEPTH == 32
@@ -50,6 +50,14 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT
50
50
  static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_332;
51
51
  #endif // LV_COLOR_DEPTH
52
52
 
53
+ #ifdef USE_LVGL_IMAGE
54
+ // Shortcut / overload, so that the source of an image can easily be updated
55
+ // from within a lambda.
56
+ inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) {
57
+ lv_img_set_src(obj, image->get_lv_img_dsc());
58
+ }
59
+ #endif // USE_LVGL_IMAGE
60
+
53
61
  // Parent class for things that wrap an LVGL object
54
62
  class LvCompound {
55
63
  public:
@@ -110,6 +118,8 @@ class LvglComponent : public PollingComponent {
110
118
  constexpr static const char *const TAG = "lvgl";
111
119
 
112
120
  public:
121
+ LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh, int draw_rounding,
122
+ bool resume_on_input);
113
123
  static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
114
124
 
115
125
  float get_setup_priority() const override { return setup_priority::PROCESSOR; }
@@ -119,19 +129,31 @@ class LvglComponent : public PollingComponent {
119
129
  void add_on_idle_callback(std::function<void(uint32_t)> &&callback) {
120
130
  this->idle_callbacks_.add(std::move(callback));
121
131
  }
122
- void add_display(display::Display *display) { this->displays_.push_back(display); }
123
- void add_init_lambda(const std::function<void(LvglComponent *)> &lamb) { this->init_lambdas_.push_back(lamb); }
132
+ void add_on_pause_callback(std::function<void(bool)> &&callback) { this->pause_callbacks_.add(std::move(callback)); }
124
133
  void dump_config() override;
125
- void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; }
126
134
  bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
127
- void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; }
128
135
  lv_disp_t *get_disp() { return this->disp_; }
136
+ lv_obj_t *get_scr_act() { return lv_disp_get_scr_act(this->disp_); }
137
+ // Pause or resume the display.
138
+ // @param paused If true, pause the display. If false, resume the display.
139
+ // @param show_snow If true, show the snow effect when paused.
129
140
  void set_paused(bool paused, bool show_snow);
130
- void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
131
- void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
132
- void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
133
- lv_event_code_t event3);
134
141
  bool is_paused() const { return this->paused_; }
142
+ // If the display is paused and we have resume_on_input_ set to true, resume the display.
143
+ void maybe_wakeup() {
144
+ if (this->paused_ && this->resume_on_input_) {
145
+ this->set_paused(false, false);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Initialize the LVGL library and register custom events.
151
+ */
152
+ static void esphome_lvgl_init();
153
+ static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
154
+ static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
155
+ static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
156
+ lv_event_code_t event3);
135
157
  void add_page(LvPageType *page);
136
158
  void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);
137
159
  void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
@@ -144,12 +166,21 @@ class LvglComponent : public PollingComponent {
144
166
  lv_group_focus_obj(mark);
145
167
  }
146
168
  }
169
+ // rounding factor to align bounds of update area when drawing
170
+ size_t draw_rounding{2};
171
+
172
+ display::DisplayRotation rotation{display::DISPLAY_ROTATION_0_DEGREES};
147
173
 
148
174
  protected:
149
175
  void write_random_();
150
- void draw_buffer_(const lv_area_t *area, const uint8_t *ptr);
176
+ void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
151
177
  void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
178
+
152
179
  std::vector<display::Display *> displays_{};
180
+ size_t buffer_frac_{1};
181
+ bool full_refresh_{};
182
+ bool resume_on_input_{};
183
+
153
184
  lv_disp_draw_buf_t draw_buf_{};
154
185
  lv_disp_drv_t disp_drv_{};
155
186
  lv_disp_t *disp_{};
@@ -157,14 +188,12 @@ class LvglComponent : public PollingComponent {
157
188
  std::vector<LvPageType *> pages_{};
158
189
  size_t current_page_{0};
159
190
  bool show_snow_{};
160
- lv_coord_t snow_line_{};
161
191
  bool page_wrap_{true};
162
192
  std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
163
193
 
164
- std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_;
165
194
  CallbackManager<void(uint32_t)> idle_callbacks_{};
166
- size_t buffer_frac_{1};
167
- bool full_refresh_{};
195
+ CallbackManager<void(bool)> pause_callbacks_{};
196
+ lv_color_t *rotate_buf_{};
168
197
  };
169
198
 
170
199
  class IdleTrigger : public Trigger<> {
@@ -176,6 +205,14 @@ class IdleTrigger : public Trigger<> {
176
205
  bool is_idle_{};
177
206
  };
178
207
 
208
+ class PauseTrigger : public Trigger<> {
209
+ public:
210
+ explicit PauseTrigger(LvglComponent *parent, TemplatableValue<bool> paused);
211
+
212
+ protected:
213
+ TemplatableValue<bool> paused_;
214
+ };
215
+
179
216
  template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
180
217
  public:
181
218
  explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
@@ -198,9 +235,12 @@ template<typename... Ts> class LvglCondition : public Condition<Ts...>, public P
198
235
  #ifdef USE_LVGL_TOUCHSCREEN
199
236
  class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
200
237
  public:
201
- LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time);
238
+ LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent);
202
239
  void update(const touchscreen::TouchPoints_t &tpoints) override;
203
- void release() override { touch_pressed_ = false; }
240
+ void release() override {
241
+ touch_pressed_ = false;
242
+ this->parent_->maybe_wakeup();
243
+ }
204
244
  lv_indev_drv_t *get_drv() { return &this->drv_; }
205
245
 
206
246
  protected:
@@ -215,16 +255,11 @@ class LVEncoderListener : public Parented<LvglComponent> {
215
255
  public:
216
256
  LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt);
217
257
 
218
- void set_left_button(binary_sensor::BinarySensor *left_button) {
219
- left_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_LEFT, state); });
220
- }
221
- void set_right_button(binary_sensor::BinarySensor *right_button) {
222
- right_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_RIGHT, state); });
223
- }
224
-
225
- void set_enter_button(binary_sensor::BinarySensor *enter_button) {
226
- enter_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_ENTER, state); });
258
+ #ifdef USE_BINARY_SENSOR
259
+ void add_button(binary_sensor::BinarySensor *button, lv_key_t key) {
260
+ button->add_on_state_callback([this, key](bool state) { this->event(key, state); });
227
261
  }
262
+ #endif
228
263
 
229
264
  #ifdef USE_LVGL_ROTARY_ENCODER
230
265
  void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor) {
@@ -236,12 +271,18 @@ class LVEncoderListener : public Parented<LvglComponent> {
236
271
  if (!this->parent_->is_paused()) {
237
272
  this->pressed_ = pressed;
238
273
  this->key_ = key;
274
+ } else if (!pressed) {
275
+ // maybe wakeup on release if paused
276
+ this->parent_->maybe_wakeup();
239
277
  }
240
278
  }
241
279
 
242
280
  void set_count(int32_t count) {
243
- if (!this->parent_->is_paused())
281
+ if (!this->parent_->is_paused()) {
244
282
  this->count_ = count;
283
+ } else {
284
+ this->parent_->maybe_wakeup();
285
+ }
245
286
  }
246
287
 
247
288
  lv_indev_drv_t *get_drv() { return &this->drv_; }
@@ -255,6 +296,48 @@ class LVEncoderListener : public Parented<LvglComponent> {
255
296
  };
256
297
  #endif // USE_LVGL_KEY_LISTENER
257
298
 
299
+ #if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
300
+ class LvSelectable : public LvCompound {
301
+ public:
302
+ virtual size_t get_selected_index() = 0;
303
+ virtual void set_selected_index(size_t index, lv_anim_enable_t anim) = 0;
304
+ void set_selected_text(const std::string &text, lv_anim_enable_t anim);
305
+ std::string get_selected_text();
306
+ std::vector<std::string> get_options() { return this->options_; }
307
+ void set_options(std::vector<std::string> options);
308
+
309
+ protected:
310
+ virtual void set_option_string(const char *options) = 0;
311
+ std::vector<std::string> options_{};
312
+ };
313
+
314
+ #ifdef USE_LVGL_DROPDOWN
315
+ class LvDropdownType : public LvSelectable {
316
+ public:
317
+ size_t get_selected_index() override { return lv_dropdown_get_selected(this->obj); }
318
+ void set_selected_index(size_t index, lv_anim_enable_t anim) override { lv_dropdown_set_selected(this->obj, index); }
319
+
320
+ protected:
321
+ void set_option_string(const char *options) override { lv_dropdown_set_options(this->obj, options); }
322
+ };
323
+ #endif // USE_LVGL_DROPDOWN
324
+
325
+ #ifdef USE_LVGL_ROLLER
326
+ class LvRollerType : public LvSelectable {
327
+ public:
328
+ size_t get_selected_index() override { return lv_roller_get_selected(this->obj); }
329
+ void set_selected_index(size_t index, lv_anim_enable_t anim) override {
330
+ lv_roller_set_selected(this->obj, index, anim);
331
+ }
332
+ void set_mode(lv_roller_mode_t mode) { this->mode_ = mode; }
333
+
334
+ protected:
335
+ void set_option_string(const char *options) override { lv_roller_set_options(this->obj, options, this->mode_); }
336
+ lv_roller_mode_t mode_{LV_ROLLER_MODE_NORMAL};
337
+ };
338
+ #endif
339
+ #endif // defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
340
+
258
341
  #ifdef USE_LVGL_BUTTONMATRIX
259
342
  class LvButtonMatrixType : public key_provider::KeyProvider, public LvCompound {
260
343
  public:
@@ -0,0 +1,17 @@
1
+ #pragma once
2
+ /**
3
+ * This header is for use in components that might or might not use LVGL. There is a platformio bug where
4
+ the mere mention of a header file, even if ifdefed, causes the build to fail. This is a workaround, since if this
5
+ file is included in the build, LVGL is always included.
6
+ */
7
+ #ifdef USE_LVGL
8
+ // required for clang-tidy
9
+ #ifndef LV_CONF_H
10
+ #define LV_CONF_SKIP 1 // NOLINT
11
+ #endif // LV_CONF_H
12
+
13
+ #include <lvgl.h>
14
+ namespace esphome {
15
+ namespace lvgl {} // namespace lvgl
16
+ } // namespace esphome
17
+ #endif // USE_LVGL