This commit is contained in:
rfdarter 2024-10-17 04:48:19 +02:00
parent 1c845e0ff8
commit 436e490094
6 changed files with 173 additions and 0 deletions

View File

@ -0,0 +1,25 @@
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome import automation
from esphome.components import time
from esphome.const import CONF_ID
from .. import template_ns
CODEOWNERS = ["@RFDarter"]
TemplateRealTimeClock = template_ns.class_("TemplateRealTimeClock", time.RealTimeClock)
WriteAction = template_ns.class_("SystemTimeSetAction", automation.Action)
CONFIG_SCHEMA = time.TIME_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TemplateRealTimeClock),
}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await time.register_time(var, config)

View File

@ -0,0 +1,21 @@
#include "template_time.h"
#include "esphome/core/log.h"
namespace esphome {
namespace template_ {
static const char *const TAG = "template.time";
void TemplateRealTimeClock::setup() {}
void TemplateRealTimeClock::update() {}
void TemplateRealTimeClock::dump_config() {
ESP_LOGCONFIG(TAG, "template.time");
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
}
float TemplateRealTimeClock::get_setup_priority() const { return setup_priority::DATA; }
} // namespace template_
} // namespace esphome

View File

@ -0,0 +1,19 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/time/real_time_clock.h"
namespace esphome {
namespace template_ {
class TemplateRealTimeClock : public time::RealTimeClock {
public:
void setup() override;
void update() override;
void dump_config() override;
float get_setup_priority() const override;
};
} // namespace template_
} // namespace esphome

View File

@ -11,6 +11,8 @@ import esphome.config_validation as cv
from esphome.const import (
CONF_AT,
CONF_CRON,
CONF_DATETIME,
CONF_DAY,
CONF_DAYS_OF_MONTH,
CONF_DAYS_OF_WEEK,
CONF_HOUR,
@ -18,6 +20,7 @@ from esphome.const import (
CONF_ID,
CONF_MINUTE,
CONF_MINUTES,
CONF_MONTH,
CONF_MONTHS,
CONF_ON_TIME,
CONF_ON_TIME_SYNC,
@ -25,6 +28,7 @@ from esphome.const import (
CONF_SECONDS,
CONF_TIMEZONE,
CONF_TRIGGER_ID,
CONF_YEAR,
)
from esphome.core import coroutine_with_priority
@ -33,11 +37,14 @@ _LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@OttoWinter"]
IS_PLATFORM_COMPONENT = True
CONF_UTC = "utc"
time_ns = cg.esphome_ns.namespace("time")
RealTimeClock = time_ns.class_("RealTimeClock", cg.PollingComponent)
CronTrigger = time_ns.class_("CronTrigger", automation.Trigger.template(), cg.Component)
SyncTrigger = time_ns.class_("SyncTrigger", automation.Trigger.template(), cg.Component)
TimeHasTimeCondition = time_ns.class_("TimeHasTimeCondition", Condition)
SystemTimeSetAction = time_ns.class_("SystemTimeSetAction", automation.Action)
def _load_tzdata(iana_key: str) -> Optional[bytes]:
@ -344,3 +351,39 @@ async def to_code(config):
async def time_has_time_to_code(config, condition_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(condition_id, template_arg, paren)
@automation.register_action(
"system_time.set",
SystemTimeSetAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(RealTimeClock),
cv.Required(CONF_DATETIME): cv.Any(
cv.returning_lambda, cv.date_time(date=True, time=True)
),
cv.Optional(CONF_UTC, default=False): cv.boolean,
},
),
)
async def system_time_set_to_code(config, action_id, template_arg, args):
action_var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(action_var, config[CONF_ID])
datetime_config = config[CONF_DATETIME]
cg.add(action_var.set_utc(config[CONF_UTC]))
if cg.is_template(datetime_config):
template_ = await cg.templatable(datetime_config, args, cg.ESPTime)
cg.add(action_var.set_time(template_))
else:
datetime_struct = cg.StructInitializer(
cg.ESPTime,
("second", datetime_config[CONF_SECOND]),
("minute", datetime_config[CONF_MINUTE]),
("hour", datetime_config[CONF_HOUR]),
("day_of_month", datetime_config[CONF_DAY]),
("month", datetime_config[CONF_MONTH]),
("year", datetime_config[CONF_YEAR]),
)
cg.add(action_var.set_time(datetime_struct))
return action_var

View File

@ -58,5 +58,52 @@ void RealTimeClock::apply_timezone_() {
tzset();
}
void RealTimeClock::set_time(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second,
bool utc) {
ESPTime time{
.second = second,
.minute = minute,
.hour = hour,
.day_of_week = 1, // not used
.day_of_month = day,
.day_of_year = 1, // ignored by recalc_timestamp_utc(false)
.month = month,
.year = year,
.is_dst = false, // not used
.timestamp = 0 // overwritten by recalc_timestamp_utc(false)
};
if (utc) {
time.recalc_timestamp_utc();
} else {
time.recalc_timestamp_local();
}
if (!time.is_valid()) {
ESP_LOGE(TAG, "Invalid time, not syncing to system clock.");
return;
}
time::RealTimeClock::synchronize_epoch_(time.timestamp);
};
void RealTimeClock::set_time(ESPTime datetime, bool utc) {
return this->set_time(datetime.year, datetime.month, datetime.day_of_month, datetime.hour, datetime.minute,
datetime.second, utc);
};
void RealTimeClock::set_time(const std::string &datetime, bool utc) {
ESPTime val{};
if (!ESPTime::strptime(datetime, val)) {
ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
return;
}
this->set_time(val, utc);
}
void RealTimeClock::set_time(time_t epoch_seconds, bool utc) {
ESPTime val = ESPTime::from_epoch_local(epoch_seconds);
this->set_time(val, utc);
}
} // namespace time
} // namespace esphome

View File

@ -20,6 +20,12 @@ class RealTimeClock : public PollingComponent {
public:
explicit RealTimeClock();
void set_time(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second,
bool utc = false);
void set_time(ESPTime datetime, bool utc = false);
void set_time(const std::string &datetime, bool utc = false);
void set_time(time_t epoch_seconds, bool utc = false);
/// Set the time zone.
void set_timezone(const std::string &tz) { this->timezone_ = tz; }
@ -60,5 +66,17 @@ template<typename... Ts> class TimeHasTimeCondition : public Condition<Ts...> {
RealTimeClock *parent_;
};
template<typename... Ts> class SystemTimeSetAction : public Action<Ts...>, public Parented<RealTimeClock> {
public:
TEMPLATABLE_VALUE(ESPTime, time)
TEMPLATABLE_VALUE(bool, utc)
void play(Ts... x) override {
if (this->time_.has_value() && this->utc_.has_value()) {
this->parent_->set_time(this->time_.value(x...), this->utc_.value(x...));
}
}
};
} // namespace time
} // namespace esphome