Merge pull request #1459 from esphome/bump-1.16.0b2

1.16.0b2
This commit is contained in:
Jesse Hills 2021-01-11 21:08:18 +13:00 committed by GitHub
commit 8c849b9002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 312 additions and 21 deletions

View File

@ -29,6 +29,7 @@ esphome/components/ct_clamp/* @jesserockz
esphome/components/debug/* @OttoWinter esphome/components/debug/* @OttoWinter
esphome/components/dfplayer/* @glmnet esphome/components/dfplayer/* @glmnet
esphome/components/dht/* @OttoWinter esphome/components/dht/* @OttoWinter
esphome/components/ds1307/* @badbadc0ffee
esphome/components/exposure_notifications/* @OttoWinter esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb esphome/components/ezo/* @ssieb
esphome/components/fastled_base/* @OttoWinter esphome/components/fastled_base/* @OttoWinter

View File

@ -62,8 +62,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
error = true; error = true;
break; break;
} }
uint32_t val = (uint32_t(buffer[i]) << 0) | (uint32_t(buffer[i + 1]) << 8) | (uint32_t(buffer[i + 2]) << 16) | uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
(uint32_t(buffer[i + 3]) << 24);
if (!this->decode_32bit(field_id, Proto32Bit(val))) { if (!this->decode_32bit(field_id, Proto32Bit(val))) {
ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val); ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
} }

View File

View File

@ -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

View File

@ -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<typename... Ts> class WriteAction : public Action<Ts...>, public Parented<DS1307Component> {
public:
void play(Ts... x) override { this->parent_->write(); }
};
template<typename... Ts> class ReadAction : public Action<Ts...>, public Parented<DS1307Component> {
public:
void play(Ts... x) override { this->parent_->read(); }
};
} // namespace ds1307
} // namespace esphome

View File

@ -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)

View File

@ -41,7 +41,7 @@ void MAX31855Sensor::read_data_() {
this->read_array(data, 4); this->read_array(data, 4);
this->disable(); this->disable();
const uint32_t mem = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3] << 0; const uint32_t mem = encode_uint32(data[0], data[1], data[2], data[3]);
// Verify we got data // Verify we got data
if (mem != 0xFFFFFFFF) { if (mem != 0xFFFFFFFF) {

View File

@ -46,8 +46,7 @@ void rdm6300::RDM6300Component::loop() {
} else { } else {
// Valid data // Valid data
this->status_clear_warning(); this->status_clear_warning();
const uint32_t result = (uint32_t(this->buffer_[1]) << 24) | (uint32_t(this->buffer_[2]) << 16) | const uint32_t result = encode_uint32(this->buffer_[1], this->buffer_[2], this->buffer_[3], this->buffer_[4]);
(uint32_t(this->buffer_[3]) << 8) | this->buffer_[4];
bool report = result != last_id_; bool report = result != last_id_;
for (auto *card : this->cards_) { for (auto *card : this->cards_) {
if (card->process(result)) { if (card->process(result)) {

View File

@ -90,16 +90,34 @@ void ICACHE_RAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensor
if (arg->pin_b->digital_read()) if (arg->pin_b->digital_read())
input_state |= STATE_PIN_B_HIGH; input_state |= STATE_PIN_B_HIGH;
int8_t rotation_dir = 0;
uint16_t new_state = STATE_LOOKUP_TABLE[input_state]; uint16_t new_state = STATE_LOOKUP_TABLE[input_state];
if ((new_state & arg->resolution & STATE_HAS_INCREMENTED) != 0) { if ((new_state & arg->resolution & STATE_HAS_INCREMENTED) != 0) {
if (arg->counter < arg->max_value) if (arg->counter < arg->max_value)
arg->counter++; arg->counter++;
arg->on_clockwise_callback_.call(); rotation_dir = 1;
} }
if ((new_state & arg->resolution & STATE_HAS_DECREMENTED) != 0) { if ((new_state & arg->resolution & STATE_HAS_DECREMENTED) != 0) {
if (arg->counter > arg->min_value) if (arg->counter > arg->min_value)
arg->counter--; arg->counter--;
arg->on_anticlockwise_callback_.call(); rotation_dir = -1;
}
if (rotation_dir != 0) {
auto first_zero = std::find(arg->rotation_events.begin(), arg->rotation_events.end(), 0); // find first zero
if (first_zero == arg->rotation_events.begin() // are we at the start (first event this loop iteration)
|| std::signbit(*std::prev(first_zero)) !=
std::signbit(rotation_dir) // or is the last stored event the wrong direction
|| *std::prev(first_zero) == std::numeric_limits<int8_t>::lowest() // or the last event slot is full (negative)
|| *std::prev(first_zero) == std::numeric_limits<int8_t>::max()) { // or the last event slot is full (positive)
if (first_zero != arg->rotation_events.end()) { // we have a free rotation slot
*first_zero += rotation_dir; // store the rotation into a new slot
} else {
arg->rotation_events_overflow = true;
}
} else {
*std::prev(first_zero) += rotation_dir; // store the rotation into the previous slot
}
} }
arg->state = new_state; arg->state = new_state;
@ -137,6 +155,35 @@ void RotaryEncoderSensor::dump_config() {
} }
} }
void RotaryEncoderSensor::loop() { void RotaryEncoderSensor::loop() {
std::array<int8_t, 8> rotation_events;
bool rotation_events_overflow;
ets_intr_lock();
rotation_events = this->store_.rotation_events;
rotation_events_overflow = this->store_.rotation_events_overflow;
this->store_.rotation_events.fill(0);
this->store_.rotation_events_overflow = false;
ets_intr_unlock();
if (rotation_events_overflow) {
ESP_LOGW(TAG, "Captured more rotation events than expected");
}
for (auto events : rotation_events) {
if (events == 0) // we are at the end of the recorded events
break;
if (events > 0) {
while (events--) {
this->on_clockwise_callback_.call();
}
} else {
while (events++) {
this->on_anticlockwise_callback_.call();
}
}
}
if (this->pin_i_ != nullptr && this->pin_i_->digital_read()) { if (this->pin_i_ != nullptr && this->pin_i_->digital_read()) {
this->store_.counter = 0; this->store_.counter = 0;
} }

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <array>
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/esphal.h" #include "esphome/core/esphal.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
@ -27,8 +29,8 @@ struct RotaryEncoderSensorStore {
int32_t last_read{0}; int32_t last_read{0};
uint8_t state{0}; uint8_t state{0};
CallbackManager<void()> on_clockwise_callback_; std::array<int8_t, 8> rotation_events{};
CallbackManager<void()> on_anticlockwise_callback_; bool rotation_events_overflow{false};
static void gpio_intr(RotaryEncoderSensorStore *arg); static void gpio_intr(RotaryEncoderSensorStore *arg);
}; };
@ -66,11 +68,11 @@ class RotaryEncoderSensor : public sensor::Sensor, public Component {
float get_setup_priority() const override; float get_setup_priority() const override;
void add_on_clockwise_callback(std::function<void()> callback) { void add_on_clockwise_callback(std::function<void()> callback) {
this->store_.on_clockwise_callback_.add(std::move(callback)); this->on_clockwise_callback_.add(std::move(callback));
} }
void add_on_anticlockwise_callback(std::function<void()> callback) { void add_on_anticlockwise_callback(std::function<void()> callback) {
this->store_.on_anticlockwise_callback_.add(std::move(callback)); this->on_anticlockwise_callback_.add(std::move(callback));
} }
protected: protected:
@ -79,6 +81,9 @@ class RotaryEncoderSensor : public sensor::Sensor, public Component {
GPIOPin *pin_i_{nullptr}; /// Index pin, if this is not nullptr, the counter will reset to 0 once this pin is HIGH. GPIOPin *pin_i_{nullptr}; /// Index pin, if this is not nullptr, the counter will reset to 0 once this pin is HIGH.
RotaryEncoderSensorStore store_{}; RotaryEncoderSensorStore store_{};
CallbackManager<void()> on_clockwise_callback_;
CallbackManager<void()> on_anticlockwise_callback_;
}; };
template<typename... Ts> class RotaryEncoderSetValueAction : public Action<Ts...> { template<typename... Ts> class RotaryEncoderSetValueAction : public Action<Ts...> {

View File

@ -281,8 +281,7 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) {
case TuyaDatapointType::INTEGER: case TuyaDatapointType::INTEGER:
if (data_len != 4) if (data_len != 4)
return; return;
datapoint.value_uint = datapoint.value_uint = encode_uint32(data[0], data[1], data[2], data[3]);
(uint32_t(data[0]) << 24) | (uint32_t(data[1]) << 16) | (uint32_t(data[2]) << 8) | (uint32_t(data[3]) << 0);
break; break;
case TuyaDatapointType::ENUM: case TuyaDatapointType::ENUM:
if (data_len != 1) if (data_len != 1)

View File

@ -70,8 +70,7 @@ bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_l
} }
// idle time since last motion, 4 byte, 32-bit unsigned integer, 1 min // idle time since last motion, 4 byte, 32-bit unsigned integer, 1 min
else if ((value_type == 0x17) && (value_length == 4)) { else if ((value_type == 0x17) && (value_length == 4)) {
const uint32_t idle_time = const uint32_t idle_time = encode_uint32(data[3], data[2], data[1], data[0]);
uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16) | (uint32_t(data[2]) << 24);
result.idle_time = idle_time / 60.0f; result.idle_time = idle_time / 60.0f;
result.has_motion = (idle_time) ? false : true; result.has_motion = (idle_time) ? false : true;
} else { } else {

View File

@ -2,7 +2,7 @@
MAJOR_VERSION = 1 MAJOR_VERSION = 1
MINOR_VERSION = 16 MINOR_VERSION = 16
PATCH_VERSION = '0b1' PATCH_VERSION = '0b2'
__short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}' __short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}'
__version__ = f'{__short_version__}.{PATCH_VERSION}' __version__ = f'{__short_version__}.{PATCH_VERSION}'

View File

@ -299,6 +299,10 @@ std::array<uint8_t, 2> decode_uint16(uint16_t value) {
return {msb, lsb}; return {msb, lsb};
} }
uint32_t encode_uint32(uint8_t msb, uint8_t byte2, uint8_t byte3, uint8_t lsb) {
return (uint32_t(msb) << 24) | (uint32_t(byte2) << 16) | (uint32_t(byte3) << 8) | uint32_t(lsb);
}
std::string hexencode(const uint8_t *data, uint32_t len) { std::string hexencode(const uint8_t *data, uint32_t len) {
char buf[20]; char buf[20];
std::string res; std::string res;

View File

@ -132,6 +132,8 @@ uint32_t reverse_bits_32(uint32_t x);
uint16_t encode_uint16(uint8_t msb, uint8_t lsb); uint16_t encode_uint16(uint8_t msb, uint8_t lsb);
/// Decode a 16-bit unsigned integer into an array of two values: most significant byte, least significant byte. /// Decode a 16-bit unsigned integer into an array of two values: most significant byte, least significant byte.
std::array<uint8_t, 2> decode_uint16(uint16_t value); std::array<uint8_t, 2> decode_uint16(uint16_t value);
/// Encode a 32-bit unsigned integer given four bytes in MSB -> LSB order
uint32_t encode_uint32(uint8_t msb, uint8_t byte2, uint8_t byte3, uint8_t lsb);
/*** /***
* An interrupt helper class. * An interrupt helper class.

View File

@ -209,11 +209,8 @@ def _esp8266_add_lwip_type():
cg.add_build_flag('-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH') cg.add_build_flag('-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH')
@coroutine_with_priority(100.0) @coroutine_with_priority(30.0)
def to_code(config): def _add_automations(config):
cg.add_global(cg.global_ns.namespace('esphome').using)
cg.add(cg.App.pre_setup(config[CONF_NAME], cg.RawExpression('__DATE__ ", " __TIME__')))
for conf in config.get(CONF_ON_BOOT, []): for conf in config.get(CONF_ON_BOOT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], conf.get(CONF_PRIORITY)) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], conf.get(CONF_PRIORITY))
yield cg.register_component(trigger, conf) yield cg.register_component(trigger, conf)
@ -229,6 +226,14 @@ def to_code(config):
yield cg.register_component(trigger, conf) yield cg.register_component(trigger, conf)
yield automation.build_automation(trigger, [], conf) yield automation.build_automation(trigger, [], conf)
@coroutine_with_priority(100.0)
def to_code(config):
cg.add_global(cg.global_ns.namespace('esphome').using)
cg.add(cg.App.pre_setup(config[CONF_NAME], cg.RawExpression('__DATE__ ", " __TIME__')))
CORE.add_job(_add_automations, config)
# Set LWIP build constants for ESP8266 # Set LWIP build constants for ESP8266
if CORE.is_esp8266: if CORE.is_esp8266:
CORE.add_job(_esp8266_add_lwip_type) CORE.add_job(_esp8266_add_lwip_type)

View File

@ -1837,6 +1837,20 @@ time:
then: then:
- lambda: 'ESP_LOGD("main", "time");' - lambda: 'ESP_LOGD("main", "time");'
- platform: gps - 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: cover:
- platform: template - platform: template