Merge branch 'dev' into optolink

This commit is contained in:
j0ta29 2023-07-17 10:33:03 +02:00 committed by GitHub
commit 125a364fea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 304 additions and 79 deletions

View file

@ -24,6 +24,7 @@ ATTENUATION_MODES = {
}
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
adc2_channel_t = cg.global_ns.enum("adc2_channel_t")
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
# pin to adc1 channel mapping
@ -78,6 +79,49 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
},
}
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
# TODO: add other variants
VARIANT_ESP32: {
4: adc2_channel_t.ADC2_CHANNEL_0,
0: adc2_channel_t.ADC2_CHANNEL_1,
2: adc2_channel_t.ADC2_CHANNEL_2,
15: adc2_channel_t.ADC2_CHANNEL_3,
13: adc2_channel_t.ADC2_CHANNEL_4,
12: adc2_channel_t.ADC2_CHANNEL_5,
14: adc2_channel_t.ADC2_CHANNEL_6,
27: adc2_channel_t.ADC2_CHANNEL_7,
25: adc2_channel_t.ADC2_CHANNEL_8,
26: adc2_channel_t.ADC2_CHANNEL_9,
},
VARIANT_ESP32S2: {
11: adc2_channel_t.ADC2_CHANNEL_0,
12: adc2_channel_t.ADC2_CHANNEL_1,
13: adc2_channel_t.ADC2_CHANNEL_2,
14: adc2_channel_t.ADC2_CHANNEL_3,
15: adc2_channel_t.ADC2_CHANNEL_4,
16: adc2_channel_t.ADC2_CHANNEL_5,
17: adc2_channel_t.ADC2_CHANNEL_6,
18: adc2_channel_t.ADC2_CHANNEL_7,
19: adc2_channel_t.ADC2_CHANNEL_8,
20: adc2_channel_t.ADC2_CHANNEL_9,
},
VARIANT_ESP32S3: {
11: adc2_channel_t.ADC2_CHANNEL_0,
12: adc2_channel_t.ADC2_CHANNEL_1,
13: adc2_channel_t.ADC2_CHANNEL_2,
14: adc2_channel_t.ADC2_CHANNEL_3,
15: adc2_channel_t.ADC2_CHANNEL_4,
16: adc2_channel_t.ADC2_CHANNEL_5,
17: adc2_channel_t.ADC2_CHANNEL_6,
18: adc2_channel_t.ADC2_CHANNEL_7,
19: adc2_channel_t.ADC2_CHANNEL_8,
20: adc2_channel_t.ADC2_CHANNEL_9,
},
VARIANT_ESP32C3: {
5: adc2_channel_t.ADC2_CHANNEL_0,
},
}
def validate_adc_pin(value):
if str(value).upper() == "VCC":
@ -89,11 +133,18 @@ def validate_adc_pin(value):
if CORE.is_esp32:
value = pins.internal_gpio_input_pin_number(value)
variant = get_esp32_variant()
if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
if (
variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL
and variant not in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL
):
raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
if (
value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]
and value not in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
):
raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
return pins.internal_gpio_input_pin_schema(value)
if CORE.is_esp8266:
@ -104,7 +155,7 @@ def validate_adc_pin(value):
)
if value != 17: # A0
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC")
return pins.gpio_pin_schema(
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value)
@ -112,7 +163,7 @@ def validate_adc_pin(value):
if CORE.is_rp2040:
value = pins.internal_gpio_input_pin_number(value)
if value not in (26, 27, 28, 29):
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC.")
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC")
return pins.internal_gpio_input_pin_schema(value)
raise NotImplementedError

View file

