diff --git a/CODEOWNERS b/CODEOWNERS index 9cf1d30e19..95729bae8d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -29,6 +29,7 @@ esphome/components/ct_clamp/* @jesserockz esphome/components/debug/* @OttoWinter esphome/components/dfplayer/* @glmnet esphome/components/dht/* @OttoWinter +esphome/components/ds1307/* @badbadc0ffee esphome/components/exposure_notifications/* @OttoWinter esphome/components/ezo/* @ssieb esphome/components/fastled_base/* @OttoWinter diff --git a/esphome/components/ds1307/__init__.py b/esphome/components/ds1307/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/ds1307/ds1307.cpp b/esphome/components/ds1307/ds1307.cpp new file mode 100644 index 0000000000..990767ddd4 --- /dev/null +++ b/esphome/components/ds1307/ds1307.cpp @@ -0,0 +1,104 @@ +#include "ds1307.h" +#include "esphome/core/log.h" + +// Datasheet: +// - https://datasheets.maximintegrated.com/en/ds/DS1307.pdf + +namespace esphome { +namespace ds1307 { + +static const char *TAG = "ds1307"; + +void DS1307Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up DS1307..."); + if (!this->read_rtc_()) { + this->mark_failed(); + } + this->set_interval(15 * 60 * 1000, [&]() { this->read(); }); +} + +void DS1307Component::dump_config() { + ESP_LOGCONFIG(TAG, "DS1307:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with DS1307 failed!"); + } + ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); +} + +float DS1307Component::get_setup_priority() const { return setup_priority::DATA; } + +void DS1307Component::read() { + if (!this->read_rtc_()) { + return; + } + if (ds1307_.reg.ch) { + ESP_LOGW(TAG, "RTC halted, not syncing to system clock."); + return; + } + time::ESPTime rtc_time{.second = uint8_t(ds1307_.reg.second + 10 * ds1307_.reg.second_10), + .minute = uint8_t(ds1307_.reg.minute + 10u * ds1307_.reg.minute_10), + .hour = uint8_t(ds1307_.reg.hour + 10u * ds1307_.reg.hour_10), + .day_of_week = uint8_t(ds1307_.reg.weekday), + .day_of_month = uint8_t(ds1307_.reg.day + 10u * ds1307_.reg.day_10), + .day_of_year = 1, // ignored by recalc_timestamp_utc(false) + .month = uint8_t(ds1307_.reg.month + 10u * ds1307_.reg.month_10), + .year = uint16_t(ds1307_.reg.year + 10u * ds1307_.reg.year_10 + 2000)}; + rtc_time.recalc_timestamp_utc(false); + if (!rtc_time.is_valid()) { + ESP_LOGE(TAG, "Invalid RTC time, not syncing to system clock."); + return; + } + time::RealTimeClock::synchronize_epoch_(rtc_time.timestamp); +} + +void DS1307Component::write() { + auto now = time::RealTimeClock::utcnow(); + if (!now.is_valid()) { + ESP_LOGE(TAG, "Invalid system time, not syncing to RTC."); + return; + } + ds1307_.reg.year = (now.year - 2000) % 10; + ds1307_.reg.year_10 = (now.year - 2000) / 10 % 10; + ds1307_.reg.month = now.month % 10; + ds1307_.reg.month_10 = now.month / 10; + ds1307_.reg.day = now.day_of_month % 10; + ds1307_.reg.day_10 = now.day_of_month / 10; + ds1307_.reg.weekday = now.day_of_week; + ds1307_.reg.hour = now.hour % 10; + ds1307_.reg.hour_10 = now.hour / 10; + ds1307_.reg.minute = now.minute % 10; + ds1307_.reg.minute_10 = now.minute / 10; + ds1307_.reg.second = now.second % 10; + ds1307_.reg.second_10 = now.second / 10; + ds1307_.reg.ch = false; + + this->write_rtc_(); +} + +bool DS1307Component::read_rtc_() { + if (!this->read_bytes(0, this->ds1307_.raw, sizeof(this->ds1307_.raw))) { + ESP_LOGE(TAG, "Can't read I2C data."); + return false; + } + ESP_LOGD(TAG, "Read %0u%0u:%0u%0u:%0u%0u 20%0u%0u-%0u%0u-%0u%0u CH:%s RS:%0u SQWE:%s OUT:%s", ds1307_.reg.hour_10, + ds1307_.reg.hour, ds1307_.reg.minute_10, ds1307_.reg.minute, ds1307_.reg.second_10, ds1307_.reg.second, + ds1307_.reg.year_10, ds1307_.reg.year, ds1307_.reg.month_10, ds1307_.reg.month, ds1307_.reg.day_10, + ds1307_.reg.day, ONOFF(ds1307_.reg.ch), ds1307_.reg.rs, ONOFF(ds1307_.reg.sqwe), ONOFF(ds1307_.reg.out)); + + return true; +} + +bool DS1307Component::write_rtc_() { + if (!this->write_bytes(0, this->ds1307_.raw, sizeof(this->ds1307_.raw))) { + ESP_LOGE(TAG, "Can't write I2C data."); + return false; + } + ESP_LOGD(TAG, "Write %0u%0u:%0u%0u:%0u%0u 20%0u%0u-%0u%0u-%0u%0u CH:%s RS:%0u SQWE:%s OUT:%s", ds1307_.reg.hour_10, + ds1307_.reg.hour, ds1307_.reg.minute_10, ds1307_.reg.minute, ds1307_.reg.second_10, ds1307_.reg.second, + ds1307_.reg.year_10, ds1307_.reg.year, ds1307_.reg.month_10, ds1307_.reg.month, ds1307_.reg.day_10, + ds1307_.reg.day, ONOFF(ds1307_.reg.ch), ds1307_.reg.rs, ONOFF(ds1307_.reg.sqwe), ONOFF(ds1307_.reg.out)); + return true; +} +} // namespace ds1307 +} // namespace esphome diff --git a/esphome/components/ds1307/ds1307.h b/esphome/components/ds1307/ds1307.h new file mode 100644 index 0000000000..1f732a7c7d --- /dev/null +++ b/esphome/components/ds1307/ds1307.h @@ -0,0 +1,69 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/time/real_time_clock.h" + +namespace esphome { +namespace ds1307 { + +class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void read(); + void write(); + + protected: + bool read_rtc_(); + bool write_rtc_(); + union DS1307Reg { + struct { + uint8_t second : 4; + uint8_t second_10 : 3; + bool ch : 1; + + uint8_t minute : 4; + uint8_t minute_10 : 3; + uint8_t unused_1 : 1; + + uint8_t hour : 4; + uint8_t hour_10 : 2; + uint8_t unused_2 : 2; + + uint8_t weekday : 3; + uint8_t unused_3 : 5; + + uint8_t day : 4; + uint8_t day_10 : 2; + uint8_t unused_4 : 2; + + uint8_t month : 4; + uint8_t month_10 : 1; + uint8_t unused_5 : 3; + + uint8_t year : 4; + uint8_t year_10 : 4; + + uint8_t rs : 2; + uint8_t unused_6 : 2; + bool sqwe : 1; + uint8_t unused_7 : 2; + bool out : 1; + } reg; + mutable uint8_t raw[sizeof(reg)]; + } ds1307_; +}; + +template class WriteAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->write(); } +}; + +template class ReadAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->read(); } +}; +} // namespace ds1307 +} // namespace esphome diff --git a/esphome/components/ds1307/time.py b/esphome/components/ds1307/time.py new file mode 100644 index 0000000000..c381fb7b2a --- /dev/null +++ b/esphome/components/ds1307/time.py @@ -0,0 +1,44 @@ +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 + + +CODEOWNERS = ['@badbadc0ffee'] +DEPENDENCIES = ['i2c'] +ds1307_ns = cg.esphome_ns.namespace('ds1307') +DS1307Component = ds1307_ns.class_('DS1307Component', time.RealTimeClock, i2c.I2CDevice) +WriteAction = ds1307_ns.class_('WriteAction', automation.Action) +ReadAction = ds1307_ns.class_('ReadAction', automation.Action) + + +CONFIG_SCHEMA = time.TIME_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(DS1307Component), +}).extend(i2c.i2c_device_schema(0x68)) + + +@automation.register_action('ds1307.write', WriteAction, cv.Schema({ + cv.GenerateID(): cv.use_id(DS1307Component), +})) +def ds1307_write_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_parented(var, config[CONF_ID]) + yield var + + +@automation.register_action('ds1307.read', ReadAction, automation.maybe_simple_id({ + cv.GenerateID(): cv.use_id(DS1307Component), +})) +def ds1307_read_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_parented(var, config[CONF_ID]) + yield var + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + + yield cg.register_component(var, config) + yield i2c.register_i2c_device(var, config) + yield time.register_time(var, config) diff --git a/tests/test1.yaml b/tests/test1.yaml index 0bccc3f57a..58fc7d4bb2 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1837,6 +1837,20 @@ time: then: - lambda: 'ESP_LOGD("main", "time");' - platform: gps + on_time: + seconds: 0 + minutes: /15 + then: + ds1307.write: + id: ds1307_time + - platform: ds1307 + id: ds1307_time + on_time: + seconds: 0 + then: + ds1307.read + + cover: - platform: template