Compare commits

...

50 Commits

Author SHA1 Message Date
Bram Kragten
e2266aa671 Merge branch 'dev' 2024-05-01 12:04:10 +02:00
Bram Kragten
68a79490dc Bumped version to 20240501.0 2024-05-01 12:03:26 +02:00
Paul Bottein
6febe8552e Allow to reorder alarm modes in card feature (#20684) 2024-05-01 11:55:06 +02:00
Bram Kragten
f611f23f6f Make sure lovelace theme background is set on it's container (#20683) 2024-05-01 11:24:40 +02:00
Bram Kragten
ef4f11fdf8 20240430.0 (#20681) 2024-04-30 23:58:58 +02:00
Bram Kragten
627e06663b Bumped version to 20240430.0 2024-04-30 23:44:32 +02:00
Paul Bottein
ab01633069 Fix ha settings row display in more info settings (#20680) 2024-04-30 21:12:53 +00:00
Bram Kragten
17dcc90638 Update entity status filter and grouping (#20679) 2024-04-30 23:04:48 +02:00
Paul Bottein
d0df029ff1 Update check update icon and add toast when checking update (#20677)
* Update check update icon

* Add toast when checking for update
2024-04-30 19:21:30 +00:00
Paul Bottein
86a7e69812 Allow to reorder and filter options in select options card feature (#20675) 2024-04-30 21:14:49 +02:00
Adam Kapos
af9417f2a6 Add theme support for dialog surface background (#20653)
* Add theme support for dialog surface background

* Change from review

* Change from review

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Run prettier

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2024-04-30 21:12:36 +02:00
Paul Bottein
7120ad99b9 Add customize mode option to card features with modes (#20670)
* Add customize mode options to card features with modes

* Better type

* Fix water heater and humidifier

* Clean schema
2024-04-30 18:38:51 +02:00
Adam Kapos
334c245b65 Fix visual differences between regular and energy dashboards (#20654)
* Fix visual differences between regular and energy dashboards

* Order padding properties the same way between energy and lovelace

* Change from code review
2024-04-30 15:07:54 +02:00
Nicooow
bcb72d83b8 Fix an inconsistency in dark mode (#20671)
* add app-theme-color var

* Fix Prettier format

* Fix regression on default dark theme

* prevent duplicate calculation
2024-04-30 12:03:19 +00:00
karwosts
c99e0e846b More config/entities status filters (#20638) 2024-04-30 12:32:32 +02:00
J. Nick Koston
ec3f63e8a3 Fallback to raw config entry reason if localize returns an empty string (#20668)
Show config entry reason if localize returns an empty string
2024-04-30 12:25:45 +02:00
karwosts
1bc33a30ec Display version info for custom integrations (#20652)
* Display version info for custom integrations

* no width
2024-04-30 12:23:20 +02:00
krazos
8cca233b7c Update unlock icon for tile card lock features (#20667)
Update unlock icon for tile card lock features so it's easier to see the difference between lock and unlock buttons
2024-04-29 20:53:33 +02:00
karwosts
a78608bfb4 Reorderable card-feature modes (#20647)
* Reorderable card-feature modes

* unused var in getStubConfig
2024-04-29 17:48:01 +02:00
Bram Kragten
e7c1ac94af 20240429.0 (#20665) 2024-04-29 17:44:33 +02:00
Bram Kragten
1a797b3415 Bumped version to 20240429.0 2024-04-29 17:36:46 +02:00
Bram Kragten
2b27a4da2b Show abort reason when no translation (#20664) 2024-04-29 17:35:30 +02:00
Bram Kragten
1df92fa863 Remove strict connections (#20662)
* Remove strict connections

* Update cloud-remote-pref.ts
2024-04-29 16:42:23 +02:00
Bram Kragten
cdde85315a fix list items cloud account (#20663) 2024-04-29 14:26:14 +00:00
Paul Bottein
dc67f9faf4 Fix cloud page design on mobile (#20661) 2024-04-29 16:03:02 +02:00
dependabot[bot]
3ad1be50a2 Bump actions/upload-artifact from 4.3.2 to 4.3.3 (#20658)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-29 12:38:02 +02:00
dependabot[bot]
8aadfe7d28 Bump actions/checkout from 4.1.3 to 4.1.4 (#20659)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-29 12:33:17 +02:00
renovate[bot]
cff54b73a4 Update dependency @lokalise/node-api to v12.4.1 (#20643)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-27 20:01:32 +02:00
Philip Allgaier
b54cfeb0c0 Hide "Browse Media" button for unavailable media players (#20629)
* Hide "Browse Media" button for unavailable media players
2024-04-27 14:36:42 +02:00
renovate[bot]
cefe612b11 Update dependency @octokit/plugin-retry to v7.1.1 (#20641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-27 11:16:55 +02:00
renovate[bot]
4bc874b497 Update workbox monorepo to v7.1.0 (#20642)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-27 11:16:43 +02:00
Philip Allgaier
f3abaa8e02 Align lawn-mower and vacuum more-info layouts (#20632) 2024-04-26 14:07:38 +02:00
Philip Allgaier
21a563fe98 Add details for offset format to sun trigger (#20625)
Add details for offset to sun trigger
2024-04-26 14:05:04 +02:00
Paul Bottein
1acbcccd62 20240426.0 (#20636) 2024-04-26 11:42:26 +02:00
Paul Bottein
35d6c638ab Bumped version to 20240426.0 2024-04-26 11:40:38 +02:00
Bram Kragten
68f8239708 Update cloud remote settings (#20619)
* Update cloud remote settings

* Change again

* Update cloud-remote-pref.ts

* Update UI

* Add missing translations

* use hr and simplify condition

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2024-04-26 11:36:03 +02:00
renovate[bot]
0db64cca0b Update dependency @babel/helper-define-polyfill-provider to v0.6.2 (#20627)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-25 23:37:11 -04:00
renovate[bot]
accfda5f4b Update typescript-eslint monorepo to v7.7.1 (#20628) 2024-04-25 20:51:55 -04:00
Philip Allgaier
c97c20f57d Add mock area registry to demo to fix card picker (#20626) 2024-04-25 18:50:16 +00:00
Philip Allgaier
2725d0191d Disable counter more-info dec/inc buttons when min/max reached (#20624) 2024-04-25 20:49:20 +02:00
renovate[bot]
852cc62398 Update dependency @types/leaflet to v1.9.12 (#20623)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-25 17:56:27 +02:00
David F. Mulcahey
654e3ce437 Fix ZHA UI issues (#20622) 2024-04-25 16:16:00 +02:00
Bram Kragten
20a3a00aec add inital data for language selector (#20620)
* add inital data for language selector

* Update compute-initial-ha-form-data.ts
2024-04-25 15:25:18 +02:00
Bram Kragten
22b927d666 make sure we always have trigger and action (#20621)
* make sure we always have trigger and action

* script too
2024-04-25 15:24:33 +02:00
Philip Allgaier
709d6be2e3 Fix wrong chevron icon direction for groups in data tables (#20617)
Fix chevron icon for groups in data table
2024-04-25 11:28:36 +02:00
Bram Kragten
64f54d9aaa 20240424.1 (#20609) 2024-04-24 14:45:22 +02:00
Bram Kragten
fbda9ca418 Bumped version to 20240424.1 2024-04-24 14:30:36 +02:00
Paul Bottein
4e97e3763e Use theme mode property for ha-map (#20606)
* Use theme mode property for ha-map

* Use theme mode in ha-location-editor

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2024-04-24 14:28:33 +02:00
Bram Kragten
4c9c52d27d Add filter for domains to entity settings (#20605) 2024-04-24 13:53:14 +02:00
Bram Kragten
8712adbf8d 20240424.0 (#20602) 2024-04-24 11:21:24 +02:00
77 changed files with 1670 additions and 881 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.3
uses: actions/checkout@v4.1.4
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.3
uses: actions/checkout@v4.1.4
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.3
uses: actions/checkout@v4.1.4
- 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.3
uses: actions/checkout@v4.1.4
- 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.3
uses: actions/checkout@v4.1.4
- 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.2
uses: actions/upload-artifact@v4.3.3
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.3
uses: actions/checkout@v4.1.4
- 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.2
uses: actions/upload-artifact@v4.3.3
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.3
uses: actions/checkout@v4.1.4
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.3
uses: actions/checkout@v4.1.4
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.3
uses: actions/checkout@v4.1.4
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.3
uses: actions/checkout@v4.1.4
- 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.3
uses: actions/checkout@v4.1.4
- 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.3
uses: actions/checkout@v4.1.4
- 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.2
uses: actions/upload-artifact@v4.3.3
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v4.3.2
uses: actions/upload-artifact@v4.3.3
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.3
uses: actions/checkout@v4.1.4
- 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.3
uses: actions/checkout@v4.1.4
- name: Upload Translations
run: |

View File

@@ -10,6 +10,7 @@ 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";
@@ -23,10 +24,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")
@@ -62,6 +63,7 @@ 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 { mdiStorePlus, mdiUpdate } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { mdiRefresh, mdiStorePlus } from "@mdi/js";
import { CSSResultGroup, LitElement, TemplateResult, css, html } 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=${mdiUpdate}
.path=${mdiRefresh}
.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.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",
"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",
"xss": "1.0.15"
},
"devDependencies": {
"@babel/core": "7.24.4",
"@babel/helper-define-polyfill-provider": "0.6.1",
"@babel/helper-define-polyfill-provider": "0.6.2",
"@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.0",
"@lokalise/node-api": "12.4.1",
"@octokit/auth-oauth-device": "7.1.1",
"@octokit/plugin-retry": "7.1.0",
"@octokit/plugin-retry": "7.1.1",
"@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.11",
"@types/leaflet": "1.9.12",
"@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.0",
"@typescript-eslint/parser": "7.7.0",
"@typescript-eslint/eslint-plugin": "7.7.1",
"@typescript-eslint/parser": "7.7.1",
"@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.0.0"
"workbox-build": "7.1.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 = "20240424.0"
version = "20240501.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

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

View File

@@ -1,4 +1,4 @@
import { mdiArrowDown, mdiArrowUp, mdiChevronDown } from "@mdi/js";
import { mdiArrowDown, mdiArrowUp, mdiChevronUp } from "@mdi/js";
import deepClone from "deep-clone-simple";
import {
CSSResultGroup,
@@ -578,7 +578,7 @@ export class HaDataTable extends LitElement {
@click=${this._collapseGroup}
>
<ha-icon-button
.path=${mdiChevronDown}
.path=${mdiChevronUp}
class=${this._collapsedGroups.includes(groupName)
? "collapsed"
: ""}

View File

@@ -127,6 +127,10 @@ 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

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

View File

@@ -0,0 +1,198 @@
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">
<mwc-list class="ha-scrollbar" multi>
<lit-virtualizer
.items=${this._entities(
this.hass.states,

View File

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

View File

@@ -71,6 +71,10 @@ 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,
@@ -93,7 +97,9 @@ export const computeInitialHaFormData = (
) {
data[field.name] = {};
} else {
throw new Error("Selector not supported in initial form data");
throw new Error(
`Selector ${Object.keys(selector)[0]} not supported in initial form data`
);
}
}
});

View File

@@ -1,13 +1,29 @@
import { FormfieldBase } from "@material/mwc-formfield/mwc-formfield-base";
import { styles } from "@material/mwc-formfield/mwc-formfield.css";
import { css } from "lit";
import { css, html } 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;
@@ -39,6 +55,9 @@ 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

@@ -8,6 +8,9 @@ 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">
@@ -51,7 +54,7 @@ export class HaSettingsRow extends LitElement {
.body[three-line] {
min-height: var(--paper-item-body-three-line-min-height, 88px);
}
.body > * {
:host(:not([wrap-heading])) 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 } from "../../types";
import type { HomeAssistant, ThemeMode } from "../../types";
import "../ha-input-helper-text";
import "./ha-map";
import type { HaMap } from "./ha-map";
@@ -61,7 +61,8 @@ export class HaLocationsEditor extends LitElement {
@property({ type: Number }) public zoom = 16;
@property({ type: Boolean }) public darkMode = false;
@property({ attribute: "theme-mode", type: String })
public themeMode: ThemeMode = "auto";
@state() private _locationMarkers?: Record<string, Marker | Circle>;
@@ -133,7 +134,7 @@ export class HaLocationsEditor extends LitElement {
.layers=${this._getLayers(this._circles, this._locationMarkers)}
.zoom=${this.zoom}
.autoFit=${this.autoFit}
?forceDarkMode=${this.darkMode}
.themeMode=${this.themeMode}
></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,
LatLngTuple,
LatLngExpression,
LatLngTuple,
Layer,
Map,
Marker,
Polyline,
} from "leaflet";
import { isToday } from "date-fns";
import { css, CSSResultGroup, PropertyValues, ReactiveElement } from "lit";
import { CSSResultGroup, PropertyValues, ReactiveElement, css } 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 } from "../../types";
import { HomeAssistant, ThemeMode } from "../../types";
import { isTouch } from "../../util/is_touch";
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,9 +69,8 @@ export class HaMap extends ReactiveElement {
@property({ type: Boolean }) public fitZones = false;
@property({ type: Boolean }) public forceDarkMode = false;
@property({ type: Boolean }) public forceLightMode = false;
@property({ attribute: "theme-mode", type: String })
public themeMode: ThemeMode = "auto";
@property({ type: Number }) public zoom = 14;
@@ -156,8 +155,7 @@ export class HaMap extends ReactiveElement {
}
if (
!changedProps.has("forceDarkMode") &&
!changedProps.has("forceLightMode") &&
!changedProps.has("themeMode") &&
(!changedProps.has("hass") ||
(oldHass && oldHass.themes?.darkMode === this.hass.themes?.darkMode))
) {
@@ -166,14 +164,18 @@ 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", darkMode);
map!.classList.toggle("forced-dark", this.forceDarkMode);
map!.classList.toggle("forced-light", this.forceLightMode);
map!.classList.toggle("dark", this._darkMode);
map!.classList.toggle("forced-dark", this.themeMode === "dark");
map!.classList.toggle("forced-light", this.themeMode === "light");
}
private async _loadMap(): Promise<void> {
@@ -403,13 +405,7 @@ export class HaMap extends ReactiveElement {
"--dark-primary-color"
);
const className = this.forceLightMode
? "light"
: this.forceDarkMode
? "dark"
: this.hass.themes.darkMode
? "dark"
: "light";
const className = this._darkMode ? "dark" : "light";
for (const entity of this.entities) {
const stateObj = hass.states[getEntityId(entity)];

View File

@@ -11,6 +11,7 @@ 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";
@@ -96,3 +97,9 @@ 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,6 +44,7 @@ export interface IntegrationManifest {
| "local_polling"
| "local_push";
single_config_entry?: boolean;
version?: string;
}
export interface IntegrationSetup {
domain: string;

View File

@@ -118,6 +118,10 @@ 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 | "";
): TemplateResult | string;
renderShowFormStepHeader(
hass: HomeAssistant,

View File

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

View File

@@ -16,21 +16,23 @@ 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}
.disabled=${disabled ||
Number(this.stateObj.state) === this.stateObj.attributes.maximum}
>
${this.hass!.localize("ui.card.counter.actions.increment")}
</mwc-button>
<mwc-button
.action=${"decrement"}
@click=${this._handleActionClick}
.disabled=${disabled}
.disabled=${disabled ||
Number(this.stateObj.state) === this.stateObj.attributes.minimum}
>
${this.hass!.localize("ui.card.counter.actions.decrement")}
</mwc-button>

View File

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

View File

@@ -19,6 +19,7 @@ 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,
@@ -62,7 +63,8 @@ class MoreInfoMediaPlayer extends LitElement {
`
)}
</div>
${supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA)
${!isUnavailableState(stateObj.state) &&
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">
<div class="flex-horizontal space-around">
${VACUUM_COMMANDS.filter((item) =>
item.isVisible(stateObj)
).map(
@@ -327,6 +327,9 @@ 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 mql = matchMedia("(prefers-color-scheme: dark)");
const darkMql = 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"
.darkMode=${mql.matches}
.themeMode=${darkMql.matches ? "dark" : "light"}
.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 twoline>
<ha-list-item noninteractive 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>
<ha-list-item noninteractive>
${this.hass.localize(
"ui.panel.config.cloud.account.connection_status"
)}:
@@ -187,6 +187,7 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
<cloud-remote-pref
.hass=${this.hass}
.narrow=${this.narrow}
.cloudStatus=${this.cloudStatus}
></cloud-remote-pref>

View File

@@ -1,12 +1,13 @@
import { mdiContentCopy, mdiHelpCircle } from "@mdi/js";
import { mdiContentCopy, mdiEye, mdiEyeOff, mdiHelpCircle } from "@mdi/js";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } 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
@@ -21,7 +22,6 @@ 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,6 +29,10 @@ 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;
@@ -109,36 +113,70 @@ export class CloudRemotePref extends LitElement {
)}
></ha-alert>
`
: ""}
${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>
: 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>
<ha-expansion-panel
outlined
.header=${this.hass.localize(
"ui.panel.config.cloud.account.remote.advanced_options"
"ui.panel.config.cloud.account.remote.security_options"
)}
>
<ha-settings-row>
<ha-settings-row wrap-heading>
<span slot="heading"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.external_activation"
@@ -154,62 +192,8 @@ export class CloudRemotePref extends LitElement {
@change=${this._toggleAllowRemoteEnabledChanged}
></ha-switch>
</ha-settings-row>
<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>
<hr />
<ha-settings-row .narrow=${this.narrow}>
<span slot="heading"
>${this.hass.localize(
"ui.panel.config.cloud.account.remote.certificate_info"
@@ -249,6 +233,10 @@ export class CloudRemotePref extends LitElement {
});
}
private _toggleUnmaskedUrl(): void {
this._unmaskedUrl = !this._unmaskedUrl;
}
private async _toggleChanged(ev) {
const toggle = ev.target as HaSwitch;
@@ -279,18 +267,6 @@ 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);
@@ -299,40 +275,6 @@ 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 {
@@ -343,8 +285,8 @@ export class CloudRemotePref extends LitElement {
}
.header-actions {
position: absolute;
right: 24px;
inset-inline-end: 24px;
right: 16px;
inset-inline-end: 16px;
inset-inline-start: initial;
top: 24px;
display: flex;
@@ -378,17 +320,51 @@ export class CloudRemotePref extends LitElement {
.card-actions a {
text-decoration: none;
}
ha-svg-icon {
--mdc-icon-size: 18px;
color: var(--secondary-text-color);
cursor: pointer;
ha-expansion-panel {
margin-top: 16px;
}
ha-formfield {
margin-top: 8px;
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;
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;
}
`;
}
}

View File

@@ -188,6 +188,7 @@ 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, mdiUpdate } from "@mdi/js";
import { mdiDotsVertical, mdiRefresh } from "@mdi/js";
import { HassEntities } from "home-assistant-js-websocket";
import { css, html, LitElement, TemplateResult } from "lit";
import { LitElement, TemplateResult, css, html } 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 {
fetchHassioSupervisorInfo,
HassioSupervisorInfo,
SupervisorOptions,
fetchHassioSupervisorInfo,
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=${mdiUpdate}
.path=${mdiRefresh}
@click=${this._checkUpdates}
></ha-icon-button>
<ha-button-menu multi>

View File

@@ -4,7 +4,7 @@ import {
mdiDotsVertical,
mdiMagnify,
mdiPower,
mdiUpdate,
mdiRefresh,
} 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=${mdiUpdate}></ha-svg-icon>
<ha-svg-icon slot="graphic" .path=${mdiRefresh}></ha-svg-icon>
</ha-list-item>
<ha-list-item graphic="icon">

View File

@@ -53,6 +53,7 @@ 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";
@@ -115,6 +116,9 @@ export interface EntityRow extends StateEntity {
localized_platform: string;
domain: string;
label_entries: LabelRegistryEntry[];
enabled: string;
visible: string;
available: string;
}
@customElement("ha-config-entities")
@@ -197,20 +201,36 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
private _states = memoize((localize: LocalizeFunc) => [
{
value: "disabled",
label: localize("ui.panel.config.entities.picker.status.disabled"),
},
{
value: "hidden",
label: localize("ui.panel.config.entities.picker.status.hidden"),
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.readonly"),
label: localize("ui.panel.config.entities.picker.status.unmanageable"),
},
{
value: "restored",
label: localize("ui.panel.config.entities.picker.status.not_provided"),
},
]);
@@ -309,7 +329,6 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
type: "icon",
sortable: true,
filterable: true,
groupable: true,
width: "68px",
template: (entry) =>
entry.unavailable ||
@@ -338,7 +357,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.restored"
"ui.panel.config.entities.picker.status.not_provided"
)
: entry.unavailable
? this.hass.localize(
@@ -353,13 +372,31 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
"ui.panel.config.entities.picker.status.hidden"
)
: this.hass.localize(
"ui.panel.config.entities.picker.status.readonly"
"ui.panel.config.entities.picker.status.unmanageable"
)}
</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,
@@ -388,18 +425,24 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
const stateFilters = filters["ha-filter-states"]?.value;
const showReadOnly =
!stateFilters?.length || stateFilters.includes("readonly");
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");
let filteredEntities = showReadOnly
? entities.concat(stateEntities)
: entities;
let filteredEntities = entities.concat(stateEntities);
let filteredConfigEntry: ConfigEntry | undefined;
const filteredDomains = new Set<string>();
@@ -443,6 +486,10 @@ 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))
@@ -454,26 +501,29 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
}
});
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 (!showUnavailable && unavailable) {
if (
!(
(showAvailable && available) ||
(showUnavailable && unavailable) ||
(showRestored && restored) ||
(showVisible && !hidden) ||
(showHidden && hidden) ||
(showDisabled && disabled) ||
(showEnabled && !disabled) ||
(showReadOnly && readonly)
)
) {
continue;
}
@@ -495,21 +545,30 @@ 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.restored")
? localize("ui.panel.config.entities.picker.status.not_provided")
: unavailable
? localize("ui.panel.config.entities.picker.status.unavailable")
: entry.disabled_by
: disabled
? localize("ui.panel.config.entities.picker.status.disabled")
: entry.hidden_by
: hidden
? localize("ui.panel.config.entities.picker.status.hidden")
: entry.readonly
: readonly
? localize(
"ui.panel.config.entities.picker.status.readonly"
"ui.panel.config.entities.picker.status.unmanageable"
)
: 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"),
});
}
@@ -782,6 +841,15 @@ ${
.narrow=${this.narrow}
@expanded-changed=${this._filterExpanded}
></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}
@@ -847,7 +915,7 @@ ${
protected firstUpdated() {
this._filters = {
"ha-filter-states": {
value: ["unavailable", "readonly"],
value: ["enabled"],
items: undefined,
},
};
@@ -862,10 +930,7 @@ ${
this._filters = {
...this._filters,
"ha-filter-states": {
value: [
...(this._filters["ha-filter-states"]?.value || []),
"disabled",
],
value: [],
items: undefined,
},
"ha-filter-integrations": {
@@ -878,10 +943,7 @@ ${
this._filters = {
...this._filters,
"ha-filter-states": {
value: [
...(this._filters["ha-filter-states"]?.value || []),
"disabled",
],
value: [],
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.readonly"
"ui.panel.config.entities.picker.status.unmanageable"
)}
</simple-tooltip>
</div>

View File

@@ -269,6 +269,9 @@ 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
@@ -554,18 +557,22 @@ 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
)
.then(
(localize) =>
localize(
`component.${item.domain}.exceptions.${item.error_reason_translation_key}.message`,
item.error_reason_translation_placeholders ?? undefined
) || item.reason
);
stateTextExtra = html`${until(lokalisePromExc)}`;
} else {
const lokalisePromError = this.hass
.loadBackendTranslation("config", item.domain)
.then((localize) =>
localize(`component.${item.domain}.config.error.${item.reason}`)
.then(
(localize) =>
localize(
`component.${item.domain}.config.error.${item.reason}`
) || item.reason
);
stateTextExtra = html`${until(lokalisePromError, item.reason)}`;
}
@@ -1404,6 +1411,12 @@ 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,6 +47,7 @@ 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.05,
springConstant: 0,
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

@@ -112,14 +112,14 @@ class PanelEnergy extends LitElement {
</hui-energy-period-selector>
</div>
</div>
<hui-view
id="view"
.hass=${this.hass}
.narrow=${this.narrow}
.lovelace=${this._lovelace}
.index=${this._viewIndex}
@reload-energy-panel=${this._reloadView}
></hui-view>
<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>
`;
}
@@ -401,12 +401,10 @@ class PanelEnergy extends LitElement {
min-height: 100vh;
box-sizing: border-box;
padding-left: env(safe-area-inset-left);
padding-inline-start: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
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

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

View File

@@ -16,12 +16,14 @@ import {
AlarmControlPanelEntity,
AlarmMode,
ALARM_MODES,
supportedAlarmModes,
} from "../../../data/alarm_control_panel";
import { UNAVAILABLE } from "../../../data/entity";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { AlarmModesCardFeatureConfig } from "./types";
import { showEnterCodeDialog } from "../../../dialogs/enter-code/show-enter-code-dialog";
import { filterModes } from "./common/filter-modes";
export const supportsAlarmModesCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -164,9 +166,12 @@ class HuiAlarmModeCardFeature
const color = stateColorCss(this.stateObj);
const modes = this._modes(this.stateObj, this._config.modes);
const supportedModes = supportedAlarmModes(this.stateObj);
const options = modes.map<ControlSelectOption>((mode) => ({
const options = filterModes(
supportedModes,
this._config.modes
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.localize(`ui.card.alarm_control_panel.modes.${mode}`),
path: ALARM_MODES[mode].path,
@@ -196,7 +201,7 @@ class HuiAlarmModeCardFeature
)}
style=${styleMap({
"--control-select-color": color,
"--modes-count": modes.length.toString(),
"--modes-count": options.length.toString(),
})}
.disabled=${this.stateObj!.state === UNAVAILABLE}
>

View File

@@ -15,6 +15,7 @@ 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);
@@ -40,14 +41,10 @@ class HuiClimateFanModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(
_,
stateObj?: HassEntity
): ClimateFanModesCardFeatureConfig {
static getStubConfig(): ClimateFanModesCardFeatureConfig {
return {
type: "climate-fan-modes",
style: "dropdown",
fan_modes: stateObj?.attributes.fan_modes || [],
};
}
@@ -122,25 +119,24 @@ class HuiClimateFanModesCardFeature
const stateObj = this.stateObj;
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>`,
}));
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>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -19,6 +19,7 @@ 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) => {
@@ -42,13 +43,9 @@ class HuiClimateHvacModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(
_,
stateObj?: HassEntity
): ClimateHvacModesCardFeatureConfig {
static getStubConfig(): ClimateHvacModesCardFeatureConfig {
return {
type: "climate-hvac-modes",
hvac_modes: stateObj?.attributes.hvac_modes || [],
};
}
@@ -122,21 +119,23 @@ class HuiClimateHvacModesCardFeature
const color = stateColorCss(this.stateObj);
const modes = this._config.hvac_modes || [];
const ordererHvacModes = (this.stateObj.attributes.hvac_modes || [])
.concat()
.sort(compareClimateHvacModes);
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>
`,
}));
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>
`,
}));
if (this._config.style === "dropdown") {
return html`

View File

@@ -15,6 +15,7 @@ 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);
@@ -40,14 +41,10 @@ class HuiClimatePresetModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(
_,
stateObj?: HassEntity
): ClimatePresetModesCardFeatureConfig {
static getStubConfig(): ClimatePresetModesCardFeatureConfig {
return {
type: "climate-preset-modes",
style: "dropdown",
preset_modes: stateObj?.attributes.preset_modes || [],
};
}
@@ -124,25 +121,24 @@ class HuiClimatePresetModesCardFeature
const stateObj = this.stateObj;
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>`,
}));
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>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -15,6 +15,7 @@ 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);
@@ -40,14 +41,10 @@ class HuiClimateSwingModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(
_,
stateObj?: HassEntity
): ClimateSwingModesCardFeatureConfig {
static getStubConfig(): ClimateSwingModesCardFeatureConfig {
return {
type: "climate-swing-modes",
style: "dropdown",
swing_modes: stateObj?.attributes.swing_modes || [],
};
}
@@ -124,25 +121,24 @@ class HuiClimateSwingModesCardFeature
const stateObj = this.stateObj;
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>`,
}));
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>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -15,6 +15,7 @@ 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);
@@ -39,14 +40,10 @@ class HuiFanPresetModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(
_,
stateObj?: HassEntity
): FanPresetModesCardFeatureConfig {
static getStubConfig(): FanPresetModesCardFeatureConfig {
return {
type: "fan-preset-modes",
style: "dropdown",
preset_modes: stateObj?.attributes.preset_modes || [],
};
}
@@ -121,25 +118,24 @@ class HuiFanPresetModesCardFeature
const stateObj = this.stateObj;
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>`,
}));
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>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -18,6 +18,7 @@ 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);
@@ -43,14 +44,10 @@ class HuiHumidifierModesCardFeature
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
static getStubConfig(
_,
stateObj?: HassEntity
): HumidifierModesCardFeatureConfig {
static getStubConfig(): HumidifierModesCardFeatureConfig {
return {
type: "humidifier-modes",
style: "dropdown",
modes: stateObj?.attributes.available_modes || [],
};
}
@@ -125,25 +122,24 @@ class HuiHumidifierModesCardFeature
const stateObj = this.stateObj;
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>`,
}));
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>`,
}));
if (this._config.style === "icons") {
return html`

View File

@@ -1,4 +1,4 @@
import { mdiLock, mdiLockOpen } from "@mdi/js";
import { mdiLock, mdiLockOpenVariant } 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=${mdiLockOpen}></ha-svg-icon>
<ha-svg-icon .path=${mdiLockOpenVariant}></ha-svg-icon>
</ha-control-button>
</ha-control-button-group>
`;

View File

@@ -9,8 +9,9 @@ import { UNAVAILABLE } from "../../../data/entity";
import { InputSelectEntity } from "../../../data/input_select";
import { SelectEntity } from "../../../data/select";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature } from "../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { SelectOptionsCardFeatureConfig } from "./types";
import { filterModes } from "./common/filter-modes";
export const supportsSelectOptionsCardFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id);
@@ -41,6 +42,13 @@ 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");
@@ -105,6 +113,11 @@ 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
@@ -118,7 +131,7 @@ class HuiSelectOptionsCardFeature
@selected=${this._valueChanged}
@closed=${stopPropagation}
>
${stateObj.attributes.options!.map(
${options.map(
(option) => html`
<ha-list-item .value=${option}>
${this.hass!.formatEntityState(stateObj, option)}

View File

@@ -19,6 +19,7 @@ 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
@@ -40,13 +41,9 @@ class HuiWaterHeaterOperationModeCardFeature
@state() _currentOperationMode?: OperationMode;
static getStubConfig(
_,
stateObj?: HassEntity
): WaterHeaterOperationModesCardFeatureConfig {
static getStubConfig(): WaterHeaterOperationModesCardFeatureConfig {
return {
type: "water-heater-operation-modes",
operation_modes: stateObj?.attributes.operation_list || [],
};
}
@@ -107,16 +104,18 @@ class HuiWaterHeaterOperationModeCardFeature
const color = stateColorCss(this.stateObj);
const modes = this._config.operation_modes || [];
const orderedModes = (this.stateObj.attributes.operation_list || [])
.concat()
.sort(compareWaterHeaterOperationMode);
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),
}));
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),
}));
return html`
<div class="container">

View File

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

View File

@@ -159,6 +159,9 @@ 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">
@@ -169,9 +172,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
.paths=${this._getHistoryPaths(this._config, this._stateHistory)}
.autoFit=${this._config.auto_fit || false}
.fitZones=${this._config.fit_zones}
?forceDarkMode=${this._config.theme_mode === "dark" ||
this._config.dark_mode}
?forceLightMode=${this._config.theme_mode === "light"}
.themeMode=${themeMode}
interactiveZones
renderPassive
></ha-map>

View File

@@ -3,17 +3,23 @@ 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 type { SchemaUnion } from "../../../../components/ha-form/types";
import { AlarmMode, ALARM_MODES } from "../../../../data/alarm_control_panel";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import { supportedAlarmModes } from "../../../../data/alarm_control_panel";
import type { HomeAssistant } from "../../../../types";
import {
LovelaceCardFeatureContext,
AlarmModesCardFeatureConfig,
LovelaceCardFeatureContext,
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
import "../../../../components/ha-form/ha-form";
type AlarmModesCardFeatureData = AlarmModesCardFeatureConfig & {
customize_modes: boolean;
};
@customElement("hui-alarm-modes-card-feature-editor")
export class HuiAlarmModesCardFeatureEditor
@@ -31,31 +37,40 @@ export class HuiAlarmModesCardFeatureEditor
}
private _schema = memoizeOne(
(localize: LocalizeFunc, stateObj?: HassEntity) =>
(
localize: LocalizeFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
name: "modes",
name: "customize_modes",
selector: {
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}`
)}`,
})),
},
boolean: {},
},
},
] as const
...(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[]
);
protected render() {
@@ -63,16 +78,25 @@ 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);
const schema = this._schema(
this.hass.localize,
stateObj,
data.customize_modes
);
return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.data=${data}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
@@ -81,7 +105,21 @@ export class HuiAlarmModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
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 });
}
private _computeLabelCallback = (
@@ -89,13 +127,12 @@ 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 this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
return "";
}
};
}

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",
"cover-tilt-position",
"cover-tilt",
"fan-preset-modes",
"fan-speed",
"humidifier-modes",
@@ -82,14 +82,15 @@ type UiFeatureTypes = (typeof UI_FEATURE_TYPES)[number];
const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
"alarm-modes",
"climate-hvac-modes",
"climate-fan-modes",
"climate-swing-modes",
"climate-hvac-modes",
"climate-preset-modes",
"climate-swing-modes",
"fan-preset-modes",
"humidifier-modes",
"lawn-mower-commands",
"numeric-input",
"select-options",
"update-actions",
"vacuum-commands",
"water-heater-operation-modes",

View File

@@ -17,6 +17,10 @@ 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
@@ -36,7 +40,8 @@ export class HuiClimateFanModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj?: HassEntity
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
@@ -55,19 +60,33 @@ export class HuiClimateFanModesCardFeatureEditor
},
},
{
name: "fan_modes",
name: "customize_modes",
selector: {
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.fan_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(stateObj, "fan_mode", mode),
})) || [],
},
boolean: {},
},
},
...(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[]
);
@@ -80,16 +99,17 @@ export class HuiClimateFanModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: ClimateFanModesCardFeatureConfig = {
const data: ClimateFanModesCardFeatureData = {
style: "dropdown",
fan_modes: [],
...this._config,
customize_modes: this._config.fan_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj
stateObj,
data.customize_modes
);
return html`
@@ -104,7 +124,21 @@ export class HuiClimateFanModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
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 });
}
private _computeLabelCallback = (
@@ -113,6 +147,7 @@ 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,8 +6,11 @@ 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 { SchemaUnion } from "../../../../components/ha-form/types";
import { HVAC_MODES } from "../../../../data/climate";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import { compareClimateHvacModes } from "../../../../data/climate";
import type { HomeAssistant } from "../../../../types";
import {
ClimateHvacModesCardFeatureConfig,
@@ -15,6 +18,10 @@ 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
@@ -34,7 +41,8 @@ export class HuiClimateHvacModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityState: FormatEntityStateFunc,
stateObj?: HassEntity
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
@@ -53,21 +61,34 @@ export class HuiClimateHvacModesCardFeatureEditor
},
},
{
name: "hvac_modes",
name: "customize_modes",
selector: {
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,
})),
},
boolean: {},
},
},
] as const
...(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[]
);
protected render() {
@@ -79,16 +100,17 @@ export class HuiClimateHvacModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: ClimateHvacModesCardFeatureConfig = {
const data: ClimateHvacModesCardFeatureData = {
style: "icons",
hvac_modes: [],
...this._config,
customize_modes: this._config.hvac_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityState,
stateObj
stateObj,
data.customize_modes
);
return html`
@@ -103,7 +125,24 @@ export class HuiClimateHvacModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
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 });
}
private _computeLabelCallback = (
@@ -112,6 +151,7 @@ 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,6 +17,10 @@ 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
@@ -36,7 +40,8 @@ export class HuiClimatePresetModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj?: HassEntity
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
@@ -55,23 +60,33 @@ export class HuiClimatePresetModesCardFeatureEditor
},
},
{
name: "preset_modes",
name: "customize_modes",
selector: {
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.preset_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"preset_mode",
mode
),
})) || [],
},
boolean: {},
},
},
...(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[]
);
@@ -84,16 +99,17 @@ export class HuiClimatePresetModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: ClimatePresetModesCardFeatureConfig = {
const data: ClimatePresetModesCardFeatureData = {
style: "dropdown",
preset_modes: [],
...this._config,
customize_modes: this._config.preset_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj
stateObj,
data.customize_modes
);
return html`
@@ -108,7 +124,21 @@ export class HuiClimatePresetModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
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 });
}
private _computeLabelCallback = (
@@ -117,6 +147,7 @@ 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,6 +17,10 @@ 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
@@ -36,7 +40,8 @@ export class HuiClimateSwingModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj?: HassEntity
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
@@ -55,23 +60,33 @@ export class HuiClimateSwingModesCardFeatureEditor
},
},
{
name: "swing_modes",
name: "customize_modes",
selector: {
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.swing_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"swing_mode",
mode
),
})) || [],
},
boolean: {},
},
},
...(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[]
);
@@ -84,16 +99,17 @@ export class HuiClimateSwingModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: ClimateSwingModesCardFeatureConfig = {
const data: ClimateSwingModesCardFeatureData = {
style: "dropdown",
swing_modes: [],
...this._config,
customize_modes: this._config.swing_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj
stateObj,
data.customize_modes
);
return html`
@@ -108,7 +124,21 @@ export class HuiClimateSwingModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
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 });
}
private _computeLabelCallback = (
@@ -117,6 +147,7 @@ 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,6 +17,10 @@ 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
@@ -36,7 +40,8 @@ export class HuiFanPresetModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj?: HassEntity
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
@@ -55,23 +60,33 @@ export class HuiFanPresetModesCardFeatureEditor
},
},
{
name: "preset_modes",
name: "customize_modes",
selector: {
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.preset_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(
stateObj,
"preset_mode",
mode
),
})) || [],
},
boolean: {},
},
},
...(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[]
);
@@ -84,16 +99,17 @@ export class HuiFanPresetModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: FanPresetModesCardFeatureConfig = {
const data: FanPresetModesCardFeatureData = {
style: "dropdown",
preset_modes: [],
...this._config,
customize_modes: this._config.preset_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj
stateObj,
data.customize_modes
);
return html`
@@ -108,7 +124,21 @@ export class HuiFanPresetModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
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 });
}
private _computeLabelCallback = (
@@ -117,6 +147,7 @@ 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,6 +17,10 @@ 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
@@ -36,7 +40,8 @@ export class HuiHumidifierModesCardFeatureEditor
(
localize: LocalizeFunc,
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
stateObj?: HassEntity
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
@@ -55,19 +60,33 @@ export class HuiHumidifierModesCardFeatureEditor
},
},
{
name: "modes",
name: "customize_modes",
selector: {
select: {
multiple: true,
mode: "list",
options:
stateObj?.attributes.available_modes?.map((mode) => ({
value: mode,
label: formatEntityAttributeValue(stateObj, "mode", mode),
})) || [],
},
boolean: {},
},
},
...(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[]
);
@@ -80,16 +99,17 @@ export class HuiHumidifierModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const data: HumidifierModesCardFeatureConfig = {
const data: HumidifierModesCardFeatureData = {
style: "dropdown",
modes: [],
...this._config,
customize_modes: this._config.modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityAttributeValue,
stateObj
stateObj,
data.customize_modes
);
return html`
@@ -104,7 +124,21 @@ export class HuiHumidifierModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
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 });
}
private _computeLabelCallback = (
@@ -113,6 +147,7 @@ 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

@@ -0,0 +1,140 @@
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,14 +5,22 @@ 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 { SchemaUnion } from "../../../../components/ha-form/types";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import {
WaterHeaterOperationModesCardFeatureConfig,
LovelaceCardFeatureContext,
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
import { OPERATION_MODES } from "../../../../data/water_heater";
import { compareWaterHeaterOperationMode } from "../../../../data/water_heater";
type WaterHeaterOperationModesCardFeatureData =
WaterHeaterOperationModesCardFeatureConfig & {
customize_modes: boolean;
};
@customElement("hui-water-heater-operation-modes-card-feature-editor")
export class HuiWaterHeaterOperationModesCardFeatureEditor
@@ -30,24 +38,41 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
}
private _schema = memoizeOne(
(formatEntityState: FormatEntityStateFunc, stateObj?: HassEntity) =>
(
formatEntityState: FormatEntityStateFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
name: "operation_modes",
name: "customize_modes",
selector: {
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,
})),
},
boolean: {},
},
},
] as const
...(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[]
);
protected render() {
@@ -59,12 +84,21 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
? this.hass.states[this.context?.entity_id]
: undefined;
const schema = this._schema(this.hass.formatEntityState, stateObj);
const data: WaterHeaterOperationModesCardFeatureData = {
...this._config,
customize_modes: this._config.operation_modes !== undefined,
};
const schema = this._schema(
this.hass.formatEntityState,
stateObj,
data.customize_modes
);
return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.data=${data}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
@@ -73,7 +107,23 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
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 });
}
private _computeLabelCallback = (
@@ -81,13 +131,12 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
) => {
switch (schema.name) {
case "operation_modes":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.water-heater-modes.${schema.name}`
`ui.panel.lovelace.editor.features.types.water-heater-operation-modes.${schema.name}`
);
default:
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
return "";
}
};
}

View File

@@ -886,9 +886,9 @@ class HUIRoot extends LitElement {
const configBackground = viewConfig.background || this.config.background;
if (configBackground) {
this.style.setProperty("--lovelace-background", configBackground);
root.style.setProperty("--lovelace-background", configBackground);
} else {
this.style.removeProperty("--lovelace-background");
root.style.removeProperty("--lovelace-background");
}
root.appendChild(view);
@@ -1013,8 +1013,6 @@ 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,6 +143,11 @@ export class HUIView extends ReactiveElement {
return this;
}
public connectedCallback(): void {
super.connectedCallback();
this._applyTheme();
}
public willUpdate(changedProperties: PropertyValues): void {
super.willUpdate(changedProperties);
@@ -212,7 +217,7 @@ export class HUIView extends ReactiveElement {
this.hass.themes !== oldHass.themes ||
this.hass.selectedTheme !== oldHass.selectedTheme
) {
applyThemesOnElement(this, this.hass.themes, this._viewConfigTheme);
this._applyTheme();
}
}
if (changedProperties.has("narrow")) {
@@ -238,6 +243,28 @@ 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,6 +1889,7 @@
"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",
@@ -2941,7 +2942,7 @@
"event": "[%key:ui::panel::config::automation::editor::triggers::type::homeassistant::event%]",
"sunrise": "Sunrise",
"sunset": "Sunset",
"offset": "Offset (optional)",
"offset": "Offset in seconds or HH:MM:SS (optional)",
"description": {
"picker": "When the sun sets or rises.",
"sets": "When the sun sets{hasDuration, select, \n true { offset by {duration}} \n other {}\n }",
@@ -3821,38 +3822,42 @@
}
},
"remote": {
"title": "Remote control",
"title": "Remote access",
"connected": "Connected",
"not_connected": "Not connected",
"reconnecting": "Not connected. Trying to reconnect.",
"access_is_being_prepared": "Remote control is being prepared. We will notify you when it's ready.",
"access_is_being_prepared": "Remote access 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 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",
"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.",
"link_learn_how_it_works": "Learn how it works",
"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.",
"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.",
"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.",
"strict_connection_copy_link": "Copy link",
"certificate_info": "Certificate info",
"certificate_expire": "Will be renewed at {date}",
"more_info": "More info"
"certificate_info": "Certificate information",
"certificate_expire": "Certificate renewal at {date}",
"more_info": "More details"
},
"alexa": {
"title": "Alexa",
@@ -4057,12 +4062,14 @@
"search": "Search {number} entities",
"unnamed_entity": "Unnamed entity",
"status": {
"restored": "Restored",
"available": "Available",
"unavailable": "Unavailable",
"enabled": "Enabled",
"disabled": "Disabled",
"readonly": "Read-only",
"hidden": "Hidden"
"visible": "Visible",
"hidden": "Hidden",
"not_provided": "Not provided",
"unmanageable": "Unmanageable"
},
"headers": {
"state_icon": "State icon",
@@ -4072,7 +4079,10 @@
"area": "Area",
"disabled_by": "Disabled by",
"status": "Status",
"domain": "Domain"
"domain": "Domain",
"availability": "Availability",
"visibility": "Visibility",
"enabled": "Enabled"
},
"selected": "{number} selected",
"enable_selected": {
@@ -5960,7 +5970,8 @@
"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"
@@ -5992,16 +6003,18 @@
"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%]"
},
"swing_modes": "Swing modes"
"customize_modes": "Customize swing modes"
},
"climate-hvac-modes": {
"label": "Climate HVAC modes",
@@ -6010,7 +6023,8 @@
"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",
@@ -6019,6 +6033,7 @@
"dropdown": "Dropdown",
"icons": "Icons"
},
"customize_modes": "Customize preset modes",
"preset_modes": "Preset modes"
},
"fan-preset-modes": {
@@ -6028,6 +6043,7 @@
"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": {
@@ -6040,10 +6056,13 @@
"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"
"label": "Select options",
"options": "Options",
"customize_options": "Customize options"
},
"numeric-input": {
"label": "Numeric input",
@@ -6061,7 +6080,8 @@
},
"water-heater-operation-modes": {
"label": "Water heater operation modes",
"operation_modes": "Operation modes"
"operation_modes": "Operation modes",
"customize_modes": "Customize 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.11.1, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.0":
"@babel/core@npm:7.24.4, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.0, @babel/core@npm:^7.24.4":
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.1, @babel/helper-define-polyfill-provider@npm:^0.6.1":
version: 0.6.1
resolution: "@babel/helper-define-polyfill-provider@npm:0.6.1"
"@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"
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/316e7c0f05d2ae233d5fbb622c6339436da8d2b2047be866b64a16e6996c078a23b4adfebbdb33bc6a9882326a6cc20b95daa79a5e0edc92e9730e36d45fa523
checksum: 10/bb32ec12024d3f16e70641bc125d2534a97edbfdabbc9f69001ec9c4ce46f877c7a224c566aa6c8c510c3b0def2e43dc4433bf6a40896ba5ce0cef4ea5ccbcff
languageName: node
linkType: hard
@@ -2173,10 +2173,10 @@ __metadata:
languageName: node
linkType: hard
"@lokalise/node-api@npm:12.4.0":
version: 12.4.0
resolution: "@lokalise/node-api@npm:12.4.0"
checksum: 10/5e44b23e7139487961f291dcea4f7082fe86d8b017142890831f0ac83338d7f749565e7f96a628eb0f594d894abd905dfec1c31c140fd07bcf0c120db118ee15
"@lokalise/node-api@npm:12.4.1":
version: 12.4.1
resolution: "@lokalise/node-api@npm:12.4.1"
checksum: 10/54cf96a759ea0642b5ad13246cc281c2cfa1592d44752e58be996177c48a66038e8f513d509d17db35eeef2e7ad2ac243a375b628a40f36e88ca015ab5f89367
languageName: node
linkType: hard
@@ -3389,16 +3389,16 @@ __metadata:
languageName: node
linkType: hard
"@octokit/plugin-retry@npm:7.1.0":
version: 7.1.0
resolution: "@octokit/plugin-retry@npm:7.1.0"
"@octokit/plugin-retry@npm:7.1.1":
version: 7.1.1
resolution: "@octokit/plugin-retry@npm:7.1.1"
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/c445c76daa37c38fd095a280f28c6e9afbfdaab8290f0ca17253e3553b3fcf55f028ae72c923440e1a06b87e4e04f24d8a5a4976f6a7494d33d368ec4518a7de
checksum: 10/ff7d2f0b45e61ff688c213ad28670fceffa8d56603850beabab21cbb79021d91d8bf40cdc2062949eeb6b031c6e5ad39bcbbc40b0caa7924c81d3c90b8fb0843
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,22 +3830,6 @@ __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"
@@ -3889,6 +3873,22 @@ __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.11":
version: 1.9.11
resolution: "@types/leaflet@npm:1.9.11"
"@types/leaflet@npm:*, @types/leaflet@npm:1.9.12":
version: 1.9.12
resolution: "@types/leaflet@npm:1.9.12"
dependencies:
"@types/geojson": "npm:*"
checksum: 10/a7f3936b83f1007fa74f65eee7a905e582966c3218d3a45ad1c713445038e69cdefeb668c8f0cb70bc293e77d3d801b299a90bc2fd33e52ff90fd93f342108a2
checksum: 10/ff6dce2f613b97bdc3ceb929e6eeaaa8bef8bbafdf9758935b1d679cbaf76360e366080d77e42da58e41aac146434c5d18c70ec919d37e01e0592f0a4f2e967e
languageName: node
linkType: hard
@@ -4574,15 +4574,15 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/eslint-plugin@npm:7.7.0"
"@typescript-eslint/eslint-plugin@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/eslint-plugin@npm:7.7.1"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@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"
"@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"
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/9e6b6fbb9920581813c01daaa2f89419c3476e42823755c0627f4491640cfaffaebeb0592231ed4f318eefadfcdd4560b77b2903d66ab4e0c8df746a7037a603
checksum: 10/54064fe466edcebece50cf4cfc4cb18753bcba7da0e3f0db29bf628586716b14945cadf01529ebc3d823e35bc62debf21aa636ae1f5e4fa92670dce65b3dec8c
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/parser@npm:7.7.0"
"@typescript-eslint/parser@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/parser@npm:7.7.1"
dependencies:
"@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"
"@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"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.56.0
peerDependenciesMeta:
typescript:
optional: true
checksum: 10/9f8c53ca29af09cd366e37420410319c8f69e9f4a676513ecd91f5e6d822b9935b6a8ad7ec931d604fc4a0ecd93d51063d0c93227f78f2380196c8a7fa6970d1
checksum: 10/39cd5c686e9f7e86da669fc3622b203e1025f162d42c4f45373e827c659b8823535fe4ea62ccb5e672ef999f8491d74c8c5c4c497367c884672fc835497ea180
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/scope-manager@npm:7.7.0"
"@typescript-eslint/scope-manager@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/scope-manager@npm:7.7.1"
dependencies:
"@typescript-eslint/types": "npm:7.7.0"
"@typescript-eslint/visitor-keys": "npm:7.7.0"
checksum: 10/c8890aaf99b57543774e50549c5b178c13695b21a6b30c65292268137fe5e6856cc0e050c118b47b5835dd8a48c96e042fc75891a7f6093a0b94b6b3b251afd9
"@typescript-eslint/types": "npm:7.7.1"
"@typescript-eslint/visitor-keys": "npm:7.7.1"
checksum: 10/7823cd15e7205d2c0d9e69432717c385b2ecd7559d5edba79113c2e97c6c5e8ca3dae9343a734bc740be97e096bfcb9dfb81a3da697f9fbf5600a56a42cf70e9
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/type-utils@npm:7.7.0"
"@typescript-eslint/type-utils@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/type-utils@npm:7.7.1"
dependencies:
"@typescript-eslint/typescript-estree": "npm:7.7.0"
"@typescript-eslint/utils": "npm:7.7.0"
"@typescript-eslint/typescript-estree": "npm:7.7.1"
"@typescript-eslint/utils": "npm:7.7.1"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^1.3.0"
peerDependencies:
@@ -4640,23 +4640,23 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 10/a3f5358b4b7046458ea573607f3d6ea7f48e16524390b24c9360bdf8b03cc89fc6eb5da31b3e541e7f1e5f6958194ecaad5b644ca9b0d90c9a7b182f345451aa
checksum: 10/c64dfd3e535741270012d289d1327e487df877adfa8a9920b1f8d6616f3b7159ef8ee1d6b62e866b6a5c64d675c5008e87f4ea20b5fc032e95f197a749d38ae6
languageName: node
linkType: hard
"@typescript-eslint/types@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/types@npm:7.7.0"
checksum: 10/d54ff9eeea168188fcbf1c8efe42892d1646ead801ea0a0f1312c80cfb74ee5dd61a145bc982919fb396683fb4578f98f7ad90e5d466d7aa1ca593e4338e1a2e
"@typescript-eslint/types@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/types@npm:7.7.1"
checksum: 10/a1ecbaf3b8a5243394d421644f2b3eb164feea645e36dd07f1afb5008598201f19c7988141fc162c647f380dda7cf571017c0eabbbc4c5432b0143383853e134
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/typescript-estree@npm:7.7.0"
"@typescript-eslint/typescript-estree@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/typescript-estree@npm:7.7.1"
dependencies:
"@typescript-eslint/types": "npm:7.7.0"
"@typescript-eslint/visitor-keys": "npm:7.7.0"
"@typescript-eslint/types": "npm:7.7.1"
"@typescript-eslint/visitor-keys": "npm:7.7.1"
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/40af26b3edb07af439f99728aa149bbc8668dae4a700a128abaf98d7f9bc0d5d31f8027aa1d13d6a55b22c20738d7cab84a3046a56417a2551de58671b39dbdf
checksum: 10/df5fe6c573b15e8058b88d1535eeca11115118adc54225f511d2762d74e2d453205ba27e63f6666cb5f3dc73d639208a183fb05db1f75063b115d52b1fae3e20
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/utils@npm:7.7.0"
"@typescript-eslint/utils@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/utils@npm:7.7.1"
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.0"
"@typescript-eslint/types": "npm:7.7.0"
"@typescript-eslint/typescript-estree": "npm:7.7.0"
"@typescript-eslint/scope-manager": "npm:7.7.1"
"@typescript-eslint/types": "npm:7.7.1"
"@typescript-eslint/typescript-estree": "npm:7.7.1"
semver: "npm:^7.6.0"
peerDependencies:
eslint: ^8.56.0
checksum: 10/4223233ee022460a74f389302b50779537dfbb3bd414486dca356d2628a08d5b2c4c6002bae3bdffad92b368569024faf25faee9be739340d9459c23549a866f
checksum: 10/5a352c3a849300b5d676bf5f451418a2fb0cd3ab515f3733521ad03cf047849c52c76f6e5d2406e08f6d0dbad3a4708b490f909c91a1a9e3d73060a750b3bca2
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:7.7.0":
version: 7.7.0
resolution: "@typescript-eslint/visitor-keys@npm:7.7.0"
"@typescript-eslint/visitor-keys@npm:7.7.1":
version: 7.7.1
resolution: "@typescript-eslint/visitor-keys@npm:7.7.1"
dependencies:
"@typescript-eslint/types": "npm:7.7.0"
"@typescript-eslint/types": "npm:7.7.1"
eslint-visitor-keys: "npm:^3.4.3"
checksum: 10/9f03591ab60b0b164f6bb222b5d5ae75f73fbe7f264be9318f770be9dc5dff8138d34701928940ffc18924058ae80754a738a1e623912a297d57a8a59cdfb41d
checksum: 10/dcc5748b10bb1b169516b33e87b6d86b562e25725a95e5ac515cb197589d9667aaa7cfffa93234095a73c80addb6dd88e2a9ab01d2be0c274254b5be1ca4057a
languageName: node
linkType: hard
@@ -6246,7 +6246,7 @@ __metadata:
languageName: node
linkType: hard
"builtin-modules@npm:^3.1.0, builtin-modules@npm:^3.3.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.1"
"@babel/helper-define-polyfill-provider": "npm:0.6.2"
"@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.0"
"@lokalise/node-api": "npm:12.4.1"
"@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.0"
"@octokit/plugin-retry": "npm:7.1.1"
"@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.11"
"@types/leaflet": "npm:1.9.12"
"@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.0"
"@typescript-eslint/parser": "npm:7.7.0"
"@typescript-eslint/eslint-plugin": "npm:7.7.1"
"@typescript-eslint/parser": "npm:7.7.1"
"@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.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"
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"
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.0":
"rollup-plugin-terser@npm:7.0.2":
version: 7.0.2
resolution: "rollup-plugin-terser@npm:7.0.2"
dependencies:
@@ -14410,6 +14410,13 @@ __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"
@@ -15194,9 +15201,9 @@ __metadata:
languageName: node
linkType: hard
"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"
"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"
dependencies:
"@jridgewell/source-map": "npm:^0.3.3"
acorn: "npm:^8.8.2"
@@ -15204,7 +15211,7 @@ __metadata:
source-map-support: "npm:~0.5.20"
bin:
terser: bin/terser
checksum: 10/f4ee378065a327c85472f351ac232fa47ec84d4f15df7ec58c044b41e3c063cf11aaedd90dcfe9c7f2a6ef01d4aab23deb61622301170dc77d0a8b6a6a83cf5e
checksum: 10/79459106281fccb2ff4243ba1553e4aa67a71b336bb8c091b131bb26347fcf03791c6abf6870bd17fe4a210256e08910207cf5733c0d6ba840289e67d5aa84d3
languageName: node
linkType: hard
@@ -16578,36 +16585,37 @@ __metadata:
languageName: node
linkType: hard
"workbox-background-sync@npm:7.0.0":
version: 7.0.0
resolution: "workbox-background-sync@npm:7.0.0"
"workbox-background-sync@npm:7.1.0":
version: 7.1.0
resolution: "workbox-background-sync@npm:7.1.0"
dependencies:
idb: "npm:^7.0.1"
workbox-core: "npm:7.0.0"
checksum: 10/41f89cd970c69bebe1ac648485c89f033ccd116f8140696de5f5489f2138e67ccc30ce33158025b8e312c49ad390f80eb8e372f9c569f5d6f35ab1e6185c2d74
workbox-core: "npm:7.1.0"
checksum: 10/0a303af41a02703ecd962c9a003eebd437e4373a468f3ac4ab0b7969a6849bc98a51f1b48915423dee2f5315d8ae34407465aa3a609ea683499043d1a9b44366
languageName: node
linkType: hard
"workbox-broadcast-update@npm:7.0.0":
version: 7.0.0
resolution: "workbox-broadcast-update@npm:7.0.0"
"workbox-broadcast-update@npm:7.1.0":
version: 7.1.0
resolution: "workbox-broadcast-update@npm:7.1.0"
dependencies:
workbox-core: "npm:7.0.0"
checksum: 10/e6110207465c574327dc89cbedad90795093e181341e2b093141a7171c46d2d4bb5862ba2a3a1e0c753e359f4462bf9929ce911c2082fe566f2551da295abf68
workbox-core: "npm:7.1.0"
checksum: 10/8dd87c05b14c0e7f03711d8a189949d5b16a33c53fc0d252569ba4758e450baca2d1aac45628b4210532c930176b1796a2a917be51000289001e401bdc5d3915
languageName: node
linkType: hard
"workbox-build@npm:7.0.0":
version: 7.0.0
resolution: "workbox-build@npm:7.0.0"
"workbox-build@npm:7.1.0":
version: 7.1.0
resolution: "workbox-build@npm:7.1.0"
dependencies:
"@apideck/better-ajv-errors": "npm:^0.3.1"
"@babel/core": "npm:^7.11.1"
"@babel/core": "npm:^7.24.4"
"@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:^11.2.1"
"@rollup/plugin-node-resolve": "npm:^15.2.3"
"@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"
@@ -16617,154 +16625,153 @@ __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.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
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
languageName: node
linkType: hard
"workbox-cacheable-response@npm:7.0.0":
version: 7.0.0
resolution: "workbox-cacheable-response@npm:7.0.0"
"workbox-cacheable-response@npm:7.1.0":
version: 7.1.0
resolution: "workbox-cacheable-response@npm:7.1.0"
dependencies:
workbox-core: "npm:7.0.0"
checksum: 10/b18de42a55802f18ecb7d08c6f3dac5f18cded64f36bb74b00eceb01199d2f8362c96d76ee12e75381fb51a06d705d901e3985c24017dc3d3e3fc9fc36282e51
workbox-core: "npm:7.1.0"
checksum: 10/1eb294765256d5010325c702208b8f621ad59a0323981bdd8d9d991b9ab7888f39f9a051f90ba583840168cd3252852511666ddc3db64f20f91c97cbea159796
languageName: node
linkType: hard
"workbox-core@npm:7.0.0":
version: 7.0.0
resolution: "workbox-core@npm:7.0.0"
checksum: 10/680c65e926517a6cd7b515243b9a33a5f4cdc45de31e060fbdc89e28f79b10a2fa211576802a29790cd37fa8a801f3fccfb9cbe371acaa8095c858c9afefc7e6
"workbox-core@npm:7.1.0":
version: 7.1.0
resolution: "workbox-core@npm:7.1.0"
checksum: 10/b890e0e257c12d3a818eee9dabdfdc8d7d228b89f9734f7612f14e664ca5414c511778d5aef5159248db4c6c161587cff6d2332f0543e3628a1e0cd5a1f0b3ac
languageName: node
linkType: hard
"workbox-expiration@npm:7.0.0":
version: 7.0.0
resolution: "workbox-expiration@npm:7.0.0"
"workbox-expiration@npm:7.1.0":
version: 7.1.0
resolution: "workbox-expiration@npm:7.1.0"
dependencies:
idb: "npm:^7.0.1"
workbox-core: "npm:7.0.0"
checksum: 10/a9b23c7c76cbabe8f04567e603428db939cb169cf40c1e5ba1c5a1eb966f7b8a4d0182dffdd3d77917b5edca966be0d6e347b7eff274292256fe6ea9a40fa754
workbox-core: "npm:7.1.0"
checksum: 10/45c7a27b217355fc30929482625c43cbfa04c914162a26b92c7e91fcb3a20e9982b50026bf4bb37382a320e1426818e3726b999dce1c8c08d2aa330eee569308
languageName: node
linkType: hard
"workbox-google-analytics@npm:7.0.0":
version: 7.0.0
resolution: "workbox-google-analytics@npm:7.0.0"
"workbox-google-analytics@npm:7.1.0":
version: 7.1.0
resolution: "workbox-google-analytics@npm:7.1.0"
dependencies:
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
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
languageName: node
linkType: hard
"workbox-navigation-preload@npm:7.0.0":
version: 7.0.0
resolution: "workbox-navigation-preload@npm:7.0.0"
"workbox-navigation-preload@npm:7.1.0":
version: 7.1.0
resolution: "workbox-navigation-preload@npm:7.1.0"
dependencies:
workbox-core: "npm:7.0.0"
checksum: 10/69bd82c12adabf7a210a3a9d8ce01082ef7276521ba375e0455ba3c17cf42b101783f0ec2af4ab0191989b0ea118794e0c8ebe3800abe806142ac7fef0674ac5
workbox-core: "npm:7.1.0"
checksum: 10/e4a2e40f1292b1a5e70c7efe69d280635b211c98577ef88f3941b4627e592bb5b21aae262da74636f06ea1fc61065e3002f41cff12f25b05de0259e2700b6ca8
languageName: node
linkType: hard
"workbox-precaching@npm:7.0.0":
version: 7.0.0
resolution: "workbox-precaching@npm:7.0.0"
"workbox-precaching@npm:7.1.0":
version: 7.1.0
resolution: "workbox-precaching@npm:7.1.0"
dependencies:
workbox-core: "npm:7.0.0"
workbox-routing: "npm:7.0.0"
workbox-strategies: "npm:7.0.0"
checksum: 10/8882d5ba888b08aba5e82656b26cd2f293c5adbb858bdff5e17cb4bf1823063cf51d16d3d6640716de96f11cf458f8aaf33b5ffef1ef07d327df8680711bf02e
workbox-core: "npm:7.1.0"
workbox-routing: "npm:7.1.0"
workbox-strategies: "npm:7.1.0"
checksum: 10/4f91a1cb1fbc1af1f467e3aa4f1a315465a7537ea42d3fbc3f85da342b10085a64736e80de42611622b83650c422b8770a1fc7fb5008a75abfc07a8c1393e049
languageName: node
linkType: hard
"workbox-range-requests@npm:7.0.0":
version: 7.0.0
resolution: "workbox-range-requests@npm:7.0.0"
"workbox-range-requests@npm:7.1.0":
version: 7.1.0
resolution: "workbox-range-requests@npm:7.1.0"
dependencies:
workbox-core: "npm:7.0.0"
checksum: 10/8cb991173df19f01f00aa6fee901946d73cdaefb7fef74fda99e53cdfeb0400efebba152f36ba02048af605464b725c743d306ec6e244b1bf6d1fb4df29a284f
workbox-core: "npm:7.1.0"
checksum: 10/a92d9c28a1c033a4d9e8a958613f2d44b374ef6f4f7609bf8f574ae5fe41c0800b251fb17f8ca7cd3ebc3c53cdbc22fe5a4c5f0afabd63a5960cbe4333dbbf2a
languageName: node
linkType: hard
"workbox-recipes@npm:7.0.0":
version: 7.0.0
resolution: "workbox-recipes@npm:7.0.0"
"workbox-recipes@npm:7.1.0":
version: 7.1.0
resolution: "workbox-recipes@npm:7.1.0"
dependencies:
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
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
languageName: node
linkType: hard
"workbox-routing@npm:7.0.0":
version: 7.0.0
resolution: "workbox-routing@npm:7.0.0"
"workbox-routing@npm:7.1.0":
version: 7.1.0
resolution: "workbox-routing@npm:7.1.0"
dependencies:
workbox-core: "npm:7.0.0"
checksum: 10/294c4b0f136e39c44678caa736195bd99bf93e55ba3605d0a880e36e7ffc9a4b6be36c2c37bca3ee1f905615641167d5b2a0eab742f46b94536f2c5eaac0e832
workbox-core: "npm:7.1.0"
checksum: 10/3598d65801ae6fb1e05512aff1cc3a642c1e3beace248349c6401f678033433ec3083ea0849a28665f0bb11e2493e9e66b0d066ee5de59a84f70baa3a59d841c
languageName: node
linkType: hard
"workbox-strategies@npm:7.0.0":
version: 7.0.0
resolution: "workbox-strategies@npm:7.0.0"
"workbox-strategies@npm:7.1.0":
version: 7.1.0
resolution: "workbox-strategies@npm:7.1.0"
dependencies:
workbox-core: "npm:7.0.0"
checksum: 10/7d7dbe9dff54c22e01d01238dbf0b3ba259b85c08cbd8b617b9f5104efef948b760848fcf36385d26fd651a2fe57c4faa3d6a4fc2739e05fc684da0a7eb2197a
workbox-core: "npm:7.1.0"
checksum: 10/52734ecce926ba6c135b5c7cb31906e40ad6bc767c77d45e74414b8adbb980f8a81bc1253af64750ce22202d0f1c4f01161785829cfb7bcb3f59408da9130555
languageName: node
linkType: hard
"workbox-streams@npm:7.0.0":
version: 7.0.0
resolution: "workbox-streams@npm:7.0.0"
"workbox-streams@npm:7.1.0":
version: 7.1.0
resolution: "workbox-streams@npm:7.1.0"
dependencies:
workbox-core: "npm:7.0.0"
workbox-routing: "npm:7.0.0"
checksum: 10/a11a134536e21a04d29dd2b0da8a79524ae27c993c98c63db8c6ddf49449a2f9d4d09766c6351cca08538b713e837e7d3ddf93842fd15eea3ecab75bc674cdf2
workbox-core: "npm:7.1.0"
workbox-routing: "npm:7.1.0"
checksum: 10/759011add716b69be2fc07f847476de6b299b451201e26861529156a8da9a145a9c10b5408f28937142dd82b796e08fdc0f557a7560ff4a9e1ec6affae1d1efb
languageName: node
linkType: hard
"workbox-sw@npm:7.0.0":
version: 7.0.0
resolution: "workbox-sw@npm:7.0.0"
checksum: 10/2b34da7efad8788e024bb22b25293459a9b0adc19fe185c3d155a4956f67ccdd35829a88b82436b4afe956a99c0ac934e5624a1469a762a53b522c2f32329d34
"workbox-sw@npm:7.1.0":
version: 7.1.0
resolution: "workbox-sw@npm:7.1.0"
checksum: 10/ece8081e41a45e2e42e0be597e5a2a8be8aa25ebf16a496599a76d4a044fc922e0b40d3fcb9c82682db1911b0d6e51e761593922c90f40d11d7b06f7a4b773c7
languageName: node
linkType: hard
"workbox-window@npm:7.0.0":
version: 7.0.0
resolution: "workbox-window@npm:7.0.0"
"workbox-window@npm:7.1.0":
version: 7.1.0
resolution: "workbox-window@npm:7.1.0"
dependencies:
"@types/trusted-types": "npm:^2.0.2"
workbox-core: "npm:7.0.0"
checksum: 10/5511ed9b86602ee6999233fc09ab9da298b7d62dbf865737c920d7f768877f8f9031896fad413f9b34f46ed2db58e0967b66b08cd78cf2f70bd7f734e7cac324
workbox-core: "npm:7.1.0"
checksum: 10/2706c55b81857966c28087a2b0ef40b7791e1bd441b880b7525b7e1b4834ae89c4f1bcfdb07cc155487a85f7c566007e1f9edf65539d7f4a52e2ceee48f547b5
languageName: node
linkType: hard