def websocket_sign_path( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Handle a sign path request.""" connection.send_message(websocket_api.result_message(msg['id'], { 'path': async_sign_path(hass, connection.refresh_token_id, msg['path'], timedelta(seconds=msg['expires'])) }))
async def websocket_current_user(hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return the current user.""" user = connection.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], }))
def websocket_setup_mfa(hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]) -> None: """Return a setup flow for mfa auth module.""" async def async_setup_flow(msg: dict[str, Any]) -> None: """Return a setup flow for mfa auth module.""" flow_manager: MfaFlowManager = hass.data[DATA_SETUP_FLOW_MGR] if (flow_id := msg.get("flow_id")) is not None: result = await flow_manager.async_configure( flow_id, msg.get("user_input")) connection.send_message( websocket_api.result_message(msg["id"], _prepare_result_json(result))) return mfa_module_id = msg["mfa_module_id"] if hass.auth.get_auth_mfa_module(mfa_module_id) is None: connection.send_message( websocket_api.error_message( msg["id"], "no_module", f"MFA module {mfa_module_id} is not found")) return result = await flow_manager.async_init( mfa_module_id, data={"user_id": connection.user.id}) connection.send_message( websocket_api.result_message(msg["id"], _prepare_result_json(result)))
async def websocket_current_user(hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return the current user.""" user = connection.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, "is_admin": user.is_admin, "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], }, ))
async def hacs_repository_info( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ) -> None: """Return information about HACS.""" hacs: HacsBase = hass.data.get(DOMAIN) repository = hacs.repositories.get_by_id(msg["repository_id"]) if repository is None: connection.send_error(msg["id"], "repository_not_found", "Repository not found") return if not repository.updated_info: await repository.update_repository(ignore_issues=True, force=True) repository.updated_info = True connection.send_message( websocket_api.result_message( msg["id"], { "additional_info": repository.additional_info, "authors": repository.data.authors, "available_version": repository.display_available_version, "beta": repository.data.show_beta, "can_download": repository.can_download, "category": repository.data.category, "config_flow": repository.data.config_flow, "country": repository.repository_manifest.country, "custom": not hacs.repositories.is_default(str(repository.data.id)), "default_branch": repository.data.default_branch, "description": repository.data.description, "domain": repository.data.domain, "downloads": repository.data.downloads, "file_name": repository.data.file_name, "full_name": repository.data.full_name, "hide_default_branch": repository.repository_manifest.hide_default_branch, "homeassistant": repository.repository_manifest.homeassistant, "id": repository.data.id, "installed_version": repository.display_installed_version, "installed": repository.data.installed, "issues": repository.data.open_issues, "last_updated": repository.data.last_updated, "local_path": repository.content.path.local, "name": repository.display_name, "new": repository.data.new, "pending_upgrade": repository.pending_update, "releases": repository.data.published_tags, "ref": repository.ref, "selected_tag": repository.data.selected_tag, "stars": repository.data.stargazers_count, "state": repository.state, "status": repository.display_status, "topics": repository.data.topics, "version_or_commit": repository.display_version_or_commit, }, ) )
def websocket_get_notifications(hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return a list of persistent_notifications.""" connection.send_message( websocket_api.result_message(msg['id'], [{ key: data[key] for key in (ATTR_NOTIFICATION_ID, ATTR_MESSAGE, ATTR_STATUS, ATTR_TITLE, ATTR_CREATED_AT) } for data in hass.data[DOMAIN]['notifications'].values()]))
async def hacs_repository_ignore( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Ignore a repository.""" hacs: HacsBase = hass.data.get(DOMAIN) repository = hacs.repositories.get_by_id(msg["repository"]) hacs.common.ignored_repositories.append(repository.data.full_name) await hacs.data.async_write() connection.send_message(websocket_api.result_message(msg["id"]))
async def ws_get_list_statistic_ids( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict ) -> None: """Fetch a list of available statistic_id.""" connection.send_message( await get_instance(hass).async_add_executor_job( _ws_get_list_statistic_ids, hass, msg["id"], msg.get("statistic_type"), ) )
async def websocket_delete_refresh_token( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Handle a delete refresh token request.""" refresh_token = connection.user.refresh_tokens.get(msg["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"], {}))
async def hacs_repositories_removed( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Get information about removed repositories.""" hacs: HacsBase = hass.data.get(DOMAIN) content = [] for repo in hacs.repositories.list_removed: if repo.repository not in hacs.common.ignored_repositories: content.append(repo.to_json()) connection.send_message(websocket_api.result_message(msg["id"], content))
async def hacs_critical_list( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """List critical repositories.""" connection.send_message( websocket_api.result_message( msg["id"], (await async_load_from_store(hass, "critical") or []), ) )
async def hacs_repository_refresh( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Refresh a repository.""" hacs: HacsBase = hass.data.get(DOMAIN) repository = hacs.repositories.get_by_id(msg["repository"]) await repository.update_repository(ignore_issues=True, force=True) await hacs.data.async_write() connection.send_message(websocket_api.result_message(msg["id"], {}))
async def hacs_repository_state( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Set the state of a repository""" hacs: HacsBase = hass.data.get(DOMAIN) repository = hacs.repositories.get_by_id(msg["repository"]) repository.state = msg["state"] await hacs.data.async_write() connection.send_message(websocket_api.result_message(msg["id"], {}))
def websocket_get_notifications( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return a list of persistent_notifications.""" connection.send_message( websocket_api.result_message(msg['id'], [ { key: data[key] for key in (ATTR_NOTIFICATION_ID, ATTR_MESSAGE, ATTR_STATUS, ATTR_TITLE, ATTR_CREATED_AT) } for data in hass.data[DOMAIN]['notifications'].values() ]) )
async def websocket_delete_refresh_token( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Handle a delete refresh token request.""" refresh_token = connection.user.refresh_tokens.get(msg['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'], {}))
async def websocket_create_long_lived_access_token( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Create or a long-lived access token.""" refresh_token = await hass.auth.async_create_refresh_token( connection.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))
async def hacs_critical_acknowledge( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Acknowledge critical repository.""" repository = msg["repository"] critical = await async_load_from_store(hass, "critical") for repo in critical: if repository == repo["repository"]: repo["acknowledged"] = True await async_save_to_store(hass, "critical", critical) connection.send_message(websocket_api.result_message(msg["id"], critical))
async def websocket_create_long_lived_access_token( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Create or a long-lived access token.""" refresh_token = await hass.auth.async_create_refresh_token( connection.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))
def websocket_refresh_tokens( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return metadata of users refresh tokens.""" current_id = connection.refresh_token_id connection.send_message(websocket_api.result_message(msg['id'], [{ 'id': refresh.id, 'client_id': refresh.client_id, 'client_name': refresh.client_name, 'client_icon': refresh.client_icon, 'type': refresh.token_type, 'created_at': refresh.created_at, 'is_current': refresh.id == current_id, 'last_used_at': refresh.last_used_at, 'last_used_ip': refresh.last_used_ip, } for refresh in connection.user.refresh_tokens.values()]))
async def hacs_repositories_remove( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Remove custom repositoriy.""" hacs: HacsBase = hass.data.get(DOMAIN) hacs.log.warning(connection.context) hacs.log.warning(msg) repository = hacs.repositories.get_by_id(msg["repository"]) repository.remove() await hacs.data.async_write() connection.send_message(websocket_api.result_message(msg["id"], {}))
def websocket_sign_path(hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Handle a sign path request.""" connection.send_message( websocket_api.result_message( msg["id"], { "path": async_sign_path( hass, msg["path"], timedelta(seconds=msg["expires"]), ) }, ))
async def hacs_repository_beta( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Show or hide beta versions of a repository""" hacs: HacsBase = hass.data.get(DOMAIN) repository = hacs.repositories.get_by_id(msg["repository"]) repository.data.show_beta = msg["show_beta"] await repository.update_repository(force=True) repository.state = None await hacs.data.async_write() connection.send_message(websocket_api.result_message(msg["id"], {}))
async def handle_info(hass: HomeAssistantType, connection: websocket_api.ActiveConnection, msg: Dict): """Handle an info request.""" info_callbacks = hass.data.get(DOMAIN, {}).get('info', {}) data = OrderedDict() data['homeassistant'] = \ await hass.helpers.system_info.async_get_system_info() if info_callbacks: for domain, domain_data in zip(info_callbacks, await asyncio.gather(*[ _info_wrapper(hass, info_callback) for info_callback in info_callbacks.values() ])): data[domain] = domain_data connection.send_message(websocket_api.result_message(msg['id'], data))
async def handle_info(hass: HomeAssistantType, connection: websocket_api.ActiveConnection, msg: Dict): """Handle an info request.""" info_callbacks = hass.data.get(DOMAIN, {}).get('info', {}) data = OrderedDict() data['homeassistant'] = \ await hass.helpers.system_info.async_get_system_info() if info_callbacks: for domain, domain_data in zip( info_callbacks, await asyncio.gather(*[ _info_wrapper(hass, info_callback) for info_callback in info_callbacks.values() ])): data[domain] = domain_data connection.send_message(websocket_api.result_message(msg['id'], data))
async def hacs_subscribe( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict, ) -> None: """Handle websocket subscriptions.""" @callback def forward_messages(data: dict | None = None): """Forward events to websocket.""" connection.send_message(websocket_api.event_message(msg["id"], data)) connection.subscriptions[msg["id"]] = async_dispatcher_connect( hass, msg["signal"], forward_messages, ) connection.send_message(websocket_api.result_message(msg["id"]))
async def hacs_repositories_list( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """List repositories.""" hacs: HacsBase = hass.data.get(DOMAIN) connection.send_message( websocket_api.result_message( msg["id"], [ { "authors": repo.data.authors, "available_version": repo.display_available_version, "installed_version": repo.display_installed_version, "config_flow": repo.data.config_flow, "can_download": repo.can_download, "category": repo.data.category, "country": repo.repository_manifest.country, "custom": not hacs.repositories.is_default(str(repo.data.id)), "description": repo.data.description, "domain": repo.data.domain, "downloads": repo.data.downloads, "file_name": repo.data.file_name, "full_name": repo.data.full_name, "hide": repo.data.hide, "homeassistant": repo.repository_manifest.homeassistant, "id": repo.data.id, "installed": repo.data.installed, "last_updated": repo.data.last_updated, "local_path": repo.content.path.local, "name": repo.display_name, "new": repo.data.new, "pending_upgrade": repo.pending_update, "stars": repo.data.stargazers_count, "state": repo.state, "status": repo.display_status, "topics": repo.data.topics, } for repo in hacs.repositories.list_all if repo.data.category in (msg.get("categories") or hacs.common.categories) and not repo.ignored_by_country_configuration ], ) )
def websocket_refresh_tokens(hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return metadata of users refresh tokens.""" current_id = connection.refresh_token_id connection.send_message( websocket_api.result_message( msg["id"], [{ "id": refresh.id, "client_id": refresh.client_id, "client_name": refresh.client_name, "client_icon": refresh.client_icon, "type": refresh.token_type, "created_at": refresh.created_at, "is_current": refresh.id == current_id, "last_used_at": refresh.last_used_at, "last_used_ip": refresh.last_used_ip, } for refresh in connection.user.refresh_tokens.values()], ))
async def hacs_repository_version( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Set the version of a repository""" hacs: HacsBase = hass.data.get(DOMAIN) repository = hacs.repositories.get_by_id(msg["repository"]) if msg["version"] == repository.data.default_branch: repository.data.selected_tag = None else: repository.data.selected_tag = msg["version"] await repository.update_repository(force=True) repository.state = None await hacs.data.async_write() connection.send_message(websocket_api.result_message(msg["id"], {}))
async def websocket_create_long_lived_access_token( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Create or a long-lived access token.""" refresh_token = await hass.auth.async_create_refresh_token( connection.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"]), ) try: access_token = hass.auth.async_create_access_token(refresh_token) except InvalidAuthError as exc: return websocket_api.error_message( msg["id"], websocket_api.const.ERR_UNAUTHORIZED, str(exc)) connection.send_message( websocket_api.result_message(msg["id"], access_token))
async def websocket_delete_registration(hass: HomeAssistantType, connection: ActiveConnection, msg: dict) -> None: """Delete the registration for the given webhook_id.""" user = connection.user webhook_id = msg.get(CONF_WEBHOOK_ID) if webhook_id is None: connection.send_error(msg['id'], ERR_INVALID_FORMAT, "Webhook ID not provided") return config_entry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] registration = config_entry.data if registration is None: connection.send_error(msg['id'], ERR_NOT_FOUND, "Webhook ID not found in storage") return if registration[CONF_USER_ID] != user.id and not user.is_admin: return error_message( msg['id'], ERR_UNAUTHORIZED, 'User is not registration owner') await hass.config_entries.async_remove(config_entry.entry_id) hass.data[DOMAIN][DATA_DELETED_IDS].append(webhook_id) store = hass.data[DOMAIN][DATA_STORE] try: await store.async_save(savable_state(hass)) except HomeAssistantError: return error_message( msg['id'], 'internal_error', 'Error deleting registration') if (CONF_CLOUDHOOK_URL in registration and "cloud" in hass.config.components): await async_delete_cloudhook(hass, webhook_id) connection.send_message(result_message(msg['id'], 'ok'))
async def websocket_delete_registration(hass: HomeAssistantType, connection: ActiveConnection, msg: dict) -> None: """Delete the registration for the given webhook_id.""" user = connection.user webhook_id = msg.get(CONF_WEBHOOK_ID) if webhook_id is None: connection.send_error(msg['id'], ERR_INVALID_FORMAT, "Webhook ID not provided") return config_entry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id] registration = config_entry.data if registration is None: connection.send_error(msg['id'], ERR_NOT_FOUND, "Webhook ID not found in storage") return if registration[CONF_USER_ID] != user.id and not user.is_admin: return error_message(msg['id'], ERR_UNAUTHORIZED, 'User is not registration owner') await hass.config_entries.async_remove(config_entry.entry_id) hass.data[DOMAIN][DATA_DELETED_IDS].append(webhook_id) store = hass.data[DOMAIN][DATA_STORE] try: await store.async_save(savable_state(hass)) except HomeAssistantError: return error_message(msg['id'], 'internal_error', 'Error deleting registration') if (CONF_CLOUDHOOK_URL in registration and "cloud" in hass.config.components): await async_delete_cloudhook(hass, webhook_id) connection.send_message(result_message(msg['id'], 'ok'))
def websocket_get_notifications( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: Mapping[str, Any], ) -> None: """Return a list of dwains_theme_notifications.""" connection.send_message( websocket_api.result_message( msg["id"], [{ key: data[key] for key in ( ATTR_NOTIFICATION_ID, ATTR_MESSAGE, ATTR_STATUS, ATTR_TITLE, ATTR_CREATED_AT, ) } for data in hass.data[DOMAIN]["notifications"].values()], ))
async def websocket_get_user_registrations(hass: HomeAssistantType, connection: ActiveConnection, msg: dict) -> None: """Return all registrations or just registrations for given user ID.""" user_id = msg.get(CONF_USER_ID, connection.user.id) if user_id != connection.user.id and not connection.user.is_admin: # If user ID is provided and is not current user ID and current user # isn't an admin user connection.send_error(msg['id'], ERR_UNAUTHORIZED, "Unauthorized") return user_registrations = [] for config_entry in hass.config_entries.async_entries(domain=DOMAIN): registration = config_entry.data if connection.user.is_admin or registration[CONF_USER_ID] is user_id: user_registrations.append(safe_registration(registration)) connection.send_message(result_message(msg['id'], user_registrations))
async def websocket_current_user( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg): """Return the current user.""" user = connection.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, 'is_admin': user.is_admin, '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], }))
async def websocket_get_user_registrations( hass: HomeAssistantType, connection: ActiveConnection, msg: dict) -> None: """Return all registrations or just registrations for given user ID.""" user_id = msg.get(CONF_USER_ID, connection.user.id) if user_id != connection.user.id and not connection.user.is_admin: # If user ID is provided and is not current user ID and current user # isn't an admin user connection.send_error(msg['id'], ERR_UNAUTHORIZED, "Unauthorized") return user_registrations = [] for config_entry in hass.config_entries.async_entries(domain=DOMAIN): registration = config_entry.data if connection.user.is_admin or registration[CONF_USER_ID] is user_id: user_registrations.append(safe_registration(registration)) connection.send_message( result_message(msg['id'], user_registrations))
async def websocket_camera_thumbnail(hass: HomeAssistant, connection: ActiveConnection, msg: dict) -> None: """Handle get camera thumbnail websocket command. Async friendly. """ _LOGGER.warning( "The websocket command 'camera_thumbnail' has been deprecated") try: image = await async_get_image(hass, msg["entity_id"]) await connection.send_big_result( msg["id"], { "content_type": image.content_type, "content": base64.b64encode(image.content).decode("utf-8"), }, ) except HomeAssistantError: connection.send_message( websocket_api.error_message(msg["id"], "image_fetch_failed", "Unable to fetch image"))
async def hacs_repository_release_notes( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ): """Return release notes.""" hacs: HacsBase = hass.data.get(DOMAIN) repository = hacs.repositories.get_by_id(msg["repository"]) connection.send_message( websocket_api.result_message( msg["id"], [ { "name": x.name, "body": x.body, "tag": x.tag_name, } for x in repository.releases.objects if not repository.data.installed_version or version_left_higher_then_right(x.tag_name, repository.data.installed_version) ], ) )