From ae611a88006a7a53045c572409e3e4ea95b5fb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Sat, 29 Jun 2019 00:15:16 +0200 Subject: [PATCH] Add blackout and no-blackout commands --- README.md | 26 +++++++++++++++ custom_components/browser_mod/browser_mod.js | 6 ++-- js/main.js | 35 ++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5fcf3fe..ff7ffbe 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ The `media_player` entity also has some extra attributes presenting the current | `visibility` | Whether the frontend is currently visible on the *device*. | | `userAgent` | The User Agent of the associated browser. | | `currentUser` | The user currently logged in on the *device*. | +| 'blackout' | Whether the view on the *device* is currently blacked out (see below). | **NOTE: Because apple is apple; on iOS you need to touch the screen once after loading the frontend before any playback will work.** @@ -161,6 +162,27 @@ service_data: will close all more-info dialogs and popups that are open on all connected *devices*. +### blackout +``` +service: browser_mod.command +service_data: + command: blackout +``` + +Will cover the entire window (or screen if in full screen mode) with black. +Moving the mouse, touching the screen or pressing any key will restore the view. + +Note: This will *not* turn off your screen backlight. Most screens will still emit light in a dark room. + +### no-blackout +``` +service: browser_mod.command +service_data: + command: no-blackout +``` + +Remove a blackout. + ## `browser-player` card To control the playback in the current *device*, `browser_mod` includes a custom lovelace card. Just add @@ -175,6 +197,10 @@ The player card also displays the `entityID`. Click it to select, so you can cop ![browser-player](https://user-images.githubusercontent.com/1299821/60288980-a4d07a80-9915-11e9-88ba-e078a3aa24f4.png) +# Support + +[Home Assistant community forum thread](https://community.home-assistant.io/t/browser-mod-turn-your-browser-into-a-controllable-device-and-a-media-player/123806) + # FAQ ### Does this replace lovelace-player and lovelace-browser-commander diff --git a/custom_components/browser_mod/browser_mod.js b/custom_components/browser_mod/browser_mod.js index bfb4196..5f03c46 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 n(o){if(t[o])return t[o].exports;var s=t[o]={i:o,l:!1,exports:{}};return e[o].call(s.exports,s,s.exports,n),s.l=!0,s.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(o,s,function(t){return e[t]}.bind(null,s));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);let o=function(){if(window.fully&&"function"==typeof fully.getDeviceId)return fully.getDeviceId();if(!localStorage["lovelace-player-device-id"]){const e=()=>Math.floor(1e5*(1+Math.random())).toString(16).substring(1);localStorage["lovelace-player-device-id"]=`${e()}${e()}-${e()}${e()}`}return localStorage["lovelace-player-device-id"]}();function s(e){return document.querySelector("home-assistant").provideHass(e)}function r(e,t,n=null){if((e=new Event(e,{bubbles:!0,cancelable:!1,composed:!0})).detail=t||{},n)n.dispatchEvent(e);else{var o=document.querySelector("home-assistant");(o=(o=(o=(o=(o=(o=(o=(o=(o=(o=(o=o&&o.shadowRoot)&&o.querySelector("home-assistant-main"))&&o.shadowRoot)&&o.querySelector("app-drawer-layout partial-panel-resolver"))&&o.shadowRoot||o)&&o.querySelector("ha-panel-lovelace"))&&o.shadowRoot)&&o.querySelector("hui-root"))&&o.shadowRoot)&&o.querySelector("ha-app-layout #view"))&&o.firstElementChild)&&o.dispatchEvent(e)}}const a="custom:";function i(e,t){const n=document.createElement("hui-error-card");return n.setConfig({type:"error",error:e,config:t}),n}function l(e,t){if(!t||"object"!=typeof t||!t.type)return i(`No ${e} type configured`,t);let n=t.type;if(n=n.startsWith(a)?n.substr(a.length):`hui-${n}-${e}`,customElements.get(n))return function(e,t){const n=document.createElement(e);try{n.setConfig(t)}catch(e){return i(e,t)}return n}(n,t);const o=i(`Custom element doesn't exist: ${n}.`,t);o.style.display="None";const s=setTimeout(()=>{o.style.display=""},2e3);return customElements.whenDefined(n).then(()=>{clearTimeout(s),r("ll-rebuild",{},o)}),o}function c(e,t=!1){r("hass-more-info",{entityId:e},document.querySelector("home-assistant"));const n=document.querySelector("home-assistant")._moreInfoEl;return n.large=t,n}const u=Object.getPrototypeOf(customElements.get("home-assistant-main")),d=u.prototype.html,p=u.prototype.css;class h extends u{static get properties(){return{hass:{},config:{}}}setConfig(e){this._config=e,this.el?this.el.setConfig(e):this.el=this.create(e),this._hass&&(this.el.hass=this._hass),this.noHass&&s(this)}set config(e){this.setConfig(e)}set hass(e){this._hass=e,this.el&&(this.el.hass=e)}createRenderRoot(){return this}render(){return d`${this.el}`}}if(!customElements.get("card-maker")){class e extends h{create(e){return function(e){return l("card",e)}(e)}}customElements.define("card-maker",e)}if(!customElements.get("element-maker")){class e extends h{create(e){return function(e){return l("element",e)}(e)}}customElements.define("element-maker",e)}if(!customElements.get("entity-row-maker")){class e extends h{create(e){return function(e){const t=new Set(["call-service","divider","section","weblink"]);if(!e)return i("Invalid configuration given.",e);if("string"==typeof e&&(e={entity:e}),"object"!=typeof e||!e.entity&&!e.type)return i("Invalid configuration given.",e);const n=e.type||"default";if(t.has(n)||n.startsWith(a))return l("row",e);const o=e.entity.split(".",1)[0];return Object.assign(e,{type:{alert:"toggle",automation:"toggle",climate:"climate",cover:"cover",fan:"toggle",group:"group",input_boolean:"toggle",input_number:"input-number",input_select:"input-select",input_text:"input-text",light:"toggle",lock:"lock",media_player:"media-player",remote:"toggle",scene:"scene",script:"script",sensor:"sensor",timer:"timer",switch:"toggle",vacuum:"toggle",water_heater:"climate",input_datetime:"input-datetime"}[o]||"text"}),l("entity-row",e)}(e)}}customElements.define("entity-row-maker",e)}function m(e,t,n=!1,o=null){c(Object.keys(document.querySelector("home-assistant").hass.states)[0]);const s=document.createElement("card-maker");s.noHass=!0,s.config=t;const r=document.createElement("div");r.innerHTML=`\n \n \n \n
\n ${e}\n
\n
\n `,r.appendChild(s);const a=document.querySelector("home-assistant")._moreInfoEl;a.large=n,a._page="none",a.shadowRoot.appendChild(r);let i={};if(o)for(var l in o)i[l]=a.style[l],a.style.setProperty(l,o[l]);return setTimeout(()=>{let e=setInterval(()=>{if(a.getAttribute("aria-hidden"))for(var t in clearInterval(e),r.parentNode.removeChild(r),i)a.style.setProperty(t,i[t])},100)},1e3),a}customElements.define("browser-player",class extends u{static get properties(){return{hass:{}}}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){c(window.browser_mod.entity_id)}handlePlayPause(e){window.browser_mod.player.paused?window.browser_mod.play({}):window.browser_mod.pause({})}render(){const e=window.browser_mod.player;return d` +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var s=t[o]={i:o,l:!1,exports:{}};return e[o].call(s.exports,s,s.exports,n),s.l=!0,s.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(o,s,function(t){return e[t]}.bind(null,s));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);let o=function(){if(window.fully&&"function"==typeof fully.getDeviceId)return fully.getDeviceId();if(!localStorage["lovelace-player-device-id"]){const e=()=>Math.floor(1e5*(1+Math.random())).toString(16).substring(1);localStorage["lovelace-player-device-id"]=`${e()}${e()}-${e()}${e()}`}return localStorage["lovelace-player-device-id"]}();function s(e){return document.querySelector("home-assistant").provideHass(e)}function i(e,t,n=null){if((e=new Event(e,{bubbles:!0,cancelable:!1,composed:!0})).detail=t||{},n)n.dispatchEvent(e);else{var o=document.querySelector("home-assistant");(o=(o=(o=(o=(o=(o=(o=(o=(o=(o=(o=o&&o.shadowRoot)&&o.querySelector("home-assistant-main"))&&o.shadowRoot)&&o.querySelector("app-drawer-layout partial-panel-resolver"))&&o.shadowRoot||o)&&o.querySelector("ha-panel-lovelace"))&&o.shadowRoot)&&o.querySelector("hui-root"))&&o.shadowRoot)&&o.querySelector("ha-app-layout #view"))&&o.firstElementChild)&&o.dispatchEvent(e)}}const a="custom:";function r(e,t){const n=document.createElement("hui-error-card");return n.setConfig({type:"error",error:e,config:t}),n}function l(e,t){if(!t||"object"!=typeof t||!t.type)return r(`No ${e} type configured`,t);let n=t.type;if(n=n.startsWith(a)?n.substr(a.length):`hui-${n}-${e}`,customElements.get(n))return function(e,t){const n=document.createElement(e);try{n.setConfig(t)}catch(e){return r(e,t)}return n}(n,t);const o=r(`Custom element doesn't exist: ${n}.`,t);o.style.display="None";const s=setTimeout(()=>{o.style.display=""},2e3);return customElements.whenDefined(n).then(()=>{clearTimeout(s),i("ll-rebuild",{},o)}),o}function c(e,t=!1){i("hass-more-info",{entityId:e},document.querySelector("home-assistant"));const n=document.querySelector("home-assistant")._moreInfoEl;return n.large=t,n}const d=Object.getPrototypeOf(customElements.get("home-assistant-main")),u=d.prototype.html,p=d.prototype.css;class h extends d{static get properties(){return{hass:{},config:{}}}setConfig(e){this._config=e,this.el?this.el.setConfig(e):this.el=this.create(e),this._hass&&(this.el.hass=this._hass),this.noHass&&s(this)}set config(e){this.setConfig(e)}set hass(e){this._hass=e,this.el&&(this.el.hass=e)}createRenderRoot(){return this}render(){return u`${this.el}`}}if(!customElements.get("card-maker")){class e extends h{create(e){return function(e){return l("card",e)}(e)}}customElements.define("card-maker",e)}if(!customElements.get("element-maker")){class e extends h{create(e){return function(e){return l("element",e)}(e)}}customElements.define("element-maker",e)}if(!customElements.get("entity-row-maker")){class e extends h{create(e){return function(e){const t=new Set(["call-service","divider","section","weblink"]);if(!e)return r("Invalid configuration given.",e);if("string"==typeof e&&(e={entity:e}),"object"!=typeof e||!e.entity&&!e.type)return r("Invalid configuration given.",e);const n=e.type||"default";if(t.has(n)||n.startsWith(a))return l("row",e);const o=e.entity.split(".",1)[0];return Object.assign(e,{type:{alert:"toggle",automation:"toggle",climate:"climate",cover:"cover",fan:"toggle",group:"group",input_boolean:"toggle",input_number:"input-number",input_select:"input-select",input_text:"input-text",light:"toggle",lock:"lock",media_player:"media-player",remote:"toggle",scene:"scene",script:"script",sensor:"sensor",timer:"timer",switch:"toggle",vacuum:"toggle",water_heater:"climate",input_datetime:"input-datetime"}[o]||"text"}),l("entity-row",e)}(e)}}customElements.define("entity-row-maker",e)}function m(e,t,n=!1,o=null){c(Object.keys(document.querySelector("home-assistant").hass.states)[0]);const s=document.createElement("card-maker");s.noHass=!0,s.config=t;const i=document.createElement("div");i.innerHTML=`\n \n \n \n
\n ${e}\n
\n
\n `,i.appendChild(s);const a=document.querySelector("home-assistant")._moreInfoEl;a.large=n,a._page="none",a.shadowRoot.appendChild(i);let r={};if(o)for(var l in o)r[l]=a.style[l],a.style.setProperty(l,o[l]);return setTimeout(()=>{let e=setInterval(()=>{if(a.getAttribute("aria-hidden"))for(var t in clearInterval(e),i.parentNode.removeChild(i),r)a.style.setProperty(t,r[t])},100)},1e3),a}customElements.define("browser-player",class extends d{static get properties(){return{hass:{}}}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){c(window.browser_mod.entity_id)}handlePlayPause(e){window.browser_mod.player.paused?window.browser_mod.play({}):window.browser_mod.pause({})}render(){const e=window.browser_mod.player;return u`
- ${"stopped"===window.browser_mod.player_state?d`
`:d` + ${"stopped"===window.browser_mod.player_state?u`
`:u` {if("browser_mod"===e&&"command"===n&&s.deviceID){const e=s.deviceID.indexOf("this");-1!==e&&(s.deviceID[e]=o)}t(e,n,s)},this.hassPatched=!0,document.querySelector("home-assistant").hassChanged(e,e)}playOnce(e){window.browser_mod.playedOnce||(window.browser_mod.player.play(),window.browser_mod.playedOnce=!0)}constructor(){window.hassConnection.then(e=>this.connect(e.conn)),this.player=new Audio,this.playedOnce=!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),s(this)}connect(e){this.conn=e,e.subscribeMessage(e=>this.callback(e),{type:"browser_mod/connect",deviceID:o})}callback(e){switch(e.command){case"update":this.update(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"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)}}get player_state(){return this.player.src?this.player.ended?"stopped":this.player.paused?"paused":"playing":"stopped"}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)}popup(e){e.title&&e.card&&m(e.title,e.card,e.large,e.style)}close_popup(e){document.querySelector("home-assistant")._moreInfoEl.close()}navigate(e){e.navigation_path&&(history.pushState(null,"",e.navigation_path),r("location-changed",{},document.querySelector("home-assistant")))}more_info(e){e.entity_id&&c(e.entity_id,e.large)}set_theme(e){e.theme||(e.theme="default"),r("settheme",e.theme,document.querySelector("home-assistant"))}update(e=null){this.conn&&(e&&e.entity_id&&(this.entity_id=e.entity_id),this.conn.sendMessage({type:"browser_mod/update",deviceID:o,data:{browser:{path:window.location.pathname,visibility:document.visibilityState,userAgent:navigator.userAgent,currentUser:this._hass&&this._hass.user&&this._hass.user.name},player:{volume:this.player.volume,muted:this.player.muted,src:this.player.src,state:this.player_state}}}))}}}]); \ No newline at end of file + `}});window.browser_mod=new class{set hass(e){if(!e)return;if(this._hass=e,this.hassPatched)return;const t=e.callService;e.callService=(e,n,s)=>{if("browser_mod"===e&&"command"===n&&s.deviceID){const e=s.deviceID.indexOf("this");-1!==e&&(s.deviceID[e]=o)}t(e,n,s)},this.hassPatched=!0,document.querySelector("home-assistant").hassChanged(e,e)}playOnce(e){window.browser_mod.playedOnce||(window.browser_mod.player.play(),window.browser_mod.playedOnce=!0)}constructor(){window.hassConnection.then(e=>this.connect(e.conn)),this.player=new Audio,this.playedOnce=!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)),s(this),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)}connect(e){this.conn=e,e.subscribeMessage(e=>this.callback(e),{type:"browser_mod/connect",deviceID:o})}callback(e){switch(e.command){case"update":this.update(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"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"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"}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)}popup(e){e.title&&e.card&&m(e.title,e.card,e.large,e.style)}close_popup(e){document.querySelector("home-assistant")._moreInfoEl.close()}navigate(e){e.navigation_path&&(history.pushState(null,"",e.navigation_path),i("location-changed",{},document.querySelector("home-assistant")))}more_info(e){e.entity_id&&c(e.entity_id,e.large)}set_theme(e){e.theme||(e.theme="default"),i("settheme",e.theme,document.querySelector("home-assistant"))}blackout(e){this._blackout.style.visibility="visible",this.update()}no_blackout(e){this._blackout.style.visibility="hidden",this.update()}update(e=null){this.conn&&(e&&e.entity_id&&(this.entity_id=e.entity_id),this.conn.sendMessage({type:"browser_mod/update",deviceID:o,data:{browser:{path:window.location.pathname,visibility:document.visibilityState,userAgent:navigator.userAgent,currentUser:this._hass&&this._hass.user&&this._hass.user.name,blackout:Boolean("visible"===this._blackout.style.visibility)},player:{volume:this.player.volume,muted:this.player.muted,src:this.player.src,state:this.player_state}}}))}}}]); \ No newline at end of file diff --git a/js/main.js b/js/main.js index 1e91c4b..06a1d18 100644 --- a/js/main.js +++ b/js/main.js @@ -47,7 +47,25 @@ class BrowserMod { 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); + + 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); } connect(conn) { @@ -95,6 +113,13 @@ class BrowserMod { case "set-theme": this.set_theme(msg); break; + + case "blackout": + this.blackout(msg); + break; + case "no-blackout": + this.no_blackout(msg); + break; } } @@ -150,6 +175,15 @@ class BrowserMod { fireEvent("settheme", msg.theme, document.querySelector("home-assistant")); } + blackout(msg){ + this._blackout.style.visibility = "visible"; + this.update(); + } + no_blackout(msg){ + this._blackout.style.visibility = "hidden"; + this.update(); + } + update(msg=null) { if(!this.conn) return; @@ -167,6 +201,7 @@ class BrowserMod { visibility: document.visibilityState, userAgent: navigator.userAgent, currentUser: this._hass && this._hass.user && this._hass.user.name, + blackout: Boolean(this._blackout.style.visibility === "visible"), }, player: { volume: this.player.volume,