ESPHome 2026.1.0-dev
Loading...
Searching...
No Matches
datetime_entity.cpp
Go to the documentation of this file.
1#include "datetime_entity.h"
4#ifdef USE_DATETIME_DATETIME
5
6#include "esphome/core/log.h"
7
8namespace esphome::datetime {
9
10static const char *const TAG = "datetime.datetime_entity";
11
13 if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
14 this->set_has_state(false);
15 return;
16 }
17 if (this->year_ < 1970 || this->year_ > 3000) {
18 this->set_has_state(false);
19 ESP_LOGE(TAG, "Year must be between 1970 and 3000");
20 return;
21 }
22 if (this->month_ < 1 || this->month_ > 12) {
23 this->set_has_state(false);
24 ESP_LOGE(TAG, "Month must be between 1 and 12");
25 return;
26 }
27 if (this->day_ > days_in_month(this->month_, this->year_)) {
28 this->set_has_state(false);
29 ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
30 return;
31 }
32 if (this->hour_ > 23) {
33 this->set_has_state(false);
34 ESP_LOGE(TAG, "Hour must be between 0 and 23");
35 return;
36 }
37 if (this->minute_ > 59) {
38 this->set_has_state(false);
39 ESP_LOGE(TAG, "Minute must be between 0 and 59");
40 return;
41 }
42 if (this->second_ > 59) {
43 this->set_has_state(false);
44 ESP_LOGE(TAG, "Second must be between 0 and 59");
45 return;
46 }
47 this->set_has_state(true);
48 ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
49 this->month_, this->day_, this->hour_, this->minute_, this->second_);
50 this->state_callback_.call();
51#if defined(USE_DATETIME_DATETIME) && defined(USE_CONTROLLER_REGISTRY)
53#endif
54}
55
57
59 ESPTime obj;
60 obj.year = this->year_;
61 obj.month = this->month_;
62 obj.day_of_month = this->day_;
63 obj.hour = this->hour_;
64 obj.minute = this->minute_;
65 obj.second = this->second_;
67 return obj;
68}
69
71 if (this->year_.has_value() && (this->year_ < 1970 || this->year_ > 3000)) {
72 ESP_LOGE(TAG, "Year must be between 1970 and 3000");
73 this->year_.reset();
74 this->month_.reset();
75 this->day_.reset();
76 }
77 if (this->month_.has_value() && (this->month_ < 1 || this->month_ > 12)) {
78 ESP_LOGE(TAG, "Month must be between 1 and 12");
79 this->month_.reset();
80 this->day_.reset();
81 }
82 if (this->day_.has_value()) {
83 uint16_t year = 0;
84 uint8_t month = 0;
85 if (this->month_.has_value()) {
86 month = *this->month_;
87 } else {
88 if (this->parent_->month != 0) {
89 month = this->parent_->month;
90 } else {
91 ESP_LOGE(TAG, "Month must be set to validate day");
92 this->day_.reset();
93 }
94 }
95 if (this->year_.has_value()) {
96 year = *this->year_;
97 } else {
98 if (this->parent_->year != 0) {
99 year = this->parent_->year;
100 } else {
101 ESP_LOGE(TAG, "Year must be set to validate day");
102 this->day_.reset();
103 }
104 }
105 if (this->day_.has_value() && *this->day_ > days_in_month(month, year)) {
106 ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(month, year), month);
107 this->day_.reset();
108 }
109 }
110
111 if (this->hour_.has_value() && this->hour_ > 23) {
112 ESP_LOGE(TAG, "Hour must be between 0 and 23");
113 this->hour_.reset();
114 }
115 if (this->minute_.has_value() && this->minute_ > 59) {
116 ESP_LOGE(TAG, "Minute must be between 0 and 59");
117 this->minute_.reset();
118 }
119 if (this->second_.has_value() && this->second_ > 59) {
120 ESP_LOGE(TAG, "Second must be between 0 and 59");
121 this->second_.reset();
122 }
123}
124
126 this->validate_();
127 ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
128
129 if (this->year_.has_value()) {
130 ESP_LOGD(TAG, " Year: %d", *this->year_);
131 }
132 if (this->month_.has_value()) {
133 ESP_LOGD(TAG, " Month: %d", *this->month_);
134 }
135 if (this->day_.has_value()) {
136 ESP_LOGD(TAG, " Day: %d", *this->day_);
137 }
138 if (this->hour_.has_value()) {
139 ESP_LOGD(TAG, " Hour: %d", *this->hour_);
140 }
141 if (this->minute_.has_value()) {
142 ESP_LOGD(TAG, " Minute: %d", *this->minute_);
143 }
144 if (this->second_.has_value()) {
145 ESP_LOGD(TAG, " Second: %d", *this->second_);
146 }
147 this->parent_->control(*this);
148}
149
150DateTimeCall &DateTimeCall::set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
151 uint8_t second) {
152 this->year_ = year;
153 this->month_ = month;
154 this->day_ = day;
155 this->hour_ = hour;
156 this->minute_ = minute;
157 this->second_ = second;
158 return *this;
159};
160
162 return this->set_datetime(datetime.year, datetime.month, datetime.day_of_month, datetime.hour, datetime.minute,
163 datetime.second);
164};
165
166DateTimeCall &DateTimeCall::set_datetime(const std::string &datetime) {
167 ESPTime val{};
168 if (!ESPTime::strptime(datetime, val)) {
169 ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
170 return *this;
171 }
172 return this->set_datetime(val);
173}
174
176 ESPTime val = ESPTime::from_epoch_local(epoch_seconds);
177 return this->set_datetime(val);
178}
179
181 DateTimeCall call = datetime->make_call();
182 call.set_datetime(this->year, this->month, this->day, this->hour, this->minute, this->second);
183 return call;
184}
185
187 time->year_ = this->year;
188 time->month_ = this->month;
189 time->day_ = this->day;
190 time->hour_ = this->hour;
191 time->minute_ = this->minute;
192 time->second_ = this->second;
193 time->publish_state();
194}
195
196#ifdef USE_TIME
197static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
198 // there has been a drastic time synchronization
199
201 if (!this->parent_->has_state()) {
202 return;
203 }
204 ESPTime time = this->parent_->rtc_->now();
205 if (!time.is_valid()) {
206 return;
207 }
208 if (this->last_check_.has_value()) {
209 if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
210 // We went back in time (a lot), probably caused by time synchronization
211 ESP_LOGW(TAG, "Time has jumped back!");
212 } else if (*this->last_check_ >= time) {
213 // already handled this one
214 return;
215 } else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
216 // We went ahead in time (a lot), probably caused by time synchronization
217 ESP_LOGW(TAG, "Time has jumped ahead!");
218 this->last_check_ = time;
219 return;
220 }
221
222 while (true) {
223 this->last_check_->increment_second();
224 if (*this->last_check_ >= time)
225 break;
226
227 if (this->matches_(*this->last_check_)) {
228 this->trigger();
229 break;
230 }
231 }
232 }
233
234 this->last_check_ = time;
235 if (!time.fields_in_range()) {
236 ESP_LOGW(TAG, "Time is out of range!");
237 ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u Day=%02u Month=%02u Year=%04u", time.second, time.minute,
238 time.hour, time.day_of_month, time.month, time.year);
239 }
240
241 if (this->matches_(time))
242 this->trigger();
243}
244
245bool OnDateTimeTrigger::matches_(const ESPTime &time) const {
246 return time.is_valid() && time.year == this->parent_->year && time.month == this->parent_->month &&
247 time.day_of_month == this->parent_->day && time.hour == this->parent_->hour &&
248 time.minute == this->parent_->minute && time.second == this->parent_->second;
249}
250#endif
251
252} // namespace esphome::datetime
253
254#endif // USE_DATETIME_TIME
static void notify_datetime_update(datetime::DateTimeEntity *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_
DateTimeCall & set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
virtual void control(const DateTimeCall &call)=0
ESPTime state_as_esptime() const override
bool matches_(const ESPTime &time) const
bool has_value() const
Definition optional.h:92
uint8_t month
Definition date_entity.h:1
uint16_t year
Definition date_entity.h:0
uint8_t day
Definition date_entity.h:2
uint8_t second
uint8_t minute
uint8_t hour
mopeka_std_values val[4]
uint8_t days_in_month(uint8_t month, uint16_t year)
Definition time.cpp:8
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
static ESPTime from_epoch_local(time_t epoch)
Convert an UTC epoch timestamp to a local time ESPTime instance.
Definition time.h:85
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
void recalc_timestamp_local()
Recalculate the timestamp field from the other fields of this ESPTime instance assuming local fields.
Definition time.cpp:185
uint8_t day_of_month
day of the month [1-31]
Definition time.h:27
uint16_t year
year
Definition time.h:33
bool fields_in_range() const
Check if all time fields of this ESPTime are in range.
Definition time.h:64
uint8_t month
month; january=1 [1-12]
Definition time.h:31
DateTimeCall to_call(DateTimeEntity *datetime)