Merge branch 'entity_name_must_be_unique' into integration

This commit is contained in:
J. Nick Koston 2025-06-30 18:31:40 -05:00
commit 6733cd4ed1
No known key found for this signature in database
4 changed files with 75 additions and 4 deletions

View File

@ -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)

View File

@ -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"

View File

@ -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:

View File

@ -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]