From fee47fe2f16340a5f39f50c556fe65575f9acdaa Mon Sep 17 00:00:00 2001 From: Panoplian <153834574+albert-canfield@users.noreply.github.com> Date: Wed, 14 Oct 2026 19:43:23 +0100 Subject: [PATCH] [pcf85063] Convert the weekday to and from the chip's 0-6 range (#20736) --- esphome/components/pcf85063/pcf85063.cpp | 5 +- tests/components/pcf85063/__init__.py | 6 ++ tests/components/pcf85063/test_pcf85063.cpp | 77 +++++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 tests/components/pcf85063/__init__.py create mode 100644 tests/components/pcf85063/test_pcf85063.cpp diff --git a/esphome/components/pcf85063/pcf85063.cpp b/esphome/components/pcf85063/pcf85063.cpp index 8ce76e9781c..bafe19ed2a4 100644 --- a/esphome/components/pcf85063/pcf85063.cpp +++ b/esphome/components/pcf85063/pcf85063.cpp @@ -37,7 +37,8 @@ void PCF85063Component::read_time() { .second = uint8_t(pcf85063_.reg.second + 10 * pcf85063_.reg.second_10), .minute = uint8_t(pcf85063_.reg.minute + 10u * pcf85063_.reg.minute_10), .hour = uint8_t(pcf85063_.reg.hour + 10u * pcf85063_.reg.hour_10), - .day_of_week = uint8_t(pcf85063_.reg.weekday), + // The chip counts weekdays 0-6 from Sunday, ESPTime counts 1-7 + .day_of_week = uint8_t(this->pcf85063_.reg.weekday + 1), .day_of_month = uint8_t(pcf85063_.reg.day + 10u * pcf85063_.reg.day_10), .month = uint8_t(pcf85063_.reg.month + 10u * pcf85063_.reg.month_10), .year = uint16_t(pcf85063_.reg.year + 10u * pcf85063_.reg.year_10 + 2000), @@ -62,7 +63,7 @@ void PCF85063Component::write_time() { pcf85063_.reg.month_10 = now.month / 10; pcf85063_.reg.day = now.day_of_month % 10; pcf85063_.reg.day_10 = now.day_of_month / 10; - pcf85063_.reg.weekday = now.day_of_week; + this->pcf85063_.reg.weekday = now.day_of_week - 1; pcf85063_.reg.hour = now.hour % 10; pcf85063_.reg.hour_10 = now.hour / 10; pcf85063_.reg.minute = now.minute % 10; diff --git a/tests/components/pcf85063/__init__.py b/tests/components/pcf85063/__init__.py new file mode 100644 index 00000000000..9465ea845e2 --- /dev/null +++ b/tests/components/pcf85063/__init__.py @@ -0,0 +1,6 @@ +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + # The driver lives in the time platform, so pull in what it builds against. + manifest.dependencies = manifest.dependencies + ["i2c", "time"] diff --git a/tests/components/pcf85063/test_pcf85063.cpp b/tests/components/pcf85063/test_pcf85063.cpp new file mode 100644 index 00000000000..0e5c5e6c20d --- /dev/null +++ b/tests/components/pcf85063/test_pcf85063.cpp @@ -0,0 +1,77 @@ +#include + +#include +#include + +#include "esphome/components/pcf85063/pcf85063.h" +#include "esphome/core/time.h" + +namespace esphome::pcf85063::testing { + +static constexpr uint8_t ADDRESS = 0x51; +static constexpr uint8_t WEEKDAYS_REG = 0x08; +static constexpr uint8_t CHIP_DAYS_PER_WEEK = 7; // The chip counts weekdays 0-6, Sunday = 0 + +// The chip's 18 registers behind an address pointer that moves on after every byte. +class RegisterBus final : public i2c::I2CBus { + public: + i2c::ErrorCode write_readv(uint8_t address, const uint8_t *data, size_t size, uint8_t *out, + size_t out_size) override { + EXPECT_EQ(address, ADDRESS); + if (size != 0) { + this->pointer_ = data[0]; + for (size_t i = 1; i < size; i++) + this->registers[this->next_()] = data[i]; + } + for (size_t i = 0; i < out_size; i++) + out[i] = this->registers[this->next_()]; + return i2c::ERROR_OK; + } + + std::array registers{}; + + protected: + size_t next_() { return this->pointer_++ % this->registers.size(); } + + uint8_t pointer_{0}; +}; + +// write_time() takes the host's current time, so reading it back never changes the host clock. +class PCF85063Test : public ::testing::Test { + protected: + void SetUp() override { + this->rtc_.set_i2c_bus(&this->bus_); + this->rtc_.set_i2c_address(ADDRESS); + this->rtc_.add_on_time_sync_callback([this]() { ++this->syncs_; }); + this->rtc_.setup(); + } + + RegisterBus bus_; + PCF85063Component rtc_; + unsigned syncs_{0}; +}; + +TEST_F(PCF85063Test, WriteTimeStoresTheChipsWeekday) { + // Read the clock on both sides of the write, in case UTC midnight falls in between. + const int before = ESPTime::from_epoch_utc(::time(nullptr)).day_of_week - 1; + this->rtc_.write_time(); + const int after = ESPTime::from_epoch_utc(::time(nullptr)).day_of_week - 1; + const int stored = this->bus_.registers[WEEKDAYS_REG]; + EXPECT_TRUE(stored == before || stored == after) << "stored " << stored << ", expected " << before; +} + +// After a write the chip keeps counting on its own and goes from Saturday (6) back to Sunday (0) at midnight. +// Seven steps from the written value visit every weekday once, and each has to read back as a valid time. +// Only the weekday moves, so the time read back is still the host's time. +TEST_F(PCF85063Test, WeekdayRoundTripsThroughSundayAndSaturday) { + this->rtc_.write_time(); + uint8_t &weekday = this->bus_.registers[WEEKDAYS_REG]; + for (uint8_t day = 0; day < CHIP_DAYS_PER_WEEK; day++) { + weekday = (weekday + 1) % CHIP_DAYS_PER_WEEK; + const unsigned syncs_before = this->syncs_; + this->rtc_.read_time(); + EXPECT_EQ(this->syncs_, syncs_before + 1) << "chip weekday " << unsigned(weekday); + } +} + +} // namespace esphome::pcf85063::testing