From 8c2ed9a307c5209f8d1cb7159c598707d40c133b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 1 Sep 2026 08:12:48 +0000 Subject: [PATCH 1/4] Handle unreachable network SSH connect errors Co-authored-by: gensyn <36128035+gensyn@users.noreply.github.com> --- coordinator.py | 3 ++- tests/integration_tests/test_integration.py | 19 +++++++++++++++++++ tests/unit_tests/test_coordinator.py | 9 +++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/coordinator.py b/coordinator.py index 4efa8b4..a8aa8d3 100644 --- a/coordinator.py +++ b/coordinator.py @@ -13,6 +13,7 @@ import logging import socket import sys +from errno import ENETUNREACH, EHOSTUNREACH from pathlib import Path from typing import Any @@ -162,7 +163,7 @@ async def async_execute(self, data: dict[str, Any]) -> dict[str, Any]: translation_key="connection_timed_out", ) from exc except OSError as exc: - if isinstance(exc, socket.gaierror): + if isinstance(exc, socket.gaierror) or exc.errno in (ENETUNREACH, EHOSTUNREACH): _LOGGER.warning("Host %s is not reachable: %s", host, exc) raise ServiceValidationError( "Host is not reachable.", diff --git a/tests/integration_tests/test_integration.py b/tests/integration_tests/test_integration.py index 8728c4c..199d004 100644 --- a/tests/integration_tests/test_integration.py +++ b/tests/integration_tests/test_integration.py @@ -658,6 +658,25 @@ async def test_host_not_reachable(self, hass: HomeAssistant) -> None: assert exc_info.value.translation_key == "host_not_reachable" + async def test_network_unreachable(self, hass: HomeAssistant) -> None: + err = OSError(101, "Connect call failed ('192.0.2.1', 22)") + entry = _make_entry() + await _setup_entry(hass, entry) + + with patch("custom_components.ssh_command.coordinator.connect", + return_value=_MockConnectRaises(err)): + with patch("pathlib.Path.exists", return_value=False): + with pytest.raises(ServiceValidationError) as exc_info: + await hass.services.async_call( + DOMAIN, + SERVICE_EXECUTE, + SERVICE_DATA_BASE, + blocking=True, + return_response=True, + ) + + assert exc_info.value.translation_key == "host_not_reachable" + async def test_other_oserror_is_reraised(self, hass: HomeAssistant) -> None: err = OSError("something else") entry = _make_entry() diff --git a/tests/unit_tests/test_coordinator.py b/tests/unit_tests/test_coordinator.py index c6a8957..f4a9c5c 100644 --- a/tests/unit_tests/test_coordinator.py +++ b/tests/unit_tests/test_coordinator.py @@ -108,6 +108,15 @@ async def test_async_execute_name_resolution_failure(self): self.assertEqual(ctx.exception.translation_key, "host_not_reachable") + async def test_async_execute_network_unreachable(self): + err = OSError(101, "Connect call failed ('192.0.2.1', 22)") + + with patch("ssh_command.coordinator.connect", return_value=_MockConnectRaises(err)): + with self.assertRaises(ServiceValidationError) as ctx: + await self.coordinator.async_execute(EXECUTE_DATA_BASE) + + self.assertEqual(ctx.exception.translation_key, "host_not_reachable") + async def test_async_execute_other_oserror_reraised(self): err = OSError("something else") From 441022b95846b7e90855131e09fdfb591025a0e2 Mon Sep 17 00:00:00 2001 From: gensyn <36128035+gensyn@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:24:30 +0200 Subject: [PATCH 2/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/unit_tests/test_coordinator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_coordinator.py b/tests/unit_tests/test_coordinator.py index f4a9c5c..f4f639b 100644 --- a/tests/unit_tests/test_coordinator.py +++ b/tests/unit_tests/test_coordinator.py @@ -109,8 +109,9 @@ async def test_async_execute_name_resolution_failure(self): self.assertEqual(ctx.exception.translation_key, "host_not_reachable") async def test_async_execute_network_unreachable(self): - err = OSError(101, "Connect call failed ('192.0.2.1', 22)") + from errno import ENETUNREACH + err = OSError(ENETUNREACH, "Connect call failed ('192.0.2.1', 22)") with patch("ssh_command.coordinator.connect", return_value=_MockConnectRaises(err)): with self.assertRaises(ServiceValidationError) as ctx: await self.coordinator.async_execute(EXECUTE_DATA_BASE) From 1f1040142e80de99e90268def3393add87d5729d Mon Sep 17 00:00:00 2001 From: gensyn <36128035+gensyn@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:24:46 +0200 Subject: [PATCH 3/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/integration_tests/test_integration.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration_tests/test_integration.py b/tests/integration_tests/test_integration.py index 199d004..5e99060 100644 --- a/tests/integration_tests/test_integration.py +++ b/tests/integration_tests/test_integration.py @@ -659,8 +659,9 @@ async def test_host_not_reachable(self, hass: HomeAssistant) -> None: assert exc_info.value.translation_key == "host_not_reachable" async def test_network_unreachable(self, hass: HomeAssistant) -> None: - err = OSError(101, "Connect call failed ('192.0.2.1', 22)") - entry = _make_entry() + from errno import ENETUNREACH + + err = OSError(ENETUNREACH, "Connect call failed ('192.0.2.1', 22)") await _setup_entry(hass, entry) with patch("custom_components.ssh_command.coordinator.connect", From 7b59662e7741b72fc45e8f2fda4a9652dbdaf6b6 Mon Sep 17 00:00:00 2001 From: gensyn Date: Tue, 1 Sep 2026 10:31:20 +0200 Subject: [PATCH 4/4] Fixed test --- tests/integration_tests/test_integration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration_tests/test_integration.py b/tests/integration_tests/test_integration.py index 5e99060..43e030e 100644 --- a/tests/integration_tests/test_integration.py +++ b/tests/integration_tests/test_integration.py @@ -662,6 +662,7 @@ async def test_network_unreachable(self, hass: HomeAssistant) -> None: from errno import ENETUNREACH err = OSError(ENETUNREACH, "Connect call failed ('192.0.2.1', 22)") + entry = _make_entry() await _setup_entry(hass, entry) with patch("custom_components.ssh_command.coordinator.connect",