def send_entry_not_found(
    connection: websocket_api.ActiveConnection, msg_id: int
) -> None:
    """Send Config entry not found error."""
    connection.send_error(
        msg_id, websocket_api.const.ERR_NOT_FOUND, "Config entry not found"
    )
Beispiel #2
0
async def ws_get_statistics_during_period(
    hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
) -> None:
    """Handle statistics websocket command."""
    start_time_str = msg["start_time"]
    end_time_str = msg.get("end_time")

    start_time = dt_util.parse_datetime(start_time_str)
    if start_time:
        start_time = dt_util.as_utc(start_time)
    else:
        connection.send_error(msg["id"], "invalid_start_time", "Invalid start_time")
        return

    if end_time_str:
        end_time = dt_util.parse_datetime(end_time_str)
        if end_time:
            end_time = dt_util.as_utc(end_time)
        else:
            connection.send_error(msg["id"], "invalid_end_time", "Invalid end_time")
            return
    else:
        end_time = None

    statistics = await hass.async_add_executor_job(
        statistics_during_period,
        hass,
        start_time,
        end_time,
        msg.get("statistic_ids"),
        msg.get("period"),
    )
    connection.send_result(msg["id"], statistics)
Beispiel #3
0
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,
            },
        )
    )
Beispiel #4
0
async def ws_backup_end(hass: HomeAssistant,
                        connection: websocket_api.ActiveConnection,
                        msg: dict) -> None:
    """Backup end notification."""

    instance: Recorder = hass.data[DATA_INSTANCE]
    _LOGGER.info("Backup end notification, releasing write lock")
    if not instance.unlock_database():
        connection.send_error(msg["id"], "database_unlock_failed",
                              "Failed to unlock database.")
    connection.send_result(msg["id"])
Beispiel #5
0
def handle_get(
    hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
):
    """List all possible diagnostic handlers."""
    domain = msg["domain"]

    if (info := hass.data[DOMAIN].get(domain)) is None:
        connection.send_error(
            msg["id"], websocket_api.ERR_NOT_FOUND, "Domain not supported"
        )
        return
Beispiel #6
0
def ws_get_prefs(
    hass: HomeAssistant,
    connection: websocket_api.ActiveConnection,
    msg: dict,
    manager: EnergyManager,
) -> None:
    """Handle get prefs command."""
    if manager.data is None:
        connection.send_error(msg["id"], websocket_api.ERR_NOT_FOUND, "No prefs")
        return

    connection.send_result(msg["id"], manager.data)
Beispiel #7
0
async def websocket_browse_media(hass: HomeAssistant,
                                 connection: ActiveConnection,
                                 msg: dict) -> None:
    """Browse available media."""
    try:
        media = await async_browse_media(hass, msg.get("media_content_id", ""))
        connection.send_result(
            msg["id"],
            media.as_dict(),
        )
    except BrowseError as err:
        connection.send_error(msg["id"], "browse_media_failed", str(err))
Beispiel #8
0
async def ws_backup_start(
    hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
) -> None:
    """Backup start notification."""

    _LOGGER.info("Backup start notification, locking database for writes")
    instance = get_instance(hass)
    try:
        await instance.lock_database()
    except TimeoutError as err:
        connection.send_error(msg["id"], "timeout_error", str(err))
        return
    connection.send_result(msg["id"])
Beispiel #9
0
async def ws_create_person(hass: HomeAssistantType,
                           connection: websocket_api.ActiveConnection, msg):
    """Create a person."""
    manager = hass.data[DOMAIN]  # type: PersonManager
    try:
        person = await manager.async_create_person(
            name=msg['name'],
            user_id=msg.get('user_id'),
            device_trackers=msg['device_trackers'])
        connection.send_result(msg['id'], person)
    except ValueError as err:
        connection.send_error(msg['id'],
                              websocket_api.const.ERR_INVALID_FORMAT, str(err))
Beispiel #10
0
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:
        connection.send_error(msg["id"], "invalid_token_id",
                              "Received invalid token")
        return

    await hass.auth.async_remove_refresh_token(refresh_token)

    connection.send_result(msg["id"], {})
Beispiel #11
0
    async def ws_delete_item(
        self, hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
    ) -> None:
        """Delete a item."""
        try:
            await self.storage_collection.async_delete_item(msg[self.item_id_key])
        except ItemNotFound:
            connection.send_error(
                msg["id"],
                websocket_api.const.ERR_NOT_FOUND,
                f"Unable to find {self.item_id_key} {msg[self.item_id_key]}",
            )

        connection.send_result(msg["id"])
