diff --git a/esphome/components/lvgl/animation.h b/esphome/components/lvgl/animation.h index 1e0abce358..26bb433f87 100644 --- a/esphome/components/lvgl/animation.h +++ b/esphome/components/lvgl/animation.h @@ -21,12 +21,22 @@ class LvAnimationTiming { class LvAnimationTimingRoundTrip : public LvAnimationTiming { public: + // moving_length_ is the fraction of progress spent moving in each direction, in (0, 0.5]. + // Callers must pass pause in [0, 1) -- pause == 1.0 would make moving_length_ zero and divide by zero below. + LvAnimationTimingRoundTrip(float pause) : moving_length_((1.0f - pause) / 2.0f) {} float map_progress(float value) override { - value *= 2.0f; - if (value > 1.0f) - return 2.0f - value; - return value; + if (value < this->moving_length_) { + return value / this->moving_length_; + } + if (value > 1.0f - this->moving_length_) { + return (1.0f - value) / this->moving_length_; + } + // pause in the middle + return 1.0f; } + + protected: + float moving_length_{}; }; class LvAnimationTimingGravity : public LvAnimationTiming { diff --git a/esphome/components/lvgl/animation.py b/esphome/components/lvgl/animation.py index 2b1500f2c4..95d45de5ea 100644 --- a/esphome/components/lvgl/animation.py +++ b/esphome/components/lvgl/animation.py @@ -42,6 +42,7 @@ LvAnimationTimingRoundTrip = lvgl_ns.class_("LvAnimationTimingRoundTrip") LvAnimationTimingEaseInOut = lvgl_ns.class_("LvAnimationTimingEaseInOut") CONF_BOUNCE = "bounce" +CONF_PAUSE = "pause" def timing_class(name, extras=None): @@ -60,10 +61,20 @@ TIMING_SCHEMA = cv.maybe_simple_value( cv.typed_schema( dict( [ - timing_class("round_trip"), + timing_class( + "round_trip", + { + cv.Optional(CONF_PAUSE, default=0.0): cv.All( + cv.percentage, + cv.float_range( + min=0.0, max=1.0, min_included=True, max_included=False + ), + ) + }, + ), timing_class( "ease_in_out", - {cv.Optional(CONF_WEIGHT, default=2.0): lv_positive_float}, + {cv.Optional(CONF_WEIGHT, default=1.0): cv.zero_to_one_float}, ), timing_class( "gravity", diff --git a/tests/component_tests/lvgl/test_animation.py b/tests/component_tests/lvgl/test_animation.py index 1a2cde632c..ce9a162d99 100644 --- a/tests/component_tests/lvgl/test_animation.py +++ b/tests/component_tests/lvgl/test_animation.py @@ -169,14 +169,27 @@ class TestTimingSchema: def test_round_trip_string(self) -> None: assert TIMING_SCHEMA("round_trip")["type"] == "round_trip" + def test_round_trip_default_pause(self) -> None: + # Back-compat default: no pause, matching the pre-existing round_trip behavior. + assert TIMING_SCHEMA("round_trip")["pause"] == pytest.approx(0.0) + + def test_round_trip_pause_percentage_string(self) -> None: + result = TIMING_SCHEMA({"type": "round_trip", "pause": "50%"}) + assert result["pause"] == pytest.approx(0.5) + + def test_round_trip_pause_rejects_one(self) -> None: + # pause == 1.0 would make moving_length_ zero and divide by zero in map_progress. + with pytest.raises((Invalid, MultipleInvalid)): + TIMING_SCHEMA({"type": "round_trip", "pause": 1.0}) + def test_ease_in_out_default_weight(self) -> None: result = TIMING_SCHEMA("ease_in_out") assert result["type"] == "ease_in_out" - assert result["weight"] == pytest.approx(2.0) + assert result["weight"] == pytest.approx(1.0) def test_ease_in_out_custom_weight(self) -> None: - result = TIMING_SCHEMA({"type": "ease_in_out", "weight": 3}) - assert result["weight"] == pytest.approx(3.0) + result = TIMING_SCHEMA({"type": "ease_in_out", "weight": 0.5}) + assert result["weight"] == pytest.approx(0.5) def test_gravity_defaults(self) -> None: result = TIMING_SCHEMA("gravity") diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 903480dde1..46c1fd362a 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -174,7 +174,8 @@ lvgl: - id: anim_color duration: 2s timing: - - round_trip + - type: round_trip + pause: 0.5 - type: gravity bounce: 0.3 acceleration: 0.8 @@ -1557,17 +1558,20 @@ font: image: - id: cat_image + platform: file resize: 256x48 file: $component_dir/logo-text.svg type: RGB565 transparency: alpha_channel - id: dog_image + platform: file file: $component_dir/logo-text.svg resize: 256x48 type: BINARY transparency: chroma_key - id: alert + platform: file file: $component_dir/logo-text.svg type: grayscale resize: 100x100 diff --git a/tests/components/lvgl/test.host.yaml b/tests/components/lvgl/test.host.yaml index 90cbb3c0a5..3fa54fa3d6 100644 --- a/tests/components/lvgl/test.host.yaml +++ b/tests/components/lvgl/test.host.yaml @@ -39,7 +39,7 @@ lvgl: timing: - round_trip - type: ease_in_out - weight: 3 + weight: 0.5 on_start: - logger.log: anim started on_stop: