[emontx] Read the emonTx firmware version (#20619)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Frédéric Metrich
2026-10-12 22:43:02 -10:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 25843c5242
commit 77c9fa2fa4
7 changed files with 235 additions and 6 deletions
+6
View File
@@ -24,3 +24,9 @@ sensor:
tag_name: P1
emontx_id: test_emontx
unit_of_measurement: W
text_sensor:
- platform: emontx
emontx_id: test_emontx
version:
name: emonTx firmware version
@@ -0,0 +1,11 @@
import esphome.codegen as cg
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; only its define is wanted here.
async def to_code_testing(config: ConfigType) -> None:
cg.add_define("USE_EMONTX_VERSION")
manifest.to_code = to_code_testing
@@ -0,0 +1,116 @@
#include <gtest/gtest.h>
#include <string>
#include "esphome/components/emontx/emontx.h"
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "../common.h"
namespace esphome::emontx::testing {
namespace {
class EmonTxVersionTest : public ::testing::Test {
protected:
void SetUp() override {
// The test main does not construct App as generated code does
static const bool APP_CONSTRUCTED = (new (&App) Application(), true);
(void) APP_CONSTRUCTED;
this->sut_.set_uart_parent(&this->uart_);
this->sut_.set_version_text_sensor(&this->version_);
}
void TearDown() override {
App.scheduler.cancel_timeout(&this->sut_, uint32_t{0});
App.scheduler.call(millis());
}
// Lets the delayed 'v' go out
static void run_timers() {
App.scheduler.call(millis());
delay(300); // NOLINT - test only, the scheduler reads the real clock
App.scheduler.call(millis());
}
std::string tx_() const { return std::string(this->uart_.tx.begin(), this->uart_.tx.end()); }
MockUARTComponent uart_;
EmonTx sut_;
text_sensor::TextSensor version_;
};
} // namespace
// 'v' must not reach the emonTx in the same burst as the line feed, or it joins the pending command
TEST_F(EmonTxVersionTest, SetupRequestsVersionAfterDelay) {
this->sut_.setup();
EXPECT_EQ(this->tx_(), "\n");
run_timers();
EXPECT_EQ(this->tx_(), "\nv\n");
}
TEST_F(EmonTxVersionTest, SetupUnlocksBeforeRequestingVersion) {
this->sut_.set_lock_on_reboot(true);
this->sut_.setup();
EXPECT_EQ(this->tx_(), "\nemonunlock\n\n");
run_timers();
EXPECT_EQ(this->tx_(), "\nemonunlock\n\nv\n");
}
TEST_F(EmonTxVersionTest, VersionLinePublished) {
this->uart_.push_rx("> Firmware:\r\n - Bootloader: UART (.bin)\r\n - Version: v1.1.1\r\n");
this->sut_.loop();
ASSERT_TRUE(this->version_.has_state());
EXPECT_EQ(this->version_.get_state(), "v1.1.1");
}
TEST_F(EmonTxVersionTest, OtherLinesNotPublished) {
this->uart_.push_rx(" - Build: 768be1b (Default)\r\n{\"P1\":100}\r\n - Version:\r\n - Version: \r\n");
this->sut_.loop();
EXPECT_FALSE(this->version_.has_state());
}
// MSG restarts at 1 after a reboot, e.g. a firmware update
TEST_F(EmonTxVersionTest, MsgCounterResetRequestsVersion) {
this->uart_.push_rx("{\"MSG\":41}\n{\"MSG\":42}\n");
this->sut_.loop();
EXPECT_TRUE(this->uart_.tx.empty());
this->uart_.push_rx("{\"MSG\":1}\n");
this->sut_.loop();
EXPECT_EQ(this->tx_(), "\n");
run_timers();
EXPECT_EQ(this->tx_(), "\nv\n");
}
// The emonTx may have been flashed during the pause, with MSG already past its old value
TEST_F(EmonTxVersionTest, ResumeRequestsVersion) {
this->sut_.set_paused(true);
this->sut_.loop();
this->sut_.set_paused(false);
this->sut_.loop();
EXPECT_EQ(this->tx_(), "\n");
run_timers();
EXPECT_EQ(this->tx_(), "\nv\n");
}
TEST_F(EmonTxVersionTest, RequestDroppedByPauseSentAgainOnResume) {
this->sut_.setup();
this->sut_.set_paused(true);
this->sut_.loop();
run_timers();
EXPECT_EQ(this->tx_(), "\n");
this->sut_.set_paused(false);
this->sut_.loop();
run_timers();
EXPECT_EQ(this->tx_(), "\n\nv\n");
}
} // namespace esphome::emontx::testing