/*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } /** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const t$2=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,e$5=Symbol(),n$4=new Map;class s$3{constructor(t,n){if(this._$cssResult$=!0,n!==e$5)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t;}get styleSheet(){let e=n$4.get(this.cssText);return t$2&&void 0===e&&(n$4.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const o$4=t=>new s$3("string"==typeof t?t:t+"",e$5),r$2=(t,...n)=>{const o=1===t.length?t[0]:n.reduce(((e,n,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(n)+t[s+1]),t[0]);return new s$3(o,e$5)},i$3=(e,n)=>{t$2?e.adoptedStyleSheets=n.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):n.forEach((t=>{const n=document.createElement("style"),s=window.litNonce;void 0!==s&&n.setAttribute("nonce",s),n.textContent=t.cssText,e.appendChild(n);}));},S$1=t$2?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const n of t.cssRules)e+=n.cssText;return o$4(e)})(t):t; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */var s$2;const e$4=window.trustedTypes,r$1=e$4?e$4.emptyScript:"",h$1=window.reactiveElementPolyfillSupport,o$3={toAttribute(t,i){switch(i){case Boolean:t=t?r$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,i){let s=t;switch(i){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t);}catch(t){s=null;}}return s}},n$3=(t,i)=>i!==t&&(i==i||t==t),l$2={attribute:!0,type:String,converter:o$3,reflect:!1,hasChanged:n$3};class a$1 extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o();}static addInitializer(t){var i;null!==(i=this.l)&&void 0!==i||(this.l=[]),this.l.push(t);}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((i,s)=>{const e=this._$Eh(s,i);void 0!==e&&(this._$Eu.set(e,s),t.push(e));})),t}static createProperty(t,i=l$2){if(i.state&&(i.attribute=!1),this.finalize(),this.elementProperties.set(t,i),!i.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,e=this.getPropertyDescriptor(t,s,i);void 0!==e&&Object.defineProperty(this.prototype,t,e);}}static getPropertyDescriptor(t,i,s){return {get(){return this[i]},set(e){const r=this[t];this[i]=e,this.requestUpdate(t,r,s);},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||l$2}static finalize(){if(this.hasOwnProperty("finalized"))return !1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),this.elementProperties=new Map(t.elementProperties),this._$Eu=new Map,this.hasOwnProperty("properties")){const t=this.properties,i=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of i)this.createProperty(s,t[s]);}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(i){const s=[];if(Array.isArray(i)){const e=new Set(i.flat(1/0).reverse());for(const i of e)s.unshift(S$1(i));}else void 0!==i&&s.push(S$1(i));return s}static _$Eh(t,i){const s=i.attribute;return !1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}o(){var t;this._$Ep=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(t=this.constructor.l)||void 0===t||t.forEach((t=>t(this)));}addController(t){var i,s;(null!==(i=this._$Eg)&&void 0!==i?i:this._$Eg=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t));}removeController(t){var i;null===(i=this._$Eg)||void 0===i||i.splice(this._$Eg.indexOf(t)>>>0,1);}_$Em(){this.constructor.elementProperties.forEach(((t,i)=>{this.hasOwnProperty(i)&&(this._$Et.set(i,this[i]),delete this[i]);}));}createRenderRoot(){var t;const s=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return i$3(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostConnected)||void 0===i?void 0:i.call(t)}));}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostDisconnected)||void 0===i?void 0:i.call(t)}));}attributeChangedCallback(t,i,s){this._$AK(t,s);}_$ES(t,i,s=l$2){var e,r;const h=this.constructor._$Eh(t,s);if(void 0!==h&&!0===s.reflect){const n=(null!==(r=null===(e=s.converter)||void 0===e?void 0:e.toAttribute)&&void 0!==r?r:o$3.toAttribute)(i,s.type);this._$Ei=t,null==n?this.removeAttribute(h):this.setAttribute(h,n),this._$Ei=null;}}_$AK(t,i){var s,e,r;const h=this.constructor,n=h._$Eu.get(t);if(void 0!==n&&this._$Ei!==n){const t=h.getPropertyOptions(n),l=t.converter,a=null!==(r=null!==(e=null===(s=l)||void 0===s?void 0:s.fromAttribute)&&void 0!==e?e:"function"==typeof l?l:null)&&void 0!==r?r:o$3.fromAttribute;this._$Ei=n,this[n]=a(i,t.type),this._$Ei=null;}}requestUpdate(t,i,s){let e=!0;void 0!==t&&(((s=s||this.constructor.getPropertyOptions(t)).hasChanged||n$3)(this[t],i)?(this._$AL.has(t)||this._$AL.set(t,i),!0===s.reflect&&this._$Ei!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):e=!1),!this.isUpdatePending&&e&&(this._$Ep=this._$E_());}async _$E_(){this.isUpdatePending=!0;try{await this._$Ep;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((t,i)=>this[i]=t)),this._$Et=void 0);let i=!1;const s=this._$AL;try{i=this.shouldUpdate(s),i?(this.willUpdate(s),null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostUpdate)||void 0===i?void 0:i.call(t)})),this.update(s)):this._$EU();}catch(t){throw i=!1,this._$EU(),t}i&&this._$AE(s);}willUpdate(t){}_$AE(t){var i;null===(i=this._$Eg)||void 0===i||i.forEach((t=>{var i;return null===(i=t.hostUpdated)||void 0===i?void 0:i.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t);}_$EU(){this._$AL=new Map,this.isUpdatePending=!1;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(t){return !0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,i)=>this._$ES(i,this[i],t))),this._$EC=void 0),this._$EU();}updated(t){}firstUpdated(t){}}a$1.finalized=!0,a$1.elementProperties=new Map,a$1.elementStyles=[],a$1.shadowRootOptions={mode:"open"},null==h$1||h$1({ReactiveElement:a$1}),(null!==(s$2=globalThis.reactiveElementVersions)&&void 0!==s$2?s$2:globalThis.reactiveElementVersions=[]).push("1.3.1"); /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ var t$1;const i$2=globalThis.trustedTypes,s$1=i$2?i$2.createPolicy("lit-html",{createHTML:t=>t}):void 0,e$3=`lit$${(Math.random()+"").slice(9)}$`,o$2="?"+e$3,n$2=`<${o$2}>`,l$1=document,h=(t="")=>l$1.createComment(t),r=t=>null===t||"object"!=typeof t&&"function"!=typeof t,d=Array.isArray,u=t=>{var i;return d(t)||"function"==typeof(null===(i=t)||void 0===i?void 0:i[Symbol.iterator])},c=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,v=/-->/g,a=/>/g,f=/>|[ \n\r](?:([^\s"'>=/]+)([ \n\r]*=[ \n\r]*(?:[^ \n\r"'`<>=]|("|')|))|$)/g,_=/'/g,m=/"/g,g=/^(?:script|style|textarea|title)$/i,p=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),$=p(1),b=Symbol.for("lit-noChange"),w=Symbol.for("lit-nothing"),T=new WeakMap,x=(t,i,s)=>{var e,o;const n=null!==(e=null==s?void 0:s.renderBefore)&&void 0!==e?e:i;let l=n._$litPart$;if(void 0===l){const t=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;n._$litPart$=l=new N(i.insertBefore(h(),t),t,void 0,null!=s?s:{});}return l._$AI(t),l},A=l$1.createTreeWalker(l$1,129,null,!1),C=(t,i)=>{const o=t.length-1,l=[];let h,r=2===i?"":"",d=c;for(let i=0;i"===u[0]?(d=null!=h?h:c,p=-1):void 0===u[1]?p=-2:(p=d.lastIndex-u[2].length,o=u[1],d=void 0===u[3]?f:'"'===u[3]?m:_):d===m||d===_?d=f:d===v||d===a?d=c:(d=f,h=void 0);const y=d===f&&t[i+1].startsWith("/>")?" ":"";r+=d===c?s+n$2:p>=0?(l.push(o),s.slice(0,p)+"$lit$"+s.slice(p)+e$3+y):s+e$3+(-2===p?(l.push(void 0),i):y);}const u=r+(t[o]||">")+(2===i?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return [void 0!==s$1?s$1.createHTML(u):u,l]};class E{constructor({strings:t,_$litType$:s},n){let l;this.parts=[];let r=0,d=0;const u=t.length-1,c=this.parts,[v,a]=C(t,s);if(this.el=E.createElement(v,n),A.currentNode=this.el.content,2===s){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(l=A.nextNode())&&c.length0){l.textContent=i$2?i$2.emptyScript:"";for(let i=0;i2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=w;}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,i=this,s,e){const o=this.strings;let n=!1;if(void 0===o)t=P(this,t,i,0),n=!r(t)||t!==this._$AH&&t!==b,n&&(this._$AH=t);else {const e=t;let l,h;for(t=o[0],l=0;l"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(n){n.createProperty(e.key,i);}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this));},finisher(n){n.createProperty(e.key,i);}};function e$2(e){return (n,t)=>void 0!==t?((i,e,n)=>{e.constructor.createProperty(n,i);})(e,n,t):i$1(e,n)} /** * @license * Copyright 2021 Google LLC * SPDX-License-Identifier: BSD-3-Clause */var n;null!=(null===(n=window.HTMLSlotElement)||void 0===n?void 0:n.prototype.assignedElements)?(o,n)=>o.assignedElements(n):(o,n)=>o.assignedNodes(n).filter((o=>o.nodeType===Node.ELEMENT_NODE)); class BrowserPlayerEditor extends s { setConfig(config) { } render() { return $ ` Nothing to configure. `; } } (async () => { while (!window.browser_mod) { await new Promise((resolve) => setTimeout(resolve, 1000)); } await window.browser_mod.connectionPromise; 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, }); } })(); class BrowserPlayer extends s { 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)); } this.player = window.browser_mod.player; for (const event of [ "play", "pause", "ended", "volumechange", "canplay", "loadeddata", ]) this.player.addEventListener(event, () => this.requestUpdate()); } handleMute(ev) { this.player.muted = !this.player.muted; } handleVolumeChange(ev) { const volume_level = parseFloat(ev.target.value); this.player.volume = volume_level; } handleMoreInfo(ev) { var _a; this.dispatchEvent(new CustomEvent("hass-more-info", { bubbles: true, composed: true, cancelable: false, detail: { entityId: (_a = window.browser_mod.browserEntities) === null || _a === void 0 ? void 0 : _a.player, }, })); } handlePlayPause(ev) { if (!this.player.src || this.player.paused || this.player.ended) this.player.play(); else this.player.pause(); } render() { if (!window.browser_mod) { window.setTimeout(() => this.requestUpdate(), 100); return $ ``; } return $ ` ${window.browser_mod.player_state === "stopped" ? $ `` : $ ` `} ${window.browser_mod.browserID} `; } static get styles() { return r$2 ` paper-icon-button[highlight] { color: var(--accent-color); } .card-content { display: flex; justify-content: center; } .placeholder { width: 24px; padding: 8px; } .browser-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; } `; } } __decorate([ e$2() ], BrowserPlayer.prototype, "hass", void 0); (async () => { while (!window.browser_mod) { await new Promise((resolve) => setTimeout(resolve, 1000)); } await window.browser_mod.connectionPromise; if (!customElements.get("browser-player")) customElements.define("browser-player", BrowserPlayer); })(); async function hass_base_el() { await Promise.race([ customElements.whenDefined("home-assistant"), customElements.whenDefined("hc-main"), ]); const element = customElements.get("home-assistant") ? "home-assistant" : "hc-main"; while (!document.querySelector(element)) await new Promise((r) => window.setTimeout(r, 100)); return document.querySelector(element); } async function hass() { const base = await hass_base_el(); while (!base.hass) await new Promise((r) => window.setTimeout(r, 100)); return base.hass; } async function provideHass(el) { const base = await hass_base_el(); base.provideHass(el); } const loadLoadCardHelpers = async () => { var _a, _b, _c; if (window.loadCardHelpers !== undefined) return; await customElements.whenDefined("partial-panel-resolver"); const ppResolver = document.createElement("partial-panel-resolver"); const routes = ppResolver.getRoutes([ { component_name: "lovelace", url_path: "a", }, ]); await ((_c = (_b = (_a = routes === null || routes === void 0 ? void 0 : routes.routes) === null || _a === void 0 ? void 0 : _a.a) === null || _b === void 0 ? void 0 : _b.load) === null || _c === void 0 ? void 0 : _c.call(_b)); }; const ID_STORAGE_KEY = "browser_mod-browser-id"; const ConnectionMixin = (SuperClass) => { class BrowserModConnection extends SuperClass { constructor() { super(...arguments); this.connected = false; this.connectionPromise = new Promise((resolve) => { this._connectionResolve = resolve; }); this.browserEntities = {}; } LOG(...args) { const dt = new Date(); console.log(`${dt.toLocaleTimeString()}`, ...args); } fireEvent(event, detail = undefined) { this.dispatchEvent(new CustomEvent(event, { detail })); } incoming_message(msg) { var _a; if (msg.command) { this.LOG("Command:", msg); this.fireEvent(`command-${msg.command}`, msg); } else if (msg.browserEntities) { this.browserEntities = msg.browserEntities; } else if (msg.result) { this.update_config(msg.result); } (_a = this._connectionResolve) === null || _a === void 0 ? void 0 : _a.call(this); } update_config(cfg) { var _a; this.LOG("Receive:", cfg); let update = false; if (!this.registered && ((_a = cfg.browsers) === null || _a === void 0 ? void 0 : _a[this.browserID])) { update = true; } this._data = cfg; if (!this.connected) { this.connected = true; this.fireEvent("browser-mod-connected"); } this.fireEvent("browser-mod-config-update"); if (update) this.sendUpdate({}); } async connect() { const conn = (await hass()).connection; this.connection = conn; // Subscribe to configuration updates conn.subscribeMessage((msg) => this.incoming_message(msg), { type: "browser_mod/connect", browserID: this.browserID, }); // Keep connection status up to date conn.addEventListener("disconnected", () => { this.connected = false; this.fireEvent("browser-mod-disconnected"); }); conn.addEventListener("ready", () => { this.connected = true; this.fireEvent("browser-mod-connected"); this.sendUpdate({}); }); provideHass(this); } get config() { var _a, _b; return (_b = (_a = this._data) === null || _a === void 0 ? void 0 : _a.config) !== null && _b !== void 0 ? _b : {}; } get browsers() { var _a, _b; return (_b = (_a = this._data) === null || _a === void 0 ? void 0 : _a.browsers) !== null && _b !== void 0 ? _b : []; } get registered() { var _a; return ((_a = this.browsers) === null || _a === void 0 ? void 0 : _a[this.browserID]) !== undefined; } set registered(reg) { (async () => { if (reg) { if (this.registered) return; await this.connection.sendMessage({ type: "browser_mod/register", browserID: this.browserID, }); } else { if (!this.registered) return; await this.connection.sendMessage({ type: "browser_mod/unregister", browserID: this.browserID, }); } })(); } async _reregister(newData = {}) { await this.connection.sendMessage({ type: "browser_mod/reregister", browserID: this.browserID, data: Object.assign(Object.assign({}, this.browsers[this.browserID]), newData), }); } get meta() { if (!this.registered) return null; return this.browsers[this.browserID].meta; } set meta(value) { this._reregister({ meta: value }); } get cameraEnabled() { if (!this.registered) return null; return this.browsers[this.browserID].camera; } set cameraEnabled(value) { this._reregister({ camera: value }); } sendUpdate(data) { if (!this.connected || !this.registered) return; this.LOG("Send:", data); this.connection.sendMessage({ type: "browser_mod/update", browserID: this.browserID, data, }); } get browserID() { if (localStorage[ID_STORAGE_KEY]) return localStorage[ID_STORAGE_KEY]; this.browserID = ""; return this.browserID; } set browserID(id) { var _a, _b; function _createBrowserID() { var _a, _b; const s4 = () => { return Math.floor((1 + Math.random()) * 100000) .toString(16) .substring(1); }; return (_b = (_a = window.fully) === null || _a === void 0 ? void 0 : _a.getDeviceId()) !== null && _b !== void 0 ? _b : `${s4()}${s4()}-${s4()}${s4()}`; } if (id === "") id = _createBrowserID(); const oldID = localStorage[ID_STORAGE_KEY]; localStorage[ID_STORAGE_KEY] = id; this.fireEvent("browser-mod-config-update"); if (((_a = this.browsers) === null || _a === void 0 ? void 0 : _a[oldID]) !== undefined && ((_b = this.browsers) === null || _b === void 0 ? void 0 : _b[this.browserID]) === undefined) { (async () => { await this.connection.sendMessage({ type: "browser_mod/reregister", browserID: oldID, data: Object.assign(Object.assign({}, this.browsers[oldID]), { browserID: this.browserID }), }); })(); } } } return BrowserModConnection; }; const ScreenSaverMixin = (SuperClass) => { class ScreenSaverMixinClass extends SuperClass { constructor() { super(); this._listeners = {}; this._brightness = 255; const panel = (this._panel = document.createElement("div")); document.body.append(panel); panel.classList.add("browser-mod-blackout"); panel.attachShadow({ mode: "open" }); const styleEl = document.createElement("style"); panel.shadowRoot.append(styleEl); styleEl.innerHTML = ` :host { background: rgba(0,0,0, var(--darkness)); position: fixed; left: 0; top: 0; bottom: 0; right: 0; width: 100%; height: 100%; z-index: 10000; display: block; pointer-events: none; } :host([dark]) { background: rgba(0,0,0,1); } `; this.addEventListener("command-screen_off", () => this._screen_off()); this.addEventListener("command-screen_on", (ev) => this._screen_on(ev)); this.addEventListener("fully-update", () => this.send_screen_status()); this.connectionPromise.then(() => this._screen_on()); } send_screen_status() { let screen_on = !this._panel.hasAttribute("dark"); let screen_brightness = this._brightness; if (this.fully) { screen_on = this.fully_screen; screen_brightness = this.fully_brightness; } this.sendUpdate({ screen_on, screen_brightness }); } _screen_off() { if (this.fully) { this.fully_screen = false; } else { this._panel.setAttribute("dark", ""); } this.send_screen_status(); const l = () => this._screen_on(); for (const ev of ["pointerdown", "pointermove", "keydown"]) { this._listeners[ev] = l; window.addEventListener(ev, l); } } _screen_on(ev = undefined) { var _a, _b; if (this.fully) { this.fully_screen = true; if ((_a = ev === null || ev === void 0 ? void 0 : ev.detail) === null || _a === void 0 ? void 0 : _a.brightness) { this.fully_brightness = ev.detail.brightness; } } else { if ((_b = ev === null || ev === void 0 ? void 0 : ev.detail) === null || _b === void 0 ? void 0 : _b.brightness) { this._brightness = ev.detail.brightness; this._panel.style.setProperty("--darkness", 1 - ev.detail.brightness / 255); } this._panel.removeAttribute("dark"); } this.send_screen_status(); for (const ev of ["pointerdown", "pointermove", "keydown"]) { if (this._listeners[ev]) { window.removeEventListener(ev, this._listeners[ev]); this._listeners[ev] = undefined; } } } } return ScreenSaverMixinClass; }; const MediaPlayerMixin = (SuperClass) => { return class MediaPlayerMixinClass extends SuperClass { constructor() { super(); this.player = new Audio(); this._player_enabled = false; for (const ev of ["play", "pause", "ended", "volumechange"]) { this.player.addEventListener(ev, () => this._player_update()); } this.firstInteraction.then(() => { this._player_enabled = true; if (!this.player.ended) this.player.play(); }); this.addEventListener("command-player-play", (ev) => { var _a; if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_content_id) this.player.src = ev.detail.media_content_id; this.player.play(); }); this.addEventListener("command-player-pause", (ev) => this.player.pause()); this.addEventListener("command-player-stop", (ev) => { this.player.src = null; this.player.pause(); }); this.addEventListener("command-player-set-volume", (ev) => { var _a; if (((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.volume_level) === undefined) return; this.player.volume = ev.detail.volume_level; }); this.addEventListener("command-player-mute", (ev) => { var _a; if (((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.mute) !== undefined) this.player.muted = Boolean(ev.detail.mute); else this.player.muted = !this.player.muted; }); this.connectionPromise.then(() => this._player_update()); } _player_update() { const state = this._player_enabled ? this.player.src ? this.player.ended ? "stopped" : this.player.paused ? "paused" : "playing" : "stopped" : "unavailable"; this.sendUpdate({ player: { volume: this.player.volume, muted: this.player.muted, src: this.player.src, state, }, }); } }; }; const CameraMixin = (SuperClass) => { return class CameraMixinClass extends SuperClass { // TODO: Enable WebRTC? // https://levelup.gitconnected.com/establishing-the-webrtc-connection-videochat-with-javascript-step-3-48d4ae0e9ea4 constructor() { super(); this._framerate = 2; this._setup_camera(); } async _setup_camera() { if (this._video) return; await this.connectionPromise; await this.firstInteraction; if (!this.cameraEnabled) return; if (this.fully) return this.update_camera(); const div = document.createElement("div"); document.body.append(div); div.classList.add("browser-mod-camera"); div.attachShadow({ mode: "open" }); const styleEl = document.createElement("style"); div.shadowRoot.append(styleEl); styleEl.innerHTML = ` :host { display: none; }`; const video = (this._video = document.createElement("video")); div.shadowRoot.append(video); video.autoplay = true; video.playsInline = true; video.style.display = "none"; const canvas = (this._canvas = document.createElement("canvas")); div.shadowRoot.append(canvas); canvas.style.display = "none"; if (!navigator.mediaDevices) return; const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false, }); video.srcObject = stream; video.play(); this.update_camera(); } async update_camera() { var _a; if (!this.cameraEnabled) { const stream = (_a = this._video) === null || _a === void 0 ? void 0 : _a.srcObject; if (stream) { stream.getTracks().forEach((t) => t.stop()); this._video.scrObject = undefined; } return; } if (this.fully) { this.sendUpdate({ camera: this.fully_camera, }); } else { const video = this._video; const width = video.videoWidth; const height = video.videoHeight; this._canvas.width = width; this._canvas.height = height; const context = this._canvas.getContext("2d"); context.drawImage(video, 0, 0, width, height); this.sendUpdate({ camera: this._canvas.toDataURL("image/jpeg"), }); } const interval = Math.round(1000 / this._framerate); setTimeout(() => this.update_camera(), interval); } }; }; const RequireInteractMixin = (SuperClass) => { return class RequireInteractMixinClass extends SuperClass { constructor() { super(); this.firstInteraction = new Promise((resolve) => { this._interactionResolve = resolve; }); this.show_indicator(); } async show_indicator() { await this.connectionPromise; if (!this.registered) return; const interactSymbol = document.createElement("div"); document.body.append(interactSymbol); interactSymbol.classList.add("browser-mod-require-interaction"); interactSymbol.attachShadow({ mode: "open" }); const styleEl = document.createElement("style"); interactSymbol.shadowRoot.append(styleEl); styleEl.innerHTML = ` :host { position: fixed; right: 8px; bottom: 8px; color: var(--warning-color, red); opacity: 0.5; --mdc-icon-size: 48px; } ha-icon::before { content: "browser_mod"; font-size: 0.75rem; position: absolute; right: 0; bottom: 90%; } video { display: none; } `; const icon = document.createElement("ha-icon"); interactSymbol.shadowRoot.append(icon); icon.icon = "mdi:gesture-tap"; // If we are allowed to play a video, we can assume no interaction is needed const video = (this._video = document.createElement("video")); interactSymbol.shadowRoot.append(video); const vPlay = video.play(); if (vPlay) { vPlay .then(() => { this._interactionResolve(); }) .catch((e) => { if (e.name === "AbortError") this._interactionResolve(); }); video.pause(); } window.addEventListener("pointerdown", this._interactionResolve); // if (this.fully) this._interactionResolve(); await this.firstInteraction; interactSymbol.remove(); } }; }; const FullyMixin = (C) => { return class FullyMixinClass extends C { constructor() { super(); this._fully_screensaver = false; if (!this.fully) return; for (const ev of [ "screenOn", "screenOff", "pluggedAC", "pluggedUSB", "onBatteryLevelChanged", "unplugged", "networkReconnect", "onMotion", "onDaydreamStart", "onDaydreamStop", ]) { window.fully.bind(ev, `window.browser_mod.fullyEvent("${ev}");`); } window.fully.bind("onScreensaverStart", `window.browser_mod._fully_screensaver = true; window.browser_mod.fullyEvent();`); window.fully.bind("onScreensaverStop", `window.browser_mod._fully_screensaver = false; window.browser_mod.fullyEvent();`); return; } get fully() { return window.fully !== undefined; } get fully_screen() { var _a; return this._fully_screensaver === false && ((_a = window.fully) === null || _a === void 0 ? void 0 : _a.getScreenOn()); } set fully_screen(state) { var _a, _b, _c; if (state) { (_a = window.fully) === null || _a === void 0 ? void 0 : _a.turnScreenOn(); (_b = window.fully) === null || _b === void 0 ? void 0 : _b.stopScreensaver(); } else { (_c = window.fully) === null || _c === void 0 ? void 0 : _c.turnScreenOff(); } } get fully_brightness() { var _a; return (_a = window.fully) === null || _a === void 0 ? void 0 : _a.getScreenBrightness(); } set fully_brightness(br) { var _a; (_a = window.fully) === null || _a === void 0 ? void 0 : _a.setScreenBrightness(br); } get fully_camera() { var _a; return (_a = window.fully) === null || _a === void 0 ? void 0 : _a.getCamshotJpgBase64(); } fullyEvent(event = undefined) { this.fireEvent("fully-update", { event }); } }; }; const BrowserStateMixin = (SuperClass) => { return class BrowserStateMixinClass extends SuperClass { constructor() { super(); document.addEventListener("visibilitychange", () => this._browser_state_update()); window.addEventListener("location-changed", () => this._browser_state_update()); this.addEventListener("fully-update", () => this._browser_state_update()); this.connectionPromise.then(() => this._browser_state_update()); } _browser_state_update() { const update = async () => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; const battery = (_b = (_a = navigator).getBattery) === null || _b === void 0 ? void 0 : _b.call(_a); this.sendUpdate({ browser: { path: window.location.pathname, visibility: document.visibilityState, userAgent: navigator.userAgent, currentUser: (_d = (_c = this.hass) === null || _c === void 0 ? void 0 : _c.user) === null || _d === void 0 ? void 0 : _d.name, fullyKiosk: this.fully || false, width: window.innerWidth, height: window.innerHeight, battery_level: (_f = (_e = window.fully) === null || _e === void 0 ? void 0 : _e.getBatteryLevel()) !== null && _f !== void 0 ? _f : (battery === null || battery === void 0 ? void 0 : battery.level) * 100, charging: (_h = (_g = window.fully) === null || _g === void 0 ? void 0 : _g.isPlugged()) !== null && _h !== void 0 ? _h : battery === null || battery === void 0 ? void 0 : battery.charging, darkMode: (_k = (_j = this.hass) === null || _j === void 0 ? void 0 : _j.themes) === null || _k === void 0 ? void 0 : _k.darkMode, userData: (_l = this.hass) === null || _l === void 0 ? void 0 : _l.user, ip_address: (_m = window.fully) === null || _m === void 0 ? void 0 : _m.getIp4Address(), }, }); }; update(); } }; }; const ServicesMixin = (SuperClass) => { return class ServicesMixinClass extends SuperClass { /* Structure of service call: service: [data: ] Sequence: service: browser_mod.sequence data: sequence: - - - ... Delay service: browser_mod.delay data: time: Popup: service: browser_mod.popup data: [title: ] [content: ] [right_button: ] [right_button_action: ] [left_button: ] [left_button_action: ] [dismissable: ] [dismiss_action: ] [timeout: ] [timeout_action: ] Close popup service: browser_mod.close_popup Javascript console print service: browser_mod.console data: message: */ constructor() { super(); const cmds = ["sequence", "delay", "popup", "close_popup"]; for (const service of cmds) { this.addEventListener(`command-${service}`, (ev) => { this._service_action({ service, data: ev.detail, }); }); } } async _service_action({ service, data }) { let _service = service; if (!_service.startsWith("browser_mod.") && _service.includes(".")) ; if (_service.startsWith("browser_mod.")) { _service = _service.substring(12); } switch (_service) { case "sequence": for (const a of data.sequence) await this._service_action(a); break; case "delay": await new Promise((resolve) => setTimeout(resolve, data.time)); break; case "popup": const { title, content } = data, d = __rest(data, ["title", "content"]); for (const [k, v] of Object.entries(d)) { if (k.endsWith("_action")) { d[k] = () => this._service_action(v); } } this.showPopup(title, content, d); break; case "close_popup": this.closePopup(); break; case "console": console.log(data.message); break; } } }; }; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const t={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},e$1=t=>(...e)=>({_$litDirective$:t,values:e});class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i;}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}} /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */class e extends i{constructor(i){if(super(i),this.it=w,i.type!==t.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(r){if(r===w||null==r)return this.ft=void 0,this.it=r;if(r===b)return r;if("string"!=typeof r)throw Error(this.constructor.directiveName+"() called with a non-string value");if(r===this.it)return this.ft;this.it=r;const s=[r];return s.raw=s,this.ft={_$litType$:this.constructor.resultType,strings:s,values:[]}}}e.directiveName="unsafeHTML",e.resultType=1;const o=e$1(e); class BrowserModPopup extends s { closeDialog() { this.open = false; clearInterval(this._timeoutTimer); } openDialog() { this.open = true; if (this.timeout) { this._timeoutStart = new Date().getTime(); this._timeoutTimer = setInterval(() => { const ellapsed = new Date().getTime() - this._timeoutStart; const progress = (ellapsed / this.timeout) * 100; this.style.setProperty("--progress", `${progress}%`); if (ellapsed >= this.timeout) this._timeout(); }, 10); } } async setupDialog(title, content, { right_button = undefined, right_button_action = undefined, left_button = undefined, left_button_action = undefined, dismissable = true, dismiss_action = undefined, timeout = undefined, timeout_action = undefined, } = {}) { this.title = title; if (content && typeof content === "object") { // Create a card from config in content this.card = true; const helpers = await window.loadCardHelpers(); const card = await helpers.createCardElement(content); card.hass = window.browser_mod.hass; provideHass(card); this.content = card; } else { // Basic HTML content this.card = undefined; this.content = o(content); } this.right_button = right_button; this.left_button = left_button; this.actions = right_button === undefined ? undefined : ""; this.dismissable = dismissable; this.timeout = timeout; this._actions = { right_button_action, left_button_action, dismiss_action, timeout_action, }; } _primary() { var _a, _b, _c; if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) this._actions.dismiss_action = undefined; this.closeDialog(); (_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.right_button_action) === null || _c === void 0 ? void 0 : _c.call(_b); } _secondary() { var _a, _b, _c; if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) this._actions.dismiss_action = undefined; this.closeDialog(); (_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.left_button_action) === null || _c === void 0 ? void 0 : _c.call(_b); } _dismiss() { var _a, _b; this.closeDialog(); (_b = (_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) === null || _b === void 0 ? void 0 : _b.call(_a); } _timeout() { var _a, _b, _c; if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) this._actions.dismiss_action = undefined; this.closeDialog(); (_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.timeout_action) === null || _c === void 0 ? void 0 : _c.call(_b); } render() { if (!this.open) return $ ``; return $ ` ${this.timeout ? $ ` ` : ""} ${this.title ? $ ` ${this.dismissable ? $ ` ` : ""} ${this.title} ` : $ ``} ${this.content} ${this.right_button !== undefined ? $ ` ` : ""} ${this.left_button !== undefined ? $ ` ` : ""} `; } static get styles() { return r$2 ` ha-dialog { --mdc-dialog-min-width: 400px; --mdc-dialog-max-width: 600px; --mdc-dialog-heading-ink-color: var(--primary-text-color); --mdc-dialog-content-ink-color: var(--primary-text-color); --justify-action-buttons: space-between; --mdc-dialog-box-shadow: 0px 0px 0px var(--ha-card-border-width, 1px) var(--ha-card-border-color, var(--divider-color, #e0e0e0)); --ha-dialog-border-radius: 8px; --mdc-theme-surface: var( --ha-card-background, var(--card-background-color, white) ); } :host([wide]) ha-dialog { --mdc-dialog-max-width: 90vw; } :host([fullscreen]) ha-dialog { --mdc-dialog-min-width: 100vw; --mdc-dialog-max-width: 100vw; --mdc-dialog-min-height: 100%; --mdc-dialog-max-height: 100%; --mdc-shape-medium: 0px; --vertial-align-dialog: flex-end; } .progress::before { content: ""; position: absolute; left: 0; width: calc(100% - var(--progress, 60%)); top: 0; height: 2px; background: var(--primary-color); z-index: 10; } app-toolbar { flex-shrink: 0; color: var(--primary-text-color); background-color: var(--sidebar-background-color); } ha-icon-button > * { display: flex; } .main-title { margin-left: 16px; line-height: 1.3em; max-height: 2.6em; overflow: hidden; text-overflow: ellipsis; } .content { --padding-x: 24px; --padding-y: 20px; margin: -20px -24px; padding: var(--padding-y) var(--padding-y); --header-height: 64px; --footer-height: 0px; } .content:first-child { --header-height: 0px; } :host([card]) .content { --padding-x: 0px; --padding-y: 0px; } :host([actions]) .content { border-bottom: 1px solid var(--divider-color); --footer-height: 54px; } :host([wide]) .content { width: calc(90vw - 2 * var(--padding-x)); } :host([fullscreen]) .content { height: calc( 100vh - var(--header-height) - var(--footer-height) - 2 * var(--padding-y) ); } @media all and (max-width: 450px), all and (max-height: 500px) { ha-dialog { --mdc-dialog-min-width: 100vw; --mdc-dialog-max-width: 100vw; --mdc-dialog-min-height: 100%; --mdc-dialog-max-height: 100%; --mdc-shape-medium: 0px; --vertial-align-dialog: flex-end; } } `; } } __decorate([ e$2() ], BrowserModPopup.prototype, "open", void 0); __decorate([ e$2() ], BrowserModPopup.prototype, "content", void 0); __decorate([ e$2() ], BrowserModPopup.prototype, "title", void 0); __decorate([ e$2({ reflect: true }) ], BrowserModPopup.prototype, "actions", void 0); __decorate([ e$2({ reflect: true }) ], BrowserModPopup.prototype, "card", void 0); __decorate([ e$2() ], BrowserModPopup.prototype, "right_button", void 0); __decorate([ e$2() ], BrowserModPopup.prototype, "left_button", void 0); __decorate([ e$2() ], BrowserModPopup.prototype, "dismissable", void 0); customElements.define("browser-mod-popup", BrowserModPopup); const PopupMixin = (SuperClass) => { return class PopupMixinClass extends SuperClass { constructor() { super(); loadLoadCardHelpers(); this._popupEl = document.createElement("browser-mod-popup"); document.body.append(this._popupEl); // const historyListener = async (ev) => { // const popupState = ev.state?.browserModPopup; // if (popupState) { // if (popupState.open) { // this._popupEl.setupDialog(...popupState.args); // this._popupEl.openDialog(); // } else { // this._popupEl.closeDialog(); // } // } // }; // window.addEventListener("popstate", historyListener); } showPopup(...args) { // if (history.state?.browserModPopup === undefined) { // history.replaceState( // { // browserModPopup: { // open: false, // }, // }, // "" // ); // } // history.pushState( // { // browserModPopup: { // open: true, // args, // }, // }, // "" // ); this._popupEl.setupDialog(...args); this._popupEl.openDialog(); } closePopup(...args) { this._popupEl.closeDialog(); } }; }; var name = "browser_mod"; var version = "2.0.0b0"; var description = ""; var scripts = { build: "rollup -c", watch: "rollup -c --watch", "update-card-tools": "npm uninstall card-tools && npm install thomasloven/lovelace-card-tools" }; var keywords = [ ]; var author = "Thomas Lovén"; var license = "MIT"; var devDependencies = { "@babel/core": "^7.17.9", "@rollup/plugin-babel": "^5.3.1", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.2.1", lit: "^2.2.2", rollup: "^2.70.2", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", typescript: "^4.6.3" }; var dependencies = { "card-tools": "github:thomasloven/lovelace-card-tools" }; var pjson = { name: name, "private": true, version: version, description: description, scripts: scripts, keywords: keywords, author: author, license: license, devDependencies: devDependencies, dependencies: dependencies }; /* TODO: - Fix nomenclature x Command -> Service x Device -> Browser - Popups X Basic popups - Card-mod integration X Timeout X Fullscreen - Motion/occupancy tracker x Information about interaction requirement x Information about fullykiosk - Commands x Rename browser_mod commands to browser_mod services x Framework - ll-custom handling - Commands x popup x close_popup - more-info - navigate - lovelace-reload - window-reload - screensaver x sequence x delay - toast? - Redesign services to target devices - frontend editor for popup cards - also screensavers - Tweaks - Save sidebar - Save sidebar per user - Kiosk mode - Kiosk mode per user - Favicon templates - Title templates - Quickbar tweaks (ctrl+enter)? - Video player? - Media_seek - Screensavers */ class BrowserMod extends ServicesMixin(PopupMixin(BrowserStateMixin(CameraMixin(MediaPlayerMixin(ScreenSaverMixin(FullyMixin(RequireInteractMixin(ConnectionMixin(EventTarget))))))))) { constructor() { super(); 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 %cBrowserID: ${this.browserID}`, "color: green; font-weight: bold", ""); } } if (!window.browser_mod) window.browser_mod = new BrowserMod(); export { BrowserMod };