[nrf52] fix zigbee time component (#20527)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
tomaszduda23
2026-10-12 11:52:25 +13:00
committed by Jesse Hills
co-authored by Claude
parent 2727d35655
commit 60402614cd
3 changed files with 26 additions and 6 deletions
+8
View File
@@ -26,6 +26,8 @@ from esphome.const import (
CONF_SECONDS,
CONF_TIMEZONE,
CONF_TRIGGER_ID,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
PLATFORM_BK72XX,
PLATFORM_ESP32,
PLATFORM_ESP8266,
@@ -462,6 +464,12 @@ async def register_time(time_var, config):
async def to_code(config):
if CORE.using_zephyr:
zephyr_add_prj_conf("POSIX_CLOCK", True)
# From sdk-nrf 2.8.0 newlib time() gets the clock through gettimeofday(),
# which Zephyr only provides with XSI_SINGLE_PROCESS. Without it time()
# always returns -1.
if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(2, 8, 0):
zephyr_add_prj_conf("POSIX_SINGLE_PROCESS", True)
zephyr_add_prj_conf("XSI_SINGLE_PROCESS", True)
cg.add_define("USE_TIME")
cg.add_global(time_ns.using)
@@ -14,6 +14,7 @@ from ..zigbee_zephyr import (
ZigbeeClusterDesc,
ZigbeeComponent,
get_slot_index,
zigbee_assign,
zigbee_new_attr_list,
zigbee_new_cluster_list,
zigbee_new_variable,
@@ -81,6 +82,7 @@ async def _add_time_zephyr(config: ConfigType) -> None:
# Create the binary attributes structure
time_attrs = zigbee_new_variable(attrs_name, "zb_zcl_time_attrs_t")
zigbee_assign(time_attrs.time, cg.RawExpression("ZB_ZCL_TIME_TIME_INVALID_VALUE"))
attr_list = zigbee_new_attr_list(
attr_list_name,
"ZB_ZCL_DECLARE_TIME_ATTR_LIST",
@@ -2,6 +2,7 @@
#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_TIME)
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include <limits>
namespace esphome::zigbee {
@@ -10,12 +11,14 @@ static const char *const TAG = "zigbee.time";
// This time standard is the number of
// seconds since 0 hrs 0 mins 0 sec on 1st January 2000 UTC (Universal Coordinated Time).
constexpr time_t EPOCH_2000 = 946684800;
// Largest ZCL time that still fits the 32-bit epoch. Also rejects ZB_ZCL_TIME_TIME_INVALID_VALUE.
constexpr uint32_t MAX_ZCL_TIME = std::numeric_limits<uint32_t>::max() - EPOCH_2000;
ZigbeeTime *global_time = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void ZigbeeTime::sync_time(zb_ret_t status, zb_uint32_t auth_level, zb_uint16_t short_addr, zb_uint8_t endpoint,
zb_uint32_t nw_time) {
if (status == RET_OK && auth_level >= ZB_ZCL_TIME_HAS_SYNCHRONIZED_BIT) {
if (status == RET_OK && auth_level >= ZB_ZCL_TIME_HAS_SYNCHRONIZED_BIT && nw_time <= MAX_ZCL_TIME) {
global_time->set_epoch_time(nw_time + EPOCH_2000);
} else if (status != RET_TIMEOUT || !global_time->has_time_) {
ESP_LOGE(TAG, "Status: %d, auth_level: %u, short_addr: %d, endpoint: %d, nw_time: %u", status, auth_level,
@@ -26,7 +29,6 @@ void ZigbeeTime::sync_time(zb_ret_t status, zb_uint32_t auth_level, zb_uint16_t
void ZigbeeTime::setup() {
global_time = this;
this->parent_->add_callback(this->endpoint_, [this](zb_bufid_t bufid) { this->zcl_device_cb_(bufid); });
synchronize_epoch_(EPOCH_2000);
this->parent_->add_on_join_callback([this](bool x) { zb_zcl_time_server_synchronize(this->endpoint_, sync_time); });
}
@@ -39,8 +41,12 @@ void ZigbeeTime::dump_config() {
}
void ZigbeeTime::update() {
time_t time = timestamp_now();
this->cluster_attributes_->time = time - EPOCH_2000;
const time_t now = this->timestamp_now();
if (now < EPOCH_2000) {
this->cluster_attributes_->time = ZB_ZCL_TIME_TIME_INVALID_VALUE;
} else {
this->cluster_attributes_->time = static_cast<uint32_t>(now - EPOCH_2000);
}
}
void ZigbeeTime::set_epoch_time(uint32_t epoch) {
@@ -63,8 +69,12 @@ void ZigbeeTime::zcl_device_cb_(zb_bufid_t bufid) {
if (cluster_id == ZB_ZCL_CLUSTER_ID_TIME) {
if (attr_id == ZB_ZCL_ATTR_TIME_TIME_ID) {
zb_uint32_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data32;
ESP_LOGI(TAG, "Synchronize time to %u", value);
this->defer([this, value]() { synchronize_epoch_(value + EPOCH_2000); });
if (value <= MAX_ZCL_TIME) {
ESP_LOGI(TAG, "Synchronize time to %u", value);
this->defer([this, value]() { this->synchronize_epoch_(value + EPOCH_2000); });
} else {
ESP_LOGW(TAG, "Ignoring invalid time %u", value);
}
} else if (attr_id == ZB_ZCL_ATTR_TIME_TIME_STATUS_ID) {
zb_uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;
ESP_LOGI(TAG, "Time status %hd", value);