1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-02 03:12:20 +01:00

[ade7880] Fix duplicate sensor name validation error (#10155)

This commit is contained in:
J. Nick Koston
2025-08-10 16:12:36 -05:00
committed by GitHub
parent 6bfe281d18
commit 279f56141e
2 changed files with 94 additions and 68 deletions

View File

@@ -36,6 +36,7 @@ from esphome.const import (
UNIT_WATT, UNIT_WATT,
UNIT_WATT_HOURS, UNIT_WATT_HOURS,
) )
from esphome.types import ConfigType
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
@@ -51,6 +52,20 @@ CONF_POWER_GAIN = "power_gain"
CONF_NEUTRAL = "neutral" CONF_NEUTRAL = "neutral"
# Tuple of power channel phases
POWER_PHASES = (CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C)
# Tuple of sensor types that can be configured for power channels
POWER_SENSOR_TYPES = (
CONF_CURRENT,
CONF_VOLTAGE,
CONF_ACTIVE_POWER,
CONF_APPARENT_POWER,
CONF_POWER_FACTOR,
CONF_FORWARD_ACTIVE_ENERGY,
CONF_REVERSE_ACTIVE_ENERGY,
)
NEUTRAL_CHANNEL_SCHEMA = cv.Schema( NEUTRAL_CHANNEL_SCHEMA = cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(NeutralChannel), cv.GenerateID(): cv.declare_id(NeutralChannel),
@@ -150,7 +165,64 @@ POWER_CHANNEL_SCHEMA = cv.Schema(
} }
) )
CONFIG_SCHEMA = (
def prefix_sensor_name(
sensor_conf: ConfigType,
channel_name: str,
channel_config: ConfigType,
sensor_type: str,
) -> None:
"""Helper to prefix sensor name with channel name.
Args:
sensor_conf: The sensor configuration (dict or string)
channel_name: The channel name to prefix with
channel_config: The channel configuration to update
sensor_type: The sensor type key in the channel config
"""
if isinstance(sensor_conf, dict) and CONF_NAME in sensor_conf:
sensor_name = sensor_conf[CONF_NAME]
if sensor_name and not sensor_name.startswith(channel_name):
sensor_conf[CONF_NAME] = f"{channel_name} {sensor_name}"
elif isinstance(sensor_conf, str):
# Simple value case - convert to dict with prefixed name
channel_config[sensor_type] = {CONF_NAME: f"{channel_name} {sensor_conf}"}
def process_channel_sensors(
config: ConfigType, channel_key: str, sensor_types: tuple
) -> None:
"""Process sensors for a channel and prefix their names.
Args:
config: The main configuration
channel_key: The channel key (e.g., CONF_PHASE_A, CONF_NEUTRAL)
sensor_types: Tuple of sensor types to process for this channel
"""
if not (channel_config := config.get(channel_key)) or not (
channel_name := channel_config.get(CONF_NAME)
):
return
for sensor_type in sensor_types:
if sensor_conf := channel_config.get(sensor_type):
prefix_sensor_name(sensor_conf, channel_name, channel_config, sensor_type)
def preprocess_channels(config: ConfigType) -> ConfigType:
"""Preprocess channel configurations to add channel name prefix to sensor names."""
# Process power channels
for channel in POWER_PHASES:
process_channel_sensors(config, channel, POWER_SENSOR_TYPES)
# Process neutral channel
process_channel_sensors(config, CONF_NEUTRAL, (CONF_CURRENT,))
return config
CONFIG_SCHEMA = cv.All(
preprocess_channels,
cv.Schema( cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(ADE7880), cv.GenerateID(): cv.declare_id(ADE7880),
@@ -167,7 +239,7 @@ CONFIG_SCHEMA = (
} }
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x38)) .extend(i2c.i2c_device_schema(0x38)),
) )
@@ -188,15 +260,7 @@ async def neutral_channel(config):
async def power_channel(config): async def power_channel(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
for sensor_type in [ for sensor_type in POWER_SENSOR_TYPES:
CONF_CURRENT,
CONF_VOLTAGE,
CONF_ACTIVE_POWER,
CONF_APPARENT_POWER,
CONF_POWER_FACTOR,
CONF_FORWARD_ACTIVE_ENERGY,
CONF_REVERSE_ACTIVE_ENERGY,
]:
if conf := config.get(sensor_type): if conf := config.get(sensor_type):
sens = await sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(getattr(var, f"set_{sensor_type}")(sens)) cg.add(getattr(var, f"set_{sensor_type}")(sens))
@@ -216,44 +280,6 @@ async def power_channel(config):
return var return var
def final_validate(config):
for channel in [CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]:
if channel := config.get(channel):
channel_name = channel.get(CONF_NAME)
for sensor_type in [
CONF_CURRENT,
CONF_VOLTAGE,
CONF_ACTIVE_POWER,
CONF_APPARENT_POWER,
CONF_POWER_FACTOR,
CONF_FORWARD_ACTIVE_ENERGY,
CONF_REVERSE_ACTIVE_ENERGY,
]:
if conf := channel.get(sensor_type):
sensor_name = conf.get(CONF_NAME)
if (
sensor_name
and channel_name
and not sensor_name.startswith(channel_name)
):
conf[CONF_NAME] = f"{channel_name} {sensor_name}"
if channel := config.get(CONF_NEUTRAL):
channel_name = channel.get(CONF_NAME)
if conf := channel.get(CONF_CURRENT):
sensor_name = conf.get(CONF_NAME)
if (
sensor_name
and channel_name
and not sensor_name.startswith(channel_name)
):
conf[CONF_NAME] = f"{channel_name} {sensor_name}"
FINAL_VALIDATE_SCHEMA = final_validate
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config) await cg.register_component(var, config)

