1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-15 15:18:16 +00:00

add real time clock

This commit is contained in:
Tomasz Duda 2024-07-12 16:03:16 +02:00
parent e10bd36559
commit ce7f666d6b
7 changed files with 35 additions and 18 deletions

View File

@ -25,8 +25,9 @@ from esphome.const import (
CONF_HOUR, CONF_HOUR,
CONF_MINUTE, CONF_MINUTE,
) )
from esphome.core import coroutine_with_priority from esphome.core import coroutine_with_priority, CORE
from esphome.automation import Condition from esphome.automation import Condition
from esphome.components.zephyr import zephyr_add_prj_conf
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -269,7 +270,10 @@ def validate_tz(value: str) -> str:
TIME_SCHEMA = cv.Schema( TIME_SCHEMA = cv.Schema(
{ {
cv.Optional(CONF_TIMEZONE, default=detect_tz): validate_tz, cv.Optional(CONF_TIMEZONE, default=detect_tz): cv.All(
cv.only_with_arduino_or_esp_idf,
validate_tz,
),
cv.Optional(CONF_ON_TIME): automation.validate_automation( cv.Optional(CONF_ON_TIME): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CronTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CronTrigger),
@ -294,6 +298,7 @@ TIME_SCHEMA = cv.Schema(
async def setup_time_core_(time_var, config): async def setup_time_core_(time_var, config):
if not CORE.using_zephyr:
cg.add(time_var.set_timezone(config[CONF_TIMEZONE])) cg.add(time_var.set_timezone(config[CONF_TIMEZONE]))
for conf in config.get(CONF_ON_TIME, []): for conf in config.get(CONF_ON_TIME, []):
@ -328,6 +333,8 @@ async def register_time(time_var, config):
@coroutine_with_priority(100.0) @coroutine_with_priority(100.0)
async def to_code(config): async def to_code(config):
if CORE.using_zephyr:
zephyr_add_prj_conf("POSIX_CLOCK", True)
cg.add_define("USE_TIME") cg.add_define("USE_TIME")
cg.add_global(time_ns.using) cg.add_global(time_ns.using)

View File

@ -2,13 +2,15 @@
#include "esphome/core/log.h" #include "esphome/core/log.h"
#ifdef USE_HOST #ifdef USE_HOST
#include <sys/time.h> #include <sys/time.h>
#elif defined(USE_ZEPHYR)
#include <zephyr/posix/time.h>
#else #else
#include "lwip/opt.h" #include "lwip/opt.h"
#endif #endif
#ifdef USE_ESP8266 #ifdef USE_ESP8266
#include "sys/time.h" #include "sys/time.h"
#endif #endif
#ifdef USE_RP2040 #if defined(USE_RP2040) || defined(USE_ZEPHYR)
#include <sys/time.h> #include <sys/time.h>
#endif #endif
#include <cerrno> #include <cerrno>
@ -22,15 +24,18 @@ static const char *const TAG = "time";
RealTimeClock::RealTimeClock() = default; RealTimeClock::RealTimeClock() = default;
void RealTimeClock::call_setup() { void RealTimeClock::call_setup() {
#ifndef USE_ZEPHYR
this->apply_timezone_(); this->apply_timezone_();
#endif
PollingComponent::call_setup(); PollingComponent::call_setup();
} }
void RealTimeClock::synchronize_epoch_(uint32_t epoch) { void RealTimeClock::synchronize_epoch_(uint32_t epoch) {
ESP_LOGVV(TAG, "Got epoch %" PRIu32, epoch);
// Update UTC epoch time. // Update UTC epoch time.
#ifndef USE_ZEPHYR
struct timeval timev { struct timeval timev {
.tv_sec = static_cast<time_t>(epoch), .tv_usec = 0, .tv_sec = static_cast<time_t>(epoch), .tv_usec = 0,
}; };
ESP_LOGVV(TAG, "Got epoch %" PRIu32, epoch);
struct timezone tz = {0, 0}; struct timezone tz = {0, 0};
int ret = settimeofday(&timev, &tz); int ret = settimeofday(&timev, &tz);
if (ret == EINVAL) { if (ret == EINVAL) {
@ -45,7 +50,18 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) {
if (ret != 0) { if (ret != 0) {
ESP_LOGW(TAG, "setimeofday() failed with code %d", ret); ESP_LOGW(TAG, "setimeofday() failed with code %d", ret);
} }
#else
struct timespec ts;
ts.tv_nsec = 0;
ts.tv_sec = static_cast<time_t>(epoch);
int ret = clock_settime(CLOCK_REALTIME, &ts);
if (ret != 0) {
ESP_LOGW(TAG, "clock_settime() failed with code %d", ret);
}
#endif
auto time = this->now(); auto time = this->now();
ESP_LOGD(TAG, "Synchronized time: %04d-%02d-%02d %02d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour, ESP_LOGD(TAG, "Synchronized time: %04d-%02d-%02d %02d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour,
time.minute, time.second); time.minute, time.second);
@ -53,10 +69,12 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) {
this->time_sync_callback_.call(); this->time_sync_callback_.call();
} }
#ifndef USE_ZEPHYR
void RealTimeClock::apply_timezone_() { void RealTimeClock::apply_timezone_() {
setenv("TZ", this->timezone_.c_str(), 1); setenv("TZ", this->timezone_.c_str(), 1);
tzset(); tzset();
} }
#endif
} // namespace time } // namespace time
} // namespace esphome } // namespace esphome

View File

@ -19,13 +19,13 @@ namespace time {
class RealTimeClock : public PollingComponent { class RealTimeClock : public PollingComponent {
public: public:
explicit RealTimeClock(); explicit RealTimeClock();
#ifndef USE_ZEPHYR
/// Set the time zone. /// Set the time zone.
void set_timezone(const std::string &tz) { this->timezone_ = tz; } void set_timezone(const std::string &tz) { this->timezone_ = tz; }
/// Get the time zone currently in use. /// Get the time zone currently in use.
std::string get_timezone() { return this->timezone_; } std::string get_timezone() { return this->timezone_; }
#endif
/// Get the time in the currently defined timezone. /// Get the time in the currently defined timezone.
ESPTime now() { return ESPTime::from_epoch_local(this->timestamp_now()); } ESPTime now() { return ESPTime::from_epoch_local(this->timestamp_now()); }
@ -44,10 +44,10 @@ class RealTimeClock : public PollingComponent {
protected: protected:
/// Report a unix epoch as current time. /// Report a unix epoch as current time.
void synchronize_epoch_(uint32_t epoch); void synchronize_epoch_(uint32_t epoch);
#ifndef USE_ZEPHYR
std::string timezone_{}; std::string timezone_{};
void apply_timezone_(); void apply_timezone_();
#endif
CallbackManager<void()> time_sync_callback_; CallbackManager<void()> time_sync_callback_;
}; };

View File

@ -609,6 +609,7 @@ only_on_nrf52 = only_on(PLATFORM_NRF52)
only_with_arduino = only_with_framework("arduino") only_with_arduino = only_with_framework("arduino")
only_with_esp_idf = only_with_framework("esp-idf") only_with_esp_idf = only_with_framework("esp-idf")
only_with_zephyr = only_with_framework("zephyr") only_with_zephyr = only_with_framework("zephyr")
only_with_arduino_or_esp_idf = only_with_framework(["arduino", "esp-idf"])
# Adapted from: # Adapted from:

View File

@ -58,10 +58,6 @@ for f in ./tests/components/$target_component/*.*.yaml; do
IFS='.' read -r -a file_name <<< "${folder_name[4]}" IFS='.' read -r -a file_name <<< "${folder_name[4]}"
test_name="${file_name[0]}" test_name="${file_name[0]}"
if [ "$test_name" = "exclude" ]; then
# this is not a test file. we need to skip it.
continue
fi
target_platform="${file_name[1]}" target_platform="${file_name[1]}"
file_name_parts=${#file_name[@]} file_name_parts=${#file_name[@]}
@ -73,11 +69,6 @@ for f in ./tests/components/$target_component/*.*.yaml; do
IFS='.' read -r -a file_name <<< "${folder_name[3]}" IFS='.' read -r -a file_name <<< "${folder_name[3]}"
target_platform="${file_name[1]}" target_platform="${file_name[1]}"
if [ -f "./tests/components/$target_component/exclude.$test_name.$target_platform.yaml" ]; then
echo "Component $target_component is excluded from testing on $target_platform"
continue
fi
start_esphome start_esphome
done done