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