ESPHome  2024.11.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 #endif // USE_LVGL_IMAGE
60 
61 // Parent class for things that wrap an LVGL object
62 class LvCompound {
63  public:
64  virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
65  lv_obj_t *obj{};
66 };
67 
68 class LvPageType {
69  public:
70  LvPageType(bool skip) : skip(skip) {}
71 
72  void setup(size_t index) {
73  this->index = index;
74  this->obj = lv_obj_create(nullptr);
75  }
76  lv_obj_t *obj{};
77  size_t index{};
78  bool skip;
79 };
80 
81 using LvLambdaType = std::function<void(lv_obj_t *)>;
82 using set_value_lambda_t = std::function<void(float)>;
83 using event_callback_t = void(_lv_event_t *);
84 using text_lambda_t = std::function<const char *()>;
85 
86 template<typename... Ts> class ObjUpdateAction : public Action<Ts...> {
87  public:
88  explicit ObjUpdateAction(std::function<void(Ts...)> &&lamb) : lamb_(std::move(lamb)) {}
89 
90  void play(Ts... x) override { this->lamb_(x...); }
91 
92  protected:
93  std::function<void(Ts...)> lamb_;
94 };
95 #ifdef USE_LVGL_FONT
96 class FontEngine {
97  public:
98  FontEngine(font::Font *esp_font);
99  const lv_font_t *get_lv_font();
100 
101  const font::GlyphData *get_glyph_data(uint32_t unicode_letter);
102  uint16_t baseline{};
103  uint16_t height{};
104  uint8_t bpp{};
105 
106  protected:
107  font::Font *font_{};
108  uint32_t last_letter_{};
109  const font::GlyphData *last_data_{};
110  lv_font_t lv_font_{};
111 };
112 #endif // USE_LVGL_FONT
113 #ifdef USE_LVGL_ANIMIMG
114 void lv_animimg_stop(lv_obj_t *obj);
115 #endif // USE_LVGL_ANIMIMG
116 
118  constexpr static const char *const TAG = "lvgl";
119 
120  public:
121  LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh, int draw_rounding,
122  bool resume_on_input);
123  static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
124 
125  float get_setup_priority() const override { return setup_priority::PROCESSOR; }
126  void setup() override;
127  void update() override;
128  void loop() override;
129  void add_on_idle_callback(std::function<void(uint32_t)> &&callback) {
130  this->idle_callbacks_.add(std::move(callback));
131  }
132  void add_on_pause_callback(std::function<void(bool)> &&callback) { this->pause_callbacks_.add(std::move(callback)); }
133  void dump_config() override;
134  bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
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.
140  void set_paused(bool paused, bool show_snow);
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 
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);
157  void add_page(LvPageType *page);
158  void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);
159  void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
160  void show_prev_page(lv_scr_load_anim_t anim, uint32_t time);
161  void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
162  void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); }
163  void restore_focus_mark(lv_group_t *group) {
164  auto *mark = this->focus_marks_[group];
165  if (mark != nullptr) {
166  lv_group_focus_obj(mark);
167  }
168  }
169  // rounding factor to align bounds of update area when drawing
170  size_t draw_rounding{2};
171 
173 
174  protected:
175  void write_random_();
176  void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
177  void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
178 
179  std::vector<display::Display *> displays_{};
180  size_t buffer_frac_{1};
181  bool full_refresh_{};
182  bool resume_on_input_{};
183 
184  lv_disp_draw_buf_t draw_buf_{};
185  lv_disp_drv_t disp_drv_{};
186  lv_disp_t *disp_{};
187  bool paused_{};
188  std::vector<LvPageType *> pages_{};
189  size_t current_page_{0};
190  bool show_snow_{};
191  bool page_wrap_{true};
192  std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
193 
195  CallbackManager<void(bool)> pause_callbacks_{};
196  lv_color_t *rotate_buf_{};
197 };
198 
199 class IdleTrigger : public Trigger<> {
200  public:
201  explicit IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeout);
202 
203  protected:
205  bool is_idle_{};
206 };
207 
208 class PauseTrigger : public Trigger<> {
209  public:
210  explicit PauseTrigger(LvglComponent *parent, TemplatableValue<bool> paused);
211 
212  protected:
214 };
215 
216 template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
217  public:
218  explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
219  void play(Ts... x) override { this->action_(this->parent_); }
220 
221  protected:
222  std::function<void(LvglComponent *)> action_{};
223 };
224 
225 template<typename... Ts> class LvglCondition : public Condition<Ts...>, public Parented<LvglComponent> {
226  public:
227  LvglCondition(std::function<bool(LvglComponent *)> &&condition_lambda)
228  : condition_lambda_(std::move(condition_lambda)) {}
229  bool check(Ts... x) override { return this->condition_lambda_(this->parent_); }
230 
231  protected:
232  std::function<bool(LvglComponent *)> condition_lambda_{};
233 };
234 
235 #ifdef USE_LVGL_TOUCHSCREEN
236 class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
237  public:
238  LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent);
239  void update(const touchscreen::TouchPoints_t &tpoints) override;
240  void release() override {
241  touch_pressed_ = false;
242  this->parent_->maybe_wakeup();
243  }
244  lv_indev_drv_t *get_drv() { return &this->drv_; }
245 
246  protected:
247  lv_indev_drv_t drv_{};
248  touchscreen::TouchPoint touch_point_{};
249  bool touch_pressed_{};
250 };
251 #endif // USE_LVGL_TOUCHSCREEN
252 
253 #ifdef USE_LVGL_KEY_LISTENER
254 class LVEncoderListener : public Parented<LvglComponent> {
255  public:
256  LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt);
257 
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); });
261  }
262 #endif
263 
264 #ifdef USE_LVGL_ROTARY_ENCODER
266  sensor->register_listener([this](int32_t count) { this->set_count(count); });
267  }
268 #endif // USE_LVGL_ROTARY_ENCODER
269 
270  void event(int key, bool pressed) {
271  if (!this->parent_->is_paused()) {
272  this->pressed_ = pressed;
273  this->key_ = key;
274  } else if (!pressed) {
275  // maybe wakeup on release if paused
276  this->parent_->maybe_wakeup();
277  }
278  }
279 
280  void set_count(int32_t count) {
281  if (!this->parent_->is_paused()) {
282  this->count_ = count;
283  } else {
284  this->parent_->maybe_wakeup();
285  }
286  }
287 
288  lv_indev_drv_t *get_drv() { return &this->drv_; }
289 
290  protected:
291  lv_indev_drv_t drv_{};
292  bool pressed_{};
293  int32_t count_{};
294  int32_t last_count_{};
295  int key_{};
296 };
297 #endif // USE_LVGL_KEY_LISTENER
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 
341 #ifdef USE_LVGL_BUTTONMATRIX
343  public:
344  void set_obj(lv_obj_t *lv_obj) override;
345  uint16_t get_selected() { return lv_btnmatrix_get_selected_btn(this->obj); }
346  void set_key(size_t idx, uint8_t key) { this->key_map_[idx] = key; }
347 
348  protected:
349  std::map<size_t, uint8_t> key_map_{};
350 };
351 #endif // USE_LVGL_BUTTONMATRIX
352 
353 #ifdef USE_LVGL_KEYBOARD
355  public:
356  void set_obj(lv_obj_t *lv_obj) override;
357 };
358 #endif // USE_LVGL_KEYBOARD
359 } // namespace lvgl
360 } // 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:129
void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor)
Definition: lvgl_esphome.h:265
void loop()
void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image)
Definition: lvgl_esphome.h:56
LvglCondition(std::function< bool(LvglComponent *)> &&condition_lambda)
Definition: lvgl_esphome.h:227
bool check(Ts... x) override
Definition: lvgl_esphome.h:229
std::vector< TouchPoint > TouchPoints_t
Definition: touchscreen.h:30
bool is_idle(uint32_t idle_ms)
Definition: lvgl_esphome.h:134
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:329
interface for components that provide keypresses
Definition: key_provider.h:10
MediaPlayerStateTrigger< MediaPlayerState::MEDIA_PLAYER_STATE_IDLE > IdleTrigger
Definition: automation.h:56
float get_setup_priority() const override
Definition: lvgl_esphome.h:125
void set_option_string(const char *options) override
Definition: lvgl_esphome.h:321
lv_indev_drv_t * get_drv()
Definition: lvgl_esphome.h:244
uint16_t x
Definition: tt21100.cpp:17
void play(Ts... x) override
Definition: lvgl_esphome.h:219
STL namespace.
Component for rendering LVGL.
Definition: lvgl_esphome.h:117
This class simplifies creating components that periodically check a state.
Definition: component.h:283
void set_mode(lv_roller_mode_t mode)
Definition: lvgl_esphome.h:332
void add_button(binary_sensor::BinarySensor *button, lv_key_t key)
Definition: lvgl_esphome.h:259
void(_lv_event_t *) event_callback_t
Definition: lvgl_esphome.h:83
TemplatableValue< bool > paused_
Definition: lvgl_esphome.h:213
lv_event_code_t lv_update_event
std::function< void(float)> set_value_lambda_t
Definition: lvgl_esphome.h:82
lv_event_code_t lv_api_event
void restore_focus_mark(lv_group_t *group)
Definition: lvgl_esphome.h:163
void setup(size_t index)
Definition: lvgl_esphome.h:72
virtual void set_obj(lv_obj_t *lv_obj)
Definition: lvgl_esphome.h:64
Base class for all automation conditions.
Definition: automation.h:74
void add_on_pause_callback(std::function< void(bool)> &&callback)
Definition: lvgl_esphome.h:132
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:183
void set_page_wrap(bool wrap)
Definition: lvgl_esphome.h:161
std::function< void(Ts...)> lamb_
Definition: lvgl_esphome.h:93
MediaPlayerStateTrigger< MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED > PauseTrigger
Definition: automation.h:58
LvglAction(std::function< void(LvglComponent *)> &&lamb)
Definition: lvgl_esphome.h:218
uint8_t type
const float PROCESSOR
For components that use data from sensors like displays.
Definition: component.cpp:20
uint8_t options
size_t get_selected_index() override
Definition: lvgl_esphome.h:328
TemplatableValue< uint32_t > timeout_
Definition: lvgl_esphome.h:204
lv_img_dsc_t * get_lv_img_dsc()
Definition: image.cpp:83
std::function< void(lv_obj_t *)> LvLambdaType
Definition: lvgl_esphome.h:81
void set_key(size_t idx, uint8_t key)
Definition: lvgl_esphome.h:346
void event(int key, bool pressed)
Definition: lvgl_esphome.h:270
std::function< const char *()> text_lambda_t
Definition: lvgl_esphome.h:84
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:162
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:306
void set_count(int32_t count)
Definition: lvgl_esphome.h:280
void lv_animimg_stop(lv_obj_t *obj)
size_t get_selected_index() override
Definition: lvgl_esphome.h:317
ObjUpdateAction(std::function< void(Ts...)> &&lamb)
Definition: lvgl_esphome.h:88
esphome::sensor::Sensor * sensor
Definition: statsd.h:38
Helper class to easily give an object a parent of type T.
Definition: helpers.h:522
void play(Ts... x) override
Definition: lvgl_esphome.h:90
bool state
Definition: fan.h:34
void set_selected_index(size_t index, lv_anim_enable_t anim) override
Definition: lvgl_esphome.h:318
void set_option_string(const char *options) override
Definition: lvgl_esphome.h:335