async def websocket_delete(opp, connection, msg): """Delete username and related credential.""" provider = _get_provider(opp) await provider.async_initialize() credentials = await provider.async_get_or_create_credentials( {"username": msg["username"]}) # if not new, an existing credential exists. # Removing the credential will also remove the auth. if not credentials.is_new: await opp.auth.async_remove_credentials(credentials) connection.send_message(websocket_api.result_message(msg["id"])) return try: provider.data.async_remove_auth(msg["username"]) await provider.data.async_save() except auth_ha.InvalidUser: connection.send_message( websocket_api.error_message(msg["id"], "auth_not_found", "Given username was not found.")) return connection.send_message(websocket_api.result_message(msg["id"]))
async def async_setup_flow(msg): """Return a setup flow for mfa auth module.""" flow_manager = opp.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( websocket_api.result_message(msg["id"], _prepare_result_json(result))) return mfa_module_id = msg.get("mfa_module_id") mfa_module = opp.auth.get_auth_mfa_module(mfa_module_id) if mfa_module 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)))
def websocket_get_themes(opp, connection, msg): """Handle get themes command.""" if opp.config.safe_mode: connection.send_message( websocket_api.result_message( msg["id"], { "themes": { "safe_mode": { "primary-color": "#db4437", "accent-color": "#ffca28", } }, "default_theme": "safe_mode", }, )) return connection.send_message( websocket_api.result_message( msg["id"], { "themes": opp.data[DATA_THEMES], "default_theme": opp.data[DATA_DEFAULT_THEME], "default_dark_theme": opp.data.get(DATA_DEFAULT_DARK_THEME), }, ))
async def websocket_list_devices(opp, connection, msg): """Handle list devices command.""" registry = await async_get_registry(opp) connection.send_message( websocket_api.result_message( msg["id"], [_entry_dict(entry) for entry in registry.devices.values()]))
async def websocket_current_user(opp: OpenPeerPower, connection: websocket_api.ActiveConnection, msg): """Return the current user.""" user = connection.user enabled_modules = await opp.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 opp.auth.auth_mfa_modules], }, ))
async def websocket_update_prefs(opp, connection, msg): """Handle request for account info.""" cloud = opp.data[DOMAIN] changes = dict(msg) changes.pop("id") changes.pop("type") # If we turn alexa linking on, validate that we can fetch access token if changes.get(PREF_ALEXA_REPORT_STATE): try: with async_timeout.timeout(10): await cloud.client.alexa_config.async_get_access_token() except asyncio.TimeoutError: connection.send_error( msg["id"], "alexa_timeout", "Timeout validating Alexa access token." ) return except (alexa_errors.NoTokenAvailable, RequireRelink): connection.send_error( msg["id"], "alexa_relink", "Please go to the Alexa app and re-link the Open Peer Power " "skill and then try to enable state reporting.", ) return await cloud.client.prefs.async_update(**changes) connection.send_message(websocket_api.result_message(msg["id"]))
async def websocket_subscription(opp, connection, msg): """Handle request for account info.""" cloud = opp.data[DOMAIN] with async_timeout.timeout(REQUEST_TIMEOUT): response = await cloud.fetch_subscription_info() if response.status != 200: connection.send_message( websocket_api.error_message( msg["id"], "request_failed", "Failed to request subscription" ) ) data = await response.json() # Check if a user is subscribed but local info is outdated # In that case, let's refresh and reconnect if data.get("provider") and not cloud.is_connected: _LOGGER.debug("Found disconnected account with valid subscriotion, connecting") await opp.async_add_executor_job(cloud.auth.renew_access_token) # Cancel reconnect in progress if cloud.iot.state != STATE_DISCONNECTED: await cloud.iot.disconnect() opp.async_create_task(cloud.iot.connect()) connection.send_message(websocket_api.result_message(msg["id"], data))
def websocket_subscribe_breakpoint_events(opp, connection, msg): """Subscribe to breakpoint events.""" @callback def breakpoint_hit(key, run_id, node): """Forward events to websocket.""" connection.send_message( websocket_api.event_message( msg["id"], { "domain": key[0], "item_id": key[1], "run_id": run_id, "node": node, }, )) remove_signal = async_dispatcher_connect(opp, SCRIPT_BREAKPOINT_HIT, breakpoint_hit) @callback def unsub(): """Unsubscribe from breakpoint events.""" remove_signal() if (SCRIPT_BREAKPOINT_HIT not in opp.data.get(DATA_DISPATCHER, {}) or not opp.data[DATA_DISPATCHER][SCRIPT_BREAKPOINT_HIT]): breakpoint_clear_all(opp) async_dispatcher_send(opp, SCRIPT_DEBUG_CONTINUE_ALL) connection.subscriptions[msg["id"]] = unsub connection.send_message(websocket_api.result_message(msg["id"]))
async def websocket_update(opp, connection, msg): """Update a user.""" user = await opp.auth.async_get_user(msg.pop("user_id")) if not user: connection.send_message( websocket_api.error_message(msg["id"], websocket_api.const.ERR_NOT_FOUND, "User not found")) return if user.system_generated: connection.send_message( websocket_api.error_message( msg["id"], "cannot_modify_system_generated", "Unable to update system generated users.", )) return msg.pop("type") msg_id = msg.pop("id") await opp.auth.async_update_user(user, **msg) connection.send_message( websocket_api.result_message(msg_id, {"user": _user_info(user)}))
async def websocket_set_user_data(opp, connection, msg, store, data): """Handle set global data command. Async friendly. """ data[msg["key"]] = msg["value"] await store.async_save(data) connection.send_message(websocket_api.result_message(msg["id"]))
def websocket_cloud_status(opp, connection, msg): """Handle request for account info. Async friendly. """ cloud = opp.data[DOMAIN] connection.send_message( websocket_api.result_message(msg["id"], _account_data(cloud)) )
def websocket_list(opp, connection, msg): """Return a list of webhooks.""" handlers = opp.data.setdefault(DOMAIN, {}) result = [ {"webhook_id": webhook_id, "domain": info["domain"], "name": info["name"]} for webhook_id, info in handlers.items() ] connection.send_message(websocket_api.result_message(msg["id"], result))
async def websocket_get_translations(opp, connection, msg): """Handle get translations command. Async friendly. """ resources = await async_get_translations(opp, msg["language"]) connection.send_message( websocket_api.result_message(msg["id"], {"resources": resources}) )
async def websocket_get_user_data(opp, connection, msg, store, data): """Handle get global data command. Async friendly. """ connection.send_message( websocket_api.result_message( msg["id"], {"value": data.get(msg["key"]) if "key" in msg else data}))
async def websocket_update_device(opp, connection, msg): """Handle update area websocket command.""" registry = await async_get_registry(opp) msg.pop("type") msg_id = msg.pop("id") entry = registry.async_update_device(**msg) connection.send_message(websocket_api.result_message(msg_id, _entry_dict(entry)))
def websocket_get_panels(opp, connection, msg): """Handle get panels command.""" user_is_admin = connection.user.is_admin panels = { panel_key: panel.to_response() for panel_key, panel in connection.opp.data[DATA_PANELS].items() if user_is_admin or not panel.require_admin } connection.send_message(websocket_api.result_message(msg["id"], panels))
async def websocket_subscribe(opp: OpenPeerPower, connection: ActiveConnection, msg: dict): """Subscribe to supervisor events.""" @callback def forward_messages(data): """Forward events to websocket.""" connection.send_message(websocket_api.event_message(msg[WS_ID], data)) connection.subscriptions[msg[WS_ID]] = async_dispatcher_connect( opp, EVENT_SUPERVISOR_EVENT, forward_messages) connection.send_message(websocket_api.result_message(msg[WS_ID]))
async def websocket_list_areas(opp, connection, msg): """Handle list areas command.""" registry = await async_get_registry(opp) connection.send_message( websocket_api.result_message( msg["id"], [{ "name": entry.name, "area_id": entry.id } for entry in registry.async_list_areas()], ))
async def websocket_create_area(opp, connection, msg): """Create area command.""" registry = await async_get_registry(opp) try: entry = registry.async_create(msg["name"]) except ValueError as err: connection.send_message( websocket_api.error_message(msg["id"], "invalid_info", str(err))) else: connection.send_message( websocket_api.result_message(msg["id"], _entry_dict(entry)))
async def websocket_get_translations(opp, connection, msg): """Handle get translations command.""" resources = await async_get_translations( opp, msg["language"], msg["category"], msg.get("integration"), msg.get("config_flow"), ) connection.send_message( websocket_api.result_message(msg["id"], {"resources": resources}))
async def websocket_delete_refresh_token( opp: OpenPeerPower, 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 opp.auth.async_remove_refresh_token(refresh_token) connection.send_message(websocket_api.result_message(msg["id"], {}))
async def websocket_delete_area(opp, connection, msg): """Delete area command.""" registry = await async_get_registry(opp) try: await registry.async_delete(msg["area_id"]) except KeyError: connection.send_message( websocket_api.error_message(msg["id"], "invalid_info", "Area ID doesn't exist")) else: connection.send_message( websocket_api.result_message(msg["id"], "success"))
async def websocket_create_long_lived_access_token( opp: OpenPeerPower, connection: websocket_api.ActiveConnection, msg ): """Create or a long-lived access token.""" refresh_token = await opp.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 = opp.auth.async_create_access_token(refresh_token) connection.send_message(websocket_api.result_message(msg["id"], access_token))
async def websocket_remove_entity(opp, connection, msg): """Handle remove entity websocket command. Async friendly. """ registry = await async_get_registry(opp) if msg["entity_id"] not in registry.entities: connection.send_message( websocket_api.error_message(msg["id"], ERR_NOT_FOUND, "Entity not found")) return registry.async_remove(msg["entity_id"]) connection.send_message(websocket_api.result_message(msg["id"]))
async def websocket_handle_update(opp, connection, msg): """Handle update shopping_list item.""" msg_id = msg.pop("id") item_id = msg.pop("item_id") msg.pop("type") data = msg try: item = await opp.data[DOMAIN].async_update(item_id, data) opp.bus.async_fire(EVENT, {"action": "update", "item": item}) connection.send_message(websocket_api.result_message(msg_id, item)) except KeyError: connection.send_message( websocket_api.error_message(msg_id, "item_not_found", "Item not found") )
async def websocket_get_entity(opp, connection, msg): """Handle get entity registry entry command. Async friendly. """ registry = await async_get_registry(opp) entry = registry.entities.get(msg["entity_id"]) if entry is None: connection.send_message( websocket_api.error_message(msg["id"], ERR_NOT_FOUND, "Entity not found")) return connection.send_message( websocket_api.result_message(msg["id"], _entry_ext_dict(entry)))
def websocket_sign_path(opp: OpenPeerPower, connection: websocket_api.ActiveConnection, msg): """Handle a sign path request.""" connection.send_message( websocket_api.result_message( msg["id"], { "path": async_sign_path( opp, connection.refresh_token_id, msg["path"], timedelta(seconds=msg["expires"]), ) }, ))
async def async_depose(msg): """Remove user from mfa auth module.""" mfa_module_id = msg["mfa_module_id"] try: await opp.auth.async_disable_user_mfa(connection.user, msg["mfa_module_id"]) except ValueError as err: connection.send_message( websocket_api.error_message( msg["id"], "disable_failed", f"Cannot disable MFA Module {mfa_module_id}: {err}", )) return connection.send_message(websocket_api.result_message( msg["id"], "done"))
async def websocket_delete(opp, connection, msg): """Delete a user.""" if msg["user_id"] == connection.user.id: connection.send_message( websocket_api.error_message(msg["id"], "no_delete_self", "Unable to delete your own account")) return user = await opp.auth.async_get_user(msg["user_id"]) if not user: connection.send_message( websocket_api.error_message(msg["id"], "not_found", "User not found")) return await opp.auth.async_remove_user(user) connection.send_message(websocket_api.result_message(msg["id"]))
def websocket_refresh_tokens(opp: OpenPeerPower, 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()], ))