ESPHome  2025.4.0
lvgl_esphome.h
Go to the documentation of this file.
1 #pragma once
2 #include "esphome/core/defines.h"
3 
4 #ifdef USE_BINARY_SENSOR
6 #endif // USE_BINARY_SENSOR
7 #ifdef USE_LVGL_IMAGE
9 #endif // USE_LVGL_IMAGE
10 #ifdef USE_LVGL_ROTARY_ENCODER
12 #endif // USE_LVGL_ROTARY_ENCODER
13 
14 // required for clang-tidy
15 #ifndef LV_CONF_H
16 #define LV_CONF_SKIP 1 // NOLINT
17 #endif // LV_CONF_H
18 
21 #include "esphome/core/component.h"
22 #include "esphome/core/log.h"
23 #include <lvgl.h>
24 #include <map>
25 #include <utility>
26 #include <vector>
27 
28 #ifdef USE_LVGL_FONT
30 #endif // USE_LVGL_FONT
31 #ifdef USE_LVGL_TOUCHSCREEN
33 #endif // USE_LVGL_TOUCHSCREEN
34 
35 #if defined(USE_LVGL_BUTTONMATRIX) || defined(USE_LVGL_KEYBOARD)
37 #endif // USE_LVGL_BUTTONMATRIX
38 
39 namespace esphome {
40 namespace lvgl {
41 
42 extern lv_event_code_t lv_api_event; // NOLINT
43 extern lv_event_code_t lv_update_event; // NOLINT
44 extern std::string lv_event_code_name_for(uint8_t event_code);
45 #if LV_COLOR_DEPTH == 16
47 #elif LV_COLOR_DEPTH == 32
49 #else // LV_COLOR_DEPTH
51 #endif // LV_COLOR_DEPTH
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 inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image) {
60  lv_disp_set_bg_image(disp, image->get_lv_img_dsc());
61 }
62 
63 inline void lv_obj_set_style_bg_img_src(lv_obj_t *obj, esphome::image::Image *image, lv_style_selector_t selector) {
64  lv_obj_set_style_bg_img_src(obj, image->get_lv_img_dsc(), selector);
65 }
66 #ifdef USE_LVGL_CANVAS
67 inline void lv_canvas_draw_img(lv_obj_t *canvas, lv_coord_t x, lv_coord_t y, image::Image *image,
68  lv_draw_img_dsc_t *dsc) {
69  lv_canvas_draw_img(canvas, x, y, image->get_lv_img_dsc(), dsc);
70 }
71 #endif
72 
73 #ifdef USE_LVGL_METER
74 inline lv_meter_indicator_t *lv_meter_add_needle_img(lv_obj_t *obj, lv_meter_scale_t *scale, esphome::image::Image *src,
75  lv_coord_t pivot_x, lv_coord_t pivot_y) {
76  return lv_meter_add_needle_img(obj, scale, src->get_lv_img_dsc(), pivot_x, pivot_y);
77 }
78 #endif // USE_LVGL_METER
79 #endif // USE_LVGL_IMAGE
80 #ifdef USE_LVGL_ANIMIMG
81 inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) {
82  auto *dsc = static_cast<std::vector<lv_img_dsc_t *> *>(lv_obj_get_user_data(img));
83  if (dsc == nullptr) {
84  // object will be lazily allocated but never freed.
85  dsc = new std::vector<lv_img_dsc_t *>(images.size()); // NOLINT
86  lv_obj_set_user_data(img, dsc);
87  }
88  dsc->clear();
89  for (auto &image : images) {
90  dsc->push_back(image->get_lv_img_dsc());
91  }
92  lv_animimg_set_src(img, (const void **) dsc->data(), dsc->size());
93 }
94 
95 #endif // USE_LVGL_ANIMIMG
96 
97 // Parent class for things that wrap an LVGL object
98 class LvCompound {
99  public:
100  virtual ~LvCompound() = default;
101  virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
102  lv_obj_t *obj{};
103 };
104 
105 class LvglComponent;
106 
107 class LvPageType : public Parented<LvglComponent> {
108  public:
109  LvPageType(bool skip) : skip(skip) {}
110 
111  void setup(size_t index) {
112  this->index = index;
113  this->obj = lv_obj_create(nullptr);
114  }
115 
116  bool is_showing() const;
117 
118  lv_obj_t *obj{};
119  size_t index{};
120  bool skip;
121 };
122 
123 using LvLambdaType = std::function<void(lv_obj_t *)>;
124 using set_value_lambda_t = std::function<void(float)>;
125 using event_callback_t = void(_lv_event_t *);
126 using text_lambda_t = std::function<const char *()>;
127 
128 template<typename... Ts> class ObjUpdateAction : public Action<Ts...> {
129  public:
130  explicit ObjUpdateAction(std::function<void(Ts...)> &&lamb) : lamb_(std::move(lamb)) {}
131 
132  void play(Ts... x) override { this->lamb_(x...); }
133 
134  protected:
135  std::function<void(Ts...)> lamb_;
136 };
137 #ifdef USE_LVGL_FONT
138 class FontEngine {
139  public:
140  FontEngine(font::Font *esp_font);
141  const lv_font_t *get_lv_font();
142 
143  const font::GlyphData *get_glyph_data(uint32_t unicode_letter);
144  uint16_t baseline{};
145  uint16_t height{};
146  uint8_t bpp{};
147 
148  protected:
149  font::Font *font_{};
150  uint32_t last_letter_{};
151  const font::GlyphData *last_data_{};
152  lv_font_t lv_font_{};
153 };
154 #endif // USE_LVGL_FONT
155 #ifdef USE_LVGL_ANIMIMG
156 void lv_animimg_stop(lv_obj_t *obj);
157 #endif // USE_LVGL_ANIMIMG
158 
160  constexpr static const char *const TAG = "lvgl";
161 
162  public:
163  LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh, int draw_rounding,
164  bool resume_on_input);
165  static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
166 
167  float get_setup_priority() const override { return setup_priority::PROCESSOR; }
168  void setup() override;
169  void update() override;
170  void loop() override;
171  void add_on_idle_callback(std::function<void(uint32_t)> &&callback) {
172  this->idle_callbacks_.add(std::move(callback));
173  }
174  void add_on_pause_callback(std::function<void(bool)> &&callback) { this->pause_callbacks_.add(std::move(callback)); }
175  void dump_config() override;
176  bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
177  lv_disp_t *get_disp() { return this->disp_; }
178  lv_obj_t *get_scr_act() { return lv_disp_get_scr_act(this->disp_); }
179  // Pause or resume the display.
180  // @param paused If true, pause the display. If false, resume the display.
181  // @param show_snow If true, show the snow effect when paused.
182  void set_paused(bool paused, bool show_snow);
183  bool is_paused() const { return this->paused_; }
184  // If the display is paused and we have resume_on_input_ set to true, resume the display.
185  void maybe_wakeup() {
186  if (this->paused_ && this->resume_on_input_) {
187  this->set_paused(false, false);
188  }
189  }
190 
194  static void esphome_lvgl_init();
195  static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
196  static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
197  static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
198  lv_event_code_t event3);
199  void add_page(LvPageType *page);
200  void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);
201  void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
202  void show_prev_page(lv_scr_load_anim_t anim, uint32_t time);
203  void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
204  size_t get_current_page() const;
205  void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); }
206  void restore_focus_mark(lv_group_t *group) {
207  auto *mark = this->focus_marks_[group];
208  if (mark != nullptr) {
209  lv_group_focus_obj(mark);
210  }
211  }
212  // rounding factor to align bounds of update area when drawing
213  size_t draw_rounding{2};
214 
216 
217  protected:
218  void write_random_();
219  void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
220  void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
221 
222  std::vector<display::Display *> displays_{};
223  size_t buffer_frac_{1};
224  bool full_refresh_{};
225  bool resume_on_input_{};
226 
227  lv_disp_draw_buf_t draw_buf_{};
228  lv_disp_drv_t disp_drv_{};
229  lv_disp_t *disp_{};
230  bool paused_{};
231  std::vector<LvPageType *> pages_{};
232  size_t current_page_{0};
233  bool show_snow_{};
234  bool page_wrap_{true};
235  std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
236 
238  CallbackManager<void(bool)> pause_callbacks_{};
239  lv_color_t *rotate_buf_{};
240 };
241 
242 class IdleTrigger : public Trigger<> {
243  public:
244  explicit IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeout);
245 
246  protected:
248  bool is_idle_{};
249 };
250 
251 class PauseTrigger : public Trigger<> {
252  public:
253  explicit PauseTrigger(LvglComponent *parent, TemplatableValue<bool> paused);
254 
255  protected:
257 };
258 
259 template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
260  public:
261  explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
262  void play(Ts... x) override { this->action_(this->parent_); }
263 
264  protected:
265  std::function<void(LvglComponent *)> action_{};
266 };
267 
268 template<typename Tc, typename... Ts> class LvglCondition : public Condition<Ts...>, public Parented<Tc> {
269  public:
270  LvglCondition(std::function<bool(Tc *)> &&condition_lambda) : condition_lambda_(std::move(condition_lambda)) {}
271  bool check(Ts... x) override { return this->condition_lambda_(this->parent_); }
272 
273  protected:
274  std::function<bool(Tc *)> condition_lambda_{};
275 };
276 
277 #ifdef USE_LVGL_TOUCHSCREEN
278 class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
279  public:
280  LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent);
281  void update(const touchscreen::TouchPoints_t &tpoints) override;
282  void release() override {
283  touch_pressed_ = false;
284  this->parent_->maybe_wakeup();
285  }
286  lv_indev_drv_t *get_drv() { return &this->drv_; }
287 
288  protected:
289  lv_indev_drv_t drv_{};
290  touchscreen::TouchPoint touch_point_{};
291  bool touch_pressed_{};
292 };
293 #endif // USE_LVGL_TOUCHSCREEN
294 
295 #ifdef USE_LVGL_KEY_LISTENER
296 class LVEncoderListener : public Parented<LvglComponent> {
297  public:
298  LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt);
299 
300 #ifdef USE_BINARY_SENSOR
301  void add_button(binary_sensor::BinarySensor *button, lv_key_t key) {
302  button->add_on_state_callback([this, key](bool state) { this->event(key, state); });
303  }
304 #endif
305 
306 #ifdef USE_LVGL_ROTARY_ENCODER
308  sensor->register_listener([this](int32_t count) { this->set_count(count); });
309  }
310 #endif // USE_LVGL_ROTARY_ENCODER
311 
312  void event(int key, bool pressed) {
313  if (!this->parent_->is_paused()) {
314  this->pressed_ = pressed;
315  this->key_ = key;
316  } else if (!pressed) {
317  // maybe wakeup on release if paused
318  this->parent_->maybe_wakeup();
319  }
320  }
321 
322  void set_count(int32_t count) {
323  if (!this->parent_->is_paused()) {
324  this->count_ = count;
325  } else {
326  this->parent_->maybe_wakeup();
327  }
328  }
329 
330  lv_indev_drv_t *get_drv() { return &this->drv_; }
331 
332  protected:
333  lv_indev_drv_t drv_{};
334  bool pressed_{};
335  int32_t count_{};
336  int32_t last_count_{};
337  int key_{};
338 };
339 #endif // USE_LVGL_KEY_LISTENER
340 
341 #ifdef USE_LVGL_LINE
342 class LvLineType : public LvCompound {
343  public:
344  std::vector<lv_point_t> get_points() { return this->points_; }
345  void set_points(std::vector<lv_point_t> points) {
346  this->points_ = std::move(points);
347  lv_line_set_points(this->obj, this->points_.data(), this->points_.size());
348  }
349 
350  protected:
351  std::vector<lv_point_t> points_{};
352 };
353 #endif
354 #if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
355 class LvSelectable : public LvCompound {
356  public:
357  virtual size_t get_selected_index() = 0;
358  virtual void set_selected_index(size_t index, lv_anim_enable_t anim) = 0;
359  void set_selected_text(const std::string &text, lv_anim_enable_t anim);
360  std::string get_selected_text();
361  std::vector<std::string> get_options() { return this->options_; }
362  void set_options(std::vector<std::string> options);
363 
364  protected:
365  virtual void set_option_string(const char *options) = 0;
366  std::vector<std::string> options_{};
367 };
368 
369 #ifdef USE_LVGL_DROPDOWN
370 class LvDropdownType : public LvSelectable {
371  public:
372  size_t get_selected_index() override { return lv_dropdown_get_selected(this->obj); }
373  void set_selected_index(size_t index, lv_anim_enable_t anim) override { lv_dropdown_set_selected(this->obj, index); }
374 
375  protected:
376  void set_option_string(const char *options) override { lv_dropdown_set_options(this->obj, options); }
377 };
378 #endif // USE_LVGL_DROPDOWN
379 
380 #ifdef USE_LVGL_ROLLER
381 class LvRollerType : public LvSelectable {
382  public:
383  size_t get_selected_index() override { return lv_roller_get_selected(this->obj); }
384  void set_selected_index(size_t index, lv_anim_enable_t anim) override {
385  lv_roller_set_selected(this->obj, index, anim);
386  }
387  void set_mode(lv_roller_mode_t mode) { this->mode_ = mode; }
388 
389  protected:
390  void set_option_string(const char *options) override { lv_roller_set_options(this->obj, options, this->mode_); }
391  lv_roller_mode_t mode_{LV_ROLLER_MODE_NORMAL};
392 };
393 #endif
394 #endif // defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
395 
396 #ifdef USE_LVGL_BUTTONMATRIX
398  public:
399  void set_obj(lv_obj_t *lv_obj) override;
400  uint16_t get_selected() { return lv_btnmatrix_get_selected_btn(this->obj); }
401  void set_key(size_t idx, uint8_t key) { this->key_map_[idx] = key; }
402 
403  protected:
404  std::map<size_t, uint8_t> key_map_{};
405 };
406 #endif // USE_LVGL_BUTTONMATRIX
407 
408 #ifdef USE_LVGL_KEYBOARD
410  public:
411  void set_obj(lv_obj_t *lv_obj) override;
412 };
413 #endif // USE_LVGL_KEYBOARD
414 } // namespace lvgl
415 } // namespace esphome
void setup()
std::string lv_event_code_name_for(uint8_t event_code)
void add_on_idle_callback(std::function< void(uint32_t)> &&callback)
Definition: lvgl_esphome.h:171
bool state
Definition: fan.h:34
std::vector< lv_point_t > get_points()
Definition: lvgl_esphome.h:344
void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor)
Definition: lvgl_esphome.h:307
void loop()
void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image)
Definition: lvgl_esphome.h:56
void set_points(std::vector< lv_point_t > points)
Definition: lvgl_esphome.h:345
bool check(Ts... x) override
Definition: lvgl_esphome.h:271
std::vector< TouchPoint > TouchPoints_t
Definition: touchscreen.h:30
bool is_idle(uint32_t idle_ms)
Definition: lvgl_esphome.h:176
void register_listener(std::function< void(uint32_t)> listener)
void set_selected_index(size_t index, lv_anim_enable_t anim) override
Definition: lvgl_esphome.h:384
LvglCondition(std::function< bool(Tc *)> &&condition_lambda)
Definition: lvgl_esphome.h:270
interface for components that provide keypresses
Definition: key_provider.h:10
MediaPlayerStateTrigger< MediaPlayerState::MEDIA_PLAYER_STATE_IDLE > IdleTrigger
Definition: automation.h:65
float get_setup_priority() const override
Definition: lvgl_esphome.h:167
void set_option_string(const char *options) override
Definition: lvgl_esphome.h:376
void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image)
Definition: lvgl_esphome.h:59
lv_indev_drv_t * get_drv()
Definition: lvgl_esphome.h:286
uint16_t x
Definition: tt21100.cpp:17
void lv_obj_set_style_bg_img_src(lv_obj_t *obj, esphome::image::Image *image, lv_style_selector_t selector)
Definition: lvgl_esphome.h:63
void lv_canvas_draw_img(lv_obj_t *canvas, lv_coord_t x, lv_coord_t y, image::Image *image, lv_draw_img_dsc_t *dsc)
Definition: lvgl_esphome.h:67
void play(Ts... x) override
Definition: lvgl_esphome.h:262
STL namespace.
Component for rendering LVGL.
Definition: lvgl_esphome.h:159
This class simplifies creating components that periodically check a state.
Definition: component.h:296
void set_mode(lv_roller_mode_t mode)
Definition: lvgl_esphome.h:387
virtual ~LvCompound()=default
void add_button(binary_sensor::BinarySensor *button, lv_key_t key)
Definition: lvgl_esphome.h:301
void(_lv_event_t *) event_callback_t
Definition: lvgl_esphome.h:125
uint16_t y
Definition: tt21100.cpp:18
TemplatableValue< bool > paused_
Definition: lvgl_esphome.h:256
lv_event_code_t lv_update_event
std::function< void(float)> set_value_lambda_t
Definition: lvgl_esphome.h:124
lv_event_code_t lv_api_event
void restore_focus_mark(lv_group_t *group)
Definition: lvgl_esphome.h:206
void setup(size_t index)
Definition: lvgl_esphome.h:111
virtual void set_obj(lv_obj_t *lv_obj)
Definition: lvgl_esphome.h:101
Base class for all automation conditions.
Definition: automation.h:74
void add_on_pause_callback(std::function< void(bool)> &&callback)
Definition: lvgl_esphome.h:174
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:183
void set_page_wrap(bool wrap)
Definition: lvgl_esphome.h:203
std::function< void(Ts...)> lamb_
Definition: lvgl_esphome.h:135
MediaPlayerStateTrigger< MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED > PauseTrigger
Definition: automation.h:67
LvglAction(std::function< void(LvglComponent *)> &&lamb)
Definition: lvgl_esphome.h:261
const float PROCESSOR
For components that use data from sensors like displays.
Definition: component.cpp:20
lv_meter_indicator_t * lv_meter_add_needle_img(lv_obj_t *obj, lv_meter_scale_t *scale, esphome::image::Image *src, lv_coord_t pivot_x, lv_coord_t pivot_y)
Definition: lvgl_esphome.h:74
uint8_t type
void lv_animimg_set_src(lv_obj_t *img, std::vector< image::Image *> images)
Definition: lvgl_esphome.h:81
uint8_t options
size_t get_selected_index() override
Definition: lvgl_esphome.h:383
TemplatableValue< uint32_t > timeout_
Definition: lvgl_esphome.h:247
lv_img_dsc_t * get_lv_img_dsc()
Definition: image.cpp:90
std::function< void(lv_obj_t *)> LvLambdaType
Definition: lvgl_esphome.h:123
void set_key(size_t idx, uint8_t key)
Definition: lvgl_esphome.h:401
void event(int key, bool pressed)
Definition: lvgl_esphome.h:312
std::function< const char *()> text_lambda_t
Definition: lvgl_esphome.h:126
void add_on_state_callback(std::function< void(bool)> &&callback)
Add a callback to be notified of state changes.
void set_focus_mark(lv_group_t *group)
Definition: lvgl_esphome.h:205
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
Base class for all binary_sensor-type classes.
Definition: binary_sensor.h:37
std::vector< std::string > get_options()
Definition: lvgl_esphome.h:361
void set_count(int32_t count)
Definition: lvgl_esphome.h:322
void lv_animimg_stop(lv_obj_t *obj)
size_t get_selected_index() override
Definition: lvgl_esphome.h:372
ObjUpdateAction(std::function< void(Ts...)> &&lamb)
Definition: lvgl_esphome.h:130
esphome::sensor::Sensor * sensor
Definition: statsd.h:38
Helper class to easily give an object a parent of type T.
Definition: helpers.h:538
void play(Ts... x) override
Definition: lvgl_esphome.h:132
void set_selected_index(size_t index, lv_anim_enable_t anim) override
Definition: lvgl_esphome.h:373
void set_option_string(const char *options) override
Definition: lvgl_esphome.h:390