462 lines
12 KiB
JavaScript

import { deviceID } from "card-tools/src/deviceId";
import { lovelace_view, provideHass, load_lovelace, lovelace, hass } from "card-tools/src/hass";
import { popUp, closePopUp } from "card-tools/src/popup";
import { fireEvent } from "card-tools/src/event";
import { moreInfo } from "card-tools/src/more-info.js";
import "./browser-player";
class BrowserMod {
set hass(hass) {
if(!hass) return;
this._hass = hass;
if(this.hassPatched) return;
const callService = hass.callService;
const newCallService = (domain, service, serviceData) => {
if(serviceData && serviceData.deviceID) {
if(Array.isArray(serviceData.deviceID)) {
const index = serviceData.deviceID.indexOf('this');
if(index !== -1) {
serviceData = JSON.parse(JSON.stringify(serviceData));
serviceData.deviceID[index] = deviceID;
}
} else if(serviceData.deviceID === "this") {
serviceData = JSON.parse(JSON.stringify(serviceData));
serviceData.deviceID = deviceID;
}
}
return callService(domain, service, serviceData);
};
hass.callService = newCallService;
this.hassPatched = true;
if(document.querySelector("hc-main"))
document.querySelector("hc-main").hassChanged(hass,hass);
else
document.querySelector("home-assistant").hassChanged(hass, hass);
}
playOnce(ev) {
if(this._video) this._video.play();
if(window.browser_mod.playedOnce) return;
window.browser_mod.player.play();
window.browser_mod.playedOnce = true;
}
async _load_lovelace() {
if(!await load_lovelace()) {
let timer = window.setTimeout(this._load_lovelace.bind(this), 100);
}
}
_connect() {
if(!window.hassConnection) {
window.setTimeout(() => this._connect(), 100);
} else {
window.hassConnection.then((conn) => this.connect(conn.conn));
}
}
constructor() {
this.entity_id = deviceID.replace("-","_");
this.cast = document.querySelector("hc-main") !== null;
if(!this.cast) {
window.setTimeout(this._load_lovelace.bind(this), 500);
this._connect();
document.querySelector("home-assistant").addEventListener("hass-more-info", this.popup_card.bind(this));
} else {
this.connect(hass().connection);
}
this.player = new Audio();
this.playedOnce = false;
this.autoclose_popup_active = false;
const updater = this.update.bind(this);
this.player.addEventListener("ended", updater);
this.player.addEventListener("play", updater);
this.player.addEventListener("pause", updater);
this.player.addEventListener("volumechange", updater);
document.addEventListener("visibilitychange", updater);
window.addEventListener("location-changed", updater);
window.addEventListener("click", this.playOnce);
window.addEventListener("mousemove", this.no_blackout.bind(this));
window.addEventListener("mousedown", this.no_blackout.bind(this));
window.addEventListener("keydown", this.no_blackout.bind(this));
window.addEventListener("touchstart", this.no_blackout.bind(this));
provideHass(this);
if(window.fully)
{
this._fullyMotion = false;
this._motionTimeout = undefined;
fully.bind('screenOn', 'browser_mod.update();');
fully.bind('screenOff', 'browser_mod.update();');
fully.bind('pluggedAC', 'browser_mod.update();');
fully.bind('pluggedUSB', 'browser_mod.update();');
fully.bind('onBatteryLevelChanged', 'browser_mod.update();');
fully.bind('unplugged', 'browser_mod.update();');
fully.bind('networkReconnect', 'browser_mod.update();');
fully.bind('onMotion', 'browser_mod.fullyMotion();');
}
this._screenSaver = undefined;
this._screenSaverTimer = undefined;
this._screenSaverTime = 0;
this._blackout = document.createElement("div");
this._blackout.style.cssText = `
position: fixed;
left: 0;
top: 0;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
background: black;
visibility: hidden;
`;
document.body.appendChild(this._blackout);
const pjson = require('../package.json');
console.info(`%cBROWSER_MOD ${pjson.version} IS INSTALLED
%cDeviceID: ${deviceID}`,
"color: green; font-weight: bold", "");
}
connect(conn) {
this.conn = conn
conn.subscribeMessage((msg) => this.callback(msg), {
type: 'browser_mod/connect',
deviceID: deviceID,
});
}
callback(msg) {
switch (msg.command) {
case "update":
this.update(msg);
break;
case "debug":
this.debug(msg);
break;
case "play":
this.play(msg);
break;
case "pause":
this.pause(msg);
break;
case "stop":
this.stop(msg);
break;
case "set_volume":
this.set_volume(msg);
break;
case "mute":
this.mute(msg);
break;
case "toast":
this.toast(msg);
break;
case "popup":
this.popup(msg);
break;
case "close-popup":
this.close_popup(msg);
break;
case "navigate":
this.navigate(msg);
break;
case "more-info":
this.more_info(msg);
break;
case "set-theme":
this.set_theme(msg);
break;
case "lovelace-reload":
this.lovelace_reload(msg);
break;
case "blackout":
this.blackout(msg);
break;
case "no-blackout":
this.no_blackout(msg);
break;
}
}
get player_state() {
if (!this.player.src) return "stopped";
if (this.player.ended) return "stopped";
if (this.player.paused) return "paused";
return "playing";
}
popup_card(ev) {
if(!lovelace()) return;
const ll = lovelace();
const data = {
...ll.config.popup_cards,
...ll.config.views[ll.current_view].popup_cards,
};
if(!ev.detail || !ev.detail.entityId) return;
const d = data[ev.detail.entityId];
if(!d) return;
window.setTimeout(() => {
fireEvent("hass-more-info", {entityId: "."}, document.querySelector("home-assistant"));
popUp(d.title, d.card, d.large || false, d.style);
}, 50);
}
debug(msg) {
popUp(`deviceID`, {type: "markdown", content: `# ${deviceID}`})
alert(deviceID);
}
_set_screensaver(fn, time) {
clearTimeout(this._screenSaverTimer);
if(!fn) {
if(this._screenSaverTime)
this._screenSaverTimer = setTimeout(this._screenSaver, this._screenSaverTime)
} else {
time = parseInt(time)
if(time == -1) {
clearTimeout(this._screenSaverTimer);
this._screenSaverTime = 0;
return;
}
this._screenSaverTime = time * 1000;
this._screenSaver = fn;
this._screenSaverTimer = setTimeout(this._screenSaver, this._screenSaverTime)
}
}
play(msg) {
const src = msg.media_content_id;
if(src)
this.player.src = src;
this.player.play();
}
pause(msg) {
this.player.pause();
}
stop(msg) {
this.player.pause();
this.player.src = null;
}
set_volume(msg) {
if (msg.volume_level === undefined) return;
this.player.volume = msg.volume_level;
}
mute(msg) {
if (msg.mute === undefined)
msg.mute = !this.player.muted;
this.player.muted = Boolean(msg.mute)
}
toast(msg) {
if(!msg.message) return;
fireEvent("hass-notification", {
message: msg.message,
duration: msg.duration !== undefined ? parseInt(msg.duration) : undefined
}, document.querySelector("home-assistant"));
}
popup(msg){
if(!msg.title && !msg.auto_close) return;
if(!msg.card) return;
const fn = () => {
popUp(msg.title, msg.card, msg.large, msg.style, msg.auto_close||msg.hide_header);
if(msg.auto_close)
this.autoclose_popup_active = true;
};
if(msg.auto_close && msg.time) {
this._set_screensaver(fn, msg.time);
} else {
// closePopUp();
fn();
}
}
close_popup(msg){
this._set_screensaver();
this.autoclose_popup_active = false;
closePopUp();
}
navigate(msg){
if(!msg.navigation_path) return;
history.pushState(null, "", msg.navigation_path);
fireEvent("location-changed", {}, document.querySelector("home-assistant"));
}
more_info(msg){
if(!msg.entity_id) return;
moreInfo(msg.entity_id, msg.large);
}
set_theme(msg){
if(!msg.theme) msg.theme = "default";
fireEvent("settheme", msg.theme, document.querySelector("home-assistant"));
}
lovelace_reload(msg) {
const ll = lovelace_view();
if (ll)
fireEvent("config-refresh", {}, ll);
}
blackout(msg){
const fn = () => {
if (window.fully)
{
fully.turnScreenOff();
} else {
this._blackout.style.visibility = "visible";
}
this.update();
};
if(msg.time) {
this._set_screensaver(fn, msg.time)
} else {
fn();
}
}
no_blackout(msg){
this._set_screensaver();
if(this.autoclose_popup_active)
return this.close_popup();
if (window.fully)
{
if (!fully.getScreenOn())
fully.turnScreenOn();
if (msg.brightness)
fully.setScreenBrightness(msg.brightness);
this.update();
} else {
if(this._blackout.style.visibility !== "hidden") {
this._blackout.style.visibility = "hidden";
this.update();
}
}
}
is_blackout(){
if (window.fully)
return !fully.getScreenOn();
return Boolean(this._blackout.style.visibility === "visible")
}
fullyMotion() {
this._fullyMotion = true;
clearTimeout(this._motionTimeout);
this._motionTimeout = setTimeout(() => {
this._fullyMotion = false;
this.update();
}, 5000);
this.update();
}
start_camera() {
if(this._video) return;
this._video = document.createElement("video");
this._video.autoplay = true;
this._video.playsInline = true;
this._video.style.cssText = `
visibility: hidden;
width: 0;
height: 0;
`;
this._canvas = document.createElement("canvas");
this._canvas.style.cssText = `
visibility: hidden;
width: 0;
height: 0;
`;
document.body.appendChild(this._canvas);
document.body.appendChild(this._video);
if(!navigator.mediaDevices) {
return;
}
navigator.mediaDevices.getUserMedia({video: true, audio: false}).then((stream) => {
this._video.srcObject = stream;
this._video.play();
this.send_cam();
});
}
send_cam(data) {
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.conn.sendMessage({
type: 'browser_mod/update',
deviceID: deviceID,
data: {
camera: this._canvas.toDataURL('image/jpeg'),
},
});
setTimeout(this.send_cam.bind(this), 500);
}
update(msg=null) {
if(!this.conn) return;
if(msg) {
if(msg.name) {
this.entity_id = msg.name.toLowerCase();
}
if(msg.camera) {
this.start_camera();
}
}
this.conn.sendMessage({
type: 'browser_mod/update',
deviceID: deviceID,
data: {
browser: {
path: window.location.pathname,
visibility: document.visibilityState,
userAgent: navigator.userAgent,
currentUser: this._hass && this._hass.user && this._hass.user.name,
fullyKiosk: window.fully ? true : undefined,
width: window.innerWidth,
height: window.innerHeight,
},
player: {
volume: this.player.volume,
muted: this.player.muted,
src: this.player.src,
state: this.player_state,
},
screen: {
blackout: this.is_blackout(),
brightness: window.fully ? fully.getScreenBrightness() : undefined,
},
fully: window.fully ? {
battery: window.fully ? fully.getBatteryLevel() : undefined,
charging: window.fully ? fully.isPlugged(): undefined,
motion: window.fully ? this._fullyMotion : undefined,
} : undefined,
},
});
}
}
const bases = [customElements.whenDefined('home-assistant-main'), customElements.whenDefined('hui-view')];
Promise.race(bases).then(() => {
window.browser_mod = window.browser_mod || new BrowserMod();
});