Add persistent notifications to bootstrap (#3738)
* Add persistent notifications to bootstrap * Rebase, Fix test
This commit is contained in:
parent
39a446c43c
commit
cb322f72db
4 changed files with 36 additions and 15 deletions
|
@ -32,6 +32,8 @@ _CURRENT_SETUP = []
|
||||||
ATTR_COMPONENT = 'component'
|
ATTR_COMPONENT = 'component'
|
||||||
|
|
||||||
ERROR_LOG_FILENAME = 'home-assistant.log'
|
ERROR_LOG_FILENAME = 'home-assistant.log'
|
||||||
|
_PERSISTENT_PLATFORMS = set()
|
||||||
|
_PERSISTENT_VALIDATION = set()
|
||||||
|
|
||||||
|
|
||||||
def setup_component(hass: core.HomeAssistant, domain: str,
|
def setup_component(hass: core.HomeAssistant, domain: str,
|
||||||
|
@ -149,7 +151,7 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
|
||||||
try:
|
try:
|
||||||
config = component.CONFIG_SCHEMA(config)
|
config = component.CONFIG_SCHEMA(config)
|
||||||
except vol.Invalid as ex:
|
except vol.Invalid as ex:
|
||||||
log_exception(ex, domain, config)
|
log_exception(ex, domain, config, hass)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
elif hasattr(component, 'PLATFORM_SCHEMA'):
|
elif hasattr(component, 'PLATFORM_SCHEMA'):
|
||||||
|
@ -159,7 +161,7 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
|
||||||
try:
|
try:
|
||||||
p_validated = component.PLATFORM_SCHEMA(p_config)
|
p_validated = component.PLATFORM_SCHEMA(p_config)
|
||||||
except vol.Invalid as ex:
|
except vol.Invalid as ex:
|
||||||
log_exception(ex, domain, config)
|
log_exception(ex, domain, config, hass)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Not all platform components follow same pattern for platforms
|
# Not all platform components follow same pattern for platforms
|
||||||
|
@ -181,7 +183,7 @@ def prepare_setup_component(hass: core.HomeAssistant, config: dict,
|
||||||
p_validated = platform.PLATFORM_SCHEMA(p_validated)
|
p_validated = platform.PLATFORM_SCHEMA(p_validated)
|
||||||
except vol.Invalid as ex:
|
except vol.Invalid as ex:
|
||||||
log_exception(ex, '{}.{}'.format(domain, p_name),
|
log_exception(ex, '{}.{}'.format(domain, p_name),
|
||||||
p_validated)
|
p_validated, hass)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
platforms.append(p_validated)
|
platforms.append(p_validated)
|
||||||
|
@ -211,6 +213,13 @@ def prepare_setup_platform(hass: core.HomeAssistant, config, domain: str,
|
||||||
# Not found
|
# Not found
|
||||||
if platform is None:
|
if platform is None:
|
||||||
_LOGGER.error('Unable to find platform %s', platform_path)
|
_LOGGER.error('Unable to find platform %s', platform_path)
|
||||||
|
|
||||||
|
_PERSISTENT_PLATFORMS.add(platform_path)
|
||||||
|
message = ('Unable to find the following platforms: ' +
|
||||||
|
', '.join(list(_PERSISTENT_PLATFORMS)) +
|
||||||
|
'(please check your configuration)')
|
||||||
|
persistent_notification.create(
|
||||||
|
hass, message, 'Invalid platforms', 'platform_errors')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Already loaded
|
# Already loaded
|
||||||
|
@ -257,7 +266,7 @@ def from_config_dict(config: Dict[str, Any],
|
||||||
try:
|
try:
|
||||||
conf_util.process_ha_core_config(hass, core_config)
|
conf_util.process_ha_core_config(hass, core_config)
|
||||||
except vol.Invalid as ex:
|
except vol.Invalid as ex:
|
||||||
log_exception(ex, 'homeassistant', core_config)
|
log_exception(ex, 'homeassistant', core_config, hass)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
conf_util.process_ha_config_upgrade(hass)
|
conf_util.process_ha_config_upgrade(hass)
|
||||||
|
@ -305,6 +314,7 @@ def from_config_dict(config: Dict[str, Any],
|
||||||
hass.loop.run_until_complete(
|
hass.loop.run_until_complete(
|
||||||
hass.loop.run_in_executor(None, component_setup)
|
hass.loop.run_in_executor(None, component_setup)
|
||||||
)
|
)
|
||||||
|
|
||||||
return hass
|
return hass
|
||||||
|
|
||||||
|
|
||||||
|
@ -397,9 +407,16 @@ def _ensure_loader_prepared(hass: core.HomeAssistant) -> None:
|
||||||
loader.prepare(hass)
|
loader.prepare(hass)
|
||||||
|
|
||||||
|
|
||||||
def log_exception(ex, domain, config):
|
def log_exception(ex, domain, config, hass=None):
|
||||||
"""Generate log exception for config validation."""
|
"""Generate log exception for config validation."""
|
||||||
message = 'Invalid config for [{}]: '.format(domain)
|
message = 'Invalid config for [{}]: '.format(domain)
|
||||||
|
if hass is not None:
|
||||||
|
_PERSISTENT_VALIDATION.add(domain)
|
||||||
|
message = ('The following platforms contain invalid configuration: ' +
|
||||||
|
', '.join(list(_PERSISTENT_VALIDATION)) +
|
||||||
|
' (please check your configuration)')
|
||||||
|
persistent_notification.create(
|
||||||
|
hass, message, 'Invalid config', 'invalid_config')
|
||||||
|
|
||||||
if 'extra keys not allowed' in ex.error_message:
|
if 'extra keys not allowed' in ex.error_message:
|
||||||
message += '[{}] is an invalid option for [{}]. Check: {}->{}.'\
|
message += '[{}] is an invalid option for [{}]. Check: {}->{}.'\
|
||||||
|
|
|
@ -199,9 +199,10 @@ def check(config_path):
|
||||||
res['secrets'][node.value] = val
|
res['secrets'][node.value] = val
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def mock_except(ex, domain, config): # pylint: disable=unused-variable
|
def mock_except(ex, domain, config, # pylint: disable=unused-variable
|
||||||
|
hass=None):
|
||||||
"""Mock bootstrap.log_exception."""
|
"""Mock bootstrap.log_exception."""
|
||||||
MOCKS['except'][1](ex, domain, config)
|
MOCKS['except'][1](ex, domain, config, hass)
|
||||||
res['except'][domain] = config.get(domain, config)
|
res['except'][domain] = config.get(domain, config)
|
||||||
|
|
||||||
# Patches to skip functions
|
# Patches to skip functions
|
||||||
|
|
|
@ -74,13 +74,15 @@ class TestCheckConfig(unittest.TestCase):
|
||||||
with patch_yaml_files(files):
|
with patch_yaml_files(files):
|
||||||
res = check_config.check(get_test_config_dir('component.yaml'))
|
res = check_config.check(get_test_config_dir('component.yaml'))
|
||||||
change_yaml_files(res)
|
change_yaml_files(res)
|
||||||
self.assertDictEqual({
|
|
||||||
'components': {},
|
self.assertDictEqual({}, res['components'])
|
||||||
'except': {'http': {'password': 'err123'}},
|
self.assertDictEqual(
|
||||||
'secret_cache': {},
|
{'http': {'password': 'err123'}},
|
||||||
'secrets': {},
|
res['except']
|
||||||
'yaml_files': ['.../component.yaml']
|
)
|
||||||
}, res)
|
self.assertDictEqual({}, res['secret_cache'])
|
||||||
|
self.assertDictEqual({}, res['secrets'])
|
||||||
|
self.assertListEqual(['.../component.yaml'], res['yaml_files'])
|
||||||
|
|
||||||
files = {
|
files = {
|
||||||
'platform.yaml': (BASE_CONFIG + 'mqtt:\n\n'
|
'platform.yaml': (BASE_CONFIG + 'mqtt:\n\n'
|
||||||
|
|
|
@ -269,11 +269,12 @@ class TestBootstrap:
|
||||||
def test_home_assistant_core_config_validation(self):
|
def test_home_assistant_core_config_validation(self):
|
||||||
"""Test if we pass in wrong information for HA conf."""
|
"""Test if we pass in wrong information for HA conf."""
|
||||||
# Extensive HA conf validation testing is done in test_config.py
|
# Extensive HA conf validation testing is done in test_config.py
|
||||||
|
hass = get_test_home_assistant()
|
||||||
assert None is bootstrap.from_config_dict({
|
assert None is bootstrap.from_config_dict({
|
||||||
'homeassistant': {
|
'homeassistant': {
|
||||||
'latitude': 'some string'
|
'latitude': 'some string'
|
||||||
}
|
}
|
||||||
})
|
}, hass=hass)
|
||||||
|
|
||||||
def test_component_setup_with_validation_and_dependency(self):
|
def test_component_setup_with_validation_and_dependency(self):
|
||||||
"""Test all config is passed to dependencies."""
|
"""Test all config is passed to dependencies."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue