Allow changing camera prefs (#3035)

* Check camera supported_features before streaming

* Allow mutating camera prefs

* Move when we fetch prefs
This commit is contained in:
Paulus Schoutsen 2019-03-29 16:43:32 -07:00 committed by GitHub
parent f4319d9b13
commit 56e3514e40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 120 additions and 10 deletions

View File

@ -1,6 +1,13 @@
import { HomeAssistant, CameraEntity } from "../types";
import { timeCachePromiseFunc } from "../common/util/time-cache-function-promise";
export const CAMERA_SUPPORT_ON_OFF = 1;
export const CAMERA_SUPPORT_STREAM = 2;
export interface CameraPreferences {
preload_stream: boolean;
}
export interface CameraThumbnail {
content_type: string;
content: string;
@ -41,3 +48,22 @@ export const fetchStreamUrl = (
}
return hass.callWS<Stream>(data);
};
export const fetchCameraPrefs = (hass: HomeAssistant, entityId: string) =>
hass.callWS<CameraPreferences>({
type: "camera/get_prefs",
entity_id: entityId,
});
export const updateCameraPrefs = (
hass: HomeAssistant,
entityId: string,
prefs: {
preload_stream?: boolean;
}
) =>
hass.callWS<CameraPreferences>({
type: "camera/update_prefs",
entity_id: entityId,
...prefs,
});

View File

@ -1,15 +1,36 @@
import { property, UpdatingElement, PropertyValues } from "lit-element";
import {
property,
PropertyValues,
LitElement,
TemplateResult,
html,
CSSResult,
css,
} from "lit-element";
import computeStateName from "../../../common/entity/compute_state_name";
import { HomeAssistant, CameraEntity } from "../../../types";
import { fireEvent } from "../../../common/dom/fire_event";
import { fetchStreamUrl, computeMJPEGStreamUrl } from "../../../data/camera";
import {
fetchStreamUrl,
computeMJPEGStreamUrl,
CAMERA_SUPPORT_STREAM,
CameraPreferences,
fetchCameraPrefs,
updateCameraPrefs,
} from "../../../data/camera";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "@polymer/paper-checkbox/paper-checkbox";
// Not duplicate import, it's for typing
// tslint:disable-next-line
import { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
type HLSModule = typeof import("hls.js");
class MoreInfoCamera extends UpdatingElement {
class MoreInfoCamera extends LitElement {
@property() public hass?: HomeAssistant;
@property() public stateObj?: CameraEntity;
@property() private _cameraPrefs?: CameraPreferences;
private _hlsPolyfillInstance?: Hls;
public disconnectedCallback() {
@ -17,6 +38,22 @@ class MoreInfoCamera extends UpdatingElement {
this._teardownPlayback();
}
protected render(): TemplateResult | void {
return html`
<div id="root"></div>
${this._cameraPrefs
? html`
<paper-checkbox
.checked=${this._cameraPrefs.preload_stream}
@change=${this._handleCheckboxChanged}
>
Preload stream
</paper-checkbox>
`
: undefined}
`;
}
protected updated(changedProps: PropertyValues) {
if (!changedProps.has("stateObj")) {
return;
@ -46,7 +83,10 @@ class MoreInfoCamera extends UpdatingElement {
return;
}
if (!this.hass!.config.components.includes("stream")) {
if (
!this.hass!.config.components.includes("stream") ||
!supportsFeature(this.stateObj, CAMERA_SUPPORT_STREAM)
) {
this._renderMJPEG();
return;
}
@ -73,6 +113,8 @@ class MoreInfoCamera extends UpdatingElement {
this.hass!,
this.stateObj.entity_id
);
// Fetch in background while we set up the video.
this._fetchCameraPrefs();
if (Hls.isSupported()) {
this._renderHLSPolyfill(videoEl, Hls, url);
@ -81,16 +123,20 @@ class MoreInfoCamera extends UpdatingElement {
}
return;
} catch (err) {
// Fails if entity doesn't support it. In that case we go
// for mjpeg.
// When an error happens, we will do nothing so we render mjpeg.
}
}
this._renderMJPEG();
}
private get _videoRoot(): HTMLDivElement {
return this.shadowRoot!.getElementById("root")! as HTMLDivElement;
}
private async _renderHLSNative(videoEl: HTMLVideoElement, url: string) {
videoEl.src = url;
this._videoRoot.appendChild(videoEl);
await new Promise((resolve) =>
videoEl.addEventListener("loadedmetadata", resolve)
);
@ -110,7 +156,7 @@ class MoreInfoCamera extends UpdatingElement {
hls.attachMedia(videoEl);
});
hls.loadSource(url);
this.appendChild(videoEl);
this._videoRoot.appendChild(videoEl);
videoEl.addEventListener("loadeddata", () =>
fireEvent(this, "iron-resize")
);
@ -124,7 +170,7 @@ class MoreInfoCamera extends UpdatingElement {
? "/demo/webcamp.jpg"
: computeMJPEGStreamUrl(this.stateObj!);
img.alt = computeStateName(this.stateObj!);
this.appendChild(img);
this._videoRoot.appendChild(img);
}
private _teardownPlayback(): any {
@ -132,11 +178,48 @@ class MoreInfoCamera extends UpdatingElement {
this._hlsPolyfillInstance.destroy();
this._hlsPolyfillInstance = undefined;
}
while (this.lastChild) {
this.removeChild(this.lastChild);
const root = this._videoRoot;
while (root.lastChild) {
root.removeChild(root.lastChild);
}
this.stateObj = undefined;
}
private async _fetchCameraPrefs() {
this._cameraPrefs = await fetchCameraPrefs(
this.hass!,
this.stateObj!.entity_id
);
}
private async _handleCheckboxChanged(ev) {
const checkbox = ev.currentTarget as PaperCheckboxElement;
try {
this._cameraPrefs = await updateCameraPrefs(
this.hass!,
this.stateObj!.entity_id,
{
preload_stream: checkbox.checked!,
}
);
} catch (err) {
alert(err.message);
checkbox.checked = !checkbox.checked;
}
}
static get styles(): CSSResult {
return css`
paper-checkbox {
position: absolute;
top: 0;
right: 0;
background-color: var(--secondary-background-color);
padding: 5px;
border-bottom-left-radius: 6px;
}
`;
}
}
customElements.define("more-info-camera", MoreInfoCamera);

View File

@ -43,6 +43,7 @@ class MoreInfoContent extends UpdatingElement {
}
protected firstUpdated(): void {
this.style.position = "relative";
this.style.display = "block";
}