From 91a4cea45389ced1116f3dce909981cffed76773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Sat, 24 Oct 2020 00:32:14 +0200 Subject: [PATCH] Huge restructuring of frontend code. 1.2.0 prerelease --- custom_components/browser_mod/browser_mod.js | 10 +- js/browser.js | 30 ++ js/camera.js | 46 ++ js/connection.js | 71 +++ js/fullyKiosk.js | 39 ++ js/main.js | 457 +++---------------- js/mediaPlayer.js | 51 +++ js/popups.js | 79 ++++ js/screensaver.js | 118 +++++ package-lock.json | 4 +- package.json | 2 +- test/configuration.yaml | 12 + 12 files changed, 506 insertions(+), 413 deletions(-) create mode 100644 js/browser.js create mode 100644 js/camera.js create mode 100644 js/connection.js create mode 100644 js/fullyKiosk.js create mode 100644 js/mediaPlayer.js create mode 100644 js/popups.js create mode 100644 js/screensaver.js diff --git a/custom_components/browser_mod/browser_mod.js b/custom_components/browser_mod/browser_mod.js index e452bba..825b75a 100644 --- a/custom_components/browser_mod/browser_mod.js +++ b/custom_components/browser_mod/browser_mod.js @@ -1,4 +1,4 @@ -!function(e){var t={};function o(i){if(t[i])return t[i].exports;var s=t[i]={i:i,l:!1,exports:{}};return e[i].call(s.exports,s,s.exports,o),s.l=!0,s.exports}o.m=e,o.c=t,o.d=function(e,t,i){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(o.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)o.d(i,s,function(t){return e[t]}.bind(null,s));return i},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=1)}([function(e){e.exports=JSON.parse('{"name":"browser_mod","private":true,"version":"1.1.7","description":"","scripts":{"build":"webpack","watch":"webpack --watch --mode=development","update-card-tools":"npm uninstall card-tools && npm install thomasloven/lovelace-card-tools"},"keywords":[],"author":"Thomas Lovén","license":"MIT","devDependencies":{"webpack":"^4.44.1","webpack-cli":"^3.3.12"},"dependencies":{"card-tools":"github:thomasloven/lovelace-card-tools"}}')},function(e,t,o){"use strict";o.r(t);const i="lovelace-player-device-id";function s(){if(!localStorage[i]){const e=()=>Math.floor(1e5*(1+Math.random())).toString(16).substring(1);window.fully&&"function"==typeof fully.getDeviceId?localStorage[i]=fully.getDeviceId():localStorage[i]=`${e()}${e()}-${e()}${e()}`}return localStorage[i]}let a=s();const n=new URLSearchParams(window.location.search);var r;function l(){return document.querySelector("hc-main")?document.querySelector("hc-main").hass:document.querySelector("home-assistant")?document.querySelector("home-assistant").hass:void 0}function c(e){return document.querySelector("hc-main")?document.querySelector("hc-main").provideHass(e):document.querySelector("home-assistant")?document.querySelector("home-assistant").provideHass(e):void 0}function d(){var e,t=document.querySelector("hc-main");return t?((e=t._lovelaceConfig).current_view=t._lovelacePath,e):(t=(t=(t=(t=(t=(t=(t=(t=(t=document.querySelector("home-assistant"))&&t.shadowRoot)&&t.querySelector("home-assistant-main"))&&t.shadowRoot)&&t.querySelector("app-drawer-layout partial-panel-resolver"))&&t.shadowRoot||t)&&t.querySelector("ha-panel-lovelace"))&&t.shadowRoot)&&t.querySelector("hui-root"))?((e=t.lovelace).current_view=t.___curView,e):null}function u(){var e=document.querySelector("hc-main");return e=e?(e=(e=(e=e&&e.shadowRoot)&&e.querySelector("hc-lovelace"))&&e.shadowRoot)&&e.querySelector("hui-view")||e.querySelector("hui-panel-view"):(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=document.querySelector("home-assistant"))&&e.shadowRoot)&&e.querySelector("home-assistant-main"))&&e.shadowRoot)&&e.querySelector("app-drawer-layout partial-panel-resolver"))&&e.shadowRoot||e)&&e.querySelector("ha-panel-lovelace"))&&e.shadowRoot)&&e.querySelector("hui-root"))&&e.shadowRoot)&&e.querySelector("ha-app-layout"))&&e.querySelector("#view"))&&e.firstElementChild}async function h(){if(customElements.get("hui-view"))return!0;await customElements.whenDefined("partial-panel-resolver");const e=document.createElement("partial-panel-resolver");if(e.hass={panels:[{url_path:"tmp",component_name:"lovelace"}]},e._updateRoutes(),await e.routerOptions.routes.tmp.load(),!customElements.get("ha-panel-lovelace"))return!1;const t=document.createElement("ha-panel-lovelace");return t.hass=l(),void 0===t.hass&&(await new Promise(e=>{window.addEventListener("connection-status",t=>{console.log(t),e()},{once:!0})}),t.hass=l()),t.panel={config:{mode:null}},t._fetchConfig(),!0}async function m(e,t,o=!1){let i=e;"string"==typeof t&&(t=t.split(/(\$| )/));for(const[e,s]of t.entries())if(s.trim().length){if(!i)return null;i.localName&&i.localName.includes("-")&&await customElements.whenDefined(i.localName),i.updateComplete&&await i.updateComplete,i="$"===s?o&&e==t.length-1?[i.shadowRoot]:i.shadowRoot:o&&e==t.length-1?i.querySelectorAll(s):i.querySelector(s)}return i}async function p(e,t,o=!1,i=1e4){return Promise.race([m(e,t,o),new Promise((e,t)=>setTimeout(()=>t(new Error("timeout")),i))]).catch(e=>{if(!e.message||"timeout"!==e.message)throw e;return null})}function w(e,t,o=null){if((e=new Event(e,{bubbles:!0,cancelable:!1,composed:!0})).detail=t||{},o)o.dispatchEvent(e);else{var i=u();i&&i.dispatchEvent(e)}}n.get("deviceID")&&null!==(r=n.get("deviceID"))&&("clear"===r?localStorage.removeItem(i):localStorage[i]=r,a=s());let y=window.cardHelpers;new Promise(async(e,t)=>{y&&e();const o=async()=>{y=await window.loadCardHelpers(),window.cardHelpers=y,e()};window.loadCardHelpers?o():window.addEventListener("load",async()=>{h(),window.loadCardHelpers&&o()})});async function v(e,t,o=!1,i={},s=!1){if(!customElements.get("card-tools-popup")){const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html,o=e.prototype.css;class i extends e{static get properties(){return{open:{},large:{reflect:!0,type:Boolean},hass:{}}}updated(e){e.has("hass")&&this.card&&(this.card.hass=this.hass)}closeDialog(){this.open=!1}async _makeCard(){const e=await window.loadCardHelpers();this.card=await e.createCardElement(this._card),this.card.hass=this.hass,this.requestUpdate()}async _applyStyles(){let e=await p(this,"$ ha-dialog");customElements.whenDefined("card-mod").then(async()=>{if(!e)return;customElements.get("card-mod").applyToElement(e,"more-info",this._style,{config:this._card},[],!1)})}async showDialog(e,t,o=!1,i={},s=!1){this.title=e,this._card=t,this.large=o,this._style=i,this.fullscreen=!!s,this._makeCard(),await this.updateComplete,this.open=!0,await this._applyStyles()}_enlarge(){this.large=!this.large}render(){return this.open?t` +!function(e){var t={};function o(s){if(t[s])return t[s].exports;var a=t[s]={i:s,l:!1,exports:{}};return e[s].call(a.exports,a,a.exports,o),a.l=!0,a.exports}o.m=e,o.c=t,o.d=function(e,t,s){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(o.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)o.d(s,a,function(t){return e[t]}.bind(null,a));return s},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=1)}([function(e){e.exports=JSON.parse('{"name":"browser_mod","private":true,"version":"1.2.0","description":"","scripts":{"build":"webpack","watch":"webpack --watch --mode=development","update-card-tools":"npm uninstall card-tools && npm install thomasloven/lovelace-card-tools"},"keywords":[],"author":"Thomas Lovén","license":"MIT","devDependencies":{"webpack":"^4.44.1","webpack-cli":"^3.3.12"},"dependencies":{"card-tools":"github:thomasloven/lovelace-card-tools"}}')},function(e,t,o){"use strict";o.r(t);const s="lovelace-player-device-id";function a(){if(!localStorage[s]){const e=()=>Math.floor(1e5*(1+Math.random())).toString(16).substring(1);window.fully&&"function"==typeof fully.getDeviceId?localStorage[s]=fully.getDeviceId():localStorage[s]=`${e()}${e()}-${e()}${e()}`}return localStorage[s]}let i=a();const n=new URLSearchParams(window.location.search);var r;function l(){return document.querySelector("hc-main")?document.querySelector("hc-main").hass:document.querySelector("home-assistant")?document.querySelector("home-assistant").hass:void 0}function c(e){return document.querySelector("hc-main")?document.querySelector("hc-main").provideHass(e):document.querySelector("home-assistant")?document.querySelector("home-assistant").provideHass(e):void 0}function d(){var e,t=document.querySelector("hc-main");return t?((e=t._lovelaceConfig).current_view=t._lovelacePath,e):(t=(t=(t=(t=(t=(t=(t=(t=(t=document.querySelector("home-assistant"))&&t.shadowRoot)&&t.querySelector("home-assistant-main"))&&t.shadowRoot)&&t.querySelector("app-drawer-layout partial-panel-resolver"))&&t.shadowRoot||t)&&t.querySelector("ha-panel-lovelace"))&&t.shadowRoot)&&t.querySelector("hui-root"))?((e=t.lovelace).current_view=t.___curView,e):null}function u(){var e=document.querySelector("hc-main");return e=e?(e=(e=(e=e&&e.shadowRoot)&&e.querySelector("hc-lovelace"))&&e.shadowRoot)&&e.querySelector("hui-view")||e.querySelector("hui-panel-view"):(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=document.querySelector("home-assistant"))&&e.shadowRoot)&&e.querySelector("home-assistant-main"))&&e.shadowRoot)&&e.querySelector("app-drawer-layout partial-panel-resolver"))&&e.shadowRoot||e)&&e.querySelector("ha-panel-lovelace"))&&e.shadowRoot)&&e.querySelector("hui-root"))&&e.shadowRoot)&&e.querySelector("ha-app-layout"))&&e.querySelector("#view"))&&e.firstElementChild}async function h(){if(customElements.get("hui-view"))return!0;await customElements.whenDefined("partial-panel-resolver");const e=document.createElement("partial-panel-resolver");if(e.hass={panels:[{url_path:"tmp",component_name:"lovelace"}]},e._updateRoutes(),await e.routerOptions.routes.tmp.load(),!customElements.get("ha-panel-lovelace"))return!1;const t=document.createElement("ha-panel-lovelace");return t.hass=l(),void 0===t.hass&&(await new Promise(e=>{window.addEventListener("connection-status",t=>{console.log(t),e()},{once:!0})}),t.hass=l()),t.panel={config:{mode:null}},t._fetchConfig(),!0}async function p(e,t,o=!1){let s=e;"string"==typeof t&&(t=t.split(/(\$| )/)),""===t[t.length-1]&&t.pop();for(const[e,a]of t.entries())if(a.trim().length){if(!s)return null;s.localName&&s.localName.includes("-")&&await customElements.whenDefined(s.localName),s.updateComplete&&await s.updateComplete,s="$"===a?o&&e==t.length-1?[s.shadowRoot]:s.shadowRoot:o&&e==t.length-1?s.querySelectorAll(a):s.querySelector(a)}return s}async function m(e,t,o=!1,s=1e4){return Promise.race([p(e,t,o),new Promise((e,t)=>setTimeout(()=>t(new Error("timeout")),s))]).catch(e=>{if(!e.message||"timeout"!==e.message)throw e;return null})}function y(e,t,o=null){if((e=new Event(e,{bubbles:!0,cancelable:!1,composed:!0})).detail=t||{},o)o.dispatchEvent(e);else{var s=u();s&&s.dispatchEvent(e)}}n.get("deviceID")&&null!==(r=n.get("deviceID"))&&("clear"===r?localStorage.removeItem(s):localStorage[s]=r,i=a());let w=window.cardHelpers;new Promise(async(e,t)=>{w&&e();const o=async()=>{w=await window.loadCardHelpers(),window.cardHelpers=w,e()};window.loadCardHelpers?o():window.addEventListener("load",async()=>{h(),window.loadCardHelpers&&o()})});async function _(){const e=document.querySelector("home-assistant")||document.querySelector("hc-root");y("hass-more-info",{entityId:"."},e);const t=await m(e,"$ card-tools-popup");t&&t.closeDialog()}async function v(e,t,o=!1,s={},a=!1){if(!customElements.get("card-tools-popup")){const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html,o=e.prototype.css;class s extends e{static get properties(){return{open:{},large:{reflect:!0,type:Boolean},hass:{}}}updated(e){e.has("hass")&&this.card&&(this.card.hass=this.hass)}closeDialog(){this.open=!1}async _makeCard(){const e=await window.loadCardHelpers();this.card=await e.createCardElement(this._card),this.card.hass=this.hass,this.requestUpdate()}async _applyStyles(){let e=await m(this,"$ ha-dialog");customElements.whenDefined("card-mod").then(async()=>{if(!e)return;customElements.get("card-mod").applyToElement(e,"more-info",this._style,{config:this._card},[],!1)})}async showDialog(e,t,o=!1,s={},a=!1){this.title=e,this._card=t,this.large=o,this._style=s,this.fullscreen=!!a,this._makeCard(),await this.updateComplete,this.open=!0,await this._applyStyles()}_enlarge(){this.large=!this.large}render(){return this.open?t` {if(e.state&&"cardToolsPopup"in e.state)if(e.state.cardToolsPopup){const{title:t,card:o,large:i,style:s,fullscreen:a}=e.state.params;v(t,o,i,s,a)}else n.closeDialog()};window.addEventListener("popstate",e),window._moreInfoDialogListener=!0}history.replaceState({cardToolsPopup:!1},""),history.pushState({cardToolsPopup:!0,params:{title:e,card:t,large:o,style:i,fullscreen:s}},""),n.showDialog(e,t,o,i,s)}async function g(e,t=!1){const o=document.querySelector("hc-main")||document.querySelector("home-assistant");w("hass-more-info",{entityId:e},o);const i=await p(o,"$ ha-more-info-dialog");return i&&(i.large=t),i}const _=[customElements.whenDefined("home-assistant-main"),customElements.whenDefined("hui-view")];Promise.race(_).then(()=>{const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html;e.prototype.css;class o extends e{setConfig(e){}render(){return t` + `}}customElements.define("card-tools-popup",s)}const i=document.querySelector("home-assistant")||document.querySelector("hc-root");if(!i)return;let n=await m(i,"$ card-tools-popup");if(!n){n=document.createElement("card-tools-popup");const e=i.shadowRoot.querySelector("ha-more-info-dialog");e?i.shadowRoot.insertBefore(n,e):i.shadowRoot.appendChild(n),c(n)}if(!window._moreInfoDialogListener){const e=async e=>{if(e.state&&"cardToolsPopup"in e.state)if(e.state.cardToolsPopup){const{title:t,card:o,large:s,style:a,fullscreen:i}=e.state.params;v(t,o,s,a,i)}else n.closeDialog()};window.addEventListener("popstate",e),window._moreInfoDialogListener=!0}history.replaceState({cardToolsPopup:!1},""),history.pushState({cardToolsPopup:!0,params:{title:e,card:t,large:o,style:s,fullscreen:a}},""),n.showDialog(e,t,o,s,a)}async function g(e,t=!1){const o=document.querySelector("hc-main")||document.querySelector("home-assistant");y("hass-more-info",{entityId:e},o);const s=await m(o,"$ ha-more-info-dialog");return s&&(s.large=t),s}const f=[customElements.whenDefined("home-assistant-main"),customElements.whenDefined("hui-view")];Promise.race(f).then(()=>{const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html;e.prototype.css;class o extends e{setConfig(e){}render(){return t`
Nothing to configure.
- `}}customElements.get("browser-player-editor")||(customElements.define("browser-player-editor",o),window.customCards=window.customCards||[],window.customCards.push({type:"browser-player",name:"Browser Player",preview:!0}))});const f=[customElements.whenDefined("home-assistant-main"),customElements.whenDefined("hui-view")];Promise.race(f).then(()=>{const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html,o=e.prototype.css;customElements.get("browser-player")||customElements.define("browser-player",class extends e{static get properties(){return{hass:{}}}static getConfigElement(){return document.createElement("browser-player-editor")}static getStubConfig(){return{}}setConfig(e){this._config=e}handleMute(e){window.browser_mod.mute({})}handleVolumeChange(e){const t=parseFloat(e.target.value);window.browser_mod.set_volume({volume_level:t})}handleMoreInfo(e){g("media_player."+window.browser_mod.entity_id)}handlePlayPause(e){window.browser_mod.player.paused?window.browser_mod.play({}):window.browser_mod.pause({})}render(){if(!window.browser_mod)return window.setTimeout(()=>this.requestUpdate(),100),t``;const e=window.browser_mod.player;return t` + `}}customElements.get("browser-player-editor")||(customElements.define("browser-player-editor",o),window.customCards=window.customCards||[],window.customCards.push({type:"browser-player",name:"Browser Player",preview:!0}))});const b=[customElements.whenDefined("home-assistant-main"),customElements.whenDefined("hui-view")];Promise.race(b).then(()=>{const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html,o=e.prototype.css;customElements.get("browser-player")||customElements.define("browser-player",class extends e{static get properties(){return{hass:{}}}static getConfigElement(){return document.createElement("browser-player-editor")}static getStubConfig(){return{}}setConfig(e){this._config=e}handleMute(e){window.browser_mod.mute({})}handleVolumeChange(e){const t=parseFloat(e.target.value);window.browser_mod.set_volume({volume_level:t})}handleMoreInfo(e){g("media_player."+window.browser_mod.entity_id)}handlePlayPause(e){window.browser_mod.player.paused?window.browser_mod.play({}):window.browser_mod.pause({})}render(){if(!window.browser_mod)return window.setTimeout(()=>this.requestUpdate(),100),t``;const e=window.browser_mod.player;return t`
- ${a} + ${i}
@@ -146,4 +146,4 @@ -moz-user-select: all; -ms-user-select: all; } - `}})});class b{set hass(e){if(!e)return;if(this._hass=e,this.hassPatched)return;const t=e.callService;e.callService=(e,o,i)=>{if(i&&i.deviceID)if(Array.isArray(i.deviceID)){const e=i.deviceID.indexOf("this");-1!==e&&((i=JSON.parse(JSON.stringify(i))).deviceID[e]=a)}else"this"===i.deviceID&&((i=JSON.parse(JSON.stringify(i))).deviceID=a);return t(e,o,i)},this.hassPatched=!0,document.querySelector("hc-main")?document.querySelector("hc-main").hassChanged(e,e):document.querySelector("home-assistant").hassChanged(e,e)}playOnce(e){this._video&&this._video.play(),window.browser_mod.playedOnce||(window.browser_mod.player.play(),window.browser_mod.playedOnce=!0)}async _load_lovelace(){if(!await h()){window.setTimeout(this._load_lovelace.bind(this),100)}}_connect(){window.hassConnection?window.hassConnection.then(e=>this.connect(e.conn)):window.setTimeout(()=>this._connect(),100)}constructor(){this.entity_id=a.replace("-","_"),this.cast=null!==document.querySelector("hc-main"),this.cast?this.connect(l().connection):(window.setTimeout(this._load_lovelace.bind(this),500),this._connect(),document.querySelector("home-assistant").addEventListener("hass-more-info",this.popup_card.bind(this))),this.player=new Audio,this.playedOnce=!1,this.autoclose_popup_active=!1;const e=this.update.bind(this);this.player.addEventListener("ended",e),this.player.addEventListener("play",e),this.player.addEventListener("pause",e),this.player.addEventListener("volumechange",e),document.addEventListener("visibilitychange",e),window.addEventListener("location-changed",e),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)),c(this),window.fully&&(this._fullyMotion=!1,this._motionTimeout=void 0,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=void 0,this._screenSaverTimer=void 0,this._screenSaverTime=0,this._blackout=document.createElement("div"),this._blackout.style.cssText="\n position: fixed;\n left: 0;\n top: 0;\n padding: 0;\n margin: 0;\n width: 100%;\n height: 100%;\n background: black;\n visibility: hidden;\n ",document.body.appendChild(this._blackout);const t=o(0);console.info(`%cBROWSER_MOD ${t.version} IS INSTALLED\n %cDeviceID: ${a}`,"color: green; font-weight: bold","")}connect(e){this.conn=e,e.subscribeMessage(e=>this.callback(e),{type:"browser_mod/connect",deviceID:a})}callback(e){switch(e.command){case"update":this.update(e);break;case"debug":this.debug(e);break;case"play":this.play(e);break;case"pause":this.pause(e);break;case"stop":this.stop(e);break;case"set_volume":this.set_volume(e);break;case"mute":this.mute(e);break;case"toast":this.toast(e);break;case"popup":this.popup(e);break;case"close-popup":this.close_popup(e);break;case"navigate":this.navigate(e);break;case"more-info":this.more_info(e);break;case"set-theme":this.set_theme(e);break;case"lovelace-reload":this.lovelace_reload(e);break;case"blackout":this.blackout(e);break;case"no-blackout":this.no_blackout(e)}}get player_state(){return this.player.src?this.player.ended?"stopped":this.player.paused?"paused":"playing":"stopped"}popup_card(e){if(!d())return;const t=d(),o={...t.config.popup_cards,...t.config.views[t.current_view].popup_cards};if(!e.detail||!e.detail.entityId)return;const i=o[e.detail.entityId];i&&window.setTimeout(()=>{w("hass-more-info",{entityId:"."},document.querySelector("home-assistant")),v(i.title,i.card,i.large||!1,i.style)},50)}debug(e){v("deviceID",{type:"markdown",content:"# "+a}),alert(a)}_set_screensaver(e,t){if(clearTimeout(this._screenSaverTimer),e){if(-1==(t=parseInt(t)))return clearTimeout(this._screenSaverTimer),void(this._screenSaverTime=0);this._screenSaverTime=1e3*t,this._screenSaver=e,this._screenSaverTimer=setTimeout(this._screenSaver,this._screenSaverTime)}else this._screenSaverTime&&(this._screenSaverTimer=setTimeout(this._screenSaver,this._screenSaverTime))}play(e){const t=e.media_content_id;t&&(this.player.src=t),this.player.play()}pause(e){this.player.pause()}stop(e){this.player.pause(),this.player.src=null}set_volume(e){void 0!==e.volume_level&&(this.player.volume=e.volume_level)}mute(e){void 0===e.mute&&(e.mute=!this.player.muted),this.player.muted=Boolean(e.mute)}toast(e){e.message&&w("hass-notification",{message:e.message,duration:void 0!==e.duration?parseInt(e.duration):void 0},document.querySelector("home-assistant"))}popup(e){if(!e.title&&!e.auto_close)return;if(!e.card)return;const t=()=>{v(e.title,e.card,e.large,e.style,e.auto_close||e.hide_header),e.auto_close&&(this.autoclose_popup_active=!0)};e.auto_close&&e.time?this._set_screensaver(t,e.time):t()}close_popup(e){this._set_screensaver(),this.autoclose_popup_active=!1,async function(){const e=document.querySelector("home-assistant")||document.querySelector("hc-root");w("hass-more-info",{entityId:"."},e);const t=await p(e,"$ card-tools-popup");t&&t.closeDialog()}()}navigate(e){e.navigation_path&&(history.pushState(null,"",e.navigation_path),w("location-changed",{},document.querySelector("home-assistant")))}more_info(e){e.entity_id&&g(e.entity_id,e.large)}set_theme(e){e.theme||(e.theme="default"),w("settheme",e.theme,document.querySelector("home-assistant"))}lovelace_reload(e){const t=u();t&&w("config-refresh",{},t)}blackout(e){const t=()=>{window.fully?fully.turnScreenOff():this._blackout.style.visibility="visible",this.update()};e.time?this._set_screensaver(t,e.time):t()}no_blackout(e){if(this._set_screensaver(),this.autoclose_popup_active)return this.close_popup();window.fully?(fully.getScreenOn()||fully.turnScreenOn(),e.brightness&&fully.setScreenBrightness(e.brightness),this.update()):"hidden"!==this._blackout.style.visibility&&(this._blackout.style.visibility="hidden",this.update())}is_blackout(){return window.fully?!fully.getScreenOn():Boolean("visible"===this._blackout.style.visibility)}fullyMotion(){this._fullyMotion=!0,clearTimeout(this._motionTimeout),this._motionTimeout=setTimeout(()=>{this._fullyMotion=!1,this.update()},5e3),this.update()}start_camera(){this._video||(this._video=document.createElement("video"),this._video.autoplay=!0,this._video.playsInline=!0,this._video.style.cssText="\n visibility: hidden;\n width: 0;\n height: 0;\n ",this._canvas=document.createElement("canvas"),this._canvas.style.cssText="\n visibility: hidden;\n width: 0;\n height: 0;\n ",document.body.appendChild(this._canvas),document.body.appendChild(this._video),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia({video:!0,audio:!1}).then(e=>{this._video.srcObject=e,this._video.play(),this.send_cam()}))}send_cam(e){this._canvas.width=this._video.videoWidth,this._canvas.height=this._video.videoHeight;this._canvas.getContext("2d").drawImage(this._video,0,0,this._video.videoWidth,this._video.videoHeight),this.conn.sendMessage({type:"browser_mod/update",deviceID:a,data:{camera:this._canvas.toDataURL("image/jpeg")}}),setTimeout(this.send_cam.bind(this),500)}update(e=null){this.conn&&(e&&(e.name&&(this.entity_id=e.name.toLowerCase()),e.camera&&this.start_camera()),this.conn.sendMessage({type:"browser_mod/update",deviceID:a,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||void 0,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():void 0},fully:window.fully?{battery:window.fully?fully.getBatteryLevel():void 0,charging:window.fully?fully.isPlugged():void 0,motion:window.fully?this._fullyMotion:void 0}:void 0}}))}}const S=[customElements.whenDefined("home-assistant-main"),customElements.whenDefined("hui-view")];Promise.race(S).then(()=>{window.browser_mod=window.browser_mod||new b})}]); \ No newline at end of file + `}})});class S{async connect(){if(null!==document.querySelector("hc-main"))this._connection=l().connection;else{if(!window.hassConnection)return void window.setTimeout(()=>this._do_connect(),100);this._connection=(await window.hassConnection).conn}this._connection.subscribeMessage(e=>this.msg_callback(e),{type:"browser_mod/connect",deviceID:i}),this._hass_patched=!1,c(this)}set hass(e){if(this._hass=e,!e||this._hass_patched)return;this._hass_patched=!0;const t=e.callService;e.callService=(e,o,s)=>{if(s&&s.deviceID){s=JSON.parse(JSON.stringify(s));const e=JSON.stringify(s.deviceID).replace('"this"',`"${i}"`);s.deviceID=JSON.parse(e)}return t(e,o,s)},document.querySelector("hc-main")?document.querySelector("hc-main").hassChanged(e,e):document.querySelector("home-assistant").hassChanged(e,e)}get connected(){return void 0!==this._connection}msg_callback(e){console.log(e)}sendUpdate(e){this.connected&&this._connection.sendMessage({type:"browser_mod/update",deviceID:i,data:e})}}const x=e=>class extends e{constructor(){super(),this.player=new Audio;for(const e of["play","pause","ended","volumechange"])this.player.addEventListener(e,()=>this.player_update());window.addEventListener("click",()=>this.player.play(),{once:!0})}player_update(e){this.sendUpdate({player:{volume:this.player.volume,muted:this.player.muted,src:this.player.src,state:this.player_state}})}get player_state(){return this.player.src?this.player.ended?"stopped":this.player.paused?"paused":"playing":"stopped"}player_play(e){e&&(this.player.src=e),this.player.play()}player_pause(){this.player.pause()}player_stop(){this.player.pause(),this.player.src=null}player_set_volume(e){void 0!==e&&(this.player.volume=e)}player_mute(e){void 0===e&&(e=!this.player.muted),this.player.muted=Boolean(e)}},E=e=>class extends e{get isFully(){return void 0!==window.fully}constructor(){if(super(),this.isFully){this._fullyMotion=!1,this._motionTimeout=void 0;for(const e of["screenOn","screenOff","pluggedAC","pluggedUSB","onBatteryLevelChanged","unplugged","networkReconnect"])fully.bind(e,"window.browser_mod.fully_update();");fully.bind("onMotion","window.browser_mod.fullyMotionTriggered();")}}fully_update(){this.isFully&&this.sendUpdate({fully:{battery:fully.getBatteryLevel(),charging:fully.isPlugged(),motion:this._fullyMotion}})}fullyMotionTriggered(){this._fullyMotion=!0,clearTimeout(this._motionTimeout),this._motionTimeout=setTimeout(()=>{this._fullyMotion=!1,this.fully_update()},5e3),this.fully_update()}},k=e=>class extends e{setup_camera(){console.log("Starting camera"),this._video||(this._video=document.createElement("video"),this._video.autoplay=!0,this._video.playsInline=!0,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),navigator.mediaDevices&&(console.log("Starting devices"),navigator.mediaDevices.getUserMedia({video:!0,audio:!1}).then(e=>{this._video.srcObject=e,this._video.play(),this.update_camera()}),this._camera_framerate=2,window.addEventListener("click",()=>this._video.play(),{once:!0})))}update_camera(){this._canvas.width=this._video.videoWidth,this._canvas.height=this._video.videoHeight;this._canvas.getContext("2d").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(1e3/this._camera_framerate))}},q=e=>class extends e{constructor(){super(),this._blackout_panel=document.createElement("div"),this._screenSaver=void 0,this._screenSaverTimer=void 0,this._screenSaverTimeOut=0,this._screenSaver={fn:void 0,clearfn:void 0,timer:void 0,timeout:void 0,listeners:{},active:!1},this._blackout_panel.style.cssText="\n position: fixed;\n left: 0;\n top: 0;\n padding: 0;\n margin: 0;\n width: 100%;\n height: 100%;\n background: black;\n display: none;\n ",document.body.appendChild(this._blackout_panel)}screensaver_set(e,t,o){this._ss_clear(),this._screenSaver={fn:e,clearfn:t,timer:void 0,timeout:o,listeners:{},active:!1};const s=()=>this.screensaver_update();for(const e of["mousemove","mousedown","keydown","touchstart"])window.addEventListener(e,s),this._screenSaver.listeners[e]=s;this._screenSaver.timer=window.setTimeout(()=>this._ss_run(),1e3*o)}screensaver_update(){this._screenSaver.active?this.screensaver_stop():(window.clearTimeout(this._screenSaver.timer),this._screenSaver.timer=window.setTimeout(()=>this._ss_run(),1e3*this._screenSaver.timeout))}screensaver_stop(){this._ss_clear(),this._screenSaver.active=!1,this._screenSaver.clearfn&&this._screenSaver.clearfn(),this._screenSaver.timeout&&this.screensaver_set(this._screenSaver.fn,this._screenSaver.clearfn,this._screenSaver.timeout)}_ss_clear(){window.clearTimeout(this._screenSaverTimer);for(const[e,t]of Object.entries(this._screenSaver.listeners))window.removeEventListener(e,t)}_ss_run(){this._screenSaver.active=!0,this._screenSaver.fn()}do_blackout(e){this.screensaver_set(()=>{this.isFully?window.fully.turnScreenOff():this._blackout_panel.style.display="block",this.screen_update()},()=>{(this._blackout_panel.style.display="block")&&(this._blackout_panel.style.display="none"),this.isFully&&!window.fully.getScreenOn()&&window.fully.turnScreenOn(),this.screen_update()},e||0)}no_blackout(){this.screensaver_stop()}screen_update(){this.sendUpdate({screen:{blackout:this.isFully?window.fully.getScreenOn():Boolean("block"===this._blackout_panel.style.display),brightness:this.isFully?window.fully.getScreenBrightness():void 0}})}},D=e=>class extends e{constructor(){super(),document.querySelector("home-assistant")&&document.querySelector("home-assistant").addEventListener("hass-more-info",e=>this._popup_card(e));null!==document.querySelector("hc-main")||h()}_popup_card(e){if(!d)return;if(!e.detail||!e.detail.entityId)return;const t={...d().config.popup_cards,...d().config.views[d().current_view].popup_cards}[e.detail.entityId];t&&window.queueMicrotask(()=>{y("hass-more-info",{entityID:"."},document.querySelector("home-assistant")),v(t.title,t.card,t.large||!1,t.style)})}do_popup(e){if(!(e.title||e.auto_close||e.hide_header))return;if(!e.card)return;const t=()=>{v(e.tile,e.card,e.large,e.style,e.auto_close||e.hide_header)};e.auto_close?this.screensaver_set(t,_,e.time):t()}do_close_popup(){this.screensaver_stop(),_()}do_more_info(e,t){e&&g(e,t)}do_toast(e,t){e&&y("hass-notification",{message:e,duration:parseInt(t)},document.querySelector("home-assistant"))}},O=e=>class extends e{constructor(){super(),document.addEventListener("visibilitychange",()=>this.sensor_update()),window.addEventListener("location-changed",()=>this.sensor_update()),window.setInterval(()=>this.sensor_update(),1e4)}sensor_update(){this.sendUpdate({browser:{path:window.location.pathname,visibility:document.visibilityState,userAgent:navigator.userAgent,currentUser:this._hass&&this._hass.user&&this._hass.user.name,fullyKiosk:this.isFully,width:window.innerWidth,height:window.innerHeight}})}do_navigate(e){e&&(history.pushState(null,"",e),y("location-changed",{},document.querySelector("home-assistant")))}};class C extends(O(D(q(k(E(x(S))))))){constructor(){super(),this.entity_id=i.replace("-","_"),this.cast=null!==document.querySelector("hc-main"),this.connect();const e=o(0);console.info(`%cBROWSER_MOD ${e.version} IS INSTALLED\n %cDeviceID: ${i}`,"color: green; font-weight: bold","")}msg_callback(e){({update:e=>this.update(e),debug:e=>this.debug(e),play:e=>this.player_play(e.media_content_id),pause:e=>this.player_pause(),stop:e=>this.player_stop(),set_volume:e=>this.player_set_volume(e.volume_level),mute:e=>this.player_mute(e.mute),toast:e=>this.do_toast(e.message,e.duration),popup:e=>this.do_popup(e),"close-popup":e=>this.do_close_popup(),"more-info":e=>this.do_more_info(e.entity_id,e.large),navigate:e=>this.do_navigate(e.navigation_path),"set-theme":e=>this.set_theme(e),"lovelace-reload":e=>this.lovelace_reload(e),blackout:e=>this.do_blackout(e.time?parseInt(e.time):void 0),"no-blackout":e=>{e.brightness&&this.isFully&&window.fully.setScreenBrightness(e.brightness),this.no_blackout()}})[e.command](e)}debug(e){v("deviceID",{type:"markdown",content:"# "+i}),alert(i)}set_theme(e){e.theme||(e.theme="default"),y("settheme",e.theme,document.querySelector("home-assistant"))}lovelace_reload(e){const t=u();t&&y("config-refresh",{},t)}update(e=null){e&&(e.name&&(this.entity_id=e.name.toLowerCase()),e.camera&&this.setup_camera()),this.player_update(),this.fully_update(),this.screen_update(),this.sensor_update()}}const $=[customElements.whenDefined("home-assistant"),customElements.whenDefined("hc-main")];Promise.race($).then(()=>{window.browser_mod=window.browser_mod||new C,window.bm=e=>window.browser_mod.msg_callback(e)})}]); \ No newline at end of file diff --git a/js/browser.js b/js/browser.js new file mode 100644 index 0000000..ff4a610 --- /dev/null +++ b/js/browser.js @@ -0,0 +1,30 @@ +import { fireEvent } from "card-tools/src/event"; + +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() { + this.sendUpdate({browser: { + path: window.location.pathname, + visibility: document.visibilityState, + userAgent: navigator.userAgent, + currentUser: this._hass &&this._hass.user && this._hass.user.name, + fullyKiosk: this.isFully, + width: window.innerWidth, + height: window.innerHeight, + }}); + } + + do_navigate(path) { + if (!path) return; + history.pushState(null, "", path); + fireEvent("location-changed", {}, document.querySelector("home-assistant")); + } +} diff --git a/js/camera.js b/js/camera.js new file mode 100644 index 0000000..1212c89 --- /dev/null +++ b/js/camera.js @@ -0,0 +1,46 @@ +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", () => 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)); + } + +} diff --git a/js/connection.js b/js/connection.js new file mode 100644 index 0000000..9ae7621 --- /dev/null +++ b/js/connection.js @@ -0,0 +1,71 @@ +import { deviceID } from "card-tools/src/deviceId"; +import { hass, provideHass } from "card-tools/src/hass"; + +export class BrowserModConnection{ + + async connect() { + const isCast = document.querySelector("hc-main") !== null; + if(!isCast) { + if(!window.hassConnection) { + window.setTimeout(() => this._do_connect(), 100); + return; + } else { + 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, + }); + + this._hass_patched = false; + provideHass(this); + } + + set hass(hass) { + this._hass = hass; + if(!hass || this._hass_patched) return; + + this._hass_patched = true; + const callService = hass.callService; + + hass.callService = (domain, service, serviceData) => { + if(serviceData && serviceData.deviceID) { + serviceData = JSON.parse(JSON.stringify(serviceData)); + + const orig = JSON.stringify(serviceData.deviceID); + const patched = orig.replace('"this"', `"${deviceID}"`); + serviceData.deviceID = JSON.parse(patched); + } + return callService(domain, service, serviceData); + } + + if (document.querySelector("hc-main")) + document.querySelector("hc-main").hassChanged(hass, hass); + else + document.querySelector("home-assistant").hassChanged(hass, hass); + } + + 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, + } + ) + } + + +} diff --git a/js/fullyKiosk.js b/js/fullyKiosk.js new file mode 100644 index 0000000..e1521e2 --- /dev/null +++ b/js/fullyKiosk.js @@ -0,0 +1,39 @@ +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 event of ["screenOn", "screenOff", "pluggedAC", "pluggedUSB", "onBatteryLevelChanged", "unplugged", "networkReconnect"]) { + fully.bind(event, "window.browser_mod.fully_update();"); + } + + fully.bind("onMotion", "window.browser_mod.fullyMotionTriggered();"); + } + + fully_update() { + if(!this.isFully) return + this.sendUpdate({fully: { + battery: fully.getBatteryLevel(), + charging: fully.isPlugged(), + motion: this._fullyMotion, + }}) + } + + fullyMotionTriggered() { + this._fullyMotion = true; + clearTimeout(this._motionTimeout); + this._motionTimeout = setTimeout(() => { + this._fullyMotion = false; + this.fully_update(); + }, 5000); + this.fully_update(); + } +} diff --git a/js/main.js b/js/main.js index 4f30c11..5e3d03e 100644 --- a/js/main.js +++ b/js/main.js @@ -1,123 +1,33 @@ 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 { lovelace_view } from "card-tools/src/hass"; +import { popUp } 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 { +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"; - 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; +class BrowserMod extends + BrowserModBrowserMixin( + BrowserModPopupsMixin( + BrowserModScreensaverMixin( + BrowserModCameraMixin( + FullyKioskMixin( + BrowserModMediaPlayerMixin( + BrowserModConnection +)))))) { - 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("-","_"); + super(); + 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); + this.connect(); const pjson = require('../package.json'); console.info(`%cBROWSER_MOD ${pjson.version} IS INSTALLED @@ -125,94 +35,36 @@ class BrowserMod { "color: green; font-weight: bold", ""); } - connect(conn) { - this.conn = conn - conn.subscribeMessage((msg) => this.callback(msg), { - type: 'browser_mod/connect', - deviceID: deviceID, - }); - } + msg_callback(msg) { + const handlers = { + update: (msg) => this.update(msg), + debug: (msg) => this.debug(msg), - callback(msg) { - switch (msg.command) { - case "update": - this.update(msg); - break; + 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), - case "debug": - this.debug(msg); - break; + 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), - 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; + navigate: (msg) => this.do_navigate(msg.navigation_path), + "set-theme": (msg) => this.set_theme(msg), + "lovelace-reload": (msg) => this.lovelace_reload(msg), - 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, + blackout: (msg) => this.do_blackout(msg.time ? parseInt(msg.time) : undefined), + "no-blackout": (msg) => { + if(msg.brightness && this.isFully) { + window.fully.setScreenBrightness(msg.brightness); + } + this.no_blackout() + }, }; - 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); + handlers[msg.command](msg); } debug(msg) { @@ -220,87 +72,6 @@ class BrowserMod { 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")); @@ -312,150 +83,26 @@ class BrowserMod { 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.setup_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, - }, - }); - + this.player_update(); + this.fully_update(); + this.screen_update(); + this.sensor_update(); } } -const bases = [customElements.whenDefined('home-assistant-main'), customElements.whenDefined('hui-view')]; +const bases = [customElements.whenDefined('home-assistant'), customElements.whenDefined('hc-main')]; Promise.race(bases).then(() => { window.browser_mod = window.browser_mod || new BrowserMod(); + window.bm = (cmd) => window.browser_mod.msg_callback(cmd); }); diff --git a/js/mediaPlayer.js b/js/mediaPlayer.js new file mode 100644 index 0000000..c2fd804 --- /dev/null +++ b/js/mediaPlayer.js @@ -0,0 +1,51 @@ +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", () => 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); + } +} diff --git a/js/popups.js b/js/popups.js new file mode 100644 index 0000000..1985f55 --- /dev/null +++ b/js/popups.js @@ -0,0 +1,79 @@ +import { fireEvent } from "card-tools/src/event"; +import { load_lovelace, lovelace } 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; + + window.queueMicrotask(() => { + fireEvent("hass-more-info", {entityID: "."}, document.querySelector("home-assistant")); + popUp( + d.title, + d.card, + d.large || false, + d.style + ); + }); + } + + do_popup(cfg) { + if (!(cfg.title || cfg.auto_close || cfg.hide_header)) return; + if (!cfg.card) return; + + const open = () => { + popUp( + cfg.tile, + 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), + }, document.querySelector("home-assistant")); + } + + +} diff --git a/js/screensaver.js b/js/screensaver.js new file mode 100644 index 0000000..33ff757 --- /dev/null +++ b/js/screensaver.js @@ -0,0 +1,118 @@ +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, v); + } + } + + _ss_run() { + this._screenSaver.active = true; + this._screenSaver.fn(); + } + + do_blackout(timeout) { + this.screensaver_set( + () => { + if(this.isFully) + window.fully.turnScreenOff(); + 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 && !window.fully.getScreenOn()) + window.fully.turnScreenOn(); + this.screen_update(); + }, + timeout || 0 + ); + } + + no_blackout() { + this.screensaver_stop(); + } + + screen_update() { + this.sendUpdate({screen: { + blackout: this.isFully + ? window.fully.getScreenOn() + : Boolean(this._blackout_panel.style.display === "block"), + brightness: this.isFully ? window.fully.getScreenBrightness() : undefined, + }}) + } +} diff --git a/package-lock.json b/package-lock.json index 9d205f2..4414228 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "browser_mod", - "version": "1.1.6", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -655,7 +655,7 @@ "dev": true }, "card-tools": { - "version": "github:thomasloven/lovelace-card-tools#6d5ae3800e4937aa424edc17108f43b964aecce7", + "version": "github:thomasloven/lovelace-card-tools#1272cf67c56e8f576e24c13f510568d544ad5d0b", "from": "github:thomasloven/lovelace-card-tools" }, "chalk": { diff --git a/package.json b/package.json index 1dd245c..4e4220e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "browser_mod", "private": true, - "version": "1.1.7", + "version": "1.2.0", "description": "", "scripts": { "build": "webpack", diff --git a/test/configuration.yaml b/test/configuration.yaml index fe698b1..4be3ad6 100644 --- a/test/configuration.yaml +++ b/test/configuration.yaml @@ -6,6 +6,8 @@ browser_mod: devices: camdevice: camera: true + testdevice: + alias: test lovelace: mode: yaml @@ -27,3 +29,13 @@ frontend: .mdc-dialog { backdrop-filter: grayscale(0.7) blur(5px); } + +tts: + - platform: google_translate + base_url: http://localhost:5001 + + +script: + cm_debug: + sequence: + - service: browser_mod.debug