diff --git a/CODEOWNERS b/CODEOWNERS
index 204d2b58bd..f6f7ac6f9c 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -266,6 +266,7 @@ esphome/components/mcp23x17_base/* @jesserockz
 esphome/components/mcp23xxx_base/* @jesserockz
 esphome/components/mcp2515/* @danielschramm @mvturnho
 esphome/components/mcp3204/* @rsumner
+esphome/components/mcp4461/* @p1ngb4ck
 esphome/components/mcp4728/* @berfenger
 esphome/components/mcp47a1/* @jesserockz
 esphome/components/mcp9600/* @mreditor97
diff --git a/esphome/components/mcp4461/__init__.py b/esphome/components/mcp4461/__init__.py
new file mode 100644
index 0000000000..1764629ff3
--- /dev/null
+++ b/esphome/components/mcp4461/__init__.py
@@ -0,0 +1,42 @@
+import esphome.codegen as cg
+import esphome.config_validation as cv
+from esphome.components import i2c
+from esphome.const import CONF_ID
+
+CODEOWNERS = ["@p1ngb4ck"]
+DEPENDENCIES = ["i2c"]
+MULTI_CONF = True
+CONF_DISABLE_WIPER_0 = "disable_wiper_0"
+CONF_DISABLE_WIPER_1 = "disable_wiper_1"
+CONF_DISABLE_WIPER_2 = "disable_wiper_2"
+CONF_DISABLE_WIPER_3 = "disable_wiper_3"
+
+mcp4461_ns = cg.esphome_ns.namespace("mcp4461")
+Mcp4461Component = mcp4461_ns.class_("Mcp4461Component", cg.Component, i2c.I2CDevice)
+CONF_MCP4461_ID = "mcp4461_id"
+
+CONFIG_SCHEMA = (
+    cv.Schema(
+        {
+            cv.GenerateID(): cv.declare_id(Mcp4461Component),
+            cv.Optional(CONF_DISABLE_WIPER_0, default=False): cv.boolean,
+            cv.Optional(CONF_DISABLE_WIPER_1, default=False): cv.boolean,
+            cv.Optional(CONF_DISABLE_WIPER_2, default=False): cv.boolean,
+            cv.Optional(CONF_DISABLE_WIPER_3, default=False): cv.boolean,
+        }
+    )
+    .extend(cv.COMPONENT_SCHEMA)
+    .extend(i2c.i2c_device_schema(0x2C))
+)
+
+
+async def to_code(config):
+    var = cg.new_Pvariable(
+        config[CONF_ID],
+        config[CONF_DISABLE_WIPER_0],
+        config[CONF_DISABLE_WIPER_1],
+        config[CONF_DISABLE_WIPER_2],
+        config[CONF_DISABLE_WIPER_3],
+    )
+    await cg.register_component(var, config)
+    await i2c.register_i2c_device(var, config)
diff --git a/esphome/components/mcp4461/mcp4461.cpp b/esphome/components/mcp4461/mcp4461.cpp
new file mode 100644
index 0000000000..5393241281
--- /dev/null
+++ b/esphome/components/mcp4461/mcp4461.cpp
@@ -0,0 +1,618 @@
+#include "mcp4461.h"
+
+#include "esphome/core/helpers.h"
+#include "esphome/core/hal.h"
+
+namespace esphome {
+namespace mcp4461 {
+
+static const char *const TAG = "mcp4461";
+constexpr uint8_t EEPROM_WRITE_TIMEOUT_MS = 10;
+
+void Mcp4461Component::setup() {
+  ESP_LOGCONFIG(TAG, "Setting up mcp4461 using address (0x%02X)...", this->address_);
+  auto err = this->write(nullptr, 0);
+  if (err != i2c::ERROR_OK) {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->mark_failed();
+    return;
+  }
+  // save WP/WL status
+  this->update_write_protection_status_();
+  for (uint8_t i = 0; i < 8; i++) {
+    if (this->reg_[i].initial_value.has_value()) {
+      uint16_t initial_state = static_cast<uint16_t>(*this->reg_[i].initial_value * 256.0f);
+      this->write_wiper_level_(i, initial_state);
+    }
+    if (this->reg_[i].enabled) {
+      this->reg_[i].state = this->read_wiper_level_(i);
+    } else {
+      // only volatile wipers can be set disabled on hw level
+      if (i < 4) {
+        this->reg_[i].state = 0;
+        Mcp4461WiperIdx wiper_idx = static_cast<Mcp4461WiperIdx>(i);
+        this->disable_wiper_(wiper_idx);
+      }
+    }
+  }
+}
+
+void Mcp4461Component::set_initial_value(Mcp4461WiperIdx wiper, float initial_value) {
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  this->reg_[wiper_idx].initial_value = initial_value;
+}
+
+void Mcp4461Component::initialize_terminal_disabled(Mcp4461WiperIdx wiper, char terminal) {
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  switch (terminal) {
+    case 'a':
+      this->reg_[wiper_idx].terminal_a = false;
+      break;
+    case 'b':
+      this->reg_[wiper_idx].terminal_b = false;
+      break;
+    case 'w':
+      this->reg_[wiper_idx].terminal_w = false;
+      break;
+  }
+}
+
+void Mcp4461Component::update_write_protection_status_() {
+  uint8_t status_register_value = this->get_status_register_();
+  this->write_protected_ = static_cast<bool>((status_register_value >> 0) & 0x01);
+  this->reg_[0].wiper_lock_active = static_cast<bool>((status_register_value >> 2) & 0x01);
+  this->reg_[1].wiper_lock_active = static_cast<bool>((status_register_value >> 3) & 0x01);
+  this->reg_[2].wiper_lock_active = static_cast<bool>((status_register_value >> 5) & 0x01);
+  this->reg_[3].wiper_lock_active = static_cast<bool>((status_register_value >> 6) & 0x01);
+}
+
+void Mcp4461Component::dump_config() {
+  ESP_LOGCONFIG(TAG, "mcp4461:");
+  LOG_I2C_DEVICE(this);
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+  }
+  // log wiper status
+  for (uint8_t i = 0; i < 8; ++i) {
+    // terminals only valid for volatile wipers 0-3 - enable/disable is terminal hw
+    // so also invalid for nonvolatile. For these, only print current level.
+    // reworked to be a one-line intentionally, as output would not be in order
+    if (i < 4) {
+      ESP_LOGCONFIG(TAG, "  ├── Volatile wiper [%u] level: %u, Status: %s, HW: %s, A: %s, B: %s, W: %s", i,
+                    this->reg_[i].state, ONOFF(this->reg_[i].terminal_hw), ONOFF(this->reg_[i].terminal_a),
+                    ONOFF(this->reg_[i].terminal_b), ONOFF(this->reg_[i].terminal_w), ONOFF(this->reg_[i].enabled));
+    } else {
+      ESP_LOGCONFIG(TAG, "  ├── Nonvolatile wiper [%u] level: %u", i, this->reg_[i].state);
+    }
+  }
+}
+
+void Mcp4461Component::loop() {
+  if (this->status_has_warning()) {
+    this->get_status_register_();
+  }
+  for (uint8_t i = 0; i < 8; i++) {
+    if (this->reg_[i].update_level) {
+      // set wiper i state if changed
+      if (this->reg_[i].state != this->read_wiper_level_(i)) {
+        this->write_wiper_level_(i, this->reg_[i].state);
+      }
+    }
+    this->reg_[i].update_level = false;
+    // can be true only for wipers 0-3
+    // setting changes for terminals of nonvolatile wipers
+    // is prohibited in public methods
+    if (this->reg_[i].update_terminal) {
+      // set terminal register changes
+      Mcp4461TerminalIdx terminal_connector =
+          i < 2 ? Mcp4461TerminalIdx::MCP4461_TERMINAL_0 : Mcp4461TerminalIdx::MCP4461_TERMINAL_1;
+      uint8_t new_terminal_value = this->calc_terminal_connector_byte_(terminal_connector);
+      ESP_LOGV(TAG, "updating terminal %u to new value %u", static_cast<uint8_t>(terminal_connector),
+               new_terminal_value);
+      this->set_terminal_register_(terminal_connector, new_terminal_value);
+    }
+    this->reg_[i].update_terminal = false;
+  }
+}
+
+uint8_t Mcp4461Component::get_status_register_() {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return 0;
+  }
+  uint8_t addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_STATUS);
+  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::READ);
+  uint16_t buf;
+  if (!this->read_byte_16(reg, &buf)) {
+    this->error_code_ = MCP4461_STATUS_REGISTER_ERROR;
+    this->mark_failed();
+    return 0;
+  }
+  uint8_t msb = buf >> 8;
+  uint8_t lsb = static_cast<uint8_t>(buf & 0x00ff);
+  if (msb != 1 || ((lsb >> 7) & 0x01) != 1 || ((lsb >> 1) & 0x01) != 1) {
+    // D8, D7 and R1 bits are hardlocked to 1 -> a status msb bit 0 (bit 9 of status register) of 0 or lsb bit 1/7 = 0
+    // indicate device/communication issues, therefore mark component failed
+    this->error_code_ = MCP4461_STATUS_REGISTER_INVALID;
+    this->mark_failed();
+    return 0;
+  }
+  this->status_clear_warning();
+  return lsb;
+}
+
+void Mcp4461Component::read_status_register_to_log() {
+  uint8_t status_register_value = this->get_status_register_();
+  ESP_LOGI(TAG, "D7:  %u, WL3: %u, WL2: %u, EEWA: %u, WL1: %u, WL0: %u, R1: %u, WP: %u",
+           ((status_register_value >> 7) & 0x01), ((status_register_value >> 6) & 0x01),
+           ((status_register_value >> 5) & 0x01), ((status_register_value >> 4) & 0x01),
+           ((status_register_value >> 3) & 0x01), ((status_register_value >> 2) & 0x01),
+           ((status_register_value >> 1) & 0x01), ((status_register_value >> 0) & 0x01));
+}
+
+uint8_t Mcp4461Component::get_wiper_address_(uint8_t wiper) {
+  uint8_t addr;
+  bool nonvolatile = false;
+  if (wiper > 3) {
+    nonvolatile = true;
+    wiper = wiper - 4;
+  }
+  switch (wiper) {
+    case 0:
+      addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW0);
+      break;
+    case 1:
+      addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW1);
+      break;
+    case 2:
+      addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW2);
+      break;
+    case 3:
+      addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW3);
+      break;
+    default:
+      ESP_LOGW(TAG, "unknown wiper specified");
+      return 0;
+  }
+  if (nonvolatile) {
+    addr = addr + 0x20;
+  }
+  return addr;
+}
+
+uint16_t Mcp4461Component::get_wiper_level_(Mcp4461WiperIdx wiper) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return 0;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  if (!(this->reg_[wiper_idx].enabled)) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
+    return 0;
+  }
+  if (!(this->reg_[wiper_idx].enabled)) {
+    ESP_LOGW(TAG, "reading from disabled volatile wiper %u, returning 0", wiper_idx);
+    return 0;
+  }
+  return this->read_wiper_level_(wiper_idx);
+}
+
+uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) {
+  uint8_t addr = this->get_wiper_address_(wiper_idx);
+  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
+  if (wiper_idx > 3) {
+    if (!this->is_eeprom_ready_for_writing_(true)) {
+      return 0;
+    }
+  }
+  uint16_t buf = 0;
+  if (!(this->read_byte_16(reg, &buf))) {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->status_set_warning();
+    ESP_LOGW(TAG, "Error fetching %swiper %u value", (wiper_idx > 3) ? "nonvolatile " : "", wiper_idx);
+    return 0;
+  }
+  return buf;
+}
+
+bool Mcp4461Component::update_wiper_level_(Mcp4461WiperIdx wiper) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return false;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  if (!(this->reg_[wiper_idx].enabled)) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
+    return false;
+  }
+  uint16_t data = this->get_wiper_level_(wiper);
+  ESP_LOGV(TAG, "Got value %u from wiper %u", data, wiper_idx);
+  this->reg_[wiper_idx].state = data;
+  return true;
+}
+
+bool Mcp4461Component::set_wiper_level_(Mcp4461WiperIdx wiper, uint16_t value) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return false;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  if (value > 0x100) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_VALUE_INVALID)));
+    return false;
+  }
+  if (!(this->reg_[wiper_idx].enabled)) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
+    return false;
+  }
+  if (this->reg_[wiper_idx].wiper_lock_active) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
+    return false;
+  }
+  ESP_LOGV(TAG, "Setting MCP4461 wiper %u to %u", wiper_idx, value);
+  this->reg_[wiper_idx].state = value;
+  this->reg_[wiper_idx].update_level = true;
+  return true;
+}
+
+void Mcp4461Component::write_wiper_level_(uint8_t wiper, uint16_t value) {
+  bool nonvolatile = wiper > 3;
+  if (!(this->mcp4461_write_(this->get_wiper_address_(wiper), value, nonvolatile))) {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->status_set_warning();
+    ESP_LOGW(TAG, "Error writing %swiper %u level %u", (wiper > 3) ? "nonvolatile " : "", wiper, value);
+  }
+}
+
+void Mcp4461Component::enable_wiper_(Mcp4461WiperIdx wiper) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  if ((this->reg_[wiper_idx].enabled)) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_ENABLED)));
+    return;
+  }
+  if (this->reg_[wiper_idx].wiper_lock_active) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
+    return;
+  }
+  ESP_LOGV(TAG, "Enabling wiper %u", wiper_idx);
+  this->reg_[wiper_idx].enabled = true;
+  if (wiper_idx < 4) {
+    this->reg_[wiper_idx].terminal_hw = true;
+    this->reg_[wiper_idx].update_terminal = true;
+  }
+}
+
+void Mcp4461Component::disable_wiper_(Mcp4461WiperIdx wiper) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  if (!(this->reg_[wiper_idx].enabled)) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
+    return;
+  }
+  if (this->reg_[wiper_idx].wiper_lock_active) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
+    return;
+  }
+  ESP_LOGV(TAG, "Disabling wiper %u", wiper_idx);
+  this->reg_[wiper_idx].enabled = true;
+  if (wiper_idx < 4) {
+    this->reg_[wiper_idx].terminal_hw = true;
+    this->reg_[wiper_idx].update_terminal = true;
+  }
+}
+
+bool Mcp4461Component::increase_wiper_(Mcp4461WiperIdx wiper) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return false;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  if (!(this->reg_[wiper_idx].enabled)) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
+    return false;
+  }
+  if (this->reg_[wiper_idx].wiper_lock_active) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
+    return false;
+  }
+  if (this->reg_[wiper_idx].state == 256) {
+    ESP_LOGV(TAG, "Maximum wiper level reached, further increase of wiper %u prohibited", wiper_idx);
+    return false;
+  }
+  ESP_LOGV(TAG, "Increasing wiper %u", wiper_idx);
+  uint8_t addr = this->get_wiper_address_(wiper_idx);
+  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
+  auto err = this->write(&this->address_, reg, sizeof(reg));
+  if (err != i2c::ERROR_OK) {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->status_set_warning();
+    return false;
+  }
+  this->reg_[wiper_idx].state++;
+  return true;
+}
+
+bool Mcp4461Component::decrease_wiper_(Mcp4461WiperIdx wiper) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return false;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  if (!(this->reg_[wiper_idx].enabled)) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
+    return false;
+  }
+  if (this->reg_[wiper_idx].wiper_lock_active) {
+    ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
+    return false;
+  }
+  if (this->reg_[wiper_idx].state == 0) {
+    ESP_LOGV(TAG, "Minimum wiper level reached, further decrease of wiper %u prohibited", wiper_idx);
+    return false;
+  }
+  ESP_LOGV(TAG, "Decreasing wiper %u", wiper_idx);
+  uint8_t addr = this->get_wiper_address_(wiper_idx);
+  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::DECREMENT);
+  auto err = this->write(&this->address_, reg, sizeof(reg));
+  if (err != i2c::ERROR_OK) {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->status_set_warning();
+    return false;
+  }
+  this->reg_[wiper_idx].state--;
+  return true;
+}
+
+uint8_t Mcp4461Component::calc_terminal_connector_byte_(Mcp4461TerminalIdx terminal_connector) {
+  uint8_t i = static_cast<uint8_t>(terminal_connector) <= 1 ? 0 : 2;
+  uint8_t new_value_byte = 0;
+  new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_b);
+  new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_w) << 1;
+  new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_a) << 2;
+  new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_hw) << 3;
+  new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_b) << 4;
+  new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_w) << 5;
+  new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_a) << 6;
+  new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_hw) << 7;
+  return new_value_byte;
+}
+
+uint8_t Mcp4461Component::get_terminal_register_(Mcp4461TerminalIdx terminal_connector) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return 0;
+  }
+  uint8_t reg = static_cast<uint8_t>(terminal_connector) == 0 ? static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON0)
+                                                              : static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1);
+  reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
+  uint16_t buf;
+  if (this->read_byte_16(reg, &buf)) {
+    return static_cast<uint8_t>(buf & 0x00ff);
+  } else {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->status_set_warning();
+    ESP_LOGW(TAG, "Error fetching terminal register value");
+    return 0;
+  }
+}
+
+void Mcp4461Component::update_terminal_register_(Mcp4461TerminalIdx terminal_connector) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return;
+  }
+  if ((static_cast<uint8_t>(terminal_connector) != 0 && static_cast<uint8_t>(terminal_connector) != 1)) {
+    return;
+  }
+  uint8_t terminal_data = this->get_terminal_register_(terminal_connector);
+  if (terminal_data == 0) {
+    return;
+  }
+  ESP_LOGV(TAG, "Got terminal register %u data 0x%02X", static_cast<uint8_t>(terminal_connector), terminal_data);
+  uint8_t wiper_index = 0;
+  if (static_cast<uint8_t>(terminal_connector) == 1) {
+    wiper_index = 2;
+  }
+  this->reg_[wiper_index].terminal_b = ((terminal_data >> 0) & 0x01);
+  this->reg_[wiper_index].terminal_w = ((terminal_data >> 1) & 0x01);
+  this->reg_[wiper_index].terminal_a = ((terminal_data >> 2) & 0x01);
+  this->reg_[wiper_index].terminal_hw = ((terminal_data >> 3) & 0x01);
+  this->reg_[(wiper_index + 1)].terminal_b = ((terminal_data >> 4) & 0x01);
+  this->reg_[(wiper_index + 1)].terminal_w = ((terminal_data >> 5) & 0x01);
+  this->reg_[(wiper_index + 1)].terminal_a = ((terminal_data >> 6) & 0x01);
+  this->reg_[(wiper_index + 1)].terminal_hw = ((terminal_data >> 7) & 0x01);
+}
+
+bool Mcp4461Component::set_terminal_register_(Mcp4461TerminalIdx terminal_connector, uint8_t data) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return false;
+  }
+  uint8_t addr;
+  if (static_cast<uint8_t>(terminal_connector) == 0) {
+    addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON0);
+  } else if (static_cast<uint8_t>(terminal_connector) == 1) {
+    addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1);
+  } else {
+    ESP_LOGW(TAG, "Invalid terminal connector id %u specified", static_cast<uint8_t>(terminal_connector));
+    return false;
+  }
+  if (!(this->mcp4461_write_(addr, data))) {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->status_set_warning();
+    return false;
+  }
+  return true;
+}
+
+void Mcp4461Component::enable_terminal_(Mcp4461WiperIdx wiper, char terminal) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  ESP_LOGV(TAG, "Enabling terminal %c of wiper %u", terminal, wiper_idx);
+  switch (terminal) {
+    case 'h':
+      this->reg_[wiper_idx].terminal_hw = true;
+      break;
+    case 'a':
+      this->reg_[wiper_idx].terminal_a = true;
+      break;
+    case 'b':
+      this->reg_[wiper_idx].terminal_b = true;
+      break;
+    case 'w':
+      this->reg_[wiper_idx].terminal_w = true;
+      break;
+    default:
+      ESP_LOGW(TAG, "Unknown terminal %c specified", terminal);
+      return;
+  }
+  this->reg_[wiper_idx].update_terminal = false;
+}
+
+void Mcp4461Component::disable_terminal_(Mcp4461WiperIdx wiper, char terminal) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return;
+  }
+  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
+  ESP_LOGV(TAG, "Disabling terminal %c of wiper %u", terminal, wiper_idx);
+  switch (terminal) {
+    case 'h':
+      this->reg_[wiper_idx].terminal_hw = false;
+      break;
+    case 'a':
+      this->reg_[wiper_idx].terminal_a = false;
+      break;
+    case 'b':
+      this->reg_[wiper_idx].terminal_b = false;
+      break;
+    case 'w':
+      this->reg_[wiper_idx].terminal_w = false;
+      break;
+    default:
+      ESP_LOGW(TAG, "Unknown terminal %c specified", terminal);
+      return;
+  }
+  this->reg_[wiper_idx].update_terminal = false;
+}
+
+uint16_t Mcp4461Component::get_eeprom_value(Mcp4461EepromLocation location) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return 0;
+  }
+  uint8_t reg = 0;
+  reg |= static_cast<uint8_t>(Mcp4461EepromLocation::MCP4461_EEPROM_1) + (static_cast<uint8_t>(location) * 0x10);
+  reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
+  uint16_t buf;
+  if (!this->is_eeprom_ready_for_writing_(true)) {
+    return 0;
+  }
+  if (!this->read_byte_16(reg, &buf)) {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->status_set_warning();
+    ESP_LOGW(TAG, "Error fetching EEPROM location value");
+    return 0;
+  }
+  return buf;
+}
+
+bool Mcp4461Component::set_eeprom_value(Mcp4461EepromLocation location, uint16_t value) {
+  if (this->is_failed()) {
+    ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
+    return false;
+  }
+  uint8_t addr = 0;
+  if (value > 511) {
+    return false;
+  }
+  if (value > 256) {
+    addr = 1;
+  }
+  addr |= static_cast<uint8_t>(Mcp4461EepromLocation::MCP4461_EEPROM_1) + (static_cast<uint8_t>(location) * 0x10);
+  if (!(this->mcp4461_write_(addr, value, true))) {
+    this->error_code_ = MCP4461_STATUS_I2C_ERROR;
+    this->status_set_warning();
+    ESP_LOGW(TAG, "Error writing EEPROM value");
+    return false;
+  }
+  return true;
+}
+
+bool Mcp4461Component::is_writing_() {
+  /* Read the EEPROM write-active status from the status register */
+  bool writing = static_cast<bool>((this->get_status_register_() >> 4) & 0x01);
+
+  /* If EEPROM is no longer writing, reset the timeout flag */
+  if (!writing) {
+    /* This is protected boolean flag in Mcp4461Component class */
+    this->last_eeprom_write_timed_out_ = false;
+  }
+
+  return writing;
+}
+
+bool Mcp4461Component::is_eeprom_ready_for_writing_(bool wait_if_not_ready) {
+  /* Check initial write status */
+  bool ready_for_write = !this->is_writing_();
+
+  /* Return early if no waiting is required or EEPROM is already ready */
+  if (ready_for_write || !wait_if_not_ready || this->last_eeprom_write_timed_out_) {
+    return ready_for_write;
+  }
+
+  /* Timestamp before starting the loop */
+  const uint32_t start_millis = millis();
+
+  ESP_LOGV(TAG, "Waiting until EEPROM is ready for write, start_millis = %" PRIu32, start_millis);
+
+  /* Loop until EEPROM is ready or timeout is reached */
+  while (!ready_for_write && ((millis() - start_millis) < EEPROM_WRITE_TIMEOUT_MS)) {
+    ready_for_write = !this->is_writing_();
+
+    /* If ready, exit early */
+    if (ready_for_write) {
+      ESP_LOGV(TAG, "EEPROM is ready for new write, elapsed_millis = %" PRIu32, millis() - start_millis);
+      return true;
+    }
+
+    /* Not ready yet, yield before checking again */
+    yield();
+  }
+
+  /* If still not ready after timeout, log error and mark the timeout */
+  ESP_LOGE(TAG, "EEPROM write timeout exceeded (%u ms)", EEPROM_WRITE_TIMEOUT_MS);
+  this->last_eeprom_write_timed_out_ = true;
+
+  return false;
+}
+
+bool Mcp4461Component::mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile) {
+  uint8_t reg = data > 0xff ? 1 : 0;
+  uint8_t value_byte = static_cast<uint8_t>(data & 0x00ff);
+  ESP_LOGV(TAG, "Writing value %u to address %u", data, addr);
+  reg |= addr;
+  reg |= static_cast<uint8_t>(Mcp4461Commands::WRITE);
+  if (nonvolatile) {
+    if (this->write_protected_) {
+      ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WRITE_PROTECTED)));
+      return false;
+    }
+    if (!this->is_eeprom_ready_for_writing_(true)) {
+      return false;
+    }
+  }
+  return this->write_byte(reg, value_byte);
+}
+}  // namespace mcp4461
+}  // namespace esphome
diff --git a/esphome/components/mcp4461/mcp4461.h b/esphome/components/mcp4461/mcp4461.h
new file mode 100644
index 0000000000..9b7f60f201
--- /dev/null
+++ b/esphome/components/mcp4461/mcp4461.h
@@ -0,0 +1,171 @@
+#pragma once
+
+#include "esphome/core/component.h"
+#include "esphome/core/log.h"
+#include "esphome/components/i2c/i2c.h"
+
+namespace esphome {
+namespace mcp4461 {
+
+struct WiperState {
+  bool enabled = true;
+  uint16_t state = 0;
+  optional<float> initial_value;
+  bool terminal_a = true;
+  bool terminal_b = true;
+  bool terminal_w = true;
+  bool terminal_hw = true;
+  bool wiper_lock_active = false;
+  bool update_level = false;
+  bool update_terminal = false;
+};
+
+// default wiper state is 128 / 0x80h
+enum class Mcp4461Commands : uint8_t { WRITE = 0x00, INCREMENT = 0x04, DECREMENT = 0x08, READ = 0x0C };
+
+enum class Mcp4461Addresses : uint8_t {
+  MCP4461_VW0 = 0x00,
+  MCP4461_VW1 = 0x10,
+  MCP4461_VW2 = 0x60,
+  MCP4461_VW3 = 0x70,
+  MCP4461_STATUS = 0x50,
+  MCP4461_TCON0 = 0x40,
+  MCP4461_TCON1 = 0xA0,
+  MCP4461_EEPROM_1 = 0xB0
+};
+
+enum class Mcp4461WiperIdx : uint8_t {
+  MCP4461_WIPER_0 = 0,
+  MCP4461_WIPER_1 = 1,
+  MCP4461_WIPER_2 = 2,
+  MCP4461_WIPER_3 = 3,
+  MCP4461_WIPER_4 = 4,
+  MCP4461_WIPER_5 = 5,
+  MCP4461_WIPER_6 = 6,
+  MCP4461_WIPER_7 = 7
+};
+
+enum class Mcp4461EepromLocation : uint8_t {
+  MCP4461_EEPROM_0 = 0,
+  MCP4461_EEPROM_1 = 1,
+  MCP4461_EEPROM_2 = 2,
+  MCP4461_EEPROM_3 = 3,
+  MCP4461_EEPROM_4 = 4
+};
+
+enum class Mcp4461TerminalIdx : uint8_t { MCP4461_TERMINAL_0 = 0, MCP4461_TERMINAL_1 = 1 };
+
+class Mcp4461Wiper;
+
+// Mcp4461Component
+class Mcp4461Component : public Component, public i2c::I2CDevice {
+ public:
+  Mcp4461Component(bool disable_wiper_0, bool disable_wiper_1, bool disable_wiper_2, bool disable_wiper_3)
+      : wiper_0_disabled_(disable_wiper_0),
+        wiper_1_disabled_(disable_wiper_1),
+        wiper_2_disabled_(disable_wiper_2),
+        wiper_3_disabled_(disable_wiper_3) {
+    this->reg_[0].enabled = !wiper_0_disabled_;
+    this->reg_[1].enabled = !wiper_1_disabled_;
+    this->reg_[2].enabled = !wiper_2_disabled_;
+    this->reg_[3].enabled = !wiper_3_disabled_;
+  }
+
+  void setup() override;
+  void dump_config() override;
+  float get_setup_priority() const override { return setup_priority::HARDWARE; }
+  void loop() override;
+  /// @brief get eeprom value from location
+  /// @param[in] location - eeprom location 0-4
+  /// @return eeprom value of specified location (9 bits max)
+  uint16_t get_eeprom_value(Mcp4461EepromLocation location);
+  /// @brief set eeprom value at specified location
+  /// @param[in] location - eeprom location 0-4
+  /// @param[in] value - 9 bits value to store
+  bool set_eeprom_value(Mcp4461EepromLocation location, uint16_t value);
+  /// @brief public function used to set initial value
+  /// @param[in] wiper - the wiper to set the value for
+  /// @param[in] initial_value - the initial value in range 0-1.0 as float
+  void set_initial_value(Mcp4461WiperIdx wiper, float initial_value);
+  /// @brief public function used to set disable terminal config
+  /// @param[in] wiper - the wiper to set the value for
+  /// @param[in] terminal - the terminal to disable, one of ['a','b','w','h']
+  void initialize_terminal_disabled(Mcp4461WiperIdx wiper, char terminal);
+  /// @brief read status register to log
+  void read_status_register_to_log();
+
+ protected:
+  friend class Mcp4461Wiper;
+  void update_write_protection_status_();
+  uint8_t get_wiper_address_(uint8_t wiper);
+  uint16_t read_wiper_level_(uint8_t wiper);
+  uint8_t get_status_register_();
+  uint16_t get_wiper_level_(Mcp4461WiperIdx wiper);
+  bool set_wiper_level_(Mcp4461WiperIdx wiper, uint16_t value);
+  bool update_wiper_level_(Mcp4461WiperIdx wiper);
+  void enable_wiper_(Mcp4461WiperIdx wiper);
+  void disable_wiper_(Mcp4461WiperIdx wiper);
+  bool increase_wiper_(Mcp4461WiperIdx wiper);
+  bool decrease_wiper_(Mcp4461WiperIdx wiper);
+  void enable_terminal_(Mcp4461WiperIdx wiper, char terminal);
+  void disable_terminal_(Mcp4461WiperIdx, char terminal);
+  bool is_writing_();
+  bool is_eeprom_ready_for_writing_(bool wait_if_not_ready);
+  void write_wiper_level_(uint8_t wiper, uint16_t value);
+  bool mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile = false);
+  uint8_t calc_terminal_connector_byte_(Mcp4461TerminalIdx terminal_connector);
+  void update_terminal_register_(Mcp4461TerminalIdx terminal_connector);
+  uint8_t get_terminal_register_(Mcp4461TerminalIdx terminal_connector);
+  bool set_terminal_register_(Mcp4461TerminalIdx terminal_connector, uint8_t data);
+
+  // Converts a status to a human readable string
+  static const LogString *get_message_string(int status) {
+    switch (status) {
+      case MCP4461_STATUS_I2C_ERROR:
+        return LOG_STR("I2C error - communication with MCP4461 failed!");
+      case MCP4461_STATUS_REGISTER_ERROR:
+        return LOG_STR("Status register could not be read");
+      case MCP4461_STATUS_REGISTER_INVALID:
+        return LOG_STR("Invalid status register value - bits 1,7 or 8 are 0");
+      case MCP4461_VALUE_INVALID:
+        return LOG_STR("Invalid value for wiper given");
+      case MCP4461_WRITE_PROTECTED:
+        return LOG_STR("MCP4461 is write protected. Setting nonvolatile wipers/eeprom values is prohibited.");
+      case MCP4461_WIPER_ENABLED:
+        return LOG_STR("MCP4461 Wiper is already enabled, ignoring cmd to enable.");
+      case MCP4461_WIPER_DISABLED:
+        return LOG_STR("MCP4461 Wiper is disabled. All actions on this wiper are prohibited.");
+      case MCP4461_WIPER_LOCKED:
+        return LOG_STR("MCP4461 Wiper is locked using WiperLock-technology. All actions on this wiper are prohibited.");
+      case MCP4461_STATUS_OK:
+        return LOG_STR("Status OK");
+      default:
+        return LOG_STR("Unknown");
+    }
+  }
+
+  enum ErrorCode {
+    MCP4461_STATUS_OK = 0,               // CMD completed successfully
+    MCP4461_FAILED,                      // component failed
+    MCP4461_STATUS_I2C_ERROR,            // Unable to communicate with device
+    MCP4461_STATUS_REGISTER_INVALID,     // Status register value was invalid
+    MCP4461_STATUS_REGISTER_ERROR,       // Error fetching status register
+    MCP4461_PROHIBITED_FOR_NONVOLATILE,  //
+    MCP4461_VALUE_INVALID,               // Invalid value given for wiper / eeprom
+    MCP4461_WRITE_PROTECTED,  // The value was read, but the CRC over the payload (valid and data) does not match
+    MCP4461_WIPER_ENABLED,    // The wiper is enabled, discard additional enabling actions
+    MCP4461_WIPER_DISABLED,   // The wiper is disabled - all actions for this wiper will be aborted/discarded
+    MCP4461_WIPER_LOCKED,     // The wiper is locked using WiperLock-technology - all actions for this wiper will be
+                              // aborted/discarded
+  } error_code_{MCP4461_STATUS_OK};
+
+  WiperState reg_[8];
+  bool last_eeprom_write_timed_out_{false};
+  bool write_protected_{false};
+  bool wiper_0_disabled_{false};
+  bool wiper_1_disabled_{false};
+  bool wiper_2_disabled_{false};
+  bool wiper_3_disabled_{false};
+};
+}  // namespace mcp4461
+}  // namespace esphome
diff --git a/esphome/components/mcp4461/output/__init__.py b/esphome/components/mcp4461/output/__init__.py
new file mode 100644
index 0000000000..ba59f97643
--- /dev/null
+++ b/esphome/components/mcp4461/output/__init__.py
@@ -0,0 +1,60 @@
+import esphome.codegen as cg
+import esphome.config_validation as cv
+from esphome.components import output
+from esphome.const import CONF_CHANNEL, CONF_ID, CONF_INITIAL_VALUE
+from .. import Mcp4461Component, CONF_MCP4461_ID, mcp4461_ns
+
+DEPENDENCIES = ["mcp4461"]
+
+Mcp4461Wiper = mcp4461_ns.class_(
+    "Mcp4461Wiper", output.FloatOutput, cg.Parented.template(Mcp4461Component)
+)
+
+Mcp4461WiperIdx = mcp4461_ns.enum("Mcp4461WiperIdx", is_class=True)
+CHANNEL_OPTIONS = {
+    "A": Mcp4461WiperIdx.MCP4461_WIPER_0,
+    "B": Mcp4461WiperIdx.MCP4461_WIPER_1,
+    "C": Mcp4461WiperIdx.MCP4461_WIPER_2,
+    "D": Mcp4461WiperIdx.MCP4461_WIPER_3,
+    "E": Mcp4461WiperIdx.MCP4461_WIPER_4,
+    "F": Mcp4461WiperIdx.MCP4461_WIPER_5,
+    "G": Mcp4461WiperIdx.MCP4461_WIPER_6,
+    "H": Mcp4461WiperIdx.MCP4461_WIPER_7,
+}
+
+CONF_TERMINAL_A = "terminal_a"
+CONF_TERMINAL_B = "terminal_b"
+CONF_TERMINAL_W = "terminal_w"
+
+CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
+    {
+        cv.Required(CONF_ID): cv.declare_id(Mcp4461Wiper),
+        cv.GenerateID(CONF_MCP4461_ID): cv.use_id(Mcp4461Component),
+        cv.Required(CONF_CHANNEL): cv.enum(CHANNEL_OPTIONS, upper=True),
+        cv.Optional(CONF_TERMINAL_A, default=True): cv.boolean,
+        cv.Optional(CONF_TERMINAL_B, default=True): cv.boolean,
+        cv.Optional(CONF_TERMINAL_W, default=True): cv.boolean,
+        cv.Optional(CONF_INITIAL_VALUE): cv.float_range(min=0.0, max=1.0),
+    }
+)
+
+
+async def to_code(config):
+    parent = await cg.get_variable(config[CONF_MCP4461_ID])
+    var = cg.new_Pvariable(
+        config[CONF_ID],
+        parent,
+        config[CONF_CHANNEL],
+    )
+    if not config[CONF_TERMINAL_A]:
+        cg.add(parent.initialize_terminal_disabled(config[CONF_CHANNEL], "a"))
+    if not config[CONF_TERMINAL_B]:
+        cg.add(parent.initialize_terminal_disabled(config[CONF_CHANNEL], "b"))
+    if not config[CONF_TERMINAL_W]:
+        cg.add(parent.initialize_terminal_disabled(config[CONF_CHANNEL], "w"))
+    if CONF_INITIAL_VALUE in config:
+        cg.add(
+            parent.set_initial_value(config[CONF_CHANNEL], config[CONF_INITIAL_VALUE])
+        )
+    await output.register_output(var, config)
+    await cg.register_parented(var, config[CONF_MCP4461_ID])
diff --git a/esphome/components/mcp4461/output/mcp4461_output.cpp b/esphome/components/mcp4461/output/mcp4461_output.cpp
new file mode 100644
index 0000000000..2d85a5df61
--- /dev/null
+++ b/esphome/components/mcp4461/output/mcp4461_output.cpp
@@ -0,0 +1,73 @@
+#include "mcp4461_output.h"
+#include <cmath>
+
+#include "esphome/core/helpers.h"
+#include "esphome/core/log.h"
+
+namespace esphome {
+namespace mcp4461 {
+
+static const char *const TAG = "mcp4461.output";
+
+// public set_level function
+void Mcp4461Wiper::set_level(float state) {
+  if (!std::isfinite(state)) {
+    ESP_LOGW(TAG, "Finite state state value is required.");
+    return;
+  }
+  state = clamp(state, 0.0f, 1.0f);
+  if (this->is_inverted()) {
+    state = 1.0f - state;
+  }
+  this->write_state(state);
+}
+
+// floats from other components (like light etc.) are passed as "percentage floats"
+// this function converts them to the 0 - 256 range used by the MCP4461
+void Mcp4461Wiper::write_state(float state) {
+  if (this->parent_->set_wiper_level_(this->wiper_, static_cast<uint16_t>(std::roundf(state * 256)))) {
+    this->state_ = state;
+  }
+}
+
+float Mcp4461Wiper::read_state() { return (static_cast<float>(this->parent_->get_wiper_level_(this->wiper_)) / 256.0); }
+
+float Mcp4461Wiper::update_state() {
+  this->state_ = this->read_state();
+  return this->state_;
+}
+
+void Mcp4461Wiper::set_state(bool state) {
+  if (state) {
+    this->turn_on();
+  } else {
+    this->turn_off();
+  }
+}
+
+void Mcp4461Wiper::turn_on() { this->parent_->enable_wiper_(this->wiper_); }
+
+void Mcp4461Wiper::turn_off() { this->parent_->disable_wiper_(this->wiper_); }
+
+void Mcp4461Wiper::increase_wiper() {
+  if (this->parent_->increase_wiper_(this->wiper_)) {
+    this->state_ = this->update_state();
+    ESP_LOGV(TAG, "Increased wiper %u to %u", static_cast<uint8_t>(this->wiper_),
+             static_cast<uint16_t>(std::roundf(this->state_ * 256)));
+  }
+}
+
+void Mcp4461Wiper::decrease_wiper() {
+  if (this->parent_->decrease_wiper_(this->wiper_)) {
+    this->state_ = this->update_state();
+    ESP_LOGV(TAG, "Decreased wiper %u to %u", static_cast<uint8_t>(this->wiper_),
+             static_cast<uint16_t>(std::roundf(this->state_ * 256)));
+  }
+}
+
+void Mcp4461Wiper::enable_terminal(char terminal) { this->parent_->enable_terminal_(this->wiper_, terminal); }
+
+void Mcp4461Wiper::disable_terminal(char terminal) { this->parent_->disable_terminal_(this->wiper_, terminal); }
+
+}  // namespace mcp4461
+}  // namespace esphome
diff --git a/esphome/components/mcp4461/output/mcp4461_output.h b/esphome/components/mcp4461/output/mcp4461_output.h
new file mode 100644
index 0000000000..4055cef30a
--- /dev/null
+++ b/esphome/components/mcp4461/output/mcp4461_output.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include "../mcp4461.h"
+#include "esphome/core/component.h"
+#include "esphome/components/output/float_output.h"
+#include "esphome/components/i2c/i2c.h"
+
+namespace esphome {
+namespace mcp4461 {
+
+class Mcp4461Wiper : public output::FloatOutput, public Parented<Mcp4461Component> {
+ public:
+  Mcp4461Wiper(Mcp4461Component *parent, Mcp4461WiperIdx wiper) : parent_(parent), wiper_(wiper) {}
+  /// @brief Set level of wiper
+  /// @param[in] state - The desired float level in range 0-1.0
+  void set_level(float state);
+  /// @brief Enables/Disables current output using bool parameter
+  /// @param[in] state boolean var representing desired state (true=ON, false=OFF)
+  void set_state(bool state) override;
+  /// @brief Enables current output
+  void turn_on() override;
+  /// @brief Disables current output
+  void turn_off() override;
+  /// @brief Read current device wiper state without updating internal output state
+  /// @return float - current device state as float in range 0 - 1.0
+  float read_state();
+  /// @brief Update current output state using device wiper state
+  /// @return float - current updated output state as float in range 0 - 1.0
+  float update_state();
+  /// @brief Increase wiper by 1 tap
+  void increase_wiper();
+  /// @brief Decrease wiper by 1 tap
+  void decrease_wiper();
+  /// @brief Enable given terminal
+  /// @param[in] terminal single char parameter defining desired terminal to enable, one of { 'a', 'b', 'w', 'h' }
+  void enable_terminal(char terminal);
+  /// @brief Disable given terminal
+  /// @param[in] terminal single char parameter defining desired terminal to disable, one of { 'a', 'b', 'w', 'h' }
+  void disable_terminal(char terminal);
+
+ protected:
+  void write_state(float state) override;
+  Mcp4461Component *parent_;
+  Mcp4461WiperIdx wiper_;
+  float state_;
+};
+
+}  // namespace mcp4461
+}  // namespace esphome
diff --git a/tests/components/mcp4461/common.yaml b/tests/components/mcp4461/common.yaml
new file mode 100644
index 0000000000..ce1866fdb8
--- /dev/null
+++ b/tests/components/mcp4461/common.yaml
@@ -0,0 +1,28 @@
+i2c:
+  - id: i2c_mcp4461
+    sda: ${sda_pin}
+    scl: ${scl_pin}
+
+mcp4461:
+  - id: mcp4461_digipot_01
+
+output:
+  - platform: mcp4461
+    id: digipot_wiper_1
+    mcp4461_id: mcp4461_digipot_01
+    channel: A
+
+  - platform: mcp4461
+    id: digipot_wiper_2
+    mcp4461_id: mcp4461_digipot_01
+    channel: B
+
+  - platform: mcp4461
+    id: digipot_wiper_3
+    mcp4461_id: mcp4461_digipot_01
+    channel: C
+
+  - platform: mcp4461
+    id: digipot_wiper_4
+    mcp4461_id: mcp4461_digipot_01
+    channel: D
diff --git a/tests/components/mcp4461/test.esp32-ard.yaml b/tests/components/mcp4461/test.esp32-ard.yaml
new file mode 100644
index 0000000000..c5deb7ca0a
--- /dev/null
+++ b/tests/components/mcp4461/test.esp32-ard.yaml
@@ -0,0 +1,5 @@
+substitutions:
+  sda_pin: GPIO16
+  scl_pin: GPIO17
+
+<<: !include common.yaml
diff --git a/tests/components/mcp4461/test.esp32-c3-ard.yaml b/tests/components/mcp4461/test.esp32-c3-ard.yaml
new file mode 100644
index 0000000000..a87353b78b
--- /dev/null
+++ b/tests/components/mcp4461/test.esp32-c3-ard.yaml
@@ -0,0 +1,5 @@
+substitutions:
+  sda_pin: GPIO4
+  scl_pin: GPIO5
+
+<<: !include common.yaml
diff --git a/tests/components/mcp4461/test.esp32-c3-idf.yaml b/tests/components/mcp4461/test.esp32-c3-idf.yaml
new file mode 100644
index 0000000000..a87353b78b
--- /dev/null
+++ b/tests/components/mcp4461/test.esp32-c3-idf.yaml
@@ -0,0 +1,5 @@
+substitutions:
+  sda_pin: GPIO4
+  scl_pin: GPIO5
+
+<<: !include common.yaml
diff --git a/tests/components/mcp4461/test.esp32-idf.yaml b/tests/components/mcp4461/test.esp32-idf.yaml
new file mode 100644
index 0000000000..c5deb7ca0a
--- /dev/null
+++ b/tests/components/mcp4461/test.esp32-idf.yaml
@@ -0,0 +1,5 @@
+substitutions:
+  sda_pin: GPIO16
+  scl_pin: GPIO17
+
+<<: !include common.yaml
diff --git a/tests/components/mcp4461/test.esp8266-ard.yaml b/tests/components/mcp4461/test.esp8266-ard.yaml
new file mode 100644
index 0000000000..a87353b78b
--- /dev/null
+++ b/tests/components/mcp4461/test.esp8266-ard.yaml
@@ -0,0 +1,5 @@
+substitutions:
+  sda_pin: GPIO4
+  scl_pin: GPIO5
+
+<<: !include common.yaml