Require core config detection to be triggerd manually (#24019)

* Detect core config

* Remove elevation

* Lint

* Lint

* Fix type
This commit is contained in:
Paulus Schoutsen 2019-05-22 17:24:46 -07:00 committed by GitHub
parent f207e01510
commit 9e96397e6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 188 additions and 323 deletions

View file

@ -5,12 +5,18 @@ import voluptuous as vol
from homeassistant.components.http import HomeAssistantView
from homeassistant.config import async_check_ha_config_file
from homeassistant.components import websocket_api
from homeassistant.const import (
CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL
)
from homeassistant.helpers import config_validation as cv
from homeassistant.util import location
async def async_setup(hass):
"""Set up the Hassbian config."""
hass.http.register_view(CheckConfigView)
hass.components.websocket_api.async_register_command(websocket_core_update)
websocket_api.async_register_command(hass, websocket_update_config)
websocket_api.async_register_command(hass, websocket_detect_config)
return True
@ -35,18 +41,57 @@ class CheckConfigView(HomeAssistantView):
@websocket_api.require_admin
@websocket_api.async_response
@websocket_api.websocket_command({
vol.Required('type'): 'config/core/update',
vol.Optional('latitude'): vol.Coerce(float),
vol.Optional('longitude'): vol.Coerce(float),
vol.Optional('elevation'): vol.Coerce(int),
vol.Optional('unit_system'): vol.Coerce(str),
vol.Optional('location_name'): vol.Coerce(str),
vol.Optional('time_zone'): vol.Coerce(str),
'type': 'config/core/update',
vol.Optional('latitude'): cv.latitude,
vol.Optional('longitude'): cv.longitude,
vol.Optional('elevation'): int,
vol.Optional('unit_system'): cv.unit_system,
vol.Optional('location_name'): str,
vol.Optional('time_zone'): cv.time_zone,
})
async def websocket_core_update(hass, connection, msg):
"""Handle request for account info."""
async def websocket_update_config(hass, connection, msg):
"""Handle update core config command."""
data = dict(msg)
data.pop('id')
data.pop('type')
await hass.config.update(**data)
connection.send_result(msg['id'])
try:
await hass.config.update(**data)
connection.send_result(msg['id'])
except ValueError as err:
connection.send_error(
msg['id'], 'invalid_info', str(err)
)
@websocket_api.require_admin
@websocket_api.async_response
@websocket_api.websocket_command({
'type': 'config/core/detect',
})
async def websocket_detect_config(hass, connection, msg):
"""Detect core config."""
session = hass.helpers.aiohttp_client.async_get_clientsession()
location_info = await location.async_detect_location_info(session)
info = {}
if location_info is None:
connection.send_result(msg['id'], info)
return
if location_info.use_metric:
info['unit_system'] = CONF_UNIT_SYSTEM_METRIC
else:
info['unit_system'] = CONF_UNIT_SYSTEM_IMPERIAL
if location_info.latitude:
info['latitude'] = location_info.latitude
if location_info.longitude:
info['longitude'] = location_info.longitude
if location_info.time_zone:
info['time_zone'] = location_info.time_zone
connection.send_result(msg['id'], info)

View file

@ -3,10 +3,11 @@ from homeassistant.core import callback
from homeassistant.loader import bind_hass
from homeassistant.helpers.storage import Store
from .const import DOMAIN, STEP_USER, STEPS, STEP_INTEGRATION
from .const import (
DOMAIN, STEP_USER, STEPS, STEP_INTEGRATION, STEP_CORE_CONFIG)
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 2
STORAGE_VERSION = 3
class OnboadingStorage(Store):
@ -15,7 +16,10 @@ class OnboadingStorage(Store):
async def _async_migrate_func(self, old_version, old_data):
"""Migrate to the new version."""
# From version 1 -> 2, we automatically mark the integration step done
old_data['done'].append(STEP_INTEGRATION)
if old_version < 2:
old_data['done'].append(STEP_INTEGRATION)
if old_version < 3:
old_data['done'].append(STEP_CORE_CONFIG)
return old_data

View file

@ -1,10 +1,12 @@
"""Constants for the onboarding component."""
DOMAIN = 'onboarding'
STEP_USER = 'user'
STEP_CORE_CONFIG = 'core_config'
STEP_INTEGRATION = 'integration'
STEPS = [
STEP_USER,
STEP_CORE_CONFIG,
STEP_INTEGRATION,
]

View file

@ -7,13 +7,16 @@ from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.core import callback
from .const import DOMAIN, STEP_USER, STEPS, DEFAULT_AREAS, STEP_INTEGRATION
from .const import (
DOMAIN, STEP_USER, STEPS, DEFAULT_AREAS, STEP_INTEGRATION,
STEP_CORE_CONFIG)
async def async_setup(hass, data, store):
"""Set up the onboarding view."""
hass.http.register_view(OnboardingView(data, store))
hass.http.register_view(UserOnboardingView(data, store))
hass.http.register_view(CoreConfigOnboardingView(data, store))
hass.http.register_view(IntegrationOnboardingView(data, store))
@ -128,6 +131,26 @@ class UserOnboardingView(_BaseOnboardingView):
})
class CoreConfigOnboardingView(_BaseOnboardingView):
"""View to finish core config onboarding step."""
url = '/api/onboarding/core_config'
name = 'api:onboarding:core_config'
step = STEP_CORE_CONFIG
async def post(self, request):
"""Handle finishing core config step."""
hass = request.app['hass']
async with self._lock:
if self._async_is_done():
return self.json_message('Core config step already done', 403)
await self._async_mark_done(hass)
return self.json({})
class IntegrationOnboardingView(_BaseOnboardingView):
"""View to finish integration onboarding step."""
@ -139,7 +162,7 @@ class IntegrationOnboardingView(_BaseOnboardingView):
vol.Required('client_id'): str,
}))
async def post(self, request, data):
"""Handle user creation, area creation."""
"""Handle token creation."""
hass = request.app['hass']
user = request['hass_user']

