diff --git a/README.md b/README.md index 7846706..5e70261 100644 --- a/README.md +++ b/README.md @@ -15,143 +15,19 @@ - Restart Home Assistant -# Browser Mod Panel +# Browser Mod Configuration Panel When you're logged in as an administrator you should see a new panel called _Browser Mod_ in the sidebar. This is where you controll any Browser Mod settings. -## This browser - -A basic concept for Browser Mod is the _Browser_. A _Browser_ is identified by a unique `BrowserID` stored in the browsers [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API). - -Browser Mod will initially assigning a random `BrowserID` to each _Browser_ that connects, but you can change this if you want. - -LocalStorage works basically like cookies in that the information is stored locally on your device. Unlike a cookie, though, the information is bound to a URL. Therefore you may get different `BrowserID`s in the same browser if you e.g. access Home Assistant through different URLs inside and outside of your LAN, or through Home Assistant Cloud. - -### Register - -Registering a _Browser_ as a device will create a Home Assistant Device associated with that browser. The device has the following entities: - -- A `media_player` entitiy which will play sound through the browser. -- A `light` entity will turn the screen on or off and controll the brightness if you are using [Fully Kiosk Browser](https://www.fully-kiosk.com/) (FKB). If you are not using FKB the function will be simulated by covering the screen with a black (or semitransparent) box. -- A motion `binary_sensor` which reacts to mouse and/or keyboard activity in the Browser. In FKB this can also react to motion in front of the devices camera. -- A number of `sensor` and `binary_sensor` entities providing different bits of information about the Browser which you may or may not find useful. - -Registering a browser also enables it to act as a target for Browser Mod _services_. - -### BrowserId - -This box lets you set the `BrowserID` for the current _Browser_. -Note that it is possible to assign the same `BrowserID` to several browsers, but unpredictable things _may_ happen if several of them are open at the same time. -There may be benefits to using the same `BrowserID` in some cases, so you'll have to experiment with what works for you. - -### Enable camera - -If your device has a camera, this will allow it to be forwarded as a `camera` entity to Home Assistant. - -## Registered Browsers - -This section shows all currently registered _Browsers_ and allows you to unregister them. This is useful e.g. if a `BrowserID` has changed or if you do not have access to a device anymore. - -### Register CAST browser - -If you are using [Home Assistant Cast](https://www.home-assistant.io/integrations/cast/#home-assistant-cast) to display a lovelace view on a Chromecast device it will get a BrowserID of "`CAST`". Since you can't access the Browser Mod config panel from the device, clicking this button will register the `CAST` browser. Most Browser Mod services will work under Home Assistant Cast. - -## Frontend Settings - -This section is for settings that change the default behavior of the Home Assistant frontend. - -Each setting has three levels, _Global_, _Browser_ and _User_. - -- Changes made on the _Global_ tab will be applied for everyone on every browser. -- Changes made on the _Browser_ tab will be applied for this _Browser_. The settings here override any _Global_ settings. -- Changes made on the _User_ tab will be applied for the user you're currently logged in as - on any device. The settings here override any _Global_ or _Browser_ settings. - -Note that if a setting is set at a lower level but _cleared_ on a higher, it is not _undone_. It's just not overridden. - -Also note that _User_ level settings can only be made when logged in as the user in question, and that the Browser Mod configuration panel is only available to administrators. If you need to change a setting for a non-admin user, you will need to temporarily make them admin for the setup, and then un-admin them. - -### Favicon template - -This allows you to set and dynamically update the favicon of the browser tab/window. I.e. the little icon next to the page title. Favicons can be .png or .ico files and should be placed in your `/www` directory. The box here should then contain a jinja [template](https://www.home-assistant.io/docs/configuration/templating/) which resolves to the path of the icon with `/www/` replaced by `/local/` (see [Hosting files](https://www.home-assistant.io/integrations/http/#hosting-files)). - -> Ex: -> -> ```jinja -> {% if is_state("light.bed_light", "on") %} -> /local/icons/green.png -> {% else %} -> /local/icons/red.png -> {% endif %} -> ``` - -Note that this _only_ applies to the current favicon of the page, not any manifest icons such as the loading icon or the icon you get if you save the page to your smartphones homescreen. For those, please see the [hass-favicon](https://github.com/thomasloven/hass-favicon) integration. - -### Title template - -This allows you to set and dynamically update the title text of the browser tab/window by means on a Jinja [template](https://www.home-assistant.io/docs/configuration/templating/). - -> Ex: -> -> ```jinja -> {{ states.persistent_notification | list | count}} - Home Assistant -> ``` - -### Hide Sidebar - -This will hide the sidebar wit the navigation links. You can still access all the pages via normal links. - -> Tip: add `/browser-mod` to the end of your home assistant URL when you need to turn this off again... - -### Hide header - -This will hide the header bar. Completely. It does not care if there are useful navigation links there or not. It's gone. - -> Tip: See the big yellow warning box at the top of this card? For some reason, it seems to be really easy to forget you turned this on. Please do not bother the Home Assistant team about the header bar missing if you have hidden it yourself. Really, I've forgotten multiple times myself. - -### Sidebar order - -Did you know that you can change the order and hide items from the sidebar? To do so, either go into your profile settings at the bottom left and click "Change the order and hide items from the sidebar", or click and hold on the "Home Assistant" text at the top of the sidebar. - -Normally, the order and hidden items only applies to the current device, but this will make it persistent according to the levels described above. - -### Default dashboard - -Like the Sidebar order, the default dashboard (the page shown when you simply access `https:///` with nothing after the `/`) can be set in your profile settings but only applies to the current device. This fixes that. +## See [Configuration Panel](documentation/configuration-panel.md) for more info # Browser Mod Services Browser Mod has a number of services you can call to cause things to happen in the target Browser. -
Reading guide -Service parameters are described using the following conventions: +## See [Services](documentation/services.md) for more info -- `` describes the type of a parameter, e.g. - - - `` is a piece of text - - `` is a number - - `` means the value must be either `true` or `false` with `true` being the default - - `` means a full service call specification. Note that this can be any service, not just Browser Mod services - - `` is a list of BrowserIDs - -- Square brackets `[ ]` indicate that a parameter is optional and can be omitted. - -### `` - -A service call is a combination of a service and it's data: - -Ex, a `` for `browser_mod.more_info` with `light.bed_light` as entity: - -```yaml -service: browser_mod.more_info -data: - entity: light.bed_light -``` - -Seems obvious, hopefully. - -
- -## Calling services +### Calling services Services can be called from the backend using the normal service call procedures. Registered Browsers can be selected as targets through their device: ![GUI service call](https://user-images.githubusercontent.com/1299821/180668350-1cbe751d-615d-4102-b939-e49e9cd2ca74.png) @@ -181,233 +57,38 @@ tap_action: Services called via `fire-dom-event` or called as a part of a different service call will (by default) _only_ target the current Browser (even if it's not registered). -Finally, in service calls via `fire-dom-event` or as part of a different service call, the following a parameter `browser_id` with the value `THIS` will be replaced with the current Browsers browser ID. - -Ex: - -```yaml -tap_action: - action: fire-dom-event - browser_mod: - service: script.print_clicking_browser - data: - browser_id: THIS -``` - -with the script: - -```yaml -script: - print_clicking_browser: - sequence: - - service: system_log.write - data: - message: "Button was clicked in {{browser_id}}" -``` - -Will print `"Button was clicked in 79be65e8-f06c78f" to the Home Assistant log. - -## Services - -### `browser_mod.navigate` - -```yaml -service: browser_mod.navigate -data: - path: - [browser_id: ] -``` - -This will point the browser to the Home Assistant path given as `path`. -E.g. `/lovelace/`, `/my-dashboard/bedroom`, `/browser_mod/`, `/config/devices/device/20911cc5a63b1caafa2089618545eb8a`... - -### `browser_mod.refresh` - -```yaml -service: browser_mod.refresh -data: - [browser_id: ] -``` - -This will reload the current page. - -### `browser_mod.more_info` - -```yaml -service: browser_mod.more_info -data: - entity: - [large: ] - [ignore_popup_card: ] - [browser_id: ] -``` - -This will show the more-info dialog for `entity`. \ -If `large` is true it will be displayed wider (as if you had clicked the name in the dialog). \ -If `ignore_popup_card` is true the more-info dialog will be shown even if it is currently overriden by a popup-card. - -### `browser_mod.popup` - -```yaml -service: browser_mod.popup -data: - [title: ] - content: - [size: ] - [right_button: ] - [right_button_action: ] - [left_button: ] - [left_button_action: ] - [dismissable: ] - [dismiss_action: ] - [autoclose: ] - [timeout: ] - [timeout_action: ] - [style: ] - [browser_id: ] -``` - -This will display a popup dialog. \ -`content` may be either some text (including HTML) or a dashboard card configuration. \ -If `size` is `wide` or `fullscreen` the card will be displayed wider or covering the entire window. \ -`right_button` and `left_button` specify the text of two action buttons. \ -When either action button is clicked, the dialog is closed and the service specified as `right_button_action` or `left_button_action` is called. \ -If `dismissable` is false, the dialog cannot be closed by the user without clicking either action button. If it is true and the dialog is dismissed, `dismiss_action` is called. \ -If `autoclose` is true the dialog will close automatically when the mouse, screen or keyboard is touched, at which point `dismiss_action` will be called. \ -If `timeout` is specified the dialog will close automatically after `timeout` milliseconds, at which point `timeout_action` will be called. \ -Finally, `style` lets you specify some CSS styles to apply to the dialog itself (to style a card in the dialog check out [card-mod](https://github.com/thomasloven/lovelace-card-mod)) - -Note that any Browser Mod services performed as `_action`s here will be performed only on the same Browser as initiated the action unless `browser_id` is given. - -Ex: - -```yaml -service: browser_mod.popup -data: - title: The title - content: The content - right_button: Right button - left_button: Left button -``` - -![Popup](https://user-images.githubusercontent.com/1299821/180668969-c647f301-3f3d-4f3b-a1f8-d95af8b48873.png) - -```yaml -service: browser_mod.popup -data: - title: The title - right_button: Right button - left_button: Left button - content: - type: entities - entities: - - light.bed_light - - light.ceiling_lights - - light.kitchen_lights -``` - -![Popup with card](https://user-images.githubusercontent.com/1299821/180669077-bbc86831-3a8a-4e54-b098-d900d62d3508.png) - -```yaml -service: browser_mod.popup -data: - content: Do you want to turn the light on? - right_button: "Yes" - left_button: "No" - right_button_action: - service: light.turn_on - data: - entity_id: light.bed_light - left_button_action: - service: browser_mod.popup - data: - title: Really? - content: Are you sure? - right_button: "Yes" - left_button: "No" - right_button_action: - service: browser_mod.popup - data: - content: Fine, live in darkness. - dismissable: false - title: Ok - timeout: 3000 - left_button_action: - service: light.turn_on - data: - entity_id: light.bed_light -``` - -![Advanced popup](https://user-images.githubusercontent.com/1299821/180670190-18cf8eee-cf18-47b9-84d1-e62ef327c615.gif) - -### `browser_mod.close_popup` - -```yaml -service: browser_mod.close_popup -data: - [browser_id: ] -``` - -This will close any currently open popup or more-info dialogs. - -### `browser_mod.sequence` - -```yaml -service: browser_mod.sequence -data: - sequence: - - - - - - ... - [browser_id: ] -``` - -This will perform the listed servie calls one after the other. - -Note that if `browser_id` is omitted in the service calls listed in `sequence` the services will be performed on the Browser that's targeted as a whole rather than all browsers. - -### `browser_mod.delay` - -```yaml -service: browser_mod.delay -data: - time: - [browser_id: ] -``` - -This will wait for `time` millliseconds. It's probably most useful as part of a `browser_mod.sequence` call... - -### `browsermod.console` - -```yaml -service: browser_mod.console -data: - message: - [browser_id: ] -``` - -This will print `message` to the console of the target Browser. - -### `browsermod.javascript` - -```yaml -service: browser_mod.console -data: - code: - [browser_id: ] -``` - -This will run the arbitrary javascript `code` in the target Browser. - -Only use this one if you know what you're doing. - - # Popup card -Add a popup card to a lovelace view (via the GUI) and it will override the more-info dialog for the given entity. Try it. More info to come. +A popup card can be used to replace the more-info dialog of an entity with something of your choosing. + +To use it, add a "Custom: Popup card" to a dashboard view via the GUI, pick the entity you want to override, configure the card and set up the popup like for the [`browser_mod.popup` service](documentation/services.md). + +The card will be visible only while you're in Edit mode. + +As long as the popup card is (would be) visible, i.e. you stay on the same view; +whenever the more-info dialog for the entitiy you selected would be opened, the popup card will be shown instead. + +Yaml configuration: + +```yaml +type: custom:popup-card +entity: +card: + type: ...etc... +[any parameter from the browser_mod.popup service call except "content"] +``` + # Browser Player +Browser player is a card that allows you to controll the volume and playback on the current Browsers media player. + +Add it to a dashboard via the GUI or through yaml: + +```yaml +type: custom:browser-player +``` + --- Buy Me A Coffee diff --git a/custom_components/browser_mod/browser.py b/custom_components/browser_mod/browser.py index 2063a3a..cddbc00 100644 --- a/custom_components/browser_mod/browser.py +++ b/custom_components/browser_mod/browser.py @@ -131,7 +131,7 @@ class BrowserModBrowser: del self.entities["camera"] self.send( - None, deviceEntities={k: v.entity_id for k, v in self.entities.items()} + None, browserEntities={k: v.entity_id for k, v in self.entities.items()} ) def send(self, command, **kwargs): diff --git a/custom_components/browser_mod/browser_mod.js b/custom_components/browser_mod/browser_mod.js index 5238601..4d02763 100644 --- a/custom_components/browser_mod/browser_mod.js +++ b/custom_components/browser_mod/browser_mod.js @@ -1,195 +1,24 @@ -/*! ***************************************************************************** -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$3=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$3&&void 0===e&&(n$4.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const o$5=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$4=(e,n)=>{t$3?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$3?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const n of t.cssRules)e+=n.cssText;return o$5(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$4={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$4,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$4(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$4.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$4.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$2;const i$3=globalThis.trustedTypes,s$1=i$3?i$3.createPolicy("lit-html",{createHTML:t=>t}):void 0,e$3=`lit$${(Math.random()+"").slice(9)}$`,o$3="?"+e$3,n$2=`<${o$3}>`,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$3?i$3.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$2(e,n)} - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function t$1(t){return e$2({...t,state:!0})} - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const o$1=({finisher:e,descriptor:t})=>(o,n)=>{var r;if(void 0===n){const n=null!==(r=o.originalKey)&&void 0!==r?r:o.key,i=null!=t?{kind:"method",placement:"prototype",key:n,descriptor:t(o.key)}:{...o,key:n};return null!=e&&(i.finisher=function(t){e(t,n);}),i}{const r=o.constructor;void 0!==t&&Object.defineProperty(o,n,t(n)),null==e||e(r,n);}}; - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function i$1(i,n){return o$1({descriptor:o=>{const t={get(){var o,n;return null!==(n=null===(o=this.renderRoot)||void 0===o?void 0:o.querySelector(i))&&void 0!==n?n:null},enumerable:!0,configurable:!0};if(n){const n="symbol"==typeof o?Symbol():"__"+o;t.get=function(){var o,t;return void 0===this[n]&&(this[n]=null!==(t=null===(o=this.renderRoot)||void 0===o?void 0:o.querySelector(i))&&void 0!==t?t:null),this[n]};}return t}})} - -/** - * @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 $ ` +function e(e,t,i,s){var o,n=arguments.length,r=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(r=(n<3?o(r):n>3?o(t,i,r):o(t,i))||r);return n>3&&r&&Object.defineProperty(t,i,r),r}const t=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new Map;class o{constructor(e,t){if(this._$cssResult$=!0,t!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e}get styleSheet(){let e=s.get(this.cssText);return t&&void 0===e&&(s.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const n=(e,...t)=>{const s=1===e.length?e[0]:t.reduce(((t,i,s)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[s+1]),e[0]);return new o(s,i)},r=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new o("string"==typeof e?e:e+"",i))(t)})(e):e;var a;const d=window.trustedTypes,l=d?d.emptyScript:"",c=window.reactiveElementPolyfillSupport,h={toAttribute(e,t){switch(t){case Boolean:e=e?l:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},u=(e,t)=>t!==e&&(t==t||e==e),p={attribute:!0,type:String,converter:h,reflect:!1,hasChanged:u};class v extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o()}static addInitializer(e){var t;null!==(t=this.l)&&void 0!==t||(this.l=[]),this.l.push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach(((t,i)=>{const s=this._$Eh(i,t);void 0!==s&&(this._$Eu.set(s,i),e.push(s))})),e}static createProperty(e,t=p){if(t.state&&(t.attribute=!1),this.finalize(),this.elementProperties.set(e,t),!t.noAccessor&&!this.prototype.hasOwnProperty(e)){const i="symbol"==typeof e?Symbol():"__"+e,s=this.getPropertyDescriptor(e,i,t);void 0!==s&&Object.defineProperty(this.prototype,e,s)}}static getPropertyDescriptor(e,t,i){return{get(){return this[t]},set(s){const o=this[e];this[t]=s,this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)||p}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const e=Object.getPrototypeOf(this);if(e.finalize(),this.elementProperties=new Map(e.elementProperties),this._$Eu=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const i of t)this.createProperty(i,e[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(r(e))}else void 0!==e&&t.push(r(e));return t}static _$Eh(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}o(){var e;this._$Ep=new Promise((e=>this.enableUpdating=e)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(e=this.constructor.l)||void 0===e||e.forEach((e=>e(this)))}addController(e){var t,i;(null!==(t=this._$Eg)&&void 0!==t?t:this._$Eg=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(i=e.hostConnected)||void 0===i||i.call(e))}removeController(e){var t;null===(t=this._$Eg)||void 0===t||t.splice(this._$Eg.indexOf(e)>>>0,1)}_$Em(){this.constructor.elementProperties.forEach(((e,t)=>{this.hasOwnProperty(t)&&(this._$Et.set(t,this[t]),delete this[t])}))}createRenderRoot(){var e;const i=null!==(e=this.shadowRoot)&&void 0!==e?e:this.attachShadow(this.constructor.shadowRootOptions);return((e,i)=>{t?e.adoptedStyleSheets=i.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):i.forEach((t=>{const i=document.createElement("style"),s=window.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=t.cssText,e.appendChild(i)}))})(i,this.constructor.elementStyles),i}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}))}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}))}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ES(e,t,i=p){var s,o;const n=this.constructor._$Eh(e,i);if(void 0!==n&&!0===i.reflect){const r=(null!==(o=null===(s=i.converter)||void 0===s?void 0:s.toAttribute)&&void 0!==o?o:h.toAttribute)(t,i.type);this._$Ei=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Ei=null}}_$AK(e,t){var i,s,o;const n=this.constructor,r=n._$Eu.get(e);if(void 0!==r&&this._$Ei!==r){const e=n.getPropertyOptions(r),a=e.converter,d=null!==(o=null!==(s=null===(i=a)||void 0===i?void 0:i.fromAttribute)&&void 0!==s?s:"function"==typeof a?a:null)&&void 0!==o?o:h.fromAttribute;this._$Ei=r,this[r]=d(t,e.type),this._$Ei=null}}requestUpdate(e,t,i){let s=!0;void 0!==e&&(((i=i||this.constructor.getPropertyOptions(e)).hasChanged||u)(this[e],t)?(this._$AL.has(e)||this._$AL.set(e,t),!0===i.reflect&&this._$Ei!==e&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(e,i))):s=!1),!this.isUpdatePending&&s&&(this._$Ep=this._$E_())}async _$E_(){this.isUpdatePending=!0;try{await this._$Ep}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((e,t)=>this[t]=e)),this._$Et=void 0);let t=!1;const i=this._$AL;try{t=this.shouldUpdate(i),t?(this.willUpdate(i),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)})),this.update(i)):this._$EU()}catch(e){throw t=!1,this._$EU(),e}t&&this._$AE(i)}willUpdate(e){}_$AE(e){var t;null===(t=this._$Eg)||void 0===t||t.forEach((e=>{var t;return null===(t=e.hostUpdated)||void 0===t?void 0:t.call(e)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EU(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(e){return!0}update(e){void 0!==this._$EC&&(this._$EC.forEach(((e,t)=>this._$ES(t,this[t],e))),this._$EC=void 0),this._$EU()}updated(e){}firstUpdated(e){}}var m;v.finalized=!0,v.elementProperties=new Map,v.elementStyles=[],v.shadowRootOptions={mode:"open"},null==c||c({ReactiveElement:v}),(null!==(a=globalThis.reactiveElementVersions)&&void 0!==a?a:globalThis.reactiveElementVersions=[]).push("1.3.1");const _=globalThis.trustedTypes,g=_?_.createPolicy("lit-html",{createHTML:e=>e}):void 0,w=`lit$${(Math.random()+"").slice(9)}$`,b="?"+w,y=`<${b}>`,f=document,$=(e="")=>f.createComment(e),E=e=>null===e||"object"!=typeof e&&"function"!=typeof e,A=Array.isArray,x=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,S=/-->/g,C=/>/g,T=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,P=/'/g,k=/"/g,I=/^(?:script|style|textarea|title)$/i,M=(e=>(t,...i)=>({_$litType$:e,strings:t,values:i}))(1),O=Symbol.for("lit-noChange"),U=Symbol.for("lit-nothing"),L=new WeakMap,D=f.createTreeWalker(f,129,null,!1),R=(e,t)=>{const i=e.length-1,s=[];let o,n=2===t?"":"",r=x;for(let t=0;t"===d[0]?(r=null!=o?o:x,l=-1):void 0===d[1]?l=-2:(l=r.lastIndex-d[2].length,a=d[1],r=void 0===d[3]?T:'"'===d[3]?k:P):r===k||r===P?r=T:r===S||r===C?r=x:(r=T,o=void 0);const h=r===T&&e[t+1].startsWith("/>")?" ":"";n+=r===x?i+y:l>=0?(s.push(a),i.slice(0,l)+"$lit$"+i.slice(l)+w+h):i+w+(-2===l?(s.push(void 0),t):h)}const a=n+(e[i]||"")+(2===t?"":"");if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==g?g.createHTML(a):a,s]};class H{constructor({strings:e,_$litType$:t},i){let s;this.parts=[];let o=0,n=0;const r=e.length-1,a=this.parts,[d,l]=R(e,t);if(this.el=H.createElement(d,i),D.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(s=D.nextNode())&&a.length0){s.textContent=_?_.emptyScript:"";for(let i=0;i{var t;return A(e)||"function"==typeof(null===(t=e)||void 0===t?void 0:t[Symbol.iterator])})(e)?this.S(e):this.$(e)}M(e,t=this._$AB){return this._$AA.parentNode.insertBefore(e,t)}k(e){this._$AH!==e&&(this._$AR(),this._$AH=this.M(e))}$(e){this._$AH!==U&&E(this._$AH)?this._$AA.nextSibling.data=e:this.k(f.createTextNode(e)),this._$AH=e}T(e){var t;const{values:i,_$litType$:s}=e,o="number"==typeof s?this._$AC(e):(void 0===s.el&&(s.el=H.createElement(s.h,this.options)),s);if((null===(t=this._$AH)||void 0===t?void 0:t._$AD)===o)this._$AH.m(i);else{const e=new N(o,this),t=e.p(this.options);e.m(i),this.k(t),this._$AH=e}}_$AC(e){let t=L.get(e.strings);return void 0===t&&L.set(e.strings,t=new H(e)),t}S(e){A(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,s=0;for(const o of e)s===t.length?t.push(i=new z(this.M($()),this.M($()),this,this.options)):i=t[s],i._$AI(o),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=U}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(e,t=this,i,s){const o=this.strings;let n=!1;if(void 0===o)e=j(this,e,t,0),n=!E(e)||e!==this._$AH&&e!==O,n&&(this._$AH=e);else{const s=e;let r,a;for(e=o[0],r=0;r{var s,o;const n=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:t;let r=n._$litPart$;if(void 0===r){const e=null!==(o=null==i?void 0:i.renderBefore)&&void 0!==o?o:null;n._$litPart$=r=new z(t.insertBefore($(),e),e,void 0,null!=i?i:{})}return r._$AI(e),r})(t,this.renderRoot,this.renderOptions)}connectedCallback(){var e;super.connectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!0)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!1)}render(){return O}}Q.finalized=!0,Q._$litElement$=!0,null===(J=globalThis.litElementHydrateSupport)||void 0===J||J.call(globalThis,{LitElement:Q});const X=globalThis.litElementPolyfillSupport;null==X||X({LitElement:Q}),(null!==(Z=globalThis.litElementVersions)&&void 0!==Z?Z:globalThis.litElementVersions=[]).push("3.2.0");const Y=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?{...t,finisher(i){i.createProperty(t.key,e)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:t.key,initializer(){"function"==typeof t.initializer&&(this[t.key]=t.initializer.call(this))},finisher(i){i.createProperty(t.key,e)}};function ee(e){return(t,i)=>void 0!==i?((e,t,i)=>{t.constructor.createProperty(i,e)})(e,t,i):Y(e,t)}function te(e){return ee({...e,state:!0})}function ie(e,t){return(({finisher:e,descriptor:t})=>(i,s)=>{var o;if(void 0===s){const s=null!==(o=i.originalKey)&&void 0!==o?o:i.key,n=null!=t?{kind:"method",placement:"prototype",key:s,descriptor:t(i.key)}:{...i,key:s};return null!=e&&(n.finisher=function(t){e(t,s)}),n}{const o=i.constructor;void 0!==t&&Object.defineProperty(i,s,t(s)),null==e||e(o,s)}})({descriptor:i=>{const s={get(){var t,i;return null!==(i=null===(t=this.renderRoot)||void 0===t?void 0:t.querySelector(e))&&void 0!==i?i:null},enumerable:!0,configurable:!0};if(t){const t="symbol"==typeof i?Symbol():"__"+i;s.get=function(){var i,s;return void 0===this[t]&&(this[t]=null!==(s=null===(i=this.renderRoot)||void 0===i?void 0:i.querySelector(e))&&void 0!==s?s:null),this[t]}}return s}})}var se;null===(se=window.HTMLSlotElement)||void 0===se||se.prototype.assignedElements;class oe extends Q{setConfig(e){}render(){return M`
Nothing to configure.
`}}(async()=>{for(;!window.browser_mod;)await new Promise((e=>setTimeout(e,1e3)));await window.browser_mod.connectionPromise,customElements.get("browser-player-editor")||(customElements.define("browser-player-editor",oe),window.customCards=window.customCards||[],window.customCards.push({type:"browser-player",name:"Browser Player",preview:!0}))})();class ne extends Q{static getConfigElement(){return document.createElement("browser-player-editor")}static getStubConfig(){return{}}async setConfig(e){for(;!window.browser_mod;)await new Promise((e=>setTimeout(e,1e3)));for(const e of["play","pause","ended","volumechange","canplay","loadeddata"])window.browser_mod._audio_player.addEventListener(e,(()=>this.requestUpdate()));window.browser_mod._video_player.addEventListener(event,(()=>this.requestUpdate()))}handleMute(e){window.browser_mod.player.muted=!window.browser_mod.player.muted}handleVolumeChange(e){const t=parseFloat(e.target.value);window.browser_mod.player.volume=t}handleMoreInfo(e){var t;this.dispatchEvent(new CustomEvent("hass-more-info",{bubbles:!0,composed:!0,cancelable:!1,detail:{entityId:null===(t=window.browser_mod.browserEntities)||void 0===t?void 0:t.player}}))}handlePlayPause(e){!window.browser_mod.player.src||window.browser_mod.player.paused||window.browser_mod.player.ended?(window.browser_mod.player.play(),window.browser_mod._show_video_player()):window.browser_mod.player.pause()}render(){return window.browser_mod?M`
- ${window.browser_mod.player_state === "stopped" - ? $ `
` - : $ ` + ${"stopped"===window.browser_mod.player_state?M`
`:M` `} @@ -200,10 +29,7 @@ class BrowserPlayer extends s {
${window.browser_mod.browserID}
- `; - } - static get styles() { - return r$2 ` + `:(window.setTimeout((()=>this.requestUpdate()),100),M``)}static get styles(){return n` paper-icon-button[highlight] { color: var(--accent-color); } @@ -227,1109 +53,51 @@ class BrowserPlayer extends s { 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); -})(); - -const TIMEOUT_ERROR = "SELECTTREE-TIMEOUT"; -async function _await_el(el) { - var _a; - if ((_a = el.localName) === null || _a === void 0 ? void 0 : _a.includes("-")) - await customElements.whenDefined(el.localName); - if (el.updateComplete) - await el.updateComplete; -} -async function _selectTree(root, path, all = false) { - let el = [root]; - if (typeof path === "string") { - path = path.split(/(\$| )/); - } - while (path[path.length - 1] === "") - path.pop(); - for (const [i, p] of path.entries()) { - const e = el[0]; - if (!e) - return null; - if (!p.trim().length) - continue; - _await_el(e); - el = p === "$" ? [e.shadowRoot] : e.querySelectorAll(p); - } - return all ? el : el[0]; -} -async function selectTree(root, path, all = false, timeout = 10000) { - return Promise.race([ - _selectTree(root, path, all), - new Promise((_, reject) => setTimeout(() => reject(new Error(TIMEOUT_ERROR)), timeout)), - ]).catch((err) => { - if (!err.message || err.message !== TIMEOUT_ERROR) - throw err; - return null; - }); -} -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 loadHaForm = async () => { - if (customElements.get("ha-form")) - return; - await loadLoadCardHelpers(); - const helpers = await window.loadCardHelpers(); - if (!helpers) - return; - const card = await helpers.createCardElement({ type: "entity" }); - if (!card) - return; - await card.getConfigElement(); -}; - -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) { - return; - } - 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/register", - browserID: this.browserID, - data: Object.assign(Object.assign({}, this.browsers[this.browserID]), newData), - }); - } - get global_settings() { - var _a; - const settings = {}; - const global = (_a = this._data.settings) !== null && _a !== void 0 ? _a : {}; - for (const [k, v] of Object.entries(global)) { - if (v !== null) - settings[k] = v; - } - return settings; - } - get user_settings() { - var _a; - const settings = {}; - const user = (_a = this._data.user_settings[this.hass.user.id]) !== null && _a !== void 0 ? _a : {}; - for (const [k, v] of Object.entries(user)) { - if (v !== null) - settings[k] = v; - } - return settings; - } - get browser_settings() { - var _a, _b; - const settings = {}; - const browser = (_b = (_a = this.browsers[this.browserID]) === null || _a === void 0 ? void 0 : _a.settings) !== null && _b !== void 0 ? _b : {}; - for (const [k, v] of Object.entries(browser)) { - if (v !== null) - settings[k] = v; - } - return settings; - } - get settings() { - return Object.assign(Object.assign(Object.assign({}, this.global_settings), this.browser_settings), this.user_settings); - } - set_setting(key, value, level) { - var _a; - switch (level) { - case "global": { - this.connection.sendMessage({ - type: "browser_mod/settings", - key, - value, - }); - break; - } - case "user": { - const user = this.hass.user.id; - this.connection.sendMessage({ - type: "browser_mod/settings", - user, - key, - value, - }); - break; - } - case "browser": { - const settings = (_a = this.browsers[this.browserID]) === null || _a === void 0 ? void 0 : _a.settings; - settings[key] = value; - this._reregister({ settings }); - break; - } - } - } - 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, - }); - } - browserIDChanged(oldID, newID) { - var _a, _b; - 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/register", - 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._audio_player = new Audio(); - this._video_player = document.createElement("video"); - this._video_player.controls = true; - this._video_player.style.setProperty("width", "100%"); - this.player = this._audio_player; - this._player_enabled = false; - for (const ev of ["play", "pause", "ended", "volumechange"]) { - this._audio_player.addEventListener(ev, () => this._player_update()); - this._video_player.addEventListener(ev, () => this._player_update()); - } - for (const ev of ["timeupdate"]) { - this._audio_player.addEventListener(ev, () => this._player_update_choked()); - this._video_player.addEventListener(ev, () => this._player_update_choked()); - } - this.firstInteraction.then(() => { - this._player_enabled = true; - if (!this.player.ended) - this.player.play(); - }); - this.addEventListener("command-player-play", (ev) => { - var _a, _b, _c; - if (this.player.src) - this.player.pause(); - if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_type) - if ((_b = ev.detail) === null || _b === void 0 ? void 0 : _b.media_type.startsWith("video")) - this.player = this._video_player; - else - this.player = this._audio_player; - if ((_c = ev.detail) === null || _c === void 0 ? void 0 : _c.media_content_id) - this.player.src = ev.detail.media_content_id; - this.player.play(); - this._show_video_player(); - }); - 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.addEventListener("command-player-seek", (ev) => { - this.player.currentTime = ev.detail.position; - setTimeout(() => this._player_update(), 10); - }); - this.addEventListener("command-player-turn-off", (ev) => { - if (this.player === this._video_player && - this._video_player.isConnected) - this.closePopup(); - else if (this.player.src) - this.player.pause(); - this.player.src = ""; - this._player_update(); - }); - this.connectionPromise.then(() => this._player_update()); - } - _show_video_player() { - if (this.player === this._video_player && this.player.src) { - selectTree(document, "home-assistant $ dialog-media-player-browse").then((el) => el === null || el === void 0 ? void 0 : el.closeDialog()); - this.showPopup(undefined, this._video_player, { - dismiss_action: () => this._video_player.pause(), - size: "wide", - }); - } - else if (this.player !== this._video_player && - this._video_player.isConnected) { - this.closePopup(); - } - } - _player_update_choked() { - if (this._player_update_cooldown) - return; - this._player_update_cooldown = window.setTimeout(() => (this._player_update_cooldown = undefined), 3000); - this._player_update(); - } - _player_update() { - const state = this._player_enabled - ? !this.player.src || this.player.src === window.location.href - ? "off" - : this.player.ended - ? "stopped" - : this.player.paused - ? "paused" - : "playing" - : "unavailable"; - this.sendUpdate({ - player: { - volume: this.player.volume, - muted: this.player.muted, - src: this.player.src, - state, - media_duration: this.player.duration, - media_position: this.player.currentTime, - }, - }); - } - }; -}; - -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\\00a0Mod"; - 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(); - } - async browser_navigate(path) { - if (!path) - return; - history.pushState(null, "", path); - window.dispatchEvent(new CustomEvent("location-changed")); - } - }; -}; - -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: ] - [size: ] - [right_button: ] - [right_button_action: ] - [left_button: ] - [left_button_action: ] - [dismissable: ] - [dismiss_action: ] - [autoclose: ] - [timeout: ] - [timeout_action: ] - [style: ] - - More-info: - service: browser_mod.more_info - data: - entity: - [large: ] - [ignore_popup_card: ] - - Close popup: - service: browser_mod.close_popup - - Navigate to path: - service: browser_mod.navigate - data: - path: - - Refresh browser: - service: browser_mod.refresh - - Browser console print: - service: browser_mod.console - data: - message: - - Run javascript: - service: browser_mod.javascript - data: - code: - */ - constructor() { - super(); - const cmds = [ - "sequence", - "delay", - "popup", - "more_info", - "close_popup", - "navigate", - "refresh", - "console", - "javascript", - ]; - for (const service of cmds) { - this.addEventListener(`command-${service}`, (ev) => { - this.service(service, ev.detail); - }); - } - document.body.addEventListener("ll-custom", (ev) => { - if (ev.detail.browser_mod) { - this._service_action(ev.detail.browser_mod); - } - }); - } - async service(service, data) { - this._service_action({ service, data }); - } - async _service_action({ service, data }) { - let _service = service; - if ((!_service.startsWith("browser_mod.") && _service.includes(".")) || - data.browser_id !== undefined) { - const d = Object.assign({}, data); - if (d.browser_id === "THIS") - d.browser_id = this.browserID; - // CALL HOME ASSISTANT SERVICE - const [domain, srv] = _service.split("."); - return this.hass.callService(domain, srv, d); - } - 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 "more_info": - const { entity, large, ignore_popup_card } = data; - this.showMoreInfo(entity, large, ignore_popup_card); - 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 "navigate": - this.browser_navigate(data.path); - break; - case "refresh": - window.location.href = window.location.href; - break; - case "console": - console.log(data.message); - break; - case "javascript": - const code = ` - "use strict"; - // Insert global definitions here - const hass = (document.querySelector("home-assistant") || document.querySelector("hc-main")).hass; - ${data.code} - `; - const fn = new Function(code); - fn(); - break; - } - } - }; -}; - -const ActivityMixin = (SuperClass) => { - return class ActivityMixinClass extends SuperClass { - constructor() { - super(); - this.activityTriggered = false; - this._activityCooldown = 15000; - for (const ev of ["pointerdown", "pointermove", "keydown"]) { - window.addEventListener(ev, () => this.activityTrigger(true)); - } - this.addEventListener("fully-update", () => { - this.activityTrigger(); - }); - } - activityTrigger(touched = false) { - if (!this.activityTriggered) { - this.sendUpdate({ - activity: true, - }); - } - this.activityTriggered = true; - if (touched) { - this.fireEvent("browser-mod-activity"); - } - clearTimeout(this._activityTimeout); - this._activityTimeout = setTimeout(() => this.activityReset(), this._activityCooldown); - } - activityReset() { - clearTimeout(this._activityTimeout); - if (this.activityTriggered) { - this.sendUpdate({ - activity: false, - }); - } - this.activityTriggered = false; - } - }; -}; - -/** - * @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 { - async closeDialog() { - this.open = false; - clearInterval(this._timeoutTimer); - if (this._autocloseListener) { - window.browser_mod.removeEventListener("browser-mod-activity", this._autocloseListener); - this._autocloseListener = undefined; - } - } - openDialog() { - var _a; - this.open = true; - (_a = this.dialog) === null || _a === void 0 ? void 0 : _a.show(); - 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); - } - this._autocloseListener = undefined; - if (this._autoclose) { - this._autocloseListener = () => this.dialog.close(); - window.browser_mod.addEventListener("browser-mod-activity", this._autocloseListener, { once: true }); - } - } - 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, size = undefined, style = undefined, autoclose = false, } = {}) { - this.title = title; - if (content && content instanceof HTMLElement) { - this.card = undefined; - this.content = content; - } - else 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, - }; - this.wide = size === "wide" ? "" : undefined; - this.fullscreen = size === "fullscreen" ? "" : undefined; - this._style = style; - this._autoclose = autoclose; - } - async _primary() { - var _a, _b, _c, _d; - if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) - this._actions.dismiss_action = undefined; - (_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close(); - (_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.right_button_action) === null || _d === void 0 ? void 0 : _d.call(_c); - } - async _secondary() { - var _a, _b, _c, _d; - if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) - this._actions.dismiss_action = undefined; - (_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close(); - (_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.left_button_action) === null || _d === void 0 ? void 0 : _d.call(_c); - } - async _dismiss(ev) { - var _a, _b, _c; - (_a = this.dialog) === null || _a === void 0 ? void 0 : _a.close(); - (_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.dismiss_action) === null || _c === void 0 ? void 0 : _c.call(_b); - } - async _timeout() { - var _a, _b, _c, _d; - if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) - this._actions.dismiss_action = undefined; - (_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close(); - (_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.timeout_action) === null || _d === void 0 ? void 0 : _d.call(_c); - } - render() { - if (!this.open) - return $ ``; - return $ ` + `}}e([ee()],ne.prototype,"hass",void 0),(async()=>{for(;!window.browser_mod;)await new Promise((e=>setTimeout(e,1e3)));await window.browser_mod.connectionPromise,customElements.get("browser-player")||customElements.define("browser-player",ne)})();async function re(e){var t;(null===(t=e.localName)||void 0===t?void 0:t.includes("-"))&&await customElements.whenDefined(e.localName),e.updateComplete&&await e.updateComplete}async function ae(e,t,i=!1){let s=[e];for("string"==typeof t&&(t=t.split(/(\$| )/));""===t[t.length-1];)t.pop();for(const[e,i]of t.entries()){const e=s[0];if(!e)return null;i.trim().length&&(re(e),s="$"===i?[e.shadowRoot]:e.querySelectorAll(i))}return i?s:s[0]}async function de(e,t,i=!1,s=1e4){return Promise.race([ae(e,t,i),new Promise(((e,t)=>setTimeout((()=>t(new Error("SELECTTREE-TIMEOUT"))),s)))]).catch((e=>{if(!e.message||"SELECTTREE-TIMEOUT"!==e.message)throw e;return null}))}async function le(){await Promise.race([customElements.whenDefined("home-assistant"),customElements.whenDefined("hc-main")]);const e=customElements.get("home-assistant")?"home-assistant":"hc-main";for(;!document.querySelector(e);)await new Promise((e=>window.setTimeout(e,100)));return document.querySelector(e)}async function ce(e){(await le()).provideHass(e)}const he=async()=>{var e,t,i;if(void 0!==window.loadCardHelpers)return;await customElements.whenDefined("partial-panel-resolver");const s=document.createElement("partial-panel-resolver").getRoutes([{component_name:"lovelace",url_path:"a"}]);await(null===(i=null===(t=null===(e=null==s?void 0:s.routes)||void 0===e?void 0:e.a)||void 0===t?void 0:t.load)||void 0===i?void 0:i.call(t))},ue=e=>class extends e{constructor(){super(...arguments),this.connected=!1,this.connectionPromise=new Promise((e=>{this._connectionResolve=e})),this.browserEntities={}}LOG(...e){}fireEvent(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t}))}incoming_message(e){var t;e.command?(this.LOG("Command:",e),this.fireEvent(`command-${e.command}`,e)):e.browserEntities?this.browserEntities=e.browserEntities:e.result&&this.update_config(e.result),null===(t=this._connectionResolve)||void 0===t||t.call(this)}update_config(e){var t;this.LOG("Receive:",e);let i=!1;!this.registered&&(null===(t=e.browsers)||void 0===t?void 0:t[this.browserID])&&(i=!0),this._data=e,this.connected||(this.connected=!0,this.fireEvent("browser-mod-connected")),this.fireEvent("browser-mod-config-update"),i&&this.sendUpdate({})}async connect(){const e=(await async function(){const e=await le();for(;!e.hass;)await new Promise((e=>window.setTimeout(e,100)));return e.hass}()).connection;this.connection=e,e.subscribeMessage((e=>this.incoming_message(e)),{type:"browser_mod/connect",browserID:this.browserID}),e.addEventListener("disconnected",(()=>{this.connected=!1,this.fireEvent("browser-mod-disconnected")})),e.addEventListener("ready",(()=>{this.connected=!0,this.fireEvent("browser-mod-connected"),this.sendUpdate({})})),ce(this)}get config(){var e,t;return null!==(t=null===(e=this._data)||void 0===e?void 0:e.config)&&void 0!==t?t:{}}get browsers(){var e,t;return null!==(t=null===(e=this._data)||void 0===e?void 0:e.browsers)&&void 0!==t?t:[]}get registered(){var e;return void 0!==(null===(e=this.browsers)||void 0===e?void 0:e[this.browserID])}set registered(e){(async()=>{if(e){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(e={}){await this.connection.sendMessage({type:"browser_mod/register",browserID:this.browserID,data:Object.assign(Object.assign({},this.browsers[this.browserID]),e)})}get global_settings(){var e;const t={},i=null!==(e=this._data.settings)&&void 0!==e?e:{};for(const[e,s]of Object.entries(i))null!==s&&(t[e]=s);return t}get user_settings(){var e;const t={},i=null!==(e=this._data.user_settings[this.hass.user.id])&&void 0!==e?e:{};for(const[e,s]of Object.entries(i))null!==s&&(t[e]=s);return t}get browser_settings(){var e,t;const i={},s=null!==(t=null===(e=this.browsers[this.browserID])||void 0===e?void 0:e.settings)&&void 0!==t?t:{};for(const[e,t]of Object.entries(s))null!==t&&(i[e]=t);return i}get settings(){return Object.assign(Object.assign(Object.assign({},this.global_settings),this.browser_settings),this.user_settings)}set_setting(e,t,i){var s;switch(i){case"global":this.connection.sendMessage({type:"browser_mod/settings",key:e,value:t});break;case"user":{const i=this.hass.user.id;this.connection.sendMessage({type:"browser_mod/settings",user:i,key:e,value:t});break}case"browser":{const i=null===(s=this.browsers[this.browserID])||void 0===s?void 0:s.settings;i[e]=t,this._reregister({settings:i});break}}}get cameraEnabled(){return this.registered?this.browsers[this.browserID].camera:null}set cameraEnabled(e){this._reregister({camera:e})}sendUpdate(e){this.connected&&this.registered&&(this.LOG("Send:",e),this.connection.sendMessage({type:"browser_mod/update",browserID:this.browserID,data:e}))}browserIDChanged(e,t){var i,s;this.fireEvent("browser-mod-config-update"),void 0!==(null===(i=this.browsers)||void 0===i?void 0:i[e])&&void 0===(null===(s=this.browsers)||void 0===s?void 0:s[this.browserID])&&(async()=>{await this.connection.sendMessage({type:"browser_mod/register",browserID:e,data:Object.assign(Object.assign({},this.browsers[e]),{browserID:this.browserID})})})()}},pe=e=>class extends e{constructor(){super(),this._listeners={},this._brightness=255;const e=this._panel=document.createElement("div");document.body.append(e),e.classList.add("browser-mod-blackout"),e.attachShadow({mode:"open"});const t=document.createElement("style");e.shadowRoot.append(t),t.innerHTML="\n :host {\n background: rgba(0,0,0, var(--darkness));\n position: fixed;\n left: 0;\n top: 0;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 100%;\n z-index: 10000;\n display: block;\n pointer-events: none;\n }\n :host([dark]) {\n background: rgba(0,0,0,1);\n }\n ",this.addEventListener("command-screen_off",(()=>this._screen_off())),this.addEventListener("command-screen_on",(e=>this._screen_on(e))),this.addEventListener("fully-update",(()=>this.send_screen_status())),this.connectionPromise.then((()=>this._screen_on()))}send_screen_status(){let e=!this._panel.hasAttribute("dark"),t=this._brightness;this.fully&&(e=this.fully_screen,t=this.fully_brightness),this.sendUpdate({screen_on:e,screen_brightness:t})}_screen_off(){this.fully?this.fully_screen=!1:this._panel.setAttribute("dark",""),this.send_screen_status();const e=()=>this._screen_on();for(const t of["pointerdown","pointermove","keydown"])this._listeners[t]=e,window.addEventListener(t,e)}_screen_on(e){var t,i;this.fully?(this.fully_screen=!0,(null===(t=null==e?void 0:e.detail)||void 0===t?void 0:t.brightness)&&(this.fully_brightness=e.detail.brightness)):((null===(i=null==e?void 0:e.detail)||void 0===i?void 0:i.brightness)&&(this._brightness=e.detail.brightness,this._panel.style.setProperty("--darkness",1-e.detail.brightness/255)),this._panel.removeAttribute("dark")),this.send_screen_status();for(const e of["pointerdown","pointermove","keydown"])this._listeners[e]&&(window.removeEventListener(e,this._listeners[e]),this._listeners[e]=void 0)}},ve=e=>class extends e{constructor(){super(),this._audio_player=new Audio,this._video_player=document.createElement("video"),this._video_player.controls=!0,this._video_player.style.setProperty("width","100%"),this.player=this._audio_player,this._player_enabled=!1;for(const e of["play","pause","ended","volumechange"])this._audio_player.addEventListener(e,(()=>this._player_update())),this._video_player.addEventListener(e,(()=>this._player_update()));for(const e of["timeupdate"])this._audio_player.addEventListener(e,(()=>this._player_update_choked())),this._video_player.addEventListener(e,(()=>this._player_update_choked()));this.firstInteraction.then((()=>{this._player_enabled=!0,this.player.ended||this.player.play()})),this.addEventListener("command-player-play",(e=>{var t,i,s;this.player.src&&this.player.pause(),(null===(t=e.detail)||void 0===t?void 0:t.media_type)&&((null===(i=e.detail)||void 0===i?void 0:i.media_type.startsWith("video"))?this.player=this._video_player:this.player=this._audio_player),(null===(s=e.detail)||void 0===s?void 0:s.media_content_id)&&(this.player.src=e.detail.media_content_id),this.player.play(),this._show_video_player()})),this.addEventListener("command-player-pause",(e=>this.player.pause())),this.addEventListener("command-player-stop",(e=>{this.player.src=null,this.player.pause()})),this.addEventListener("command-player-set-volume",(e=>{var t;void 0!==(null===(t=e.detail)||void 0===t?void 0:t.volume_level)&&(this.player.volume=e.detail.volume_level)})),this.addEventListener("command-player-mute",(e=>{var t;void 0!==(null===(t=e.detail)||void 0===t?void 0:t.mute)?this.player.muted=Boolean(e.detail.mute):this.player.muted=!this.player.muted})),this.addEventListener("command-player-seek",(e=>{this.player.currentTime=e.detail.position,setTimeout((()=>this._player_update()),10)})),this.addEventListener("command-player-turn-off",(e=>{this.player===this._video_player&&this._video_player.isConnected?this.closePopup():this.player.src&&this.player.pause(),this.player.src="",this._player_update()})),this.connectionPromise.then((()=>this._player_update()))}_show_video_player(){this.player===this._video_player&&this.player.src?(de(document,"home-assistant $ dialog-media-player-browse").then((e=>null==e?void 0:e.closeDialog())),this.showPopup(void 0,this._video_player,{dismiss_action:()=>this._video_player.pause(),size:"wide"})):this.player!==this._video_player&&this._video_player.isConnected&&this.closePopup()}_player_update_choked(){this._player_update_cooldown||(this._player_update_cooldown=window.setTimeout((()=>this._player_update_cooldown=void 0),3e3),this._player_update())}_player_update(){const e=this._player_enabled?this.player.src&&this.player.src!==window.location.href?this.player.ended?"stopped":this.player.paused?"paused":"playing":"off":"unavailable";this.sendUpdate({player:{volume:this.player.volume,muted:this.player.muted,src:this.player.src,state:e,media_duration:this.player.duration,media_position:this.player.currentTime}})}},me=e=>class extends e{constructor(){super(),this._framerate=2,this._setup_camera()}async _setup_camera(){if(this._video)return;if(await this.connectionPromise,await this.firstInteraction,!this.cameraEnabled)return;if(this.fully)return this.update_camera();const e=document.createElement("div");document.body.append(e),e.classList.add("browser-mod-camera"),e.attachShadow({mode:"open"});const t=document.createElement("style");e.shadowRoot.append(t),t.innerHTML="\n :host {\n display: none;\n }";const i=this._video=document.createElement("video");e.shadowRoot.append(i),i.autoplay=!0,i.playsInline=!0,i.style.display="none";const s=this._canvas=document.createElement("canvas");if(e.shadowRoot.append(s),s.style.display="none",!navigator.mediaDevices)return;const o=await navigator.mediaDevices.getUserMedia({video:!0,audio:!1});i.srcObject=o,i.play(),this.update_camera()}async update_camera(){var e;if(!this.cameraEnabled){const t=null===(e=this._video)||void 0===e?void 0:e.srcObject;return void(t&&(t.getTracks().forEach((e=>e.stop())),this._video.scrObject=void 0))}if(this.fully)this.sendUpdate({camera:this.fully_camera});else{const e=this._video,t=e.videoWidth,i=e.videoHeight;this._canvas.width=t,this._canvas.height=i;this._canvas.getContext("2d").drawImage(e,0,0,t,i),this.sendUpdate({camera:this._canvas.toDataURL("image/jpeg")})}const t=Math.round(1e3/this._framerate);setTimeout((()=>this.update_camera()),t)}},_e=e=>class extends e{constructor(){super(),this.firstInteraction=new Promise((e=>{this._interactionResolve=e})),this.show_indicator()}async show_indicator(){if(await this.connectionPromise,!this.registered)return;const e=document.createElement("div");document.body.append(e),e.classList.add("browser-mod-require-interaction"),e.attachShadow({mode:"open"});const t=document.createElement("style");e.shadowRoot.append(t),t.innerHTML='\n :host {\n position: fixed;\n right: 8px;\n bottom: 8px;\n color: var(--warning-color, red);\n opacity: 0.5;\n --mdc-icon-size: 48px;\n }\n ha-icon::before {\n content: "Browser\\00a0Mod";\n font-size: 0.75rem;\n position: absolute;\n right: 0;\n bottom: 90%;\n }\n video {\n display: none;\n }\n ';const i=document.createElement("ha-icon");e.shadowRoot.append(i),i.icon="mdi:gesture-tap";const s=this._video=document.createElement("video");e.shadowRoot.append(s);const o=s.play();o&&(o.then((()=>{this._interactionResolve()})).catch((e=>{"AbortError"===e.name&&this._interactionResolve()})),s.pause()),window.addEventListener("pointerdown",this._interactionResolve),await this.firstInteraction,e.remove()}},ge=e=>class extends e{constructor(){if(super(),this._fully_screensaver=!1,this.fully){for(const e of["screenOn","screenOff","pluggedAC","pluggedUSB","onBatteryLevelChanged","unplugged","networkReconnect","onMotion","onDaydreamStart","onDaydreamStop"])window.fully.bind(e,`window.browser_mod.fullyEvent("${e}");`);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();")}}get fully(){return void 0!==window.fully}get fully_screen(){var e;return!1===this._fully_screensaver&&(null===(e=window.fully)||void 0===e?void 0:e.getScreenOn())}set fully_screen(e){var t,i,s;e?(null===(t=window.fully)||void 0===t||t.turnScreenOn(),null===(i=window.fully)||void 0===i||i.stopScreensaver()):null===(s=window.fully)||void 0===s||s.turnScreenOff()}get fully_brightness(){var e;return null===(e=window.fully)||void 0===e?void 0:e.getScreenBrightness()}set fully_brightness(e){var t;null===(t=window.fully)||void 0===t||t.setScreenBrightness(e)}get fully_camera(){var e;return null===(e=window.fully)||void 0===e?void 0:e.getCamshotJpgBase64()}fullyEvent(e){this.fireEvent("fully-update",{event:e})}},we=e=>class extends e{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(){(async()=>{var e,t,i,s,o,n,r,a,d,l,c,h;const u=null===(t=(e=navigator).getBattery)||void 0===t?void 0:t.call(e);this.sendUpdate({browser:{path:window.location.pathname,visibility:document.visibilityState,userAgent:navigator.userAgent,currentUser:null===(s=null===(i=this.hass)||void 0===i?void 0:i.user)||void 0===s?void 0:s.name,fullyKiosk:this.fully||!1,width:window.innerWidth,height:window.innerHeight,battery_level:null!==(n=null===(o=window.fully)||void 0===o?void 0:o.getBatteryLevel())&&void 0!==n?n:100*(null==u?void 0:u.level),charging:null!==(a=null===(r=window.fully)||void 0===r?void 0:r.isPlugged())&&void 0!==a?a:null==u?void 0:u.charging,darkMode:null===(l=null===(d=this.hass)||void 0===d?void 0:d.themes)||void 0===l?void 0:l.darkMode,userData:null===(c=this.hass)||void 0===c?void 0:c.user,ip_address:null===(h=window.fully)||void 0===h?void 0:h.getIp4Address()}})})()}async browser_navigate(e){e&&(history.pushState(null,"",e),window.dispatchEvent(new CustomEvent("location-changed")))}},be=e=>class extends e{constructor(){super();const e=["sequence","delay","popup","more_info","close_popup","navigate","refresh","console","javascript"];for(const t of e)this.addEventListener(`command-${t}`,(e=>{this.service(t,e.detail)}));document.body.addEventListener("ll-custom",(e=>{e.detail.browser_mod&&this._service_action(e.detail.browser_mod)}))}async service(e,t){this._service_action({service:e,data:t})}async _service_action({service:e,data:t}){let i=e;if(!i.startsWith("browser_mod.")&&i.includes(".")||void 0!==t.browser_id){const e=Object.assign({},t);"THIS"===e.browser_id&&(e.browser_id=this.browserID);const[s,o]=i.split(".");return this.hass.callService(s,o,e)}switch(i.startsWith("browser_mod.")&&(i=i.substring(12)),i){case"sequence":for(const e of t.sequence)await this._service_action(e);break;case"delay":await new Promise((e=>setTimeout(e,t.time)));break;case"more_info":const{entity:e,large:i,ignore_popup_card:s}=t;this.showMoreInfo(e,i,s);break;case"popup":const{title:o,content:n}=t,r=function(e,t){var i={};for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.indexOf(s)<0&&(i[s]=e[s]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(s=Object.getOwnPropertySymbols(e);othis._service_action(t));this.showPopup(o,n,r);break;case"close_popup":this.closePopup();break;case"navigate":this.browser_navigate(t.path);break;case"refresh":window.location.href=window.location.href;break;case"console":console.log(t.message);break;case"javascript":const a=`\n "use strict";\n // Insert global definitions here\n const hass = (document.querySelector("home-assistant") || document.querySelector("hc-main")).hass;\n ${t.code}\n `;new Function(a)()}}},ye=e=>class extends e{constructor(){super(),this.activityTriggered=!1,this._activityCooldown=15e3;for(const e of["pointerdown","pointermove","keydown"])window.addEventListener(e,(()=>this.activityTrigger(!0)));this.addEventListener("fully-update",(()=>{this.activityTrigger()}))}activityTrigger(e=!1){this.activityTriggered||this.sendUpdate({activity:!0}),this.activityTriggered=!0,e&&this.fireEvent("browser-mod-activity"),clearTimeout(this._activityTimeout),this._activityTimeout=setTimeout((()=>this.activityReset()),this._activityCooldown)}activityReset(){clearTimeout(this._activityTimeout),this.activityTriggered&&this.sendUpdate({activity:!1}),this.activityTriggered=!1}},fe=2;class $e extends class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}}{constructor(e){if(super(e),this.it=U,e.type!==fe)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(e){if(e===U||null==e)return this.ft=void 0,this.it=e;if(e===O)return e;if("string"!=typeof e)throw Error(this.constructor.directiveName+"() called with a non-string value");if(e===this.it)return this.ft;this.it=e;const t=[e];return t.raw=t,this.ft={_$litType$:this.constructor.resultType,strings:t,values:[]}}}$e.directiveName="unsafeHTML",$e.resultType=1;const Ee=(e=>(...t)=>({_$litDirective$:e,values:t}))($e);class Ae extends Q{async closeDialog(){this.open=!1,clearInterval(this._timeoutTimer),this._autocloseListener&&(window.browser_mod.removeEventListener("browser-mod-activity",this._autocloseListener),this._autocloseListener=void 0)}openDialog(){var e;this.open=!0,null===(e=this.dialog)||void 0===e||e.show(),this.timeout&&(this._timeoutStart=(new Date).getTime(),this._timeoutTimer=setInterval((()=>{const e=(new Date).getTime()-this._timeoutStart,t=e/this.timeout*100;this.style.setProperty("--progress",`${t}%`),e>=this.timeout&&this._timeout()}),10)),this._autocloseListener=void 0,this._autoclose&&(this._autocloseListener=()=>this.dialog.close(),window.browser_mod.addEventListener("browser-mod-activity",this._autocloseListener,{once:!0}))}async setupDialog(e,t,{right_button:i,right_button_action:s,left_button:o,left_button_action:n,dismissable:r=!0,dismiss_action:a,timeout:d,timeout_action:l,size:c,style:h,autoclose:u=!1}={}){if(this.title=e,t&&t instanceof HTMLElement)this.card=void 0,this.content=t;else if(t&&"object"==typeof t){this.card=!0;const e=await window.loadCardHelpers(),i=await e.createCardElement(t);i.hass=window.browser_mod.hass,ce(i),this.content=i}else this.card=void 0,this.content=Ee(t);this.right_button=i,this.left_button=o,this.actions=void 0===i?void 0:"",this.dismissable=r,this.timeout=d,this._actions={right_button_action:s,left_button_action:n,dismiss_action:a,timeout_action:l},this.wide="wide"===c?"":void 0,this.fullscreen="fullscreen"===c?"":void 0,this._style=h,this._autoclose=u}async _primary(){var e,t,i,s;(null===(e=this._actions)||void 0===e?void 0:e.dismiss_action)&&(this._actions.dismiss_action=void 0),null===(t=this.dialog)||void 0===t||t.close(),null===(s=null===(i=this._actions)||void 0===i?void 0:i.right_button_action)||void 0===s||s.call(i)}async _secondary(){var e,t,i,s;(null===(e=this._actions)||void 0===e?void 0:e.dismiss_action)&&(this._actions.dismiss_action=void 0),null===(t=this.dialog)||void 0===t||t.close(),null===(s=null===(i=this._actions)||void 0===i?void 0:i.left_button_action)||void 0===s||s.call(i)}async _dismiss(e){var t,i,s;null===(t=this.dialog)||void 0===t||t.close(),null===(s=null===(i=this._actions)||void 0===i?void 0:i.dismiss_action)||void 0===s||s.call(i)}async _timeout(){var e,t,i,s;(null===(e=this._actions)||void 0===e?void 0:e.dismiss_action)&&(this._actions.dismiss_action=void 0),null===(t=this.dialog)||void 0===t||t.close(),null===(s=null===(i=this._actions)||void 0===i?void 0:i.timeout_action)||void 0===s||s.call(i)}render(){return this.open?M` - ${this.timeout - ? $ `
` - : ""} - ${this.title - ? $ ` + ${this.timeout?M`
`:""} + ${this.title?M` - ${this.dismissable - ? $ ` + ${this.dismissable?M` - ` - : ""} + `:""}
${this.title}
- ` - : $ ``} + `:M``}
${this.content}
- ${this.right_button !== undefined - ? $ ` + ${void 0!==this.right_button?M` - ` - : ""} - ${this.left_button !== undefined - ? $ ` + `:""} + ${void 0!==this.left_button?M` - ` - : ""} + `:""}
- `; - } - static get styles() { - return r$2 ` + `:M``}static get styles(){return n` ha-dialog { z-index: 10; --mdc-dialog-min-width: var(--popup-min-width, 400px); @@ -1377,7 +145,7 @@ class BrowserModPopup extends s { color: var(--primary-text-color); background-color: var( --popup-header-background-color, - var(--popup-background-color, --sidebar-background-color) + var(--popup-background-color, var(--sidebar-background-color)) ); } ha-icon-button > * { @@ -1430,287 +198,7 @@ class BrowserModPopup extends s { --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); -__decorate([ - e$2({ reflect: true }) -], BrowserModPopup.prototype, "wide", void 0); -__decorate([ - e$2({ reflect: true }) -], BrowserModPopup.prototype, "fullscreen", void 0); -__decorate([ - e$2() -], BrowserModPopup.prototype, "_style", void 0); -__decorate([ - i$1("ha-dialog") -], BrowserModPopup.prototype, "dialog", void 0); -if (!customElements.get("browser-mod-popup")) - 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).then(() => this._popupEl.openDialog()); - } - closePopup(...args) { - this._popupEl.closeDialog(); - this.showMoreInfo(""); - } - async showMoreInfo(entityId, large = false, ignore_popup_card = undefined) { - const base = await hass_base_el(); - base.dispatchEvent(new CustomEvent("hass-more-info", { - bubbles: true, - composed: true, - cancelable: false, - detail: { entityId, ignore_popup_card }, - })); - if (large) { - await new Promise((resolve) => setTimeout(resolve, 50)); - const dialog = base.shadowRoot.querySelector("ha-more-info-dialog"); - if (dialog) - dialog.large = true; - } - } - }; -}; - -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 -}; - -const configSchema = [ - { - name: "entity", - label: "Entity", - selector: { entity: {} }, - }, - { - name: "title", - label: "Title", - selector: { text: {} }, - }, - { - name: "size", - selector: { - select: { mode: "dropdown", options: ["normal", "wide", "fullscreen"] }, - }, - }, - { - type: "grid", - schema: [ - { - name: "right_button", - label: "Right button", - selector: { text: {} }, - }, - { - name: "left_button", - label: "Left button", - selector: { text: {} }, - }, - ], - }, - { - type: "grid", - schema: [ - { - name: "right_button_action", - label: "Right button action", - selector: { object: {} }, - }, - { - name: "left_button_action", - label: "Left button action", - selector: { object: {} }, - }, - ], - }, - { - type: "grid", - schema: [ - { - name: "dismissable", - label: "User dismissable", - selector: { boolean: {} }, - }, - { - name: "timeout", - label: "Auto close timeout (ms)", - selector: { number: { mode: "box" } }, - }, - ], - }, - { - type: "grid", - schema: [ - { - name: "dismiss_action", - label: "Dismiss action", - selector: { object: {} }, - }, - { - name: "timeout_action", - label: "Timeout action", - selector: { object: {} }, - }, - ], - }, - { - name: "style", - label: "CSS style", - selector: { text: { multiline: true } }, - }, -]; -class PopupCardEditor extends s { - constructor() { - super(...arguments); - this._selectedTab = 0; - this._cardGUIMode = true; - this._cardGUIModeAvailable = true; - } - setConfig(config) { - this._config = config; - } - connectedCallback() { - super.connectedCallback(); - loadHaForm(); - } - _handleSwitchTab(ev) { - this._selectedTab = parseInt(ev.detail.index, 10); - } - _configChanged(ev) { - ev.stopPropagation(); - if (!this._config) - return; - this._config = Object.assign({}, ev.detail.value); - this.dispatchEvent(new CustomEvent("config-changed", { detail: { config: this._config } })); - } - _cardConfigChanged(ev) { - ev.stopPropagation(); - if (!this._config) - return; - const card = Object.assign({}, ev.detail.config); - this._config = Object.assign(Object.assign({}, this._config), { card }); - this._cardGUIModeAvailable = ev.detail.guiModeAvailable; - this.dispatchEvent(new CustomEvent("config-changed", { detail: { config: this._config } })); - } - _toggleCardMode(ev) { - var _a; - (_a = this._cardEditorEl) === null || _a === void 0 ? void 0 : _a.toggleMode(); - } - _deleteCard(ev) { - if (!this._config) - return; - this._config = Object.assign({}, this._config); - delete this._config.card; - this.dispatchEvent(new CustomEvent("config-changed", { detail: { config: this._config } })); - } - _cardGUIModeChanged(ev) { - ev.stopPropagation(); - this._cardGUIMode = ev.detail.guiMode; - this._cardGUIModeAvailable = ev.detail.guiModeAvailable; - } - render() { - if (!this.hass || !this._config) { - return $ ``; - } - return $ ` + `}}e([ee()],Ae.prototype,"open",void 0),e([ee()],Ae.prototype,"content",void 0),e([ee()],Ae.prototype,"title",void 0),e([ee({reflect:!0})],Ae.prototype,"actions",void 0),e([ee({reflect:!0})],Ae.prototype,"card",void 0),e([ee()],Ae.prototype,"right_button",void 0),e([ee()],Ae.prototype,"left_button",void 0),e([ee()],Ae.prototype,"dismissable",void 0),e([ee({reflect:!0})],Ae.prototype,"wide",void 0),e([ee({reflect:!0})],Ae.prototype,"fullscreen",void 0),e([ee()],Ae.prototype,"_style",void 0),e([ie("ha-dialog")],Ae.prototype,"dialog",void 0),customElements.get("browser-mod-popup")||customElements.define("browser-mod-popup",Ae);const xe=e=>class extends e{constructor(){super(),he(),this._popupEl=document.createElement("browser-mod-popup"),document.body.append(this._popupEl)}showPopup(...e){this._popupEl.setupDialog(...e).then((()=>this._popupEl.openDialog()))}closePopup(...e){this._popupEl.closeDialog(),this.showMoreInfo("")}async showMoreInfo(e,t=!1,i){const s=await le();if(s.dispatchEvent(new CustomEvent("hass-more-info",{bubbles:!0,composed:!0,cancelable:!1,detail:{entityId:e,ignore_popup_card:i}})),t){await new Promise((e=>setTimeout(e,50)));const e=s.shadowRoot.querySelector("ha-more-info-dialog");e&&(e.large=!0)}}};var Se="2.0.0b1";const Ce=[{name:"entity",label:"Entity",selector:{entity:{}}},{name:"title",label:"Title",selector:{text:{}}},{name:"size",selector:{select:{mode:"dropdown",options:["normal","wide","fullscreen"]}}},{type:"grid",schema:[{name:"right_button",label:"Right button",selector:{text:{}}},{name:"left_button",label:"Left button",selector:{text:{}}}]},{type:"grid",schema:[{name:"right_button_action",label:"Right button action",selector:{object:{}}},{name:"left_button_action",label:"Left button action",selector:{object:{}}}]},{type:"grid",schema:[{name:"dismissable",label:"User dismissable",selector:{boolean:{}}},{name:"timeout",label:"Auto close timeout (ms)",selector:{number:{mode:"box"}}}]},{type:"grid",schema:[{name:"dismiss_action",label:"Dismiss action",selector:{object:{}}},{name:"timeout_action",label:"Timeout action",selector:{object:{}}}]},{name:"style",label:"CSS style",selector:{text:{multiline:!0}}}];class Te extends Q{constructor(){super(...arguments),this._selectedTab=0,this._cardGUIMode=!0,this._cardGUIModeAvailable=!0}setConfig(e){this._config=e}connectedCallback(){super.connectedCallback(),(async()=>{if(customElements.get("ha-form"))return;await he();const e=await window.loadCardHelpers();if(!e)return;const t=await e.createCardElement({type:"entity"});t&&await t.getConfigElement()})()}_handleSwitchTab(e){this._selectedTab=parseInt(e.detail.index,10)}_configChanged(e){e.stopPropagation(),this._config&&(this._config=Object.assign({},e.detail.value),this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}})))}_cardConfigChanged(e){if(e.stopPropagation(),!this._config)return;const t=Object.assign({},e.detail.config);this._config=Object.assign(Object.assign({},this._config),{card:t}),this._cardGUIModeAvailable=e.detail.guiModeAvailable,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}}))}_toggleCardMode(e){var t;null===(t=this._cardEditorEl)||void 0===t||t.toggleMode()}_deleteCard(e){this._config&&(this._config=Object.assign({},this._config),delete this._config.card,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:this._config}})))}_cardGUIModeChanged(e){e.stopPropagation(),this._cardGUIMode=e.detail.guiMode,this._cardGUIModeAvailable=e.detail.guiModeAvailable}render(){return this.hass&&this._config?M`
- ${[this._renderSettingsEditor, this._renderCardEditor][this._selectedTab].bind(this)()} + ${[this._renderSettingsEditor,this._renderCardEditor][this._selectedTab].bind(this)()}
- `; - } - _renderSettingsEditor() { - return $ `
+ `:M``}_renderSettingsEditor(){return M`
{ var _a; return (_a = s.label) !== null && _a !== void 0 ? _a : s.name; }} + .schema=${Ce} + .computeLabel=${e=>{var t;return null!==(t=e.label)&&void 0!==t?t:e.name}} @value-changed=${this._configChanged} > -
`; - } - _renderCardEditor() { - return $ ` +
`}_renderCardEditor(){return M`
- ${this._config.card - ? $ ` + ${this._config.card?M`
- ${!this._cardEditorEl || this._cardGUIMode - ? "Show code editor" - : "Show visual editor"} + ${!this._cardEditorEl||this._cardGUIMode?"Show code editor":"Show visual editor"} - ` - : $ ` + `:M` `}
- `; - } - static get styles() { - return r$2 ` + `}static get styles(){return n` mwc-tab-bar { border-bottom: 1px solid var(--divider-color); } @@ -1797,118 +272,13 @@ class PopupCardEditor extends s { .gui-mode-button { margin-right: auto; } - `; - } -} -__decorate([ - t$1() -], PopupCardEditor.prototype, "_config", void 0); -__decorate([ - e$2() -], PopupCardEditor.prototype, "lovelace", void 0); -__decorate([ - e$2() -], PopupCardEditor.prototype, "hass", void 0); -__decorate([ - t$1() -], PopupCardEditor.prototype, "_selectedTab", void 0); -__decorate([ - t$1() -], PopupCardEditor.prototype, "_cardGUIMode", void 0); -__decorate([ - t$1() -], PopupCardEditor.prototype, "_cardGUIModeAvailable", void 0); -__decorate([ - i$1("hui-card-element-editor") -], PopupCardEditor.prototype, "_cardEditorEl", void 0); -(async () => { - while (!window.browser_mod) { - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - await window.browser_mod.connectionPromise; - if (!customElements.get("popup-card-editor")) - customElements.define("popup-card-editor", PopupCardEditor); - window.customCards = window.customCards || []; - window.customCards.push({ - type: "popup-card", - name: "Popup card", - preview: false, - description: "Replace the more-info dialog for a given entity in the view that includes this card. (Browser Mod)", - }); -})(); - -class PopupCard extends s { - constructor() { - super(); - this.popup = this.popup.bind(this); - } - static getConfigElement() { - return document.createElement("popup-card-editor"); - } - static getStubConfig(hass, entities) { - const entity = entities[0]; - return { - entity, - title: "Custom popup", - dismissable: true, - card: { type: "markdown", content: "This replaces the more-info dialog" }, - }; - } - setConfig(config) { - this._config = config; - (async () => { - const ch = await window.loadCardHelpers(); - this._element = await ch.createCardElement(config.card); - this._element.hass = this.hass; - })(); - } - async connectedCallback() { - super.connectedCallback(); - window.addEventListener("hass-more-info", this.popup); - if (this.parentElement.localName === "hui-card-preview") { - this.editMode = true; - } - } - async disconnectedCallback() { - super.disconnectedCallback(); - window.removeEventListener("hass-more-info", this.popup); - } - popup(ev) { - var _a, _b, _c; - if (((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.entityId) === this._config.entity && - !((_b = ev.detail) === null || _b === void 0 ? void 0 : _b.ignore_popup_card)) { - ev.stopPropagation(); - ev.preventDefault(); - const config = Object.assign({}, this._config); - delete config.card; - (_c = window.browser_mod) === null || _c === void 0 ? void 0 : _c.service("popup", Object.assign({ content: this._config.card }, this._config)); - setTimeout(() => this.dispatchEvent(new CustomEvent("hass-more-info", { - bubbles: true, - composed: true, - cancelable: false, - detail: { entityId: "." }, - })), 50); - } - } - updated(changedProperties) { - super.updated(changedProperties); - if (changedProperties.has("hass")) { - if (this._element) - this._element.hass = this.hass; - } - } - render() { - if (!this.editMode) - return $ ``; - return $ ` + `}}e([te()],Te.prototype,"_config",void 0),e([ee()],Te.prototype,"lovelace",void 0),e([ee()],Te.prototype,"hass",void 0),e([te()],Te.prototype,"_selectedTab",void 0),e([te()],Te.prototype,"_cardGUIMode",void 0),e([te()],Te.prototype,"_cardGUIModeAvailable",void 0),e([ie("hui-card-element-editor")],Te.prototype,"_cardEditorEl",void 0),(async()=>{for(;!window.browser_mod;)await new Promise((e=>setTimeout(e,1e3)));await window.browser_mod.connectionPromise,customElements.get("popup-card-editor")||(customElements.define("popup-card-editor",Te),window.customCards=window.customCards||[],window.customCards.push({type:"popup-card",name:"Popup card",preview:!1,description:"Replace the more-info dialog for a given entity in the view that includes this card. (Browser Mod)"}))})();class Pe extends Q{constructor(){super(),this.popup=this.popup.bind(this)}static getConfigElement(){return document.createElement("popup-card-editor")}static getStubConfig(e,t){return{entity:t[0],title:"Custom popup",dismissable:!0,card:{type:"markdown",content:"This replaces the more-info dialog"}}}setConfig(e){this._config=e,(async()=>{const t=await window.loadCardHelpers();this._element=await t.createCardElement(e.card),this._element.hass=this.hass})()}async connectedCallback(){super.connectedCallback(),window.addEventListener("hass-more-info",this.popup),"hui-card-preview"===this.parentElement.localName?(this.editMode=!0,this.removeAttribute("hidden")):this.setAttribute("hidden","")}async disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("hass-more-info",this.popup)}popup(e){var t,i,s;if((null===(t=e.detail)||void 0===t?void 0:t.entityId)===this._config.entity&&!(null===(i=e.detail)||void 0===i?void 0:i.ignore_popup_card)){e.stopPropagation(),e.preventDefault();delete Object.assign({},this._config).card,null===(s=window.browser_mod)||void 0===s||s.service("popup",Object.assign({content:this._config.card},this._config)),setTimeout((()=>this.dispatchEvent(new CustomEvent("hass-more-info",{bubbles:!0,composed:!0,cancelable:!1,detail:{entityId:"."}}))),50)}}updated(e){super.updated(e),e.has("hass")&&this._element&&(this._element.hass=this.hass)}render(){return this.editMode?M`
- ${this._config.dismissable - ? $ ` + ${this._config.dismissable?M` - ` - : ""} + `:""}
${this._config.title}
${this._element} @@ -1917,35 +287,25 @@ class PopupCard extends s { ${this._config.style} } - ${this._config.right_button !== undefined || - this._config.left_button !== undefined - ? $ ` + ${void 0!==this._config.right_button||void 0!==this._config.left_button?M`
- ${this._config.left_button !== undefined - ? $ ` + ${void 0!==this._config.left_button?M` - ` - : ""} + `:""} - ${this._config.right_button !== undefined - ? $ ` + ${void 0!==this._config.right_button?M` - ` - : ""} + `:""}
- ` - : ""} -
`; - } - static get styles() { - return r$2 ` + `:""} +
`:M``}static get styles(){return n` :host { display: none !important; } @@ -1992,253 +352,4 @@ class PopupCard extends s { padding: 8px; border-top: 1px solid transparent; } - `; - } -} -__decorate([ - e$2() -], PopupCard.prototype, "hass", void 0); -__decorate([ - t$1() -], PopupCard.prototype, "_config", void 0); -__decorate([ - e$2({ attribute: "edit-mode", reflect: true }) -], PopupCard.prototype, "editMode", void 0); -__decorate([ - t$1() -], PopupCard.prototype, "_element", void 0); -(async () => { - while (!window.browser_mod) { - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - await window.browser_mod.connectionPromise; - if (!customElements.get("popup-card")) - customElements.define("popup-card", PopupCard); -})(); - -const AutoSettingsMixin = (SuperClass) => { - return class AutoSettingsMixinClass extends SuperClass { - constructor() { - super(); - this.__currentTitle = undefined; - this._auto_settings_setup(); - this.addEventListener("browser-mod-config-update", () => this._auto_settings_setup()); - window.addEventListener("location-changed", () => { - this._updateTitle(); - setTimeout(() => this._updateTitle(), 500); - setTimeout(() => this._updateTitle(), 1000); - setTimeout(() => this._updateTitle(), 5000); - }); - } - async _auto_settings_setup() { - await this.connectionPromise; - const settings = this.settings; - // Sidebar panel order and hiding - if (settings.sidebarPanelOrder) { - localStorage.setItem("sidebarPanelOrder", settings.sidebarPanelOrder); - } - if (settings.sidebarHiddenPanels) { - localStorage.setItem("sidebarHiddenPanels", settings.sidebarHiddenPanels); - } - // Default panel - if (settings.defaultPanel) { - localStorage.setItem("defaultPanel", settings.defaultPanel); - } - // Hide sidebar - if (settings.hideSidebar === true) { - selectTree(document.body, "home-assistant$home-assistant-main$app-drawer-layout").then((el) => { var _a; return (_a = el === null || el === void 0 ? void 0 : el.style) === null || _a === void 0 ? void 0 : _a.setProperty("--app-drawer-width", "0px"); }); - selectTree(document.body, "home-assistant$home-assistant-main$app-drawer-layout app-drawer").then((el) => { var _a; return (_a = el === null || el === void 0 ? void 0 : el.remove) === null || _a === void 0 ? void 0 : _a.call(el); }); - } - // Hide header - if (settings.hideHeader === true) { - customElements.whenDefined("app-header-layout").then(() => { - const appHeader = customElements.get("app-header").prototype; - const _attached = appHeader.attached; - appHeader.attached = function () { - _attached.bind(this)(); - this.style.setProperty("display", "none"); - }; - }); - } - // Favicon template - if (settings.faviconTemplate !== undefined) { - (async () => { - if (this._faviconTemplateSubscription) { - this._faviconTemplateSubscription(); - } - this._faviconTemplateSubscription = undefined; - this._faviconTemplateSubscription = - await this.connection.subscribeMessage(this._updateFavicon, { - type: "render_template", - template: settings.faviconTemplate, - variables: {}, - }); - })(); - } - // Title template - if (settings.titleTemplate !== undefined) { - (async () => { - if (this._titleTemplateSubscription) { - this._titleTemplateSubscription(); - } - this._titleTemplateSubscription = undefined; - this._titleTemplateSubscription = - await this.connection.subscribeMessage(this._updateTitle.bind(this), { - type: "render_template", - template: settings.titleTemplate, - variables: {}, - }); - })(); - } - } - get _currentFavicon() { - const link = document.head.querySelector("link[rel~='icon']"); - return link === null || link === void 0 ? void 0 : link.href; - } - _updateFavicon({ result }) { - const link = document.head.querySelector("link[rel~='icon']"); - link.href = result; - window.browser_mod.fireEvent("browser-mod-favicon-update"); - } - get _currentTitle() { - return this.__currentTitle; - } - _updateTitle(data = undefined) { - if (data) - this.__currentTitle = data.result; - if (this.__currentTitle) - document.title = this.__currentTitle; - window.browser_mod.fireEvent("browser-mod-favicon-update"); - } - }; -}; - -const ID_STORAGE_KEY = "browser_mod-browser-id"; -const BrowserIDMixin = (SuperClass) => { - return class BrowserIDMixinClass extends SuperClass { - constructor() { - super(); - if (Storage) { - if (!Storage.prototype.browser_mod_patched) { - const _clear = Storage.prototype.clear; - Storage.prototype.clear = function () { - const browserId = this.getItem(ID_STORAGE_KEY); - const suspendWhenHidden = this.getItem("suspendWhenHidden"); - _clear.apply(this); - this.setItem(ID_STORAGE_KEY, browserId); - this.setItem("suspendWhenHidden", suspendWhenHidden); - }; - Storage.prototype.browser_mod_patched = true; - } - } - } - async recall_id() { - // If the connection is still open, but the BrowserID has disappeared - recall it from the backend - // This happens e.g. when the frontend cache is reset in the Compainon app - if (!this.connection) - return; - const recalledID = await this.connection.sendMessagePromise({ - type: "browser_mod/recall_id", - }); - if (recalledID) { - localStorage[ID_STORAGE_KEY] = recalledID; - } - } - get browserID() { - if (document.querySelector("hc-main")) - return "CAST"; - if (localStorage[ID_STORAGE_KEY]) - return localStorage[ID_STORAGE_KEY]; - this.browserID = ""; - this.recall_id(); - return this.browserID; - } - set browserID(id) { - 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.browserIDChanged(oldID, id); - } - browserIDChanged(oldID, newID) { } - }; -}; - -/* - TODO: - - Fix nomenclature - x Command -> Service - x Device -> Browser - - Popups - X Basic popups - x Styling - X Timeout - X Fullscreen - x Popup-card - x Motion/occupancy tracker - x Information about interaction requirement - x Information about fullykiosk - - Commands - x Change targets from the frontend - x Send browser ID to the backend in service calls? - x Rename browser_mod commands to browser_mod services - x Framework - x ll-custom handling - - Commands - x popup - x Auto-close - x close_popup - x more-info - x navigate - - lovelace-reload? - - Not needed - x window-reload - - screensaver ? - - Refer to automations instead - x sequence - x delay - x javascript eval - - toast? - - Replaced with popups with timeout - x Redesign services to target devices - x frontend editor for popup cards - - also screensavers - - Saved frontend settings - X Framework - x Save sidebar - x Kiosk mode - x Default dashboard - - Screensaver? - x Favicon templates - x Title templates - - Tweaks - - Quickbar tweaks (ctrl+enter)? - x Card-mod preload - x Video player? - x Media_seek - - Screensavers - x IMPORTANT: FIX DEFAULT HIDING OF ENTITIES - - NOFIX. Home Assistant bug - X Check functionality with CAST - may need to add frontend part as a lovelace resource - */ -class BrowserMod extends ServicesMixin(PopupMixin(ActivityMixin(BrowserStateMixin(CameraMixin(MediaPlayerMixin(ScreenSaverMixin(AutoSettingsMixin(FullyMixin(RequireInteractMixin(ConnectionMixin(BrowserIDMixin(EventTarget)))))))))))) { - constructor() { - super(); - this.connect(); - 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 }; + `}}e([ee()],Pe.prototype,"hass",void 0),e([te()],Pe.prototype,"_config",void 0),e([ee({attribute:"edit-mode",reflect:!0})],Pe.prototype,"editMode",void 0),e([te()],Pe.prototype,"_element",void 0),(async()=>{for(;!window.browser_mod;)await new Promise((e=>setTimeout(e,1e3)));await window.browser_mod.connectionPromise,customElements.get("popup-card")||customElements.define("popup-card",Pe)})();const ke=e=>class extends e{constructor(){super(),this.__currentTitle=void 0,this._auto_settings_setup(),this.addEventListener("browser-mod-config-update",(()=>this._auto_settings_setup())),window.addEventListener("location-changed",(()=>{this._updateTitle(),setTimeout((()=>this._updateTitle()),500),setTimeout((()=>this._updateTitle()),1e3),setTimeout((()=>this._updateTitle()),5e3)}))}async _auto_settings_setup(){await this.connectionPromise;const e=this.settings;e.sidebarPanelOrder&&localStorage.setItem("sidebarPanelOrder",e.sidebarPanelOrder),e.sidebarHiddenPanels&&localStorage.setItem("sidebarHiddenPanels",e.sidebarHiddenPanels),e.defaultPanel&&localStorage.setItem("defaultPanel",e.defaultPanel),!0===e.hideSidebar&&(de(document.body,"home-assistant$home-assistant-main$app-drawer-layout").then((e=>{var t;return null===(t=null==e?void 0:e.style)||void 0===t?void 0:t.setProperty("--app-drawer-width","0px")})),de(document.body,"home-assistant$home-assistant-main$app-drawer-layout app-drawer").then((e=>{var t;return null===(t=null==e?void 0:e.remove)||void 0===t?void 0:t.call(e)}))),!0===e.hideHeader&&customElements.whenDefined("app-header-layout").then((()=>{const e=customElements.get("app-header").prototype,t=e.attached;e.attached=function(){t.bind(this)(),this.style.setProperty("display","none")}})),void 0!==e.faviconTemplate&&(async()=>{this._faviconTemplateSubscription&&this._faviconTemplateSubscription(),this._faviconTemplateSubscription=void 0,this._faviconTemplateSubscription=await this.connection.subscribeMessage(this._updateFavicon,{type:"render_template",template:e.faviconTemplate,variables:{}})})(),void 0!==e.titleTemplate&&(async()=>{this._titleTemplateSubscription&&this._titleTemplateSubscription(),this._titleTemplateSubscription=void 0,this._titleTemplateSubscription=await this.connection.subscribeMessage(this._updateTitle.bind(this),{type:"render_template",template:e.titleTemplate,variables:{}})})()}get _currentFavicon(){const e=document.head.querySelector("link[rel~='icon']");return null==e?void 0:e.href}_updateFavicon({result:e}){document.head.querySelector("link[rel~='icon']").href=e,window.browser_mod.fireEvent("browser-mod-favicon-update")}get _currentTitle(){return this.__currentTitle}_updateTitle(e){e&&(this.__currentTitle=e.result),this.__currentTitle&&(document.title=this.__currentTitle),window.browser_mod.fireEvent("browser-mod-favicon-update")}},Ie="browser_mod-browser-id",Me=e=>class extends e{constructor(){if(super(),Storage&&!Storage.prototype.browser_mod_patched){const e=Storage.prototype.clear;Storage.prototype.clear=function(){const t=this.getItem(Ie),i=this.getItem("suspendWhenHidden");e.apply(this),this.setItem(Ie,t),this.setItem("suspendWhenHidden",i)},Storage.prototype.browser_mod_patched=!0}}async recall_id(){if(!this.connection)return;const e=await this.connection.sendMessagePromise({type:"browser_mod/recall_id"});e&&(localStorage[Ie]=e)}get browserID(){return document.querySelector("hc-main")?"CAST":localStorage[Ie]?localStorage[Ie]:(this.browserID="",this.recall_id(),this.browserID)}set browserID(e){""===e&&(e=function(){var e,t;const i=()=>Math.floor(1e5*(1+Math.random())).toString(16).substring(1);return null!==(t=null===(e=window.fully)||void 0===e?void 0:e.getDeviceId())&&void 0!==t?t:`${i()}${i()}-${i()}${i()}`}());const t=localStorage[Ie];localStorage[Ie]=e,this.browserIDChanged(t,e)}browserIDChanged(e,t){}};class Oe extends(be(xe(ye(we(me(ve(pe(ke(ge(_e(ue(Me(EventTarget))))))))))))){constructor(){super(),this.connect(),console.info(`%cBROWSER_MOD ${Se} IS INSTALLED\n %cBrowserID: ${this.browserID}`,"color: green; font-weight: bold","")}}window.browser_mod||(window.browser_mod=new Oe);export{Oe as BrowserMod}; diff --git a/custom_components/browser_mod/browser_mod_panel.js b/custom_components/browser_mod/browser_mod_panel.js index 50be7d9..5d7b7d9 100644 --- a/custom_components/browser_mod/browser_mod_panel.js +++ b/custom_components/browser_mod/browser_mod_panel.js @@ -1,144 +1,14 @@ -/*! ***************************************************************************** -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 __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$3=Symbol(),n$4=new Map;class s$3{constructor(t,n){if(this._$cssResult$=!0,n!==e$3)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$3=t=>new s$3("string"==typeof t?t:t+"",e$3),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$3)},i$2=(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$3(e)})(t):t; - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var s$2;const e$2=window.trustedTypes,r$1=e$2?e$2.emptyScript:"",h$1=window.reactiveElementPolyfillSupport,o$2={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$2,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$2(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$2.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$2.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$1=globalThis.trustedTypes,s$1=i$1?i$1.createPolicy("lit-html",{createHTML:t=>t}):void 0,e$1=`lit$${(Math.random()+"").slice(9)}$`,o$1="?"+e$1,n$2=`<${o$1}>`,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$1+y):s+e$1+(-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$1?i$1.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(e){return (n,t)=>void 0!==t?((i,e,n)=>{e.constructor.createProperty(n,i);})(e,n,t):i(e,n)} - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function t(t){return e({...t,state:!0})} - -/** - * @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)); - -// Loads in ha-config-dashboard which is used to copy styling -// Also provides ha-settings-row -const loadConfigDashboard = async () => { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; - await customElements.whenDefined("partial-panel-resolver"); - const ppResolver = document.createElement("partial-panel-resolver"); - const routes = ppResolver.getRoutes([ - { - component_name: "config", - 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)); - await customElements.whenDefined("ha-panel-config"); - const configRouter = document.createElement("ha-panel-config"); - await ((_g = (_f = (_e = (_d = configRouter === null || configRouter === void 0 ? void 0 : configRouter.routerOptions) === null || _d === void 0 ? void 0 : _d.routes) === null || _e === void 0 ? void 0 : _e.dashboard) === null || _f === void 0 ? void 0 : _f.load) === null || _g === void 0 ? void 0 : _g.call(_f)); // Load ha-config-dashboard - await ((_l = (_k = (_j = (_h = configRouter === null || configRouter === void 0 ? void 0 : configRouter.routerOptions) === null || _h === void 0 ? void 0 : _h.routes) === null || _j === void 0 ? void 0 : _j.cloud) === null || _k === void 0 ? void 0 : _k.load) === null || _l === void 0 ? void 0 : _l.call(_k)); // Load ha-settings-row - await customElements.whenDefined("ha-config-dashboard"); -}; -const loadDeveloperToolsTemplate = async () => { - var _a, _b, _c, _d, _e, _f, _g; - await customElements.whenDefined("partial-panel-resolver"); - await customElements.whenDefined("partial-panel-resolver"); - const ppResolver = document.createElement("partial-panel-resolver"); - const routes = ppResolver.getRoutes([ - { - component_name: "developer-tools", - 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 dtRouter = document.createElement("developer-tools-router"); - await ((_g = (_f = (_e = (_d = dtRouter === null || dtRouter === void 0 ? void 0 : dtRouter.routerOptions) === null || _d === void 0 ? void 0 : _d.routes) === null || _e === void 0 ? void 0 : _e.template) === null || _f === void 0 ? void 0 : _f.load) === null || _g === void 0 ? void 0 : _g.call(_f)); - await customElements.whenDefined("developer-tools-template"); -}; - -class BrowserModRegisteredBrowsersCard$1 extends s { - constructor() { - super(...arguments); - this.dirty = false; - } - toggleRegister() { - var _a; - if (!((_a = window.browser_mod) === null || _a === void 0 ? void 0 : _a.connected)) - return; - window.browser_mod.registered = !window.browser_mod.registered; - this.dirty = true; - } - changeBrowserID(ev) { - window.browser_mod.browserID = ev.target.value; - this.dirty = true; - } - toggleCameraEnabled() { - window.browser_mod.cameraEnabled = !window.browser_mod.cameraEnabled; - this.dirty = true; - } - firstUpdated() { - window.browser_mod.addEventListener("browser-mod-config-update", () => this.requestUpdate()); - } - render() { - var _a, _b, _c, _d, _e; - return $ ` +function e(e,t,s,i){var o,r=arguments.length,n=r<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,s):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(e,t,s,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(n=(r<3?o(n):r>3?o(t,s,n):o(t,s))||n);return r>3&&n&&Object.defineProperty(t,s,n),n}const t=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s=Symbol(),i=new Map;class o{constructor(e,t){if(this._$cssResult$=!0,t!==s)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e}get styleSheet(){let e=i.get(this.cssText);return t&&void 0===e&&(i.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const r=(e,...t)=>{const i=1===e.length?e[0]:t.reduce(((t,s,i)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+e[i+1]),e[0]);return new o(i,s)},n=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const s of e.cssRules)t+=s.cssText;return(e=>new o("string"==typeof e?e:e+"",s))(t)})(e):e;var a;const l=window.trustedTypes,d=l?l.emptyScript:"",h=window.reactiveElementPolyfillSupport,c={toAttribute(e,t){switch(t){case Boolean:e=e?d:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let s=e;switch(t){case Boolean:s=null!==e;break;case Number:s=null===e?null:Number(e);break;case Object:case Array:try{s=JSON.parse(e)}catch(e){s=null}}return s}},u=(e,t)=>t!==e&&(t==t||e==e),p={attribute:!0,type:String,converter:c,reflect:!1,hasChanged:u};class w extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o()}static addInitializer(e){var t;null!==(t=this.l)&&void 0!==t||(this.l=[]),this.l.push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach(((t,s)=>{const i=this._$Eh(s,t);void 0!==i&&(this._$Eu.set(i,s),e.push(i))})),e}static createProperty(e,t=p){if(t.state&&(t.attribute=!1),this.finalize(),this.elementProperties.set(e,t),!t.noAccessor&&!this.prototype.hasOwnProperty(e)){const s="symbol"==typeof e?Symbol():"__"+e,i=this.getPropertyDescriptor(e,s,t);void 0!==i&&Object.defineProperty(this.prototype,e,i)}}static getPropertyDescriptor(e,t,s){return{get(){return this[t]},set(i){const o=this[e];this[t]=i,this.requestUpdate(e,o,s)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)||p}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const e=Object.getPrototypeOf(this);if(e.finalize(),this.elementProperties=new Map(e.elementProperties),this._$Eu=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const s of t)this.createProperty(s,e[s])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const s=new Set(e.flat(1/0).reverse());for(const e of s)t.unshift(n(e))}else void 0!==e&&t.push(n(e));return t}static _$Eh(e,t){const s=t.attribute;return!1===s?void 0:"string"==typeof s?s:"string"==typeof e?e.toLowerCase():void 0}o(){var e;this._$Ep=new Promise((e=>this.enableUpdating=e)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(e=this.constructor.l)||void 0===e||e.forEach((e=>e(this)))}addController(e){var t,s;(null!==(t=this._$Eg)&&void 0!==t?t:this._$Eg=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(s=e.hostConnected)||void 0===s||s.call(e))}removeController(e){var t;null===(t=this._$Eg)||void 0===t||t.splice(this._$Eg.indexOf(e)>>>0,1)}_$Em(){this.constructor.elementProperties.forEach(((e,t)=>{this.hasOwnProperty(t)&&(this._$Et.set(t,this[t]),delete this[t])}))}createRenderRoot(){var e;const s=null!==(e=this.shadowRoot)&&void 0!==e?e:this.attachShadow(this.constructor.shadowRootOptions);return((e,s)=>{t?e.adoptedStyleSheets=s.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):s.forEach((t=>{const s=document.createElement("style"),i=window.litNonce;void 0!==i&&s.setAttribute("nonce",i),s.textContent=t.cssText,e.appendChild(s)}))})(s,this.constructor.elementStyles),s}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}))}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}))}attributeChangedCallback(e,t,s){this._$AK(e,s)}_$ES(e,t,s=p){var i,o;const r=this.constructor._$Eh(e,s);if(void 0!==r&&!0===s.reflect){const n=(null!==(o=null===(i=s.converter)||void 0===i?void 0:i.toAttribute)&&void 0!==o?o:c.toAttribute)(t,s.type);this._$Ei=e,null==n?this.removeAttribute(r):this.setAttribute(r,n),this._$Ei=null}}_$AK(e,t){var s,i,o;const r=this.constructor,n=r._$Eu.get(e);if(void 0!==n&&this._$Ei!==n){const e=r.getPropertyOptions(n),a=e.converter,l=null!==(o=null!==(i=null===(s=a)||void 0===s?void 0:s.fromAttribute)&&void 0!==i?i:"function"==typeof a?a:null)&&void 0!==o?o:c.fromAttribute;this._$Ei=n,this[n]=l(t,e.type),this._$Ei=null}}requestUpdate(e,t,s){let i=!0;void 0!==e&&(((s=s||this.constructor.getPropertyOptions(e)).hasChanged||u)(this[e],t)?(this._$AL.has(e)||this._$AL.set(e,t),!0===s.reflect&&this._$Ei!==e&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(e,s))):i=!1),!this.isUpdatePending&&i&&(this._$Ep=this._$E_())}async _$E_(){this.isUpdatePending=!0;try{await this._$Ep}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((e,t)=>this[t]=e)),this._$Et=void 0);let t=!1;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)})),this.update(s)):this._$EU()}catch(e){throw t=!1,this._$EU(),e}t&&this._$AE(s)}willUpdate(e){}_$AE(e){var t;null===(t=this._$Eg)||void 0===t||t.forEach((e=>{var t;return null===(t=e.hostUpdated)||void 0===t?void 0:t.call(e)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EU(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(e){return!0}update(e){void 0!==this._$EC&&(this._$EC.forEach(((e,t)=>this._$ES(t,this[t],e))),this._$EC=void 0),this._$EU()}updated(e){}firstUpdated(e){}}var g;w.finalized=!0,w.elementProperties=new Map,w.elementStyles=[],w.shadowRootOptions={mode:"open"},null==h||h({ReactiveElement:w}),(null!==(a=globalThis.reactiveElementVersions)&&void 0!==a?a:globalThis.reactiveElementVersions=[]).push("1.3.1");const v=globalThis.trustedTypes,b=v?v.createPolicy("lit-html",{createHTML:e=>e}):void 0,m=`lit$${(Math.random()+"").slice(9)}$`,_="?"+m,$=`<${_}>`,f=document,y=(e="")=>f.createComment(e),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,S=Array.isArray,E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,C=/-->/g,x=/>/g,T=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,P=/'/g,U=/"/g,k=/^(?:script|style|textarea|title)$/i,H=(e=>(t,...s)=>({_$litType$:e,strings:t,values:s}))(1),O=Symbol.for("lit-noChange"),M=Symbol.for("lit-nothing"),B=new WeakMap,D=f.createTreeWalker(f,129,null,!1),I=(e,t)=>{const s=e.length-1,i=[];let o,r=2===t?"":"",n=E;for(let t=0;t"===l[0]?(n=null!=o?o:E,d=-1):void 0===l[1]?d=-2:(d=n.lastIndex-l[2].length,a=l[1],n=void 0===l[3]?T:'"'===l[3]?U:P):n===U||n===P?n=T:n===C||n===x?n=E:(n=T,o=void 0);const c=n===T&&e[t+1].startsWith("/>")?" ":"";r+=n===E?s+$:d>=0?(i.push(a),s.slice(0,d)+"$lit$"+s.slice(d)+m+c):s+m+(-2===d?(i.push(void 0),t):c)}const a=r+(e[s]||"")+(2===t?"":"");if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==b?b.createHTML(a):a,i]};class R{constructor({strings:e,_$litType$:t},s){let i;this.parts=[];let o=0,r=0;const n=e.length-1,a=this.parts,[l,d]=I(e,t);if(this.el=R.createElement(l,s),D.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(i=D.nextNode())&&a.length0){i.textContent=v?v.emptyScript:"";for(let s=0;s{var t;return S(e)||"function"==typeof(null===(t=e)||void 0===t?void 0:t[Symbol.iterator])})(e)?this.S(e):this.$(e)}M(e,t=this._$AB){return this._$AA.parentNode.insertBefore(e,t)}k(e){this._$AH!==e&&(this._$AR(),this._$AH=this.M(e))}$(e){this._$AH!==M&&A(this._$AH)?this._$AA.nextSibling.data=e:this.k(f.createTextNode(e)),this._$AH=e}T(e){var t;const{values:s,_$litType$:i}=e,o="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=R.createElement(i.h,this.options)),i);if((null===(t=this._$AH)||void 0===t?void 0:t._$AD)===o)this._$AH.m(s);else{const e=new L(o,this),t=e.p(this.options);e.m(s),this.k(t),this._$AH=e}}_$AC(e){let t=B.get(e.strings);return void 0===t&&B.set(e.strings,t=new R(e)),t}S(e){S(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let s,i=0;for(const o of e)i===t.length?t.push(s=new z(this.M(y()),this.M(y()),this,this.options)):s=t[i],s._$AI(o),i++;i2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=M}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(e,t=this,s,i){const o=this.strings;let r=!1;if(void 0===o)e=N(this,e,t,0),r=!A(e)||e!==this._$AH&&e!==O,r&&(this._$AH=e);else{const i=e;let n,a;for(e=o[0],n=0;n{var i,o;const r=null!==(i=null==s?void 0:s.renderBefore)&&void 0!==i?i:t;let n=r._$litPart$;if(void 0===n){const e=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;r._$litPart$=n=new z(t.insertBefore(y(),e),e,void 0,null!=s?s:{})}return n._$AI(e),n})(t,this.renderRoot,this.renderOptions)}connectedCallback(){var e;super.connectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!0)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!1)}render(){return O}}Z.finalized=!0,Z._$litElement$=!0,null===(J=globalThis.litElementHydrateSupport)||void 0===J||J.call(globalThis,{LitElement:Z});const Q=globalThis.litElementPolyfillSupport;null==Q||Q({LitElement:Z}),(null!==(Y=globalThis.litElementVersions)&&void 0!==Y?Y:globalThis.litElementVersions=[]).push("3.2.0");const X=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?{...t,finisher(s){s.createProperty(t.key,e)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:t.key,initializer(){"function"==typeof t.initializer&&(this[t.key]=t.initializer.call(this))},finisher(s){s.createProperty(t.key,e)}};function ee(e){return(t,s)=>void 0!==s?((e,t,s)=>{t.constructor.createProperty(s,e)})(e,t,s):X(e,t)}var te;null===(te=window.HTMLSlotElement)||void 0===te||te.prototype.assignedElements;class se extends Z{constructor(){super(...arguments),this.dirty=!1}toggleRegister(){var e;(null===(e=window.browser_mod)||void 0===e?void 0:e.connected)&&(window.browser_mod.registered=!window.browser_mod.registered,this.dirty=!0)}changeBrowserID(e){window.browser_mod.browserID=e.target.value,this.dirty=!0}toggleCameraEnabled(){window.browser_mod.cameraEnabled=!window.browser_mod.cameraEnabled,this.dirty=!0}firstUpdated(){window.browser_mod.addEventListener("browser-mod-config-update",(()=>this.requestUpdate()))}render(){var e,t,s,i,o;return H`

This Browser
- ${((_a = window.browser_mod) === null || _a === void 0 ? void 0 : _a.connected) - ? $ ` + ${(null===(e=window.browser_mod)||void 0===e?void 0:e.connected)?H` - ` - : $ ` + `:H`
- ${this.dirty - ? $ ` + ${this.dirty?H` It is strongly recommended to refresh your browser window after changing any of the settings in this box. - ` - : ""} + `:""}
@@ -163,24 +31,23 @@ class BrowserModRegisteredBrowsersCard$1 extends s { >Enable this browser as a Device in Home Assistant - BrowserID + Browser ID A unique identifier for this browser-device combination. - ${((_d = window.browser_mod) === null || _d === void 0 ? void 0 : _d.registered) - ? $ ` + ${(null===(i=window.browser_mod)||void 0===i?void 0:i.registered)?H` ${this._renderSuspensionAlert()} Enable camera @@ -189,22 +56,16 @@ class BrowserModRegisteredBrowsersCard$1 extends s { dependent) ${this._renderInteractionAlert()} ${this._renderFKBSettingsInfo()} - ` - : ""} + `:""}
- `; - } - _renderSuspensionAlert() { - if (!this.hass.suspendWhenHidden) - return $ ``; - return $ ` + `}_renderSuspensionAlert(){return this.hass.suspendWhenHidden?H` Home Assistant will close the websocket connection to the server automatically after 5 minutes of inactivity.

@@ -219,13 +80,9 @@ class BrowserModRegisteredBrowsersCard$1 extends s { >Profile Settings and disabling the option - "${this.hass.localize("ui.panel.profile.suspend.header") || - "Automatically close connection"}". + "${this.hass.localize("ui.panel.profile.suspend.header")||"Automatically close connection"}".
- `; - } - _renderInteractionAlert() { - return $ ` + `:H``}_renderInteractionAlert(){return H` For security reasons many browsers require the user to interact with a webpage before allowing audio playback or video capture. This may affect @@ -238,94 +95,33 @@ class BrowserModRegisteredBrowsersCard$1 extends s { corner of the screen, please tap or click anywhere on the page. This should allow Browser Mod to work again. - `; - } - _renderFKBSettingsInfo() { - var _a, _b; - if (!((_a = window.browser_mod) === null || _a === void 0 ? void 0 : _a.fully) || !this.getFullySettings()) - return $ ``; - return $ ` - ${((_b = window.browser_mod) === null || _b === void 0 ? void 0 : _b.fully) && this.getFullySettings() - ? $ ` + `}_renderFKBSettingsInfo(){var e,t;return(null===(e=window.browser_mod)||void 0===e?void 0:e.fully)&&this.getFullySettings()?H` + ${(null===(t=window.browser_mod)||void 0===t?void 0:t.fully)&&this.getFullySettings()?H` You are using FullyKiosk Browser. It is recommended to enable the following settings:
    ${this.getFullySettings()}
-
` - : ""} - `; - } - getFullySettings() { - if (!window.browser_mod.fully) - return null; - const retval = []; - const wcs = []; - // Web Content Settings - // Autoplay Videos - if (window.fully.getBooleanSetting("autoplayVideos") !== "true") - wcs.push($ `
  • Autoplay Videos
  • `); - // Autoplay Audio - if (window.fully.getBooleanSetting("autoplayAudio") !== "true") - wcs.push($ `
  • Autoplay Audio
  • `); - // Enable Webcam Access (PLUS) - if (window.fully.getBooleanSetting("webcamAccess") !== "true") - wcs.push($ `
  • Enable Webcam Access (PLUS)
  • `); - if (wcs.length !== 0) { - retval.push($ `
  • Web Content Settings
  • +
    `:""} + `:H``}getFullySettings(){if(!window.browser_mod.fully)return null;const e=[],t=[];"true"!==window.fully.getBooleanSetting("autoplayVideos")&&t.push(H`
  • Autoplay Videos
  • `),"true"!==window.fully.getBooleanSetting("autoplayAudio")&&t.push(H`
  • Autoplay Audio
  • `),"true"!==window.fully.getBooleanSetting("webcamAccess")&&t.push(H`
  • Enable Webcam Access (PLUS)
  • `),0!==t.length&&e.push(H`
  • Web Content Settings
    • - ${wcs} -
    `); - } - // Advanced Web Settings - // Enable JavaScript Interface (PLUS) - if (window.fully.getBooleanSetting("websiteIntegration") !== "true") - retval.push($ `
  • Advanced Web Settings
  • + ${t} + `),"true"!==window.fully.getBooleanSetting("websiteIntegration")&&e.push(H`
  • Advanced Web Settings
    • Enable JavaScript Interface (PLUS)
    • -
    `); - // Device Management - // Keep Screen On - if (window.fully.getBooleanSetting("keepScreenOn") !== "true") - retval.push($ `
  • Device Management
  • + `),"true"!==window.fully.getBooleanSetting("keepScreenOn")&&e.push(H`
  • Device Management
    • Keep Screen On
    • -
    `); - // Power Settings - // Prevent from Sleep while Screen Off - if (window.fully.getBooleanSetting("preventSleepWhileScreenOff") !== "true") - retval.push($ `
  • Power Settings
  • + `),"true"!==window.fully.getBooleanSetting("preventSleepWhileScreenOff")&&e.push(H`
  • Power Settings
    • Prevent from Sleep while Screen Off
    • -
    `); - const md = []; - // Motion Detection (PLUS) - // Enable Visual Motion Detection - if (window.fully.getBooleanSetting("motionDetection") !== "true") - md.push($ `
  • Enable Visual Motion Detection
  • `); - // Turn Screen On on Motion - if (window.fully.getBooleanSetting("screenOnOnMotion") !== "true") - md.push($ `
  • Turn Screen On on Motion
  • `); - // Exit Screensaver on Motion - if (window.fully.getBooleanSetting("stopScreensaverOnMotion") !== "true") - md.push($ `
  • Exit Screensaver on Motion
  • `); - if (md.length !== 0) { - retval.push($ `
  • Motion Detection (PLUS)
  • + `);const s=[];return"true"!==window.fully.getBooleanSetting("motionDetection")&&s.push(H`
  • Enable Visual Motion Detection
  • `),"true"!==window.fully.getBooleanSetting("screenOnOnMotion")&&s.push(H`
  • Turn Screen On on Motion
  • `),"true"!==window.fully.getBooleanSetting("stopScreensaverOnMotion")&&s.push(H`
  • Exit Screensaver on Motion
  • `),0!==s.length&&e.push(H`
  • Motion Detection (PLUS)
    • - ${md} -
    `); - } - // Remote Administration (PLUS) - // Enable Remote Administration - if (window.fully.getBooleanSetting("remoteAdmin") !== "true") - retval.push($ `
  • Remote Administration (PLUS)
  • + ${s} + `),"true"!==window.fully.getBooleanSetting("remoteAdmin")&&e.push(H`
  • Remote Administration (PLUS)
    • Enable Remote Administration
    • -
    `); - return retval.length ? retval : null; - } - static get styles() { - return r$2 ` + `),e.length?e:null}static get styles(){return r` .card-header { display: flex; justify-content: space-between; @@ -335,107 +131,36 @@ class BrowserModRegisteredBrowsersCard$1 extends s { display: block; margin-top: 8px; } - `; - } -} -__decorate([ - e() -], BrowserModRegisteredBrowsersCard$1.prototype, "hass", void 0); -__decorate([ - e() -], BrowserModRegisteredBrowsersCard$1.prototype, "dirty", void 0); -customElements.define("browser-mod-browser-settings-card", BrowserModRegisteredBrowsersCard$1); - -class BrowserModRegisteredBrowsersCard extends s { - firstUpdated() { - window.browser_mod.addEventListener("browser-mod-config-update", () => this.requestUpdate()); - } - unregister_browser(ev) { - const browserID = ev.currentTarget.browserID; - const unregisterCallback = () => { - console.log(browserID, window.browser_mod.browserID); - if (browserID === window.browser_mod.browserID) { - console.log("Unregister self"); - window.browser_mod.registered = false; - } - else { - window.browser_mod.connection.sendMessage({ - type: "browser_mod/unregister", - browserID, - }); - } - }; - window.browser_mod.showPopup("Unregister browser", `Are you sure you want to unregister Browser ${browserID}?`, { - right_button: "Yes", - right_button_action: unregisterCallback, - left_button: "No", - }); - } - register_cast() { - window.browser_mod.connection.sendMessage({ - type: "browser_mod/register", - browserID: "CAST", - }); - } - render() { - return $ ` + `}}e([ee()],se.prototype,"hass",void 0),e([ee()],se.prototype,"dirty",void 0),customElements.define("browser-mod-browser-settings-card",se);class ie extends Z{firstUpdated(){window.browser_mod.addEventListener("browser-mod-config-update",(()=>this.requestUpdate()))}unregister_browser(e){const t=e.currentTarget.browserID;window.browser_mod.showPopup("Unregister browser",`Are you sure you want to unregister Browser ${t}?`,{right_button:"Yes",right_button_action:()=>{console.log(t,window.browser_mod.browserID),t===window.browser_mod.browserID?(console.log("Unregister self"),window.browser_mod.registered=!1):window.browser_mod.connection.sendMessage({type:"browser_mod/unregister",browserID:t})},left_button:"No"})}register_cast(){window.browser_mod.connection.sendMessage({type:"browser_mod/register",browserID:"CAST"})}render(){return H`
    - ${Object.keys(window.browser_mod.browsers).map((d) => $ ` - ${d} + ${Object.keys(window.browser_mod.browsers).map((e=>H` + ${e} Last connected: - + - `)} + `))}
    - ${window.browser_mod.browsers["CAST"] === undefined - ? $ ` + ${void 0===window.browser_mod.browsers.CAST?H`
    Register CAST Browser
    - ` - : ""} + `:""}
    - `; - } - static get styles() { - return r$2 ` + `}static get styles(){return r` ha-icon-button > * { display: flex; } - `; - } -} -__decorate([ - e() -], BrowserModRegisteredBrowsersCard.prototype, "hass", void 0); -customElements.define("browser-mod-registered-browsers-card", BrowserModRegisteredBrowsersCard); - -loadDeveloperToolsTemplate(); -class BrowserModFrontendSettingsCard extends s { - constructor() { - super(...arguments); - this._selectedTab = 0; - } - firstUpdated() { - window.browser_mod.addEventListener("browser-mod-config-update", () => this.requestUpdate()); - window.browser_mod.addEventListener("browser-mod-favicon-update", () => this.requestUpdate()); - } - _handleSwitchTab(ev) { - this._selectedTab = parseInt(ev.detail.index, 10); - } - render() { - const level = ["user", "browser", "global"][this._selectedTab]; - return $ ` + `}}e([ee()],ie.prototype,"hass",void 0),customElements.define("browser-mod-registered-browsers-card",ie),(async()=>{var e,t,s,i,o,r,n;await customElements.whenDefined("partial-panel-resolver"),await customElements.whenDefined("partial-panel-resolver");const a=document.createElement("partial-panel-resolver").getRoutes([{component_name:"developer-tools",url_path:"a"}]);await(null===(s=null===(t=null===(e=null==a?void 0:a.routes)||void 0===e?void 0:e.a)||void 0===t?void 0:t.load)||void 0===s?void 0:s.call(t));const l=document.createElement("developer-tools-router");await(null===(n=null===(r=null===(o=null===(i=null==l?void 0:l.routerOptions)||void 0===i?void 0:i.routes)||void 0===o?void 0:o.template)||void 0===r?void 0:r.load)||void 0===n?void 0:n.call(r)),await customElements.whenDefined("developer-tools-template")})();class oe extends Z{constructor(){super(...arguments),this._selectedTab=0}firstUpdated(){window.browser_mod.addEventListener("browser-mod-config-update",(()=>this.requestUpdate())),window.browser_mod.addEventListener("browser-mod-favicon-update",(()=>this.requestUpdate()))}_handleSwitchTab(e){this._selectedTab=parseInt(e.detail.index,10)}render(){const e=["user","browser","global"][this._selectedTab];return H`
    @@ -460,49 +185,31 @@ class BrowserModFrontendSettingsCard extends s { .activeIndex=${this._selectedTab} @MDCTabBar:activated=${this._handleSwitchTab} > - + - ${this._render_settings(level)} + ${this._render_settings(e)}
    - `; - } - _render_settings(level) { - const global = window.browser_mod.global_settings; - const browser = window.browser_mod.browser_settings; - const user = window.browser_mod.user_settings; - const current = { global, browser, user }[level]; - const DESC_BOOLEAN = (val) => ({ true: "Enabled", false: "Disabled", undefined: "Unset" }[String(val)]); - const DESC_SET_UNSET = (val) => (val === undefined ? "Unset" : "Set"); - const OVERRIDDEN = (key) => { - if (level !== "browser" && browser[key] !== undefined) - return $ `
    Overridden by browser setting`; - if (level === "global" && user[key] !== undefined) - return $ `
    Overridden by user setting`; - }; - return $ ` + `}_render_settings(e){const t=window.browser_mod.global_settings,s=window.browser_mod.browser_settings,i=window.browser_mod.user_settings,o={global:t,browser:s,user:i}[e],r=e=>({true:"Enabled",false:"Disabled",undefined:"Unset"}[String(e)]),n=e=>void 0===e?"Unset":"Set",a=t=>"browser"!==e&&void 0!==s[t]?H`
    Overridden by browser setting`:"global"===e&&void 0!==i[t]?H`
    Overridden by user setting`:void 0;return H`
    Favicon template - ${OVERRIDDEN("faviconTemplate")} + ${a("faviconTemplate")} { - const tpl = ev.detail.value || undefined; - window.browser_mod.set_setting("faviconTemplate", tpl, level); - }} + .value=${o.faviconTemplate} + @value-changed=${t=>{const s=t.detail.value||void 0;window.browser_mod.set_setting("faviconTemplate",s,e)}} > window.browser_mod.set_setting("faviconTemplate", undefined, level)} + @click=${()=>window.browser_mod.set_setting("faviconTemplate",void 0,e)} > Clear @@ -512,19 +219,16 @@ class BrowserModFrontendSettingsCard extends s { Title template - ${OVERRIDDEN("titleTemplate")} + ${a("titleTemplate")} { - const tpl = ev.detail.value || undefined; - window.browser_mod.set_setting("titleTemplate", tpl, level); - }} + .value=${o.titleTemplate} + @value-changed=${t=>{const s=t.detail.value||void 0;window.browser_mod.set_setting("titleTemplate",s,e)}} > window.browser_mod.set_setting("titleTemplate", undefined, level)} + @click=${()=>window.browser_mod.set_setting("titleTemplate",void 0,e)} > Clear @@ -535,22 +239,22 @@ class BrowserModFrontendSettingsCard extends s { Hide Sidebar Hide the sidebar and hamburger menu - Currently: ${DESC_BOOLEAN(current.hideSidebar)} - ${OVERRIDDEN("hideSidebar")} + Currently: ${r(o.hideSidebar)} + ${a("hideSidebar")} window.browser_mod.set_setting("hideSidebar", true, level)} + @click=${()=>window.browser_mod.set_setting("hideSidebar",!0,e)} > Enable window.browser_mod.set_setting("hideSidebar", false, level)} + @click=${()=>window.browser_mod.set_setting("hideSidebar",!1,e)} > Disable window.browser_mod.set_setting("hideSidebar", undefined, level)} + @click=${()=>window.browser_mod.set_setting("hideSidebar",void 0,e)} > Clear @@ -561,22 +265,22 @@ class BrowserModFrontendSettingsCard extends s { Hide Header Hide the header on all pages - Currently: ${DESC_BOOLEAN(current.hideHeader)} - ${OVERRIDDEN("hideHeader")} + Currently: ${r(o.hideHeader)} + ${a("hideHeader")} window.browser_mod.set_setting("hideHeader", true, level)} + @click=${()=>window.browser_mod.set_setting("hideHeader",!0,e)} > Enable window.browser_mod.set_setting("hideHeader", false, level)} + @click=${()=>window.browser_mod.set_setting("hideHeader",!1,e)} > Disable window.browser_mod.set_setting("hideHeader", undefined, level)} + @click=${()=>window.browser_mod.set_setting("hideHeader",void 0,e)} > Clear @@ -589,26 +293,20 @@ class BrowserModFrontendSettingsCard extends s { Order and visibility of sidebar buttons - Currently: ${DESC_SET_UNSET(current.sidebarPanelOrder)} - ${OVERRIDDEN("sidebarPanelOrder")} + Currently: ${n(o.sidebarPanelOrder)} + ${a("sidebarPanelOrder")} Clearing this does NOT restore the original default order. { - window.browser_mod.set_setting("sidebarPanelOrder", localStorage.getItem("sidebarPanelOrder"), level); - window.browser_mod.set_setting("sidebarHiddenPanels", localStorage.getItem("sidebarHiddenPanels"), level); - }} + @click=${()=>{window.browser_mod.set_setting("sidebarPanelOrder",localStorage.getItem("sidebarPanelOrder"),e),window.browser_mod.set_setting("sidebarHiddenPanels",localStorage.getItem("sidebarHiddenPanels"),e)}} > Set { - window.browser_mod.set_setting("sidebarPanelOrder", undefined, level); - window.browser_mod.set_setting("sidebarHiddenPanels", undefined, level); - }} + @click=${()=>{window.browser_mod.set_setting("sidebarPanelOrder",void 0,e),window.browser_mod.set_setting("sidebarHiddenPanels",void 0,e)}} > Clear @@ -621,33 +319,26 @@ class BrowserModFrontendSettingsCard extends s { The dashboard that's displayed by default - Currently: ${DESC_SET_UNSET(current.defaultPanel)} - ${OVERRIDDEN("defaultPanel")} + Currently: ${n(o.defaultPanel)} + ${a("defaultPanel")} Clearing this does NOT restore the original default dashboard. { - window.browser_mod.set_setting("defaultPanel", localStorage.getItem("defaultPanel"), level); - }} + @click=${()=>{window.browser_mod.set_setting("defaultPanel",localStorage.getItem("defaultPanel"),e)}} > Set { - window.browser_mod.set_setting("defaultPanel", undefined, level); - }} + @click=${()=>{window.browser_mod.set_setting("defaultPanel",void 0,e)}} > Clear
    - `; - } - static get styles() { - return r$2 ` + `}static get styles(){return r` .box { border: 1px solid var(--divider-color); padding: 8px; @@ -665,24 +356,7 @@ class BrowserModFrontendSettingsCard extends s { display: flex; align-items: center; } - `; - } -} -__decorate([ - e() -], BrowserModFrontendSettingsCard.prototype, "hass", void 0); -__decorate([ - t() -], BrowserModFrontendSettingsCard.prototype, "_selectedTab", void 0); -customElements.define("browser-mod-frontend-settings-card", BrowserModFrontendSettingsCard); - -loadConfigDashboard().then(() => { - class BrowserModPanel extends s { - firstUpdated() { - window.browser_mod.addEventListener("browser-mod-config-update", () => this.requestUpdate()); - } - render() { - return $ ` + `}}e([ee()],oe.prototype,"hass",void 0),e([function(e){return ee({...e,state:!0})}()],oe.prototype,"_selectedTab",void 0),customElements.define("browser-mod-frontend-settings-card",oe),(async()=>{var e,t,s,i,o,r,n,a,l,d,h;await customElements.whenDefined("partial-panel-resolver");const c=document.createElement("partial-panel-resolver").getRoutes([{component_name:"config",url_path:"a"}]);await(null===(s=null===(t=null===(e=null==c?void 0:c.routes)||void 0===e?void 0:e.a)||void 0===t?void 0:t.load)||void 0===s?void 0:s.call(t)),await customElements.whenDefined("ha-panel-config");const u=document.createElement("ha-panel-config");await(null===(n=null===(r=null===(o=null===(i=null==u?void 0:u.routerOptions)||void 0===i?void 0:i.routes)||void 0===o?void 0:o.dashboard)||void 0===r?void 0:r.load)||void 0===n?void 0:n.call(r)),await(null===(h=null===(d=null===(l=null===(a=null==u?void 0:u.routerOptions)||void 0===a?void 0:a.routes)||void 0===l?void 0:l.cloud)||void 0===d?void 0:d.load)||void 0===h?void 0:h.call(d)),await customElements.whenDefined("ha-config-dashboard")})().then((()=>{class t extends Z{firstUpdated(){window.browser_mod.addEventListener("browser-mod-config-update",(()=>this.requestUpdate()))}render(){return H` @@ -708,13 +382,7 @@ loadConfigDashboard().then(() => { > - `; - } - static get styles() { - var _a, _b; - return [ - ...((_b = (_a = customElements.get("ha-config-dashboard")) === null || _a === void 0 ? void 0 : _a.styles) !== null && _b !== void 0 ? _b : []), - r$2 ` + `}static get styles(){var e,t;return[...null!==(t=null===(e=customElements.get("ha-config-dashboard"))||void 0===e?void 0:e.styles)&&void 0!==t?t:[],r` :host { --app-header-background-color: var(--sidebar-background-color); --app-header-text-color: var(--sidebar-text-color); @@ -724,18 +392,4 @@ loadConfigDashboard().then(() => { ha-config-section { padding: 16px 0; } - `, - ]; - } - } - __decorate([ - e() - ], BrowserModPanel.prototype, "hass", void 0); - __decorate([ - e() - ], BrowserModPanel.prototype, "narrow", void 0); - __decorate([ - e() - ], BrowserModPanel.prototype, "connection", void 0); - customElements.define("browser-mod-panel", BrowserModPanel); -}); + `]}}e([ee()],t.prototype,"hass",void 0),e([ee()],t.prototype,"narrow",void 0),e([ee()],t.prototype,"connection",void 0),customElements.define("browser-mod-panel",t)})); diff --git a/custom_components/browser_mod/manifest.json b/custom_components/browser_mod/manifest.json index a63f790..3c87489 100644 --- a/custom_components/browser_mod/manifest.json +++ b/custom_components/browser_mod/manifest.json @@ -5,7 +5,7 @@ "dependencies": ["panel_custom", "websocket_api", "http", "frontend", "lovelace"], "codeowners": [], "requirements": [], - "version": "2.0b0", + "version": "2.0.0b1", "iot_class": "local_push", "config_flow": true } diff --git a/documentation/configuration-panel.md b/documentation/configuration-panel.md new file mode 100644 index 0000000..f7bbbb1 --- /dev/null +++ b/documentation/configuration-panel.md @@ -0,0 +1,102 @@ +# The Browser Mod Configuration Panel + +## This browser + +A basic concept for Browser Mod is the _Browser_. A _Browser_ is identified by a unique `BrowserID` stored in the browsers [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API). + +Browser Mod will initially assigning a random `BrowserID` to each _Browser_ that connects, but you can change this if you want. + +LocalStorage works basically like cookies in that the information is stored locally on your device. Unlike a cookie, though, the information is bound to a URL. Therefore you may get different `BrowserID`s in the same browser if you e.g. access Home Assistant through different URLs inside and outside of your LAN, or through Home Assistant Cloud. + +### Register + +Registering a _Browser_ as a device will create a Home Assistant Device associated with that browser. The device has the following entities: + +- A `media_player` entitiy which will play sound and video through the browser. +- A `light` entity will turn the screen on or off and controll the brightness if you are using [Fully Kiosk Browser](https://www.fully-kiosk.com/) (FKB). If you are not using FKB the function will be simulated by covering the screen with a black (or semitransparent) box. +- A motion `binary_sensor` which reacts to mouse and/or keyboard activity in the Browser. In FKB this can also react to motion in front of the devices camera. +- A number of `sensor` and `binary_sensor` entities providing different bits of information about the Browser which you may or may not find useful. + +Registering a browser also enables it to act as a target for Browser Mod _services_. + +### Browser ID + +This box lets you set the `BrowserID` for the current _Browser_. +Note that it is possible to assign the same `BrowserID` to several browsers, but unpredictable things _may_ happen if several of them are open at the same time. +There may be benefits to using the same `BrowserID` in some cases, so you'll have to experiment with what works for you. + +Browser Mod is trying hard to keep the Browser ID constant + +### Enable camera + +If your device has a camera, this will allow it to be forwarded as a `camera` entity to Home Assistant. + +## Registered Browsers + +This section shows all currently registered _Browsers_ and allows you to unregister them. This is useful e.g. if a `BrowserID` has changed or if you do not have access to a device anymore. + +### Register CAST browser + +If you are using [Home Assistant Cast](https://www.home-assistant.io/integrations/cast/#home-assistant-cast) to display a lovelace view on a Chromecast device it will get a BrowserID of "`CAST`". Since you can't access the Browser Mod config panel from the device, clicking this button will register the `CAST` browser. Most Browser Mod services will work under Home Assistant Cast. + +## Frontend Settings + +This section is for settings that change the default behavior of the Home Assistant frontend. + +Each setting has three levels, _Global_, _Browser_ and _User_. + +- Changes made on the _Global_ tab will be applied for everyone on every browser. +- Changes made on the _Browser_ tab will be applied for this _Browser_. The settings here override any _Global_ settings. +- Changes made on the _User_ tab will be applied for the user you're currently logged in as - on any device. The settings here override any _Global_ or _Browser_ settings. + +Note that if a setting is set at a lower level but _cleared_ on a higher, it is not _undone_. It's just not overridden. + +Also note that _User_ level settings can only be made when logged in as the user in question, and that the Browser Mod configuration panel is only available to administrators. If you need to change a setting for a non-admin user, you will need to temporarily make them admin for the setup, and then un-admin them. + +### Favicon template + +This allows you to set and dynamically update the favicon of the browser tab/window. I.e. the little icon next to the page title. Favicons can be .png or .ico files and should be placed in your `/www` directory. The box here should then contain a jinja [template](https://www.home-assistant.io/docs/configuration/templating/) which resolves to the path of the icon with `/www/` replaced by `/local/` (see [Hosting files](https://www.home-assistant.io/integrations/http/#hosting-files)). + +> Ex: +> +> ```jinja +> {% if is_state("light.bed_light", "on") %} +> /local/icons/green.png +> {% else %} +> /local/icons/red.png +> {% endif %} +> ``` + +Note that this _only_ applies to the current favicon of the page, not any manifest icons such as the loading icon or the icon you get if you save the page to your smartphones homescreen. For those, please see the [hass-favicon](https://github.com/thomasloven/hass-favicon) integration. + +### Title template + +This allows you to set and dynamically update the title text of the browser tab/window by means on a Jinja [template](https://www.home-assistant.io/docs/configuration/templating/). + +> Ex: +> +> ```jinja +> {{ states.persistent_notification | list | count}} - Home Assistant +> ``` + +### Hide Sidebar + +This will hide the sidebar wit the navigation links. You can still access all the pages via normal links. + +> Tip: add `/browser-mod` to the end of your home assistant URL when you need to turn this off again... + +### Hide header + +This will hide the header bar. Completely. It does not care if there are useful navigation links there or not. It's gone. + +> Tip: See the big yellow warning box at the top of this card? For some reason, it seems to be really easy to forget you turned this on. Please do not bother the Home Assistant team about the header bar missing if you have hidden it yourself. Really, I've forgotten multiple times myself. + +### Sidebar order + +Did you know that you can change the order and hide items from the sidebar? To do so, either go into your profile settings at the bottom left and click "Change the order and hide items from the sidebar", or click and hold on the "Home Assistant" text at the top of the sidebar. + +Normally, the order and hidden items only applies to the current device, but this will make it persistent according to the levels described above. + +### Default dashboard + +Like the Sidebar order, the default dashboard (the page shown when you simply access `https:///` with nothing after the `/`) can be set in your profile settings but only applies to the current device. This fixes that. \ No newline at end of file diff --git a/documentation/popups.md b/documentation/popups.md new file mode 100644 index 0000000..ea310fd --- /dev/null +++ b/documentation/popups.md @@ -0,0 +1,65 @@ + +## Anatomy of a popup + +```yaml +service: browser_mod.popup +data: + title: The title + content: The content + right_button: Right button + left_button: Left button +``` + +![Popup](https://user-images.githubusercontent.com/1299821/180668969-c647f301-3f3d-4f3b-a1f8-d95af8b48873.png) + +## Displaying a dashboard card in a popup + +```yaml +service: browser_mod.popup +data: + title: The title + right_button: Right button + left_button: Left button + content: + type: entities + entities: + - light.bed_light + - light.ceiling_lights + - light.kitchen_lights +``` + +![Popup with card](https://user-images.githubusercontent.com/1299821/180669077-bbc86831-3a8a-4e54-b098-d900d62d3508.png) + +## Actionable popups + +```yaml +service: browser_mod.popup +data: + content: Do you want to turn the light on? + right_button: "Yes" + left_button: "No" + right_button_action: + service: light.turn_on + data: + entity_id: light.bed_light + left_button_action: + service: browser_mod.popup + data: + title: Really? + content: Are you sure? + right_button: "Yes" + left_button: "No" + right_button_action: + service: browser_mod.popup + data: + content: Fine, live in darkness. + dismissable: false + title: Ok + timeout: 3000 + left_button_action: + service: light.turn_on + data: + entity_id: light.bed_light +``` + +![Advanced popup](https://user-images.githubusercontent.com/1299821/180670190-18cf8eee-cf18-47b9-84d1-e62ef327c615.gif) \ No newline at end of file diff --git a/documentation/services.md b/documentation/services.md new file mode 100644 index 0000000..2fbc4bd --- /dev/null +++ b/documentation/services.md @@ -0,0 +1,254 @@ + +## Reading guide +Service parameters are described using the following conventions: + +- `` in brackets describe the type of a parameter, e.g. + + - `` is a piece of text + - `` is a number + - `` means the value must be either `true` or `false` with `true` being the default + - `` means a full service call specification. Note that this can be any service, not just Browser Mod services + - `` is a list of BrowserIDs + +- Square brackets `[ ]` indicate that a parameter is optional and can be omitted. + +### `` + +A service call is a combination of a service and it's data: + +Ex, a `` for `browser_mod.more_info` with `light.bed_light` as entity: + +```yaml +service: browser_mod.more_info +data: + entity: light.bed_light +``` + +If `data` contains `browser_id: THIS` then `THIS` will be replaced with the current browser ID. + + +## A note about targets + +Browser Mod services can be called in two different ways which behave slightly differently. + +The first way is as a *server* call. This is when the service is called from a script or automation, from the dev-services panel or from a dashboard `call-service` action. + +The second way is as a *browser* call. This is when the service is called from a dashboard `fire-dom-event` action, as a part of a `browser_mod.sequence` call or as a `browser_mod.popup` `_action`. + +The notable difference between the two is when no target (`browser_id`) is specified, in which case: +- A *server* call will perform the service on ALL REGISTERED BROWSERS. +- A *browser* call will perform the service on THE CURRENT BROWSER, i.e. the browser it was called from. + +--- + +Finally, in *browser* calls, a parameter `browser_id` with the value `THIS` will be replaced with the current Browsers browser ID. + +Ex: + +```yaml +tap_action: + action: fire-dom-event + browser_mod: + service: script.print_clicking_browser + data: + browser_id: THIS +``` + +with the script: + +```yaml +script: + print_clicking_browser: + sequence: + - service: system_log.write + data: + message: "Button was clicked in {{browser_id}}" +``` + +Will print `"Button was clicked in 79be65e8-f06c78f" to the Home Assistant log. + +# Browser Mod Services + +## `browser_mod.navigate` + +Point the browser to the given Home Assistant path. + +```yaml +service: browser_mod.navigate +data: + path: + [browser_id: + - + - ] +``` + +| | | +|---|---| +|`path` | A Home Assistant path.
    E.x. `/lovelace/`, `/my-dashboard/bedroom`, `/browser_mod/`, `/config/devices/device/20911cc5a63b1caafa2089618545eb8a`...| + +## `browser_mod.refresh` + +Reload the current page. + +```yaml +service: browser_mod.refresh +data: + [browser_id: ] +``` + +## `browser_mod.more_info` + +Show a more-info dialog. + +```yaml +service: browser_mod.more_info +data: + entity: + [large: ] + [ignore_popup_card: ] + [browser_id: ] +``` + +| | | +|---|---| +|`entity`| The entity whose more-info dialog to display. | +|`large`| If true, the dialog will be displayed wider, as if you had clicked the title of the dialog. | +| `ignore_popup_card` | If true the more-info dialog will be shown even if there's currently a popup-card which would override it. | + +## `browser_mod.popup` + +Display a popup dialog + +```yaml +service: browser_mod.popup +data: + [title: ] + content: + [size: ] + [right_button: ] + [right_button_action: ] + [left_button: ] + [left_button_action: ] + [dismissable: ] + [dismiss_action: ] + [autoclose: ] + [timeout: ] + [timeout_action: ] + [style: ] + [browser_id: ] +``` + +| | | +|---|---| +|`title` | The title of the popup window.| +|`content`| HTML or a dashboard card configuration to display.| +| `size` | `wide` will make the popup window wider. `fullscreen` will make it cover the entire screen. | +| `right_button`| The text of the right action button.| +| `right_button_action`| Action to perform when the right action button is pressed. | +| `left_button`| The text of the left action button.| +| `left_button_action`| Action to perform when the left action button is pressed. | +| `dismissable`| If false the dialog cannot be closed by the user without clicking an action button. | +| `dismiss_action` | An action to perform if the dialog is closed by the user without clicking an action button. | +| `autoclose` | If true the dialog will close automatically when the mouse, screen or keyboard is touched. This will perform the `dismiss_action`. | +| `timeout` | If set will close the dialog after `timeout` milliseconds. | +| `timeout_action` | An action to perform if the dialog is closed by timeout. | +| `style` | CSS styles to apply to the dialog. | + +The default value for `style` is as follows: + +```yaml +style: + --popup-min-width: 400px; + --popup-max-width: 600px; + --popup-border-width: var(--ha-card-border-width, 2px); + --popup-border-color: var(--ha-card-border-color, var(--divider-color, #eee)); + --popup-border-radius: 8px; + --popup-background-color: var(--ha-card-background, var(--card-background-color, white)); + --popup-header-background-color: var(--popup-background-color, var(--sidebar-background-color)); +``` + +Note that any Browser Mod services performed as `_action`s here will be performed only on the same Browser as initiated the action unless `browser_id` is given. + +For usage examples, see [popups.md](popups.md). + + +## `browser_mod.close_popup` + +Close any currently open popup or more-info dialog. + +```yaml +service: browser_mod.close_popup +data: + [browser_id: ] +``` + +## `browser_mod.sequence` + +Perform several services sequentially. + +```yaml +service: browser_mod.sequence +data: + sequence: + - + - + - ... + [browser_id: ] +``` + +| | | +|---|---| +|`sequence` | List of actions to perform. | + +Note that if `browser_id` is omitted in the service calls listed in `sequence` the services will be performed on the Browser that's targeted as a whole rather than all browsers. + +## `browser_mod.delay` + +Wait for a specified time. + +```yaml +service: browser_mod.delay +data: + time: + [browser_id: ] +``` + +| | | +|---|---| +|`time` | Number of milliseconds to wait.| + +This is probably most useful as part of a `browser_mod.sequence` call. + +## `browsermod.console` + +Print a text to the browsers javascript console. + +```yaml +service: browser_mod.console +data: + message: + [browser_id: ] +``` + +| | | +|---|---| +|`message` | Text to print. | + +## `browsermod.javascript` + +Run arbitrary javascript code in the browser. + +```yaml +service: browser_mod.console +data: + code: + [browser_id: ] +``` + +| | | +|---|---| +|`code` | Code to run. | + +Only use this one if you know what you're doing. + +The `hass` frontend object is available as global variable `hass`. \ No newline at end of file diff --git a/js/config_panel/browser-settings-card.ts b/js/config_panel/browser-settings-card.ts index 032644b..a1c2805 100644 --- a/js/config_panel/browser-settings-card.ts +++ b/js/config_panel/browser-settings-card.ts @@ -69,7 +69,7 @@ class BrowserModRegisteredBrowsersCard extends LitElement { - BrowserID + Browser ID A unique identifier for this browser-device combination. diff --git a/js/plugin/browser-player.ts b/js/plugin/browser-player.ts index c8ec5f0..170484f 100644 --- a/js/plugin/browser-player.ts +++ b/js/plugin/browser-player.ts @@ -8,8 +8,6 @@ import "./types"; class BrowserPlayer extends LitElement { @property() hass; - player; - static getConfigElement() { return document.createElement("browser-player-editor"); } @@ -21,7 +19,6 @@ class BrowserPlayer extends LitElement { while (!window.browser_mod) { await new Promise((resolve) => setTimeout(resolve, 1000)); } - this.player = window.browser_mod.player; for (const event of [ "play", @@ -31,14 +28,19 @@ class BrowserPlayer extends LitElement { "canplay", "loadeddata", ]) - this.player.addEventListener(event, () => this.requestUpdate()); + window.browser_mod._audio_player.addEventListener(event, () => + this.requestUpdate() + ); + window.browser_mod._video_player.addEventListener(event, () => + this.requestUpdate() + ); } handleMute(ev) { - this.player.muted = !this.player.muted; + window.browser_mod.player.muted = !window.browser_mod.player.muted; } handleVolumeChange(ev) { const volume_level = parseFloat(ev.target.value); - this.player.volume = volume_level; + window.browser_mod.player.volume = volume_level; } handleMoreInfo(ev) { this.dispatchEvent( @@ -53,9 +55,16 @@ class BrowserPlayer extends LitElement { ); } handlePlayPause(ev) { - if (!this.player.src || this.player.paused || this.player.ended) - this.player.play(); - else this.player.pause(); + if ( + !window.browser_mod.player.src || + window.browser_mod.player.paused || + window.browser_mod.player.ended + ) { + window.browser_mod.player.play(); + window.browser_mod._show_video_player(); + } else { + window.browser_mod.player.pause(); + } } render() { @@ -68,15 +77,17 @@ class BrowserPlayer extends LitElement {
    @@ -85,9 +96,9 @@ class BrowserPlayer extends LitElement { : html` diff --git a/js/plugin/main.ts b/js/plugin/main.ts index 2c08bd7..7f371d6 100644 --- a/js/plugin/main.ts +++ b/js/plugin/main.ts @@ -67,7 +67,7 @@ import { BrowserIDMixin } from "./browserID"; - Tweaks - Quickbar tweaks (ctrl+enter)? x Card-mod preload - x Video player? + x Video player x Media_seek - Screensavers x IMPORTANT: FIX DEFAULT HIDING OF ENTITIES diff --git a/js/plugin/popup-card-editor.ts b/js/plugin/popup-card-editor.ts index b1de087..aa499eb 100644 --- a/js/plugin/popup-card-editor.ts +++ b/js/plugin/popup-card-editor.ts @@ -256,14 +256,15 @@ class PopupCardEditor extends LitElement { } await window.browser_mod.connectionPromise; - if (!customElements.get("popup-card-editor")) + if (!customElements.get("popup-card-editor")) { customElements.define("popup-card-editor", PopupCardEditor); - (window as any).customCards = (window as any).customCards || []; - (window as any).customCards.push({ - type: "popup-card", - name: "Popup card", - preview: false, - description: - "Replace the more-info dialog for a given entity in the view that includes this card. (Browser Mod)", - }); + (window as any).customCards = (window as any).customCards || []; + (window as any).customCards.push({ + type: "popup-card", + name: "Popup card", + preview: false, + description: + "Replace the more-info dialog for a given entity in the view that includes this card. (Browser Mod)", + }); + } })(); diff --git a/js/plugin/popup-card.ts b/js/plugin/popup-card.ts index b847870..1ac2ac1 100644 --- a/js/plugin/popup-card.ts +++ b/js/plugin/popup-card.ts @@ -43,6 +43,9 @@ class PopupCard extends LitElement { if (this.parentElement.localName === "hui-card-preview") { this.editMode = true; + this.removeAttribute("hidden"); + } else { + this.setAttribute("hidden", ""); } } diff --git a/js/plugin/popups.ts b/js/plugin/popups.ts index b1fdfce..f450573 100644 --- a/js/plugin/popups.ts +++ b/js/plugin/popups.ts @@ -241,7 +241,7 @@ class BrowserModPopup extends LitElement { color: var(--primary-text-color); background-color: var( --popup-header-background-color, - var(--popup-background-color, --sidebar-background-color) + var(--popup-background-color, var(--sidebar-background-color)) ); } ha-icon-button > * { diff --git a/package.json b/package.json index 8808ad6..ddbd213 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "browser_mod", "private": true, - "version": "2.0.0b0", + "version": "2.0.0b1", "description": "", "scripts": { "build": "rollup -c", diff --git a/test/configuration.yaml b/test/configuration.yaml index 54407dc..97c540e 100644 --- a/test/configuration.yaml +++ b/test/configuration.yaml @@ -5,8 +5,7 @@ demo: http: use_x_forwarded_for: true trusted_proxies: - - 172.17.0.4 - # Update this as needed for testing with Cast + - 172.17.0.0/24 logger: default: warning