ESPHome  2024.9.0
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 static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
98  // there has been a drastic time synchronization
99 
101  if (!this->parent_->has_state()) {
102  return;
103  }
104  ESPTime time = this->parent_->rtc_->now();
105  if (!time.is_valid()) {
106  return;
107  }
108  if (this->last_check_.has_value()) {
109  if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
110  // We went back in time (a lot), probably caused by time synchronization
111  ESP_LOGW(TAG, "Time has jumped back!");
112  } else if (*this->last_check_ >= time) {
113  // already handled this one
114  return;
115  } else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
116  // We went ahead in time (a lot), probably caused by time synchronization
117  ESP_LOGW(TAG, "Time has jumped ahead!");
118  this->last_check_ = time;
119  return;
120  }
121 
122  while (true) {
123  this->last_check_->increment_second();
124  if (*this->last_check_ >= time)
125  break;
126 
127  if (this->matches_(*this->last_check_)) {
128  this->trigger();
129  break;
130  }
131  }
132  }
133 
134  this->last_check_ = time;
135  if (!time.fields_in_range()) {
136  ESP_LOGW(TAG, "Time is out of range!");
137  ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u", time.second, time.minute, time.hour);
138  }
139 
140  if (this->matches_(time))
141  this->trigger();
142 }
143 
144 bool OnTimeTrigger::matches_(const ESPTime &time) const {
145  return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
146  time.second == this->parent_->second;
147 }
148 
149 } // namespace datetime
150 } // namespace esphome
151 
152 #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:26
void increment_second()
Increment this clock instance by one second.
Definition: time.cpp:118
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
Implementation of SPI Controller mode.
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