View file

@ -18,13 +18,13 @@ from homeassistant.auth import providers as auth_providers,\
from homeassistant.const import (
ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ASSUMED_STATE,
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_PACKAGES, CONF_UNIT_SYSTEM,
CONF_TIME_ZONE, CONF_ELEVATION, CONF_UNIT_SYSTEM_METRIC,
CONF_TIME_ZONE, CONF_ELEVATION,
CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS,
__version__, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, CONF_CUSTOMIZE_GLOB,
CONF_WHITELIST_EXTERNAL_DIRS, CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES,
CONF_TYPE, CONF_ID)
from homeassistant.core import (
DOMAIN as CONF_CORE, SOURCE_DISCOVERED, SOURCE_YAML, HomeAssistant,
DOMAIN as CONF_CORE, SOURCE_YAML, HomeAssistant,
callback)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.loader import (
@ -32,7 +32,6 @@ from homeassistant.loader import (
)
from homeassistant.util.yaml import load_yaml, SECRET_YAML
import homeassistant.helpers.config_validation as cv
from homeassistant.util import location as loc_util
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
from homeassistant.helpers.entity_values import EntityValues
from homeassistant.helpers import config_per_platform, extract_domain_configs
@ -52,22 +51,6 @@ FILE_MIGRATION = (
('ios.conf', '.ios.conf'),
)
DEFAULT_CORE_CONFIG = (
# Tuples (attribute, default, auto detect property, description)
(CONF_NAME, 'Home', None, 'Name of the location where Home Assistant is '
'running'),
(CONF_LATITUDE, 0, 'latitude', 'Location required to calculate the time'
' the sun rises and sets'),
(CONF_LONGITUDE, 0, 'longitude', None),
(CONF_ELEVATION, 0, None, 'Impacts weather/sunrise data'
' (altitude above sea level in meters)'),
(CONF_UNIT_SYSTEM, CONF_UNIT_SYSTEM_METRIC, None,
'{} for Metric, {} for Imperial'.format(CONF_UNIT_SYSTEM_METRIC,
CONF_UNIT_SYSTEM_IMPERIAL)),
(CONF_TIME_ZONE, 'UTC', 'time_zone', 'Pick yours from here: http://en.wiki'
'pedia.org/wiki/List_of_tz_database_time_zones'),
(CONF_CUSTOMIZE, '!include customize.yaml', None, 'Customization file'),
) # type: Tuple[Tuple[str, Any, Any, Optional[str]], ...]
DEFAULT_CONFIG = """
# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:
@ -207,8 +190,7 @@ def get_default_config_dir() -> str:
return os.path.join(data_dir, CONFIG_DIR_NAME) # type: ignore
async def async_ensure_config_exists(hass: HomeAssistant, config_dir: str,
detect_location: bool = True)\
async def async_ensure_config_exists(hass: HomeAssistant, config_dir: str) \
-> Optional[str]:
"""Ensure a configuration file exists in given configuration directory.
@ -220,49 +202,22 @@ async def async_ensure_config_exists(hass: HomeAssistant, config_dir: str,
if config_path is None:
print("Unable to find configuration. Creating default one in",
config_dir)
config_path = await async_create_default_config(
hass, config_dir, detect_location)
config_path = await async_create_default_config(hass, config_dir)
return config_path
async def async_create_default_config(
hass: HomeAssistant, config_dir: str, detect_location: bool = True
) -> Optional[str]:
async def async_create_default_config(hass: HomeAssistant, config_dir: str) \
-> Optional[str]:
"""Create a default configuration file in given configuration directory.
Return path to new config file if success, None if failed.
This method needs to run in an executor.
"""
info = {attr: default for attr, default, _, _ in DEFAULT_CORE_CONFIG}
if detect_location:
session = hass.helpers.aiohttp_client.async_get_clientsession()
location_info = await loc_util.async_detect_location_info(session)
else:
location_info = None
if location_info:
if location_info.use_metric:
info[CONF_UNIT_SYSTEM] = CONF_UNIT_SYSTEM_METRIC
else:
info[CONF_UNIT_SYSTEM] = CONF_UNIT_SYSTEM_IMPERIAL
for attr, default, prop, _ in DEFAULT_CORE_CONFIG:
if prop is None:
continue
info[attr] = getattr(location_info, prop) or default
if location_info.latitude and location_info.longitude:
info[CONF_ELEVATION] = await loc_util.async_get_elevation(
session, location_info.latitude, location_info.longitude)
return await hass.async_add_executor_job(
_write_default_config, config_dir, info
)
return await hass.async_add_executor_job(_write_default_config, config_dir)
def _write_default_config(config_dir: str, info: Dict)\
def _write_default_config(config_dir: str)\
-> Optional[str]:
"""Write the default config."""
from homeassistant.components.config.group import (
@ -271,8 +226,6 @@ def _write_default_config(config_dir: str, info: Dict)\
CONFIG_PATH as AUTOMATION_CONFIG_PATH)
from homeassistant.components.config.script import (
CONFIG_PATH as SCRIPT_CONFIG_PATH)
from homeassistant.components.config.customize import (
CONFIG_PATH as CUSTOMIZE_CONFIG_PATH)
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
secret_path = os.path.join(config_dir, SECRET_YAML)
@ -280,21 +233,11 @@ def _write_default_config(config_dir: str, info: Dict)\
group_yaml_path = os.path.join(config_dir, GROUP_CONFIG_PATH)
automation_yaml_path = os.path.join(config_dir, AUTOMATION_CONFIG_PATH)
script_yaml_path = os.path.join(config_dir, SCRIPT_CONFIG_PATH)
customize_yaml_path = os.path.join(config_dir, CUSTOMIZE_CONFIG_PATH)
# Writing files with YAML does not create the most human readable results
# So we're hard coding a YAML template.
try:
with open(config_path, 'wt') as config_file:
config_file.write("homeassistant:\n")
for attr, _, _, description in DEFAULT_CORE_CONFIG:
if info[attr] is None:
continue
elif description:
config_file.write(" # {}\n".format(description))
config_file.write(" {}: {}\n".format(attr, info[attr]))
config_file.write(DEFAULT_CONFIG)
with open(secret_path, 'wt') as secret_file:
@ -312,9 +255,6 @@ def _write_default_config(config_dir: str, info: Dict)\
with open(script_yaml_path, 'wt'):
pass
with open(customize_yaml_path, 'wt'):
pass
return config_path
except IOError:
@ -576,55 +516,6 @@ async def async_process_ha_core_config(
"with '%s: %s'", CONF_TEMPERATURE_UNIT, unit,
CONF_UNIT_SYSTEM, hac.units.name)
# Shortcut if no auto-detection necessary
if None not in (hac.latitude, hac.longitude, hac.units,
hac.time_zone, hac.elevation):
return
discovered = [] # type: List[Tuple[str, Any]]
# If we miss some of the needed values, auto detect them
if None in (hac.latitude, hac.longitude, hac.units,
hac.time_zone):
hac.config_source = SOURCE_DISCOVERED
info = await loc_util.async_detect_location_info(
hass.helpers.aiohttp_client.async_get_clientsession()
)
if info is None:
_LOGGER.error("Could not detect location information")
return
if hac.latitude is None and hac.longitude is None:
hac.latitude, hac.longitude = (info.latitude, info.longitude)
discovered.append(('latitude', hac.latitude))
discovered.append(('longitude', hac.longitude))
if hac.units is None:
hac.units = METRIC_SYSTEM if info.use_metric else IMPERIAL_SYSTEM
discovered.append((CONF_UNIT_SYSTEM, hac.units.name))
if hac.location_name is None:
hac.location_name = info.city
discovered.append(('name', info.city))
if hac.time_zone is None:
hac.set_time_zone(info.time_zone)
discovered.append(('time_zone', info.time_zone))
if hac.elevation is None and hac.latitude is not None and \
hac.longitude is not None:
elevation = await loc_util.async_get_elevation(
hass.helpers.aiohttp_client.async_get_clientsession(),
hac.latitude, hac.longitude)
hac.elevation = elevation
discovered.append(('elevation', elevation))
if discovered:
_LOGGER.warning(
"Incomplete core configuration. Auto detected %s",
", ".join('{}: {}'.format(key, val) for key, val in discovered))
def _log_pkg_error(
package: str, component: str, config: Dict, message: str) -> None:

View file

@ -57,7 +57,7 @@ CALLABLE_T = TypeVar('CALLABLE_T', bound=Callable)
CALLBACK_TYPE = Callable[[], None]
# pylint: enable=invalid-name
CORE_STORAGE_KEY = 'homeassistant.core_config'
CORE_STORAGE_KEY = 'core.config'
CORE_STORAGE_VERSION = 1
DOMAIN = 'homeassistant'
@ -1181,14 +1181,14 @@ class Config:
"""Initialize a new config object."""
self.hass = hass
self.latitude = None # type: Optional[float]
self.longitude = None # type: Optional[float]
self.elevation = None # type: Optional[int]
self.location_name = None # type: Optional[str]
self.time_zone = None # type: Optional[datetime.tzinfo]
self.latitude = 0 # type: float
self.longitude = 0 # type: float
self.elevation = 0 # type: int
self.location_name = "Home" # type: str
self.time_zone = dt_util.UTC # type: datetime.tzinfo
self.units = METRIC_SYSTEM # type: UnitSystem
self.config_source = None # type: Optional[str]
self.config_source = "default" # type: str
# If True, pip install is skipped for requirements on startup
self.skip_pip = False # type: bool

View file

@ -65,30 +65,6 @@ def distance(lat1: Optional[float], lon1: Optional[float],
return result * 1000
async def async_get_elevation(session: aiohttp.ClientSession, latitude: float,
longitude: float) -> int:
"""Return elevation for given latitude and longitude."""
try:
resp = await session.get(ELEVATION_URL, params={
'locations': '{},{}'.format(latitude, longitude),
}, timeout=5)
except (aiohttp.ClientError, asyncio.TimeoutError):
return 0
if resp.status != 200:
return 0
try:
raw_info = await resp.json()
except (aiohttp.ClientError, ValueError):
return 0
try:
return int(float(raw_info['results'][0]['elevation']))
except (ValueError, KeyError, IndexError):
return 0
# Author: https://github.com/maurycyp
# Source: https://github.com/maurycyp/vincenty
# License: https://github.com/maurycyp/vincenty/blob/master/LICENSE

View file

@ -1,24 +1,31 @@
"""Test hassbian config."""
import asyncio
from unittest.mock import patch
import pytest
from homeassistant.bootstrap import async_setup_component
from homeassistant.components import config
from homeassistant.components.websocket_api.const import TYPE_RESULT
from homeassistant.const import CONF_UNIT_SYSTEM, CONF_UNIT_SYSTEM_IMPERIAL
import homeassistant.util.dt as dt_util
from homeassistant.util import dt as dt_util, location
from tests.common import mock_coro
ORIG_TIME_ZONE = dt_util.DEFAULT_TIME_ZONE
@pytest.fixture
async def client(hass, hass_ws_client):
"""Fixture that can interact with the config manager API."""
with patch.object(config, 'SECTIONS', ['core']):
assert await async_setup_component(hass, 'config', {})
return await hass_ws_client(hass)
async def test_validate_config_ok(hass, hass_client):
"""Test checking config."""
with patch.object(config, 'SECTIONS', ['core']):
await async_setup_component(hass, 'config', {})
await asyncio.sleep(0.1, loop=hass.loop)
client = await hass_client()
with patch(
@ -42,11 +49,8 @@ async def test_validate_config_ok(hass, hass_client):
assert result['errors'] == 'beer'
async def test_websocket_core_update(hass, hass_ws_client):
async def test_websocket_core_update(hass, client):
"""Test core config update websocket command."""
with patch.object(config, 'SECTIONS', ['core']):
await async_setup_component(hass, 'config', {})
assert hass.config.latitude != 60
assert hass.config.longitude != 50
assert hass.config.elevation != 25
@ -54,7 +58,6 @@ async def test_websocket_core_update(hass, hass_ws_client):
assert hass.config.units.name != CONF_UNIT_SYSTEM_IMPERIAL
assert hass.config.time_zone.zone != 'America/New_York'
client = await hass_ws_client(hass)
await client.send_json({
'id': 5,
'type': 'config/core/update',
@ -92,7 +95,7 @@ async def test_websocket_core_update_not_admin(
await client.send_json({
'id': 6,
'type': 'config/core/update',
'latitude': 123,
'latitude': 23,
})
msg = await client.receive_json()
@ -103,16 +106,12 @@ async def test_websocket_core_update_not_admin(
assert msg['error']['code'] == 'unauthorized'
async def test_websocket_bad_core_update(hass, hass_ws_client):
async def test_websocket_bad_core_update(hass, client):
"""Test core config update fails with bad parameters."""
with patch.object(config, 'SECTIONS', ['core']):
await async_setup_component(hass, 'config', {})
client = await hass_ws_client(hass)
await client.send_json({
'id': 7,
'type': 'config/core/update',
'latituude': 123,
'latituude': 23,
})
msg = await client.receive_json()
@ -121,3 +120,48 @@ async def test_websocket_bad_core_update(hass, hass_ws_client):
assert msg['type'] == TYPE_RESULT
assert not msg['success']
assert msg['error']['code'] == 'invalid_format'
async def test_detect_config(hass, client):
"""Test detect config."""
with patch('homeassistant.util.location.async_detect_location_info',
return_value=mock_coro(None)):
await client.send_json({
'id': 1,
'type': 'config/core/detect',
})
msg = await client.receive_json()
assert msg['success'] is True
assert msg['result'] == {}
async def test_detect_config_fail(hass, client):
"""Test detect config."""
with patch('homeassistant.util.location.async_detect_location_info',
return_value=mock_coro(location.LocationInfo(
ip=None,
country_code=None,
country_name=None,
region_code=None,
region_name=None,
city=None,
zip_code=None,
latitude=None,
longitude=None,
use_metric=True,
time_zone='Europe/Amsterdam',
))):
await client.send_json({
'id': 1,
'type': 'config/core/detect',
})
msg = await client.receive_json()
assert msg['success'] is True
assert msg['result'] == {
'unit_system': 'metric',
'time_zone': 'Europe/Amsterdam',
}

View file

@ -44,8 +44,6 @@ def check_real(func):
# Guard a few functions that would make network connections
location.async_detect_location_info = \
check_real(location.async_detect_location_info)
location.async_get_elevation = \
check_real(location.async_get_elevation)
util.get_local_ip = lambda: '127.0.0.1'

View file

@ -12,17 +12,16 @@ import pytest
from voluptuous import MultipleInvalid, Invalid
import yaml
from homeassistant.core import (
DOMAIN, SOURCE_STORAGE, Config, HomeAssistantError)
from homeassistant.core import SOURCE_STORAGE, HomeAssistantError
import homeassistant.config as config_util
from homeassistant.loader import async_get_integration
from homeassistant.const import (
ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ASSUMED_STATE,
CONF_LATITUDE, CONF_LONGITUDE, CONF_UNIT_SYSTEM, CONF_NAME,
CONF_TIME_ZONE, CONF_ELEVATION, CONF_CUSTOMIZE, __version__,
CONF_CUSTOMIZE, __version__,
CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT,
CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES)
from homeassistant.util import location as location_util, dt as dt_util
from homeassistant.util import dt as dt_util
from homeassistant.util.yaml import SECRET_YAML
from homeassistant.helpers.entity import Entity
from homeassistant.components.config.group import (
@ -31,12 +30,10 @@ from homeassistant.components.config.automation import (
CONFIG_PATH as AUTOMATIONS_CONFIG_PATH)
from homeassistant.components.config.script import (
CONFIG_PATH as SCRIPTS_CONFIG_PATH)
from homeassistant.components.config.customize import (
CONFIG_PATH as CUSTOMIZE_CONFIG_PATH)
import homeassistant.scripts.check_config as check_config
from tests.common import (
get_test_config_dir, patch_yaml_files, mock_coro)
get_test_config_dir, patch_yaml_files)
CONFIG_DIR = get_test_config_dir()
YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE)
@ -45,7 +42,6 @@ VERSION_PATH = os.path.join(CONFIG_DIR, config_util.VERSION_FILE)
GROUP_PATH = os.path.join(CONFIG_DIR, GROUP_CONFIG_PATH)
AUTOMATIONS_PATH = os.path.join(CONFIG_DIR, AUTOMATIONS_CONFIG_PATH)
SCRIPTS_PATH = os.path.join(CONFIG_DIR, SCRIPTS_CONFIG_PATH)
CUSTOMIZE_PATH = os.path.join(CONFIG_DIR, CUSTOMIZE_CONFIG_PATH)
ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE
@ -77,20 +73,16 @@ def teardown():
if os.path.isfile(SCRIPTS_PATH):
os.remove(SCRIPTS_PATH)
if os.path.isfile(CUSTOMIZE_PATH):
os.remove(CUSTOMIZE_PATH)
async def test_create_default_config(hass):
"""Test creation of default config."""
await config_util.async_create_default_config(hass, CONFIG_DIR, False)
await config_util.async_create_default_config(hass, CONFIG_DIR)
assert os.path.isfile(YAML_PATH)
assert os.path.isfile(SECRET_PATH)
assert os.path.isfile(VERSION_PATH)
assert os.path.isfile(GROUP_PATH)
assert os.path.isfile(AUTOMATIONS_PATH)
assert os.path.isfile(CUSTOMIZE_PATH)
def test_find_config_file_yaml():
@ -106,7 +98,7 @@ async def test_ensure_config_exists_creates_config(hass):
If not creates a new config file.
"""
with mock.patch('builtins.print') as mock_print:
await config_util.async_ensure_config_exists(hass, CONFIG_DIR, False)
await config_util.async_ensure_config_exists(hass, CONFIG_DIR)
assert os.path.isfile(YAML_PATH)
assert mock_print.called
@ -115,7 +107,7 @@ async def test_ensure_config_exists_creates_config(hass):
async def test_ensure_config_exists_uses_existing_config(hass):
"""Test that calling ensure_config_exists uses existing config."""
create_file(YAML_PATH)
await config_util.async_ensure_config_exists(hass, CONFIG_DIR, False)
await config_util.async_ensure_config_exists(hass, CONFIG_DIR)
with open(YAML_PATH) as f:
content = f.read()
@ -168,38 +160,6 @@ def test_load_yaml_config_preserves_key_order():
list(config_util.load_yaml_config_file(YAML_PATH).items())
async def test_create_default_config_detect_location(hass):
"""Test that detect location sets the correct config keys."""
with mock.patch('homeassistant.util.location.async_detect_location_info',
return_value=mock_coro(location_util.LocationInfo(
'0.0.0.0', 'US', 'United States', 'CA', 'California',
'San Diego', '92122', 'America/Los_Angeles', 32.8594,
-117.2073, True))), \
mock.patch('homeassistant.util.location.async_get_elevation',
return_value=mock_coro(101)), \
mock.patch('builtins.print') as mock_print:
await config_util.async_ensure_config_exists(hass, CONFIG_DIR)
config = config_util.load_yaml_config_file(YAML_PATH)
assert DOMAIN in config
ha_conf = config[DOMAIN]
expected_values = {
CONF_LATITUDE: 32.8594,
CONF_LONGITUDE: -117.2073,
CONF_ELEVATION: 101,
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
CONF_NAME: 'Home',
CONF_TIME_ZONE: 'America/Los_Angeles',
CONF_CUSTOMIZE: OrderedDict(),
}
assert expected_values == ha_conf
assert mock_print.called
async def test_create_default_config_returns_none_if_write_error(hass):
"""Test the writing of a default configuration.
@ -207,7 +167,7 @@ async def test_create_default_config_returns_none_if_write_error(hass):
"""
with mock.patch('builtins.print') as mock_print:
assert await config_util.async_create_default_config(
hass, os.path.join(CONFIG_DIR, 'non_existing_dir/'), False) is None
hass, os.path.join(CONFIG_DIR, 'non_existing_dir/')) is None
assert mock_print.called
@ -418,7 +378,7 @@ def test_migrate_no_file_on_upgrade(mock_os, mock_shutil, hass):
async def test_loading_configuration_from_storage(hass, hass_storage):
"""Test loading core config onto hass object."""
hass_storage["homeassistant.core_config"] = {
hass_storage["core.config"] = {
'data': {
'elevation': 10,
'latitude': 55,
@ -427,7 +387,7 @@ async def test_loading_configuration_from_storage(hass, hass_storage):
'time_zone': 'Europe/Copenhagen',
'unit_system': 'metric'
},
'key': 'homeassistant.core_config',
'key': 'core.config',
'version': 1
}
await config_util.async_process_ha_core_config(
@ -455,23 +415,23 @@ async def test_updating_configuration(hass, hass_storage):
'time_zone': 'Europe/Copenhagen',
'unit_system': 'metric'
},
'key': 'homeassistant.core_config',
'key': 'core.config',
'version': 1
}
hass_storage["homeassistant.core_config"] = dict(core_data)
hass_storage["core.config"] = dict(core_data)
await config_util.async_process_ha_core_config(
hass, {'whitelist_external_dirs': '/tmp'})
await hass.config.update(latitude=50)
new_core_data = copy.deepcopy(core_data)
new_core_data['data']['latitude'] = 50
assert hass_storage["homeassistant.core_config"] == new_core_data
assert hass_storage["core.config"] == new_core_data
assert hass.config.latitude == 50
async def test_override_stored_configuration(hass, hass_storage):
"""Test loading core and YAML config onto hass object."""
hass_storage["homeassistant.core_config"] = {
hass_storage["core.config"] = {
'data': {
'elevation': 10,
'latitude': 55,
@ -480,7 +440,7 @@ async def test_override_stored_configuration(hass, hass_storage):
'time_zone': 'Europe/Copenhagen',
'unit_system': 'metric'
},
'key': 'homeassistant.core_config',
'key': 'core.config',
'version': 1
}
await config_util.async_process_ha_core_config(hass, {
@ -571,59 +531,6 @@ async def test_loading_configuration_from_packages(hass):
})
@asynctest.mock.patch(
'homeassistant.util.location.async_detect_location_info',
autospec=True, return_value=mock_coro(location_util.LocationInfo(
'0.0.0.0', 'US', 'United States', 'CA',
'California', 'San Diego', '92122',
'America/Los_Angeles', 32.8594, -117.2073, True)))
@asynctest.mock.patch('homeassistant.util.location.async_get_elevation',
autospec=True, return_value=mock_coro(101))
async def test_discovering_configuration(mock_detect, mock_elevation, hass):
"""Test auto discovery for missing core configs."""
hass.config.latitude = None
hass.config.longitude = None
hass.config.elevation = None
hass.config.location_name = None
hass.config.time_zone = None
await config_util.async_process_ha_core_config(hass, {})
assert hass.config.latitude == 32.8594
assert hass.config.longitude == -117.2073
assert hass.config.elevation == 101
assert hass.config.location_name == 'San Diego'
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
assert hass.config.units.is_metric
assert hass.config.time_zone.zone == 'America/Los_Angeles'
assert hass.config.config_source == config_util.SOURCE_DISCOVERED
@asynctest.mock.patch('homeassistant.util.location.async_detect_location_info',
autospec=True, return_value=mock_coro(None))
@asynctest.mock.patch('homeassistant.util.location.async_get_elevation',
return_value=mock_coro(0))
async def test_discovering_configuration_auto_detect_fails(mock_detect,
mock_elevation,
hass):
"""Test config remains unchanged if discovery fails."""
hass.config = Config(hass)
hass.config.config_dir = "/test/config"
await config_util.async_process_ha_core_config(hass, {})
blankConfig = Config(hass)
assert hass.config.latitude == blankConfig.latitude
assert hass.config.longitude == blankConfig.longitude
assert hass.config.elevation == blankConfig.elevation
assert hass.config.location_name == blankConfig.location_name
assert hass.config.units == blankConfig.units
assert hass.config.time_zone == blankConfig.time_zone
assert len(hass.config.whitelist_external_dirs) == 1
assert "/test/config/www" in hass.config.whitelist_external_dirs
assert hass.config.config_source == config_util.SOURCE_DISCOVERED
@asynctest.mock.patch(
'homeassistant.scripts.check_config.check_ha_config_file')
async def test_check_ha_config_file_correct(mock_check, hass):

View file

@ -891,17 +891,17 @@ class TestConfig(unittest.TestCase):
"""Test as dict."""
self.config.config_dir = '/tmp/ha-config'
expected = {
'latitude': None,
'longitude': None,
'elevation': None,
'latitude': 0,
'longitude': 0,
'elevation': 0,
CONF_UNIT_SYSTEM: METRIC_SYSTEM.as_dict(),
'location_name': None,
'location_name': "Home",
'time_zone': 'UTC',
'components': set(),
'config_dir': '/tmp/ha-config',
'whitelist_external_dirs': set(),
'version': __version__,
'config_source': None,
'config_source': "default",
}
assert expected == self.config.as_dict()

View file

@ -116,10 +116,8 @@ async def test_detect_location_info_ip_api(aioclient_mock, session):
async def test_detect_location_info_both_queries_fail(session):
"""Ensure we return None if both queries fail."""
with patch('homeassistant.util.location.async_get_elevation',
return_value=mock_coro(0)), \
patch('homeassistant.util.location._get_ipapi',
return_value=mock_coro(None)), \
with patch('homeassistant.util.location._get_ipapi',
return_value=mock_coro(None)), \
patch('homeassistant.util.location._get_ip_api',
return_value=mock_coro(None)):
info = await location_util.async_detect_location_info(
@ -137,26 +135,3 @@ async def test_ip_api_query_raises(raising_session):
"""Test ip api query when the request to API fails."""
info = await location_util._get_ip_api(raising_session)
assert info is None
async def test_elevation_query_raises(raising_session):
"""Test elevation when the request to API fails."""
elevation = await location_util.async_get_elevation(
raising_session, 10, 10, _test_real=True)
assert elevation == 0
async def test_elevation_query_fails(aioclient_mock, session):
"""Test elevation when the request to API fails."""
aioclient_mock.get(location_util.ELEVATION_URL, text='{}', status=401)
elevation = await location_util.async_get_elevation(
session, 10, 10, _test_real=True)
assert elevation == 0
async def test_elevation_query_nonjson(aioclient_mock, session):
"""Test if elevation API returns a non JSON value."""
aioclient_mock.get(location_util.ELEVATION_URL, text='{ I am not JSON }')
elevation = await location_util.async_get_elevation(
session, 10, 10, _test_real=True)
assert elevation == 0