mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 18:27:09 +00:00
Load HA core config from storage (#23872)
* Load HA core config from storage * Tweak * Lint, review comments * Fix test * Add tests * Lint * Address comments
This commit is contained in:
parent
9be384690a
commit
c2fc8a0d61
@ -50,6 +50,13 @@ FILE_MIGRATION = (
|
|||||||
('ios.conf', '.ios.conf'),
|
('ios.conf', '.ios.conf'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CORE_STORAGE_KEY = 'homeassistant.core_config'
|
||||||
|
CORE_STORAGE_VERSION = 1
|
||||||
|
|
||||||
|
SOURCE_DISCOVERED = 'discovered'
|
||||||
|
SOURCE_STORAGE = 'storage'
|
||||||
|
SOURCE_YAML = 'yaml'
|
||||||
|
|
||||||
DEFAULT_CORE_CONFIG = (
|
DEFAULT_CORE_CONFIG = (
|
||||||
# Tuples (attribute, default, auto detect property, description)
|
# Tuples (attribute, default, auto detect property, description)
|
||||||
(CONF_NAME, 'Home', None, 'Name of the location where Home Assistant is '
|
(CONF_NAME, 'Home', None, 'Name of the location where Home Assistant is '
|
||||||
@ -473,6 +480,42 @@ def _format_config_error(ex: vol.Invalid, domain: str, config: Dict) -> str:
|
|||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
def _set_time_zone(hass: HomeAssistant, time_zone_str: Optional[str]) -> None:
|
||||||
|
"""Help to set the time zone."""
|
||||||
|
if time_zone_str is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
time_zone = date_util.get_time_zone(time_zone_str)
|
||||||
|
|
||||||
|
if time_zone:
|
||||||
|
hass.config.time_zone = time_zone
|
||||||
|
date_util.set_default_time_zone(time_zone)
|
||||||
|
else:
|
||||||
|
_LOGGER.error("Received invalid time zone %s", time_zone_str)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_load_ha_core_config(hass: HomeAssistant) -> None:
|
||||||
|
"""Store [homeassistant] core config."""
|
||||||
|
store = hass.helpers.storage.Store(CORE_STORAGE_VERSION, CORE_STORAGE_KEY,
|
||||||
|
private=True)
|
||||||
|
data = await store.async_load()
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
|
||||||
|
hac = hass.config
|
||||||
|
hac.config_source = SOURCE_STORAGE
|
||||||
|
hac.latitude = data['latitude']
|
||||||
|
hac.longitude = data['longitude']
|
||||||
|
hac.elevation = data['elevation']
|
||||||
|
unit_system = data['unit_system']
|
||||||
|
if unit_system == CONF_UNIT_SYSTEM_IMPERIAL:
|
||||||
|
hac.units = IMPERIAL_SYSTEM
|
||||||
|
else:
|
||||||
|
hac.units = METRIC_SYSTEM
|
||||||
|
hac.location_name = data['location_name']
|
||||||
|
_set_time_zone(hass, data['time_zone'])
|
||||||
|
|
||||||
|
|
||||||
async def async_process_ha_core_config(
|
async def async_process_ha_core_config(
|
||||||
hass: HomeAssistant, config: Dict,
|
hass: HomeAssistant, config: Dict,
|
||||||
api_password: Optional[str] = None,
|
api_password: Optional[str] = None,
|
||||||
@ -511,20 +554,14 @@ async def async_process_ha_core_config(
|
|||||||
auth_conf,
|
auth_conf,
|
||||||
mfa_conf))
|
mfa_conf))
|
||||||
|
|
||||||
|
await async_load_ha_core_config(hass)
|
||||||
|
|
||||||
hac = hass.config
|
hac = hass.config
|
||||||
|
|
||||||
def set_time_zone(time_zone_str: Optional[str]) -> None:
|
if any([k in config for k in [
|
||||||
"""Help to set the time zone."""
|
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_ELEVATION,
|
||||||
if time_zone_str is None:
|
CONF_TIME_ZONE, CONF_UNIT_SYSTEM]]):
|
||||||
return
|
hac.config_source = SOURCE_YAML
|
||||||
|
|
||||||
time_zone = date_util.get_time_zone(time_zone_str)
|
|
||||||
|
|
||||||
if time_zone:
|
|
||||||
hac.time_zone = time_zone
|
|
||||||
date_util.set_default_time_zone(time_zone)
|
|
||||||
else:
|
|
||||||
_LOGGER.error("Received invalid time zone %s", time_zone_str)
|
|
||||||
|
|
||||||
for key, attr in ((CONF_LATITUDE, 'latitude'),
|
for key, attr in ((CONF_LATITUDE, 'latitude'),
|
||||||
(CONF_LONGITUDE, 'longitude'),
|
(CONF_LONGITUDE, 'longitude'),
|
||||||
@ -533,7 +570,7 @@ async def async_process_ha_core_config(
|
|||||||
if key in config:
|
if key in config:
|
||||||
setattr(hac, attr, config[key])
|
setattr(hac, attr, config[key])
|
||||||
|
|
||||||
set_time_zone(config.get(CONF_TIME_ZONE))
|
_set_time_zone(hass, config.get(CONF_TIME_ZONE))
|
||||||
|
|
||||||
# Init whitelist external dir
|
# Init whitelist external dir
|
||||||
hac.whitelist_external_dirs = {hass.config.path('www')}
|
hac.whitelist_external_dirs = {hass.config.path('www')}
|
||||||
@ -591,6 +628,7 @@ async def async_process_ha_core_config(
|
|||||||
# If we miss some of the needed values, auto detect them
|
# If we miss some of the needed values, auto detect them
|
||||||
if None in (hac.latitude, hac.longitude, hac.units,
|
if None in (hac.latitude, hac.longitude, hac.units,
|
||||||
hac.time_zone):
|
hac.time_zone):
|
||||||
|
hac.config_source = SOURCE_DISCOVERED
|
||||||
info = await loc_util.async_detect_location_info(
|
info = await loc_util.async_detect_location_info(
|
||||||
hass.helpers.aiohttp_client.async_get_clientsession()
|
hass.helpers.aiohttp_client.async_get_clientsession()
|
||||||
)
|
)
|
||||||
@ -613,7 +651,7 @@ async def async_process_ha_core_config(
|
|||||||
discovered.append(('name', info.city))
|
discovered.append(('name', info.city))
|
||||||
|
|
||||||
if hac.time_zone is None:
|
if hac.time_zone is None:
|
||||||
set_time_zone(info.time_zone)
|
_set_time_zone(hass, info.time_zone)
|
||||||
discovered.append(('time_zone', info.time_zone))
|
discovered.append(('time_zone', info.time_zone))
|
||||||
|
|
||||||
if hac.elevation is None and hac.latitude is not None and \
|
if hac.elevation is None and hac.latitude is not None and \
|
||||||
@ -630,6 +668,24 @@ async def async_process_ha_core_config(
|
|||||||
", ".join('{}: {}'.format(key, val) for key, val in discovered))
|
", ".join('{}: {}'.format(key, val) for key, val in discovered))
|
||||||
|
|
||||||
|
|
||||||
|
async def async_store_ha_core_config(hass: HomeAssistant) -> None:
|
||||||
|
"""Store [homeassistant] core config."""
|
||||||
|
config = hass.config.as_dict()
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'latitude': config['latitude'],
|
||||||
|
'longitude': config['longitude'],
|
||||||
|
'elevation': config['elevation'],
|
||||||
|
'unit_system': hass.config.units.name,
|
||||||
|
'location_name': config['location_name'],
|
||||||
|
'time_zone': config['time_zone'],
|
||||||
|
}
|
||||||
|
|
||||||
|
store = hass.helpers.storage.Store(CORE_STORAGE_VERSION, CORE_STORAGE_KEY,
|
||||||
|
private=True)
|
||||||
|
await store.async_save(data)
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
"""Log an error while merging packages."""
|
"""Log an error while merging packages."""
|
||||||
|
@ -1177,6 +1177,8 @@ class Config:
|
|||||||
self.time_zone = None # type: Optional[datetime.tzinfo]
|
self.time_zone = None # type: Optional[datetime.tzinfo]
|
||||||
self.units = METRIC_SYSTEM # type: UnitSystem
|
self.units = METRIC_SYSTEM # type: UnitSystem
|
||||||
|
|
||||||
|
self.config_source = None # type: Optional[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
|
||||||
|
|
||||||
@ -1251,7 +1253,8 @@ class Config:
|
|||||||
'components': self.components,
|
'components': self.components,
|
||||||
'config_dir': self.config_dir,
|
'config_dir': self.config_dir,
|
||||||
'whitelist_external_dirs': self.whitelist_external_dirs,
|
'whitelist_external_dirs': self.whitelist_external_dirs,
|
||||||
'version': __version__
|
'version': __version__,
|
||||||
|
'config_source': self.config_source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -414,6 +414,64 @@ def test_migrate_no_file_on_upgrade(mock_os, mock_shutil, hass):
|
|||||||
assert mock_os.rename.call_count == 0
|
assert mock_os.rename.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_loading_configuration_from_storage(hass, hass_storage):
|
||||||
|
"""Test loading core config onto hass object."""
|
||||||
|
hass_storage["homeassistant.core_config"] = {
|
||||||
|
'data': {
|
||||||
|
'elevation': 10,
|
||||||
|
'latitude': 55,
|
||||||
|
'location_name': 'Home',
|
||||||
|
'longitude': 13,
|
||||||
|
'time_zone': 'Europe/Copenhagen',
|
||||||
|
'unit_system': 'metric'
|
||||||
|
},
|
||||||
|
'key': 'homeassistant.core_config',
|
||||||
|
'version': 1
|
||||||
|
}
|
||||||
|
await config_util.async_process_ha_core_config(
|
||||||
|
hass, {'whitelist_external_dirs': '/tmp'})
|
||||||
|
|
||||||
|
assert hass.config.latitude == 55
|
||||||
|
assert hass.config.longitude == 13
|
||||||
|
assert hass.config.elevation == 10
|
||||||
|
assert hass.config.location_name == 'Home'
|
||||||
|
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
|
||||||
|
assert hass.config.time_zone.zone == 'Europe/Copenhagen'
|
||||||
|
assert len(hass.config.whitelist_external_dirs) == 2
|
||||||
|
assert '/tmp' in hass.config.whitelist_external_dirs
|
||||||
|
assert hass.config.config_source == config_util.SOURCE_STORAGE
|
||||||
|
|
||||||
|
|
||||||
|
async def test_override_stored_configuration(hass, hass_storage):
|
||||||
|
"""Test loading core and YAML config onto hass object."""
|
||||||
|
hass_storage["homeassistant.core_config"] = {
|
||||||
|
'data': {
|
||||||
|
'elevation': 10,
|
||||||
|
'latitude': 55,
|
||||||
|
'location_name': 'Home',
|
||||||
|
'longitude': 13,
|
||||||
|
'time_zone': 'Europe/Copenhagen',
|
||||||
|
'unit_system': 'metric'
|
||||||
|
},
|
||||||
|
'key': 'homeassistant.core_config',
|
||||||
|
'version': 1
|
||||||
|
}
|
||||||
|
await config_util.async_process_ha_core_config(hass, {
|
||||||
|
'latitude': 60,
|
||||||
|
'whitelist_external_dirs': '/tmp',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert hass.config.latitude == 60
|
||||||
|
assert hass.config.longitude == 13
|
||||||
|
assert hass.config.elevation == 10
|
||||||
|
assert hass.config.location_name == 'Home'
|
||||||
|
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
|
||||||
|
assert hass.config.time_zone.zone == 'Europe/Copenhagen'
|
||||||
|
assert len(hass.config.whitelist_external_dirs) == 2
|
||||||
|
assert '/tmp' in hass.config.whitelist_external_dirs
|
||||||
|
assert hass.config.config_source == config_util.SOURCE_YAML
|
||||||
|
|
||||||
|
|
||||||
async def test_loading_configuration(hass):
|
async def test_loading_configuration(hass):
|
||||||
"""Test loading core config onto hass object."""
|
"""Test loading core config onto hass object."""
|
||||||
hass.config = mock.Mock()
|
hass.config = mock.Mock()
|
||||||
@ -436,6 +494,7 @@ async def test_loading_configuration(hass):
|
|||||||
assert hass.config.time_zone.zone == 'America/New_York'
|
assert hass.config.time_zone.zone == 'America/New_York'
|
||||||
assert len(hass.config.whitelist_external_dirs) == 2
|
assert len(hass.config.whitelist_external_dirs) == 2
|
||||||
assert '/tmp' in hass.config.whitelist_external_dirs
|
assert '/tmp' in hass.config.whitelist_external_dirs
|
||||||
|
assert hass.config.config_source == config_util.SOURCE_YAML
|
||||||
|
|
||||||
|
|
||||||
async def test_loading_configuration_temperature_unit(hass):
|
async def test_loading_configuration_temperature_unit(hass):
|
||||||
@ -457,6 +516,7 @@ async def test_loading_configuration_temperature_unit(hass):
|
|||||||
assert hass.config.location_name == 'Huis'
|
assert hass.config.location_name == 'Huis'
|
||||||
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
|
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
|
||||||
assert hass.config.time_zone.zone == 'America/New_York'
|
assert hass.config.time_zone.zone == 'America/New_York'
|
||||||
|
assert hass.config.config_source == config_util.SOURCE_YAML
|
||||||
|
|
||||||
|
|
||||||
async def test_loading_configuration_from_packages(hass):
|
async def test_loading_configuration_from_packages(hass):
|
||||||
@ -515,6 +575,7 @@ async def test_discovering_configuration(mock_detect, mock_elevation, hass):
|
|||||||
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
|
assert hass.config.units.name == CONF_UNIT_SYSTEM_METRIC
|
||||||
assert hass.config.units.is_metric
|
assert hass.config.units.is_metric
|
||||||
assert hass.config.time_zone.zone == 'America/Los_Angeles'
|
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',
|
@asynctest.mock.patch('homeassistant.util.location.async_detect_location_info',
|
||||||
@ -539,6 +600,7 @@ async def test_discovering_configuration_auto_detect_fails(mock_detect,
|
|||||||
assert hass.config.time_zone == blankConfig.time_zone
|
assert hass.config.time_zone == blankConfig.time_zone
|
||||||
assert len(hass.config.whitelist_external_dirs) == 1
|
assert len(hass.config.whitelist_external_dirs) == 1
|
||||||
assert "/test/config/www" in hass.config.whitelist_external_dirs
|
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(
|
||||||
|
@ -900,6 +900,7 @@ class TestConfig(unittest.TestCase):
|
|||||||
'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,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == self.config.as_dict()
|
assert expected == self.config.as_dict()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user