@ -20,20 +20,20 @@ namespace adc {
static const char *const TAG = "adc";
// 13bit for S2, and 12bit for all other esp32 variants
// 13-bit for S2, 12-bit for all other ESP32 variants
#ifdef USE_ESP32
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
#if USE_ESP32_VARIANT_ESP32S2
static const int SOC_ADC_RTC_MAX_BITWIDTH = 13;
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
#else
static const int SOC_ADC_RTC_MAX_BITWIDTH = 12;
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
#endif
#endif
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
static const int32_t ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
static const int32_t ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
#endif
#ifdef USE_RP2040
@ -47,14 +47,21 @@ extern "C"
#endif
#ifdef USE_ESP32
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!autorange_) {
adc1_config_channel_atten(channel_, attenuation_);
if (channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!autorange_) {
adc1_config_channel_atten(channel1_, attenuation_);
}
} else if (channel2_ != ADC2_CHANNEL_MAX) {
if (!autorange_) {
adc2_config_channel_atten(channel2_, attenuation_);
}
}
// load characteristics for each attenuation
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
for (int32_t i = 0; i < (int32_t) ADC_ATTEN_MAX; i++) {
auto adc_unit = channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
1100, // default vref
&cal_characteristics_[i]);
switch (cal_value) {
@ -136,9 +143,9 @@ void ADCSensor::update() {
#ifdef USE_ESP8266
float ADCSensor::sample() {
#ifdef USE_ADC_SENSOR_VCC
int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
int32_t raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
int raw = analogRead(this->pin_->get_pin()); // NOLINT
int32_t raw = analogRead(this->pin_->get_pin()); // NOLINT
#endif
if (output_raw_) {
return raw;
@ -150,29 +157,53 @@ float ADCSensor::sample() {
#ifdef USE_ESP32
float ADCSensor::sample() {
if (!autorange_) {
int raw = adc1_get_raw(channel_);
int32_t raw = -1;
if (channel1_ != ADC1_CHANNEL_MAX) {
raw = adc1_get_raw(channel1_);
} else if (channel2_ != ADC2_CHANNEL_MAX) {
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
}
if (raw == -1) {
return NAN;
}
if (output_raw_) {
return raw;
}
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int32_t) attenuation_]);
return mv / 1000.0f;
}
int raw11, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel_);
if (raw11 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel_);
if (raw6 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel_);
if (raw2 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel_);
int32_t raw11 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
if (channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel1_);
if (raw11 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel1_);
if (raw6 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel1_);
if (raw2 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel1_);
}
}
}
} else if (channel2_ != ADC2_CHANNEL_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_11);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw11);
if (raw11 < ADC_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
if (raw6 < ADC_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_2_5);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
if (raw2 < ADC_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_0);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
}
}
}
}
@ -181,10 +212,10 @@ float ADCSensor::sample() {
return NAN;
}
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_11]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
uint32_t c11 = std::min(raw11, ADC_HALF);
@ -212,7 +243,7 @@ float ADCSensor::sample() {
adc_select_input(pin - 26);
}
int raw = adc_read();
int32_t raw = adc_read();
if (this->is_temperature_) {
adc_set_temp_sensor_enabled(false);
}

View file

@ -19,16 +19,23 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_ESP32
/// Set the attenuation for this pin. Only available on the ESP32.
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
void set_channel(adc1_channel_t channel) { channel_ = channel; }
void set_channel1(adc1_channel_t channel) {
channel1_ = channel;
channel2_ = ADC2_CHANNEL_MAX;
}
void set_channel2(adc2_channel_t channel) {
channel2_ = channel;
channel1_ = ADC1_CHANNEL_MAX;
}
void set_autorange(bool autorange) { autorange_ = autorange; }
#endif
/// Update adc values.
/// Update ADC values
void update() override;
/// Setup ADc
/// Setup ADC
void setup() override;
void dump_config() override;
/// `HARDWARE_LATE` setup priority.
/// `HARDWARE_LATE` setup priority
float get_setup_priority() const override;
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
@ -52,9 +59,10 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_ESP32
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
adc1_channel_t channel_{};
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
bool autorange_{false};
esp_adc_cal_characteristics_t cal_characteristics_[(int) ADC_ATTEN_MAX] = {};
esp_adc_cal_characteristics_t cal_characteristics_[(int32_t) ADC_ATTEN_MAX] = {};
#endif
};

View file

