Replace util.get_local_ip in favor of components.network.async_get_source_ip() - part 4 (#58669)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
b3ffc1e183
commit
5fc51130ea
21 changed files with 83 additions and 125 deletions
|
@ -124,7 +124,7 @@ class AmbiclimateFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
)
|
)
|
||||||
|
|
||||||
def _cb_url(self):
|
def _cb_url(self):
|
||||||
return f"{get_url(self.hass)}{AUTH_CALLBACK_PATH}"
|
return f"{get_url(self.hass, prefer_external=True)}{AUTH_CALLBACK_PATH}"
|
||||||
|
|
||||||
async def _get_authorize_url(self):
|
async def _get_authorize_url(self):
|
||||||
oauth = self._generate_oauth()
|
oauth = self._generate_oauth()
|
||||||
|
|
|
@ -5,7 +5,6 @@ from aiohttp import web
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.network import async_get_source_ip
|
from homeassistant.components.network import async_get_source_ip
|
||||||
from homeassistant.components.network.const import PUBLIC_TARGET_IP
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_ENTITIES,
|
CONF_ENTITIES,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
|
@ -106,7 +105,7 @@ ATTR_EMULATED_HUE_NAME = "emulated_hue_name"
|
||||||
|
|
||||||
async def async_setup(hass, yaml_config):
|
async def async_setup(hass, yaml_config):
|
||||||
"""Activate the emulated_hue component."""
|
"""Activate the emulated_hue component."""
|
||||||
local_ip = await async_get_source_ip(hass, PUBLIC_TARGET_IP)
|
local_ip = await async_get_source_ip(hass)
|
||||||
config = Config(hass, yaml_config.get(DOMAIN, {}), local_ip)
|
config = Config(hass, yaml_config.get(DOMAIN, {}), local_ip)
|
||||||
await config.async_setup()
|
await config.async_setup()
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.network import async_get_source_ip
|
from homeassistant.components.network import async_get_source_ip
|
||||||
from homeassistant.components.network.const import PUBLIC_TARGET_IP
|
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
|
@ -71,9 +70,7 @@ async def async_setup_entry(hass, config_entry):
|
||||||
|
|
||||||
name = config[CONF_NAME]
|
name = config[CONF_NAME]
|
||||||
listen_port = config[CONF_LISTEN_PORT]
|
listen_port = config[CONF_LISTEN_PORT]
|
||||||
host_ip = config.get(CONF_HOST_IP) or await async_get_source_ip(
|
host_ip = config.get(CONF_HOST_IP) or await async_get_source_ip(hass)
|
||||||
hass, PUBLIC_TARGET_IP
|
|
||||||
)
|
|
||||||
advertise_ip = config.get(CONF_ADVERTISE_IP)
|
advertise_ip = config.get(CONF_ADVERTISE_IP)
|
||||||
advertise_port = config.get(CONF_ADVERTISE_PORT)
|
advertise_port = config.get(CONF_ADVERTISE_PORT)
|
||||||
upnp_bind_multicast = config.get(CONF_UPNP_BIND_MULTICAST)
|
upnp_bind_multicast = config.get(CONF_UPNP_BIND_MULTICAST)
|
||||||
|
|
|
@ -18,6 +18,7 @@ from homeassistant.components.binary_sensor import (
|
||||||
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.components.humidifier import DOMAIN as HUMIDIFIER_DOMAIN
|
from homeassistant.components.humidifier import DOMAIN as HUMIDIFIER_DOMAIN
|
||||||
|
from homeassistant.components.network.const import MDNS_TARGET_IP
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -119,8 +120,6 @@ STATUS_WAIT = 3
|
||||||
|
|
||||||
PORT_CLEANUP_CHECK_INTERVAL_SECS = 1
|
PORT_CLEANUP_CHECK_INTERVAL_SECS = 1
|
||||||
|
|
||||||
MDNS_TARGET_IP = "224.0.0.251"
|
|
||||||
|
|
||||||
_HOMEKIT_CONFIG_UPDATE_TIME = (
|
_HOMEKIT_CONFIG_UPDATE_TIME = (
|
||||||
5 # number of seconds to wait for homekit to see the c# change
|
5 # number of seconds to wait for homekit to see the c# change
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,6 +13,7 @@ from aiohttp.typedefs import StrOrURL
|
||||||
from aiohttp.web_exceptions import HTTPMovedPermanently, HTTPRedirection
|
from aiohttp.web_exceptions import HTTPMovedPermanently, HTTPRedirection
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.network import async_get_source_ip
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, SERVER_PORT
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, SERVER_PORT
|
||||||
from homeassistant.core import Event, HomeAssistant
|
from homeassistant.core import Event, HomeAssistant
|
||||||
from homeassistant.helpers import storage
|
from homeassistant.helpers import storage
|
||||||
|
@ -20,7 +21,6 @@ import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.setup import async_start_setup, async_when_setup_or_start
|
from homeassistant.setup import async_start_setup, async_when_setup_or_start
|
||||||
import homeassistant.util as hass_util
|
|
||||||
from homeassistant.util import ssl as ssl_util
|
from homeassistant.util import ssl as ssl_util
|
||||||
|
|
||||||
from .auth import setup_auth
|
from .auth import setup_auth
|
||||||
|
@ -190,7 +190,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
|
||||||
hass.http = server
|
hass.http = server
|
||||||
|
|
||||||
local_ip = await hass.async_add_executor_job(hass_util.get_local_ip)
|
local_ip = await async_get_source_ip(hass)
|
||||||
|
|
||||||
host = local_ip
|
host = local_ip
|
||||||
if server_host is not None:
|
if server_host is not None:
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Sensor platform for local_ip."""
|
"""Sensor platform for local_ip."""
|
||||||
|
|
||||||
from homeassistant.components.network import async_get_source_ip
|
from homeassistant.components.network import async_get_source_ip
|
||||||
from homeassistant.components.network.const import PUBLIC_TARGET_IP
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import SensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
|
@ -33,6 +32,4 @@ class IPSensor(SensorEntity):
|
||||||
|
|
||||||
async def async_update(self) -> None:
|
async def async_update(self) -> None:
|
||||||
"""Fetch new state data for the sensor."""
|
"""Fetch new state data for the sensor."""
|
||||||
self._attr_native_value = await async_get_source_ip(
|
self._attr_native_value = await async_get_source_ip(self.hass)
|
||||||
self.hass, target_ip=PUBLIC_TARGET_IP
|
|
||||||
)
|
|
||||||
|
|
|
@ -2,30 +2,28 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from ipaddress import IPv4Address, IPv6Address, ip_interface
|
from ipaddress import IPv4Address, IPv6Address, ip_interface
|
||||||
import logging
|
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
|
|
||||||
from . import util
|
from . import util
|
||||||
from .const import DOMAIN, IPV4_BROADCAST_ADDR
|
from .const import IPV4_BROADCAST_ADDR, PUBLIC_TARGET_IP
|
||||||
from .models import Adapter
|
from .models import Adapter
|
||||||
from .network import Network
|
from .network import Network, async_get_network
|
||||||
|
|
||||||
ZEROCONF_DOMAIN = "zeroconf" # cannot import from zeroconf due to circular dep
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
async def async_get_adapters(hass: HomeAssistant) -> list[Adapter]:
|
async def async_get_adapters(hass: HomeAssistant) -> list[Adapter]:
|
||||||
"""Get the network adapter configuration."""
|
"""Get the network adapter configuration."""
|
||||||
network: Network = hass.data[DOMAIN]
|
network: Network = await async_get_network(hass)
|
||||||
return network.adapters
|
return network.adapters
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
async def async_get_source_ip(hass: HomeAssistant, target_ip: str) -> str:
|
async def async_get_source_ip(
|
||||||
|
hass: HomeAssistant, target_ip: str = PUBLIC_TARGET_IP
|
||||||
|
) -> str:
|
||||||
"""Get the source ip for a target ip."""
|
"""Get the source ip for a target ip."""
|
||||||
adapters = await async_get_adapters(hass)
|
adapters = await async_get_adapters(hass)
|
||||||
all_ipv4s = []
|
all_ipv4s = []
|
||||||
|
@ -88,20 +86,10 @@ async def async_get_ipv4_broadcast_addresses(hass: HomeAssistant) -> set[IPv4Add
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up network for Home Assistant."""
|
"""Set up network for Home Assistant."""
|
||||||
|
|
||||||
hass.data[DOMAIN] = network = Network(hass)
|
|
||||||
await network.async_setup()
|
|
||||||
if ZEROCONF_DOMAIN in config:
|
|
||||||
await network.async_migrate_from_zeroconf(config[ZEROCONF_DOMAIN])
|
|
||||||
network.async_configure()
|
|
||||||
|
|
||||||
_LOGGER.debug("Adapters: %s", network.adapters)
|
|
||||||
|
|
||||||
# Avoid circular issue: http->network->websocket_api->http
|
# Avoid circular issue: http->network->websocket_api->http
|
||||||
from .websocket import ( # pylint: disable=import-outside-toplevel
|
from .websocket import ( # pylint: disable=import-outside-toplevel
|
||||||
async_register_websocket_commands,
|
async_register_websocket_commands,
|
||||||
)
|
)
|
||||||
|
|
||||||
async_register_websocket_commands(hass)
|
async_register_websocket_commands(hass)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -11,6 +11,8 @@ DOMAIN: Final = "network"
|
||||||
STORAGE_KEY: Final = "core.network"
|
STORAGE_KEY: Final = "core.network"
|
||||||
STORAGE_VERSION: Final = 1
|
STORAGE_VERSION: Final = 1
|
||||||
|
|
||||||
|
DATA_NETWORK: Final = "network"
|
||||||
|
|
||||||
ATTR_ADAPTERS: Final = "adapters"
|
ATTR_ADAPTERS: Final = "adapters"
|
||||||
ATTR_CONFIGURED_ADAPTERS: Final = "configured_adapters"
|
ATTR_CONFIGURED_ADAPTERS: Final = "configured_adapters"
|
||||||
DEFAULT_CONFIGURED_ADAPTERS: list[str] = []
|
DEFAULT_CONFIGURED_ADAPTERS: list[str] = []
|
||||||
|
|
|
@ -1,23 +1,35 @@
|
||||||
"""Network helper class for the network integration."""
|
"""Network helper class for the network integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.singleton import singleton
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_CONFIGURED_ADAPTERS,
|
ATTR_CONFIGURED_ADAPTERS,
|
||||||
|
DATA_NETWORK,
|
||||||
DEFAULT_CONFIGURED_ADAPTERS,
|
DEFAULT_CONFIGURED_ADAPTERS,
|
||||||
STORAGE_KEY,
|
STORAGE_KEY,
|
||||||
STORAGE_VERSION,
|
STORAGE_VERSION,
|
||||||
)
|
)
|
||||||
from .models import Adapter
|
from .models import Adapter
|
||||||
from .util import (
|
from .util import async_load_adapters, enable_adapters, enable_auto_detected_adapters
|
||||||
adapters_with_exernal_addresses,
|
|
||||||
async_load_adapters,
|
_LOGGER = logging.getLogger(__name__)
|
||||||
enable_adapters,
|
|
||||||
enable_auto_detected_adapters,
|
|
||||||
)
|
@singleton(DATA_NETWORK)
|
||||||
|
@callback
|
||||||
|
async def async_get_network(hass: HomeAssistant) -> Network:
|
||||||
|
"""Get network singleton."""
|
||||||
|
network = Network(hass)
|
||||||
|
await network.async_setup()
|
||||||
|
network.async_configure()
|
||||||
|
|
||||||
|
_LOGGER.debug("Adapters: %s", network.adapters)
|
||||||
|
return network
|
||||||
|
|
||||||
|
|
||||||
class Network:
|
class Network:
|
||||||
|
@ -41,21 +53,6 @@ class Network:
|
||||||
await self.async_load()
|
await self.async_load()
|
||||||
self.adapters = await async_load_adapters()
|
self.adapters = await async_load_adapters()
|
||||||
|
|
||||||
async def async_migrate_from_zeroconf(self, zc_config: dict[str, Any]) -> None:
|
|
||||||
"""Migrate configuration from zeroconf."""
|
|
||||||
if self._data or not zc_config:
|
|
||||||
return
|
|
||||||
|
|
||||||
from homeassistant.components.zeroconf import ( # pylint: disable=import-outside-toplevel
|
|
||||||
CONF_DEFAULT_INTERFACE,
|
|
||||||
)
|
|
||||||
|
|
||||||
if zc_config.get(CONF_DEFAULT_INTERFACE) is False:
|
|
||||||
self._data[ATTR_CONFIGURED_ADAPTERS] = adapters_with_exernal_addresses(
|
|
||||||
self.adapters
|
|
||||||
)
|
|
||||||
await self._async_save()
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_configure(self) -> None:
|
def async_configure(self) -> None:
|
||||||
"""Configure from storage."""
|
"""Configure from storage."""
|
||||||
|
|
|
@ -57,15 +57,6 @@ def enable_auto_detected_adapters(adapters: list[Adapter]) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def adapters_with_exernal_addresses(adapters: list[Adapter]) -> list[str]:
|
|
||||||
"""Enable all interfaces with an external address."""
|
|
||||||
return [
|
|
||||||
adapter["name"]
|
|
||||||
for adapter in adapters
|
|
||||||
if _adapter_has_external_address(adapter)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _adapter_has_external_address(adapter: Adapter) -> bool:
|
def _adapter_has_external_address(adapter: Adapter) -> bool:
|
||||||
"""Adapter has a non-loopback and non-link-local address."""
|
"""Adapter has a non-loopback and non-link-local address."""
|
||||||
return any(
|
return any(
|
||||||
|
|
|
@ -7,13 +7,8 @@ from homeassistant.components import websocket_api
|
||||||
from homeassistant.components.websocket_api.connection import ActiveConnection
|
from homeassistant.components.websocket_api.connection import ActiveConnection
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
|
||||||
from .const import (
|
from .const import ATTR_ADAPTERS, ATTR_CONFIGURED_ADAPTERS, NETWORK_CONFIG_SCHEMA
|
||||||
ATTR_ADAPTERS,
|
from .network import async_get_network
|
||||||
ATTR_CONFIGURED_ADAPTERS,
|
|
||||||
DOMAIN,
|
|
||||||
NETWORK_CONFIG_SCHEMA,
|
|
||||||
)
|
|
||||||
from .network import Network
|
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -32,7 +27,7 @@ async def websocket_network_adapters(
|
||||||
msg: dict,
|
msg: dict,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Return network preferences."""
|
"""Return network preferences."""
|
||||||
network: Network = hass.data[DOMAIN]
|
network = await async_get_network(hass)
|
||||||
connection.send_result(
|
connection.send_result(
|
||||||
msg["id"],
|
msg["id"],
|
||||||
{
|
{
|
||||||
|
@ -56,7 +51,7 @@ async def websocket_network_adapters_configure(
|
||||||
msg: dict,
|
msg: dict,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Update network config."""
|
"""Update network config."""
|
||||||
network: Network = hass.data[DOMAIN]
|
network = await async_get_network(hass)
|
||||||
|
|
||||||
await network.async_reconfig(msg["config"])
|
await network.async_reconfig(msg["config"])
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ from zeroconf.asyncio import AsyncServiceInfo
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components import network
|
from homeassistant.components import network
|
||||||
from homeassistant.components.network import async_get_source_ip
|
from homeassistant.components.network import async_get_source_ip
|
||||||
|
from homeassistant.components.network.const import MDNS_TARGET_IP
|
||||||
from homeassistant.components.network.models import Adapter
|
from homeassistant.components.network.models import Adapter
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
|
@ -52,8 +53,6 @@ DEFAULT_IPV6 = True
|
||||||
HOMEKIT_PAIRED_STATUS_FLAG = "sf"
|
HOMEKIT_PAIRED_STATUS_FLAG = "sf"
|
||||||
HOMEKIT_MODEL = "md"
|
HOMEKIT_MODEL = "md"
|
||||||
|
|
||||||
MDNS_TARGET_IP = "224.0.0.251"
|
|
||||||
|
|
||||||
# Property key=value has a max length of 255
|
# Property key=value has a max length of 255
|
||||||
# so we use 230 to leave space for key=
|
# so we use 230 to leave space for key=
|
||||||
MAX_PROPERTY_VALUE_LEN = 230
|
MAX_PROPERTY_VALUE_LEN = 230
|
||||||
|
|
|
@ -5,10 +5,9 @@ import asyncio
|
||||||
from collections.abc import Callable, Coroutine, Iterable, KeysView
|
from collections.abc import Callable, Coroutine, Iterable, KeysView
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import enum
|
import enum
|
||||||
from functools import lru_cache, wraps
|
from functools import wraps
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import socket
|
|
||||||
import string
|
import string
|
||||||
import threading
|
import threading
|
||||||
from types import MappingProxyType
|
from types import MappingProxyType
|
||||||
|
@ -129,26 +128,6 @@ def ensure_unique_string(
|
||||||
return test_string
|
return test_string
|
||||||
|
|
||||||
|
|
||||||
# Taken from: http://stackoverflow.com/a/11735897
|
|
||||||
@lru_cache(maxsize=None)
|
|
||||||
def get_local_ip() -> str:
|
|
||||||
"""Try to determine the local IP address of the machine."""
|
|
||||||
try:
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
|
|
||||||
# Use Google Public DNS server to determine own IP
|
|
||||||
sock.connect(("8.8.8.8", 80))
|
|
||||||
|
|
||||||
return sock.getsockname()[0] # type: ignore
|
|
||||||
except OSError:
|
|
||||||
try:
|
|
||||||
return socket.gethostbyname(socket.gethostname())
|
|
||||||
except socket.gaierror:
|
|
||||||
return "127.0.0.1"
|
|
||||||
finally:
|
|
||||||
sock.close()
|
|
||||||
|
|
||||||
|
|
||||||
# Taken from http://stackoverflow.com/a/23728630
|
# Taken from http://stackoverflow.com/a/23728630
|
||||||
def get_random_string(length: int = 10) -> str:
|
def get_random_string(length: int = 10) -> str:
|
||||||
"""Return a random string with letters and digits."""
|
"""Return a random string with letters and digits."""
|
||||||
|
|
|
@ -12,6 +12,7 @@ bcrypt==3.1.7
|
||||||
certifi>=2021.5.30
|
certifi>=2021.5.30
|
||||||
ciso8601==2.2.0
|
ciso8601==2.2.0
|
||||||
httpx==0.19.0
|
httpx==0.19.0
|
||||||
|
ifaddr==0.1.7
|
||||||
jinja2==3.0.3
|
jinja2==3.0.3
|
||||||
PyJWT==2.1.0
|
PyJWT==2.1.0
|
||||||
cryptography==35.0.0
|
cryptography==35.0.0
|
||||||
|
|
|
@ -134,8 +134,8 @@ IGNORE_VIOLATIONS = {
|
||||||
# Demo
|
# Demo
|
||||||
("demo", "manual"),
|
("demo", "manual"),
|
||||||
("demo", "openalpr_local"),
|
("demo", "openalpr_local"),
|
||||||
# Migration of settings from zeroconf to network
|
# This would be a circular dep
|
||||||
("network", "zeroconf"),
|
("http", "network"),
|
||||||
# This should become a helper method that integrations can submit data to
|
# This should become a helper method that integrations can submit data to
|
||||||
("websocket_api", "lovelace"),
|
("websocket_api", "lovelace"),
|
||||||
("websocket_api", "shopping_list"),
|
("websocket_api", "shopping_list"),
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -43,6 +43,7 @@ REQUIRES = [
|
||||||
"certifi>=2021.5.30",
|
"certifi>=2021.5.30",
|
||||||
"ciso8601==2.2.0",
|
"ciso8601==2.2.0",
|
||||||
"httpx==0.19.0",
|
"httpx==0.19.0",
|
||||||
|
"ifaddr==0.1.7",
|
||||||
"jinja2==3.0.3",
|
"jinja2==3.0.3",
|
||||||
"PyJWT==2.1.0",
|
"PyJWT==2.1.0",
|
||||||
# PyJWT has loose dependency. We want the latest one.
|
# PyJWT has loose dependency. We want the latest one.
|
||||||
|
|
|
@ -179,7 +179,10 @@ async def setup_mock_motioneye_config_entry(
|
||||||
|
|
||||||
await async_process_ha_core_config(
|
await async_process_ha_core_config(
|
||||||
hass,
|
hass,
|
||||||
{"external_url": "https://example.com"},
|
{
|
||||||
|
"internal_url": "https://internal.url",
|
||||||
|
"external_url": "https://external.url",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
config_entry = config_entry or create_mock_motioneye_config_entry(hass)
|
config_entry = config_entry or create_mock_motioneye_config_entry(hass)
|
||||||
|
|
|
@ -77,7 +77,7 @@ async def test_setup_camera_without_webhook(hass: HomeAssistant) -> None:
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_ENABLED] = True
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_ENABLED] = True
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_URL] = (
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_URL] = (
|
||||||
"https://example.com"
|
"https://internal.url"
|
||||||
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
||||||
+ f"?{WEB_HOOK_MOTION_DETECTED_QUERY_STRING}&device_id={device.id}"
|
+ f"?{WEB_HOOK_MOTION_DETECTED_QUERY_STRING}&device_id={device.id}"
|
||||||
)
|
)
|
||||||
|
@ -85,7 +85,7 @@ async def test_setup_camera_without_webhook(hass: HomeAssistant) -> None:
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_ENABLED] = True
|
expected_camera[KEY_WEB_HOOK_STORAGE_ENABLED] = True
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
expected_camera[KEY_WEB_HOOK_STORAGE_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_URL] = (
|
expected_camera[KEY_WEB_HOOK_STORAGE_URL] = (
|
||||||
"https://example.com"
|
"https://internal.url"
|
||||||
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
||||||
+ f"?{WEB_HOOK_FILE_STORED_QUERY_STRING}&device_id={device.id}"
|
+ f"?{WEB_HOOK_FILE_STORED_QUERY_STRING}&device_id={device.id}"
|
||||||
)
|
)
|
||||||
|
@ -132,7 +132,7 @@ async def test_setup_camera_with_wrong_webhook(
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_ENABLED] = True
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_ENABLED] = True
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_URL] = (
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_URL] = (
|
||||||
"https://example.com"
|
"https://internal.url"
|
||||||
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
||||||
+ f"?{WEB_HOOK_MOTION_DETECTED_QUERY_STRING}&device_id={device.id}"
|
+ f"?{WEB_HOOK_MOTION_DETECTED_QUERY_STRING}&device_id={device.id}"
|
||||||
)
|
)
|
||||||
|
@ -140,7 +140,7 @@ async def test_setup_camera_with_wrong_webhook(
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_ENABLED] = True
|
expected_camera[KEY_WEB_HOOK_STORAGE_ENABLED] = True
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
expected_camera[KEY_WEB_HOOK_STORAGE_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_URL] = (
|
expected_camera[KEY_WEB_HOOK_STORAGE_URL] = (
|
||||||
"https://example.com"
|
"https://internal.url"
|
||||||
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
||||||
+ f"?{WEB_HOOK_FILE_STORED_QUERY_STRING}&device_id={device.id}"
|
+ f"?{WEB_HOOK_FILE_STORED_QUERY_STRING}&device_id={device.id}"
|
||||||
)
|
)
|
||||||
|
@ -185,7 +185,7 @@ async def test_setup_camera_with_old_webhook(
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_ENABLED] = True
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_ENABLED] = True
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
||||||
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_URL] = (
|
expected_camera[KEY_WEB_HOOK_NOTIFICATIONS_URL] = (
|
||||||
"https://example.com"
|
"https://internal.url"
|
||||||
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
||||||
+ f"?{WEB_HOOK_MOTION_DETECTED_QUERY_STRING}&device_id={device.id}"
|
+ f"?{WEB_HOOK_MOTION_DETECTED_QUERY_STRING}&device_id={device.id}"
|
||||||
)
|
)
|
||||||
|
@ -193,7 +193,7 @@ async def test_setup_camera_with_old_webhook(
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_ENABLED] = True
|
expected_camera[KEY_WEB_HOOK_STORAGE_ENABLED] = True
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
expected_camera[KEY_WEB_HOOK_STORAGE_HTTP_METHOD] = KEY_HTTP_METHOD_POST_JSON
|
||||||
expected_camera[KEY_WEB_HOOK_STORAGE_URL] = (
|
expected_camera[KEY_WEB_HOOK_STORAGE_URL] = (
|
||||||
"https://example.com"
|
"https://internal.url"
|
||||||
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
||||||
+ f"?{WEB_HOOK_FILE_STORED_QUERY_STRING}&device_id={device.id}"
|
+ f"?{WEB_HOOK_FILE_STORED_QUERY_STRING}&device_id={device.id}"
|
||||||
)
|
)
|
||||||
|
@ -223,7 +223,7 @@ async def test_setup_camera_with_correct_webhook(
|
||||||
KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD
|
KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD
|
||||||
] = KEY_HTTP_METHOD_POST_JSON
|
] = KEY_HTTP_METHOD_POST_JSON
|
||||||
cameras[KEY_CAMERAS][0][KEY_WEB_HOOK_NOTIFICATIONS_URL] = (
|
cameras[KEY_CAMERAS][0][KEY_WEB_HOOK_NOTIFICATIONS_URL] = (
|
||||||
"https://example.com"
|
"https://internal.url"
|
||||||
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
||||||
+ f"?{WEB_HOOK_MOTION_DETECTED_QUERY_STRING}&device_id={device.id}"
|
+ f"?{WEB_HOOK_MOTION_DETECTED_QUERY_STRING}&device_id={device.id}"
|
||||||
)
|
)
|
||||||
|
@ -232,7 +232,7 @@ async def test_setup_camera_with_correct_webhook(
|
||||||
KEY_WEB_HOOK_STORAGE_HTTP_METHOD
|
KEY_WEB_HOOK_STORAGE_HTTP_METHOD
|
||||||
] = KEY_HTTP_METHOD_POST_JSON
|
] = KEY_HTTP_METHOD_POST_JSON
|
||||||
cameras[KEY_CAMERAS][0][KEY_WEB_HOOK_STORAGE_URL] = (
|
cameras[KEY_CAMERAS][0][KEY_WEB_HOOK_STORAGE_URL] = (
|
||||||
"https://example.com"
|
"https://internal.url"
|
||||||
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
+ URL_WEBHOOK_PATH.format(webhook_id=config_entry.data[CONF_WEBHOOK_ID])
|
||||||
+ f"?{WEB_HOOK_FILE_STORED_QUERY_STRING}&device_id={device.id}"
|
+ f"?{WEB_HOOK_FILE_STORED_QUERY_STRING}&device_id={device.id}"
|
||||||
)
|
)
|
||||||
|
|
9
tests/components/network/conftest.py
Normal file
9
tests/components/network/conftest.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
"""Tests for the Network Configuration integration."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def mock_get_source_ip():
|
||||||
|
"""Override mock of network util's async_get_source_ip."""
|
||||||
|
return
|
|
@ -8,6 +8,7 @@ from homeassistant.components import network
|
||||||
from homeassistant.components.network.const import (
|
from homeassistant.components.network.const import (
|
||||||
ATTR_ADAPTERS,
|
ATTR_ADAPTERS,
|
||||||
ATTR_CONFIGURED_ADAPTERS,
|
ATTR_CONFIGURED_ADAPTERS,
|
||||||
|
DOMAIN,
|
||||||
MDNS_TARGET_IP,
|
MDNS_TARGET_IP,
|
||||||
STORAGE_KEY,
|
STORAGE_KEY,
|
||||||
STORAGE_VERSION,
|
STORAGE_VERSION,
|
||||||
|
@ -59,10 +60,10 @@ async def test_async_detect_interfaces_setting_non_loopback_route(hass, hass_sto
|
||||||
"homeassistant.components.network.util.ifaddr.get_adapters",
|
"homeassistant.components.network.util.ifaddr.get_adapters",
|
||||||
return_value=_generate_mock_adapters(),
|
return_value=_generate_mock_adapters(),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
network_obj = hass.data[network.DOMAIN]
|
network_obj = hass.data[DOMAIN]
|
||||||
assert network_obj.configured_adapters == []
|
assert network_obj.configured_adapters == []
|
||||||
|
|
||||||
assert network_obj.adapters == [
|
assert network_obj.adapters == [
|
||||||
|
@ -121,10 +122,10 @@ async def test_async_detect_interfaces_setting_loopback_route(hass, hass_storage
|
||||||
"homeassistant.components.network.util.ifaddr.get_adapters",
|
"homeassistant.components.network.util.ifaddr.get_adapters",
|
||||||
return_value=_generate_mock_adapters(),
|
return_value=_generate_mock_adapters(),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
network_obj = hass.data[network.DOMAIN]
|
network_obj = hass.data[DOMAIN]
|
||||||
assert network_obj.configured_adapters == []
|
assert network_obj.configured_adapters == []
|
||||||
assert network_obj.adapters == [
|
assert network_obj.adapters == [
|
||||||
{
|
{
|
||||||
|
@ -182,10 +183,10 @@ async def test_async_detect_interfaces_setting_empty_route(hass, hass_storage):
|
||||||
"homeassistant.components.network.util.ifaddr.get_adapters",
|
"homeassistant.components.network.util.ifaddr.get_adapters",
|
||||||
return_value=_generate_mock_adapters(),
|
return_value=_generate_mock_adapters(),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
network_obj = hass.data[network.DOMAIN]
|
network_obj = hass.data[DOMAIN]
|
||||||
assert network_obj.configured_adapters == []
|
assert network_obj.configured_adapters == []
|
||||||
assert network_obj.adapters == [
|
assert network_obj.adapters == [
|
||||||
{
|
{
|
||||||
|
@ -243,10 +244,10 @@ async def test_async_detect_interfaces_setting_exception(hass, hass_storage):
|
||||||
"homeassistant.components.network.util.ifaddr.get_adapters",
|
"homeassistant.components.network.util.ifaddr.get_adapters",
|
||||||
return_value=_generate_mock_adapters(),
|
return_value=_generate_mock_adapters(),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
network_obj = hass.data[network.DOMAIN]
|
network_obj = hass.data[DOMAIN]
|
||||||
assert network_obj.configured_adapters == []
|
assert network_obj.configured_adapters == []
|
||||||
assert network_obj.adapters == [
|
assert network_obj.adapters == [
|
||||||
{
|
{
|
||||||
|
@ -309,10 +310,10 @@ async def test_interfaces_configured_from_storage(hass, hass_storage):
|
||||||
"homeassistant.components.network.util.ifaddr.get_adapters",
|
"homeassistant.components.network.util.ifaddr.get_adapters",
|
||||||
return_value=_generate_mock_adapters(),
|
return_value=_generate_mock_adapters(),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
network_obj = hass.data[network.DOMAIN]
|
network_obj = hass.data[DOMAIN]
|
||||||
assert network_obj.configured_adapters == ["eth0", "eth1", "vtun0"]
|
assert network_obj.configured_adapters == ["eth0", "eth1", "vtun0"]
|
||||||
|
|
||||||
assert network_obj.adapters == [
|
assert network_obj.adapters == [
|
||||||
|
@ -378,10 +379,10 @@ async def test_interfaces_configured_from_storage_websocket_update(
|
||||||
"homeassistant.components.network.util.ifaddr.get_adapters",
|
"homeassistant.components.network.util.ifaddr.get_adapters",
|
||||||
return_value=_generate_mock_adapters(),
|
return_value=_generate_mock_adapters(),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
network_obj = hass.data[network.DOMAIN]
|
network_obj = hass.data[DOMAIN]
|
||||||
assert network_obj.configured_adapters == ["eth0", "eth1", "vtun0"]
|
assert network_obj.configured_adapters == ["eth0", "eth1", "vtun0"]
|
||||||
ws_client = await hass_ws_client(hass)
|
ws_client = await hass_ws_client(hass)
|
||||||
await ws_client.send_json({"id": 1, "type": "network"})
|
await ws_client.send_json({"id": 1, "type": "network"})
|
||||||
|
@ -507,7 +508,7 @@ async def test_async_get_source_ip_matching_interface(hass, hass_storage):
|
||||||
"homeassistant.components.network.util.socket.socket",
|
"homeassistant.components.network.util.socket.socket",
|
||||||
return_value=_mock_socket(["192.168.1.5"]),
|
return_value=_mock_socket(["192.168.1.5"]),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5"
|
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5"
|
||||||
|
@ -528,7 +529,7 @@ async def test_async_get_source_ip_interface_not_match(hass, hass_storage):
|
||||||
"homeassistant.components.network.util.socket.socket",
|
"homeassistant.components.network.util.socket.socket",
|
||||||
return_value=_mock_socket(["192.168.1.5"]),
|
return_value=_mock_socket(["192.168.1.5"]),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "169.254.3.2"
|
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "169.254.3.2"
|
||||||
|
@ -549,7 +550,7 @@ async def test_async_get_source_ip_cannot_determine_target(hass, hass_storage):
|
||||||
"homeassistant.components.network.util.socket.socket",
|
"homeassistant.components.network.util.socket.socket",
|
||||||
return_value=_mock_socket([None]),
|
return_value=_mock_socket([None]),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5"
|
assert await network.async_get_source_ip(hass, MDNS_TARGET_IP) == "192.168.1.5"
|
||||||
|
@ -570,7 +571,7 @@ async def test_async_get_ipv4_broadcast_addresses_default(hass, hass_storage):
|
||||||
"homeassistant.components.network.util.ifaddr.get_adapters",
|
"homeassistant.components.network.util.ifaddr.get_adapters",
|
||||||
return_value=_generate_mock_adapters(),
|
return_value=_generate_mock_adapters(),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert await network.async_get_ipv4_broadcast_addresses(hass) == {
|
assert await network.async_get_ipv4_broadcast_addresses(hass) == {
|
||||||
|
@ -593,7 +594,7 @@ async def test_async_get_ipv4_broadcast_addresses_multiple(hass, hass_storage):
|
||||||
"homeassistant.components.network.util.ifaddr.get_adapters",
|
"homeassistant.components.network.util.ifaddr.get_adapters",
|
||||||
return_value=_generate_mock_adapters(),
|
return_value=_generate_mock_adapters(),
|
||||||
):
|
):
|
||||||
assert await async_setup_component(hass, network.DOMAIN, {network.DOMAIN: {}})
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert await network.async_get_ipv4_broadcast_addresses(hass) == {
|
assert await network.async_get_ipv4_broadcast_addresses(hass) == {
|
||||||
|
|
|
@ -598,7 +598,7 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config):
|
||||||
return component
|
return component
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture(autouse=True)
|
||||||
def mock_get_source_ip():
|
def mock_get_source_ip():
|
||||||
"""Mock network util's async_get_source_ip."""
|
"""Mock network util's async_get_source_ip."""
|
||||||
with patch(
|
with patch(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue