Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
067b188
Add get_url helper, deprecate base_url
frenck May 5, 2020
d774049
Fix tests
frenck May 5, 2020
5ba6600
Return value is not Optional actually
frenck May 5, 2020
5fda03e
Add normalize_url util
frenck May 5, 2020
9820729
Fix broad exception checking in tests
frenck May 5, 2020
1a9f89d
Migrate cast to get_url helper
frenck May 5, 2020
6048e70
Migrate doorbird to get_url helper
frenck May 5, 2020
4499eca
Accept optional string on is_ip_address util
frenck May 6, 2020
82f23cc
Adjust helper strategy
frenck May 6, 2020
f51913d
Migrate almond to get_url helper
frenck May 6, 2020
3c57db9
Fix Home Assistant Cast tests
frenck May 6, 2020
4f6d01f
Migrate alexa to get_url helper
frenck May 6, 2020
6676d04
Migrate webhook to get_url helper
frenck May 6, 2020
33811e7
Migrate browser.open in main to get_url
frenck May 6, 2020
ff78396
Migrate OAuth2 config entry flow to get_url
frenck May 6, 2020
1b6950b
Migrate ambiclimate to get_url helper
frenck May 6, 2020
8c8a84f
Fix Withings tests
frenck May 6, 2020
d4ee5d9
Migrate fitbit to get_url helper
frenck May 6, 2020
bafbfc6
Migrate telegram_bot to get_url helper
frenck May 6, 2020
cbd6e3f
Migrate wink to get_url helper
frenck May 6, 2020
ebd7227
Migrate smartthings to get_url helper
frenck May 6, 2020
365258b
Migrate media_player to get_url helper
frenck May 6, 2020
bb671d7
Migrate plex to get_url helper
frenck May 6, 2020
7c991fd
Migrate zeroconf service info
frenck May 6, 2020
591b32c
Migrate konnected to get_url helper
frenck May 6, 2020
4b87f78
Migrate camera to get_url helper
frenck May 6, 2020
4f3a937
Adjust alexa get_url params based on feedback
frenck May 6, 2020
63520a5
Migrate google_assistant to get_url helper
frenck May 6, 2020
b7dd938
Migrate tts to get_url helper
frenck May 6, 2020
0549d66
Fix plex tests after rebase
frenck May 6, 2020
0dec544
Fix google_translate TTS tests
frenck May 6, 2020
8f0ec6b
Fix marytts TTS tests
frenck May 6, 2020
5a63f6c
Fix voicerrs TTS tests
frenck May 6, 2020
be86e30
Fix yandextts TTS tests
frenck May 6, 2020
447e0c4
Fix tts tests
frenck May 6, 2020
af91fb2
Remove SSL requirement from camera integration
frenck May 7, 2020
8cd58b3
Remove camera stream SSL requirement from google_assistant integration
frenck May 7, 2020
d948b53
Always use 127.0.0.1 for main browser.open
frenck May 7, 2020
4061968
Add local_ip to http ApiConfig
frenck May 7, 2020
8cbe55c
Raise exception in Alexa if no suitable URL found
frenck May 7, 2020
5227ef9
Remove Optional parameter from is_ip_address
frenck May 7, 2020
6d1ffe8
Migrate base_url to core on first load
frenck May 7, 2020
122378c
Test migration base_url -> internal/external
balloob May 7, 2020
e991cc7
Better test name
balloob May 7, 2020
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
13 changes: 11 additions & 2 deletions homeassistant/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import threading
from typing import List

import yarl

from homeassistant.const import REQUIRED_PYTHON_VER, RESTART_EXIT_CODE, __version__


Expand Down Expand Up @@ -256,10 +258,17 @@ async def setup_and_run_hass(config_dir: str, args: argparse.Namespace) -> int:
if hass is None:
return 1

if args.open_ui and hass.config.api is not None:
if args.open_ui:
import webbrowser # pylint: disable=import-outside-toplevel

hass.add_job(webbrowser.open, hass.config.api.base_url)
if hass.config.api is not None:
scheme = "https" if hass.config.api.use_ssl else "http"
url = str(
yarl.URL.build(
scheme=scheme, host="127.0.0.1", port=hass.config.api.port
)
)
hass.add_job(webbrowser.open, url)

return await hass.async_run()