Beispiel #12
0
async def ws_create_person(hass: HomeAssistantType,
                           connection: websocket_api.ActiveConnection, msg):
    """Create a person."""
    manager = hass.data[DOMAIN]  # type: PersonManager
    try:
        person = await manager.async_create_person(
            name=msg['name'],
            user_id=msg.get('user_id'),
            device_trackers=msg['device_trackers']
        )
        connection.send_result(msg['id'], person)
    except ValueError as err:
        connection.send_error(
            msg['id'], websocket_api.const.ERR_INVALID_FORMAT, str(err))
Beispiel #13
0
async def ws_update_person(hass: HomeAssistantType,
                           connection: websocket_api.ActiveConnection, msg):
    """Update a person."""
    manager = hass.data[DOMAIN]  # type: PersonManager
    changes = {}
    for key in ('name', 'user_id', 'device_trackers'):
        if key in msg:
            changes[key] = msg[key]

    try:
        person = await manager.async_update_person(msg['person_id'], **changes)
        connection.send_result(msg['id'], person)
    except ValueError as err:
        connection.send_error(
            msg['id'], websocket_api.const.ERR_INVALID_FORMAT, str(err))
Beispiel #14
0
async def ws_camera_stream(hass: HomeAssistant, connection: ActiveConnection,
                           msg: dict) -> None:
    """Handle get camera stream websocket command.

    Async friendly.
    """
    try:
        entity_id = msg["entity_id"]
        camera = _get_camera_from_entity_id(hass, entity_id)
        url = await _async_stream_endpoint_url(hass, camera, fmt=msg["format"])
        connection.send_result(msg["id"], {"url": url})
    except HomeAssistantError as ex:
        _LOGGER.error("Error requesting stream: %s", ex)
        connection.send_error(msg["id"], "start_stream_failed", str(ex))
    except asyncio.TimeoutError:
        _LOGGER.error("Timeout getting stream source")
        connection.send_error(msg["id"], "start_stream_failed",
                              "Timeout getting stream source")
Beispiel #15
0
async def websocket_resolve_media(
    hass: HomeAssistant, connection: ActiveConnection, msg: dict
) -> None:
    """Resolve media."""
    try:
        media = await async_resolve_media(hass, msg["media_content_id"])
        url = media.url
    except Unresolvable as err:
        connection.send_error(msg["id"], "resolve_media_failed", str(err))
    else:
        if url[0] == "/":
            url = async_sign_path(
                hass,
                quote(url),
                timedelta(seconds=msg["expires"]),
            )

        connection.send_result(msg["id"], {"url": url, "mime_type": media.mime_type})
Beispiel #16
0
def handle_get(hass: HomeAssistant, connection: websocket_api.ActiveConnection,
               msg: dict):
    """List all possible diagnostic handlers."""
    domain = msg["domain"]
    info = hass.data[DOMAIN].get(domain)

    if info is None:
        connection.send_error(msg["id"], websocket_api.ERR_NOT_FOUND,
                              "Domain not supported")
        return

    connection.send_result(
        msg["id"],
        {
            "domain": domain,
            "handlers": {key: val is not None
                         for key, val in info.items()},
        },
    )
Beispiel #17
0
async def websocket_resolve_media(
    hass: HomeAssistant, connection: ActiveConnection, msg: dict
) -> None:
    """Resolve media."""
    try:
        media = await async_resolve_media(hass, msg["media_content_id"])
    except Unresolvable as err:
        connection.send_error(msg["id"], "resolve_media_failed", str(err))
        return

    connection.send_result(
        msg["id"],
        {
            "url": async_process_play_media_url(
                hass, media.url, allow_relative_url=True
            ),
            "mime_type": media.mime_type,
        },
    )
Beispiel #18
0
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:
        connection.send_error(msg["id"], websocket_api.const.ERR_UNAUTHORIZED,
                              str(exc))
        return

    connection.send_result(msg["id"], access_token)
Beispiel #19
0
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'))
Beispiel #20
0
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'))
Beispiel #21
0
 async def ws_create_item(
     self, hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
 ) -> None:
     """Create a item."""
     try:
         data = dict(msg)
         data.pop("id")
         data.pop("type")
         item = await self.storage_collection.async_create_item(data)
         connection.send_result(msg["id"], item)
     except vol.Invalid as err:
         connection.send_error(
             msg["id"],
             websocket_api.const.ERR_INVALID_FORMAT,
             humanize_error(data, err),
         )
     except ValueError as err:
         connection.send_error(
             msg["id"], websocket_api.const.ERR_INVALID_FORMAT, str(err)
         )
