Compare commits

..

1 Commits

Author SHA1 Message Date
Bram Kragten
e3be190b36 Add no device option to device filter 2024-04-24 12:56:24 +02:00
83 changed files with 989 additions and 1736 deletions

View File

@@ -21,7 +21,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
with:
ref: dev
@@ -57,7 +57,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
with:
ref: master

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Setup Node
uses: actions/setup-node@v4.0.2
with:
@@ -58,7 +58,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Setup Node
uses: actions/setup-node@v4.0.2
with:
@@ -76,7 +76,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Setup Node
uses: actions/setup-node@v4.0.2
with:
@@ -89,7 +89,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.3
uses: actions/upload-artifact@v4.3.2
with:
name: frontend-bundle-stats
path: build/stats/*.json
@@ -100,7 +100,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Setup Node
uses: actions/setup-node@v4.0.2
with:
@@ -113,7 +113,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.3
uses: actions/upload-artifact@v4.3.2
with:
name: supervisor-bundle-stats
path: build/stats/*.json

View File

@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.

View File

@@ -22,7 +22,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
with:
ref: dev
@@ -58,7 +58,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
with:
ref: master

View File

@@ -16,7 +16,7 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Setup Node
uses: actions/setup-node@v4.0.2

View File

@@ -21,7 +21,7 @@ jobs:
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Setup Node
uses: actions/setup-node@v4.0.2

View File

@@ -20,7 +20,7 @@ jobs:
contents: write
steps:
- name: Checkout the repository
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
@@ -57,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts
uses: actions/upload-artifact@v4.3.3
uses: actions/upload-artifact@v4.3.2
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v4.3.3
uses: actions/upload-artifact@v4.3.2
with:
name: translations
path: translations.tar.gz

View File

@@ -23,7 +23,7 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4.1.4
uses: actions/checkout@v4.1.3
- name: Upload Translations
run: |

View File

@@ -10,6 +10,7 @@ const WebpackBar = require("webpackbar");
const {
TransformAsyncModulesPlugin,
} = require("transform-async-modules-webpack-plugin");
const { dependencies } = require("../package.json");
const paths = require("./paths.cjs");
const bundle = require("./bundle.cjs");
@@ -156,7 +157,10 @@ const createWebpackConfig = ({
transform: (stats) => JSON.stringify(filterStats(stats)),
}),
!latestBuild &&
new TransformAsyncModulesPlugin({ browserslistEnv: "legacy" }),
new TransformAsyncModulesPlugin({
browserslistEnv: "legacy",
runtime: { version: dependencies["@babel/runtime"] },
}),
].filter(Boolean),
resolve: {
extensions: [".ts", ".js", ".json"],

View File

@@ -10,7 +10,6 @@ import {
import { HomeAssistantAppEl } from "../../src/layouts/home-assistant";
import { HomeAssistant } from "../../src/types";
import { selectedDemoConfig } from "./configs/demo-configs";
import { mockAreaRegistry } from "./stubs/area_registry";
import { mockAuth } from "./stubs/auth";
import { mockConfigEntries } from "./stubs/config_entries";
import { mockEnergy } from "./stubs/energy";
@@ -24,10 +23,10 @@ import { mockLovelace } from "./stubs/lovelace";
import { mockMediaPlayer } from "./stubs/media_player";
import { mockPersistentNotification } from "./stubs/persistent_notification";
import { mockRecorder } from "./stubs/recorder";
import { mockTodo } from "./stubs/todo";
import { mockSensor } from "./stubs/sensor";
import { mockSystemLog } from "./stubs/system_log";
import { mockTemplate } from "./stubs/template";
import { mockTodo } from "./stubs/todo";
import { mockTranslations } from "./stubs/translations";
@customElement("ha-demo")
@@ -63,7 +62,6 @@ export class HaDemo extends HomeAssistantAppEl {
mockEnergy(hass);
mockPersistentNotification(hass);
mockConfigEntries(hass);
mockAreaRegistry(hass);
mockEntityRegistry(hass, [
{
config_entry_id: "co2signal",

View File

@@ -1,19 +1,19 @@
import { mdiRefresh, mdiStorePlus } from "@mdi/js";
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
import { mdiStorePlus, mdiUpdate } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-fab";
import { reloadHassioAddons } from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-subpage";
import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
import { supervisorTabs } from "../hassio-tabs";
import "./hassio-addons";
import "../../../src/layouts/hass-subpage";
import { reloadHassioAddons } from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import { fireEvent } from "../../../src/common/dom/fire_event";
@customElement("hassio-dashboard")
class HassioDashboard extends LitElement {
@@ -43,7 +43,7 @@ class HassioDashboard extends LitElement {
<ha-icon-button
slot="toolbar-icon"
@click=${this._handleCheckUpdates}
.path=${mdiRefresh}
.path=${mdiUpdate}
.label=${this.supervisor.localize("store.check_updates")}
></ha-icon-button>
<hassio-addons

View File

@@ -141,26 +141,26 @@
"vue": "2.7.16",
"vue2-daterange-picker": "0.6.8",
"weekstart": "2.0.0",
"workbox-cacheable-response": "7.1.0",
"workbox-core": "7.1.0",
"workbox-expiration": "7.1.0",
"workbox-precaching": "7.1.0",
"workbox-routing": "7.1.0",
"workbox-strategies": "7.1.0",
"workbox-cacheable-response": "7.0.0",
"workbox-core": "7.0.0",
"workbox-expiration": "7.0.0",
"workbox-precaching": "7.0.0",
"workbox-routing": "7.0.0",
"workbox-strategies": "7.0.0",
"xss": "1.0.15"
},
"devDependencies": {
"@babel/core": "7.24.4",
"@babel/helper-define-polyfill-provider": "0.6.2",
"@babel/helper-define-polyfill-provider": "0.6.1",
"@babel/plugin-proposal-decorators": "7.24.1",
"@babel/plugin-transform-runtime": "7.24.3",
"@babel/preset-env": "7.24.4",
"@babel/preset-typescript": "7.24.1",
"@bundle-stats/plugin-webpack-filter": "4.12.2",
"@koa/cors": "5.0.0",
"@lokalise/node-api": "12.4.1",
"@lokalise/node-api": "12.4.0",
"@octokit/auth-oauth-device": "7.1.1",
"@octokit/plugin-retry": "7.1.1",
"@octokit/plugin-retry": "7.1.0",
"@octokit/rest": "20.1.0",
"@open-wc/dev-server-hmr": "0.1.4",
"@rollup/plugin-babel": "6.0.4",
@@ -175,7 +175,7 @@
"@types/glob": "8.1.0",
"@types/html-minifier-terser": "7.0.2",
"@types/js-yaml": "4.0.9",
"@types/leaflet": "1.9.12",
"@types/leaflet": "1.9.11",
"@types/leaflet-draw": "1.0.11",
"@types/luxon": "3.4.2",
"@types/mocha": "10.0.6",
@@ -185,8 +185,8 @@
"@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "7.7.1",
"@typescript-eslint/parser": "7.7.1",
"@typescript-eslint/eslint-plugin": "7.7.0",
"@typescript-eslint/parser": "7.7.0",
"@web/dev-server": "0.1.38",
"@web/dev-server-rollup": "0.4.1",
"babel-loader": "9.1.3",
@@ -245,7 +245,7 @@
"webpack-manifest-plugin": "5.0.0",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "6.0.1",
"workbox-build": "7.1.0"
"workbox-build": "7.0.0"
},
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
"resolutions": {

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20240501.1"
version = "20240424.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

@@ -61,8 +61,11 @@ export const applyThemesOnElement = (
const accentColor = themeSettings?.accentColor;
if (darkMode && primaryColor) {
themeRules["app-theme-color"] = hexBlend(primaryColor, "#121212", 8);
themeRules["app-header-background-color"] = themeRules["app-theme-color"];
themeRules["app-header-background-color"] = hexBlend(
primaryColor,
"#121212",
8
);
}
if (primaryColor) {

View File

@@ -20,7 +20,6 @@ export class HaInputChip extends MdInputChip {
0.15
);
--ha-input-chip-selected-container-opacity: 1;
--md-input-chip-label-text-font: Roboto, sans-serif;
}
/** Set the size of mdc icons **/
::slotted([slot="icon"]) {

View File

@@ -1,4 +1,4 @@
import { mdiArrowDown, mdiArrowUp, mdiChevronUp } from "@mdi/js";
import { mdiArrowDown, mdiArrowUp, mdiChevronDown } from "@mdi/js";
import deepClone from "deep-clone-simple";
import {
CSSResultGroup,
@@ -565,30 +565,36 @@ export class HaDataTable extends LitElement {
}, {});
const groupedItems: DataTableRowData[] = [];
Object.entries(sorted).forEach(([groupName, rows]) => {
groupedItems.push({
append: true,
content: html`<div
class="mdc-data-table__cell group-header"
role="cell"
.group=${groupName}
@click=${this._collapseGroup}
>
<ha-icon-button
.path=${mdiChevronUp}
class=${this._collapsedGroups.includes(groupName)
? "collapsed"
: ""}
if (
groupName !== UNDEFINED_GROUP_KEY ||
Object.keys(sorted).length > 1
) {
groupedItems.push({
append: true,
content: html`<div
class="mdc-data-table__cell group-header"
role="cell"
.group=${groupName}
@click=${this._collapseGroup}
>
</ha-icon-button>
${groupName === UNDEFINED_GROUP_KEY
? this.hass.localize("ui.components.data-table.ungrouped")
: groupName || ""}
</div>`,
});
<ha-icon-button
.path=${mdiChevronDown}
class=${this._collapsedGroups.includes(groupName)
? "collapsed"
: ""}
>
</ha-icon-button>
${groupName === UNDEFINED_GROUP_KEY
? this.hass.localize("ui.components.data-table.ungrouped")
: groupName || ""}
</div>`,
});
}
if (!this._collapsedGroups.includes(groupName)) {
groupedItems.push(...rows);
}
});
items = groupedItems;
}

View File