@ -1,5 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
import esphome.final_validate as fv
from esphome.core import CORE
from esphome.components import sensor, voltage_sampler
from esphome.components.esp32 import get_esp32_variant
from esphome.const import (
@ -8,15 +10,15 @@ from esphome.const import (
CONF_NUMBER,
CONF_PIN,
CONF_RAW,
CONF_WIFI,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
)
from esphome.core import CORE
from . import (
ATTENUATION_MODES,
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
validate_adc_pin,
)
@ -25,7 +27,23 @@ AUTO_LOAD = ["voltage_sampler"]
def validate_config(config):
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set.")
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set")
return config
def final_validate_config(config):
if CORE.is_esp32:
variant = get_esp32_variant()
if (
CONF_WIFI in fv.full_config.get()
and config[CONF_PIN][CONF_NUMBER]
in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
):
raise cv.Invalid(
f"{variant} doesn't support ADC on this pin when Wi-Fi is configured"
)
return config
@ -55,6 +73,8 @@ CONFIG_SCHEMA = cv.All(
validate_config,
)
FINAL_VALIDATE_SCHEMA = final_validate_config
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
@ -81,5 +101,15 @@ async def to_code(config):
if CORE.is_esp32:
variant = get_esp32_variant()
pin_num = config[CONF_PIN][CONF_NUMBER]
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel(chan))
if (
variant in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL
and pin_num in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]
):
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel1(chan))
elif (
variant in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL
and pin_num in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
):
chan = ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel2(chan))

View file

@ -76,7 +76,7 @@ void AirthingsWaveBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
}
}
bool AirthingsWaveBase::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
bool AirthingsWaveBase::is_valid_voc_value_(uint16_t voc) { return voc <= 16383; }
void AirthingsWaveBase::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {

View file

@ -51,9 +51,9 @@ void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
this->response_received_();
}
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return radon <= 16383; }
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return co2 <= 16383; }
void AirthingsWavePlus::dump_config() {
// these really don't belong here, but there doesn't seem to be a

View file

@ -269,10 +269,7 @@ void Display::do_update_() {
} else if (this->writer_.has_value()) {
(*this->writer_)(*this);
}
// remove all not ended clipping regions
while (is_clipping()) {
end_clipping();
}
this->clear_clipping_();
}
void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
@ -322,13 +319,51 @@ void Display::shrink_clipping(Rect add_rect) {
this->clipping_rectangle_.back().shrink(add_rect);
}
}
Rect Display::get_clipping() {
Rect Display::get_clipping() const {
if (this->clipping_rectangle_.empty()) {
return Rect();
} else {
return this->clipping_rectangle_.back();
}
}
void Display::clear_clipping_() { this->clipping_rectangle_.clear(); }
bool Display::clip(int x, int y) {
if (x < 0 || x >= this->get_width() || y < 0 || y >= this->get_height())
return false;
if (!this->get_clipping().inside(x, y))
return false;
return true;
}
bool Display::clamp_x_(int x, int w, int &min_x, int &max_x) {
min_x = std::max(x, 0);
max_x = std::min(x + w, this->get_width());
if (!this->clipping_rectangle_.empty()) {
const auto &rect = this->clipping_rectangle_.back();
if (!rect.is_set())
return false;
min_x = std::max(min_x, (int) rect.x);
max_x = std::min(max_x, (int) rect.x2());
}
return min_x < max_x;
}
bool Display::clamp_y_(int y, int h, int &min_y, int &max_y) {
min_y = std::max(y, 0);
max_y = std::min(y + h, this->get_height());
if (!this->clipping_rectangle_.empty()) {
const auto &rect = this->clipping_rectangle_.back();
if (!rect.is_set())
return false;
min_y = std::max(min_y, (int) rect.y);
max_y = std::min(max_y, (int) rect.y2());
}
return min_y < max_y;
}
DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {}
void DisplayPage::show() { this->parent_->show_page(this); }

View file

@ -472,14 +472,21 @@ class Display {
*
* return rect for active clipping region
*/
Rect get_clipping();
Rect get_clipping() const;
bool is_clipping() const { return !this->clipping_rectangle_.empty(); }
/** Check if pixel is within region of display.
*/
bool clip(int x, int y);
protected:
bool clamp_x_(int x, int w, int &min_x, int &max_x);
bool clamp_y_(int y, int h, int &min_y, int &max_y);
void vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, va_list arg);
void do_update_();
void clear_clipping_();
DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
optional<display_writer_t> writer_{};

View file

@ -60,11 +60,11 @@ void Rect::shrink(Rect rect) {
}
}
bool Rect::equal(Rect rect) {
bool Rect::equal(Rect rect) const {
return (rect.x == this->x) && (rect.w == this->w) && (rect.y == this->y) && (rect.h == this->h);
}
bool Rect::inside(int16_t test_x, int16_t test_y, bool absolute) { // NOLINT
bool Rect::inside(int16_t test_x, int16_t test_y, bool absolute) const { // NOLINT
if (!this->is_set()) {
return true;
}
@ -75,7 +75,7 @@ bool Rect::inside(int16_t test_x, int16_t test_y, bool absolute) { // NOLINT
}
}
bool Rect::inside(Rect rect, bool absolute) {
bool Rect::inside(Rect rect, bool absolute) const {
if (!this->is_set() || !rect.is_set()) {
return true;
}

View file

@ -16,19 +16,19 @@ class Rect {
Rect() : x(VALUE_NO_SET), y(VALUE_NO_SET), w(VALUE_NO_SET), h(VALUE_NO_SET) {} // NOLINT
inline Rect(int16_t x, int16_t y, int16_t w, int16_t h) ALWAYS_INLINE : x(x), y(y), w(w), h(h) {}
inline int16_t x2() { return this->x + this->w; }; ///< X coordinate of corner
inline int16_t y2() { return this->y + this->h; }; ///< Y coordinate of corner
inline int16_t x2() const { return this->x + this->w; }; ///< X coordinate of corner
inline int16_t y2() const { return this->y + this->h; }; ///< Y coordinate of corner
inline bool is_set() ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); }
inline bool is_set() const ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); }
void expand(int16_t horizontal, int16_t vertical);
void extend(Rect rect);
void shrink(Rect rect);
bool inside(Rect rect, bool absolute = true);
bool inside(int16_t test_x, int16_t test_y, bool absolute = true);
bool equal(Rect rect);
bool inside(Rect rect, bool absolute = true) const;
bool inside(int16_t test_x, int16_t test_y, bool absolute = true) const;
bool equal(Rect rect) const;
void info(const std::string &prefix = "rect info:");
};

