mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +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): | ||||
|     """ | ||||
|     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 | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import number | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import CONF_RESTORE_VALUE | ||||
| from esphome.cpp_generator import MockObj | ||||
|  | ||||
| from ..defines import CONF_ANIMATED, CONF_UPDATE_ON_RELEASE, CONF_WIDGET | ||||
| @@ -10,21 +11,21 @@ from ..lvcode import ( | ||||
|     EVENT_ARG, | ||||
|     UPDATE_EVENT, | ||||
|     LambdaContext, | ||||
|     LvContext, | ||||
|     ReturnStatement, | ||||
|     lv, | ||||
|     lv_add, | ||||
|     lvgl_static, | ||||
| ) | ||||
| from ..types import LV_EVENT, LvNumber, lvgl_ns | ||||
| 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( | ||||
|     { | ||||
|         cv.Required(CONF_WIDGET): cv.use_id(LvNumber), | ||||
|         cv.Optional(CONF_ANIMATED, default=True): animated, | ||||
|         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): | ||||
|     widget = await get_widgets(config, CONF_WIDGET) | ||||
|     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() | ||||
|     async with LambdaContext([], return_type=cg.float_) as value: | ||||
|         value.add(ReturnStatement(widget.get_value())) | ||||
|     async with LambdaContext([(cg.float_, "v")]) as control: | ||||
|         await widget.set_property( | ||||
|             "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] | ||||
|         ) | ||||
|         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 = ( | ||||
|         LV_EVENT.VALUE_CHANGED | ||||
|         if not config[CONF_UPDATE_ON_RELEASE] | ||||
|         else LV_EVENT.RELEASED | ||||
|     ) | ||||
|     async with LvContext(): | ||||
|         lv_add(var.set_control_lambda(await control.get_lambda())) | ||||
|         lv_add( | ||||
|     var = await number.new_number( | ||||
|         config, | ||||
|         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( | ||||
|             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 "esphome/components/number/number.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/preferences.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace lvgl { | ||||
|  | ||||
| class LVGLNumber : public number::Number { | ||||
| class LVGLNumber : public number::Number, public Component { | ||||
|  public: | ||||
|   void set_control_lambda(std::function<void(float)> control_lambda) { | ||||
|     this->control_lambda_ = std::move(control_lambda); | ||||
|     if (this->initial_state_.has_value()) { | ||||
|       this->control_lambda_(this->initial_state_.value()); | ||||
|       this->initial_state_.reset(); | ||||
|   LVGLNumber(std::function<void(float)> control_lambda, std::function<float()> value_lambda, lv_event_code_t event, | ||||
|              bool restore) | ||||
|       : control_lambda_(std::move(control_lambda)), | ||||
|         value_lambda_(std::move(value_lambda)), | ||||
|         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: | ||||
|   void control(float value) override { | ||||
|     if (this->control_lambda_ != nullptr) { | ||||
|     this->control_lambda_(value); | ||||
|     } else { | ||||
|       this->initial_state_ = value; | ||||
|     this->publish_state(value); | ||||
|     if (this->restore_) | ||||
|       this->pref_.save(&value); | ||||
|   } | ||||
|   } | ||||
|   std::function<void(float)> control_lambda_{}; | ||||
|   optional<float> initial_state_{}; | ||||
|   std::function<void(float)> control_lambda_; | ||||
|   std::function<float()> value_lambda_; | ||||
|   lv_event_code_t event_; | ||||
|   bool restore_; | ||||
|   ESPPreferenceObject pref_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace lvgl | ||||
|   | ||||
| @@ -1,18 +1,19 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import select | ||||
| 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 ..lvcode import LvContext | ||||
| 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( | ||||
|     { | ||||
|         cv.Required(CONF_WIDGET): cv.use_id(LvSelect), | ||||
|         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 = widget[0] | ||||
|     options = widget.config.get(CONF_OPTIONS, []) | ||||
|     selector = await select.new_select(config, options=options) | ||||
|     await wait_for_widgets() | ||||
|     async with LvContext() as ctx: | ||||
|         ctx.add( | ||||
|             selector.set_widget( | ||||
|                 widget.var, | ||||
|                 literal("LV_ANIM_ON" if config[CONF_ANIMATED] else "LV_ANIM_OFF"), | ||||
|             ) | ||||
|     animated = literal("LV_ANIM_ON" if config[CONF_ANIMATED] else "LV_ANIM_OFF") | ||||
|     selector = cg.new_Pvariable( | ||||
|         config[CONF_ID], widget.var, animated, config[CONF_RESTORE_VALUE] | ||||
|     ) | ||||
|     await select.register_select(selector, config, options=options) | ||||
|     await cg.register_component(selector, config) | ||||
|   | ||||
| @@ -11,12 +11,20 @@ | ||||
| namespace esphome { | ||||
| namespace lvgl { | ||||
|  | ||||
| class LVGLSelect : public select::Select { | ||||
| class LVGLSelect : public select::Select, public Component { | ||||
|  public: | ||||
|   void set_widget(LvSelectable *widget, lv_anim_enable_t anim = LV_ANIM_OFF) { | ||||
|     this->widget_ = widget; | ||||
|     this->anim_ = anim; | ||||
|   LVGLSelect(LvSelectable *widget, lv_anim_enable_t anim, bool restore) | ||||
|       : widget_(widget), anim_(anim), restore_(restore) {} | ||||
|  | ||||
|   void setup() override { | ||||
|     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( | ||||
|         this->widget_->obj, | ||||
|         [](lv_event_t *e) { | ||||
| @@ -24,11 +32,6 @@ class LVGLSelect : public select::Select { | ||||
|           it->set_options_(); | ||||
|         }, | ||||
|         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 *self = static_cast<LVGLSelect *>(e->user_data); | ||||
|       self->publish(); | ||||
| @@ -37,21 +40,25 @@ class LVGLSelect : public select::Select { | ||||
|     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: | ||||
|   void control(const std::string &value) override { | ||||
|     if (this->widget_ != nullptr) { | ||||
|     this->widget_->set_selected_text(value, this->anim_); | ||||
|     } else { | ||||
|       this->initial_state_ = value; | ||||
|     } | ||||
|     this->publish(); | ||||
|   } | ||||
|   void set_options_() { this->traits.set_options(this->widget_->get_options()); } | ||||
|  | ||||
|   LvSelectable *widget_{}; | ||||
|   optional<std::string> initial_state_{}; | ||||
|   lv_anim_enable_t anim_{LV_ANIM_OFF}; | ||||
|   LvSelectable *widget_; | ||||
|   lv_anim_enable_t anim_; | ||||
|   bool restore_; | ||||
|   ESPPreferenceObject pref_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace lvgl | ||||
|   | ||||
| @@ -38,6 +38,7 @@ number: | ||||
|     widget: slider_id | ||||
|     name: LVGL Slider | ||||
|     update_on_release: true | ||||
|     restore_value: true | ||||
|   - platform: lvgl | ||||
|     widget: lv_arc | ||||
|     id: lvgl_arc_number | ||||
|   | ||||
| @@ -990,3 +990,13 @@ color: | ||||
|     green_int: 123 | ||||
|     blue_int: 64 | ||||
|     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