ESPHome  2024.11.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 #ifdef USE_TIME
98 static 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 
145 bool 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
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:29
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