Compare commits
	
		
			3 Commits
		
	
	
		
			8703964438
			...
			fffb017287
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fffb017287 | |||
| 39f727206f | |||
| ae9ffb65c1 | 
@ -3,6 +3,7 @@ import logging
 | 
				
			|||||||
from homeassistant.components.websocket_api import event_message
 | 
					from homeassistant.components.websocket_api import event_message
 | 
				
			||||||
from homeassistant.helpers import device_registry, entity_registry
 | 
					from homeassistant.helpers import device_registry, entity_registry
 | 
				
			||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
 | 
					from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
 | 
				
			||||||
 | 
					from homeassistant.core import callback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .const import DATA_BROWSERS, DOMAIN, DATA_ADDERS
 | 
					from .const import DATA_BROWSERS, DOMAIN, DATA_ADDERS
 | 
				
			||||||
from .sensor import BrowserSensor
 | 
					from .sensor import BrowserSensor
 | 
				
			||||||
@ -130,11 +131,14 @@ class BrowserModBrowser:
 | 
				
			|||||||
            er.async_remove(self.entities["camera"].entity_id)
 | 
					            er.async_remove(self.entities["camera"].entity_id)
 | 
				
			||||||
            del self.entities["camera"]
 | 
					            del self.entities["camera"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        hass.create_task(
 | 
				
			||||||
            self.send(
 | 
					            self.send(
 | 
				
			||||||
                None, browserEntities={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):
 | 
					    @callback
 | 
				
			||||||
 | 
					    async def send(self, command, **kwargs):
 | 
				
			||||||
        """Send a command to this browser."""
 | 
					        """Send a command to this browser."""
 | 
				
			||||||
        if self.connection is None:
 | 
					        if self.connection is None:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -9,6 +9,8 @@ from homeassistant.components.websocket_api import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from homeassistant.components import websocket_api
 | 
					from homeassistant.components import websocket_api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from homeassistant.core import callback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .const import (
 | 
					from .const import (
 | 
				
			||||||
    BROWSER_ID,
 | 
					    BROWSER_ID,
 | 
				
			||||||
    DATA_STORE,
 | 
					    DATA_STORE,
 | 
				
			||||||
@ -40,6 +42,7 @@ async def async_setup_connection(hass):
 | 
				
			|||||||
        browserID = msg[BROWSER_ID]
 | 
					        browserID = msg[BROWSER_ID]
 | 
				
			||||||
        store = hass.data[DOMAIN][DATA_STORE]
 | 
					        store = hass.data[DOMAIN][DATA_STORE]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @callback
 | 
				
			||||||
        def send_update(data):
 | 
					        def send_update(data):
 | 
				
			||||||
            connection.send_message(event_message(msg["id"], {"result": data}))
 | 
					            connection.send_message(event_message(msg["id"], {"result": data}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@
 | 
				
			|||||||
  "dependencies": ["panel_custom", "websocket_api", "http", "frontend", "lovelace"],
 | 
					  "dependencies": ["panel_custom", "websocket_api", "http", "frontend", "lovelace"],
 | 
				
			||||||
  "codeowners": [],
 | 
					  "codeowners": [],
 | 
				
			||||||
  "requirements": [],
 | 
					  "requirements": [],
 | 
				
			||||||
  "version": "2.0.0b4",
 | 
					  "version": "2.0.0b5",
 | 
				
			||||||
  "iot_class": "local_push",
 | 
					  "iot_class": "local_push",
 | 
				
			||||||
  "config_flow": true
 | 
					  "config_flow": true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,7 @@ async def async_setup_services(hass):
 | 
				
			|||||||
            if target not in browsers:
 | 
					            if target not in browsers:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            browser = browsers[target]
 | 
					            browser = browsers[target]
 | 
				
			||||||
            browser.send(service, **data)
 | 
					            hass.create_task(browser.send(service, **data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle_service(call):
 | 
					    def handle_service(call):
 | 
				
			||||||
        service = call.service
 | 
					        service = call.service
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,7 @@ class SettingsStoreData:
 | 
				
			|||||||
    defaultPanel = attr.ib(type=str, default=None)
 | 
					    defaultPanel = attr.ib(type=str, default=None)
 | 
				
			||||||
    sidebarPanelOrder = attr.ib(type=list, default=None)
 | 
					    sidebarPanelOrder = attr.ib(type=list, default=None)
 | 
				
			||||||
    sidebarHiddenPanels = attr.ib(type=list, default=None)
 | 
					    sidebarHiddenPanels = attr.ib(type=list, default=None)
 | 
				
			||||||
 | 
					    sidebarTitle = attr.ib(type=str, default=None)
 | 
				
			||||||
    faviconTemplate = attr.ib(type=str, default=None)
 | 
					    faviconTemplate = attr.ib(type=str, default=None)
 | 
				
			||||||
    titleTemplate = attr.ib(type=str, default=None)
 | 
					    titleTemplate = attr.ib(type=str, default=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										294
									
								
								js/config_panel/browser-mod-settings-table.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								js/config_panel/browser-mod-settings-table.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,294 @@
 | 
				
			|||||||
 | 
					import { LitElement, html, css } from "lit";
 | 
				
			||||||
 | 
					import { property } from "lit/decorators.js";
 | 
				
			||||||
 | 
					import { selectTree } from "../helpers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BrowserModSettingsTable extends LitElement {
 | 
				
			||||||
 | 
					  @property() settingKey;
 | 
				
			||||||
 | 
					  @property() settingSelector = {
 | 
				
			||||||
 | 
					    template: {},
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @property() hass;
 | 
				
			||||||
 | 
					  @property() default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @property() tableData = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  _users = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  firstUpdated() {
 | 
				
			||||||
 | 
					    window.browser_mod.addEventListener("browser-mod-config-update", () =>
 | 
				
			||||||
 | 
					      this.updateTable()
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  updated(changedProperties) {
 | 
				
			||||||
 | 
					    if (changedProperties.has("settingKey")) this.updateTable();
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      changedProperties.has("hass") &&
 | 
				
			||||||
 | 
					      changedProperties.get("hass") === undefined
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					      this.updateTable();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async fetchUsers(): Promise<any[]> {
 | 
				
			||||||
 | 
					    if (this._users === undefined)
 | 
				
			||||||
 | 
					      this._users = await this.hass.callWS({ type: "config/auth/list" });
 | 
				
			||||||
 | 
					    return this._users;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clearSetting(type, target) {
 | 
				
			||||||
 | 
					    const clearSettingCallback = async () => {
 | 
				
			||||||
 | 
					      if (this.settingKey === "sidebarPanelOrder") {
 | 
				
			||||||
 | 
					        const sideBar: any = await selectTree(
 | 
				
			||||||
 | 
					          document,
 | 
				
			||||||
 | 
					          "home-assistant $ home-assistant-main $ app-drawer-layout app-drawer ha-sidebar"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        window.browser_mod.setSetting(type, target, {
 | 
				
			||||||
 | 
					          sidebarHiddenPanels: "[]",
 | 
				
			||||||
 | 
					          sidebarPanelOrder: "[]",
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        window.browser_mod.setSetting(type, target, {
 | 
				
			||||||
 | 
					          sidebarHiddenPanels: undefined,
 | 
				
			||||||
 | 
					          sidebarPanelOrder: undefined,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (this.default)
 | 
				
			||||||
 | 
					        window.browser_mod.setSetting(type, target, {
 | 
				
			||||||
 | 
					          [this.settingKey]: this.default,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      window.browser_mod.setSetting(type, target, {
 | 
				
			||||||
 | 
					        [this.settingKey]: undefined,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    window.browser_mod?.showPopup(
 | 
				
			||||||
 | 
					      "Are you sure",
 | 
				
			||||||
 | 
					      "Do you wish to clear this setting?",
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        right_button: "Yes",
 | 
				
			||||||
 | 
					        right_button_action: clearSettingCallback,
 | 
				
			||||||
 | 
					        left_button: "No",
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  changeSetting(type, target) {
 | 
				
			||||||
 | 
					    const changeSettingCallback = async (newValue) => {
 | 
				
			||||||
 | 
					      if (this.settingKey === "sidebarPanelOrder") {
 | 
				
			||||||
 | 
					        const sideBar: any = await selectTree(
 | 
				
			||||||
 | 
					          document,
 | 
				
			||||||
 | 
					          "home-assistant $ home-assistant-main $ app-drawer-layout app-drawer ha-sidebar"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        window.browser_mod.setSetting(type, target, {
 | 
				
			||||||
 | 
					          sidebarHiddenPanels: JSON.stringify(sideBar._hiddenPanels),
 | 
				
			||||||
 | 
					          sidebarPanelOrder: JSON.stringify(sideBar._panelOrder),
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        console.log(sideBar._hiddenPanels, sideBar._panelOrder);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let value = newValue.value;
 | 
				
			||||||
 | 
					      window.browser_mod.setSetting(type, target, { [this.settingKey]: value });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const settings = window.browser_mod?.getSetting?.(this.settingKey);
 | 
				
			||||||
 | 
					    const def =
 | 
				
			||||||
 | 
					      (type === "global" ? settings.global : settings[type][target]) ??
 | 
				
			||||||
 | 
					      this.default;
 | 
				
			||||||
 | 
					    window.browser_mod?.showPopup(
 | 
				
			||||||
 | 
					      "Change value",
 | 
				
			||||||
 | 
					      (this.settingSelector as any).plaintext ?? [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          name: "value",
 | 
				
			||||||
 | 
					          label: (this.settingSelector as any).label ?? "",
 | 
				
			||||||
 | 
					          default: def,
 | 
				
			||||||
 | 
					          selector: this.settingSelector,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        right_button: "OK",
 | 
				
			||||||
 | 
					        right_button_action: changeSettingCallback,
 | 
				
			||||||
 | 
					        left_button: "Cancel",
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addBrowserSetting() {
 | 
				
			||||||
 | 
					    const settings = window.browser_mod?.getSetting?.(this.settingKey);
 | 
				
			||||||
 | 
					    const allBrowsers = window.browser_mod._data.browsers;
 | 
				
			||||||
 | 
					    const browsers = [];
 | 
				
			||||||
 | 
					    for (const target of Object.keys(allBrowsers)) {
 | 
				
			||||||
 | 
					      if (settings.browser[target] == null) browsers.push(target);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (browsers.length === 0) {
 | 
				
			||||||
 | 
					      window.browser_mod.showPopup(
 | 
				
			||||||
 | 
					        "No browsers to configure",
 | 
				
			||||||
 | 
					        "All registered browsers have already been configured.",
 | 
				
			||||||
 | 
					        { right_button: "OK" }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    window.browser_mod.showPopup(
 | 
				
			||||||
 | 
					      "Select browser to configure",
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          name: "browser",
 | 
				
			||||||
 | 
					          label: "",
 | 
				
			||||||
 | 
					          selector: {
 | 
				
			||||||
 | 
					            select: { options: browsers },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        right_button: "Next",
 | 
				
			||||||
 | 
					        right_button_action: (value) =>
 | 
				
			||||||
 | 
					          this.changeSetting("browser", value.browser),
 | 
				
			||||||
 | 
					        left_button: "Cancel",
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async addUserSetting() {
 | 
				
			||||||
 | 
					    const settings = window.browser_mod?.getSetting?.(this.settingKey);
 | 
				
			||||||
 | 
					    const allUsers = await this.fetchUsers();
 | 
				
			||||||
 | 
					    const users = [];
 | 
				
			||||||
 | 
					    for (const target of allUsers) {
 | 
				
			||||||
 | 
					      if (target.username && settings.user[target.id] == null)
 | 
				
			||||||
 | 
					        users.push({ label: target.name, value: target.id });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (users.length === 0) {
 | 
				
			||||||
 | 
					      window.browser_mod.showPopup(
 | 
				
			||||||
 | 
					        "No users to configure",
 | 
				
			||||||
 | 
					        "All users have already been configured.",
 | 
				
			||||||
 | 
					        { right_button: "OK" }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    window.browser_mod.showPopup(
 | 
				
			||||||
 | 
					      "Select user to configure",
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          name: "user",
 | 
				
			||||||
 | 
					          label: "",
 | 
				
			||||||
 | 
					          selector: {
 | 
				
			||||||
 | 
					            select: { options: users },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        right_button: "Next",
 | 
				
			||||||
 | 
					        right_button_action: (value) => this.changeSetting("user", value.user),
 | 
				
			||||||
 | 
					        left_button: "Cancel",
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async updateTable() {
 | 
				
			||||||
 | 
					    if (this.hass === undefined) return;
 | 
				
			||||||
 | 
					    const users = await this.fetchUsers();
 | 
				
			||||||
 | 
					    const settings = window.browser_mod?.getSetting?.(this.settingKey);
 | 
				
			||||||
 | 
					    const data = [];
 | 
				
			||||||
 | 
					    for (const [k, v] of Object.entries(settings.user)) {
 | 
				
			||||||
 | 
					      const user = users.find((usr) => usr.id === k);
 | 
				
			||||||
 | 
					      data.push({
 | 
				
			||||||
 | 
					        name: `User: ${user.name}`,
 | 
				
			||||||
 | 
					        value: String(v),
 | 
				
			||||||
 | 
					        controls: html`
 | 
				
			||||||
 | 
					          <ha-icon-button @click=${() => this.changeSetting("user", k)}>
 | 
				
			||||||
 | 
					            <ha-icon .icon=${"mdi:pencil"} style="display:flex;"></ha-icon>
 | 
				
			||||||
 | 
					          </ha-icon-button>
 | 
				
			||||||
 | 
					          <ha-icon-button @click=${() => this.clearSetting("user", k)}>
 | 
				
			||||||
 | 
					            <ha-icon .icon=${"mdi:delete"} style="display:flex;"></ha-icon>
 | 
				
			||||||
 | 
					          </ha-icon-button>
 | 
				
			||||||
 | 
					        `,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data.push({
 | 
				
			||||||
 | 
					      name: "",
 | 
				
			||||||
 | 
					      value: html`
 | 
				
			||||||
 | 
					        <mwc-button @click=${() => this.addUserSetting()}>
 | 
				
			||||||
 | 
					          <ha-icon .icon=${"mdi:plus"}></ha-icon>
 | 
				
			||||||
 | 
					          Add user setting
 | 
				
			||||||
 | 
					        </mwc-button>
 | 
				
			||||||
 | 
					      `,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const [k, v] of Object.entries(settings.browser)) {
 | 
				
			||||||
 | 
					      data.push({
 | 
				
			||||||
 | 
					        name: `Browser: ${k}`,
 | 
				
			||||||
 | 
					        value: String(v),
 | 
				
			||||||
 | 
					        controls: html`
 | 
				
			||||||
 | 
					          <ha-icon-button @click=${() => this.changeSetting("browser", k)}>
 | 
				
			||||||
 | 
					            <ha-icon .icon=${"mdi:pencil"} style="display:flex;"></ha-icon>
 | 
				
			||||||
 | 
					          </ha-icon-button>
 | 
				
			||||||
 | 
					          <ha-icon-button @click=${() => this.clearSetting("browser", k)}>
 | 
				
			||||||
 | 
					            <ha-icon .icon=${"mdi:delete"} style="display:flex;"></ha-icon>
 | 
				
			||||||
 | 
					          </ha-icon-button>
 | 
				
			||||||
 | 
					        `,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data.push({
 | 
				
			||||||
 | 
					      name: "",
 | 
				
			||||||
 | 
					      value: html`
 | 
				
			||||||
 | 
					        <mwc-button @click=${() => this.addBrowserSetting()}>
 | 
				
			||||||
 | 
					          <ha-icon .icon=${"mdi:plus"}></ha-icon>
 | 
				
			||||||
 | 
					          Add browser setting
 | 
				
			||||||
 | 
					        </mwc-button>
 | 
				
			||||||
 | 
					      `,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data.push({
 | 
				
			||||||
 | 
					      name: "GLOBAL",
 | 
				
			||||||
 | 
					      value:
 | 
				
			||||||
 | 
					        settings.global != null
 | 
				
			||||||
 | 
					          ? String(settings.global)
 | 
				
			||||||
 | 
					          : html`<span style="color: var(--warning-color);">DEFAULT</span>`,
 | 
				
			||||||
 | 
					      controls: html`
 | 
				
			||||||
 | 
					        <ha-icon-button @click=${() => this.changeSetting("global", null)}>
 | 
				
			||||||
 | 
					          <ha-icon .icon=${"mdi:pencil"} style="display:flex;"></ha-icon>
 | 
				
			||||||
 | 
					        </ha-icon-button>
 | 
				
			||||||
 | 
					        <ha-icon-button @click=${() => this.clearSetting("global", null)}>
 | 
				
			||||||
 | 
					          <ha-icon .icon=${"mdi:delete"} style="display:flex;"></ha-icon>
 | 
				
			||||||
 | 
					        </ha-icon-button>
 | 
				
			||||||
 | 
					      `,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    this.tableData = data;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    const global = window.browser_mod?.global_settings?.[this.settingKey];
 | 
				
			||||||
 | 
					    const columns = {
 | 
				
			||||||
 | 
					      name: {
 | 
				
			||||||
 | 
					        title: "Name",
 | 
				
			||||||
 | 
					        grows: true,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      value: {
 | 
				
			||||||
 | 
					        title: "Value",
 | 
				
			||||||
 | 
					        grows: true,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      controls: {},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return html`
 | 
				
			||||||
 | 
					      <ha-data-table .columns=${columns} .data=${this.tableData} auto-height>
 | 
				
			||||||
 | 
					      </ha-data-table>
 | 
				
			||||||
 | 
					    `;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static get styles() {
 | 
				
			||||||
 | 
					    return css`
 | 
				
			||||||
 | 
					      :host {
 | 
				
			||||||
 | 
					        display: block;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    `;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					customElements.define("browser-mod-settings-table", BrowserModSettingsTable);
 | 
				
			||||||
@ -136,7 +136,7 @@ class BrowserModRegisteredBrowsersCard extends LitElement {
 | 
				
			|||||||
  private _renderInteractionAlert() {
 | 
					  private _renderInteractionAlert() {
 | 
				
			||||||
    return html`
 | 
					    return html`
 | 
				
			||||||
      <ha-alert title="Interaction requirement">
 | 
					      <ha-alert title="Interaction requirement">
 | 
				
			||||||
        For security reasons many browsers require the user to interact with a
 | 
					        For privacy reasons many browsers require the user to interact with a
 | 
				
			||||||
        webpage before allowing audio playback or video capture. This may affect
 | 
					        webpage before allowing audio playback or video capture. This may affect
 | 
				
			||||||
        the
 | 
					        the
 | 
				
			||||||
        <code>media_player</code> and <code>camera</code> components of Browser
 | 
					        <code>media_player</code> and <code>camera</code> components of Browser
 | 
				
			||||||
 | 
				
			|||||||
@ -1,38 +1,73 @@
 | 
				
			|||||||
import { LitElement, html, css } from "lit";
 | 
					import { LitElement, html, css } from "lit";
 | 
				
			||||||
import { property, state } from "lit/decorators.js";
 | 
					import { property, state } from "lit/decorators.js";
 | 
				
			||||||
import { loadDeveloperToolsTemplate } from "../helpers";
 | 
					import { loadDeveloperToolsTemplate, selectTree } from "../helpers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "./browser-mod-settings-table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
loadDeveloperToolsTemplate();
 | 
					loadDeveloperToolsTemplate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BrowserModFrontendSettingsCard extends LitElement {
 | 
					class BrowserModFrontendSettingsCard extends LitElement {
 | 
				
			||||||
  @property() hass;
 | 
					  @property() hass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @state() _selectedTab = 0;
 | 
					  @state() _dashboards = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @state() _editSidebar = false;
 | 
				
			||||||
 | 
					  _savedSidebar = { panelOrder: [], hiddenPanels: [] };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  firstUpdated() {
 | 
					  firstUpdated() {
 | 
				
			||||||
    window.browser_mod.addEventListener("browser-mod-config-update", () =>
 | 
					    window.browser_mod.addEventListener("browser-mod-config-update", () =>
 | 
				
			||||||
      this.requestUpdate()
 | 
					      this.requestUpdate()
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    window.browser_mod.addEventListener("browser-mod-favicon-update", () =>
 | 
					 | 
				
			||||||
      this.requestUpdate()
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _handleSwitchTab(ev: CustomEvent) {
 | 
					  updated(changedProperties) {
 | 
				
			||||||
    this._selectedTab = parseInt(ev.detail.index, 10);
 | 
					    if (
 | 
				
			||||||
 | 
					      changedProperties.has("hass") &&
 | 
				
			||||||
 | 
					      changedProperties.get("hass") === undefined
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      (async () =>
 | 
				
			||||||
 | 
					        (this._dashboards = await this.hass.callWS({
 | 
				
			||||||
 | 
					          type: "lovelace/dashboards/list",
 | 
				
			||||||
 | 
					        })))();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async toggleEditSidebar() {
 | 
				
			||||||
 | 
					    const sideBar: any = await selectTree(
 | 
				
			||||||
 | 
					      document,
 | 
				
			||||||
 | 
					      "home-assistant $ home-assistant-main $ app-drawer-layout app-drawer ha-sidebar"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    sideBar.editMode = !sideBar.editMode;
 | 
				
			||||||
 | 
					    this._editSidebar = sideBar.editMode;
 | 
				
			||||||
 | 
					    if (this._editSidebar) {
 | 
				
			||||||
 | 
					      this._savedSidebar = {
 | 
				
			||||||
 | 
					        panelOrder: sideBar._panelOrder,
 | 
				
			||||||
 | 
					        hiddenPanels: sideBar._hiddenPanels,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      sideBar._panelOrder = this._savedSidebar.panelOrder ?? [];
 | 
				
			||||||
 | 
					      sideBar._hiddenPanels = this._savedSidebar.hiddenPanels ?? [];
 | 
				
			||||||
 | 
					      this._savedSidebar = { panelOrder: [], hiddenPanels: [] };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    const level = ["user", "browser", "global"][this._selectedTab];
 | 
					    const db = this._dashboards.map((d) => {
 | 
				
			||||||
 | 
					      return { value: d.url_path, label: d.title };
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    const dashboardSelector = {
 | 
				
			||||||
 | 
					      select: {
 | 
				
			||||||
 | 
					        options: [{ value: "lovelace", label: "lovelace (default)" }, ...db],
 | 
				
			||||||
 | 
					        custom_value: true,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    return html`
 | 
					    return html`
 | 
				
			||||||
      <ha-card header="Frontend Settings" outlined>
 | 
					      <ha-card header="Frontend Settings" outlined>
 | 
				
			||||||
        <div class="card-content">
 | 
					        <div class="card-content">
 | 
				
			||||||
        <ha-alert alert-type="warning">
 | 
					          <ha-alert alert-type="warning" title="Please note:">
 | 
				
			||||||
          <p>
 | 
					            The settings in this section severely change the way the Home
 | 
				
			||||||
            Please note: The settings in this section severely change the way the Home
 | 
					 | 
				
			||||||
            Assistant frontend works and looks. It is very easy to forget that
 | 
					            Assistant frontend works and looks. It is very easy to forget that
 | 
				
			||||||
            you made a setting here when you switch devices or user.
 | 
					            you made a setting here when you switch devices or user.
 | 
				
			||||||
          </p>
 | 
					 | 
				
			||||||
            <p>
 | 
					            <p>
 | 
				
			||||||
              Do not report any issues to Home Assistant before clearing
 | 
					              Do not report any issues to Home Assistant before clearing
 | 
				
			||||||
              <b>EVERY</b> setting here and thouroghly clearing all your browser
 | 
					              <b>EVERY</b> setting here and thouroghly clearing all your browser
 | 
				
			||||||
@ -41,234 +76,119 @@ class BrowserModFrontendSettingsCard extends LitElement {
 | 
				
			|||||||
            </p>
 | 
					            </p>
 | 
				
			||||||
          </ha-alert>
 | 
					          </ha-alert>
 | 
				
			||||||
          <p>
 | 
					          <p>
 | 
				
			||||||
          Global settings are applied for all users and browsers.</br>
 | 
					            Settings below are applied by first match. I.e. if a matching User
 | 
				
			||||||
          User settings are applied to the current user and overrides any Global settings.</br>
 | 
					            setting exists, it will be applied. Otherwise any matching Browser
 | 
				
			||||||
          Browser settings are applied for the current browser and overrides any User or Global settings.
 | 
					            setting and otherwise the GLOBAL setting if that differs from
 | 
				
			||||||
 | 
					            DEFAULT.
 | 
				
			||||||
          </p>
 | 
					          </p>
 | 
				
			||||||
          <mwc-tab-bar
 | 
					 | 
				
			||||||
            .activeIndex=${this._selectedTab}
 | 
					 | 
				
			||||||
            @MDCTabBar:activated=${this._handleSwitchTab}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <mwc-tab .label=${"User (" + this.hass.user.name + ")"}></mwc-tab>
 | 
					 | 
				
			||||||
            <ha-icon .icon=${"mdi:chevron-double-right"}></ha-icon>
 | 
					 | 
				
			||||||
            <mwc-tab .label=${"Browser"}></mwc-tab>
 | 
					 | 
				
			||||||
            <ha-icon .icon=${"mdi:chevron-double-right"}></ha-icon>
 | 
					 | 
				
			||||||
            <mwc-tab .label=${"Global"}></mwc-tab>
 | 
					 | 
				
			||||||
          </mwc-tab-bar>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          ${this._render_settings(level)}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </ha-card>
 | 
					 | 
				
			||||||
    `;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  _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 html`<br />Overridden by browser setting`;
 | 
					 | 
				
			||||||
      if (level === "global" && user[key] !== undefined)
 | 
					 | 
				
			||||||
        return html`<br />Overridden by user setting`;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return html`
 | 
					 | 
				
			||||||
      <div class="box">
 | 
					 | 
				
			||||||
        <ha-settings-row>
 | 
					 | 
				
			||||||
          <span slot="heading">Favicon template</span>
 | 
					 | 
				
			||||||
          ${OVERRIDDEN("faviconTemplate")}
 | 
					 | 
				
			||||||
          <img src="${window.browser_mod._currentFavicon}" class="favicon" />
 | 
					 | 
				
			||||||
        </ha-settings-row>
 | 
					 | 
				
			||||||
        <ha-code-editor
 | 
					 | 
				
			||||||
          .hass=${this.hass}
 | 
					 | 
				
			||||||
          .value=${current.faviconTemplate}
 | 
					 | 
				
			||||||
          @value-changed=${(ev) => {
 | 
					 | 
				
			||||||
            const tpl = ev.detail.value || undefined;
 | 
					 | 
				
			||||||
            window.browser_mod.set_setting("faviconTemplate", tpl, level);
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        ></ha-code-editor>
 | 
					 | 
				
			||||||
        <ha-settings-row>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() =>
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting(
 | 
					 | 
				
			||||||
                "faviconTemplate",
 | 
					 | 
				
			||||||
                undefined,
 | 
					 | 
				
			||||||
                level
 | 
					 | 
				
			||||||
              )}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Clear
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
        </ha-settings-row>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <div class="separator"></div>
 | 
					          <div class="separator"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <ha-settings-row>
 | 
					          <ha-settings-row>
 | 
				
			||||||
            <span slot="heading">Title template</span>
 | 
					            <span slot="heading">Title template</span>
 | 
				
			||||||
          ${OVERRIDDEN("titleTemplate")}
 | 
					            <span slot="description">
 | 
				
			||||||
 | 
					              Jinja template for the browser window/tab title
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
          </ha-settings-row>
 | 
					          </ha-settings-row>
 | 
				
			||||||
        <ha-code-editor
 | 
					          <browser-mod-settings-table
 | 
				
			||||||
            .hass=${this.hass}
 | 
					            .hass=${this.hass}
 | 
				
			||||||
          .value=${current.titleTemplate}
 | 
					            .settingKey=${"titleTemplate"}
 | 
				
			||||||
          @value-changed=${(ev) => {
 | 
					          ></browser-mod-settings-table>
 | 
				
			||||||
            const tpl = ev.detail.value || undefined;
 | 
					 | 
				
			||||||
            window.browser_mod.set_setting("titleTemplate", tpl, level);
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        ></ha-code-editor>
 | 
					 | 
				
			||||||
        <ha-settings-row>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() =>
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting("titleTemplate", undefined, level)}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Clear
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
        </ha-settings-row>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <div class="separator"></div>
 | 
					          <div class="separator"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <ha-settings-row>
 | 
					          <ha-settings-row>
 | 
				
			||||||
          <span slot="heading">Hide Sidebar</span>
 | 
					            <span slot="heading">Favicon template</span>
 | 
				
			||||||
          <span slot="description">Hide the sidebar and hamburger menu</span>
 | 
					            <span slot="description">
 | 
				
			||||||
          Currently: ${DESC_BOOLEAN(current.hideSidebar)}
 | 
					              Jinja template for the browser favicon
 | 
				
			||||||
          ${OVERRIDDEN("hideSidebar")}
 | 
					            </span>
 | 
				
			||||||
        </ha-settings-row>
 | 
					 | 
				
			||||||
        <ha-settings-row>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() =>
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting("hideSidebar", true, level)}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Enable
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() =>
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting("hideSidebar", false, level)}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Disable
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() =>
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting("hideSidebar", undefined, level)}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Clear
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          </ha-settings-row>
 | 
					          </ha-settings-row>
 | 
				
			||||||
 | 
					          <browser-mod-settings-table
 | 
				
			||||||
 | 
					            .hass=${this.hass}
 | 
				
			||||||
 | 
					            .settingKey=${"faviconTemplate"}
 | 
				
			||||||
 | 
					          ></browser-mod-settings-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <div class="separator"></div>
 | 
					          <div class="separator"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <ha-settings-row>
 | 
					          <ha-settings-row>
 | 
				
			||||||
          <span slot="heading">Hide Header</span>
 | 
					            <span slot="heading">Hide sidebar</span>
 | 
				
			||||||
          <span slot="description">Hide the header on all pages</span>
 | 
					            <span slot="description">
 | 
				
			||||||
          Currently: ${DESC_BOOLEAN(current.hideHeader)}
 | 
					              Completely remove the sidebar from all panels
 | 
				
			||||||
          ${OVERRIDDEN("hideHeader")}
 | 
					            </span>
 | 
				
			||||||
          </ha-settings-row>
 | 
					          </ha-settings-row>
 | 
				
			||||||
 | 
					          <browser-mod-settings-table
 | 
				
			||||||
 | 
					            .hass=${this.hass}
 | 
				
			||||||
 | 
					            .settingKey=${"hideSidebar"}
 | 
				
			||||||
 | 
					            .settingSelector=${{ boolean: {}, label: "Hide sidebar" }}
 | 
				
			||||||
 | 
					          ></browser-mod-settings-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div class="separator"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <ha-settings-row>
 | 
					          <ha-settings-row>
 | 
				
			||||||
          <mwc-button
 | 
					            <span slot="heading">Hide header</span>
 | 
				
			||||||
            @click=${() =>
 | 
					            <span slot="description">
 | 
				
			||||||
              window.browser_mod.set_setting("hideHeader", true, level)}
 | 
					              Completely remove the header from all panels
 | 
				
			||||||
          >
 | 
					            </span>
 | 
				
			||||||
            Enable
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() =>
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting("hideHeader", false, level)}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Disable
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() =>
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting("hideHeader", undefined, level)}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Clear
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          </ha-settings-row>
 | 
					          </ha-settings-row>
 | 
				
			||||||
 | 
					          <browser-mod-settings-table
 | 
				
			||||||
 | 
					            .hass=${this.hass}
 | 
				
			||||||
 | 
					            .settingKey=${"hideHeader"}
 | 
				
			||||||
 | 
					            .settingSelector=${{ boolean: {}, label: "Hide header" }}
 | 
				
			||||||
 | 
					          ></browser-mod-settings-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div class="separator"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <ha-settings-row>
 | 
				
			||||||
 | 
					            <span slot="heading">Default dashboard</span>
 | 
				
			||||||
 | 
					            <span slot="description">
 | 
				
			||||||
 | 
					              The dashboard that is showed when navigating to
 | 
				
			||||||
 | 
					              ${location.origin}/
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					          </ha-settings-row>
 | 
				
			||||||
 | 
					          <browser-mod-settings-table
 | 
				
			||||||
 | 
					            .hass=${this.hass}
 | 
				
			||||||
 | 
					            .settingKey=${"defaultPanel"}
 | 
				
			||||||
 | 
					            .settingSelector=${dashboardSelector}
 | 
				
			||||||
 | 
					            .default=${"lovelace"}
 | 
				
			||||||
 | 
					          ></browser-mod-settings-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <div class="separator"></div>
 | 
					          <div class="separator"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <ha-settings-row>
 | 
					          <ha-settings-row>
 | 
				
			||||||
            <span slot="heading">Sidebar order</span>
 | 
					            <span slot="heading">Sidebar order</span>
 | 
				
			||||||
            <span slot="description">
 | 
					            <span slot="description">
 | 
				
			||||||
            Order and visibility of sidebar buttons
 | 
					              Order and visibility of sidebar items. <br />Click EDIT and set
 | 
				
			||||||
 | 
					              the sidebar up as you want. Then save the settings and finally
 | 
				
			||||||
 | 
					              click RESTORE.
 | 
				
			||||||
            </span>
 | 
					            </span>
 | 
				
			||||||
          Currently: ${DESC_SET_UNSET(current.sidebarPanelOrder)}
 | 
					            <mwc-button @click=${() => this.toggleEditSidebar()}>
 | 
				
			||||||
          ${OVERRIDDEN("sidebarPanelOrder")}
 | 
					              ${this._editSidebar ? "Restore" : "Edit"}
 | 
				
			||||||
        </ha-settings-row>
 | 
					 | 
				
			||||||
        <ha-settings-row>
 | 
					 | 
				
			||||||
          <span slot="description">
 | 
					 | 
				
			||||||
            Clearing this does NOT restore the original default order.
 | 
					 | 
				
			||||||
          </span>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() => {
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting(
 | 
					 | 
				
			||||||
                "sidebarPanelOrder",
 | 
					 | 
				
			||||||
                localStorage.getItem("sidebarPanelOrder"),
 | 
					 | 
				
			||||||
                level
 | 
					 | 
				
			||||||
              );
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting(
 | 
					 | 
				
			||||||
                "sidebarHiddenPanels",
 | 
					 | 
				
			||||||
                localStorage.getItem("sidebarHiddenPanels"),
 | 
					 | 
				
			||||||
                level
 | 
					 | 
				
			||||||
              );
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Set
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() => {
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting(
 | 
					 | 
				
			||||||
                "sidebarPanelOrder",
 | 
					 | 
				
			||||||
                undefined,
 | 
					 | 
				
			||||||
                level
 | 
					 | 
				
			||||||
              );
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting(
 | 
					 | 
				
			||||||
                "sidebarHiddenPanels",
 | 
					 | 
				
			||||||
                undefined,
 | 
					 | 
				
			||||||
                level
 | 
					 | 
				
			||||||
              );
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Clear
 | 
					 | 
				
			||||||
            </mwc-button>
 | 
					            </mwc-button>
 | 
				
			||||||
          </ha-settings-row>
 | 
					          </ha-settings-row>
 | 
				
			||||||
 | 
					          <browser-mod-settings-table
 | 
				
			||||||
 | 
					            .hass=${this.hass}
 | 
				
			||||||
 | 
					            .settingKey=${"sidebarPanelOrder"}
 | 
				
			||||||
 | 
					            .settingSelector=${{
 | 
				
			||||||
 | 
					              plaintext: "Press OK to store the current sidebar order",
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            .default=${"lovelace"}
 | 
				
			||||||
 | 
					          ></browser-mod-settings-table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <div class="separator"></div>
 | 
					          <div class="separator"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <ha-settings-row>
 | 
					          <ha-settings-row>
 | 
				
			||||||
          <span slot="heading">Default dashboard</span>
 | 
					            <span slot="heading">Sidebar title</span>
 | 
				
			||||||
          <span slot="description"
 | 
					 | 
				
			||||||
            >The dashboard that's displayed by default</span
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
          Currently: ${DESC_SET_UNSET(current.defaultPanel)}
 | 
					 | 
				
			||||||
          ${OVERRIDDEN("defaultPanel")}
 | 
					 | 
				
			||||||
        </ha-settings-row>
 | 
					 | 
				
			||||||
        <ha-settings-row>
 | 
					 | 
				
			||||||
            <span slot="description">
 | 
					            <span slot="description">
 | 
				
			||||||
            Clearing this does NOT restore the original default dashboard.
 | 
					              The title at the top of the sidebar
 | 
				
			||||||
            </span>
 | 
					            </span>
 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() => {
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting(
 | 
					 | 
				
			||||||
                "defaultPanel",
 | 
					 | 
				
			||||||
                localStorage.getItem("defaultPanel"),
 | 
					 | 
				
			||||||
                level
 | 
					 | 
				
			||||||
              );
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Set
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          <mwc-button
 | 
					 | 
				
			||||||
            @click=${() => {
 | 
					 | 
				
			||||||
              window.browser_mod.set_setting("defaultPanel", undefined, level);
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Clear
 | 
					 | 
				
			||||||
          </mwc-button>
 | 
					 | 
				
			||||||
          </ha-settings-row>
 | 
					          </ha-settings-row>
 | 
				
			||||||
 | 
					          <browser-mod-settings-table
 | 
				
			||||||
 | 
					            .hass=${this.hass}
 | 
				
			||||||
 | 
					            .settingKey=${"sidebarTitle"}
 | 
				
			||||||
 | 
					            .settingSelector=${{ text: {} }}
 | 
				
			||||||
 | 
					          ></browser-mod-settings-table>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					      </ha-card>
 | 
				
			||||||
    `;
 | 
					    `;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -280,7 +200,7 @@ class BrowserModFrontendSettingsCard extends LitElement {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      .separator {
 | 
					      .separator {
 | 
				
			||||||
        border-bottom: 1px solid var(--divider-color);
 | 
					        border-bottom: 1px solid var(--divider-color);
 | 
				
			||||||
        margin: 0 -8px;
 | 
					        margin: 16px -16px 0px;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      img.favicon {
 | 
					      img.favicon {
 | 
				
			||||||
        width: 64px;
 | 
					        width: 64px;
 | 
				
			||||||
 | 
				
			|||||||
@ -15,12 +15,13 @@ loadConfigDashboard().then(() => {
 | 
				
			|||||||
    @property() connection;
 | 
					    @property() connection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    firstUpdated() {
 | 
					    firstUpdated() {
 | 
				
			||||||
      window.browser_mod.addEventListener("browser-mod-config-update", () =>
 | 
					      window.addEventListener("browser-mod-config-update", () =>
 | 
				
			||||||
        this.requestUpdate()
 | 
					        this.requestUpdate()
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
 | 
					      if (!window.browser_mod) return html``;
 | 
				
			||||||
      return html`
 | 
					      return html`
 | 
				
			||||||
        <ha-app-layout>
 | 
					        <ha-app-layout>
 | 
				
			||||||
          <app-header slot="header" fixed>
 | 
					          <app-header slot="header" fixed>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,17 @@
 | 
				
			|||||||
const TIMEOUT_ERROR = "SELECTTREE-TIMEOUT";
 | 
					const TIMEOUT_ERROR = "SELECTTREE-TIMEOUT";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function _await_el(el) {
 | 
					export async function await_element(el, hard = false) {
 | 
				
			||||||
  if (el.localName?.includes("-"))
 | 
					  if (el.localName?.includes("-"))
 | 
				
			||||||
    await customElements.whenDefined(el.localName);
 | 
					    await customElements.whenDefined(el.localName);
 | 
				
			||||||
  if (el.updateComplete) await el.updateComplete;
 | 
					  if (el.updateComplete) await el.updateComplete;
 | 
				
			||||||
 | 
					  if (hard) {
 | 
				
			||||||
 | 
					    if (el.pageRendered) await el.pageRendered;
 | 
				
			||||||
 | 
					    if (el._panelState) {
 | 
				
			||||||
 | 
					      let rounds = 0;
 | 
				
			||||||
 | 
					      while (el._panelState !== "loaded" && rounds++ < 5)
 | 
				
			||||||
 | 
					        await new Promise((r) => setTimeout(r, 100));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function _selectTree(root, path, all = false) {
 | 
					async function _selectTree(root, path, all = false) {
 | 
				
			||||||
@ -18,7 +26,7 @@ async function _selectTree(root, path, all = false) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (!p.trim().length) continue;
 | 
					    if (!p.trim().length) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _await_el(e);
 | 
					    await_element(e);
 | 
				
			||||||
    el = p === "$" ? [e.shadowRoot] : e.querySelectorAll(p);
 | 
					    el = p === "$" ? [e.shadowRoot] : e.querySelectorAll(p);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return all ? el : el[0];
 | 
					  return all ? el : el[0];
 | 
				
			||||||
@ -102,6 +110,7 @@ export const loadConfigDashboard = async () => {
 | 
				
			|||||||
  const configRouter: any = document.createElement("ha-panel-config");
 | 
					  const configRouter: any = document.createElement("ha-panel-config");
 | 
				
			||||||
  await configRouter?.routerOptions?.routes?.dashboard?.load?.(); // Load ha-config-dashboard
 | 
					  await configRouter?.routerOptions?.routes?.dashboard?.load?.(); // Load ha-config-dashboard
 | 
				
			||||||
  await configRouter?.routerOptions?.routes?.cloud?.load?.(); // Load ha-settings-row
 | 
					  await configRouter?.routerOptions?.routes?.cloud?.load?.(); // Load ha-settings-row
 | 
				
			||||||
 | 
					  await configRouter?.routerOptions?.routes?.entities?.load?.(); // Load ha-data-table
 | 
				
			||||||
  await customElements.whenDefined("ha-config-dashboard");
 | 
					  await customElements.whenDefined("ha-config-dashboard");
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -132,3 +141,32 @@ export function throttle(timeout) {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function runOnce(restart = false) {
 | 
				
			||||||
 | 
					  return function (target, propertyKey, descriptor) {
 | 
				
			||||||
 | 
					    const fn = descriptor.value;
 | 
				
			||||||
 | 
					    let running = undefined;
 | 
				
			||||||
 | 
					    const newfn = function (...rest) {
 | 
				
			||||||
 | 
					      if (restart && running === false) running = true;
 | 
				
			||||||
 | 
					      if (running !== undefined) return;
 | 
				
			||||||
 | 
					      running = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const retval = fn.bind(this)(...rest);
 | 
				
			||||||
 | 
					      if (running) {
 | 
				
			||||||
 | 
					        running = undefined;
 | 
				
			||||||
 | 
					        return newfn.bind(this)(...rest);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        running = undefined;
 | 
				
			||||||
 | 
					        return retval;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    descriptor.value = newfn;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function waitRepeat(fn, times, delay) {
 | 
				
			||||||
 | 
					  while (times--) {
 | 
				
			||||||
 | 
					    fn();
 | 
				
			||||||
 | 
					    await new Promise((r) => setTimeout(r, delay));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ export const ConnectionMixin = (SuperClass) => {
 | 
				
			|||||||
    public browserEntities = {};
 | 
					    public browserEntities = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    LOG(...args) {
 | 
					    LOG(...args) {
 | 
				
			||||||
      return;
 | 
					      if (window.browser_mod_log === undefined) return;
 | 
				
			||||||
      const dt = new Date();
 | 
					      const dt = new Date();
 | 
				
			||||||
      console.log(`${dt.toLocaleTimeString()}`, ...args);
 | 
					      console.log(`${dt.toLocaleTimeString()}`, ...args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,7 +24,7 @@ export const ConnectionMixin = (SuperClass) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fireEvent(event, detail = undefined) {
 | 
					    private fireEvent(event, detail = undefined) {
 | 
				
			||||||
      this.dispatchEvent(new CustomEvent(event, { detail }));
 | 
					      this.dispatchEvent(new CustomEvent(event, { detail, bubbles: true }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private incoming_message(msg) {
 | 
					    private incoming_message(msg) {
 | 
				
			||||||
@ -37,6 +37,7 @@ export const ConnectionMixin = (SuperClass) => {
 | 
				
			|||||||
        this.update_config(msg.result);
 | 
					        this.update_config(msg.result);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this._connectionResolve?.();
 | 
					      this._connectionResolve?.();
 | 
				
			||||||
 | 
					      this._connectionResolve = undefined;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private update_config(cfg) {
 | 
					    private update_config(cfg) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,37 @@
 | 
				
			|||||||
import { selectTree } from "../helpers";
 | 
					import { await_element, waitRepeat, runOnce, selectTree } from "../helpers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const AutoSettingsMixin = (SuperClass) => {
 | 
					export const AutoSettingsMixin = (SuperClass) => {
 | 
				
			||||||
  return class AutoSettingsMixinClass extends SuperClass {
 | 
					  class AutoSettingsMixinClass extends SuperClass {
 | 
				
			||||||
    _faviconTemplateSubscription;
 | 
					    _faviconTemplateSubscription;
 | 
				
			||||||
    _titleTemplateSubscription;
 | 
					    _titleTemplateSubscription;
 | 
				
			||||||
    __currentTitle = undefined;
 | 
					    __currentTitle = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @runOnce()
 | 
				
			||||||
 | 
					    async runHideHeader() {
 | 
				
			||||||
 | 
					      while (!(await this._hideHeader()))
 | 
				
			||||||
 | 
					        await new Promise((r) => setTimeout(r, 500));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @runOnce(true)
 | 
				
			||||||
 | 
					    async runUpdateTitle() {
 | 
				
			||||||
 | 
					      await waitRepeat(() => this._updateTitle(), 3, 500);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
      super();
 | 
					      super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this._auto_settings_setup();
 | 
					      const runUpdates = async () => {
 | 
				
			||||||
      this.addEventListener("browser-mod-config-update", () =>
 | 
					        this.runUpdateTitle();
 | 
				
			||||||
        this._auto_settings_setup()
 | 
					        this.runHideHeader();
 | 
				
			||||||
      );
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      window.addEventListener("location-changed", () => {
 | 
					      this._auto_settings_setup();
 | 
				
			||||||
        this._updateTitle();
 | 
					      this.addEventListener("browser-mod-config-update", () => {
 | 
				
			||||||
        setTimeout(() => this._updateTitle(), 500);
 | 
					        this._auto_settings_setup();
 | 
				
			||||||
        setTimeout(() => this._updateTitle(), 1000);
 | 
					        runUpdates();
 | 
				
			||||||
        setTimeout(() => this._updateTitle(), 5000);
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      window.addEventListener("location-changed", runUpdates);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async _auto_settings_setup() {
 | 
					    async _auto_settings_setup() {
 | 
				
			||||||
@ -40,7 +52,7 @@ export const AutoSettingsMixin = (SuperClass) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Default panel
 | 
					      // Default panel
 | 
				
			||||||
      if (settings.defaultPanel) {
 | 
					      if (settings.defaultPanel) {
 | 
				
			||||||
        localStorage.setItem("defaultPanel", settings.defaultPanel);
 | 
					        localStorage.setItem("defaultPanel", `"${settings.defaultPanel}"`);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Hide sidebar
 | 
					      // Hide sidebar
 | 
				
			||||||
@ -55,18 +67,18 @@ export const AutoSettingsMixin = (SuperClass) => {
 | 
				
			|||||||
        ).then((el) => el?.remove?.());
 | 
					        ).then((el) => el?.remove?.());
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Hide header
 | 
					      // Sidebar title
 | 
				
			||||||
      if (settings.hideHeader === true) {
 | 
					      if (settings.sidebarTitle) {
 | 
				
			||||||
        customElements.whenDefined("app-header-layout").then(() => {
 | 
					        selectTree(
 | 
				
			||||||
          const appHeader = customElements.get("app-header").prototype;
 | 
					          document,
 | 
				
			||||||
          const _attached = appHeader.attached;
 | 
					          "home-assistant $ home-assistant-main $ app-drawer-layout app-drawer ha-sidebar $ .title"
 | 
				
			||||||
          appHeader.attached = function () {
 | 
					        ).then((el) => {
 | 
				
			||||||
            _attached.bind(this)();
 | 
					          if (el) (el as HTMLElement).innerHTML = settings.sidebarTitle;
 | 
				
			||||||
            this.style.setProperty("display", "none");
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Hide header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Favicon template
 | 
					      // Favicon template
 | 
				
			||||||
      if (settings.faviconTemplate !== undefined) {
 | 
					      if (settings.faviconTemplate !== undefined) {
 | 
				
			||||||
        (async () => {
 | 
					        (async () => {
 | 
				
			||||||
@ -111,7 +123,6 @@ export const AutoSettingsMixin = (SuperClass) => {
 | 
				
			|||||||
    _updateFavicon({ result }) {
 | 
					    _updateFavicon({ result }) {
 | 
				
			||||||
      const link: any = document.head.querySelector("link[rel~='icon']");
 | 
					      const link: any = document.head.querySelector("link[rel~='icon']");
 | 
				
			||||||
      link.href = result;
 | 
					      link.href = result;
 | 
				
			||||||
      window.browser_mod.fireEvent("browser-mod-favicon-update");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get _currentTitle() {
 | 
					    get _currentTitle() {
 | 
				
			||||||
@ -121,7 +132,77 @@ export const AutoSettingsMixin = (SuperClass) => {
 | 
				
			|||||||
    _updateTitle(data = undefined) {
 | 
					    _updateTitle(data = undefined) {
 | 
				
			||||||
      if (data) this.__currentTitle = data.result;
 | 
					      if (data) this.__currentTitle = data.result;
 | 
				
			||||||
      if (this.__currentTitle) document.title = this.__currentTitle;
 | 
					      if (this.__currentTitle) document.title = this.__currentTitle;
 | 
				
			||||||
      window.browser_mod.fireEvent("browser-mod-favicon-update");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					
 | 
				
			||||||
 | 
					    async _hideHeader() {
 | 
				
			||||||
 | 
					      if (this.settings.hideHeader !== true) return true;
 | 
				
			||||||
 | 
					      let el = await selectTree(
 | 
				
			||||||
 | 
					        document,
 | 
				
			||||||
 | 
					        "home-assistant $ home-assistant-main $ app-drawer-layout partial-panel-resolver"
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      if (!el) return false;
 | 
				
			||||||
 | 
					      let steps = 0;
 | 
				
			||||||
 | 
					      while (el && el.localName !== "ha-app-layout" && steps++ < 5) {
 | 
				
			||||||
 | 
					        await await_element(el, true);
 | 
				
			||||||
 | 
					        const next =
 | 
				
			||||||
 | 
					          el.querySelector("ha-app-layout") ??
 | 
				
			||||||
 | 
					          el.firstElementChild ??
 | 
				
			||||||
 | 
					          el.shadowRoot;
 | 
				
			||||||
 | 
					        el = next;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (el?.localName !== "ha-app-layout") return false;
 | 
				
			||||||
 | 
					      if (el.header) {
 | 
				
			||||||
 | 
					        el.header.style.setProperty("display", "none");
 | 
				
			||||||
 | 
					        setTimeout(() => el._updateLayoutStates(), 0);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getSetting(key) {
 | 
				
			||||||
 | 
					      const retval = { global: undefined, browser: {}, user: {} };
 | 
				
			||||||
 | 
					      retval.global = this._data.settings?.[key];
 | 
				
			||||||
 | 
					      for (const [k, v] of Object.entries(this._data.browsers ?? {})) {
 | 
				
			||||||
 | 
					        if ((v as any).settings?.[key] != null)
 | 
				
			||||||
 | 
					          retval.browser[k] = (v as any).settings[key];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      for (const [k, v] of Object.entries(this._data.user_settings ?? {})) {
 | 
				
			||||||
 | 
					        if (v[key] != null) retval.user[k] = v[key];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return retval;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setSetting(type, target, settings) {
 | 
				
			||||||
 | 
					      if (type === "global") {
 | 
				
			||||||
 | 
					        for (const [key, value] of Object.entries(settings))
 | 
				
			||||||
 | 
					          this.connection.sendMessage({
 | 
				
			||||||
 | 
					            type: "browser_mod/settings",
 | 
				
			||||||
 | 
					            key,
 | 
				
			||||||
 | 
					            value,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					      } else if (type === "browser") {
 | 
				
			||||||
 | 
					        const browser = this._data.browsers[target];
 | 
				
			||||||
 | 
					        const newsettings = { ...browser.settings, ...settings };
 | 
				
			||||||
 | 
					        console.log(newsettings);
 | 
				
			||||||
 | 
					        this.connection.sendMessage({
 | 
				
			||||||
 | 
					          type: "browser_mod/register",
 | 
				
			||||||
 | 
					          browserID: target,
 | 
				
			||||||
 | 
					          data: {
 | 
				
			||||||
 | 
					            ...browser,
 | 
				
			||||||
 | 
					            settings: newsettings,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      } else if (type === "user") {
 | 
				
			||||||
 | 
					        const user = target;
 | 
				
			||||||
 | 
					        for (const [key, value] of Object.entries(settings))
 | 
				
			||||||
 | 
					          this.connection.sendMessage({
 | 
				
			||||||
 | 
					            type: "browser_mod/settings",
 | 
				
			||||||
 | 
					            user,
 | 
				
			||||||
 | 
					            key,
 | 
				
			||||||
 | 
					            value,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return AutoSettingsMixinClass;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -223,7 +223,6 @@ class BrowserModPopup extends LitElement {
 | 
				
			|||||||
  static get styles() {
 | 
					  static get styles() {
 | 
				
			||||||
    return css`
 | 
					    return css`
 | 
				
			||||||
      ha-dialog {
 | 
					      ha-dialog {
 | 
				
			||||||
        --dialog-backdrop-filter: blur(5px);
 | 
					 | 
				
			||||||
        z-index: 10;
 | 
					        z-index: 10;
 | 
				
			||||||
        --mdc-dialog-min-width: var(--popup-min-width, 400px);
 | 
					        --mdc-dialog-min-width: var(--popup-min-width, 400px);
 | 
				
			||||||
        --mdc-dialog-max-width: var(--popup-max-width, 600px);
 | 
					        --mdc-dialog-max-width: var(--popup-max-width, 600px);
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,7 @@ interface FullyKiosk {
 | 
				
			|||||||
declare global {
 | 
					declare global {
 | 
				
			||||||
  interface Window {
 | 
					  interface Window {
 | 
				
			||||||
    browser_mod?: BrowserMod;
 | 
					    browser_mod?: BrowserMod;
 | 
				
			||||||
 | 
					    browser_mod_log?: any;
 | 
				
			||||||
    fully?: FullyKiosk;
 | 
					    fully?: FullyKiosk;
 | 
				
			||||||
    hassConnection?: Promise<any>;
 | 
					    hassConnection?: Promise<any>;
 | 
				
			||||||
    customCards?: [{}?];
 | 
					    customCards?: [{}?];
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "browser_mod",
 | 
					  "name": "browser_mod",
 | 
				
			||||||
  "version": "2.0.0b2",
 | 
					  "version": "2.0.0b4",
 | 
				
			||||||
  "lockfileVersion": 1,
 | 
					  "lockfileVersion": 1,
 | 
				
			||||||
  "requires": true,
 | 
					  "requires": true,
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "browser_mod",
 | 
					  "name": "browser_mod",
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "version": "2.0.0b4",
 | 
					  "version": "2.0.0b5",
 | 
				
			||||||
  "description": "",
 | 
					  "description": "",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "build": "rollup -c",
 | 
					    "build": "rollup -c",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										25
									
								
								test/automations.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								test/automations.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					- id: "1660669793583"
 | 
				
			||||||
 | 
					  alias: Toggle bed light
 | 
				
			||||||
 | 
					  description: ""
 | 
				
			||||||
 | 
					  trigger:
 | 
				
			||||||
 | 
					    - platform: time_pattern
 | 
				
			||||||
 | 
					      seconds: /3
 | 
				
			||||||
 | 
					  condition: []
 | 
				
			||||||
 | 
					  action:
 | 
				
			||||||
 | 
					    - type: toggle
 | 
				
			||||||
 | 
					      device_id: 98861bdf58b3c79183c03be06da14f27
 | 
				
			||||||
 | 
					      entity_id: light.bed_light
 | 
				
			||||||
 | 
					      domain: light
 | 
				
			||||||
 | 
					  mode: single
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- alias: Popup when kitchen light togggled
 | 
				
			||||||
 | 
					  trigger:
 | 
				
			||||||
 | 
					    - platform: state
 | 
				
			||||||
 | 
					      entity_id: light.kitchen_lights
 | 
				
			||||||
 | 
					  action:
 | 
				
			||||||
 | 
					    - service: browser_mod.popup
 | 
				
			||||||
 | 
					      data:
 | 
				
			||||||
 | 
					        title: automation
 | 
				
			||||||
 | 
					        content:
 | 
				
			||||||
 | 
					          type: markdown
 | 
				
			||||||
 | 
					          content: "{%raw%}{{states('light.bed_light')}}{%endraw%}"
 | 
				
			||||||
@ -1,5 +1,7 @@
 | 
				
			|||||||
default_config:
 | 
					default_config:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					automation: !include test/automations.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
demo:
 | 
					demo:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
http:
 | 
					http:
 | 
				
			||||||
@ -12,6 +14,8 @@ logger:
 | 
				
			|||||||
  logs:
 | 
					  logs:
 | 
				
			||||||
    custom_components.browser_mod: info
 | 
					    custom_components.browser_mod: info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# debugpy:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# browser_mod:
 | 
					# browser_mod:
 | 
				
			||||||
#   devices:
 | 
					#   devices:
 | 
				
			||||||
#     camdevice:
 | 
					#     camdevice:
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,7 @@ views:
 | 
				
			|||||||
          action: more-info
 | 
					          action: more-info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - !include views/popup.yaml
 | 
					  - !include views/popup.yaml
 | 
				
			||||||
 | 
					  - !include views/frontend-backend.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - title: Popup card
 | 
					  - title: Popup card
 | 
				
			||||||
    popup_cards:
 | 
					    popup_cards:
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										30
									
								
								test/views/frontend-backend.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								test/views/frontend-backend.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					title: frontend vs backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cards:
 | 
				
			||||||
 | 
					  - type: entities
 | 
				
			||||||
 | 
					    entities:
 | 
				
			||||||
 | 
					      - light.bed_light
 | 
				
			||||||
 | 
					      - light.kitchen_lights
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - type: button
 | 
				
			||||||
 | 
					    name: fire-dom-event
 | 
				
			||||||
 | 
					    tap_action:
 | 
				
			||||||
 | 
					      action: fire-dom-event
 | 
				
			||||||
 | 
					      browser_mod:
 | 
				
			||||||
 | 
					        service: browser_mod.popup
 | 
				
			||||||
 | 
					        data:
 | 
				
			||||||
 | 
					          title: fire-dom-event
 | 
				
			||||||
 | 
					          content:
 | 
				
			||||||
 | 
					            type: markdown
 | 
				
			||||||
 | 
					            content: "{{states('light.bed_light')}}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - type: button
 | 
				
			||||||
 | 
					    name: call-service
 | 
				
			||||||
 | 
					    tap_action:
 | 
				
			||||||
 | 
					      action: call-service
 | 
				
			||||||
 | 
					      service: browser_mod.popup
 | 
				
			||||||
 | 
					      data:
 | 
				
			||||||
 | 
					        title: call-service
 | 
				
			||||||
 | 
					        content:
 | 
				
			||||||
 | 
					          type: markdown
 | 
				
			||||||
 | 
					          content: "{{states('light.bed_light')}}"
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user