View file

@ -87,7 +87,7 @@ async def to_code(config):
cg.add_build_flag("-DDSMR_WATER_MBUS_ID=" + str(config[CONF_WATER_MBUS_ID]))
# DSMR Parser
cg.add_library("glmnet/Dsmr", "0.7")
cg.add_library("glmnet/Dsmr", "0.8")
# Crypto
cg.add_library("rweather/Crypto", "0.4.0")

View file

@ -243,6 +243,30 @@ CONFIG_SCHEMA = cv.Schema(
device_class=DEVICE_CLASS_WATER,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(
"active_energy_import_current_average_demand"
): sensor.sensor_schema(
unit_of_measurement=UNIT_KILOWATT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(
"active_energy_import_maximum_demand_running_month"
): sensor.sensor_schema(
unit_of_measurement=UNIT_KILOWATT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(
"active_energy_import_maximum_demand_last_13_months"
): sensor.sensor_schema(
unit_of_measurement=UNIT_KILOWATT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
}
).extend(cv.COMPONENT_SCHEMA)

View file

@ -217,6 +217,7 @@ OffsetFilter = sensor_ns.class_("OffsetFilter", Filter)
MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter)
FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", Filter)
ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter)
TimeoutFilter = sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
DebounceFilter = sensor_ns.class_("DebounceFilter", Filter, cg.Component)
HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component)
DeltaFilter = sensor_ns.class_("DeltaFilter", Filter)
@ -536,6 +537,15 @@ async def heartbeat_filter_to_code(config, filter_id):
return var
@FILTER_REGISTRY.register(
"timeout", TimeoutFilter, cv.positive_time_period_milliseconds
)
async def timeout_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config)
await cg.register_component(var, {})
return var
@FILTER_REGISTRY.register(
"debounce", DebounceFilter, cv.positive_time_period_milliseconds
)

View file

