From f76fc0c6e1b7c19833032ccee2654a2e9bb707d1 Mon Sep 17 00:00:00 2001 From: Bluetooth Devices Bot Date: Wed, 20 May 2026 05:21:37 +0000 Subject: [PATCH 1/2] Add failing tests for address_is_local case sensitivity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mDNS and DNS names are case-insensitive per RFC 6762 §16 / RFC 4343, but address_is_local() does a case-sensitive endswith(".local") so "MyESP.LOCAL" is rejected. These parametrized cases pin the expected behaviour; they fail against the current implementation. --- tests/test_util.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index d79f032c..8efec248 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 is_ip_address +from aioesphomeapi.util import address_is_local, is_ip_address @pytest.mark.parametrize( @@ -119,3 +119,22 @@ async def _eager_task(): ) def test_is_ip_address(address: str | None, expected: bool) -> None: assert is_ip_address(address) is expected + + +@pytest.mark.parametrize( + ("address", "expected"), + [ + ("myesp.local", True), + ("myesp.local.", True), + # mDNS / DNS names are case-insensitive (RFC 6762 §16, RFC 4343). + ("MyESP.LOCAL", True), + ("myesp.Local", True), + ("MYESP.LOCAL.", True), + ("myesp", False), + ("myesp.localdomain", False), + ("host.example.com", False), + ("", False), + ], +) +def test_address_is_local(address: str, expected: bool) -> None: + assert address_is_local(address) is expected From cc0868f37f2a93299555379c94b549af2cf2aa49 Mon Sep 17 00:00:00 2001 From: Bluetooth Devices Bot Date: Wed, 20 May 2026 05:22:15 +0000 Subject: [PATCH 2/2] Fix address_is_local to match .local case-insensitively MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mDNS hostnames are case-insensitive (RFC 6762 §16, RFC 4343), but address_is_local() used a case-sensitive endswith(".local") so an address advertised as "MyESP.LOCAL" or with a mixed-case TLD was not recognised as local. Lowercase before comparing. --- aioesphomeapi/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aioesphomeapi/util.py b/aioesphomeapi/util.py index cc91a820..02072522 100644 --- a/aioesphomeapi/util.py +++ b/aioesphomeapi/util.py @@ -45,7 +45,7 @@ def host_is_name_part(address: str) -> bool: def address_is_local(address: str) -> bool: """Return True if the address is a local address.""" - return address.removesuffix(".").endswith(".local") + return address.removesuffix(".").lower().endswith(".local") def is_ip_address(address: str | None) -> bool: