From 04c59a1a8c745978789c0fc7e17ef68387553b3b Mon Sep 17 00:00:00 2001 From: Bluetooth Devices Bot Date: Wed, 20 May 2026 18:38:28 +0000 Subject: [PATCH] Match name and preferred address case-insensitively in build_log_name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mDNS / DNS labels are case-insensitive (RFC 6762 ยง16, RFC 4343). The previous case-sensitive startswith check meant a supplied name 'myesp' with a resolved 'MYESP.local' produced 'myesp @ MYESP.local' instead of dropping the redundant prefix. Adds the first test coverage for build_log_name, including the mixed- case case that exercises the fix. --- aioesphomeapi/util.py | 4 ++-- tests/test_util.py | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/aioesphomeapi/util.py b/aioesphomeapi/util.py index 02072522b..2823ad066 100644 --- a/aioesphomeapi/util.py +++ b/aioesphomeapi/util.py @@ -88,8 +88,8 @@ def build_log_name( return name or addresses[0] if ( name - and name != preferred_address - and not preferred_address.startswith(f"{name}.") + and name.lower() != preferred_address.lower() + and not preferred_address.lower().startswith(f"{name.lower()}.") ): return f"{name} @ {preferred_address}" return preferred_address diff --git a/tests/test_util.py b/tests/test_util.py index 8efec248c..ad15c3f0d 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -5,7 +5,7 @@ import pytest from aioesphomeapi import util -from aioesphomeapi.util import address_is_local, is_ip_address +from aioesphomeapi.util import address_is_local, build_log_name, is_ip_address @pytest.mark.parametrize( @@ -138,3 +138,42 @@ def test_is_ip_address(address: str | None, expected: bool) -> None: ) def test_address_is_local(address: str, expected: bool) -> None: assert address_is_local(address) is expected + + +@pytest.mark.parametrize( + ("name", "addresses", "connected_address", "expected"), + [ + # Bare hostname only. + ("myesp", ["myesp"], None, "myesp"), + (None, ["myesp"], None, "myesp"), + # mDNS .local only. + (None, ["myesp.local"], None, "myesp"), + ("myesp", ["myesp.local"], None, "myesp.local"), + # IP only. + (None, ["192.168.1.10"], None, "192.168.1.10"), + ("myesp", ["192.168.1.10"], None, "myesp @ 192.168.1.10"), + # Connected address provided. + ("myesp", ["192.168.1.10"], "192.168.1.10", "myesp @ 192.168.1.10"), + (None, ["10.0.0.1"], "192.168.1.10", "192.168.1.10"), + ("myesp", ["myesp"], "192.168.1.10", "myesp @ 192.168.1.10"), + # Multiple addresses: name derived from one, IP becomes preferred. + (None, ["myesp", "192.168.1.10"], None, "myesp @ 192.168.1.10"), + (None, ["myesp.local", "192.168.1.10"], None, "myesp @ 192.168.1.10"), + (None, ["192.168.1.10", "myesp.local"], None, "myesp @ 192.168.1.10"), + # Case-insensitive prefix match between name and .local address. + ("myesp", ["MYESP.local"], None, "MYESP.local"), + ("MYESP", ["myesp.local"], None, "myesp.local"), + # Bare hostname overrides supplied name (loop reassigns). + ("alice", ["bob"], None, "bob"), + # Bracketed IPv6 as preferred address. + ("myesp", ["[::1]:6053"], None, "myesp @ [::1]:6053"), + (None, ["[::1]:6053"], None, "[::1]:6053"), + ], +) +def test_build_log_name( + name: str | None, + addresses: list[str], + connected_address: str | None, + expected: str, +) -> None: + assert build_log_name(name, addresses, connected_address) == expected