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