mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-29 22:24:26 +00:00 
			
		
		
		
	| @@ -32,7 +32,7 @@ from esphome.const import ( | ||||
|     SECRETS_FILES, | ||||
| ) | ||||
| from esphome.core import CORE, EsphomeError, coroutine | ||||
| from esphome.helpers import indent | ||||
| from esphome.helpers import indent, is_ip_address | ||||
| from esphome.util import ( | ||||
|     run_external_command, | ||||
|     run_external_process, | ||||
| @@ -308,8 +308,10 @@ def upload_program(config, args, host): | ||||
|     password = ota_conf.get(CONF_PASSWORD, "") | ||||
|  | ||||
|     if ( | ||||
|         get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED] | ||||
|     ) and CONF_MQTT in config: | ||||
|         not is_ip_address(CORE.address) | ||||
|         and (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED]) | ||||
|         and CONF_MQTT in config | ||||
|     ): | ||||
|         from esphome import mqtt | ||||
|  | ||||
|         host = mqtt.get_esphome_device_ip( | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
|   | ||||
| @@ -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 | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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)) | ||||
|   | ||||
| @@ -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 | ||||
| ) | ||||
|   | ||||
| @@ -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); }); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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!"); | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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_ | ||||
|   | ||||
| @@ -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_ | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| """Constants used by esphome.""" | ||||
|  | ||||
| __version__ = "2023.7.0b1" | ||||
| __version__ = "2023.7.0b2" | ||||
|  | ||||
| ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" | ||||
| VALID_SUBSTITUTIONS_CHARACTERS = ( | ||||
|   | ||||
| @@ -2475,7 +2475,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 | ||||
| @@ -2511,7 +2510,6 @@ switch: | ||||
|       } | ||||
|     optimistic: true | ||||
|     assumed_state: false | ||||
|     restore_state: true | ||||
|     on_turn_off: | ||||
|       - switch.template.publish: | ||||
|           id: my_switch | ||||
|   | ||||
| @@ -86,6 +86,7 @@ sensor: | ||||
|       - delta: 100 | ||||
|       - throttle: 100ms | ||||
|       - debounce: 500s | ||||
|       - timeout: 10min | ||||
|       - calibrate_linear: | ||||
|           - 0 -> 0 | ||||
|           - 100 -> 100 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user