diff --git a/CODEOWNERS b/CODEOWNERS
index f0075549fd..56e20133ef 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -49,6 +49,7 @@ esphome/components/atc_mithermometer/* @ahpohl
 esphome/components/atm90e26/* @danieltwagner
 esphome/components/atm90e32/* @circuitsetup @descipher
 esphome/components/audio/* @kahrendt
+esphome/components/audio_adc/* @kbx81
 esphome/components/audio_dac/* @kbx81
 esphome/components/axs15231/* @clydebarrow
 esphome/components/b_parasite/* @rbaron
diff --git a/esphome/components/audio_adc/__init__.py b/esphome/components/audio_adc/__init__.py
new file mode 100644
index 0000000000..dd3c958821
--- /dev/null
+++ b/esphome/components/audio_adc/__init__.py
@@ -0,0 +1,41 @@
+from esphome import automation
+import esphome.codegen as cg
+import esphome.config_validation as cv
+from esphome.const import CONF_ID, CONF_MIC_GAIN
+from esphome.core import coroutine_with_priority
+
+CODEOWNERS = ["@kbx81"]
+IS_PLATFORM_COMPONENT = True
+
+audio_adc_ns = cg.esphome_ns.namespace("audio_adc")
+AudioAdc = audio_adc_ns.class_("AudioAdc")
+
+SetMicGainAction = audio_adc_ns.class_("SetMicGainAction", automation.Action)
+
+
+SET_MIC_GAIN_ACTION_SCHEMA = cv.maybe_simple_value(
+    {
+        cv.GenerateID(): cv.use_id(AudioAdc),
+        cv.Required(CONF_MIC_GAIN): cv.templatable(cv.decibel),
+    },
+    key=CONF_MIC_GAIN,
+)
+
+
+@automation.register_action(
+    "audio_adc.set_mic_gain", SetMicGainAction, SET_MIC_GAIN_ACTION_SCHEMA
+)
+async def audio_adc_set_mic_gain_to_code(config, action_id, template_arg, args):
+    paren = await cg.get_variable(config[CONF_ID])
+    var = cg.new_Pvariable(action_id, template_arg, paren)
+
+    template_ = await cg.templatable(config.get(CONF_MIC_GAIN), args, float)
+    cg.add(var.set_mic_gain(template_))
+
+    return var
+
+
+@coroutine_with_priority(100.0)
+async def to_code(config):
+    cg.add_define("USE_AUDIO_ADC")
+    cg.add_global(audio_adc_ns.using)
diff --git a/esphome/components/audio_adc/audio_adc.h b/esphome/components/audio_adc/audio_adc.h
new file mode 100644
index 0000000000..94bfb57db5
--- /dev/null
+++ b/esphome/components/audio_adc/audio_adc.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "esphome/core/defines.h"
+#include "esphome/core/hal.h"
+
+namespace esphome {
+namespace audio_adc {
+
+class AudioAdc {
+ public:
+  virtual bool set_mic_gain(float mic_gain) = 0;
+
+  virtual float mic_gain() = 0;
+};
+
+}  // namespace audio_adc
+}  // namespace esphome
diff --git a/esphome/components/audio_adc/automation.h b/esphome/components/audio_adc/automation.h
new file mode 100644
index 0000000000..1b0bc2a6ad
--- /dev/null
+++ b/esphome/components/audio_adc/automation.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "esphome/core/automation.h"
+#include "esphome/core/component.h"
+#include "audio_adc.h"
+
+namespace esphome {
+namespace audio_adc {
+
+template<typename... Ts> class SetMicGainAction : public Action<Ts...> {
+ public:
+  explicit SetMicGainAction(AudioAdc *audio_adc) : audio_adc_(audio_adc) {}
+
+  TEMPLATABLE_VALUE(float, mic_gain)
+
+  void play(Ts... x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); }
+
+ protected:
+  AudioAdc *audio_adc_;
+};
+
+}  // namespace audio_adc
+}  // namespace esphome
diff --git a/esphome/components/es7210/__init__.py b/esphome/components/es7210/__init__.py
index 8e63d7f04f..e69de29bb2 100644
--- a/esphome/components/es7210/__init__.py
+++ b/esphome/components/es7210/__init__.py
@@ -1,67 +0,0 @@
-import esphome.codegen as cg
-from esphome.components import i2c
-import esphome.config_validation as cv
-from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE
-
-CODEOWNERS = ["@kahrendt"]
-DEPENDENCIES = ["i2c"]
-
-es7210_ns = cg.esphome_ns.namespace("es7210")
-ES7210 = es7210_ns.class_("ES7210", cg.Component, i2c.I2CDevice)
-
-
-es7210_bits_per_sample = es7210_ns.enum("ES7210BitsPerSample")
-ES7210_BITS_PER_SAMPLE_ENUM = {
-    16: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_16,
-    24: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_24,
-    32: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_32,
-}
-
-
-es7210_mic_gain = es7210_ns.enum("ES7210MicGain")
-ES7210_MIC_GAIN_ENUM = {
-    "0DB": es7210_mic_gain.ES7210_MIC_GAIN_0DB,
-    "3DB": es7210_mic_gain.ES7210_MIC_GAIN_3DB,
-    "6DB": es7210_mic_gain.ES7210_MIC_GAIN_6DB,
-    "9DB": es7210_mic_gain.ES7210_MIC_GAIN_9DB,
-    "12DB": es7210_mic_gain.ES7210_MIC_GAIN_12DB,
-    "15DB": es7210_mic_gain.ES7210_MIC_GAIN_15DB,
-    "18DB": es7210_mic_gain.ES7210_MIC_GAIN_18DB,
-    "21DB": es7210_mic_gain.ES7210_MIC_GAIN_21DB,
-    "24DB": es7210_mic_gain.ES7210_MIC_GAIN_24DB,
-    "27DB": es7210_mic_gain.ES7210_MIC_GAIN_27DB,
-    "30DB": es7210_mic_gain.ES7210_MIC_GAIN_30DB,
-    "33DB": es7210_mic_gain.ES7210_MIC_GAIN_33DB,
-    "34.5DB": es7210_mic_gain.ES7210_MIC_GAIN_34_5DB,
-    "36DB": es7210_mic_gain.ES7210_MIC_GAIN_36DB,
-    "37.5DB": es7210_mic_gain.ES7210_MIC_GAIN_37_5DB,
-}
-
-_validate_bits = cv.float_with_unit("bits", "bit")
-
-CONFIG_SCHEMA = (
-    cv.Schema(
-        {
-            cv.GenerateID(): cv.declare_id(ES7210),
-            cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All(
-                _validate_bits, cv.enum(ES7210_BITS_PER_SAMPLE_ENUM)
-            ),
-            cv.Optional(CONF_MIC_GAIN, default="24DB"): cv.enum(
-                ES7210_MIC_GAIN_ENUM, upper=True
-            ),
-            cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1),
-        }
-    )
-    .extend(cv.COMPONENT_SCHEMA)
-    .extend(i2c.i2c_device_schema(0x40))
-)
-
-
-async def to_code(config):
-    var = cg.new_Pvariable(config[CONF_ID])
-    await cg.register_component(var, config)
-    await i2c.register_i2c_device(var, config)
-
-    cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE]))
-    cg.add(var.set_mic_gain(config[CONF_MIC_GAIN]))
-    cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
diff --git a/esphome/components/es7210/audio_adc.py b/esphome/components/es7210/audio_adc.py
new file mode 100644
index 0000000000..f0bd8bc25a
--- /dev/null
+++ b/esphome/components/es7210/audio_adc.py
@@ -0,0 +1,51 @@
+import esphome.codegen as cg
+from esphome.components import i2c
+from esphome.components.audio_adc import AudioAdc
+import esphome.config_validation as cv
+from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE
+
+CODEOWNERS = ["@kahrendt"]
+DEPENDENCIES = ["i2c"]
+
+es7210_ns = cg.esphome_ns.namespace("es7210")
+ES7210 = es7210_ns.class_("ES7210", AudioAdc, cg.Component, i2c.I2CDevice)
+
+
+es7210_bits_per_sample = es7210_ns.enum("ES7210BitsPerSample")
+ES7210_BITS_PER_SAMPLE_ENUM = {
+    16: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_16,
+    24: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_24,
+    32: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_32,
+}
+
+
+ES7210_MIC_GAINS = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 34.5, 36, 37.5]
+
+_validate_bits = cv.float_with_unit("bits", "bit")
+
+CONFIG_SCHEMA = (
+    cv.Schema(
+        {
+            cv.GenerateID(): cv.declare_id(ES7210),
+            cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All(
+                _validate_bits, cv.enum(ES7210_BITS_PER_SAMPLE_ENUM)
+            ),
+            cv.Optional(CONF_MIC_GAIN, default="24db"): cv.All(
+                cv.decibel, cv.one_of(*ES7210_MIC_GAINS)
+            ),
+            cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1),
+        }
+    )
+    .extend(cv.COMPONENT_SCHEMA)
+    .extend(i2c.i2c_device_schema(0x40))
+)
+
+
+async def to_code(config):
+    var = cg.new_Pvariable(config[CONF_ID])
+    await cg.register_component(var, config)
+    await i2c.register_i2c_device(var, config)
+
+    cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE]))
+    cg.add(var.set_mic_gain(config[CONF_MIC_GAIN]))
+    cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
diff --git a/esphome/components/es7210/es7210.cpp b/esphome/components/es7210/es7210.cpp
index d2f2c3c1ff..9a99e60995 100644
--- a/esphome/components/es7210/es7210.cpp
+++ b/esphome/components/es7210/es7210.cpp
@@ -25,12 +25,12 @@ static const size_t MCLK_DIV_FRE = 256;
   }
 
 void ES7210::dump_config() {
-  ESP_LOGCONFIG(TAG, "ES7210 ADC:");
+  ESP_LOGCONFIG(TAG, "ES7210 audio ADC:");
   ESP_LOGCONFIG(TAG, "  Bits Per Sample: %" PRIu8, this->bits_per_sample_);
   ESP_LOGCONFIG(TAG, "  Sample Rate: %" PRIu32, this->sample_rate_);
 
   if (this->is_failed()) {
-    ESP_LOGCONFIG(TAG, "  Failed to initialize!");
+    ESP_LOGE(TAG, "  Failed to initialize");
     return;
   }
 }
@@ -84,6 +84,16 @@ void ES7210::setup() {
   // Enable device
   ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x71));
   ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x41));
+
+  this->setup_complete_ = true;
+}
+
+bool ES7210::set_mic_gain(float mic_gain) {
+  this->mic_gain_ = clamp<float>(mic_gain, ES7210_MIC_GAIN_MIN, ES7210_MIC_GAIN_MAX);
+  if (this->setup_complete_) {
+    return this->configure_mic_gain_();
+  }
+  return true;
 }
 
 bool ES7210::configure_sample_rate_() {
@@ -122,9 +132,11 @@ bool ES7210::configure_sample_rate_() {
 
   return true;
 }
+
 bool ES7210::configure_mic_gain_() {
-  for (int i = 0; i < 4; ++i) {
-    this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00);
+  auto regv = this->es7210_gain_reg_value_(this->mic_gain_);
+  for (uint8_t i = 0; i < 4; ++i) {
+    ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00));
   }
   ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0xff));
   ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0xff));
@@ -133,29 +145,44 @@ bool ES7210::configure_mic_gain_() {
   ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
   ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00));
   ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x10, 0x10));
-  ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x0f, this->mic_gain_));
+  ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x0f, regv));
 
   // Configure mic 2
   ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
   ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00));
   ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x10, 0x10));
-  ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x0f, this->mic_gain_));
+  ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x0f, regv));
 
   // Configure mic 3
   ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
   ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00));
   ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x10, 0x10));
-  ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x0f, this->mic_gain_));
+  ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x0f, regv));
 
   // Configure mic 4
   ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
   ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00));
   ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x10, 0x10));
-  ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x0f, this->mic_gain_));
+  ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x0f, regv));
 
   return true;
 }
 
+uint8_t ES7210::es7210_gain_reg_value_(float mic_gain) {
+  // reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB
+  mic_gain += 0.5;
+  if (mic_gain <= 33.0) {
+    return (uint8_t) mic_gain / 3;
+  }
+  if (mic_gain < 36.0) {
+    return 12;
+  }
+  if (mic_gain < 37.0) {
+    return 13;
+  }
+  return 14;
+}
+
 bool ES7210::configure_i2s_format_() {
   // Configure bits per sample
   uint8_t reg_val = 0;
diff --git a/esphome/components/es7210/es7210.h b/esphome/components/es7210/es7210.h
index a40dde5aa5..8f6d9d8136 100644
--- a/esphome/components/es7210/es7210.h
+++ b/esphome/components/es7210/es7210.h
@@ -1,8 +1,11 @@
 #pragma once
 
+#include "esphome/components/audio_adc/audio_adc.h"
 #include "esphome/components/i2c/i2c.h"
 #include "esphome/core/component.h"
 
+#include "es7210_const.h"
+
 namespace esphome {
 namespace es7210 {
 
@@ -14,25 +17,7 @@ enum ES7210BitsPerSample : uint8_t {
   ES7210_BITS_PER_SAMPLE_32 = 32,
 };
 
-enum ES7210MicGain : uint8_t {
-  ES7210_MIC_GAIN_0DB = 0,
-  ES7210_MIC_GAIN_3DB,
-  ES7210_MIC_GAIN_6DB,
-  ES7210_MIC_GAIN_9DB,
-  ES7210_MIC_GAIN_12DB,
-  ES7210_MIC_GAIN_15DB,
-  ES7210_MIC_GAIN_18DB,
-  ES7210_MIC_GAIN_21DB,
-  ES7210_MIC_GAIN_24DB,
-  ES7210_MIC_GAIN_27DB,
-  ES7210_MIC_GAIN_30DB,
-  ES7210_MIC_GAIN_33DB,
-  ES7210_MIC_GAIN_34_5DB,
-  ES7210_MIC_GAIN_36DB,
-  ES7210_MIC_GAIN_37_5DB,
-};
-
-class ES7210 : public Component, public i2c::I2CDevice {
+class ES7210 : public audio_adc::AudioAdc, public Component, public i2c::I2CDevice {
   /* Class for configuring an ES7210 ADC for microphone input.
    * Based on code from:
    * - https://github.com/espressif/esp-bsp/ (accessed 20241219)
@@ -44,9 +29,11 @@ class ES7210 : public Component, public i2c::I2CDevice {
   void dump_config() override;
 
   void set_bits_per_sample(ES7210BitsPerSample bits_per_sample) { this->bits_per_sample_ = bits_per_sample; }
-  void set_mic_gain(ES7210MicGain mic_gain) { this->mic_gain_ = mic_gain; }
+  bool set_mic_gain(float mic_gain) override;
   void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; }
 
+  float mic_gain() override { return this->mic_gain_; };
+
  protected:
   /// @brief Updates an I2C registry address by modifying the current state
   /// @param reg_addr I2C register address
@@ -55,14 +42,20 @@ class ES7210 : public Component, public i2c::I2CDevice {
   /// @return True if successful, false otherwise
   bool es7210_update_reg_bit_(uint8_t reg_addr, uint8_t update_bits, uint8_t data);
 
+  /// @brief Convert floating point mic gain value to register value
+  /// @param mic_gain Gain value to convert
+  /// @return Corresponding register value for specified gain
+  uint8_t es7210_gain_reg_value_(float mic_gain);
+
   bool configure_i2s_format_();
   bool configure_mic_gain_();
   bool configure_sample_rate_();
 
+  bool setup_complete_{false};
   bool enable_tdm_{false};  // TDM is unsupported in ESPHome as of version 2024.12
-  ES7210MicGain mic_gain_;
-  ES7210BitsPerSample bits_per_sample_;
-  uint32_t sample_rate_;
+  float mic_gain_{0};
+  ES7210BitsPerSample bits_per_sample_{ES7210_BITS_PER_SAMPLE_16};
+  uint32_t sample_rate_{0};
 };
 
 }  // namespace es7210
diff --git a/esphome/components/es7210/es7210_const.h b/esphome/components/es7210/es7210_const.h
index 87fd6d86d2..e5ffea5743 100644
--- a/esphome/components/es7210/es7210_const.h
+++ b/esphome/components/es7210/es7210_const.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include "es7210.h"
+#include <cinttypes>
 
 namespace esphome {
 namespace es7210 {
@@ -42,7 +42,7 @@ static const uint8_t ES7210_MIC12_POWER_REG4B = 0x4B; /* MICBias & ADC & PGA Pow
 static const uint8_t ES7210_MIC34_POWER_REG4C = 0x4C;
 
 /*
- * Clock coefficient structer
+ * Clock coefficient structure
  */
 struct ES7210Coefficient {
   uint32_t mclk;  // mclk frequency
@@ -122,5 +122,8 @@ static const ES7210Coefficient ES7210_COEFFICIENTS[] = {
     {19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8},
 };
 
+static const float ES7210_MIC_GAIN_MIN = 0.0;
+static const float ES7210_MIC_GAIN_MAX = 37.5;
+
 }  // namespace es7210
 }  // namespace esphome
diff --git a/tests/components/es7210/common.yaml b/tests/components/es7210/common.yaml
index 5c30f7e883..3fab177cb3 100644
--- a/tests/components/es7210/common.yaml
+++ b/tests/components/es7210/common.yaml
@@ -1,6 +1,16 @@
+esphome:
+  on_boot:
+    then:
+      - audio_adc.set_mic_gain: 0db
+      - audio_adc.set_mic_gain: !lambda 'return 4;'
+
 i2c:
   - id: i2c_aic3204
     scl: ${scl_pin}
     sda: ${sda_pin}
 
-es7210:
+audio_adc:
+  - platform: es7210
+    id: es7210_adc
+    bits_per_sample: 16bit
+    sample_rate: 16000