def async_when_setup( hass: core.HomeAssistant, component: str, when_setup_cb: Callable[ [core.HomeAssistant, str], Awaitable[None]]) -> None: """Call a method when a component is setup.""" async def when_setup() -> None: """Call the callback.""" try: await when_setup_cb(hass, component) except Exception: # pylint: disable=broad-except _LOGGER.exception('Error handling when_setup callback for %s', component) # Running it in a new task so that it always runs after if component in hass.config.components: hass.async_create_task(when_setup()) return unsub = None async def loaded_event(event: core.Event) -> None: """Call the callback.""" if event.data[ATTR_COMPONENT] != component: return unsub() # type: ignore await when_setup() unsub = hass.bus.async_listen(EVENT_COMPONENT_LOADED, loaded_event)
def websocket_setup_mfa( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return a setup flow for mfa auth module.""" async def async_setup_flow(msg): """Return a setup flow for mfa auth module.""" flow_manager = hass.data[DATA_SETUP_FLOW_MGR] flow_id = msg.get('flow_id') if flow_id is not None: result = await flow_manager.async_configure( flow_id, msg.get('user_input')) connection.send_message_outside( websocket_api.result_message( msg['id'], _prepare_result_json(result))) return mfa_module_id = msg.get('mfa_module_id') mfa_module = hass.auth.get_auth_mfa_module(mfa_module_id) if mfa_module is None: connection.send_message_outside(websocket_api.error_message( msg['id'], 'no_module', 'MFA module {} is not found'.format(mfa_module_id))) return result = await flow_manager.async_init( mfa_module_id, data={'user_id': connection.user.id}) connection.send_message_outside( websocket_api.result_message( msg['id'], _prepare_result_json(result))) hass.async_create_task(async_setup_flow(msg))
def async_create(hass: HomeAssistant, message: str, title: str = None, notification_id: str = None) -> None: """Generate a notification.""" data = { key: value for key, value in [ (ATTR_TITLE, title), (ATTR_MESSAGE, message), (ATTR_NOTIFICATION_ID, notification_id), ] if value is not None } hass.async_create_task( hass.services.async_call(DOMAIN, SERVICE_CREATE, data))
async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool: """Set up the Elk M1 platform.""" from elkm1_lib.const import Max import elkm1_lib as elkm1 configs = { CONF_AREA: Max.AREAS.value, CONF_COUNTER: Max.COUNTERS.value, CONF_KEYPAD: Max.KEYPADS.value, CONF_OUTPUT: Max.OUTPUTS.value, CONF_PLC: Max.LIGHTS.value, CONF_SETTING: Max.SETTINGS.value, CONF_TASK: Max.TASKS.value, CONF_THERMOSTAT: Max.THERMOSTATS.value, CONF_ZONE: Max.ZONES.value, } def _included(ranges, set_to, values): for rng in ranges: if not rng[0] <= rng[1] <= len(values): raise vol.Invalid("Invalid range {}".format(rng)) values[rng[0]-1:rng[1]] = [set_to] * (rng[1] - rng[0] + 1) conf = hass_config[DOMAIN] config = {'temperature_unit': conf[CONF_TEMPERATURE_UNIT]} config['panel'] = {'enabled': True, 'included': [True]} for item, max_ in configs.items(): config[item] = {'enabled': conf[item][CONF_ENABLED], 'included': [not conf[item]['include']] * max_} try: _included(conf[item]['include'], True, config[item]['included']) _included(conf[item]['exclude'], False, config[item]['included']) except (ValueError, vol.Invalid) as err: _LOGGER.error("Config item: %s; %s", item, err) return False elk = elkm1.Elk({'url': conf[CONF_HOST], 'userid': conf[CONF_USERNAME], 'password': conf[CONF_PASSWORD]}) elk.connect() _create_elk_services(hass, elk) hass.data[DOMAIN] = {'elk': elk, 'config': config, 'keypads': {}} for component in SUPPORTED_DOMAINS: hass.async_create_task( discovery.async_load_platform(hass, component, DOMAIN, {}, hass_config)) return True
def websocket_delete_refresh_token( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Handle a delete refresh token request.""" async def async_delete_refresh_token(user, refresh_token_id): """Delete a refresh token.""" refresh_token = connection.user.refresh_tokens.get(refresh_token_id) if refresh_token is None: return websocket_api.error_message( msg['id'], 'invalid_token_id', 'Received invalid token') await hass.auth.async_remove_refresh_token(refresh_token) connection.send_message( websocket_api.result_message(msg['id'], {})) hass.async_create_task( async_delete_refresh_token(connection.user, msg['refresh_token_id']))
def websocket_create_long_lived_access_token( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Create or a long-lived access token.""" async def async_create_long_lived_access_token(user): """Create or a long-lived access token.""" refresh_token = await hass.auth.async_create_refresh_token( user, client_name=msg['client_name'], client_icon=msg.get('client_icon'), token_type=TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN, access_token_expiration=timedelta(days=msg['lifespan'])) access_token = hass.auth.async_create_access_token( refresh_token) connection.send_message( websocket_api.result_message(msg['id'], access_token)) hass.async_create_task( async_create_long_lived_access_token(connection.user))
def websocket_depose_mfa( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Remove user from mfa module.""" async def async_depose(msg): """Remove user from mfa auth module.""" mfa_module_id = msg['mfa_module_id'] try: await hass.auth.async_disable_user_mfa( connection.user, msg['mfa_module_id']) except ValueError as err: connection.send_message_outside(websocket_api.error_message( msg['id'], 'disable_failed', 'Cannot disable MFA Module {}: {}'.format( mfa_module_id, err))) return connection.send_message_outside( websocket_api.result_message( msg['id'], 'done')) hass.async_create_task(async_depose(msg))
def websocket_current_user( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return the current user.""" async def async_get_current_user(user): """Get current user.""" enabled_modules = await hass.auth.async_get_enabled_mfa(user) connection.send_message( websocket_api.result_message(msg['id'], { 'id': user.id, 'name': user.name, 'is_owner': user.is_owner, 'credentials': [{'auth_provider_type': c.auth_provider_type, 'auth_provider_id': c.auth_provider_id} for c in user.credentials], 'mfa_modules': [{ 'id': module.id, 'name': module.name, 'enabled': module.id in enabled_modules, } for module in hass.auth.auth_mfa_modules], })) hass.async_create_task(async_get_current_user(connection.user))
async def async_setup_component(hass: core.HomeAssistant, domain: str, config: Optional[Dict] = None) -> bool: """Set up a component and all its dependencies. This method is a coroutine. """ if domain in hass.config.components: return True setup_tasks = hass.data.get(DATA_SETUP) if setup_tasks is not None and domain in setup_tasks: return await setup_tasks[domain] # type: ignore if config is None: config = {} if setup_tasks is None: setup_tasks = hass.data[DATA_SETUP] = {} task = setup_tasks[domain] = hass.async_create_task( _async_setup_component(hass, domain, config)) return await task # type: ignore
async def async_setup_gateway_entry(hass: core.HomeAssistant, entry: config_entries.ConfigEntry): """Set up the Xiaomi Gateway component from a config entry.""" host = entry.data[CONF_HOST] token = entry.data[CONF_TOKEN] name = entry.title gateway_id = entry.unique_id # For backwards compat if entry.unique_id.endswith("-gateway"): hass.config_entries.async_update_entry(entry, unique_id=entry.data["mac"]) entry.async_on_unload(entry.add_update_listener(update_listener)) # Connect to gateway gateway = ConnectXiaomiGateway(hass, entry) if not await gateway.async_connect_gateway(host, token): return False gateway_info = gateway.gateway_info gateway_model = f"{gateway_info.model}-{gateway_info.hardware_version}" device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, connections={(dr.CONNECTION_NETWORK_MAC, gateway_info.mac_address)}, identifiers={(DOMAIN, gateway_id)}, manufacturer="Xiaomi", name=name, model=gateway_model, sw_version=gateway_info.firmware_version, ) def update_data(): """Fetch data from the subdevice.""" data = {} for sub_device in gateway.gateway_device.devices.values(): try: sub_device.update() except GatewayException as ex: _LOGGER.error("Got exception while fetching the state: %s", ex) data[sub_device.sid] = {ATTR_AVAILABLE: False} else: data[sub_device.sid] = {ATTR_AVAILABLE: True} return data async def async_update_data(): """Fetch data from the subdevice using async_add_executor_job.""" return await hass.async_add_executor_job(update_data) # Create update coordinator coordinator = DataUpdateCoordinator( hass, _LOGGER, name=name, update_method=async_update_data, # Polling interval. Will only be polled if there are subscribers. update_interval=timedelta(seconds=10), ) hass.data[DOMAIN][entry.entry_id] = { CONF_GATEWAY: gateway.gateway_device, KEY_COORDINATOR: coordinator, } for platform in GATEWAY_PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up yi-hack from a config entry.""" hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = entry.data[CONF_MAC] hass.data[DOMAIN][entry.data[CONF_NAME]] = None hass.data[DOMAIN][entry.data[CONF_NAME] + END_OF_POWER_OFF] = None hass.data[DOMAIN][entry.data[CONF_NAME] + END_OF_POWER_ON] = None conf = await hass.async_add_executor_job(get_system_conf, entry.data) mqtt = await hass.async_add_executor_job(get_mqtt_conf, entry.data) if conf is not None and mqtt is not None: updated_data = { **entry.data, CONF_MQTT_PREFIX: mqtt[CONF_MQTT_PREFIX], CONF_TOPIC_STATUS: mqtt[CONF_TOPIC_STATUS], CONF_TOPIC_MOTION_DETECTION: mqtt[CONF_TOPIC_MOTION_DETECTION], CONF_MOTION_START_MSG: mqtt[CONF_MOTION_START_MSG], CONF_MOTION_STOP_MSG: mqtt[CONF_MOTION_STOP_MSG], CONF_BIRTH_MSG: mqtt[CONF_BIRTH_MSG], CONF_WILL_MSG: mqtt[CONF_WILL_MSG], CONF_TOPIC_MOTION_DETECTION_IMAGE: mqtt[CONF_TOPIC_MOTION_DETECTION_IMAGE], } if (entry.data[CONF_HACK_NAME] == DEFAULT_BRAND) or (entry.data[CONF_HACK_NAME] == MSTAR): updated_data.update( **{ CONF_RTSP_PORT: conf[CONF_RTSP_PORT], CONF_TOPIC_BABY_CRYING: mqtt[CONF_TOPIC_BABY_CRYING], CONF_BABY_CRYING_MSG: mqtt[CONF_BABY_CRYING_MSG], }) elif (entry.data[CONF_HACK_NAME] == ALLWINNER) or (entry.data[CONF_HACK_NAME] == ALLWINNERV2) or (entry.data[CONF_HACK_NAME] == V5): updated_data.update( **{ CONF_RTSP_PORT: conf[CONF_RTSP_PORT], CONF_TOPIC_BABY_CRYING: mqtt[CONF_TOPIC_BABY_CRYING], CONF_TOPIC_SOUND_DETECTION: mqtt[CONF_TOPIC_SOUND_DETECTION], CONF_BABY_CRYING_MSG: mqtt[CONF_BABY_CRYING_MSG], CONF_SOUND_DETECTION_MSG: mqtt[CONF_SOUND_DETECTION_MSG], }) elif entry.data[CONF_HACK_NAME] == SONOFF: updated_data.update(**{ CONF_RTSP_PORT: conf[CONF_RTSP_PORT], }) hass.config_entries.async_update_entry(entry, data=updated_data) if (entry.data[CONF_HACK_NAME] == SONOFF) or (entry.data[CONF_HACK_NAME] == V5): for component in PLATFORMS_NOMEDIA: hass.async_create_task( hass.config_entries.async_forward_entry_setup( entry, component)) else: for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup( entry, component)) return True else: _LOGGER.error("Unable to get configuration from the cam") return False
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Hunter Douglas PowerView from a config entry.""" config = entry.data hub_address = config.get(CONF_HOST) websession = async_get_clientsession(hass) pv_request = AioRequest(hub_address, loop=hass.loop, websession=websession) try: async with async_timeout.timeout(10): device_info = await async_get_device_info(pv_request) except HUB_EXCEPTIONS: _LOGGER.error("Connection error to PowerView hub: %s", hub_address) raise ConfigEntryNotReady if not device_info: _LOGGER.error("Unable to initialize PowerView hub: %s", hub_address) raise ConfigEntryNotReady rooms = Rooms(pv_request) room_data = _async_map_data_by_id((await rooms.get_resources())[ROOM_DATA]) scenes = Scenes(pv_request) scene_data = _async_map_data_by_id((await scenes.get_resources())[SCENE_DATA]) shades = Shades(pv_request) shade_data = _async_map_data_by_id((await shades.get_resources())[SHADE_DATA]) async def async_update_data(): """Fetch data from shade endpoint.""" async with async_timeout.timeout(10): shade_entries = await shades.get_resources() if not shade_entries: raise UpdateFailed(f"Failed to fetch new shade data.") return _async_map_data_by_id(shade_entries[SHADE_DATA]) coordinator = DataUpdateCoordinator( hass, _LOGGER, name="powerview hub", update_method=async_update_data, update_interval=timedelta(seconds=60), ) hass.data[DOMAIN][entry.entry_id] = { PV_API: pv_request, PV_ROOM_DATA: room_data, PV_SCENE_DATA: scene_data, PV_SHADES: shades, PV_SHADE_DATA: shade_data, COORDINATOR: coordinator, DEVICE_INFO: device_info, } for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component) ) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the esphome component.""" host = entry.data[CONF_HOST] port = entry.data[CONF_PORT] password = entry.data[CONF_PASSWORD] noise_psk = entry.data.get(CONF_NOISE_PSK) device_id = None zeroconf_instance = await zeroconf.async_get_instance(hass) cli = APIClient( host, port, password, client_info=f"Home Assistant {const.__version__}", zeroconf_instance=zeroconf_instance, noise_psk=noise_psk, ) domain_data = DomainData.get(hass) entry_data = RuntimeEntryData( client=cli, entry_id=entry.entry_id, store=domain_data.get_or_create_store(hass, entry), ) domain_data.set_entry_data(entry, entry_data) async def on_stop(event: Event) -> None: """Cleanup the socket client on HA stop.""" await _cleanup_instance(hass, entry) # Use async_listen instead of async_listen_once so that we don't deregister # the callback twice when shutting down Home Assistant. # "Unable to remove unknown listener <function EventBus.async_listen_once.<locals>.onetime_listener>" entry_data.cleanup_callbacks.append( hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, on_stop)) @callback def async_on_state(state: EntityState) -> None: """Send dispatcher updates when a new state is received.""" entry_data.async_update_state(hass, state) @callback def async_on_service_call(service: HomeassistantServiceCall) -> None: """Call service when user automation in ESPHome config is triggered.""" domain, service_name = service.service.split(".", 1) service_data = service.data if service.data_template: try: data_template = { key: Template(value) # type: ignore[no-untyped-call] for key, value in service.data_template.items() } template.attach(hass, data_template) service_data.update( template.render_complex(data_template, service.variables)) except TemplateError as ex: _LOGGER.error("Error rendering data template for %s: %s", host, ex) return if service.is_event: # ESPHome uses servicecall packet for both events and service calls # Ensure the user can only send events of form 'esphome.xyz' if domain != "esphome": _LOGGER.error( "Can only generate events under esphome domain! (%s)", host) return # Call native tag scan if service_name == "tag_scanned": tag_id = service_data["tag_id"] hass.async_create_task( hass.components.tag.async_scan_tag(tag_id, device_id)) return hass.bus.async_fire(service.service, service_data) else: hass.async_create_task( hass.services.async_call(domain, service_name, service_data, blocking=True)) async def _send_home_assistant_state(entity_id: str, attribute: str | None, state: State | None) -> None: """Forward Home Assistant states to ESPHome.""" if state is None or (attribute and attribute not in state.attributes): return send_state = state.state if attribute: attr_val = state.attributes[attribute] # ESPHome only handles "on"/"off" for boolean values if isinstance(attr_val, bool): send_state = "on" if attr_val else "off" else: send_state = attr_val await cli.send_home_assistant_state(entity_id, attribute, str(send_state)) @callback def async_on_state_subscription(entity_id: str, attribute: str | None = None) -> None: """Subscribe and forward states for requested entities.""" async def send_home_assistant_state_event(event: Event) -> None: """Forward Home Assistant states updates to ESPHome.""" # Only communicate changes to the state or attribute tracked if (event.data.get("old_state") is not None and "new_state" in event.data and ((not attribute and event.data["old_state"].state == event.data["new_state"].state) or (attribute and attribute in event.data["old_state"].attributes and attribute in event.data["new_state"].attributes and event.data["old_state"].attributes[attribute] == event.data["new_state"].attributes[attribute]))): return await _send_home_assistant_state(event.data["entity_id"], attribute, event.data.get("new_state")) unsub = async_track_state_change_event( hass, [entity_id], send_home_assistant_state_event) entry_data.disconnect_callbacks.append(unsub) # Send initial state hass.async_create_task( _send_home_assistant_state(entity_id, attribute, hass.states.get(entity_id))) async def on_connect() -> None: """Subscribe to states and list entities on successful API login.""" nonlocal device_id try: entry_data.device_info = await cli.device_info() assert cli.api_version is not None entry_data.api_version = cli.api_version entry_data.available = True device_id = _async_setup_device_registry(hass, entry, entry_data.device_info) entry_data.async_update_device_state(hass) entity_infos, services = await cli.list_entities_services() await entry_data.async_update_static_infos(hass, entry, entity_infos) await _setup_services(hass, entry_data, services) await cli.subscribe_states(async_on_state) await cli.subscribe_service_calls(async_on_service_call) await cli.subscribe_home_assistant_states( async_on_state_subscription) hass.async_create_task(entry_data.async_save_to_store()) except APIConnectionError as err: _LOGGER.warning("Error getting initial data for %s: %s", host, err) # Re-connection logic will trigger after this await cli.disconnect() async def on_disconnect() -> None: """Run disconnect callbacks on API disconnect.""" for disconnect_cb in entry_data.disconnect_callbacks: disconnect_cb() entry_data.disconnect_callbacks = [] entry_data.available = False entry_data.async_update_device_state(hass) async def on_connect_error(err: Exception) -> None: """Start reauth flow if appropriate connect error type.""" if isinstance( err, (RequiresEncryptionAPIError, InvalidEncryptionKeyAPIError)): entry.async_start_reauth(hass) reconnect_logic = ReconnectLogic( client=cli, on_connect=on_connect, on_disconnect=on_disconnect, zeroconf_instance=zeroconf_instance, name=host, on_connect_error=on_connect_error, ) async def complete_setup() -> None: """Complete the config entry setup.""" infos, services = await entry_data.async_load_from_store() await entry_data.async_update_static_infos(hass, entry, infos) await _setup_services(hass, entry_data, services) await reconnect_logic.start() entry_data.cleanup_callbacks.append(reconnect_logic.stop_callback) hass.async_create_task(complete_setup()) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Hyperion from a config entry.""" host = entry.data[CONF_HOST] port = entry.data[CONF_PORT] token = entry.data.get(CONF_TOKEN) hyperion_client = await async_create_connect_hyperion_client( host, port, token=token, raw_connection=True ) # Client won't connect? => Not ready. if not hyperion_client: raise ConfigEntryNotReady version = await hyperion_client.async_sysinfo_version() if version is not None: with suppress(ValueError): if AwesomeVersion(version) < AwesomeVersion(HYPERION_VERSION_WARN_CUTOFF): _LOGGER.warning( "Using a Hyperion server version < %s is not recommended -- " "some features may be unavailable or may not function correctly. " "Please consider upgrading: %s", HYPERION_VERSION_WARN_CUTOFF, HYPERION_RELEASES_URL, ) # Client needs authentication, but no token provided? => Reauth. auth_resp = await hyperion_client.async_is_auth_required() if ( auth_resp is not None and client.ResponseOK(auth_resp) and auth_resp.get(hyperion_const.KEY_INFO, {}).get( hyperion_const.KEY_REQUIRED, False ) and token is None ): await hyperion_client.async_client_disconnect() raise ConfigEntryAuthFailed # Client login doesn't work? => Reauth. if not await hyperion_client.async_client_login(): await hyperion_client.async_client_disconnect() raise ConfigEntryAuthFailed # Cannot switch instance or cannot load state? => Not ready. if ( not await hyperion_client.async_client_switch_instance() or not client.ServerInfoResponseOK(await hyperion_client.async_get_serverinfo()) ): await hyperion_client.async_client_disconnect() raise ConfigEntryNotReady # We need 1 root client (to manage instances being removed/added) and then 1 client # per Hyperion server instance which is shared for all entities associated with # that instance. hass.data[DOMAIN][entry.entry_id] = { CONF_ROOT_CLIENT: hyperion_client, CONF_INSTANCE_CLIENTS: {}, CONF_ON_UNLOAD: [], } async def async_instances_to_clients(response: dict[str, Any]) -> None: """Convert instances to Hyperion clients.""" if not response or hyperion_const.KEY_DATA not in response: return await async_instances_to_clients_raw(response[hyperion_const.KEY_DATA]) async def async_instances_to_clients_raw(instances: list[dict[str, Any]]) -> None: """Convert instances to Hyperion clients.""" device_registry = dr.async_get(hass) running_instances: set[int] = set() stopped_instances: set[int] = set() existing_instances = hass.data[DOMAIN][entry.entry_id][CONF_INSTANCE_CLIENTS] server_id = cast(str, entry.unique_id) # In practice, an instance can be in 3 states as seen by this function: # # * Exists, and is running: Should be present in HASS/registry. # * Exists, but is not running: Cannot add it yet, but entity may have be # registered from a previous time it was running. # * No longer exists at all: Should not be present in HASS/registry. # Add instances that are missing. for instance in instances: instance_num = instance.get(hyperion_const.KEY_INSTANCE) if instance_num is None: continue if not instance.get(hyperion_const.KEY_RUNNING, False): stopped_instances.add(instance_num) continue running_instances.add(instance_num) if instance_num in existing_instances: continue hyperion_client = await async_create_connect_hyperion_client( host, port, instance=instance_num, token=token ) if not hyperion_client: continue existing_instances[instance_num] = hyperion_client instance_name = instance.get(hyperion_const.KEY_FRIENDLY_NAME, DEFAULT_NAME) async_dispatcher_send( hass, SIGNAL_INSTANCE_ADD.format(entry.entry_id), instance_num, instance_name, ) # Remove entities that are are not running instances on Hyperion. for instance_num in set(existing_instances) - running_instances: del existing_instances[instance_num] async_dispatcher_send( hass, SIGNAL_INSTANCE_REMOVE.format(entry.entry_id), instance_num ) # Ensure every device associated with this config entry is still in the list of # motionEye cameras, otherwise remove the device (and thus entities). known_devices = { get_hyperion_device_id(server_id, instance_num) for instance_num in running_instances | stopped_instances } for device_entry in dr.async_entries_for_config_entry( device_registry, entry.entry_id ): for (kind, key) in device_entry.identifiers: if kind == DOMAIN and key in known_devices: break else: device_registry.async_remove_device(device_entry.id) hyperion_client.set_callbacks( { f"{hyperion_const.KEY_INSTANCE}-{hyperion_const.KEY_UPDATE}": async_instances_to_clients, } ) async def setup_then_listen() -> None: await asyncio.gather( *( hass.config_entries.async_forward_entry_setup(entry, platform) for platform in PLATFORMS ) ) assert hyperion_client if hyperion_client.instances is not None: await async_instances_to_clients_raw(hyperion_client.instances) hass.data[DOMAIN][entry.entry_id][CONF_ON_UNLOAD].append( entry.add_update_listener(_async_entry_updated) ) hass.async_create_task(setup_then_listen()) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up nordpool as config entry.""" hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, "sensor")) return True
async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up SMHI forecast as config entry.""" hass.async_create_task(hass.config_entries.async_forward_entry_setup( config_entry, 'weather')) return True
async def async_initialize_integration( hass: HomeAssistant, *, config_entry: ConfigEntry | None = None, config: dict[str, Any] | None = None, ) -> bool: """Initialize the integration""" hass.data[DOMAIN] = hacs = HacsBase() hacs.enable_hacs() if config is not None: if DOMAIN not in config: return True if hacs.configuration.config_type == ConfigurationType.CONFIG_ENTRY: return True hacs.configuration.update_from_dict( { "config_type": ConfigurationType.YAML, **config[DOMAIN], "config": config[DOMAIN], } ) if config_entry is not None: if config_entry.source == SOURCE_IMPORT: hass.async_create_task(hass.config_entries.async_remove(config_entry.entry_id)) return False hacs.configuration.update_from_dict( { "config_entry": config_entry, "config_type": ConfigurationType.CONFIG_ENTRY, **config_entry.data, **config_entry.options, } ) integration = await async_get_integration(hass, DOMAIN) hacs.set_stage(None) hacs.log.info(STARTUP, integration.version) clientsession = async_get_clientsession(hass) hacs.integration = integration hacs.version = integration.version hacs.configuration.dev = integration.version == "0.0.0" hacs.hass = hass hacs.queue = QueueManager(hass=hass) hacs.data = HacsData(hacs=hacs) hacs.system.running = True hacs.session = clientsession hacs.core.lovelace_mode = LovelaceMode.YAML try: lovelace_info = await system_health_info(hacs.hass) hacs.core.lovelace_mode = LovelaceMode(lovelace_info.get("mode", "yaml")) except BaseException: # lgtm [py/catch-base-exception] pylint: disable=broad-except # If this happens, the users YAML is not valid, we assume YAML mode pass hacs.log.debug("Configuration type: %s", hacs.configuration.config_type) hacs.core.config_path = hacs.hass.config.path() if hacs.core.ha_version is None: hacs.core.ha_version = AwesomeVersion(HAVERSION) ## Legacy GitHub client hacs.github = GitHub( hacs.configuration.token, clientsession, headers={ "User-Agent": f"HACS/{hacs.version}", "Accept": ACCEPT_HEADERS["preview"], }, ) ## New GitHub client hacs.githubapi = GitHubAPI( token=hacs.configuration.token, session=clientsession, **{"client_name": f"HACS/{hacs.version}"}, ) async def async_startup(): """HACS startup tasks.""" hacs.enable_hacs() for location in ( hass.config.path("custom_components/custom_updater.py"), hass.config.path("custom_components/custom_updater/__init__.py"), ): if os.path.exists(location): hacs.log.critical( "This cannot be used with custom_updater. " "To use this you need to remove custom_updater form %s", location, ) hacs.disable_hacs(HacsDisabledReason.CONSTRAINS) return False if not version_left_higher_or_equal_then_right( hacs.core.ha_version.string, MINIMUM_HA_VERSION, ): hacs.log.critical( "You need HA version %s or newer to use this integration.", MINIMUM_HA_VERSION, ) hacs.disable_hacs(HacsDisabledReason.CONSTRAINS) return False if not await hacs.data.restore(): hacs.disable_hacs(HacsDisabledReason.RESTORE) return False can_update = await hacs.async_can_update() hacs.log.debug("Can update %s repositories", can_update) hacs.set_active_categories() async_register_websocket_commands(hass) async_register_frontend(hass, hacs) if hacs.configuration.config_type == ConfigurationType.YAML: hass.async_create_task( async_load_platform(hass, Platform.SENSOR, DOMAIN, {}, hacs.configuration.config) ) hacs.log.info("Update entities are only supported when using UI configuration") else: if hacs.configuration.experimental: hass.config_entries.async_setup_platforms( hacs.configuration.config_entry, [Platform.SENSOR, Platform.UPDATE] ) else: hass.config_entries.async_setup_platforms( hacs.configuration.config_entry, [Platform.SENSOR] ) hacs.set_stage(HacsStage.SETUP) if hacs.system.disabled: return False # Schedule startup tasks async_at_start(hass=hass, at_start_cb=hacs.startup_tasks) hacs.set_stage(HacsStage.WAITING) hacs.log.info("Setup complete, waiting for Home Assistant before startup tasks starts") return not hacs.system.disabled async def async_try_startup(_=None): """Startup wrapper for yaml config.""" try: startup_result = await async_startup() except AIOGitHubAPIException: startup_result = False if not startup_result: if ( hacs.configuration.config_type == ConfigurationType.YAML or hacs.system.disabled_reason != HacsDisabledReason.INVALID_TOKEN ): hacs.log.info("Could not setup HACS, trying again in 15 min") async_call_later(hass, 900, async_try_startup) return hacs.enable_hacs() await async_try_startup() # Mischief managed! return True
async def async_from_config_dict(config: Dict[str, Any], hass: core.HomeAssistant, config_dir: Optional[str] = None, enable_log: bool = True, verbose: bool = False, skip_pip: bool = False, log_rotate_days: Any = None, log_file: Any = None, log_no_color: bool = False) \ -> Optional[core.HomeAssistant]: """Try to configure Home Assistant from a configuration dictionary. Dynamically loads required components and its dependencies. This method is a coroutine. """ start = time() if enable_log: async_enable_logging(hass, verbose, log_rotate_days, log_file, log_no_color) core_config = config.get(core.DOMAIN, {}) try: await conf_util.async_process_ha_core_config(hass, core_config) except vol.Invalid as ex: conf_util.async_log_exception(ex, 'homeassistant', core_config, hass) return None await hass.async_add_executor_job( conf_util.process_ha_config_upgrade, hass) hass.config.skip_pip = skip_pip if skip_pip: _LOGGER.warning("Skipping pip installation of required modules. " "This may cause issues") # Make a copy because we are mutating it. config = OrderedDict(config) # Merge packages conf_util.merge_packages_config( hass, config, core_config.get(conf_util.CONF_PACKAGES, {})) # Ensure we have no None values after merge for key, value in config.items(): if not value: config[key] = {} hass.config_entries = config_entries.ConfigEntries(hass, config) await hass.config_entries.async_load() # Filter out the repeating and common config section [homeassistant] components = set(key.split(' ')[0] for key in config.keys() if key != core.DOMAIN) components.update(hass.config_entries.async_domains()) # setup components res = await core_components.async_setup(hass, config) if not res: _LOGGER.error("Home Assistant core failed to initialize. " "further initialization aborted") return hass await persistent_notification.async_setup(hass, config) _LOGGER.info("Home Assistant core initialized") # stage 1 for component in components: if component not in FIRST_INIT_COMPONENT: continue hass.async_create_task(async_setup_component(hass, component, config)) await hass.async_block_till_done() # stage 2 for component in components: if component in FIRST_INIT_COMPONENT: continue hass.async_create_task(async_setup_component(hass, component, config)) await hass.async_block_till_done() stop = time() _LOGGER.info("Home Assistant initialized in %.2fs", stop-start) async_register_signal_handling(hass) return hass
def async_setup_scanner_platform( hass: HomeAssistant, config: ConfigType, scanner: DeviceScanner, async_see_device: Callable[..., Coroutine[None, None, None]], platform: str, ) -> None: """Set up the connect scanner-based platform to device tracker. This method must be run in the event loop. """ interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL) update_lock = asyncio.Lock() scanner.hass = hass # Initial scan of each mac we also tell about host name for config seen: Any = set() async def async_device_tracker_scan( now: dt_util.dt.datetime | None) -> None: """Handle interval matches.""" if update_lock.locked(): LOGGER.warning( "Updating device list from %s took longer than the scheduled " "scan interval %s", platform, interval, ) return async with update_lock: found_devices = await scanner.async_scan_devices() for mac in found_devices: if mac in seen: host_name = None else: host_name = await scanner.async_get_device_name(mac) seen.add(mac) try: extra_attributes = await scanner.async_get_extra_attributes(mac ) except NotImplementedError: extra_attributes = {} kwargs: dict[str, Any] = { "mac": mac, "host_name": host_name, "source_type": SOURCE_TYPE_ROUTER, "attributes": { "scanner": scanner.__class__.__name__, **extra_attributes, }, } zone_home = hass.states.get(hass.components.zone.ENTITY_ID_HOME) if zone_home is not None: kwargs["gps"] = [ zone_home.attributes[ATTR_LATITUDE], zone_home.attributes[ATTR_LONGITUDE], ] kwargs["gps_accuracy"] = 0 hass.async_create_task(async_see_device(**kwargs)) async_track_time_interval(hass, async_device_tracker_scan, interval) hass.async_create_task(async_device_tracker_scan(None))
async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Plugwise Smiles from a config entry.""" websession = async_get_clientsession(hass, verify_ssl=False) api = Smile( host=entry.data[CONF_HOST], username=entry.data.get(CONF_USERNAME, DEFAULT_USERNAME), password=entry.data[CONF_PASSWORD], port=entry.data.get(CONF_PORT, DEFAULT_PORT), timeout=30, websession=websession, ) try: connected = await api.connect() if not connected: _LOGGER.error("Unable to connect to Smile") raise ConfigEntryNotReady except InvalidAuthentication: _LOGGER.error("Invalid username or Smile ID") return False except PlugwiseException as err: _LOGGER.error("Error while communicating to device %s", api.smile_name) raise ConfigEntryNotReady from err except asyncio.TimeoutError as err: _LOGGER.error("Timeout while connecting to Smile %s", api.smile_name) raise ConfigEntryNotReady from err update_interval = timedelta(seconds=entry.options.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL[api.smile_type])) async def async_update_data(): """Update data via API endpoint.""" try: async with async_timeout.timeout(DEFAULT_TIMEOUT): await api.full_update_device() return True except XMLDataMissingError as err: raise UpdateFailed("Smile update failed") from err coordinator = DataUpdateCoordinator( hass, _LOGGER, name=f"Smile {api.smile_name}", update_method=async_update_data, update_interval=update_interval, ) await coordinator.async_refresh() if not coordinator.last_update_success: raise ConfigEntryNotReady api.get_all_devices() if entry.unique_id is None: if api.smile_version[0] != "1.8.0": hass.config_entries.async_update_entry( entry, unique_id=api.smile_hostname) undo_listener = entry.add_update_listener(_update_listener) hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { "api": api, COORDINATOR: coordinator, PW_TYPE: GATEWAY, UNDO_UPDATE_LISTENER: undo_listener, } device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, api.gateway_id)}, manufacturer="Plugwise", name=entry.title, model=f"Smile {api.smile_name}", sw_version=api.smile_version[0], ) single_master_thermostat = api.single_master_thermostat() platforms = PLATFORMS_GATEWAY if single_master_thermostat is None: platforms = SENSOR_PLATFORMS for component in platforms: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up TDAmeritrade from a config entry.""" _LOGGER.debug("Setting up entry") config_flow.OAuth2FlowHandler.async_register_implementation( hass, config_entry_oauth2_flow.LocalOAuth2Implementation( hass, entry.domain, entry.data["consumer_key"], None, OAUTH2_AUTHORIZE, OAUTH2_TOKEN, ), ) implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry)) session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) auth = api.AsyncConfigEntryAuth( aiohttp_client.async_get_clientsession(hass), session) client = AmeritradeAPI(auth) async def place_order_service(call): """Handle a place trade service call.""" price = call.data["price"] instruction = call.data["instruction"] quantity = call.data["quantity"] symbol = call.data["symbol"] account_id = call.data["account_id"] order_type = call.data["order_type"] session = call.data["session"] duration = call.data["duration"] order_strategy_type = call.data["orderStrategyType"] asset_type = call.data["assetType"] return await client.async_place_order( price, instruction, quantity, symbol, account_id, order_type=order_type, session=session, duration=duration, orderStrategyType=order_strategy_type, assetType=asset_type, ) async def get_quote_service(call): """Handle a place trade service call.""" symbol = call.data["symbol"] res = await client.async_get_quote(ticker=symbol) hass.states.async_set( f"get_quote_service.{symbol}", res[symbol]["lastPrice"], attributes=res[symbol], ) return True _LOGGER.debug("Registering Services") hass.services.async_register(DOMAIN, "place_order", place_order_service) hass.services.async_register(DOMAIN, "get_quote", get_quote_service) hass.data.setdefault(DOMAIN, {}) hass_data = dict(entry.data) entry.update_listeners = [] hass_data["unsub"] = entry.add_update_listener(options_update_listener) hass_data["client"] = AmeritradeAPI(auth) hass.data[DOMAIN][entry.entry_id] = hass_data if entry.state == ConfigEntryState.NOT_LOADED: for component in PLATFORMS: _LOGGER.debug("Setting up %s component", component) hass.async_create_task( hass.config_entries.async_forward_entry_setup( entry, component)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry): autolog("<<<") global UPDATE_UNLISTENER if UPDATE_UNLISTENER: UPDATE_UNLISTENER() if not config_entry.unique_id: hass.config_entries.async_update_entry( config_entry, unique_id=config_entry.title ) config = {} for key, value in config_entry.data.items(): config[key] = value for key, value in config_entry.options.items(): config[key] = value if config_entry.options: hass.config_entries.async_update_entry(config_entry, data=config, options={}) UPDATE_UNLISTENER = config_entry.add_update_listener(_update_listener) hass.data[DOMAIN][config.get(CONF_URL)] = {} _jelly = JellyfinClientManager(hass, config) try: await _jelly.connect() hass.data[DOMAIN][config.get(CONF_URL)]["manager"] = _jelly except: _LOGGER.error("Cannot connect to Jellyfin server.") raise ConfigEntryNotReady async def async_service_handler(service): """Map services to methods""" method = SERVICE_TO_METHOD.get(service.service) params = {key: value for key, value in service.data.items() if key != "entity_id"} entity_id = service.data.get(ATTR_ENTITY_ID) for sensor in hass.data[DOMAIN][config.get(CONF_URL)]["sensor"]["entities"]: if sensor.entity_id == entity_id: await getattr(sensor, method['method'])(**params) for media_player in hass.data[DOMAIN][config.get(CONF_URL)]["media_player"]["entities"]: if media_player.entity_id == entity_id: await getattr(media_player, method['method'])(**params) for my_service in SERVICE_TO_METHOD: schema = SERVICE_TO_METHOD[my_service].get('schema', SERVICE_SCHEMA) hass.services.async_register( DOMAIN, my_service, async_service_handler, schema=schema) await _jelly.start() for platform in PLATFORMS: hass.data[DOMAIN][config.get(CONF_URL)][platform] = {} hass.data[DOMAIN][config.get(CONF_URL)][platform]["entities"] = [] hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, platform) ) async_dispatcher_send(hass, SIGNAL_STATE_UPDATED) async def stop_jellyfin(event): """Stop Jellyfin connection.""" await _jelly.stop() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_jellyfin) return True
async def async_setup(hass: HomeAssistant, base_config: dict): _LOGGER.info(f'Используется версия модуля mosportal: {pkg_resources.get_distribution("mosportal").version}') config = base_config[DOMAIN] _LOGGER.debug("настройка компонента моспортал") client = PortalWrap( hass, Session( config[CONF_USERNAME], config[CONF_PASSWORD], cookie_save_path=join(dirname(abspath(__file__)), '..', '..', '.storage') ), config[CONF_FLAT], config[CONF_PAYCODE] ) hass.data[DOMAIN] = client meter_list = await client.fetch_data() if meter_list: _LOGGER.debug("счетчики получены") hass.async_create_task( async_load_platform( hass, SENSOR_DOMAIN, DOMAIN, discovered={meter.meter_id: meter.name for meter in meter_list.values()}, hass_config=config, ) ) async def trigger_get_epd_service(call): try: month = call.data.get('month', datetime.now().month) year = call.data.get('year', datetime.now().year) data = call.data.get('data', {}) await hass.async_add_executor_job( client.get_epd_service, month, year, data ) except BaseException as e: _LOGGER.exception(f'ошибка постановки задачи {e}') async def publish_water_usage(call): try: if 'meter_list_to_update' not in call.data: _LOGGER.error('переданы не корректные данные на вход в сервис') return meter_list_to_update = { item['meter_id']: item for item in call.data['meter_list_to_update'] } await hass.async_add_executor_job( client.publish_water_usage, meter_list_to_update ) except BaseException as e: _LOGGER.exception(f'ошибка постановки задачи {e}') hass.services.async_register(DOMAIN, 'get_epd', trigger_get_epd_service) hass.services.async_register(DOMAIN, 'publish_water_usage', publish_water_usage) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Elk-M1 Control from a config entry.""" conf = entry.data _LOGGER.debug("Setting up elkm1 %s", conf["host"]) temperature_unit = TEMP_FAHRENHEIT if conf[CONF_TEMPERATURE_UNIT] in (BARE_TEMP_CELSIUS, TEMP_CELSIUS): temperature_unit = TEMP_CELSIUS config = {"temperature_unit": temperature_unit} if not conf[CONF_AUTO_CONFIGURE]: # With elkm1-lib==0.7.16 and later auto configure is available config["panel"] = {"enabled": True, "included": [True]} for item, max_ in ELK_ELEMENTS.items(): config[item] = { "enabled": conf[item][CONF_ENABLED], "included": [not conf[item]["include"]] * max_, } try: _included(conf[item]["include"], True, config[item]["included"]) _included(conf[item]["exclude"], False, config[item]["included"]) except (ValueError, vol.Invalid) as err: _LOGGER.error("Config item: %s; %s", item, err) return False elk = elkm1.Elk({ "url": conf[CONF_HOST], "userid": conf[CONF_USERNAME], "password": conf[CONF_PASSWORD], }) elk.connect() if not await async_wait_for_elk_to_sync(elk, SYNC_TIMEOUT): _LOGGER.error( "Timed out after %d seconds while trying to sync with ElkM1 at %s", SYNC_TIMEOUT, conf[CONF_HOST], ) elk.disconnect() raise ConfigEntryNotReady if elk.invalid_auth: _LOGGER.error("Authentication failed for ElkM1") return False hass.data[DOMAIN][entry.entry_id] = { "elk": elk, "prefix": conf[CONF_PREFIX], "auto_configure": conf[CONF_AUTO_CONFIGURE], "config": config, "keypads": {}, } for component in SUPPORTED_DOMAINS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Scheduler integration from a config entry.""" session = async_get_clientsession(hass) store = await async_get_registry(hass) coordinator = SchedulerCoordinator(hass, session, entry, store) device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(const.DOMAIN, coordinator.id)}, name="Scheduler", model="Scheduler", sw_version=const.VERSION, manufacturer="@nielsfaber", ) hass.data.setdefault(const.DOMAIN, {}) hass.data[const.DOMAIN] = {"coordinator": coordinator, "schedules": {}} if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=coordinator.id) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, PLATFORM)) await async_register_websockets(hass) def service_create_schedule(service): coordinator.async_create_schedule(dict(service.data)) hass.services.async_register(const.DOMAIN, const.SERVICE_ADD, service_create_schedule, schema=const.SCHEDULE_SCHEMA) async def async_service_edit_schedule(service): match = None for (schedule_id, entity) in hass.data[const.DOMAIN]["schedules"].items(): if entity.entity_id == service.data[const.ATTR_ENTITY_ID]: match = schedule_id continue if not match: raise vol.Invalid("Entity not found: {}".format( service.data[const.ATTR_ENTITY_ID])) else: data = dict(service.data) del data[const.ATTR_ENTITY_ID] await coordinator.async_edit_schedule(match, data) hass.services.async_register(const.DOMAIN, const.SERVICE_EDIT, async_service_edit_schedule, schema=const.SCHEDULE_SCHEMA.extend( {vol.Required(ATTR_ENTITY_ID): cv.string})) async def async_service_remove_schedule(service): match = None for (schedule_id, entity) in hass.data[const.DOMAIN]["schedules"].items(): if entity.entity_id == service.data["entity_id"]: match = schedule_id continue if not match: raise vol.Invalid("Entity not found: {}".format( service.data["entity_id"])) else: await coordinator.async_delete_schedule(match) hass.services.async_register( const.DOMAIN, const.SERVICE_REMOVE, async_service_remove_schedule, schema=vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.string})) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Plugwise Smiles from a config entry.""" websession = async_get_clientsession(hass, verify_ssl=False) api = Smile( host=entry.data["host"], password=entry.data["password"], websession=websession ) try: connected = await api.connect() if not connected: _LOGGER.error("Unable to connect to Smile") raise ConfigEntryNotReady except Smile.InvalidAuthentication: _LOGGER.error("Invalid Smile ID") return False except Smile.PlugwiseError: _LOGGER.error("Error while communicating to device") raise ConfigEntryNotReady except asyncio.TimeoutError: _LOGGER.error("Timeout while connecting to Smile") raise ConfigEntryNotReady if api.smile_type == "power": update_interval = timedelta(seconds=10) else: update_interval = timedelta(seconds=60) async def async_update_data(): """Update data via API endpoint.""" try: async with async_timeout.timeout(10): await api.full_update_device() return True except Smile.XMLDataMissingError: raise UpdateFailed("Smile update failed") coordinator = DataUpdateCoordinator( hass, _LOGGER, name="Smile", update_method=async_update_data, update_interval=update_interval, ) await coordinator.async_refresh() if not coordinator.last_update_success: raise ConfigEntryNotReady api.get_all_devices() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { "api": api, "coordinator": coordinator, } device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, api.gateway_id)}, manufacturer="Plugwise", name=entry.title, model=f"Smile {api.smile_name}", sw_version=api.smile_version[0], ) platforms = ALL_PLATFORMS single_master_thermostat = api.single_master_thermostat() if single_master_thermostat is None: platforms = SENSOR_PLATFORMS for component in platforms: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component) ) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Do setup of vera.""" # Use options entered during initial config flow or provided from configuration.yml if config_entry.data.get(CONF_LIGHTS) or config_entry.data.get( CONF_EXCLUDE): hass.config_entries.async_update_entry( entry=config_entry, data=config_entry.data, options=new_options( config_entry.data.get(CONF_LIGHTS, []), config_entry.data.get(CONF_EXCLUDE, []), ), ) base_url = config_entry.data[CONF_CONTROLLER] light_ids = config_entry.options.get(CONF_LIGHTS, []) exclude_ids = config_entry.options.get(CONF_EXCLUDE, []) # Initialize the Vera controller. controller = veraApi.VeraController(base_url) controller.start() hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STOP, lambda event: hass.async_add_executor_job(controller.stop), ) try: all_devices = await hass.async_add_executor_job(controller.get_devices) all_scenes = await hass.async_add_executor_job(controller.get_scenes) except RequestException: # There was a network related error connecting to the Vera controller. _LOGGER.exception("Error communicating with Vera API") return False # Exclude devices unwanted by user. devices = [ device for device in all_devices if device.device_id not in exclude_ids ] vera_devices = defaultdict(list) for device in devices: device_type = map_vera_device(device, light_ids) if device_type is None: continue vera_devices[device_type].append(device) vera_scenes = [] for scene in all_scenes: vera_scenes.append(scene) controller_data = ControllerData(controller=controller, devices=vera_devices, scenes=vera_scenes) hass.data[DOMAIN] = controller_data # Forward the config data to the necessary platforms. for platform in get_configured_platforms(controller_data): hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, platform)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """ Set up Strava Home Assistant config entry initiated through the HASS-UI. """ hass.data.setdefault(DOMAIN, {}) # OAuth Stuff try: implementation = await config_entry_oauth2_flow.async_get_config_entry_implementation( hass=hass, config_entry=entry ) except ValueError: implementation = config_entry_oauth2_flow.LocalOAuth2Implementation( hass, DOMAIN, entry.data[CONF_CLIENT_ID], entry.data[CONF_CLIENT_SECRET], OAUTH2_AUTHORIZE, OAUTH2_TOKEN, ) OAuth2FlowHandler.async_register_implementation(hass, implementation) oauth_websession = config_entry_oauth2_flow.OAuth2Session( hass, entry, implementation ) await oauth_websession.async_ensure_token_valid() # webhook view to get notifications for strava activity updates def strava_update_event_factory(data, event_type=CONF_STRAVA_DATA_UPDATE_EVENT): hass.bus.fire(event_type, data) strava_webhook_view = StravaWebhookView( oauth_websession=oauth_websession, event_factory=strava_update_event_factory, host=get_url(hass, allow_internal=False, allow_ip=False), hass=hass, ) hass.http.register_view(strava_webhook_view) # event listeners async def strava_startup_functions(): await renew_webhook_subscription( hass=hass, entry=entry, webhook_view=strava_webhook_view ) await strava_webhook_view.fetch_strava_data() return True def ha_start_handler(event): """ called when HA rebooted i.e. after all webhook views have been registered and are available """ hass.async_create_task(strava_startup_functions()) def component_reload_handler(event): """called when the component reloads""" hass.async_create_task(strava_startup_functions()) async def async_strava_config_update_handler(): """called when user changes sensor configs""" await strava_webhook_view.fetch_strava_data() return def strava_config_update_handler(event): hass.async_create_task(async_strava_config_update_handler()) def core_config_update_handler(event): """ handles relevant changes to the HA core config. In particular, for URL and Unit System changes """ if "external_url" in event.data.keys(): hass.async_create_task( renew_webhook_subscription( hass=hass, entry=entry, webhook_view=strava_webhook_view ) ) if "unit_system" in event.data.keys(): hass.async_create_task(strava_webhook_view.fetch_strava_data()) # register event listeners hass.data[DOMAIN]["remove_update_listener"] = [] # if hass.bus.async_listeners().get(EVENT_HOMEASSISTANT_START, 0) < 1: hass.data[DOMAIN]["remove_update_listener"].append( hass.bus.async_listen(EVENT_HOMEASSISTANT_START, ha_start_handler) ) # if hass.bus.async_listeners().get(EVENT_CORE_CONFIG_UPDATE, 0) < 1: hass.data[DOMAIN]["remove_update_listener"].append( hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, core_config_update_handler) ) if hass.bus.async_listeners().get(CONF_STRAVA_RELOAD_EVENT, 0) < 1: hass.data[DOMAIN]["remove_update_listener"].append( hass.bus.async_listen(CONF_STRAVA_RELOAD_EVENT, component_reload_handler) ) if hass.bus.async_listeners().get(CONF_STRAVA_CONFIG_UPDATE_EVENT, 0) < 1: hass.data[DOMAIN]["remove_update_listener"].append( hass.bus.async_listen( CONF_STRAVA_CONFIG_UPDATE_EVENT, strava_config_update_handler ) ) hass.data[DOMAIN]["remove_update_listener"] = [ entry.add_update_listener(strava_config_update_helper) ] for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component) ) return True
async def async_setup_entry( # noqa: C901 hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up ozw from a config entry.""" hass.data.setdefault(DOMAIN, {}) ozw_data = hass.data[DOMAIN][entry.entry_id] = {} ozw_data[DATA_UNSUBSCRIBE] = [] data_nodes = {} hass.data[DOMAIN][NODES_VALUES] = data_values = {} removed_nodes = [] manager_options = {"topic_prefix": f"{TOPIC_OPENZWAVE}/"} if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=DOMAIN) if entry.data.get(CONF_USE_ADDON): # Do not use MQTT integration. Use own MQTT client. # Retrieve discovery info from the OpenZWave add-on. discovery_info = await hass.components.hassio.async_get_addon_discovery_info( "core_zwave") if not discovery_info: _LOGGER.error("Failed to get add-on discovery info") raise ConfigEntryNotReady discovery_info_config = discovery_info["config"] host = discovery_info_config["host"] port = discovery_info_config["port"] username = discovery_info_config["username"] password = discovery_info_config["password"] mqtt_client = MQTTClient(host, port, username=username, password=password) manager_options["send_message"] = mqtt_client.send_message else: mqtt_entries = hass.config_entries.async_entries("mqtt") if not mqtt_entries or mqtt_entries[ 0].state is not ConfigEntryState.LOADED: _LOGGER.error("MQTT integration is not set up") return False mqtt_entry = mqtt_entries[0] # MQTT integration only has one entry. @callback def send_message(topic, payload): if mqtt_entry.state is not ConfigEntryState.LOADED: _LOGGER.error("MQTT integration is not set up") return mqtt.async_publish(hass, topic, json.dumps(payload)) manager_options["send_message"] = send_message options = OZWOptions(**manager_options) manager = OZWManager(options) hass.data[DOMAIN][MANAGER] = manager @callback def async_node_added(node): # Caution: This is also called on (re)start. _LOGGER.debug("[NODE ADDED] node_id: %s", node.id) data_nodes[node.id] = node if node.id not in data_values: data_values[node.id] = [] @callback def async_node_changed(node): _LOGGER.debug("[NODE CHANGED] node_id: %s", node.id) data_nodes[node.id] = node # notify devices about the node change if node.id not in removed_nodes: hass.async_create_task(async_handle_node_update(hass, node)) @callback def async_node_removed(node): _LOGGER.debug("[NODE REMOVED] node_id: %s", node.id) data_nodes.pop(node.id) # node added/removed events also happen on (re)starts of hass/mqtt/ozw # cleanup device/entity registry if we know this node is permanently deleted # entities itself are removed by the values logic if node.id in removed_nodes: hass.async_create_task(async_handle_remove_node(hass, node)) removed_nodes.remove(node.id) @callback def async_instance_event(message): event = message["event"] event_data = message["data"] _LOGGER.debug("[INSTANCE EVENT]: %s - data: %s", event, event_data) # The actual removal action of a Z-Wave node is reported as instance event # Only when this event is detected we cleanup the device and entities from hass # Note: Find a more elegant way of doing this, e.g. a notification of this event from OZW if event in ("removenode", "removefailednode") and "Node" in event_data: removed_nodes.append(event_data["Node"]) @callback def async_value_added(value): node = value.node # Clean up node.node_id and node.id use. They are the same. node_id = value.node.node_id # Filter out CommandClasses we're definitely not interested in. if value.command_class in (CommandClass.MANUFACTURER_SPECIFIC, ): return _LOGGER.debug( "[VALUE ADDED] node_id: %s - label: %s - value: %s - value_id: %s - CC: %s", value.node.id, value.label, value.value, value.value_id_key, value.command_class, ) node_data_values = data_values[node_id] # Check if this value should be tracked by an existing entity value_unique_id = create_value_id(value) for values in node_data_values: values.async_check_value(value) if values.values_id == value_unique_id: return # this value already has an entity # Run discovery on it and see if any entities need created for schema in DISCOVERY_SCHEMAS: if not check_node_schema(node, schema): continue if not check_value_schema( value, schema[const.DISC_VALUES][const.DISC_PRIMARY]): continue values = ZWaveDeviceEntityValues(hass, options, schema, value) values.async_setup() # This is legacy and can be cleaned up since we are in the main thread: # We create a new list and update the reference here so that # the list can be safely iterated over in the main thread data_values[node_id] = node_data_values + [values] @callback def async_value_changed(value): # if an entity belonging to this value needs updating, # it's handled within the entity logic _LOGGER.debug( "[VALUE CHANGED] node_id: %s - label: %s - value: %s - value_id: %s - CC: %s", value.node.id, value.label, value.value, value.value_id_key, value.command_class, ) # Handle a scene activation message if value.command_class in ( CommandClass.SCENE_ACTIVATION, CommandClass.CENTRAL_SCENE, ): async_handle_scene_activated(hass, value) return @callback def async_value_removed(value): _LOGGER.debug( "[VALUE REMOVED] node_id: %s - label: %s - value: %s - value_id: %s - CC: %s", value.node.id, value.label, value.value, value.value_id_key, value.command_class, ) # signal all entities using this value for removal value_unique_id = create_value_id(value) async_dispatcher_send(hass, const.SIGNAL_DELETE_ENTITY, value_unique_id) # remove value from our local list node_data_values = data_values[value.node.id] node_data_values[:] = [ item for item in node_data_values if item.values_id != value_unique_id ] # Listen to events for node and value changes for event, event_callback in ( (EVENT_NODE_ADDED, async_node_added), (EVENT_NODE_CHANGED, async_node_changed), (EVENT_NODE_REMOVED, async_node_removed), (EVENT_VALUE_ADDED, async_value_added), (EVENT_VALUE_CHANGED, async_value_changed), (EVENT_VALUE_REMOVED, async_value_removed), (EVENT_INSTANCE_EVENT, async_instance_event), ): ozw_data[DATA_UNSUBSCRIBE].append(options.listen( event, event_callback)) # Register Services services = ZWaveServices(hass, manager) services.async_register() # Register WebSocket API async_register_api(hass) @callback def async_receive_message(msg): manager.receive_message(msg.topic, msg.payload) async def start_platforms(): await asyncio.gather( *(hass.config_entries.async_forward_entry_setup(entry, platform) for platform in PLATFORMS)) if entry.data.get(CONF_USE_ADDON): mqtt_client_task = asyncio.create_task( mqtt_client.start_client(manager)) async def async_stop_mqtt_client(event=None): """Stop the mqtt client. Do not unsubscribe the manager topic. """ mqtt_client_task.cancel() with suppress(asyncio.CancelledError): await mqtt_client_task ozw_data[DATA_UNSUBSCRIBE].append( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_stop_mqtt_client)) ozw_data[DATA_STOP_MQTT_CLIENT] = async_stop_mqtt_client else: ozw_data[DATA_UNSUBSCRIBE].append(await mqtt.async_subscribe( hass, f"{manager.options.topic_prefix}#", async_receive_message)) hass.async_create_task(start_platforms()) return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up an Utility Meter.""" component = EntityComponent(_LOGGER, DOMAIN, hass) hass.data[DATA_UTILITY] = {} register_services = False for meter, conf in config[DOMAIN].items(): _LOGGER.debug("Setup %s.%s", DOMAIN, meter) hass.data[DATA_UTILITY][meter] = conf hass.data[DATA_UTILITY][meter][DATA_TARIFF_SENSORS] = [] if not conf[CONF_TARIFFS]: # only one entity is required name = conf.get(CONF_NAME, meter) hass.async_create_task( discovery.async_load_platform( hass, SENSOR_DOMAIN, DOMAIN, {name: {CONF_METER: meter, CONF_NAME: name}}, config, ) ) else: # create tariff selection await component.async_add_entities( [TariffSelect(meter, list(conf[CONF_TARIFFS]))] ) hass.data[DATA_UTILITY][meter][CONF_TARIFF_ENTITY] = "{}.{}".format( DOMAIN, meter ) # add one meter for each tariff tariff_confs = {} for tariff in conf[CONF_TARIFFS]: name = f"{meter} {tariff}" tariff_confs[name] = { CONF_METER: meter, CONF_NAME: name, CONF_TARIFF: tariff, } hass.async_create_task( discovery.async_load_platform( hass, SENSOR_DOMAIN, DOMAIN, tariff_confs, config ) ) register_services = True if register_services: component.async_register_entity_service(SERVICE_RESET, {}, "async_reset_meters") component.async_register_entity_service( SERVICE_SELECT_TARIFF, {vol.Required(ATTR_TARIFF): cv.string}, "async_select_tariff", ) component.async_register_entity_service( SERVICE_SELECT_NEXT_TARIFF, {}, "async_next_tariff" ) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Toyota Connected Services from a config entry.""" if hass.data.get(DOMAIN) is None: hass.data.setdefault(DOMAIN, {}) _LOGGER.info(STARTUP_MESSAGE) email = entry.data[CONF_EMAIL] password = entry.data[CONF_PASSWORD] locale = entry.data[CONF_LOCALE] uuid = entry.data[CONF_UUID] token = entry.data[CONF_API_TOKEN] region = entry.data[CONF_REGION] client = MyT( username=email, password=password, locale=locale, uuid=uuid, region=region.lower(), token=token, ) async def async_update_data(): """Fetch data from Toyota API.""" vehicles = [] try: vehicles = await client.gather_information() except ToyotaLoginError as ex: _LOGGER.error(ex) except Exception as ex: # pylint: disable=broad-except _LOGGER.error(ex) _LOGGER.debug(vehicles) return vehicles coordinator = DataUpdateCoordinator( hass, _LOGGER, name=DOMAIN, update_method=async_update_data, update_interval=UPDATE_INTERVAL, ) # Fetch initial data so we have data when entities subscribe await coordinator.async_refresh() hass.data[DOMAIN][entry.entry_id] = { DATA_CLIENT: client, DATA_COORDINATOR: coordinator, } if not coordinator.last_update_success: raise ConfigEntryNotReady # Setup components for platform in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform)) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry): """Set up BLE Monitor from a config entry.""" _LOGGER.debug("Initializing BLE Monitor entry (config entry): %s", config_entry) # Prevent unload to be triggered each time we update the config entry global UPDATE_UNLISTENER if UPDATE_UNLISTENER: UPDATE_UNLISTENER() if not config_entry.unique_id: hass.config_entries.async_update_entry(config_entry, unique_id=config_entry.title) _LOGGER.debug("async_setup_entry: domain %s", CONFIG_YAML) config = {} if not CONFIG_YAML: for key, value in config_entry.data.items(): config[key] = value for key, value in config_entry.options.items(): config[key] = value config[CONFIG_IS_FLOW] = True if CONF_DEVICES not in config: config[CONF_DEVICES] = [] else: # device configuration is taken from yaml, but yaml config already removed # save unique IDs (only once) if "ids_from_name" in config: devlist = config[CONF_DEVICES] for dev_idx, dev_conf in enumerate(devlist): if CONF_NAME in dev_conf: devlist[dev_idx][CONF_UNIQUE_ID] = dev_conf[CONF_NAME] del config["ids_from_name"] else: for key, value in CONFIG_YAML.items(): config[key] = value if CONF_HCI_INTERFACE in CONFIG_YAML: hci_list = [] if isinstance(CONFIG_YAML[CONF_HCI_INTERFACE], list): for hci in CONFIG_YAML[CONF_HCI_INTERFACE]: hci_list.append(str(hci)) else: hci_list.append(str(CONFIG_YAML[CONF_HCI_INTERFACE])) config[CONF_HCI_INTERFACE] = hci_list hass.config_entries.async_update_entry(config_entry, data={}, options=config) _LOGGER.debug("async_setup_entry: %s", config) UPDATE_UNLISTENER = config_entry.add_update_listener( _async_update_listener) if CONF_HCI_INTERFACE not in config: config[CONF_HCI_INTERFACE] = [DEFAULT_HCI_INTERFACE] else: hci_list = config_entry.options.get(CONF_HCI_INTERFACE) for i, hci in enumerate(hci_list): hci_list[i] = int(hci) config[CONF_HCI_INTERFACE] = hci_list _LOGGER.debug("HCI interface is %s", config[CONF_HCI_INTERFACE]) blemonitor = BLEmonitor(config) hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, blemonitor.shutdown_handler) blemonitor.start() hass.data[DOMAIN] = {} hass.data[DOMAIN]["blemonitor"] = blemonitor hass.data[DOMAIN]["config_entry_id"] = config_entry.entry_id for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup( config_entry, component)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Load a config entry.""" conf = hass.data.get(DATA_MQTT_CONFIG) # Config entry was created because user had configuration.yaml entry # They removed that, so remove entry. if conf is None and entry.source == config_entries.SOURCE_IMPORT: hass.async_create_task(hass.config_entries.async_remove( entry.entry_id)) return False # If user didn't have configuration.yaml config, generate defaults if conf is None: conf = CONFIG_SCHEMA({DOMAIN: dict(entry.data)})[DOMAIN] elif any(key in conf for key in entry.data): shared_keys = conf.keys() & entry.data.keys() override = {k: entry.data[k] for k in shared_keys} if CONF_PASSWORD in override: override[CONF_PASSWORD] = "********" _LOGGER.info( "Data in your configuration entry is going to override your " "configuration.yaml: %s", override, ) conf = _merge_config(entry, conf) hass.data[DATA_MQTT] = MQTT( hass, entry, conf, ) await hass.data[DATA_MQTT].async_connect() async def async_stop_mqtt(_event: Event): """Stop MQTT component.""" await hass.data[DATA_MQTT].async_disconnect() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_stop_mqtt) async def async_publish_service(call: ServiceCall) -> None: """Handle MQTT publish service calls.""" msg_topic = call.data.get(ATTR_TOPIC) msg_topic_template = call.data.get(ATTR_TOPIC_TEMPLATE) payload = call.data.get(ATTR_PAYLOAD) payload_template = call.data.get(ATTR_PAYLOAD_TEMPLATE) qos: int = call.data[ATTR_QOS] retain: bool = call.data[ATTR_RETAIN] if msg_topic_template is not None: try: rendered_topic = template.Template( msg_topic_template, hass).async_render(parse_result=False) msg_topic = valid_publish_topic(rendered_topic) except (jinja2.TemplateError, TemplateError) as exc: _LOGGER.error( "Unable to publish: rendering topic template of %s " "failed because %s", msg_topic_template, exc, ) return except vol.Invalid as err: _LOGGER.error( "Unable to publish: topic template '%s' produced an " "invalid topic '%s' after rendering (%s)", msg_topic_template, rendered_topic, err, ) return if payload_template is not None: try: payload = MqttCommandTemplate( template.Template(payload_template), hass=hass).async_render() except (jinja2.TemplateError, TemplateError) as exc: _LOGGER.error( "Unable to publish to %s: rendering payload template of " "%s failed because %s", msg_topic, payload_template, exc, ) return await hass.data[DATA_MQTT].async_publish(msg_topic, payload, qos, retain) hass.services.async_register(DOMAIN, SERVICE_PUBLISH, async_publish_service, schema=MQTT_PUBLISH_SCHEMA) async def async_dump_service(call: ServiceCall) -> None: """Handle MQTT dump service calls.""" messages = [] @callback def collect_msg(msg): messages.append((msg.topic, msg.payload.replace("\n", ""))) unsub = await async_subscribe(hass, call.data["topic"], collect_msg) def write_dump(): with open(hass.config.path("mqtt_dump.txt"), "wt", encoding="utf8") as fp: for msg in messages: fp.write(",".join(msg) + "\n") async def finish_dump(_): """Write dump to file.""" unsub() await hass.async_add_executor_job(write_dump) event.async_call_later(hass, call.data["duration"], finish_dump) hass.services.async_register( DOMAIN, SERVICE_DUMP, async_dump_service, schema=vol.Schema({ vol.Required("topic"): valid_subscribe_topic, vol.Optional("duration", default=5): int, }), ) if conf.get(CONF_DISCOVERY): await _async_setup_discovery(hass, conf, entry) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Netatmo from a config entry.""" implementation = await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry) # Set unique id if non was set (migration) if not entry.unique_id: hass.config_entries.async_update_entry(entry, unique_id=DOMAIN) hass.data[DOMAIN][entry.entry_id] = { AUTH: api.ConfigEntryNetatmoAuth(hass, entry, implementation) } data_handler = NetatmoDataHandler(hass, entry) await data_handler.async_setup() hass.data[DOMAIN][entry.entry_id][DATA_HANDLER] = data_handler for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) async def unregister_webhook(_): if CONF_WEBHOOK_ID not in entry.data: return _LOGGER.debug("Unregister Netatmo webhook (%s)", entry.data[CONF_WEBHOOK_ID]) webhook_unregister(hass, entry.data[CONF_WEBHOOK_ID]) async def register_webhook(event): if CONF_WEBHOOK_ID not in entry.data: data = {**entry.data, CONF_WEBHOOK_ID: secrets.token_hex()} hass.config_entries.async_update_entry(entry, data=data) if hass.components.cloud.async_active_subscription(): if CONF_CLOUDHOOK_URL not in entry.data: webhook_url = await hass.components.cloud.async_create_cloudhook( entry.data[CONF_WEBHOOK_ID]) data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url} hass.config_entries.async_update_entry(entry, data=data) else: webhook_url = entry.data[CONF_CLOUDHOOK_URL] else: webhook_url = hass.components.webhook.async_generate_url( entry.data[CONF_WEBHOOK_ID]) if entry.data[ "auth_implementation"] == "cloud" and not webhook_url.startswith( "https://"): _LOGGER.warning( "Webhook not registered - " "https and port 443 is required to register the webhook") return try: webhook_register(hass, DOMAIN, "Netatmo", entry.data[CONF_WEBHOOK_ID], handle_webhook) await hass.async_add_executor_job( hass.data[DOMAIN][entry.entry_id][AUTH].addwebhook, webhook_url) _LOGGER.info("Register Netatmo webhook: %s", webhook_url) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, "light")) except pyatmo.ApiError as err: _LOGGER.error("Error during webhook registration - %s", err) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, unregister_webhook) if hass.state == CoreState.running: await register_webhook(None) else: hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, register_webhook) hass.services.async_register(DOMAIN, "register_webhook", register_webhook) hass.services.async_register(DOMAIN, "unregister_webhook", unregister_webhook) return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up the StarLine device from a config entry.""" account = StarlineAccount(hass, config_entry) await account.update() await account.update_obd() if not account.api.available: raise ConfigEntryNotReady if DOMAIN not in hass.data: hass.data[DOMAIN] = {} hass.data[DOMAIN][config_entry.entry_id] = account device_registry = await hass.helpers.device_registry.async_get_registry() for device in account.api.devices.values(): device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, **account.device_info(device) ) for domain in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(config_entry, domain) ) async def async_set_scan_interval(call): """Set scan interval.""" options = dict(config_entry.options) options[CONF_SCAN_INTERVAL] = call.data[CONF_SCAN_INTERVAL] hass.config_entries.async_update_entry(entry=config_entry, options=options) async def async_set_scan_obd_interval(call): """Set OBD info scan interval.""" options = dict(config_entry.options) options[CONF_SCAN_OBD_INTERVAL] = call.data[CONF_SCAN_INTERVAL] hass.config_entries.async_update_entry(entry=config_entry, options=options) async def async_update(call=None): """Update all data.""" await account.update() await account.update_obd() hass.services.async_register(DOMAIN, SERVICE_UPDATE_STATE, async_update) hass.services.async_register( DOMAIN, SERVICE_SET_SCAN_INTERVAL, async_set_scan_interval, schema=vol.Schema( { vol.Required(CONF_SCAN_INTERVAL): vol.All( vol.Coerce(int), vol.Range(min=10) ) } ), ) hass.services.async_register( DOMAIN, SERVICE_SET_SCAN_OBD_INTERVAL, async_set_scan_obd_interval, schema=vol.Schema( { vol.Required(CONF_SCAN_INTERVAL): vol.All( vol.Coerce(int), vol.Range(min=180) ) } ), ) config_entry.add_update_listener(async_options_updated) await async_options_updated(hass, config_entry) return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Create a (EMEA/EU-based) Honeywell TCC system.""" async def load_auth_tokens(store) -> tuple[dict, dict | None]: app_storage = await store.async_load() tokens = dict(app_storage or {}) if tokens.pop(CONF_USERNAME, None) != config[DOMAIN][CONF_USERNAME]: # any tokens won't be valid, and store might be be corrupt await store.async_save({}) return ({}, None) # evohomeasync2 requires naive/local datetimes as strings if tokens.get(ACCESS_TOKEN_EXPIRES) is not None: tokens[ACCESS_TOKEN_EXPIRES] = _dt_aware_to_naive( dt_util.parse_datetime(tokens[ACCESS_TOKEN_EXPIRES])) user_data = tokens.pop(USER_DATA, None) return (tokens, user_data) store = hass.helpers.storage.Store(STORAGE_VER, STORAGE_KEY) tokens, user_data = await load_auth_tokens(store) client_v2 = evohomeasync2.EvohomeClient( config[DOMAIN][CONF_USERNAME], config[DOMAIN][CONF_PASSWORD], **tokens, session=async_get_clientsession(hass), ) try: await client_v2.login() except (aiohttp.ClientError, evohomeasync2.AuthenticationError) as err: _handle_exception(err) return False finally: config[DOMAIN][CONF_PASSWORD] = "REDACTED" loc_idx = config[DOMAIN][CONF_LOCATION_IDX] try: loc_config = client_v2.installation_info[loc_idx] except IndexError: _LOGGER.error( "Config error: '%s' = %s, but the valid range is 0-%s. " "Unable to continue. Fix any configuration errors and restart HA", CONF_LOCATION_IDX, loc_idx, len(client_v2.installation_info) - 1, ) return False if _LOGGER.isEnabledFor(logging.DEBUG): _config = {"locationInfo": {"timeZone": None}, GWS: [{TCS: None}]} _config["locationInfo"]["timeZone"] = loc_config["locationInfo"][ "timeZone"] _config[GWS][0][TCS] = loc_config[GWS][0][TCS] _LOGGER.debug("Config = %s", _config) client_v1 = evohomeasync.EvohomeClient( client_v2.username, client_v2.password, user_data=user_data, session=async_get_clientsession(hass), ) hass.data[DOMAIN] = {} hass.data[DOMAIN]["broker"] = broker = EvoBroker(hass, client_v2, client_v1, store, config[DOMAIN]) await broker.save_auth_tokens() await broker.async_update() # get initial state hass.async_create_task( async_load_platform(hass, Platform.CLIMATE, DOMAIN, {}, config)) if broker.tcs.hotwater: hass.async_create_task( async_load_platform(hass, Platform.WATER_HEATER, DOMAIN, {}, config)) hass.helpers.event.async_track_time_interval( broker.async_update, config[DOMAIN][CONF_SCAN_INTERVAL]) setup_service_functions(hass, broker) return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Ness Alarm platform.""" conf = config[DOMAIN] zones = conf[CONF_ZONES] host = conf[CONF_HOST] port = conf[CONF_DEVICE_PORT] scan_interval = conf[CONF_SCAN_INTERVAL] infer_arming_state = conf[CONF_INFER_ARMING_STATE] client = Client( host=host, port=port, loop=hass.loop, update_interval=scan_interval.total_seconds(), infer_arming_state=infer_arming_state, ) hass.data[DATA_NESS] = client async def _close(event): await client.close() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _close) hass.async_create_task( async_load_platform(hass, "binary_sensor", DOMAIN, {CONF_ZONES: zones}, config)) hass.async_create_task( async_load_platform(hass, "alarm_control_panel", DOMAIN, {}, config)) def on_zone_change(zone_id: int, state: bool): """Receives and propagates zone state updates.""" async_dispatcher_send(hass, SIGNAL_ZONE_CHANGED, ZoneChangedData(zone_id=zone_id, state=state)) def on_state_change(arming_state: ArmingState): """Receives and propagates arming state updates.""" async_dispatcher_send(hass, SIGNAL_ARMING_STATE_CHANGED, arming_state) client.on_zone_change(on_zone_change) client.on_state_change(on_state_change) # Force update for current arming status and current zone states hass.loop.create_task(client.keepalive()) hass.loop.create_task(client.update()) async def handle_panic(call: ServiceCall) -> None: await client.panic(call.data[ATTR_CODE]) async def handle_aux(call: ServiceCall) -> None: await client.aux(call.data[ATTR_OUTPUT_ID], call.data[ATTR_STATE]) hass.services.async_register(DOMAIN, SERVICE_PANIC, handle_panic, schema=SERVICE_SCHEMA_PANIC) hass.services.async_register(DOMAIN, SERVICE_AUX, handle_aux, schema=SERVICE_SCHEMA_AUX) return True
async def async_setup_entry( hass: core.HomeAssistant, entry: config_entries.ConfigEntry ): """Set up the xiaomi aqara components from a config entry.""" hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN].setdefault(GATEWAYS_KEY, {}) # Connect to Xiaomi Aqara Gateway xiaomi_gateway = await hass.async_add_executor_job( XiaomiGateway, entry.data[CONF_HOST], entry.data[CONF_SID], entry.data[CONF_KEY], DEFAULT_DISCOVERY_RETRY, entry.data[CONF_INTERFACE], entry.data[CONF_PORT], entry.data[CONF_PROTOCOL], ) hass.data[DOMAIN][GATEWAYS_KEY][entry.entry_id] = xiaomi_gateway gateway_discovery = hass.data[DOMAIN].setdefault( LISTENER_KEY, XiaomiGatewayDiscovery(hass.add_job, [], entry.data[CONF_INTERFACE]), ) if len(hass.data[DOMAIN][GATEWAYS_KEY]) == 1: # start listining for local pushes (only once) await hass.async_add_executor_job(gateway_discovery.listen) # register stop callback to shutdown listining for local pushes def stop_xiaomi(event): """Stop Xiaomi Socket.""" _LOGGER.debug("Shutting down Xiaomi Gateway Listener") gateway_discovery.stop_listen() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_xiaomi) gateway_discovery.gateways[entry.data[CONF_HOST]] = xiaomi_gateway _LOGGER.debug( "Gateway with host '%s' connected, listening for broadcasts", entry.data[CONF_HOST], ) device_registry = await dr.async_get_registry(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, entry.unique_id)}, manufacturer="Xiaomi Aqara", name=entry.title, sw_version=entry.data[CONF_PROTOCOL], ) if entry.data[CONF_KEY] is not None: platforms = GATEWAY_PLATFORMS else: platforms = GATEWAY_PLATFORMS_NO_KEY for platform in platforms: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform) ) return True
async def async_from_config_dict(config: Dict[str, Any], hass: core.HomeAssistant, config_dir: Optional[str] = None, enable_log: bool = True, verbose: bool = False, skip_pip: bool = False, log_rotate_days: Any = None, log_file: Any = None, log_no_color: bool = False) \ -> Optional[core.HomeAssistant]: """Try to configure Home Assistant from a configuration dictionary. Dynamically loads required components and its dependencies. This method is a coroutine. """ start = time() if enable_log: async_enable_logging(hass, verbose, log_rotate_days, log_file, log_no_color) core_config = config.get(core.DOMAIN, {}) has_api_password = bool((config.get('http') or {}).get('api_password')) has_trusted_networks = bool((config.get('http') or {}) .get('trusted_networks')) try: await conf_util.async_process_ha_core_config( hass, core_config, has_api_password, has_trusted_networks) except vol.Invalid as config_err: conf_util.async_log_exception( config_err, 'homeassistant', core_config, hass) return None except HomeAssistantError: _LOGGER.error("Home Assistant core failed to initialize. " "Further initialization aborted") return None await hass.async_add_executor_job( conf_util.process_ha_config_upgrade, hass) hass.config.skip_pip = skip_pip if skip_pip: _LOGGER.warning("Skipping pip installation of required modules. " "This may cause issues") # Make a copy because we are mutating it. config = OrderedDict(config) # Merge packages conf_util.merge_packages_config( hass, config, core_config.get(conf_util.CONF_PACKAGES, {})) hass.config_entries = config_entries.ConfigEntries(hass, config) await hass.config_entries.async_load() # Filter out the repeating and common config section [homeassistant] components = set(key.split(' ')[0] for key in config.keys() if key != core.DOMAIN) components.update(hass.config_entries.async_domains()) # Resolve all dependencies of all components. for component in list(components): try: components.update(loader.component_dependencies(hass, component)) except loader.LoaderError: # Ignore it, or we'll break startup # It will be properly handled during setup. pass # setup components res = await core_components.async_setup(hass, config) if not res: _LOGGER.error("Home Assistant core failed to initialize. " "Further initialization aborted") return hass await persistent_notification.async_setup(hass, config) _LOGGER.info("Home Assistant core initialized") # stage 1 for component in components: if component not in FIRST_INIT_COMPONENT: continue hass.async_create_task(async_setup_component(hass, component, config)) await hass.async_block_till_done() # stage 2 for component in components: if component in FIRST_INIT_COMPONENT: continue hass.async_create_task(async_setup_component(hass, component, config)) await hass.async_block_till_done() stop = time() _LOGGER.info("Home Assistant initialized in %.2fs", stop-start) # TEMP: warn users for invalid slugs # Remove after 0.94 or 1.0 if cv.INVALID_SLUGS_FOUND or cv.INVALID_ENTITY_IDS_FOUND: msg = [] if cv.INVALID_ENTITY_IDS_FOUND: msg.append( "Your configuration contains invalid entity ID references. " "Please find and update the following. " "This will become a breaking change." ) msg.append('\n'.join('- {} -> {}'.format(*item) for item in cv.INVALID_ENTITY_IDS_FOUND.items())) if cv.INVALID_SLUGS_FOUND: msg.append( "Your configuration contains invalid slugs. " "Please find and update the following. " "This will become a breaking change." ) msg.append('\n'.join('- {} -> {}'.format(*item) for item in cv.INVALID_SLUGS_FOUND.items())) hass.components.persistent_notification.async_create( '\n\n'.join(msg), "Config Warning", "config_warning" ) # TEMP: warn users of invalid extra keys # Remove after 0.92 if cv.INVALID_EXTRA_KEYS_FOUND: msg = [] msg.append( "Your configuration contains extra keys " "that the platform does not support (but were silently " "accepted before 0.88). Please find and remove the following." "This will become a breaking change." ) msg.append('\n'.join('- {}'.format(it) for it in cv.INVALID_EXTRA_KEYS_FOUND)) hass.components.persistent_notification.async_create( '\n\n'.join(msg), "Config Warning", "config_warning" ) return hass
async def websocket_update_topology(hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]) -> None: """Update the ZHA network topology.""" zha_gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] hass.async_create_task(zha_gateway.application_controller.topology.scan())
def async_dismiss(hass: HomeAssistant, notification_id: str) -> None: """Remove a notification.""" data = {ATTR_NOTIFICATION_ID: notification_id} hass.async_create_task( hass.services.async_call(DOMAIN, SERVICE_DISMISS, data))
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up motionEye from a config entry.""" hass.data.setdefault(DOMAIN, {}) client = create_motioneye_client( entry.data[CONF_URL], admin_username=entry.data.get(CONF_ADMIN_USERNAME), admin_password=entry.data.get(CONF_ADMIN_PASSWORD), surveillance_username=entry.data.get(CONF_SURVEILLANCE_USERNAME), surveillance_password=entry.data.get(CONF_SURVEILLANCE_PASSWORD), ) try: await client.async_client_login() except MotionEyeClientInvalidAuthError as exc: await client.async_client_close() raise ConfigEntryAuthFailed from exc except MotionEyeClientError as exc: await client.async_client_close() raise ConfigEntryNotReady from exc @callback async def async_update_data() -> dict[str, Any] | None: try: return await client.async_get_cameras() except MotionEyeClientError as exc: raise UpdateFailed("Error communicating with API") from exc coordinator = DataUpdateCoordinator( hass, _LOGGER, name=DOMAIN, update_method=async_update_data, update_interval=DEFAULT_SCAN_INTERVAL, ) hass.data[DOMAIN][entry.entry_id] = { CONF_CLIENT: client, CONF_COORDINATOR: coordinator, } current_cameras: set[tuple[str, str]] = set() device_registry = await dr.async_get_registry(hass) @callback def _async_process_motioneye_cameras() -> None: """Process motionEye camera additions and removals.""" inbound_camera: set[tuple[str, str]] = set() if coordinator.data is None or KEY_CAMERAS not in coordinator.data: return for camera in coordinator.data[KEY_CAMERAS]: if not is_acceptable_camera(camera): return camera_id = camera[KEY_ID] device_identifier = get_motioneye_device_identifier( entry.entry_id, camera_id ) inbound_camera.add(device_identifier) if device_identifier in current_cameras: continue current_cameras.add(device_identifier) _add_camera( hass, device_registry, client, entry, camera_id, camera, device_identifier, ) # Ensure every device associated with this config entry is still in the list of # motionEye cameras, otherwise remove the device (and thus entities). for device_entry in dr.async_entries_for_config_entry( device_registry, entry.entry_id ): for identifier in device_entry.identifiers: if identifier in inbound_camera: break else: device_registry.async_remove_device(device_entry.id) async def setup_then_listen() -> None: await asyncio.gather( *[ hass.config_entries.async_forward_entry_setup(entry, platform) for platform in PLATFORMS ] ) entry.async_on_unload( coordinator.async_add_listener(_async_process_motioneye_cameras) ) await coordinator.async_refresh() hass.async_create_task(setup_then_listen()) return True