import logging import esphome.codegen as cg from esphome.components import sensor, voltage_sampler from esphome.components.esp32 import get_esp32_variant import esphome.config_validation as cv from esphome.const import ( CONF_ATTENUATION, CONF_ID, CONF_NUMBER, CONF_PIN, CONF_RAW, CONF_WIFI, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT, UNIT_VOLT, ) from esphome.core import CORE import esphome.final_validate as fv from . import ( ATTENUATION_MODES, ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, ESP32_VARIANT_ADC2_PIN_TO_CHANNEL, SAMPLING_MODES, adc_ns, validate_adc_pin, ) _LOGGER = logging.getLogger(__name__) AUTO_LOAD = ["voltage_sampler"] CONF_SAMPLES = "samples" CONF_SAMPLING_MODE = "sampling_mode" _attenuation = cv.enum(ATTENUATION_MODES, lower=True) _sampling_mode = cv.enum(SAMPLING_MODES, lower=True) 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") if config.get(CONF_ATTENUATION, None) == "auto" and config.get(CONF_SAMPLES, 1) > 1: raise cv.Invalid( "Automatic attenuation cannot be used when multisampling is set" ) if config.get(CONF_ATTENUATION) == "11db": _LOGGER.warning( "`attenuation: 11db` is deprecated, use `attenuation: 12db` instead" ) # Alter value here so `config` command prints the recommended change config[CONF_ATTENUATION] = _attenuation("12db") 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 ADCSensor = adc_ns.class_( "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler ) CONFIG_SCHEMA = cv.All( sensor.sensor_schema( ADCSensor, unit_of_measurement=UNIT_VOLT, accuracy_decimals=2, device_class=DEVICE_CLASS_VOLTAGE, state_class=STATE_CLASS_MEASUREMENT, ) .extend( { cv.Required(CONF_PIN): validate_adc_pin, cv.Optional(CONF_RAW, default=False): cv.boolean, cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All( cv.only_on_esp32, _attenuation ), cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255), cv.Optional(CONF_SAMPLING_MODE, default="avg"): _sampling_mode, } ) .extend(cv.polling_component_schema("60s")), validate_config, ) FINAL_VALIDATE_SCHEMA = final_validate_config async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await sensor.register_sensor(var, config) if config[CONF_PIN] == "VCC": cg.add_define("USE_ADC_SENSOR_VCC") elif config[CONF_PIN] == "TEMPERATURE": cg.add(var.set_is_temperature()) else: pin = await cg.gpio_pin_expression(config[CONF_PIN]) cg.add(var.set_pin(pin)) cg.add(var.set_output_raw(config[CONF_RAW])) cg.add(var.set_sample_count(config[CONF_SAMPLES])) cg.add(var.set_sampling_mode(config[CONF_SAMPLING_MODE])) if attenuation := config.get(CONF_ATTENUATION): if attenuation == "auto": cg.add(var.set_autorange(cg.global_ns.true)) else: cg.add(var.set_attenuation(attenuation)) if CORE.is_esp32: variant = get_esp32_variant() pin_num = config[CONF_PIN][CONF_NUMBER] 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))