Split js parts
This commit is contained in:
23
js/plugin/browser-player-editor.ts
Normal file
23
js/plugin/browser-player-editor.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { hass_loaded } from "card-tools/src/hass";
|
||||
|
||||
class BrowserPlayerEditor extends LitElement {
|
||||
setConfig(config) {}
|
||||
render() {
|
||||
return html` <div>Nothing to configure.</div> `;
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await hass_loaded();
|
||||
|
||||
if (!customElements.get("browser-player-editor")) {
|
||||
customElements.define("browser-player-editor", BrowserPlayerEditor);
|
||||
window.customCards = window.customCards || [];
|
||||
window.customCards.push({
|
||||
type: "browser-player",
|
||||
name: "Browser Player",
|
||||
preview: true,
|
||||
});
|
||||
}
|
||||
})();
|
||||
140
js/plugin/browser-player.ts
Normal file
140
js/plugin/browser-player.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { LitElement, html, css } from "lit";
|
||||
import { property } from "lit/decorators.js";
|
||||
|
||||
import { deviceID, setDeviceID } from "card-tools/src/deviceID";
|
||||
import { fireEvent } from "card-tools/src/event";
|
||||
import { hass_loaded } from "card-tools/src/hass";
|
||||
|
||||
import "./browser-player-editor.ts";
|
||||
|
||||
import "./types";
|
||||
|
||||
class BrowserPlayer extends LitElement {
|
||||
@property() hass;
|
||||
|
||||
static getConfigElement() {
|
||||
return document.createElement("browser-player-editor");
|
||||
}
|
||||
static getStubConfig() {
|
||||
return {};
|
||||
}
|
||||
|
||||
async setConfig(config) {
|
||||
while (!window.browser_mod) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
for (const event of [
|
||||
"play",
|
||||
"pause",
|
||||
"ended",
|
||||
"volumechange",
|
||||
"canplay",
|
||||
"loadeddata",
|
||||
])
|
||||
window.browser_mod.player.addEventListener(event, () =>
|
||||
this.requestUpdate()
|
||||
);
|
||||
}
|
||||
handleMute(ev) {
|
||||
window.browser_mod.player_mute();
|
||||
}
|
||||
handleVolumeChange(ev) {
|
||||
const vol = parseFloat(ev.target.value);
|
||||
window.browser_mod.player_set_volume(vol);
|
||||
}
|
||||
handleMoreInfo(ev) {
|
||||
fireEvent(
|
||||
"hass-more-info",
|
||||
{ entityId: `media_player.${window.browser_mod.entity_id}` },
|
||||
this
|
||||
);
|
||||
}
|
||||
handlePlayPause(ev) {
|
||||
if (window.browser_mod.player.paused) window.browser_mod.player_play();
|
||||
else window.browser_mod.player_pause();
|
||||
}
|
||||
setDeviceID() {
|
||||
const newID = prompt("Set deviceID", deviceID);
|
||||
if (newID !== deviceID) {
|
||||
setDeviceID(newID);
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!window.browser_mod) {
|
||||
window.setTimeout(() => this.requestUpdate(), 100);
|
||||
return html``;
|
||||
}
|
||||
const player = window.browser_mod.player;
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<ha-icon-button @click=${this.handleMute}>
|
||||
<ha-icon
|
||||
.icon=${player.muted ? "mdi:volume-off" : "mdi:volume-high"}
|
||||
></ha-icon>
|
||||
</ha-icon-button>
|
||||
<ha-slider
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
?disabled=${player.muted}
|
||||
value=${player.volume}
|
||||
@change=${this.handleVolumeChange}
|
||||
></ha-slider>
|
||||
|
||||
${window.browser_mod.player_state === "stopped"
|
||||
? html`<div class="placeholder"></div>`
|
||||
: html`
|
||||
<ha-icon-button @click=${this.handlePlayPause} highlight>
|
||||
<ha-icon
|
||||
.icon=${player.paused ? "mdi:play" : "mdi:pause"}
|
||||
></ha-icon>
|
||||
</ha-icon-button>
|
||||
`}
|
||||
<ha-icon-button @click=${this.handleMoreInfo}>
|
||||
<ha-icon .icon=${"mdi:cog"}></ha-icon>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
|
||||
<div class="device-id" @click=${this.setDeviceID}>${deviceID}</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
paper-icon-button[highlight] {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.placeholder {
|
||||
width: 24px;
|
||||
padding: 8px;
|
||||
}
|
||||
.device-id {
|
||||
opacity: 0.7;
|
||||
font-size: xx-small;
|
||||
margin-top: -10px;
|
||||
user-select: all;
|
||||
-webkit-user-select: all;
|
||||
-moz-user-select: all;
|
||||
-ms-user-select: all;
|
||||
}
|
||||
ha-icon-button ha-icon {
|
||||
display: flex;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await hass_loaded();
|
||||
|
||||
if (!customElements.get("browser-player"))
|
||||
customElements.define("browser-player", BrowserPlayer);
|
||||
})();
|
||||
43
js/plugin/browser.ts
Normal file
43
js/plugin/browser.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { fireEvent } from "card-tools/src/event";
|
||||
import { ha_element } from "card-tools/src/hass";
|
||||
|
||||
export const BrowserModBrowserMixin = (C) =>
|
||||
class extends C {
|
||||
constructor() {
|
||||
super();
|
||||
document.addEventListener("visibilitychange", () => this.sensor_update());
|
||||
window.addEventListener("location-changed", () => this.sensor_update());
|
||||
|
||||
window.setInterval(() => this.sensor_update(), 10000);
|
||||
}
|
||||
|
||||
sensor_update() {
|
||||
const update = async () => {
|
||||
const battery = (<any>navigator).getBattery?.();
|
||||
this.sendUpdate({
|
||||
browser: {
|
||||
path: window.location.pathname,
|
||||
visibility: document.visibilityState,
|
||||
userAgent: navigator.userAgent,
|
||||
currentUser: this.hass?.user?.name,
|
||||
fullyKiosk: this.isFully,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
battery_level:
|
||||
window.fully?.getBatteryLevel() ?? battery?.level * 100,
|
||||
charging: window.fully?.isPlugged() ?? battery?.charging,
|
||||
darkMode: this.hass?.themes?.darkMode,
|
||||
userData: this.hass?.user,
|
||||
config: this.config,
|
||||
},
|
||||
});
|
||||
};
|
||||
update();
|
||||
}
|
||||
|
||||
do_navigate(path) {
|
||||
if (!path) return;
|
||||
history.pushState(null, "", path);
|
||||
fireEvent("location-changed", {}, ha_element());
|
||||
}
|
||||
};
|
||||
63
js/plugin/camera.ts
Normal file
63
js/plugin/camera.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
export const BrowserModCameraMixin = (C) =>
|
||||
class extends C {
|
||||
setup_camera() {
|
||||
console.log("Starting camera");
|
||||
|
||||
if (this._video) return;
|
||||
this._video = document.createElement("video");
|
||||
this._video.autoplay = true;
|
||||
this._video.playsInline = true;
|
||||
this._video.style.display = "none";
|
||||
|
||||
this._canvas = document.createElement("canvas");
|
||||
this._canvas.style.display = "none";
|
||||
|
||||
document.body.appendChild(this._video);
|
||||
document.body.appendChild(this._canvas);
|
||||
|
||||
if (!navigator.mediaDevices) return;
|
||||
|
||||
console.log("Starting devices");
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({ video: true, audio: false })
|
||||
.then((stream) => {
|
||||
this._video.srcObject = stream;
|
||||
this._video.play();
|
||||
this.update_camera();
|
||||
});
|
||||
|
||||
this._camera_framerate = 2;
|
||||
|
||||
window.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (this._video.ended || this._video.paused) this._video.play();
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
update_camera() {
|
||||
this._canvas.width = this._video.videoWidth;
|
||||
this._canvas.height = this._video.videoHeight;
|
||||
|
||||
const context = this._canvas.getContext("2d");
|
||||
context.drawImage(
|
||||
this._video,
|
||||
0,
|
||||
0,
|
||||
this._video.videoWidth,
|
||||
this._video.videoHeight
|
||||
);
|
||||
|
||||
this.sendUpdate({
|
||||
camera: this._canvas.toDataURL("image/jpeg"),
|
||||
});
|
||||
setTimeout(
|
||||
() => this.update_camera(),
|
||||
Math.round(1000 / this._camera_framerate)
|
||||
);
|
||||
}
|
||||
};
|
||||
42
js/plugin/connection.ts
Normal file
42
js/plugin/connection.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { deviceID } from "card-tools/src/deviceID";
|
||||
import { hass, provideHass } from "card-tools/src/hass";
|
||||
|
||||
export class BrowserModConnection {
|
||||
hass;
|
||||
connection;
|
||||
|
||||
async connect() {
|
||||
const isCast = document.querySelector("hc-main") !== null;
|
||||
if (!isCast) {
|
||||
while (!window.hassConnection)
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 100));
|
||||
this.connection = (await window.hassConnection).conn;
|
||||
} else {
|
||||
this.connection = hass().connection;
|
||||
}
|
||||
|
||||
this.connection.subscribeMessage((msg) => this.msg_callback(msg), {
|
||||
type: "browser_mod/connect",
|
||||
deviceID: deviceID,
|
||||
});
|
||||
|
||||
provideHass(this);
|
||||
}
|
||||
|
||||
get connected() {
|
||||
return this.connection !== undefined;
|
||||
}
|
||||
|
||||
msg_callback(message) {
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
sendUpdate(data) {
|
||||
if (!this.connected) return;
|
||||
this.connection.sendMessage({
|
||||
type: "browser_mod/update",
|
||||
deviceID,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
94
js/plugin/fullyKiosk.ts
Normal file
94
js/plugin/fullyKiosk.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
export const FullyKioskMixin = (C) =>
|
||||
class extends C {
|
||||
get isFully() {
|
||||
return window.fully !== undefined;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (!this.isFully) return;
|
||||
|
||||
this._fullyMotion = false;
|
||||
this._motionTimeout = undefined;
|
||||
|
||||
for (const ev of [
|
||||
"screenOn",
|
||||
"screenOff",
|
||||
"pluggedAC",
|
||||
"pluggedUSB",
|
||||
"onBatteryLevelChanged",
|
||||
"unplugged",
|
||||
"networkReconnect",
|
||||
"onMotion",
|
||||
]) {
|
||||
window.fully.bind(ev, `window.browser_mod.fully_update("${ev}");`);
|
||||
}
|
||||
|
||||
window.fully.bind(
|
||||
"onScreensaverStart",
|
||||
`window.browser_mod.fully_screensaver = true; window.browser_mod.screen_update();`
|
||||
);
|
||||
window.fully.bind(
|
||||
"onScreensaverStop",
|
||||
`window.browser_mod.fully_screensaver = false; window.browser_mod.screen_update();`
|
||||
);
|
||||
|
||||
this._keepingAlive = false;
|
||||
}
|
||||
|
||||
fully_update(event?) {
|
||||
if (!this.isFully) return;
|
||||
if (event === "screenOn") {
|
||||
window.clearTimeout(this._keepAliveTimer);
|
||||
if (!this._keepingAlive) this.screen_update();
|
||||
} else if (event === "screenOff") {
|
||||
this.screen_update();
|
||||
this._keepingAlive = false;
|
||||
if (this.config.force_stay_awake) {
|
||||
this._keepAliveTimer = window.setTimeout(() => {
|
||||
this._keepingAlive = true;
|
||||
window.fully.turnScreenOn();
|
||||
window.fully.turnScreenOff();
|
||||
}, 270000);
|
||||
}
|
||||
} else if (event === "onMotion") {
|
||||
this.fullyMotionTriggered();
|
||||
}
|
||||
|
||||
this.sendUpdate({
|
||||
fully: {
|
||||
battery: window.fully.getBatteryLevel(),
|
||||
charging: window.fully.isPlugged(),
|
||||
motion: this._fullyMotion,
|
||||
ip: window.fully.getIp4Address(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
startCamera() {
|
||||
if (this._fullyCameraTimer !== undefined) return;
|
||||
this._fullyCameraTimer = window.setInterval(() => {
|
||||
this.sendUpdate({
|
||||
camera: window.fully.getCamshotJpgBase64(),
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
stopCamera() {
|
||||
window.clearInterval(this._fullyCameraTimer);
|
||||
this._fullyCameraTimer = undefined;
|
||||
}
|
||||
|
||||
fullyMotionTriggered() {
|
||||
if (this._keepingAlive) return;
|
||||
this._fullyMotion = true;
|
||||
this.startCamera();
|
||||
clearTimeout(this._motionTimeout);
|
||||
this._motionTimeout = setTimeout(() => {
|
||||
this._fullyMotion = false;
|
||||
this.stopCamera();
|
||||
this.fully_update();
|
||||
}, 5000);
|
||||
this.fully_update();
|
||||
}
|
||||
};
|
||||
145
js/plugin/main.ts
Normal file
145
js/plugin/main.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { deviceID } from "card-tools/src/deviceID";
|
||||
import { lovelace_view } from "card-tools/src/hass";
|
||||
import { popUp } from "card-tools/src/popup";
|
||||
import { fireEvent } from "card-tools/src/event";
|
||||
import { ha_element, hass_loaded } from "card-tools/src/hass";
|
||||
import "./browser-player";
|
||||
|
||||
import { BrowserModConnection } from "./connection";
|
||||
import { BrowserModMediaPlayerMixin } from "./mediaPlayer";
|
||||
import { FullyKioskMixin } from "./fullyKiosk";
|
||||
import { BrowserModCameraMixin } from "./camera";
|
||||
import { BrowserModScreensaverMixin } from "./screensaver";
|
||||
import { BrowserModPopupsMixin } from "./popups";
|
||||
import { BrowserModBrowserMixin } from "./browser";
|
||||
import pjson from "../../package.json";
|
||||
|
||||
const ext = (baseClass, mixins) =>
|
||||
mixins.reduceRight((base, mixin) => mixin(base), baseClass);
|
||||
|
||||
export class BrowserMod extends ext(BrowserModConnection, [
|
||||
BrowserModBrowserMixin,
|
||||
BrowserModPopupsMixin,
|
||||
BrowserModScreensaverMixin,
|
||||
BrowserModCameraMixin,
|
||||
FullyKioskMixin,
|
||||
BrowserModMediaPlayerMixin,
|
||||
]) {
|
||||
constructor() {
|
||||
super();
|
||||
this.entity_id = deviceID.replace("-", "_");
|
||||
this.cast = document.querySelector("hc-main") !== null;
|
||||
this.connect();
|
||||
|
||||
document.body.addEventListener("ll-custom", (ev) => {
|
||||
if ((ev as CustomEvent).detail.browser_mod) {
|
||||
this.msg_callback((ev as CustomEvent).detail.browser_mod);
|
||||
}
|
||||
});
|
||||
|
||||
console.info(
|
||||
`%cBROWSER_MOD ${pjson.version} IS INSTALLED
|
||||
%cDeviceID: ${deviceID}`,
|
||||
"color: green; font-weight: bold",
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
async msg_callback(msg) {
|
||||
const handlers = {
|
||||
update: (msg) => this.update(msg),
|
||||
debug: (msg) => this.debug(msg),
|
||||
|
||||
play: (msg) => this.player_play(msg.media_content_id),
|
||||
pause: (msg) => this.player_pause(),
|
||||
stop: (msg) => this.player_stop(),
|
||||
"set-volume": (msg) => this.player_set_volume(msg.volume_level),
|
||||
mute: (msg) => this.player_mute(msg.mute),
|
||||
|
||||
toast: (msg) => this.do_toast(msg.message, msg.duration),
|
||||
popup: (msg) => this.do_popup(msg),
|
||||
"close-popup": (msg) => this.do_close_popup(),
|
||||
"more-info": (msg) => this.do_more_info(msg.entity_id, msg.large),
|
||||
|
||||
navigate: (msg) => this.do_navigate(msg.navigation_path),
|
||||
"set-theme": (msg) => this.set_theme(msg),
|
||||
"lovelace-reload": (msg) => this.lovelace_reload(msg),
|
||||
"window-reload": () => window.location.reload(),
|
||||
|
||||
blackout: (msg) =>
|
||||
this.do_blackout(msg.time ? parseInt(msg.time) : undefined),
|
||||
"no-blackout": (msg) => {
|
||||
if (msg.brightness && this.isFully) {
|
||||
(window as any).fully.setScreenBrightness(msg.brightness);
|
||||
}
|
||||
this.no_blackout();
|
||||
},
|
||||
|
||||
"call-service": (msg) => this.call_service(msg),
|
||||
commands: async (msg) => {
|
||||
for (const m of msg.commands) {
|
||||
await this.msg_callback(m);
|
||||
}
|
||||
},
|
||||
delay: async (msg) =>
|
||||
await new Promise((resolve) => {
|
||||
window.setTimeout(resolve, msg.seconds * 1000);
|
||||
}),
|
||||
};
|
||||
|
||||
await handlers[msg.command.replace("_", "-")](msg);
|
||||
}
|
||||
|
||||
debug(msg) {
|
||||
popUp(`deviceID`, { type: "markdown", content: `# ${deviceID}` });
|
||||
alert(deviceID);
|
||||
}
|
||||
|
||||
set_theme(msg) {
|
||||
if (!msg.theme) msg.theme = "default";
|
||||
fireEvent("settheme", { theme: msg.theme }, ha_element());
|
||||
}
|
||||
|
||||
lovelace_reload(msg) {
|
||||
const ll = lovelace_view();
|
||||
if (ll) fireEvent("config-refresh", {}, ll);
|
||||
}
|
||||
|
||||
call_service(msg) {
|
||||
const _replaceThis = (data) => {
|
||||
if (data === "this") return deviceID;
|
||||
if (Array.isArray(data)) return data.map(_replaceThis);
|
||||
if (data.constructor == Object) {
|
||||
for (const key in data) data[key] = _replaceThis(data[key]);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
const [domain, service] = msg.service.split(".", 2);
|
||||
let service_data = _replaceThis(
|
||||
JSON.parse(JSON.stringify(msg.service_data))
|
||||
);
|
||||
this.hass.callService(domain, service, service_data);
|
||||
}
|
||||
|
||||
update(msg = null) {
|
||||
if (msg) {
|
||||
if (msg.name) {
|
||||
this.entity_id = msg.name.toLowerCase();
|
||||
}
|
||||
if (msg.camera && !this.isFully) {
|
||||
this.setup_camera();
|
||||
}
|
||||
this.config = { ...this.config, ...msg };
|
||||
}
|
||||
this.player_update();
|
||||
this.fully_update();
|
||||
this.screen_update();
|
||||
this.sensor_update();
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await hass_loaded();
|
||||
|
||||
if (!window.browser_mod) window.browser_mod = new BrowserMod();
|
||||
})();
|
||||
59
js/plugin/mediaPlayer.ts
Normal file
59
js/plugin/mediaPlayer.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
export const BrowserModMediaPlayerMixin = (C) =>
|
||||
class extends C {
|
||||
constructor() {
|
||||
super();
|
||||
this.player = new Audio();
|
||||
|
||||
for (const event of ["play", "pause", "ended", "volumechange"]) {
|
||||
this.player.addEventListener(event, () => this.player_update());
|
||||
}
|
||||
|
||||
window.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (!this.player.ended) this.player.play();
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
player_update(ev?) {
|
||||
this.sendUpdate({
|
||||
player: {
|
||||
volume: this.player.volume,
|
||||
muted: this.player.muted,
|
||||
src: this.player.src,
|
||||
state: this.player_state,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
get player_state() {
|
||||
if (!this.player.src) return "stopped";
|
||||
if (this.player.ended) return "stopped";
|
||||
if (this.player.paused) return "paused";
|
||||
return "playing";
|
||||
}
|
||||
|
||||
player_play(src) {
|
||||
if (src) this.player.src = src;
|
||||
this.player.play();
|
||||
}
|
||||
player_pause() {
|
||||
this.player.pause();
|
||||
}
|
||||
player_stop() {
|
||||
this.player.pause();
|
||||
this.player.src = null;
|
||||
}
|
||||
player_set_volume(level) {
|
||||
if (level === undefined) return;
|
||||
this.player.volume = level;
|
||||
}
|
||||
player_mute(mute) {
|
||||
if (mute === undefined) mute = !this.player.muted;
|
||||
this.player.muted = Boolean(mute);
|
||||
}
|
||||
};
|
||||
85
js/plugin/popups.ts
Normal file
85
js/plugin/popups.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { fireEvent } from "card-tools/src/event";
|
||||
import { load_lovelace, lovelace, ha_element } from "card-tools/src/hass";
|
||||
import { moreInfo } from "card-tools/src/more-info";
|
||||
import { closePopUp, popUp } from "card-tools/src/popup";
|
||||
|
||||
export const BrowserModPopupsMixin = (C) =>
|
||||
class extends C {
|
||||
constructor() {
|
||||
super();
|
||||
if (document.querySelector("home-assistant"))
|
||||
document
|
||||
.querySelector("home-assistant")
|
||||
.addEventListener("hass-more-info", (ev) => this._popup_card(ev));
|
||||
|
||||
const isCast = document.querySelector("hc-main") !== null;
|
||||
if (!isCast) load_lovelace();
|
||||
}
|
||||
|
||||
_popup_card(ev) {
|
||||
if (!lovelace()) return;
|
||||
if (!ev.detail || !ev.detail.entityId) return;
|
||||
const data = {
|
||||
...lovelace().config.popup_cards,
|
||||
...lovelace().config.views[lovelace().current_view].popup_cards,
|
||||
};
|
||||
const d = data[ev.detail.entityId];
|
||||
if (!d) return;
|
||||
|
||||
this.do_popup(d);
|
||||
window.setTimeout(() => {
|
||||
fireEvent("hass-more-info", { entityID: "." }, ha_element());
|
||||
}, 50);
|
||||
}
|
||||
|
||||
do_popup(cfg) {
|
||||
if (!(cfg.title || cfg.auto_close || cfg.hide_header)) {
|
||||
console.error(
|
||||
"browser_mod: popup: Must specify title, auto_close or hide_header."
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!cfg.card) {
|
||||
console.error("browser_mod: popup: No card specified");
|
||||
return;
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
popUp(
|
||||
cfg.title,
|
||||
cfg.card,
|
||||
cfg.large,
|
||||
cfg.style,
|
||||
cfg.auto_close || cfg.hide_header
|
||||
);
|
||||
};
|
||||
|
||||
if (cfg.auto_close) {
|
||||
this.screensaver_set(open, closePopUp, cfg.time);
|
||||
} else {
|
||||
open();
|
||||
}
|
||||
}
|
||||
|
||||
do_close_popup() {
|
||||
this.screensaver_stop();
|
||||
closePopUp();
|
||||
}
|
||||
|
||||
do_more_info(entity_id, large) {
|
||||
if (!entity_id) return;
|
||||
moreInfo(entity_id, large);
|
||||
}
|
||||
|
||||
do_toast(message, duration) {
|
||||
if (!message) return;
|
||||
fireEvent(
|
||||
"hass-notification",
|
||||
{
|
||||
message,
|
||||
duration: parseInt(duration),
|
||||
},
|
||||
ha_element()
|
||||
);
|
||||
}
|
||||
};
|
||||
138
js/plugin/screensaver.ts
Normal file
138
js/plugin/screensaver.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
export const BrowserModScreensaverMixin = (C) =>
|
||||
class extends C {
|
||||
constructor() {
|
||||
super();
|
||||
this._blackout_panel = document.createElement("div");
|
||||
|
||||
this._screenSaver = undefined;
|
||||
this._screenSaverTimer = undefined;
|
||||
this._screenSaverTimeOut = 0;
|
||||
|
||||
this._screenSaver = {
|
||||
fn: undefined,
|
||||
clearfn: undefined,
|
||||
timer: undefined,
|
||||
timeout: undefined,
|
||||
listeners: {},
|
||||
active: false,
|
||||
};
|
||||
|
||||
this._blackout_panel.style.cssText = `
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: black;
|
||||
display: none;
|
||||
`;
|
||||
document.body.appendChild(this._blackout_panel);
|
||||
}
|
||||
|
||||
screensaver_set(fn, clearfn, time) {
|
||||
this._ss_clear();
|
||||
this._screenSaver = {
|
||||
fn,
|
||||
clearfn,
|
||||
timer: undefined,
|
||||
timeout: time,
|
||||
listeners: {},
|
||||
active: false,
|
||||
};
|
||||
const l = () => this.screensaver_update();
|
||||
for (const event of ["mousemove", "mousedown", "keydown", "touchstart"]) {
|
||||
window.addEventListener(event, l);
|
||||
this._screenSaver.listeners[event] = l;
|
||||
}
|
||||
this._screenSaver.timer = window.setTimeout(
|
||||
() => this._ss_run(),
|
||||
time * 1000
|
||||
);
|
||||
}
|
||||
|
||||
screensaver_update() {
|
||||
if (this._screenSaver.active) {
|
||||
this.screensaver_stop();
|
||||
} else {
|
||||
window.clearTimeout(this._screenSaver.timer);
|
||||
this._screenSaver.timer = window.setTimeout(
|
||||
() => this._ss_run(),
|
||||
this._screenSaver.timeout * 1000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
screensaver_stop() {
|
||||
this._ss_clear();
|
||||
this._screenSaver.active = false;
|
||||
if (this._screenSaver.clearfn) this._screenSaver.clearfn();
|
||||
if (this._screenSaver.timeout) {
|
||||
this.screensaver_set(
|
||||
this._screenSaver.fn,
|
||||
this._screenSaver.clearfn,
|
||||
this._screenSaver.timeout
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_ss_clear() {
|
||||
window.clearTimeout(this._screenSaverTimer);
|
||||
for (const [k, v] of Object.entries(this._screenSaver.listeners)) {
|
||||
window.removeEventListener(k as any, v as any);
|
||||
}
|
||||
}
|
||||
|
||||
_ss_run() {
|
||||
this._screenSaver.active = true;
|
||||
this._screenSaver.fn();
|
||||
}
|
||||
|
||||
do_blackout(timeout) {
|
||||
this.screensaver_set(
|
||||
() => {
|
||||
if (this.isFully) {
|
||||
if (this.config.screensaver) window.fully.startScreensaver();
|
||||
else window.fully.turnScreenOff(true);
|
||||
} else {
|
||||
this._blackout_panel.style.display = "block";
|
||||
}
|
||||
this.screen_update();
|
||||
},
|
||||
() => {
|
||||
if ((this._blackout_panel.style.display = "block"))
|
||||
this._blackout_panel.style.display = "none";
|
||||
if (this.isFully) {
|
||||
if (!window.fully.getScreenOn()) window.fully.turnScreenOn();
|
||||
window.fully.stopScreensaver();
|
||||
}
|
||||
this.screen_update();
|
||||
},
|
||||
timeout || 0
|
||||
);
|
||||
}
|
||||
|
||||
no_blackout() {
|
||||
if (this.isFully) {
|
||||
window.fully.turnScreenOn();
|
||||
window.fully.stopScreensaver();
|
||||
}
|
||||
this.screensaver_stop();
|
||||
}
|
||||
|
||||
screen_update() {
|
||||
this.sendUpdate({
|
||||
screen: {
|
||||
blackout: this.isFully
|
||||
? this.fully_screensaver !== undefined
|
||||
? this.fully_screensaver
|
||||
: !window.fully.getScreenOn()
|
||||
: Boolean(this._blackout_panel.style.display === "block"),
|
||||
brightness: this.isFully
|
||||
? window.fully.getScreenBrightness()
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
36
js/plugin/types.ts
Normal file
36
js/plugin/types.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
const a = {};
|
||||
|
||||
import { BrowserMod } from "./main";
|
||||
|
||||
interface FullyKiosk {
|
||||
// Get device info
|
||||
getIp4Address: { (): String };
|
||||
getDeviceId: { (): String };
|
||||
getBatteryLevel: { (): Number };
|
||||
getScreenBrightness: { (): Number };
|
||||
getScreenOn: { (): Boolean };
|
||||
isPlugged: { (): Boolean };
|
||||
|
||||
// Controll device, show notifications, send network data etc.
|
||||
turnScreenOn: { () };
|
||||
turnScreenOff: { (keepAlive?: Boolean) };
|
||||
|
||||
// Control fully and browsing
|
||||
startScreensaver: { () };
|
||||
stopScreensaver: { () };
|
||||
|
||||
// Respond to events
|
||||
bind: { (event: String, action: String) };
|
||||
|
||||
// Motion detection
|
||||
getCamshotJpgBase64: { (): String };
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
browser_mod?: BrowserMod;
|
||||
fully?: FullyKiosk;
|
||||
hassConnection?: Promise<any>;
|
||||
customCards?: [{}?];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user