mirror of https://github.com/esphome/esphome.git
157 lines
4.3 KiB
C++
157 lines
4.3 KiB
C++
#include "time_entity.h"
|
|
|
|
#ifdef USE_DATETIME_TIME
|
|
|
|
#include "esphome/core/log.h"
|
|
|
|
namespace esphome {
|
|
namespace datetime {
|
|
|
|
static const char *const TAG = "datetime.time_entity";
|
|
|
|
void TimeEntity::publish_state() {
|
|
if (this->hour_ > 23) {
|
|
this->has_state_ = false;
|
|
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
|
return;
|
|
}
|
|
if (this->minute_ > 59) {
|
|
this->has_state_ = false;
|
|
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
|
return;
|
|
}
|
|
if (this->second_ > 59) {
|
|
this->has_state_ = false;
|
|
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
|
return;
|
|
}
|
|
this->has_state_ = true;
|
|
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
|
this->second_);
|
|
this->state_callback_.call();
|
|
}
|
|
|
|
TimeCall TimeEntity::make_call() { return TimeCall(this); }
|
|
|
|
void TimeCall::validate_() {
|
|
if (this->hour_.has_value() && this->hour_ > 23) {
|
|
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
|
this->hour_.reset();
|
|
}
|
|
if (this->minute_.has_value() && this->minute_ > 59) {
|
|
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
|
this->minute_.reset();
|
|
}
|
|
if (this->second_.has_value() && this->second_ > 59) {
|
|
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
|
this->second_.reset();
|
|
}
|
|
}
|
|
|
|
void TimeCall::perform() {
|
|
this->validate_();
|
|
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
|
|
if (this->hour_.has_value()) {
|
|
ESP_LOGD(TAG, " Hour: %d", *this->hour_);
|
|
}
|
|
if (this->minute_.has_value()) {
|
|
ESP_LOGD(TAG, " Minute: %d", *this->minute_);
|
|
}
|
|
if (this->second_.has_value()) {
|
|
ESP_LOGD(TAG, " Second: %d", *this->second_);
|
|
}
|
|
this->parent_->control(*this);
|
|
}
|
|
|
|
TimeCall &TimeCall::set_time(uint8_t hour, uint8_t minute, uint8_t second) {
|
|
this->hour_ = hour;
|
|
this->minute_ = minute;
|
|
this->second_ = second;
|
|
return *this;
|
|
};
|
|
|
|
TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); };
|
|
|
|
TimeCall &TimeCall::set_time(const std::string &time) {
|
|
ESPTime val{};
|
|
if (!ESPTime::strptime(time, val)) {
|
|
ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
|
|
return *this;
|
|
}
|
|
return this->set_time(val);
|
|
}
|
|
|
|
TimeCall TimeEntityRestoreState::to_call(TimeEntity *time) {
|
|
TimeCall call = time->make_call();
|
|
call.set_time(this->hour, this->minute, this->second);
|
|
return call;
|
|
}
|
|
|
|
void TimeEntityRestoreState::apply(TimeEntity *time) {
|
|
time->hour_ = this->hour;
|
|
time->minute_ = this->minute;
|
|
time->second_ = this->second;
|
|
time->publish_state();
|
|
}
|
|
|
|
#ifdef USE_TIME
|
|
|
|
static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
|
|
// there has been a drastic time synchronization
|
|
|
|
void OnTimeTrigger::loop() {
|
|
if (!this->parent_->has_state()) {
|
|
return;
|
|
}
|
|
ESPTime time = this->rtc_->now();
|
|
if (!time.is_valid()) {
|
|
return;
|
|
}
|
|
if (this->last_check_.has_value()) {
|
|
if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
|
|
// We went back in time (a lot), probably caused by time synchronization
|
|
ESP_LOGW(TAG, "Time has jumped back!");
|
|
} else if (*this->last_check_ >= time) {
|
|
// already handled this one
|
|
return;
|
|
} else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
|
|
// We went ahead in time (a lot), probably caused by time synchronization
|
|
ESP_LOGW(TAG, "Time has jumped ahead!");
|
|
this->last_check_ = time;
|
|
return;
|
|
}
|
|
|
|
while (true) {
|
|
this->last_check_->increment_second();
|
|
if (*this->last_check_ >= time)
|
|
break;
|
|
|
|
if (this->matches_(*this->last_check_)) {
|
|
this->trigger();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
this->last_check_ = time;
|
|
if (!time.fields_in_range()) {
|
|
ESP_LOGW(TAG, "Time is out of range!");
|
|
ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u", time.second, time.minute, time.hour);
|
|
}
|
|
|
|
if (this->matches_(time))
|
|
this->trigger();
|
|
}
|
|
|
|
bool OnTimeTrigger::matches_(const ESPTime &time) const {
|
|
return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
|
|
time.second == this->parent_->second;
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace datetime
|
|
} // namespace esphome
|
|
|
|
#endif // USE_DATETIME_TIME
|