Beispiel #22
0
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))
Beispiel #23
0
async def websocket_resolve_media(hass: HomeAssistant,
                                  connection: ActiveConnection,
                                  msg: dict) -> None:
    """Resolve media."""
    try:
        media = await async_resolve_media(hass, msg["media_content_id"])
    except Unresolvable as err:
        connection.send_error(msg["id"], "resolve_media_failed", str(err))
        return

    data = dataclasses.asdict(media)

    if data["url"][0] == "/":
        data["url"] = async_sign_path(
            hass,
            quote(data["url"]),
            timedelta(seconds=msg["expires"]),
        )

    connection.send_result(msg["id"], data)
Beispiel #24
0
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))
Beispiel #25
0
    async def ws_update_item(
        self, hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
    ) -> None:
        """Update a item."""
        data = dict(msg)
        msg_id = data.pop("id")
        item_id = data.pop(self.item_id_key)
        data.pop("type")

        try:
            item = await self.storage_collection.async_update_item(item_id, data)
            connection.send_result(msg_id, item)
        except ItemNotFound:
            connection.send_error(
                msg["id"],
                websocket_api.const.ERR_NOT_FOUND,
                f"Unable to find {self.item_id_key} {item_id}",
            )
        except vol.Invalid as err:
            connection.send_error(
                msg["id"],
                websocket_api.const.ERR_INVALID_FORMAT,
                humanize_error(data, err),
            )
        except ValueError as err:
            connection.send_error(
                msg_id, websocket_api.const.ERR_INVALID_FORMAT, str(err)
            )
Beispiel #26
0
async def ws_camera_web_rtc_offer(hass: HomeAssistant,
                                  connection: ActiveConnection,
                                  msg: dict) -> None:
    """Handle the signal path for a WebRTC stream.

    This signal path is used to route the offer created by the client to the
    camera device through the integration for negitioation on initial setup,
    which returns an answer. The actual streaming is handled entirely between
    the client and camera device.

    Async friendly.
    """
    entity_id = msg["entity_id"]
    offer = msg["offer"]
    camera = _get_camera_from_entity_id(hass, entity_id)
    if camera.frontend_stream_type != STREAM_TYPE_WEB_RTC:
        connection.send_error(
            msg["id"],
            "web_rtc_offer_failed",
            f"Camera does not support WebRTC, frontend_stream_type={camera.frontend_stream_type}",
        )
        return
    try:
        answer = await camera.async_handle_web_rtc_offer(offer)
    except (HomeAssistantError, ValueError) as ex:
        _LOGGER.error("Error handling WebRTC offer: %s", ex)
        connection.send_error(msg["id"], "web_rtc_offer_failed", str(ex))
    except asyncio.TimeoutError:
        _LOGGER.error("Timeout handling WebRTC offer")
        connection.send_error(msg["id"], "web_rtc_offer_failed",
                              "Timeout handling WebRTC offer")
    else:
        connection.send_result(msg["id"], {"answer": answer})
Beispiel #27
0
async def websocket_remove_media(hass: HomeAssistant,
                                 connection: websocket_api.ActiveConnection,
                                 msg: dict) -> None:
    """Remove media."""
    try:
        item = MediaSourceItem.from_uri(hass, msg["media_content_id"])
    except ValueError as err:
        connection.send_error(msg["id"], websocket_api.ERR_INVALID_FORMAT,
                              str(err))
        return

    source: LocalSource = hass.data[DOMAIN][DOMAIN]

    try:
        source_dir_id, location = source.async_parse_identifier(item)
    except Unresolvable as err:
        connection.send_error(msg["id"], websocket_api.ERR_INVALID_FORMAT,
                              str(err))
        return

    item_path = source.async_full_path(source_dir_id, location)

    def _do_delete() -> tuple[str, str] | None:
        if not item_path.exists():
            return websocket_api.ERR_NOT_FOUND, "Path does not exist"

        if not item_path.is_file():
            return websocket_api.ERR_NOT_SUPPORTED, "Path is not a file"

        item_path.unlink()
        return None

    try:
        error = await hass.async_add_executor_job(_do_delete)
    except OSError as err:
        error = (websocket_api.ERR_UNKNOWN_ERROR, str(err))

    if error:
        connection.send_error(msg["id"], *error)
    else:
        connection.send_result(msg["id"])