mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-06-06 21:09:53 +02:00
[lvgl] PR stage 3 (#7160)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
@@ -1,23 +1,32 @@
|
||||
#pragma once
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_LVGL
|
||||
|
||||
#ifdef USE_LVGL_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif // USE_LVGL_BINARY_SENSOR
|
||||
#ifdef USE_LVGL_ROTARY_ENCODER
|
||||
#include "esphome/components/rotary_encoder/rotary_encoder.h"
|
||||
#endif // USE_LVGL_ROTARY_ENCODER
|
||||
|
||||
// required for clang-tidy
|
||||
#ifndef LV_CONF_H
|
||||
#define LV_CONF_SKIP 1 // NOLINT
|
||||
#endif
|
||||
#endif // LV_CONF_H
|
||||
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esphome/components/display/display_color_utils.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <lvgl.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#ifdef USE_LVGL_IMAGE
|
||||
#include "esphome/components/image/image.h"
|
||||
#endif // USE_LVGL_IMAGE
|
||||
|
||||
#ifdef USE_LVGL_FONT
|
||||
#include "esphome/components/font/font.h"
|
||||
#endif
|
||||
#endif // USE_LVGL_FONT
|
||||
#ifdef USE_LVGL_TOUCHSCREEN
|
||||
#include "esphome/components/touchscreen/touchscreen.h"
|
||||
#endif // USE_LVGL_TOUCHSCREEN
|
||||
@@ -40,7 +49,7 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT
|
||||
// Parent class for things that wrap an LVGL object
|
||||
class LvCompound final {
|
||||
public:
|
||||
virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
|
||||
void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
|
||||
lv_obj_t *obj{};
|
||||
};
|
||||
|
||||
@@ -49,6 +58,15 @@ using set_value_lambda_t = std::function<void(float)>;
|
||||
using event_callback_t = void(_lv_event_t *);
|
||||
using text_lambda_t = std::function<const char *()>;
|
||||
|
||||
template<typename... Ts> class ObjUpdateAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ObjUpdateAction(std::function<void(Ts...)> &&lamb) : lamb_(std::move(lamb)) {}
|
||||
|
||||
void play(Ts... x) override { this->lamb_(x...); }
|
||||
|
||||
protected:
|
||||
std::function<void(Ts...)> lamb_;
|
||||
};
|
||||
#ifdef USE_LVGL_FONT
|
||||
class FontEngine {
|
||||
public:
|
||||
@@ -67,6 +85,9 @@ class FontEngine {
|
||||
lv_font_t lv_font_{};
|
||||
};
|
||||
#endif // USE_LVGL_FONT
|
||||
#ifdef USE_LVGL_IMAGE
|
||||
lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc = nullptr);
|
||||
#endif // USE_LVGL_IMAGE
|
||||
|
||||
class LvglComponent : public PollingComponent {
|
||||
constexpr static const char *const TAG = "lvgl";
|
||||
@@ -92,27 +113,54 @@ class LvglComponent : public PollingComponent {
|
||||
area->y2++;
|
||||
}
|
||||
|
||||
void loop() override { lv_timer_handler_run_in_period(5); }
|
||||
void setup() override;
|
||||
|
||||
void update() override {}
|
||||
void update() override {
|
||||
// update indicators
|
||||
if (this->paused_) {
|
||||
return;
|
||||
}
|
||||
this->idle_callbacks_.call(lv_disp_get_inactive_time(this->disp_));
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
if (this->paused_) {
|
||||
if (this->show_snow_)
|
||||
this->write_random_();
|
||||
}
|
||||
lv_timer_handler_run_in_period(5);
|
||||
}
|
||||
|
||||
void add_on_idle_callback(std::function<void(uint32_t)> &&callback) {
|
||||
this->idle_callbacks_.add(std::move(callback));
|
||||
}
|
||||
void add_display(display::Display *display) { this->displays_.push_back(display); }
|
||||
void add_init_lambda(const std::function<void(lv_disp_t *)> &lamb) { this->init_lambdas_.push_back(lamb); }
|
||||
void add_init_lambda(const std::function<void(LvglComponent *)> &lamb) { this->init_lambdas_.push_back(lamb); }
|
||||
void dump_config() override;
|
||||
void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; }
|
||||
bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
|
||||
void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; }
|
||||
lv_disp_t *get_disp() { return this->disp_; }
|
||||
void set_paused(bool paused, bool show_snow) {
|
||||
this->paused_ = paused;
|
||||
this->show_snow_ = show_snow;
|
||||
this->snow_line_ = 0;
|
||||
if (!paused && lv_scr_act() != nullptr) {
|
||||
lv_disp_trig_activity(this->disp_); // resets the inactivity time
|
||||
lv_obj_invalidate(lv_scr_act());
|
||||
}
|
||||
}
|
||||
|
||||
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
|
||||
lv_obj_add_event_cb(obj, callback, event, this);
|
||||
if (event == LV_EVENT_VALUE_CHANGED) {
|
||||
lv_obj_add_event_cb(obj, callback, lv_custom_event, this);
|
||||
}
|
||||
}
|
||||
bool is_paused() const { return this->paused_; }
|
||||
|
||||
protected:
|
||||
void write_random_();
|
||||
void draw_buffer_(const lv_area_t *area, const uint8_t *ptr);
|
||||
void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||||
std::vector<display::Display *> displays_{};
|
||||
@@ -120,12 +168,52 @@ class LvglComponent : public PollingComponent {
|
||||
lv_disp_drv_t disp_drv_{};
|
||||
lv_disp_t *disp_{};
|
||||
bool paused_{};
|
||||
bool show_snow_{};
|
||||
lv_coord_t snow_line_{};
|
||||
|
||||
std::vector<std::function<void(lv_disp_t *)>> init_lambdas_;
|
||||
std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_;
|
||||
CallbackManager<void(uint32_t)> idle_callbacks_{};
|
||||
size_t buffer_frac_{1};
|
||||
bool full_refresh_{};
|
||||
};
|
||||
|
||||
class IdleTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeout) : timeout_(std::move(timeout)) {
|
||||
parent->add_on_idle_callback([this](uint32_t idle_time) {
|
||||
if (!this->is_idle_ && idle_time > this->timeout_.value()) {
|
||||
this->is_idle_ = true;
|
||||
this->trigger();
|
||||
} else if (this->is_idle_ && idle_time < this->timeout_.value()) {
|
||||
this->is_idle_ = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
TemplatableValue<uint32_t> timeout_;
|
||||
bool is_idle_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
|
||||
public:
|
||||
explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
|
||||
void play(Ts... x) override { this->action_(this->parent_); }
|
||||
|
||||
protected:
|
||||
std::function<void(LvglComponent *)> action_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class LvglCondition : public Condition<Ts...>, public Parented<LvglComponent> {
|
||||
public:
|
||||
LvglCondition(std::function<bool(LvglComponent *)> &&condition_lambda)
|
||||
: condition_lambda_(std::move(condition_lambda)) {}
|
||||
bool check(Ts... x) override { return this->condition_lambda_(this->parent_); }
|
||||
|
||||
protected:
|
||||
std::function<bool(LvglComponent *)> condition_lambda_{};
|
||||
};
|
||||
|
||||
#ifdef USE_LVGL_TOUCHSCREEN
|
||||
class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
|
||||
public:
|
||||
@@ -160,7 +248,62 @@ class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglC
|
||||
bool touch_pressed_{};
|
||||
};
|
||||
#endif // USE_LVGL_TOUCHSCREEN
|
||||
|
||||
#ifdef USE_LVGL_KEY_LISTENER
|
||||
class LVEncoderListener : public Parented<LvglComponent> {
|
||||
public:
|
||||
LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt) {
|
||||
lv_indev_drv_init(&this->drv_);
|
||||
this->drv_.type = type;
|
||||
this->drv_.user_data = this;
|
||||
this->drv_.long_press_time = lpt;
|
||||
this->drv_.long_press_repeat_time = lprt;
|
||||
this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) {
|
||||
auto *l = static_cast<LVEncoderListener *>(d->user_data);
|
||||
data->state = l->pressed_ ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
data->key = l->key_;
|
||||
data->enc_diff = (int16_t) (l->count_ - l->last_count_);
|
||||
l->last_count_ = l->count_;
|
||||
data->continue_reading = false;
|
||||
};
|
||||
}
|
||||
|
||||
void set_left_button(binary_sensor::BinarySensor *left_button) {
|
||||
left_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_LEFT, state); });
|
||||
}
|
||||
void set_right_button(binary_sensor::BinarySensor *right_button) {
|
||||
right_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_RIGHT, state); });
|
||||
}
|
||||
|
||||
void set_enter_button(binary_sensor::BinarySensor *enter_button) {
|
||||
enter_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_ENTER, state); });
|
||||
}
|
||||
|
||||
void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor) {
|
||||
sensor->register_listener([this](int32_t count) { this->set_count(count); });
|
||||
}
|
||||
|
||||
void event(int key, bool pressed) {
|
||||
if (!this->parent_->is_paused()) {
|
||||
this->pressed_ = pressed;
|
||||
this->key_ = key;
|
||||
}
|
||||
}
|
||||
|
||||
void set_count(int32_t count) {
|
||||
if (!this->parent_->is_paused())
|
||||
this->count_ = count;
|
||||
}
|
||||
|
||||
lv_indev_drv_t *get_drv() { return &this->drv_; }
|
||||
|
||||
protected:
|
||||
lv_indev_drv_t drv_{};
|
||||
bool pressed_{};
|
||||
int32_t count_{};
|
||||
int32_t last_count_{};
|
||||
int key_{};
|
||||
};
|
||||
#endif // USE_LVGL_KEY_LISTENER
|
||||
} // namespace lvgl
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_LVGL
|
||||
|
||||
Reference in New Issue
Block a user