async def websocket_handle_thumbnail(opp, connection, msg): """Handle get media player cover command. Async friendly. """ component = opp.data[DOMAIN] player = component.get_entity(msg["entity_id"]) if player is None: connection.send_message( websocket_api.error_message(msg["id"], "entity_not_found", "Entity not found")) return data, content_type = await player.async_get_media_image() if data is None: connection.send_message( websocket_api.error_message(msg["id"], "thumbnail_fetch_failed", "Failed to fetch thumbnail")) return await connection.send_big_result( msg["id"], { "content_type": content_type, "content": base64.b64encode(data).decode("utf-8"), }, )
async def websocket_handle_thumbnail(opp, connection, msg): """Handle get media player cover command. Async friendly. """ component = opp.data[DOMAIN] player = component.get_entity(msg["entity_id"]) if player is None: connection.send_message( websocket_api.error_message(msg["id"], ERR_NOT_FOUND, "Entity not found")) return _LOGGER.warning( "The websocket command media_player_thumbnail is deprecated. Use /api/media_player_proxy instead" ) data, content_type = await player.async_get_media_image() if data is None: connection.send_message( websocket_api.error_message(msg["id"], "thumbnail_fetch_failed", "Failed to fetch thumbnail")) return await connection.send_big_result( msg["id"], { "content_type": content_type, "content": base64.b64encode(data).decode("utf-8"), }, )
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_update_entity(opp, connection, msg): """Handle update 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 changes = {} for key in ("name", "icon", "area_id", "disabled_by"): if key in msg: changes[key] = msg[key] if "new_entity_id" in msg and msg["new_entity_id"] != msg["entity_id"]: changes["new_entity_id"] = msg["new_entity_id"] if opp.states.get(msg["new_entity_id"]) is not None: connection.send_message( websocket_api.error_message( msg["id"], "invalid_info", "Entity with this ID is already registered", )) return if "disabled_by" in msg and msg["disabled_by"] is None: entity = registry.entities[msg["entity_id"]] if entity.device_id: device_registry = await opp.helpers.device_registry.async_get_registry( ) device = device_registry.async_get(entity.device_id) if device.disabled: connection.send_message( websocket_api.error_message(msg["id"], "invalid_info", "Device is disabled")) return try: if changes: entry = registry.async_update_entity(msg["entity_id"], **changes) except ValueError as err: connection.send_message( websocket_api.error_message(msg["id"], "invalid_info", str(err))) return result = {"entity_entry": _entry_ext_dict(entry)} if "disabled_by" in changes and changes["disabled_by"] is None: config_entry = opp.config_entries.async_get_entry( entry.config_entry_id) if config_entry and not config_entry.supports_unload: result["require_restart"] = True else: result["reload_delay"] = config_entries.RELOAD_AFTER_UPDATE_DELAY connection.send_result(msg["id"], result)
async def websocket_browse_media(opp, connection, msg): """ Browse media available to the media_player entity. To use, media_player integrations can implement MediaPlayerEntity.async_browse_media() """ component = opp.data[DOMAIN] player: MediaPlayerDevice | None = component.get_entity(msg["entity_id"]) if player is None: connection.send_error(msg["id"], "entity_not_found", "Entity not found") return if not player.supported_features & SUPPORT_BROWSE_MEDIA: connection.send_message( websocket_api.error_message( msg["id"], ERR_NOT_SUPPORTED, "Player does not support browsing media")) return media_content_type = msg.get(ATTR_MEDIA_CONTENT_TYPE) media_content_id = msg.get(ATTR_MEDIA_CONTENT_ID) try: payload = await player.async_browse_media(media_content_type, media_content_id) except NotImplementedError: _LOGGER.error( "%s allows media browsing but its integration (%s) does not", player.entity_id, player.platform.platform_name, ) connection.send_message( websocket_api.error_message( msg["id"], ERR_NOT_SUPPORTED, "Integration does not support browsing media", )) return except BrowseError as err: connection.send_message( websocket_api.error_message(msg["id"], ERR_UNKNOWN_ERROR, str(err))) return # For backwards compat if isinstance(payload, BrowseMedia): payload = payload.as_dict() else: _LOGGER.warning("Browse Media should use new BrowseMedia class") connection.send_result(msg["id"], payload)
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))
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)))
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"]))
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)))
def with_cloud_auth(opp, connection, msg): """Require to be logged into the cloud.""" cloud = opp.data[DOMAIN] if not cloud.is_logged_in: connection.send_message( websocket_api.error_message( msg["id"], "not_logged_in", "You need to be logged in to the cloud." ) ) return handler(opp, connection, msg)
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_get_device(opp, connection, msg): """Get ZHA devices.""" zha_gateway = opp.data[DATA_ZHA][DATA_ZHA_GATEWAY] ieee = msg[ATTR_IEEE] device = None if ieee in zha_gateway.devices: device = zha_gateway.devices[ieee].zha_device_info if not device: connection.send_message( websocket_api.error_message(msg[ID], websocket_api.const.ERR_NOT_FOUND, "ZHA Device not found")) return connection.send_result(msg[ID], device)
async def websocket_create(opp, connection, msg): """Create credentials and attach to a user.""" provider = _get_provider(opp) await provider.async_initialize() user = await opp.auth.async_get_user(msg["user_id"]) if user is None: connection.send_message( websocket_api.error_message(msg["id"], "not_found", "User not found")) return if user.system_generated: connection.send_message( websocket_api.error_message( msg["id"], "system_generated", "Cannot add credentials to a system generated user.", )) return try: await opp.async_add_executor_job(provider.data.add_auth, msg["username"], msg["password"]) except auth_ha.InvalidUser: connection.send_message( websocket_api.error_message(msg["id"], "username_exists", "Username already exists")) return credentials = await provider.async_get_or_create_credentials( {"username": msg["username"]}) await opp.auth.async_link_user(user, credentials) await provider.data.async_save() connection.send_message(websocket_api.result_message(msg["id"]))
async def websocket_change_password(opp, connection, msg): """Change user password.""" user = connection.user if user is None: connection.send_message( websocket_api.error_message(msg["id"], "user_not_found", "User not found")) return provider = _get_provider(opp) await provider.async_initialize() username = None for credential in user.credentials: if credential.auth_provider_type == provider.type: username = credential.data["username"] break if username is None: connection.send_message( websocket_api.error_message(msg["id"], "credentials_not_found", "Credentials not found")) return try: await provider.async_validate_login(username, msg["current_password"]) except auth_ha.InvalidAuth: connection.send_message( websocket_api.error_message(msg["id"], "invalid_password", "Invalid password")) return await opp.async_add_executor_job(provider.data.change_password, username, msg["new_password"]) await provider.data.async_save() connection.send_message(websocket_api.result_message(msg["id"]))
async def websocket_migrate_zwave(opp, connection, msg): """Migrate the zwave integration device and entity data to ozw integration.""" if "zwave" not in opp.config.components: _LOGGER.error("Can not migrate, zwave integration is not loaded") connection.send_message( websocket_api.error_message(msg["id"], "zwave_not_loaded", "Integration zwave is not loaded")) return zwave = opp.components.zwave zwave_data = await zwave.async_get_ozw_migration_data(opp) _LOGGER.debug("Migration zwave data: %s", zwave_data) ozw_data = await async_get_migration_data(opp) _LOGGER.debug("Migration ozw data: %s", ozw_data) can_migrate = map_node_values(zwave_data, ozw_data) zwave_entity_ids = [ entry["entity_entry"].entity_id for entry in zwave_data.values() ] ozw_entity_ids = [ entry["entity_entry"].entity_id for entry in ozw_data.values() ] migration_device_map = { zwave_device_id: ozw_device_id for ozw_device_id, zwave_device_id in can_migrate["device_entries"].items() } migration_entity_map = { zwave_entry["entity_entry"].entity_id: ozw_entity_id for ozw_entity_id, zwave_entry in can_migrate["entity_entries"].items() } _LOGGER.debug("Migration entity map: %s", migration_entity_map) if not msg[DRY_RUN]: await async_migrate(opp, can_migrate) connection.send_result( msg[ID], { "migration_device_map": migration_device_map, "zwave_entity_ids": zwave_entity_ids, "ozw_entity_ids": ozw_entity_ids, "migration_entity_map": migration_entity_map, "migrated": not msg[DRY_RUN], }, )
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_group(opp, connection, msg): """Get ZHA group.""" zha_gateway = opp.data[DATA_ZHA][DATA_ZHA_GATEWAY] group_id = msg[GROUP_ID] group = None if group_id in zha_gateway.groups: group = zha_gateway.groups.get(group_id).group_info if not group: connection.send_message( websocket_api.error_message(msg[ID], websocket_api.const.ERR_NOT_FOUND, "ZHA Group not found")) return connection.send_result(msg[ID], group)
async def websocket_update_entity(opp, connection, msg): """Handle update 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 changes = {} for key in ("name", "icon", "disabled_by"): if key in msg: changes[key] = msg[key] if "new_entity_id" in msg and msg["new_entity_id"] != msg["entity_id"]: changes["new_entity_id"] = msg["new_entity_id"] if opp.states.get(msg["new_entity_id"]) is not None: connection.send_message( websocket_api.error_message(msg["id"], "invalid_info", "Entity is already registered")) return try: if changes: entry = registry.async_update_entity(msg["entity_id"], **changes) 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_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_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)))
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_remove_group_members(opp, connection, msg): """Remove members from a ZHA group.""" zha_gateway = opp.data[DATA_ZHA][DATA_ZHA_GATEWAY] group_id = msg[GROUP_ID] members = msg[ATTR_MEMBERS] zha_group = None if group_id in zha_gateway.groups: zha_group = zha_gateway.groups.get(group_id) await zha_group.async_remove_members(members) if not zha_group: connection.send_message( websocket_api.error_message(msg[ID], websocket_api.const.ERR_NOT_FOUND, "ZHA Group not found")) return ret_group = zha_group.group_info connection.send_result(msg[ID], ret_group)
async def websocket_camera_thumbnail(opp, connection, msg): """Handle get camera thumbnail websocket command. Async friendly. """ try: image = await async_get_image(opp, msg["entity_id"]) await connection.send_big_result( msg["id"], { "content_type": image.content_type, "content": base64.b64encode(image.content).decode("utf-8"), }, ) except OpenPeerPowerError: connection.send_message( websocket_api.error_message(msg["id"], "image_fetch_failed", "Unable to fetch image"))
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"]), ) try: access_token = opp.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_camera_thumbnail( opp: OpenPeerPower, 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(opp, msg["entity_id"]) await connection.send_big_result( msg["id"], { "content_type": image.content_type, "content": base64.b64encode(image.content).decode("utf-8"), }, ) except OpenPeerPowerError: connection.send_message( websocket_api.error_message( msg["id"], "image_fetch_failed", "Unable to fetch image" ) )