ESPHome 2026.5.0-dev
Loading...
Searching...
No Matches
lvgl_esphome.h
Go to the documentation of this file.
1#pragma once
3
4#ifdef USE_BINARY_SENSOR
6#endif // USE_BINARY_SENSOR
7#ifdef USE_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
22
23#include <list>
24#include <lvgl.h>
25#include <map>
26#include <utility>
27#include <vector>
28
29#ifdef USE_ESP32_VARIANT_ESP32P4
30#include "driver/ppa.h"
31#endif
32
33#ifdef USE_FONT
35#endif // USE_LVGL_FONT
36#ifdef USE_TOUCHSCREEN
38#endif // USE_LVGL_TOUCHSCREEN
39
40#if defined(USE_LVGL_BUTTONMATRIX) || defined(USE_LVGL_KEYBOARD)
42#endif // USE_LVGL_BUTTONMATRIX
43
44namespace esphome::lvgl {
45
46#if LV_COLOR_DEPTH == 16
47using lv_color_data = uint16_t;
48#endif
49#if LV_COLOR_DEPTH == 32
51#endif
52
53extern lv_event_code_t lv_api_event; // NOLINT
54extern lv_event_code_t lv_update_event; // NOLINT
55extern std::string lv_event_code_name_for(lv_event_t *event);
56
57lv_obj_t *lv_container_create(lv_obj_t *parent);
58#ifdef USE_LVGL_SCALE
59void lv_scale_draw_event_cb(lv_event_t *e, int16_t range_start, int16_t range_end, lv_color_t color_start,
60 lv_color_t color_end, int width, bool local);
61#endif
62#if LV_COLOR_DEPTH == 16
64#elif LV_COLOR_DEPTH == 32
66#else // LV_COLOR_DEPTH
68#endif // LV_COLOR_DEPTH
69
70#if defined(USE_FONT) && defined(USE_LVGL_FONT)
71inline void lv_obj_set_style_text_font(lv_obj_t *obj, const font::Font *font, lv_style_selector_t part) {
72 lv_obj_set_style_text_font(obj, font->get_lv_font(), part);
73}
74inline void lv_style_set_text_font(lv_style_t *style, const font::Font *font) {
75 lv_style_set_text_font(style, font->get_lv_font());
76}
77#endif
78#if defined(USE_LVGL_IMAGE) && defined(USE_IMAGE)
79#if LV_USE_IMAGE
80// Shortcut / overload, so that the source of an image widget can easily be updated from within a lambda.
81inline void lv_image_set_src(lv_obj_t *obj, image::Image *image) { ::lv_image_set_src(obj, image->get_lv_image_dsc()); }
82#endif // LV_USE_IMAGE
83
84inline void lv_obj_set_style_bitmap_mask_src(lv_obj_t *obj, image::Image *image, lv_style_selector_t selector) {
86}
87
88inline void lv_obj_set_style_bg_image_src(lv_obj_t *obj, image::Image *image, lv_style_selector_t selector) {
90}
91#endif // USE_LVGL_IMAGE
92#ifdef USE_LVGL_ANIMIMG
93inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) {
94 auto *dsc = static_cast<std::vector<lv_image_dsc_t *> *>(lv_obj_get_user_data(img));
95 if (dsc == nullptr) {
96 // object will be lazily allocated but never freed.
97 dsc = new std::vector<lv_image_dsc_t *>(images.size()); // NOLINT
98 lv_obj_set_user_data(img, dsc);
99 }
100 dsc->clear();
101 for (auto &image : images) {
102 dsc->push_back(image->get_lv_image_dsc());
103 }
104 lv_animimg_set_src(img, (const void **) dsc->data(), dsc->size());
105}
106#endif // USE_LVGL_ANIMIMG
107
108#ifdef USE_LVGL_METER
109int16_t lv_get_needle_angle_for_value(lv_obj_t *obj, int value);
110#endif
111
112// Parent class for things that wrap an LVGL object
114 public:
115 virtual ~LvCompound() = default;
116 virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
117 lv_obj_t *obj{};
118};
119
120class LvglComponent;
121
122class LvPageType : public Parented<LvglComponent> {
123 public:
125
126 void setup(size_t index) {
127 this->index = index;
128 this->obj = lv_obj_create(nullptr);
129 }
130
131 bool is_showing() const;
132
133 lv_obj_t *obj{};
134 size_t index{};
135 bool skip;
136};
137
138using event_callback_t = void(lv_event_t *);
139
141 public:
142 LvLambdaComponent(void (*callback)()) : callback_(callback) {}
143
144 void setup() override { this->callback_(); }
145 // execute after the LvglComponent is setup
146 float get_setup_priority() const override { return setup_priority::PROCESSOR - 5; }
147
148 protected:
149 void (*callback_)();
150};
151
152template<typename... Ts> class ObjUpdateAction : public Action<Ts...> {
153 public:
154 explicit ObjUpdateAction(std::function<void(Ts...)> &&lamb) : lamb_(std::move(lamb)) {}
155
156 void play(const Ts &...x) override { this->lamb_(x...); }
157
158 protected:
159 std::function<void(Ts...)> lamb_;
160};
161#ifdef USE_LVGL_ANIMIMG
162void lv_animimg_stop(lv_obj_t *obj);
163#endif // USE_LVGL_ANIMIMG
169
171 constexpr static const char *const TAG = "lvgl";
172
173 public:
174 LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh, int draw_rounding,
175 bool resume_on_input, bool update_when_display_idle, RotationType rotation_type);
176 static void static_flush_cb(lv_display_t *disp_drv, const lv_area_t *area, uint8_t *color_p);
177
178 float get_setup_priority() const override { return setup_priority::PROCESSOR; }
179 void setup() override;
180 void update() override;
181 void loop() override;
182 template<typename F> void add_on_idle_callback(F &&callback) { this->idle_callbacks_.add(std::forward<F>(callback)); }
183
184 static void render_end_cb(lv_event_t *event);
185 static void render_start_cb(lv_event_t *event);
186 void dump_config() override;
187 lv_display_t *get_disp() { return this->disp_; }
188 lv_obj_t *get_screen_active() { return lv_display_get_screen_active(this->disp_); }
189 // Pause or resume the display.
190 // @param paused If true, pause the display. If false, resume the display.
191 // @param show_snow If true, show the snow effect when paused.
192 void set_paused(bool paused, bool show_snow);
193
194 // Returns true if the display is explicitly paused, or a blocking display update is in progress.
195 bool is_paused() const;
196 // If the display is paused and we have resume_on_input_ set to true, resume the display.
198 if (this->paused_ && this->resume_on_input_) {
199 this->set_paused(false, false);
200 }
201 }
202
206 static void esphome_lvgl_init();
207 static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
208 static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
209 static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
210 lv_event_code_t event3);
211
212 void add_page(LvPageType *page);
213 void show_page(size_t index, lv_screen_load_anim_t anim, uint32_t time);
214 void show_next_page(lv_screen_load_anim_t anim, uint32_t time);
215 void show_prev_page(lv_screen_load_anim_t anim, uint32_t time);
216 void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
217 void set_big_endian(bool big_endian) { this->big_endian_ = big_endian; }
218 size_t get_current_page() const;
219 void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); }
220 void restore_focus_mark(lv_group_t *group) {
221 auto *mark = this->focus_marks_[group];
222 if (mark != nullptr) {
223 lv_group_focus_obj(mark);
224 }
225 }
226 // rounding factor to align bounds of update area when drawing
227 size_t draw_rounding{2};
228
229 void set_pause_trigger(Trigger<> *trigger) { this->pause_callback_ = trigger; }
230 void set_resume_trigger(Trigger<> *trigger) { this->resume_callback_ = trigger; }
231 void set_draw_start_trigger(Trigger<> *trigger) { this->draw_start_callback_ = trigger; }
232 void set_draw_end_trigger(Trigger<> *trigger) { this->draw_end_callback_ = trigger; }
235 void rotate_coordinates(int32_t &x, int32_t &y) const;
236
237 uint16_t get_width() const { return lv_display_get_horizontal_resolution(this->disp_); }
238 uint16_t get_height() const { return lv_display_get_vertical_resolution(this->disp_); }
239
240 protected:
241 void set_resolution_() const;
242 void draw_end_();
243 // Not checking for non-null callback since the
244 // LVGL callback that calls it is not set in that case
245 void draw_start_() const { this->draw_start_callback_->trigger(); }
246
247 void write_random_();
248 void draw_buffer_(const lv_area_t *area, lv_color_data *ptr);
249#ifdef USE_ESP32_VARIANT_ESP32P4
250 bool ppa_rotate_(const lv_color_data *src, lv_color_data *dst, uint16_t width, uint16_t height,
251 uint32_t height_rounded);
252#endif
253 void flush_cb_(lv_display_t *disp_drv, const lv_area_t *area, uint8_t *color_p);
254
255 std::vector<display::Display *> displays_{};
256 size_t buffer_frac_{1};
260
261 uint8_t *draw_buf_{};
262 lv_display_t *disp_{};
263 uint16_t width_{};
264 uint16_t height_{};
265 bool paused_{};
266 std::vector<LvPageType *> pages_{};
267 size_t current_page_{0};
269 bool page_wrap_{true};
271 std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
272
278 void *rotate_buf_{};
281#ifdef USE_ESP32_VARIANT_ESP32P4
282 ppa_client_handle_t ppa_client_{};
283#endif
284};
285
286class IdleTrigger : public Trigger<> {
287 public:
288 explicit IdleTrigger(LvglComponent *parent, TemplatableFn<uint32_t> timeout);
289
290 protected:
292 bool is_idle_{};
293};
294
295template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
296 public:
297 explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
298 void play(const Ts &...x) override { this->action_(this->parent_); }
299
300 protected:
301 std::function<void(LvglComponent *)> action_{};
302};
303
304template<typename Tc, typename... Ts> class LvglCondition : public Condition<Ts...>, public Parented<Tc> {
305 public:
306 LvglCondition(std::function<bool(Tc *)> &&condition_lambda) : condition_lambda_(std::move(condition_lambda)) {}
307 bool check(const Ts &...x) override { return this->condition_lambda_(this->parent_); }
308
309 protected:
310 std::function<bool(Tc *)> condition_lambda_{};
311};
312
313#ifdef USE_LVGL_TOUCHSCREEN
314class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
315 public:
316 LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent);
317 void update(const touchscreen::TouchPoints_t &tpoints) override;
318 void release() override {
319 touch_pressed_ = false;
320 this->parent_->maybe_wakeup();
321 }
322 lv_indev_t *get_drv() { return this->drv_; }
323
324 protected:
325 lv_indev_t *drv_{};
328};
329#endif // USE_LVGL_TOUCHSCREEN
330
331#ifdef USE_LVGL_METER
332
333class IndicatorLine : public LvCompound {
334 public:
335 IndicatorLine() = default;
336
337 void set_obj(lv_obj_t *lv_obj) override;
338
339 void set_value(int value);
340
341 private:
342 void update_length_();
343
344 int16_t angle_{};
345 lv_point_precise_t points_[2]{};
346};
347#endif
348
349#ifdef USE_LVGL_KEY_LISTENER
350class LVEncoderListener : public Parented<LvglComponent> {
351 public:
352 LVEncoderListener(lv_indev_type_t type, uint16_t long_press_time, uint16_t long_press_repeat_time);
353
354#ifdef USE_BINARY_SENSOR
355 void add_button(binary_sensor::BinarySensor *button, lv_key_t key) {
356 button->add_on_state_callback([this, key](bool state) { this->event(key, state); });
357 }
358#endif
359
360#ifdef USE_LVGL_ROTARY_ENCODER
362 sensor->register_listener([this](int32_t count) { this->set_count(count); });
363 }
364#endif // USE_LVGL_ROTARY_ENCODER
365
366 void event(int key, bool pressed) {
367 if (!this->parent_->is_paused()) {
368 this->pressed_ = pressed;
369 this->key_ = key;
370 } else if (!pressed) {
371 // maybe wakeup on release if paused
372 this->parent_->maybe_wakeup();
373 }
374 }
375
376 void set_count(int32_t count) {
377 if (!this->parent_->is_paused()) {
378 this->count_ = count;
379 } else {
380 this->parent_->maybe_wakeup();
381 }
382 }
383
384 lv_indev_t *get_drv() { return this->drv_; }
385
386 protected:
387 lv_indev_t *drv_{};
388 bool pressed_{};
389 int32_t count_{};
390 int32_t last_count_{};
391 int key_{};
392};
393#endif // USE_LVGL_KEY_LISTENER
394
395#ifdef USE_LVGL_LINE
396class LvLineType : public LvCompound {
397 public:
399 this->points_ = std::move(points);
400 lv_line_set_points(this->obj, this->points_.begin(), this->points_.size());
401 }
402
403 protected:
405};
406#endif
407#if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
408class LvSelectable : public LvCompound {
409 public:
410 virtual size_t get_selected_index() = 0;
411 virtual void set_selected_index(size_t index, lv_anim_enable_t anim) = 0;
412 void set_selected_text(const std::string &text, lv_anim_enable_t anim);
413 std::string get_selected_text();
414 const std::vector<std::string> &get_options() { return this->options_; }
415 void set_options(std::vector<std::string> options);
416
417 protected:
418 virtual void set_option_string(const char *options) = 0;
419 std::vector<std::string> options_{};
420};
421
422#ifdef USE_LVGL_DROPDOWN
424 public:
425 size_t get_selected_index() override { return lv_dropdown_get_selected(this->obj); }
426 void set_selected_index(size_t index, lv_anim_enable_t anim) override { lv_dropdown_set_selected(this->obj, index); }
427
428 protected:
429 void set_option_string(const char *options) override { lv_dropdown_set_options(this->obj, options); }
430};
431#endif // USE_LVGL_DROPDOWN
432
433#ifdef USE_LVGL_ROLLER
435 public:
436 size_t get_selected_index() override { return lv_roller_get_selected(this->obj); }
437 void set_selected_index(size_t index, lv_anim_enable_t anim) override {
438 lv_roller_set_selected(this->obj, index, anim);
439 }
440 void set_mode(lv_roller_mode_t mode) { this->mode_ = mode; }
441
442 protected:
443 void set_option_string(const char *options) override { lv_roller_set_options(this->obj, options, this->mode_); }
444 lv_roller_mode_t mode_{LV_ROLLER_MODE_NORMAL};
445};
446#endif
447#endif // defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
448
449#ifdef USE_LVGL_BUTTONMATRIX
451 public:
452 void set_obj(lv_obj_t *lv_obj) override;
453 uint16_t get_selected() { return lv_buttonmatrix_get_selected_button(this->obj); }
454 void set_key(size_t idx, uint8_t key) { this->key_map_[idx] = key; }
455
456 protected:
457 std::map<size_t, uint8_t> key_map_{};
458};
459#endif // USE_LVGL_BUTTONMATRIX
460
461#ifdef USE_LVGL_KEYBOARD
463 public:
464 void set_obj(lv_obj_t *lv_obj) override;
465};
466#endif // USE_LVGL_KEYBOARD
467} // namespace esphome::lvgl
BedjetMode mode
BedJet operating mode.
Base class for all automation conditions.
Definition automation.h:459
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Definition helpers.h:522
Helper class to easily give an object a parent of type T.
Definition helpers.h:2013
This class simplifies creating components that periodically check a state.
Definition component.h:602
void add_on_state_callback(F &&callback)
Function-pointer-only templatable storage (4 bytes on 32-bit).
Definition automation.h:40
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
Definition automation.h:482
Base class for all binary_sensor-type classes.
const lv_font_t * get_lv_font() const
Definition font.h:77
lv_image_dsc_t * get_lv_image_dsc()
Definition image.cpp:108
interface for components that provide keypresses
TemplatableFn< uint32_t > timeout_
IdleTrigger(LvglComponent *parent, TemplatableFn< uint32_t > timeout)
void set_obj(lv_obj_t *lv_obj) override
void add_button(binary_sensor::BinarySensor *button, lv_key_t key)
void event(int key, bool pressed)
void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor)
LVEncoderListener(lv_indev_type_t type, uint16_t long_press_time, uint16_t long_press_repeat_time)
LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent)
touchscreen::TouchPoint touch_point_
void update(const touchscreen::TouchPoints_t &tpoints) override
void set_key(size_t idx, uint8_t key)
std::map< size_t, uint8_t > key_map_
void set_obj(lv_obj_t *lv_obj) override
virtual ~LvCompound()=default
virtual void set_obj(lv_obj_t *lv_obj)
size_t get_selected_index() override
void set_option_string(const char *options) override
void set_selected_index(size_t index, lv_anim_enable_t anim) override
void set_obj(lv_obj_t *lv_obj) override
LvLambdaComponent(void(*callback)())
float get_setup_priority() const override
void set_points(FixedVector< lv_point_precise_t > points)
FixedVector< lv_point_precise_t > points_
void setup(size_t index)
void set_mode(lv_roller_mode_t mode)
void set_option_string(const char *options) override
void set_selected_index(size_t index, lv_anim_enable_t anim) override
size_t get_selected_index() override
const std::vector< std::string > & get_options()
void set_selected_text(const std::string &text, lv_anim_enable_t anim)
void set_options(std::vector< std::string > options)
std::vector< std::string > options_
virtual void set_selected_index(size_t index, lv_anim_enable_t anim)=0
virtual size_t get_selected_index()=0
virtual void set_option_string(const char *options)=0
void play(const Ts &...x) override
LvglAction(std::function< void(LvglComponent *)> &&lamb)
std::function< void(LvglComponent *)> action_
Component for rendering LVGL.
void set_paused(bool paused, bool show_snow)
void set_pause_trigger(Trigger<> *trigger)
display::DisplayRotation rotation_
void rotate_coordinates(int32_t &x, int32_t &y) const
std::vector< LvPageType * > pages_
void set_rotation(display::DisplayRotation rotation)
static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event)
void set_focus_mark(lv_group_t *group)
bool ppa_rotate_(const lv_color_data *src, lv_color_data *dst, uint16_t width, uint16_t height, uint32_t height_rounded)
void set_draw_end_trigger(Trigger<> *trigger)
CallbackManager< void(uint32_t)> idle_callbacks_
void set_big_endian(bool big_endian)
void show_next_page(lv_screen_load_anim_t anim, uint32_t time)
void set_resume_trigger(Trigger<> *trigger)
std::vector< display::Display * > displays_
static void esphome_lvgl_init()
Initialize the LVGL library and register custom events.
void show_prev_page(lv_screen_load_anim_t anim, uint32_t time)
ppa_client_handle_t ppa_client_
LvglComponent(std::vector< display::Display * > displays, float buffer_frac, bool full_refresh, int draw_rounding, bool resume_on_input, bool update_when_display_idle, RotationType rotation_type)
static void render_start_cb(lv_event_t *event)
void add_on_idle_callback(F &&callback)
void add_page(LvPageType *page)
float get_setup_priority() const override
static void static_flush_cb(lv_display_t *disp_drv, const lv_area_t *area, uint8_t *color_p)
std::map< lv_group_t *, lv_obj_t * > focus_marks_
static void render_end_cb(lv_event_t *event)
void draw_buffer_(const lv_area_t *area, lv_color_data *ptr)
void show_page(size_t index, lv_screen_load_anim_t anim, uint32_t time)
display::DisplayRotation get_rotation() const
void set_draw_start_trigger(Trigger<> *trigger)
void restore_focus_mark(lv_group_t *group)
void flush_cb_(lv_display_t *disp_drv, const lv_area_t *area, uint8_t *color_p)
LvglCondition(std::function< bool(Tc *)> &&condition_lambda)
bool check(const Ts &...x) override
std::function< bool(Tc *)> condition_lambda_
void play(const Ts &...x) override
std::function< void(Ts...)> lamb_
ObjUpdateAction(std::function< void(Ts...)> &&lamb)
uint16_t type
uint8_t options
bool state
Definition fan.h:2
@ DISPLAY_ROTATION_0_DEGREES
Definition display.h:135
void lv_obj_set_style_bitmap_mask_src(lv_obj_t *obj, image::Image *image, lv_style_selector_t selector)
int16_t lv_get_needle_angle_for_value(lv_obj_t *obj, int value)
void lv_style_set_text_font(lv_style_t *style, const font::Font *font)
void lv_animimg_stop(lv_obj_t *obj)
uint16_t lv_color_data
void(lv_event_t *) event_callback_t
void lv_animimg_set_src(lv_obj_t *img, std::vector< image::Image * > images)
void lv_image_set_src(lv_obj_t *obj, image::Image *image)
void lv_obj_set_style_text_font(lv_obj_t *obj, const font::Font *font, lv_style_selector_t part)
void lv_obj_set_style_bg_image_src(lv_obj_t *obj, image::Image *image, lv_style_selector_t selector)
lv_event_code_t lv_update_event
lv_obj_t * lv_container_create(lv_obj_t *parent)
std::string lv_event_code_name_for(lv_event_t *event)
void lv_scale_draw_event_cb(lv_event_t *e, int16_t range_start, int16_t range_end, lv_color_t color_start, lv_color_t color_end, int width, bool local)
Function to apply colors to ticks based on position.
lv_event_code_t lv_api_event
constexpr float PROCESSOR
For components that use data from sensors like displays.
Definition component.h:44
std::vector< TouchPoint > TouchPoints_t
Definition touchscreen.h:30
static void uint32_t
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6