ESPHome 2026.2.1
Loading...
Searching...
No Matches
time_entity.cpp
Go to the documentation of this file.
1#include "time_entity.h"
4#ifdef USE_DATETIME_TIME
5
6#include "esphome/core/log.h"
7
8namespace esphome::datetime {
9
10static const char *const TAG = "datetime.time_entity";
11
13 if (this->hour_ > 23) {
14 this->set_has_state(false);
15 ESP_LOGE(TAG, "Hour must be between 0 and 23");
16 return;
17 }
18 if (this->minute_ > 59) {
19 this->set_has_state(false);
20 ESP_LOGE(TAG, "Minute must be between 0 and 59");
21 return;
22 }
23 if (this->second_ > 59) {
24 this->set_has_state(false);
25 ESP_LOGE(TAG, "Second must be between 0 and 59");
26 return;
27 }
28 this->set_has_state(true);
29 ESP_LOGD(TAG, "'%s' >> %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_, this->second_);
30 this->state_callback_.call();
31#if defined(USE_DATETIME_TIME) && defined(USE_CONTROLLER_REGISTRY)
33#endif
34}
35
37
39 if (this->hour_.has_value() && this->hour_ > 23) {
40 ESP_LOGE(TAG, "Hour must be between 0 and 23");
41 this->hour_.reset();
42 }
43 if (this->minute_.has_value() && this->minute_ > 59) {
44 ESP_LOGE(TAG, "Minute must be between 0 and 59");
45 this->minute_.reset();
46 }
47 if (this->second_.has_value() && this->second_ > 59) {
48 ESP_LOGE(TAG, "Second must be between 0 and 59");
49 this->second_.reset();
50 }
51}
52
54 this->validate_();
55 ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
56 if (this->hour_.has_value()) {
57 ESP_LOGD(TAG, " Hour: %d", *this->hour_);
58 }
59 if (this->minute_.has_value()) {
60 ESP_LOGD(TAG, " Minute: %d", *this->minute_);
61 }
62 if (this->second_.has_value()) {
63 ESP_LOGD(TAG, " Second: %d", *this->second_);
64 }
65 this->parent_->control(*this);
66}
67
68TimeCall &TimeCall::set_time(uint8_t hour, uint8_t minute, uint8_t second) {
69 this->hour_ = hour;
70 this->minute_ = minute;
71 this->second_ = second;
72 return *this;
73};
74
75TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); };
76
77TimeCall &TimeCall::set_time(const char *time, size_t len) {
78 ESPTime val{};
79 if (!ESPTime::strptime(time, len, val)) {
80 ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
81 return *this;
82 }
83 return this->set_time(val);
84}
85
87 TimeCall call = time->make_call();
88 call.set_time(this->hour, this->minute, this->second);
89 return call;
90}
91
93 time->hour_ = this->hour;
94 time->minute_ = this->minute;
95 time->second_ = this->second;
96 time->publish_state();
97}
98
99#ifdef USE_TIME
100static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
101 // there has been a drastic time synchronization
102
104 if (!this->parent_->has_state()) {
105 return;
106 }
107 ESPTime time = this->parent_->rtc_->now();
108 if (!time.is_valid()) {
109 return;
110 }
111 if (this->last_check_.has_value()) {
112 if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
113 // We went back in time (a lot), probably caused by time synchronization
114 ESP_LOGW(TAG, "Time has jumped back!");
115 } else if (*this->last_check_ >= time) {
116 // already handled this one
117 return;
118 } else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
119 // We went ahead in time (a lot), probably caused by time synchronization
120 ESP_LOGW(TAG, "Time has jumped ahead!");
121 this->last_check_ = time;
122 return;
123 }
124
125 while (true) {
126 this->last_check_->increment_second();
127 if (*this->last_check_ >= time)
128 break;
129
130 if (this->matches_(*this->last_check_)) {
131 this->trigger();
132 break;
133 }
134 }
135 }
136
137 this->last_check_ = time;
138 if (!time.fields_in_range()) {
139 ESP_LOGW(TAG, "Time is out of range!");
140 ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u", time.second, time.minute, time.hour);
141 }
142
143 if (this->matches_(time))
144 this->trigger();
145}
146
147bool OnTimeTrigger::matches_(const ESPTime &time) const {
148 return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
149 time.second == this->parent_->second;
150}
151#endif
152
153} // namespace esphome::datetime
154
155#endif // USE_DATETIME_TIME
static void notify_time_update(datetime::TimeEntity *obj)
const StringRef & get_name() const
void set_has_state(bool state)
constexpr const char * c_str() const
Definition string_ref.h:73
void trigger(const Ts &...x)
Definition automation.h:279
LazyCallbackManager< void()> state_callback_
bool matches_(const ESPTime &time) const
optional< ESPTime > last_check_
optional< uint8_t > second_
Definition time_entity.h:98
optional< uint8_t > hour_
Definition time_entity.h:96
optional< uint8_t > minute_
Definition time_entity.h:97
TimeCall & set_time(uint8_t hour, uint8_t minute, uint8_t second)
virtual void control(const TimeCall &call)=0
bool has_value() const
Definition optional.h:92
uint8_t second
uint8_t minute
uint8_t hour
mopeka_std_values val[4]
std::string size_t len
Definition helpers.h:692
A more user-friendly version of struct tm from time.h.
Definition time.h:17
uint8_t minute
minutes after the hour [0-59]
Definition time.h:26
uint8_t second
seconds after the minute [0-60]
Definition time.h:24
uint8_t hour
hours since midnight [0-23]
Definition time.h:28
time_t timestamp
unix epoch time (seconds since UTC Midnight January 1, 1970)
Definition time.h:42
static bool strptime(const char *time_to_parse, size_t len, ESPTime &esp_time)
Convert a string to ESPTime struct as specified by the format argument.
Definition time.cpp:70
bool is_valid() const
Check if this ESPTime is valid (all fields in range and year is greater than 2018)
Definition time.h:74
bool fields_in_range() const
Check if all time fields of this ESPTime are in range.
Definition time.h:77