@@ -19,7 +19,6 @@ import { HomeAssistant } from "../types";
import "./ha-list-item";
import "./ha-select";
import type { HaSelect } from "./ha-select";
import { getExtendedEntityRegistryEntry } from "../data/entity_registry";
const NONE = "__NONE_OPTION__";
@@ -108,23 +107,13 @@ export class HaConversationAgentPicker extends LitElement {
}
private async _maybeFetchConfigEntry() {
if (!this.value || !(this.value in this.hass.entities)) {
if (!this.value || this.value === "homeassistant") {
this._configEntry = undefined;
return;
}
try {
const regEntry = await getExtendedEntityRegistryEntry(
this.hass,
this.value
);
if (!regEntry.config_entry_id) {
this._configEntry = undefined;
return;
}
this._configEntry = (
await getConfigEntry(this.hass, regEntry.config_entry_id)
await getConfigEntry(this.hass, this.value)
).config_entry;
} catch (err) {
this._configEntry = undefined;

View File

@@ -127,10 +127,6 @@ export class HaDialog extends DialogBase {
border-radius: var(--ha-dialog-border-radius, 28px);
-webkit-backdrop-filter: var(--ha-dialog-surface-backdrop-filter, none);
backdrop-filter: var(--ha-dialog-surface-backdrop-filter, none);
background: var(
--ha-dialog-surface-background,
var(--mdc-theme-surface, #fff)
);
}
:host([flexContent]) .mdc-dialog .mdc-dialog__content {
display: flex;

View File

@@ -20,6 +20,8 @@ import "./ha-check-list-item";
import "./ha-expansion-panel";
import "./search-input-outlined";
export const FILTER_NO_DEVICE = "__NO_DEVICE__";
@customElement("ha-filter-devices")
export class HaFilterDevices extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -32,6 +34,9 @@ export class HaFilterDevices extends LitElement {
@property({ type: Boolean }) public narrow = false;
@property({ type: Boolean, attribute: "no-device-option" })
public noDeviceOption = false;
@state() private _shouldRender = false;
@state() private _filter?: string;
@@ -69,11 +74,12 @@ export class HaFilterDevices extends LitElement {
@value-changed=${this._handleSearchChange}
>
</search-input-outlined>
<mwc-list class="ha-scrollbar" multi>
<mwc-list class="ha-scrollbar">
<lit-virtualizer
.items=${this._devices(
this.hass.devices,
this._filter || "",
this.noDeviceOption,
this.value
)}
.keyFunction=${this._keyFunction}
@@ -137,9 +143,13 @@ export class HaFilterDevices extends LitElement {
}
private _devices = memoizeOne(
(devices: HomeAssistant["devices"], filter: string, _value) => {
const values = Object.values(devices);
return values
(
devices: HomeAssistant["devices"],
filter: string,
noDeviceOption: boolean,
_value
) => {
const values = Object.values(devices)
.filter(
(device) =>
!filter ||
@@ -152,6 +162,28 @@ export class HaFilterDevices extends LitElement {
this.hass.locale.language
)
);
if (noDeviceOption) {
values.unshift({
id: FILTER_NO_DEVICE,
name: this.hass.localize("ui.panel.config.devices.no_device"),
area_id: null,
configuration_url: null,
config_entries: [],
connections: [],
disabled_by: null,
entry_type: null,
identifiers: [],
manufacturer: null,
model: null,
name_by_user: null,
sw_version: null,
hw_version: null,
via_device_id: null,
serial_number: null,
labels: [],
});
}
return values;
}
);
@@ -171,7 +203,7 @@ export class HaFilterDevices extends LitElement {
for (const deviceId of this.value) {
value.push(deviceId);
if (this.type) {
if (this.type && deviceId !== FILTER_NO_DEVICE) {
relatedPromises.push(findRelated(this.hass, "device", deviceId));
}
}

View File

@@ -1,198 +0,0 @@
import { mdiFilterVariantRemove } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { stringCompare } from "../common/string/compare";
import { domainToName } from "../data/integration";
import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant } from "../types";
import "./ha-domain-icon";
import "./search-input-outlined";
import { computeDomain } from "../common/entity/compute_domain";
@customElement("ha-filter-domains")
export class HaFilterDomains extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public value?: string[];
@property({ type: Boolean }) public narrow = false;
@property({ type: Boolean, reflect: true }) public expanded = false;
@state() private _shouldRender = false;
@state() private _filter?: string;
protected render() {
return html`
<ha-expansion-panel
leftChevron
.expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged}
>
<div slot="header" class="header">
${this.hass.localize(
"ui.panel.config.entities.picker.headers.domain"
)}
${this.value?.length
? html`<div class="badge">${this.value?.length}</div>
<ha-icon-button
.path=${mdiFilterVariantRemove}
@click=${this._clearFilter}
></ha-icon-button>`
: nothing}
</div>
${this._shouldRender
? html`<search-input-outlined
.hass=${this.hass}
.filter=${this._filter}
@value-changed=${this._handleSearchChange}
>
</search-input-outlined>
<mwc-list
class="ha-scrollbar"
@click=${this._handleItemClick}
multi
>
${repeat(
this._domains(this.hass.states, this._filter),
(i) => i,
(domain) =>
html`<ha-check-list-item
.value=${domain}
.selected=${(this.value || []).includes(domain)}
graphic="icon"
>
<ha-domain-icon
slot="graphic"
.hass=${this.hass}
.domain=${domain}
brandFallback
></ha-domain-icon>
${domainToName(this.hass.localize, domain)}
</ha-check-list-item>`
)}
</mwc-list> `
: nothing}
</ha-expansion-panel>
`;
}
private _domains = memoizeOne((states, filter) => {
const domains = new Set<string>();
Object.keys(states).forEach((entityId) => {
domains.add(computeDomain(entityId));
});
return Array.from(domains)
.filter((domain) => !filter || domain.toLowerCase().includes(filter))
.sort((a, b) => stringCompare(a, b, this.hass.locale.language));
});
protected updated(changed) {
if (changed.has("expanded") && this.expanded) {
setTimeout(() => {
if (!this.expanded) return;
this.renderRoot.querySelector("mwc-list")!.style.height =
`${this.clientHeight - 49 - 32}px`; // 32px is the height of the search input
}, 300);
}
}
private _expandedWillChange(ev) {
this._shouldRender = ev.detail.expanded;
}
private _expandedChanged(ev) {
this.expanded = ev.detail.expanded;
}
private _handleItemClick(ev) {
const listItem = ev.target.closest("ha-check-list-item");
const value = listItem?.value;
if (!value) {
return;
}
if (this.value?.includes(value)) {
this.value = this.value?.filter((val) => val !== value);
} else {
this.value = [...(this.value || []), value];
}
listItem.selected = this.value.includes(value);
fireEvent(this, "data-table-filter-changed", {
value: this.value,
items: undefined,
});
}
private _clearFilter(ev) {
ev.preventDefault();
this.value = undefined;
fireEvent(this, "data-table-filter-changed", {
value: undefined,
items: undefined,
});
}
private _handleSearchChange(ev: CustomEvent) {
this._filter = ev.detail.value.toLowerCase();
}
static get styles(): CSSResultGroup {
return [
haStyleScrollbar,
css`
:host {
border-bottom: 1px solid var(--divider-color);
}
:host([expanded]) {
flex: 1;
height: 0;
}
ha-expansion-panel {
--ha-card-border-radius: 0;
--expansion-panel-content-padding: 0;
}
.header {
display: flex;
align-items: center;
}
.header ha-icon-button {
margin-inline-start: auto;
margin-inline-end: 8px;
}
.badge {
display: inline-block;
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: 0;
min-width: 16px;
box-sizing: border-box;
border-radius: 50%;
font-weight: 400;
font-size: 11px;
background-color: var(--primary-color);
line-height: 16px;
text-align: center;
padding: 0px 2px;
color: var(--text-primary-color);
}
search-input-outlined {
display: block;
padding: 0 8px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-filter-domains": HaFilterDomains;
}
}

View File

@@ -71,7 +71,7 @@ export class HaFilterEntities extends LitElement {
@value-changed=${this._handleSearchChange}
>
</search-input-outlined>
<mwc-list class="ha-scrollbar" multi>
<mwc-list class="ha-scrollbar">
<lit-virtualizer
.items=${this._entities(
this.hass.states,

View File

@@ -55,11 +55,7 @@ export class HaFilterIntegrations extends LitElement {
@value-changed=${this._handleSearchChange}
>
</search-input-outlined>
<mwc-list
class="ha-scrollbar"
@click=${this._handleItemClick}
multi
>
<mwc-list class="ha-scrollbar" @click=${this._handleItemClick}>
${repeat(
this._integrations(this._manifests, this._filter, this.value),
(i) => i.domain,

View File

@@ -71,10 +71,6 @@ export const computeInitialHaFormData = (
if (selector.country?.countries?.length) {
data[field.name] = selector.country.countries[0];
}
} else if ("language" in selector) {
if (selector.language?.languages?.length) {
data[field.name] = selector.language.languages[0];
}
} else if ("duration" in selector) {
data[field.name] = {
hours: 0,
@@ -97,9 +93,7 @@ export const computeInitialHaFormData = (
) {
data[field.name] = {};
} else {
throw new Error(
`Selector ${Object.keys(selector)[0]} not supported in initial form data`
);
throw new Error("Selector not supported in initial form data");
}
}
});

View File

@@ -1,29 +1,13 @@
import { FormfieldBase } from "@material/mwc-formfield/mwc-formfield-base";
import { styles } from "@material/mwc-formfield/mwc-formfield.css";
import { css, html } from "lit";
import { css } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../common/dom/fire_event";
@customElement("ha-formfield")
export class HaFormfield extends FormfieldBase {
@property({ type: Boolean, reflect: true }) public disabled = false;
protected override render() {
const classes = {
"mdc-form-field--align-end": this.alignEnd,
"mdc-form-field--space-between": this.spaceBetween,
"mdc-form-field--nowrap": this.nowrap,
};
return html` <div class="mdc-form-field ${classMap(classes)}">
<slot></slot>
<label class="mdc-label" @click=${this._labelClick}
><slot name="label">${this.label}</slot></label
>
</div>`;
}
protected _labelClick() {
const input = this.input as HTMLInputElement | undefined;
if (!input) return;
@@ -55,9 +39,6 @@ export class HaFormfield extends FormfieldBase {
margin-inline-end: 10px;
margin-inline-start: inline;
}
.mdc-form-field {
align-items: var(--ha-formfield-align-items, center);
}
.mdc-form-field > label {
direction: var(--direction);
margin-inline-start: 0;

View File

@@ -25,7 +25,6 @@ export class HaMenuItem extends MdMenuItem {
--md-sys-color-on-primary-container: var(--primary-text-color);
--md-sys-color-on-secondary-container: var(--primary-text-color);
--md-menu-item-label-text-font: Roboto, sans-serif;
}
:host(.warning) {
--md-menu-item-label-text-color: var(--error-color);

View File

@@ -8,9 +8,6 @@ export class HaSettingsRow extends LitElement {
@property({ type: Boolean, attribute: "three-line" })
public threeLine = false;
@property({ type: Boolean, attribute: "wrap-heading", reflect: true })
public wrapHeading = false;
protected render(): TemplateResult {
return html`
<div class="prefix-wrap">
@@ -54,7 +51,7 @@ export class HaSettingsRow extends LitElement {
.body[three-line] {
min-height: var(--paper-item-body-three-line-min-height, 88px);
}
:host(:not([wrap-heading])) body > * {
.body > * {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

View File

@@ -19,7 +19,7 @@ import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import type { LeafletModuleType } from "../../common/dom/setup-leaflet-map";
import type { HomeAssistant, ThemeMode } from "../../types";
import type { HomeAssistant } from "../../types";
import "../ha-input-helper-text";
import "./ha-map";
import type { HaMap } from "./ha-map";
@@ -61,8 +61,7 @@ export class HaLocationsEditor extends LitElement {
@property({ type: Number }) public zoom = 16;
@property({ attribute: "theme-mode", type: String })
public themeMode: ThemeMode = "auto";
@property({ type: Boolean }) public darkMode = false;
@state() private _locationMarkers?: Record<string, Marker | Circle>;
@@ -134,7 +133,7 @@ export class HaLocationsEditor extends LitElement {
.layers=${this._getLayers(this._circles, this._locationMarkers)}
.zoom=${this.zoom}
.autoFit=${this.autoFit}
.themeMode=${this.themeMode}
?forceDarkMode=${this.darkMode}
></ha-map>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`

View File

@@ -1,32 +1,32 @@
import { isToday } from "date-fns";
import type {
Circle,
CircleMarker,
LatLngExpression,
LatLngTuple,
LatLngExpression,
Layer,
Map,
Marker,
Polyline,
} from "leaflet";
import { CSSResultGroup, PropertyValues, ReactiveElement, css } from "lit";
import { isToday } from "date-fns";
import { css, CSSResultGroup, PropertyValues, ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { formatDateTime } from "../../common/datetime/format_date_time";
import {
formatTimeWeekday,
formatTimeWithSeconds,
} from "../../common/datetime/format_time";
import {
LeafletModuleType,
setupLeafletMap,
} from "../../common/dom/setup-leaflet-map";
import {
formatTimeWithSeconds,
formatTimeWeekday,
} from "../../common/datetime/format_time";
import { formatDateTime } from "../../common/datetime/format_date_time";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill";
import { HomeAssistant, ThemeMode } from "../../types";
import { isTouch } from "../../util/is_touch";
import { HomeAssistant } from "../../types";
import "../ha-icon-button";
import "./ha-entity-marker";
import { isTouch } from "../../util/is_touch";
const getEntityId = (entity: string | HaMapEntity): string =>
typeof entity === "string" ? entity : entity.entity_id;
@@ -69,8 +69,9 @@ export class HaMap extends ReactiveElement {
@property({ type: Boolean }) public fitZones = false;
@property({ attribute: "theme-mode", type: String })
public themeMode: ThemeMode = "auto";
@property({ type: Boolean }) public forceDarkMode = false;
@property({ type: Boolean }) public forceLightMode = false;
@property({ type: Number }) public zoom = 14;
@@ -155,7 +156,8 @@ export class HaMap extends ReactiveElement {
}
if (
!changedProps.has("themeMode") &&
!changedProps.has("forceDarkMode") &&
!changedProps.has("forceLightMode") &&
(!changedProps.has("hass") ||
(oldHass && oldHass.themes?.darkMode === this.hass.themes?.darkMode))
) {
@@ -164,18 +166,14 @@ export class HaMap extends ReactiveElement {
this._updateMapStyle();
}
private get _darkMode() {
return (
this.themeMode === "dark" ||
(this.themeMode === "auto" && Boolean(this.hass.themes.darkMode))
);
}
private _updateMapStyle(): void {
const darkMode =
!this.forceLightMode &&
(this.forceDarkMode || (this.hass.themes.darkMode ?? false));
const map = this.renderRoot.querySelector("#map");
map!.classList.toggle("dark", this._darkMode);
map!.classList.toggle("forced-dark", this.themeMode === "dark");
map!.classList.toggle("forced-light", this.themeMode === "light");
map!.classList.toggle("dark", darkMode);
map!.classList.toggle("forced-dark", this.forceDarkMode);
map!.classList.toggle("forced-light", this.forceLightMode);
}
private async _loadMap(): Promise<void> {
@@ -405,7 +403,13 @@ export class HaMap extends ReactiveElement {
"--dark-primary-color"
);
const className = this._darkMode ? "dark" : "light";
const className = this.forceLightMode
? "light"
: this.forceDarkMode
? "dark"
: this.hass.themes.darkMode
? "dark"
: "light";
for (const entity of this.entities) {
const stateObj = hass.states[getEntityId(entity)];

View File

@@ -11,7 +11,6 @@ import {
HassEntityBase,
} from "home-assistant-js-websocket";
import { HomeAssistant } from "../types";
import { supportsFeature } from "../common/entity/supports-feature";
export const FORMAT_TEXT = "text";
export const FORMAT_NUMBER = "number";
@@ -97,9 +96,3 @@ export const ALARM_MODES: Record<AlarmMode, AlarmConfig> = {
path: mdiShieldOff,
},
};
export const supportedAlarmModes = (stateObj: AlarmControlPanelEntity) =>
(Object.keys(ALARM_MODES) as AlarmMode[]).filter((mode) => {
const feature = ALARM_MODES[mode].feature;
return !feature || supportsFeature(stateObj, feature);
});

View File

@@ -44,7 +44,6 @@ export interface IntegrationManifest {
| "local_polling"
| "local_push";
single_config_entry?: boolean;
version?: string;
}
export interface IntegrationSetup {
domain: string;

View File

@@ -118,10 +118,6 @@ export const checkForEntityUpdates = async (
return;
}
showToast(element, {
message: hass.localize("ui.panel.config.updates.checking_updates"),
});
let updated = 0;
const unsubscribeEvents = await hass.connection.subscribeEvents<HassEvent>(

View File

@@ -52,7 +52,7 @@ export const showConfigFlowDialog = (
? html`
<ha-markdown allowsvg breaks .content=${description}></ha-markdown>
`
: step.reason;
: "";
},
renderShowFormStepHeader(hass, step) {

View File

@@ -34,7 +34,7 @@ export interface FlowConfig {
renderAbortDescription(
hass: HomeAssistant,
step: DataEntryFlowStepAbort
): TemplateResult | string;
): TemplateResult | "";
renderShowFormStepHeader(
hass: HomeAssistant,

View File

@@ -65,7 +65,7 @@ export const showOptionsFlowDialog = (
.content=${description}
></ha-markdown>
`
: step.reason;
: "";
},
renderShowFormStepHeader(hass, step) {

View File

@@ -16,23 +16,21 @@ class MoreInfoCounter extends LitElement {
return nothing;
}
const disabled = isUnavailableState(this.stateObj.state);
const disabled = isUnavailableState(this.stateObj!.state);
return html`
<div class="actions">
<mwc-button
.action=${"increment"}
@click=${this._handleActionClick}
.disabled=${disabled ||
Number(this.stateObj.state) === this.stateObj.attributes.maximum}
.disabled=${disabled}
>
${this.hass!.localize("ui.card.counter.actions.increment")}
</mwc-button>
<mwc-button
.action=${"decrement"}
@click=${this._handleActionClick}
.disabled=${disabled ||
Number(this.stateObj.state) === this.stateObj.attributes.minimum}
.disabled=${disabled}
>
${this.hass!.localize("ui.card.counter.actions.decrement")}
</mwc-button>

View File

@@ -189,7 +189,6 @@ class MoreInfoLawnMower extends LitElement {
.flex-horizontal {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.space-around {
justify-content: space-around;

View File

@@ -19,7 +19,6 @@ import "../../../components/ha-select";
import "../../../components/ha-slider";
import "../../../components/ha-svg-icon";
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
import { isUnavailableState } from "../../../data/entity";
import {
MediaPickedEvent,
MediaPlayerEntity,
@@ -63,8 +62,7 @@ class MoreInfoMediaPlayer extends LitElement {
`
)}
</div>
${!isUnavailableState(stateObj.state) &&
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA)
${supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA)
? html`
<mwc-button
.label=${this.hass.localize(

View File

@@ -142,7 +142,7 @@ class MoreInfoVacuum extends LitElement {
"ui.dialogs.more_info_control.vacuum.commands"
)}
</div>
<div class="flex-horizontal space-around">
<div class="flex-horizontal">
${VACUUM_COMMANDS.filter((item) =>
item.isVisible(stateObj)
).map(
@@ -327,9 +327,6 @@ class MoreInfoVacuum extends LitElement {
flex-direction: row;
justify-content: space-between;
}
.space-around {
justify-content: space-around;
}
`;
}
}

View File

@@ -41,7 +41,7 @@ import type { HomeAssistant } from "../types";
import { onBoardingStyles } from "./styles";
const AMSTERDAM: [number, number] = [52.3731339, 4.8903147];
const darkMql = matchMedia("(prefers-color-scheme: dark)");
const mql = matchMedia("(prefers-color-scheme: dark)");
const LOCATION_MARKER_ID = "location";
@customElement("onboarding-location")
@@ -199,7 +199,7 @@ class OnboardingLocation extends LitElement {
this._highlightedMarker
)}
zoom="14"
.themeMode=${darkMql.matches ? "dark" : "light"}
.darkMode=${mql.matches}
.disabled=${this._working}
@location-updated=${this._locationChanged}
@marker-clicked=${this._markerClicked}

View File

@@ -97,7 +97,7 @@ export class HaManualAutomationEditor extends LitElement {
<ha-automation-trigger
role="region"
aria-labelledby="triggers-heading"
.triggers=${this.config.trigger || []}
.triggers=${this.config.trigger}
.path=${["trigger"]}
@value-changed=${this._triggerChanged}
@item-moved=${this._itemMoved}

View File

@@ -80,7 +80,7 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
)}
>
<div class="account-row">
<ha-list-item noninteractive twoline>
<ha-list-item twoline>
${this.cloudStatus.email.replace(
/(\w{3})[\w.-]+@([\w.]+\w)/,
"$1***@$2"
@@ -118,7 +118,7 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
: ""}
<div class="account-row">
<ha-list-item noninteractive>
<ha-list-item>
${this.hass.localize(
"ui.panel.config.cloud.account.connection_status"
)}:
@@ -187,7 +187,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
<cloud-remote-pref
.hass=${this.hass}
.narrow=${this.narrow}
.cloudStatus=${this.cloudStatus}
></cloud-remote-pref>

View File

@@ -1,13 +1,12 @@
import { mdiContentCopy, mdiEye, mdiEyeOff, mdiHelpCircle } from "@mdi/js";
import { mdiContentCopy, mdiHelpCircle } from "@mdi/js";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-radio";
import "../../../../components/ha-settings-row";
import "../../../../components/ha-switch";
// eslint-disable-next-line
@@ -22,6 +21,7 @@ import {
import type { HomeAssistant } from "../../../../types";
import { showToast } from "../../../../util/toast";
import { showCloudCertificateDialog } from "../dialog-cloud-certificate/show-dialog-cloud-certificate";
import { showAlertDialog } from "../../../lovelace/custom-card-helpers";
@customElement("cloud-remote-pref")
export class CloudRemotePref extends LitElement {
@@ -29,10 +29,6 @@ export class CloudRemotePref extends LitElement {
@property({ attribute: false }) public cloudStatus?: CloudStatusLoggedIn;
@property({ type: Boolean }) public narrow = false;
@state() private _unmaskedUrl = false;
protected render() {
if (!this.cloudStatus) {
return nothing;
@@ -113,70 +109,36 @@ export class CloudRemotePref extends LitElement {
)}
></ha-alert>
`
: strict_connection === "drop_connection"
? html`<ha-alert
alert-type="warning"
.title=${this.hass.localize(
`ui.panel.config.cloud.account.remote.drop_connection_warning_title`
)}
>${this.hass.localize(
`ui.panel.config.cloud.account.remote.drop_connection_warning`
)}</ha-alert
>`
: nothing}
<p>
${this.hass.localize("ui.panel.config.cloud.account.remote.info")}
</p>
${remote_connected
? nothing
: html`
<p>
${this.hass.localize(
"ui.panel.config.cloud.account.remote.info_instance_will_be_available"
)}
</p>
`}
<div class="url-container">
<div class="textfield-container">
<ha-textfield
.value=${this._unmaskedUrl
? `https://${remote_domain}`
: "https://•••••••••••••••••.ui.nabu.casa"}
readonly
.suffix=${
// reserve some space for the icon.
html`<div style="width: 24px"></div>`
}
></ha-textfield>
<ha-icon-button
class="toggle-unmasked-url"
toggles
.label=${this.hass.localize(
`ui.panel.config.cloud.account.remote.${this._unmaskedUrl ? "hide" : "show"}_url`
)}
@click=${this._toggleUnmaskedUrl}
.path=${this._unmaskedUrl ? mdiEyeOff : mdiEye}
></ha-icon-button>
</div>
<ha-button
.url=${`https://${remote_domain}`}
@click=${this._copyURL}
unelevated
>
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.cloud.account.remote.copy_link"
)}
</ha-button>
</div>
: ""}
${this.hass.localize("ui.panel.config.cloud.account.remote.info")}
${this.hass.localize(
`ui.panel.config.cloud.account.remote.${
remote_connected
? "instance_is_available"
: "instance_will_be_available"
}`
)}
<a
href="https://${remote_domain}"
target="_blank"
class="break-word"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.nabu_casa_url"
)}</a
>.
<ha-svg-icon
.url=${`https://${remote_domain}`}
@click=${this._copyURL}
.path=${mdiContentCopy}
></ha-svg-icon>
<ha-expansion-panel
outlined
.header=${this.hass.localize(
"ui.panel.config.cloud.account.remote.security_options"
"ui.panel.config.cloud.account.remote.advanced_options"
)}
>
<ha-settings-row wrap-heading>
<ha-settings-row>
<span slot="heading"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.external_activation"
@@ -192,8 +154,62 @@ export class CloudRemotePref extends LitElement {
@change=${this._toggleAllowRemoteEnabledChanged}
></ha-switch>
</ha-settings-row>
<hr />
<ha-settings-row .narrow=${this.narrow}>
<ha-settings-row>
<span slot="heading"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection"
)}</span
>
<span slot="description"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_secondary"
)}</span
>
<ha-select
.label=${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_mode"
)}
@selected=${this._setStrictConnectionMode}
naturalMenuWidth
.value=${strict_connection}
>
<ha-list-item value="disabled">
${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_modes.disabled"
)}
</ha-list-item>
<ha-list-item value="guard_page">
${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_modes.guard_page"
)}
</ha-list-item>
<ha-list-item value="drop_connection">
${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_modes.drop_connection"
)}
</ha-list-item>
</ha-select>
</ha-settings-row>
${strict_connection !== "disabled"
? html` <ha-settings-row>
<span slot="heading"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_link"
)}</span
>
<span slot="description"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_link_secondary"
)}</span
>
<ha-button @click=${this._createLoginUrl}
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_create_link"
)}</ha-button
>
</ha-settings-row>`
: nothing}
<ha-settings-row>
<span slot="heading"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.certificate_info"
@@ -233,10 +249,6 @@ export class CloudRemotePref extends LitElement {
});
}
private _toggleUnmaskedUrl(): void {
this._unmaskedUrl = !this._unmaskedUrl;
}
private async _toggleChanged(ev) {
const toggle = ev.target as HaSwitch;
@@ -267,6 +279,18 @@ export class CloudRemotePref extends LitElement {
}
}
private async _setStrictConnectionMode(ev) {
const mode = ev.target.value;
try {
await updateCloudPref(this.hass, {
strict_connection: mode,
});
fireEvent(this, "ha-refresh-cloud-status");
} catch (err: any) {
alert(err.message);
}
}
private async _copyURL(ev): Promise<void> {
const url = ev.currentTarget.url;
await copyToClipboard(url);
@@ -275,6 +299,40 @@ export class CloudRemotePref extends LitElement {
});
}
private async _createLoginUrl() {
try {
const result = await this.hass.callService(
"cloud",
"create_temporary_strict_connection_url",
undefined,
undefined,
false,
true
);
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_link"
),
text: html`${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_link_created_message"
)}
<pre>${result.response.url}</pre>
<ha-button
.url=${result.response.url}
@click=${this._copyURL}
unelevated
>
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.cloud.account.remote.strict_connection_copy_link"
)}
</ha-button>`,
});
} catch (err: any) {
showAlertDialog(this, { text: err.message });
}
}
static get styles(): CSSResultGroup {
return css`
.preparing {
@@ -285,8 +343,8 @@ export class CloudRemotePref extends LitElement {
}
.header-actions {
position: absolute;
right: 16px;
inset-inline-end: 16px;
right: 24px;
inset-inline-end: 24px;
inset-inline-start: initial;
top: 24px;
display: flex;
@@ -320,50 +378,16 @@ export class CloudRemotePref extends LitElement {
.card-actions a {
text-decoration: none;
}
ha-expansion-panel {
margin-top: 16px;
ha-svg-icon {
--mdc-icon-size: 18px;
color: var(--secondary-text-color);
cursor: pointer;
}
ha-settings-row {
padding: 0;
border-top: none !important;
}
ha-expansion-panel {
--expansion-panel-content-padding: 0 16px;
--expansion-panel-summary-padding: 0 16px;
}
ha-alert {
display: block;
margin-bottom: 16px;
}
.url-container {
display: flex;
align-items: center;
gap: 8px;
ha-formfield {
margin-top: 8px;
}
.textfield-container {
position: relative;
flex: 1;
}
.textfield-container ha-textfield {
display: block;
}
.toggle-unmasked-url {
position: absolute;
top: 8px;
right: 8px;
inset-inline-start: initial;
inset-inline-end: 8px;
--mdc-icon-button-size: 40px;
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
direction: var(--direction);
}
hr {
border: none;
height: 1px;
background-color: var(--divider-color);
margin: 8px 0;
ha-expansion-panel {
margin-top: 8px;
}
`;
}