Expand Down
12 changes: 9 additions & 3 deletions homeassistant/components/alexa/entities.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Alexa entity adapters."""
import logging
from typing import List
from urllib.parse import urlparse

from homeassistant.components import (
alarm_control_panel,
Expand Down Expand Up @@ -799,8 +798,15 @@ def _check_requirements(self):
)
return False

url = urlparse(network.async_get_external_url(self.hass))
if url.scheme != "https":
try:
network.async_get_url(
self.hass,
allow_internal=False,
allow_ip=False,
require_ssl=True,
require_standard_port=True,
)
except network.NoURLAvailableError:
_LOGGER.debug(
"%s requires HTTPS for AlexaCameraStreamController", self.entity_id
)
Expand Down
13 changes: 12 additions & 1 deletion homeassistant/components/alexa/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1533,7 +1533,18 @@ async def async_api_initialize_camera_stream(hass, config, directive, context):
entity = directive.entity
stream_source = await camera.async_request_stream(hass, entity.entity_id, fmt="hls")
camera_image = hass.states.get(entity.entity_id).attributes["entity_picture"]
external_url = network.async_get_external_url(hass)

try:
external_url = network.async_get_url(
hass,
allow_internal=False,
allow_ip=False,
require_ssl=True,
require_standard_port=True,
)
except network.NoURLAvailableError:
raise AlexaInvalidValueError("Failed to find suitable URL to serve to Alexa")

payload = {
"cameraStreams": [
{
Expand Down
21 changes: 11 additions & 10 deletions homeassistant/components/almond/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,17 @@ async def _configure_almond_for_ha(
hass: HomeAssistant, entry: config_entries.ConfigEntry, api: WebAlmondAPI
):
"""Configure Almond to connect to HA."""

if entry.data["type"] == TYPE_OAUTH2:
# If we're connecting over OAuth2, we will only set up connection
# with Home Assistant if we're remotely accessible.
hass_url = network.async_get_external_url(hass)
else:
hass_url = hass.config.api.base_url

# If hass_url is None, we're not going to configure Almond to connect to HA.
if hass_url is None:
try:
if entry.data["type"] == TYPE_OAUTH2:
# If we're connecting over OAuth2, we will only set up connection
# with Home Assistant if we're remotely accessible.
hass_url = network.async_get_url(
hass, allow_internal=False, prefer_cloud=True
)
else:
hass_url = network.async_get_url(hass)
except network.NoURLAvailableError:
# If no URL is available, we're not going to configure Almond to connect to HA.
return

_LOGGER.debug("Configuring Almond to connect to Home Assistant at %s", hass_url)
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/ambiclimate/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from homeassistant.components.http import HomeAssistantView
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.network import async_get_url

from .const import (
AUTH_CALLBACK_NAME,
Expand Down Expand Up @@ -122,16 +123,15 @@ def _generate_oauth(self):
clientsession = async_get_clientsession(self.hass)
callback_url = self._cb_url()

oauth = ambiclimate.AmbiclimateOAuth(
return ambiclimate.AmbiclimateOAuth(
config.get(CONF_CLIENT_ID),
config.get(CONF_CLIENT_SECRET),
callback_url,
clientsession,
)
return oauth

def _cb_url(self):
return f"{self.hass.config.api.base_url}{AUTH_CALLBACK_PATH}"
return f"{async_get_url(self.hass)}{AUTH_CALLBACK_PATH}"

async def _get_authorize_url(self):
oauth = self._generate_oauth()
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/camera/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.network import async_get_url
from homeassistant.loader import bind_hass
from homeassistant.setup import async_when_setup

Expand Down Expand Up @@ -684,7 +685,7 @@ async def async_handle_play_stream_service(camera, service_call):
)
data = {
ATTR_ENTITY_ID: entity_ids,
ATTR_MEDIA_CONTENT_ID: f"{hass.config.api.base_url}{url}",
ATTR_MEDIA_CONTENT_ID: f"{async_get_url(hass)}{url}",
ATTR_MEDIA_CONTENT_TYPE: FORMAT_CONTENT_TYPE[fmt],
}

Expand Down
11 changes: 2 additions & 9 deletions homeassistant/components/cast/home_assistant_cast.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from homeassistant import auth, config_entries, core
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.helpers import config_validation as cv, dispatcher
from homeassistant.helpers.network import async_get_url

from .const import DOMAIN, SIGNAL_HASS_CAST_SHOW_VIEW

Expand Down Expand Up @@ -40,15 +41,7 @@ async def async_setup_ha_cast(

async def handle_show_view(call: core.ServiceCall):
"""Handle a Show View service call."""
hass_url = hass.config.api.base_url

# Home Assistant Cast only works with https urls. If user has no configured
# base url, use their remote url.
if not hass_url.lower().startswith("https://"):
try:
hass_url = hass.components.cloud.async_remote_ui_url()
except hass.components.cloud.CloudNotAvailable:
pass
hass_url = async_get_url(hass, require_ssl=True)

controller = HomeAssistantController(
# If you are developing Home Assistant Cast, uncomment and set to your dev app id.
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/config/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ async def post(self, request):
vol.Optional("unit_system"): cv.unit_system,
vol.Optional("location_name"): str,
vol.Optional("time_zone"): cv.time_zone,
vol.Optional("external_url"): vol.Any(cv.url, None),
vol.Optional("internal_url"): vol.Any(cv.url, None),
}
)
async def websocket_update_config(hass, connection, msg):
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/doorbird/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.network import async_get_url
from homeassistant.util import dt as dt_util, slugify

from .const import CONF_EVENTS, DOMAIN, DOOR_STATION, DOOR_STATION_INFO, PLATFORMS
Expand Down Expand Up @@ -252,7 +253,7 @@ def token(self):
def register_events(self, hass):
"""Register events on device."""
# Get the URL of this server
hass_url = hass.config.api.base_url
hass_url = async_get_url(hass)

# Override url if another is specified in the configuration
if self.custom_url is not None:
Expand Down
9 changes: 5 additions & 4 deletions homeassistant/components/fitbit/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.icon import icon_for_battery_level
from homeassistant.helpers.network import async_get_url
from homeassistant.util.json import load_json, save_json

_CONFIGURING = {}
Expand Down Expand Up @@ -180,7 +181,7 @@ def fitbit_configuration_callback(callback_data):
else:
setup_platform(hass, config, add_entities, discovery_info)

start_url = f"{hass.config.api.base_url}{FITBIT_AUTH_CALLBACK_PATH}"
start_url = f"{async_get_url(hass)}{FITBIT_AUTH_CALLBACK_PATH}"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not async context here. Looks like the whole platform is sync.


description = f"""Please create a Fitbit developer app at
https://dev.fitbit.com/apps/new.
Expand Down Expand Up @@ -215,7 +216,7 @@ def request_oauth_completion(hass):
def fitbit_configuration_callback(callback_data):
"""Handle configuration updates."""

