ESPHome  2024.4.2
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 
7 namespace esphome {
8 namespace datetime {
9 
10 static 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 
66 TimeCall &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 
73 TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); };
74 
75 TimeCall &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
98 
99 static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
100  // there has been a drastic time synchronization
101 
103  if (!this->parent_->has_state()) {
104  return;
105  }
106  ESPTime time = this->rtc_->now();
107  if (!time.is_valid()) {
108  return;
109  }
110  if (this->last_check_.has_value()) {
111  if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
112  // We went back in time (a lot), probably caused by time synchronization
113  ESP_LOGW(TAG, "Time has jumped back!");
114  } else if (*this->last_check_ >= time) {
115  // already handled this one
116  return;
117  } else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
118  // We went ahead in time (a lot), probably caused by time synchronization
119  ESP_LOGW(TAG, "Time has jumped ahead!");
120  this->last_check_ = time;
121  return;
122  }
123 
124  while (true) {
125  this->last_check_->increment_second();
126  if (*this->last_check_ >= time)
127  break;
128 
129  if (this->matches_(*this->last_check_)) {
130  this->trigger();
131  break;
132  }
133  }
134  }
135 
136  this->last_check_ = time;
137  if (!time.fields_in_range()) {
138  ESP_LOGW(TAG, "Time is out of range!");
139  ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u", time.second, time.minute, time.hour);
140  }
141 
142  if (this->matches_(time))
143  this->trigger();
144 }
145 
146 bool OnTimeTrigger::matches_(const ESPTime &time) const {
147  return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
148  time.second == this->parent_->second;
149 }
150 
151 #endif
152 
153 } // namespace datetime
154 } // namespace esphome
155 
156 #endif // USE_DATETIME_TIME
TimeCall & set_time(uint8_t hour, uint8_t minute, uint8_t second)
Definition: time_entity.cpp:66
A more user-friendly version of struct tm from time.h.
Definition: time.h:17
mopeka_std_values val[4]
CallbackManager< void()> state_callback_
Definition: datetime_base.h:21
void increment_second()
Increment this clock instance by one second.
Definition: time.cpp:108
uint8_t second
seconds after the minute [0-60]
Definition: time.h:21
time_t timestamp
unix epoch time (seconds since UTC Midnight January 1, 1970)
Definition: time.h:39
uint8_t minute
minutes after the hour [0-59]
Definition: time.h:23
bool is_valid() const
Check if this ESPTime is valid (all fields in range and year is greater than 2018) ...
Definition: time.h:61
TimeCall to_call(TimeEntity *time)
Definition: time_entity.cpp:84
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
uint8_t hour
hours since midnight [0-23]
Definition: time.h:25
const StringRef & get_name() const
Definition: entity_base.cpp:10
bool matches_(const ESPTime &time) const
bool fields_in_range() const
Check if all time fields of this ESPTime are in range.
Definition: time.h:64
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