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