mirror of
https://github.com/esphome/esphome.git
synced 2026-10-17 11:42:51 +00:00
[dfrobot_sen0395] Forward trigger arguments in settings actions (#20885)
Co-authored-by: J. Nick Koston <nick@home-assistant.io> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
co-authored by
J. Nick Koston
J. Nick Koston
parent
57dd09018e
commit
c0b7c5e590
@@ -26,17 +26,22 @@ class DfrobotSen0395SettingsAction final : public Action<Ts...>, public Parented
|
||||
TEMPLATABLE_VALUE(float, det_min4)
|
||||
TEMPLATABLE_VALUE(float, det_max4)
|
||||
|
||||
void play(const Ts &...x) {
|
||||
void play(const Ts &...x) override {
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(0));
|
||||
if (this->factory_reset_.has_value() && this->factory_reset_.value(x...) == true) {
|
||||
this->parent_->enqueue(make_unique<FactoryResetCommand>());
|
||||
}
|
||||
if (this->det_min1_.has_value() && this->det_max1_.has_value()) {
|
||||
if (this->det_min1_.value() >= 0 && this->det_max1_.value() >= 0) {
|
||||
this->parent_->enqueue(make_unique<DetRangeCfgCommand>(
|
||||
this->det_min1_.value_or(-1), this->det_max1_.value_or(-1), this->det_min2_.value_or(-1),
|
||||
this->det_max2_.value_or(-1), this->det_min3_.value_or(-1), this->det_max3_.value_or(-1),
|
||||
this->det_min4_.value_or(-1), this->det_max4_.value_or(-1)));
|
||||
// Each segment is read once, so the guard and the command see the same values.
|
||||
const float min1 = this->det_min1_.value(x...);
|
||||
if (min1 >= 0) {
|
||||
const float max1 = this->det_max1_.value(x...);
|
||||
if (max1 >= 0) {
|
||||
this->parent_->enqueue(make_unique<DetRangeCfgCommand>(
|
||||
min1, max1, this->det_min2_.value_or(x..., -1), this->det_max2_.value_or(x..., -1),
|
||||
this->det_min3_.value_or(x..., -1), this->det_max3_.value_or(x..., -1),
|
||||
this->det_min4_.value_or(x..., -1), this->det_max4_.value_or(x..., -1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this->delay_after_detect_.has_value() && this->delay_after_disappear_.has_value()) {
|
||||
|
||||
@@ -17,6 +17,21 @@ esphome:
|
||||
dfrobot_sen0395:
|
||||
- id: mmwave
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
id: mmwave_max_distance
|
||||
name: mmwave max distance
|
||||
min_value: 1
|
||||
max_value: 9
|
||||
step: 0.5
|
||||
set_action:
|
||||
- dfrobot_sen0395.settings:
|
||||
id: mmwave
|
||||
detection_segments:
|
||||
- 0cm
|
||||
- !lambda "return x;"
|
||||
sensitivity: 5
|
||||
|
||||
binary_sensor:
|
||||
- platform: dfrobot_sen0395
|
||||
id: mmwave_detected
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
#include <array>
|
||||
#include <gtest/gtest.h>
|
||||
#include "esphome/components/dfrobot_sen0395/automation.h"
|
||||
|
||||
namespace esphome::dfrobot_sen0395::testing {
|
||||
namespace {
|
||||
struct CallRecord {
|
||||
std::array<int, 8> segment_calls{};
|
||||
std::array<float, 8> seen_float{};
|
||||
std::array<int, 8> seen_int{};
|
||||
int sensitivity_calls{0};
|
||||
float sensitivity_argument{0};
|
||||
};
|
||||
|
||||
// TemplatableFn stores only a function pointer, so the test lambdas cannot capture. They record into this
|
||||
// function-local static instead, which SetUp() clears before every test.
|
||||
CallRecord &call_record() {
|
||||
static CallRecord record{};
|
||||
return record;
|
||||
}
|
||||
|
||||
float segment_value(size_t index, float x, int y = 0) {
|
||||
CallRecord &record = call_record();
|
||||
record.segment_calls[index]++;
|
||||
record.seen_float[index] = x;
|
||||
record.seen_int[index] = y;
|
||||
return x + y + static_cast<float>(index);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// The component is final and its command queue protected, so these tests check the segment
|
||||
// reads and their arguments; the enqueued DetRangeCfgCommand itself is not inspected.
|
||||
class SettingsActionTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override { call_record() = CallRecord{}; }
|
||||
|
||||
DfrobotSen0395Component radar_;
|
||||
};
|
||||
|
||||
TEST_F(SettingsActionTest, EmptyTriggerPreservesSegmentCallableCounts) {
|
||||
DfrobotSen0395SettingsAction<> action;
|
||||
action.set_parent(&this->radar_);
|
||||
action.set_det_min1([]() -> float { return segment_value(0, 0); });
|
||||
action.set_det_max1([]() -> float { return segment_value(1, 0); });
|
||||
action.set_det_min2([]() -> float { return segment_value(2, 0); });
|
||||
action.set_det_max2([]() -> float { return segment_value(3, 0); });
|
||||
action.set_det_min3([]() -> float { return segment_value(4, 0); });
|
||||
action.set_det_max3([]() -> float { return segment_value(5, 0); });
|
||||
action.set_det_min4([]() -> float { return segment_value(6, 0); });
|
||||
action.set_det_max4([]() -> float { return segment_value(7, 0); });
|
||||
action.play();
|
||||
for (size_t i = 0; i < call_record().segment_calls.size(); i++) {
|
||||
EXPECT_EQ(call_record().segment_calls[i], 1) << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SettingsActionTest, EmptyTriggerWithoutSegmentsIsValid) {
|
||||
DfrobotSen0395SettingsAction<> action;
|
||||
action.set_parent(&this->radar_);
|
||||
action.play();
|
||||
for (auto calls : call_record().segment_calls) {
|
||||
EXPECT_EQ(calls, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SettingsActionTest, EmptyTriggerNegativeMinKeepsShortCircuit) {
|
||||
DfrobotSen0395SettingsAction<> action;
|
||||
action.set_parent(&this->radar_);
|
||||
action.set_det_min1([]() -> float { return segment_value(0, -1); });
|
||||
action.set_det_max1([]() -> float { return segment_value(1, 0); });
|
||||
action.play();
|
||||
EXPECT_EQ(call_record().segment_calls[0], 1);
|
||||
EXPECT_EQ(call_record().segment_calls[1], 0);
|
||||
}
|
||||
|
||||
TEST_F(SettingsActionTest, FloatTriggerForwardsAllSegmentsAndPreservesCounts) {
|
||||
DfrobotSen0395SettingsAction<float> action;
|
||||
action.set_parent(&this->radar_);
|
||||
action.set_det_min1([](float x) -> float { return segment_value(0, x); });
|
||||
action.set_det_max1([](float x) -> float { return segment_value(1, x); });
|
||||
action.set_det_min2([](float x) -> float { return segment_value(2, x); });
|
||||
action.set_det_max2([](float x) -> float { return segment_value(3, x); });
|
||||
action.set_det_min3([](float x) -> float { return segment_value(4, x); });
|
||||
action.set_det_max3([](float x) -> float { return segment_value(5, x); });
|
||||
action.set_det_min4([](float x) -> float { return segment_value(6, x); });
|
||||
action.set_det_max4([](float x) -> float { return segment_value(7, x); });
|
||||
action.play(2.5f);
|
||||
for (size_t i = 0; i < call_record().segment_calls.size(); i++) {
|
||||
EXPECT_EQ(call_record().segment_calls[i], 1) << i;
|
||||
EXPECT_FLOAT_EQ(call_record().seen_float[i], 2.5f) << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SettingsActionTest, MultipleTriggerArgumentsReachConfiguredSegments) {
|
||||
DfrobotSen0395SettingsAction<float, int> action;
|
||||
action.set_parent(&this->radar_);
|
||||
action.set_det_min1([](float x, int y) -> float { return segment_value(0, x, y); });
|
||||
action.set_det_max1([](float x, int y) -> float { return segment_value(1, x, y); });
|
||||
action.play(1.5f, 2);
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
EXPECT_EQ(call_record().segment_calls[i], 1) << i;
|
||||
EXPECT_FLOAT_EQ(call_record().seen_float[i], 1.5f) << i;
|
||||
EXPECT_EQ(call_record().seen_int[i], 2) << i;
|
||||
}
|
||||
for (size_t i = 2; i < call_record().segment_calls.size(); i++) {
|
||||
EXPECT_EQ(call_record().segment_calls[i], 0) << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SettingsActionTest, FloatTriggerWithoutSegmentsStillEvaluatesSensitivity) {
|
||||
DfrobotSen0395SettingsAction<float> action;
|
||||
action.set_parent(&this->radar_);
|
||||
action.set_sensitivity([](float x) -> int8_t {
|
||||
call_record().sensitivity_calls++;
|
||||
call_record().sensitivity_argument = x;
|
||||
return 5;
|
||||
});
|
||||
action.play(3.25f);
|
||||
EXPECT_EQ(call_record().sensitivity_calls, 1);
|
||||
EXPECT_FLOAT_EQ(call_record().sensitivity_argument, 3.25f);
|
||||
for (auto calls : call_record().segment_calls) {
|
||||
EXPECT_EQ(calls, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SettingsActionTest, FloatTriggerNegativeMinKeepsShortCircuit) {
|
||||
DfrobotSen0395SettingsAction<float> action;
|
||||
action.set_parent(&this->radar_);
|
||||
action.set_det_min1([](float x) -> float { return segment_value(0, x); });
|
||||
action.set_det_max1([](float x) -> float { return segment_value(1, x); });
|
||||
action.play(-1.0f);
|
||||
EXPECT_EQ(call_record().segment_calls[0], 1);
|
||||
EXPECT_EQ(call_record().segment_calls[1], 0);
|
||||
EXPECT_FLOAT_EQ(call_record().seen_float[0], -1.0f);
|
||||
}
|
||||
|
||||
TEST_F(SettingsActionTest, FloatTriggerUnpairedSegmentIsNotEvaluated) {
|
||||
DfrobotSen0395SettingsAction<float> action;
|
||||
action.set_parent(&this->radar_);
|
||||
action.set_det_min1([](float x) -> float { return segment_value(0, x); });
|
||||
action.play(4.0f);
|
||||
EXPECT_EQ(call_record().segment_calls[0], 0);
|
||||
}
|
||||
|
||||
} // namespace esphome::dfrobot_sen0395::testing
|
||||
Reference in New Issue
Block a user