Require core config detection to be triggerd manually (#24019)
* Detect core config * Remove elevation * Lint * Lint * Fix type
This commit is contained in:
parent
f207e01510
commit
9e96397e6a
12 changed files with 188 additions and 323 deletions
|
@ -5,12 +5,18 @@ import voluptuous as vol
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.config import async_check_ha_config_file
|
from homeassistant.config import async_check_ha_config_file
|
||||||
from homeassistant.components import websocket_api
|
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):
|
async def async_setup(hass):
|
||||||
"""Set up the Hassbian config."""
|
"""Set up the Hassbian config."""
|
||||||
hass.http.register_view(CheckConfigView)
|
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
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,18 +41,57 @@ class CheckConfigView(HomeAssistantView):
|
||||||
@websocket_api.require_admin
|
@websocket_api.require_admin
|
||||||
@websocket_api.async_response
|
@websocket_api.async_response
|
||||||
@websocket_api.websocket_command({
|
@websocket_api.websocket_command({
|
||||||
vol.Required('type'): 'config/core/update',
|
'type': 'config/core/update',
|
||||||
vol.Optional('latitude'): vol.Coerce(float),
|
vol.Optional('latitude'): cv.latitude,
|
||||||
vol.Optional('longitude'): vol.Coerce(float),
|
vol.Optional('longitude'): cv.longitude,
|
||||||
vol.Optional('elevation'): vol.Coerce(int),
|
vol.Optional('elevation'): int,
|
||||||
vol.Optional('unit_system'): vol.Coerce(str),
|
vol.Optional('unit_system'): cv.unit_system,
|
||||||
vol.Optional('location_name'): vol.Coerce(str),
|
vol.Optional('location_name'): str,
|
||||||
vol.Optional('time_zone'): vol.Coerce(str),
|
vol.Optional('time_zone'): cv.time_zone,
|
||||||
})
|
})
|
||||||
async def websocket_core_update(hass, connection, msg):
|
async def websocket_update_config(hass, connection, msg):
|
||||||
"""Handle request for account info."""
|
"""Handle update core config command."""
|
||||||
data = dict(msg)
|
data = dict(msg)
|
||||||
data.pop('id')
|
data.pop('id')
|
||||||
data.pop('type')
|
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)
|
||||||
|
|
|
@ -3,10 +3,11 @@ from homeassistant.core import callback
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.helpers.storage import Store
|
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_KEY = DOMAIN
|
||||||
STORAGE_VERSION = 2
|
STORAGE_VERSION = 3
|
||||||
|
|
||||||
|
|
||||||
class OnboadingStorage(Store):
|
class OnboadingStorage(Store):
|
||||||
|
@ -15,7 +16,10 @@ class OnboadingStorage(Store):
|
||||||
async def _async_migrate_func(self, old_version, old_data):
|
async def _async_migrate_func(self, old_version, old_data):
|
||||||
"""Migrate to the new version."""
|
"""Migrate to the new version."""
|
||||||
# From version 1 -> 2, we automatically mark the integration step done
|
# 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
|
return old_data
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
"""Constants for the onboarding component."""
|
"""Constants for the onboarding component."""
|
||||||
DOMAIN = 'onboarding'
|
DOMAIN = 'onboarding'
|
||||||
STEP_USER = 'user'
|
STEP_USER = 'user'
|
||||||
|
STEP_CORE_CONFIG = 'core_config'
|
||||||
STEP_INTEGRATION = 'integration'
|
STEP_INTEGRATION = 'integration'
|
||||||
|
|
||||||
STEPS = [
|
STEPS = [
|
||||||
STEP_USER,
|
STEP_USER,
|
||||||
|
STEP_CORE_CONFIG,
|
||||||
STEP_INTEGRATION,
|
STEP_INTEGRATION,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,16 @@ from homeassistant.components.http.data_validator import RequestDataValidator
|
||||||
from homeassistant.components.http.view import HomeAssistantView
|
from homeassistant.components.http.view import HomeAssistantView
|
||||||
from homeassistant.core import callback
|
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):
|
async def async_setup(hass, data, store):
|
||||||
"""Set up the onboarding view."""
|
"""Set up the onboarding view."""
|
||||||
hass.http.register_view(OnboardingView(data, store))
|
hass.http.register_view(OnboardingView(data, store))
|
||||||
hass.http.register_view(UserOnboardingView(data, store))
|
hass.http.register_view(UserOnboardingView(data, store))
|
||||||
|
hass.http.register_view(CoreConfigOnboardingView(data, store))
|
||||||
hass.http.register_view(IntegrationOnboardingView(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):
|
class IntegrationOnboardingView(_BaseOnboardingView):
|
||||||
"""View to finish integration onboarding step."""
|
"""View to finish integration onboarding step."""
|
||||||
|
|
||||||
|
@ -139,7 +162,7 @@ class IntegrationOnboardingView(_BaseOnboardingView):
|
||||||
vol.Required('client_id'): str,
|
vol.Required('client_id'): str,
|
||||||
}))
|
}))
|
||||||
async def post(self, request, data):
|
async def post(self, request, data):
|
||||||
"""Handle user creation, area creation."""
|
"""Handle token creation."""
|
||||||
hass = request.app['hass']
|
hass = request.app['hass']
|
||||||
user = request['hass_user']
|
user = request['hass_user']
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,13 @@ from homeassistant.auth import providers as auth_providers,\
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ASSUMED_STATE,
|
ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ASSUMED_STATE,
|
||||||
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_PACKAGES, CONF_UNIT_SYSTEM,
|
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,
|
CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS,
|
||||||
__version__, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, CONF_CUSTOMIZE_GLOB,
|
__version__, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, CONF_CUSTOMIZE_GLOB,
|
||||||
CONF_WHITELIST_EXTERNAL_DIRS, CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES,
|
CONF_WHITELIST_EXTERNAL_DIRS, CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES,
|
||||||
CONF_TYPE, CONF_ID)
|
CONF_TYPE, CONF_ID)
|
||||||
from homeassistant.core import (
|
from homeassistant.core import (
|
||||||
DOMAIN as CONF_CORE, SOURCE_DISCOVERED, SOURCE_YAML, HomeAssistant,
|
DOMAIN as CONF_CORE, SOURCE_YAML, HomeAssistant,
|
||||||
callback)
|
callback)
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.loader import (
|
from homeassistant.loader import (
|
||||||
|
@ -32,7 +32,6 @@ from homeassistant.loader import (
|
||||||
)
|
)
|
||||||
from homeassistant.util.yaml import load_yaml, SECRET_YAML
|
from homeassistant.util.yaml import load_yaml, SECRET_YAML
|
||||||
import homeassistant.helpers.config_validation as cv
|
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.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
|
||||||
from homeassistant.helpers.entity_values import EntityValues
|
from homeassistant.helpers.entity_values import EntityValues
|
||||||
from homeassistant.helpers import config_per_platform, extract_domain_configs
|
from homeassistant.helpers import config_per_platform, extract_domain_configs
|
||||||
|
@ -52,22 +51,6 @@ FILE_MIGRATION = (
|
||||||
('ios.conf', '.ios.conf'),
|
('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 = """
|
DEFAULT_CONFIG = """
|
||||||
# Configure a default setup of Home Assistant (frontend, api, etc)
|
# Configure a default setup of Home Assistant (frontend, api, etc)
|
||||||
default_config:
|
default_config:
|
||||||
|
@ -207,8 +190,7 @@ def get_default_config_dir() -> str:
|
||||||
return os.path.join(data_dir, CONFIG_DIR_NAME) # type: ignore
|
return os.path.join(data_dir, CONFIG_DIR_NAME) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
async def async_ensure_config_exists(hass: HomeAssistant, config_dir: str,
|
async def async_ensure_config_exists(hass: HomeAssistant, config_dir: str) \
|
||||||
detect_location: bool = True)\
|
|
||||||
-> Optional[str]:
|
-> Optional[str]:
|
||||||
"""Ensure a configuration file exists in given configuration directory.
|
"""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:
|
if config_path is None:
|
||||||
print("Unable to find configuration. Creating default one in",
|
print("Unable to find configuration. Creating default one in",
|
||||||
config_dir)
|
config_dir)
|
||||||
config_path = await async_create_default_config(
|
config_path = await async_create_default_config(hass, config_dir)
|
||||||
hass, config_dir, detect_location)
|
|
||||||
|
|
||||||
return config_path
|
return config_path
|
||||||
|
|
||||||
|
|
||||||
async def async_create_default_config(
|
async def async_create_default_config(hass: HomeAssistant, config_dir: str) \
|
||||||
hass: HomeAssistant, config_dir: str, detect_location: bool = True
|
-> Optional[str]:
|
||||||
) -> Optional[str]:
|
|
||||||
"""Create a default configuration file in given configuration directory.
|
"""Create a default configuration file in given configuration directory.
|
||||||
|
|
||||||
Return path to new config file if success, None if failed.
|
Return path to new config file if success, None if failed.
|
||||||
This method needs to run in an executor.
|
This method needs to run in an executor.
|
||||||
"""
|
"""
|
||||||
info = {attr: default for attr, default, _, _ in DEFAULT_CORE_CONFIG}
|
return await hass.async_add_executor_job(_write_default_config, config_dir)
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _write_default_config(config_dir: str, info: Dict)\
|
def _write_default_config(config_dir: str)\
|
||||||
-> Optional[str]:
|
-> Optional[str]:
|
||||||
"""Write the default config."""
|
"""Write the default config."""
|
||||||
from homeassistant.components.config.group import (
|
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)
|
CONFIG_PATH as AUTOMATION_CONFIG_PATH)
|
||||||
from homeassistant.components.config.script import (
|
from homeassistant.components.config.script import (
|
||||||
CONFIG_PATH as SCRIPT_CONFIG_PATH)
|
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)
|
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||||
secret_path = os.path.join(config_dir, SECRET_YAML)
|
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)
|
group_yaml_path = os.path.join(config_dir, GROUP_CONFIG_PATH)
|
||||||
automation_yaml_path = os.path.join(config_dir, AUTOMATION_CONFIG_PATH)
|
automation_yaml_path = os.path.join(config_dir, AUTOMATION_CONFIG_PATH)
|
||||||
script_yaml_path = os.path.join(config_dir, SCRIPT_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
|
# Writing files with YAML does not create the most human readable results
|
||||||
# So we're hard coding a YAML template.
|
# So we're hard coding a YAML template.
|
||||||
try:
|
try:
|
||||||
with open(config_path, 'wt') as config_file:
|
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)
|
config_file.write(DEFAULT_CONFIG)
|
||||||
|
|
||||||
with open(secret_path, 'wt') as secret_file:
|
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'):
|
with open(script_yaml_path, 'wt'):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with open(customize_yaml_path, 'wt'):
|
|
||||||
pass
|
|
||||||
|
|
||||||
return config_path
|
return config_path
|
||||||
|
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -576,55 +516,6 @@ async def async_process_ha_core_config(
|
||||||
"with '%s: %s'", CONF_TEMPERATURE_UNIT, unit,
|
"with '%s: %s'", CONF_TEMPERATURE_UNIT, unit,
|
||||||
CONF_UNIT_SYSTEM, hac.units.name)
|
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(
|
def _log_pkg_error(
|
||||||
package: str, component: str, config: Dict, message: str) -> None:
|
package: str, component: str, config: Dict, message: str) -> None:
|
||||||
|
|
|
@ -57,7 +57,7 @@ CALLABLE_T = TypeVar('CALLABLE_T', bound=Callable)
|
||||||
CALLBACK_TYPE = Callable[[], None]
|
CALLBACK_TYPE = Callable[[], None]
|
||||||
# pylint: enable=invalid-name
|
# pylint: enable=invalid-name
|
||||||
|
|
||||||
CORE_STORAGE_KEY = 'homeassistant.core_config'
|
CORE_STORAGE_KEY = 'core.config'
|
||||||
CORE_STORAGE_VERSION = 1
|
CORE_STORAGE_VERSION = 1
|
||||||
|
|
||||||
DOMAIN = 'homeassistant'
|
DOMAIN = 'homeassistant'
|
||||||
|
@ -1181,14 +1181,14 @@ class Config:
|
||||||
"""Initialize a new config object."""
|
"""Initialize a new config object."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
|
|
||||||
self.latitude = None # type: Optional[float]
|
self.latitude = 0 # type: float
|
||||||
self.longitude = None # type: Optional[float]
|
self.longitude = 0 # type: float
|
||||||
self.elevation = None # type: Optional[int]
|
self.elevation = 0 # type: int
|
||||||
self.location_name = None # type: Optional[str]
|
self.location_name = "Home" # type: str
|
||||||
self.time_zone = None # type: Optional[datetime.tzinfo]
|
self.time_zone = dt_util.UTC # type: datetime.tzinfo
|
||||||
self.units = METRIC_SYSTEM # type: UnitSystem
|
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
|
# If True, pip install is skipped for requirements on startup
|
||||||
self.skip_pip = False # type: bool
|
self.skip_pip = False # type: bool
|
||||||
|
|
|
@ -65,30 +65,6 @@ def distance(lat1: Optional[float], lon1: Optional[float],
|
||||||
return result * 1000
|
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
|
# Author: https://github.com/maurycyp
|
||||||
# Source: https://github.com/maurycyp/vincenty
|
# Source: https://github.com/maurycyp/vincenty
|
||||||
# License: https://github.com/maurycyp/vincenty/blob/master/LICENSE
|
# License: https://github.com/maurycyp/vincenty/blob/master/LICENSE
|
||||||
|
|
|
@ -1,24 +1,31 @@
|
||||||
"""Test hassbian config."""
|
"""Test hassbian config."""
|
||||||
import asyncio
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.bootstrap import async_setup_component
|
from homeassistant.bootstrap import async_setup_component
|
||||||
from homeassistant.components import config
|
from homeassistant.components import config
|
||||||
from homeassistant.components.websocket_api.const import TYPE_RESULT
|
from homeassistant.components.websocket_api.const import TYPE_RESULT
|
||||||
from homeassistant.const import CONF_UNIT_SYSTEM, CONF_UNIT_SYSTEM_IMPERIAL
|
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
|
from tests.common import mock_coro
|
||||||
|
|
||||||
ORIG_TIME_ZONE = dt_util.DEFAULT_TIME_ZONE
|
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):
|
async def test_validate_config_ok(hass, hass_client):
|
||||||
"""Test checking config."""
|
"""Test checking config."""
|
||||||
with patch.object(config, 'SECTIONS', ['core']):
|
with patch.object(config, 'SECTIONS', ['core']):
|
||||||
await async_setup_component(hass, 'config', {})
|
await async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
await asyncio.sleep(0.1, loop=hass.loop)
|
|
||||||
|
|
||||||
client = await hass_client()
|
client = await hass_client()
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
|
@ -42,11 +49,8 @@ async def test_validate_config_ok(hass, hass_client):
|
||||||
assert result['errors'] == 'beer'
|
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."""
|
"""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.latitude != 60
|
||||||
assert hass.config.longitude != 50
|
assert hass.config.longitude != 50
|
||||||
assert hass.config.elevation != 25
|
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.units.name != CONF_UNIT_SYSTEM_IMPERIAL
|
||||||
assert hass.config.time_zone.zone != 'America/New_York'
|
assert hass.config.time_zone.zone != 'America/New_York'
|
||||||
|
|
||||||
client = await hass_ws_client(hass)
|
|
||||||
await client.send_json({
|
await client.send_json({
|
||||||
'id': 5,
|
'id': 5,
|
||||||
'type': 'config/core/update',
|
'type': 'config/core/update',
|
||||||
|
@ -92,7 +95,7 @@ async def test_websocket_core_update_not_admin(
|
||||||
await client.send_json({
|
await client.send_json({
|
||||||
'id': 6,
|
'id': 6,
|
||||||
'type': 'config/core/update',
|
'type': 'config/core/update',
|
||||||
'latitude': 123,
|
'latitude': 23,
|
||||||
})
|
})
|
||||||
|
|
||||||
msg = await client.receive_json()
|
msg = await client.receive_json()
|
||||||
|
@ -103,16 +106,12 @@ async def test_websocket_core_update_not_admin(
|
||||||
assert msg['error']['code'] == 'unauthorized'
|
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."""
|
"""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({
|
await client.send_json({
|
||||||
'id': 7,
|
'id': 7,
|
||||||
'type': 'config/core/update',
|
'type': 'config/core/update',
|
||||||
'latituude': 123,
|
'latituude': 23,
|
||||||
})
|
})
|
||||||
|
|
||||||
msg = await client.receive_json()
|
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 msg['type'] == TYPE_RESULT
|
||||||
assert not msg['success']
|
assert not msg['success']
|
||||||
assert msg['error']['code'] == 'invalid_format'
|
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',
|
||||||
|
}
|
||||||
|
|
|
@ -44,8 +44,6 @@ def check_real(func):
|
||||||
# Guard a few functions that would make network connections
|
# Guard a few functions that would make network connections
|
||||||
location.async_detect_location_info = \
|
location.async_detect_location_info = \
|
||||||
check_real(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'
|
util.get_local_ip = lambda: '127.0.0.1'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,17 +12,16 @@ import pytest
|
||||||
from voluptuous import MultipleInvalid, Invalid
|
from voluptuous import MultipleInvalid, Invalid
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from homeassistant.core import (
|
from homeassistant.core import SOURCE_STORAGE, HomeAssistantError
|
||||||
DOMAIN, SOURCE_STORAGE, Config, HomeAssistantError)
|
|
||||||
import homeassistant.config as config_util
|
import homeassistant.config as config_util
|
||||||
from homeassistant.loader import async_get_integration
|
from homeassistant.loader import async_get_integration
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ASSUMED_STATE,
|
ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_ASSUMED_STATE,
|
||||||
CONF_LATITUDE, CONF_LONGITUDE, CONF_UNIT_SYSTEM, CONF_NAME,
|
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_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT,
|
||||||
CONF_AUTH_PROVIDERS, CONF_AUTH_MFA_MODULES)
|
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.util.yaml import SECRET_YAML
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.components.config.group import (
|
from homeassistant.components.config.group import (
|
||||||
|
@ -31,12 +30,10 @@ from homeassistant.components.config.automation import (
|
||||||
CONFIG_PATH as AUTOMATIONS_CONFIG_PATH)
|
CONFIG_PATH as AUTOMATIONS_CONFIG_PATH)
|
||||||
from homeassistant.components.config.script import (
|
from homeassistant.components.config.script import (
|
||||||
CONFIG_PATH as SCRIPTS_CONFIG_PATH)
|
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
|
import homeassistant.scripts.check_config as check_config
|
||||||
|
|
||||||
from tests.common import (
|
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()
|
CONFIG_DIR = get_test_config_dir()
|
||||||
YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE)
|
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)
|
GROUP_PATH = os.path.join(CONFIG_DIR, GROUP_CONFIG_PATH)
|
||||||
AUTOMATIONS_PATH = os.path.join(CONFIG_DIR, AUTOMATIONS_CONFIG_PATH)
|
AUTOMATIONS_PATH = os.path.join(CONFIG_DIR, AUTOMATIONS_CONFIG_PATH)
|
||||||
SCRIPTS_PATH = os.path.join(CONFIG_DIR, SCRIPTS_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
|
ORIG_TIMEZONE = dt_util.DEFAULT_TIME_ZONE
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,20 +73,16 @@ def teardown():
|
||||||
if os.path.isfile(SCRIPTS_PATH):
|
if os.path.isfile(SCRIPTS_PATH):
|
||||||
os.remove(SCRIPTS_PATH)
|
os.remove(SCRIPTS_PATH)
|
||||||
|
|
||||||
if os.path.isfile(CUSTOMIZE_PATH):
|
|
||||||
os.remove(CUSTOMIZE_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_create_default_config(hass):
|
async def test_create_default_config(hass):
|
||||||
"""Test creation of default config."""
|
"""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(YAML_PATH)
|
||||||
assert os.path.isfile(SECRET_PATH)
|
assert os.path.isfile(SECRET_PATH)
|
||||||
assert os.path.isfile(VERSION_PATH)
|
assert os.path.isfile(VERSION_PATH)
|
||||||
assert os.path.isfile(GROUP_PATH)
|
assert os.path.isfile(GROUP_PATH)
|
||||||
assert os.path.isfile(AUTOMATIONS_PATH)
|
assert os.path.isfile(AUTOMATIONS_PATH)
|
||||||
assert os.path.isfile(CUSTOMIZE_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
def test_find_config_file_yaml():
|
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.
|
If not creates a new config file.
|
||||||
"""
|
"""
|
||||||
with mock.patch('builtins.print') as mock_print:
|
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 os.path.isfile(YAML_PATH)
|
||||||
assert mock_print.called
|
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):
|
async def test_ensure_config_exists_uses_existing_config(hass):
|
||||||
"""Test that calling ensure_config_exists uses existing config."""
|
"""Test that calling ensure_config_exists uses existing config."""
|
||||||
create_file(YAML_PATH)
|
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:
|
with open(YAML_PATH) as f:
|
||||||
content = f.read()
|
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())
|
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):
|
async def test_create_default_config_returns_none_if_write_error(hass):
|
||||||
"""Test the writing of a default configuration.
|
"""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:
|
with mock.patch('builtins.print') as mock_print:
|
||||||
assert await config_util.async_create_default_config(
|
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
|
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):
|
async def test_loading_configuration_from_storage(hass, hass_storage):
|
||||||
"""Test loading core config onto hass object."""
|
"""Test loading core config onto hass object."""
|
||||||
hass_storage["homeassistant.core_config"] = {
|
hass_storage["core.config"] = {
|
||||||
'data': {
|
'data': {
|
||||||
'elevation': 10,
|
'elevation': 10,
|
||||||
'latitude': 55,
|
'latitude': 55,
|
||||||
|
@ -427,7 +387,7 @@ async def test_loading_configuration_from_storage(hass, hass_storage):
|
||||||
'time_zone': 'Europe/Copenhagen',
|
'time_zone': 'Europe/Copenhagen',
|
||||||
'unit_system': 'metric'
|
'unit_system': 'metric'
|
||||||
},
|
},
|
||||||
'key': 'homeassistant.core_config',
|
'key': 'core.config',
|
||||||
'version': 1
|
'version': 1
|
||||||
}
|
}
|
||||||
await config_util.async_process_ha_core_config(
|
await config_util.async_process_ha_core_config(
|
||||||
|
@ -455,23 +415,23 @@ async def test_updating_configuration(hass, hass_storage):
|
||||||
'time_zone': 'Europe/Copenhagen',
|
'time_zone': 'Europe/Copenhagen',
|
||||||
'unit_system': 'metric'
|
'unit_system': 'metric'
|
||||||
},
|
},
|
||||||
'key': 'homeassistant.core_config',
|
'key': 'core.config',
|
||||||
'version': 1
|
'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(
|
await config_util.async_process_ha_core_config(
|
||||||
hass, {'whitelist_external_dirs': '/tmp'})
|
hass, {'whitelist_external_dirs': '/tmp'})
|
||||||
await hass.config.update(latitude=50)
|
await hass.config.update(latitude=50)
|
||||||
|
|
||||||
new_core_data = copy.deepcopy(core_data)
|
new_core_data = copy.deepcopy(core_data)
|
||||||
new_core_data['data']['latitude'] = 50
|
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
|
assert hass.config.latitude == 50
|
||||||
|
|
||||||
|
|
||||||
async def test_override_stored_configuration(hass, hass_storage):
|
async def test_override_stored_configuration(hass, hass_storage):
|
||||||
"""Test loading core and YAML config onto hass object."""
|
"""Test loading core and YAML config onto hass object."""
|
||||||
hass_storage["homeassistant.core_config"] = {
|
hass_storage["core.config"] = {
|
||||||
'data': {
|
'data': {
|
||||||
'elevation': 10,
|
'elevation': 10,
|
||||||
'latitude': 55,
|
'latitude': 55,
|
||||||
|
@ -480,7 +440,7 @@ async def test_override_stored_configuration(hass, hass_storage):
|
||||||
'time_zone': 'Europe/Copenhagen',
|
'time_zone': 'Europe/Copenhagen',
|
||||||
'unit_system': 'metric'
|
'unit_system': 'metric'
|
||||||
},
|
},
|
||||||
'key': 'homeassistant.core_config',
|
'key': 'core.config',
|
||||||
'version': 1
|
'version': 1
|
||||||
}
|
}
|
||||||
await config_util.async_process_ha_core_config(hass, {
|
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(
|
@asynctest.mock.patch(
|
||||||
'homeassistant.scripts.check_config.check_ha_config_file')
|
'homeassistant.scripts.check_config.check_ha_config_file')
|
||||||
async def test_check_ha_config_file_correct(mock_check, hass):
|
async def test_check_ha_config_file_correct(mock_check, hass):
|
||||||
|
|
|
@ -891,17 +891,17 @@ class TestConfig(unittest.TestCase):
|
||||||
"""Test as dict."""
|
"""Test as dict."""
|
||||||
self.config.config_dir = '/tmp/ha-config'
|
self.config.config_dir = '/tmp/ha-config'
|
||||||
expected = {
|
expected = {
|
||||||
'latitude': None,
|
'latitude': 0,
|
||||||
'longitude': None,
|
'longitude': 0,
|
||||||
'elevation': None,
|
'elevation': 0,
|
||||||
CONF_UNIT_SYSTEM: METRIC_SYSTEM.as_dict(),
|
CONF_UNIT_SYSTEM: METRIC_SYSTEM.as_dict(),
|
||||||
'location_name': None,
|
'location_name': "Home",
|
||||||
'time_zone': 'UTC',
|
'time_zone': 'UTC',
|
||||||
'components': set(),
|
'components': set(),
|
||||||
'config_dir': '/tmp/ha-config',
|
'config_dir': '/tmp/ha-config',
|
||||||
'whitelist_external_dirs': set(),
|
'whitelist_external_dirs': set(),
|
||||||
'version': __version__,
|
'version': __version__,
|
||||||
'config_source': None,
|
'config_source': "default",
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == self.config.as_dict()
|
assert expected == self.config.as_dict()
|
||||||
|
|
|
@ -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):
|
async def test_detect_location_info_both_queries_fail(session):
|
||||||
"""Ensure we return None if both queries fail."""
|
"""Ensure we return None if both queries fail."""
|
||||||
with patch('homeassistant.util.location.async_get_elevation',
|
with patch('homeassistant.util.location._get_ipapi',
|
||||||
return_value=mock_coro(0)), \
|
return_value=mock_coro(None)), \
|
||||||
patch('homeassistant.util.location._get_ipapi',
|
|
||||||
return_value=mock_coro(None)), \
|
|
||||||
patch('homeassistant.util.location._get_ip_api',
|
patch('homeassistant.util.location._get_ip_api',
|
||||||
return_value=mock_coro(None)):
|
return_value=mock_coro(None)):
|
||||||
info = await location_util.async_detect_location_info(
|
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."""
|
"""Test ip api query when the request to API fails."""
|
||||||
info = await location_util._get_ip_api(raising_session)
|
info = await location_util._get_ip_api(raising_session)
|
||||||
assert info is None
|
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
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue