def async_from_config_file(config_path: str, hass: core.HomeAssistant, verbose: bool=False, skip_pip: bool=True, log_rotate_days: Any=None): """Read the configuration file and try to start all the functionality. Will add functionality to 'hass' parameter. This method is a coroutine. """ # Set config dir to directory holding config file config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = config_dir yield from hass.async_add_job(mount_local_lib_path, config_dir) async_enable_logging(hass, verbose, log_rotate_days) try: config_dict = yield from hass.async_add_job( conf_util.load_yaml_config_file, config_path) except HomeAssistantError as err: _LOGGER.error('Error loading %s: %s', config_path, err) return None finally: clear_secret_cache() hass = yield from async_from_config_dict( config_dict, hass, enable_log=False, skip_pip=skip_pip) return hass
def _load_restore_cache(hass: HomeAssistant): """Load the restore cache to be used by other components.""" @callback def remove_cache(event): """Remove the states cache.""" hass.data.pop(DATA_RESTORE_CACHE, None) hass.bus.listen_once(EVENT_HOMEASSISTANT_START, remove_cache) last_run = last_recorder_run(hass) if last_run is None or last_run.end is None: _LOGGER.debug('Not creating cache - no suitable last run found: %s', last_run) hass.data[DATA_RESTORE_CACHE] = {} return last_end_time = last_run.end - timedelta(seconds=1) # Unfortunately the recorder_run model do not return offset-aware time last_end_time = last_end_time.replace(tzinfo=dt_util.UTC) _LOGGER.debug("Last run: %s - %s", last_run.start, last_end_time) states = get_states(hass, last_end_time, run=last_run) # Cache the states hass.data[DATA_RESTORE_CACHE] = { state.entity_id: state for state in states} _LOGGER.debug('Created cache with %s', list(hass.data[DATA_RESTORE_CACHE]))
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 setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the ISY 994 platform.""" hass.data[ISY994_NODES] = {} for domain in SUPPORTED_DOMAINS: hass.data[ISY994_NODES][domain] = [] hass.data[ISY994_WEATHER] = [] hass.data[ISY994_PROGRAMS] = {} for domain in SUPPORTED_DOMAINS: hass.data[ISY994_PROGRAMS][domain] = [] isy_config = config.get(DOMAIN) user = isy_config.get(CONF_USERNAME) password = isy_config.get(CONF_PASSWORD) tls_version = isy_config.get(CONF_TLS_VER) host = urlparse(isy_config.get(CONF_HOST)) ignore_identifier = isy_config.get(CONF_IGNORE_STRING) sensor_identifier = isy_config.get(CONF_SENSOR_STRING) enable_climate = isy_config.get(CONF_ENABLE_CLIMATE) if host.scheme == 'http': https = False port = host.port or 80 elif host.scheme == 'https': https = True port = host.port or 443 else: _LOGGER.error("isy994 host value in configuration is invalid") return False import PyISY # Connect to ISY controller. isy = PyISY.ISY(host.hostname, port, username=user, password=password, use_https=https, tls_ver=tls_version, log=_LOGGER) if not isy.connected: return False _categorize_nodes(hass, isy.nodes, ignore_identifier, sensor_identifier) _categorize_programs(hass, isy.programs) if enable_climate and isy.configuration.get('Weather Information'): _categorize_weather(hass, isy.climate) def stop(event: object) -> None: """Stop ISY auto updates.""" isy.auto_update = False # Listen for HA stop to disconnect. hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop) # Load platforms for the devices in the ISY controller that we support. for component in SUPPORTED_DOMAINS: discovery.load_platform(hass, component, DOMAIN, {}, config) isy.auto_update = True return True
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_platform(hass: HomeAssistant, config, async_add_devices, discovery_info=None): """Set up DLNA DMR platform.""" if config.get(CONF_URL) is not None: url = config[CONF_URL] name = config.get(CONF_NAME) elif discovery_info is not None: url = discovery_info['ssdp_description'] name = discovery_info.get('name') if DLNA_DMR_DATA not in hass.data: hass.data[DLNA_DMR_DATA] = {} if 'lock' not in hass.data[DLNA_DMR_DATA]: hass.data[DLNA_DMR_DATA]['lock'] = asyncio.Lock() # build upnp/aiohttp requester from async_upnp_client.aiohttp import AiohttpSessionRequester session = async_get_clientsession(hass) requester = AiohttpSessionRequester(session, True) # ensure event handler has been started with await hass.data[DLNA_DMR_DATA]['lock']: server_host = config.get(CONF_LISTEN_IP) if server_host is None: server_host = get_local_ip() server_port = config.get(CONF_LISTEN_PORT, DEFAULT_LISTEN_PORT) event_handler = await async_start_event_handler(hass, server_host, server_port, requester) # create upnp device from async_upnp_client import UpnpFactory factory = UpnpFactory(requester, disable_state_variable_validation=True) try: upnp_device = await factory.async_create_device(url) except (asyncio.TimeoutError, aiohttp.ClientError): raise PlatformNotReady() # wrap with DmrDevice from async_upnp_client.dlna import DmrDevice dlna_device = DmrDevice(upnp_device, event_handler) # create our own device device = DlnaDmrDevice(dlna_device, name) _LOGGER.debug("Adding device: %s", device) async_add_devices([device], True)
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 async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the recorder.""" conf = config.get(DOMAIN, {}) keep_days = conf.get(CONF_PURGE_KEEP_DAYS) purge_interval = conf.get(CONF_PURGE_INTERVAL) db_url = conf.get(CONF_DB_URL, None) if not db_url: db_url = DEFAULT_URL.format( hass_config_path=hass.config.path(DEFAULT_DB_FILE)) include = conf.get(CONF_INCLUDE, {}) exclude = conf.get(CONF_EXCLUDE, {}) instance = hass.data[DATA_INSTANCE] = Recorder( hass=hass, keep_days=keep_days, purge_interval=purge_interval, uri=db_url, include=include, exclude=exclude) instance.async_initialize() instance.start() @asyncio.coroutine def async_handle_purge_service(service): """Handle calls to the purge service.""" instance.do_adhoc_purge(service.data[ATTR_KEEP_DAYS]) descriptions = yield from hass.async_add_job( conf_util.load_yaml_config_file, path.join( path.dirname(__file__), 'services.yaml')) hass.services.async_register(DOMAIN, SERVICE_PURGE, async_handle_purge_service, descriptions.get(SERVICE_PURGE), schema=SERVICE_PURGE_SCHEMA) return (yield from instance.async_db_ready)
def _async_process_requirements(hass: core.HomeAssistant, name: str, requirements) -> bool: """Install the requirements for a component. This method is a coroutine. """ if hass.config.skip_pip: return True pip_lock = hass.data.get(DATA_PIP_LOCK) if pip_lock is None: pip_lock = hass.data[DATA_PIP_LOCK] = asyncio.Lock(loop=hass.loop) def pip_install(mod): """Install packages.""" if pkg_util.running_under_virtualenv(): return pkg_util.install_package( mod, constraints=os.path.join( os.path.dirname(__file__), CONSTRAINT_FILE)) return pkg_util.install_package( mod, target=hass.config.path('deps'), constraints=os.path.join( os.path.dirname(__file__), CONSTRAINT_FILE)) with (yield from pip_lock): for req in requirements: ret = yield from hass.async_add_job(pip_install, req) if not ret: _LOGGER.error("Not initializing %s because could not install " "dependency %s", name, req) async_notify_setup_error(hass, name) return False return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the HomematicIP Cloud component.""" hass.data[DOMAIN] = {} accesspoints = config.get(DOMAIN, []) for conf in accesspoints: if conf[CONF_ACCESSPOINT] not in configured_haps(hass): hass.async_add_job(hass.config_entries.flow.async_init( DOMAIN, context={'source': config_entries.SOURCE_IMPORT}, data={ HMIPC_HAPID: conf[CONF_ACCESSPOINT], HMIPC_AUTHTOKEN: conf[CONF_AUTHTOKEN], HMIPC_NAME: conf[CONF_NAME], } )) 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 run(args): """Handle Home Assistant auth provider script.""" parser = argparse.ArgumentParser( description="Manage Home Assistant users") parser.add_argument( '--script', choices=['auth']) parser.add_argument( '-c', '--config', default=get_default_config_dir(), help="Directory that contains the Home Assistant configuration") subparsers = parser.add_subparsers(dest='func') subparsers.required = True parser_list = subparsers.add_parser('list') parser_list.set_defaults(func=list_users) parser_add = subparsers.add_parser('add') parser_add.add_argument('username', type=str) parser_add.add_argument('password', type=str) parser_add.set_defaults(func=add_user) parser_validate_login = subparsers.add_parser('validate') parser_validate_login.add_argument('username', type=str) parser_validate_login.add_argument('password', type=str) parser_validate_login.set_defaults(func=validate_login) parser_change_pw = subparsers.add_parser('change_password') parser_change_pw.add_argument('username', type=str) parser_change_pw.add_argument('new_password', type=str) parser_change_pw.set_defaults(func=change_password) args = parser.parse_args(args) loop = asyncio.get_event_loop() hass = HomeAssistant(loop=loop) loop.run_until_complete(run_command(hass, args)) # Triggers save on used storage helpers with delay (core auth) logging.getLogger('homeassistant.core').setLevel(logging.WARNING) loop.run_until_complete(hass.async_stop())
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))
def async_setup(hass: HomeAssistant, yaml_config: Dict[str, Any]): """Activate Google Actions component.""" config = yaml_config.get(DOMAIN, {}) agent_user_id = config.get(CONF_AGENT_USER_ID) api_key = config.get(CONF_API_KEY) if api_key is not None: descriptions = yield from hass.async_add_job( conf_util.load_yaml_config_file, os.path.join( os.path.dirname(__file__), 'services.yaml') ) hass.http.register_view(GoogleAssistantAuthView(hass, config)) hass.http.register_view(GoogleAssistantView(hass, config)) @asyncio.coroutine def request_sync_service_handler(call): """Handle request sync service calls.""" websession = async_get_clientsession(hass) try: with async_timeout.timeout(5, loop=hass.loop): res = yield from websession.post( REQUEST_SYNC_BASE_URL, params={'key': api_key}, json={'agent_user_id': agent_user_id}) _LOGGER.info("Submitted request_sync request to Google") res.raise_for_status() except aiohttp.ClientResponseError: body = yield from res.read() _LOGGER.error( 'request_sync request failed: %d %s', res.status, body) except (asyncio.TimeoutError, aiohttp.ClientError): _LOGGER.error("Could not contact Google for request_sync") # Register service only if api key is provided if api_key is not None: hass.services.async_register( DOMAIN, SERVICE_REQUEST_SYNC, request_sync_service_handler, descriptions.get(SERVICE_REQUEST_SYNC)) return True
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 (yield from setup_tasks[domain]) if config is None: config = {} if setup_tasks is None: setup_tasks = hass.data[DATA_SETUP] = {} task = setup_tasks[domain] = hass.async_add_job( _async_setup_component(hass, domain, config)) return (yield from task)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up an access point from a config entry.""" hap = HomematicipHAP(hass, entry) hapid = entry.data[HMIPC_HAPID].replace('-', '').upper() hass.data[DOMAIN][hapid] = hap if not await hap.async_setup(): return False # Register hap as device in registry. device_registry = await dr.async_get_registry(hass) home = hap.home # Add the HAP name from configuration if set. hapname = home.label \ if not home.name else "{} {}".format(home.label, home.name) device_registry.async_get_or_create( config_entry_id=home.id, identifiers={(DOMAIN, home.id)}, manufacturer='eQ-3', name=hapname, model=home.modelType, sw_version=home.currentAPVersion, ) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Initialize config entry which represents an installed SmartApp.""" # For backwards compat if entry.unique_id is None: hass.config_entries.async_update_entry( entry, unique_id=format_unique_id(entry.data[CONF_APP_ID], entry.data[CONF_LOCATION_ID]), ) if not validate_webhook_requirements(hass): _LOGGER.warning( "The 'base_url' of the 'http' integration must be configured and start with 'https://'" ) return False api = SmartThings(async_get_clientsession(hass), entry.data[CONF_ACCESS_TOKEN]) remove_entry = False try: # See if the app is already setup. This occurs when there are # installs in multiple SmartThings locations (valid use-case) manager = hass.data[DOMAIN][DATA_MANAGER] smart_app = manager.smartapps.get(entry.data[CONF_APP_ID]) if not smart_app: # Validate and setup the app. app = await api.app(entry.data[CONF_APP_ID]) smart_app = setup_smartapp(hass, app) # Validate and retrieve the installed app. installed_app = await validate_installed_app( api, entry.data[CONF_INSTALLED_APP_ID]) # Get scenes scenes = await async_get_entry_scenes(entry, api) # Get SmartApp token to sync subscriptions token = await api.generate_tokens( entry.data[CONF_CLIENT_ID], entry.data[CONF_CLIENT_SECRET], entry.data[CONF_REFRESH_TOKEN], ) hass.config_entries.async_update_entry( entry, data={ **entry.data, CONF_REFRESH_TOKEN: token.refresh_token }) # Get devices and their current status devices = await api.devices(location_ids=[installed_app.location_id]) async def retrieve_device_status(device): try: await device.status.refresh() except ClientResponseError: _LOGGER.debug( "Unable to update status for device: %s (%s), the device will be excluded", device.label, device.device_id, exc_info=True, ) devices.remove(device) await asyncio.gather(*(retrieve_device_status(d) for d in devices.copy())) # Sync device subscriptions await smartapp_sync_subscriptions( hass, token.access_token, installed_app.location_id, installed_app.installed_app_id, devices, ) # Setup device broker broker = DeviceBroker(hass, entry, token, smart_app, devices, scenes) broker.connect() hass.data[DOMAIN][DATA_BROKERS][entry.entry_id] = broker except ClientResponseError as ex: if ex.status in (HTTP_UNAUTHORIZED, HTTP_FORBIDDEN): _LOGGER.exception( "Unable to setup configuration entry '%s' - please reconfigure the integration", entry.title, ) remove_entry = True else: _LOGGER.debug(ex, exc_info=True) raise ConfigEntryNotReady from ex except (ClientConnectionError, RuntimeWarning) as ex: _LOGGER.debug(ex, exc_info=True) raise ConfigEntryNotReady from ex if remove_entry: hass.async_create_task(hass.config_entries.async_remove( entry.entry_id)) # only create new flow if there isn't a pending one for SmartThings. flows = hass.config_entries.flow.async_progress() if not [flow for flow in flows if flow["handler"] == DOMAIN]: hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT})) return False hass.config_entries.async_setup_platforms(entry, PLATFORMS) 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)) hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) 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.async_on_unload( config_entry.add_update_listener(async_options_updated)) await async_options_updated(hass, config_entry) return True
def setup_platform( hass: HomeAssistant, config: ConfigType, add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Monoprice Blackbird 4k 8x8 HDBaseT Matrix platform.""" if DATA_BLACKBIRD not in hass.data: hass.data[DATA_BLACKBIRD] = {} port = config.get(CONF_PORT) host = config.get(CONF_HOST) connection = None if port is not None: try: blackbird = get_blackbird(port) connection = port except SerialException: _LOGGER.error("Error connecting to the Blackbird controller") return if host is not None: try: blackbird = get_blackbird(host, False) connection = host except socket.timeout: _LOGGER.error("Error connecting to the Blackbird controller") return sources = { source_id: extra[CONF_NAME] for source_id, extra in config[CONF_SOURCES].items() } devices = [] for zone_id, extra in config[CONF_ZONES].items(): _LOGGER.info("Adding zone %d - %s", zone_id, extra[CONF_NAME]) unique_id = f"{connection}-{zone_id}" device = BlackbirdZone(blackbird, sources, zone_id, extra[CONF_NAME]) hass.data[DATA_BLACKBIRD][unique_id] = device devices.append(device) add_entities(devices, True) def service_handle(service: ServiceCall) -> None: """Handle for services.""" entity_ids = service.data.get(ATTR_ENTITY_ID) source = service.data.get(ATTR_SOURCE) if entity_ids: devices = [ device for device in hass.data[DATA_BLACKBIRD].values() if device.entity_id in entity_ids ] else: devices = hass.data[DATA_BLACKBIRD].values() for device in devices: if service.service == SERVICE_SETALLZONES: device.set_all_zones(source) hass.services.register(DOMAIN, SERVICE_SETALLZONES, service_handle, schema=BLACKBIRD_SETALLZONES_SCHEMA)
def setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Zabbix component.""" conf = config[DOMAIN] protocol = "https" if conf[CONF_SSL] else "http" url = urljoin(f"{protocol}://{conf[CONF_HOST]}", conf[CONF_PATH]) username = conf.get(CONF_USERNAME) password = conf.get(CONF_PASSWORD) publish_states_host = conf.get(CONF_PUBLISH_STATES_HOST) entities_filter = convert_include_exclude_filter(conf) try: zapi = ZabbixAPI(url=url, user=username, password=password) _LOGGER.info("Connected to Zabbix API Version %s", zapi.api_version()) except ZabbixAPIException as login_exception: _LOGGER.error("Unable to login to the Zabbix API: %s", login_exception) return False except HTTPError as http_error: _LOGGER.error("HTTPError when connecting to Zabbix API: %s", http_error) zapi = None _LOGGER.error(RETRY_MESSAGE, http_error) event_helper.call_later(hass, RETRY_INTERVAL, lambda _: setup(hass, config)) return True hass.data[DOMAIN] = zapi def event_to_metrics(event, float_keys, string_keys): """Add an event to the outgoing Zabbix list.""" state = event.data.get("new_state") if state is None or state.state in (STATE_UNKNOWN, "", STATE_UNAVAILABLE): return entity_id = state.entity_id if not entities_filter(entity_id): return floats = {} strings = {} try: _state_as_value = float(state.state) floats[entity_id] = _state_as_value except ValueError: try: _state_as_value = float(state_helper.state_as_number(state)) floats[entity_id] = _state_as_value except ValueError: strings[entity_id] = state.state for key, value in state.attributes.items(): # For each value we try to cast it as float # But if we can not do it we store the value # as string attribute_id = f"{entity_id}/{key}" try: float_value = float(value) except (ValueError, TypeError): float_value = None if float_value is None or not math.isfinite(float_value): strings[attribute_id] = str(value) else: floats[attribute_id] = float_value metrics = [] float_keys_count = len(float_keys) float_keys.update(floats) if len(float_keys) != float_keys_count: floats_discovery = [] for float_key in float_keys: floats_discovery.append({"{#KEY}": float_key}) metric = ZabbixMetric( publish_states_host, "homeassistant.floats_discovery", json.dumps(floats_discovery), ) metrics.append(metric) for key, value in floats.items(): metric = ZabbixMetric(publish_states_host, f"homeassistant.float[{key}]", value) metrics.append(metric) string_keys.update(strings) return metrics if publish_states_host: zabbix_sender = ZabbixSender(zabbix_server=conf[CONF_HOST]) instance = ZabbixThread(hass, zabbix_sender, event_to_metrics) instance.setup(hass) return True
def async_enable_logging(hass: core.HomeAssistant, verbose: bool = False, log_rotate_days=None, log_file=None) -> None: """Set up the logging. This method must be run in the event loop. """ logging.basicConfig(level=logging.INFO) fmt = ("%(asctime)s %(levelname)s (%(threadName)s) " "[%(name)s] %(message)s") colorfmt = "%(log_color)s{}%(reset)s".format(fmt) datefmt = '%Y-%m-%d %H:%M:%S' # Suppress overly verbose logs from libraries that aren't helpful logging.getLogger('requests').setLevel(logging.WARNING) logging.getLogger('urllib3').setLevel(logging.WARNING) logging.getLogger('aiohttp.access').setLevel(logging.WARNING) try: from colorlog import ColoredFormatter logging.getLogger().handlers[0].setFormatter( ColoredFormatter(colorfmt, datefmt=datefmt, reset=True, log_colors={ 'DEBUG': 'cyan', 'INFO': 'green', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'red', })) except ImportError: pass # Log errors to a file if we have write access to file or config dir if log_file is None: err_log_path = hass.config.path(ERROR_LOG_FILENAME) else: err_log_path = os.path.abspath(log_file) err_path_exists = os.path.isfile(err_log_path) err_dir = os.path.dirname(err_log_path) # Check if we can write to the error log if it exists or that # we can create files in the containing directory if not. if (err_path_exists and os.access(err_log_path, os.W_OK)) or \ (not err_path_exists and os.access(err_dir, os.W_OK)): if log_rotate_days: err_handler = logging.handlers.TimedRotatingFileHandler( err_log_path, when='midnight', backupCount=log_rotate_days) else: err_handler = logging.FileHandler(err_log_path, mode='w', delay=True) err_handler.setLevel(logging.INFO if verbose else logging.WARNING) err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt)) async_handler = AsyncHandler(hass.loop, err_handler) @asyncio.coroutine def async_stop_async_handler(event): """Cleanup async handler.""" logging.getLogger('').removeHandler(async_handler) yield from async_handler.async_close(blocking=True) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE, async_stop_async_handler) logger = logging.getLogger('') logger.addHandler(async_handler) logger.setLevel(logging.INFO) # Save the log file location for access by other components. hass.data[DATA_LOGGING] = err_log_path else: _LOGGER.error("Unable to setup error log %s (access denied)", err_log_path)
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
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) \ -> Optional[core.HomeAssistant]: """Try to configure Home Assistant from a config dict. Dynamically loads required components and its dependencies. This method is a coroutine. """ hass.async_track_tasks() setup_lock = hass.data.get('setup_lock') if setup_lock is None: setup_lock = hass.data['setup_lock'] = asyncio.Lock(loop=hass.loop) yield from setup_lock.acquire() core_config = config.get(core.DOMAIN, {}) try: yield from conf_util.async_process_ha_core_config(hass, core_config) except vol.Invalid as ex: async_log_exception(ex, 'homeassistant', core_config, hass) return None yield from hass.loop.run_in_executor( None, conf_util.process_ha_config_upgrade, hass) if enable_log: async_enable_logging(hass, verbose, log_rotate_days) hass.config.skip_pip = skip_pip if skip_pip: _LOGGER.warning('Skipping pip installation of required modules. ' 'This may cause issues.') if not loader.PREPARED: yield from hass.loop.run_in_executor(None, loader.prepare, hass) # Merge packages conf_util.merge_packages_config( config, core_config.get(conf_util.CONF_PACKAGES, {})) # Make a copy because we are mutating it. # Use OrderedDict in case original one was one. # Convert values to dictionaries if they are None new_config = OrderedDict() for key, value in config.items(): new_config[key] = value or {} config = new_config # Filter out the repeating and common config section [homeassistant] components = set(key.split(' ')[0] for key in config.keys() if key != core.DOMAIN) # setup components # pylint: disable=not-an-iterable res = yield from core_components.async_setup(hass, config) if not res: _LOGGER.error('Home Assistant core failed to initialize. ' 'Further initialization aborted.') return hass yield from persistent_notification.async_setup(hass, config) _LOGGER.info('Home Assistant core initialized') # Give event decorators access to HASS event_decorators.HASS = hass service.HASS = hass # Setup the components dependency_blacklist = loader.DEPENDENCY_BLACKLIST - set(components) for domain in loader.load_order_components(components): if domain in dependency_blacklist: raise HomeAssistantError( '{} is not allowed to be a dependency'.format(domain)) yield from _async_setup_component(hass, domain, config) setup_lock.release() yield from hass.async_stop_track_tasks() async_register_signal_handling(hass) return hass
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Z-Wave JS component.""" hass.data[DOMAIN] = {} 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 Dyson from a config entry.""" device = get_device( entry.data[CONF_SERIAL], entry.data[CONF_CREDENTIAL], entry.data[CONF_DEVICE_TYPE], ) if not isinstance(device, Dyson360Eye): # Set up coordinator async def async_update_data(): """Poll environmental data from the device.""" try: await hass.async_add_executor_job( device.request_environmental_data) except DysonException as err: raise UpdateFailed( "Failed to request environmental data") from err coordinator = DataUpdateCoordinator( hass, _LOGGER, name="environmental", update_method=async_update_data, update_interval=ENVIRONMENTAL_DATA_UPDATE_INTERVAL, ) else: coordinator = None async def _async_forward_entry_setup(): for component in _async_get_platforms(device): hass.async_create_task( hass.config_entries.async_forward_entry_setup( entry, component)) def setup_entry(host: str, is_discovery: bool = True) -> bool: try: device.connect(host) except DysonException: if is_discovery: _LOGGER.error( "Failed to connect to device %s at %s", device.serial, host, ) return raise ConfigEntryNotReady hass.data[DOMAIN][DATA_DEVICES][entry.entry_id] = device hass.data[DOMAIN][DATA_COORDINATORS][entry.entry_id] = coordinator asyncio.run_coroutine_threadsafe(_async_forward_entry_setup(), hass.loop).result() host = entry.data.get(CONF_HOST) if host: await hass.async_add_executor_job( partial(setup_entry, host, is_discovery=False)) else: discovery = hass.data[DOMAIN][DATA_DISCOVERY] if discovery is None: discovery = DysonDiscovery() hass.data[DOMAIN][DATA_DISCOVERY] = discovery _LOGGER.debug("Starting dyson discovery") discovery.start_discovery(await async_get_instance(hass)) def stop_discovery(_): _LOGGER.debug("Stopping dyson discovery") discovery.stop_discovery() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_discovery) await hass.async_add_executor_job(discovery.register_device, device, setup_entry) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Z-Wave JS from a config entry.""" use_addon = entry.data.get(CONF_USE_ADDON) if use_addon: await async_ensure_addon_running(hass, entry) client = ZwaveClient(entry.data[CONF_URL], async_get_clientsession(hass)) dev_reg = device_registry.async_get(hass) ent_reg = entity_registry.async_get(hass) @callback def async_on_node_ready(node: ZwaveNode) -> None: """Handle node ready event.""" LOGGER.debug("Processing node %s", node) # register (or update) node in device registry register_node_in_dev_reg(hass, entry, dev_reg, client, node) # run discovery on all node values and create/update entities for disc_info in async_discover_values(node): LOGGER.debug("Discovered entity: %s", disc_info) # This migration logic was added in 2021.3 to handle a breaking change to # the value_id format. Some time in the future, this call (as well as the # helper functions) can be removed. async_migrate_discovered_value(ent_reg, client, disc_info) async_dispatcher_send( hass, f"{DOMAIN}_{entry.entry_id}_add_{disc_info.platform}", disc_info) # add listener for stateless node value notification events node.on( "value notification", lambda event: async_on_value_notification(event[ "value_notification"]), ) # add listener for stateless node notification events node.on("notification", lambda event: async_on_notification(event["notification"])) @callback def async_on_node_added(node: ZwaveNode) -> None: """Handle node added event.""" # we only want to run discovery when the node has reached ready state, # otherwise we'll have all kinds of missing info issues. if node.ready: async_on_node_ready(node) return # if node is not yet ready, register one-time callback for ready state LOGGER.debug("Node added: %s - waiting for it to become ready", node.node_id) node.once( "ready", lambda event: async_on_node_ready(event["node"]), ) # we do submit the node to device registry so user has # some visual feedback that something is (in the process of) being added register_node_in_dev_reg(hass, entry, dev_reg, client, node) @callback def async_on_node_removed(node: ZwaveNode) -> None: """Handle node removed event.""" # grab device in device registry attached to this node dev_id = get_device_id(client, node) device = dev_reg.async_get_device({dev_id}) # note: removal of entity registry entry is handled by core dev_reg.async_remove_device(device.id) # type: ignore @callback def async_on_value_notification(notification: ValueNotification) -> None: """Relay stateless value notification events from Z-Wave nodes to hass.""" device = dev_reg.async_get_device( {get_device_id(client, notification.node)}) raw_value = value = notification.value if notification.metadata.states: value = notification.metadata.states.get(str(value), value) hass.bus.async_fire( ZWAVE_JS_VALUE_NOTIFICATION_EVENT, { ATTR_DOMAIN: DOMAIN, ATTR_NODE_ID: notification.node.node_id, ATTR_HOME_ID: client.driver.controller.home_id, ATTR_ENDPOINT: notification.endpoint, ATTR_DEVICE_ID: device.id, # type: ignore ATTR_COMMAND_CLASS: notification.command_class, ATTR_COMMAND_CLASS_NAME: notification.command_class_name, ATTR_LABEL: notification.metadata.label, ATTR_PROPERTY: notification.property_, ATTR_PROPERTY_NAME: notification.property_name, ATTR_PROPERTY_KEY: notification.property_key, ATTR_PROPERTY_KEY_NAME: notification.property_key_name, ATTR_VALUE: value, ATTR_VALUE_RAW: raw_value, }, ) @callback def async_on_notification( notification: EntryControlNotification | NotificationNotification, ) -> None: """Relay stateless notification events from Z-Wave nodes to hass.""" device = dev_reg.async_get_device( {get_device_id(client, notification.node)}) event_data = { ATTR_DOMAIN: DOMAIN, ATTR_NODE_ID: notification.node.node_id, ATTR_HOME_ID: client.driver.controller.home_id, ATTR_DEVICE_ID: device.id, # type: ignore ATTR_COMMAND_CLASS: notification.command_class, } if isinstance(notification, EntryControlNotification): event_data.update({ ATTR_COMMAND_CLASS_NAME: "Entry Control", ATTR_EVENT_TYPE: notification.event_type, ATTR_DATA_TYPE: notification.data_type, ATTR_EVENT_DATA: notification.event_data, }) else: event_data.update({ ATTR_COMMAND_CLASS_NAME: "Notification", ATTR_LABEL: notification.label, ATTR_TYPE: notification.type_, ATTR_EVENT: notification.event, ATTR_EVENT_LABEL: notification.event_label, ATTR_PARAMETERS: notification.parameters, }) hass.bus.async_fire(ZWAVE_JS_NOTIFICATION_EVENT, event_data) entry_hass_data: dict = hass.data[DOMAIN].setdefault(entry.entry_id, {}) # connect and throw error if connection failed try: async with timeout(CONNECT_TIMEOUT): await client.connect() except InvalidServerVersion as err: if not entry_hass_data.get(DATA_INVALID_SERVER_VERSION_LOGGED): LOGGER.error("Invalid server version: %s", err) entry_hass_data[DATA_INVALID_SERVER_VERSION_LOGGED] = True if use_addon: async_ensure_addon_updated(hass) raise ConfigEntryNotReady from err except (asyncio.TimeoutError, BaseZwaveJSServerError) as err: if not entry_hass_data.get(DATA_CONNECT_FAILED_LOGGED): LOGGER.error("Failed to connect: %s", err) entry_hass_data[DATA_CONNECT_FAILED_LOGGED] = True raise ConfigEntryNotReady from err else: LOGGER.info("Connected to Zwave JS Server") entry_hass_data[DATA_CONNECT_FAILED_LOGGED] = False entry_hass_data[DATA_INVALID_SERVER_VERSION_LOGGED] = False unsubscribe_callbacks: list[Callable] = [] entry_hass_data[DATA_CLIENT] = client entry_hass_data[DATA_UNSUBSCRIBE] = unsubscribe_callbacks services = ZWaveServices(hass, ent_reg) services.async_register() # Set up websocket API async_register_api(hass) async def start_platforms() -> None: """Start platforms and perform discovery.""" # wait until all required platforms are ready await asyncio.gather(*[ hass.config_entries.async_forward_entry_setup(entry, platform) for platform in PLATFORMS ]) driver_ready = asyncio.Event() async def handle_ha_shutdown(event: Event) -> None: """Handle HA shutdown.""" await disconnect_client(hass, entry, client, listen_task, platform_task) listen_task = asyncio.create_task( client_listen(hass, entry, client, driver_ready)) entry_hass_data[DATA_CLIENT_LISTEN_TASK] = listen_task unsubscribe_callbacks.append( hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, handle_ha_shutdown)) try: await driver_ready.wait() except asyncio.CancelledError: LOGGER.debug("Cancelling start platforms") return LOGGER.info("Connection to Zwave JS Server initialized") # Check for nodes that no longer exist and remove them stored_devices = device_registry.async_entries_for_config_entry( dev_reg, entry.entry_id) known_devices = [ dev_reg.async_get_device({get_device_id(client, node)}) for node in client.driver.controller.nodes.values() ] # Devices that are in the device registry that are not known by the controller can be removed for device in stored_devices: if device not in known_devices: dev_reg.async_remove_device(device.id) # run discovery on all ready nodes for node in client.driver.controller.nodes.values(): async_on_node_added(node) # listen for new nodes being added to the mesh client.driver.controller.on( "node added", lambda event: async_on_node_added(event["node"])) # listen for nodes being removed from the mesh # NOTE: This will not remove nodes that were removed when HA was not running client.driver.controller.on( "node removed", lambda event: async_on_node_removed(event["node"])) platform_task = hass.async_create_task(start_platforms()) entry_hass_data[DATA_START_PLATFORM_TASK] = platform_task return True
async def async_setup_platform( hass: HomeAssistant, config: ConfigType, async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the KEF platform.""" if DOMAIN not in hass.data: hass.data[DOMAIN] = {} host = config[CONF_HOST] speaker_type = config[CONF_TYPE] port = config[CONF_PORT] name = config[CONF_NAME] maximum_volume = config[CONF_MAX_VOLUME] volume_step = config[CONF_VOLUME_STEP] inverse_speaker_mode = config[CONF_INVERSE_SPEAKER_MODE] supports_on = config[CONF_SUPPORTS_ON] standby_time = config.get(CONF_STANDBY_TIME) sources = SOURCES[speaker_type] _LOGGER.debug( "Setting up %s with host: %s, port: %s, name: %s, sources: %s", DOMAIN, host, port, name, sources, ) mode = get_ip_mode(host) mac = await hass.async_add_executor_job( partial(get_mac_address, **{mode: host})) if mac is None: raise PlatformNotReady("Cannot get the ip address of kef speaker.") unique_id = f"kef-{mac}" media_player = KefMediaPlayer( name, host, port, maximum_volume, volume_step, standby_time, inverse_speaker_mode, supports_on, sources, speaker_type, loop=hass.loop, unique_id=unique_id, ) if host in hass.data[DOMAIN]: _LOGGER.debug("%s is already configured", host) else: hass.data[DOMAIN][host] = media_player async_add_entities([media_player], update_before_add=True) platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( SERVICE_MODE, { vol.Optional("desk_mode"): cv.boolean, vol.Optional("wall_mode"): cv.boolean, vol.Optional("phase_correction"): cv.boolean, vol.Optional("high_pass"): cv.boolean, vol.Optional("sub_polarity"): vol.In(["-", "+"]), vol.Optional("bass_extension"): vol.In( ["Less", "Standard", "Extra"]), }, "set_mode", ) platform.async_register_entity_service(SERVICE_UPDATE_DSP, {}, "update_dsp") def add_service(name, which, option): options = DSP_OPTION_MAPPING[which] dtype = type(options[0]) # int or float platform.async_register_entity_service( name, { vol.Required(option): vol.All(vol.Coerce(float), vol.Coerce(dtype), vol.In(options)) }, f"set_{which}", ) add_service(SERVICE_DESK_DB, "desk_db", "db_value") add_service(SERVICE_WALL_DB, "wall_db", "db_value") add_service(SERVICE_TREBLE_DB, "treble_db", "db_value") add_service(SERVICE_HIGH_HZ, "high_hz", "hz_value") add_service(SERVICE_LOW_HZ, "low_hz", "hz_value") add_service(SERVICE_SUB_DB, "sub_db", "db_value")
def setup(hass: HomeAssistant, base_config: ConfigType) -> bool: # noqa: C901 """Set up the CEC capability.""" hass.data[DOMAIN] = {} # Parse configuration into a dict of device name to physical address # represented as a list of four elements. device_aliases = {} devices = base_config[DOMAIN].get(CONF_DEVICES, {}) _LOGGER.debug("Parsing config %s", devices) device_aliases.update(parse_mapping(devices)) _LOGGER.debug("Parsed devices: %s", device_aliases) platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH) loop = ( # Create own thread if more than 1 CPU hass.loop if multiprocessing.cpu_count() < 2 else None) host = base_config[DOMAIN].get(CONF_HOST) display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME, DEFAULT_DISPLAY_NAME) if host: adapter = TcpAdapter(host, name=display_name, activate_source=False) else: adapter = CecAdapter(name=display_name[:12], activate_source=False) hdmi_network = HDMINetwork(adapter, loop=loop) def _adapter_watchdog(now=None): _LOGGER.debug("Reached _adapter_watchdog") event.call_later(hass, WATCHDOG_INTERVAL, _adapter_watchdog) if not adapter.initialized: _LOGGER.info("Adapter not initialized; Trying to restart") hass.bus.fire(EVENT_HDMI_CEC_UNAVAILABLE) adapter.init() @callback def _async_initialized_callback(*_: Any): """Add watchdog on initialization.""" return event.async_call_later(hass, WATCHDOG_INTERVAL, _adapter_watchdog) hdmi_network.set_initialized_callback(_async_initialized_callback) def _volume(call: ServiceCall) -> None: """Increase/decrease volume and mute/unmute system.""" mute_key_mapping = { ATTR_TOGGLE: KEY_MUTE_TOGGLE, ATTR_ON: KEY_MUTE_ON, ATTR_OFF: KEY_MUTE_OFF, } for cmd, att in call.data.items(): if cmd == CMD_UP: _process_volume(KEY_VOLUME_UP, att) elif cmd == CMD_DOWN: _process_volume(KEY_VOLUME_DOWN, att) elif cmd == CMD_MUTE: hdmi_network.send_command( KeyPressCommand(mute_key_mapping[att], dst=ADDR_AUDIOSYSTEM)) hdmi_network.send_command( KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM)) _LOGGER.info("Audio muted") else: _LOGGER.warning("Unknown command %s", cmd) def _process_volume(cmd, att): if isinstance(att, (str, )): att = att.strip() if att == CMD_PRESS: hdmi_network.send_command( KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM)) elif att == CMD_RELEASE: hdmi_network.send_command(KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM)) else: att = 1 if att == "" else int(att) for _ in range(0, att): hdmi_network.send_command( KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM)) hdmi_network.send_command( KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM)) def _tx(call: ServiceCall) -> None: """Send CEC command.""" data = call.data if ATTR_RAW in data: command = CecCommand(data[ATTR_RAW]) else: if ATTR_SRC in data: src = data[ATTR_SRC] else: src = ADDR_UNREGISTERED if ATTR_DST in data: dst = data[ATTR_DST] else: dst = ADDR_BROADCAST if ATTR_CMD in data: cmd = data[ATTR_CMD] else: _LOGGER.error("Attribute 'cmd' is missing") return if ATTR_ATT in data: if isinstance(data[ATTR_ATT], (list, )): att = data[ATTR_ATT] else: att = reduce(lambda x, y: f"{x}:{y:x}", data[ATTR_ATT]) else: att = "" command = CecCommand(cmd, dst, src, att) hdmi_network.send_command(command) def _standby(call: ServiceCall) -> None: hdmi_network.standby() def _power_on(call: ServiceCall) -> None: hdmi_network.power_on() def _select_device(call: ServiceCall) -> None: """Select the active device.""" if not (addr := call.data[ATTR_DEVICE]): _LOGGER.error("Device not found: %s", call.data[ATTR_DEVICE]) return if addr in device_aliases: addr = device_aliases[addr] else: entity = hass.states.get(addr) _LOGGER.debug("Selecting entity %s", entity) if entity is not None: addr = entity.attributes["physical_address"] _LOGGER.debug("Address acquired: %s", addr) if addr is None: _LOGGER.error("Device %s has not physical address", call.data[ATTR_DEVICE]) return if not isinstance(addr, (PhysicalAddress, )): addr = PhysicalAddress(addr) hdmi_network.active_source(addr) _LOGGER.info("Selected %s (%s)", call.data[ATTR_DEVICE], addr)
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) hass.config.skip_pip = skip_pip if skip_pip: _LOGGER.warning("Skipping pip installation of required modules. " "This may cause issues") core_config = config.get(core.DOMAIN, {}) api_password = config.get("http", {}).get("api_password") trusted_networks = config.get("http", {}).get("trusted_networks") try: await conf_util.async_process_ha_core_config(hass, core_config, api_password, 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 # Make a copy because we are mutating it. config = OrderedDict(config) # Merge packages await 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_initialize() await _async_set_up_integrations(hass, config) stop = time() _LOGGER.info("Home Assistant initialized in %.2fs", stop - start) if sys.version_info[:3] < (3, 6, 1): msg = ("Python 3.6.0 support is deprecated and will " "be removed in the first release after October 2. Please " "upgrade Python to 3.6.1 or higher.") _LOGGER.warning(msg) hass.components.persistent_notification.async_create( msg, "Python version", "python_version") return hass
async def async_setup_entry( hass: HomeAssistant, entry: config_entries.ConfigEntry ) -> bool: """Set up the ISY 994 integration.""" # As there currently is no way to import options from yaml # when setting up a config entry, we fallback to adding # the options to the config entry and pull them out here if # they are missing from the options _async_import_options_from_data_if_missing(hass, entry) hass.data[DOMAIN][entry.entry_id] = {} hass_isy_data = hass.data[DOMAIN][entry.entry_id] hass_isy_data[ISY994_NODES] = {} for platform in SUPPORTED_PLATFORMS: hass_isy_data[ISY994_NODES][platform] = [] hass_isy_data[ISY994_PROGRAMS] = {} for platform in SUPPORTED_PROGRAM_PLATFORMS: hass_isy_data[ISY994_PROGRAMS][platform] = [] hass_isy_data[ISY994_VARIABLES] = [] isy_config = entry.data isy_options = entry.options # Required user = isy_config[CONF_USERNAME] password = isy_config[CONF_PASSWORD] host = urlparse(isy_config[CONF_HOST]) # Optional tls_version = isy_config.get(CONF_TLS_VER) ignore_identifier = isy_options.get(CONF_IGNORE_STRING, DEFAULT_IGNORE_STRING) sensor_identifier = isy_options.get(CONF_SENSOR_STRING, DEFAULT_SENSOR_STRING) variable_identifier = isy_options.get( CONF_VAR_SENSOR_STRING, DEFAULT_VAR_SENSOR_STRING ) if host.scheme == "http": https = False port = host.port or 80 elif host.scheme == "https": https = True port = host.port or 443 else: _LOGGER.error("isy994 host value in configuration is invalid") return False # Connect to ISY controller. isy = await hass.async_add_executor_job( partial( ISY, host.hostname, port, username=user, password=password, use_https=https, tls_ver=tls_version, log=_LOGGER, webroot=host.path, ) ) if not isy.connected: return False _categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier, sensor_identifier) _categorize_programs(hass_isy_data, isy.programs) _categorize_variables(hass_isy_data, isy.variables, variable_identifier) # Dump ISY Clock Information. Future: Add ISY as sensor to Hass with attrs _LOGGER.info(repr(isy.clock)) hass_isy_data[ISY994_ISY] = isy await _async_get_or_create_isy_device_in_registry(hass, entry, isy) # Load platforms for the devices in the ISY controller that we support. for platform in SUPPORTED_PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform) ) def _start_auto_update() -> None: """Start isy auto update.""" _LOGGER.debug("ISY Starting Event Stream and automatic updates.") isy.auto_update = True await hass.async_add_executor_job(_start_auto_update) undo_listener = entry.add_update_listener(_async_update_listener) hass_isy_data[UNDO_UPDATE_LISTENER] = undo_listener # Register Integration-wide Services: async_setup_services(hass) return True
async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool: """Set up the Traccar component.""" hass.data[DOMAIN] = {"devices": set(), "unsub_device_tracker": {}} return True
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) \ -> 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) if sys.version_info[:2] < (3, 5): _LOGGER.warning( 'Python 3.4 support has been deprecated and will be removed in ' 'the beginning of 2018. Please upgrade Python or your operating ' 'system. More info: https://home-assistant.io/blog/2017/10/06/' 'deprecating-python-3.4-support/') core_config = config.get(core.DOMAIN, {}) try: yield from 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 yield from hass.async_add_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") if not loader.PREPARED: yield from hass.async_add_job(loader.prepare, hass) # Merge packages conf_util.merge_packages_config( config, core_config.get(conf_util.CONF_PACKAGES, {})) # Make a copy because we are mutating it. # Use OrderedDict in case original one was one. # Convert values to dictionaries if they are None new_config = OrderedDict() for key, value in config.items(): new_config[key] = value or {} config = new_config # Filter out the repeating and common config section [homeassistant] components = set( key.split(' ')[0] for key in config.keys() if key != core.DOMAIN) # setup components # pylint: disable=not-an-iterable res = yield from core_components.async_setup(hass, config) if not res: _LOGGER.error("Home Assistant core failed to initialize. " "further initialization aborted") return hass yield from 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_add_job(async_setup_component(hass, component, config)) yield from hass.async_block_till_done() # stage 2 for component in components: if component in FIRST_INIT_COMPONENT: continue hass.async_add_job(async_setup_component(hass, component, config)) yield from hass.async_block_till_done() stop = time() _LOGGER.info("Home Assistant initialized in %.2fs", stop - start) async_register_signal_handling(hass) return hass
async def async_setup(hass: HomeAssistant, config: dict): """Set up the WiLight with Config Flow component.""" hass.data[DOMAIN] = {} 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) hass.config.skip_pip = skip_pip if skip_pip: _LOGGER.warning("Skipping pip installation of required modules. " "This may cause issues") core_config = config.get(core.DOMAIN, {}) api_password = config.get('http', {}).get('api_password') trusted_networks = config.get('http', {}).get('trusted_networks') try: await conf_util.async_process_ha_core_config( hass, core_config, api_password, 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 # Make a copy because we are mutating it. config = OrderedDict(config) # Merge packages await 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_initialize() await _async_set_up_integrations(hass, config) 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
def _async_setup_component(hass: core.HomeAssistant, domain: str, config) -> bool: """Set up a component for Home Assistant. This method is a coroutine. """ def log_error(msg, link=True): """Log helper.""" _LOGGER.error("Setup failed for %s: %s", domain, msg) async_notify_setup_error(hass, domain, link) component = loader.get_component(domain) if not component: log_error("Component not found.", False) return False # Validate no circular dependencies components = loader.load_order_component(domain) # OrderedSet is empty if component or dependencies could not be resolved if not components: log_error("Unable to resolve component or dependencies.") return False processed_config = \ conf_util.async_process_component_config(hass, config, domain) if processed_config is None: log_error("Invalid config.") return False try: yield from async_process_deps_reqs(hass, config, domain, component) except HomeAssistantError as err: log_error(str(err)) return False start = timer() _LOGGER.info("Setting up %s", domain) if hasattr(component, 'PLATFORM_SCHEMA'): # Entity components have their own warning warn_task = None else: warn_task = hass.loop.call_later( SLOW_SETUP_WARNING, _LOGGER.warning, "Setup of %s is taking over %s seconds.", domain, SLOW_SETUP_WARNING) try: if hasattr(component, 'async_setup'): result = yield from component.async_setup(hass, processed_config) else: result = yield from hass.async_add_job( component.setup, hass, processed_config) except Exception: # pylint: disable=broad-except _LOGGER.exception("Error during setup of component %s", domain) async_notify_setup_error(hass, domain, True) return False finally: end = timer() if warn_task: warn_task.cancel() _LOGGER.info("Setup of domain %s took %.1f seconds.", domain, end - start) if result is False: log_error("Component failed to initialize.") return False elif result is not True: log_error("Component did not return boolean if setup was successful. " "Disabling component.") loader.set_component(domain, None) return False for entry in hass.config_entries.async_entries(domain): yield from entry.async_setup(hass, component=component) hass.config.components.add(component.DOMAIN) # Cleanup if domain in hass.data[DATA_SETUP]: hass.data[DATA_SETUP].pop(domain) hass.bus.async_fire( EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN} ) 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_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) \ -> 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) if sys.version_info[:2] < (3, 5): _LOGGER.warning( 'Python 3.4 support has been deprecated and will be removed in ' 'the beginning of 2018. Please upgrade Python or your operating ' 'system. More info: https://home-assistant.io/blog/2017/10/06/' 'deprecating-python-3.4-support/' ) core_config = config.get(core.DOMAIN, {}) try: yield from 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 yield from hass.async_add_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") if not loader.PREPARED: yield from hass.async_add_job(loader.prepare, hass) # Merge packages conf_util.merge_packages_config( config, core_config.get(conf_util.CONF_PACKAGES, {})) # Make a copy because we are mutating it. # Use OrderedDict in case original one was one. # Convert values to dictionaries if they are None new_config = OrderedDict() for key, value in config.items(): new_config[key] = value or {} config = new_config # Filter out the repeating and common config section [homeassistant] components = set(key.split(' ')[0] for key in config.keys() if key != core.DOMAIN) # setup components # pylint: disable=not-an-iterable res = yield from core_components.async_setup(hass, config) if not res: _LOGGER.error("Home Assistant core failed to initialize. " "further initialization aborted") return hass yield from 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_add_job(async_setup_component(hass, component, config)) yield from hass.async_block_till_done() # stage 2 for component in components: if component in FIRST_INIT_COMPONENT: continue hass.async_add_job(async_setup_component(hass, component, config)) yield from hass.async_block_till_done() stop = time() _LOGGER.info("Home Assistant initialized in %.2fs", stop-start) async_register_signal_handling(hass) return hass
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
def async_enable_logging( hass: core.HomeAssistant, verbose: bool = False, log_rotate_days: Optional[int] = None, log_file: Optional[str] = None, log_no_color: bool = False, ) -> None: """Set up the logging. This method must be run in the event loop. """ fmt = "%(asctime)s %(levelname)s (%(threadName)s) " "[%(name)s] %(message)s" datefmt = "%Y-%m-%d %H:%M:%S" if not log_no_color: try: from colorlog import ColoredFormatter # basicConfig must be called after importing colorlog in order to # ensure that the handlers it sets up wraps the correct streams. logging.basicConfig(level=logging.INFO) colorfmt = f"%(log_color)s{fmt}%(reset)s" logging.getLogger().handlers[0].setFormatter( ColoredFormatter( colorfmt, datefmt=datefmt, reset=True, log_colors={ "DEBUG": "cyan", "INFO": "green", "WARNING": "yellow", "ERROR": "red", "CRITICAL": "red", }, )) except ImportError: pass # If the above initialization failed for any reason, setup the default # formatting. If the above succeeds, this wil result in a no-op. logging.basicConfig(format=fmt, datefmt=datefmt, level=logging.INFO) # Suppress overly verbose logs from libraries that aren't helpful logging.getLogger("requests").setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING) logging.getLogger("aiohttp.access").setLevel(logging.WARNING) # Log errors to a file if we have write access to file or config dir if log_file is None: err_log_path = hass.config.path(ERROR_LOG_FILENAME) else: err_log_path = os.path.abspath(log_file) err_path_exists = os.path.isfile(err_log_path) err_dir = os.path.dirname(err_log_path) # Check if we can write to the error log if it exists or that # we can create files in the containing directory if not. if (err_path_exists and os.access(err_log_path, os.W_OK)) or ( not err_path_exists and os.access(err_dir, os.W_OK)): if log_rotate_days: err_handler: logging.FileHandler = logging.handlers.TimedRotatingFileHandler( err_log_path, when="midnight", backupCount=log_rotate_days) else: err_handler = logging.FileHandler(err_log_path, mode="w", delay=True) err_handler.setLevel(logging.INFO if verbose else logging.WARNING) err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt)) async_handler = AsyncHandler(hass.loop, err_handler) async def async_stop_async_handler(_: Any) -> None: """Cleanup async handler.""" logging.getLogger("").removeHandler(async_handler) # type: ignore await async_handler.async_close(blocking=True) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE, async_stop_async_handler) logger = logging.getLogger("") logger.addHandler(async_handler) # type: ignore logger.setLevel(logging.INFO) # Save the log file location for access by other components. hass.data[DATA_LOGGING] = err_log_path else: _LOGGER.error("Unable to set up error log %s (access denied)", err_log_path)
def _compile_statistics( # noqa: C901 hass: HomeAssistant, session: Session, start: datetime.datetime, end: datetime.datetime, ) -> list[StatisticResult]: """Compile statistics for all entities during start-end.""" result: list[StatisticResult] = [] sensor_states = _get_sensor_states(hass) wanted_statistics = _wanted_statistics(sensor_states) old_metadatas = statistics.get_metadata_with_session( hass, session, statistic_ids=[i.entity_id for i in sensor_states]) # Get history between start and end entities_full_history = [ i.entity_id for i in sensor_states if "sum" in wanted_statistics[i.entity_id] ] history_list = {} if entities_full_history: history_list = history.get_significant_states_with_session( # type: ignore[no-untyped-call] hass, session, start - datetime.timedelta.resolution, end, entity_ids=entities_full_history, significant_changes_only=False, ) entities_significant_history = [ i.entity_id for i in sensor_states if "sum" not in wanted_statistics[i.entity_id] ] if entities_significant_history: _history_list = history.get_significant_states_with_session( # type: ignore[no-untyped-call] hass, session, start - datetime.timedelta.resolution, end, entity_ids=entities_significant_history, ) history_list = {**history_list, **_history_list} # If there are no recent state changes, the sensor's state may already be pruned # from the recorder. Get the state from the state machine instead. for _state in sensor_states: if _state.entity_id not in history_list: history_list[_state.entity_id] = (_state, ) for _state in sensor_states: # pylint: disable=too-many-nested-blocks entity_id = _state.entity_id if entity_id not in history_list: continue state_class = _state.attributes[ATTR_STATE_CLASS] device_class = _state.attributes.get(ATTR_DEVICE_CLASS) entity_history = history_list[entity_id] unit, fstates = _normalize_states(hass, session, old_metadatas, entity_history, device_class, entity_id) if not fstates: continue # Check metadata if old_metadata := old_metadatas.get(entity_id): if old_metadata[1]["unit_of_measurement"] != unit: if WARN_UNSTABLE_UNIT not in hass.data: hass.data[WARN_UNSTABLE_UNIT] = set() if entity_id not in hass.data[WARN_UNSTABLE_UNIT]: hass.data[WARN_UNSTABLE_UNIT].add(entity_id) _LOGGER.warning( "The %sunit of %s (%s) does not match the unit of already " "compiled statistics (%s). Generation of long term statistics " "will be suppressed unless the unit changes back to %s. " "Go to %s to fix this", "normalized " if device_class in DEVICE_CLASS_UNITS else "", entity_id, unit, old_metadata[1]["unit_of_measurement"], old_metadata[1]["unit_of_measurement"], LINK_DEV_STATISTICS, ) continue # Set meta data meta: StatisticMetaData = { "has_mean": "mean" in wanted_statistics[entity_id], "has_sum": "sum" in wanted_statistics[entity_id], "name": None, "source": RECORDER_DOMAIN, "statistic_id": entity_id, "unit_of_measurement": unit, } # Make calculations stat: StatisticData = {"start": start} if "max" in wanted_statistics[entity_id]: stat["max"] = max(*itertools.islice( zip(*fstates), 1)) # type: ignore[typeddict-item] if "min" in wanted_statistics[entity_id]: stat["min"] = min(*itertools.islice( zip(*fstates), 1)) # type: ignore[typeddict-item] if "mean" in wanted_statistics[entity_id]: stat["mean"] = _time_weighted_average(fstates, start, end) if "sum" in wanted_statistics[entity_id]: last_reset = old_last_reset = None new_state = old_state = None _sum = 0.0 last_stats = statistics.get_last_short_term_statistics( hass, 1, entity_id, False) if entity_id in last_stats: # We have compiled history for this sensor before, use that as a starting point last_reset = old_last_reset = last_stats[entity_id][0][ "last_reset"] new_state = old_state = last_stats[entity_id][0]["state"] _sum = last_stats[entity_id][0]["sum"] or 0.0 for fstate, state in fstates: reset = False if (state_class != STATE_CLASS_TOTAL_INCREASING and (last_reset := _last_reset_as_utc_isoformat( state.attributes.get("last_reset"), entity_id)) != old_last_reset and last_reset is not None): if old_state is None: _LOGGER.info( "Compiling initial sum statistics for %s, zero point set to %s", entity_id, fstate, ) else: _LOGGER.info( "Detected new cycle for %s, last_reset set to %s (old last_reset %s)", entity_id, last_reset, old_last_reset, ) reset = True elif old_state is None and last_reset is None: reset = True _LOGGER.info( "Compiling initial sum statistics for %s, zero point set to %s", entity_id, fstate, ) elif state_class == STATE_CLASS_TOTAL_INCREASING: try: if old_state is None or reset_detected( hass, entity_id, fstate, new_state, state): reset = True _LOGGER.info( "Detected new cycle for %s, value dropped from %s to %s, " "triggered by state with last_updated set to %s", entity_id, new_state, state.last_updated.isoformat(), fstate, ) except HomeAssistantError: continue if reset: # The sensor has been reset, update the sum if old_state is not None: _sum += new_state - old_state # ..and update the starting point new_state = fstate old_last_reset = last_reset # Force a new cycle for an existing sensor to start at 0 if old_state is not None: old_state = 0.0 else: old_state = new_state else: new_state = fstate
def publish(hass: HomeAssistant, topic, payload, qos=0, retain=False) -> None: """Publish message to an MQTT topic.""" hass.add_job(async_publish, hass, topic, payload, qos, retain)
def async_set_agent(hass: core.HomeAssistant, agent: AbstractConversationAgent): """Set the agent to handle the conversations.""" hass.data[DATA_AGENT] = agent
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) async with async_timeout.timeout(10): rooms = Rooms(pv_request) room_data = _async_map_data_by_id( (await rooms.get_resources())[ROOM_DATA]) async with async_timeout.timeout(10): scenes = Scenes(pv_request) scene_data = _async_map_data_by_id( (await scenes.get_resources())[SCENE_DATA]) async with async_timeout.timeout(10): shades = Shades(pv_request) shade_data = _async_map_data_by_id( (await shades.get_resources())[SHADE_DATA]) except HUB_EXCEPTIONS as err: _LOGGER.error("Connection error to PowerView hub: %s", hub_address) raise ConfigEntryNotReady from err if not device_info: _LOGGER.error("Unable to initialize PowerView hub: %s", hub_address) raise ConfigEntryNotReady 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("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 platform in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform)) return True
async def async_process_ha_core_config( hass: HomeAssistant, config: Dict, api_password: Optional[str] = None, trusted_networks: Optional[Any] = None) -> None: """Process the [homeassistant] section from the configuration. This method is a coroutine. """ config = CORE_CONFIG_SCHEMA(config) # Only load auth during startup. if not hasattr(hass, 'auth'): auth_conf = config.get(CONF_AUTH_PROVIDERS) if auth_conf is None: auth_conf = [{'type': 'homeassistant'}] if api_password: auth_conf.append({ 'type': 'legacy_api_password', 'api_password': api_password, }) if trusted_networks: auth_conf.append({ 'type': 'trusted_networks', 'trusted_networks': trusted_networks, }) mfa_conf = config.get(CONF_AUTH_MFA_MODULES, [ { 'type': 'totp', 'id': 'totp', 'name': 'Authenticator app' }, ]) setattr(hass, 'auth', await auth.auth_manager_from_config(hass, auth_conf, mfa_conf)) hac = hass.config def set_time_zone(time_zone_str: Optional[str]) -> None: """Help to set the time zone.""" if time_zone_str is None: return time_zone = date_util.get_time_zone(time_zone_str) if time_zone: hac.time_zone = time_zone date_util.set_default_time_zone(time_zone) else: _LOGGER.error("Received invalid time zone %s", time_zone_str) for key, attr in ((CONF_LATITUDE, 'latitude'), (CONF_LONGITUDE, 'longitude'), (CONF_NAME, 'location_name'), (CONF_ELEVATION, 'elevation')): if key in config: setattr(hac, attr, config[key]) set_time_zone(config.get(CONF_TIME_ZONE)) # Init whitelist external dir hac.whitelist_external_dirs = {hass.config.path('www')} if CONF_WHITELIST_EXTERNAL_DIRS in config: hac.whitelist_external_dirs.update( set(config[CONF_WHITELIST_EXTERNAL_DIRS])) # Customize cust_exact = dict(config[CONF_CUSTOMIZE]) cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN]) cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB]) for name, pkg in config[CONF_PACKAGES].items(): pkg_cust = pkg.get(CONF_CORE) if pkg_cust is None: continue try: pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust) except vol.Invalid: _LOGGER.warning("Package %s contains invalid customize", name) continue cust_exact.update(pkg_cust[CONF_CUSTOMIZE]) cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN]) cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB]) hass.data[DATA_CUSTOMIZE] = \ EntityValues(cust_exact, cust_domain, cust_glob) if CONF_UNIT_SYSTEM in config: if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL: hac.units = IMPERIAL_SYSTEM else: hac.units = METRIC_SYSTEM elif CONF_TEMPERATURE_UNIT in config: unit = config[CONF_TEMPERATURE_UNIT] if unit == TEMP_CELSIUS: hac.units = METRIC_SYSTEM else: hac.units = IMPERIAL_SYSTEM _LOGGER.warning( "Found deprecated temperature unit in core " "configuration expected unit system. Replace '%s: %s' " "with '%s: %s'", CONF_TEMPERATURE_UNIT, unit, CONF_UNIT_SYSTEM, hac.units.name) # Shortcut if no auto-detection necessary if None not in (hac.latitude, hac.longitude, hac.units, hac.time_zone, hac.elevation): return discovered = [] # type: List[Tuple[str, Any]] # If we miss some of the needed values, auto detect them if None in (hac.latitude, hac.longitude, hac.units, hac.time_zone): info = await hass.async_add_executor_job(loc_util.detect_location_info) if info is None: _LOGGER.error("Could not detect location information") return if hac.latitude is None and hac.longitude is None: hac.latitude, hac.longitude = (info.latitude, info.longitude) discovered.append(('latitude', hac.latitude)) discovered.append(('longitude', hac.longitude)) if hac.units is None: hac.units = METRIC_SYSTEM if info.use_metric else IMPERIAL_SYSTEM discovered.append((CONF_UNIT_SYSTEM, hac.units.name)) if hac.location_name is None: hac.location_name = info.city discovered.append(('name', info.city)) if hac.time_zone is None: set_time_zone(info.time_zone) discovered.append(('time_zone', info.time_zone)) if hac.elevation is None and hac.latitude is not None and \ hac.longitude is not None: elevation = await hass.async_add_executor_job(loc_util.elevation, hac.latitude, hac.longitude) hac.elevation = elevation discovered.append(('elevation', elevation)) if discovered: _LOGGER.warning( "Incomplete core configuration. Auto detected %s", ", ".join('{}: {}'.format(key, val) for key, val in discovered))
async def async_setup(hass: HomeAssistant, config: dict): """Set up the kmtronic component.""" hass.data[DOMAIN] = {} return True
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, 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 = {} hci_list = [] bt_mac_list = [] if not CONFIG_YAML: # Configuration in UI 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"] if not config[CONF_BT_INTERFACE]: default_hci = list(BT_INTERFACES.keys())[list(BT_INTERFACES.values()).index(DEFAULT_BT_INTERFACE)] hci_list.append(int(default_hci)) bt_mac_list.append(str(DEFAULT_BT_INTERFACE)) else: bt_interface_list = config[CONF_BT_INTERFACE] for bt_mac in bt_interface_list: hci = list(BT_INTERFACES.keys())[list(BT_INTERFACES.values()).index(bt_mac)] hci_list.append(int(hci)) bt_mac_list.append(str(bt_mac)) else: # Configuration in YAML for key, value in CONFIG_YAML.items(): config[key] = value _LOGGER.warning("Available Bluetooth interfaces for BLE monitor: %s", BT_MAC_INTERFACES) if config[CONF_HCI_INTERFACE]: # Configuration of BT interface with hci number for hci in CONFIG_YAML[CONF_HCI_INTERFACE]: try: hci_list.append(int(hci)) bt_mac = BT_INTERFACES.get(hci) if bt_mac: bt_mac_list.append(str(bt_mac)) else: _LOGGER.error("Bluetooth interface hci%i is not available", hci) except ValueError: _LOGGER.error("Bluetooth interface hci%i is not available", hci) else: # Configuration of BT interface with mac address CONF_BT_INTERFACES = [x.upper() for x in CONFIG_YAML[CONF_BT_INTERFACE]] for bt_mac in CONF_BT_INTERFACES: try: hci = list(BT_INTERFACES.keys())[list(BT_INTERFACES.values()).index(bt_mac)] hci_list.append(int(hci)) bt_mac_list.append(str(bt_mac)) except ValueError: _LOGGER.error("Bluetooth interface with MAC address %s is not available", bt_mac) if not hci_list: # Fall back in case no hci interfaces are added default_hci = list(BT_INTERFACES.keys())[list(BT_INTERFACES.values()).index(DEFAULT_BT_INTERFACE)] hci_list.append(int(default_hci)) bt_mac_list.append(str(DEFAULT_BT_INTERFACE)) _LOGGER.warning("No configured Bluetooth interfaces was found, using default interface instead") config[CONF_HCI_INTERFACE] = hci_list config[CONF_BT_INTERFACE] = bt_mac_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) _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_process_ha_core_config( hass: HomeAssistant, config: Dict) -> None: """Process the [homeassistant] section from the configuration. This method is a coroutine. """ config = CORE_CONFIG_SCHEMA(config) # Only load auth during startup. if not hasattr(hass, 'auth'): setattr(hass, 'auth', await auth.auth_manager_from_config( hass, config.get(CONF_AUTH_PROVIDERS, []))) hac = hass.config def set_time_zone(time_zone_str: Optional[str]) -> None: """Help to set the time zone.""" if time_zone_str is None: return time_zone = date_util.get_time_zone(time_zone_str) if time_zone: hac.time_zone = time_zone date_util.set_default_time_zone(time_zone) else: _LOGGER.error("Received invalid time zone %s", time_zone_str) for key, attr in ((CONF_LATITUDE, 'latitude'), (CONF_LONGITUDE, 'longitude'), (CONF_NAME, 'location_name'), (CONF_ELEVATION, 'elevation')): if key in config: setattr(hac, attr, config[key]) set_time_zone(config.get(CONF_TIME_ZONE)) # Init whitelist external dir hac.whitelist_external_dirs = {hass.config.path('www')} if CONF_WHITELIST_EXTERNAL_DIRS in config: hac.whitelist_external_dirs.update( set(config[CONF_WHITELIST_EXTERNAL_DIRS])) # Customize cust_exact = dict(config[CONF_CUSTOMIZE]) cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN]) cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB]) for name, pkg in config[CONF_PACKAGES].items(): pkg_cust = pkg.get(CONF_CORE) if pkg_cust is None: continue try: pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust) except vol.Invalid: _LOGGER.warning("Package %s contains invalid customize", name) continue cust_exact.update(pkg_cust[CONF_CUSTOMIZE]) cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN]) cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB]) hass.data[DATA_CUSTOMIZE] = \ EntityValues(cust_exact, cust_domain, cust_glob) if CONF_UNIT_SYSTEM in config: if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL: hac.units = IMPERIAL_SYSTEM else: hac.units = METRIC_SYSTEM elif CONF_TEMPERATURE_UNIT in config: unit = config[CONF_TEMPERATURE_UNIT] if unit == TEMP_CELSIUS: hac.units = METRIC_SYSTEM else: hac.units = IMPERIAL_SYSTEM _LOGGER.warning("Found deprecated temperature unit in core " "configuration expected unit system. Replace '%s: %s' " "with '%s: %s'", CONF_TEMPERATURE_UNIT, unit, CONF_UNIT_SYSTEM, hac.units.name) # Shortcut if no auto-detection necessary if None not in (hac.latitude, hac.longitude, hac.units, hac.time_zone, hac.elevation): return discovered = [] # type: List[Tuple[str, Any]] # If we miss some of the needed values, auto detect them if None in (hac.latitude, hac.longitude, hac.units, hac.time_zone): info = await hass.async_add_executor_job( loc_util.detect_location_info) if info is None: _LOGGER.error("Could not detect location information") return if hac.latitude is None and hac.longitude is None: hac.latitude, hac.longitude = (info.latitude, info.longitude) discovered.append(('latitude', hac.latitude)) discovered.append(('longitude', hac.longitude)) if hac.units is None: hac.units = METRIC_SYSTEM if info.use_metric else IMPERIAL_SYSTEM discovered.append((CONF_UNIT_SYSTEM, hac.units.name)) if hac.location_name is None: hac.location_name = info.city discovered.append(('name', info.city)) if hac.time_zone is None: set_time_zone(info.time_zone) discovered.append(('time_zone', info.time_zone)) if hac.elevation is None and hac.latitude is not None and \ hac.longitude is not None: elevation = await hass.async_add_executor_job( loc_util.elevation, hac.latitude, hac.longitude) hac.elevation = elevation discovered.append(('elevation', elevation)) if discovered: _LOGGER.warning( "Incomplete core configuration. Auto detected %s", ", ".join('{}: {}'.format(key, val) for key, val in discovered))
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" # unload srp client hass.data[SRP_ENERGY_DOMAIN] = None # Remove config entry return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
def async_enable_logging(hass: core.HomeAssistant, verbose: bool = False, log_rotate_days: Optional[int] = None, log_file: Optional[str] = None, log_no_color: bool = False) -> None: """Set up the logging. This method must be run in the event loop. """ fmt = ("%(asctime)s %(levelname)s (%(threadName)s) " "[%(name)s] %(message)s") datefmt = '%Y-%m-%d %H:%M:%S' if not log_no_color: try: from colorlog import ColoredFormatter # basicConfig must be called after importing colorlog in order to # ensure that the handlers it sets up wraps the correct streams. logging.basicConfig(level=logging.INFO) colorfmt = "%(log_color)s{}%(reset)s".format(fmt) logging.getLogger().handlers[0].setFormatter(ColoredFormatter( colorfmt, datefmt=datefmt, reset=True, log_colors={ 'DEBUG': 'cyan', 'INFO': 'green', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'red', } )) except ImportError: pass # If the above initialization failed for any reason, setup the default # formatting. If the above succeeds, this wil result in a no-op. logging.basicConfig(format=fmt, datefmt=datefmt, level=logging.INFO) # Suppress overly verbose logs from libraries that aren't helpful logging.getLogger('requests').setLevel(logging.WARNING) logging.getLogger('urllib3').setLevel(logging.WARNING) logging.getLogger('aiohttp.access').setLevel(logging.WARNING) # Log errors to a file if we have write access to file or config dir if log_file is None: err_log_path = hass.config.path(ERROR_LOG_FILENAME) else: err_log_path = os.path.abspath(log_file) err_path_exists = os.path.isfile(err_log_path) err_dir = os.path.dirname(err_log_path) # Check if we can write to the error log if it exists or that # we can create files in the containing directory if not. if (err_path_exists and os.access(err_log_path, os.W_OK)) or \ (not err_path_exists and os.access(err_dir, os.W_OK)): if log_rotate_days: err_handler = logging.handlers.TimedRotatingFileHandler( err_log_path, when='midnight', backupCount=log_rotate_days) # type: logging.FileHandler else: err_handler = logging.FileHandler( err_log_path, mode='w', delay=True) err_handler.setLevel(logging.INFO if verbose else logging.WARNING) err_handler.setFormatter(logging.Formatter(fmt, datefmt=datefmt)) async_handler = AsyncHandler(hass.loop, err_handler) async def async_stop_async_handler(_: Any) -> None: """Cleanup async handler.""" logging.getLogger('').removeHandler(async_handler) # type: ignore await async_handler.async_close(blocking=True) hass.bus.async_listen_once( EVENT_HOMEASSISTANT_CLOSE, async_stop_async_handler) logger = logging.getLogger('') logger.addHandler(async_handler) # type: ignore logger.setLevel(logging.INFO) # Save the log file location for access by other components. hass.data[DATA_LOGGING] = err_log_path else: _LOGGER.error( "Unable to set up error log %s (access denied)", err_log_path)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the camera component.""" component = hass.data[DOMAIN] = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) prefs = CameraPreferences(hass) await prefs.async_initialize() hass.data[DATA_CAMERA_PREFS] = prefs hass.http.register_view(CameraImageView(component)) hass.http.register_view(CameraMjpegStream(component)) hass.components.websocket_api.async_register_command( WS_TYPE_CAMERA_THUMBNAIL, websocket_camera_thumbnail, SCHEMA_WS_CAMERA_THUMBNAIL) hass.components.websocket_api.async_register_command(ws_camera_stream) hass.components.websocket_api.async_register_command( ws_camera_web_rtc_offer) hass.components.websocket_api.async_register_command(websocket_get_prefs) hass.components.websocket_api.async_register_command( websocket_update_prefs) await component.async_setup(config) async def preload_stream(_event: Event) -> None: for camera in component.entities: camera = cast(Camera, camera) camera_prefs = prefs.get(camera.entity_id) if not camera_prefs.preload_stream: continue stream = await camera.create_stream() if not stream: continue stream.keepalive = True stream.add_provider("hls") stream.start() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, preload_stream) @callback def update_tokens(time: datetime) -> None: """Update tokens of the entities.""" for entity in component.entities: entity = cast(Camera, entity) entity.async_update_token() entity.async_write_ha_state() hass.helpers.event.async_track_time_interval(update_tokens, TOKEN_CHANGE_INTERVAL) component.async_register_entity_service(SERVICE_ENABLE_MOTION, {}, "async_enable_motion_detection") component.async_register_entity_service(SERVICE_DISABLE_MOTION, {}, "async_disable_motion_detection") component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off") component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on") component.async_register_entity_service(SERVICE_SNAPSHOT, CAMERA_SERVICE_SNAPSHOT, async_handle_snapshot_service) component.async_register_entity_service( SERVICE_PLAY_STREAM, CAMERA_SERVICE_PLAY_STREAM, async_handle_play_stream_service, ) component.async_register_entity_service(SERVICE_RECORD, CAMERA_SERVICE_RECORD, async_handle_record_service) return True
async def async_setup(hass: HomeAssistant, config: dict): """Set up the Garmin Connect component.""" hass.data[DOMAIN] = {} return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Homewizard from a config entry.""" _LOGGER.debug("__init__ async_setup_entry") # Migrate `homewizard_energy` (custom_component) to `homewizard` if entry.source == SOURCE_IMPORT and "old_config_entry_id" in entry.data: # Remove the old config entry ID from the entry data so we don't try this again # on the next setup data = entry.data.copy() old_config_entry_id = data.pop("old_config_entry_id") hass.config_entries.async_update_entry(entry, data=data) _LOGGER.debug( ("Setting up imported homewizard_energy entry %s for the first time as " "homewizard entry %s"), old_config_entry_id, entry.entry_id, ) ent_reg = er.async_get(hass) for entity in er.async_entries_for_config_entry( ent_reg, old_config_entry_id): _LOGGER.debug("Removing %s", entity.entity_id) ent_reg.async_remove(entity.entity_id) _LOGGER.debug("Re-creating %s for the new config entry", entity.entity_id) # We will precreate the entity so that any customizations can be preserved new_entity = ent_reg.async_get_or_create( entity.domain, DOMAIN, entity.unique_id, suggested_object_id=entity.entity_id.split(".")[1], disabled_by=entity.disabled_by, config_entry=entry, original_name=entity.original_name, original_icon=entity.original_icon, ) _LOGGER.debug("Re-created %s", new_entity.entity_id) # If there are customizations on the old entity, apply them to the new one if entity.name or entity.icon: ent_reg.async_update_entity(new_entity.entity_id, name=entity.name, icon=entity.icon) # Remove the old config entry and now the entry is fully migrated hass.async_create_task( hass.config_entries.async_remove(old_config_entry_id)) # Create coordinator coordinator = Coordinator(hass, entry.data[CONF_IP_ADDRESS]) try: await coordinator.async_config_entry_first_refresh() except ConfigEntryNotReady: await coordinator.api.close() raise # Finalize hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup(hass: HomeAssistant, config: dict) -> Awaitable[bool]: """Set up the persistent notification component.""" persistent_notifications = OrderedDict() hass.data[DOMAIN] = {'notifications': persistent_notifications} @callback def create_service(call): """Handle a create notification service call.""" title = call.data.get(ATTR_TITLE) message = call.data.get(ATTR_MESSAGE) notification_id = call.data.get(ATTR_NOTIFICATION_ID) if notification_id is not None: entity_id = ENTITY_ID_FORMAT.format(slugify(notification_id)) else: entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, DEFAULT_OBJECT_ID, hass=hass) notification_id = entity_id.split('.')[1] attr = {} if title is not None: try: title.hass = hass title = title.async_render() except TemplateError as ex: _LOGGER.error('Error rendering title %s: %s', title, ex) title = title.template attr[ATTR_TITLE] = title try: message.hass = hass message = message.async_render() except TemplateError as ex: _LOGGER.error('Error rendering message %s: %s', message, ex) message = message.template attr[ATTR_MESSAGE] = message hass.states.async_set(entity_id, STATE, attr) # Store notification and fire event # This will eventually replace state machine storage persistent_notifications[entity_id] = { ATTR_MESSAGE: message, ATTR_NOTIFICATION_ID: notification_id, ATTR_STATUS: STATUS_UNREAD, ATTR_TITLE: title, ATTR_CREATED_AT: dt_util.utcnow(), } hass.bus.async_fire(EVENT_PERSISTENT_NOTIFICATIONS_UPDATED) @callback def dismiss_service(call): """Handle the dismiss notification service call.""" notification_id = call.data.get(ATTR_NOTIFICATION_ID) entity_id = ENTITY_ID_FORMAT.format(slugify(notification_id)) if entity_id not in persistent_notifications: return hass.states.async_remove(entity_id) del persistent_notifications[entity_id] hass.bus.async_fire(EVENT_PERSISTENT_NOTIFICATIONS_UPDATED) @callback def mark_read_service(call): """Handle the mark_read notification service call.""" notification_id = call.data.get(ATTR_NOTIFICATION_ID) entity_id = ENTITY_ID_FORMAT.format(slugify(notification_id)) if entity_id not in persistent_notifications: _LOGGER.error('Marking persistent_notification read failed: ' 'Notification ID %s not found.', notification_id) return persistent_notifications[entity_id][ATTR_STATUS] = STATUS_READ hass.bus.async_fire(EVENT_PERSISTENT_NOTIFICATIONS_UPDATED) hass.services.async_register(DOMAIN, SERVICE_CREATE, create_service, SCHEMA_SERVICE_CREATE) hass.services.async_register(DOMAIN, SERVICE_DISMISS, dismiss_service, SCHEMA_SERVICE_DISMISS) hass.services.async_register(DOMAIN, SERVICE_MARK_READ, mark_read_service, SCHEMA_SERVICE_MARK_READ) hass.components.websocket_api.async_register_command( WS_TYPE_GET_NOTIFICATIONS, websocket_get_notifications, SCHEMA_WS_GET ) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Nest from a config entry with dispatch between old/new flows.""" if DATA_SDM not in entry.data: return await async_setup_legacy_entry(hass, entry) implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry ) ) config = hass.data[DOMAIN][DATA_NEST_CONFIG] session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) auth = api.AsyncConfigEntryAuth( aiohttp_client.async_get_clientsession(hass), session, API_URL, ) subscriber = GoogleNestSubscriber( auth, config[CONF_PROJECT_ID], config[CONF_SUBSCRIBER_ID] ) callback = SignalUpdateCallback(hass) subscriber.set_update_callback(callback.async_handle_event) try: await subscriber.start_async() except AuthException as err: _LOGGER.debug("Subscriber authentication error: %s", err) hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH}, data=entry.data, ) ) return False except ConfigurationException as err: _LOGGER.error("Configuration error: %s", err) subscriber.stop_async() return False except GoogleNestException as err: if DATA_NEST_UNAVAILABLE not in hass.data[DOMAIN]: _LOGGER.error("Subscriber error: %s", err) hass.data[DOMAIN][DATA_NEST_UNAVAILABLE] = True subscriber.stop_async() raise ConfigEntryNotReady from err try: await subscriber.async_get_device_manager() except GoogleNestException as err: if DATA_NEST_UNAVAILABLE not in hass.data[DOMAIN]: _LOGGER.error("Device manager error: %s", err) hass.data[DOMAIN][DATA_NEST_UNAVAILABLE] = True subscriber.stop_async() raise ConfigEntryNotReady from err hass.data[DOMAIN].pop(DATA_NEST_UNAVAILABLE, None) hass.data[DOMAIN][DATA_SUBSCRIBER] = subscriber for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component) ) return True