ESPHome 2026.1.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_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
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
39namespace esphome {
40namespace lvgl {
41
42extern lv_event_code_t lv_api_event; // NOLINT
43extern lv_event_code_t lv_update_event; // NOLINT
44extern 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_FONT
54inline void lv_obj_set_style_text_font(lv_obj_t *obj, const font::Font *font, lv_style_selector_t part) {
55 lv_obj_set_style_text_font(obj, font->get_lv_font(), part);
56}
57inline void lv_style_set_text_font(lv_style_t *style, const font::Font *font) {
58 lv_style_set_text_font(style, font->get_lv_font());
59}
60#endif
61#ifdef USE_LVGL_IMAGE
62// Shortcut / overload, so that the source of an image can easily be updated
63// from within a lambda.
64inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) {
65 lv_img_set_src(obj, image->get_lv_img_dsc());
66}
67inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image) {
69}
70
71inline void lv_obj_set_style_bg_img_src(lv_obj_t *obj, esphome::image::Image *image, lv_style_selector_t selector) {
72 lv_obj_set_style_bg_img_src(obj, image->get_lv_img_dsc(), selector);
73}
74#ifdef USE_LVGL_CANVAS
75inline void lv_canvas_draw_img(lv_obj_t *canvas, lv_coord_t x, lv_coord_t y, image::Image *image,
76 lv_draw_img_dsc_t *dsc) {
77 lv_canvas_draw_img(canvas, x, y, image->get_lv_img_dsc(), dsc);
78}
79#endif
80
81#ifdef USE_LVGL_METER
82inline lv_meter_indicator_t *lv_meter_add_needle_img(lv_obj_t *obj, lv_meter_scale_t *scale, esphome::image::Image *src,
83 lv_coord_t pivot_x, lv_coord_t pivot_y) {
84 return lv_meter_add_needle_img(obj, scale, src->get_lv_img_dsc(), pivot_x, pivot_y);
85}
86#endif // USE_LVGL_METER
87#endif // USE_LVGL_IMAGE
88#ifdef USE_LVGL_ANIMIMG
89inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) {
90 auto *dsc = static_cast<std::vector<lv_img_dsc_t *> *>(lv_obj_get_user_data(img));
91 if (dsc == nullptr) {
92 // object will be lazily allocated but never freed.
93 dsc = new std::vector<lv_img_dsc_t *>(images.size()); // NOLINT
94 lv_obj_set_user_data(img, dsc);
95 }
96 dsc->clear();
97 for (auto &image : images) {
98 dsc->push_back(image->get_lv_img_dsc());
99 }
100 lv_animimg_set_src(img, (const void **) dsc->data(), dsc->size());
101}
102
103#endif // USE_LVGL_ANIMIMG
104
105// Parent class for things that wrap an LVGL object
107 public:
108 virtual ~LvCompound() = default;
109 virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
110 lv_obj_t *obj{};
111};
112
113class LvglComponent;
114
115class LvPageType : public Parented<LvglComponent> {
116 public:
118
119 void setup(size_t index) {
120 this->index = index;
121 this->obj = lv_obj_create(nullptr);
122 }
123
124 bool is_showing() const;
125
126 lv_obj_t *obj{};
127 size_t index{};
128 bool skip;
129};
130
131using LvLambdaType = std::function<void(lv_obj_t *)>;
132using set_value_lambda_t = std::function<void(float)>;
133using event_callback_t = void(_lv_event_t *);
134using text_lambda_t = std::function<const char *()>;
135
136template<typename... Ts> class ObjUpdateAction : public Action<Ts...> {
137 public:
138 explicit ObjUpdateAction(std::function<void(Ts...)> &&lamb) : lamb_(std::move(lamb)) {}
139
140 void play(const Ts &...x) override { this->lamb_(x...); }
141
142 protected:
143 std::function<void(Ts...)> lamb_;
144};
145#ifdef USE_LVGL_ANIMIMG
146void lv_animimg_stop(lv_obj_t *obj);
147#endif // USE_LVGL_ANIMIMG
148
150 constexpr static const char *const TAG = "lvgl";
151
152 public:
153 LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh, int draw_rounding,
154 bool resume_on_input, bool update_when_display_idle);
155 static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
156
157 float get_setup_priority() const override { return setup_priority::PROCESSOR; }
158 void setup() override;
159 void update() override;
160 void loop() override;
161 void add_on_idle_callback(std::function<void(uint32_t)> &&callback) {
162 this->idle_callbacks_.add(std::move(callback));
163 }
164
165 static void monitor_cb(lv_disp_drv_t *disp_drv, uint32_t time, uint32_t px);
166 static void render_start_cb(lv_disp_drv_t *disp_drv);
167 void dump_config() override;
168 lv_disp_t *get_disp() { return this->disp_; }
169 lv_obj_t *get_scr_act() { return lv_disp_get_scr_act(this->disp_); }
170 // Pause or resume the display.
171 // @param paused If true, pause the display. If false, resume the display.
172 // @param show_snow If true, show the snow effect when paused.
173 void set_paused(bool paused, bool show_snow);
174
175 // Returns true if the display is explicitly paused, or a blocking display update is in progress.
176 bool is_paused() const;
177 // If the display is paused and we have resume_on_input_ set to true, resume the display.
179 if (this->paused_ && this->resume_on_input_) {
180 this->set_paused(false, false);
181 }
182 }
183
187 static void esphome_lvgl_init();
188 static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
189 static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
190 static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
191 lv_event_code_t event3);
192 void add_page(LvPageType *page);
193 void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);
194 void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
195 void show_prev_page(lv_scr_load_anim_t anim, uint32_t time);
196 void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
197 size_t get_current_page() const;
198 void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); }
199 void restore_focus_mark(lv_group_t *group) {
200 auto *mark = this->focus_marks_[group];
201 if (mark != nullptr) {
202 lv_group_focus_obj(mark);
203 }
204 }
205 // rounding factor to align bounds of update area when drawing
206 size_t draw_rounding{2};
207
209 void set_pause_trigger(Trigger<> *trigger) { this->pause_callback_ = trigger; }
210 void set_resume_trigger(Trigger<> *trigger) { this->resume_callback_ = trigger; }
211 void set_draw_start_trigger(Trigger<> *trigger) { this->draw_start_callback_ = trigger; }
212 void set_draw_end_trigger(Trigger<> *trigger) { this->draw_end_callback_ = trigger; }
213
214 protected:
215 void draw_end_();
216 // Not checking for non-null callback since the
217 // LVGL callback that calls it is not set in that case
218 void draw_start_() const { this->draw_start_callback_->trigger(); }
219
220 void write_random_();
221 void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
222 void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
223 std::vector<display::Display *> displays_{};
224 size_t buffer_frac_{1};
228
229 lv_disp_draw_buf_t draw_buf_{};
230 lv_disp_drv_t disp_drv_{};
231 lv_disp_t *disp_{};
232 bool paused_{};
233 std::vector<LvPageType *> pages_{};
234 size_t current_page_{0};
236 bool page_wrap_{true};
237 std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
238
244 lv_color_t *rotate_buf_{};
245};
246
247class IdleTrigger : public Trigger<> {
248 public:
249 explicit IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeout);
250
251 protected:
253 bool is_idle_{};
254};
255
256template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
257 public:
258 explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
259 void play(const Ts &...x) override { this->action_(this->parent_); }
260
261 protected:
262 std::function<void(LvglComponent *)> action_{};
263};
264
265template<typename Tc, typename... Ts> class LvglCondition : public Condition<Ts...>, public Parented<Tc> {
266 public:
267 LvglCondition(std::function<bool(Tc *)> &&condition_lambda) : condition_lambda_(std::move(condition_lambda)) {}
268 bool check(const Ts &...x) override { return this->condition_lambda_(this->parent_); }
269
270 protected:
271 std::function<bool(Tc *)> condition_lambda_{};
272};
273
274#ifdef USE_LVGL_TOUCHSCREEN
275class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
276 public:
277 LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent);
278 void update(const touchscreen::TouchPoints_t &tpoints) override;
279 void release() override {
280 touch_pressed_ = false;
281 this->parent_->maybe_wakeup();
282 }
283 lv_indev_drv_t *get_drv() { return &this->drv_; }
284
285 protected:
286 lv_indev_drv_t drv_{};
289};
290#endif // USE_LVGL_TOUCHSCREEN
291
292#ifdef USE_LVGL_KEY_LISTENER
293class LVEncoderListener : public Parented<LvglComponent> {
294 public:
295 LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt);
296
297#ifdef USE_BINARY_SENSOR
298 void add_button(binary_sensor::BinarySensor *button, lv_key_t key) {
299 button->add_on_state_callback([this, key](bool state) { this->event(key, state); });
300 }
301#endif
302
303#ifdef USE_LVGL_ROTARY_ENCODER
305 sensor->register_listener([this](int32_t count) { this->set_count(count); });
306 }
307#endif // USE_LVGL_ROTARY_ENCODER
308
309 void event(int key, bool pressed) {
310 if (!this->parent_->is_paused()) {
311 this->pressed_ = pressed;
312 this->key_ = key;
313 } else if (!pressed) {
314 // maybe wakeup on release if paused
315 this->parent_->maybe_wakeup();
316 }
317 }
318
319 void set_count(int32_t count) {
320 if (!this->parent_->is_paused()) {
321 this->count_ = count;
322 } else {
323 this->parent_->maybe_wakeup();
324 }
325 }
326
327 lv_indev_drv_t *get_drv() { return &this->drv_; }
328
329 protected:
330 lv_indev_drv_t drv_{};
331 bool pressed_{};
332 int32_t count_{};
333 int32_t last_count_{};
334 int key_{};
335};
336#endif // USE_LVGL_KEY_LISTENER
337
338#ifdef USE_LVGL_LINE
339class LvLineType : public LvCompound {
340 public:
341 std::vector<lv_point_t> get_points() { return this->points_; }
342 void set_points(std::vector<lv_point_t> points) {
343 this->points_ = std::move(points);
344 lv_line_set_points(this->obj, this->points_.data(), this->points_.size());
345 }
346
347 protected:
348 std::vector<lv_point_t> points_{};
349};
350#endif
351#if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
352class LvSelectable : public LvCompound {
353 public:
354 virtual size_t get_selected_index() = 0;
355 virtual void set_selected_index(size_t index, lv_anim_enable_t anim) = 0;
356 void set_selected_text(const std::string &text, lv_anim_enable_t anim);
357 std::string get_selected_text();
358 const std::vector<std::string> &get_options() { return this->options_; }
359 void set_options(std::vector<std::string> options);
360
361 protected:
362 virtual void set_option_string(const char *options) = 0;
363 std::vector<std::string> options_{};
364};
365
366#ifdef USE_LVGL_DROPDOWN
368 public:
369 size_t get_selected_index() override { return lv_dropdown_get_selected(this->obj); }
370 void set_selected_index(size_t index, lv_anim_enable_t anim) override { lv_dropdown_set_selected(this->obj, index); }
371
372 protected:
373 void set_option_string(const char *options) override { lv_dropdown_set_options(this->obj, options); }
374};
375#endif // USE_LVGL_DROPDOWN
376
377#ifdef USE_LVGL_ROLLER
379 public:
380 size_t get_selected_index() override { return lv_roller_get_selected(this->obj); }
381 void set_selected_index(size_t index, lv_anim_enable_t anim) override {
382 lv_roller_set_selected(this->obj, index, anim);
383 }
384 void set_mode(lv_roller_mode_t mode) { this->mode_ = mode; }
385
386 protected:
387 void set_option_string(const char *options) override { lv_roller_set_options(this->obj, options, this->mode_); }
388 lv_roller_mode_t mode_{LV_ROLLER_MODE_NORMAL};
389};
390#endif
391#endif // defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
392
393#ifdef USE_LVGL_BUTTONMATRIX
395 public:
396 void set_obj(lv_obj_t *lv_obj) override;
397 uint16_t get_selected() { return lv_btnmatrix_get_selected_btn(this->obj); }
398 void set_key(size_t idx, uint8_t key) { this->key_map_[idx] = key; }
399
400 protected:
401 std::map<size_t, uint8_t> key_map_{};
402};
403#endif // USE_LVGL_BUTTONMATRIX
404
405#ifdef USE_LVGL_KEYBOARD
407 public:
408 void set_obj(lv_obj_t *lv_obj) override;
409};
410#endif // USE_LVGL_KEYBOARD
411} // namespace lvgl
412} // namespace esphome
BedjetMode mode
BedJet operating mode.
Base class for all automation conditions.
Definition automation.h:183
Helper class to easily give an object a parent of type T.
Definition helpers.h:1102
This class simplifies creating components that periodically check a state.
Definition component.h:474
void add_on_state_callback(std::function< void(T)> &&callback)
void trigger(const Ts &...x)
Inform the parent automation that the event has triggered.
Definition automation.h:204
Base class for all binary_sensor-type classes.
const lv_font_t * get_lv_font() const
Definition font.h:77
lv_img_dsc_t * get_lv_img_dsc()
Definition image.cpp:108
interface for components that provide keypresses
IdleTrigger(LvglComponent *parent, TemplatableValue< uint32_t > timeout)
TemplatableValue< uint32_t > timeout_
void add_button(binary_sensor::BinarySensor *button, lv_key_t key)
LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt)
void event(int key, bool pressed)
void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor)
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
void set_points(std::vector< lv_point_t > points)
std::vector< lv_point_t > points_
std::vector< lv_point_t > get_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)
static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
void set_pause_trigger(Trigger<> *trigger)
LvglComponent(std::vector< display::Display * > displays, float buffer_frac, bool full_refresh, int draw_rounding, bool resume_on_input, bool update_when_display_idle)
std::vector< LvPageType * > pages_
static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event)
void show_next_page(lv_scr_load_anim_t anim, uint32_t time)
void set_focus_mark(lv_group_t *group)
void set_draw_end_trigger(Trigger<> *trigger)
CallbackManager< void(uint32_t)> idle_callbacks_
void add_on_idle_callback(std::function< void(uint32_t)> &&callback)
lv_disp_draw_buf_t draw_buf_
display::DisplayRotation rotation
void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time)
void show_prev_page(lv_scr_load_anim_t anim, uint32_t time)
void set_resume_trigger(Trigger<> *trigger)
static void render_start_cb(lv_disp_drv_t *disp_drv)
std::vector< display::Display * > displays_
static void esphome_lvgl_init()
Initialize the LVGL library and register custom events.
void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
void add_page(LvPageType *page)
float get_setup_priority() const override
std::map< lv_group_t *, lv_obj_t * > focus_marks_
void draw_buffer_(const lv_area_t *area, lv_color_t *ptr)
static void monitor_cb(lv_disp_drv_t *disp_drv, uint32_t time, uint32_t px)
void set_draw_start_trigger(Trigger<> *trigger)
void restore_focus_mark(lv_group_t *group)
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)
void register_listener(std::function< void(uint32_t)> listener)
uint16_t type
uint8_t options
bool state
Definition fan.h:0
@ DISPLAY_ROTATION_0_DEGREES
Definition display.h:135
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)
void lv_style_set_text_font(lv_style_t *style, const font::Font *font)
void lv_animimg_stop(lv_obj_t *obj)
std::string lv_event_code_name_for(uint8_t event_code)
std::function< void(float)> set_value_lambda_t
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)
void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image)
std::function< void(lv_obj_t *)> LvLambdaType
void lv_animimg_set_src(lv_obj_t *img, std::vector< image::Image * > images)
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_img_src(lv_obj_t *obj, esphome::image::Image *image, lv_style_selector_t selector)
lv_event_code_t lv_update_event
void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image)
std::function< const char *()> text_lambda_t
void(_lv_event_t *) event_callback_t
lv_event_code_t lv_api_event
const float PROCESSOR
For components that use data from sensors like displays.
Definition component.cpp:82
std::vector< TouchPoint > TouchPoints_t
Definition touchscreen.h:30
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6