mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[ade7880] Fix duplicate sensor name validation error (#10155)
This commit is contained in:
		| @@ -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) | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user