Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions homeassistant/components/network/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
from __future__ import annotations

from ipaddress import IPv4Address, IPv6Address, ip_interface
import logging

from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass

Expand All @@ -12,6 +14,8 @@
from .models import Adapter
from .network import Network, async_get_network

_LOGGER = logging.getLogger(__name__)


@bind_hass
async def async_get_adapters(hass: HomeAssistant) -> list[Adapter]:
Expand All @@ -32,6 +36,16 @@ async def async_get_source_ip(
all_ipv4s.extend([ipv4["address"] for ipv4 in ipv4s])

source_ip = util.async_get_source_ip(target_ip)
if not all_ipv4s:
_LOGGER.warning(
"Because the system does not have any enabled IPv4 addresses, source address detection may be inaccurate"
)
if source_ip is None:
raise HomeAssistantError(
"Could not determine source ip because the system does not have any enabled IPv4 addresses and creating a socket failed"
)
return source_ip

return source_ip if source_ip in all_ipv4s else all_ipv4s[0]


Expand Down
48 changes: 48 additions & 0 deletions tests/components/network/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from unittest.mock import MagicMock, Mock, patch

import ifaddr
import pytest

from homeassistant.components import network
from homeassistant.components.network.const import (
Expand All @@ -13,6 +14,7 @@
STORAGE_KEY,
STORAGE_VERSION,
)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component

_NO_LOOPBACK_IPADDR = "192.168.1.5"
Expand Down Expand Up @@ -602,3 +604,49 @@ async def test_async_get_ipv4_broadcast_addresses_multiple(hass, hass_storage):
IPv4Address("192.168.1.255"),
IPv4Address("169.254.255.255"),
}


async def test_async_get_source_ip_no_enabled_addresses(hass, hass_storage, caplog):
"""Test getting the source ip address when all adapters are disabled."""
hass_storage[STORAGE_KEY] = {
"version": STORAGE_VERSION,
"key": STORAGE_KEY,
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]},
}

with patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=[],
), patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket(["192.168.1.5"]),
):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()

assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5"

assert "source address detection may be inaccurate" in caplog.text


async def test_async_get_source_ip_cannot_be_determined_and_no_enabled_addresses(
hass, hass_storage, caplog
):
"""Test getting the source ip address when all adapters are disabled and getting it fails."""
hass_storage[STORAGE_KEY] = {
"version": STORAGE_VERSION,
"key": STORAGE_KEY,
"data": {ATTR_CONFIGURED_ADAPTERS: ["eth1"]},
}

with patch(
"homeassistant.components.network.util.ifaddr.get_adapters",
return_value=[],
), patch(
"homeassistant.components.network.util.socket.socket",
return_value=_mock_socket([None]),
):
assert not await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done()
with pytest.raises(HomeAssistantError):
await network.async_get_source_ip(hass, MDNS_TARGET_IP)