def setup(hass, config): """ Setup zone. """ entities = set() for key in extract_domain_configs(config, DOMAIN): entries = config[key] if not isinstance(entries, list): entries = entries, for entry in entries: name = entry.get(CONF_NAME, DEFAULT_NAME) latitude = entry.get(ATTR_LATITUDE) longitude = entry.get(ATTR_LONGITUDE) radius = entry.get(ATTR_RADIUS, DEFAULT_RADIUS) icon = entry.get(ATTR_ICON) if None in (latitude, longitude): logging.getLogger(__name__).error( 'Each zone needs a latitude and longitude.') continue zone = Zone(hass, name, latitude, longitude, radius, icon) zone.entity_id = generate_entity_id(ENTITY_ID_FORMAT, name, entities) zone.update_ha_state() entities.add(zone.entity_id) if ENTITY_ID_HOME not in entities: zone = Zone(hass, hass.config.location_name, hass.config.latitude, hass.config.longitude, DEFAULT_RADIUS, ICON_HOME) zone.entity_id = ENTITY_ID_HOME zone.update_ha_state() return True
def setup(hass, config): """Setup scenes.""" logger = logging.getLogger(__name__) # You are not allowed to mutate the original config so make a copy config = dict(config) for config_key in extract_domain_configs(config, DOMAIN): platform_config = config[config_key] if not isinstance(platform_config, list): platform_config = [platform_config] if not any(CONF_PLATFORM in entry for entry in platform_config): platform_config = [{'platform': 'homeassistant', 'states': entry} for entry in platform_config] config[config_key] = platform_config component = EntityComponent(logger, DOMAIN, hass) component.setup(config) def handle_scene_service(service): """Handle calls to the switch services.""" target_scenes = component.extract_from_service(service) for scene in target_scenes: scene.activate() hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service, schema=SCENE_SERVICE_SCHEMA) return True
def _process_config(hass, config, component): """Process config and add automations.""" success = False for config_key in extract_domain_configs(config, DOMAIN): conf = config[config_key] for list_no, config_block in enumerate(conf): name = config_block.get(CONF_ALIAS) or "{} {}".format(config_key, list_no) hidden = config_block[CONF_HIDE_ENTITY] action = _get_action(hass, config_block.get(CONF_ACTION, {}), name) if CONF_CONDITION in config_block: cond_func = _process_if(hass, config, config_block) if cond_func is None: continue else: def cond_func(variables): """Condition will always pass.""" return True attach_triggers = partial(_process_trigger, hass, config, config_block.get(CONF_TRIGGER, []), name) entity = AutomationEntity(name, attach_triggers, cond_func, action, hidden) component.add_entities((entity,)) success = True return success
def prepare_setup_component(hass: core.HomeAssistant, config: dict, domain: str): """Prepare setup of a component and return processed config.""" # pylint: disable=too-many-return-statements component = loader.get_component(domain) missing_deps = [dep for dep in getattr(component, "DEPENDENCIES", []) if dep not in hass.config.components] if missing_deps: _LOGGER.error("Not initializing %s because not all dependencies loaded: %s", domain, ", ".join(missing_deps)) return None if hasattr(component, "CONFIG_SCHEMA"): try: config = component.CONFIG_SCHEMA(config) except vol.Invalid as ex: log_exception(ex, domain, config, hass) return None elif hasattr(component, "PLATFORM_SCHEMA"): 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: log_exception(ex, domain, config, hass) 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 = prepare_setup_platform(hass, config, domain, p_name) if platform is None: continue # Validate platform specific schema if hasattr(platform, "PLATFORM_SCHEMA"): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: log_exception(ex, "{}.{}".format(domain, p_name), p_validated, hass) continue platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = {key: value for key, value in config.items() if key not in filter_keys} config[domain] = platforms if not _handle_requirements(hass, component, domain): return None return config
def async_process_component_config(hass, config, domain): """Check component configuration and return processed configuration. Returns None on error. This method must be run in the event loop. """ component = get_component(hass, domain) if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) except vol.Invalid as ex: async_log_exception(ex, domain, config, hass) return None elif hasattr(component, 'PLATFORM_SCHEMA'): 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: async_log_exception(ex, domain, config, hass) 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 = get_platform(hass, domain, p_name) if platform is None: continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): # pylint: disable=no-member try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: async_log_exception(ex, '{}.{}'.format(domain, p_name), p_validated, hass) continue platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = {key: value for key, value in config.items() if key not in filter_keys} config[domain] = platforms return config
def test_extract_domain_configs(self): config = { 'zone': None, 'zoner': None, 'zone ': None, 'zone Hallo': None, 'zone 100': None, } self.assertEqual(set(['zone', 'zone Hallo', 'zone 100']), set(helpers.extract_domain_configs(config, 'zone')))
def setup(hass, config): """Setup the automation.""" for config_key in extract_domain_configs(config, DOMAIN): conf = config[config_key] for list_no, config_block in enumerate(conf): name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, list_no)) _setup_automation(hass, config_block, name, config) return True
def test_extract_domain_configs(self): """Test the extraction of domain configuration.""" config = { 'zone': None, 'zoner': None, 'zone ': None, 'zone Hallo': None, 'zone 100': None, } self.assertEqual(set(['zone', 'zone Hallo', 'zone 100']), set(helpers.extract_domain_configs(config, 'zone')))
def test_extract_domain_configs(): """Test the extraction of domain configuration.""" config = { 'zone': None, 'zoner': None, 'zone ': None, 'zone Hallo': None, 'zone 100': None, } assert set(['zone', 'zone Hallo', 'zone 100']) == \ set(helpers.extract_domain_configs(config, 'zone'))
def test_extract_domain_configs(): """Test the extraction of domain configuration.""" config = { "zone": None, "zoner": None, "zone ": None, "zone Hallo": None, "zone 100": None, } assert set(["zone", "zone Hallo", "zone 100" ]) == set(helpers.extract_domain_configs(config, "zone"))
def test_extract_domain_configs(self): """Test the extraction of domain configuration.""" config = { 'zone': None, 'zoner': None, 'zone ': None, 'zone Hallo': None, 'zone 100': None, } self.assertEqual(set(['zone', 'zone Hallo', 'zone 100']), set(helpers.extract_domain_configs(config, 'zone')))
async def _async_process_config(hass, config, component): """Process config and add automations. This method is a coroutine. """ entities = [] for config_key in extract_domain_configs(config, DOMAIN): conf = config[config_key] for list_no, config_block in enumerate(conf): automation_id = config_block.get(CONF_ID) name = config_block.get(CONF_ALIAS) or f"{config_key} {list_no}" hidden = config_block[CONF_HIDE_ENTITY] initial_state = config_block.get(CONF_INITIAL_STATE) action = _async_get_action(hass, config_block.get(CONF_ACTION, {}), name) if CONF_CONDITION in config_block: cond_func = await _async_process_if(hass, config, config_block) if cond_func is None: continue else: def cond_func(variables): """Condition will always pass.""" return True async_attach_triggers = partial( _async_process_trigger, hass, config, config_block.get(CONF_TRIGGER, []), name, ) entity = AutomationEntity( automation_id, name, async_attach_triggers, cond_func, action, hidden, initial_state, ) entities.append(entity) if entities: await component.async_add_entities(entities)
def setup(hass, config): """Setup the automation.""" success = False for config_key in extract_domain_configs(config, DOMAIN): conf = config[config_key] for list_no, config_block in enumerate(conf): name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, list_no)) success = (_setup_automation(hass, config_block, name, config) or success) return success
def setup(hass, config): """Setup the automation.""" for config_key in extract_domain_configs(config, DOMAIN): conf = config[config_key] if not isinstance(conf, list): conf = [conf] for list_no, config_block in enumerate(conf): name = config_block.get(CONF_ALIAS, "{}, {}".format(config_key, list_no)) _setup_automation(hass, config_block, name, config) return True
def _async_process_config(hass, config, component): """Process config and add automations. This method is a coroutine. """ entities = [] tasks = [] for config_key in extract_domain_configs(config, DOMAIN): conf = config[config_key] for list_no, config_block in enumerate(conf): name = config_block.get(CONF_ALIAS) or "{} {}".format( config_key, list_no) hidden = config_block[CONF_HIDE_ENTITY] action = _async_get_action(hass, config_block.get(CONF_ACTION, {}), name) if CONF_CONDITION in config_block: cond_func = _async_process_if(hass, config, config_block) if cond_func is None: continue else: def cond_func(variables): """Condition will always pass.""" return True async_attach_triggers = partial(_async_process_trigger, hass, config, config_block.get(CONF_TRIGGER, []), name) entity = AutomationEntity(name, async_attach_triggers, cond_func, action, hidden) if config_block[CONF_INITIAL_STATE]: tasks.append(entity.async_enable()) entities.append(entity) if tasks: yield from asyncio.wait(tasks, loop=hass.loop) if entities: yield from component.async_add_entities(entities) return len(entities) > 0
async def _async_process_config(hass, config, component): """Process config and add automations. This method is a coroutine. """ entities = [] for config_key in extract_domain_configs(config, DOMAIN): conf = config[config_key] for list_no, config_block in enumerate(conf): automation_id = config_block.get(CONF_ID) name = config_block.get(CONF_ALIAS) or f"{config_key} {list_no}" initial_state = config_block.get(CONF_INITIAL_STATE) action_script = Script( hass, config_block[CONF_ACTION], name, script_mode=config_block[CONF_MODE], max_runs=config_block[CONF_MAX], logger=_LOGGER, ) if CONF_CONDITION in config_block: cond_func = await _async_process_if(hass, config, config_block) if cond_func is None: continue else: cond_func = None entity = AutomationEntity( automation_id, name, config_block[CONF_TRIGGER], cond_func, action_script, initial_state, ) entities.append(entity) if entities: await component.async_add_entities(entities)
def _async_process_config(hass, config, component): """Process config and add automations. This method is a coroutine. """ entities = [] tasks = [] for config_key in extract_domain_configs(config, DOMAIN): conf = config[config_key] for list_no, config_block in enumerate(conf): name = config_block.get(CONF_ALIAS) or "{} {}".format(config_key, list_no) hidden = config_block[CONF_HIDE_ENTITY] action = _async_get_action(hass, config_block.get(CONF_ACTION, {}), name) if CONF_CONDITION in config_block: cond_func = _async_process_if(hass, config, config_block) if cond_func is None: continue else: def cond_func(variables): """Condition will always pass.""" return True async_attach_triggers = partial( _async_process_trigger, hass, config, config_block.get(CONF_TRIGGER, []), name) entity = AutomationEntity(name, async_attach_triggers, cond_func, action, hidden) if config_block[CONF_INITIAL_STATE]: tasks.append(entity.async_enable()) entities.append(entity) if tasks: yield from asyncio.wait(tasks, loop=hass.loop) if entities: yield from component.async_add_entities(entities) return len(entities) > 0
def setup(hass, config): """Setup the .""" def _resume_mp(config): media_player = "media_player." + config.get("media_player", "") resume_state = None resume = False def _state_change(entity_id=None, old_state=None, new_state=None): nonlocal resume_state, resume content_id = hass.states.get(media_player).attributes.get( "media_content_id", []) if resume_state and new_state.state != 'playing' and resume: print(resume_state) data = { ATTR_MEDIA_CONTENT_ID: resume_state.attributes.get("media_content_id", ""), ATTR_MEDIA_CONTENT_TYPE: "audio/mp3", ATTR_ENTITY_ID: media_player, } resume_state = None resume = True hass.services.call(DOMAIN_MP, SERVICE_PLAY_MEDIA, data) elif (new_state.state == 'playing' and ("nrk.no" in content_id or "nrk-mms-live.online.no" in content_id)): resume_state = new_state elif (resume_state and new_state.state == 'playing' and "tts_proxy" in content_id): resume = True else: resume_state = None resume = False track_state_change(hass, media_player, _state_change) for config_key in extract_domain_configs(config, DOMAIN): _resume_mp(config[config_key]) return True
def async_setup(hass, config): """Setup scenes.""" logger = logging.getLogger(__name__) # You are not allowed to mutate the original config so make a copy config = dict(config) for config_key in extract_domain_configs(config, DOMAIN): platform_config = config[config_key] if not isinstance(platform_config, list): platform_config = [platform_config] if not any(CONF_PLATFORM in entry for entry in platform_config): platform_config = [{ 'platform': 'homeassistant', 'states': entry } for entry in platform_config] config[config_key] = platform_config component = EntityComponent(logger, DOMAIN, hass) yield from component.async_setup(config) @asyncio.coroutine def async_handle_scene_service(service): """Handle calls to the switch services.""" target_scenes = component.async_extract_from_service(service) tasks = [scene.async_activate() for scene in target_scenes] if tasks: yield from asyncio.wait(tasks, loop=hass.loop) hass.services.async_register(DOMAIN, SERVICE_TURN_ON, async_handle_scene_service, schema=SCENE_SERVICE_SCHEMA) return True
async def _async_process_config( hass: HomeAssistant, config: dict[str, Any], component: EntityComponent, ) -> bool: """Process config and add automations. Returns if blueprints were used. """ entities = [] blueprints_used = False for config_key in extract_domain_configs(config, DOMAIN): conf: list[dict[str, Any] | blueprint.BlueprintInputs] = config[config_key] for list_no, config_block in enumerate(conf): raw_blueprint_inputs = None raw_config = None if isinstance(config_block, blueprint.BlueprintInputs): blueprints_used = True blueprint_inputs = config_block raw_blueprint_inputs = blueprint_inputs.config_with_inputs try: raw_config = blueprint_inputs.async_substitute() config_block = cast( Dict[str, Any], await async_validate_config_item(hass, raw_config), ) except vol.Invalid as err: LOGGER.error( "Blueprint %s generated invalid automation with inputs %s: %s", blueprint_inputs.blueprint.name, blueprint_inputs.inputs, humanize_error(config_block, err), ) continue else: raw_config = cast(AutomationConfig, config_block).raw_config automation_id = config_block.get(CONF_ID) name = config_block.get(CONF_ALIAS) or f"{config_key} {list_no}" initial_state = config_block.get(CONF_INITIAL_STATE) action_script = Script( hass, config_block[CONF_ACTION], name, DOMAIN, running_description="automation actions", script_mode=config_block[CONF_MODE], max_runs=config_block[CONF_MAX], max_exceeded=config_block[CONF_MAX_EXCEEDED], logger=LOGGER, # We don't pass variables here # Automation will already render them to use them in the condition # and so will pass them on to the script. ) if CONF_CONDITION in config_block: cond_func = await _async_process_if(hass, name, config, config_block) if cond_func is None: continue else: cond_func = None # Add trigger variables to variables variables = None if CONF_TRIGGER_VARIABLES in config_block: variables = ScriptVariables( dict(config_block[CONF_TRIGGER_VARIABLES].as_dict()) ) if CONF_VARIABLES in config_block: if variables: variables.variables.update(config_block[CONF_VARIABLES].as_dict()) else: variables = config_block[CONF_VARIABLES] entity = AutomationEntity( automation_id, name, config_block[CONF_TRIGGER], cond_func, action_script, initial_state, variables, config_block.get(CONF_TRIGGER_VARIABLES), raw_config, raw_blueprint_inputs, config_block[CONF_TRACE], ) entities.append(entity) if entities: await component.async_add_entities(entities) return blueprints_used
def async_process_component_config(hass: HomeAssistant, config: Dict, domain: str) -> Optional[Dict]: """Check component configuration and return processed configuration. Returns None on error. This method must be run in the event loop. """ component = get_component(hass, domain) if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) # type: ignore except vol.Invalid as ex: async_log_exception(ex, domain, config, hass) return None elif hasattr(component, 'PLATFORM_SCHEMA'): 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: async_log_exception(ex, domain, config, hass) 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 = get_platform(hass, domain, p_name) if platform is None: continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): # pylint: disable=no-member try: p_validated = platform.PLATFORM_SCHEMA( # type: ignore p_validated) except vol.Invalid as ex: async_log_exception(ex, '{}.{}'.format(domain, p_name), p_validated, hass) continue platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = { key: value for key, value in config.items() if key not in filter_keys } config[domain] = platforms return config
def async_prepare_setup_component(hass: core.HomeAssistant, config: dict, domain: str): """Prepare setup of a component and return processed config. This method is a coroutine. """ # pylint: disable=too-many-return-statements component = loader.get_component(domain) missing_deps = [ dep for dep in getattr(component, 'DEPENDENCIES', []) if dep not in hass.config.components ] if missing_deps: _LOGGER.error( 'Not initializing %s because not all dependencies loaded: %s', domain, ", ".join(missing_deps)) return None if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) except vol.Invalid as ex: async_log_exception(ex, domain, config, hass) return None elif hasattr(component, 'PLATFORM_SCHEMA'): 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: async_log_exception(ex, domain, config, hass) 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 = yield from async_prepare_setup_platform( hass, config, domain, p_name) if platform is None: continue # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: # pylint: disable=no-member p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.Invalid as ex: async_log_exception(ex, '{}.{}'.format(domain, p_name), p_validated, hass) continue platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = { key: value for key, value in config.items() if key not in filter_keys } config[domain] = platforms res = yield from hass.loop.run_in_executor(None, _handle_requirements, hass, component, domain) if not res: return None return config
def config_without_domain(config: Dict, domain: str) -> Dict: """Return a config with all configuration for a domain removed.""" filter_keys = extract_domain_configs(config, domain) return {key: value for key, value in config.items() if key not in filter_keys}
async def _async_process_config(hass, config, component) -> bool: """Process script configuration. Return true, if Blueprints were used. """ entities = [] blueprints_used = False for config_key in extract_domain_configs(config, DOMAIN): conf: dict[str, dict[str, Any] | BlueprintInputs] = config[config_key] for object_id, config_block in conf.items(): raw_blueprint_inputs = None raw_config = None if isinstance(config_block, BlueprintInputs): blueprints_used = True blueprint_inputs = config_block raw_blueprint_inputs = blueprint_inputs.config_with_inputs try: raw_config = blueprint_inputs.async_substitute() config_block = cast( Dict[str, Any], await async_validate_config_item(hass, raw_config), ) except vol.Invalid as err: LOGGER.error( "Blueprint %s generated invalid script with input %s: %s", blueprint_inputs.blueprint.name, blueprint_inputs.inputs, humanize_error(config_block, err), ) continue else: raw_config = cast(ScriptConfig, config_block).raw_config entities.append( ScriptEntity(hass, object_id, config_block, raw_config, raw_blueprint_inputs)) await component.async_add_entities(entities) async def service_handler(service): """Execute a service call to script.<script name>.""" entity_id = ENTITY_ID_FORMAT.format(service.service) script_entity = component.get_entity(entity_id) await script_entity.async_turn_on(variables=service.data, context=service.context) # Register services for all entities that were created successfully. for entity in entities: hass.services.async_register(DOMAIN, entity.object_id, service_handler, schema=SCRIPT_SERVICE_SCHEMA) # Register the service description service_desc = { CONF_NAME: entity.name, CONF_DESCRIPTION: entity.description, CONF_FIELDS: entity.fields, } async_set_service_schema(hass, DOMAIN, entity.object_id, service_desc) return blueprints_used
def _setup_component(hass, domain, config): """Setup a component for Home Assistant.""" # pylint: disable=too-many-return-statements,too-many-branches if domain in hass.config.components: return True with _SETUP_LOCK: # It might have been loaded while waiting for lock if domain in hass.config.components: return True if domain in _CURRENT_SETUP: _LOGGER.error('Attempt made to setup %s during setup of %s', domain, domain) return False component = loader.get_component(domain) missing_deps = [ dep for dep in getattr(component, 'DEPENDENCIES', []) if dep not in hass.config.components ] if missing_deps: _LOGGER.error( 'Not initializing %s because not all dependencies loaded: %s', domain, ", ".join(missing_deps)) return False if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) except vol.MultipleInvalid as ex: cv.log_exception(_LOGGER, ex, domain, config) return False elif hasattr(component, 'PLATFORM_SCHEMA'): 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.MultipleInvalid as ex: cv.log_exception(_LOGGER, ex, domain, p_config) return False # 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 = prepare_setup_platform(hass, config, domain, p_name) if platform is None: return False # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.MultipleInvalid as ex: cv.log_exception(_LOGGER, ex, '{}.{}'.format(domain, p_name), p_validated) return False platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = { key: value for key, value in config.items() if key not in filter_keys } config[domain] = platforms if not _handle_requirements(hass, component, domain): return False _CURRENT_SETUP.append(domain) try: if not component.setup(hass, config): _LOGGER.error('component %s failed to initialize', domain) return False except Exception: # pylint: disable=broad-except _LOGGER.exception('Error during setup of component %s', domain) return False finally: _CURRENT_SETUP.remove(domain) hass.config.components.append(component.DOMAIN) # Assumption: if a component does not depend on groups # it communicates with devices if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []): hass.pool.add_worker() hass.bus.fire(EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}) return True
async def _async_process_config( hass: HomeAssistant, config: Dict[str, Any], component: EntityComponent, ) -> None: """Process config and add automations. This method is a coroutine. """ entities = [] for config_key in extract_domain_configs(config, DOMAIN): conf: List[Union[Dict[str, Any], blueprint.BlueprintInputs]] = config[ # type: ignore config_key] for list_no, config_block in enumerate(conf): if isinstance(config_block, blueprint.BlueprintInputs): # type: ignore blueprint_inputs = config_block try: config_block = cast( Dict[str, Any], PLATFORM_SCHEMA(blueprint_inputs.async_substitute()), ) except vol.Invalid as err: _LOGGER.error( "Blueprint %s generated invalid automation with inputs %s: %s", blueprint_inputs.blueprint.name, blueprint_inputs.inputs, humanize_error(config_block, err), ) continue automation_id = config_block.get(CONF_ID) name = config_block.get(CONF_ALIAS) or f"{config_key} {list_no}" initial_state = config_block.get(CONF_INITIAL_STATE) action_script = Script( hass, config_block[CONF_ACTION], name, DOMAIN, running_description="automation actions", script_mode=config_block[CONF_MODE], max_runs=config_block[CONF_MAX], max_exceeded=config_block[CONF_MAX_EXCEEDED], logger=_LOGGER, # We don't pass variables here # Automation will already render them to use them in the condition # and so will pass them on to the script. ) if CONF_CONDITION in config_block: cond_func = await _async_process_if(hass, config, config_block) if cond_func is None: continue else: cond_func = None entity = AutomationEntity( automation_id, name, config_block[CONF_TRIGGER], cond_func, action_script, initial_state, config_block.get(CONF_VARIABLES), ) entities.append(entity) if entities: await component.async_add_entities(entities)
def _setup_component(hass, domain, config): """Setup a component for Home Assistant.""" # pylint: disable=too-many-return-statements,too-many-branches if domain in hass.config.components: return True with _SETUP_LOCK: # It might have been loaded while waiting for lock if domain in hass.config.components: return True if domain in _CURRENT_SETUP: _LOGGER.error('Attempt made to setup %s during setup of %s', domain, domain) return False component = loader.get_component(domain) missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', []) if dep not in hass.config.components] if missing_deps: _LOGGER.error( 'Not initializing %s because not all dependencies loaded: %s', domain, ", ".join(missing_deps)) return False if hasattr(component, 'CONFIG_SCHEMA'): try: config = component.CONFIG_SCHEMA(config) except vol.MultipleInvalid as ex: _LOGGER.error('Invalid config for [%s]: %s', domain, ex) return False elif hasattr(component, 'PLATFORM_SCHEMA'): 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.MultipleInvalid as ex: _LOGGER.error('Invalid platform config for [%s]: %s. %s', domain, ex, p_config) return False # Not all platform components follow same pattern for platforms # Sof 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 = prepare_setup_platform(hass, config, domain, p_name) if platform is None: return False # Validate platform specific schema if hasattr(platform, 'PLATFORM_SCHEMA'): try: p_validated = platform.PLATFORM_SCHEMA(p_validated) except vol.MultipleInvalid as ex: _LOGGER.error( 'Invalid platform config for [%s.%s]: %s. %s', domain, p_name, ex, p_config) return False platforms.append(p_validated) # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) config = {key: value for key, value in config.items() if key not in filter_keys} config[domain] = platforms if not _handle_requirements(hass, component, domain): return False _CURRENT_SETUP.append(domain) try: if not component.setup(hass, config): _LOGGER.error('component %s failed to initialize', domain) return False except Exception: # pylint: disable=broad-except _LOGGER.exception('Error during setup of component %s', domain) return False finally: _CURRENT_SETUP.remove(domain) hass.config.components.append(component.DOMAIN) # Assumption: if a component does not depend on groups # it communicates with devices if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []): hass.pool.add_worker() hass.bus.fire( EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}) return True