mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 09:16:38 +00:00
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:
parent
f4319d9b13
commit
56e3514e40
@ -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,
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -43,6 +43,7 @@ class MoreInfoContent extends UpdatingElement {
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
this.style.position = "relative";
|
||||
this.style.display = "block";
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user