View File

@@ -12,12 +12,12 @@ sensor:
frequency: 60Hz frequency: 60Hz
phase_a: phase_a:
name: Channel A name: Channel A
voltage: Channel A Voltage voltage: Voltage
current: Channel A Current current: Current
active_power: Channel A Active Power active_power: Active Power
power_factor: Channel A Power Factor power_factor: Power Factor
forward_active_energy: Channel A Forward Active Energy forward_active_energy: Forward Active Energy
reverse_active_energy: Channel A Reverse Active Energy reverse_active_energy: Reverse Active Energy
calibration: calibration:
current_gain: 3116628 current_gain: 3116628
voltage_gain: -757178 voltage_gain: -757178
@@ -25,12 +25,12 @@ sensor:
phase_angle: 188 phase_angle: 188
phase_b: phase_b:
name: Channel B name: Channel B
voltage: Channel B Voltage voltage: Voltage
current: Channel B Current current: Current
active_power: Channel B Active Power active_power: Active Power
power_factor: Channel B Power Factor power_factor: Power Factor
forward_active_energy: Channel B Forward Active Energy forward_active_energy: Forward Active Energy
reverse_active_energy: Channel B Reverse Active Energy reverse_active_energy: Reverse Active Energy
calibration: calibration:
current_gain: 3133655 current_gain: 3133655
voltage_gain: -755235 voltage_gain: -755235
@@ -38,12 +38,12 @@ sensor:
phase_angle: 188 phase_angle: 188
phase_c: phase_c:
name: Channel C name: Channel C
voltage: Channel C Voltage voltage: Voltage
current: Channel C Current current: Current
active_power: Channel C Active Power active_power: Active Power
power_factor: Channel C Power Factor power_factor: Power Factor
forward_active_energy: Channel C Forward Active Energy forward_active_energy: Forward Active Energy
reverse_active_energy: Channel C Reverse Active Energy reverse_active_energy: Reverse Active Energy
calibration: calibration:
current_gain: 3111158 current_gain: 3111158
voltage_gain: -743813 voltage_gain: -743813
@@ -51,6 +51,6 @@ sensor:
phase_angle: 180 phase_angle: 180
neutral: neutral:
name: Neutral name: Neutral
current: Neutral Current current: Current
calibration: calibration:
current_gain: 3189 current_gain: 3189