View File

@@ -188,7 +188,6 @@ export class CloudTTSPref extends LitElement {
}
.row > * {
flex: 1;
width: 0;
}
.row > *:first-child {
margin-right: 8px;

View File

@@ -1,7 +1,7 @@
import { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical, mdiRefresh } from "@mdi/js";
import { mdiDotsVertical, mdiUpdate } from "@mdi/js";
import { HassEntities } from "home-assistant-js-websocket";
import { LitElement, TemplateResult, css, html } from "lit";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
@@ -14,11 +14,11 @@ import "../../../components/ha-check-list-item";
import "../../../components/ha-metric";
import { extractApiErrorMessage } from "../../../data/hassio/common";
import {
HassioSupervisorInfo,
SupervisorOptions,
fetchHassioSupervisorInfo,
HassioSupervisorInfo,
reloadSupervisor,
setSupervisorOption,
SupervisorOptions,
} from "../../../data/hassio/supervisor";
import {
checkForEntityUpdates,
@@ -66,7 +66,7 @@ class HaConfigSectionUpdates extends LitElement {
.label=${this.hass.localize(
"ui.panel.config.updates.check_updates"
)}
.path=${mdiRefresh}
.path=${mdiUpdate}
@click=${this._checkUpdates}
></ha-icon-button>
<ha-button-menu multi>

View File

@@ -4,7 +4,7 @@ import {
mdiDotsVertical,
mdiMagnify,
mdiPower,
mdiRefresh,
mdiUpdate,
} from "@mdi/js";
import { HassEntities, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
@@ -206,7 +206,7 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
<ha-list-item graphic="icon">
${this.hass.localize("ui.panel.config.updates.check_updates")}
<ha-svg-icon slot="graphic" .path=${mdiRefresh}></ha-svg-icon>
<ha-svg-icon slot="graphic" .path=${mdiUpdate}></ha-svg-icon>
</ha-list-item>
<ha-list-item graphic="icon">

View File

@@ -105,7 +105,7 @@ export class DialogEnergyDeviceSettings
type="text"
.disabled=${!this._device}
.value=${this._device?.name || ""}
@input=${this._nameChanged}
@change=${this._nameChanged}
>
</ha-textfield>

View File

@@ -52,8 +52,6 @@ import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-alert";
import "../../../components/ha-button-menu";
import "../../../components/ha-check-list-item";
import "../../../components/ha-filter-devices";
import "../../../components/ha-filter-domains";
import "../../../components/ha-filter-floor-areas";
import "../../../components/ha-filter-integrations";
import "../../../components/ha-filter-labels";
@@ -98,6 +96,7 @@ import { configSections } from "../ha-panel-config";
import "../integrations/ha-integration-overflow-menu";
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import { FILTER_NO_DEVICE } from "../../../components/ha-filter-devices";
export interface StateEntity
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
@@ -116,9 +115,6 @@ export interface EntityRow extends StateEntity {
localized_platform: string;
domain: string;
label_entries: LabelRegistryEntry[];
enabled: string;
visible: string;
available: string;
}
@customElement("ha-config-entities")
@@ -200,37 +196,21 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
};
private _states = memoize((localize: LocalizeFunc) => [
{
value: "available",
label: localize("ui.panel.config.entities.picker.status.available"),
},
{
value: "unavailable",
label: localize("ui.panel.config.entities.picker.status.unavailable"),
},
{
value: "enabled",
label: localize("ui.panel.config.entities.picker.status.enabled"),
},
{
value: "disabled",
label: localize("ui.panel.config.entities.picker.status.disabled"),
},
{
value: "visible",
label: localize("ui.panel.config.entities.picker.status.visible"),
},
{
value: "hidden",
label: localize("ui.panel.config.entities.picker.status.hidden"),
},
{
value: "readonly",
label: localize("ui.panel.config.entities.picker.status.unmanageable"),
value: "unavailable",
label: localize("ui.panel.config.entities.picker.status.unavailable"),
},
{
value: "restored",
label: localize("ui.panel.config.entities.picker.status.not_provided"),
value: "readonly",
label: localize("ui.panel.config.entities.picker.status.readonly"),
},
]);
@@ -329,6 +309,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
type: "icon",
sortable: true,
filterable: true,
groupable: true,
width: "68px",
template: (entry) =>
entry.unavailable ||
@@ -357,7 +338,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
<simple-tooltip animation-delay="0" position="left">
${entry.restored
? this.hass.localize(
"ui.panel.config.entities.picker.status.not_provided"
"ui.panel.config.entities.picker.status.restored"
)
: entry.unavailable
? this.hass.localize(
@@ -372,31 +353,13 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
"ui.panel.config.entities.picker.status.hidden"
)
: this.hass.localize(
"ui.panel.config.entities.picker.status.unmanageable"
"ui.panel.config.entities.picker.status.readonly"
)}
</simple-tooltip>
</div>
`
: "—",
},
available: {
title: localize("ui.panel.config.entities.picker.headers.availability"),
sortable: true,
groupable: true,
hidden: true,
},
visible: {
title: localize("ui.panel.config.entities.picker.headers.visibility"),
sortable: true,
groupable: true,
hidden: true,
},
enabled: {
title: localize("ui.panel.config.entities.picker.headers.enabled"),
sortable: true,
groupable: true,
hidden: true,
},
labels: {
title: "",
hidden: true,
@@ -425,24 +388,18 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
const stateFilters = filters["ha-filter-states"]?.value;
const showEnabled =
!stateFilters?.length || stateFilters.includes("enabled");
const showDisabled =
!stateFilters?.length || stateFilters.includes("disabled");
const showVisible =
!stateFilters?.length || stateFilters.includes("visible");
const showHidden =
!stateFilters?.length || stateFilters.includes("hidden");
const showAvailable =
!stateFilters?.length || stateFilters.includes("available");
const showUnavailable =
!stateFilters?.length || stateFilters.includes("unavailable");
const showRestored =
!stateFilters?.length || stateFilters.includes("restored");
const showReadOnly =
!stateFilters?.length || stateFilters.includes("readonly");
const showDisabled =
!stateFilters?.length || stateFilters.includes("disabled");
const showHidden =
!stateFilters?.length || stateFilters.includes("hidden");
const showUnavailable =
!stateFilters?.length || stateFilters.includes("unavailable");
let filteredEntities = entities.concat(stateEntities);
let filteredEntities = showReadOnly
? entities.concat(stateEntities)
: entities;
let filteredConfigEntry: ConfigEntry | undefined;
const filteredDomains = new Set<string>();
@@ -486,44 +443,41 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
entryIds.includes(entity.config_entry_id))
);
filter.value!.forEach((domain) => filteredDomains.add(domain));
} else if (key === "ha-filter-domains" && filter.value?.length) {
filteredEntities = filteredEntities.filter((entity) =>
filter.value?.includes(computeDomain(entity.entity_id))
);
} else if (key === "ha-filter-labels" && filter.value?.length) {
filteredEntities = filteredEntities.filter((entity) =>
entity.labels.some((lbl) => filter.value!.includes(lbl))
);
} else if (filter.items) {
filteredEntities = filteredEntities.filter((entity) =>
filter.items!.has(entity.entity_id)
filteredEntities = filteredEntities.filter(
(entity) =>
filter.items!.has(entity.entity_id) ||
(key === "ha-filter-devices" &&
filter.value?.includes(FILTER_NO_DEVICE) &&
!entity.device_id)
);
}
});
if (!showDisabled) {
filteredEntities = filteredEntities.filter(
(entity) => !entity.disabled_by
);
}
if (!showHidden) {
filteredEntities = filteredEntities.filter(
(entity) => !entity.hidden_by
);
}
for (const entry of filteredEntities) {
const entity = this.hass.states[entry.entity_id];
const unavailable = entity?.state === UNAVAILABLE;
const restored = entity?.attributes.restored === true;
const areaId = entry.area_id ?? devices[entry.device_id!]?.area_id;
const area = areaId ? areas[areaId] : undefined;
const hidden = !!entry.hidden_by;
const disabled = !!entry.disabled_by;
const readonly = entry.readonly;
const available = entity?.state && entity.state !== UNAVAILABLE;
if (
!(
(showAvailable && available) ||
(showUnavailable && unavailable) ||
(showRestored && restored) ||
(showVisible && !hidden) ||
(showHidden && hidden) ||
(showDisabled && disabled) ||
(showEnabled && !disabled) ||
(showReadOnly && readonly)
)
) {
if (!showUnavailable && unavailable) {
continue;
}
@@ -545,30 +499,21 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
area: area ? area.name : "—",
domain: domainToName(localize, computeDomain(entry.entity_id)),
status: restored
? localize("ui.panel.config.entities.picker.status.not_provided")
? localize("ui.panel.config.entities.picker.status.restored")
: unavailable
? localize("ui.panel.config.entities.picker.status.unavailable")
: disabled
: entry.disabled_by
? localize("ui.panel.config.entities.picker.status.disabled")
: hidden
: entry.hidden_by
? localize("ui.panel.config.entities.picker.status.hidden")
: readonly
: entry.readonly
? localize(
"ui.panel.config.entities.picker.status.unmanageable"
"ui.panel.config.entities.picker.status.readonly"
)
: localize(
"ui.panel.config.entities.picker.status.available"
),
label_entries: labelsEntries,
available: unavailable
? localize("ui.panel.config.entities.picker.status.unavailable")
: localize("ui.panel.config.entities.picker.status.available"),
enabled: disabled
? localize("ui.panel.config.entities.picker.status.disabled")
: localize("ui.panel.config.entities.picker.status.enabled"),
visible: hidden
? localize("ui.panel.config.entities.picker.status.hidden")
: localize("ui.panel.config.entities.picker.status.visible"),
});
}
@@ -840,16 +785,8 @@ ${
.expanded=${this._expandedFilter === "ha-filter-devices"}
.narrow=${this.narrow}
@expanded-changed=${this._filterExpanded}
no-device-option
></ha-filter-devices>
<ha-filter-domains
.hass=${this.hass}
.value=${this._filters["ha-filter-domains"]?.value}
@data-table-filter-changed=${this._filterChanged}
slot="filter-pane"
.expanded=${this._expandedFilter === "ha-filter-domains"}
.narrow=${this.narrow}
@expanded-changed=${this._filterExpanded}
></ha-filter-domains>
<ha-filter-integrations
.hass=${this.hass}
.value=${this._filters["ha-filter-integrations"]?.value}
@@ -915,7 +852,7 @@ ${
protected firstUpdated() {
this._filters = {
"ha-filter-states": {
value: ["enabled"],
value: ["unavailable", "readonly"],
items: undefined,
},
};
@@ -930,7 +867,10 @@ ${
this._filters = {
...this._filters,
"ha-filter-states": {
value: [],
value: [
...(this._filters["ha-filter-states"]?.value || []),
"disabled",
],
items: undefined,
},
"ha-filter-integrations": {
@@ -943,7 +883,10 @@ ${
this._filters = {
...this._filters,
"ha-filter-states": {
value: [],
value: [
...(this._filters["ha-filter-states"]?.value || []),
"disabled",
],
items: undefined,
},
config_entry: {

View File

@@ -309,7 +309,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
<ha-svg-icon .path=${mdiPencilOff}></ha-svg-icon>
<simple-tooltip animation-delay="0" position="left">
${this.hass.localize(
"ui.panel.config.entities.picker.status.unmanageable"
"ui.panel.config.entities.picker.status.readonly"
)}
</simple-tooltip>
</div>

View File

@@ -269,9 +269,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
@error=${this._onImageError}
/>
</div>
${this._manifest?.version != null
? html`<div class="version">${this._manifest.version}</div>`
: nothing}
${this._manifest?.is_built_in === false
? html`<ha-alert alert-type="warning"
><ha-svg-icon
@@ -557,22 +554,18 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
if (item.error_reason_translation_key) {
const lokalisePromExc = this.hass
.loadBackendTranslation("exceptions", item.domain)
.then(
(localize) =>
localize(
`component.${item.domain}.exceptions.${item.error_reason_translation_key}.message`,
item.error_reason_translation_placeholders ?? undefined
) || item.reason
.then((localize) =>
localize(
`component.${item.domain}.exceptions.${item.error_reason_translation_key}.message`,
item.error_reason_translation_placeholders ?? undefined
)
);
stateTextExtra = html`${until(lokalisePromExc)}`;
} else {
const lokalisePromError = this.hass
.loadBackendTranslation("config", item.domain)
.then(
(localize) =>
localize(
`component.${item.domain}.config.error.${item.reason}`
) || item.reason
.then((localize) =>
localize(`component.${item.domain}.config.error.${item.reason}`)
);
stateTextExtra = html`${until(lokalisePromError, item.reason)}`;
}
@@ -1411,12 +1404,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
display: flex;
justify-content: center;
}
.version {
padding-top: 8px;
display: flex;
justify-content: center;
color: var(--secondary-text-color);
}
.overview .card-actions {
padding: 0;
}

View File

@@ -47,7 +47,6 @@ class DialogZHAReconfigureDevice extends LitElement {
public showDialog(params: ZHAReconfigureDeviceDialogParams): void {
this._params = params;
this._clusterConfigurationStatuses = new Map();
this._stages = undefined;
}

View File

@@ -424,7 +424,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
? {
physics: {
barnesHut: {
springConstant: 0,
springConstant: 0.05,
avoidOverlap: 10,
damping: 0.09,
},

View File

@@ -64,7 +64,7 @@ export const showRepairsFlowDialog = (
.content=${description}
></ha-markdown>
`
: step.reason;
: "";
},
renderShowFormStepHeader(hass, step) {

View File

@@ -122,7 +122,7 @@ export class HaManualScriptEditor extends LitElement {
<ha-automation-action
role="region"
aria-labelledby="sequence-heading"
.actions=${this.config.sequence || []}
.actions=${this.config.sequence}
.path=${["sequence"]}
@value-changed=${this._sequenceChanged}
@item-moved=${this._itemMoved}

View File

@@ -573,9 +573,6 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
flex-grow: 1;
height: 100%;
}
.flex mwc-list {
padding-bottom: 64px;
}
.flex mwc-list,
.flex .empty {
border-left: 1px solid var(--divider-color);

View File

@@ -88,34 +88,38 @@ class PanelEnergy extends LitElement {
collectionKey="energy_dashboard"
>
${this.hass.user?.is_admin
? html` <ha-list-item
slot="overflow-menu"
graphic="icon"
@request-selected=${this._navigateConfig}
>
<ha-svg-icon slot="graphic" .path=${mdiPencil}> </ha-svg-icon>
${this.hass!.localize("ui.panel.energy.configure")}
</ha-list-item>`
? html`
<ha-list-item
slot="overflow-menu"
graphic="icon"
@request-selected=${this._navigateConfig}
>
<ha-svg-icon slot="graphic" .path=${mdiPencil}>
</ha-svg-icon>
${this.hass!.localize("ui.panel.energy.configure")}
</ha-list-item>
<ha-list-item
slot="overflow-menu"
graphic="icon"
@request-selected=${this._dumpCSV}
>
<ha-svg-icon slot="graphic" .path=${mdiDownload}>
</ha-svg-icon>
${this.hass!.localize("ui.panel.energy.download_data")}
</ha-list-item>
`
: nothing}
<ha-list-item
slot="overflow-menu"
graphic="icon"
@request-selected=${this._dumpCSV}
>
<ha-svg-icon slot="graphic" .path=${mdiDownload}> </ha-svg-icon>
${this.hass!.localize("ui.panel.energy.download_data")}
</ha-list-item>
</hui-energy-period-selector>
</div>
</div>
<div id="view" @reload-energy-panel=${this._reloadView}>
<hui-view
.hass=${this.hass}
.narrow=${this.narrow}
.lovelace=${this._lovelace}
.index=${this._viewIndex}
></hui-view>
</div>
<hui-view
id="view"
.hass=${this.hass}
.narrow=${this.narrow}
.lovelace=${this._lovelace}
.index=${this._viewIndex}
@reload-energy-panel=${this._reloadView}
></hui-view>
`;
}
@@ -397,10 +401,12 @@ class PanelEnergy extends LitElement {
min-height: 100vh;
box-sizing: border-box;
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
padding-inline-start: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
padding-inline-end: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
}
hui-view {
background: var(
--lovelace-background,
var(--primary-background-color)

View File

@@ -1,7 +0,0 @@
export const filterModes = (
supportedModes: string[] | undefined,
selectedModes: string[] | undefined
): string[] =>
selectedModes
? selectedModes.filter((mode) => (supportedModes || []).includes(mode))
: supportedModes || [];

View File

@@ -13,17 +13,15 @@ import "../../../components/ha-control-select";
import type { ControlSelectOption } from "../../../components/ha-control-select";
import "../../../components/ha-control-slider";
import {
ALARM_MODES,
AlarmControlPanelEntity,
AlarmMode,
supportedAlarmModes,
ALARM_MODES,
} from "../../../data/alarm_control_panel";
import { UNAVAILABLE } from "../../../data/entity";
import { showEnterCodeDialog } from "../../../dialogs/enter-code/show-enter-code-dialog";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { filterModes } from "./common/filter-modes";
import { AlarmModesCardFeatureConfig } from "./types";
import { showEnterCodeDialog } from "../../../dialogs/enter-code/show-enter-code-dialog";
export const supportsAlarmModesCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -43,9 +41,15 @@ class HuiAlarmModeCardFeature
@state() _currentMode?: AlarmMode;
static getStubConfig(): AlarmModesCardFeatureConfig {
static getStubConfig(_, stateObj?: HassEntity): AlarmModesCardFeatureConfig {
return {
type: "alarm-modes",
modes: stateObj
? (Object.keys(ALARM_MODES) as AlarmMode[]).filter((mode) => {
const feature = ALARM_MODES[mode as AlarmMode].feature;
return !feature || supportsFeature(stateObj, feature);
})
: [],
};
}
@@ -160,12 +164,9 @@ class HuiAlarmModeCardFeature
const color = stateColorCss(this.stateObj);
const supportedModes = supportedAlarmModes(this.stateObj);
const modes = this._modes(this.stateObj, this._config.modes);
const options = filterModes(
supportedModes,
this._config.modes
).map<ControlSelectOption>((mode) => ({
const options = modes.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.localize(`ui.card.alarm_control_panel.modes.${mode}`),
path: ALARM_MODES[mode].path,
@@ -195,7 +196,7 @@ class HuiAlarmModeCardFeature
)}
style=${styleMap({
"--control-select-color": color,
"--modes-count": options.length.toString(),
"--modes-count": modes.length.toString(),
})}
.disabled=${this.stateObj!.state === UNAVAILABLE}
>

View File

@@ -15,7 +15,6 @@ import { UNAVAILABLE } from "../../../data/entity";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { ClimateFanModesCardFeatureConfig } from "./types";
import { filterModes } from "./common/filter-modes";
export const supportsClimateFanModesCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -41,10 +40,14 @@ class HuiClimateFanModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(): ClimateFanModesCardFeatureConfig {
static getStubConfig(
_,
stateObj?: HassEntity
): ClimateFanModesCardFeatureConfig {
return {
type: "climate-fan-modes",
style: "dropdown",
fan_modes: stateObj?.attributes.fan_modes || [],
};
}
@@ -119,24 +122,25 @@ class HuiClimateFanModesCardFeature
const stateObj = this.stateObj;
const options = filterModes(
stateObj.attributes.fan_modes,
this._config!.fan_modes
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"fan_mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="fan_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
const modes = stateObj.attributes.fan_modes || [];
const options = modes
.filter((mode) => (this._config!.fan_modes || []).includes(mode))
.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"fan_mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="fan_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -19,7 +19,6 @@ import {
import { UNAVAILABLE } from "../../../data/entity";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { filterModes } from "./common/filter-modes";
import { ClimateHvacModesCardFeatureConfig } from "./types";
export const supportsClimateHvacModesCardFeature = (stateObj: HassEntity) => {
@@ -43,9 +42,13 @@ class HuiClimateHvacModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(): ClimateHvacModesCardFeatureConfig {
static getStubConfig(
_,
stateObj?: HassEntity
): ClimateHvacModesCardFeatureConfig {
return {
type: "climate-hvac-modes",
hvac_modes: stateObj?.attributes.hvac_modes || [],
};
}
@@ -119,23 +122,21 @@ class HuiClimateHvacModesCardFeature
const color = stateColorCss(this.stateObj);
const ordererHvacModes = (this.stateObj.attributes.hvac_modes || [])
.concat()
.sort(compareClimateHvacModes);
const modes = this._config.hvac_modes || [];
const options = filterModes(
ordererHvacModes,
this._config.hvac_modes
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityState(this.stateObj!, mode),
icon: html`
<ha-svg-icon
slot="graphic"
.path=${climateHvacModeIcon(mode)}
></ha-svg-icon>
`,
}));
const options = modes
.filter((mode) => this.stateObj?.attributes.hvac_modes.includes(mode))
.sort(compareClimateHvacModes)
.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityState(this.stateObj!, mode),
icon: html`
<ha-svg-icon
slot="graphic"
.path=${climateHvacModeIcon(mode)}
></ha-svg-icon>
`,
}));
if (this._config.style === "dropdown") {
return html`

View File

@@ -15,7 +15,6 @@ import { UNAVAILABLE } from "../../../data/entity";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { ClimatePresetModesCardFeatureConfig } from "./types";
import { filterModes } from "./common/filter-modes";
export const supportsClimatePresetModesCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -41,10 +40,14 @@ class HuiClimatePresetModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(): ClimatePresetModesCardFeatureConfig {
static getStubConfig(
_,
stateObj?: HassEntity
): ClimatePresetModesCardFeatureConfig {
return {
type: "climate-preset-modes",
style: "dropdown",
preset_modes: stateObj?.attributes.preset_modes || [],
};
}
@@ -121,24 +124,25 @@ class HuiClimatePresetModesCardFeature
const stateObj = this.stateObj;
const options = filterModes(
stateObj.attributes.preset_modes,
this._config!.preset_modes
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"preset_mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="preset_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
const modes = stateObj.attributes.preset_modes || [];
const options = modes
.filter((mode) => (this._config!.preset_modes || []).includes(mode))
.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"preset_mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="preset_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -15,7 +15,6 @@ import { UNAVAILABLE } from "../../../data/entity";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { ClimateSwingModesCardFeatureConfig } from "./types";
import { filterModes } from "./common/filter-modes";
export const supportsClimateSwingModesCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -41,10 +40,14 @@ class HuiClimateSwingModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(): ClimateSwingModesCardFeatureConfig {
static getStubConfig(
_,
stateObj?: HassEntity
): ClimateSwingModesCardFeatureConfig {
return {
type: "climate-swing-modes",
style: "dropdown",
swing_modes: stateObj?.attributes.swing_modes || [],
};
}
@@ -121,24 +124,25 @@ class HuiClimateSwingModesCardFeature
const stateObj = this.stateObj;
const options = filterModes(
stateObj.attributes.swing_modes,
this._config!.swing_modes
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"swing_mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="swing_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
const modes = stateObj.attributes.swing_modes || [];
const options = modes
.filter((mode) => (this._config!.swing_modes || []).includes(mode))
.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"swing_mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="swing_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -15,7 +15,6 @@ import { UNAVAILABLE } from "../../../data/entity";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { FanPresetModesCardFeatureConfig } from "./types";
import { filterModes } from "./common/filter-modes";
export const supportsFanPresetModesCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -40,10 +39,14 @@ class HuiFanPresetModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(): FanPresetModesCardFeatureConfig {
static getStubConfig(
_,
stateObj?: HassEntity
): FanPresetModesCardFeatureConfig {
return {
type: "fan-preset-modes",
style: "dropdown",
preset_modes: stateObj?.attributes.preset_modes || [],
};
}
@@ -118,24 +121,25 @@ class HuiFanPresetModesCardFeature
const stateObj = this.stateObj;
const options = filterModes(
stateObj.attributes.preset_modes,
this._config!.preset_modes
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"preset_mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="preset_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
const modes = stateObj.attributes.preset_modes || [];
const options = modes
.filter((mode) => (this._config!.preset_modes || []).includes(mode))
.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"preset_mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="preset_mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -18,7 +18,6 @@ import {
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { HumidifierModesCardFeatureConfig } from "./types";
import { filterModes } from "./common/filter-modes";
export const supportsHumidifierModesCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -44,10 +43,14 @@ class HuiHumidifierModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(): HumidifierModesCardFeatureConfig {
static getStubConfig(
_,
stateObj?: HassEntity
): HumidifierModesCardFeatureConfig {
return {
type: "humidifier-modes",
style: "dropdown",
modes: stateObj?.attributes.available_modes || [],
};
}
@@ -122,24 +125,25 @@ class HuiHumidifierModesCardFeature
const stateObj = this.stateObj;
const options = filterModes(
stateObj.attributes.available_modes,
this._config!.modes
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
const modes = stateObj.attributes.available_modes || [];
const options = modes
.filter((mode) => (this._config!.modes || []).includes(mode))
.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityAttributeValue(
this.stateObj!,
"mode",
mode
),
icon: html`<ha-attribute-icon
slot="graphic"
.hass=${this.hass}
.stateObj=${stateObj}
attribute="mode"
.attributeValue=${mode}
></ha-attribute-icon>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -1,4 +1,4 @@
import { mdiLock, mdiLockOpenVariant } from "@mdi/js";
import { mdiLock, mdiLockOpen } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
@@ -90,7 +90,7 @@ class HuiLockCommandsCardFeature
pulse: isLocking(this.stateObj) || isUnlocking(this.stateObj),
})}
>
<ha-svg-icon .path=${mdiLockOpenVariant}></ha-svg-icon>
<ha-svg-icon .path=${mdiLockOpen}></ha-svg-icon>
</ha-control-button>
</ha-control-button-group>
`;

View File

@@ -9,9 +9,8 @@ import { UNAVAILABLE } from "../../../data/entity";
import { InputSelectEntity } from "../../../data/input_select";
import { SelectEntity } from "../../../data/select";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { LovelaceCardFeature } from "../types";
import { SelectOptionsCardFeatureConfig } from "./types";
import { filterModes } from "./common/filter-modes";
export const supportsSelectOptionsCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -42,13 +41,6 @@ class HuiSelectOptionsCardFeature
};
}
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
await import(
"../editor/config-elements/hui-select-options-card-feature-editor"
);
return document.createElement("hui-select-options-card-feature-editor");
}
public setConfig(config: SelectOptionsCardFeatureConfig): void {
if (!config) {
throw new Error("Invalid configuration");
@@ -113,11 +105,6 @@ class HuiSelectOptionsCardFeature
const stateObj = this.stateObj;
const options = filterModes(
this.stateObj.attributes.options,
this._config.options
);
return html`
<div class="container">
<ha-control-select-menu
@@ -131,7 +118,7 @@ class HuiSelectOptionsCardFeature
@selected=${this._valueChanged}
@closed=${stopPropagation}
>
${options.map(
${stateObj.attributes.options!.map(
(option) => html`
<ha-list-item .value=${option}>
${this.hass!.formatEntityState(stateObj, option)}

View File

@@ -19,7 +19,6 @@ import {
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { WaterHeaterOperationModesCardFeatureConfig } from "./types";
import { filterModes } from "./common/filter-modes";
export const supportsWaterHeaterOperationModesCardFeature = (
stateObj: HassEntity
@@ -41,9 +40,13 @@ class HuiWaterHeaterOperationModeCardFeature
@state() _currentOperationMode?: OperationMode;
static getStubConfig(): WaterHeaterOperationModesCardFeatureConfig {
static getStubConfig(
_,
stateObj?: HassEntity
): WaterHeaterOperationModesCardFeatureConfig {
return {
type: "water-heater-operation-modes",
operation_modes: stateObj?.attributes.operation_list || [],
};
}
@@ -104,18 +107,16 @@ class HuiWaterHeaterOperationModeCardFeature
const color = stateColorCss(this.stateObj);
const orderedModes = (this.stateObj.attributes.operation_list || [])
.concat()
.sort(compareWaterHeaterOperationMode);
const modes = this._config.operation_modes || [];
const options = filterModes(
orderedModes,
this._config.operation_modes
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityState(this.stateObj!, mode),
path: computeOperationModeIcon(mode as OperationMode),
}));
const options = modes
.filter((mode) => this.stateObj?.attributes.operation_list.includes(mode))
.sort(compareWaterHeaterOperationMode)
.map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityState(this.stateObj!, mode),
path: computeOperationModeIcon(mode),
}));
return html`
<div class="container">

View File

@@ -75,7 +75,6 @@ export interface ClimatePresetModesCardFeatureConfig {
export interface SelectOptionsCardFeatureConfig {
type: "select-options";
options?: string[];
}
export interface NumericInputCardFeatureConfig {

View File

@@ -159,9 +159,6 @@ class HuiMapCard extends LitElement implements LovelaceCard {
? false
: this.hass.themes.darkMode;
const themeMode =
this._config.theme_mode || (this._config.dark_mode ? "dark" : "auto");
return html`
<ha-card id="card" .header=${this._config.title}>
<div id="root">
@@ -172,7 +169,9 @@ class HuiMapCard extends LitElement implements LovelaceCard {
.paths=${this._getHistoryPaths(this._config, this._stateHistory)}
.autoFit=${this._config.auto_fit || false}
.fitZones=${this._config.fit_zones}
.themeMode=${themeMode}
?forceDarkMode=${this._config.theme_mode === "dark" ||
this._config.dark_mode}
?forceLightMode=${this._config.theme_mode === "light"}
interactiveZones
renderPassive
></ha-map>

View File

@@ -3,23 +3,17 @@ import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { supportsFeature } from "../../../../common/entity/supports-feature";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import { supportedAlarmModes } from "../../../../data/alarm_control_panel";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import { AlarmMode, ALARM_MODES } from "../../../../data/alarm_control_panel";
import type { HomeAssistant } from "../../../../types";
import {
AlarmModesCardFeatureConfig,
LovelaceCardFeatureContext,
AlarmModesCardFeatureConfig,
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
type AlarmModesCardFeatureData = AlarmModesCardFeatureConfig & {
customize_modes: boolean;
};
import "../../../../components/ha-form/ha-form";
@customElement("hui-alarm-modes-card-feature-editor")
export class HuiAlarmModesCardFeatureEditor
@@ -37,40 +31,31 @@ export class HuiAlarmModesCardFeatureEditor
}
private _schema = memoizeOne(
(
localize: LocalizeFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
(localize: LocalizeFunc, stateObj?: HassEntity) =>
[
{
name: "customize_modes",
name: "modes",
selector: {
boolean: {},
select: {
multiple: true,
mode: "list",
options: Object.keys(ALARM_MODES)
.filter((mode) => {
const feature = ALARM_MODES[mode as AlarmMode].feature;
return (
stateObj && (!feature || supportsFeature(stateObj, feature))
);
})
.map((mode) => ({
value: mode,
label: `${localize(
`ui.panel.lovelace.editor.features.types.alarm-modes.modes_list.${mode}`
)}`,
})),
},
},
},
...(customizeModes
? ([
{
name: "modes",
selector: {
select: {
multiple: true,
reorder: true,
options: stateObj
? supportedAlarmModes(stateObj).map((mode) => ({
value: mode,
label: `${localize(
`ui.panel.lovelace.editor.features.types.alarm-modes.modes_list.${mode}`
)}`,
}))
: [],
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
] as const
);
protected render() {
@@ -78,25 +63,16 @@ export class HuiAlarmModesCardFeatureEditor
return nothing;
}
const data: AlarmModesCardFeatureData = {
...this._config,
customize_modes: this._config.modes !== undefined,
};
const stateObj = this.context?.entity_id
? this.hass.states[this.context?.entity_id]
: undefined;
const schema = this._schema(
this.hass.localize,
stateObj,
data.customize_modes
);
const schema = this._schema(this.hass.localize, stateObj);
return html`
<ha-form
.hass=${this.hass}
.data=${data}
.data=${this._config}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
@@ -105,21 +81,7 @@ export class HuiAlarmModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
const { customize_modes, ...config } = ev.detail
.value as AlarmModesCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_modes && !config.modes) {
config.modes = stateObj ? supportedAlarmModes(stateObj) : [];
}
if (!customize_modes && config.modes) {
delete config.modes;
}
fireEvent(this, "config-changed", { config: config });
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
@@ -127,12 +89,13 @@ export class HuiAlarmModesCardFeatureEditor
) => {
switch (schema.name) {
case "modes":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.alarm-modes.${schema.name}`
);
default:
return "";
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
}
};
}

View File

@@ -21,9 +21,9 @@ import {
import { HomeAssistant } from "../../../../types";
import { supportsAlarmModesCardFeature } from "../../card-features/hui-alarm-modes-card-feature";
import { supportsClimateFanModesCardFeature } from "../../card-features/hui-climate-fan-modes-card-feature";
import { supportsClimateSwingModesCardFeature } from "../../card-features/hui-climate-swing-modes-card-feature";
import { supportsClimateHvacModesCardFeature } from "../../card-features/hui-climate-hvac-modes-card-feature";
import { supportsClimatePresetModesCardFeature } from "../../card-features/hui-climate-preset-modes-card-feature";
import { supportsClimateSwingModesCardFeature } from "../../card-features/hui-climate-swing-modes-card-feature";
import { supportsCoverOpenCloseCardFeature } from "../../card-features/hui-cover-open-close-card-feature";
import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature";
import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature";
@@ -53,13 +53,13 @@ type SupportsFeature = (stateObj: HassEntity) => boolean;
const UI_FEATURE_TYPES = [
"alarm-modes",
"climate-fan-modes",
"climate-swing-modes",
"climate-hvac-modes",
"climate-preset-modes",
"climate-swing-modes",
"cover-open-close",
"cover-position",
"cover-tilt-position",
"cover-tilt",
"cover-tilt-position",
"fan-preset-modes",
"fan-speed",
"humidifier-modes",
@@ -82,15 +82,14 @@ type UiFeatureTypes = (typeof UI_FEATURE_TYPES)[number];
const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
"alarm-modes",
"climate-fan-modes",
"climate-hvac-modes",
"climate-preset-modes",
"climate-fan-modes",
"climate-swing-modes",
"climate-preset-modes",
"fan-preset-modes",
"humidifier-modes",
"lawn-mower-commands",
"numeric-input",
"select-options",
"update-actions",
"vacuum-commands",
"water-heater-operation-modes",

View File

@@ -17,10 +17,6 @@ import {
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
type ClimateFanModesCardFeatureData = ClimateFanModesCardFeatureConfig & {
customize_modes: boolean;
};
@customElement("hui-climate-fan-modes-card-feature-editor")
export class HuiClimateFanModesCardFeatureEditor
extends LitElement
@@ -40,8 +36,7 @@ export class HuiClimateFanModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
stateObj?: HassEntity
) =>
[
{
@@ -60,33 +55,19 @@ export class HuiClimateFanModesCardFeatureEditor
},
},
{
name: "customize_modes",
name: "fan_modes",
selector: {
boolean: {},
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.fan_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(stateObj, "fan_mode", mode),
})) || [],
},
},
},
...(customizeModes
? ([
{
name: "fan_modes",
selector: {
select: {
multiple: true,
reorder: true,
options:
stateObj?.attributes.fan_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"fan_mode",
mode
),
})) || [],
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
);
@@ -99,17 +80,16 @@ export class HuiClimateFanModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: ClimateFanModesCardFeatureData = {
const data: ClimateFanModesCardFeatureConfig = {
style: "dropdown",
fan_modes: [],
...this._config,
customize_modes: this._config.fan_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj,
data.customize_modes
stateObj
);
return html`
@@ -124,21 +104,7 @@ export class HuiClimateFanModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
const { customize_modes, ...config } = ev.detail
.value as ClimateFanModesCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_modes && !config.fan_modes) {
config.fan_modes = stateObj?.attributes.fan_modes || [];
}
if (!customize_modes && config.fan_modes) {
delete config.fan_modes;
}
fireEvent(this, "config-changed", { config: config });
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
@@ -147,7 +113,6 @@ export class HuiClimateFanModesCardFeatureEditor
switch (schema.name) {
case "style":
case "fan_modes":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.climate-fan-modes.${schema.name}`
);

View File

@@ -6,11 +6,8 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import type { FormatEntityStateFunc } from "../../../../common/translations/entity-state";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import { compareClimateHvacModes } from "../../../../data/climate";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import { HVAC_MODES } from "../../../../data/climate";
import type { HomeAssistant } from "../../../../types";
import {
ClimateHvacModesCardFeatureConfig,
@@ -18,10 +15,6 @@ import {
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
type ClimateHvacModesCardFeatureData = ClimateHvacModesCardFeatureConfig & {
customize_modes: boolean;
};
@customElement("hui-climate-hvac-modes-card-feature-editor")
export class HuiClimateHvacModesCardFeatureEditor
extends LitElement
@@ -41,8 +34,7 @@ export class HuiClimateHvacModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityState: FormatEntityStateFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
stateObj?: HassEntity
) =>
[
{
@@ -61,34 +53,21 @@ export class HuiClimateHvacModesCardFeatureEditor
},
},
{
name: "customize_modes",
name: "hvac_modes",
selector: {
boolean: {},
select: {
multiple: true,
mode: "list",
options: HVAC_MODES.filter((mode) =>
stateObj?.attributes.hvac_modes?.includes(mode)
).map((mode) => ({
value: mode,
label: stateObj ? formatEntityState(stateObj, mode) : mode,
})),
},
},
},
...(customizeModes
? ([
{
name: "hvac_modes",
selector: {
select: {
reorder: true,
multiple: true,
options: (stateObj?.attributes.hvac_modes || [])
.concat()
.sort(compareClimateHvacModes)
.map((mode) => ({
value: mode,
label: stateObj
? formatEntityState(stateObj, mode)
: mode,
})),
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
] as const
);
protected render() {
@@ -100,17 +79,16 @@ export class HuiClimateHvacModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: ClimateHvacModesCardFeatureData = {
const data: ClimateHvacModesCardFeatureConfig = {
style: "icons",
hvac_modes: [],
...this._config,
customize_modes: this._config.hvac_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityState,
stateObj,
data.customize_modes
stateObj
);
return html`
@@ -125,24 +103,7 @@ export class HuiClimateHvacModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
const { customize_modes, ...config } = ev.detail
.value as ClimateHvacModesCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_modes && !config.hvac_modes) {
const ordererHvacModes = (stateObj?.attributes.hvac_modes || [])
.concat()
.sort(compareClimateHvacModes);
config.hvac_modes = ordererHvacModes;
}
if (!customize_modes && config.hvac_modes) {
delete config.hvac_modes;
}
fireEvent(this, "config-changed", { config: config });
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
@@ -151,7 +112,6 @@ export class HuiClimateHvacModesCardFeatureEditor
switch (schema.name) {
case "hvac_modes":
case "style":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.climate-hvac-modes.${schema.name}`
);

View File

@@ -17,10 +17,6 @@ import {
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
type ClimatePresetModesCardFeatureData = ClimatePresetModesCardFeatureConfig & {
customize_modes: boolean;
};
@customElement("hui-climate-preset-modes-card-feature-editor")
export class HuiClimatePresetModesCardFeatureEditor
extends LitElement
@@ -40,8 +36,7 @@ export class HuiClimatePresetModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
stateObj?: HassEntity
) =>
[
{
@@ -60,33 +55,23 @@ export class HuiClimatePresetModesCardFeatureEditor
},
},
{
name: "customize_modes",
name: "preset_modes",
selector: {
boolean: {},
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.preset_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"preset_mode",
mode
),
})) || [],
},
},
},
...(customizeModes
? ([
{
name: "preset_modes",
selector: {
select: {
reorder: true,
multiple: true,
options:
stateObj?.attributes.preset_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"preset_mode",
mode
),
})) || [],
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
);
@@ -99,17 +84,16 @@ export class HuiClimatePresetModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: ClimatePresetModesCardFeatureData = {
const data: ClimatePresetModesCardFeatureConfig = {
style: "dropdown",
preset_modes: [],
...this._config,
customize_modes: this._config.preset_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj,
data.customize_modes
stateObj
);
return html`
@@ -124,21 +108,7 @@ export class HuiClimatePresetModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
const { customize_modes, ...config } = ev.detail
.value as ClimatePresetModesCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_modes && !config.preset_modes) {
config.preset_modes = stateObj?.attributes.preset_modes || [];
}
if (!customize_modes && config.preset_modes) {
delete config.preset_modes;
}
fireEvent(this, "config-changed", { config: config });
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
@@ -147,7 +117,6 @@ export class HuiClimatePresetModesCardFeatureEditor
switch (schema.name) {
case "style":
case "preset_modes":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.climate-preset-modes.${schema.name}`
);

View File

@@ -17,10 +17,6 @@ import {
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
type ClimateSwingModesCardFeatureData = ClimateSwingModesCardFeatureConfig & {
customize_modes: boolean;
};
@customElement("hui-climate-swing-modes-card-feature-editor")
export class HuiClimateSwingModesCardFeatureEditor
extends LitElement
@@ -40,8 +36,7 @@ export class HuiClimateSwingModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
stateObj?: HassEntity
) =>
[
{
@@ -60,33 +55,23 @@ export class HuiClimateSwingModesCardFeatureEditor
},
},
{
name: "customize_modes",
name: "swing_modes",
selector: {
boolean: {},
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.swing_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"swing_mode",
mode
),
})) || [],
},
},
},
...(customizeModes
? ([
{
name: "swing_modes",
selector: {
select: {
reorder: true,
multiple: true,
options:
stateObj?.attributes.swing_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"swing_mode",
mode
),
})) || [],
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
);
@@ -99,17 +84,16 @@ export class HuiClimateSwingModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: ClimateSwingModesCardFeatureData = {
const data: ClimateSwingModesCardFeatureConfig = {
style: "dropdown",
swing_modes: [],
...this._config,
customize_modes: this._config.swing_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj,
data.customize_modes
stateObj
);
return html`
@@ -124,21 +108,7 @@ export class HuiClimateSwingModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
const { customize_modes, ...config } = ev.detail
.value as ClimateSwingModesCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_modes && !config.swing_modes) {
config.swing_modes = stateObj?.attributes.swing_modes || [];
}
if (!customize_modes && config.swing_modes) {
delete config.swing_modes;
}
fireEvent(this, "config-changed", { config: config });
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
@@ -147,7 +117,6 @@ export class HuiClimateSwingModesCardFeatureEditor
switch (schema.name) {
case "style":
case "swing_modes":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.climate-swing-modes.${schema.name}`
);

View File

@@ -17,10 +17,6 @@ import {
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
type FanPresetModesCardFeatureData = FanPresetModesCardFeatureConfig & {
customize_modes: boolean;
};
@customElement("hui-fan-preset-modes-card-feature-editor")
export class HuiFanPresetModesCardFeatureEditor
extends LitElement
@@ -40,8 +36,7 @@ export class HuiFanPresetModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
stateObj?: HassEntity
) =>
[
{
@@ -60,33 +55,23 @@ export class HuiFanPresetModesCardFeatureEditor
},
},
{
name: "customize_modes",
name: "preset_modes",
selector: {
boolean: {},
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.preset_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"preset_mode",
mode
),
})) || [],
},
},
},
...(customizeModes
? ([
{
name: "preset_modes",
selector: {
select: {
reorder: true,
multiple: true,
options:
stateObj?.attributes.preset_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"preset_mode",
mode
),
})) || [],
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
);
@@ -99,17 +84,16 @@ export class HuiFanPresetModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: FanPresetModesCardFeatureData = {
const data: FanPresetModesCardFeatureConfig = {
style: "dropdown",
preset_modes: [],
...this._config,
customize_modes: this._config.preset_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj,
data.customize_modes
stateObj
);
return html`
@@ -124,21 +108,7 @@ export class HuiFanPresetModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
const { customize_modes, ...config } = ev.detail
.value as FanPresetModesCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_modes && !config.preset_modes) {
config.preset_modes = stateObj?.attributes.preset_modes || [];
}
if (!customize_modes && config.preset_modes) {
delete config.preset_modes;
}
fireEvent(this, "config-changed", { config: config });
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
@@ -147,7 +117,6 @@ export class HuiFanPresetModesCardFeatureEditor
switch (schema.name) {
case "style":
case "preset_modes":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.fan-preset-modes.${schema.name}`
);

View File

@@ -17,10 +17,6 @@ import {
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
type HumidifierModesCardFeatureData = HumidifierModesCardFeatureConfig & {
customize_modes: boolean;
};
@customElement("hui-humidifier-modes-card-feature-editor")
export class HuiHumidifierModesCardFeatureEditor
extends LitElement
@@ -40,8 +36,7 @@ export class HuiHumidifierModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
stateObj?: HassEntity
) =>
[
{
@@ -60,33 +55,19 @@ export class HuiHumidifierModesCardFeatureEditor
},
},
{
name: "customize_modes",
name: "modes",
selector: {
boolean: {},
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.available_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(stateObj, "mode", mode),
})) || [],
},
},
},
...(customizeModes
? ([
{
name: "modes",
selector: {
select: {
reorder: true,
multiple: true,
options:
stateObj?.attributes.available_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"mode",
mode
),
})) || [],
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
);
@@ -99,17 +80,16 @@ export class HuiHumidifierModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: HumidifierModesCardFeatureData = {
const data: HumidifierModesCardFeatureConfig = {
style: "dropdown",
modes: [],
...this._config,
customize_modes: this._config.modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj,
data.customize_modes
stateObj
);
return html`
@@ -124,21 +104,7 @@ export class HuiHumidifierModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
const { customize_modes, ...config } = ev.detail
.value as HumidifierModesCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_modes && !config.modes) {
config.modes = stateObj?.attributes.available_modes || [];
}
if (!customize_modes && config.modes) {
delete config.modes;
}
fireEvent(this, "config-changed", { config: config });
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
@@ -147,7 +113,6 @@ export class HuiHumidifierModesCardFeatureEditor
switch (schema.name) {
case "style":
case "modes":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.humidifier-modes.${schema.name}`
);

View File

@@ -1,140 +0,0 @@
import { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { FormatEntityStateFunc } from "../../../../common/translations/entity-state";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import {
LovelaceCardFeatureContext,
SelectOptionsCardFeatureConfig,
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
type SelectOptionsCardFeatureData = SelectOptionsCardFeatureConfig & {
customize_options: boolean;
};
@customElement("hui-select-options-card-feature-editor")
export class HuiSelectOptionsCardFeatureEditor
extends LitElement
implements LovelaceCardFeatureEditor
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
@state() private _config?: SelectOptionsCardFeatureConfig;
public setConfig(config: SelectOptionsCardFeatureConfig): void {
this._config = config;
}
private _schema = memoizeOne(
(
formatEntityState: FormatEntityStateFunc,
stateObj: HassEntity | undefined,
customizeOptions: boolean
) =>
[
{
name: "customize_options",
selector: {
boolean: {},
},
},
...(customizeOptions
? ([
{
name: "options",
selector: {
select: {
multiple: true,
reorder: true,
options:
stateObj?.attributes.options?.map((option) => ({
value: option,
label: formatEntityState(stateObj, option),
})) || [],
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
);
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
const stateObj = this.context?.entity_id
? this.hass.states[this.context?.entity_id]
: undefined;
const data: SelectOptionsCardFeatureData = {
...this._config,
customize_options: this._config.options !== undefined,
};
const schema = this._schema(
this.hass.formatEntityState,
stateObj,
data.customize_options
);
return html`
<ha-form
.hass=${this.hass}
.data=${data}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
`;
}
private _valueChanged(ev: CustomEvent): void {
const { customize_options, ...config } = ev.detail
.value as SelectOptionsCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_options && !config.options) {
config.options = stateObj?.attributes.options || [];
}
if (!customize_options && config.options) {
delete config.options;
}
fireEvent(this, "config-changed", { config: config });
}
private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
switch (schema.name) {
case "options":
case "customize_options":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.select-options.${schema.name}`
);
default:
return "";
}
};
}
declare global {
interface HTMLElementTagNameMap {
"hui-select-options-card-feature-editor": HuiSelectOptionsCardFeatureEditor;
}
}

View File

@@ -5,22 +5,14 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { FormatEntityStateFunc } from "../../../../common/translations/entity-state";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import {
WaterHeaterOperationModesCardFeatureConfig,
LovelaceCardFeatureContext,
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
import { compareWaterHeaterOperationMode } from "../../../../data/water_heater";
type WaterHeaterOperationModesCardFeatureData =
WaterHeaterOperationModesCardFeatureConfig & {
customize_modes: boolean;
};
import { OPERATION_MODES } from "../../../../data/water_heater";
@customElement("hui-water-heater-operation-modes-card-feature-editor")
export class HuiWaterHeaterOperationModesCardFeatureEditor
@@ -38,41 +30,24 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
}
private _schema = memoizeOne(
(
formatEntityState: FormatEntityStateFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
(formatEntityState: FormatEntityStateFunc, stateObj?: HassEntity) =>
[
{
name: "customize_modes",
name: "operation_modes",
selector: {
boolean: {},
select: {
multiple: true,
mode: "list",
options: OPERATION_MODES.filter((mode) =>
stateObj?.attributes.operation_list?.includes(mode)
).map((mode) => ({
value: mode,
label: stateObj ? formatEntityState(stateObj, mode) : mode,
})),
},
},
},
...(customizeModes
? ([
{
name: "operation_modes",
selector: {
select: {
reorder: true,
multiple: true,
options: (stateObj?.attributes.operation_list || [])
.concat()
.sort(compareWaterHeaterOperationMode)
.map((mode) => ({
value: mode,
label: stateObj
? formatEntityState(stateObj, mode)
: mode,
})),
},
},
},
] as const satisfies readonly HaFormSchema[])
: []),
] as const satisfies readonly HaFormSchema[]
] as const
);
protected render() {
@@ -84,21 +59,12 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: WaterHeaterOperationModesCardFeatureData = {
...this._config,
customize_modes: this._config.operation_modes !== undefined,
};
const schema = this._schema(
this.hass.formatEntityState,
stateObj,
data.customize_modes
);
const schema = this._schema(this.hass.formatEntityState, stateObj);
return html`
<ha-form
.hass=${this.hass}
.data=${data}
.data=${this._config}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
@@ -107,23 +73,7 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
const { customize_modes, ...config } = ev.detail
.value as WaterHeaterOperationModesCardFeatureData;
const stateObj = this.context?.entity_id
? this.hass!.states[this.context?.entity_id]
: undefined;
if (customize_modes && !config.operation_modes) {
config.operation_modes = (stateObj?.attributes.operation_list || [])
.concat()
.sort(compareWaterHeaterOperationMode);
}
if (!customize_modes && config.operation_modes) {
delete config.operation_modes;
}
fireEvent(this, "config-changed", { config: config });
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
@@ -131,12 +81,13 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
) => {
switch (schema.name) {
case "operation_modes":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.water-heater-operation-modes.${schema.name}`
`ui.panel.lovelace.editor.features.types.water-heater-modes.${schema.name}`
);
default:
return "";
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
}
};
}

View File

@@ -886,9 +886,9 @@ class HUIRoot extends LitElement {
const configBackground = viewConfig.background || this.config.background;
if (configBackground) {
root.style.setProperty("--lovelace-background", configBackground);
this.style.setProperty("--lovelace-background", configBackground);
} else {
root.style.removeProperty("--lovelace-background");
this.style.removeProperty("--lovelace-background");
}
root.appendChild(view);
@@ -1013,6 +1013,8 @@ class HUIRoot extends LitElement {
padding-inline-start: env(safe-area-inset-left);
padding-inline-end: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
}
hui-view {
background: var(
--lovelace-background,
var(--primary-background-color)

View File

@@ -143,11 +143,6 @@ export class HUIView extends ReactiveElement {
return this;
}
public connectedCallback(): void {
super.connectedCallback();
this._applyTheme();
}
public willUpdate(changedProperties: PropertyValues): void {
super.willUpdate(changedProperties);
@@ -217,7 +212,7 @@ export class HUIView extends ReactiveElement {
this.hass.themes !== oldHass.themes ||
this.hass.selectedTheme !== oldHass.selectedTheme
) {
this._applyTheme();
applyThemesOnElement(this, this.hass.themes, this._viewConfigTheme);
}
}
if (changedProperties.has("narrow")) {
@@ -243,28 +238,6 @@ export class HUIView extends ReactiveElement {
}
}
private _applyTheme() {
applyThemesOnElement(this, this.hass.themes, this._viewConfigTheme);
if (this._viewConfigTheme) {
// Set lovelace background color to root element, so it will be placed under the header too
const computedStyles = getComputedStyle(this);
let lovelaceBackground = computedStyles.getPropertyValue(
"--lovelace-background"
);
if (!lovelaceBackground) {
lovelaceBackground = computedStyles.getPropertyValue(
"--primary-background-color"
);
}
if (lovelaceBackground) {
this.parentElement?.style.setProperty(
"--lovelace-background",
lovelaceBackground
);
}
}
}
private async _initializeConfig() {
let viewConfig = this.lovelace.config.views[this.index];
let isStrategy = false;

View File

@@ -1889,7 +1889,6 @@
"check_updates": "Check for updates",
"no_new_updates": "No new updates found",
"updates_refreshed": "{count} {count, plural,\n one {update}\n other {updates}\n} refreshed",
"checking_updates": "Checking for updates...",
"title": "{count} {count, plural,\n one {update}\n other {updates}\n}",
"unable_to_fetch": "Unable to load updates",
"more_updates": "Show all updates",
@@ -2942,7 +2941,7 @@
"event": "[%key:ui::panel::config::automation::editor::triggers::type::homeassistant::event%]",
"sunrise": "Sunrise",
"sunset": "Sunset",
"offset": "Offset in seconds or HH:MM:SS (optional)",
"offset": "Offset (optional)",
"description": {
"picker": "When the sun sets or rises.",
"sets": "When the sun sets{hasDuration, select, \n true { offset by {duration}} \n other {}\n }",
@@ -3822,42 +3821,38 @@
}
},
"remote": {
"title": "Remote access",
"title": "Remote control",
"connected": "Connected",
"not_connected": "Not connected",
"reconnecting": "Not connected. Trying to reconnect.",
"access_is_being_prepared": "Remote access is being prepared. We will notify you when it's ready.",
"access_is_being_prepared": "Remote control is being prepared. We will notify you when it's ready.",
"cerificate_loading": "Your certificate is loading.",
"cerificate_loaded": "Your certificate is loaded, waiting for validation.",
"cerificate_error": "There was an error generating the certificate, check your logs.",
"info": "Home Assistant Cloud provides a secure remote access to your instance while away from home. For more information on remote access and these settings visit our security documentation.",
"info_instance_will_be_available": "Your instance will be available at your Nabu Casa URL.",
"info": "Home Assistant Cloud provides a secure remote connection to your instance while away from home.",
"instance_is_available": "Your instance is available at your",
"instance_will_be_available": "Your instance will be available at your",
"link_learn_how_it_works": "Learn how it works",
"show_url": "Show full URL",
"hide_url": "Hide URL",
"copy_link": "Copy link",
"security_options": "Security options",
"strict_connection": "Remote login access",
"strict_connection_secondary": "Choose what happens when new devices visit your remote access link.",
"strict_connection_option_disabled": "Show login page",
"strict_connection_option_disabled_secondary": "Any new device visiting your remote access link are presented with a login page.",
"strict_connection_option_guard_page": "Block remote logins",
"strict_connection_option_guard_page_secondary": "New devices must log in with a temporary access link. Devices accessing the link that are not logged in will be presented with a page explaining the restrictions.",
"strict_connection_option_guard_page_warning": "This prevents outsiders from trying to log in to your system but also your own devices if they have not logged in previously.",
"strict_connection_option_drop_connection": "Block remote logins and show nothing",
"strict_connection_option_drop_connection_secondary": "This is the same as the above setting but instead provides a blank page for additional security.",
"strict_connection_option_drop_connection_warning": "This prevents outsiders from snooping the remote web address and trying to log in, but it may appear as if there is no system running when users try to access it.",
"external_activation": "Allow external activation of remote access",
"external_activation_secondary": "If you disable remote access on this page, having this setting enabled allows you to reactivate it remotely via your Nabu Casa account.",
"drop_connection_warning_title": "Remote log in has been deactivated",
"drop_connection_warning": "The below security options may make it appear the system is not running.",
"strict_connection_link": "Provide temporary login access",
"strict_connection_link_secondary": "This provides a link for new devices to login for the next hour.",
"nabu_casa_url": "Nabu Casa URL",
"advanced_options": "Advanced options",
"external_activation": "Allow external activation of remote control",
"external_activation_secondary": "Allows you to turn on remote control from your Nabu Casa account page, even if you're outside your local network",
"strict_connection": "Restrict access to logged in users",
"strict_connection_secondary": "When a user is not logged in to your Home Assistant instance, they will not be able to access your instance remotely",
"strict_connection_mode": "Mode",
"strict_connection_modes": {
"disabled": "Disabled",
"guard_page": "Guard page",
"drop_connection": "Drop connection"
},
"strict_connection_link": "Create login link",
"strict_connection_link_secondary": "You can create a link that will give temporary access to the login page.",
"strict_connection_create_link": "Create link",
"strict_connection_link_created_message": "Give this link to the person you want to give remote access to the login page of your Home Assistant instance.",
"certificate_info": "Certificate information",
"certificate_expire": "Certificate renewal at {date}",
"more_info": "More details"
"strict_connection_copy_link": "Copy link",
"certificate_info": "Certificate info",
"certificate_expire": "Will be renewed at {date}",
"more_info": "More info"
},
"alexa": {
"title": "Alexa",
@@ -3946,6 +3941,7 @@
"name": "Name",
"update": "Update",
"no_devices": "No devices",
"no_device": "No device",
"enabled_label": "Enable {type}",
"enabled_cause": "The {type} is disabled by {cause}.",
"disabled_by": {
@@ -4062,14 +4058,12 @@
"search": "Search {number} entities",
"unnamed_entity": "Unnamed entity",
"status": {
"restored": "Restored",
"available": "Available",
"unavailable": "Unavailable",
"enabled": "Enabled",
"disabled": "Disabled",
"visible": "Visible",
"hidden": "Hidden",
"not_provided": "Not provided",
"unmanageable": "Unmanageable"
"readonly": "Read-only",
"hidden": "Hidden"
},
"headers": {
"state_icon": "State icon",
@@ -4079,10 +4073,7 @@
"area": "Area",
"disabled_by": "Disabled by",
"status": "Status",
"domain": "Domain",
"availability": "Availability",
"visibility": "Visibility",
"enabled": "Enabled"
"domain": "Domain"
},
"selected": "{number} selected",
"enable_selected": {
@@ -5970,8 +5961,7 @@
"armed_vacation": "[%key:ui::card::alarm_control_panel::modes::armed_vacation%]",
"armed_custom_bypass": "[%key:ui::card::alarm_control_panel::modes::armed_custom_bypass%]",
"disarmed": "[%key:ui::card::alarm_control_panel::modes::disarmed%]"
},
"customize_modes": "Customize alarm modes"
}
},
"light-brightness": {
"label": "Light brightness"
@@ -6003,18 +5993,16 @@
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
},
"customize_modes": "Customize fan modes",
"fan_modes": "Fan modes"
},
"climate-swing-modes": {
"label": "Climate swing modes",
"swing_modes": "Swing modes",
"style": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style%]",
"style_list": {
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
},
"customize_modes": "Customize swing modes"
"swing_modes": "Swing modes"
},
"climate-hvac-modes": {
"label": "Climate HVAC modes",
@@ -6023,8 +6011,7 @@
"style_list": {
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
},
"customize_modes": "Customize HVAC modes"
}
},
"climate-preset-modes": {
"label": "Climate preset modes",
@@ -6033,7 +6020,6 @@
"dropdown": "Dropdown",
"icons": "Icons"
},
"customize_modes": "Customize preset modes",
"preset_modes": "Preset modes"
},
"fan-preset-modes": {
@@ -6043,7 +6029,6 @@
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
},
"customize_modes": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::customize_modes%]",
"preset_modes": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::preset_modes%]"
},
"humidifier-toggle": {
@@ -6056,13 +6041,10 @@
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
},
"customize_modes": "Customize modes",
"modes": "Modes"
},
"select-options": {
"label": "Select options",
"options": "Options",
"customize_options": "Customize options"
"label": "Select options"
},
"numeric-input": {
"label": "Numeric input",
@@ -6080,8 +6062,7 @@
},
"water-heater-operation-modes": {
"label": "Water heater operation modes",
"operation_modes": "Operation modes",
"customize_modes": "Customize operation modes"
"operation_modes": "Operation modes"
},
"lawn-mower-commands": {
"label": "Lawn mower commands",

425
yarn.lock
View File

@@ -62,7 +62,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/core@npm:7.24.4, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.0, @babel/core@npm:^7.24.4":
"@babel/core@npm:7.24.4, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.0":
version: 7.24.4
resolution: "@babel/core@npm:7.24.4"
dependencies:
@@ -160,9 +160,9 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-define-polyfill-provider@npm:0.6.2, @babel/helper-define-polyfill-provider@npm:^0.6.1":
version: 0.6.2
resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2"
"@babel/helper-define-polyfill-provider@npm:0.6.1, @babel/helper-define-polyfill-provider@npm:^0.6.1":
version: 0.6.1
resolution: "@babel/helper-define-polyfill-provider@npm:0.6.1"
dependencies:
"@babel/helper-compilation-targets": "npm:^7.22.6"
"@babel/helper-plugin-utils": "npm:^7.22.5"
@@ -171,7 +171,7 @@ __metadata:
resolve: "npm:^1.14.2"
peerDependencies:
"@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0
checksum: 10/bb32ec12024d3f16e70641bc125d2534a97edbfdabbc9f69001ec9c4ce46f877c7a224c566aa6c8c510c3b0def2e43dc4433bf6a40896ba5ce0cef4ea5ccbcff
checksum: 10/316e7c0f05d2ae233d5fbb622c6339436da8d2b2047be866b64a16e6996c078a23b4adfebbdb33bc6a9882326a6cc20b95daa79a5e0edc92e9730e36d45fa523
languageName: node
linkType: hard
@@ -2173,10 +2173,10 @@ __metadata:
languageName: node
linkType: hard
"@lokalise/node-api@npm:12.4.1":
version: 12.4.1
resolution: "@lokalise/node-api@npm:12.4.1"
checksum: 10/54cf96a759ea0642b5ad13246cc281c2cfa1592d44752e58be996177c48a66038e8f513d509d17db35eeef2e7ad2ac243a375b628a40f36e88ca015ab5f89367
"@lokalise/node-api@npm:12.4.0":
version: 12.4.0
resolution: "@lokalise/node-api@npm:12.4.0"
checksum: 10/5e44b23e7139487961f291dcea4f7082fe86d8b017142890831f0ac83338d7f749565e7f96a628eb0f594d894abd905dfec1c31c140fd07bcf0c120db118ee15
languageName: node
linkType: hard
@@ -3389,16 +3389,16 @@ __metadata:
languageName: node
linkType: hard
"@octokit/plugin-retry@npm:7.1.1":
version: 7.1.1
resolution: "@octokit/plugin-retry@npm:7.1.1"
"@octokit/plugin-retry@npm:7.1.0":
version: 7.1.0
resolution: "@octokit/plugin-retry@npm:7.1.0"
dependencies:
"@octokit/request-error": "npm:^6.0.0"
"@octokit/types": "npm:^13.0.0"
bottleneck: "npm:^2.15.3"
peerDependencies:
"@octokit/core": ">=6"
checksum: 10/ff7d2f0b45e61ff688c213ad28670fceffa8d56603850beabab21cbb79021d91d8bf40cdc2062949eeb6b031c6e5ad39bcbbc40b0caa7924c81d3c90b8fb0843
checksum: 10/c445c76daa37c38fd095a280f28c6e9afbfdaab8290f0ca17253e3553b3fcf55f028ae72c923440e1a06b87e4e04f24d8a5a4976f6a7494d33d368ec4518a7de
languageName: node
linkType: hard
@@ -3811,7 +3811,7 @@ __metadata:
languageName: node
linkType: hard
"@rollup/plugin-node-resolve@npm:15.2.3, @rollup/plugin-node-resolve@npm:^15.2.3":
"@rollup/plugin-node-resolve@npm:15.2.3":
version: 15.2.3
resolution: "@rollup/plugin-node-resolve@npm:15.2.3"
dependencies:
@@ -3830,6 +3830,22 @@ __metadata:
languageName: node
linkType: hard
"@rollup/plugin-node-resolve@npm:^11.2.1":
version: 11.2.1
resolution: "@rollup/plugin-node-resolve@npm:11.2.1"
dependencies:
"@rollup/pluginutils": "npm:^3.1.0"
"@types/resolve": "npm:1.17.1"
builtin-modules: "npm:^3.1.0"
deepmerge: "npm:^4.2.2"
is-module: "npm:^1.0.0"
resolve: "npm:^1.19.0"
peerDependencies:
rollup: ^1.20.0||^2.0.0
checksum: 10/8007f6a01d709da1078df19bb5ecb1339f43042786a68d98645e0a4c1765064d1500a1b86b65e12de6ae35d9b1ae693e22e63b3ebb69a627ce81172ea21cc228
languageName: node
linkType: hard
"@rollup/plugin-node-resolve@npm:^13.0.4":
version: 13.3.0
resolution: "@rollup/plugin-node-resolve@npm:13.3.0"
@@ -3873,22 +3889,6 @@ __metadata:
languageName: node
linkType: hard
"@rollup/plugin-terser@npm:^0.4.3":
version: 0.4.4
resolution: "@rollup/plugin-terser@npm:0.4.4"
dependencies:
serialize-javascript: "npm:^6.0.1"
smob: "npm:^1.0.0"
terser: "npm:^5.17.4"
peerDependencies:
rollup: ^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
checksum: 10/a5e066ddea55fc8c32188bc8b484cca619713516f10e3a06801881ec98bf37459ca24e5fe8711f93a5fa7f26a6e9132a47bc1a61c01e0b513dfd79a96cdc6eb7
languageName: node
linkType: hard
"@rollup/pluginutils@npm:^3.1.0":
version: 3.1.0
resolution: "@rollup/pluginutils@npm:3.1.0"
@@ -4302,12 +4302,12 @@ __metadata:
languageName: node
linkType: hard
"@types/leaflet@npm:*, @types/leaflet@npm:1.9.12":
version: 1.9.12
resolution: "@types/leaflet@npm:1.9.12"
"@types/leaflet@npm:*, @types/leaflet@npm:1.9.11":
version: 1.9.11
resolution: "@types/leaflet@npm:1.9.11"
dependencies:
"@types/geojson": "npm:*"
checksum: 10/ff6dce2f613b97bdc3ceb929e6eeaaa8bef8bbafdf9758935b1d679cbaf76360e366080d77e42da58e41aac146434c5d18c70ec919d37e01e0592f0a4f2e967e
checksum: 10/a7f3936b83f1007fa74f65eee7a905e582966c3218d3a45ad1c713445038e69cdefeb668c8f0cb70bc293e77d3d801b299a90bc2fd33e52ff90fd93f342108a2
languageName: node
linkType: hard
@@ -4574,15 +4574,15 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/eslint-plugin@npm:7.7.1"
"@typescript-eslint/eslint-plugin@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/eslint-plugin@npm:7.7.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@typescript-eslint/scope-manager": "npm:7.7.1"
"@typescript-eslint/type-utils": "npm:7.7.1"
"@typescript-eslint/utils": "npm:7.7.1"
"@typescript-eslint/visitor-keys": "npm:7.7.1"
"@typescript-eslint/scope-manager": "npm:7.7.0"
"@typescript-eslint/type-utils": "npm:7.7.0"
"@typescript-eslint/utils": "npm:7.7.0"
"@typescript-eslint/visitor-keys": "npm:7.7.0"
debug: "npm:^4.3.4"
graphemer: "npm:^1.4.0"
ignore: "npm:^5.3.1"
@@ -4595,44 +4595,44 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 10/54064fe466edcebece50cf4cfc4cb18753bcba7da0e3f0db29bf628586716b14945cadf01529ebc3d823e35bc62debf21aa636ae1f5e4fa92670dce65b3dec8c
checksum: 10/9e6b6fbb9920581813c01daaa2f89419c3476e42823755c0627f4491640cfaffaebeb0592231ed4f318eefadfcdd4560b77b2903d66ab4e0c8df746a7037a603
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/parser@npm:7.7.1"
"@typescript-eslint/parser@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/parser@npm:7.7.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:7.7.1"
"@typescript-eslint/types": "npm:7.7.1"
"@typescript-eslint/typescript-estree": "npm:7.7.1"
"@typescript-eslint/visitor-keys": "npm:7.7.1"
"@typescript-eslint/scope-manager": "npm:7.7.0"
"@typescript-eslint/types": "npm:7.7.0"
"@typescript-eslint/typescript-estree": "npm:7.7.0"
"@typescript-eslint/visitor-keys": "npm:7.7.0"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.56.0
peerDependenciesMeta:
typescript:
optional: true
checksum: 10/39cd5c686e9f7e86da669fc3622b203e1025f162d42c4f45373e827c659b8823535fe4ea62ccb5e672ef999f8491d74c8c5c4c497367c884672fc835497ea180
checksum: 10/9f8c53ca29af09cd366e37420410319c8f69e9f4a676513ecd91f5e6d822b9935b6a8ad7ec931d604fc4a0ecd93d51063d0c93227f78f2380196c8a7fa6970d1
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/scope-manager@npm:7.7.1"
"@typescript-eslint/scope-manager@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/scope-manager@npm:7.7.0"
dependencies:
"@typescript-eslint/types": "npm:7.7.1"
"@typescript-eslint/visitor-keys": "npm:7.7.1"
checksum: 10/7823cd15e7205d2c0d9e69432717c385b2ecd7559d5edba79113c2e97c6c5e8ca3dae9343a734bc740be97e096bfcb9dfb81a3da697f9fbf5600a56a42cf70e9
"@typescript-eslint/types": "npm:7.7.0"
"@typescript-eslint/visitor-keys": "npm:7.7.0"
checksum: 10/c8890aaf99b57543774e50549c5b178c13695b21a6b30c65292268137fe5e6856cc0e050c118b47b5835dd8a48c96e042fc75891a7f6093a0b94b6b3b251afd9
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/type-utils@npm:7.7.1"
"@typescript-eslint/type-utils@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/type-utils@npm:7.7.0"
dependencies:
"@typescript-eslint/typescript-estree": "npm:7.7.1"
"@typescript-eslint/utils": "npm:7.7.1"
"@typescript-eslint/typescript-estree": "npm:7.7.0"
"@typescript-eslint/utils": "npm:7.7.0"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^1.3.0"
peerDependencies:
@@ -4640,23 +4640,23 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 10/c64dfd3e535741270012d289d1327e487df877adfa8a9920b1f8d6616f3b7159ef8ee1d6b62e866b6a5c64d675c5008e87f4ea20b5fc032e95f197a749d38ae6
checksum: 10/a3f5358b4b7046458ea573607f3d6ea7f48e16524390b24c9360bdf8b03cc89fc6eb5da31b3e541e7f1e5f6958194ecaad5b644ca9b0d90c9a7b182f345451aa
languageName: node
linkType: hard
"@typescript-eslint/types@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/types@npm:7.7.1"
checksum: 10/a1ecbaf3b8a5243394d421644f2b3eb164feea645e36dd07f1afb5008598201f19c7988141fc162c647f380dda7cf571017c0eabbbc4c5432b0143383853e134
"@typescript-eslint/types@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/types@npm:7.7.0"
checksum: 10/d54ff9eeea168188fcbf1c8efe42892d1646ead801ea0a0f1312c80cfb74ee5dd61a145bc982919fb396683fb4578f98f7ad90e5d466d7aa1ca593e4338e1a2e
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/typescript-estree@npm:7.7.1"
"@typescript-eslint/typescript-estree@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/typescript-estree@npm:7.7.0"
dependencies:
"@typescript-eslint/types": "npm:7.7.1"
"@typescript-eslint/visitor-keys": "npm:7.7.1"
"@typescript-eslint/types": "npm:7.7.0"
"@typescript-eslint/visitor-keys": "npm:7.7.0"
debug: "npm:^4.3.4"
globby: "npm:^11.1.0"
is-glob: "npm:^4.0.3"
@@ -4666,34 +4666,34 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 10/df5fe6c573b15e8058b88d1535eeca11115118adc54225f511d2762d74e2d453205ba27e63f6666cb5f3dc73d639208a183fb05db1f75063b115d52b1fae3e20
checksum: 10/40af26b3edb07af439f99728aa149bbc8668dae4a700a128abaf98d7f9bc0d5d31f8027aa1d13d6a55b22c20738d7cab84a3046a56417a2551de58671b39dbdf
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/utils@npm:7.7.1"
"@typescript-eslint/utils@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/utils@npm:7.7.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.4.0"
"@types/json-schema": "npm:^7.0.15"
"@types/semver": "npm:^7.5.8"
"@typescript-eslint/scope-manager": "npm:7.7.1"
"@typescript-eslint/types": "npm:7.7.1"
"@typescript-eslint/typescript-estree": "npm:7.7.1"
"@typescript-eslint/scope-manager": "npm:7.7.0"
"@typescript-eslint/types": "npm:7.7.0"
"@typescript-eslint/typescript-estree": "npm:7.7.0"
semver: "npm:^7.6.0"
peerDependencies:
eslint: ^8.56.0
checksum: 10/5a352c3a849300b5d676bf5f451418a2fb0cd3ab515f3733521ad03cf047849c52c76f6e5d2406e08f6d0dbad3a4708b490f909c91a1a9e3d73060a750b3bca2
checksum: 10/4223233ee022460a74f389302b50779537dfbb3bd414486dca356d2628a08d5b2c4c6002bae3bdffad92b368569024faf25faee9be739340d9459c23549a866f
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/visitor-keys@npm:7.7.1"
"@typescript-eslint/visitor-keys@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/visitor-keys@npm:7.7.0"
dependencies:
"@typescript-eslint/types": "npm:7.7.1"
"@typescript-eslint/types": "npm:7.7.0"
eslint-visitor-keys: "npm:^3.4.3"
checksum: 10/dcc5748b10bb1b169516b33e87b6d86b562e25725a95e5ac515cb197589d9667aaa7cfffa93234095a73c80addb6dd88e2a9ab01d2be0c274254b5be1ca4057a
checksum: 10/9f03591ab60b0b164f6bb222b5d5ae75f73fbe7f264be9318f770be9dc5dff8138d34701928940ffc18924058ae80754a738a1e623912a297d57a8a59cdfb41d
languageName: node
linkType: hard
@@ -6246,7 +6246,7 @@ __metadata:
languageName: node
linkType: hard
"builtin-modules@npm:^3.3.0":
"builtin-modules@npm:^3.1.0, builtin-modules@npm:^3.3.0":
version: 3.3.0
resolution: "builtin-modules@npm:3.3.0"
checksum: 10/62e063ab40c0c1efccbfa9ffa31873e4f9d57408cb396a2649981a0ecbce56aabc93c28feaccbc5658c95aab2703ad1d11980e62ec2e5e72637404e1eb60f39e
@@ -9556,7 +9556,7 @@ __metadata:
resolution: "home-assistant-frontend@workspace:."
dependencies:
"@babel/core": "npm:7.24.4"
"@babel/helper-define-polyfill-provider": "npm:0.6.2"
"@babel/helper-define-polyfill-provider": "npm:0.6.1"
"@babel/plugin-proposal-decorators": "npm:7.24.1"
"@babel/plugin-transform-runtime": "npm:7.24.3"
"@babel/preset-env": "npm:7.24.4"
@@ -9592,7 +9592,7 @@ __metadata:
"@lit-labs/motion": "npm:1.0.7"
"@lit-labs/observers": "npm:2.0.2"
"@lit-labs/virtualizer": "npm:2.0.12"
"@lokalise/node-api": "npm:12.4.1"
"@lokalise/node-api": "npm:12.4.0"
"@lrnwebcomponents/simple-tooltip": "npm:8.0.2"
"@material/chips": "npm:=14.0.0-canary.53b3cad2f.0"
"@material/data-table": "npm:=14.0.0-canary.53b3cad2f.0"
@@ -9624,7 +9624,7 @@ __metadata:
"@mdi/js": "npm:7.4.47"
"@mdi/svg": "npm:7.4.47"
"@octokit/auth-oauth-device": "npm:7.1.1"
"@octokit/plugin-retry": "npm:7.1.1"
"@octokit/plugin-retry": "npm:7.1.0"
"@octokit/rest": "npm:20.1.0"
"@open-wc/dev-server-hmr": "npm:0.1.4"
"@polymer/paper-item": "npm:3.0.1"
@@ -9644,7 +9644,7 @@ __metadata:
"@types/glob": "npm:8.1.0"
"@types/html-minifier-terser": "npm:7.0.2"
"@types/js-yaml": "npm:4.0.9"
"@types/leaflet": "npm:1.9.12"
"@types/leaflet": "npm:1.9.11"
"@types/leaflet-draw": "npm:1.0.11"
"@types/luxon": "npm:3.4.2"
"@types/mocha": "npm:10.0.6"
@@ -9654,8 +9654,8 @@ __metadata:
"@types/tar": "npm:6.1.13"
"@types/ua-parser-js": "npm:0.7.39"
"@types/webspeechapi": "npm:0.0.29"
"@typescript-eslint/eslint-plugin": "npm:7.7.1"
"@typescript-eslint/parser": "npm:7.7.1"
"@typescript-eslint/eslint-plugin": "npm:7.7.0"
"@typescript-eslint/parser": "npm:7.7.0"
"@vaadin/combo-box": "npm:24.3.11"
"@vaadin/vaadin-themable-mixin": "npm:24.3.11"
"@vibrant/color": "npm:3.2.1-alpha.1"
@@ -9766,13 +9766,13 @@ __metadata:
webpack-stats-plugin: "npm:1.1.3"
webpackbar: "npm:6.0.1"
weekstart: "npm:2.0.0"
workbox-build: "npm:7.1.0"
workbox-cacheable-response: "npm:7.1.0"
workbox-core: "npm:7.1.0"
workbox-expiration: "npm:7.1.0"
workbox-precaching: "npm:7.1.0"
workbox-routing: "npm:7.1.0"
workbox-strategies: "npm:7.1.0"
workbox-build: "npm:7.0.0"
workbox-cacheable-response: "npm:7.0.0"
workbox-core: "npm:7.0.0"
workbox-expiration: "npm:7.0.0"
workbox-precaching: "npm:7.0.0"
workbox-routing: "npm:7.0.0"
workbox-strategies: "npm:7.0.0"
xss: "npm:1.0.15"
languageName: unknown
linkType: soft
@@ -13921,7 +13921,7 @@ __metadata:
languageName: node
linkType: hard
"rollup-plugin-terser@npm:7.0.2":
"rollup-plugin-terser@npm:7.0.2, rollup-plugin-terser@npm:^7.0.0":
version: 7.0.2
resolution: "rollup-plugin-terser@npm:7.0.2"
dependencies:
@@ -14410,13 +14410,6 @@ __metadata:
languageName: node
linkType: hard
"smob@npm:^1.0.0":
version: 1.5.0
resolution: "smob@npm:1.5.0"
checksum: 10/a1ea453bcea89989062626ea30a1fcb42c62e96255619c8641ffa1d7ab42baf415975c67c718127036901b9e487d8bf4c46219e50cec54295412c1227700b8fe
languageName: node
linkType: hard
"snapdragon-node@npm:^2.0.1":
version: 2.1.1
resolution: "snapdragon-node@npm:2.1.1"
@@ -15201,9 +15194,9 @@ __metadata:
languageName: node
linkType: hard
"terser@npm:^5.0.0, terser@npm:^5.15.1, terser@npm:^5.17.4, terser@npm:^5.26.0":
version: 5.30.4
resolution: "terser@npm:5.30.4"
"terser@npm:^5.0.0, terser@npm:^5.15.1, terser@npm:^5.26.0":
version: 5.30.3
resolution: "terser@npm:5.30.3"
dependencies:
"@jridgewell/source-map": "npm:^0.3.3"
acorn: "npm:^8.8.2"
@@ -15211,7 +15204,7 @@ __metadata:
source-map-support: "npm:~0.5.20"
bin:
terser: bin/terser
checksum: 10/79459106281fccb2ff4243ba1553e4aa67a71b336bb8c091b131bb26347fcf03791c6abf6870bd17fe4a210256e08910207cf5733c0d6ba840289e67d5aa84d3
checksum: 10/f4ee378065a327c85472f351ac232fa47ec84d4f15df7ec58c044b41e3c063cf11aaedd90dcfe9c7f2a6ef01d4aab23deb61622301170dc77d0a8b6a6a83cf5e
languageName: node
linkType: hard
@@ -16585,37 +16578,36 @@ __metadata:
languageName: node
linkType: hard
"workbox-background-sync@npm:7.1.0":
version: 7.1.0
resolution: "workbox-background-sync@npm:7.1.0"
"workbox-background-sync@npm:7.0.0":
version: 7.0.0
resolution: "workbox-background-sync@npm:7.0.0"
dependencies:
idb: "npm:^7.0.1"
workbox-core: "npm:7.1.0"
checksum: 10/0a303af41a02703ecd962c9a003eebd437e4373a468f3ac4ab0b7969a6849bc98a51f1b48915423dee2f5315d8ae34407465aa3a609ea683499043d1a9b44366
workbox-core: "npm:7.0.0"
checksum: 10/41f89cd970c69bebe1ac648485c89f033ccd116f8140696de5f5489f2138e67ccc30ce33158025b8e312c49ad390f80eb8e372f9c569f5d6f35ab1e6185c2d74
languageName: node
linkType: hard
"workbox-broadcast-update@npm:7.1.0":
version: 7.1.0
resolution: "workbox-broadcast-update@npm:7.1.0"
"workbox-broadcast-update@npm:7.0.0":
version: 7.0.0
resolution: "workbox-broadcast-update@npm:7.0.0"
dependencies:
workbox-core: "npm:7.1.0"
checksum: 10/8dd87c05b14c0e7f03711d8a189949d5b16a33c53fc0d252569ba4758e450baca2d1aac45628b4210532c930176b1796a2a917be51000289001e401bdc5d3915
workbox-core: "npm:7.0.0"
checksum: 10/e6110207465c574327dc89cbedad90795093e181341e2b093141a7171c46d2d4bb5862ba2a3a1e0c753e359f4462bf9929ce911c2082fe566f2551da295abf68
languageName: node
linkType: hard
"workbox-build@npm:7.1.0":
version: 7.1.0
resolution: "workbox-build@npm:7.1.0"
"workbox-build@npm:7.0.0":
version: 7.0.0
resolution: "workbox-build@npm:7.0.0"
dependencies:
"@apideck/better-ajv-errors": "npm:^0.3.1"
"@babel/core": "npm:^7.24.4"
"@babel/core": "npm:^7.11.1"
"@babel/preset-env": "npm:^7.11.0"
"@babel/runtime": "npm:^7.11.2"
"@rollup/plugin-babel": "npm:^5.2.0"
"@rollup/plugin-node-resolve": "npm:^15.2.3"
"@rollup/plugin-node-resolve": "npm:^11.2.1"
"@rollup/plugin-replace": "npm:^2.4.1"
"@rollup/plugin-terser": "npm:^0.4.3"
"@surma/rollup-plugin-off-main-thread": "npm:^2.2.3"
ajv: "npm:^8.6.0"
common-tags: "npm:^1.8.0"
@@ -16625,153 +16617,154 @@ __metadata:
lodash: "npm:^4.17.20"
pretty-bytes: "npm:^5.3.0"
rollup: "npm:^2.43.1"
rollup-plugin-terser: "npm:^7.0.0"
source-map: "npm:^0.8.0-beta.0"
stringify-object: "npm:^3.3.0"
strip-comments: "npm:^2.0.1"
tempy: "npm:^0.6.0"
upath: "npm:^1.2.0"
workbox-background-sync: "npm:7.1.0"
workbox-broadcast-update: "npm:7.1.0"
workbox-cacheable-response: "npm:7.1.0"
workbox-core: "npm:7.1.0"
workbox-expiration: "npm:7.1.0"
workbox-google-analytics: "npm:7.1.0"
workbox-navigation-preload: "npm:7.1.0"
workbox-precaching: "npm:7.1.0"
workbox-range-requests: "npm:7.1.0"
workbox-recipes: "npm:7.1.0"
workbox-routing: "npm:7.1.0"
workbox-strategies: "npm:7.1.0"
workbox-streams: "npm:7.1.0"
workbox-sw: "npm:7.1.0"
workbox-window: "npm:7.1.0"
checksum: 10/6d2086899e65f7728fe3c2cc7d14dbc18bec2ae8c2e1e681f552e0162b8c138b2c2a235ddcf820b3b966cb06b60319fcaa9eb1831f35f1fab1da77fa4238dcbd
workbox-background-sync: "npm:7.0.0"
workbox-broadcast-update: "npm:7.0.0"
workbox-cacheable-response: "npm:7.0.0"
workbox-core: "npm:7.0.0"
workbox-expiration: "npm:7.0.0"
workbox-google-analytics: "npm:7.0.0"
workbox-navigation-preload: "npm:7.0.0"
workbox-precaching: "npm:7.0.0"
workbox-range-requests: "npm:7.0.0"
workbox-recipes: "npm:7.0.0"
workbox-routing: "npm:7.0.0"
workbox-strategies: "npm:7.0.0"
workbox-streams: "npm:7.0.0"
workbox-sw: "npm:7.0.0"
workbox-window: "npm:7.0.0"
checksum: 10/b7b19cb27053e10de36c49fa90fa3f10e027184ea84d6b96d998120e3c801b123de2f7a0b33358ff7a1390cc729e51c6f0b25ea8c7b2504792cd7585aef9d507
languageName: node
linkType: hard
"workbox-cacheable-response@npm:7.1.0":
version: 7.1.0
resolution: "workbox-cacheable-response@npm:7.1.0"
"workbox-cacheable-response@npm:7.0.0":
version: 7.0.0
resolution: "workbox-cacheable-response@npm:7.0.0"
dependencies:
workbox-core: "npm:7.1.0"
checksum: 10/1eb294765256d5010325c702208b8f621ad59a0323981bdd8d9d991b9ab7888f39f9a051f90ba583840168cd3252852511666ddc3db64f20f91c97cbea159796
workbox-core: "npm:7.0.0"
checksum: 10/b18de42a55802f18ecb7d08c6f3dac5f18cded64f36bb74b00eceb01199d2f8362c96d76ee12e75381fb51a06d705d901e3985c24017dc3d3e3fc9fc36282e51
languageName: node
linkType: hard
"workbox-core@npm:7.1.0":
version: 7.1.0
resolution: "workbox-core@npm:7.1.0"
checksum: 10/b890e0e257c12d3a818eee9dabdfdc8d7d228b89f9734f7612f14e664ca5414c511778d5aef5159248db4c6c161587cff6d2332f0543e3628a1e0cd5a1f0b3ac
"workbox-core@npm:7.0.0":
version: 7.0.0
resolution: "workbox-core@npm:7.0.0"
checksum: 10/680c65e926517a6cd7b515243b9a33a5f4cdc45de31e060fbdc89e28f79b10a2fa211576802a29790cd37fa8a801f3fccfb9cbe371acaa8095c858c9afefc7e6
languageName: node
linkType: hard
"workbox-expiration@npm:7.1.0":
version: 7.1.0
resolution: "workbox-expiration@npm:7.1.0"
"workbox-expiration@npm:7.0.0":
version: 7.0.0
resolution: "workbox-expiration@npm:7.0.0"
dependencies:
idb: "npm:^7.0.1"
workbox-core: "npm:7.1.0"
checksum: 10/45c7a27b217355fc30929482625c43cbfa04c914162a26b92c7e91fcb3a20e9982b50026bf4bb37382a320e1426818e3726b999dce1c8c08d2aa330eee569308
workbox-core: "npm:7.0.0"
checksum: 10/a9b23c7c76cbabe8f04567e603428db939cb169cf40c1e5ba1c5a1eb966f7b8a4d0182dffdd3d77917b5edca966be0d6e347b7eff274292256fe6ea9a40fa754
languageName: node
linkType: hard
"workbox-google-analytics@npm:7.1.0":
version: 7.1.0
resolution: "workbox-google-analytics@npm:7.1.0"
"workbox-google-analytics@npm:7.0.0":
version: 7.0.0
resolution: "workbox-google-analytics@npm:7.0.0"
dependencies:
workbox-background-sync: "npm:7.1.0"
workbox-core: "npm:7.1.0"
workbox-routing: "npm:7.1.0"
workbox-strategies: "npm:7.1.0"
checksum: 10/e3652b7f37306a01bcb819ab48799ad3e71362919a31dcbaa186bedb2509b0107d9bc525ca97f287a41095a339a1fb664de4d31af40dfd2ddec9a9fac7b1b75a
workbox-background-sync: "npm:7.0.0"
workbox-core: "npm:7.0.0"
workbox-routing: "npm:7.0.0"
workbox-strategies: "npm:7.0.0"
checksum: 10/e66b390a86b6b9e872e004207c2c920629b45a1be396ec80f9a190986a4e8eaff69bd002f266bd907d0ab7a16cf2b1a8dc3719b0d2bb147640afb6eed795e0d3
languageName: node
linkType: hard
"workbox-navigation-preload@npm:7.1.0":
version: 7.1.0
resolution: "workbox-navigation-preload@npm:7.1.0"
"workbox-navigation-preload@npm:7.0.0":
version: 7.0.0
resolution: "workbox-navigation-preload@npm:7.0.0"
dependencies:
workbox-core: "npm:7.1.0"
checksum: 10/e4a2e40f1292b1a5e70c7efe69d280635b211c98577ef88f3941b4627e592bb5b21aae262da74636f06ea1fc61065e3002f41cff12f25b05de0259e2700b6ca8
workbox-core: "npm:7.0.0"
checksum: 10/69bd82c12adabf7a210a3a9d8ce01082ef7276521ba375e0455ba3c17cf42b101783f0ec2af4ab0191989b0ea118794e0c8ebe3800abe806142ac7fef0674ac5
languageName: node
linkType: hard
"workbox-precaching@npm:7.1.0":
version: 7.1.0
resolution: "workbox-precaching@npm:7.1.0"
"workbox-precaching@npm:7.0.0":
version: 7.0.0
resolution: "workbox-precaching@npm:7.0.0"
dependencies:
workbox-core: "npm:7.1.0"
workbox-routing: "npm:7.1.0"
workbox-strategies: "npm:7.1.0"
checksum: 10/4f91a1cb1fbc1af1f467e3aa4f1a315465a7537ea42d3fbc3f85da342b10085a64736e80de42611622b83650c422b8770a1fc7fb5008a75abfc07a8c1393e049
workbox-core: "npm:7.0.0"
workbox-routing: "npm:7.0.0"
workbox-strategies: "npm:7.0.0"
checksum: 10/8882d5ba888b08aba5e82656b26cd2f293c5adbb858bdff5e17cb4bf1823063cf51d16d3d6640716de96f11cf458f8aaf33b5ffef1ef07d327df8680711bf02e
languageName: node
linkType: hard
"workbox-range-requests@npm:7.1.0":
version: 7.1.0
resolution: "workbox-range-requests@npm:7.1.0"
"workbox-range-requests@npm:7.0.0":
version: 7.0.0
resolution: "workbox-range-requests@npm:7.0.0"
dependencies:
workbox-core: "npm:7.1.0"
checksum: 10/a92d9c28a1c033a4d9e8a958613f2d44b374ef6f4f7609bf8f574ae5fe41c0800b251fb17f8ca7cd3ebc3c53cdbc22fe5a4c5f0afabd63a5960cbe4333dbbf2a
workbox-core: "npm:7.0.0"
checksum: 10/8cb991173df19f01f00aa6fee901946d73cdaefb7fef74fda99e53cdfeb0400efebba152f36ba02048af605464b725c743d306ec6e244b1bf6d1fb4df29a284f
languageName: node
linkType: hard
"workbox-recipes@npm:7.1.0":
version: 7.1.0
resolution: "workbox-recipes@npm:7.1.0"
"workbox-recipes@npm:7.0.0":
version: 7.0.0
resolution: "workbox-recipes@npm:7.0.0"
dependencies:
workbox-cacheable-response: "npm:7.1.0"
workbox-core: "npm:7.1.0"
workbox-expiration: "npm:7.1.0"
workbox-precaching: "npm:7.1.0"
workbox-routing: "npm:7.1.0"
workbox-strategies: "npm:7.1.0"
checksum: 10/371daf94bc418e93038f26b677fdd50f1ae3f66937282dcfa7a67fdba3d871be22de3dc8b6192d125875df8e7ba56d110bedf82d4180380dd377ddc38655ea5a
workbox-cacheable-response: "npm:7.0.0"
workbox-core: "npm:7.0.0"
workbox-expiration: "npm:7.0.0"
workbox-precaching: "npm:7.0.0"
workbox-routing: "npm:7.0.0"
workbox-strategies: "npm:7.0.0"
checksum: 10/efb84b7eec97cd8423b33cad7d561f7c05db46fbbcdb175f3095e8efe839f5323c7af3fcd784b802fdcc7bf5e33648f95159f5462200884f37ae6aa66498e086
languageName: node
linkType: hard
"workbox-routing@npm:7.1.0":
version: 7.1.0
resolution: "workbox-routing@npm:7.1.0"
"workbox-routing@npm:7.0.0":
version: 7.0.0
resolution: "workbox-routing@npm:7.0.0"
dependencies:
workbox-core: "npm:7.1.0"
checksum: 10/3598d65801ae6fb1e05512aff1cc3a642c1e3beace248349c6401f678033433ec3083ea0849a28665f0bb11e2493e9e66b0d066ee5de59a84f70baa3a59d841c
workbox-core: "npm:7.0.0"
checksum: 10/294c4b0f136e39c44678caa736195bd99bf93e55ba3605d0a880e36e7ffc9a4b6be36c2c37bca3ee1f905615641167d5b2a0eab742f46b94536f2c5eaac0e832
languageName: node
linkType: hard
"workbox-strategies@npm:7.1.0":
version: 7.1.0
resolution: "workbox-strategies@npm:7.1.0"
"workbox-strategies@npm:7.0.0":
version: 7.0.0
resolution: "workbox-strategies@npm:7.0.0"
dependencies:
workbox-core: "npm:7.1.0"
checksum: 10/52734ecce926ba6c135b5c7cb31906e40ad6bc767c77d45e74414b8adbb980f8a81bc1253af64750ce22202d0f1c4f01161785829cfb7bcb3f59408da9130555
workbox-core: "npm:7.0.0"
checksum: 10/7d7dbe9dff54c22e01d01238dbf0b3ba259b85c08cbd8b617b9f5104efef948b760848fcf36385d26fd651a2fe57c4faa3d6a4fc2739e05fc684da0a7eb2197a
languageName: node
linkType: hard
"workbox-streams@npm:7.1.0":
version: 7.1.0
resolution: "workbox-streams@npm:7.1.0"
"workbox-streams@npm:7.0.0":
version: 7.0.0
resolution: "workbox-streams@npm:7.0.0"
dependencies:
workbox-core: "npm:7.1.0"
workbox-routing: "npm:7.1.0"
checksum: 10/759011add716b69be2fc07f847476de6b299b451201e26861529156a8da9a145a9c10b5408f28937142dd82b796e08fdc0f557a7560ff4a9e1ec6affae1d1efb
workbox-core: "npm:7.0.0"
workbox-routing: "npm:7.0.0"
checksum: 10/a11a134536e21a04d29dd2b0da8a79524ae27c993c98c63db8c6ddf49449a2f9d4d09766c6351cca08538b713e837e7d3ddf93842fd15eea3ecab75bc674cdf2
languageName: node
linkType: hard
"workbox-sw@npm:7.1.0":
version: 7.1.0
resolution: "workbox-sw@npm:7.1.0"
checksum: 10/ece8081e41a45e2e42e0be597e5a2a8be8aa25ebf16a496599a76d4a044fc922e0b40d3fcb9c82682db1911b0d6e51e761593922c90f40d11d7b06f7a4b773c7
"workbox-sw@npm:7.0.0":
version: 7.0.0
resolution: "workbox-sw@npm:7.0.0"
checksum: 10/2b34da7efad8788e024bb22b25293459a9b0adc19fe185c3d155a4956f67ccdd35829a88b82436b4afe956a99c0ac934e5624a1469a762a53b522c2f32329d34
languageName: node
linkType: hard
"workbox-window@npm:7.1.0":
version: 7.1.0
resolution: "workbox-window@npm:7.1.0"
"workbox-window@npm:7.0.0":
version: 7.0.0
resolution: "workbox-window@npm:7.0.0"
dependencies:
"@types/trusted-types": "npm:^2.0.2"
workbox-core: "npm:7.1.0"
checksum: 10/2706c55b81857966c28087a2b0ef40b7791e1bd441b880b7525b7e1b4834ae89c4f1bcfdb07cc155487a85f7c566007e1f9edf65539d7f4a52e2ceee48f547b5
workbox-core: "npm:7.0.0"
checksum: 10/5511ed9b86602ee6999233fc09ab9da298b7d62dbf865737c920d7f768877f8f9031896fad413f9b34f46ed2db58e0967b66b08cd78cf2f70bd7f734e7cac324
languageName: node
linkType: hard