From aa371c22a4a64d22e5ee75b47f8ab3d893a40439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Thu, 17 Aug 2023 00:21:56 +0200 Subject: [PATCH] Add pcf85063.start_timer action --- esphome/components/pcf85063/pcf85063.cpp | 36 +++++++++++++++++++++++ esphome/components/pcf85063/pcf85063.h | 14 +++++++++ esphome/components/pcf85063/time.py | 37 +++++++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/esphome/components/pcf85063/pcf85063.cpp b/esphome/components/pcf85063/pcf85063.cpp index 941278d..da7d82d 100644 --- a/esphome/components/pcf85063/pcf85063.cpp +++ b/esphome/components/pcf85063/pcf85063.cpp @@ -91,6 +91,42 @@ uint8_t PCF85063Component::read_nvram() { return this->pcf85063_.reg.nvram; } +/* + TIMER_CLOCK_MINUTE 60 15300 1m 4h15m + TIMER_CLOCK_SECOND 1 255 1s 4m15s +*/ +bool PCF85063Component::set_timer_interval(uint16_t interval_seconds) { + if (interval_seconds < 256) { + pcf85063_.reg.timer_clock_frequency = TIMER_CLOCK_SECOND; + pcf85063_.reg.timer_value = interval_seconds; + pcf85063_.reg.timer_enable = true; + return this->write_timer_(); + } else if (interval_seconds > 15300) { + ESP_LOGE(TAG, "Specified interval is longer than max allowed, clamping to 4h15m."); + interval_seconds = 15300; + } + div_t dm = div(interval_seconds, 60); + if (dm.rem) { + ESP_LOGI(TAG, "Interval out of seconds range, rounding down to closest whole minute."); + } + + pcf85063_.reg.timer_clock_frequency = TIMER_CLOCK_MINUTE; + pcf85063_.reg.timer_value = dm.quot; + pcf85063_.reg.timer_enable = true; + return this->write_timer_(); +} + +bool PCF85063Component::write_timer_() { + if (!this->write_bytes(0x10, &this->pcf85063_.raw[0x03], 2)) { + ESP_LOGE(TAG, "Can't write I2C data."); + return false; + } + ESP_LOGD(TAG, "Write timer %s %0u CLOCK=%0u IR=%s %0u", + ONOFF(pcf85063_.reg.timer_enable), pcf85063_.reg.timer_value, pcf85063_.reg.timer_clock_frequency, + ONOFF(pcf85063_.reg.timer_interrupt_enable), pcf85063_.reg.timer_interrupt_mode); + return true; +} + bool PCF85063Component::read_rtc_() { if (!this->read_bytes(0, this->pcf85063_.raw, sizeof(this->pcf85063_.raw))) { ESP_LOGE(TAG, "Can't read I2C data."); diff --git a/esphome/components/pcf85063/pcf85063.h b/esphome/components/pcf85063/pcf85063.h index 3d3589c..48e830d 100644 --- a/esphome/components/pcf85063/pcf85063.h +++ b/esphome/components/pcf85063/pcf85063.h @@ -30,7 +30,11 @@ class PCF85063Component : public time::RealTimeClock, public i2c::I2CDevice { void write_nvram(uint8_t); uint8_t read_nvram(); + bool set_timer_interval(uint16_t interval); + + protected: + bool write_timer_(); bool read_rtc_(); bool write_rtc_(); union PCF85063Reg { @@ -135,6 +139,16 @@ class PCF85063Component : public time::RealTimeClock, public i2c::I2CDevice { } pcf85063_; }; +template class StartTimerAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(uint16_t, timer_seconds); + void play(Ts... x) override { + this->parent_->pcf85063_.reg.timer_interrupt_enable = true; + this->parent_->pcf85063_.reg.timer_interrupt_mode = TIMER_INTERRUPT_MODE_FLAG; + this->parent_->set_timer_interval(this->timer_seconds_.value(x...)); + } +}; + template class WriteAction : public Action, public Parented { public: void play(Ts... x) override { this->parent_->write_time(); } diff --git a/esphome/components/pcf85063/time.py b/esphome/components/pcf85063/time.py index 67ec230..43a9a06 100644 --- a/esphome/components/pcf85063/time.py +++ b/esphome/components/pcf85063/time.py @@ -2,7 +2,7 @@ import esphome.config_validation as cv import esphome.codegen as cg from esphome import automation from esphome.components import i2c, time -from esphome.const import CONF_ID +from esphome.const import CONF_ID, CONF_INTERVAL CODEOWNERS = ["@brogon"] @@ -13,6 +13,7 @@ PCF85063Component = pcf85063_ns.class_( ) WriteAction = pcf85063_ns.class_("WriteAction", automation.Action) ReadAction = pcf85063_ns.class_("ReadAction", automation.Action) +StartTimerAction = pcf85063_ns.class_("StartTimerAction", automation.Action) CONFIG_SCHEMA = time.TIME_SCHEMA.extend( @@ -52,6 +53,40 @@ async def pcf85063_read_time_to_code(config, action_id, template_arg, args): return var +def validate_timer_seconds(value): + value: cv.TimePeriodSeconds = cv.positive_time_period_seconds(value) + min_interval = cv.TimePeriod(seconds=1) + max_interval = cv.TimePeriod(minutes=255) + + if value < min_interval: + raise cv.Invalid( + f"This timer interval is not possible, please choose a larger interval (at least {min_interval})" + ) + if value > max_interval: + raise cv.Invalid( + f"This timer interval is not possible, please choose a lower interval (at most {max_interval})" + ) + return value + +@automation.register_action( + "pcf85063.start_timer", + StartTimerAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(PCF85063Component), + cv.Required(CONF_INTERVAL): cv.templatable(validate_timer_seconds), + } + ), +) +async def pcf85063_start_timer_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + + template_ = await cg.templatable(config[CONF_INTERVAL], args, cv.TimePeriodSeconds) + cg.add(var.start_timer(template_)) + + await cg.register_parented(var, config[CONF_ID]) + return var + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID])