ESPHome 2025.5.0
Loading...
Searching...
No Matches
time_entity.cpp
Go to the documentation of this file.
1#include "time_entity.h"
2
3#ifdef USE_DATETIME_TIME
4
5#include "esphome/core/log.h"
6
7namespace esphome {
8namespace datetime {
9
10static const char *const TAG = "datetime.time_entity";
11
13 if (this->hour_ > 23) {
14 this->has_state_ = false;
15 ESP_LOGE(TAG, "Hour must be between 0 and 23");
16 return;
17 }
18 if (this->minute_ > 59) {
19 this->has_state_ = false;
20 ESP_LOGE(TAG, "Minute must be between 0 and 59");
21 return;
22 }
23 if (this->second_ > 59) {
24 this->has_state_ = false;
25 ESP_LOGE(TAG, "Second must be between 0 and 59");
26 return;
27 }
28 this->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}
33
35
37 if (this->hour_.has_value() && this->hour_ > 23) {
38 ESP_LOGE(TAG, "Hour must be between 0 and 23");
39 this->hour_.reset();
40 }
41 if (this->minute_.has_value() && this->minute_ > 59) {
42 ESP_LOGE(TAG, "Minute must be between 0 and 59");
43 this->minute_.reset();
44 }
45 if (this->second_.has_value() && this->second_ > 59) {
46 ESP_LOGE(TAG, "Second must be between 0 and 59");
47 this->second_.reset();
48 }
49}
50
52 this->validate_();
53 ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
54 if (this->hour_.has_value()) {
55 ESP_LOGD(TAG, " Hour: %d", *this->hour_);
56 }
57 if (this->minute_.has_value()) {
58 ESP_LOGD(TAG, " Minute: %d", *this->minute_);
59 }
60 if (this->second_.has_value()) {
61 ESP_LOGD(TAG, " Second: %d", *this->second_);
62 }
63 this->parent_->control(*this);
64}
65
66TimeCall &TimeCall::set_time(uint8_t hour, uint8_t minute, uint8_t second) {
67 this->hour_ = hour;
68 this->minute_ = minute;
69 this->second_ = second;
70 return *this;
71};
72
73TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); };
74
75TimeCall &TimeCall::set_time(const std::string &time) {
76 ESPTime val{};
77 if (!ESPTime::strptime(time, val)) {
78 ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
79 return *this;
80 }
81 return this->set_time(val);
82}
83
85 TimeCall call = time->make_call();
86 call.set_time(this->hour, this->minute, this->second);
87 return call;
88}
89
91 time->hour_ = this->hour;
92 time->minute_ = this->minute;
93 time->second_ = this->second;
94 time->publish_state();
95}
96
97#ifdef USE_TIME
98static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
99 // there has been a drastic time synchronization
100
102 if (!this->parent_->has_state()) {
103 return;
104 }
105 ESPTime time = this->parent_->rtc_->now();
106 if (!time.is_valid()) {
107 return;
108 }
109 if (this->last_check_.has_value()) {
110 if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
111 // We went back in time (a lot), probably caused by time synchronization
112 ESP_LOGW(TAG, "Time has jumped back!");
113 } else if (*this->last_check_ >= time) {
114 // already handled this one
115 return;
116 } else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
117 // We went ahead in time (a lot), probably caused by time synchronization
118 ESP_LOGW(TAG, "Time has jumped ahead!");
119 this->last_check_ = time;
120 return;
121 }
122
123 while (true) {
124 this->last_check_->increment_second();
125 if (*this->last_check_ >= time)
126 break;
127
128 if (this->matches_(*this->last_check_)) {
129 this->trigger();
130 break;
131 }
132 }
133 }
134
135 this->last_check_ = time;
136 if (!time.fields_in_range()) {
137 ESP_LOGW(TAG, "Time is out of range!");
138 ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u", time.second, time.minute, time.hour);
139 }
140
141 if (this->matches_(time))
142 this->trigger();
143}
144
145bool OnTimeTrigger::matches_(const ESPTime &time) const {
146 return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
147 time.second == this->parent_->second;
148}
149#endif
150
151} // namespace datetime
152} // namespace esphome
153
154#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_
bool matches_(const ESPTime &time) const
optional< ESPTime > last_check_
optional< uint8_t > second_
Definition time_entity.h:99
optional< uint8_t > hour_
Definition time_entity.h:97
optional< uint8_t > minute_
Definition time_entity.h:98
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:87
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
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: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
bool fields_in_range() const
Check if all time fields of this ESPTime are in range.
Definition time.h:62