mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	[lvgl] Add restore_value to select and number (#8494)
This commit is contained in:
		
				
					committed by
					
						 Jesse Hills
						Jesse Hills
					
				
			
			
				
	
			
			
			
						parent
						
							566968b6be
						
					
				
				
					commit
					c215098cb7
				
			| @@ -173,7 +173,8 @@ class LambdaContext(CodeContext): | |||||||
|  |  | ||||||
| class LvContext(LambdaContext): | class LvContext(LambdaContext): | ||||||
|     """ |     """ | ||||||
|     Code generation into the LVGL initialisation code (called in `setup()`) |     Code generation into the LVGL initialisation code, called before setup() and loop() | ||||||
|  |     Basically just does cg.add, so now fairly redundant. | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     added_lambda_count = 0 |     added_lambda_count = 0 | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| from esphome.components import number | from esphome.components import number | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome.const import CONF_RESTORE_VALUE | ||||||
| from esphome.cpp_generator import MockObj | from esphome.cpp_generator import MockObj | ||||||
|  |  | ||||||
| from ..defines import CONF_ANIMATED, CONF_UPDATE_ON_RELEASE, CONF_WIDGET | from ..defines import CONF_ANIMATED, CONF_UPDATE_ON_RELEASE, CONF_WIDGET | ||||||
| @@ -10,21 +11,21 @@ from ..lvcode import ( | |||||||
|     EVENT_ARG, |     EVENT_ARG, | ||||||
|     UPDATE_EVENT, |     UPDATE_EVENT, | ||||||
|     LambdaContext, |     LambdaContext, | ||||||
|     LvContext, |     ReturnStatement, | ||||||
|     lv, |     lv, | ||||||
|     lv_add, |  | ||||||
|     lvgl_static, |     lvgl_static, | ||||||
| ) | ) | ||||||
| from ..types import LV_EVENT, LvNumber, lvgl_ns | from ..types import LV_EVENT, LvNumber, lvgl_ns | ||||||
| from ..widgets import get_widgets, wait_for_widgets | from ..widgets import get_widgets, wait_for_widgets | ||||||
|  |  | ||||||
| LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number) | LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number, cg.Component) | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = number.number_schema(LVGLNumber).extend( | CONFIG_SCHEMA = number.number_schema(LVGLNumber).extend( | ||||||
|     { |     { | ||||||
|         cv.Required(CONF_WIDGET): cv.use_id(LvNumber), |         cv.Required(CONF_WIDGET): cv.use_id(LvNumber), | ||||||
|         cv.Optional(CONF_ANIMATED, default=True): animated, |         cv.Optional(CONF_ANIMATED, default=True): animated, | ||||||
|         cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean, |         cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean, | ||||||
|  |         cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, | ||||||
|     } |     } | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -32,32 +33,34 @@ CONFIG_SCHEMA = number.number_schema(LVGLNumber).extend( | |||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     widget = await get_widgets(config, CONF_WIDGET) |     widget = await get_widgets(config, CONF_WIDGET) | ||||||
|     widget = widget[0] |     widget = widget[0] | ||||||
|     var = await number.new_number( |  | ||||||
|         config, |  | ||||||
|         max_value=widget.get_max(), |  | ||||||
|         min_value=widget.get_min(), |  | ||||||
|         step=widget.get_step(), |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     await wait_for_widgets() |     await wait_for_widgets() | ||||||
|  |     async with LambdaContext([], return_type=cg.float_) as value: | ||||||
|  |         value.add(ReturnStatement(widget.get_value())) | ||||||
|     async with LambdaContext([(cg.float_, "v")]) as control: |     async with LambdaContext([(cg.float_, "v")]) as control: | ||||||
|         await widget.set_property( |         await widget.set_property( | ||||||
|             "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] |             "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] | ||||||
|         ) |         ) | ||||||
|         lv.event_send(widget.obj, API_EVENT, cg.nullptr) |         lv.event_send(widget.obj, API_EVENT, cg.nullptr) | ||||||
|         control.add(var.publish_state(widget.get_value())) |  | ||||||
|     async with LambdaContext(EVENT_ARG) as event: |  | ||||||
|         event.add(var.publish_state(widget.get_value())) |  | ||||||
|     event_code = ( |     event_code = ( | ||||||
|         LV_EVENT.VALUE_CHANGED |         LV_EVENT.VALUE_CHANGED | ||||||
|         if not config[CONF_UPDATE_ON_RELEASE] |         if not config[CONF_UPDATE_ON_RELEASE] | ||||||
|         else LV_EVENT.RELEASED |         else LV_EVENT.RELEASED | ||||||
|     ) |     ) | ||||||
|     async with LvContext(): |     var = await number.new_number( | ||||||
|         lv_add(var.set_control_lambda(await control.get_lambda())) |         config, | ||||||
|         lv_add( |         await control.get_lambda(), | ||||||
|  |         await value.get_lambda(), | ||||||
|  |         event_code, | ||||||
|  |         config[CONF_RESTORE_VALUE], | ||||||
|  |         max_value=widget.get_max(), | ||||||
|  |         min_value=widget.get_min(), | ||||||
|  |         step=widget.get_step(), | ||||||
|  |     ) | ||||||
|  |     async with LambdaContext(EVENT_ARG) as event: | ||||||
|  |         event.add(var.on_value()) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     cg.add( | ||||||
|         lvgl_static.add_event_cb( |         lvgl_static.add_event_cb( | ||||||
|             widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code |             widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code | ||||||
|         ) |         ) | ||||||
|     ) |     ) | ||||||
|         lv_add(var.publish_state(widget.get_value())) |  | ||||||
|   | |||||||
| @@ -3,33 +3,46 @@ | |||||||
| #include <utility> | #include <utility> | ||||||
|  |  | ||||||
| #include "esphome/components/number/number.h" | #include "esphome/components/number/number.h" | ||||||
| #include "esphome/core/automation.h" |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/preferences.h" | #include "esphome/core/preferences.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace lvgl { | namespace lvgl { | ||||||
|  |  | ||||||
| class LVGLNumber : public number::Number { | class LVGLNumber : public number::Number, public Component { | ||||||
|  public: |  public: | ||||||
|   void set_control_lambda(std::function<void(float)> control_lambda) { |   LVGLNumber(std::function<void(float)> control_lambda, std::function<float()> value_lambda, lv_event_code_t event, | ||||||
|     this->control_lambda_ = std::move(control_lambda); |              bool restore) | ||||||
|     if (this->initial_state_.has_value()) { |       : control_lambda_(std::move(control_lambda)), | ||||||
|       this->control_lambda_(this->initial_state_.value()); |         value_lambda_(std::move(value_lambda)), | ||||||
|       this->initial_state_.reset(); |         event_(event), | ||||||
|  |         restore_(restore) {} | ||||||
|  |  | ||||||
|  |   void setup() override { | ||||||
|  |     float value = this->value_lambda_(); | ||||||
|  |     if (this->restore_) { | ||||||
|  |       this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash()); | ||||||
|  |       if (this->pref_.load(&value)) { | ||||||
|  |         this->control_lambda_(value); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     this->publish_state(value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void on_value() { this->publish_state(this->value_lambda_()); } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void control(float value) override { |   void control(float value) override { | ||||||
|     if (this->control_lambda_ != nullptr) { |  | ||||||
|     this->control_lambda_(value); |     this->control_lambda_(value); | ||||||
|     } else { |     this->publish_state(value); | ||||||
|       this->initial_state_ = value; |     if (this->restore_) | ||||||
|  |       this->pref_.save(&value); | ||||||
|   } |   } | ||||||
|   } |   std::function<void(float)> control_lambda_; | ||||||
|   std::function<void(float)> control_lambda_{}; |   std::function<float()> value_lambda_; | ||||||
|   optional<float> initial_state_{}; |   lv_event_code_t event_; | ||||||
|  |   bool restore_; | ||||||
|  |   ESPPreferenceObject pref_{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace lvgl | }  // namespace lvgl | ||||||
|   | |||||||
| @@ -1,18 +1,19 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
| from esphome.components import select | from esphome.components import select | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.const import CONF_OPTIONS | from esphome.const import CONF_ID, CONF_OPTIONS, CONF_RESTORE_VALUE | ||||||
|  |  | ||||||
| from ..defines import CONF_ANIMATED, CONF_WIDGET, literal | from ..defines import CONF_ANIMATED, CONF_WIDGET, literal | ||||||
| from ..lvcode import LvContext |  | ||||||
| from ..types import LvSelect, lvgl_ns | from ..types import LvSelect, lvgl_ns | ||||||
| from ..widgets import get_widgets, wait_for_widgets | from ..widgets import get_widgets | ||||||
|  |  | ||||||
| LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select) | LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select, cg.Component) | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = select.select_schema(LVGLSelect).extend( | CONFIG_SCHEMA = select.select_schema(LVGLSelect).extend( | ||||||
|     { |     { | ||||||
|         cv.Required(CONF_WIDGET): cv.use_id(LvSelect), |         cv.Required(CONF_WIDGET): cv.use_id(LvSelect), | ||||||
|         cv.Optional(CONF_ANIMATED, default=False): cv.boolean, |         cv.Optional(CONF_ANIMATED, default=False): cv.boolean, | ||||||
|  |         cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, | ||||||
|     } |     } | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -21,12 +22,9 @@ async def to_code(config): | |||||||
|     widget = await get_widgets(config, CONF_WIDGET) |     widget = await get_widgets(config, CONF_WIDGET) | ||||||
|     widget = widget[0] |     widget = widget[0] | ||||||
|     options = widget.config.get(CONF_OPTIONS, []) |     options = widget.config.get(CONF_OPTIONS, []) | ||||||
|     selector = await select.new_select(config, options=options) |     animated = literal("LV_ANIM_ON" if config[CONF_ANIMATED] else "LV_ANIM_OFF") | ||||||
|     await wait_for_widgets() |     selector = cg.new_Pvariable( | ||||||
|     async with LvContext() as ctx: |         config[CONF_ID], widget.var, animated, config[CONF_RESTORE_VALUE] | ||||||
|         ctx.add( |  | ||||||
|             selector.set_widget( |  | ||||||
|                 widget.var, |  | ||||||
|                 literal("LV_ANIM_ON" if config[CONF_ANIMATED] else "LV_ANIM_OFF"), |  | ||||||
|             ) |  | ||||||
|     ) |     ) | ||||||
|  |     await select.register_select(selector, config, options=options) | ||||||
|  |     await cg.register_component(selector, config) | ||||||
|   | |||||||
| @@ -11,12 +11,20 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace lvgl { | namespace lvgl { | ||||||
|  |  | ||||||
| class LVGLSelect : public select::Select { | class LVGLSelect : public select::Select, public Component { | ||||||
|  public: |  public: | ||||||
|   void set_widget(LvSelectable *widget, lv_anim_enable_t anim = LV_ANIM_OFF) { |   LVGLSelect(LvSelectable *widget, lv_anim_enable_t anim, bool restore) | ||||||
|     this->widget_ = widget; |       : widget_(widget), anim_(anim), restore_(restore) {} | ||||||
|     this->anim_ = anim; |  | ||||||
|  |   void setup() override { | ||||||
|     this->set_options_(); |     this->set_options_(); | ||||||
|  |     if (this->restore_) { | ||||||
|  |       size_t index; | ||||||
|  |       this->pref_ = global_preferences->make_preference<size_t>(this->get_object_id_hash()); | ||||||
|  |       if (this->pref_.load(&index)) | ||||||
|  |         this->widget_->set_selected_index(index, LV_ANIM_OFF); | ||||||
|  |     } | ||||||
|  |     this->publish(); | ||||||
|     lv_obj_add_event_cb( |     lv_obj_add_event_cb( | ||||||
|         this->widget_->obj, |         this->widget_->obj, | ||||||
|         [](lv_event_t *e) { |         [](lv_event_t *e) { | ||||||
| @@ -24,11 +32,6 @@ class LVGLSelect : public select::Select { | |||||||
|           it->set_options_(); |           it->set_options_(); | ||||||
|         }, |         }, | ||||||
|         LV_EVENT_REFRESH, this); |         LV_EVENT_REFRESH, this); | ||||||
|     if (this->initial_state_.has_value()) { |  | ||||||
|       this->control(this->initial_state_.value()); |  | ||||||
|       this->initial_state_.reset(); |  | ||||||
|     } |  | ||||||
|     this->publish(); |  | ||||||
|     auto lamb = [](lv_event_t *e) { |     auto lamb = [](lv_event_t *e) { | ||||||
|       auto *self = static_cast<LVGLSelect *>(e->user_data); |       auto *self = static_cast<LVGLSelect *>(e->user_data); | ||||||
|       self->publish(); |       self->publish(); | ||||||
| @@ -37,21 +40,25 @@ class LVGLSelect : public select::Select { | |||||||
|     lv_obj_add_event_cb(this->widget_->obj, lamb, lv_update_event, this); |     lv_obj_add_event_cb(this->widget_->obj, lamb, lv_update_event, this); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void publish() { this->publish_state(this->widget_->get_selected_text()); } |   void publish() { | ||||||
|  |     this->publish_state(this->widget_->get_selected_text()); | ||||||
|  |     if (this->restore_) { | ||||||
|  |       auto index = this->widget_->get_selected_index(); | ||||||
|  |       this->pref_.save(&index); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void control(const std::string &value) override { |   void control(const std::string &value) override { | ||||||
|     if (this->widget_ != nullptr) { |  | ||||||
|     this->widget_->set_selected_text(value, this->anim_); |     this->widget_->set_selected_text(value, this->anim_); | ||||||
|     } else { |     this->publish(); | ||||||
|       this->initial_state_ = value; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|   void set_options_() { this->traits.set_options(this->widget_->get_options()); } |   void set_options_() { this->traits.set_options(this->widget_->get_options()); } | ||||||
|  |  | ||||||
|   LvSelectable *widget_{}; |   LvSelectable *widget_; | ||||||
|   optional<std::string> initial_state_{}; |   lv_anim_enable_t anim_; | ||||||
|   lv_anim_enable_t anim_{LV_ANIM_OFF}; |   bool restore_; | ||||||
|  |   ESPPreferenceObject pref_{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace lvgl | }  // namespace lvgl | ||||||
|   | |||||||
| @@ -38,6 +38,7 @@ number: | |||||||
|     widget: slider_id |     widget: slider_id | ||||||
|     name: LVGL Slider |     name: LVGL Slider | ||||||
|     update_on_release: true |     update_on_release: true | ||||||
|  |     restore_value: true | ||||||
|   - platform: lvgl |   - platform: lvgl | ||||||
|     widget: lv_arc |     widget: lv_arc | ||||||
|     id: lvgl_arc_number |     id: lvgl_arc_number | ||||||
|   | |||||||
| @@ -990,3 +990,13 @@ color: | |||||||
|     green_int: 123 |     green_int: 123 | ||||||
|     blue_int: 64 |     blue_int: 64 | ||||||
|     white_int: 255 |     white_int: 255 | ||||||
|  |  | ||||||
|  | select: | ||||||
|  |   - platform: lvgl | ||||||
|  |     id: lv_roller_select | ||||||
|  |     widget: lv_roller | ||||||
|  |     restore_value: true | ||||||
|  |   - platform: lvgl | ||||||
|  |     id: lv_dropdown_select | ||||||
|  |     widget: lv_dropdown | ||||||
|  |     restore_value: false | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user