From 73ef03e33f1eeaaa8edf3ac6298ee3b94b2ab19a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 29 Apr 2019 11:27:40 -0700 Subject: [PATCH] Use signed path for camera snapshot (#3138) --- .../snapshot/dialog-hassio-snapshot.ts | 2 +- src/auth/data.ts | 7 ----- src/auth/types.ts | 3 -- src/cards/ha-camera-card.js | 27 ++++++++++-------- src/data/auth.ts | 11 ++++++++ src/data/camera.ts | 22 ++++++++++++--- src/panels/lovelace/components/hui-image.ts | 28 +++++++++---------- 7 files changed, 58 insertions(+), 42 deletions(-) delete mode 100644 src/auth/data.ts delete mode 100644 src/auth/types.ts diff --git a/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts b/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts index 6ced46ca76..3d1cc54cdd 100644 --- a/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts +++ b/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts @@ -6,7 +6,7 @@ import "@polymer/paper-icon-button/paper-icon-button"; import "@polymer/paper-input/paper-input"; import { html } from "@polymer/polymer/lib/utils/html-tag"; import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { getSignedPath } from "../../../../src/auth/data"; +import { getSignedPath } from "../../../../src/data/auth"; import "../../../../src/resources/ha-style"; import "../../../../src/components/dialog/ha-paper-dialog"; diff --git a/src/auth/data.ts b/src/auth/data.ts deleted file mode 100644 index f970e90d52..0000000000 --- a/src/auth/data.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { HomeAssistant } from "../types"; -import { SignedPath } from "./types"; - -export const getSignedPath = ( - hass: HomeAssistant, - path: string -): Promise => hass.callWS({ type: "auth/sign_path", path }); diff --git a/src/auth/types.ts b/src/auth/types.ts deleted file mode 100644 index bb1b00180a..0000000000 --- a/src/auth/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface SignedPath { - path: string; -} diff --git a/src/cards/ha-camera-card.js b/src/cards/ha-camera-card.js index c26b60b72c..c59f847429 100644 --- a/src/cards/ha-camera-card.js +++ b/src/cards/ha-camera-card.js @@ -5,6 +5,7 @@ import { PolymerElement } from "@polymer/polymer/polymer-element"; import computeStateName from "../common/entity/compute_state_name"; import EventsMixin from "../mixins/events-mixin"; import LocalizeMixin from "../mixins/localize-mixin"; +import { fetchThumbnailUrlWithCache } from "../data/camera"; const UPDATE_INTERVAL = 10000; // ms /* @@ -54,6 +55,8 @@ class HaCameraCard extends LocalizeMixin(EventsMixin(PolymerElement)) { src="[[cameraFeedSrc]]" class="camera-feed" alt="[[_computeStateName(stateObj)]]" + on-load="_imageLoaded" + on-error="_imageError" />
@@ -98,23 +101,23 @@ class HaCameraCard extends LocalizeMixin(EventsMixin(PolymerElement)) { clearInterval(this.timer); } + _imageLoaded() { + this.imageLoaded = true; + } + + _imageError() { + this.imageLoaded = false; + } + cardTapped() { this.fire("hass-more-info", { entityId: this.stateObj.entity_id }); } async updateCameraFeedSrc() { - try { - const { content_type: contentType, content } = await this.hass.callWS({ - type: "camera_thumbnail", - entity_id: this.stateObj.entity_id, - }); - this.setProperties({ - imageLoaded: true, - cameraFeedSrc: `data:${contentType};base64, ${content}`, - }); - } catch (err) { - this.imageLoaded = false; - } + this.cameraFeedSrc = await fetchThumbnailUrlWithCache( + this.hass, + this.stateObj.entity_id + ); } _computeStateName(stateObj) { diff --git a/src/data/auth.ts b/src/data/auth.ts index 92a44130e2..0c5d1a9493 100644 --- a/src/data/auth.ts +++ b/src/data/auth.ts @@ -1,3 +1,5 @@ +import { HomeAssistant } from "../types"; + export interface AuthProvider { name: string; id: string; @@ -7,3 +9,12 @@ export interface AuthProvider { export interface Credential { type: string; } + +export interface SignedPath { + path: string; +} + +export const getSignedPath = ( + hass: HomeAssistant, + path: string +): Promise => hass.callWS({ type: "auth/sign_path", path }); diff --git a/src/data/camera.ts b/src/data/camera.ts index 62ff707130..ab40da3dd8 100644 --- a/src/data/camera.ts +++ b/src/data/camera.ts @@ -1,5 +1,6 @@ import { HomeAssistant, CameraEntity } from "../types"; import { timeCachePromiseFunc } from "../common/util/time-cache-function-promise"; +import { getSignedPath } from "./auth"; export const CAMERA_SUPPORT_ON_OFF = 1; export const CAMERA_SUPPORT_STREAM = 2; @@ -22,16 +23,29 @@ export const computeMJPEGStreamUrl = (entity: CameraEntity) => entity.attributes.access_token }`; -export const fetchThumbnailWithCache = ( +export const fetchThumbnailUrlWithCache = ( hass: HomeAssistant, entityId: string -) => timeCachePromiseFunc("_cameraTmb", 9000, fetchThumbnail, hass, entityId); +) => + timeCachePromiseFunc( + "_cameraTmbUrl", + 9000, + fetchThumbnailUrl, + hass, + entityId + ); -export const fetchThumbnail = (hass: HomeAssistant, entityId: string) => - hass.callWS({ +export const fetchThumbnailUrl = (hass: HomeAssistant, entityId: string) => + getSignedPath(hass, `/api/camera_proxy/${entityId}`).then(({ path }) => path); + +export const fetchThumbnail = (hass: HomeAssistant, entityId: string) => { + // tslint:disable-next-line: no-console + console.warn("This method has been deprecated."); + return hass.callWS({ type: "camera_thumbnail", entity_id: entityId, }); +}; export const fetchStreamUrl = ( hass: HomeAssistant, diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index 0d5ad799d7..870ff7b51c 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -17,8 +17,7 @@ import { import { HomeAssistant, CameraEntity } from "../../../types"; import { styleMap } from "lit-html/directives/style-map"; import { classMap } from "lit-html/directives/class-map"; -import { b64toBlob } from "../../../common/file/b64-to-blob"; -import { fetchThumbnailWithCache } from "../../../data/camera"; +import { fetchThumbnailUrlWithCache } from "../../../data/camera"; const UPDATE_INTERVAL = 10000; const DEFAULT_FILTER = "grayscale(100%)"; @@ -197,21 +196,20 @@ export class HuiImage extends LitElement { if (!this.hass || !this.cameraImage) { return; } - try { - const { - content_type: contentType, - content, - } = await fetchThumbnailWithCache(this.hass, this.cameraImage); - if (this._cameraImageSrc) { - URL.revokeObjectURL(this._cameraImageSrc); - } - this._cameraImageSrc = URL.createObjectURL( - b64toBlob(content, contentType) - ); - this._onImageLoad(); - } catch (err) { + + const cameraState = this.hass.states[this.cameraImage] as + | CameraEntity + | undefined; + + if (!cameraState) { this._onImageError(); + return; } + + this._cameraImageSrc = await fetchThumbnailUrlWithCache( + this.hass, + this.cameraImage + ); } static get styles(): CSSResult {