mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Merge branch 'entity_name_must_be_unique' into integration
This commit is contained in:
		| @@ -187,8 +187,17 @@ def entity_duplicate_validator(platform: str) -> Callable[[ConfigType], ConfigTy | ||||
|         # Get the entity name | ||||
|         entity_name = config[CONF_NAME] | ||||
|  | ||||
|         # For duplicate detection, just use the sanitized name | ||||
|         name_key = sanitize(snake_case(entity_name)) | ||||
|         # Get device name if entity is on a sub-device | ||||
|         device_name = None | ||||
|         if CONF_DEVICE_ID in config: | ||||
|             device_id_obj = config[CONF_DEVICE_ID] | ||||
|             device_name = device_id_obj.id | ||||
|  | ||||
|         # Calculate what object_id will actually be used | ||||
|         # This handles empty names correctly by using device/friendly names | ||||
|         name_key = get_base_entity_object_id( | ||||
|             entity_name, CORE.friendly_name, device_name | ||||
|         ) | ||||
|  | ||||
|         # Check for duplicates | ||||
|         unique_key = (platform, name_key) | ||||
|   | ||||
| @@ -133,7 +133,35 @@ button: | ||||
|     name: "Reset Main" | ||||
|     on_press: []  # Main device | ||||
|  | ||||
| # Scenario 6: Special characters in names - now with unique names | ||||
| # Scenario 6: Empty names (should use device names) | ||||
| select: | ||||
|   - platform: template | ||||
|     name: "" | ||||
|     device_id: controller_1 | ||||
|     options: | ||||
|       - "Option 1" | ||||
|       - "Option 2" | ||||
|     lambda: return {"Option 1"}; | ||||
|     set_action: [] | ||||
|  | ||||
|   - platform: template | ||||
|     name: "" | ||||
|     device_id: controller_2 | ||||
|     options: | ||||
|       - "Option 1" | ||||
|       - "Option 2" | ||||
|     lambda: return {"Option 1"}; | ||||
|     set_action: [] | ||||
|  | ||||
|   - platform: template | ||||
|     name: ""  # Main device | ||||
|     options: | ||||
|       - "Option 1" | ||||
|       - "Option 2" | ||||
|     lambda: return {"Option 1"}; | ||||
|     set_action: [] | ||||
|  | ||||
| # Scenario 7: Special characters in names - now with unique names | ||||
| number: | ||||
|   - platform: template | ||||
|     name: "Temperature Setpoint! Controller 1" | ||||
|   | ||||
| @@ -52,6 +52,7 @@ async def test_duplicate_entities_not_allowed_on_different_devices( | ||||
|         switches = [e for e in all_entities if e.__class__.__name__ == "SwitchInfo"] | ||||
|         buttons = [e for e in all_entities if e.__class__.__name__ == "ButtonInfo"] | ||||
|         numbers = [e for e in all_entities if e.__class__.__name__ == "NumberInfo"] | ||||
|         selects = [e for e in all_entities if e.__class__.__name__ == "SelectInfo"] | ||||
|  | ||||
|         # Scenario 1: Check that temperature sensors have unique names per device | ||||
|         temp_sensors = [s for s in sensors if "Temperature" in s.name] | ||||
| @@ -144,7 +145,28 @@ async def test_duplicate_entities_not_allowed_on_different_devices( | ||||
|             f"Reset buttons should have unique names, got {reset_names}" | ||||
|         ) | ||||
|  | ||||
|         # Scenario 7: Check special characters in number names - now unique | ||||
|         # Scenario 7: Check empty name selects (should use device names) | ||||
|         empty_selects = [s for s in selects if s.name == ""] | ||||
|         assert len(empty_selects) == 3, ( | ||||
|             f"Expected exactly 3 empty name selects, got {len(empty_selects)}" | ||||
|         ) | ||||
|  | ||||
|         # Group by device | ||||
|         c1_selects = [s for s in empty_selects if s.device_id == controller_1.device_id] | ||||
|         c2_selects = [s for s in empty_selects if s.device_id == controller_2.device_id] | ||||
|  | ||||
|         # For main device, device_id is 0 | ||||
|         main_selects = [s for s in empty_selects if s.device_id == 0] | ||||
|  | ||||
|         # Check object IDs for empty name entities - they should use device names | ||||
|         assert len(c1_selects) == 1 and c1_selects[0].object_id == "controller_1" | ||||
|         assert len(c2_selects) == 1 and c2_selects[0].object_id == "controller_2" | ||||
|         assert ( | ||||
|             len(main_selects) == 1 | ||||
|             and main_selects[0].object_id == "duplicate-entities-test" | ||||
|         ) | ||||
|  | ||||
|         # Scenario 8: Check special characters in number names - now unique | ||||
|         temp_numbers = [n for n in numbers if "Temperature Setpoint!" in n.name] | ||||
|         assert len(temp_numbers) == 2, ( | ||||
|             f"Expected exactly 2 temperature setpoint numbers, got {len(temp_numbers)}" | ||||
| @@ -170,6 +192,7 @@ async def test_duplicate_entities_not_allowed_on_different_devices( | ||||
|             + len(switches) | ||||
|             + len(buttons) | ||||
|             + len(numbers) | ||||
|             + len(selects) | ||||
|         ) | ||||
|  | ||||
|         def on_state(state) -> None: | ||||
|   | ||||
| @@ -555,6 +555,17 @@ def test_entity_duplicate_validator_with_devices() -> None: | ||||
|     assert validated3 == config3 | ||||
|     assert ("sensor", "humidity") in CORE.unique_ids | ||||
|  | ||||
|     # Empty names should use device names and be allowed | ||||
|     config4 = {CONF_NAME: "", CONF_DEVICE_ID: device1} | ||||
|     validated4 = validator(config4) | ||||
|     assert validated4 == config4 | ||||
|     assert ("sensor", "device1") in CORE.unique_ids | ||||
|  | ||||
|     config5 = {CONF_NAME: "", CONF_DEVICE_ID: device2} | ||||
|     validated5 = validator(config5) | ||||
|     assert validated5 == config5 | ||||
|     assert ("sensor", "device2") in CORE.unique_ids | ||||
|  | ||||
|  | ||||
| def test_duplicate_entity_yaml_validation( | ||||
|     yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user