@ -373,6 +373,17 @@ void OrFilter::initialize(Sensor *parent, Filter *next) {
this->phi_.initialize(parent, nullptr);
}
// TimeoutFilter
optional<float> TimeoutFilter::new_value(float value) {
this->set_timeout("timeout", this->time_period_, [this]() { this->output(NAN); });
this->output(value);
return {};
}
TimeoutFilter::TimeoutFilter(uint32_t time_period) : time_period_(time_period) {}
float TimeoutFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
// DebounceFilter
optional<float> DebounceFilter::new_value(float value) {
this->set_timeout("debounce", this->time_period_, [this, value]() { this->output(value); });

View file

@ -313,6 +313,18 @@ class ThrottleFilter : public Filter {
uint32_t min_time_between_inputs_;
};
class TimeoutFilter : public Filter, public Component {
public:
explicit TimeoutFilter(uint32_t time_period);
optional<float> new_value(float value) override;
float get_setup_priority() const override;
protected:
uint32_t time_period_;
};
class DebounceFilter : public Filter, public Component {
public:
explicit DebounceFilter(uint32_t time_period);

View file

@ -954,10 +954,18 @@ void Sprinkler::pause() {
}
void Sprinkler::resume() {
if (this->standby()) {
ESP_LOGD(TAG, "resume called but standby is enabled; no action taken");
return;
}
if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
ESP_LOGD(TAG, "Resuming valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
this->resume_duration_.value_or(0));
this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
// Resume only if valve has not been completed yet
if (!this->valve_cycle_complete_(this->paused_valve_.value())) {
ESP_LOGD(TAG, "Resuming valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
this->resume_duration_.value_or(0));
this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
}
this->reset_resume();
} else {
ESP_LOGD(TAG, "No valve to resume!");

View file

@ -43,7 +43,9 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(
single=True
),
cv.Optional(CONF_RESTORE_STATE, default=False): cv.boolean,
cv.Optional(CONF_RESTORE_STATE): cv.invalid(
"The restore_state option has been removed in 2023.7.0. Use the restore_mode option instead"
),
}
)
.extend(cv.COMPONENT_SCHEMA),
@ -70,7 +72,6 @@ async def to_code(config):
)
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE]))
cg.add(var.set_restore_state(config[CONF_RESTORE_STATE]))
@automation.register_action(

View file

@ -40,9 +40,6 @@ float TemplateSwitch::get_setup_priority() const { return setup_priority::HARDWA
Trigger<> *TemplateSwitch::get_turn_on_trigger() const { return this->turn_on_trigger_; }
Trigger<> *TemplateSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; }
void TemplateSwitch::setup() {
if (!this->restore_state_)
return;
optional<bool> initial_state = this->get_initial_state_with_restore_mode();
if (initial_state.has_value()) {
@ -57,10 +54,8 @@ void TemplateSwitch::setup() {
}
void TemplateSwitch::dump_config() {
LOG_SWITCH("", "Template Switch", this);
ESP_LOGCONFIG(TAG, " Restore State: %s", YESNO(this->restore_state_));
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
}
void TemplateSwitch::set_restore_state(bool restore_state) { this->restore_state_ = restore_state; }
void TemplateSwitch::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
} // namespace template_

View file

@ -15,7 +15,6 @@ class TemplateSwitch : public switch_::Switch, public Component {
void dump_config() override;
void set_state_lambda(std::function<optional<bool>()> &&f);
void set_restore_state(bool restore_state);
Trigger<> *get_turn_on_trigger() const;
Trigger<> *get_turn_off_trigger() const;
void set_optimistic(bool optimistic);
@ -35,7 +34,6 @@ class TemplateSwitch : public switch_::Switch, public Component {
Trigger<> *turn_on_trigger_;
Trigger<> *turn_off_trigger_;
Trigger<> *prev_trigger_{nullptr};
bool restore_state_{false};
};
} // namespace template_

View file

@ -10,6 +10,11 @@ if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ] && [ ! "$ESPHOME_NO_VENV"
source venv/bin/activate
fi
# Avoid unsafe git error when running inside devcontainer
if [ -n "$DEVCONTAINER" ];then
git config --global --add safe.directory "$PWD"
fi
pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
pip3 install setuptools wheel
pip3 install --no-use-pep517 -e .

View file

@ -2493,7 +2493,6 @@ switch:
level: !lambda "return 0.5;"
turn_off_action:
- switch.turn_on: living_room_lights_off
restore_state: false
on_turn_on:
- switch.template.publish:
id: livingroom_lights
@ -2529,7 +2528,6 @@ switch:
}
optimistic: true
assumed_state: false
restore_state: true
on_turn_off:
- switch.template.publish:
id: my_switch

View file

@ -86,6 +86,7 @@ sensor:
- delta: 100
- throttle: 100ms
- debounce: 500s
- timeout: 10min
- calibrate_linear:
- 0 -> 0
- 100 -> 100