From 01a2aaef0811e1cd4b270ac3c44729fd9911ad89 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 26 Sep 2026 01:32:57 +0100 Subject: [PATCH] [motion] Use register_apply_action for motion.clear_calibration (#19700) --- esphome/components/motion/__init__.py | 12 +----- .../components/motion/motion_component.cpp | 5 ++- esphome/components/motion/motion_component.h | 21 ++-------- tests/component_tests/motion/test_motion.py | 39 ------------------- tests/components/qmi8658/common-actions.yaml | 7 ++++ .../qmi8658/test-actions.esp8266-ard.yaml | 4 ++ 6 files changed, 20 insertions(+), 68 deletions(-) create mode 100644 tests/components/qmi8658/common-actions.yaml create mode 100644 tests/components/qmi8658/test-actions.esp8266-ard.yaml diff --git a/esphome/components/motion/__init__.py b/esphome/components/motion/__init__.py index aea052fa2f..63a104f1a7 100644 --- a/esphome/components/motion/__init__.py +++ b/esphome/components/motion/__init__.py @@ -25,7 +25,6 @@ CONF_TRANSFORM_MATRIX = "transform_matrix" CalibrateLevelAction = motion_ns.class_("CalibrateLevelAction", automation.Action) CalibrateHeadingAction = motion_ns.class_("CalibrateHeadingAction", automation.Action) -ClearCalibrationAction = motion_ns.class_("ClearCalibrationAction", automation.Action) KEY_ACCELEROMETER = "accelerometer" KEY_GYROSCOPE = "gyroscope" @@ -207,15 +206,8 @@ CLEAR_ACTION_SCHEMA = cv.Schema( ) -@automation.register_action( +automation.register_apply_action( "motion.clear_calibration", - ClearCalibrationAction, CLEAR_ACTION_SCHEMA, - synchronous=True, + automation.ApplyField(CONF_SAVE, "clear_calibration", cg.bool_), ) -async def clear_calibration_to_code(config, action_id, template_arg, args): - parent = await cg.get_variable(config[CONF_ID]) - var = cg.new_Pvariable(action_id, template_arg, parent) - if config.get(CONF_SAVE): - cg.add(var.set_save(True)) - return var diff --git a/esphome/components/motion/motion_component.cpp b/esphome/components/motion/motion_component.cpp index 8715c8385c..226dc94e63 100644 --- a/esphome/components/motion/motion_component.cpp +++ b/esphome/components/motion/motion_component.cpp @@ -56,10 +56,13 @@ bool MotionComponent::save_calibration() { ESP_LOGW(TAG, "Calibration save failed"); return false; } -void MotionComponent::clear_calibration() { +void MotionComponent::clear_calibration(bool save) { memcpy(this->matrix_, this->base_matrix_, sizeof(this->matrix_)); ESP_LOGI(TAG, "Calibration reset to build-time matrix"); log_matrix(this->matrix_); + if (save) { + this->save_calibration(); + } } void MotionComponent::update() { if (this->is_failed()) diff --git a/esphome/components/motion/motion_component.h b/esphome/components/motion/motion_component.h index b0a074a17c..d117ca0032 100644 --- a/esphome/components/motion/motion_component.h +++ b/esphome/components/motion/motion_component.h @@ -52,8 +52,9 @@ class MotionComponent : public PollingComponent { bool calibrate_heading(); /// Save the current matrix to NVS. bool save_calibration(); - /// Restore the build-time (axis_map / transform_matrix) base, discarding calibration. - void clear_calibration(); + /// Restore the build-time (axis_map / transform_matrix) base, discarding calibration, + /// and persist that base when `save` is set. + void clear_calibration(bool save = false); template void add_listener(F &&cb) { this->motion_data_callback_.add(std::forward(cb)); } @@ -135,20 +136,4 @@ template class CalibrateHeadingAction final : public Action class ClearCalibrationAction final : public Action { - public: - explicit ClearCalibrationAction(MotionComponent *parent) : parent_(parent) {} - void set_save(bool save) { this->save_ = save; } - - protected: - void play(const Ts &...) override { - this->parent_->clear_calibration(); - if (this->save_) - this->parent_->save_calibration(); - } - - MotionComponent *parent_; - bool save_{false}; -}; - } // namespace esphome::motion diff --git a/tests/component_tests/motion/test_motion.py b/tests/component_tests/motion/test_motion.py index f2c0f26344..c502e06dab 100644 --- a/tests/component_tests/motion/test_motion.py +++ b/tests/component_tests/motion/test_motion.py @@ -18,7 +18,6 @@ from esphome.components.motion import ( _build_calibrate_action, _transform_matrix, _validate_matrix_options, - clear_calibration_to_code, ) from esphome.components.motion.sensor import ( _ACCELERATIONS, @@ -785,44 +784,6 @@ class TestClearActionSchema: assert result[CONF_SAVE] is False -@pytest.fixture -def mock_clear_codegen(): - """Mock cg functions used by clear_calibration_to_code.""" - mock_var = MagicMock() - mock_parent = MagicMock() - with ( - patch( - "esphome.components.motion.cg.get_variable", - new_callable=AsyncMock, - return_value=mock_parent, - ), - patch( - "esphome.components.motion.cg.new_Pvariable", - return_value=mock_var, - ) as mock_new_pvar, - patch("esphome.components.motion.cg.add") as mock_add, - ): - yield {"new_Pvariable": mock_new_pvar, "add": mock_add, "var": mock_var} - - -@pytest.mark.asyncio -async def test_clear_action_without_save(mock_clear_codegen): - """With save=False, set_save should not be emitted.""" - config = {CONF_ID: MagicMock(), CONF_SAVE: False} - result = await clear_calibration_to_code(config, MagicMock(), MagicMock(), []) - assert result is mock_clear_codegen["var"] - mock_clear_codegen["add"].assert_not_called() - - -@pytest.mark.asyncio -async def test_clear_action_with_save(mock_clear_codegen): - """With save=True, set_save(True) should be emitted exactly once.""" - config = {CONF_ID: MagicMock(), CONF_SAVE: True} - await clear_calibration_to_code(config, MagicMock(), MagicMock(), []) - mock_clear_codegen["var"].set_save.assert_called_once_with(True) - mock_clear_codegen["add"].assert_called_once() - - # --- Calibration persistence invalidation --- # # The C++ side stores a hash of the build-time base matrix alongside the saved diff --git a/tests/components/qmi8658/common-actions.yaml b/tests/components/qmi8658/common-actions.yaml new file mode 100644 index 0000000000..b8ab845ef1 --- /dev/null +++ b/tests/components/qmi8658/common-actions.yaml @@ -0,0 +1,7 @@ +button: + - platform: template + name: Clear motion calibration + on_press: + - motion.clear_calibration: + - motion.clear_calibration: + save: true diff --git a/tests/components/qmi8658/test-actions.esp8266-ard.yaml b/tests/components/qmi8658/test-actions.esp8266-ard.yaml new file mode 100644 index 0000000000..28421ac7e6 --- /dev/null +++ b/tests/components/qmi8658/test-actions.esp8266-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + qmi8658: !include common.yaml + actions: !include common-actions.yaml