diff --git a/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp b/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp index 443d4be8a10..6929f587d10 100644 --- a/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp +++ b/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp @@ -6,8 +6,13 @@ namespace esphome::teleinfo { ESPHOME_LOG_TAG(TAG, "teleinfo_sensor"); TeleInfoSensor::TeleInfoSensor(const char *tag) { this->tag = std::string(tag); } void TeleInfoSensor::publish_val(const std::string &val) { - auto newval = parse_number(val).value_or(0.0f); - publish_state(newval); + auto newval = parse_number(val); + if (!newval.has_value()) { + // Publishing 0 instead would look like a meter reset to total_increasing energy sensors + ESP_LOGW(TAG, "Value '%s' for tag %s is not a number", val.c_str(), this->tag.c_str()); + return; + } + this->publish_state(*newval); } void TeleInfoSensor::dump_config() { LOG_SENSOR(" ", "Teleinfo Sensor", this); } } // namespace esphome::teleinfo diff --git a/tests/components/teleinfo/__init__.py b/tests/components/teleinfo/__init__.py new file mode 100644 index 00000000000..5f1db440600 --- /dev/null +++ b/tests/components/teleinfo/__init__.py @@ -0,0 +1,11 @@ +from esphome.types import ConfigType +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + # teleinfo.h includes uart.h; the hub's own to_code needs a configured UART bus. + async def to_code_testing(config: ConfigType) -> None: + pass + + manifest.to_code = to_code_testing + manifest.dependencies = manifest.dependencies + ["uart"] diff --git a/tests/components/teleinfo/sensor/__init__.py b/tests/components/teleinfo/sensor/__init__.py new file mode 100644 index 00000000000..cfaaf8d96bd --- /dev/null +++ b/tests/components/teleinfo/sensor/__init__.py @@ -0,0 +1,10 @@ +from esphome.types import ConfigType +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + # The platform's own to_code needs a configured hub; the sensor class is built directly. + async def to_code_testing(config: ConfigType) -> None: + pass + + manifest.to_code = to_code_testing diff --git a/tests/components/teleinfo/teleinfo_sensor_test.cpp b/tests/components/teleinfo/teleinfo_sensor_test.cpp new file mode 100644 index 00000000000..9b86fd61879 --- /dev/null +++ b/tests/components/teleinfo/teleinfo_sensor_test.cpp @@ -0,0 +1,47 @@ +#include + +#include + +#include "esphome/components/teleinfo/sensor/teleinfo_sensor.h" + +namespace esphome::teleinfo::testing { + +namespace { +class TeleInfoSensorTest : public ::testing::Test { + protected: + void SetUp() override { + this->sensor_.add_on_state_callback([this](float state) { + this->publishes_++; + this->last_ = state; + }); + } + + TeleInfoSensor sensor_{"EAST"}; + int publishes_{0}; + float last_{NAN}; +}; +} // namespace + +TEST_F(TeleInfoSensorTest, PublishesZeroPaddedNumber) { + this->sensor_.publish_val("000012345"); + EXPECT_EQ(this->publishes_, 1); + EXPECT_FLOAT_EQ(this->last_, 12345.0f); +} + +TEST_F(TeleInfoSensorTest, PublishesRealZero) { + this->sensor_.publish_val("000000000"); + EXPECT_EQ(this->publishes_, 1); + EXPECT_FLOAT_EQ(this->last_, 0.0f); +} + +// A value that is not a number must be dropped: publishing 0 would look like a meter reset +TEST_F(TeleInfoSensorTest, SkipsValueThatIsNotANumber) { + this->sensor_.publish_val("000012345"); + this->sensor_.publish_val("0000l2345"); + this->sensor_.publish_val("H081225223518"); + EXPECT_EQ(this->publishes_, 1); + EXPECT_FLOAT_EQ(this->last_, 12345.0f); + EXPECT_FLOAT_EQ(this->sensor_.get_state(), 12345.0f); +} + +} // namespace esphome::teleinfo::testing