From 7b660e66ee3d9c0efb7b4b8cac2f4a8b3ab479fc Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Mon, 21 Sep 2026 17:10:21 +0530 Subject: [PATCH] fix(homeassistant): detect the supervised launch through is_supervised_gateway_launch gateway.restart already answers "was this gateway launched by a generated service" (HERMES_SUPERVISED_CHILD or launchd's XPC_SERVICE_NAME, so a plist that predates the marker still counts); the hint reuses it instead of a second env read. The negative branch (plain unreachable host without a supervisor) is now an unmarked test so the Linux lane keeps covering the function; the macOS-only test holds the positive branch. --- plugins/platforms/homeassistant/adapter.py | 8 ++++---- tests/gateway/test_homeassistant.py | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/plugins/platforms/homeassistant/adapter.py b/plugins/platforms/homeassistant/adapter.py index 002e4adf87..9779925133 100644 --- a/plugins/platforms/homeassistant/adapter.py +++ b/plugins/platforms/homeassistant/adapter.py @@ -7,7 +7,6 @@ import asyncio import errno import json import logging -import os import sys import time import uuid @@ -21,6 +20,7 @@ except ImportError: AIOHTTP_AVAILABLE = False aiohttp = None # type: ignore[assignment] +from gateway.restart import is_supervised_gateway_launch from gateway.config import Platform, PlatformConfig from gateway.platforms.base import gateway_trust_env, BasePlatformAdapter, SendResult from gateway.platforms.event import MessageEvent, MessageType @@ -70,12 +70,12 @@ _TRIGGERED = ("cleared", "triggered") # binary_sensor wording, indexed by ``sta def _connect_error_detail(exc: BaseException) -> str: """Annotate macOS Local Network Privacy denials so launchd HA failures are actionable (#71206). - Only a launchd-supervised gateway on macOS (``HERMES_SUPERVISED_CHILD`` is set by the generated plist) - can be denied this way; the same errno elsewhere is a genuinely unreachable host. + Only a supervised (launchd) gateway on macOS can be denied this way; the same errno from a + Terminal-run gateway or on another OS is a genuinely unreachable host. """ text = str(exc) os_error = getattr(exc, "os_error", None) or exc.__cause__ or exc - if (sys.platform == "darwin" and os.environ.get("HERMES_SUPERVISED_CHILD") + if (sys.platform == "darwin" and is_supervised_gateway_launch() and getattr(os_error, "errno", None) == errno.EHOSTUNREACH): return ( f"{text} — macOS Local Network Privacy is blocking this launchd gateway from the LAN. " diff --git a/tests/gateway/test_homeassistant.py b/tests/gateway/test_homeassistant.py index f1c1ce27d2..a013b21f34 100644 --- a/tests/gateway/test_homeassistant.py +++ b/tests/gateway/test_homeassistant.py @@ -321,22 +321,25 @@ class TestWsUrlConstruction: class TestLocalNetworkConnectHint: - @pytest.mark.macos_only - def test_ehostunreach_under_launchd_names_the_remedy(self, monkeypatch): - """Only the launchd-supervised gateway can be denied by Local Network Privacy; the same errno from a - Terminal-run gateway is a genuinely unreachable host and must not be blamed on macOS (#71206).""" + def test_ehostunreach_outside_launchd_is_a_plain_unreachable_host(self, monkeypatch): + """The same errno from a Terminal-run gateway (or another OS) must not be blamed on macOS.""" from plugins.platforms.homeassistant.adapter import _connect_error_detail - err = OSError(errno.EHOSTUNREACH, "No route to host") monkeypatch.delenv("HERMES_SUPERVISED_CHILD", raising=False) + monkeypatch.delenv("XPC_SERVICE_NAME", raising=False) + err = OSError(errno.EHOSTUNREACH, "No route to host") assert _connect_error_detail(err) == str(err) + assert _connect_error_detail(RuntimeError("auth failed")) == "auth failed" + + @pytest.mark.macos_only + def test_ehostunreach_under_launchd_names_the_remedy(self, monkeypatch): + """Only the launchd-supervised gateway can be denied by Local Network Privacy (#71206).""" + from plugins.platforms.homeassistant.adapter import _connect_error_detail monkeypatch.setenv("HERMES_SUPERVISED_CHILD", "1") + err = OSError(errno.EHOSTUNREACH, "No route to host") detail = _connect_error_detail(err) assert detail.startswith(str(err)) assert "Local Network" in detail assert "hermes gateway install" in detail assert "71206" in detail - # Unrelated failures stay untouched. - assert _connect_error_detail(RuntimeError("auth failed")) == "auth failed" -