async def async_from_config_file(config_path: str, hass: core.HomeAssistant, verbose: bool = False, skip_pip: bool = True, log_rotate_days: Any = None, log_file: Any = None, log_no_color: bool = False)\ -> Optional[core.HomeAssistant]: """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter. This method is a coroutine. """ # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir if not is_virtual_env(): await async_mount_local_lib_path(config_dir) async_enable_logging(hass, verbose, log_rotate_days, log_file, log_no_color) try: config_dict = await hass.async_add_executor_job( conf_util.load_yaml_config_file, config_path) except HomeAssistantError as err: _LOGGER.error("Error loading %s: %s", config_path, err) return None finally: clear_secret_cache() return await async_from_config_dict( config_dict, hass, enable_log=False, skip_pip=skip_pip)
def setUp(self): """Create & load secrets file.""" config_dir = get_test_config_dir() yaml.clear_secret_cache() self._yaml_path = os.path.join(config_dir, YAML_CONFIG_FILE) self._secret_path = os.path.join(config_dir, yaml.SECRET_YAML) self._sub_folder_path = os.path.join(config_dir, "subFolder") self._unrelated_path = os.path.join(config_dir, "unrelated") load_yaml( self._secret_path, "http_pw: pwhttp\n" "comp1_un: un1\n" "comp1_pw: pw1\n" "stale_pw: not_used\n" "logger: debug\n", ) self._yaml = load_yaml( self._yaml_path, "http:\n" " api_password: !secret http_pw\n" "component:\n" " username: !secret comp1_un\n" " password: !secret comp1_pw\n" "", )
def test_bad_logger_value(self, mock_error): """Ensure logger: debug was removed.""" yaml.clear_secret_cache() load_yaml(self._secret_path, 'logger: info\npw: abc') load_yaml(self._yaml_path, 'api_password: !secret pw') assert mock_error.call_count == 1, \ "Expected an error about logger: value"
def setUp(self): # pylint: disable=invalid-name """Create & load secrets file.""" config_dir = get_test_config_dir() yaml.clear_secret_cache() self._yaml_path = os.path.join(config_dir, config_util.YAML_CONFIG_FILE) self._secret_path = os.path.join(config_dir, yaml._SECRET_YAML) self._sub_folder_path = os.path.join(config_dir, 'subFolder') if not os.path.exists(self._sub_folder_path): os.makedirs(self._sub_folder_path) self._unrelated_path = os.path.join(config_dir, 'unrelated') if not os.path.exists(self._unrelated_path): os.makedirs(self._unrelated_path) load_yaml(self._secret_path, 'http_pw: pwhttp\n' 'comp1_un: un1\n' 'comp1_pw: pw1\n' 'stale_pw: not_used\n' 'logger: debug\n') self._yaml = load_yaml(self._yaml_path, 'http:\n' ' api_password: !secret http_pw\n' 'component:\n' ' username: !secret comp1_un\n' ' password: !secret comp1_pw\n' '')
def setUp(self): # pylint: disable=invalid-name """Create & load secrets file.""" config_dir = get_test_config_dir() yaml.clear_secret_cache() self._yaml_path = os.path.join(config_dir, config_util.YAML_CONFIG_FILE) self._secret_path = os.path.join(config_dir, yaml._SECRET_YAML) self._sub_folder_path = os.path.join(config_dir, 'subFolder') if not os.path.exists(self._sub_folder_path): os.makedirs(self._sub_folder_path) self._unrelated_path = os.path.join(config_dir, 'unrelated') if not os.path.exists(self._unrelated_path): os.makedirs(self._unrelated_path) load_yaml( self._secret_path, 'http_pw: pwhttp\n' 'comp1_un: un1\n' 'comp1_pw: pw1\n' 'stale_pw: not_used\n' 'logger: debug\n') self._yaml = load_yaml( self._yaml_path, 'http:\n' ' api_password: !secret http_pw\n' 'component:\n' ' username: !secret comp1_un\n' ' password: !secret comp1_pw\n' '')
def from_config_file(config_path: str, hass: Optional[core.HomeAssistant] = None, verbose: bool = False, skip_pip: bool = True, log_rotate_days: Any = None): """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter if given, instantiates a new Home Assistant object if 'hass' is not given. """ if hass is None: hass = core.HomeAssistant() # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir mount_local_lib_path(config_dir) enable_logging(hass, verbose, log_rotate_days) try: config_dict = conf_util.load_yaml_config_file(config_path) except HomeAssistantError: return None finally: clear_secret_cache() return from_config_dict(config_dict, hass, enable_log=False, skip_pip=skip_pip)
def async_from_config_file(config_path: str, hass: core.HomeAssistant, verbose: bool=False, skip_pip: bool=True, log_rotate_days: Any=None): """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter. This method is a coroutine. """ # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir yield from hass.loop.run_in_executor( None, mount_local_lib_path, config_dir) async_enable_logging(hass, verbose, log_rotate_days) try: config_dict = yield from hass.loop.run_in_executor( None, conf_util.load_yaml_config_file, config_path) except HomeAssistantError as err: _LOGGER.error('Error loading %s: %s', config_path, err) return None finally: clear_secret_cache() hass = yield from async_from_config_dict( config_dict, hass, enable_log=False, skip_pip=skip_pip) return hass
def from_config_file( config_path: str, hass: Optional[core.HomeAssistant] = None, verbose: bool = False, skip_pip: bool = True, log_rotate_days: Any = None, ): """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter if given, instantiates a new Home Assistant object if 'hass' is not given. """ if hass is None: hass = core.HomeAssistant() # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir mount_local_lib_path(config_dir) enable_logging(hass, verbose, log_rotate_days) try: config_dict = conf_util.load_yaml_config_file(config_path) except HomeAssistantError: return None finally: clear_secret_cache() return from_config_dict(config_dict, hass, enable_log=False, skip_pip=skip_pip)
def async_from_config_file(config_path: str, hass: core.HomeAssistant, verbose: bool = False, skip_pip: bool = True, log_rotate_days: Any = None): """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter. This method is a coroutine. """ # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir yield from hass.async_add_job(mount_local_lib_path, config_dir) async_enable_logging(hass, verbose, log_rotate_days) try: config_dict = yield from hass.async_add_job( conf_util.load_yaml_config_file, config_path) except HomeAssistantError as err: _LOGGER.error('Error loading %s: %s', config_path, err) return None finally: clear_secret_cache() hass = yield from async_from_config_dict(config_dict, hass, enable_log=False, skip_pip=skip_pip) return hass
async def async_setup_hass( *, config_dir: str, verbose: bool, log_rotate_days: int, log_file: str, log_no_color: bool, skip_pip: bool, safe_mode: bool, ) -> Optional[core.HomeAssistant]: """Set up Home Assistant.""" hass = core.HomeAssistant() hass.config.config_dir = config_dir async_enable_logging(hass, verbose, log_rotate_days, log_file, log_no_color) hass.config.skip_pip = skip_pip if skip_pip: _LOGGER.warning( "Skipping pip installation of required modules. This may cause issues" ) if not await conf_util.async_ensure_config_exists(hass): _LOGGER.error("Error getting configuration path") return None _LOGGER.info("Config directory: %s", config_dir) config_dict = None if not safe_mode: await hass.async_add_executor_job(conf_util.process_ha_config_upgrade, hass) try: config_dict = await conf_util.async_hass_config_yaml(hass) except HomeAssistantError as err: _LOGGER.error( "Failed to parse configuration.yaml: %s. Falling back to safe mode", err, ) else: if not is_virtual_env(): await async_mount_local_lib_path(config_dir) await async_from_config_dict(config_dict, hass) finally: clear_secret_cache() if safe_mode or config_dict is None: _LOGGER.info("Starting in safe mode") http_conf = (await http.async_get_last_config(hass)) or {} await async_from_config_dict( {"safe_mode": {}, "http": http_conf}, hass, ) return hass
async def async_from_config_file( config_path: str, hass: core.HomeAssistant, verbose: bool = False, skip_pip: bool = True, log_rotate_days: Any = None, log_file: Any = None, log_no_color: bool = False, ) -> Optional[core.HomeAssistant]: """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter. This method is a coroutine. """ # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir if not is_virtual_env(): await async_mount_local_lib_path(config_dir) async_enable_logging(hass, verbose, log_rotate_days, log_file, log_no_color) await hass.async_add_executor_job(conf_util.process_ha_config_upgrade, hass) # ais config ais_config = str(os.path.dirname(__file__)) ais_config += "/ais-dom-config/configuration.yaml" try: config_dict = await hass.async_add_executor_job( conf_util.load_yaml_config_file, ais_config) except HomeAssistantError as err: _LOGGER.error("Error loading %s: %s", ais_config, err) return None try: user_config_dict = await hass.async_add_executor_job( conf_util.load_yaml_config_file, config_path) except HomeAssistantError as err: _LOGGER.error("Error loading %s: %s", config_path, err) # return None finally: clear_secret_cache() try: import homeassistant.components.ais_dom.ais_utils as ais_utils ais_utils.dict_merge(config_dict, user_config_dict) except: _LOGGER.error("Error loading user customize") return await async_from_config_dict(config_dict, hass, enable_log=False, skip_pip=skip_pip)
def tearDown(self): # pylint: disable=invalid-name """Clean up secrets.""" yaml.clear_secret_cache() for path in [self._yaml_path, self._secret_path, os.path.join(self._sub_folder_path, 'sub.yaml'), os.path.join(self._sub_folder_path, yaml._SECRET_YAML), os.path.join(self._unrelated_path, yaml._SECRET_YAML)]: if os.path.isfile(path): os.remove(path)
def tearDown(self): # pylint: disable=invalid-name """Clean up secrets.""" yaml.clear_secret_cache() for path in [ self._yaml_path, self._secret_path, os.path.join(self._sub_folder_path, 'sub.yaml'), os.path.join(self._sub_folder_path, yaml._SECRET_YAML), os.path.join(self._unrelated_path, yaml._SECRET_YAML) ]: if os.path.isfile(path): os.remove(path)
def test_secrets_are_not_dict(self): """Did secrets handle non-dict file.""" FILES[self._secret_path] = ('- http_pw: pwhttp\n' ' comp1_un: un1\n' ' comp1_pw: pw1\n') yaml.clear_secret_cache() with pytest.raises(HomeAssistantError): load_yaml( self._yaml_path, 'http:\n' ' api_password: !secret http_pw\n' 'component:\n' ' username: !secret comp1_un\n' ' password: !secret comp1_pw\n' '')
def test_secrets_are_not_dict(self): """Did secrets handle non-dict file.""" FILES[self._secret_path] = ( '- http_pw: pwhttp\n' ' comp1_un: un1\n' ' comp1_pw: pw1\n') yaml.clear_secret_cache() with pytest.raises(HomeAssistantError): load_yaml(self._yaml_path, 'http:\n' ' api_password: !secret http_pw\n' 'component:\n' ' username: !secret comp1_un\n' ' password: !secret comp1_pw\n' '')
def setUp(self): """Create & load secrets file.""" config_dir = get_test_config_dir() yaml.clear_secret_cache() self._yaml_path = os.path.join(config_dir, YAML_CONFIG_FILE) self._secret_path = os.path.join(config_dir, yaml.SECRET_YAML) self._sub_folder_path = os.path.join(config_dir, 'subFolder') self._unrelated_path = os.path.join(config_dir, 'unrelated') load_yaml(self._secret_path, 'http_pw: pwhttp\n' 'comp1_un: un1\n' 'comp1_pw: pw1\n' 'stale_pw: not_used\n' 'logger: debug\n') self._yaml = load_yaml(self._yaml_path, 'http:\n' ' api_password: !secret http_pw\n' 'component:\n' ' username: !secret comp1_un\n' ' password: !secret comp1_pw\n' '')
def tearDown(self): # pylint: disable=invalid-name """Clean up secrets.""" yaml.clear_secret_cache() FILES.clear()
def tearDown(self): """Clean up secrets.""" yaml.clear_secret_cache() FILES.clear()
async def async_setup_hass( *, config_dir: str, verbose: bool, log_rotate_days: int, log_file: str, log_no_color: bool, skip_pip: bool, safe_mode: bool, ) -> Optional[core.HomeAssistant]: """Set up Home Assistant.""" hass = core.HomeAssistant() hass.config.config_dir = config_dir async_enable_logging(hass, verbose, log_rotate_days, log_file, log_no_color) hass.config.skip_pip = skip_pip if skip_pip: _LOGGER.warning( "Skipping pip installation of required modules. This may cause issues" ) if not await conf_util.async_ensure_config_exists(hass): _LOGGER.error("Error getting configuration path") return None _LOGGER.info("Config directory: %s", config_dir) config_dict = None basic_setup_success = False if not safe_mode: await hass.async_add_executor_job(conf_util.process_ha_config_upgrade, hass) try: config_dict = await conf_util.async_hass_config_yaml(hass) except HomeAssistantError as err: _LOGGER.error( "Failed to parse configuration.yaml: %s. Activating safe mode", err, ) else: if not is_virtual_env(): await async_mount_local_lib_path(config_dir) basic_setup_success = ( await async_from_config_dict(config_dict, hass) is not None ) finally: clear_secret_cache() if config_dict is None: safe_mode = True elif not basic_setup_success: _LOGGER.warning("Unable to set up core integrations. Activating safe mode") safe_mode = True elif ( "frontend" in hass.data.get(DATA_SETUP, {}) and "frontend" not in hass.config.components ): _LOGGER.warning("Detected that frontend did not load. Activating safe mode") # Ask integrations to shut down. It's messy but we can't # do a clean stop without knowing what is broken hass.async_track_tasks() hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP, {}) with contextlib.suppress(asyncio.TimeoutError): async with timeout(10): await hass.async_block_till_done() safe_mode = True hass = core.HomeAssistant() hass.config.config_dir = config_dir if safe_mode: _LOGGER.info("Starting in safe mode") hass.config.safe_mode = True http_conf = (await http.async_get_last_config(hass)) or {} await async_from_config_dict( {"safe_mode": {}, "http": http_conf}, hass, ) return hass
async def check_ha_config_file(hass): """Check if Home Assistant configuration file is valid.""" config_dir = hass.config.config_dir result = HomeAssistantConfig() def _pack_error(package, component, config, message): """Handle errors from packages: _log_pkg_error.""" message = "Package {} setup failed. Component {} {}".format( package, component, message) domain = 'homeassistant.packages.{}.{}'.format(package, component) pack_config = core_config[CONF_PACKAGES].get(package, config) result.add_error(message, domain, pack_config) def _comp_error(ex, domain, config): """Handle errors from components: async_log_exception.""" result.add_error(_format_config_error(ex, domain, config), domain, config) # Load configuration.yaml try: config_path = await hass.async_add_executor_job( find_config_file, config_dir) if not config_path: return result.add_error("File configuration.yaml not found.") config = await hass.async_add_executor_job(load_yaml_config_file, config_path) except HomeAssistantError as err: return result.add_error("Error loading {}: {}".format( config_path, err)) finally: yaml.clear_secret_cache() # Extract and validate core [homeassistant] config try: core_config = config.pop(CONF_CORE, {}) core_config = CORE_CONFIG_SCHEMA(core_config) result[CONF_CORE] = core_config except vol.Invalid as err: result.add_error(err, CONF_CORE, core_config) core_config = {} # Merge packages await merge_packages_config(hass, config, core_config.get(CONF_PACKAGES, {}), _pack_error) core_config.pop(CONF_PACKAGES, None) # Filter out repeating config sections components = set(key.split(' ')[0] for key in config.keys()) # Process and validate config for domain in components: try: integration = await loader.async_get_integration(hass, domain) except loader.IntegrationNotFound: result.add_error("Integration not found: {}".format(domain)) continue try: component = integration.get_component() except ImportError: result.add_error("Component not found: {}".format(domain)) continue if (not hass.config.skip_pip and integration.requirements and not await requirements.async_process_requirements( hass, integration.domain, integration.requirements)): result.add_error("Unable to install all requirements: {}".format( ', '.join(integration.requirements))) continue if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) result[domain] = config[domain] except vol.Invalid as ex: _comp_error(ex, domain, config) continue component_platform_schema = getattr( component, 'PLATFORM_SCHEMA_BASE', getattr(component, 'PLATFORM_SCHEMA', None)) if component_platform_schema is None: continue platforms = [] for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: p_validated = component_platform_schema( # type: ignore p_config) except vol.Invalid as ex: _comp_error(ex, domain, config) continue # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform # (the automation component is one of them) if p_name is None: platforms.append(p_validated) continue try: p_integration = await loader.async_get_integration( hass, p_name) except loader.IntegrationNotFound: result.add_error( "Integration {} not found when trying to verify its {} " "platform.".format(p_name, domain)) continue try: platform = p_integration.get_platform(domain) except ImportError: result.add_error("Platform not found: {}.{}".format( domain, p_name)) continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: _comp_error(ex, '{}.{}'.format(domain, p_name), p_validated) continue platforms.append(p_validated) # Remove config for current component and add validated config back in. for filter_comp in extract_domain_configs(config, domain): del config[filter_comp] result[domain] = platforms return result
def check_ha_config_file(hass): """Check if Home Assistant configuration file is valid.""" config_dir = hass.config.config_dir result = HomeAssistantConfig() def _pack_error(package, component, config, message): """Handle errors from packages: _log_pkg_error.""" message = "Package {} setup failed. Component {} {}".format( package, component, message) domain = 'homeassistant.packages.{}.{}'.format(package, component) pack_config = core_config[CONF_PACKAGES].get(package, config) result.add_error(message, domain, pack_config) def _comp_error(ex, domain, config): """Handle errors from components: async_log_exception.""" result.add_error( _format_config_error(ex, domain, config), domain, config) # Load configuration.yaml try: config_path = find_config_file(config_dir) if not config_path: return result.add_error("File configuration.yaml not found.") config = load_yaml_config_file(config_path) except HomeAssistantError as err: return result.add_error( "Error loading {}: {}".format(config_path, err)) finally: yaml.clear_secret_cache() # Extract and validate core [homeassistant] config try: core_config = config.pop(CONF_CORE, {}) core_config = CORE_CONFIG_SCHEMA(core_config) result[CONF_CORE] = core_config except vol.Invalid as err: result.add_error(err, CONF_CORE, core_config) core_config = {} # Merge packages merge_packages_config( hass, config, core_config.get(CONF_PACKAGES, {}), _pack_error) core_config.pop(CONF_PACKAGES, None) # Filter out repeating config sections components = set(key.split(' ')[0] for key in config.keys()) # Process and validate config for domain in components: component = loader.get_component(hass, domain) if not component: result.add_error("Component not found: {}".format(domain)) continue if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) result[domain] = config[domain] except vol.Invalid as ex: _comp_error(ex, domain, config) continue if (not hasattr(component, 'PLATFORM_SCHEMA') and not hasattr(component, 'PLATFORM_SCHEMA_BASE')): continue platforms = [] for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: if hasattr(component, 'PLATFORM_SCHEMA_BASE'): p_validated = \ component.PLATFORM_SCHEMA_BASE( # type: ignore p_config) else: p_validated = component.PLATFORM_SCHEMA( # type: ignore p_config) except vol.Invalid as ex: _comp_error(ex, domain, config) continue # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform # (the automation component is one of them) if p_name is None: platforms.append(p_validated) continue platform = loader.get_platform(hass, domain, p_name) if platform is None: result.add_error( "Platform not found: {}.{}".format(domain, p_name)) continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: _comp_error( ex, '{}.{}'.format(domain, p_name), p_validated) continue platforms.append(p_validated) # Remove config for current component and add validated config back in. for filter_comp in extract_domain_configs(config, domain): del config[filter_comp] result[domain] = platforms return result
def check_ha_config_file(hass): """Check if Home Assistant configuration file is valid.""" config_dir = hass.config.config_dir result = HomeAssistantConfig() def _pack_error(package, component, config, message): """Handle errors from packages: _log_pkg_error.""" message = "Package {} setup failed. Component {} {}".format( package, component, message) domain = 'homeassistant.packages.{}.{}'.format(package, component) pack_config = core_config[CONF_PACKAGES].get(package, config) result.add_error(message, domain, pack_config) def _comp_error(ex, domain, config): """Handle errors from components: async_log_exception.""" result.add_error(_format_config_error(ex, domain, config), domain, config) # Load configuration.yaml try: config_path = find_config_file(config_dir) if not config_path: return result.add_error("File configuration.yaml not found.") config = load_yaml_config_file(config_path) except HomeAssistantError as err: return result.add_error("Error loading {}: {}".format( config_path, err)) finally: yaml.clear_secret_cache() # Extract and validate core [homeassistant] config try: core_config = config.pop(CONF_CORE, {}) core_config = CORE_CONFIG_SCHEMA(core_config) result[CONF_CORE] = core_config except vol.Invalid as err: result.add_error(err, CONF_CORE, core_config) core_config = {} # Merge packages merge_packages_config(hass, config, core_config.get(CONF_PACKAGES, {}), _pack_error) del core_config[CONF_PACKAGES] # Ensure we have no None values after merge for key, value in config.items(): if not value: config[key] = {} # Filter out repeating config sections components = set(key.split(' ')[0] for key in config.keys()) # Process and validate config for domain in components: component = loader.get_component(hass, domain) if not component: result.add_error("Component not found: {}".format(domain)) continue if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) result[domain] = config[domain] except vol.Invalid as ex: _comp_error(ex, domain, config) continue if not hasattr(component, 'PLATFORM_SCHEMA'): continue platforms = [] for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: p_validated = component.PLATFORM_SCHEMA(p_config) except vol.Invalid as ex: _comp_error(ex, domain, config) continue # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform # (the automation component is one of them) if p_name is None: platforms.append(p_validated) continue platform = loader.get_platform(hass, domain, p_name) if platform is None: result.add_error("Platform not found: {}.{}".format( domain, p_name)) continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: _comp_error(ex, '{}.{}'.format(domain, p_name), p_validated) continue platforms.append(p_validated) # Remove config for current component and add validated config back in. for filter_comp in extract_domain_configs(config, domain): del config[filter_comp] result[domain] = platforms return result