start_url = f"{hass.config.api.base_url}{FITBIT_AUTH_START}"
start_url = f"{async_get_url(hass)}{FITBIT_AUTH_START}"

description = f"Please authorize Fitbit by visiting {start_url}"

Expand Down Expand Up @@ -307,7 +308,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
config_file.get(ATTR_CLIENT_ID), config_file.get(ATTR_CLIENT_SECRET)
)

redirect_uri = f"{hass.config.api.base_url}{FITBIT_AUTH_CALLBACK_PATH}"
redirect_uri = f"{async_get_url(hass)}{FITBIT_AUTH_CALLBACK_PATH}"

fitbit_auth_start_url, _ = oauth.authorize_token_url(
redirect_uri=redirect_uri,
Expand Down Expand Up @@ -352,7 +353,7 @@ def get(self, request):

result = None
if data.get("code") is not None:
redirect_uri = f"{hass.config.api.base_url}{FITBIT_AUTH_CALLBACK_PATH}"
redirect_uri = f"{async_get_url(hass)}{FITBIT_AUTH_CALLBACK_PATH}"

try:
result = self.oauth.fetch_access_token(data.get("code"), redirect_uri)
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/google_assistant/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
)
from homeassistant.core import Context, HomeAssistant, State, callback
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.network import async_get_url
from homeassistant.helpers.storage import Store

from . import trait
Expand Down Expand Up @@ -425,7 +426,7 @@ async def sync_serialize(self, agent_user_id):
"webhookId": self.config.local_sdk_webhook_id,
"httpPort": self.hass.http.server_port,
"httpSSL": self.hass.config.api.use_ssl,
"baseUrl": self.hass.config.api.base_url,
"baseUrl": async_get_url(self.hass, prefer_external=True),
Comment thread
frenck marked this conversation as resolved.
"proxyDeviceId": agent_user_id,
}

Expand Down
5 changes: 2 additions & 3 deletions homeassistant/components/google_assistant/trait.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
TEMP_FAHRENHEIT,
)
from homeassistant.core import DOMAIN as HA_DOMAIN
from homeassistant.helpers.network import async_get_url
from homeassistant.util import color as color_util, temperature as temp_util

from .const import (
Expand Down Expand Up @@ -247,9 +248,7 @@ async def execute(self, command, data, params, challenge):
url = await self.hass.components.camera.async_request_stream(
self.state.entity_id, "hls"
)
self.stream_info = {
"cameraStreamAccessUrl": self.hass.config.api.base_url + url
}
self.stream_info = {"cameraStreamAccessUrl": f"{async_get_url(self.hass)}{url}"}


@register_trait
Expand Down
61 changes: 35 additions & 26 deletions homeassistant/components/http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,29 +63,32 @@
STORAGE_VERSION = 1


HTTP_SCHEMA = vol.Schema(
{
vol.Optional(CONF_SERVER_HOST, default=DEFAULT_SERVER_HOST): cv.string,
vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT): cv.port,
vol.Optional(CONF_BASE_URL): cv.string,
vol.Optional(CONF_SSL_CERTIFICATE): cv.isfile,
vol.Optional(CONF_SSL_PEER_CERTIFICATE): cv.isfile,
vol.Optional(CONF_SSL_KEY): cv.isfile,
vol.Optional(CONF_CORS_ORIGINS, default=[DEFAULT_CORS]): vol.All(
cv.ensure_list, [cv.string]
),
vol.Inclusive(CONF_USE_X_FORWARDED_FOR, "proxy"): cv.boolean,
vol.Inclusive(CONF_TRUSTED_PROXIES, "proxy"): vol.All(
cv.ensure_list, [ip_network]
),
vol.Optional(
CONF_LOGIN_ATTEMPTS_THRESHOLD, default=NO_LOGIN_ATTEMPT_THRESHOLD
): vol.Any(cv.positive_int, NO_LOGIN_ATTEMPT_THRESHOLD),
vol.Optional(CONF_IP_BAN_ENABLED, default=True): cv.boolean,
vol.Optional(CONF_SSL_PROFILE, default=SSL_MODERN): vol.In(
[SSL_INTERMEDIATE, SSL_MODERN]
),
}
HTTP_SCHEMA = vol.All(
cv.deprecated(CONF_BASE_URL),
vol.Schema(
{
vol.Optional(CONF_SERVER_HOST, default=DEFAULT_SERVER_HOST): cv.string,
vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT): cv.port,
vol.Optional(CONF_BASE_URL): cv.string,
vol.Optional(CONF_SSL_CERTIFICATE): cv.isfile,
vol.Optional(CONF_SSL_PEER_CERTIFICATE): cv.isfile,
vol.Optional(CONF_SSL_KEY): cv.isfile,
vol.Optional(CONF_CORS_ORIGINS, default=[DEFAULT_CORS]): vol.All(
cv.ensure_list, [cv.string]
),
vol.Inclusive(CONF_USE_X_FORWARDED_FOR, "proxy"): cv.boolean,
vol.Inclusive(CONF_TRUSTED_PROXIES, "proxy"): vol.All(
cv.ensure_list, [ip_network]
),
vol.Optional(
CONF_LOGIN_ATTEMPTS_THRESHOLD, default=NO_LOGIN_ATTEMPT_THRESHOLD
): vol.Any(cv.positive_int, NO_LOGIN_ATTEMPT_THRESHOLD),
vol.Optional(CONF_IP_BAN_ENABLED, default=True): cv.boolean,
vol.Optional(CONF_SSL_PROFILE, default=SSL_MODERN): vol.In(
[SSL_INTERMEDIATE, SSL_MODERN]
),
}
),
)

CONFIG_SCHEMA = vol.Schema({DOMAIN: HTTP_SCHEMA}, extra=vol.ALLOW_EXTRA)
Expand All @@ -102,9 +105,14 @@ class ApiConfig:
"""Configuration settings for API server."""

def __init__(
self, host: str, port: Optional[int] = SERVER_PORT, use_ssl: bool = False
self,
local_ip: str,
host: str,
port: Optional[int] = SERVER_PORT,
use_ssl: bool = False,
) -> None:
"""Initialize a new API config object."""
self.local_ip = local_ip
self.host = host
self.port = port
self.use_ssl = use_ssl
Expand Down Expand Up @@ -182,17 +190,18 @@ async def start_server(event):
hass.http = server

host = conf.get(CONF_BASE_URL)
local_ip = await hass.async_add_executor_job(hass_util.get_local_ip)

if host:
port = None
elif server_host != DEFAULT_SERVER_HOST:
host = server_host
port = server_port
else:
host = hass_util.get_local_ip()
host = local_ip
port = server_port

hass.config.api = ApiConfig(host, port, ssl_certificate is not None)
hass.config.api = ApiConfig(local_ip, host, port, ssl_certificate is not None)

return True

Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/konnected/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client, device_registry as dr
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.network import async_get_url

from .const import (
CONF_ACTIVATION,
Expand Down Expand Up @@ -297,7 +298,7 @@ def async_desired_settings_payload(self):
# keeping self.hass.data check for backwards compatibility
# newly configured integrations store this in the config entry
desired_api_host = self.options.get(CONF_API_HOST) or (
self.hass.data[DOMAIN].get(CONF_API_HOST) or self.hass.config.api.base_url
self.hass.data[DOMAIN].get(CONF_API_HOST) or async_get_url(self.hass)
)
desired_api_endpoint = desired_api_host + ENDPOINT_ROOT

Expand Down
Loading