diff --git a/.browserslistrc b/.browserslistrc index 86481f8e08..516fec9cec 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -10,6 +10,12 @@ supports es6-module-dynamic-import not Safari < 13 not iOS < 13 +# Exclude KaiOS, QQ, and UC browsers due to lack of sufficient feature support data +# Babel ignores these automatically, but we need here for Webpack to output ESM with dynamic imports +not KaiOS > 0 +not QQAndroid > 0 +not UCAndroid > 0 + # Exclude unsupported browsers not dead diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 26ee55e660..17e64a518c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile -FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.10 +FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.11 ENV \ DEBIAN_FRONTEND=noninteractive \ diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index a4dce4c2b4..0d507d1104 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,3 +1,8 @@ +categories: + - title: 'Dependency updates' + collapse-after: 3 + labels: + - 'dependencies' template: | ## What's Changed diff --git a/.github/workflows/cast_deployment.yaml b/.github/workflows/cast_deployment.yaml index 85a076bf00..120658dd0c 100644 --- a/.github/workflows/cast_deployment.yaml +++ b/.github/workflows/cast_deployment.yaml @@ -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@v3.5.2 + uses: actions/checkout@v3.5.3 with: ref: dev @@ -57,7 +57,7 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 with: ref: master diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e73c4f7a1e..16656927e6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Setup Node uses: actions/setup-node@v3.6.0 with: @@ -47,7 +47,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Setup Node uses: actions/setup-node@v3.6.0 with: @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Setup Node uses: actions/setup-node@v3.6.0 with: @@ -83,7 +83,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Setup Node uses: actions/setup-node@v3.6.0 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b71752c7f7..fda71956ed 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/demo_deployment.yaml b/.github/workflows/demo_deployment.yaml index cddec2a128..77c4b98f5c 100644 --- a/.github/workflows/demo_deployment.yaml +++ b/.github/workflows/demo_deployment.yaml @@ -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@v3.5.2 + uses: actions/checkout@v3.5.3 with: ref: dev @@ -58,7 +58,7 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 with: ref: master diff --git a/.github/workflows/design_deployment.yaml b/.github/workflows/design_deployment.yaml index 62478e741a..8b5aec8a39 100644 --- a/.github/workflows/design_deployment.yaml +++ b/.github/workflows/design_deployment.yaml @@ -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@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Setup Node uses: actions/setup-node@v3.6.0 diff --git a/.github/workflows/design_preview.yaml b/.github/workflows/design_preview.yaml index 59f487a4b6..a257f9ec98 100644 --- a/.github/workflows/design_preview.yaml +++ b/.github/workflows/design_preview.yaml @@ -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@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Setup Node uses: actions/setup-node@v3.6.0 diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index ad51a2699b..11b831a3df 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -9,7 +9,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4.0.0 + - uses: dessant/lock-threads@v4.0.1 with: github-token: ${{ github.token }} issue-lock-inactive-days: "30" diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index aeb820a8d4..76f0415496 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -6,7 +6,7 @@ on: - cron: "0 1 * * *" env: - PYTHON_VERSION: "3.10" + PYTHON_VERSION: "3.11" NODE_OPTIONS: --max_old_space_size=6144 permissions: @@ -20,7 +20,7 @@ jobs: contents: write steps: - name: Checkout the repository - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Set up Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v4 diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml index 00954a2615..71a80c804f 100644 --- a/.github/workflows/release-drafter.yaml +++ b/.github/workflows/release-drafter.yaml @@ -5,8 +5,17 @@ on: branches: - dev +permissions: + contents: read + jobs: update_release_draft: + permissions: + # write permission for contents is required to create a github release + contents: write + # write permission for pull-requests is required for autolabeler + # otherwise, read permission is required at least + pull-requests: read runs-on: ubuntu-latest steps: - uses: release-drafter/release-drafter@v5 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0766eb857f..a269b30074 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,7 +6,7 @@ on: - published env: - PYTHON_VERSION: "3.10" + PYTHON_VERSION: "3.11" NODE_OPTIONS: --max_old_space_size=6144 # Set default workflow permissions @@ -23,7 +23,7 @@ jobs: contents: write # Required to upload release assets steps: - name: Checkout the repository - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Verify version uses: home-assistant/actions/helpers/verify-version@master @@ -76,7 +76,7 @@ jobs: - name: Build wheels uses: home-assistant/wheels@2023.04.0 with: - abi: cp310 + abi: cp311 tag: musllinux_1_2 arch: amd64 wheels-key: ${{ secrets.WHEELS_KEY }} diff --git a/.github/workflows/translations.yaml b/.github/workflows/translations.yaml index 38bcdc8071..8e503deb83 100644 --- a/.github/workflows/translations.yaml +++ b/.github/workflows/translations.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repository - uses: actions/checkout@v3.5.2 + uses: actions/checkout@v3.5.3 - name: Upload Translations run: | diff --git a/build-scripts/bundle.cjs b/build-scripts/bundle.cjs index a6d14f18c8..622527f194 100644 --- a/build-scripts/bundle.cjs +++ b/build-scripts/bundle.cjs @@ -77,6 +77,7 @@ module.exports.htmlMinifierOptions = { module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({ safari10: !latestBuild, ecma: latestBuild ? 2015 : 5, + module: latestBuild, format: { comments: false }, sourceMap: !isTestBuild, }); @@ -97,7 +98,7 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({ "@babel/preset-env", { useBuiltIns: latestBuild ? false : "entry", - corejs: latestBuild ? false : { version: "3.30", proposals: true }, + corejs: latestBuild ? false : { version: "3.31", proposals: true }, bugfixes: true, }, ], diff --git a/build-scripts/gulp/locale-data.js b/build-scripts/gulp/locale-data.js index 5def7818fe..f902256877 100755 --- a/build-scripts/gulp/locale-data.js +++ b/build-scripts/gulp/locale-data.js @@ -17,6 +17,7 @@ const modules = { "intl-datetimeformat": "DateTimeFormat", "intl-numberformat": "NumberFormat", "intl-displaynames": "DisplayNames", + "intl-listformat": "ListFormat", }; gulp.task("create-locale-data", (done) => { diff --git a/build-scripts/rollup.cjs b/build-scripts/rollup.cjs index f321e9f33e..e3becb97ae 100644 --- a/build-scripts/rollup.cjs +++ b/build-scripts/rollup.cjs @@ -142,4 +142,5 @@ module.exports = { createCastConfig, createHassioConfig, createGalleryConfig, + createRollupConfig, }; diff --git a/build-scripts/webpack.cjs b/build-scripts/webpack.cjs index 58cc184ca8..c5f8334149 100644 --- a/build-scripts/webpack.cjs +++ b/build-scripts/webpack.cjs @@ -41,7 +41,7 @@ const createWebpackConfig = ({ return { name, mode: isProdBuild ? "production" : "development", - target: ["web", latestBuild ? "es2017" : "es5"], + target: `browserslist:${latestBuild ? "modern" : "legacy"}`, // For tests/CI, source maps are skipped to gain build speed // For production, generate source maps for accurate stack traces without source code // For development, generate "cheap" versions that can map to original line numbers @@ -84,6 +84,13 @@ const createWebpackConfig = ({ ], moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", + splitChunks: { + // Disable splitting for web workers with ESM output + // Imports of external chunks are broken + chunks: latestBuild + ? (chunk) => !chunk.canBeInitial() && !/^.+-worker$/.test(chunk.name) + : undefined, + }, }, plugins: [ !isStatsBuild && new WebpackBar({ fancy: !isProdBuild }), @@ -160,9 +167,12 @@ const createWebpackConfig = ({ "lit/polyfill-support$": "lit/polyfill-support.js", "@lit-labs/virtualizer/layouts/grid": "@lit-labs/virtualizer/layouts/grid.js", + "@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver": + "@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js", }, }, output: { + module: latestBuild, filename: ({ chunk }) => !isProdBuild || isStatsBuild || dontHash.has(chunk.name) ? "[name].js" @@ -196,7 +206,7 @@ const createWebpackConfig = ({ : undefined, }, experiments: { - topLevelAwait: true, + outputModule: true, }, }; }; @@ -243,4 +253,5 @@ module.exports = { createCastConfig, createHassioConfig, createGalleryConfig, + createWebpackConfig, }; diff --git a/gallery/src/data/date-options.ts b/gallery/src/data/date-options.ts new file mode 100644 index 0000000000..9bb856a26e --- /dev/null +++ b/gallery/src/data/date-options.ts @@ -0,0 +1,24 @@ +import type { ControlSelectOption } from "../../../src/components/ha-control-select"; + +export const timeOptions: ControlSelectOption[] = [ + { + value: "now", + label: "Now", + }, + { + value: "00:15:30", + label: "12:15:30 AM", + }, + { + value: "06:15:30", + label: "06:15:30 AM", + }, + { + value: "12:15:30", + label: "12:15:30 PM", + }, + { + value: "18:15:30", + label: "06:15:30 PM", + }, +]; diff --git a/gallery/src/pages/automation/describe-trigger.ts b/gallery/src/pages/automation/describe-trigger.ts index 6ed8163912..f86c452340 100644 --- a/gallery/src/pages/automation/describe-trigger.ts +++ b/gallery/src/pages/automation/describe-trigger.ts @@ -41,6 +41,7 @@ const triggers = [ { platform: "sun", event: "sunset" }, { platform: "time_pattern" }, { platform: "webhook" }, + { platform: "persistent_notification" }, { platform: "zone", entity_id: "person.person", @@ -50,6 +51,11 @@ const triggers = [ { platform: "tag" }, { platform: "time", at: "15:32" }, { platform: "template" }, + { platform: "conversation", command: "Turn on the lights" }, + { + platform: "conversation", + command: ["Turn on the lights", "Turn the lights on"], + }, { platform: "event", event_type: "homeassistant_started" }, ]; diff --git a/gallery/src/pages/automation/editor-trigger.ts b/gallery/src/pages/automation/editor-trigger.ts index bff2947bb3..cdc9ed2c83 100644 --- a/gallery/src/pages/automation/editor-trigger.ts +++ b/gallery/src/pages/automation/editor-trigger.ts @@ -19,11 +19,13 @@ import { HaTemplateTrigger } from "../../../../src/panels/config/automation/trig import { HaTimeTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time"; import { HaTimePatternTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern"; import { HaWebhookTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-webhook"; +import { HaPersistentNotificationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-persistent_notification"; import { HaZoneTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-zone"; import { HaDeviceTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-device"; import { HaStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state"; import { HaMQTTTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt"; import "../../../../src/panels/config/automation/trigger/ha-automation-trigger"; +import { HaConversationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-conversation"; const SCHEMAS: { name: string; triggers: Trigger[] }[] = [ { @@ -72,6 +74,16 @@ const SCHEMAS: { name: string; triggers: Trigger[] }[] = [ triggers: [{ platform: "webhook", ...HaWebhookTrigger.defaultConfig }], }, + { + name: "Persistent Notification", + triggers: [ + { + platform: "persistent_notification", + ...HaPersistentNotificationTrigger.defaultConfig, + }, + ], + }, + { name: "Zone", triggers: [{ platform: "zone", ...HaZoneTrigger.defaultConfig }], @@ -101,6 +113,16 @@ const SCHEMAS: { name: string; triggers: Trigger[] }[] = [ name: "Device Trigger", triggers: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }], }, + { + name: "Sentence", + triggers: [ + { platform: "conversation", ...HaConversationTrigger.defaultConfig }, + { + platform: "conversation", + command: ["Turn on the lights", "Turn the lights on"], + }, + ], + }, ]; @customElement("demo-automation-editor-trigger") diff --git a/gallery/src/pages/components/ha-control-circular-slider.markdown b/gallery/src/pages/components/ha-control-circular-slider.markdown new file mode 100644 index 0000000000..7a5a234cf2 --- /dev/null +++ b/gallery/src/pages/components/ha-control-circular-slider.markdown @@ -0,0 +1,3 @@ +--- +title: Control Circular Slider +--- diff --git a/gallery/src/pages/components/ha-control-circular-slider.ts b/gallery/src/pages/components/ha-control-circular-slider.ts new file mode 100644 index 0000000000..8c6fca52d4 --- /dev/null +++ b/gallery/src/pages/components/ha-control-circular-slider.ts @@ -0,0 +1,178 @@ +import { css, html, LitElement, TemplateResult } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-circular-slider"; +import "../../../../src/components/ha-slider"; + +@customElement("demo-components-ha-control-circular-slider") +export class DemoHaCircularSlider extends LitElement { + @state() + private current = 22; + + @state() + private low = 19; + + @state() + private high = 25; + + @state() + private changingLow?: number; + + @state() + private changingHigh?: number; + + private _lowChanged(ev) { + this.low = ev.detail.value; + } + + private _lowChanging(ev) { + this.changingLow = ev.detail.value; + } + + private _highChanged(ev) { + this.high = ev.detail.value; + } + + private _highChanging(ev) { + this.changingHigh = ev.detail.value; + } + + private _currentChanged(ev) { + this.current = ev.currentTarget.value; + } + + protected render(): TemplateResult { + return html` + +
+

Config

+
+

Current

+ +

${this.current} °C

+
+
+
+ +
+

Single

+ +
+ Low: ${this.low} °C +
+ Changing: + ${this.changingLow != null ? `${this.changingLow} °C` : "-"} +
+
+
+ +
+

Inverted

+ +
+ High: ${this.high} °C +
+ Changing: + ${this.changingHigh != null ? `${this.changingHigh} °C` : "-"} +
+
+
+ +
+

Dual

+ +
+ Low value: ${this.low} °C +
+ Low changing: + ${this.changingLow != null ? `${this.changingLow} °C` : "-"} +
+ High value: ${this.high} °C +
+ High changing: + ${this.changingHigh != null ? `${this.changingHigh} °C` : "-"} +
+
+
+ `; + } + + static get styles() { + return css` + ha-card { + max-width: 600px; + margin: 24px auto; + } + pre { + margin-top: 0; + margin-bottom: 8px; + } + p { + margin: 0; + } + p.title { + margin-bottom: 12px; + } + ha-control-circular-slider { + --control-circular-slider-color: #ff9800; + --control-circular-slider-background: #ff9800; + --control-circular-slider-background-opacity: 0.3; + } + ha-control-circular-slider[inverted] { + --control-circular-slider-color: #2196f3; + --control-circular-slider-background: #2196f3; + } + ha-control-circular-slider[dual] { + --control-circular-slider-high-color: #2196f3; + --control-circular-slider-low-color: #ff9800; + --control-circular-slider-background: var(--disabled-color); + } + .field { + display: flex; + flex-direction: row; + align-items: center; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-components-ha-control-circular-slider": DemoHaCircularSlider; + } +} diff --git a/gallery/src/pages/date-time/date-time-numeric.markdown b/gallery/src/pages/date-time/date-time-numeric.markdown new file mode 100644 index 0000000000..3310f315a1 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-numeric.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format (Numeric) +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatDateTimeNumeric: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time-numeric.ts b/gallery/src/pages/date-time/date-time-numeric.ts new file mode 100644 index 0000000000..608b3fc152 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-numeric.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatDateTimeNumeric } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time-numeric") +export class DemoDateTimeDateTimeNumeric extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatDateTimeNumeric( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatDateTimeNumeric( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatDateTimeNumeric( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time-numeric": DemoDateTimeDateTimeNumeric; + } +} diff --git a/gallery/src/pages/date-time/date-time-seconds.markdown b/gallery/src/pages/date-time/date-time-seconds.markdown new file mode 100644 index 0000000000..01cfa6c729 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-seconds.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format (Seconds) +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatDateTimeWithSeconds: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time-seconds.ts b/gallery/src/pages/date-time/date-time-seconds.ts new file mode 100644 index 0000000000..5f5b48f989 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-seconds.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatDateTimeWithSeconds } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time-seconds") +export class DemoDateTimeDateTimeSeconds extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatDateTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatDateTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatDateTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time-seconds": DemoDateTimeDateTimeSeconds; + } +} diff --git a/gallery/src/pages/date-time/date-time-short-year.markdown b/gallery/src/pages/date-time/date-time-short-year.markdown new file mode 100644 index 0000000000..19e77b55b9 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-short-year.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format (Short w/ Year) +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatShortDateTimeWithYear: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time-short-year.ts b/gallery/src/pages/date-time/date-time-short-year.ts new file mode 100644 index 0000000000..f132f2a047 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-short-year.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatShortDateTimeWithYear } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time-short-year") +export class DemoDateTimeDateTimeShortYear extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatShortDateTimeWithYear( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatShortDateTimeWithYear( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatShortDateTimeWithYear( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time-short-year": DemoDateTimeDateTimeShortYear; + } +} diff --git a/gallery/src/pages/date-time/date-time-short.markdown b/gallery/src/pages/date-time/date-time-short.markdown new file mode 100644 index 0000000000..3564a7afa0 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-short.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format (Short) +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatShortDateTime: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time-short.ts b/gallery/src/pages/date-time/date-time-short.ts new file mode 100644 index 0000000000..21f7eb1294 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-short.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatShortDateTime } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time-short") +export class DemoDateTimeDateTimeShort extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatShortDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatShortDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatShortDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time-short": DemoDateTimeDateTimeShort; + } +} diff --git a/gallery/src/pages/date-time/date-time.markdown b/gallery/src/pages/date-time/date-time.markdown new file mode 100644 index 0000000000..cef6195ab1 --- /dev/null +++ b/gallery/src/pages/date-time/date-time.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatDateTime: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time.ts b/gallery/src/pages/date-time/date-time.ts new file mode 100644 index 0000000000..4bb4b75865 --- /dev/null +++ b/gallery/src/pages/date-time/date-time.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatDateTime } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time") +export class DemoDateTimeDateTime extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time": DemoDateTimeDateTime; + } +} diff --git a/gallery/src/pages/date-time/date.markdown b/gallery/src/pages/date-time/date.markdown index 5eb64bb19e..599921e1c7 100644 --- a/gallery/src/pages/date-time/date.markdown +++ b/gallery/src/pages/date-time/date.markdown @@ -1,5 +1,5 @@ --- -title: (Numeric) Date Formatting +title: Date Format (Numeric) --- This pages lists all supported languages with their available (numeric) date formats. diff --git a/gallery/src/pages/date-time/date.ts b/gallery/src/pages/date-time/date.ts index c344f6f7cc..1ddc60ea7f 100644 --- a/gallery/src/pages/date-time/date.ts +++ b/gallery/src/pages/date-time/date.ts @@ -1,27 +1,28 @@ -import { html, css, LitElement } from "lit"; -import { customElement, property } from "lit/decorators"; -import "../../../../src/components/ha-card"; -import { HomeAssistant } from "../../../../src/types"; -import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import "@material/mwc-list/mwc-list"; +import { css, html, LitElement } from "lit"; +import { customElement } from "lit/decorators"; import { formatDateNumeric } from "../../../../src/common/datetime/format_date"; +import "../../../../src/components/ha-card"; import { + DateFormat, + FirstWeekday, FrontendLocaleData, NumberFormat, TimeFormat, - DateFormat, - FirstWeekday, + TimeZone, } from "../../../../src/data/translation"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; @customElement("demo-date-time-date") export class DemoDateTimeDate extends LitElement { - @property({ attribute: false }) hass!: HomeAssistant; - protected render() { const defaultLocale: FrontendLocaleData = { language: "en", number_format: NumberFormat.language, time_format: TimeFormat.language, date_format: DateFormat.language, + time_zone: TimeZone.local, first_weekday: FirstWeekday.language, }; const date = new Date(); @@ -41,32 +42,48 @@ export class DemoDateTimeDate extends LitElement {
${value.nativeName}
- ${formatDateNumeric(date, { - ...defaultLocale, - language: key, - date_format: DateFormat.language, - })} + ${formatDateNumeric( + date, + { + ...defaultLocale, + language: key, + date_format: DateFormat.language, + }, + demoConfig + )}
- ${formatDateNumeric(date, { - ...defaultLocale, - language: key, - date_format: DateFormat.DMY, - })} + ${formatDateNumeric( + date, + { + ...defaultLocale, + language: key, + date_format: DateFormat.DMY, + }, + demoConfig + )}
- ${formatDateNumeric(date, { - ...defaultLocale, - language: key, - date_format: DateFormat.MDY, - })} + ${formatDateNumeric( + date, + { + ...defaultLocale, + language: key, + date_format: DateFormat.MDY, + }, + demoConfig + )}
- ${formatDateNumeric(date, { - ...defaultLocale, - language: key, - date_format: DateFormat.YMD, - })} + ${formatDateNumeric( + date, + { + ...defaultLocale, + language: key, + date_format: DateFormat.YMD, + }, + demoConfig + )}
` diff --git a/gallery/src/pages/date-time/time-seconds.markdown b/gallery/src/pages/date-time/time-seconds.markdown new file mode 100644 index 0000000000..23136c29f4 --- /dev/null +++ b/gallery/src/pages/date-time/time-seconds.markdown @@ -0,0 +1,7 @@ +--- +title: Time Format (Seconds) +--- + +This pages lists all supported languages with their available time formats. + +Formatting function: `const formatTimeWithSeconds: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/time-seconds.ts b/gallery/src/pages/date-time/time-seconds.ts new file mode 100644 index 0000000000..761bc6ed58 --- /dev/null +++ b/gallery/src/pages/date-time/time-seconds.ts @@ -0,0 +1,135 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatTimeWithSeconds } from "../../../../src/common/datetime/format_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-time-seconds") +export class DemoDateTimeTimeSeconds extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 600px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-time-seconds": DemoDateTimeTimeSeconds; + } +} diff --git a/gallery/src/pages/date-time/time-weekday.markdown b/gallery/src/pages/date-time/time-weekday.markdown new file mode 100644 index 0000000000..637be6afe3 --- /dev/null +++ b/gallery/src/pages/date-time/time-weekday.markdown @@ -0,0 +1,7 @@ +--- +title: Time Format (Weekday) +--- + +This pages lists all supported languages with their available time formats. + +Formatting function: `const formatTimeWeekday: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/time-weekday.ts b/gallery/src/pages/date-time/time-weekday.ts new file mode 100644 index 0000000000..8ed5c951f5 --- /dev/null +++ b/gallery/src/pages/date-time/time-weekday.ts @@ -0,0 +1,135 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatTimeWeekday } from "../../../../src/common/datetime/format_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-time-weekday") +export class DemoDateTimeTimeWeekday extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatTimeWeekday( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatTimeWeekday( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatTimeWeekday( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 800px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-time-weekday": DemoDateTimeTimeWeekday; + } +} diff --git a/gallery/src/pages/date-time/time.markdown b/gallery/src/pages/date-time/time.markdown new file mode 100644 index 0000000000..df90d8931a --- /dev/null +++ b/gallery/src/pages/date-time/time.markdown @@ -0,0 +1,7 @@ +--- +title: Time Format +--- + +This pages lists all supported languages with their available time formats. + +Formatting function: `const formatTime: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/time.ts b/gallery/src/pages/date-time/time.ts new file mode 100644 index 0000000000..df7b101653 --- /dev/null +++ b/gallery/src/pages/date-time/time.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatTime } from "../../../../src/common/datetime/format_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-time") +export class DemoDateTimeTime extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 600px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-time": DemoDateTimeTime; + } +} diff --git a/gallery/src/pages/lovelace/markdown-card.ts b/gallery/src/pages/lovelace/markdown-card.ts index 1abca4bdcc..700283b6b8 100644 --- a/gallery/src/pages/lovelace/markdown-card.ts +++ b/gallery/src/pages/lovelace/markdown-card.ts @@ -9,7 +9,7 @@ const CONFIGS = [ heading: "markdown-it demo", config: ` - type: markdown - content: >- + content: | # h1 Heading 8-) ## h2 Heading @@ -65,6 +65,15 @@ const CONFIGS = [ >> ...by using additional greater-than signs right next to each other... > > > ...or with spaces between arrows. + > **Warning** Hey there + > This is a warning with a title + + > **Note** + > This is a note + + > **Note** + > This is a multiline note + > Lorem ipsum... ## Lists diff --git a/gallery/src/pages/misc/entity-state.ts b/gallery/src/pages/misc/entity-state.ts index 01beaa0507..c35c9fae1e 100644 --- a/gallery/src/pages/misc/entity-state.ts +++ b/gallery/src/pages/misc/entity-state.ts @@ -135,6 +135,9 @@ const ENTITIES: HassEntity[] = [ createEntity("climate.fan_only", "fan_only"), createEntity("climate.auto_idle", "auto", undefined, { hvac_action: "idle" }), createEntity("climate.auto_off", "auto", undefined, { hvac_action: "off" }), + createEntity("climate.auto_preheating", "auto", undefined, { + hvac_action: "preheating", + }), createEntity("climate.auto_heating", "auto", undefined, { hvac_action: "heating", }), @@ -354,6 +357,7 @@ export class DemoEntityState extends LitElement { hass.localize, entry.stateObj, hass.locale, + hass.config, hass.entities )}`, }, diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index 986090e53f..5800d2e1b0 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -114,11 +114,22 @@ class HassioAddonInfo extends LitElement { @state() private _error?: string; + private _fetchDataTimeout?: number; + private _addonStoreInfo = memoizeOne( (slug: string, storeAddons: StoreAddon[]) => storeAddons.find((addon) => addon.slug === slug) ); + public disconnectedCallback() { + super.disconnectedCallback(); + + if (this._fetchDataTimeout) { + clearInterval(this._fetchDataTimeout); + this._fetchDataTimeout = undefined; + } + } + protected render(): TemplateResult { const addonStoreInfo = !this.addon.detached && !this.addon.available @@ -592,7 +603,10 @@ class HassioAddonInfo extends LitElement { ` : html` - + ${this.supervisor.localize("addon.dashboard.start")} ` @@ -672,9 +686,36 @@ class HassioAddonInfo extends LitElement { super.updated(changedProps); if (changedProps.has("addon")) { this._loadData(); + if ( + !this._fetchDataTimeout && + this.addon && + "state" in this.addon && + this.addon.state === "startup" + ) { + // Addon is starting up, wait for it to start + this._scheduleDataUpdate(); + } } } + private _scheduleDataUpdate() { + this._fetchDataTimeout = window.setTimeout(async () => { + const addon = await fetchHassioAddonInfo(this.hass, this.addon.slug); + if (addon.state !== "startup") { + this._fetchDataTimeout = undefined; + this.addon = addon; + const eventdata = { + success: true, + response: undefined, + path: "start", + }; + fireEvent(this, "hass-api-called", eventdata); + } else { + this._scheduleDataUpdate(); + } + }, 500); + } + private async _loadData(): Promise { if ("state" in this.addon && this.addon.state === "started") { this._metrics = await fetchHassioStats( diff --git a/hassio/src/backups/hassio-backups.ts b/hassio/src/backups/hassio-backups.ts index 382f8ec669..7dc5c94972 100644 --- a/hassio/src/backups/hassio-backups.ts +++ b/hassio/src/backups/hassio-backups.ts @@ -136,6 +136,15 @@ export class HassioBackups extends LitElement { sortable: true, template: (entry: number) => Math.ceil(entry * 10) / 10 + " MB", }, + location: { + title: this.supervisor.localize("backup.location"), + width: "15%", + hidden: narrow, + filterable: true, + sortable: true, + template: (entry: string | null) => + entry || this.supervisor.localize("backup.data_disk"), + }, date: { title: this.supervisor.localize("backup.created"), width: "15%", diff --git a/hassio/src/components/supervisor-backup-content.ts b/hassio/src/components/supervisor-backup-content.ts index 3480cf9aba..ded21bf63a 100644 --- a/hassio/src/components/supervisor-backup-content.ts +++ b/hassio/src/components/supervisor-backup-content.ts @@ -143,7 +143,11 @@ export class SupervisorBackupContent extends LitElement { : this._localize("partial_backup")} (${Math.ceil(this.backup.size * 10) / 10 + " MB"})
${this.hass - ? formatDateTime(new Date(this.backup.date), this.hass.locale) + ? formatDateTime( + new Date(this.backup.date), + this.hass.locale, + this.hass.config + ) : this.backup.date} ` : html` `; + return html``; } const iframe = html``; @@ -132,10 +143,10 @@ class HassioIngressView extends LitElement { return; } - const addon = this.route.path.substr(1); + const addon = this.route.path.substring(1); const oldRoute = changedProps.get("route") as this["route"] | undefined; - const oldAddon = oldRoute ? oldRoute.path.substr(1) : undefined; + const oldAddon = oldRoute ? oldRoute.path.substring(1) : undefined; if (addon && addon !== oldAddon) { this._fetchData(addon); @@ -145,33 +156,23 @@ class HassioIngressView extends LitElement { private async _fetchData(addonSlug: string) { const createSessionPromise = createHassioSession(this.hass); - let addon; + let addon: HassioAddonDetails; try { addon = await fetchHassioAddonInfo(this.hass, addonSlug); } catch (err: any) { await showAlertDialog(this, { - text: "Unable to fetch add-on info to start Ingress", + text: this.supervisor.localize("ingress.error_addon_info"), title: "Supervisor", }); await nextRender(); - history.back(); + navigate("/hassio/store", { replace: true }); return; } - if (!addon.ingress_url) { + if (!addon.version) { await showAlertDialog(this, { - text: "Add-on does not support Ingress", - title: addon.name, - }); - await nextRender(); - history.back(); - return; - } - - if (addon.state !== "started") { - await showAlertDialog(this, { - text: "Add-on is not running. Please start it first", + text: this.supervisor.localize("ingress.error_addon_not_installed"), title: addon.name, }); await nextRender(); @@ -179,13 +180,74 @@ class HassioIngressView extends LitElement { return; } - let session; + if (!addon.ingress_url) { + await showAlertDialog(this, { + text: this.supervisor.localize("ingress.error_addon_not_supported"), + title: addon.name, + }); + await nextRender(); + history.back(); + return; + } + + if (!addon.state || !["startup", "started"].includes(addon.state)) { + const confirm = await showConfirmationDialog(this, { + text: this.supervisor.localize("ingress.error_addon_not_running"), + title: addon.name, + confirmText: this.supervisor.localize("ingress.start_addon"), + dismissText: this.supervisor.localize("common.no"), + }); + if (confirm) { + try { + await startHassioAddon(this.hass, addonSlug); + fireEvent(this, "supervisor-collection-refresh", { + collection: "addon", + }); + this._fetchData(addonSlug); + return; + } catch (e) { + await showAlertDialog(this, { + text: this.supervisor.localize("ingress.error_starting_addon"), + title: addon.name, + }); + await nextRender(); + navigate(`/hassio/addon/${addon.slug}/logs`, { replace: true }); + return; + } + } else { + await nextRender(); + navigate(`/hassio/addon/${addon.slug}/info`, { replace: true }); + return; + } + } + + if (addon.state === "startup") { + // Addon is starting up, wait for it to start + this._fetchDataTimeout = window.setTimeout(() => { + this._fetchData(addonSlug); + }, 500); + return; + } + + if (addon.state !== "started") { + return; + } + + if (this._fetchDataTimeout) { + clearInterval(this._fetchDataTimeout); + this._fetchDataTimeout = undefined; + } + + let session: string; try { session = await createSessionPromise; } catch (err: any) { + if (this._sessionKeepAlive) { + clearInterval(this._sessionKeepAlive); + } await showAlertDialog(this, { - text: "Unable to create an Ingress session", + text: this.supervisor.localize("ingress.error_creating_session"), title: addon.name, }); await nextRender(); @@ -207,6 +269,31 @@ class HassioIngressView extends LitElement { this._addon = addon; } + private _checkLoaded(ev): void { + if (!this._addon) { + return; + } + if (ev.target.contentDocument.body.textContent === "502: Bad Gateway") { + showConfirmationDialog(this, { + text: this.supervisor.localize("ingress.error_addon_not_ready"), + title: this._addon.name, + confirmText: this.supervisor.localize("ingress.retry"), + dismissText: this.supervisor.localize("common.no"), + confirm: async () => { + const addon = this._addon; + this._addon = undefined; + await Promise.all([ + this.updateComplete, + new Promise((resolve) => { + setTimeout(resolve, 500); + }), + ]); + this._addon = addon; + }, + }); + } + } + private _toggleMenu(): void { fireEvent(this, "hass-toggle-menu"); } diff --git a/package.json b/package.json index 07913ff11e..0c1018da1f 100644 --- a/package.json +++ b/package.json @@ -25,33 +25,34 @@ "license": "Apache-2.0", "type": "module", "dependencies": { - "@babel/runtime": "7.22.3", + "@babel/runtime": "7.22.5", "@braintree/sanitize-url": "6.0.2", - "@codemirror/autocomplete": "6.7.1", + "@codemirror/autocomplete": "6.8.1", "@codemirror/commands": "6.2.4", - "@codemirror/language": "6.7.0", + "@codemirror/language": "6.8.0", "@codemirror/legacy-modes": "6.3.2", - "@codemirror/search": "6.4.0", + "@codemirror/search": "6.5.0", "@codemirror/state": "6.2.1", - "@codemirror/view": "6.12.0", + "@codemirror/view": "6.14.0", "@egjs/hammerjs": "2.0.17", - "@formatjs/intl-datetimeformat": "6.8.0", - "@formatjs/intl-displaynames": "6.3.2", - "@formatjs/intl-getcanonicallocales": "2.2.0", - "@formatjs/intl-locale": "3.3.0", - "@formatjs/intl-numberformat": "8.5.0", - "@formatjs/intl-pluralrules": "5.2.2", - "@formatjs/intl-relativetimeformat": "11.2.2", + "@formatjs/intl-datetimeformat": "6.10.0", + "@formatjs/intl-displaynames": "6.5.0", + "@formatjs/intl-getcanonicallocales": "2.2.1", + "@formatjs/intl-listformat": "7.4.0", + "@formatjs/intl-locale": "3.3.2", + "@formatjs/intl-numberformat": "8.7.0", + "@formatjs/intl-pluralrules": "5.2.4", + "@formatjs/intl-relativetimeformat": "11.2.4", "@fullcalendar/core": "6.1.8", "@fullcalendar/daygrid": "6.1.8", "@fullcalendar/interaction": "6.1.8", "@fullcalendar/list": "6.1.8", "@fullcalendar/timegrid": "6.1.8", "@lezer/highlight": "1.1.6", - "@lit-labs/context": "0.3.2", + "@lit-labs/context": "0.3.3", "@lit-labs/motion": "1.0.3", - "@lit-labs/virtualizer": "2.0.2", - "@lrnwebcomponents/simple-tooltip": "7.0.2", + "@lit-labs/virtualizer": "2.0.3", + "@lrnwebcomponents/simple-tooltip": "7.0.5", "@material/chips": "=14.0.0-canary.53b3cad2f.0", "@material/data-table": "=14.0.0-canary.53b3cad2f.0", "@material/mwc-button": "0.27.0", @@ -77,7 +78,7 @@ "@material/mwc-top-app-bar": "0.27.0", "@material/mwc-top-app-bar-fixed": "0.27.0", "@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0", - "@material/web": "=1.0.0-pre.9", + "@material/web": "=1.0.0-pre.11", "@mdi/js": "7.2.96", "@mdi/svg": "7.2.96", "@polymer/app-layout": "3.1.0", @@ -92,8 +93,8 @@ "@polymer/paper-toast": "3.0.1", "@polymer/polymer": "3.5.1", "@thomasloven/round-slider": "0.6.0", - "@vaadin/combo-box": "24.0.8", - "@vaadin/vaadin-themable-mixin": "24.0.8", + "@vaadin/combo-box": "24.1.1", + "@vaadin/vaadin-themable-mixin": "24.1.1", "@vibrant/color": "3.2.1-alpha.1", "@vibrant/core": "3.2.1-alpha.1", "@vibrant/quantizer-mmcq": "3.2.1-alpha.1", @@ -103,7 +104,7 @@ "app-datepicker": "5.1.1", "chart.js": "3.3.2", "comlink": "4.4.1", - "core-js": "3.30.2", + "core-js": "3.31.0", "cropperjs": "1.5.13", "date-fns": "2.30.0", "date-fns-tz": "2.0.0", @@ -111,10 +112,10 @@ "deep-freeze": "0.0.1", "fuse.js": "6.6.2", "google-timezones-json": "1.1.0", - "hls.js": "1.4.4", - "home-assistant-js-websocket": "8.0.1", + "hls.js": "1.4.6", + "home-assistant-js-websocket": "8.1.0", "idb-keyval": "6.2.1", - "intl-messageformat": "10.3.5", + "intl-messageformat": "10.5.0", "js-yaml": "4.1.0", "leaflet": "1.9.4", "leaflet-draw": "1.0.4", @@ -131,9 +132,9 @@ "rrule": "2.7.2", "sortablejs": "1.15.0", "superstruct": "1.0.3", - "tinykeys": "1.4.0", - "tsparticles-engine": "2.9.3", - "tsparticles-preset-links": "2.9.3", + "tinykeys": "2.1.0", + "tsparticles-engine": "2.10.1", + "tsparticles-preset-links": "2.10.1", "unfetch": "5.0.0", "vis-data": "7.1.6", "vis-network": "9.1.6", @@ -149,18 +150,18 @@ "xss": "1.0.14" }, "devDependencies": { - "@babel/core": "7.22.1", - "@babel/plugin-proposal-decorators": "7.22.3", - "@babel/plugin-transform-runtime": "7.22.4", - "@babel/preset-env": "7.22.4", - "@babel/preset-typescript": "7.21.5", + "@babel/core": "7.22.5", + "@babel/plugin-proposal-decorators": "7.22.5", + "@babel/plugin-transform-runtime": "7.22.5", + "@babel/preset-env": "7.22.5", + "@babel/preset-typescript": "7.22.5", "@koa/cors": "4.0.0", - "@octokit/auth-oauth-device": "4.0.4", - "@octokit/plugin-retry": "5.0.0", - "@octokit/rest": "19.0.11", + "@octokit/auth-oauth-device": "5.0.2", + "@octokit/plugin-retry": "5.0.4", + "@octokit/rest": "19.0.13", "@open-wc/dev-server-hmr": "0.1.4", "@rollup/plugin-babel": "6.0.3", - "@rollup/plugin-commonjs": "25.0.0", + "@rollup/plugin-commonjs": "25.0.2", "@rollup/plugin-json": "6.0.0", "@rollup/plugin-node-resolve": "15.1.0", "@rollup/plugin-replace": "5.0.2", @@ -172,7 +173,7 @@ "@types/html-minifier-terser": "7.0.0", "@types/js-yaml": "4.0.5", "@types/leaflet": "1.9.3", - "@types/leaflet-draw": "1.0.6", + "@types/leaflet-draw": "1.0.7", "@types/marked": "4.3.1", "@types/mocha": "10.0.1", "@types/qrcode": "1.5.0", @@ -180,15 +181,15 @@ "@types/sortablejs": "1.15.1", "@types/tar": "6.1.5", "@types/webspeechapi": "0.0.29", - "@typescript-eslint/eslint-plugin": "5.59.8", - "@typescript-eslint/parser": "5.59.8", + "@typescript-eslint/eslint-plugin": "5.60.0", + "@typescript-eslint/parser": "5.60.0", "@web/dev-server": "0.1.38", "@web/dev-server-rollup": "0.4.1", "babel-loader": "9.1.2", "babel-plugin-template-html-minifier": "4.1.0", "chai": "4.3.7", "del": "7.0.0", - "eslint": "8.42.0", + "eslint": "8.43.0", "eslint-config-airbnb-base": "15.0.0", "eslint-config-airbnb-typescript": "17.0.0", "eslint-config-prettier": "8.8.0", @@ -196,13 +197,13 @@ "eslint-plugin-disable": "2.0.3", "eslint-plugin-import": "2.27.5", "eslint-plugin-lit": "1.8.3", - "eslint-plugin-lit-a11y": "2.4.1", + "eslint-plugin-lit-a11y": "3.0.0", "eslint-plugin-unused-imports": "2.0.0", "eslint-plugin-wc": "1.5.0", "esprima": "4.0.1", "fancy-log": "2.0.0", "fs-extra": "11.1.1", - "glob": "10.2.6", + "glob": "10.3.0", "gulp": "4.0.2", "gulp-flatmap": "1.0.2", "gulp-json-transform": "0.4.8", @@ -214,7 +215,7 @@ "instant-mocha": "1.5.1", "jszip": "3.10.1", "lint-staged": "13.2.2", - "lit-analyzer": "1.2.1", + "lit-analyzer": "2.0.0-pre.3", "lodash.template": "4.5.0", "magic-string": "0.30.0", "map-stream": "0.0.7", @@ -227,20 +228,20 @@ "rollup": "2.79.1", "rollup-plugin-string": "3.0.0", "rollup-plugin-terser": "7.0.2", - "rollup-plugin-visualizer": "5.9.0", + "rollup-plugin-visualizer": "5.9.2", "serve-handler": "6.1.5", - "sinon": "15.1.0", + "sinon": "15.2.0", "source-map-url": "0.4.1", "systemjs": "6.14.1", "tar": "6.1.15", "terser-webpack-plugin": "5.3.9", - "ts-lit-plugin": "1.2.1", - "typescript": "4.9.5", + "ts-lit-plugin": "2.0.0-pre.1", + "typescript": "5.1.3", "vinyl-buffer": "1.0.1", "vinyl-source-stream": "2.0.0", - "webpack": "5.84.1", - "webpack-cli": "5.1.1", - "webpack-dev-server": "4.15.0", + "webpack": "5.88.0", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.1", "webpack-manifest-plugin": "5.0.0", "webpackbar": "5.0.2", "workbox-build": "7.0.0" diff --git a/pyproject.toml b/pyproject.toml index 099766e91b..421f20a4ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,17 +1,17 @@ [build-system] -requires = ["setuptools~=62.3", "wheel~=0.37.1"] +requires = ["setuptools~=68.0", "wheel~=0.40.0"] build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20230608.0" +version = "20230628.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" authors = [ {name = "The Home Assistant Authors", email = "hello@home-assistant.io"} ] -requires-python = ">=3.4.0" +requires-python = ">=3.10.0" [project.urls] "Homepage" = "https://github.com/home-assistant/frontend" diff --git a/renovate.json b/renovate.json index 895cfc3667..291e6a4a26 100644 --- a/renovate.json +++ b/renovate.json @@ -19,14 +19,20 @@ }, "packageRules": [ { - "description": ["MDC packages are pinned to the same version as MWC"], + "description": "MDC packages are pinned to the same version as MWC", "extends": ["monorepo:material-components-web"], "enabled": false }, { - "description": ["Vue is only used by date range which is only v2"], + "description": "Vue is only used by date range which is only v2", "matchPackageNames": ["vue"], "allowedVersions": "< 3" + }, + { + "description": "Group tsparticles engine and presets", + "groupName": "tsparticles", + "matchPackageNames": ["tsparticles-engine"], + "matchPackagePrefixes": ["tsparticles-preset-"] } ] } diff --git a/script/bootstrap b/script/bootstrap index f18b75d2b8..73e44806f9 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -8,9 +8,9 @@ cd "$(dirname "$0")/.." # Install/upgrade node when inside devcontainer if [[ -n "$DEVCONTAINER" ]]; then - nodeCurrent=$(nvm version default || echo "") + nodeCurrent=$(nvm version default || :) nodeLatest=$(nvm version-remote "$(cat .nvmrc)") - if [[ -z "$nodeCurrent" ]]; then + if [[ -z "$nodeCurrent" || "$nodeCurrent" == "N/A" ]]; then nvm install elif [[ "$nodeCurrent" != "$nodeLatest" ]]; then nvm install --reinstall-packages-from="$nodeCurrent" --default diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index e14c0e7b08..0000000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -# Setuptools v62.3 doesn't support editable installs with just 'pyproject.toml' (PEP 660). -# Keep this file until it does! diff --git a/src/common/const.ts b/src/common/const.ts index 35bd81a3e2..b4f79e5126 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -33,6 +33,7 @@ import { mdiGoogleCirclesCommunities, mdiHomeAssistant, mdiHomeAutomation, + mdiImage, mdiImageFilterFrames, mdiLightbulb, mdiLightningBolt, @@ -90,6 +91,7 @@ export const FIXED_DOMAIN_ICONS = { group: mdiGoogleCirclesCommunities, homeassistant: mdiHomeAssistant, homekit: mdiHomeAutomation, + image: mdiImage, image_processing: mdiImageFilterFrames, input_button: mdiGestureTapButton, input_datetime: mdiCalendarClock, @@ -180,6 +182,7 @@ export const DOMAINS_WITH_CARD = [ "input_select", "input_number", "input_text", + "humidifier", "lock", "media_player", "number", diff --git a/src/common/datetime/absolute_time.ts b/src/common/datetime/absolute_time.ts index df67e845df..261c78ff83 100644 --- a/src/common/datetime/absolute_time.ts +++ b/src/common/datetime/absolute_time.ts @@ -1,4 +1,5 @@ import { isSameDay, isSameYear } from "date-fns"; +import { HassConfig } from "home-assistant-js-websocket"; import { FrontendLocaleData } from "../../data/translation"; import { formatShortDateTime, @@ -9,15 +10,16 @@ import { formatTime } from "./format_time"; export const absoluteTime = ( from: Date, locale: FrontendLocaleData, + config: HassConfig, to?: Date ): string => { const _to = to ?? new Date(); if (isSameDay(from, _to)) { - return formatTime(from, locale); + return formatTime(from, locale, config); } if (isSameYear(from, _to)) { - return formatShortDateTime(from, locale); + return formatShortDateTime(from, locale, config); } - return formatShortDateTimeWithYear(from, locale); + return formatShortDateTimeWithYear(from, locale, config); }; diff --git a/src/common/datetime/calc_date.ts b/src/common/datetime/calc_date.ts new file mode 100644 index 0000000000..5a4da09a29 --- /dev/null +++ b/src/common/datetime/calc_date.ts @@ -0,0 +1,25 @@ +import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz"; +import { HassConfig } from "home-assistant-js-websocket"; +import { FrontendLocaleData, TimeZone } from "../../data/translation"; + +const calcZonedDate = ( + date: Date, + tz: string, + fn: (date: Date, options?: any) => Date, + options? +) => { + const inputZoned = utcToZonedTime(date, tz); + const fnZoned = fn(inputZoned, options); + return zonedTimeToUtc(fnZoned, tz); +}; + +export const calcDate = ( + date: Date, + fn: (date: Date, options?: any) => Date, + locale: FrontendLocaleData, + config: HassConfig, + options? +) => + locale.time_zone === TimeZone.server + ? calcZonedDate(date, config.time_zone, fn, options) + : fn(date, options); diff --git a/src/common/datetime/format_date.ts b/src/common/datetime/format_date.ts index cb6893a625..ffb48bc64e 100644 --- a/src/common/datetime/format_date.ts +++ b/src/common/datetime/format_date.ts @@ -1,3 +1,4 @@ +import { HassConfig } from "home-assistant-js-websocket"; import memoizeOne from "memoize-one"; import { FrontendLocaleData, DateFormat } from "../../data/translation"; import "../../resources/intl-polyfill"; @@ -5,37 +6,44 @@ import "../../resources/intl-polyfill"; // Tuesday, August 10 export const formatDateWeekdayDay = ( dateObj: Date, - locale: FrontendLocaleData -) => formatDateWeekdayDayMem(locale).format(dateObj); + locale: FrontendLocaleData, + config: HassConfig +) => formatDateWeekdayDayMem(locale, config.time_zone).format(dateObj); const formatDateWeekdayDayMem = memoizeOne( - (locale: FrontendLocaleData) => + (locale: FrontendLocaleData, serverTimeZone: string) => new Intl.DateTimeFormat(locale.language, { weekday: "long", month: "long", day: "numeric", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }) ); // August 10, 2021 -export const formatDate = (dateObj: Date, locale: FrontendLocaleData) => - formatDateMem(locale).format(dateObj); +export const formatDate = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatDateMem(locale, config.time_zone).format(dateObj); const formatDateMem = memoizeOne( - (locale: FrontendLocaleData) => + (locale: FrontendLocaleData, serverTimeZone: string) => new Intl.DateTimeFormat(locale.language, { year: "numeric", month: "long", day: "numeric", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }) ); // 10/08/2021 export const formatDateNumeric = ( dateObj: Date, - locale: FrontendLocaleData + locale: FrontendLocaleData, + config: HassConfig ) => { - const formatter = formatDateNumericMem(locale); + const formatter = formatDateNumericMem(locale, config.time_zone); if ( locale.date_format === DateFormat.language || @@ -67,83 +75,120 @@ export const formatDateNumeric = ( return formats[locale.date_format]; }; -const formatDateNumericMem = memoizeOne((locale: FrontendLocaleData) => { - const localeString = - locale.date_format === DateFormat.system ? undefined : locale.language; +const formatDateNumericMem = memoizeOne( + (locale: FrontendLocaleData, serverTimeZone: string) => { + const localeString = + locale.date_format === DateFormat.system ? undefined : locale.language; + + if ( + locale.date_format === DateFormat.language || + locale.date_format === DateFormat.system + ) { + return new Intl.DateTimeFormat(localeString, { + year: "numeric", + month: "numeric", + day: "numeric", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }); + } - if ( - locale.date_format === DateFormat.language || - locale.date_format === DateFormat.system - ) { return new Intl.DateTimeFormat(localeString, { year: "numeric", month: "numeric", day: "numeric", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }); } - - return new Intl.DateTimeFormat(localeString, { - year: "numeric", - month: "numeric", - day: "numeric", - }); -}); +); // Aug 10 -export const formatDateShort = (dateObj: Date, locale: FrontendLocaleData) => - formatDateShortMem(locale).format(dateObj); +export const formatDateShort = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatDateShortMem(locale, config.time_zone).format(dateObj); const formatDateShortMem = memoizeOne( - (locale: FrontendLocaleData) => + (locale: FrontendLocaleData, serverTimeZone: string) => new Intl.DateTimeFormat(locale.language, { day: "numeric", month: "short", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }) ); // August 2021 export const formatDateMonthYear = ( dateObj: Date, - locale: FrontendLocaleData -) => formatDateMonthYearMem(locale).format(dateObj); + locale: FrontendLocaleData, + config: HassConfig +) => formatDateMonthYearMem(locale, config.time_zone).format(dateObj); const formatDateMonthYearMem = memoizeOne( - (locale: FrontendLocaleData) => + (locale: FrontendLocaleData, serverTimeZone: string) => new Intl.DateTimeFormat(locale.language, { month: "long", year: "numeric", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }) ); // August -export const formatDateMonth = (dateObj: Date, locale: FrontendLocaleData) => - formatDateMonthMem(locale).format(dateObj); +export const formatDateMonth = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatDateMonthMem(locale, config.time_zone).format(dateObj); const formatDateMonthMem = memoizeOne( - (locale: FrontendLocaleData) => + (locale: FrontendLocaleData, serverTimeZone: string) => new Intl.DateTimeFormat(locale.language, { month: "long", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }) ); // 2021 -export const formatDateYear = (dateObj: Date, locale: FrontendLocaleData) => - formatDateYearMem(locale).format(dateObj); +export const formatDateYear = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatDateYearMem(locale, config.time_zone).format(dateObj); const formatDateYearMem = memoizeOne( - (locale: FrontendLocaleData) => + (locale: FrontendLocaleData, serverTimeZone: string) => new Intl.DateTimeFormat(locale.language, { year: "numeric", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }) ); // Monday -export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) => - formatDateWeekdayMem(locale).format(dateObj); +export const formatDateWeekday = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatDateWeekdayMem(locale, config.time_zone).format(dateObj); const formatDateWeekdayMem = memoizeOne( - (locale: FrontendLocaleData) => + (locale: FrontendLocaleData, serverTimeZone: string) => new Intl.DateTimeFormat(locale.language, { weekday: "long", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) +); + +// Mon +export const formatDateWeekdayShort = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatDateWeekdayShortMem(locale, config.time_zone).format(dateObj); + +const formatDateWeekdayShortMem = memoizeOne( + (locale: FrontendLocaleData, serverTimeZone: string) => + new Intl.DateTimeFormat(locale.language, { + weekday: "short", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }) ); diff --git a/src/common/datetime/format_date_time.ts b/src/common/datetime/format_date_time.ts index 7c38d1fd18..52700b4a23 100644 --- a/src/common/datetime/format_date_time.ts +++ b/src/common/datetime/format_date_time.ts @@ -1,102 +1,99 @@ +import { HassConfig } from "home-assistant-js-websocket"; import memoizeOne from "memoize-one"; import { FrontendLocaleData } from "../../data/translation"; import "../../resources/intl-polyfill"; -import { useAmPm } from "./use_am_pm"; import { formatDateNumeric } from "./format_date"; import { formatTime } from "./format_time"; +import { useAmPm } from "./use_am_pm"; // August 9, 2021, 8:23 AM -export const formatDateTime = (dateObj: Date, locale: FrontendLocaleData) => - formatDateTimeMem(locale).format(dateObj); +export const formatDateTime = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatDateTimeMem(locale, config.time_zone).format(dateObj); const formatDateTimeMem = memoizeOne( - (locale: FrontendLocaleData) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - year: "numeric", - month: "long", - day: "numeric", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - hour12: useAmPm(locale), - } - ) + (locale: FrontendLocaleData, serverTimeZone: string) => + new Intl.DateTimeFormat(locale.language, { + year: "numeric", + month: "long", + day: "numeric", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // Aug 9, 2021, 8:23 AM export const formatShortDateTimeWithYear = ( dateObj: Date, - locale: FrontendLocaleData -) => formatShortDateTimeWithYearMem(locale).format(dateObj); + locale: FrontendLocaleData, + config: HassConfig +) => formatShortDateTimeWithYearMem(locale, config.time_zone).format(dateObj); const formatShortDateTimeWithYearMem = memoizeOne( - (locale: FrontendLocaleData) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - year: "numeric", - month: "short", - day: "numeric", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - hour12: useAmPm(locale), - } - ) + (locale: FrontendLocaleData, serverTimeZone: string) => + new Intl.DateTimeFormat(locale.language, { + year: "numeric", + month: "short", + day: "numeric", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // Aug 9, 8:23 AM export const formatShortDateTime = ( dateObj: Date, - locale: FrontendLocaleData -) => formatShortDateTimeMem(locale).format(dateObj); + locale: FrontendLocaleData, + config: HassConfig +) => formatShortDateTimeMem(locale, config.time_zone).format(dateObj); const formatShortDateTimeMem = memoizeOne( - (locale: FrontendLocaleData) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - month: "short", - day: "numeric", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - hour12: useAmPm(locale), - } - ) + (locale: FrontendLocaleData, serverTimeZone: string) => + new Intl.DateTimeFormat(locale.language, { + month: "short", + day: "numeric", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // August 9, 2021, 8:23:15 AM export const formatDateTimeWithSeconds = ( dateObj: Date, - locale: FrontendLocaleData -) => formatDateTimeWithSecondsMem(locale).format(dateObj); + locale: FrontendLocaleData, + config: HassConfig +) => formatDateTimeWithSecondsMem(locale, config.time_zone).format(dateObj); const formatDateTimeWithSecondsMem = memoizeOne( - (locale: FrontendLocaleData) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - year: "numeric", - month: "long", - day: "numeric", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: useAmPm(locale), - } - ) + (locale: FrontendLocaleData, serverTimeZone: string) => + new Intl.DateTimeFormat(locale.language, { + year: "numeric", + month: "long", + day: "numeric", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + second: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // 9/8/2021, 8:23 AM export const formatDateTimeNumeric = ( dateObj: Date, - locale: FrontendLocaleData -) => `${formatDateNumeric(dateObj, locale)}, ${formatTime(dateObj, locale)}`; + locale: FrontendLocaleData, + config: HassConfig +) => + `${formatDateNumeric(dateObj, locale, config)}, ${formatTime( + dateObj, + locale, + config + )}`; diff --git a/src/common/datetime/format_time.ts b/src/common/datetime/format_time.ts index ec4459fe2c..948eb553fe 100644 --- a/src/common/datetime/format_time.ts +++ b/src/common/datetime/format_time.ts @@ -1,76 +1,76 @@ +import { HassConfig } from "home-assistant-js-websocket"; import memoizeOne from "memoize-one"; import { FrontendLocaleData } from "../../data/translation"; import "../../resources/intl-polyfill"; import { useAmPm } from "./use_am_pm"; // 9:15 PM || 21:15 -export const formatTime = (dateObj: Date, locale: FrontendLocaleData) => - formatTimeMem(locale).format(dateObj); +export const formatTime = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatTimeMem(locale, config.time_zone).format(dateObj); const formatTimeMem = memoizeOne( - (locale: FrontendLocaleData) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - hour: "numeric", - minute: "2-digit", - hour12: useAmPm(locale), - } - ) + (locale: FrontendLocaleData, serverTimeZone: string) => + new Intl.DateTimeFormat(locale.language, { + hour: "numeric", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // 9:15:24 PM || 21:15:24 export const formatTimeWithSeconds = ( dateObj: Date, - locale: FrontendLocaleData -) => formatTimeWithSecondsMem(locale).format(dateObj); + locale: FrontendLocaleData, + config: HassConfig +) => formatTimeWithSecondsMem(locale, config.time_zone).format(dateObj); const formatTimeWithSecondsMem = memoizeOne( - (locale: FrontendLocaleData) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: useAmPm(locale), - } - ) + (locale: FrontendLocaleData, serverTimeZone: string) => + new Intl.DateTimeFormat(locale.language, { + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + second: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // Tuesday 7:00 PM || Tuesday 19:00 -export const formatTimeWeekday = (dateObj: Date, locale: FrontendLocaleData) => - formatTimeWeekdayMem(locale).format(dateObj); +export const formatTimeWeekday = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatTimeWeekdayMem(locale, config.time_zone).format(dateObj); const formatTimeWeekdayMem = memoizeOne( - (locale: FrontendLocaleData) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - weekday: "long", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - hour12: useAmPm(locale), - } - ) + (locale: FrontendLocaleData, serverTimeZone: string) => + new Intl.DateTimeFormat(locale.language, { + weekday: "long", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // 21:15 -export const formatTime24h = (dateObj: Date) => - formatTime24hMem().format(dateObj); +export const formatTime24h = ( + dateObj: Date, + locale: FrontendLocaleData, + config: HassConfig +) => formatTime24hMem(locale, config.time_zone).format(dateObj); const formatTime24hMem = memoizeOne( - () => + (locale: FrontendLocaleData, serverTimeZone: string) => // en-GB to fix Chrome 24:59 to 0:59 https://stackoverflow.com/a/60898146 new Intl.DateTimeFormat("en-GB", { hour: "numeric", minute: "2-digit", hour12: false, + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, }) ); diff --git a/src/common/datetime/use_am_pm.ts b/src/common/datetime/use_am_pm.ts index 97bf2911b8..4510fee522 100644 --- a/src/common/datetime/use_am_pm.ts +++ b/src/common/datetime/use_am_pm.ts @@ -8,8 +8,10 @@ export const useAmPm = memoizeOne((locale: FrontendLocaleData): boolean => { ) { const testLanguage = locale.time_format === TimeFormat.language ? locale.language : undefined; - const test = new Date().toLocaleString(testLanguage); - return test.includes("AM") || test.includes("PM"); + const test = new Date("January 1, 2023 22:00:00").toLocaleString( + testLanguage + ); + return test.includes("10"); } return locale.time_format === TimeFormat.am_pm; diff --git a/src/common/decorators/local-storage.ts b/src/common/decorators/storage.ts similarity index 64% rename from src/common/decorators/local-storage.ts rename to src/common/decorators/storage.ts index 752b95933e..a35f0b414a 100644 --- a/src/common/decorators/local-storage.ts +++ b/src/common/decorators/storage.ts @@ -1,13 +1,15 @@ import { UnsubscribeFunc } from "home-assistant-js-websocket"; -import { PropertyDeclaration, ReactiveElement } from "lit"; +import { ReactiveElement } from "lit"; +import { InternalPropertyDeclaration } from "lit/decorators"; import type { ClassElement } from "../../types"; type Callback = (oldValue: any, newValue: any) => void; -class Storage { - constructor(subscribe = true, storage = window.localStorage) { +class StorageClass { + constructor(storage = window.localStorage) { this.storage = storage; - if (!subscribe) { + if (storage !== window.localStorage) { + // storage events only work for localStorage return; } window.addEventListener("storage", (ev: StorageEvent) => { @@ -77,6 +79,7 @@ class Storage { } public setValue(storageKey: string, value: any): any { + const oldValue = this._storage[storageKey]; this._storage[storageKey] = value; try { if (value === undefined) { @@ -86,49 +89,68 @@ class Storage { } } catch (err: any) { // Safari in private mode doesn't allow localstorage + } finally { + if (this._listeners[storageKey]) { + this._listeners[storageKey].forEach((listener) => + listener(oldValue, value) + ); + } } } } -const subscribeStorage = new Storage(); +const storages: Record = {}; -export const LocalStorage = - ( - storageKey?: string, - property?: boolean, - subscribe = true, - storageType?: globalThis.Storage, - propertyOptions?: PropertyDeclaration - ): any => +export const storage = + (options: { + key?: string; + storage?: "localStorage" | "sessionStorage"; + subscribe?: boolean; + state?: boolean; + stateOptions?: InternalPropertyDeclaration; + }): any => (clsElement: ClassElement) => { - const storage = - subscribe && !storageType - ? subscribeStorage - : new Storage(subscribe, storageType); + const storageName = options.storage || "localStorage"; + + let storageInstance: StorageClass; + if (storageName && storageName in storages) { + storageInstance = storages[storageName]; + } else { + storageInstance = new StorageClass(window[storageName]); + storages[storageName] = storageInstance; + } const key = String(clsElement.key); - storageKey = storageKey || String(clsElement.key); + const storageKey = options.key || String(clsElement.key); const initVal = clsElement.initializer ? clsElement.initializer() : undefined; - storage.addFromStorage(storageKey); + storageInstance.addFromStorage(storageKey); - const subscribeChanges = (el: ReactiveElement): UnsubscribeFunc => - storage.subscribeChanges(storageKey!, (oldValue) => { - el.requestUpdate(clsElement.key, oldValue); - }); + const subscribeChanges = + options.subscribe !== false + ? (el: ReactiveElement): UnsubscribeFunc => + storageInstance.subscribeChanges( + storageKey!, + (oldValue, _newValue) => { + el.requestUpdate(clsElement.key, oldValue); + } + ) + : undefined; const getValue = (): any => - storage.hasKey(storageKey!) ? storage.getValue(storageKey!) : initVal; + storageInstance.hasKey(storageKey!) + ? storageInstance.getValue(storageKey!) + : initVal; const setValue = (el: ReactiveElement, value: any) => { let oldValue: unknown | undefined; - if (property) { + if (options.state) { oldValue = getValue(); } - storage.setValue(storageKey!, value); - if (property) { + storageInstance.setValue(storageKey!, value); + if (options.state) { el.requestUpdate(clsElement.key, oldValue); } }; @@ -148,22 +170,23 @@ export const LocalStorage = configurable: true, }, finisher(cls: typeof ReactiveElement) { - if (property && subscribe) { + if (options.state && options.subscribe) { const connectedCallback = cls.prototype.connectedCallback; const disconnectedCallback = cls.prototype.disconnectedCallback; cls.prototype.connectedCallback = function () { connectedCallback.call(this); - this[`__unbsubLocalStorage${key}`] = subscribeChanges(this); + this[`__unbsubLocalStorage${key}`] = subscribeChanges?.(this); }; cls.prototype.disconnectedCallback = function () { disconnectedCallback.call(this); - this[`__unbsubLocalStorage${key}`](); + this[`__unbsubLocalStorage${key}`]?.(); + this[`__unbsubLocalStorage${key}`] = undefined; }; } - if (property) { + if (options.state) { cls.createProperty(clsElement.key, { noAccessor: true, - ...propertyOptions, + ...options.stateOptions, }); } }, diff --git a/src/common/entity/compute_attribute_display.ts b/src/common/entity/compute_attribute_display.ts index 5fdfabe7a8..1d5d8af8a4 100644 --- a/src/common/entity/compute_attribute_display.ts +++ b/src/common/entity/compute_attribute_display.ts @@ -1,4 +1,4 @@ -import { HassEntity } from "home-assistant-js-websocket"; +import { HassConfig, HassEntity } from "home-assistant-js-websocket"; import { html, TemplateResult } from "lit"; import { until } from "lit/directives/until"; import { EntityRegistryDisplayEntry } from "../../data/entity_registry"; @@ -20,6 +20,7 @@ export const computeAttributeValueDisplay = ( localize: LocalizeFunc, stateObj: HassEntity, locale: FrontendLocaleData, + config: HassConfig, entities: HomeAssistant["entities"], attribute: string, value?: any @@ -59,14 +60,14 @@ export const computeAttributeValueDisplay = ( if (isTimestamp(attributeValue)) { const date = new Date(attributeValue); if (checkValidDate(date)) { - return formatDateTimeWithSeconds(date, locale); + return formatDateTimeWithSeconds(date, locale, config); } } // Value was not a timestamp, so only do date formatting const date = new Date(attributeValue); if (checkValidDate(date)) { - return formatDate(date, locale); + return formatDate(date, locale, config); } } } @@ -92,6 +93,7 @@ export const computeAttributeValueDisplay = ( localize, stateObj, locale, + config, entities, attribute, item diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts index a35b8cf780..c41bde1263 100644 --- a/src/common/entity/compute_state_display.ts +++ b/src/common/entity/compute_state_display.ts @@ -1,7 +1,7 @@ -import { HassEntity } from "home-assistant-js-websocket"; +import { HassConfig, HassEntity } from "home-assistant-js-websocket"; import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; import { EntityRegistryDisplayEntry } from "../../data/entity_registry"; -import { FrontendLocaleData } from "../../data/translation"; +import { FrontendLocaleData, TimeZone } from "../../data/translation"; import { updateIsInstallingFromAttributes, UPDATE_SUPPORT_PROGRESS, @@ -28,12 +28,14 @@ export const computeStateDisplaySingleEntity = ( localize: LocalizeFunc, stateObj: HassEntity, locale: FrontendLocaleData, + config: HassConfig, entity: EntityRegistryDisplayEntry | undefined, state?: string ): string => computeStateDisplayFromEntityAttributes( localize, locale, + config, entity, stateObj.entity_id, stateObj.attributes, @@ -44,6 +46,7 @@ export const computeStateDisplay = ( localize: LocalizeFunc, stateObj: HassEntity, locale: FrontendLocaleData, + config: HassConfig, entities: HomeAssistant["entities"], state?: string ): string => { @@ -54,6 +57,7 @@ export const computeStateDisplay = ( return computeStateDisplayFromEntityAttributes( localize, locale, + config, entity, stateObj.entity_id, stateObj.attributes, @@ -64,6 +68,7 @@ export const computeStateDisplay = ( export const computeStateDisplayFromEntityAttributes = ( localize: LocalizeFunc, locale: FrontendLocaleData, + config: HassConfig, entity: EntityRegistryDisplayEntry | undefined, entityId: string, attributes: any, @@ -119,29 +124,40 @@ export const computeStateDisplayFromEntityAttributes = ( if (domain === "datetime") { const time = new Date(state); - return formatDateTime(time, locale); + return formatDateTime(time, locale, config); } if (["date", "input_datetime", "time"].includes(domain)) { // If trying to display an explicit state, need to parse the explicit state to `Date` then format. // Attributes aren't available, we have to use `state`. + + // These are timezone agnostic, so we should NOT use the system timezone here. try { const components = state.split(" "); if (components.length === 2) { // Date and time. - return formatDateTime(new Date(components.join("T")), locale); + return formatDateTime( + new Date(components.join("T")), + { ...locale, time_zone: TimeZone.local }, + config + ); } if (components.length === 1) { if (state.includes("-")) { // Date only. - return formatDate(new Date(`${state}T00:00`), locale); + return formatDate( + new Date(`${state}T00:00`), + { ...locale, time_zone: TimeZone.local }, + config + ); } if (state.includes(":")) { // Time only. const now = new Date(); return formatTime( new Date(`${now.toISOString().split("T")[0]}T${state}`), - locale + { ...locale, time_zone: TimeZone.local }, + config ); } } @@ -153,12 +169,6 @@ export const computeStateDisplayFromEntityAttributes = ( } } - if (domain === "humidifier") { - if (state === "on" && attributes.humidity) { - return `${attributes.humidity} %`; - } - } - // `counter` `number` and `input_number` domains do not have a unit of measurement but should still use `formatNumber` if ( domain === "counter" || @@ -175,11 +185,13 @@ export const computeStateDisplayFromEntityAttributes = ( // state is a timestamp if ( - ["button", "input_button", "scene", "stt", "tts"].includes(domain) || + ["button", "image", "input_button", "scene", "stt", "tts"].includes( + domain + ) || (domain === "sensor" && attributes.device_class === "timestamp") ) { try { - return formatDateTime(new Date(state), locale); + return formatDateTime(new Date(state), locale, config); } catch (_err) { return state; } diff --git a/src/common/entity/domain_icon.ts b/src/common/entity/domain_icon.ts index fe4a911c79..3ced61b3f8 100644 --- a/src/common/entity/domain_icon.ts +++ b/src/common/entity/domain_icon.ts @@ -15,6 +15,7 @@ import { mdiCheckCircleOutline, mdiClock, mdiCloseCircleOutline, + mdiCrosshairsQuestion, mdiFan, mdiFanOff, mdiGestureTapButton, @@ -31,6 +32,7 @@ import { mdiPowerPlugOff, mdiRestart, mdiRobot, + mdiRobotConfused, mdiRobotOff, mdiSpeaker, mdiSpeakerOff, @@ -91,13 +93,19 @@ export const domainIconWithoutDefault = ( return alarmPanelIcon(compareState); case "automation": - return compareState === "off" ? mdiRobotOff : mdiRobot; + return compareState === "unavailable" + ? mdiRobotConfused + : compareState === "off" + ? mdiRobotOff + : mdiRobot; case "binary_sensor": return binarySensorIcon(compareState, stateObj); case "button": switch (stateObj?.attributes.device_class) { + case "identify": + return mdiCrosshairsQuestion; case "restart": return mdiRestart; case "update": diff --git a/src/common/entity/get_states.ts b/src/common/entity/get_states.ts index 5909dd2938..f8d10ad99f 100644 --- a/src/common/entity/get_states.ts +++ b/src/common/entity/get_states.ts @@ -30,6 +30,7 @@ export const FIXED_DOMAIN_STATES = { lock: ["jammed", "locked", "locking", "unlocked", "unlocking"], media_player: ["idle", "off", "paused", "playing", "standby"], person: ["home", "not_home"], + plant: ["ok", "problem"], remote: ["on", "off"], scene: [], schedule: ["on", "off"], @@ -102,7 +103,15 @@ const FIXED_DOMAIN_ATTRIBUTE_STATES = { frontend_stream_type: ["hls", "web_rtc"], }, climate: { - hvac_action: ["off", "idle", "heating", "cooling", "drying", "fan"], + hvac_action: [ + "off", + "idle", + "preheating", + "heating", + "cooling", + "drying", + "fan", + ], }, cover: { device_class: [ @@ -126,6 +135,7 @@ const FIXED_DOMAIN_ATTRIBUTE_STATES = { }, humidifier: { device_class: ["humidifier", "dehumidifier"], + action: ["off", "idle", "humidifying", "drying"], }, media_player: { device_class: ["tv", "speaker", "receiver"], diff --git a/src/common/entity/state_color.ts b/src/common/entity/state_color.ts index efcac098f2..5223076767 100644 --- a/src/common/entity/state_color.ts +++ b/src/common/entity/state_color.ts @@ -110,3 +110,15 @@ export const stateColorProperties = ( return undefined; }; + +export const stateColorBrightness = (stateObj: HassEntity): string => { + if ( + stateObj.attributes.brightness && + computeDomain(stateObj.entity_id) !== "plant" + ) { + // lowest brightness will be around 50% (that's pretty dark) + const brightness = stateObj.attributes.brightness; + return `brightness(${(brightness + 245) / 5}%)`; + } + return ""; +}; diff --git a/src/common/entity/strip_prefix_from_entity_name.ts b/src/common/entity/strip_prefix_from_entity_name.ts index e976b7b18f..bbb024fa37 100644 --- a/src/common/entity/strip_prefix_from_entity_name.ts +++ b/src/common/entity/strip_prefix_from_entity_name.ts @@ -17,12 +17,13 @@ export const stripPrefixFromEntityName = ( if (lowerCasedEntityName.startsWith(lowerCasedPrefixWithSuffix)) { const newName = entityName.substring(lowerCasedPrefixWithSuffix.length); - - // If first word already has an upper case letter (e.g. from brand name) - // leave as-is, otherwise capitalize the first word. - return hasUpperCase(newName.substr(0, newName.indexOf(" "))) - ? newName - : newName[0].toUpperCase() + newName.slice(1); + if (newName.length) { + // If first word already has an upper case letter (e.g. from brand name) + // leave as-is, otherwise capitalize the first word. + return hasUpperCase(newName.substr(0, newName.indexOf(" "))) + ? newName + : newName[0].toUpperCase() + newName.slice(1); + } } } diff --git a/src/common/translations/auto_case_noun.ts b/src/common/translations/auto_case_noun.ts new file mode 100644 index 0000000000..5099003631 --- /dev/null +++ b/src/common/translations/auto_case_noun.ts @@ -0,0 +1,19 @@ +// In a few languages nouns are always capitalized. This helper +// indicates if for a given language that is the case. + +import { capitalizeFirstLetter } from "../string/capitalize-first-letter"; + +export const useCapitalizedNouns = (language: string): boolean => { + switch (language) { + case "de": + case "lb": + return true; + default: + return false; + } +}; + +export const autoCaseNoun = (noun: string, language: string): string => + useCapitalizedNouns(language) + ? capitalizeFirstLetter(noun) + : noun.toLocaleLowerCase(language); diff --git a/src/common/translations/day_names.ts b/src/common/translations/day_names.ts index 34b18fdb4a..885cd84029 100644 --- a/src/common/translations/day_names.ts +++ b/src/common/translations/day_names.ts @@ -1,10 +1,12 @@ import { addDays, startOfWeek } from "date-fns"; +import { HassConfig } from "home-assistant-js-websocket"; import memoizeOne from "memoize-one"; import { FrontendLocaleData } from "../../data/translation"; import { formatDateWeekday } from "../datetime/format_date"; -export const dayNames = memoizeOne((locale: FrontendLocaleData): string[] => - Array.from({ length: 7 }, (_, d) => - formatDateWeekday(addDays(startOfWeek(new Date()), d), locale) - ) +export const dayNames = memoizeOne( + (locale: FrontendLocaleData, config: HassConfig): string[] => + Array.from({ length: 7 }, (_, d) => + formatDateWeekday(addDays(startOfWeek(new Date()), d), locale, config) + ) ); diff --git a/src/common/translations/month_names.ts b/src/common/translations/month_names.ts index 2e456b1855..4df4236d28 100644 --- a/src/common/translations/month_names.ts +++ b/src/common/translations/month_names.ts @@ -1,10 +1,12 @@ import { addMonths, startOfYear } from "date-fns"; +import { HassConfig } from "home-assistant-js-websocket"; import memoizeOne from "memoize-one"; import { FrontendLocaleData } from "../../data/translation"; import { formatDateMonth } from "../datetime/format_date"; -export const monthNames = memoizeOne((locale: FrontendLocaleData): string[] => - Array.from({ length: 12 }, (_, m) => - formatDateMonth(addMonths(startOfYear(new Date()), m), locale) - ) +export const monthNames = memoizeOne( + (locale: FrontendLocaleData, config: HassConfig): string[] => + Array.from({ length: 12 }, (_, m) => + formatDateMonth(addMonths(startOfYear(new Date()), m), locale, config) + ) ); diff --git a/src/components/chart/chart-date-adapter.ts b/src/components/chart/chart-date-adapter.ts index 462ee52dc1..f348823a29 100644 --- a/src/components/chart/chart-date-adapter.ts +++ b/src/components/chart/chart-date-adapter.ts @@ -80,33 +80,89 @@ _adapters._date.override({ format: function (time, fmt: keyof typeof FORMATS) { switch (fmt) { case "datetime": - return formatDateTime(new Date(time), this.options.locale); + return formatDateTime( + new Date(time), + this.options.locale, + this.options.config + ); case "datetimeseconds": - return formatDateTimeWithSeconds(new Date(time), this.options.locale); + return formatDateTimeWithSeconds( + new Date(time), + this.options.locale, + this.options.config + ); case "millisecond": - return formatTimeWithSeconds(new Date(time), this.options.locale); + return formatTimeWithSeconds( + new Date(time), + this.options.locale, + this.options.config + ); case "second": - return formatTimeWithSeconds(new Date(time), this.options.locale); + return formatTimeWithSeconds( + new Date(time), + this.options.locale, + this.options.config + ); case "minute": - return formatTime(new Date(time), this.options.locale); + return formatTime( + new Date(time), + this.options.locale, + this.options.config + ); case "hour": - return formatTime(new Date(time), this.options.locale); + return formatTime( + new Date(time), + this.options.locale, + this.options.config + ); case "weekday": - return formatDateWeekdayDay(new Date(time), this.options.locale); + return formatDateWeekdayDay( + new Date(time), + this.options.locale, + this.options.config + ); case "date": - return formatDate(new Date(time), this.options.locale); + return formatDate( + new Date(time), + this.options.locale, + this.options.config + ); case "day": - return formatDateShort(new Date(time), this.options.locale); + return formatDateShort( + new Date(time), + this.options.locale, + this.options.config + ); case "week": - return formatDate(new Date(time), this.options.locale); + return formatDate( + new Date(time), + this.options.locale, + this.options.config + ); case "month": - return formatDateMonth(new Date(time), this.options.locale); + return formatDateMonth( + new Date(time), + this.options.locale, + this.options.config + ); case "monthyear": - return formatDateMonthYear(new Date(time), this.options.locale); + return formatDateMonthYear( + new Date(time), + this.options.locale, + this.options.config + ); case "quarter": - return formatDate(new Date(time), this.options.locale); + return formatDate( + new Date(time), + this.options.locale, + this.options.config + ); case "year": - return formatDateYear(new Date(time), this.options.locale); + return formatDateYear( + new Date(time), + this.options.locale, + this.options.config + ); default: return ""; } diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index 3ce2235320..b90d57fb62 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -30,6 +30,8 @@ class StateHistoryChartLine extends LitElement { @property({ type: Boolean }) public showNames = true; + @property({ attribute: false }) public startTime!: Date; + @property({ attribute: false }) public endTime!: Date; @property({ type: Number }) public paddingYAxis = 0; @@ -57,7 +59,12 @@ class StateHistoryChartLine extends LitElement { } public willUpdate(changedProps: PropertyValues) { - if (!this.hasUpdated || changedProps.has("showNames")) { + if ( + !this.hasUpdated || + changedProps.has("showNames") || + changedProps.has("startTime") || + changedProps.has("endTime") + ) { this._chartOptions = { parsing: false, animation: false, @@ -71,8 +78,10 @@ class StateHistoryChartLine extends LitElement { adapters: { date: { locale: this.hass.locale, + config: this.hass.config, }, }, + suggestedMin: this.startTime, suggestedMax: this.endTime, ticks: { maxRotation: 0, @@ -145,6 +154,8 @@ class StateHistoryChartLine extends LitElement { } if ( changedProps.has("data") || + changedProps.has("startTime") || + changedProps.has("endTime") || this._chartTime < new Date(this.endTime.getTime() - MIN_TIME_BETWEEN_UPDATES) ) { @@ -375,6 +386,9 @@ class StateHistoryChartLine extends LitElement { lastNullDate = date; } }); + if (lastNullDate !== null) { + pushData(lastNullDate, [null]); + } } // Add an entry for final values diff --git a/src/components/chart/state-history-chart-timeline.ts b/src/components/chart/state-history-chart-timeline.ts index 2d9b93b413..b1686aa2ab 100644 --- a/src/components/chart/state-history-chart-timeline.ts +++ b/src/components/chart/state-history-chart-timeline.ts @@ -98,6 +98,7 @@ export class StateHistoryChartTimeline extends LitElement { adapters: { date: { locale: this.hass.locale, + config: this.hass.config, }, }, suggestedMin: this.startTime, @@ -181,8 +182,16 @@ export class StateHistoryChartTimeline extends LitElement { return [ d.label || "", - formatDateTimeWithSeconds(d.start, this.hass.locale), - formatDateTimeWithSeconds(d.end, this.hass.locale), + formatDateTimeWithSeconds( + d.start, + this.hass.locale, + this.hass.config + ), + formatDateTimeWithSeconds( + d.end, + this.hass.locale, + this.hass.config + ), formattedDuration, ]; }, diff --git a/src/components/chart/state-history-charts.ts b/src/components/chart/state-history-charts.ts index 0e143284e4..0ce32a583e 100644 --- a/src/components/chart/state-history-charts.ts +++ b/src/components/chart/state-history-charts.ts @@ -52,8 +52,12 @@ export class StateHistoryCharts extends LitElement { @property({ attribute: false }) public endTime?: Date; + @property({ attribute: false }) public startTime?: Date; + @property({ type: Boolean, attribute: "up-to-now" }) public upToNow = false; + @property() public hoursToShow?: number; + @property({ type: Boolean }) public showNames = true; @property({ type: Boolean }) public isLoadingData = false; @@ -95,13 +99,24 @@ export class StateHistoryCharts extends LitElement { this._computedEndTime = this.upToNow || !this.endTime || this.endTime > now ? now : this.endTime; - this._computedStartTime = new Date( - this.historyData.timeline.reduce( - (minTime, stateInfo) => - Math.min(minTime, new Date(stateInfo.data[0].last_changed).getTime()), - new Date().getTime() - ) - ); + if (this.startTime) { + this._computedStartTime = this.startTime; + } else if (this.hoursToShow) { + this._computedStartTime = new Date( + new Date().getTime() - 60 * 60 * this.hoursToShow * 1000 + ); + } else { + this._computedStartTime = new Date( + this.historyData.timeline.reduce( + (minTime, stateInfo) => + Math.min( + minTime, + new Date(stateInfo.data[0].last_changed).getTime() + ), + new Date().getTime() + ) + ); + } const combinedItems = this.historyData.timeline.length ? (this.virtualize @@ -142,6 +157,7 @@ export class StateHistoryCharts extends LitElement { .data=${item.data} .identifier=${item.identifier} .showNames=${this.showNames} + .startTime=${this._computedStartTime} .endTime=${this._computedEndTime} .paddingYAxis=${this._maxYWidth} .names=${this.names} diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index 014e658417..d3edd154f7 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -146,6 +146,7 @@ class StatisticsChart extends LitElement { adapters: { date: { locale: this.hass.locale, + config: this.hass.config, }, }, ticks: { @@ -165,7 +166,7 @@ class StatisticsChart extends LitElement { }, }, y: { - beginAtZero: false, + beginAtZero: this.chartType === "bar", ticks: { maxTicksLimit: 7, }, diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index 8ca1d6f68b..6aa6233b6d 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -349,6 +349,7 @@ export class HaDataTable extends LitElement { class="mdc-data-table__content scroller ha-scrollbar" @scroll=${this._saveScrollPos} .items=${this._items} + .keyFunction=${this._keyFunction} .renderItem=${this._renderRow} > `} @@ -357,6 +358,8 @@ export class HaDataTable extends LitElement { `; } + private _keyFunction = (row: DataTableRowData) => row[this.id] || row; + private _renderRow = (row: DataTableRowData, index: number) => { // not sure how this happens... if (!row) { diff --git a/src/components/data-table/sort_filter_worker.ts b/src/components/data-table/sort-filter-worker.ts similarity index 100% rename from src/components/data-table/sort_filter_worker.ts rename to src/components/data-table/sort-filter-worker.ts diff --git a/src/components/data-table/sort-filter.ts b/src/components/data-table/sort-filter.ts index 67d7679397..e0eacf3305 100644 --- a/src/components/data-table/sort-filter.ts +++ b/src/components/data-table/sort-filter.ts @@ -1,5 +1,5 @@ import { Remote, wrap } from "comlink"; -import type { Api } from "./sort_filter_worker"; +import type { Api } from "./sort-filter-worker"; type FilterDataType = Api["filterData"]; type FilterDataParamTypes = Parameters; @@ -9,27 +9,28 @@ type SortDataParamTypes = Parameters; let worker: Remote | undefined; +const getWorker = () => { + if (!worker) { + worker = wrap( + new Worker( + /* webpackChunkName: "sort-filter-worker" */ + new URL("./sort-filter-worker", import.meta.url) + ) + ); + } + return worker; +}; + export const filterData = ( data: FilterDataParamTypes[0], columns: FilterDataParamTypes[1], filter: FilterDataParamTypes[2] -): Promise> => { - if (!worker) { - worker = wrap(new Worker(new URL("./sort_filter_worker", import.meta.url))); - } - - return worker.filterData(data, columns, filter); -}; - +): Promise> => + getWorker().filterData(data, columns, filter); export const sortData = ( data: SortDataParamTypes[0], columns: SortDataParamTypes[1], direction: SortDataParamTypes[2], sortColumn: SortDataParamTypes[3] -): Promise> => { - if (!worker) { - worker = wrap(new Worker(new URL("./sort_filter_worker", import.meta.url))); - } - - return worker.sortData(data, columns, direction, sortColumn); -}; +): Promise> => + getWorker().sortData(data, columns, direction, sortColumn); diff --git a/src/components/device/ha-device-picker.ts b/src/components/device/ha-device-picker.ts index 5fc1bfff80..fb837d15b2 100644 --- a/src/components/device/ha-device-picker.ts +++ b/src/components/device/ha-device-picker.ts @@ -26,6 +26,10 @@ import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { ValueChangedEvent, HomeAssistant } from "../../types"; import "../ha-combo-box"; import type { HaComboBox } from "../ha-combo-box"; +import { + fuzzyFilterSort, + ScorableTextItem, +} from "../../common/string/filter/sequence-matching"; interface Device { name: string; @@ -33,6 +37,8 @@ interface Device { id: string; } +type ScorableDevice = ScorableTextItem & Device; + export type HaDevicePickerDeviceFilterFunc = ( device: DeviceRegistryEntry ) => boolean; @@ -119,13 +125,14 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { deviceFilter: this["deviceFilter"], entityFilter: this["entityFilter"], excludeDevices: this["excludeDevices"] - ): Device[] => { + ): ScorableDevice[] => { if (!devices.length) { return [ { id: "no_devices", area: "", name: this.hass.localize("ui.components.device-picker.no_devices"), + strings: [], }, ]; } @@ -235,6 +242,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { device.area_id && areaLookup[device.area_id] ? areaLookup[device.area_id].name : this.hass.localize("ui.components.device-picker.no_area"), + strings: [device.name || ""], })); if (!outputDevices.length) { return [ @@ -242,6 +250,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { id: "no_devices", area: "", name: this.hass.localize("ui.components.device-picker.no_match"), + strings: [], }, ]; } @@ -284,7 +293,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { (this._init && changedProps.has("_opened") && this._opened) ) { this._init = true; - (this.comboBox as any).items = this._getDevices( + const devices = this._getDevices( this.devices!, this.areas!, this.entities!, @@ -295,6 +304,8 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { this.entityFilter, this.excludeDevices ); + this.comboBox.items = devices; + this.comboBox.filteredItems = devices; } } @@ -314,6 +325,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { item-label-path="name" @opened-changed=${this._openedChanged} @value-changed=${this._deviceChanged} + @filter-changed=${this._filterChanged} > `; } @@ -322,6 +334,14 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) { return this.value || ""; } + private _filterChanged(ev: CustomEvent): void { + const target = ev.target as HaComboBox; + const filterString = ev.detail.value.toLowerCase(); + target.filteredItems = filterString.length + ? fuzzyFilterSort(filterString, target.items || []) + : target.items; + } + private _deviceChanged(ev: ValueChangedEvent) { ev.stopPropagation(); let newValue = ev.detail.value; diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index c06d2cd5a9..115e0795aa 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -7,15 +7,19 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; -import { caseInsensitiveStringCompare } from "../../common/string/compare"; +import { + fuzzyFilterSort, + ScorableTextItem, +} from "../../common/string/filter/sequence-matching"; import { ValueChangedEvent, HomeAssistant } from "../../types"; import "../ha-combo-box"; import type { HaComboBox } from "../ha-combo-box"; import "../ha-icon-button"; import "../ha-svg-icon"; import "./state-badge"; +import { caseInsensitiveStringCompare } from "../../common/string/compare"; -interface HassEntityWithCachedName extends HassEntity { +interface HassEntityWithCachedName extends HassEntity, ScorableTextItem { friendly_name: string; } @@ -159,6 +163,7 @@ export class HaEntityPicker extends LitElement { ), icon: "mdi:magnify", }, + strings: [], }, ]; } @@ -169,10 +174,14 @@ export class HaEntityPicker extends LitElement { ); return entityIds - .map((key) => ({ - ...hass!.states[key], - friendly_name: computeStateName(hass!.states[key]) || key, - })) + .map((key) => { + const friendly_name = computeStateName(hass!.states[key]) || key; + return { + ...hass!.states[key], + friendly_name, + strings: [key, friendly_name], + }; + }) .sort((entityA, entityB) => caseInsensitiveStringCompare( entityA.friendly_name, @@ -201,10 +210,14 @@ export class HaEntityPicker extends LitElement { } states = entityIds - .map((key) => ({ - ...hass!.states[key], - friendly_name: computeStateName(hass!.states[key]) || key, - })) + .map((key) => { + const friendly_name = computeStateName(hass!.states[key]) || key; + return { + ...hass!.states[key], + friendly_name, + strings: [key, friendly_name], + }; + }) .sort((entityA, entityB) => caseInsensitiveStringCompare( entityA.friendly_name, @@ -260,6 +273,7 @@ export class HaEntityPicker extends LitElement { ), icon: "mdi:magnify", }, + strings: [], }, ]; } @@ -293,7 +307,7 @@ export class HaEntityPicker extends LitElement { this.excludeEntities ); if (this._initedStates) { - (this.comboBox as any).filteredItems = this._states; + this.comboBox.filteredItems = this._states; } this._initedStates = true; } @@ -340,12 +354,11 @@ export class HaEntityPicker extends LitElement { } private _filterChanged(ev: CustomEvent): void { + const target = ev.target as HaComboBox; const filterString = ev.detail.value.toLowerCase(); - (this.comboBox as any).filteredItems = this._states.filter( - (entityState) => - entityState.entity_id.toLowerCase().includes(filterString) || - computeStateName(entityState).toLowerCase().includes(filterString) - ); + target.filteredItems = filterString.length + ? fuzzyFilterSort(filterString, this._states) + : this._states; } private _setValue(value: string) { diff --git a/src/components/entity/ha-entity-state-picker.ts b/src/components/entity/ha-entity-state-picker.ts index 7b92f63aa2..688b38e6db 100644 --- a/src/components/entity/ha-entity-state-picker.ts +++ b/src/components/entity/ha-entity-state-picker.ts @@ -62,6 +62,7 @@ class HaEntityStatePicker extends LitElement { this.hass.localize, state, this.hass.locale, + this.hass.config, this.hass.entities, key ) @@ -69,6 +70,7 @@ class HaEntityStatePicker extends LitElement { this.hass.localize, state, this.hass.locale, + this.hass.config, this.hass.entities, this.attribute, key diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts index b5d60d9bb1..519dbfc107 100644 --- a/src/components/entity/ha-state-label-badge.ts +++ b/src/components/entity/ha-state-label-badge.ts @@ -192,6 +192,7 @@ export class HaStateLabelBadge extends LitElement { this.hass!.localize, entityState, this.hass!.locale, + this.hass!.config, this.hass!.entities ); } diff --git a/src/components/entity/state-badge.ts b/src/components/entity/state-badge.ts index 4fbfcaf853..2b6f936497 100644 --- a/src/components/entity/state-badge.ts +++ b/src/components/entity/state-badge.ts @@ -13,7 +13,10 @@ import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; -import { stateColorCss } from "../../common/entity/state_color"; +import { + stateColorCss, + stateColorBrightness, +} from "../../common/entity/state_color"; import { iconColorCSS } from "../../common/style/icon_color_css"; import { cameraUrlWithWidthHeight } from "../../data/camera"; import { HVAC_ACTION_TO_MODE } from "../../data/climate"; @@ -153,8 +156,7 @@ export class StateBadge extends LitElement { // eslint-disable-next-line console.warn(errorMessage); } - // lowest brightness will be around 50% (that's pretty dark) - iconStyle.filter = `brightness(${(brightness + 245) / 5}%)`; + iconStyle.filter = stateColorBrightness(stateObj); } if (stateObj.attributes.hvac_action) { const hvacAction = stateObj.attributes.hvac_action; diff --git a/src/components/ha-absolute-time.ts b/src/components/ha-absolute-time.ts index f782b32317..528c658514 100644 --- a/src/components/ha-absolute-time.ts +++ b/src/components/ha-absolute-time.ts @@ -64,7 +64,11 @@ class HaAbsoluteTime extends ReactiveElement { if (!this.datetime) { this.innerHTML = this.hass.localize("ui.components.absolute_time.never"); } else { - this.innerHTML = absoluteTime(new Date(this.datetime), this.hass.locale); + this.innerHTML = absoluteTime( + new Date(this.datetime), + this.hass.locale, + this.hass.config + ); } } } diff --git a/src/components/ha-area-picker.ts b/src/components/ha-area-picker.ts index 9c06dd8015..1ea409aafe 100644 --- a/src/components/ha-area-picker.ts +++ b/src/components/ha-area-picker.ts @@ -7,6 +7,10 @@ import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; import { fireEvent } from "../common/dom/fire_event"; import { computeDomain } from "../common/entity/compute_domain"; +import { + fuzzyFilterSort, + ScorableTextItem, +} from "../common/string/filter/sequence-matching"; import { AreaRegistryEntry, createAreaRegistryEntry, @@ -28,6 +32,8 @@ import type { HaComboBox } from "./ha-combo-box"; import "./ha-icon-button"; import "./ha-svg-icon"; +type ScorableAreaRegistryEntry = ScorableTextItem & AreaRegistryEntry; + const rowRenderer: ComboBoxLitRenderer = ( item ) => html` ({ + ...area, + strings: [area.area_id, ...area.aliases, area.name], + })); + this.comboBox.items = areas; + this.comboBox.filteredItems = areas; } } @@ -345,8 +354,9 @@ export class HaAreaPicker extends LitElement { return; } - const filteredItems = this.comboBox.items?.filter((item) => - item.name.toLowerCase().includes(filter!.toLowerCase()) + const filteredItems = fuzzyFilterSort( + filter, + this.comboBox?.items || [] ); if (!this.noAdd && filteredItems?.length === 0) { this._suggestion = filter; @@ -409,7 +419,7 @@ export class HaAreaPicker extends LitElement { name, }); const areas = [...Object.values(this.hass.areas), area]; - (this.comboBox as any).filteredItems = this._getAreas( + this.comboBox.filteredItems = this._getAreas( areas, Object.values(this.hass.devices)!, Object.values(this.hass.entities)!, diff --git a/src/components/ha-attributes.ts b/src/components/ha-attributes.ts index c1092cb96f..dce1fd4c07 100644 --- a/src/components/ha-attributes.ts +++ b/src/components/ha-attributes.ts @@ -62,6 +62,7 @@ class HaAttributes extends LitElement { this.hass.localize, this.stateObj!, this.hass.locale, + this.hass.config, this.hass.entities, attribute )} diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts index 7c8be97a6e..7a1617e4ee 100644 --- a/src/components/ha-climate-state.ts +++ b/src/components/ha-climate-state.ts @@ -28,6 +28,7 @@ class HaClimateState extends LitElement { this.hass.localize, this.stateObj, this.hass.locale, + this.hass.config, this.hass.entities, "preset_mode" )}` @@ -136,6 +137,7 @@ class HaClimateState extends LitElement { this.hass.localize, this.stateObj, this.hass.locale, + this.hass.config, this.hass.entities ); @@ -144,6 +146,7 @@ class HaClimateState extends LitElement { this.hass.localize, this.stateObj, this.hass.locale, + this.hass.config, this.hass.entities, "hvac_action" )} (${stateString})` diff --git a/src/components/ha-control-circular-slider.ts b/src/components/ha-control-circular-slider.ts new file mode 100644 index 0000000000..97bc2ea566 --- /dev/null +++ b/src/components/ha-control-circular-slider.ts @@ -0,0 +1,618 @@ +import { + DIRECTION_ALL, + Manager, + Pan, + Tap, + TouchMouseInput, +} from "@egjs/hammerjs"; +import { + CSSResultGroup, + LitElement, + PropertyValues, + TemplateResult, + css, + html, + nothing, + svg, +} from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { ifDefined } from "lit/directives/if-defined"; +import { styleMap } from "lit/directives/style-map"; +import { fireEvent } from "../common/dom/fire_event"; +import { clamp } from "../common/number/clamp"; +import { arc } from "../resources/svg-arc"; + +const MAX_ANGLE = 270; +const ROTATE_ANGLE = 360 - MAX_ANGLE / 2 - 90; +const RADIUS = 145; + +function xy2polar(x: number, y: number) { + const r = Math.sqrt(x * x + y * y); + const phi = Math.atan2(y, x); + return [r, phi]; +} + +function rad2deg(rad: number) { + return (rad / (2 * Math.PI)) * 360; +} + +type ActiveSlider = "low" | "high" | "value"; + +declare global { + interface HASSDomEvents { + "value-changing": { value: unknown }; + "low-changing": { value: unknown }; + "low-changed": { value: unknown }; + "high-changing": { value: unknown }; + "high-changed": { value: unknown }; + } +} + +const A11Y_KEY_CODES = new Set([ + "ArrowRight", + "ArrowUp", + "ArrowLeft", + "ArrowDown", + "PageUp", + "PageDown", + "Home", + "End", +]); + +@customElement("ha-control-circular-slider") +export class HaControlCircularSlider extends LitElement { + @property({ type: Boolean, reflect: true }) + public disabled = false; + + @property({ type: Boolean }) + public dual?: boolean; + + @property({ type: Boolean, reflect: true }) + public inverted?: boolean; + + @property({ type: String }) + public label?: string; + + @property({ type: String, attribute: "low-label" }) + public lowLabel?: string; + + @property({ type: String, attribute: "high-label" }) + public highLabel?: string; + + @property({ type: Number }) + public value?: number; + + @property({ type: Number }) + public low?: number; + + @property({ type: Number }) + public high?: number; + + @property({ type: Number }) + public current?: number; + + @property({ type: Number }) + public step = 1; + + @property({ type: Number }) + public min = 0; + + @property({ type: Number }) + public max = 100; + + @state() + public _localValue?: number = this.value; + + @state() + public _localLow?: number = this.low; + + @state() + public _localHigh?: number = this.high; + + @state() + public _activeSlider?: ActiveSlider; + + @state() + public _lastSlider?: ActiveSlider; + + private _valueToPercentage(value: number) { + return ( + (clamp(value, this.min, this.max) - this.min) / (this.max - this.min) + ); + } + + private _percentageToValue(value: number) { + return (this.max - this.min) * value + this.min; + } + + private _steppedValue(value: number) { + return Math.round(value / this.step) * this.step; + } + + private _boundedValue(value: number) { + const min = + this._activeSlider === "high" + ? Math.min(this._localLow ?? this.max) + : this.min; + const max = + this._activeSlider === "low" + ? Math.max(this._localHigh ?? this.min) + : this.max; + return Math.min(Math.max(value, min), max); + } + + protected firstUpdated(changedProps: PropertyValues): void { + super.firstUpdated(changedProps); + this._setupListeners(); + } + + protected updated(changedProps: PropertyValues): void { + super.updated(changedProps); + if (!this._activeSlider) { + if (changedProps.has("value")) { + this._localValue = this.value; + } + if (changedProps.has("low")) { + this._localLow = this.low; + } + if (changedProps.has("high")) { + this._localHigh = this.high; + } + } + } + + connectedCallback(): void { + super.connectedCallback(); + this._setupListeners(); + } + + disconnectedCallback(): void { + super.disconnectedCallback(); + } + + private _mc?: HammerManager; + + private _getPercentageFromEvent = (e: HammerInput) => { + const bound = this._slider.getBoundingClientRect(); + const x = (2 * (e.center.x - bound.left - bound.width / 2)) / bound.width; + const y = (2 * (e.center.y - bound.top - bound.height / 2)) / bound.height; + + const [, phi] = xy2polar(x, y); + + const offset = (360 - MAX_ANGLE) / 2; + + const angle = ((rad2deg(phi) + offset - ROTATE_ANGLE + 360) % 360) - offset; + + return Math.max(Math.min(angle / MAX_ANGLE, 1), 0); + }; + + @query("#slider") + private _slider; + + @query("#interaction") + private _interaction; + + private _findActiveSlider(value: number): ActiveSlider { + if (!this.dual) return "value"; + const low = Math.max(this._localLow ?? this.min, this.min); + const high = Math.min(this._localHigh ?? this.max, this.max); + if (low >= value) { + return "low"; + } + if (high <= value) { + return "high"; + } + const lowDistance = Math.abs(value - low); + const highDistance = Math.abs(value - high); + return lowDistance <= highDistance ? "low" : "high"; + } + + private _setActiveValue(value: number) { + switch (this._activeSlider) { + case "high": + this._localHigh = value; + break; + case "low": + this._localLow = value; + break; + case "value": + this._localValue = value; + break; + } + } + + private _getActiveValue(): number | undefined { + switch (this._activeSlider) { + case "high": + return this._localHigh; + case "low": + return this._localLow; + case "value": + return this._localValue; + } + return undefined; + } + + _setupListeners() { + if (this._interaction && !this._mc) { + this._mc = new Manager(this._interaction, { + inputClass: TouchMouseInput, + }); + this._mc.add( + new Pan({ + direction: DIRECTION_ALL, + enable: true, + threshold: 0, + }) + ); + + this._mc.add(new Tap({ event: "singletap" })); + + this._mc.on("pan", (e) => { + e.srcEvent.stopPropagation(); + e.srcEvent.preventDefault(); + }); + this._mc.on("panstart", (e) => { + if (this.disabled) return; + const percentage = this._getPercentageFromEvent(e); + const raw = this._percentageToValue(percentage); + this._activeSlider = this._findActiveSlider(raw); + this._lastSlider = this._activeSlider; + this.shadowRoot?.getElementById("#slider")?.focus(); + }); + this._mc.on("pancancel", () => { + if (this.disabled) return; + this._activeSlider = undefined; + }); + this._mc.on("panmove", (e) => { + if (this.disabled) return; + const percentage = this._getPercentageFromEvent(e); + const raw = this._percentageToValue(percentage); + const bounded = this._boundedValue(raw); + this._setActiveValue(bounded); + const stepped = this._steppedValue(bounded); + if (this._activeSlider) { + fireEvent(this, `${this._activeSlider}-changing`, { value: stepped }); + } + }); + this._mc.on("panend", (e) => { + if (this.disabled) return; + const percentage = this._getPercentageFromEvent(e); + const raw = this._percentageToValue(percentage); + const bounded = this._boundedValue(raw); + const stepped = this._steppedValue(bounded); + this._setActiveValue(stepped); + if (this._activeSlider) { + fireEvent(this, `${this._activeSlider}-changing`, { + value: undefined, + }); + fireEvent(this, `${this._activeSlider}-changed`, { value: stepped }); + } + this._activeSlider = undefined; + }); + this._mc.on("singletap", (e) => { + if (this.disabled) return; + const percentage = this._getPercentageFromEvent(e); + const raw = this._percentageToValue(percentage); + this._activeSlider = this._findActiveSlider(raw); + const bounded = this._boundedValue(raw); + const stepped = this._steppedValue(bounded); + this._setActiveValue(stepped); + if (this._activeSlider) { + fireEvent(this, `${this._activeSlider}-changing`, { + value: undefined, + }); + fireEvent(this, `${this._activeSlider}-changed`, { value: stepped }); + } + this._lastSlider = this._activeSlider; + this.shadowRoot?.getElementById("#slider")?.focus(); + this._activeSlider = undefined; + }); + } + } + + private get _tenPercentStep() { + return Math.max(this.step, (this.max - this.min) / 10); + } + + private _handleKeyDown(e: KeyboardEvent) { + if (!A11Y_KEY_CODES.has(e.code)) return; + e.preventDefault(); + if (this._lastSlider) { + this.shadowRoot?.getElementById(this._lastSlider)?.focus(); + } + this._activeSlider = + this._lastSlider ?? ((e.currentTarget as any).id as ActiveSlider); + this._lastSlider = undefined; + + const value = this._getActiveValue(); + + switch (e.code) { + case "ArrowRight": + case "ArrowUp": + this._setActiveValue( + this._boundedValue((value ?? this.min) + this.step) + ); + break; + case "ArrowLeft": + case "ArrowDown": + this._setActiveValue( + this._boundedValue((value ?? this.min) - this.step) + ); + break; + case "PageUp": + this._setActiveValue( + this._steppedValue( + this._boundedValue((value ?? this.min) + this._tenPercentStep) + ) + ); + break; + case "PageDown": + this._setActiveValue( + this._steppedValue( + this._boundedValue((value ?? this.min) - this._tenPercentStep) + ) + ); + break; + case "Home": + this._setActiveValue(this._boundedValue(this.min)); + break; + case "End": + this._setActiveValue(this._boundedValue(this.max)); + break; + } + fireEvent(this, `${this._activeSlider}-changing`, { + value: this._getActiveValue(), + }); + this._activeSlider = undefined; + } + + _handleKeyUp(e: KeyboardEvent) { + if (!A11Y_KEY_CODES.has(e.code)) return; + this._activeSlider = (e.currentTarget as any).id as ActiveSlider; + e.preventDefault(); + fireEvent(this, `${this._activeSlider}-changing`, { + value: undefined, + }); + fireEvent(this, `${this._activeSlider}-changed`, { + value: this._getActiveValue(), + }); + this._activeSlider = undefined; + } + + destroyListeners() { + if (this._mc) { + this._mc.destroy(); + this._mc = undefined; + } + } + + private _strokeDashArc( + percentage: number, + inverted?: boolean + ): [string, string] { + const maxRatio = MAX_ANGLE / 360; + const f = RADIUS * 2 * Math.PI; + if (inverted) { + const arcLength = (1 - percentage) * f * maxRatio; + const strokeDasharray = `${arcLength} ${f - arcLength}`; + const strokeDashOffset = `${arcLength + f * (1 - maxRatio)}`; + return [strokeDasharray, strokeDashOffset]; + } + const arcLength = percentage * f * maxRatio; + const strokeDasharray = `${arcLength} ${f - arcLength}`; + const strokeDashOffset = "0"; + return [strokeDasharray, strokeDashOffset]; + } + + protected render(): TemplateResult { + const trackPath = arc({ x: 0, y: 0, start: 0, end: MAX_ANGLE, r: RADIUS }); + + const lowValue = this.dual ? this._localLow : this._localValue; + const highValue = this._localHigh; + const lowPercentage = this._valueToPercentage(lowValue ?? this.min); + const highPercentage = this._valueToPercentage(highValue ?? this.max); + + const [lowStrokeDasharray, lowStrokeDashOffset] = this._strokeDashArc( + lowPercentage, + this.inverted + ); + + const [highStrokeDasharray, highStrokeDashOffset] = this._strokeDashArc( + highPercentage, + true + ); + + const currentPercentage = this._valueToPercentage(this.current ?? 0); + const currentAngle = currentPercentage * MAX_ANGLE; + + return html` + + + + + + + + ${lowValue != null + ? svg` + + ` + : nothing} + ${this.dual && highValue != null + ? svg` + + ` + : nothing} + ${this.current != null + ? svg` + + + + + ` + : nothing} + + + + `; + } + + static get styles(): CSSResultGroup { + return css` + :host { + --control-circular-slider-color: var(--primary-color); + --control-circular-slider-background: #8b97a3; + --control-circular-slider-background-opacity: 0.3; + --control-circular-slider-low-color: var( + --control-circular-slider-color + ); + --control-circular-slider-high-color: var( + --control-circular-slider-color + ); + } + svg { + width: 320px; + display: block; + } + #slider { + outline: none; + } + #interaction { + display: flex; + fill: none; + stroke: transparent; + stroke-linecap: round; + stroke-width: 48px; + cursor: pointer; + } + #display { + pointer-events: none; + } + :host([disabled]) #interaction { + cursor: initial; + } + + .background { + fill: none; + stroke: var(--control-circular-slider-background); + opacity: var(--control-circular-slider-background-opacity); + transition: stroke 180ms ease-in-out, opacity 180ms ease-in-out; + stroke-linecap: round; + stroke-width: 24px; + } + + .track { + outline: none; + fill: none; + stroke-linecap: round; + stroke-width: 24px; + transition: stroke-width 300ms ease-in-out, + stroke-dasharray 300ms ease-in-out, + stroke-dashoffset 300ms ease-in-out, stroke 180ms ease-in-out, + opacity 180ms ease-in-out; + } + + .track:focus-visible { + stroke-width: 28px; + } + + .pressed .track { + transition: stroke-width 300ms ease-in-out; + } + + .current { + stroke: var(--primary-text-color); + transform: rotate(var(--current-angle, 0)); + transition: transform 300ms ease-in-out; + } + + #value { + stroke: var(--control-circular-slider-color); + } + + #low { + stroke: var(--control-circular-slider-low-color); + } + + #high { + stroke: var(--control-circular-slider-high-color); + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-control-circular-slider": HaControlCircularSlider; + } +} diff --git a/src/components/ha-control-slider.ts b/src/components/ha-control-slider.ts index 2cc172969a..3f4abcf4e3 100644 --- a/src/components/ha-control-slider.ts +++ b/src/components/ha-control-slider.ts @@ -176,7 +176,7 @@ export class HaControlSlider extends LitElement { this._mc = undefined; } this.removeEventListener("keydown", this._handleKeyDown); - this.removeEventListener("keyup", this._handleKeyDown); + this.removeEventListener("keyup", this._handleKeyUp); } private get _tenPercentStep() { diff --git a/src/components/ha-date-input.ts b/src/components/ha-date-input.ts index 2b1cc6fe22..d3b5a0607e 100644 --- a/src/components/ha-date-input.ts +++ b/src/components/ha-date-input.ts @@ -1,9 +1,11 @@ import { mdiCalendar } from "@mdi/js"; +import { HassConfig } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; -import { formatDateNumeric } from "../common/datetime/format_date"; import { firstWeekdayIndex } from "../common/datetime/first_weekday"; +import { formatDateNumeric } from "../common/datetime/format_date"; import { fireEvent } from "../common/dom/fire_event"; +import { TimeZone } from "../data/translation"; import { HomeAssistant } from "../types"; import "./ha-svg-icon"; import "./ha-textfield"; @@ -59,7 +61,11 @@ export class HaDateInput extends LitElement { .value=${this.value ? formatDateNumeric( new Date(`${this.value.split("T")[0]}T00:00:00`), - this.locale + { + ...this.locale, + time_zone: TimeZone.local, + }, + {} as HassConfig ) : ""} .required=${this.required} diff --git a/src/components/ha-date-range-picker.ts b/src/components/ha-date-range-picker.ts index 5c870215a7..557d139ae3 100644 --- a/src/components/ha-date-range-picker.ts +++ b/src/components/ha-date-range-picker.ts @@ -3,6 +3,13 @@ import "@material/mwc-list/mwc-list"; import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { mdiCalendar } from "@mdi/js"; +import { + addDays, + endOfDay, + endOfWeek, + startOfDay, + startOfWeek, +} from "date-fns"; import { css, CSSResultGroup, @@ -12,10 +19,11 @@ import { TemplateResult, } from "lit"; import { customElement, property } from "lit/decorators"; -import { formatDateTime } from "../common/datetime/format_date_time"; -import { formatDate } from "../common/datetime/format_date"; -import { useAmPm } from "../common/datetime/use_am_pm"; +import { calcDate } from "../common/datetime/calc_date"; import { firstWeekdayIndex } from "../common/datetime/first_weekday"; +import { formatDate } from "../common/datetime/format_date"; +import { formatDateTime } from "../common/datetime/format_date_time"; +import { useAmPm } from "../common/datetime/use_am_pm"; import { computeRTLDirection } from "../common/util/compute_rtl"; import { HomeAssistant } from "../types"; import "./date-range-picker"; @@ -34,7 +42,7 @@ export class HaDateRangePicker extends LitElement { @property() public endDate!: Date; - @property() public ranges?: DateRangePickerRanges; + @property() public ranges?: DateRangePickerRanges | false; @property() public autoApply = false; @@ -46,6 +54,70 @@ export class HaDateRangePicker extends LitElement { @property({ type: String }) private _rtlDirection = "ltr"; + protected willUpdate() { + if (!this.hasUpdated && this.ranges === undefined) { + const today = new Date(); + const weekStartsOn = firstWeekdayIndex(this.hass.locale); + const weekStart = calcDate( + today, + startOfWeek, + this.hass.locale, + this.hass.config, + { + weekStartsOn, + } + ); + const weekEnd = calcDate( + today, + endOfWeek, + this.hass.locale, + this.hass.config, + { + weekStartsOn, + } + ); + + this.ranges = { + [this.hass.localize("ui.components.date-range-picker.ranges.today")]: [ + calcDate(today, startOfDay, this.hass.locale, this.hass.config, { + weekStartsOn, + }), + calcDate(today, endOfDay, this.hass.locale, this.hass.config, { + weekStartsOn, + }), + ], + [this.hass.localize( + "ui.components.date-range-picker.ranges.yesterday" + )]: [ + calcDate( + addDays(today, -1), + startOfDay, + this.hass.locale, + this.hass.config, + { + weekStartsOn, + } + ), + calcDate( + addDays(today, -1), + endOfDay, + this.hass.locale, + this.hass.config, + { + weekStartsOn, + } + ), + ], + [this.hass.localize( + "ui.components.date-range-picker.ranges.this_week" + )]: [weekStart, weekEnd], + [this.hass.localize( + "ui.components.date-range-picker.ranges.last_week" + )]: [addDays(weekStart, -7), addDays(weekEnd, -7)], + }; + } + } + protected updated(changedProps: PropertyValues) { if (changedProps.has("hass")) { const oldHass = changedProps.get("hass") as HomeAssistant | undefined; @@ -65,15 +137,19 @@ export class HaDateRangePicker extends LitElement { twentyfour-hours=${this._hour24format} start-date=${this.startDate} end-date=${this.endDate} - ?ranges=${this.ranges !== undefined} + ?ranges=${this.ranges !== false} first-day=${firstWeekdayIndex(this.hass.locale)} >
const getError = (obj, item) => (obj && item.name ? obj[item.name] : null); +const getWarning = (obj, item) => (obj && item.name ? obj[item.name] : null); + @customElement("ha-form") export class HaForm extends LitElement implements HaFormElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -44,10 +46,14 @@ export class HaForm extends LitElement implements HaFormElement { @property() public error?: Record; + @property() public warning?: Record; + @property({ type: Boolean }) public disabled = false; @property() public computeError?: (schema: any, error) => string; + @property() public computeWarning?: (schema: any, warning) => string; + @property() public computeLabel?: ( schema: any, data: HaFormDataContainer @@ -98,6 +104,7 @@ export class HaForm extends LitElement implements HaFormElement { : ""} ${this.schema.map((item) => { const error = getError(this.error, item); + const warning = getWarning(this.warning, item); return html` ${error @@ -106,6 +113,12 @@ export class HaForm extends LitElement implements HaFormElement { ${this._computeError(error, item)} ` + : warning + ? html` + + ${this._computeWarning(warning, item)} + + ` : ""} ${"selector" in item ? html` * { diff --git a/src/components/ha-hls-player.ts b/src/components/ha-hls-player.ts index 3943abb615..cf3ed5a526 100644 --- a/src/components/ha-hls-player.ts +++ b/src/components/ha-hls-player.ts @@ -109,7 +109,8 @@ class HaHLSPlayer extends LitElement { private async _startHls(): Promise { const masterPlaylistPromise = fetch(this.url); - const Hls: typeof HlsType = (await import("hls.js/dist/hls.light")).default; + const Hls: typeof HlsType = (await import("hls.js/dist/hls.light.mjs")) + .default; if (!this.isConnected) { return; diff --git a/src/components/ha-hs-color-picker.ts b/src/components/ha-hs-color-picker.ts index ad739818df..ba5d038526 100644 --- a/src/components/ha-hs-color-picker.ts +++ b/src/components/ha-hs-color-picker.ts @@ -186,9 +186,8 @@ class HaHsColorPicker extends LitElement { } if (changedProps.has("value")) { if ( - this.value !== undefined && - (this._localValue?.[0] !== this.value[0] || - this._localValue?.[1] !== this.value[1]) + this._localValue?.[0] !== this.value?.[0] || + this._localValue?.[1] !== this.value?.[1] ) { this._resetPosition(); } @@ -243,7 +242,11 @@ class HaHsColorPicker extends LitElement { } private _resetPosition() { - if (this.value === undefined) return; + if (this.value === undefined) { + this._cursorPosition = undefined; + this._localValue = undefined; + return; + } this._cursorPosition = this._getCoordsFromValue(this.value); this._localValue = this.value; } @@ -373,17 +376,20 @@ class HaHsColorPicker extends LitElement { return css` :host { display: block; + outline: none; } .container { position: relative; width: 100%; height: 100%; - cursor: pointer; display: flex; } canvas { width: 100%; height: 100%; + object-fit: contain; + border-radius: 50%; + cursor: pointer; } svg { position: absolute; diff --git a/src/components/ha-humidifier-state.ts b/src/components/ha-humidifier-state.ts new file mode 100644 index 0000000000..fbf9cfe519 --- /dev/null +++ b/src/components/ha-humidifier-state.ts @@ -0,0 +1,137 @@ +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display"; +import { computeStateDisplay } from "../common/entity/compute_state_display"; +import { formatNumber } from "../common/number/format_number"; +import { blankBeforePercent } from "../common/translations/blank_before_percent"; +import { isUnavailableState, OFF } from "../data/entity"; +import { HumidifierEntity } from "../data/humidifier"; +import type { HomeAssistant } from "../types"; + +@customElement("ha-humidifier-state") +class HaHumidifierState extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HumidifierEntity; + + protected render(): TemplateResult { + const currentStatus = this._computeCurrentStatus(); + + return html`
+ ${!isUnavailableState(this.stateObj.state) + ? html` + ${this._localizeState()} + ${this.stateObj.attributes.mode + ? html`- + ${computeAttributeValueDisplay( + this.hass.localize, + this.stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities, + "mode" + )}` + : ""} + +
${this._computeTarget()}
` + : this._localizeState()} +
+ + ${currentStatus && !isUnavailableState(this.stateObj.state) + ? html`
+ ${this.hass.localize("ui.card.climate.currently")}: +
${currentStatus}
+
` + : ""}`; + } + + private _computeCurrentStatus(): string | undefined { + if (!this.hass || !this.stateObj) { + return undefined; + } + + if (this.stateObj.attributes.current_humidity != null) { + return `${formatNumber( + this.stateObj.attributes.current_humidity, + this.hass.locale + )}${blankBeforePercent(this.hass.locale)}%`; + } + + return undefined; + } + + private _computeTarget(): string { + if (!this.hass || !this.stateObj) { + return ""; + } + + if (this.stateObj.attributes.humidity != null) { + return `${formatNumber( + this.stateObj.attributes.humidity, + this.hass.locale + )}${blankBeforePercent(this.hass.locale)}%`; + } + + return ""; + } + + private _localizeState(): string { + if (isUnavailableState(this.stateObj.state)) { + return this.hass.localize(`state.default.${this.stateObj.state}`); + } + + const stateString = computeStateDisplay( + this.hass.localize, + this.stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities + ); + + return this.stateObj.attributes.action && this.stateObj.state !== OFF + ? `${computeAttributeValueDisplay( + this.hass.localize, + this.stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities, + "action" + )} (${stateString})` + : stateString; + } + + static get styles(): CSSResultGroup { + return css` + :host { + display: flex; + flex-direction: column; + justify-content: center; + white-space: nowrap; + } + + .target { + color: var(--primary-text-color); + } + + .current { + color: var(--secondary-text-color); + } + + .state-label { + font-weight: bold; + text-transform: capitalize; + } + + .unit { + display: inline-block; + direction: ltr; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-humidifier-state": HaHumidifierState; + } +} diff --git a/src/components/ha-icon-button-group.ts b/src/components/ha-icon-button-group.ts new file mode 100644 index 0000000000..cd739082ab --- /dev/null +++ b/src/components/ha-icon-button-group.ts @@ -0,0 +1,38 @@ +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement } from "lit/decorators"; + +@customElement("ha-icon-button-group") +export class HaIconButtonGroup extends LitElement { + protected render(): TemplateResult { + return html``; + } + + static get styles(): CSSResultGroup { + return css` + :host { + position: relative; + display: flex; + flex-direction: row; + align-items: center; + height: 56px; + border-radius: 28px; + background-color: rgba(139, 145, 151, 0.1); + box-sizing: border-box; + width: auto; + padding: 4px; + gap: 4px; + } + ::slotted(.separator) { + background-color: rgba(var(--rgb-primary-text-color), 0.15); + width: 1px; + height: 40px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-icon-button-group": HaIconButtonGroup; + } +} diff --git a/src/components/ha-icon-button-toggle.ts b/src/components/ha-icon-button-toggle.ts new file mode 100644 index 0000000000..56d0b37c82 --- /dev/null +++ b/src/components/ha-icon-button-toggle.ts @@ -0,0 +1,52 @@ +import { css, CSSResultGroup } from "lit"; +import { customElement, property } from "lit/decorators"; +import { HaIconButton } from "./ha-icon-button"; + +@customElement("ha-icon-button-toggle") +export class HaIconButtonToggle extends HaIconButton { + @property({ type: Boolean, reflect: true }) selected = false; + + static get styles(): CSSResultGroup { + return css` + :host { + position: relative; + } + mwc-icon-button { + position: relative; + transition: color 180ms ease-in-out; + } + mwc-icon-button::before { + opacity: 0; + transition: opacity 180ms ease-in-out; + background-color: var(--primary-text-color); + border-radius: 20px; + height: 40px; + width: 40px; + content: ""; + position: absolute; + top: -10px; + left: -10px; + bottom: -10px; + right: -10px; + margin: auto; + box-sizing: border-box; + } + :host([border-only]) mwc-icon-button::before { + background-color: transparent; + border: 2px solid var(--primary-text-color); + } + :host([selected]) mwc-icon-button { + color: var(--primary-background-color); + } + :host([selected]) mwc-icon-button::before { + opacity: 1; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-icon-button-toggle": HaIconButtonToggle; + } +} diff --git a/src/components/ha-markdown-element.ts b/src/components/ha-markdown-element.ts index 1a4691c917..7a15e35e0b 100644 --- a/src/components/ha-markdown-element.ts +++ b/src/components/ha-markdown-element.ts @@ -3,6 +3,8 @@ import { customElement, property } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; import { renderMarkdown } from "../resources/render-markdown"; +const _blockQuoteToAlert = { Note: "info", Warning: "warning" }; + @customElement("ha-markdown-element") class HaMarkdownElement extends ReactiveElement { @property() public content?; @@ -65,6 +67,34 @@ class HaMarkdownElement extends ReactiveElement { node.loading = "lazy"; } node.addEventListener("load", this._resize); + } else if (node instanceof HTMLQuoteElement) { + // Map GitHub blockquote elements to our ha-alert element + const firstElementChild = node.firstElementChild; + const quoteTitleElement = firstElementChild?.firstElementChild; + const quoteType = + quoteTitleElement?.textContent && + _blockQuoteToAlert[quoteTitleElement.textContent]; + + // GitHub is strict on how these are defined, we need to make sure we know what we have before starting to replace it + if (quoteTitleElement?.nodeName === "STRONG" && quoteType) { + const alertNote = document.createElement("ha-alert"); + alertNote.alertType = quoteType; + alertNote.title = + (firstElementChild!.childNodes[1].nodeName === "#text" && + firstElementChild!.childNodes[1].textContent?.trimStart()) || + ""; + + const childNodes = Array.from(firstElementChild!.childNodes); + for (const child of childNodes.slice( + childNodes.findIndex( + // There is always a line break between the title and the content, we want to skip that + (childNode) => childNode instanceof HTMLBRElement + ) + 1 + )) { + alertNote.appendChild(child); + } + node.firstElementChild!.replaceWith(alertNote); + } } } } diff --git a/src/components/ha-menu-button.ts b/src/components/ha-menu-button.ts index b51dd78800..2246dd136b 100644 --- a/src/components/ha-menu-button.ts +++ b/src/components/ha-menu-button.ts @@ -73,20 +73,25 @@ class HaMenuButton extends LitElement { return; } - const oldHass = changedProps.get("hass") as HomeAssistant | undefined; - const oldNarrow = - changedProps.get("narrow") || - (oldHass && oldHass.dockedSidebar === "always_hidden"); - const newNarrow = + const oldHass = changedProps.has("hass") + ? (changedProps.get("hass") as HomeAssistant | undefined) + : this.hass; + const oldNarrow = changedProps.has("narrow") + ? (changedProps.get("narrow") as boolean | undefined) + : this.narrow; + + const oldShowButton = + oldNarrow || oldHass?.dockedSidebar === "always_hidden"; + const showButton = this.narrow || this.hass.dockedSidebar === "always_hidden"; - if (oldNarrow === newNarrow) { + if (oldShowButton === showButton) { return; } - this.style.display = newNarrow || this._alwaysVisible ? "initial" : "none"; + this.style.display = showButton || this._alwaysVisible ? "initial" : "none"; - if (!newNarrow) { + if (!showButton) { if (this._unsubNotifications) { this._unsubNotifications(); this._unsubNotifications = undefined; @@ -98,6 +103,9 @@ class HaMenuButton extends LitElement { } private _subscribeNotifications() { + if (this._unsubNotifications) { + throw new Error("Already subscribed"); + } this._unsubNotifications = subscribeNotifications( this.hass.connection, (notifications) => { diff --git a/src/components/ha-picture-upload.ts b/src/components/ha-picture-upload.ts index 15d2fef7ae..0a21f82ec7 100644 --- a/src/components/ha-picture-upload.ts +++ b/src/components/ha-picture-upload.ts @@ -2,7 +2,7 @@ import { mdiImagePlus } from "@mdi/js"; import { html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; -import { createImage, generateImageThumbnailUrl } from "../data/image"; +import { createImage, generateImageThumbnailUrl } from "../data/image_upload"; import { showAlertDialog } from "../dialogs/generic/show-dialog-box"; import { CropOptions, diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts index f4b07b55aa..e8bd3ac4b4 100644 --- a/src/components/ha-selector/ha-selector-select.ts +++ b/src/components/ha-selector/ha-selector-select.ts @@ -4,6 +4,7 @@ import { css, html, LitElement } from "lit"; import { customElement, property, query } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; import { stopPropagation } from "../../common/dom/stop_propagation"; +import { ensureArray } from "../../common/array/ensure-array"; import type { SelectOption, SelectSelector } from "../../data/selector"; import type { HomeAssistant } from "../../types"; import "../ha-checkbox"; @@ -40,7 +41,7 @@ export class HaSelectSelector extends LitElement { protected render() { const options = - this.selector.select?.options.map((option) => + this.selector.select?.options?.map((option) => typeof option === "object" ? (option as SelectOption) : ({ value: option, label: option } as SelectOption) @@ -77,7 +78,8 @@ export class HaSelectSelector extends LitElement { ${this._renderHelper()} `; } - + const value = + !this.value || this.value === "" ? [] : ensureArray(this.value); return html`
${this.label} @@ -85,7 +87,7 @@ export class HaSelectSelector extends LitElement { (item: SelectOption) => html` !option.disabled && !value?.includes(option.value) @@ -231,19 +233,19 @@ export class HaSelectSelector extends LitElement { const value: string = ev.target.value; const checked = ev.target.checked; + const oldValue = + !this.value || this.value === "" ? [] : ensureArray(this.value); + if (checked) { - if (!this.value) { - newValue = [value]; - } else if (this.value.includes(value)) { + if (oldValue.includes(value)) { return; - } else { - newValue = [...this.value, value]; } + newValue = [...oldValue, value]; } else { - if (!this.value?.includes(value)) { + if (!oldValue?.includes(value)) { return; } - newValue = (this.value as string[]).filter((v) => v !== value); + newValue = oldValue.filter((v) => v !== value); } fireEvent(this, "value-changed", { @@ -252,7 +254,7 @@ export class HaSelectSelector extends LitElement { } private async _removeItem(ev) { - const value: string[] = [...(this.value! as string[])]; + const value: string[] = [...ensureArray(this.value!)]; value.splice(ev.target.idx, 1); fireEvent(this, "value-changed", { @@ -277,7 +279,10 @@ export class HaSelectSelector extends LitElement { return; } - if (newValue !== undefined && this.value?.includes(newValue)) { + const currentValue = + !this.value || this.value === "" ? [] : ensureArray(this.value); + + if (newValue !== undefined && currentValue.includes(newValue)) { return; } @@ -286,9 +291,6 @@ export class HaSelectSelector extends LitElement { this.comboBox.setInputValue(""); }, 0); - const currentValue = - !this.value || this.value === "" ? [] : (this.value as string[]); - fireEvent(this, "value-changed", { value: [...currentValue, newValue], }); diff --git a/src/components/ha-selector/ha-selector-theme.ts b/src/components/ha-selector/ha-selector-theme.ts index eccf3c5b23..b00d523413 100644 --- a/src/components/ha-selector/ha-selector-theme.ts +++ b/src/components/ha-selector/ha-selector-theme.ts @@ -24,6 +24,7 @@ export class HaThemeSelector extends LitElement { .hass=${this.hass} .value=${this.value} .label=${this.label} + .includeDefault=${this.selector.theme?.include_default} .disabled=${this.disabled} .required=${this.required} > diff --git a/src/components/ha-service-control.ts b/src/components/ha-service-control.ts index bc0cc718c1..21e5289e5f 100644 --- a/src/components/ha-service-control.ts +++ b/src/components/ha-service-control.ts @@ -249,12 +249,16 @@ export class HaServiceControl extends LitElement { ) { const targetSelector = target ? { target } : { target: {} }; const targetEntities = - ensureArray(value?.target?.entity_id || value?.data?.entity_id) || []; + ensureArray( + value?.target?.entity_id || value?.data?.entity_id + )?.slice() || []; const targetDevices = - ensureArray(value?.target?.device_id || value?.data?.device_id) || []; + ensureArray( + value?.target?.device_id || value?.data?.device_id + )?.slice() || []; const targetAreas = ensureArray( value?.target?.area_id || value?.data?.area_id - ); + )?.slice(); if (targetAreas) { targetAreas.forEach((areaId) => { const expanded = expandAreaTarget( diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index f443ca9eee..0f95bbae23 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -23,19 +23,19 @@ import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { - css, CSSResult, CSSResultGroup, - html, LitElement, - nothing, PropertyValues, + css, + html, + nothing, } from "lit"; import { customElement, eventOptions, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { guard } from "lit/directives/guard"; import memoizeOne from "memoize-one"; -import { LocalStorage } from "../common/decorators/local-storage"; +import { storage } from "../common/decorators/storage"; import { fireEvent } from "../common/dom/fire_event"; import { toggleAttribute } from "../common/dom/toggle_attribute"; import { stringCompare } from "../common/string/compare"; @@ -47,10 +47,10 @@ import { subscribeNotifications, } from "../data/persistent_notification"; import { subscribeRepairsIssueRegistry } from "../data/repairs"; -import { updateCanInstall, UpdateEntity } from "../data/update"; +import { UpdateEntity, updateCanInstall } from "../data/update"; import { SubscribeMixin } from "../mixins/subscribe-mixin"; import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive"; -import { loadSortable, SortableInstance } from "../resources/sortable.ondemand"; +import { SortableInstance, loadSortable } from "../resources/sortable.ondemand"; import { haStyleScrollbar } from "../resources/styles"; import type { HomeAssistant, PanelInfo, Route } from "../types"; import "./ha-icon"; @@ -214,15 +214,17 @@ class HaSidebar extends SubscribeMixin(LitElement) { private sortableStyleLoaded = false; - // @ts-ignore - @LocalStorage("sidebarPanelOrder", true, { - attribute: false, + @storage({ + key: "sidebarPanelOrder", + state: true, + subscribe: true, }) private _panelOrder: string[] = []; - // @ts-ignore - @LocalStorage("sidebarHiddenPanels", true, { - attribute: false, + @storage({ + key: "sidebarHiddenPanels", + state: true, + subscribe: true, }) private _hiddenPanels: string[] = []; diff --git a/src/components/ha-temp-color-picker.ts b/src/components/ha-temp-color-picker.ts index c3e19707c6..b2f2b26dfc 100644 --- a/src/components/ha-temp-color-picker.ts +++ b/src/components/ha-temp-color-picker.ts @@ -7,6 +7,8 @@ import { rgb2hex } from "../common/color/convert-color"; import { temperature2rgb } from "../common/color/convert-light-color"; import { fireEvent } from "../common/dom/fire_event"; +const SAFE_ZONE_FACTOR = 0.9; + declare global { interface HASSDomEvents { "cursor-moved": { value?: any }; @@ -50,9 +52,12 @@ function drawColorWheel( for (let y = -radius; y < radius; y += 1) { const x = radius * Math.sqrt(1 - (y / radius) ** 2); - const fraction = (y / radius + 1) / 2; + const fraction = (y / (radius * SAFE_ZONE_FACTOR) + 1) / 2; - const temperature = min + fraction * (max - min); + const temperature = Math.max( + Math.min(min + fraction * (max - min), max), + min + ); const color = rgb2hex(temperature2rgb(temperature)); @@ -134,7 +139,7 @@ class HaTempColorPicker extends LitElement { this.setAttribute("aria-valuemax", this.max.toString()); } if (changedProps.has("value")) { - if (this.value != null && this._localValue !== this.value) { + if (this._localValue !== this.value) { this._resetPosition(); } } @@ -192,7 +197,11 @@ class HaTempColorPicker extends LitElement { } private _resetPosition() { - if (this.value === undefined) return; + if (this.value === undefined) { + this._cursorPosition = undefined; + this._localValue = undefined; + return; + } const [, y] = this._getCoordsFromValue(this.value); const currentX = this._cursorPosition?.[0] ?? 0; const x = @@ -202,14 +211,23 @@ class HaTempColorPicker extends LitElement { } private _getCoordsFromValue = (temperature: number): [number, number] => { + if (this.value === this.min) { + return [0, -1]; + } + if (this.value === this.max) { + return [0, 1]; + } const fraction = (temperature - this.min) / (this.max - this.min); - const y = 2 * fraction - 1; + const y = (2 * fraction - 1) * SAFE_ZONE_FACTOR; return [0, y]; }; private _getValueFromCoord = (_x: number, y: number): number => { - const fraction = (y + 1) / 2; - const temperature = this.min + fraction * (this.max - this.min); + const fraction = (y / SAFE_ZONE_FACTOR + 1) / 2; + const temperature = Math.max( + Math.min(this.min + fraction * (this.max - this.min), this.max), + this.min + ); return Math.round(temperature); }; @@ -367,21 +385,23 @@ class HaTempColorPicker extends LitElement { :host { display: block; outline: none; - border-radius: 9999px; - } - :host(:focus-visible) { - box-shadow: 0 0 0 2px rgb(255, 160, 0); } .container { position: relative; width: 100%; height: 100%; - cursor: pointer; display: flex; } canvas { width: 100%; height: 100%; + object-fit: contain; + border-radius: 50%; + transition: box-shadow 180ms ease-in-out; + cursor: pointer; + } + :host(:focus-visible) canvas { + box-shadow: 0 0 0 2px rgb(255, 160, 0); } svg { position: absolute; diff --git a/src/components/ha-textfield.ts b/src/components/ha-textfield.ts index e411469252..289b37924c 100644 --- a/src/components/ha-textfield.ts +++ b/src/components/ha-textfield.ts @@ -99,6 +99,10 @@ export class HaTextField extends TextFieldBase { direction: var(--direction); } + .mdc-text-field__icon--trailing { + padding: var(--textfield-icon-trailing-padding, 12px); + } + .mdc-floating-label:not(.mdc-floating-label--float-above) { text-overflow: ellipsis; width: inherit; @@ -156,6 +160,9 @@ export class HaTextField extends TextFieldBase { .mdc-text-field__input[type="number"] { direction: var(--direction); } + .mdc-text-field__affix--prefix { + padding-right: var(--text-field-prefix-padding-right, 2px); + } `, // safari workaround - must be explicit document.dir === "rtl" diff --git a/src/components/ha-theme-picker.ts b/src/components/ha-theme-picker.ts index 47aadd7e62..8835a5b186 100644 --- a/src/components/ha-theme-picker.ts +++ b/src/components/ha-theme-picker.ts @@ -1,17 +1,28 @@ import "@material/mwc-list/mwc-list-item"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { + css, + CSSResultGroup, + html, + nothing, + LitElement, + TemplateResult, +} from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; import { stopPropagation } from "../common/dom/stop_propagation"; import { HomeAssistant } from "../types"; import "./ha-select"; +const DEFAULT_THEME = "default"; + @customElement("ha-theme-picker") export class HaThemePicker extends LitElement { @property() public value?: string; @property() public label?: string; + @property() includeDefault?: boolean = false; + @property({ attribute: false }) public hass?: HomeAssistant; @property({ type: Boolean, reflect: true }) public disabled = false; @@ -36,6 +47,13 @@ export class HaThemePicker extends LitElement { "ui.components.theme-picker.no_theme" )} + ${this.includeDefault + ? html`${this.hass!.localize( + "ui.components.theme-picker.default" + )}` + : nothing} ${Object.keys(this.hass!.themes.themes) .sort() .map( diff --git a/src/components/media-player/ha-browse-media-tts.ts b/src/components/media-player/ha-browse-media-tts.ts index d16f2bbe15..39e36b60b7 100644 --- a/src/components/media-player/ha-browse-media-tts.ts +++ b/src/components/media-player/ha-browse-media-tts.ts @@ -1,7 +1,7 @@ import "@material/mwc-list/mwc-list-item"; import { css, html, LitElement, nothing, PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { LocalStorage } from "../../common/decorators/local-storage"; +import { storage } from "../../common/decorators/storage"; import { fireEvent } from "../../common/dom/fire_event"; import { MediaPlayerBrowseAction, @@ -43,7 +43,12 @@ class BrowseMediaTTS extends LitElement { @state() private _provider?: TTSEngine; - @LocalStorage("TtsMessage", true, false) private _message!: string; + @storage({ + key: "TtsMessage", + state: true, + subscribe: false, + }) + private _message!: string; protected render() { return html` diff --git a/src/components/trace/ha-trace-path-details.ts b/src/components/trace/ha-trace-path-details.ts index 8264b1d0b5..fe963622f7 100644 --- a/src/components/trace/ha-trace-path-details.ts +++ b/src/components/trace/ha-trace-path-details.ts @@ -127,7 +127,8 @@ export class HaTracePathDetails extends LitElement { Executed: ${formatDateTimeWithSeconds( new Date(timestamp), - this.hass.locale + this.hass.locale, + this.hass.config )}
${result ? html`Result: diff --git a/src/components/trace/hat-trace-timeline.ts b/src/components/trace/hat-trace-timeline.ts index 7bd01cd431..2396c65879 100644 --- a/src/components/trace/hat-trace-timeline.ts +++ b/src/components/trace/hat-trace-timeline.ts @@ -335,7 +335,8 @@ class ActionRenderer { } at ${formatDateTimeWithSeconds( new Date(triggerStep.timestamp), - this.hass.locale + this.hass.locale, + this.hass.config )}`, mdiCircle ); @@ -632,7 +633,8 @@ export class HaAutomationTracer extends LitElement { const renderFinishedAt = () => formatDateTimeWithSeconds( new Date(this.trace!.timestamp.finish!), - this.hass.locale + this.hass.locale, + this.hass.config ); const renderRuntime = () => `(runtime: ${( diff --git a/src/data/automation.ts b/src/data/automation.ts index aeeaa356d2..eff766e10d 100644 --- a/src/data/automation.ts +++ b/src/data/automation.ts @@ -107,6 +107,11 @@ export interface NumericStateTrigger extends BaseTrigger { for?: string | number | ForDict; } +export interface ConversationTrigger extends BaseTrigger { + platform: "conversation"; + command: string | string[]; +} + export interface SunTrigger extends BaseTrigger { platform: "sun"; offset: number; @@ -127,6 +132,12 @@ export interface WebhookTrigger extends BaseTrigger { local_only?: boolean; } +export interface PersistentNotificationTrigger extends BaseTrigger { + platform: "persistent_notification"; + notification_id?: string; + update_type?: string[]; +} + export interface ZoneTrigger extends BaseTrigger { platform: "zone"; entity_id: string; @@ -172,8 +183,10 @@ export type Trigger = | HassTrigger | NumericStateTrigger | SunTrigger + | ConversationTrigger | TimePatternTrigger | WebhookTrigger + | PersistentNotificationTrigger | ZoneTrigger | TagTrigger | TimeTrigger @@ -380,7 +393,7 @@ export const testCondition = ( variables, }); -export type Clipboard = { +export type AutomationClipboard = { trigger?: Trigger; condition?: Condition; action?: Action; diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index d989b8617f..7b4e249386 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -1,26 +1,31 @@ +import { HassConfig } from "home-assistant-js-websocket"; +import { ensureArray } from "../common/array/ensure-array"; import { formatDuration } from "../common/datetime/format_duration"; import { formatTime, formatTimeWithSeconds, } from "../common/datetime/format_time"; -import { FrontendLocaleData } from "./translation"; import secondsToDuration from "../common/datetime/seconds_to_duration"; -import { ensureArray } from "../common/array/ensure-array"; +import { + computeAttributeNameDisplay, + computeAttributeValueDisplay, +} from "../common/entity/compute_attribute_display"; +import { computeStateDisplay } from "../common/entity/compute_state_display"; import { computeStateName } from "../common/entity/compute_state_name"; import type { HomeAssistant } from "../types"; -import { Condition, Trigger, ForDict } from "./automation"; +import { Condition, ForDict, Trigger } from "./automation"; import { DeviceCondition, DeviceTrigger, localizeDeviceAutomationCondition, localizeDeviceAutomationTrigger, } from "./device_automation"; -import { - computeAttributeNameDisplay, - computeAttributeValueDisplay, -} from "../common/entity/compute_attribute_display"; -import { computeStateDisplay } from "../common/entity/compute_state_display"; import { EntityRegistryEntry } from "./entity_registry"; +import "../resources/intl-polyfill"; +import { FrontendLocaleData } from "./translation"; + +const triggerTranslationBaseKey = + "ui.panel.config.automation.editor.triggers.type"; const describeDuration = (forTime: number | string | ForDict) => { let duration: string | null; @@ -34,7 +39,11 @@ const describeDuration = (forTime: number | string | ForDict) => { return duration; }; -const localizeTimeString = (time: string, locale: FrontendLocaleData) => { +const localizeTimeString = ( + time: string, + locale: FrontendLocaleData, + config: HassConfig +) => { const chunks = time.split(":"); if (chunks.length < 2 || chunks.length > 3) { return time; @@ -42,9 +51,9 @@ const localizeTimeString = (time: string, locale: FrontendLocaleData) => { try { const dt = new Date("1970-01-01T" + time); if (chunks.length === 2 || Number(chunks[2]) === 0) { - return formatTime(dt, locale); + return formatTime(dt, locale, config); } - return formatTimeWithSeconds(dt, locale); + return formatTimeWithSeconds(dt, locale, config); } catch { return time; } @@ -72,36 +81,62 @@ export const describeTrigger = ( hass: HomeAssistant, entityRegistry: EntityRegistryEntry[], ignoreAlias = false +) => { + try { + return tryDescribeTrigger(trigger, hass, entityRegistry, ignoreAlias); + } catch (error: any) { + // eslint-disable-next-line no-console + console.error(error); + + let msg = "Error in describing trigger"; + if (error.message) { + msg += ": " + error.message; + } + return msg; + } +}; + +const tryDescribeTrigger = ( + trigger: Trigger, + hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], + ignoreAlias = false ) => { if (trigger.alias && !ignoreAlias) { return trigger.alias; } + const disjunctionFormatter = new Intl.ListFormat("en", { + style: "long", + type: "disjunction", + }); + // Event Trigger if (trigger.platform === "event" && trigger.event_type) { - let eventTypes = ""; + const eventTypes: string[] = []; if (Array.isArray(trigger.event_type)) { - for (const [index, state] of trigger.event_type.entries()) { - eventTypes += `${index > 0 ? "," : ""} ${ - trigger.event_type.length > 1 && - index === trigger.event_type.length - 1 - ? "or" - : "" - } ${state}`; + for (const state of trigger.event_type.values()) { + eventTypes.push(state); } } else { - eventTypes = trigger.event_type.toString(); + eventTypes.push(trigger.event_type); } - return `When ${eventTypes} event is fired`; + const eventTypesString = disjunctionFormatter.format(eventTypes); + return hass.localize( + `${triggerTranslationBaseKey}.event.description.full`, + { eventTypes: eventTypesString } + ); } // Home Assistant Trigger if (trigger.platform === "homeassistant" && trigger.event) { - return `When Home Assistant is ${ - trigger.event === "start" ? "started" : "shutdown" - }`; + return hass.localize( + trigger.event === "start" + ? `${triggerTranslationBaseKey}.homeassistant.description.started` + : `${triggerTranslationBaseKey}.homeassistant.description.shutdown` + ); } // Numeric State Trigger @@ -146,7 +181,7 @@ export const describeTrigger = ( // State Trigger if (trigger.platform === "state") { let base = "When"; - let entities = ""; + const entities: string[] = []; const states = hass.states; if (trigger.attribute) { @@ -162,25 +197,22 @@ export const describeTrigger = ( } if (Array.isArray(trigger.entity_id)) { - for (const [index, entity] of trigger.entity_id.entries()) { + for (const entity of trigger.entity_id.values()) { if (states[entity]) { - entities += `${index > 0 ? "," : ""} ${ - trigger.entity_id.length > 1 && - index === trigger.entity_id.length - 1 - ? "or" - : "" - } ${computeStateName(states[entity]) || entity}`; + entities.push(computeStateName(states[entity]) || entity); } } } else if (trigger.entity_id) { - entities = states[trigger.entity_id] - ? computeStateName(states[trigger.entity_id]) - : trigger.entity_id; + entities.push( + states[trigger.entity_id] + ? computeStateName(states[trigger.entity_id]) + : trigger.entity_id + ); } - if (!entities) { + if (entities.length === 0) { // no entity_id or empty array - entities = "something"; + entities.push("something"); } base += ` ${entities} changes`; @@ -197,41 +229,41 @@ export const describeTrigger = ( base += " from any state"; } } else if (Array.isArray(trigger.from)) { - let from = ""; - for (const [index, state] of trigger.from.entries()) { - from += `${index > 0 ? "," : ""} ${ - trigger.from.length > 1 && index === trigger.from.length - 1 - ? "or" - : "" - } '${ + const from: string[] = []; + for (const state of trigger.from.values()) { + from.push( trigger.attribute ? computeAttributeValueDisplay( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, trigger.attribute, state - ) + ).toString() : computeStateDisplay( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, state ) - }'`; + ); } - if (from) { - base += ` from ${from}`; + if (from.length !== 0) { + const fromString = disjunctionFormatter.format(from); + base += ` from ${fromString}`; } } else { - base += ` from '${ + base += ` from ${ trigger.attribute ? computeAttributeValueDisplay( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, trigger.attribute, trigger.from @@ -240,10 +272,11 @@ export const describeTrigger = ( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, trigger.from.toString() ).toString() - }'`; + }`; } } @@ -253,16 +286,15 @@ export const describeTrigger = ( base += " to any state"; } } else if (Array.isArray(trigger.to)) { - let to = ""; - for (const [index, state] of trigger.to.entries()) { - to += `${index > 0 ? "," : ""} ${ - trigger.to.length > 1 && index === trigger.to.length - 1 ? "or" : "" - } '${ + const to: string[] = []; + for (const state of trigger.to.values()) { + to.push( trigger.attribute ? computeAttributeValueDisplay( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, trigger.attribute, state @@ -271,21 +303,24 @@ export const describeTrigger = ( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, state ).toString() - }'`; + ); } - if (to) { - base += ` to ${to}`; + if (to.length !== 0) { + const toString = disjunctionFormatter.format(to); + base += ` to ${toString}`; } } else { - base += ` to '${ + base += ` to ${ trigger.attribute ? computeAttributeValueDisplay( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, trigger.attribute, trigger.to @@ -294,10 +329,11 @@ export const describeTrigger = ( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, trigger.to.toString() - ).toString() - }'`; + ) + }`; } } @@ -321,29 +357,28 @@ export const describeTrigger = ( // Sun Trigger if (trigger.platform === "sun" && trigger.event) { - let base = `When the sun ${trigger.event === "sunset" ? "sets" : "rises"}`; - + let duration = ""; if (trigger.offset) { - let duration = ""; - - if (trigger.offset) { - if (typeof trigger.offset === "number") { - duration = ` offset by ${secondsToDuration(trigger.offset)!}`; - } else if (typeof trigger.offset === "string") { - duration = ` offset by ${trigger.offset}`; - } else { - duration = ` offset by ${JSON.stringify(trigger.offset)}`; - } + if (typeof trigger.offset === "number") { + duration = secondsToDuration(trigger.offset)!; + } else if (typeof trigger.offset === "string") { + duration = trigger.offset; + } else { + duration = JSON.stringify(trigger.offset); } - base += duration; } - return base; + return hass.localize( + trigger.event === "sunset" + ? `${triggerTranslationBaseKey}.sun.description.sets` + : `${triggerTranslationBaseKey}.sun.description.rises`, + { hasDuration: duration !== "", duration: duration } + ); } // Tag Trigger if (trigger.platform === "tag") { - return "When a tag is scanned"; + return hass.localize(`${triggerTranslationBaseKey}.tag.description.full`); } // Time Trigger @@ -353,13 +388,12 @@ export const describeTrigger = ( ? at : at.includes(".") ? `entity ${hass.states[at] ? computeStateName(hass.states[at]) : at}` - : localizeTimeString(at, hass.locale) + : localizeTimeString(at, hass.locale, hass.config) ); - const last = result.splice(-1, 1)[0]; - return `When the time is equal to ${ - result.length ? `${result.join(", ")} or ` : "" - }${last}`; + return hass.localize(`${triggerTranslationBaseKey}.time.description.full`, { + time: disjunctionFormatter.format(result), + }); } // Time Pattern Trigger @@ -476,119 +510,130 @@ export const describeTrigger = ( // Zone Trigger if (trigger.platform === "zone" && trigger.entity_id && trigger.zone) { - let entities = ""; - let zones = ""; - let zonesPlural = false; + const entities: string[] = []; + const zones: string[] = []; const states = hass.states; if (Array.isArray(trigger.entity_id)) { - for (const [index, entity] of trigger.entity_id.entries()) { + for (const entity of trigger.entity_id.values()) { if (states[entity]) { - entities += `${index > 0 ? "," : ""} ${ - trigger.entity_id.length > 1 && - index === trigger.entity_id.length - 1 - ? "or" - : "" - } ${computeStateName(states[entity]) || entity}`; + entities.push(computeStateName(states[entity]) || entity); } } } else { - entities = states[trigger.entity_id] - ? computeStateName(states[trigger.entity_id]) - : trigger.entity_id; + entities.push( + states[trigger.entity_id] + ? computeStateName(states[trigger.entity_id]) + : trigger.entity_id + ); } if (Array.isArray(trigger.zone)) { - if (trigger.zone.length > 1) { - zonesPlural = true; - } - - for (const [index, zone] of trigger.zone.entries()) { + for (const zone of trigger.zone.values()) { if (states[zone]) { - zones += `${index > 0 ? "," : ""} ${ - trigger.zone.length > 1 && index === trigger.zone.length - 1 - ? "or" - : "" - } ${computeStateName(states[zone]) || zone}`; + zones.push(computeStateName(states[zone]) || zone); } } } else { - zones = states[trigger.zone] - ? computeStateName(states[trigger.zone]) - : trigger.zone; + zones.push( + states[trigger.zone] + ? computeStateName(states[trigger.zone]) + : trigger.zone + ); } - return `When ${entities} ${trigger.event}s ${zones} ${ - zonesPlural ? "zones" : "zone" + const entitiesString = disjunctionFormatter.format(entities); + const zonesString = disjunctionFormatter.format(zones); + return `When ${entitiesString} ${trigger.event}s ${zonesString} ${ + zones.length > 1 ? "zones" : "zone" }`; } // Geo Location Trigger if (trigger.platform === "geo_location" && trigger.source && trigger.zone) { - let sources = ""; - let zones = ""; - let zonesPlural = false; + const sources: string[] = []; + const zones: string[] = []; const states = hass.states; if (Array.isArray(trigger.source)) { - for (const [index, source] of trigger.source.entries()) { - sources += `${index > 0 ? "," : ""} ${ - trigger.source.length > 1 && index === trigger.source.length - 1 - ? "or" - : "" - } ${source}`; + for (const source of trigger.source.values()) { + sources.push(source); } } else { - sources = trigger.source; + sources.push(trigger.source); } if (Array.isArray(trigger.zone)) { - if (trigger.zone.length > 1) { - zonesPlural = true; - } - - for (const [index, zone] of trigger.zone.entries()) { + for (const zone of trigger.zone.values()) { if (states[zone]) { - zones += `${index > 0 ? "," : ""} ${ - trigger.zone.length > 1 && index === trigger.zone.length - 1 - ? "or" - : "" - } ${computeStateName(states[zone]) || zone}`; + zones.push(computeStateName(states[zone]) || zone); } } } else { - zones = states[trigger.zone] - ? computeStateName(states[trigger.zone]) - : trigger.zone; + zones.push( + states[trigger.zone] + ? computeStateName(states[trigger.zone]) + : trigger.zone + ); } - return `When ${sources} ${trigger.event}s ${zones} ${ - zonesPlural ? "zones" : "zone" + const sourcesString = disjunctionFormatter.format(sources); + const zonesString = disjunctionFormatter.format(zones); + return `When ${sourcesString} ${trigger.event}s ${zonesString} ${ + zones.length > 1 ? "zones" : "zone" }`; } + // MQTT Trigger if (trigger.platform === "mqtt") { - return "When an MQTT message has been received"; + return hass.localize(`${triggerTranslationBaseKey}.mqtt.description.full`); } // Template Trigger if (trigger.platform === "template") { - let base = "When a template triggers"; + let duration = ""; if (trigger.for) { - const duration = describeDuration(trigger.for); - if (duration) { - base += ` for ${duration}`; - } + duration = describeDuration(trigger.for) ?? ""; } - return base; + + return hass.localize( + `${triggerTranslationBaseKey}.template.description.full`, + { hasDuration: duration !== "", duration: duration } + ); } // Webhook Trigger if (trigger.platform === "webhook") { - return "When a Webhook payload has been received"; + return hass.localize( + `${triggerTranslationBaseKey}.webhook.description.full` + ); } + // Conversation Trigger + if (trigger.platform === "conversation") { + if (!trigger.command) { + return hass.localize( + `${triggerTranslationBaseKey}.conversation.description.empty` + ); + } + + return hass.localize( + `${triggerTranslationBaseKey}.conversation.description.full`, + { + sentence: disjunctionFormatter.format( + ensureArray(trigger.command).map((cmd) => `'${cmd}'`) + ), + } + ); + } + + // Persistent Notification Trigger + if (trigger.platform === "persistent_notification") { + return "When a persistent notification is updated"; + } + + // Device Trigger if (trigger.platform === "device") { if (!trigger.device_id) { return "Device trigger"; @@ -618,11 +663,40 @@ export const describeCondition = ( hass: HomeAssistant, entityRegistry: EntityRegistryEntry[], ignoreAlias = false +) => { + try { + return tryDescribeCondition(condition, hass, entityRegistry, ignoreAlias); + } catch (error: any) { + // eslint-disable-next-line no-console + console.error(error); + + let msg = "Error in describing condition"; + if (error.message) { + msg += ": " + error.message; + } + return msg; + } +}; + +const tryDescribeCondition = ( + condition: Condition, + hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], + ignoreAlias = false ) => { if (condition.alias && !ignoreAlias) { return condition.alias; } + const conjunctionFormatter = new Intl.ListFormat("en", { + style: "long", + type: "conjunction", + }); + const disjunctionFormatter = new Intl.ListFormat("en", { + style: "long", + type: "disjunction", + }); + if (!condition.condition) { const shorthands: Array<"and" | "or" | "not"> = ["and", "or", "not"]; for (const key of shorthands) { @@ -692,21 +766,20 @@ export const describeCondition = ( } if (Array.isArray(condition.entity_id)) { - let entities = ""; - for (const [index, entity] of condition.entity_id.entries()) { + const entities: string[] = []; + for (const entity of condition.entity_id.values()) { if (hass.states[entity]) { - entities += `${index > 0 ? "," : ""} ${ - condition.entity_id.length > 1 && - index === condition.entity_id.length - 1 - ? condition.match === "any" - ? "or" - : "and" - : "" - } ${computeStateName(hass.states[entity]) || entity}`; + entities.push(computeStateName(hass.states[entity]) || entity); } } - if (entities) { - base += ` ${entities} ${condition.entity_id.length > 1 ? "are" : "is"}`; + if (entities.length !== 0) { + const entitiesString = + condition.match === "any" + ? disjunctionFormatter.format(entities) + : conjunctionFormatter.format(entities); + base += ` ${entitiesString} ${ + condition.entity_id.length > 1 ? "are" : "is" + }`; } else { // no entity_id or empty array base += " an entity"; @@ -719,7 +792,7 @@ export const describeCondition = ( } is`; } - let states = ""; + const states: string[] = []; const stateObj = hass.states[ Array.isArray(condition.entity_id) @@ -727,37 +800,36 @@ export const describeCondition = ( : condition.entity_id ]; if (Array.isArray(condition.state)) { - for (const [index, state] of condition.state.entries()) { - states += `${index > 0 ? "," : ""} ${ - condition.state.length > 1 && index === condition.state.length - 1 - ? "or" - : "" - } '${ + for (const state of condition.state.values()) { + states.push( condition.attribute ? computeAttributeValueDisplay( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, condition.attribute, state - ) + ).toString() : computeStateDisplay( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, state ) - }'`; + ); } } else if (condition.state !== "") { - states = `'${ + states.push( condition.attribute ? computeAttributeValueDisplay( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, condition.attribute, condition.state @@ -766,17 +838,19 @@ export const describeCondition = ( hass.localize, stateObj, hass.locale, + hass.config, hass.entities, condition.state.toString() - ).toString() - }'`; + ) + ); } - if (!states) { - states = "a state"; + if (states.length === 0) { + states.push("a state"); } - base += ` ${states}`; + const statesString = disjunctionFormatter.format(states); + base += ` ${statesString}`; if (condition.for) { const duration = describeDuration(condition.for); @@ -830,7 +904,7 @@ export const describeCondition = ( ? computeStateName(hass.states[condition.before]) : condition.before }` - : localizeTimeString(condition.before, hass.locale); + : localizeTimeString(condition.before, hass.locale, hass.config); const after = typeof condition.after !== "string" @@ -841,7 +915,7 @@ export const describeCondition = ( ? computeStateName(hass.states[condition.after]) : condition.after }` - : localizeTimeString(condition.after, hass.locale); + : localizeTimeString(condition.after, hass.locale, hass.config); let result = "Confirm the "; if (after || before) { @@ -865,17 +939,7 @@ export const describeCondition = ( `ui.panel.config.automation.editor.conditions.type.time.weekdays.${d}` ) ); - const last = localizedDays.pop(); - - result += " day is " + localizedDays.join(", "); - - if (localizedDays.length) { - if (localizedDays.length > 1) { - result += ","; - } - result += " or "; - } - result += last; + result += " day is " + disjunctionFormatter.format(localizedDays); } return result; @@ -921,56 +985,44 @@ export const describeCondition = ( // Zone condition if (condition.condition === "zone" && condition.entity_id && condition.zone) { - let entities = ""; - let entitiesPlural = false; - let zones = ""; - let zonesPlural = false; + const entities: string[] = []; + const zones: string[] = []; const states = hass.states; if (Array.isArray(condition.entity_id)) { - if (condition.entity_id.length > 1) { - entitiesPlural = true; - } - for (const [index, entity] of condition.entity_id.entries()) { + for (const entity of condition.entity_id.values()) { if (states[entity]) { - entities += `${index > 0 ? "," : ""} ${ - condition.entity_id.length > 1 && - index === condition.entity_id.length - 1 - ? "or" - : "" - } ${computeStateName(states[entity]) || entity}`; + entities.push(computeStateName(states[entity]) || entity); } } } else { - entities = states[condition.entity_id] - ? computeStateName(states[condition.entity_id]) - : condition.entity_id; + entities.push( + states[condition.entity_id] + ? computeStateName(states[condition.entity_id]) + : condition.entity_id + ); } if (Array.isArray(condition.zone)) { - if (condition.zone.length > 1) { - zonesPlural = true; - } - - for (const [index, zone] of condition.zone.entries()) { + for (const zone of condition.zone.values()) { if (states[zone]) { - zones += `${index > 0 ? "," : ""} ${ - condition.zone.length > 1 && index === condition.zone.length - 1 - ? "or" - : "" - } ${computeStateName(states[zone]) || zone}`; + zones.push(computeStateName(states[zone]) || zone); } } } else { - zones = states[condition.zone] - ? computeStateName(states[condition.zone]) - : condition.zone; + zones.push( + states[condition.zone] + ? computeStateName(states[condition.zone]) + : condition.zone + ); } - return `Confirm ${entities} ${entitiesPlural ? "are" : "is"} in ${zones} ${ - zonesPlural ? "zones" : "zone" - }`; + const entitiesString = disjunctionFormatter.format(entities); + const zonesString = disjunctionFormatter.format(zones); + return `Confirm ${entitiesString} ${ + entities.length > 1 ? "are" : "is" + } in ${zonesString} ${zones.length > 1 ? "zones" : "zone"}`; } if (condition.condition === "device") { diff --git a/src/data/climate.ts b/src/data/climate.ts index b72f845cbf..705f83fe15 100644 --- a/src/data/climate.ts +++ b/src/data/climate.ts @@ -16,6 +16,7 @@ export const CLIMATE_PRESET_NONE = "none"; export type HvacAction = | "off" + | "preheating" | "heating" | "cooling" | "drying" @@ -77,6 +78,7 @@ export const HVAC_ACTION_TO_MODE: Record = { cooling: "cool", drying: "dry", fan: "fan_only", + preheating: "heat", heating: "heat", idle: "off", off: "off", diff --git a/src/data/context.ts b/src/data/context.ts index f62e666ebe..7fbff9fd06 100644 --- a/src/data/context.ts +++ b/src/data/context.ts @@ -1,4 +1,5 @@ import { createContext } from "@lit-labs/context"; +import { HassConfig } from "home-assistant-js-websocket"; import { HomeAssistant } from "../types"; import { EntityRegistryEntry } from "./entity_registry"; @@ -11,7 +12,7 @@ export const areasContext = createContext("areas"); export const localizeContext = createContext("localize"); export const localeContext = createContext("locale"); -export const configContext = createContext("config"); +export const configContext = createContext("config"); export const themesContext = createContext("themes"); export const selectedThemeContext = createContext("selectedTheme"); diff --git a/src/data/conversation.ts b/src/data/conversation.ts index a51006a562..c76f6a2ecf 100644 --- a/src/data/conversation.ts +++ b/src/data/conversation.ts @@ -1,3 +1,4 @@ +import { ensureArray } from "../common/array/ensure-array"; import { HomeAssistant } from "../types"; interface IntentTarget { @@ -52,16 +53,30 @@ export interface ConversationResult { | IntentResultError; } -export interface AgentInfo { - attribution?: { name: string; url: string }; -} - export interface Agent { id: string; name: string; supported_languages: "*" | string[]; } +export interface AssitDebugResult { + intent: { + name: string; + }; + entities: Record< + string, + { + name: string; + value: string; + text: string; + } + >; +} + +export interface AssistDebugResponse { + results: (AssitDebugResult | null)[]; +} + export const processConversationInput = ( hass: HomeAssistant, text: string, @@ -87,15 +102,6 @@ export const listAgents = ( country, }); -export const getAgentInfo = ( - hass: HomeAssistant, - agent_id?: string -): Promise => - hass.callWS({ - type: "conversation/agent/info", - agent_id, - }); - export const prepareConversation = ( hass: HomeAssistant, language?: string @@ -104,3 +110,16 @@ export const prepareConversation = ( type: "conversation/prepare", language, }); + +export const debugAgent = ( + hass: HomeAssistant, + sentences: string[] | string, + language: string, + device_id?: string +): Promise => + hass.callWS({ + type: "conversation/agent/homeassistant/debug", + sentences: ensureArray(sentences), + language, + device_id, + }); diff --git a/src/data/energy.ts b/src/data/energy.ts index 151089d929..1e1fbd3c5e 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -4,12 +4,12 @@ import { addMilliseconds, addMonths, differenceInDays, - endOfToday, - endOfYesterday, - startOfToday, - startOfYesterday, + endOfDay, + startOfDay, } from "date-fns/esm"; import { Collection, getCollection } from "home-assistant-js-websocket"; +import { calcDate } from "../common/datetime/calc_date"; +import { formatTime24h } from "../common/datetime/format_time"; import { groupBy } from "../common/util/group-by"; import { HomeAssistant } from "../types"; import { ConfigEntry, getConfigEntries } from "./config_entries"; @@ -626,18 +626,40 @@ export const getEnergyDataCollection = ( collection._active = 0; collection.prefs = options.prefs; const now = new Date(); + const hour = formatTime24h(now, hass.locale, hass.config).split(":")[0]; // Set start to start of today if we have data for today, otherwise yesterday - collection.start = now.getHours() > 0 ? startOfToday() : startOfYesterday(); - collection.end = now.getHours() > 0 ? endOfToday() : endOfYesterday(); + collection.start = calcDate( + hour === "0" ? addDays(now, -1) : now, + startOfDay, + hass.locale, + hass.config + ); + collection.end = calcDate( + hour === "0" ? addDays(now, -1) : now, + endOfDay, + hass.locale, + hass.config + ); const scheduleUpdatePeriod = () => { collection._updatePeriodTimeout = window.setTimeout( () => { - collection.start = startOfToday(); - collection.end = endOfToday(); + collection.start = calcDate( + new Date(), + startOfDay, + hass.locale, + hass.config + ); + collection.end = calcDate( + new Date(), + endOfDay, + hass.locale, + hass.config + ); scheduleUpdatePeriod(); }, - addHours(endOfToday(), 1).getTime() - Date.now() // Switch to next day an hour after the day changed + addHours(calcDate(now, endOfDay, hass.locale, hass.config), 1).getTime() - + Date.now() // Switch to next day an hour after the day changed ); }; scheduleUpdatePeriod(); @@ -649,8 +671,10 @@ export const getEnergyDataCollection = ( collection.start = newStart; collection.end = newEnd; if ( - collection.start.getTime() === startOfToday().getTime() && - collection.end?.getTime() === endOfToday().getTime() && + collection.start.getTime() === + calcDate(new Date(), startOfDay, hass.locale, hass.config).getTime() && + collection.end?.getTime() === + calcDate(new Date(), endOfDay, hass.locale, hass.config).getTime() && !collection._updatePeriodTimeout ) { scheduleUpdatePeriod(); diff --git a/src/data/hassio/addon.ts b/src/data/hassio/addon.ts index 9b8aa7c2c6..def1e765e5 100644 --- a/src/data/hassio/addon.ts +++ b/src/data/hassio/addon.ts @@ -23,7 +23,13 @@ export type AddonStartup = | "services" | "application" | "once"; -export type AddonState = "started" | "stopped" | null; +export type AddonState = + | "startup" + | "started" + | "stopped" + | "unknown" + | "error" + | null; export type AddonRepository = "core" | "local" | string; interface AddonTranslations { diff --git a/src/data/hassio/backup.ts b/src/data/hassio/backup.ts index c01b9440da..b29b45590c 100644 --- a/src/data/hassio/backup.ts +++ b/src/data/hassio/backup.ts @@ -23,6 +23,7 @@ export interface HassioBackup { size: number; type: "full" | "partial"; protected: boolean; + location: string | null; content: BackupContent; } diff --git a/src/data/history.ts b/src/data/history.ts index dcc37a45a3..8202f44167 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -1,4 +1,5 @@ import { + HassConfig, HassEntities, HassEntity, HassEntityAttributeBase, @@ -269,7 +270,8 @@ const equalState = (obj1: LineChartState, obj2: LineChartState) => const processTimelineEntity = ( localize: LocalizeFunc, - language: FrontendLocaleData, + locale: FrontendLocaleData, + config: HassConfig, entities: HomeAssistant["entities"], entityId: string, states: EntityHistoryState[], @@ -290,7 +292,8 @@ const processTimelineEntity = ( data.push({ state_localize: computeStateDisplayFromEntityAttributes( localize, - language, + locale, + config, entities[entityId], entityId, { @@ -441,6 +444,7 @@ export const computeHistory = ( processTimelineEntity( localize, hass.locale, + hass.config, hass.entities, entityId, stateInfo, diff --git a/src/data/humidifier.ts b/src/data/humidifier.ts index dbf0093c61..90d70f68d4 100644 --- a/src/data/humidifier.ts +++ b/src/data/humidifier.ts @@ -2,20 +2,19 @@ import { HassEntityAttributeBase, HassEntityBase, } from "home-assistant-js-websocket"; -import { FIXED_DOMAIN_STATES } from "../common/entity/get_states"; -import { UNAVAILABLE_STATES } from "./entity"; -type HumidifierState = - | (typeof FIXED_DOMAIN_STATES.humidifier)[number] - | (typeof UNAVAILABLE_STATES)[number]; +export type HumidifierState = "on" | "off"; + +export type HumidifierAction = "off" | "idle" | "humidifying" | "drying"; export type HumidifierEntity = HassEntityBase & { - state: HumidifierState; attributes: HassEntityAttributeBase & { humidity?: number; + current_humidity?: number; min_humidity?: number; max_humidity?: number; mode?: string; + action?: HumidifierAction; available_modes?: string[]; }; }; diff --git a/src/data/image.ts b/src/data/image.ts index 94198de6dc..f28ea74860 100644 --- a/src/data/image.ts +++ b/src/data/image.ts @@ -1,54 +1,15 @@ -import { HomeAssistant } from "../types"; +import { + HassEntityAttributeBase, + HassEntityBase, +} from "home-assistant-js-websocket"; -interface Image { - filesize: number; - name: string; - uploaded_at: string; // isoformat date - content_type: string; - id: string; +interface ImageEntityAttributes extends HassEntityAttributeBase { + access_token: string; } -export interface ImageMutableParams { - name: string; +export interface ImageEntity extends HassEntityBase { + attributes: ImageEntityAttributes; } -export const generateImageThumbnailUrl = (mediaId: string, size: number) => - `/api/image/serve/${mediaId}/${size}x${size}`; - -export const fetchImages = (hass: HomeAssistant) => - hass.callWS({ type: "image/list" }); - -export const createImage = async ( - hass: HomeAssistant, - file: File -): Promise => { - const fd = new FormData(); - fd.append("file", file); - const resp = await hass.fetchWithAuth("/api/image/upload", { - method: "POST", - body: fd, - }); - if (resp.status === 413) { - throw new Error(`Uploaded image is too large (${file.name})`); - } else if (resp.status !== 200) { - throw new Error("Unknown error"); - } - return resp.json(); -}; - -export const updateImage = ( - hass: HomeAssistant, - id: string, - updates: Partial -) => - hass.callWS({ - type: "image/update", - media_id: id, - ...updates, - }); - -export const deleteImage = (hass: HomeAssistant, id: string) => - hass.callWS({ - type: "image/delete", - media_id: id, - }); +export const computeImageUrl = (entity: ImageEntity): string => + `/api/image_proxy/${entity.entity_id}?token=${entity.attributes.access_token}&state=${entity.state}`; diff --git a/src/data/image_upload.ts b/src/data/image_upload.ts new file mode 100644 index 0000000000..94198de6dc --- /dev/null +++ b/src/data/image_upload.ts @@ -0,0 +1,54 @@ +import { HomeAssistant } from "../types"; + +interface Image { + filesize: number; + name: string; + uploaded_at: string; // isoformat date + content_type: string; + id: string; +} + +export interface ImageMutableParams { + name: string; +} + +export const generateImageThumbnailUrl = (mediaId: string, size: number) => + `/api/image/serve/${mediaId}/${size}x${size}`; + +export const fetchImages = (hass: HomeAssistant) => + hass.callWS({ type: "image/list" }); + +export const createImage = async ( + hass: HomeAssistant, + file: File +): Promise => { + const fd = new FormData(); + fd.append("file", file); + const resp = await hass.fetchWithAuth("/api/image/upload", { + method: "POST", + body: fd, + }); + if (resp.status === 413) { + throw new Error(`Uploaded image is too large (${file.name})`); + } else if (resp.status !== 200) { + throw new Error("Unknown error"); + } + return resp.json(); +}; + +export const updateImage = ( + hass: HomeAssistant, + id: string, + updates: Partial +) => + hass.callWS({ + type: "image/update", + media_id: id, + ...updates, + }); + +export const deleteImage = (hass: HomeAssistant, id: string) => + hass.callWS({ + type: "image/delete", + media_id: id, + }); diff --git a/src/data/light.ts b/src/data/light.ts index 42a2507731..ed42fdeb5f 100644 --- a/src/data/light.ts +++ b/src/data/light.ts @@ -159,3 +159,5 @@ export const computeDefaultFavoriteColors = ( return colors; }; + +export const formatTempColor = (value: number) => `${value} K`; diff --git a/src/data/lock.ts b/src/data/lock.ts new file mode 100644 index 0000000000..a6c9914559 --- /dev/null +++ b/src/data/lock.ts @@ -0,0 +1,20 @@ +import { + HassEntityAttributeBase, + HassEntityBase, +} from "home-assistant-js-websocket"; + +export const FORMAT_TEXT = "text"; +export const FORMAT_NUMBER = "number"; + +export const enum LockEntityFeature { + OPEN = 1, +} + +interface LockEntityAttributes extends HassEntityAttributeBase { + code_format?: string; + changed_by?: string | null; +} + +export interface LockEntity extends HassEntityBase { + attributes: LockEntityAttributes; +} diff --git a/src/data/logbook.ts b/src/data/logbook.ts index c191ed8535..f5caa718b7 100644 --- a/src/data/logbook.ts +++ b/src/data/logbook.ts @@ -7,6 +7,7 @@ import { import { computeDomain } from "../common/entity/compute_domain"; import { computeStateDisplay } from "../common/entity/compute_state_display"; import { computeStateDomain } from "../common/entity/compute_state_domain"; +import { autoCaseNoun } from "../common/translations/auto_case_noun"; import { LocalizeFunc } from "../common/translations/localize"; import { HaEntityPickerEntityFilterFunc } from "../components/entity/ha-entity-picker"; import { HomeAssistant } from "../types"; @@ -359,15 +360,21 @@ export const localizeStateMessage = ( case "vibration": if (isOn) { return localize(`${LOGBOOK_LOCALIZE_PATH}.detected_device_class`, { - device_class: localize( - `component.binary_sensor.device_class.${device_class}` + device_class: autoCaseNoun( + localize( + `component.binary_sensor.entity_component.${device_class}.name` + ), + hass.language ), }); } if (isOff) { return localize(`${LOGBOOK_LOCALIZE_PATH}.cleared_device_class`, { - device_class: localize( - `component.binary_sensor.device_class.${device_class}` + device_class: autoCaseNoun( + localize( + `component.binary_sensor.entity_component.${device_class}.name` + ), + hass.language ), }); } @@ -439,6 +446,7 @@ export const localizeStateMessage = ( localize, stateObj, hass.locale, + hass.config, hass.entities, state ) diff --git a/src/data/lovelace.ts b/src/data/lovelace.ts index 432cb6167b..091ced7c7b 100644 --- a/src/data/lovelace.ts +++ b/src/data/lovelace.ts @@ -152,6 +152,12 @@ export interface MoreInfoActionConfig extends BaseActionConfig { action: "more-info"; } +export interface AssistActionConfig extends BaseActionConfig { + action: "assist"; + pipeline_id?: string; + start_listening?: boolean; +} + export interface NoActionConfig extends BaseActionConfig { action: "none"; } @@ -180,6 +186,7 @@ export type ActionConfig = | NavigateActionConfig | UrlActionConfig | MoreInfoActionConfig + | AssistActionConfig | NoActionConfig | CustomActionConfig; diff --git a/src/data/recorder.ts b/src/data/recorder.ts index 23752d16f2..32b471f4a7 100644 --- a/src/data/recorder.ts +++ b/src/data/recorder.ts @@ -260,10 +260,10 @@ export const calculateStatisticsSumGrowth = ( export const statisticsHaveType = ( stats: StatisticValue[], type: StatisticType -) => stats.some((stat) => stat[type] !== null); +) => stats.some((stat) => stat[type] !== undefined && stat[type] !== null); const mean_stat_types: readonly StatisticType[] = ["mean", "min", "max"]; -const sum_stat_types: readonly StatisticType[] = ["sum"]; +const sum_stat_types: readonly StatisticType[] = ["sum", "state", "change"]; export const statisticsMetaHasType = ( metadata: StatisticsMetaData, diff --git a/src/data/script.ts b/src/data/script.ts index a1288158db..6fb4fbb596 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -33,6 +33,7 @@ export const isMaxMode = arrayLiteralIncludes(MODES_MAX); export const baseActionStruct = object({ alias: optional(string()), + continue_on_error: optional(boolean()), enabled: optional(boolean()), }); @@ -50,6 +51,7 @@ export const serviceActionStruct: Describe = assign( entity_id: optional(string()), target: optional(targetStruct), data: optional(object()), + response_variable: optional(string()), }) ); @@ -99,6 +101,7 @@ export interface BlueprintScriptConfig extends ManualScriptConfig { interface BaseAction { alias?: string; + continue_on_error?: boolean; enabled?: boolean; } @@ -114,6 +117,7 @@ export interface ServiceAction extends BaseAction { entity_id?: string; target?: HassServiceTarget; data?: Record; + response_variable?: string; } export interface DeviceAction extends BaseAction { @@ -219,6 +223,7 @@ export interface VariablesAction extends BaseAction { export interface StopAction extends BaseAction { stop: string; + response_variable?: string; error?: boolean; } @@ -230,14 +235,10 @@ interface UnknownAction extends BaseAction { [key: string]: unknown; } -export type Action = +export type NonConditionAction = | EventAction | DeviceAction | ServiceAction - | Condition - | ShorthandAndCondition - | ShorthandOrCondition - | ShorthandNotCondition | DelayAction | SceneAction | WaitAction @@ -251,6 +252,13 @@ export type Action = | ParallelAction | UnknownAction; +export type Action = + | NonConditionAction + | Condition + | ShorthandAndCondition + | ShorthandOrCondition + | ShorthandNotCondition; + export interface ActionTypes { delay: DelayAction; wait_template: WaitAction; diff --git a/src/data/script_i18n.ts b/src/data/script_i18n.ts index 90d807fb85..04819d448a 100644 --- a/src/data/script_i18n.ts +++ b/src/data/script_i18n.ts @@ -38,6 +38,32 @@ export const describeAction = ( action: ActionTypes[T], actionType?: T, ignoreAlias = false +): string => { + try { + return tryDescribeAction( + hass, + entityRegistry, + action, + actionType, + ignoreAlias + ); + } catch (error: any) { + // eslint-disable-next-line no-console + console.error(error); + let msg = "Error in describing action"; + if (error.message) { + msg += ": " + error.message; + } + return msg; + } +}; + +const tryDescribeAction = ( + hass: HomeAssistant, + entityRegistry: EntityRegistryEntry[], + action: ActionTypes[T], + actionType?: T, + ignoreAlias = false ): string => { if (action.alias && !ignoreAlias) { return action.alias; diff --git a/src/data/selector.ts b/src/data/selector.ts index 6523ede4f6..acbac41a43 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -345,8 +345,7 @@ export interface TemplateSelector { } export interface ThemeSelector { - // eslint-disable-next-line @typescript-eslint/ban-types - theme: {} | null; + theme: { include_default?: boolean } | null; } export interface TimeSelector { // eslint-disable-next-line @typescript-eslint/ban-types diff --git a/src/data/service.ts b/src/data/service.ts index b9ca93fe87..f93312ecf2 100644 --- a/src/data/service.ts +++ b/src/data/service.ts @@ -1,10 +1,10 @@ -import { HomeAssistant } from "../types"; +import { Context, HomeAssistant } from "../types"; import { Action } from "./script"; export const callExecuteScript = ( hass: HomeAssistant, sequence: Action | Action[] -) => +): Promise<{ context: Context; response: Record }> => hass.callWS({ type: "execute_script", sequence, diff --git a/src/data/supervisor/mounts.ts b/src/data/supervisor/mounts.ts index 474991fd93..ea4fc8da6f 100644 --- a/src/data/supervisor/mounts.ts +++ b/src/data/supervisor/mounts.ts @@ -22,6 +22,8 @@ interface MountOptions { default_backup_mount?: string | null; } +export type CIFSVersion = "auto" | "1.0" | "2.0"; + interface SupervisorMountBase { name: string; usage: SupervisorMountUsage; @@ -42,6 +44,7 @@ export interface SupervisorNFSMount extends SupervisorMountResponse { export interface SupervisorCIFSMount extends SupervisorMountResponse { type: SupervisorMountType.CIFS; share: string; + version?: CIFSVersion; } export type SupervisorMount = SupervisorNFSMount | SupervisorCIFSMount; @@ -51,6 +54,7 @@ export type SupervisorNFSMountRequestParams = SupervisorNFSMount; export interface SupervisorCIFSMountRequestParams extends SupervisorCIFSMount { username?: string; password?: string; + version?: CIFSVersion; } export type SupervisorMountRequestParams = diff --git a/src/data/supervisor/supervisor.ts b/src/data/supervisor/supervisor.ts index e884ed271b..cc721e5b61 100644 --- a/src/data/supervisor/supervisor.ts +++ b/src/data/supervisor/supervisor.ts @@ -129,5 +129,6 @@ export const getSupervisorEventCollection = ( `_supervisor${key}Event`, (conn2) => supervisorApiWsRequest(conn2, { endpoint }), (connection, store) => - subscribeSupervisorEventUpdates(connection, store, key) + subscribeSupervisorEventUpdates(connection, store, key), + { unsubGrace: false } ); diff --git a/src/data/timer.ts b/src/data/timer.ts index a06a865bf3..5401175d41 100644 --- a/src/data/timer.ts +++ b/src/data/timer.ts @@ -94,6 +94,7 @@ export const computeDisplayTimer = ( hass.localize, stateObj, hass.locale, + hass.config, hass.entities ); } @@ -105,6 +106,7 @@ export const computeDisplayTimer = ( hass.localize, stateObj, hass.locale, + hass.config, hass.entities )})`; } diff --git a/src/data/translation.ts b/src/data/translation.ts index fb6fdaeeb9..b64e557b6b 100644 --- a/src/data/translation.ts +++ b/src/data/translation.ts @@ -17,6 +17,11 @@ export enum TimeFormat { twenty_four = "24", } +export enum TimeZone { + local = "local", + server = "server", +} + export enum DateFormat { language = "language", system = "system", @@ -42,6 +47,7 @@ export interface FrontendLocaleData { time_format: TimeFormat; date_format: DateFormat; first_weekday: FirstWeekday; + time_zone: TimeZone; } declare global { diff --git a/src/data/trigger.ts b/src/data/trigger.ts index 847c4ee50b..65ce25d726 100644 --- a/src/data/trigger.ts +++ b/src/data/trigger.ts @@ -8,6 +8,8 @@ import { mdiHomeAssistant, mdiMapMarker, mdiMapMarkerRadius, + mdiMessageAlert, + mdiMicrophoneMessage, mdiNfcVariant, mdiNumeric, mdiStateMachine, @@ -26,10 +28,12 @@ export const TRIGGER_TYPES = { mqtt: mdiSwapHorizontal, numeric_state: mdiNumeric, sun: mdiWeatherSunny, + conversation: mdiMicrophoneMessage, tag: mdiNfcVariant, template: mdiCodeBraces, time: mdiClockOutline, time_pattern: mdiAvTimer, webhook: mdiWebhook, + persistent_notification: mdiMessageAlert, zone: mdiMapMarkerRadius, }; diff --git a/src/dialogs/more-info/components/alarm_control_panel/dialog-enter-code.ts b/src/dialogs/enter-code/dialog-enter-code.ts similarity index 91% rename from src/dialogs/more-info/components/alarm_control_panel/dialog-enter-code.ts rename to src/dialogs/enter-code/dialog-enter-code.ts index acacb74666..b4cfc63016 100644 --- a/src/dialogs/more-info/components/alarm_control_panel/dialog-enter-code.ts +++ b/src/dialogs/enter-code/dialog-enter-code.ts @@ -1,14 +1,15 @@ import { mdiCheck, mdiClose } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-button"; -import "../../../../components/ha-control-button"; -import { createCloseHeading } from "../../../../components/ha-dialog"; -import "../../../../components/ha-textfield"; -import type { HaTextField } from "../../../../components/ha-textfield"; -import { HomeAssistant } from "../../../../types"; -import { HassDialog } from "../../../make-dialog-manager"; +import { ifDefined } from "lit/directives/if-defined"; +import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-button"; +import "../../components/ha-control-button"; +import { createCloseHeading } from "../../components/ha-dialog"; +import "../../components/ha-textfield"; +import type { HaTextField } from "../../components/ha-textfield"; +import { HomeAssistant } from "../../types"; +import { HassDialog } from "../make-dialog-manager"; import { EnterCodeDialogParams } from "./show-enter-code-dialog"; const BUTTONS = [ @@ -72,7 +73,8 @@ export class DialogEnterCode } private _inputValueChange(e) { - const val = (e.currentTarget! as any).value; + const field = e.currentTarget as HaTextField; + const val = field.value; this._showClearButton = !!val; } @@ -97,6 +99,7 @@ export class DialogEnterCode id="code" .label=${this.hass.localize("ui.dialogs.enter_code.input_label")} type="password" + pattern=${ifDefined(this._dialogParams.codePattern)} input-mode="text" > diff --git a/src/dialogs/more-info/components/alarm_control_panel/show-enter-code-dialog.ts b/src/dialogs/enter-code/show-enter-code-dialog.ts similarity index 91% rename from src/dialogs/more-info/components/alarm_control_panel/show-enter-code-dialog.ts rename to src/dialogs/enter-code/show-enter-code-dialog.ts index 802ae23fb1..6356c20364 100644 --- a/src/dialogs/more-info/components/alarm_control_panel/show-enter-code-dialog.ts +++ b/src/dialogs/enter-code/show-enter-code-dialog.ts @@ -1,7 +1,8 @@ -import { fireEvent } from "../../../../common/dom/fire_event"; +import { fireEvent } from "../../common/dom/fire_event"; export interface EnterCodeDialogParams { codeFormat: "text" | "number"; + codePattern?: string; submitText?: string; cancelText?: string; title?: string; diff --git a/src/dialogs/more-info/components/alarm_control_panel/ha-more-info-alarm_control_panel-modes.ts b/src/dialogs/more-info/components/alarm_control_panel/ha-more-info-alarm_control_panel-modes.ts index ea8881c5b5..22f028621b 100644 --- a/src/dialogs/more-info/components/alarm_control_panel/ha-more-info-alarm_control_panel-modes.ts +++ b/src/dialogs/more-info/components/alarm_control_panel/ha-more-info-alarm_control_panel-modes.ts @@ -14,7 +14,7 @@ import { } from "../../../../data/alarm_control_panel"; import { UNAVAILABLE } from "../../../../data/entity"; import { HomeAssistant } from "../../../../types"; -import { showEnterCodeDialogDialog } from "./show-enter-code-dialog"; +import { showEnterCodeDialogDialog } from "../../../enter-code/show-enter-code-dialog"; @customElement("ha-more-info-alarm_control_panel-modes") export class HaMoreInfoAlarmControlPanelModes extends LitElement { diff --git a/src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts b/src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts index d40e88816e..72333bd2a2 100644 --- a/src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts +++ b/src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts @@ -72,6 +72,7 @@ export class HaMoreInfoFanSpeed extends LitElement { this.hass.localize, this.stateObj, this.hass.locale, + this.hass.config, this.hass.entities, speed ); diff --git a/src/dialogs/more-info/components/ha-more-info-state-header.ts b/src/dialogs/more-info/components/ha-more-info-state-header.ts index 1e7430cb23..150058e89b 100644 --- a/src/dialogs/more-info/components/ha-more-info-state-header.ts +++ b/src/dialogs/more-info/components/ha-more-info-state-header.ts @@ -39,6 +39,7 @@ export class HaMoreInfoStateHeader extends LitElement { this.hass!.localize, stateObj, this.hass!.locale, + this.hass!.config, this.hass!.entities ); diff --git a/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts b/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts index 3c72999412..410ba70c04 100644 --- a/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts +++ b/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts @@ -1,16 +1,28 @@ import { mdiClose } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-button"; import "../../../../components/ha-dialog"; import "../../../../components/ha-dialog-header"; -import { EntityRegistryEntry } from "../../../../data/entity_registry"; -import { LightColor } from "../../../../data/light"; +import "../../../../components/ha-icon-button-toggle"; +import type { EntityRegistryEntry } from "../../../../data/entity_registry"; +import { + formatTempColor, + LightColor, + LightColorMode, + LightEntity, + lightSupportsColor, + lightSupportsColorMode, +} from "../../../../data/light"; import { haStyleDialog } from "../../../../resources/styles"; -import { HomeAssistant } from "../../../../types"; -import "./light-color-picker"; -import { LightColorFavoriteDialogParams } from "./show-dialog-light-color-favorite"; +import type { HomeAssistant } from "../../../../types"; +import "./light-color-rgb-picker"; +import "./light-color-temp-picker"; +import type { LightColorFavoriteDialogParams } from "./show-dialog-light-color-favorite"; + +export type LightPickerMode = "color_temp" | "color"; @customElement("dialog-light-color-favorite") class DialogLightColorFavorite extends LitElement { @@ -22,11 +34,26 @@ class DialogLightColorFavorite extends LitElement { @state() _color?: LightColor; + @state() private _mode?: LightPickerMode; + + @state() private _modes: LightPickerMode[] = []; + + @state() private _currentValue?: string; + + private _colorHovered(ev: CustomEvent) { + if (ev.detail && "color_temp_kelvin" in ev.detail) { + this._currentValue = formatTempColor(ev.detail.color_temp_kelvin); + } else { + this._currentValue = undefined; + } + } + public async showDialog( dialogParams: LightColorFavoriteDialogParams ): Promise { this._entry = dialogParams.entry; this._dialogParams = dialogParams; + this._updateModes(dialogParams.defaultMode); await this.updateComplete; } @@ -37,10 +64,43 @@ class DialogLightColorFavorite extends LitElement { fireEvent(this, "dialog-closed", { dialog: this.localName }); } + private _updateModes(defaultMode?: LightPickerMode) { + const supportsTemp = lightSupportsColorMode( + this.stateObj!, + LightColorMode.COLOR_TEMP + ); + + const supportsColor = lightSupportsColor(this.stateObj!); + + const modes: LightPickerMode[] = []; + if (supportsColor) { + modes.push("color"); + } + if (supportsTemp) { + modes.push("color_temp"); + } + + this._modes = modes; + this._mode = + defaultMode ?? + (this.stateObj!.attributes.color_mode + ? this.stateObj!.attributes.color_mode === LightColorMode.COLOR_TEMP + ? LightColorMode.COLOR_TEMP + : "color" + : this._modes[0]); + } + private _colorChanged(ev: CustomEvent) { this._color = ev.detail; } + get stateObj() { + return ( + this._entry && + (this.hass.states[this._entry.entity_id] as LightEntity | undefined) + ); + } + private async _cancel() { this._dialogParams?.cancel?.(); this.closeDialog(); @@ -55,8 +115,16 @@ class DialogLightColorFavorite extends LitElement { this.closeDialog(); } + private _modeChanged(ev): void { + const newMode = ev.currentTarget.mode; + if (newMode === this._mode) { + return; + } + this._mode = newMode; + } + protected render() { - if (!this._entry) { + if (!this._entry || !this.stateObj) { return nothing; } @@ -76,13 +144,58 @@ class DialogLightColorFavorite extends LitElement { > ${this._dialogParams?.title} - - +
+ ${this._currentValue} + ${this._modes.length > 1 + ? html` +
+ ${this._modes.map( + (value) => + html` + + + + ` + )} +
+ ` + : nothing} +
+ +
+ ${this._mode === "color_temp" + ? html` + + + ` + : nothing} + ${this._mode === "color" + ? html` + + + ` + : nothing} +
${this.hass.localize("ui.common.cancel")} @@ -101,16 +214,10 @@ class DialogLightColorFavorite extends LitElement { --dialog-content-padding: 0; } - light-color-picker { - display: flex; - flex-direction: column; - flex: 1; - } - @media all and (max-width: 450px), all and (max-height: 500px) { ha-dialog { --dialog-surface-margin-top: 100px; - --mdc-dialog-min-height: calc(100% - 100px); + --mdc-dialog-min-height: auto; --mdc-dialog-max-height: calc(100% - 100px); --ha-dialog-border-radius: var( --ha-dialog-bottom-sheet-border-radius, @@ -118,6 +225,54 @@ class DialogLightColorFavorite extends LitElement { ); } } + + .content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 24px; + flex: 1; + } + .modes { + display: flex; + flex-direction: row; + justify-content: flex-end; + padding: 0 24px; + } + .wheel { + width: 30px; + height: 30px; + flex: none; + border-radius: 15px; + } + .wheel.color { + background-image: url("/static/images/color_wheel.png"); + background-size: cover; + } + .wheel.color_temp { + background: linear-gradient( + 0, + rgb(166, 209, 255) 0%, + white 50%, + rgb(255, 160, 0) 100% + ); + } + .value { + pointer-events: none; + position: absolute; + top: 0; + left: 0; + right: 0; + margin: auto; + font-style: normal; + font-weight: 500; + font-size: 16px; + height: 48px; + line-height: 48px; + letter-spacing: 0.1px; + text-align: center; + } `, ]; } diff --git a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts index c8c3c8cc1b..ad00a5b105 100644 --- a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts +++ b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts @@ -30,10 +30,16 @@ import { } from "../../../../resources/sortable.ondemand"; import { HomeAssistant } from "../../../../types"; import { showConfirmationDialog } from "../../../generic/show-dialog-box"; +import type { LightPickerMode } from "./dialog-light-color-favorite"; import "./ha-favorite-color-button"; -import type { LightPickerMode } from "./light-color-picker"; import { showLightColorFavoriteDialog } from "./show-dialog-light-color-favorite"; +declare global { + interface HASSDomEvents { + "favorite-color-edit-started"; + } +} + @customElement("ha-more-info-light-favorite-colors") export class HaMoreInfoLightFavoriteColors extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -147,8 +153,8 @@ export class HaMoreInfoLightFavoriteColors extends LitElement { private _edit = async (index) => { // Make sure the current favorite color is set + fireEvent(this, "favorite-color-edit-started"); await this._apply(index); - const defaultMode: LightPickerMode = "color_temp_kelvin" in this._favoriteColors[index] ? "color_temp" diff --git a/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts b/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts deleted file mode 100644 index b556443f5e..0000000000 --- a/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; -import { customElement, property } from "lit/decorators"; -import { HomeAssistant } from "../../../../types"; -import "./light-color-picker"; -import { LightColorPickerViewParams } from "./show-view-light-color-picker"; - -@customElement("ha-more-info-view-light-color-picker") -class MoreInfoViewLightColorPicker extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public params?: LightColorPickerViewParams; - - protected render() { - if (!this.params) { - return nothing; - } - - return html` - - - `; - } - - static get styles(): CSSResultGroup { - return [ - css` - :host { - position: relative; - display: flex; - flex-direction: column; - flex: 1; - } - light-color-picker { - display: flex; - flex-direction: column; - flex: 1; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ha-more-info-view-light-color-picker": MoreInfoViewLightColorPicker; - } -} diff --git a/src/dialogs/more-info/components/lights/light-color-picker.ts b/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts similarity index 59% rename from src/dialogs/more-info/components/lights/light-color-picker.ts rename to src/dialogs/more-info/components/lights/light-color-rgb-picker.ts index 720cd2a5bb..cf50ae5c04 100644 --- a/src/dialogs/more-info/components/lights/light-color-picker.ts +++ b/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts @@ -23,21 +23,18 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import { throttle } from "../../../../common/util/throttle"; import "../../../../components/ha-button-toggle-group"; import "../../../../components/ha-hs-color-picker"; +import "../../../../components/ha-icon"; import "../../../../components/ha-icon-button-prev"; import "../../../../components/ha-labeled-slider"; import "../../../../components/ha-temp-color-picker"; import { - LightColor, getLightCurrentModeRgbColor, + LightColor, LightColorMode, LightEntity, - lightSupportsColor, lightSupportsColorMode, } from "../../../../data/light"; import { HomeAssistant } from "../../../../types"; -import "../../../../components/ha-icon"; - -export type LightPickerMode = "color_temp" | "color"; declare global { interface HASSDomEvents { @@ -45,13 +42,11 @@ declare global { } } -@customElement("light-color-picker") -class LightColorPicker extends LitElement { +@customElement("light-color-rgb-picker") +class LightRgbColorPicker extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public entityId!: string; - - @property() public defaultMode?: LightPickerMode; + @property({ attribute: false }) public stateObj!: LightEntity; @state() private _cwSliderValue?: number; @@ -65,16 +60,6 @@ class LightColorPicker extends LitElement { @state() private _hsPickerValue?: [number, number]; - @state() private _ctPickerValue?: number; - - @state() private _mode?: LightPickerMode; - - @state() private _modes: LightPickerMode[] = []; - - get stateObj() { - return this.hass.states[this.entityId] as LightEntity | undefined; - } - protected render() { if (!this.stateObj) { return nothing; @@ -100,135 +85,89 @@ class LightColorPicker extends LitElement { : ""; return html` - ${this._modes.length > 1 +
+ + + + +
+ ${supportsRgbw || supportsRgbww + ? html`` + : nothing} + ${supportsRgbw ? html` - - ${this._modes.map( - (value) => - html`` - )} - + + ` + : nothing} + ${supportsRgbww + ? html` + + ` : nothing} -
- ${this._mode === LightColorMode.COLOR_TEMP - ? html` -

- ${this._ctPickerValue ? `${this._ctPickerValue} K` : nothing} -

- - - ` - : nothing} - ${this._mode === "color" - ? html` -
- - - - -
- ${supportsRgbw || supportsRgbww - ? html`` - : nothing} - ${supportsRgbw - ? html` - - ` - : nothing} - ${supportsRgbww - ? html` - - - ` - : nothing} - ` - : nothing} -
`; } public _updateSliderValues() { const stateObj = this.stateObj; - if (stateObj?.state === "on") { + if (stateObj.state === "on") { this._brightnessAdjusted = undefined; if ( stateObj.attributes.color_mode === LightColorMode.RGB && @@ -242,10 +181,6 @@ class LightColorPicker extends LitElement { this._brightnessAdjusted = maxVal; } } - this._ctPickerValue = - stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP - ? stateObj.attributes.color_temp_kelvin - : undefined; this._wvSliderValue = stateObj.attributes.color_mode === LightColorMode.RGBW && @@ -273,8 +208,7 @@ class LightColorPicker extends LitElement { ? rgb2hs(currentRgbColor.slice(0, 3) as [number, number, number]) : undefined; } else { - this._hsPickerValue = [0, 0]; - this._ctPickerValue = undefined; + this._hsPickerValue = undefined; this._wvSliderValue = undefined; this._cwSliderValue = undefined; this._wwSliderValue = undefined; @@ -288,43 +222,9 @@ class LightColorPicker extends LitElement { return; } - if (changedProps.has("entityId")) { - const supportsTemp = lightSupportsColorMode( - this.stateObj!, - LightColorMode.COLOR_TEMP - ); - - const supportsColor = lightSupportsColor(this.stateObj!); - - const modes: LightPickerMode[] = []; - if (supportsColor) { - modes.push("color"); - } - if (supportsTemp) { - modes.push("color_temp"); - } - - this._modes = modes; - this._mode = - this.defaultMode ?? - (this.stateObj!.attributes.color_mode - ? this.stateObj!.attributes.color_mode === LightColorMode.COLOR_TEMP - ? LightColorMode.COLOR_TEMP - : "color" - : this._modes[0]); - } - this._updateSliderValues(); } - private _handleTabChanged(ev: CustomEvent): void { - const newMode = this._modes[ev.detail.index]; - if (newMode === this._mode) { - return; - } - this._mode = newMode; - } - private _hsColorCursorMoved(ev: CustomEvent) { if (!ev.detail.value) { return; @@ -404,40 +304,6 @@ class LightColorPicker extends LitElement { this._updateColor(); } - private _ctColorCursorMoved(ev: CustomEvent) { - const ct = ev.detail.value; - - if (isNaN(ct) || this._ctPickerValue === ct) { - return; - } - - this._ctPickerValue = ct; - - this._throttleUpdateColorTemp(); - } - - private _throttleUpdateColorTemp = throttle(() => { - this._updateColorTemp(); - }, 500); - - private _ctColorChanged(ev: CustomEvent) { - const ct = ev.detail.value; - - if (isNaN(ct) || this._ctPickerValue === ct) { - return; - } - - this._ctPickerValue = ct; - - this._updateColorTemp(); - } - - private _updateColorTemp() { - const color_temp_kelvin = this._ctPickerValue!; - - this._applyColor({ color_temp_kelvin }); - } - private _wvSliderChanged(ev: CustomEvent) { const target = ev.target as any; let wv = Number(target.value); @@ -574,19 +440,12 @@ class LightColorPicker extends LitElement { display: flex; flex-direction: column; } - .content { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 24px; - flex: 1; - } .native-color-picker { position: absolute; top: 0; right: 0; + z-index: 1; } .native-color-picker ha-svg-icon { @@ -639,37 +498,18 @@ class LightColorPicker extends LitElement { .color-container { position: relative; - max-width: 300px; - min-width: 200px; - margin: 0 0 44px 0; - padding-top: 44px; } ha-hs-color-picker { - width: 100%; - } - - ha-temp-color-picker { - max-width: 300px; - min-width: 200px; - margin: 20px 0 44px 0; + height: 45vh; + max-height: 320px; + min-height: 200px; } ha-labeled-slider { width: 100%; } - .color-temp-value { - font-style: normal; - font-weight: 500; - font-size: 16px; - height: 24px; - line-height: 24px; - letter-spacing: 0.1px; - margin: 0; - direction: ltr; - } - hr { border-color: var(--divider-color); border-bottom: none; @@ -682,6 +522,6 @@ class LightColorPicker extends LitElement { declare global { interface HTMLElementTagNameMap { - "light-color-picker": LightColorPicker; + "light-color-rgb-picker": LightRgbColorPicker; } } diff --git a/src/dialogs/more-info/components/lights/light-color-temp-picker.ts b/src/dialogs/more-info/components/lights/light-color-temp-picker.ts new file mode 100644 index 0000000000..4543c0dc88 --- /dev/null +++ b/src/dialogs/more-info/components/lights/light-color-temp-picker.ts @@ -0,0 +1,146 @@ +import { + css, + CSSResultGroup, + html, + LitElement, + nothing, + PropertyValues, +} from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import { throttle } from "../../../../common/util/throttle"; +import "../../../../components/ha-temp-color-picker"; +import { + LightColor, + LightColorMode, + LightEntity, +} from "../../../../data/light"; +import { HomeAssistant } from "../../../../types"; + +declare global { + interface HASSDomEvents { + "color-changed": LightColor; + "color-hovered": LightColor | undefined; + } +} + +@customElement("light-color-temp-picker") +class LightColorTempPicker extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: LightEntity; + + @state() private _ctPickerValue?: number; + + protected render() { + if (!this.stateObj) { + return nothing; + } + + return html` + + + `; + } + + public _updateSliderValues() { + const stateObj = this.stateObj; + + if (stateObj.state === "on") { + this._ctPickerValue = + stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP + ? stateObj.attributes.color_temp_kelvin + : undefined; + } else { + this._ctPickerValue = undefined; + } + } + + public willUpdate(changedProps: PropertyValues) { + super.willUpdate(changedProps); + + if (!changedProps.has("stateObj")) { + return; + } + + this._updateSliderValues(); + } + + private _ctColorCursorMoved(ev: CustomEvent) { + const ct = ev.detail.value; + + if (isNaN(ct) || this._ctPickerValue === ct) { + return; + } + + this._ctPickerValue = ct; + + fireEvent(this, "color-hovered", { + color_temp_kelvin: ct, + }); + + this._throttleUpdateColorTemp(); + } + + private _throttleUpdateColorTemp = throttle(() => { + this._updateColorTemp(); + }, 500); + + private _ctColorChanged(ev: CustomEvent) { + const ct = ev.detail.value; + + fireEvent(this, "color-hovered", undefined); + + if (isNaN(ct) || this._ctPickerValue === ct) { + return; + } + + this._ctPickerValue = ct; + + this._updateColorTemp(); + } + + private _updateColorTemp() { + const color_temp_kelvin = this._ctPickerValue!; + + this._applyColor({ color_temp_kelvin }); + } + + private _applyColor(color: LightColor, params?: Record) { + fireEvent(this, "color-changed", color); + this.hass.callService("light", "turn_on", { + entity_id: this.stateObj!.entity_id, + ...color, + ...params, + }); + } + + static get styles(): CSSResultGroup { + return [ + css` + :host { + display: flex; + flex-direction: column; + } + + ha-temp-color-picker { + height: 45vh; + max-height: 320px; + min-height: 200px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "light-color-temp-picker": LightColorTempPicker; + } +} diff --git a/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts b/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts index 28f0af8622..d8bc865093 100644 --- a/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts +++ b/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts @@ -1,7 +1,7 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import { ExtEntityRegistryEntry } from "../../../../data/entity_registry"; import { LightColor } from "../../../../data/light"; -import type { LightPickerMode } from "./light-color-picker"; +import type { LightPickerMode } from "./dialog-light-color-favorite"; export interface LightColorFavoriteDialogParams { entry: ExtEntityRegistryEntry; diff --git a/src/dialogs/more-info/components/lights/show-view-light-color-picker.ts b/src/dialogs/more-info/components/lights/show-view-light-color-picker.ts deleted file mode 100644 index 5a2bd40fa1..0000000000 --- a/src/dialogs/more-info/components/lights/show-view-light-color-picker.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { fireEvent } from "../../../../common/dom/fire_event"; -import type { LightPickerMode } from "./light-color-picker"; - -export interface LightColorPickerViewParams { - entityId: string; - defaultMode: LightPickerMode; -} - -export const loadLightColorPickerView = () => - import("./ha-more-info-view-light-color-picker"); - -export const showLightColorPickerView = ( - element: HTMLElement, - title: string, - params: LightColorPickerViewParams -): void => { - fireEvent(element, "show-child-view", { - viewTag: "ha-more-info-view-light-color-picker", - viewImport: loadLightColorPickerView, - viewTitle: title, - viewParams: params, - }); -}; diff --git a/src/dialogs/more-info/components/lock/ha-more-info-lock-toggle.ts b/src/dialogs/more-info/components/lock/ha-more-info-lock-toggle.ts new file mode 100644 index 0000000000..aafecef40d --- /dev/null +++ b/src/dialogs/more-info/components/lock/ha-more-info-lock-toggle.ts @@ -0,0 +1,222 @@ +import { + css, + CSSResultGroup, + html, + LitElement, + PropertyValues, + TemplateResult, +} from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { styleMap } from "lit/directives/style-map"; +import { domainIcon } from "../../../../common/entity/domain_icon"; +import { stateColorCss } from "../../../../common/entity/state_color"; +import "../../../../components/ha-control-button"; +import "../../../../components/ha-control-switch"; +import { UNAVAILABLE, UNKNOWN } from "../../../../data/entity"; +import { forwardHaptic } from "../../../../data/haptics"; +import { LockEntity } from "../../../../data/lock"; +import { HomeAssistant } from "../../../../types"; +import { showEnterCodeDialogDialog } from "../../../enter-code/show-enter-code-dialog"; + +@customElement("ha-more-info-lock-toggle") +export class HaMoreInfoLockToggle extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: LockEntity; + + @state() private _isOn = false; + + public willUpdate(changedProps: PropertyValues): void { + super.willUpdate(changedProps); + if (changedProps.has("stateObj")) { + this._isOn = + this.stateObj.state === "locked" || this.stateObj.state === "locking"; + } + } + + private _valueChanged(ev) { + const checked = ev.target.checked as boolean; + + if (checked) { + this._turnOn(); + } else { + this._turnOff(); + } + } + + private async _turnOn() { + this._isOn = true; + try { + await this._callService(true); + } catch (err) { + this._isOn = false; + } + } + + private async _turnOff() { + this._isOn = false; + try { + await this._callService(false); + } catch (err) { + this._isOn = true; + } + } + + private async _callService(turnOn: boolean): Promise { + if (!this.hass || !this.stateObj) { + return; + } + forwardHaptic("light"); + + let code: string | undefined; + + if (this.stateObj.attributes.code_format) { + const response = await showEnterCodeDialogDialog(this, { + codeFormat: "text", + codePattern: this.stateObj.attributes.code_format, + title: this.hass.localize( + `ui.dialogs.more_info_control.lock.${turnOn ? "lock" : "unlock"}` + ), + submitText: this.hass.localize( + `ui.dialogs.more_info_control.lock.${turnOn ? "lock" : "unlock"}` + ), + }); + if (response == null) { + throw new Error("cancel"); + } + code = response; + } + + await this.hass.callService("lock", turnOn ? "lock" : "unlock", { + entity_id: this.stateObj.entity_id, + code, + }); + } + + protected render(): TemplateResult { + const locking = this.stateObj.state === "locking"; + const unlocking = this.stateObj.state === "unlocking"; + + const color = stateColorCss(this.stateObj); + + const onIcon = domainIcon( + "lock", + this.stateObj, + locking ? "locking" : "locked" + ); + + const offIcon = domainIcon( + "lock", + this.stateObj, + unlocking ? "unlocking" : "unlocked" + ); + + if (this.stateObj.state === UNKNOWN) { + return html` +
+ + + + + + +
+ `; + } + + return html` + + + + + `; + } + + static get styles(): CSSResultGroup { + return css` + @keyframes pulse { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + ha-control-switch { + height: 45vh; + max-height: 320px; + min-height: 200px; + --control-switch-thickness: 100px; + --control-switch-border-radius: 24px; + --control-switch-padding: 6px; + --mdc-icon-size: 24px; + } + .pulse { + animation: pulse 1s infinite; + } + .buttons { + display: flex; + flex-direction: column; + width: 100px; + height: 45vh; + max-height: 320px; + min-height: 200px; + padding: 6px; + box-sizing: border-box; + } + ha-control-button { + flex: 1; + width: 100%; + --control-button-border-radius: 18px; + --mdc-icon-size: 24px; + } + ha-control-button.active { + --control-button-icon-color: white; + --control-button-background-color: var(--color); + --control-button-background-opacity: 1; + } + ha-control-button:not(:last-child) { + margin-bottom: 6px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-more-info-lock-toggle": HaMoreInfoLockToggle; + } +} diff --git a/src/dialogs/more-info/const.ts b/src/dialogs/more-info/const.ts index 8675c499e1..d6d6d615aa 100644 --- a/src/dialogs/more-info/const.ts +++ b/src/dialogs/more-info/const.ts @@ -22,6 +22,7 @@ export const DOMAINS_WITH_NEW_MORE_INFO = [ "fan", "input_boolean", "light", + "lock", "siren", "switch", ]; @@ -39,6 +40,7 @@ export const DOMAINS_WITH_MORE_INFO = [ "fan", "group", "humidifier", + "image", "input_boolean", "input_datetime", "light", diff --git a/src/dialogs/more-info/controls/more-info-alarm_control_panel.ts b/src/dialogs/more-info/controls/more-info-alarm_control_panel.ts index 7fadaa5776..7f73407ae1 100644 --- a/src/dialogs/more-info/controls/more-info-alarm_control_panel.ts +++ b/src/dialogs/more-info/controls/more-info-alarm_control_panel.ts @@ -7,8 +7,8 @@ import { stateColorCss } from "../../../common/entity/state_color"; import "../../../components/ha-outlined-button"; import { AlarmControlPanelEntity } from "../../../data/alarm_control_panel"; import type { HomeAssistant } from "../../../types"; +import { showEnterCodeDialogDialog } from "../../enter-code/show-enter-code-dialog"; import "../components/alarm_control_panel/ha-more-info-alarm_control_panel-modes"; -import { showEnterCodeDialogDialog } from "../components/alarm_control_panel/show-enter-code-dialog"; import { moreInfoControlStyle } from "../components/ha-more-info-control-style"; import "../components/ha-more-info-state-header"; diff --git a/src/dialogs/more-info/controls/more-info-climate.ts b/src/dialogs/more-info/controls/more-info-climate.ts index 52a4b0aa14..9294e06855 100644 --- a/src/dialogs/more-info/controls/more-info-climate.ts +++ b/src/dialogs/more-info/controls/more-info-climate.ts @@ -203,6 +203,7 @@ class MoreInfoClimate extends LitElement { hass.localize, stateObj, hass.locale, + this.hass.config, hass.entities, mode )} @@ -236,6 +237,7 @@ class MoreInfoClimate extends LitElement { hass.localize, stateObj, hass.locale, + hass.config, hass.entities, "preset_mode", mode @@ -270,6 +272,7 @@ class MoreInfoClimate extends LitElement { hass.localize, stateObj, hass.locale, + this.hass.config, hass.entities, "fan_mode", mode @@ -304,6 +307,7 @@ class MoreInfoClimate extends LitElement { hass.localize, stateObj, hass.locale, + this.hass.config, hass.entities, "swing_mode", mode diff --git a/src/dialogs/more-info/controls/more-info-cover.ts b/src/dialogs/more-info/controls/more-info-cover.ts index b4fe8114ba..3ea0907245 100644 --- a/src/dialogs/more-info/controls/more-info-cover.ts +++ b/src/dialogs/more-info/controls/more-info-cover.ts @@ -11,6 +11,8 @@ import { customElement, property, state } from "lit/decorators"; import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-attributes"; +import "../../../components/ha-icon-button-group"; +import "../../../components/ha-icon-button-toggle"; import { computeCoverPositionStateDisplay, CoverEntity, @@ -24,6 +26,8 @@ import "../components/cover/ha-more-info-cover-toggle"; import { moreInfoControlStyle } from "../components/ha-more-info-control-style"; import "../components/ha-more-info-state-header"; +type Mode = "position" | "button"; + @customElement("more-info-cover") class MoreInfoCover extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -34,10 +38,10 @@ class MoreInfoCover extends LitElement { @state() private _liveTilt?: number; - @state() private _mode?: "position" | "button"; + @state() private _mode?: Mode; - private _toggleMode() { - this._mode = this._mode === "position" ? "button" : "position"; + private _setMode(ev) { + this._mode = ev.currentTarget.mode; } private _positionSliderMoved(ev) { @@ -83,6 +87,7 @@ class MoreInfoCover extends LitElement { this.hass.localize, this.stateObj!, this.hass.locale, + this.hass.config, this.hass.entities, forcedState ); @@ -191,19 +196,26 @@ class MoreInfoCover extends LitElement { (supportsPosition || supportsTiltPosition) && (supportsOpenClose || supportsTilt) ? html` -
- + -
+ .selected=${this._mode === "position"} + .path=${mdiMenu} + .mode=${"position"} + @click=${this._setMode} + > + + ` : nothing } diff --git a/src/dialogs/more-info/controls/more-info-date.ts b/src/dialogs/more-info/controls/more-info-date.ts index b53829b557..6d489fe22f 100644 --- a/src/dialogs/more-info/controls/more-info-date.ts +++ b/src/dialogs/more-info/controls/more-info-date.ts @@ -4,7 +4,7 @@ import { customElement, property } from "lit/decorators"; import "../../../components/ha-date-input"; import "../../../components/ha-time-input"; import { setDateValue } from "../../../data/date"; -import { isUnavailableState } from "../../../data/entity"; +import { isUnavailableState, UNAVAILABLE } from "../../../data/entity"; import type { HomeAssistant } from "../../../types"; @customElement("more-info-date") @@ -14,15 +14,17 @@ class MoreInfoDate extends LitElement { @property({ attribute: false }) public stateObj?: HassEntity; protected render() { - if (!this.stateObj || isUnavailableState(this.stateObj.state)) { + if (!this.stateObj || this.stateObj.state === UNAVAILABLE) { return nothing; } return html` @@ -30,7 +32,9 @@ class MoreInfoDate extends LitElement { } private _dateChanged(ev: CustomEvent<{ value: string }>): void { - setDateValue(this.hass!, this.stateObj!.entity_id, ev.detail.value); + if (ev.detail.value) { + setDateValue(this.hass!, this.stateObj!.entity_id, ev.detail.value); + } } static get styles(): CSSResultGroup { diff --git a/src/dialogs/more-info/controls/more-info-datetime.ts b/src/dialogs/more-info/controls/more-info-datetime.ts index b0b25f9bc9..85d7f44bd3 100644 --- a/src/dialogs/more-info/controls/more-info-datetime.ts +++ b/src/dialogs/more-info/controls/more-info-datetime.ts @@ -5,7 +5,7 @@ import { customElement, property } from "lit/decorators"; import "../../../components/ha-date-input"; import "../../../components/ha-time-input"; import { setDateTimeValue } from "../../../data/datetime"; -import { isUnavailableState } from "../../../data/entity"; +import { isUnavailableState, UNAVAILABLE } from "../../../data/entity"; import type { HomeAssistant } from "../../../types"; @customElement("more-info-datetime") @@ -15,25 +15,27 @@ class MoreInfoDatetime extends LitElement { @property({ attribute: false }) public stateObj?: HassEntity; protected render() { - if (!this.stateObj || isUnavailableState(this.stateObj.state)) { + if (!this.stateObj || this.stateObj.state === UNAVAILABLE) { return nothing; } - const dateObj = new Date(this.stateObj.state); - const time = format(dateObj, "HH:mm:ss"); - const date = format(dateObj, "yyyy-MM-dd"); + const dateObj = isUnavailableState(this.stateObj.state) + ? undefined + : new Date(this.stateObj.state); + const time = dateObj ? format(dateObj, "HH:mm:ss") : undefined; + const date = dateObj ? format(dateObj, "yyyy-MM-dd") : undefined; return html` `; @@ -44,19 +46,23 @@ class MoreInfoDatetime extends LitElement { } private _timeChanged(ev: CustomEvent<{ value: string }>): void { - const dateObj = new Date(this.stateObj!.state); - const newTime = ev.detail.value.split(":").map(Number); - dateObj.setHours(newTime[0], newTime[1], newTime[2]); + if (ev.detail.value) { + const dateObj = new Date(this.stateObj!.state); + const newTime = ev.detail.value.split(":").map(Number); + dateObj.setHours(newTime[0], newTime[1], newTime[2]); - setDateTimeValue(this.hass!, this.stateObj!.entity_id, dateObj); + setDateTimeValue(this.hass!, this.stateObj!.entity_id, dateObj); + } } private _dateChanged(ev: CustomEvent<{ value: string }>): void { - const dateObj = new Date(this.stateObj!.state); - const newDate = ev.detail.value.split("-").map(Number); - dateObj.setFullYear(newDate[0], newDate[1] - 1, newDate[2]); + if (ev.detail.value) { + const dateObj = new Date(this.stateObj!.state); + const newDate = ev.detail.value.split("-").map(Number); + dateObj.setFullYear(newDate[0], newDate[1] - 1, newDate[2]); - setDateTimeValue(this.hass!, this.stateObj!.entity_id, dateObj); + setDateTimeValue(this.hass!, this.stateObj!.entity_id, dateObj); + } } static get styles(): CSSResultGroup { diff --git a/src/dialogs/more-info/controls/more-info-fan.ts b/src/dialogs/more-info/controls/more-info-fan.ts index af32733d77..588397c5ce 100644 --- a/src/dialogs/more-info/controls/more-info-fan.ts +++ b/src/dialogs/more-info/controls/more-info-fan.ts @@ -119,6 +119,7 @@ class MoreInfoFan extends LitElement { this.hass.localize, this.stateObj!, this.hass.locale, + this.hass.config, this.hass.entities, forcedState ); @@ -281,6 +282,7 @@ class MoreInfoFan extends LitElement { this.hass.localize, this.stateObj!, this.hass.locale, + this.hass.config, this.hass.entities, "preset_mode", this._presetMode @@ -307,6 +309,7 @@ class MoreInfoFan extends LitElement { this.hass.localize, this.stateObj!, this.hass.locale, + this.hass.config, this.hass.entities, "preset_mode", mode diff --git a/src/dialogs/more-info/controls/more-info-group.ts b/src/dialogs/more-info/controls/more-info-group.ts index 0f5131c395..8006228e16 100644 --- a/src/dialogs/more-info/controls/more-info-group.ts +++ b/src/dialogs/more-info/controls/more-info-group.ts @@ -46,7 +46,8 @@ class MoreInfoGroup extends LitElement { return; } - const baseStateObj = states.find((s) => s.state === "on") || states[0]; + const baseStateObj = + states.find((s) => s.state === this.stateObj!.state) || states[0]; const groupDomain = computeGroupDomain(this.stateObj); @@ -56,6 +57,8 @@ class MoreInfoGroup extends LitElement { this._groupDomainStateObj = { ...baseStateObj, entity_id: this.stateObj.entity_id, + last_updated: this.stateObj.last_updated, + last_changed: this.stateObj.last_changed, attributes: { ...baseStateObj.attributes, friendly_name: this.stateObj.attributes.friendly_name, diff --git a/src/dialogs/more-info/controls/more-info-humidifier.ts b/src/dialogs/more-info/controls/more-info-humidifier.ts index 1e6d270d9d..75dba4faf8 100644 --- a/src/dialogs/more-info/controls/more-info-humidifier.ts +++ b/src/dialogs/more-info/controls/more-info-humidifier.ts @@ -10,7 +10,10 @@ import { import { property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { fireEvent } from "../../../common/dom/fire_event"; -import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display"; +import { + computeAttributeNameDisplay, + computeAttributeValueDisplay, +} from "../../../common/entity/compute_attribute_display"; import { stopPropagation } from "../../../common/dom/stop_propagation"; import { supportsFeature } from "../../../common/entity/supports-feature"; import { computeRTLDirection } from "../../../common/util/compute_rtl"; @@ -22,6 +25,7 @@ import { HUMIDIFIER_SUPPORT_MODES, } from "../../../data/humidifier"; import { HomeAssistant } from "../../../types"; +import { computeStateDisplay } from "../../../common/entity/compute_state_display"; class MoreInfoHumidifier extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -49,7 +53,14 @@ class MoreInfoHumidifier extends LitElement { })} >
-
${hass.localize("ui.card.humidifier.humidity")}
+
+ ${computeAttributeNameDisplay( + hass.localize, + stateObj, + hass.entities, + "humidity" + )} +
${stateObj.attributes.humidity} %
+ + + ${computeStateDisplay( + hass.localize, + stateObj, + hass.locale, + this.hass.config, + hass.entities, + "off" + )} + + + ${computeStateDisplay( + hass.localize, + stateObj, + hass.locale, + this.hass.config, + hass.entities, + "on" + )} + + ${supportModes ? html` @@ -83,6 +123,7 @@ class MoreInfoHumidifier extends LitElement { hass.localize, stateObj, hass.locale, + this.hass.config, hass.entities, "mode", mode @@ -122,6 +163,16 @@ class MoreInfoHumidifier extends LitElement { ); } + private _handleStateChanged(ev) { + const newVal = ev.target.value || null; + this._callServiceHelper( + this.stateObj!.state, + newVal, + newVal === "on" ? "turn_on" : "turn_off", + {} + ); + } + private _handleModeChanged(ev) { const newVal = ev.target.value || null; this._callServiceHelper( @@ -178,6 +229,11 @@ class MoreInfoHumidifier extends LitElement { ha-select { width: 100%; + margin-top: 8px; + } + + ha-slider { + width: 100%; } .container-humidity .single-row { diff --git a/src/dialogs/more-info/controls/more-info-image.ts b/src/dialogs/more-info/controls/more-info-image.ts new file mode 100644 index 0000000000..d54ed0cf43 --- /dev/null +++ b/src/dialogs/more-info/controls/more-info-image.ts @@ -0,0 +1,40 @@ +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../../../components/ha-camera-stream"; +import { computeImageUrl, ImageEntity } from "../../../data/image"; +import type { HomeAssistant } from "../../../types"; + +@customElement("more-info-image") +class MoreInfoImage extends LitElement { + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: ImageEntity; + + protected render() { + if (!this.hass || !this.stateObj) { + return nothing; + } + return html`${this.stateObj.attributes.friendly_name `; + } + + static get styles(): CSSResultGroup { + return css` + :host { + display: block; + text-align: center; + } + img { + max-width: 100%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "more-info-image": MoreInfoImage; + } +} diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts index f40b41977b..730175c8dd 100644 --- a/src/dialogs/more-info/controls/more-info-light.ts +++ b/src/dialogs/more-info/controls/more-info-light.ts @@ -1,5 +1,6 @@ import "@material/mwc-list/mwc-list-item"; import { + mdiBrightness6, mdiCreation, mdiFileWordBox, mdiLightbulb, @@ -24,6 +25,8 @@ import { supportsFeature } from "../../../common/entity/supports-feature"; import { blankBeforePercent } from "../../../common/translations/blank_before_percent"; import "../../../components/ha-attributes"; import "../../../components/ha-button-menu"; +import "../../../components/ha-icon-button-group"; +import "../../../components/ha-icon-button-toggle"; import "../../../components/ha-outlined-button"; import "../../../components/ha-outlined-icon-button"; import "../../../components/ha-select"; @@ -31,6 +34,7 @@ import { UNAVAILABLE } from "../../../data/entity"; import { ExtEntityRegistryEntry } from "../../../data/entity_registry"; import { forwardHaptic } from "../../../data/haptics"; import { + formatTempColor, LightColorMode, LightEntity, LightEntityFeature, @@ -46,7 +50,10 @@ import "../components/ha-more-info-toggle"; import "../components/lights/ha-favorite-color-button"; import "../components/lights/ha-more-info-light-brightness"; import "../components/lights/ha-more-info-light-favorite-colors"; -import { showLightColorPickerView } from "../components/lights/show-view-light-color-picker"; +import "../components/lights/light-color-rgb-picker"; +import "../components/lights/light-color-temp-picker"; + +type MainControl = "brightness" | "color_temp" | "color"; @customElement("more-info-light") class MoreInfoLight extends LitElement { @@ -62,12 +69,24 @@ class MoreInfoLight extends LitElement { @state() private _selectedBrightness?: number; + @state() private _colorTempPreview?: number; + + @state() private _mainControl: MainControl = "brightness"; + private _brightnessChanged(ev) { const value = (ev.detail as any).value; if (isNaN(value)) return; this._selectedBrightness = value; } + private _tempColorHovered(ev: CustomEvent) { + if (ev.detail && "color_temp_kelvin" in ev.detail) { + this._colorTempPreview = ev.detail.color_temp_kelvin; + } else { + this._colorTempPreview = undefined; + } + } + protected updated(changedProps: PropertyValues): void { if (changedProps.has("stateObj")) { this._selectedBrightness = this.stateObj?.attributes.brightness @@ -77,6 +96,28 @@ class MoreInfoLight extends LitElement { } } + private _setMainControl(ev: any) { + ev.stopPropagation(); + this._mainControl = ev.currentTarget.control; + } + + private _resetMainControl(ev: any) { + ev.stopPropagation(); + this._mainControl = "brightness"; + } + + private get _stateOverride() { + if (this._colorTempPreview) { + return formatTempColor(this._colorTempPreview); + } + if (this._selectedBrightness) { + return `${Math.round(this._selectedBrightness)}${blankBeforePercent( + this.hass!.locale + )}%`; + } + return undefined; + } + protected render() { if (!this.hass || !this.stateObj) { return nothing; @@ -106,47 +147,60 @@ class MoreInfoLight extends LitElement { (this.entry.options?.light?.favorite_colors == null || this.entry.options.light.favorite_colors.length > 0); - const stateOverride = this._selectedBrightness - ? `${Math.round(this._selectedBrightness)}${blankBeforePercent( - this.hass!.locale - )}%` - : undefined; - return html`
- ${supportsBrightness + ${!supportsBrightness ? html` - - - ` - : html` - `} + ` + : nothing} ${supportsColorTemp || supportsColor || supportsBrightness ? html` -
+ ${supportsBrightness && this._mainControl === "brightness" + ? html` + + + ` + : nothing} + ${supportsColor && this._mainControl === "color" + ? html` + + + ` + : nothing} + ${supportsColorTemp && this._mainControl === "color_temp" + ? html` + + + ` + : nothing} + ${supportsBrightness ? html` ` : nothing} + ${supportsColor || supportsColorTemp + ? html` +
+ + + + ` + : nothing} ${supportsColor ? html` - - + ` : nothing} ${supportsColorTemp ? html` - - + ` : nothing} ${supportsWhite ? html` +
` : nothing} -
+ ${this.entry && lightSupportsFavoriteColors(this.stateObj) && (this.editMode || hasFavoriteColors) @@ -216,6 +281,7 @@ class MoreInfoLight extends LitElement { .stateObj=${this.stateObj} .entry=${this.entry} .editMode=${this.editMode} + @favorite-color-edit-started=${this._resetMainControl} > ` @@ -240,6 +306,7 @@ class MoreInfoLight extends LitElement { this.hass.localize, this.stateObj!, this.hass.locale, + this.hass.config, this.hass.entities, "effect", this._effect @@ -261,6 +328,7 @@ class MoreInfoLight extends LitElement { this.hass.localize, this.stateObj!, this.hass.locale, + this.hass.config, this.hass.entities, "effect", effect @@ -289,19 +357,6 @@ class MoreInfoLight extends LitElement { }); }; - private _showLightColorPickerView = (ev) => { - showLightColorPickerView( - this, - this.hass.localize( - "ui.dialogs.more_info_control.light.color_picker.title" - ), - { - entityId: this.stateObj!.entity_id, - defaultMode: ev.currentTarget.mode, - } - ); - }; - private _setWhite = () => { this.hass.callService("light", "turn_on", { entity_id: this.stateObj!.entity_id, @@ -345,9 +400,6 @@ class MoreInfoLight extends LitElement { flex: none; border-radius: 15px; } - ha-icon-button[disabled] .wheel { - filter: grayscale(1) opacity(0.5); - } .wheel.color { background-image: url("/static/images/color_wheel.png"); background-size: cover; @@ -360,6 +412,9 @@ class MoreInfoLight extends LitElement { rgb(255, 160, 0) 100% ); } + *[disabled] .wheel { + filter: grayscale(1) opacity(0.5); + } .buttons { flex-wrap: wrap; max-width: 250px; diff --git a/src/dialogs/more-info/controls/more-info-lock.ts b/src/dialogs/more-info/controls/more-info-lock.ts index 33afb0e93f..5fa6ec5df3 100644 --- a/src/dialogs/more-info/controls/more-info-lock.ts +++ b/src/dialogs/more-info/controls/more-info-lock.ts @@ -1,43 +1,156 @@ -import "@material/mwc-button"; -import type { HassEntity } from "home-assistant-js-websocket"; -import { css, html, LitElement, nothing } from "lit"; -import { customElement, property, query } from "lit/decorators"; +import "@material/web/iconbutton/outlined-icon-button"; +import { mdiDoorOpen, mdiLock, mdiLockOff } from "@mdi/js"; +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; +import { domainIcon } from "../../../common/entity/domain_icon"; +import { stateColorCss } from "../../../common/entity/state_color"; +import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-attributes"; -import "../../../components/ha-textfield"; -import type { HaTextField } from "../../../components/ha-textfield"; +import { UNAVAILABLE } from "../../../data/entity"; +import { LockEntity, LockEntityFeature } from "../../../data/lock"; import type { HomeAssistant } from "../../../types"; +import { showEnterCodeDialogDialog } from "../../enter-code/show-enter-code-dialog"; +import { moreInfoControlStyle } from "../components/ha-more-info-control-style"; +import "../components/ha-more-info-state-header"; +import "../components/lock/ha-more-info-lock-toggle"; @customElement("more-info-lock") class MoreInfoLock extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public stateObj?: HassEntity; + @property({ attribute: false }) public stateObj?: LockEntity; - @query("ha-textfield") private _textfield?: HaTextField; + private async _open() { + this._callService("open"); + } + + private async _lock() { + this._callService("lock"); + } + + private async _unlock() { + this._callService("unlock"); + } + + private async _callService(service: "open" | "lock" | "unlock") { + let code: string | undefined; + + if (this.stateObj!.attributes.code_format) { + const response = await showEnterCodeDialogDialog(this, { + codeFormat: "text", + codePattern: this.stateObj!.attributes.code_format, + title: this.hass.localize( + `ui.dialogs.more_info_control.lock.${service}` + ), + submitText: this.hass.localize( + `ui.dialogs.more_info_control.lock.${service}` + ), + }); + if (!response) { + return; + } + code = response; + } + + this.hass.callService("lock", service, { + entity_id: this.stateObj!.entity_id, + code, + }); + } protected render() { if (!this.hass || !this.stateObj) { return nothing; } + + const supportsOpen = supportsFeature(this.stateObj, LockEntityFeature.OPEN); + + const color = stateColorCss(this.stateObj); + const style = { + "--icon-color": color, + }; + + const isJammed = this.stateObj.state === "jammed"; + return html` - ${this.stateObj.attributes.code_format - ? html`
- - ${this.stateObj.state === "locked" - ? html`${this.hass.localize("ui.card.lock.unlock")}` - : html`${this.hass.localize("ui.card.lock.lock")}`} -
` - : ""} + +
+ ${ + this.stateObj.state === "jammed" + ? html` +
+ +
+ +
+
+ ` + : html` + + + ` + } + ${ + supportsOpen || isJammed + ? html` +
+ ${supportsOpen + ? html` + + + + ` + : nothing} + ${isJammed + ? html` + + + + + + + ` + : nothing} +
+ ` + : nothing + } +
+
${formatTime( item === "ris" ? risingDate : settingDate, - this.hass.locale + this.hass.locale, + this.hass.config )}
diff --git a/src/dialogs/more-info/controls/more-info-time.ts b/src/dialogs/more-info/controls/more-info-time.ts index c131f7e3de..8e5a99bc34 100644 --- a/src/dialogs/more-info/controls/more-info-time.ts +++ b/src/dialogs/more-info/controls/more-info-time.ts @@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/ha-date-input"; import "../../../components/ha-time-input"; -import { isUnavailableState } from "../../../data/entity"; +import { isUnavailableState, UNAVAILABLE } from "../../../data/entity"; import { setTimeValue } from "../../../data/time"; import type { HomeAssistant } from "../../../types"; @@ -14,15 +14,17 @@ class MoreInfoTime extends LitElement { @property({ attribute: false }) public stateObj?: HassEntity; protected render() { - if (!this.stateObj || isUnavailableState(this.stateObj.state)) { + if (!this.stateObj || this.stateObj.state === UNAVAILABLE) { return nothing; } return html` @@ -34,7 +36,9 @@ class MoreInfoTime extends LitElement { } private _timeChanged(ev: CustomEvent<{ value: string }>): void { - setTimeValue(this.hass!, this.stateObj!.entity_id, ev.detail.value); + if (ev.detail.value) { + setTimeValue(this.hass!, this.stateObj!.entity_id, ev.detail.value); + } } static get styles(): CSSResultGroup { diff --git a/src/dialogs/more-info/controls/more-info-vacuum.ts b/src/dialogs/more-info/controls/more-info-vacuum.ts index 652a3eb785..c470b33cba 100644 --- a/src/dialogs/more-info/controls/more-info-vacuum.ts +++ b/src/dialogs/more-info/controls/more-info-vacuum.ts @@ -105,34 +105,34 @@ class MoreInfoVacuum extends LitElement { return html` ${stateObj.state !== UNAVAILABLE ? html`
- ${supportsFeature(stateObj, VacuumEntityFeature.STATUS) - ? html` -
- ${this.hass!.localize( - "ui.dialogs.more_info_control.vacuum.status" - )}: - - - - ${computeAttributeValueDisplay( - this.hass.localize, - stateObj, - this.hass.locale, - this.hass.entities, - "status" - ) || - computeStateDisplay( - this.hass.localize, - stateObj, - this.hass.locale, - this.hass.entities - )} - - -
- ` - : ""} +
+ ${this.hass!.localize( + "ui.dialogs.more_info_control.vacuum.status" + )}: + + + + ${supportsFeature(stateObj, VacuumEntityFeature.STATUS) && + stateObj.attributes.status + ? computeAttributeValueDisplay( + this.hass.localize, + stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities, + "status" + ) + : computeStateDisplay( + this.hass.localize, + stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities + )} + + +
${supportsFeature(stateObj, VacuumEntityFeature.BATTERY) && stateObj.attributes.battery_level ? html` @@ -201,6 +201,7 @@ class MoreInfoVacuum extends LitElement { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities, "fan_speed", mode @@ -218,6 +219,7 @@ class MoreInfoVacuum extends LitElement { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities, "fan_speed" )} diff --git a/src/dialogs/more-info/controls/more-info-weather.ts b/src/dialogs/more-info/controls/more-info-weather.ts index 0b4a80ad93..628886339d 100644 --- a/src/dialogs/more-info/controls/more-info-weather.ts +++ b/src/dialogs/more-info/controls/more-info-weather.ts @@ -164,7 +164,8 @@ class MoreInfoWeather extends LitElement {
${formatTimeWeekday( new Date(item.datetime), - this.hass.locale + this.hass.locale, + this.hass.config )}
` @@ -172,7 +173,8 @@ class MoreInfoWeather extends LitElement {
${formatDateWeekdayDay( new Date(item.datetime), - this.hass.locale + this.hass.locale, + this.hass.config )}
`} diff --git a/src/dialogs/more-info/more-info-content.ts b/src/dialogs/more-info/more-info-content.ts index ead8379b1e..3783851c91 100644 --- a/src/dialogs/more-info/more-info-content.ts +++ b/src/dialogs/more-info/more-info-content.ts @@ -55,6 +55,9 @@ class MoreInfoContent extends ReactiveElement { } if (!moreInfoType) { + if (this.lastChild) { + this.removeChild(this.lastChild); + } return; } diff --git a/src/dialogs/more-info/state_more_info_control.ts b/src/dialogs/more-info/state_more_info_control.ts index 7c85f0db71..cc0d07eb67 100644 --- a/src/dialogs/more-info/state_more_info_control.ts +++ b/src/dialogs/more-info/state_more_info_control.ts @@ -18,6 +18,7 @@ const LAZY_LOADED_MORE_INFO_CONTROL = { fan: () => import("./controls/more-info-fan"), group: () => import("./controls/more-info-group"), humidifier: () => import("./controls/more-info-humidifier"), + image: () => import("./controls/more-info-image"), input_boolean: () => import("./controls/more-info-input_boolean"), input_datetime: () => import("./controls/more-info-input_datetime"), light: () => import("./controls/more-info-light"), diff --git a/src/dialogs/notifications/configurator-notification-item.ts b/src/dialogs/notifications/configurator-notification-item.ts index 47c7be0750..96174e02a1 100644 --- a/src/dialogs/notifications/configurator-notification-item.ts +++ b/src/dialogs/notifications/configurator-notification-item.ts @@ -38,6 +38,7 @@ export class HuiConfiguratorNotificationItem extends LitElement { this.hass.localize, this.notification, this.hass.locale, + this.hass.config, this.hass.entities )} diff --git a/src/dialogs/notifications/notification-drawer.ts b/src/dialogs/notifications/notification-drawer.ts index 5b4eacb27e..b11a45a345 100644 --- a/src/dialogs/notifications/notification-drawer.ts +++ b/src/dialogs/notifications/notification-drawer.ts @@ -139,11 +139,7 @@ export class HuiNotificationDrawer extends LitElement { } private _dismissAll() { - this._notifications.forEach((notification) => { - this.hass.callService("persistent_notification", "dismiss", { - notification_id: notification.notification_id, - }); - }); + this.hass.callService("persistent_notification", "dismiss_all"); this.closeDialog(); } diff --git a/src/dialogs/notifications/persistent-notification-item.ts b/src/dialogs/notifications/persistent-notification-item.ts index 310a6b93b9..40ce7e6b2f 100644 --- a/src/dialogs/notifications/persistent-notification-item.ts +++ b/src/dialogs/notifications/persistent-notification-item.ts @@ -82,7 +82,7 @@ export class HuiPersistentNotificationItem extends LitElement { } const d = new Date(notification.created_at!); - return formatDateTime(d, hass.locale); + return formatDateTime(d, hass.locale, hass.config); } } diff --git a/src/dialogs/tts-try/dialog-tts-try.ts b/src/dialogs/tts-try/dialog-tts-try.ts index 113b5345ca..0854133bba 100644 --- a/src/dialogs/tts-try/dialog-tts-try.ts +++ b/src/dialogs/tts-try/dialog-tts-try.ts @@ -1,7 +1,7 @@ import { mdiPlayCircleOutline } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; -import { LocalStorage } from "../../common/decorators/local-storage"; +import { storage } from "../../common/decorators/storage"; import { fireEvent } from "../../common/dom/fire_event"; import "../../components/ha-button"; import { createCloseHeading } from "../../components/ha-dialog"; @@ -25,10 +25,12 @@ export class TTSTryDialog extends LitElement { @query("#message") private _messageInput?: HaTextArea; - @LocalStorage("ttsTryMessages", false, false) private _messages?: Record< - string, - string - >; + @storage({ + key: "ttsTryMessages", + state: false, + subscribe: false, + }) + private _messages?: Record; public showDialog(params: TTSTryDialogParams) { this._params = params; diff --git a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts index e0ec5db5de..de68790067 100644 --- a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts +++ b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts @@ -18,16 +18,16 @@ import { TemplateResult, } from "lit"; import { customElement, property, query, state } from "lit/decorators"; -import { LocalStorage } from "../../common/decorators/local-storage"; +import { storage } from "../../common/decorators/storage"; import { fireEvent } from "../../common/dom/fire_event"; import { stopPropagation } from "../../common/dom/stop_propagation"; import "../../components/ha-button"; import "../../components/ha-button-menu"; import "../../components/ha-dialog"; +import "../../components/ha-dialog-header"; import "../../components/ha-icon-button"; import "../../components/ha-list-item"; import "../../components/ha-textfield"; -import "../../components/ha-dialog-header"; import type { HaTextField } from "../../components/ha-textfield"; import { AssistPipeline, @@ -35,12 +35,12 @@ import { listAssistPipelines, runAssistPipeline, } from "../../data/assist_pipeline"; -import { AgentInfo, getAgentInfo } from "../../data/conversation"; import { haStyleDialog } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; import { AudioRecorder } from "../../util/audio-recorder"; import { documentationUrl } from "../../util/documentation-url"; import { showAlertDialog } from "../generic/show-dialog-box"; +import { VoiceCommandDialogParams } from "./show-ha-voice-command-dialog"; interface Message { who: string; @@ -56,12 +56,15 @@ export class HaVoiceCommandDialog extends LitElement { @state() private _opened = false; - @LocalStorage("AssistPipelineId", true, false) private _pipelineId?: string; + @storage({ + key: "AssistPipelineId", + state: true, + subscribe: false, + }) + private _pipelineId?: string; @state() private _pipeline?: AssistPipeline; - @state() private _agentInfo?: AgentInfo; - @state() private _showSendButton = false; @state() private _pipelines?: AssistPipeline[]; @@ -82,7 +85,13 @@ export class HaVoiceCommandDialog extends LitElement { private _stt_binary_handler_id?: number | null; - public async showDialog(): Promise { + private _pipelinePromise?: Promise; + + public async showDialog(params?: VoiceCommandDialogParams): Promise { + if (params?.pipeline_id) { + this._pipelineId = params?.pipeline_id; + } + this._conversation = [ { who: "hass", @@ -92,13 +101,17 @@ export class HaVoiceCommandDialog extends LitElement { this._opened = true; await this.updateComplete; this._scrollMessagesBottom(); + + await this._pipelinePromise; + if (params?.start_listening && this._pipeline?.stt_engine) { + this._toggleListening(); + } } public async closeDialog(): Promise { this._opened = false; this._pipeline = undefined; this._pipelines = undefined; - this._agentInfo = undefined; this._conversation = undefined; this._conversationId = null; this._audioRecorder?.close(); @@ -230,7 +243,7 @@ export class HaVoiceCommandDialog extends LitElement {
- ${this._agentInfo && this._agentInfo.attribution - ? html` - ${this._agentInfo.attribution.name} - ` - : ""}
`; @@ -275,17 +277,13 @@ export class HaVoiceCommandDialog extends LitElement { private async _getPipeline() { try { - this._pipeline = await getAssistPipeline(this.hass, this._pipelineId); + this._pipelinePromise = getAssistPipeline(this.hass, this._pipelineId); + this._pipeline = await this._pipelinePromise; } catch (e: any) { if (e.code === "not_found") { this._pipelineId = undefined; } - return; } - this._agentInfo = await getAgentInfo( - this.hass, - this._pipeline.conversation_engine - ); } private async _loadPipelines() { @@ -392,9 +390,13 @@ export class HaVoiceCommandDialog extends LitElement { } } - private _toggleListening(ev) { + private _handleListeningButton(ev) { ev.stopPropagation(); ev.preventDefault(); + this._toggleListening(); + } + + private _toggleListening() { const supportsMicrophone = AudioRecorder.isSupported; if (!supportsMicrophone) { this._showNotSupportedMessage(); @@ -706,12 +708,6 @@ export class HaVoiceCommandDialog extends LitElement { flex: 1 0; padding: 4px; } - .attribution { - display: block; - color: var(--secondary-text-color); - padding-top: 4px; - margin-bottom: -8px; - } .messages { display: block; height: 400px; diff --git a/src/dialogs/voice-command-dialog/show-ha-voice-command-dialog.ts b/src/dialogs/voice-command-dialog/show-ha-voice-command-dialog.ts index fe446391dd..c755877cd3 100644 --- a/src/dialogs/voice-command-dialog/show-ha-voice-command-dialog.ts +++ b/src/dialogs/voice-command-dialog/show-ha-voice-command-dialog.ts @@ -3,19 +3,29 @@ import { HomeAssistant } from "../../types"; const loadVoiceCommandDialog = () => import("./ha-voice-command-dialog"); +export interface VoiceCommandDialogParams { + pipeline_id?: string; + start_listening?: boolean; +} + export const showVoiceCommandDialog = ( element: HTMLElement, - hass: HomeAssistant + hass: HomeAssistant, + dialogParams?: VoiceCommandDialogParams ): void => { if (hass.auth.external?.config.hasAssist) { hass.auth.external!.fireMessage({ type: "assist/show", + payload: { + pipeline_id: dialogParams?.pipeline_id, + start_listening: dialogParams?.start_listening, + }, }); return; } fireEvent(element, "show-dialog", { dialogTag: "ha-voice-command-dialog", dialogImport: loadVoiceCommandDialog, - dialogParams: {}, + dialogParams, }); }; diff --git a/src/entrypoints/core.ts b/src/entrypoints/core.ts index 74cfd72a5f..cc92ddd5bc 100644 --- a/src/entrypoints/core.ts +++ b/src/entrypoints/core.ts @@ -133,7 +133,15 @@ window.hassConnection.then(({ conn }) => { }); window.addEventListener("error", (e) => { - if (!__DEV__ && e.message === "ResizeObserver loop limit exceeded") { + if ( + !__DEV__ && + typeof e.message === "string" && + (e.message.includes("ResizeObserver loop limit exceeded") || + e.message.includes( + "ResizeObserver loop completed with undelivered notifications" + )) + ) { + e.preventDefault(); e.stopImmediatePropagation(); e.stopPropagation(); return; diff --git a/src/external_app/external_auth.ts b/src/external_app/external_auth.ts index 26b1f1f892..c1f0674278 100644 --- a/src/external_app/external_auth.ts +++ b/src/external_app/external_auth.ts @@ -131,7 +131,7 @@ export class ExternalAuth extends Auth { export const createExternalAuth = async (hassUrl: string) => { const auth = new ExternalAuth(hassUrl); if ( - (window.externalApp && window.externalApp.externalBus) || + window.externalApp?.externalBus || (window.webkit && window.webkit.messageHandlers.externalBus) ) { auth.external = new ExternalMessaging(); diff --git a/src/fake_data/provide_hass.ts b/src/fake_data/provide_hass.ts index 6116fbc015..3da61ed4fe 100644 --- a/src/fake_data/provide_hass.ts +++ b/src/fake_data/provide_hass.ts @@ -11,6 +11,7 @@ import { NumberFormat, DateFormat, TimeFormat, + TimeZone, } from "../data/translation"; import { translationMetadata } from "../resources/translations-metadata"; import { HomeAssistant } from "../types"; @@ -230,6 +231,7 @@ export const provideHass = ( number_format: NumberFormat.language, time_format: TimeFormat.language, date_format: DateFormat.language, + time_zone: TimeZone.local, first_weekday: FirstWeekday.language, }, resources: null as any, diff --git a/src/layouts/hass-subpage.ts b/src/layouts/hass-subpage.ts index 5c2d73436e..75decf778a 100644 --- a/src/layouts/hass-subpage.ts +++ b/src/layouts/hass-subpage.ts @@ -13,6 +13,7 @@ import { computeRTL } from "../common/util/compute_rtl"; import "../components/ha-icon-button-arrow-prev"; import "../components/ha-menu-button"; import { HomeAssistant } from "../types"; +import { haStyleScrollbar } from "../resources/styles"; @customElement("hass-subpage") class HassSubpage extends LitElement { @@ -73,7 +74,9 @@ class HassSubpage extends LitElement {
${this.header}
-
+
+ +
@@ -94,88 +97,91 @@ class HassSubpage extends LitElement { } static get styles(): CSSResultGroup { - return css` - :host { - display: block; - height: 100%; - background-color: var(--primary-background-color); - overflow: hidden; - position: relative; - } - - :host([narrow]) { - width: 100%; - position: fixed; - } - - .toolbar { - display: flex; - align-items: center; - font-size: 20px; - height: var(--header-height); - padding: 8px 12px; - pointer-events: none; - background-color: var(--app-header-background-color); - font-weight: 400; - color: var(--app-header-text-color, white); - border-bottom: var(--app-header-border-bottom, none); - box-sizing: border-box; - } - @media (max-width: 599px) { - .toolbar { - padding: 4px; + return [ + haStyleScrollbar, + css` + :host { + display: block; + height: 100%; + background-color: var(--primary-background-color); + overflow: hidden; + position: relative; } - } - .toolbar a { - color: var(--sidebar-text-color); - text-decoration: none; - } - ha-menu-button, - ha-icon-button-arrow-prev, - ::slotted([slot="toolbar-icon"]) { - pointer-events: auto; - color: var(--sidebar-icon-color); - } + :host([narrow]) { + width: 100%; + position: fixed; + } - .main-title { - margin: 0 0 0 24px; - line-height: 20px; - flex-grow: 1; - } + .toolbar { + display: flex; + align-items: center; + font-size: 20px; + height: var(--header-height); + padding: 8px 12px; + pointer-events: none; + background-color: var(--app-header-background-color); + font-weight: 400; + color: var(--app-header-text-color, white); + border-bottom: var(--app-header-border-bottom, none); + box-sizing: border-box; + } + @media (max-width: 599px) { + .toolbar { + padding: 4px; + } + } + .toolbar a { + color: var(--sidebar-text-color); + text-decoration: none; + } - .content { - position: relative; - width: 100%; - height: calc(100% - 1px - var(--header-height)); - overflow-y: auto; - overflow: auto; - -webkit-overflow-scrolling: touch; - } + ha-menu-button, + ha-icon-button-arrow-prev, + ::slotted([slot="toolbar-icon"]) { + pointer-events: auto; + color: var(--sidebar-icon-color); + } - #fab { - position: absolute; - right: calc(16px + env(safe-area-inset-right)); - bottom: calc(16px + env(safe-area-inset-bottom)); - z-index: 1; - } - :host([narrow]) #fab.tabs { - bottom: calc(84px + env(safe-area-inset-bottom)); - } - #fab[is-wide] { - bottom: 24px; - right: 24px; - } - :host([rtl]) #fab { - right: auto; - left: calc(16px + env(safe-area-inset-left)); - } - :host([rtl][is-wide]) #fab { - bottom: 24px; - left: 24px; - right: auto; - } - `; + .main-title { + margin: 0 0 0 24px; + line-height: 20px; + flex-grow: 1; + } + + .content { + position: relative; + width: 100%; + height: calc(100% - 1px - var(--header-height)); + overflow-y: auto; + overflow: auto; + -webkit-overflow-scrolling: touch; + } + + #fab { + position: absolute; + right: calc(16px + env(safe-area-inset-right)); + bottom: calc(16px + env(safe-area-inset-bottom)); + z-index: 1; + } + :host([narrow]) #fab.tabs { + bottom: calc(84px + env(safe-area-inset-bottom)); + } + #fab[is-wide] { + bottom: 24px; + right: 24px; + } + :host([rtl]) #fab { + right: auto; + left: calc(16px + env(safe-area-inset-left)); + } + :host([rtl][is-wide]) #fab { + bottom: 24px; + left: 24px; + right: auto; + } + `, + ]; } } diff --git a/src/layouts/hass-tabs-subpage.ts b/src/layouts/hass-tabs-subpage.ts index 08da982715..bbc978fd7d 100644 --- a/src/layouts/hass-tabs-subpage.ts +++ b/src/layouts/hass-tabs-subpage.ts @@ -19,6 +19,7 @@ import "../components/ha-menu-button"; import "../components/ha-svg-icon"; import "../components/ha-tab"; import { HomeAssistant, Route } from "../types"; +import { haStyleScrollbar } from "../resources/styles"; export interface PageNavigation { path: string; @@ -186,7 +187,7 @@ class HassTabsSubpage extends LitElement {
@@ -211,143 +212,146 @@ class HassTabsSubpage extends LitElement { } static get styles(): CSSResultGroup { - return css` - :host { - display: block; - height: 100%; - background-color: var(--primary-background-color); - } - - :host([narrow]) { - width: 100%; - position: fixed; - } - - ha-menu-button { - margin-right: 24px; - } - - .toolbar { - display: flex; - align-items: center; - font-size: 20px; - height: var(--header-height); - background-color: var(--sidebar-background-color); - font-weight: 400; - border-bottom: 1px solid var(--divider-color); - padding: 8px 12px; - box-sizing: border-box; - } - @media (max-width: 599px) { - .toolbar { - padding: 4px; + return [ + haStyleScrollbar, + css` + :host { + display: block; + height: 100%; + background-color: var(--primary-background-color); } - } - .toolbar a { - color: var(--sidebar-text-color); - text-decoration: none; - } - .bottom-bar a { - width: 25%; - } - #tabbar { - display: flex; - font-size: 14px; - overflow: hidden; - } + :host([narrow]) { + width: 100%; + position: fixed; + } - #tabbar > a { - overflow: hidden; - max-width: 45%; - } + ha-menu-button { + margin-right: 24px; + } - #tabbar.bottom-bar { - position: absolute; - bottom: 0; - left: 0; - padding: 0 16px; - box-sizing: border-box; - background-color: var(--sidebar-background-color); - border-top: 1px solid var(--divider-color); - justify-content: space-around; - z-index: 2; - font-size: 12px; - width: 100%; - padding-bottom: env(safe-area-inset-bottom); - } + .toolbar { + display: flex; + align-items: center; + font-size: 20px; + height: var(--header-height); + background-color: var(--sidebar-background-color); + font-weight: 400; + border-bottom: 1px solid var(--divider-color); + padding: 8px 12px; + box-sizing: border-box; + } + @media (max-width: 599px) { + .toolbar { + padding: 4px; + } + } + .toolbar a { + color: var(--sidebar-text-color); + text-decoration: none; + } + .bottom-bar a { + width: 25%; + } - #tabbar:not(.bottom-bar) { - flex: 1; - justify-content: center; - } + #tabbar { + display: flex; + font-size: 14px; + overflow: hidden; + } - :host(:not([narrow])) #toolbar-icon { - min-width: 40px; - } + #tabbar > a { + overflow: hidden; + max-width: 45%; + } - ha-menu-button, - ha-icon-button-arrow-prev, - ::slotted([slot="toolbar-icon"]) { - display: flex; - flex-shrink: 0; - pointer-events: auto; - color: var(--sidebar-icon-color); - } + #tabbar.bottom-bar { + position: absolute; + bottom: 0; + left: 0; + padding: 0 16px; + box-sizing: border-box; + background-color: var(--sidebar-background-color); + border-top: 1px solid var(--divider-color); + justify-content: space-around; + z-index: 2; + font-size: 12px; + width: 100%; + padding-bottom: env(safe-area-inset-bottom); + } - .main-title { - flex: 1; - max-height: var(--header-height); - line-height: 20px; - color: var(--sidebar-text-color); - margin: var(--main-title-margin, 0 0 0 24px); - } + #tabbar:not(.bottom-bar) { + flex: 1; + justify-content: center; + } - .content { - position: relative; - width: calc( - 100% - env(safe-area-inset-left) - env(safe-area-inset-right) - ); - margin-left: env(safe-area-inset-left); - margin-right: env(safe-area-inset-right); - height: calc(100% - 1px - var(--header-height)); - height: calc( - 100% - 1px - var(--header-height) - env(safe-area-inset-bottom) - ); - overflow: auto; - -webkit-overflow-scrolling: touch; - } + :host(:not([narrow])) #toolbar-icon { + min-width: 40px; + } - :host([narrow]) .content.tabs { - height: calc(100% - 2 * var(--header-height)); - height: calc( - 100% - 2 * var(--header-height) - env(safe-area-inset-bottom) - ); - } + ha-menu-button, + ha-icon-button-arrow-prev, + ::slotted([slot="toolbar-icon"]) { + display: flex; + flex-shrink: 0; + pointer-events: auto; + color: var(--sidebar-icon-color); + } - #fab { - position: fixed; - right: calc(16px + env(safe-area-inset-right)); - bottom: calc(16px + env(safe-area-inset-bottom)); - z-index: 1; - } - :host([narrow]) #fab.tabs { - bottom: calc(84px + env(safe-area-inset-bottom)); - } - #fab[is-wide] { - bottom: 24px; - right: 24px; - } - :host([rtl]) #fab { - right: auto; - left: calc(16px + env(safe-area-inset-left)); - } - :host([rtl][is-wide]) #fab { - bottom: 24px; - left: 24px; - right: auto; - } - `; + .main-title { + flex: 1; + max-height: var(--header-height); + line-height: 20px; + color: var(--sidebar-text-color); + margin: var(--main-title-margin, 0 0 0 24px); + } + + .content { + position: relative; + width: calc( + 100% - env(safe-area-inset-left) - env(safe-area-inset-right) + ); + margin-left: env(safe-area-inset-left); + margin-right: env(safe-area-inset-right); + height: calc(100% - 1px - var(--header-height)); + height: calc( + 100% - 1px - var(--header-height) - env(safe-area-inset-bottom) + ); + overflow: auto; + -webkit-overflow-scrolling: touch; + } + + :host([narrow]) .content.tabs { + height: calc(100% - 2 * var(--header-height)); + height: calc( + 100% - 2 * var(--header-height) - env(safe-area-inset-bottom) + ); + } + + #fab { + position: fixed; + right: calc(16px + env(safe-area-inset-right)); + bottom: calc(16px + env(safe-area-inset-bottom)); + z-index: 1; + } + :host([narrow]) #fab.tabs { + bottom: calc(84px + env(safe-area-inset-bottom)); + } + #fab[is-wide] { + bottom: 24px; + right: 24px; + } + :host([rtl]) #fab { + right: auto; + left: calc(16px + env(safe-area-inset-left)); + } + :host([rtl][is-wide]) #fab { + bottom: 24px; + left: 24px; + right: auto; + } + `, + ]; } } diff --git a/src/panels/calendar/dialog-calendar-event-detail.ts b/src/panels/calendar/dialog-calendar-event-detail.ts index aba011143a..6172e79b4c 100644 --- a/src/panels/calendar/dialog-calendar-event-detail.ts +++ b/src/panels/calendar/dialog-calendar-event-detail.ts @@ -154,23 +154,28 @@ class DialogCalendarEventDetail extends LitElement { if (isSameDay(start, end)) { if (isDate(this._data.dtstart)) { // Single date string only - return formatDate(start, this.hass.locale); + return formatDate(start, this.hass.locale, this.hass.config); } // Single day with a start/end time range - return `${formatDate(start, this.hass.locale)} ${formatTime( + return `${formatDate( start, - this.hass.locale - )} - ${formatTime(end, this.hass.locale)}`; + this.hass.locale, + this.hass.config + )} ${formatTime( + start, + this.hass.locale, + this.hass.config + )} - ${formatTime(end, this.hass.locale, this.hass.config)}`; } // An event across multiple dates, optionally with a time range return `${ isDate(this._data.dtstart) - ? formatDate(start, this.hass.locale) - : formatDateTime(start, this.hass.locale) + ? formatDate(start, this.hass.locale, this.hass.config) + : formatDateTime(start, this.hass.locale, this.hass.config) } - ${ isDate(this._data.dtend) - ? formatDate(end, this.hass.locale) - : formatDateTime(end, this.hass.locale) + ? formatDate(end, this.hass.locale, this.hass.config) + : formatDateTime(end, this.hass.locale, this.hass.config) }`; } diff --git a/src/panels/calendar/ha-panel-calendar.ts b/src/panels/calendar/ha-panel-calendar.ts index 43c824eff9..878d6348fa 100644 --- a/src/panels/calendar/ha-panel-calendar.ts +++ b/src/panels/calendar/ha-panel-calendar.ts @@ -11,7 +11,7 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { styleMap } from "lit/directives/style-map"; -import { LocalStorage } from "../../common/decorators/local-storage"; +import { storage } from "../../common/decorators/storage"; import { HASSDomEvent } from "../../common/dom/fire_event"; import { computeStateName } from "../../common/entity/compute_state_name"; import "../../components/ha-card"; @@ -41,7 +41,10 @@ class PanelCalendar extends LitElement { @state() private _error?: string = undefined; - @LocalStorage("deSelectedCalendars", true) + @storage({ + key: "deSelectedCalendars", + state: true, + }) private _deSelectedCalendars: string[] = []; private _start?: Date; diff --git a/src/panels/calendar/recurrence.ts b/src/panels/calendar/recurrence.ts index ddc1d45729..1a502c9678 100644 --- a/src/panels/calendar/recurrence.ts +++ b/src/panels/calendar/recurrence.ts @@ -247,8 +247,8 @@ export function renderRRuleAsText(hass: HomeAssistant, value: string) { return ""; }, { - dayNames: dayNames(hass.locale), - monthNames: monthNames(hass.locale), + dayNames: dayNames(hass.locale, hass.config), + monthNames: monthNames(hass.locale, hass.config), tokens: {}, }, // Format the date @@ -263,9 +263,9 @@ export function renderRRuleAsText(hass: HomeAssistant, value: string) { // need to convert it back to something Date can work with. The already localized // months names are a must in the RRule.Language structure (an empty string[] would // mean we get undefined months input in this method here). - date.setMonth(monthNames(hass.locale).indexOf(month)); + date.setMonth(monthNames(hass.locale, hass.config).indexOf(month)); date.setDate(day); - return formatDate(date, hass.locale); + return formatDate(date, hass.locale, hass.config); } ) ); diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts index 0819a14287..b601f9906e 100644 --- a/src/panels/config/automation/action/ha-automation-action-row.ts +++ b/src/panels/config/automation/action/ha-automation-action-row.ts @@ -1,10 +1,11 @@ import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { + mdiAlertCircleCheck, mdiCheck, - mdiContentDuplicate, mdiContentCopy, mdiContentCut, + mdiContentDuplicate, mdiDelete, mdiDotsVertical, mdiPlay, @@ -13,10 +14,19 @@ import { mdiSort, mdiStopCircleOutline, } from "@mdi/js"; +import deepClone from "deep-clone-simple"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; -import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; +import { + CSSResultGroup, + LitElement, + PropertyValues, + css, + html, + nothing, +} from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import { storage } from "../../../../common/decorators/storage"; import { dynamicElement } from "../../../../common/dom/dynamic-element-directive"; import { fireEvent } from "../../../../common/dom/fire_event"; import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter"; @@ -28,13 +38,17 @@ import "../../../../components/ha-expansion-panel"; import "../../../../components/ha-icon-button"; import type { HaYamlEditor } from "../../../../components/ha-yaml-editor"; import { ACTION_TYPES, YAML_ONLY_ACTION_TYPES } from "../../../../data/action"; +import { AutomationClipboard } from "../../../../data/automation"; import { validateConfig } from "../../../../data/config"; import { EntityRegistryEntry, subscribeEntityRegistry, } from "../../../../data/entity_registry"; -import { Clipboard } from "../../../../data/automation"; -import { Action, getActionType } from "../../../../data/script"; +import { + Action, + NonConditionAction, + getActionType, +} from "../../../../data/script"; import { describeAction } from "../../../../data/script_i18n"; import { callExecuteScript } from "../../../../data/service"; import { @@ -115,7 +129,13 @@ export default class HaAutomationActionRow extends LitElement { @property({ type: Boolean }) public reOrderMode = false; - @property() public clipboard?: Clipboard; + @storage({ + key: "automationClipboard", + state: false, + subscribe: true, + storage: "sessionStorage", + }) + public _clipboard?: AutomationClipboard; @state() private _entityReg: EntityRegistryEntry[] = []; @@ -184,6 +204,17 @@ export default class HaAutomationActionRow extends LitElement { + ${type !== "condition" && + (this.action as NonConditionAction).continue_on_error === true + ? html`
+ + + ${this.hass.localize( + "ui.panel.config.automation.editor.actions.continue_on_error" + )} + +
` + : nothing} ${this.hideMenu ? "" : html` @@ -373,7 +404,6 @@ export default class HaAutomationActionRow extends LitElement { narrow: this.narrow, reOrderMode: this.reOrderMode, disabled: this.disabled, - clipboard: this.clipboard, })}
`} @@ -408,10 +438,10 @@ export default class HaAutomationActionRow extends LitElement { fireEvent(this, "duplicate"); break; case 4: - fireEvent(this, "set-clipboard", { action: this.action }); + this._setClipboard(); break; case 5: - fireEvent(this, "set-clipboard", { action: this.action }); + this._setClipboard(); fireEvent(this, "value-changed", { value: null }); break; case 6: @@ -431,6 +461,13 @@ export default class HaAutomationActionRow extends LitElement { } } + private _setClipboard() { + this._clipboard = { + ...this._clipboard, + action: deepClone(this.action), + }; + } + private _onDisable() { const enabled = !(this.action.enabled ?? true); const value = { ...this.action, enabled }; diff --git a/src/panels/config/automation/action/ha-automation-action.ts b/src/panels/config/automation/action/ha-automation-action.ts index 91a6df75ae..dd9d2ad909 100644 --- a/src/panels/config/automation/action/ha-automation-action.ts +++ b/src/panels/config/automation/action/ha-automation-action.ts @@ -29,7 +29,7 @@ import type { HaSelect } from "../../../../components/ha-select"; import "../../../../components/ha-svg-icon"; import { ACTION_TYPES } from "../../../../data/action"; import { Action } from "../../../../data/script"; -import { Clipboard } from "../../../../data/automation"; +import { AutomationClipboard } from "../../../../data/automation"; import { sortableStyles } from "../../../../resources/ha-sortable-style"; import { loadSortable, @@ -52,6 +52,7 @@ import "./types/ha-automation-action-service"; import "./types/ha-automation-action-stop"; import "./types/ha-automation-action-wait_for_trigger"; import "./types/ha-automation-action-wait_template"; +import { storage } from "../../../../common/decorators/storage"; const PASTE_VALUE = "__paste__"; @@ -69,7 +70,13 @@ export default class HaAutomationAction extends LitElement { @property({ type: Boolean }) public reOrderMode = false; - @property() public clipboard?: Clipboard; + @storage({ + key: "automationClipboard", + state: true, + subscribe: true, + storage: "sessionStorage", + }) + public _clipboard?: AutomationClipboard; private _focusLastActionOnChange = false; @@ -113,7 +120,6 @@ export default class HaAutomationAction extends LitElement { @duplicate=${this._duplicateAction} @value-changed=${this._actionChanged} @re-order=${this._enterReOrderMode} - .clipboard=${this.clipboard} .hass=${this.hass} > ${this.reOrderMode @@ -162,14 +168,14 @@ export default class HaAutomationAction extends LitElement { > - ${this.clipboard?.action + ${this._clipboard?.action ? html` ${this.hass.localize( "ui.panel.config.automation.editor.actions.paste" )} (${this.hass.localize( `ui.panel.config.automation.editor.actions.type.${getType( - this.clipboard.action + this._clipboard.action )}.label` )})

${this.hass.localize( @@ -80,7 +77,6 @@ export class HaChooseAction extends LitElement implements ActionElement { .hass=${this.hass} .idx=${idx} @value-changed=${this._actionChanged} - .clipboard=${this.clipboard} > ` @@ -109,7 +105,6 @@ export class HaChooseAction extends LitElement implements ActionElement { .disabled=${this.disabled} @value-changed=${this._defaultChanged} .hass=${this.hass} - .clipboard=${this.clipboard} > ` : html` @@ -114,7 +109,6 @@ export class HaRepeatAction extends LitElement implements ActionElement { .reOrderMode=${this.reOrderMode} .disabled=${this.disabled} @value-changed=${this._actionChanged} - .clipboard=${this.clipboard} .hass=${this.hass} > `; diff --git a/src/panels/config/automation/action/types/ha-automation-action-service.ts b/src/panels/config/automation/action/types/ha-automation-action-service.ts index e59c1800dd..0c403a13dd 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-service.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-service.ts @@ -1,7 +1,17 @@ -import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; +import { + css, + CSSResultGroup, + html, + LitElement, + nothing, + PropertyValues, +} from "lit"; import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; import { assert } from "superstruct"; import { fireEvent } from "../../../../../common/dom/fire_event"; +import { computeDomain } from "../../../../../common/entity/compute_domain"; +import { computeObjectId } from "../../../../../common/entity/compute_object_id"; import { hasTemplate } from "../../../../../common/string/has-template"; import "../../../../../components/ha-service-control"; import { ServiceAction, serviceActionStruct } from "../../../../../data/script"; @@ -18,7 +28,29 @@ export class HaServiceAction extends LitElement implements ActionElement { @property({ type: Boolean }) public narrow = false; - @state() private _action!: ServiceAction; + @state() private _action?: ServiceAction; + + @state() private _responseChecked = false; + + private _fields = memoizeOne( + ( + serviceDomains: HomeAssistant["services"], + domainService: string | undefined + ): { fields: any } => { + if (!domainService) { + return { fields: {} }; + } + const domain = computeDomain(domainService); + const service = computeObjectId(domainService); + if (!(domain in serviceDomains)) { + return { fields: {} }; + } + if (!(service in serviceDomains[domain])) { + return { fields: {} }; + } + return { fields: serviceDomains[domain][service].fields }; + } + ); public static get defaultConfig() { return { service: "", data: {} }; @@ -34,7 +66,28 @@ export class HaServiceAction extends LitElement implements ActionElement { fireEvent(this, "ui-mode-not-available", err); return; } - if (this.action && hasTemplate(this.action)) { + + const fields = this._fields( + this.hass.services, + this.action?.service + ).fields; + if ( + this.action && + (Object.entries(this.action).some( + ([key, val]) => key !== "data" && hasTemplate(val) + ) || + (this.action.data && + Object.entries(this.action.data).some(([key, val]) => { + const field = fields[key]; + if ( + field?.selector && + ("template" in field.selector || "object" in field.selector) + ) { + return false; + } + return hasTemplate(val); + }))) + ) { fireEvent( this, "ui-mode-not-available", @@ -54,6 +107,12 @@ export class HaServiceAction extends LitElement implements ActionElement { } protected render() { + if (!this._action) { + return nothing; + } + const [domain, service] = this._action.service + ? this._action.service.split(".", 2) + : [undefined, undefined]; return html` + ${domain && service && this.hass.services[domain]?.[service]?.response + ? html` + ${this.hass.services[domain][service].response!.optional + ? html`` + : html`
`} + ${this.hass.localize( + "ui.panel.config.automation.editor.actions.type.service.response_variable" + )} + + ${this.hass.services[domain][service].response!.optional + ? this.hass.localize( + "ui.panel.config.automation.editor.actions.type.service.has_optional_response" + ) + : this.hass.localize( + "ui.panel.config.automation.editor.actions.type.service.has_response" + )} + + +
` + : nothing} `; } @@ -70,6 +164,39 @@ export class HaServiceAction extends LitElement implements ActionElement { if (ev.detail.value === this._action) { ev.stopPropagation(); } + const value = { ...this.action, ...ev.detail.value }; + if ("response_variable" in this.action) { + const [domain, service] = this._action!.service + ? this._action!.service.split(".", 2) + : [undefined, undefined]; + if ( + domain && + service && + this.hass.services[domain]?.[service] && + !("response" in this.hass.services[domain][service]) + ) { + delete value.response_variable; + this._responseChecked = false; + } + } + fireEvent(this, "value-changed", { value }); + } + + private _responseVariableChanged(ev) { + const value = { ...this.action, response_variable: ev.target.value }; + if (!ev.target.value) { + delete value.response_variable; + } + fireEvent(this, "value-changed", { value }); + } + + private _responseCheckboxChanged(ev) { + this._responseChecked = ev.target.checked; + if (!this._responseChecked) { + const value = { ...this.action }; + delete value.response_variable; + fireEvent(this, "value-changed", { value }); + } } static get styles(): CSSResultGroup { @@ -78,6 +205,25 @@ export class HaServiceAction extends LitElement implements ActionElement { display: block; margin: 0 -16px; } + ha-settings-row { + margin: 0 -16px; + padding: var(--service-control-padding, 0 16px); + } + ha-settings-row { + --paper-time-input-justify-content: flex-end; + --settings-row-content-width: 100%; + --settings-row-prefix-display: contents; + border-top: var( + --service-control-items-border-top, + 1px solid var(--divider-color) + ); + } + ha-checkbox { + margin-left: -16px; + } + .checkbox-spacer { + width: 32px; + } `; } } diff --git a/src/panels/config/automation/action/types/ha-automation-action-stop.ts b/src/panels/config/automation/action/types/ha-automation-action-stop.ts index 6281c8b634..6ca7177e4f 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-stop.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-stop.ts @@ -19,7 +19,7 @@ export class HaStopAction extends LitElement implements ActionElement { } protected render() { - const { error, stop } = this.action; + const { error, stop, response_variable } = this.action; return html` + `; } diff --git a/src/panels/config/automation/blueprint-automation-editor.ts b/src/panels/config/automation/blueprint-automation-editor.ts index 3131a44b26..74d3d0586e 100644 --- a/src/panels/config/automation/blueprint-automation-editor.ts +++ b/src/panels/config/automation/blueprint-automation-editor.ts @@ -138,11 +138,12 @@ export class HaBlueprintAutomationEditor extends LitElement { this.config.use_blueprint.input[key]) ?? value?.default} .disabled=${this.disabled} + .required=${value?.default === undefined} @value-changed=${this._inputChanged} >` : html` expandConditionWithShorthand(condition) ); @@ -72,7 +70,6 @@ export default class HaAutomationConditionEditor extends LitElement { condition: condition, reOrderMode: this.reOrderMode, disabled: this.disabled, - clipboard: this.clipboard, } )} diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts index 8f6cd63441..7e8bb4e270 100644 --- a/src/panels/config/automation/condition/ha-automation-condition-row.ts +++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts @@ -3,9 +3,9 @@ import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { mdiCheck, - mdiContentDuplicate, mdiContentCopy, mdiContentCut, + mdiContentDuplicate, mdiDelete, mdiDotsVertical, mdiFlask, @@ -14,9 +14,11 @@ import { mdiSort, mdiStopCircleOutline, } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import deepClone from "deep-clone-simple"; +import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import { storage } from "../../../../common/decorators/storage"; import { fireEvent } from "../../../../common/dom/fire_event"; import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter"; import { handleStructError } from "../../../../common/structs/handle-errors"; @@ -24,8 +26,8 @@ import "../../../../components/ha-button-menu"; import "../../../../components/ha-card"; import "../../../../components/ha-expansion-panel"; import "../../../../components/ha-icon-button"; +import type { AutomationClipboard } from "../../../../data/automation"; import { Condition, testCondition } from "../../../../data/automation"; -import type { Clipboard } from "../../../../data/automation"; import { describeCondition } from "../../../../data/automation_i18n"; import { CONDITION_TYPES } from "../../../../data/condition"; import { validateConfig } from "../../../../data/config"; @@ -83,7 +85,13 @@ export default class HaAutomationConditionRow extends LitElement { @property({ type: Boolean }) public disabled = false; - @property() public clipboard?: Clipboard; + @storage({ + key: "automationClipboard", + state: false, + subscribe: true, + storage: "sessionStorage", + }) + public _clipboard?: AutomationClipboard; @state() private _yamlMode = false; @@ -290,7 +298,6 @@ export default class HaAutomationConditionRow extends LitElement { .hass=${this.hass} .condition=${this.condition} .reOrderMode=${this.reOrderMode} - .clipboard=${this.clipboard} > @@ -343,10 +350,10 @@ export default class HaAutomationConditionRow extends LitElement { fireEvent(this, "duplicate"); break; case 4: - fireEvent(this, "set-clipboard", { condition: this.condition }); + this._setClipboard(); break; case 5: - fireEvent(this, "set-clipboard", { condition: this.condition }); + this._setClipboard(); fireEvent(this, "value-changed", { value: null }); break; case 6: @@ -366,6 +373,13 @@ export default class HaAutomationConditionRow extends LitElement { } } + private _setClipboard() { + this._clipboard = { + ...this._clipboard, + condition: deepClone(this.condition), + }; + } + private _onDisable() { const enabled = !(this.condition.enabled ?? true); const value = { ...this.condition, enabled }; diff --git a/src/panels/config/automation/condition/ha-automation-condition.ts b/src/panels/config/automation/condition/ha-automation-condition.ts index 3b64474463..8ee6552bae 100644 --- a/src/panels/config/automation/condition/ha-automation-condition.ts +++ b/src/panels/config/automation/condition/ha-automation-condition.ts @@ -3,9 +3,9 @@ import type { ActionDetail } from "@material/mwc-list"; import { mdiArrowDown, mdiArrowUp, + mdiContentPaste, mdiDrag, mdiPlus, - mdiContentPaste, } from "@mdi/js"; import deepClone from "deep-clone-simple"; import { @@ -13,8 +13,8 @@ import { CSSResultGroup, html, LitElement, - PropertyValues, nothing, + PropertyValues, } from "lit"; import { customElement, property } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; @@ -24,7 +24,10 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-button"; import "../../../../components/ha-button-menu"; import "../../../../components/ha-svg-icon"; -import type { Condition, Clipboard } from "../../../../data/automation"; +import type { + AutomationClipboard, + Condition, +} from "../../../../data/automation"; import type { HomeAssistant } from "../../../../types"; import "./ha-automation-condition-row"; import type HaAutomationConditionRow from "./ha-automation-condition-row"; @@ -49,6 +52,7 @@ import "./types/ha-automation-condition-template"; import "./types/ha-automation-condition-time"; import "./types/ha-automation-condition-trigger"; import "./types/ha-automation-condition-zone"; +import { storage } from "../../../../common/decorators/storage"; const PASTE_VALUE = "__paste__"; @@ -64,7 +68,13 @@ export default class HaAutomationCondition extends LitElement { @property({ type: Boolean }) public reOrderMode = false; - @property() public clipboard?: Clipboard; + @storage({ + key: "automationClipboard", + state: true, + subscribe: true, + storage: "sessionStorage", + }) + public _clipboard?: AutomationClipboard; private _focusLastConditionOnChange = false; @@ -157,7 +167,6 @@ export default class HaAutomationCondition extends LitElement { @move-condition=${this._move} @value-changed=${this._conditionChanged} @re-order=${this._enterReOrderMode} - .clipboard=${this.clipboard} .hass=${this.hass} > ${this.reOrderMode @@ -206,13 +215,13 @@ export default class HaAutomationCondition extends LitElement { > - ${this.clipboard?.condition + ${this._clipboard?.condition ? html` ${this.hass.localize( "ui.panel.config.automation.editor.conditions.paste" )} (${this.hass.localize( - `ui.panel.config.automation.editor.conditions.type.${this.clipboard.condition.condition}.label` + `ui.panel.config.automation.editor.conditions.type.${this._clipboard.condition.condition}.label` )}) ` @@ -281,7 +290,9 @@ export default class HaAutomationCondition extends LitElement { let conditions: Condition[]; if (value === PASTE_VALUE) { - conditions = this.conditions.concat(deepClone(this.clipboard!.condition)); + conditions = this.conditions.concat( + deepClone(this._clipboard!.condition) + ); } else { const condition = value as Condition["condition"]; diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts b/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts index 527e2b7234..a7aa3fb9a6 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-logical.ts @@ -1,10 +1,7 @@ import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; -import type { - LogicalCondition, - Clipboard, -} from "../../../../../data/automation"; +import type { LogicalCondition } from "../../../../../data/automation"; import type { HomeAssistant } from "../../../../../types"; import "../ha-automation-condition"; import type { ConditionElement } from "../ha-automation-condition-row"; @@ -19,8 +16,6 @@ export class HaLogicalCondition extends LitElement implements ConditionElement { @property({ type: Boolean }) public reOrderMode = false; - @property() public clipboard?: Clipboard; - public static get defaultConfig() { return { conditions: [], @@ -35,7 +30,6 @@ export class HaLogicalCondition extends LitElement implements ConditionElement { @value-changed=${this._valueChanged} .hass=${this.hass} .disabled=${this.disabled} - .clipboard=${this.clipboard} .reOrderMode=${this.reOrderMode} > `; diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-trigger.ts b/src/panels/config/automation/condition/types/ha-automation-condition-trigger.ts index 3eb72d48d2..8935668618 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-trigger.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-trigger.ts @@ -1,9 +1,11 @@ import "@material/mwc-list/mwc-list-item"; +import memoizeOne from "memoize-one"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { ensureArray } from "../../../../../common/array/ensure-array"; +import type { SchemaUnion } from "../../../../../components/ha-form/types"; import "../../../../../components/ha-select"; import type { AutomationConfig, @@ -30,6 +32,22 @@ export class HaTriggerCondition extends LitElement { }; } + private _schema = memoizeOne( + (triggers: Trigger[]) => + [ + { + name: "id", + selector: { + select: { + multiple: true, + options: triggers.map((trigger) => trigger.id!), + }, + }, + required: true, + }, + ] as const + ); + connectedCallback() { super.connectedCallback(); const details = { callback: (config) => this._automationUpdated(config) }; @@ -45,30 +63,33 @@ export class HaTriggerCondition extends LitElement { } protected render() { - const { id } = this.condition; - if (!this._triggers.length) { return this.hass.localize( "ui.panel.config.automation.editor.conditions.type.trigger.no_triggers" ); } - return html` - ${this._triggers.map( - (trigger) => - html` - ${trigger.id} - ` - )} - `; + + const schema = this._schema(this._triggers); + + return html` + + `; } + private _computeLabelCallback = ( + schema: SchemaUnion> + ): string => + this.hass.localize( + `ui.panel.config.automation.editor.conditions.type.trigger.${schema.name}` + ); + private _automationUpdated(config?: AutomationConfig) { const seenIds = new Set(); this._triggers = config?.trigger @@ -78,18 +99,24 @@ export class HaTriggerCondition extends LitElement { : []; } - private _triggerPicked(ev) { + private _valueChanged(ev: CustomEvent): void { ev.stopPropagation(); - if (!ev.target.value) { - return; + const newValue = ev.detail.value; + + if (typeof newValue.id === "string") { + if (!this._triggers.some((trigger) => trigger.id === newValue.id)) { + newValue.id = ""; + } + } else if (Array.isArray(newValue.id)) { + newValue.id = newValue.id.filter((id) => + this._triggers.some((trigger) => trigger.id === id) + ); + if (!newValue.id.length) { + newValue.id = ""; + } } - const newTrigger = ev.target.value; - if (this.condition.id === newTrigger) { - return; - } - fireEvent(this, "value-changed", { - value: { ...this.condition, id: newTrigger }, - }); + + fireEvent(this, "value-changed", { value: newValue }); } } diff --git a/src/panels/config/automation/dialog-new-automation.ts b/src/panels/config/automation/dialog-new-automation.ts index 0c160900b9..735d6aa9de 100644 --- a/src/panels/config/automation/dialog-new-automation.ts +++ b/src/panels/config/automation/dialog-new-automation.ts @@ -18,8 +18,10 @@ import "../../../components/ha-icon-next"; import "../../../components/ha-list-item"; import "../../../components/ha-tip"; import { showAutomationEditor } from "../../../data/automation"; +import { showScriptEditor } from "../../../data/script"; import { Blueprint, + BlueprintDomain, Blueprints, BlueprintSourceType, fetchBlueprints, @@ -29,6 +31,7 @@ import { HassDialog } from "../../../dialogs/make-dialog-manager"; import { haStyle, haStyleDialog } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; +import type { NewAutomationDialogParams } from "./show-dialog-new-automation"; const SOURCE_TYPE_ICONS: Record = { local: mdiFile, @@ -42,11 +45,15 @@ class DialogNewAutomation extends LitElement implements HassDialog { @state() private _opened = false; + @state() private _mode: BlueprintDomain = "automation"; + @state() public blueprints?: Blueprints; - public showDialog(): void { + public showDialog(params: NewAutomationDialogParams): void { this._opened = true; - fetchBlueprints(this.hass!, "automation").then((blueprints) => { + this._mode = params?.mode || "automation"; + + fetchBlueprints(this.hass!, this._mode).then((blueprints) => { this.blueprints = blueprints; }); } @@ -92,14 +99,14 @@ class DialogNewAutomation extends LitElement implements HassDialog { @closed=${this.closeDialog} .heading=${createCloseHeading( this.hass, - this.hass.localize("ui.panel.config.automation.dialog_new.header") + this.hass.localize(`ui.panel.config.${this._mode}.dialog_new.header`) )} > ${this.hass.localize( - "ui.panel.config.automation.dialog_new.create_empty" + `ui.panel.config.${this._mode}.dialog_new.create_empty` )} ${this.hass.localize( - "ui.panel.config.automation.dialog_new.create_empty_description" + `ui.panel.config.${this._mode}.dialog_new.create_empty_description` )} @@ -139,11 +146,11 @@ class DialogNewAutomation extends LitElement implements HassDialog { ${blueprint.author ? this.hass.localize( - `ui.panel.config.automation.dialog_new.blueprint_source.author`, + `ui.panel.config.${this._mode}.dialog_new.blueprint_source.author`, { author: blueprint.author } ) : this.hass.localize( - `ui.panel.config.automation.dialog_new.blueprint_source.${blueprint.sourceType}` + `ui.panel.config.${this._mode}.dialog_new.blueprint_source.${blueprint.sourceType}` )} @@ -161,11 +168,11 @@ class DialogNewAutomation extends LitElement implements HassDialog { ${this.hass.localize( - "ui.panel.config.automation.dialog_new.create_blueprint" + `ui.panel.config.${this._mode}.dialog_new.create_blueprint` )} ${this.hass.localize( - "ui.panel.config.automation.dialog_new.create_blueprint_description" + `ui.panel.config.${this._mode}.dialog_new.create_blueprint_description` )} @@ -180,7 +187,7 @@ class DialogNewAutomation extends LitElement implements HassDialog { rel="noreferrer noopener" > ${this.hass.localize( - "ui.panel.config.automation.dialog_new.discover_blueprint_tip" + `ui.panel.config.${this._mode}.dialog_new.discover_blueprint_tip` )} @@ -196,7 +203,11 @@ class DialogNewAutomation extends LitElement implements HassDialog { } const path = (ev.currentTarget! as any).path; this.closeDialog(); - showAutomationEditor({ use_blueprint: { path } }); + if (this._mode === "script") { + showScriptEditor({ use_blueprint: { path } }); + } else { + showAutomationEditor({ use_blueprint: { path } }); + } } private async _blank(ev) { @@ -204,7 +215,11 @@ class DialogNewAutomation extends LitElement implements HassDialog { return; } this.closeDialog(); - showAutomationEditor(); + if (this._mode === "script") { + showScriptEditor(); + } else { + showAutomationEditor(); + } } static get styles(): CSSResultGroup { diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 3aa074c345..4dba153f71 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -11,17 +11,19 @@ import { mdiPlay, mdiPlayCircleOutline, mdiRenameBox, + mdiRobotConfused, mdiStopCircleOutline, mdiTransitConnection, } from "@mdi/js"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { - css, CSSResultGroup, - html, LitElement, PropertyValues, TemplateResult, + css, + html, + nothing, } from "lit"; import { property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -46,10 +48,7 @@ import { saveAutomationConfig, showAutomationEditor, triggerAutomationActions, - Trigger, - Condition, } from "../../../data/automation"; -import { Action } from "../../../data/script"; import { fetchEntityRegistry } from "../../../data/entity_registry"; import { showAlertDialog, @@ -65,6 +64,8 @@ import { showAutomationModeDialog } from "./automation-mode-dialog/show-dialog-a import { showAutomationRenameDialog } from "./automation-rename-dialog/show-dialog-automation-rename"; import "./blueprint-automation-editor"; import "./manual-automation-editor"; +import { UNAVAILABLE } from "../../../data/entity"; +import { validateConfig } from "../../../data/config"; declare global { interface HTMLElementTagNameMap { @@ -79,11 +80,6 @@ declare global { "ui-mode-not-available": Error; duplicate: undefined; "re-order": undefined; - "set-clipboard": { - trigger?: Trigger; - condition?: Condition; - action?: Action; - }; } } @@ -114,6 +110,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { @state() private _readOnly = false; + @state() private _validationErrors?: (string | TemplateResult)[]; + @query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor; private _configSubscriptions: Record< @@ -299,9 +297,22 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { })}" @subscribe-automation-config=${this._subscribeAutomationConfig} > - ${this._errors - ? html` - ${this._errors} + ${this._errors || stateObj?.state === UNAVAILABLE + ? html` + ${this._errors || this._validationErrors} + ${stateObj?.state === UNAVAILABLE + ? html`` + : nothing} ` : ""} ${this._mode === "gui" @@ -435,6 +446,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { if (changedProps.has("entityId") && this.entityId) { getAutomationStateConfig(this.hass, this.entityId).then((c) => { this._config = this._normalizeConfig(c.config); + this._checkValidation(); }); this._entityId = this.entityId; this._dirty = false; @@ -463,6 +475,30 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { this._entityId = automation?.entity_id; } + private async _checkValidation() { + this._validationErrors = undefined; + if (!this._entityId || !this._config) { + return; + } + const stateObj = this.hass.states[this._entityId]; + if (stateObj?.state !== UNAVAILABLE) { + return; + } + const validation = await validateConfig(this.hass, { + trigger: this._config.trigger, + condition: this._config.condition, + action: this._config.action, + }); + this._validationErrors = Object.entries(validation).map(([key, value]) => + value.valid + ? "" + : html`${this.hass.localize( + `ui.panel.config.automation.editor.${key}s.header` + )}: + ${value.error}
` + ); + } + private _normalizeConfig(config: AutomationConfig): AutomationConfig { // Normalize data: ensure trigger, action and condition are lists // Happens when people copy paste their automations into the config @@ -484,6 +520,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { this._dirty = false; this._readOnly = false; this._config = this._normalizeConfig(config); + this._checkValidation(); } catch (err: any) { const entityRegistry = await fetchEntityRegistry(this.hass.connection); const entity = entityRegistry.find( @@ -694,6 +731,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { await this._promptAutomationAlias(); } + this._validationErrors = undefined; try { await saveAutomationConfig(this.hass, id, this._config!); } catch (errors: any) { diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index cb89fd8af5..65af78bae3 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -15,6 +15,7 @@ import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { differenceInDays } from "date-fns/esm"; +import { styleMap } from "lit/directives/style-map"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { relativeTime } from "../../../common/datetime/relative_time"; @@ -52,6 +53,7 @@ import { configSections } from "../ha-panel-config"; import { showNewAutomationDialog } from "./show-dialog-new-automation"; import { findRelated } from "../../../data/search"; import { fetchBlueprints } from "../../../data/blueprint"; +import { UNAVAILABLE } from "../../../data/entity"; @customElement("ha-automation-picker") class HaAutomationPicker extends LitElement { @@ -106,7 +108,15 @@ class HaAutomationPicker extends LitElement { ), type: "icon", template: (_, automation) => - html``, + html``, }, name: { title: this.hass.localize( @@ -128,7 +138,11 @@ class HaAutomationPicker extends LitElement { ${this.hass.localize("ui.card.automation.last_triggered")}: ${automation.attributes.last_triggered ? dayDifference > 3 - ? formatShortDateTime(date, this.hass.locale) + ? formatShortDateTime( + date, + this.hass.locale, + this.hass.config + ) : relativeTime(date, this.hass.locale) : this.hass.localize("ui.components.relative_time.never")} @@ -149,7 +163,11 @@ class HaAutomationPicker extends LitElement { return html` ${last_triggered ? dayDifference > 3 - ? formatShortDateTime(date, this.hass.locale) + ? formatShortDateTime( + date, + this.hass.locale, + this.hass.config + ) : relativeTime(date, this.hass.locale) : this.hass.localize("ui.components.relative_time.never")} `; @@ -478,7 +496,7 @@ class HaAutomationPicker extends LitElement { private _createNew() { if (isComponentLoaded(this.hass, "blueprint")) { - showNewAutomationDialog(this); + showNewAutomationDialog(this, { mode: "automation" }); } else { navigate("/config/automation/edit/new"); } diff --git a/src/panels/config/automation/ha-automation-trace.ts b/src/panels/config/automation/ha-automation-trace.ts index bfebbadb0b..fbf52c6974 100644 --- a/src/panels/config/automation/ha-automation-trace.ts +++ b/src/panels/config/automation/ha-automation-trace.ts @@ -192,7 +192,8 @@ export class HaAutomationTrace extends LitElement { html`` )} diff --git a/src/panels/config/automation/manual-automation-editor.ts b/src/panels/config/automation/manual-automation-editor.ts index 7324f8d5a4..e0417269b6 100644 --- a/src/panels/config/automation/manual-automation-editor.ts +++ b/src/panels/config/automation/manual-automation-editor.ts @@ -3,7 +3,6 @@ import { mdiHelpCircle } from "@mdi/js"; import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; -import deepClone from "deep-clone-simple"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-card"; import "../../../components/ha-icon-button"; @@ -11,7 +10,6 @@ import { Condition, ManualAutomationConfig, Trigger, - Clipboard, } from "../../../data/automation"; import { Action } from "../../../data/script"; import { haStyle } from "../../../resources/styles"; @@ -20,7 +18,6 @@ import { documentationUrl } from "../../../util/documentation-url"; import "./action/ha-automation-action"; import "./condition/ha-automation-condition"; import "./trigger/ha-automation-trigger"; -import { LocalStorage } from "../../../common/decorators/local-storage"; @customElement("manual-automation-editor") export class HaManualAutomationEditor extends LitElement { @@ -36,9 +33,6 @@ export class HaManualAutomationEditor extends LitElement { @property({ attribute: false }) public stateObj?: HassEntity; - @LocalStorage("automationClipboard", true, false, window.sessionStorage) - private _clipboard: Clipboard = {}; - protected render() { return html` ${this.disabled @@ -97,8 +91,6 @@ export class HaManualAutomationEditor extends LitElement { @value-changed=${this._triggerChanged} .hass=${this.hass} .disabled=${this.disabled} - @set-clipboard=${this._setClipboard} - .clipboard=${this._clipboard} >
@@ -128,8 +120,6 @@ export class HaManualAutomationEditor extends LitElement { @value-changed=${this._conditionChanged} .hass=${this.hass} .disabled=${this.disabled} - @set-clipboard=${this._setClipboard} - .clipboard=${this._clipboard} >
@@ -162,8 +152,6 @@ export class HaManualAutomationEditor extends LitElement { .hass=${this.hass} .narrow=${this.narrow} .disabled=${this.disabled} - @set-clipboard=${this._setClipboard} - .clipboard=${this._clipboard} > `; } @@ -175,11 +163,6 @@ export class HaManualAutomationEditor extends LitElement { }); } - private _setClipboard(ev: CustomEvent): void { - ev.stopPropagation(); - this._clipboard = { ...this._clipboard, ...deepClone(ev.detail) }; - } - private _conditionChanged(ev: CustomEvent): void { ev.stopPropagation(); fireEvent(this, "value-changed", { diff --git a/src/panels/config/automation/show-dialog-new-automation.ts b/src/panels/config/automation/show-dialog-new-automation.ts index 0a618c178b..1425842e80 100644 --- a/src/panels/config/automation/show-dialog-new-automation.ts +++ b/src/panels/config/automation/show-dialog-new-automation.ts @@ -1,11 +1,18 @@ import { fireEvent } from "../../../common/dom/fire_event"; +export interface NewAutomationDialogParams { + mode: "script" | "automation"; +} + export const loadNewAutomationDialog = () => import("./dialog-new-automation"); -export const showNewAutomationDialog = (element: HTMLElement): void => { +export const showNewAutomationDialog = ( + element: HTMLElement, + newAutomationDialogParams: NewAutomationDialogParams +): void => { fireEvent(element, "show-dialog", { dialogTag: "ha-dialog-new-automation", dialogImport: loadNewAutomationDialog, - dialogParams: {}, + dialogParams: newAutomationDialogParams, }); }; diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts index 52b51686bc..cbc1e7bff3 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -3,9 +3,9 @@ import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { mdiCheck, - mdiContentDuplicate, mdiContentCopy, mdiContentCut, + mdiContentDuplicate, mdiDelete, mdiDotsVertical, mdiIdentifier, @@ -15,9 +15,10 @@ import { mdiStopCircleOutline, } from "@mdi/js"; import type { UnsubscribeFunc } from "home-assistant-js-websocket"; -import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; +import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import { storage } from "../../../../common/decorators/storage"; import { dynamicElement } from "../../../../common/dom/dynamic-element-directive"; import { fireEvent } from "../../../../common/dom/fire_event"; import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter"; @@ -30,7 +31,8 @@ import "../../../../components/ha-expansion-panel"; import "../../../../components/ha-icon-button"; import "../../../../components/ha-textfield"; import { HaYamlEditor } from "../../../../components/ha-yaml-editor"; -import { subscribeTrigger, Trigger } from "../../../../data/automation"; +import type { AutomationClipboard } from "../../../../data/automation"; +import { Trigger, subscribeTrigger } from "../../../../data/automation"; import { describeTrigger } from "../../../../data/automation_i18n"; import { validateConfig } from "../../../../data/config"; import { fullEntitiesContext } from "../../../../data/context"; @@ -50,6 +52,8 @@ import "./types/ha-automation-trigger-geo_location"; import "./types/ha-automation-trigger-homeassistant"; import "./types/ha-automation-trigger-mqtt"; import "./types/ha-automation-trigger-numeric_state"; +import "./types/ha-automation-trigger-persistent_notification"; +import "./types/ha-automation-trigger-conversation"; import "./types/ha-automation-trigger-state"; import "./types/ha-automation-trigger-sun"; import "./types/ha-automation-trigger-tag"; @@ -109,6 +113,14 @@ export default class HaAutomationTriggerRow extends LitElement { @query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor; + @storage({ + key: "automationClipboard", + state: false, + subscribe: true, + storage: "sessionStorage", + }) + public _clipboard?: AutomationClipboard; + @state() @consume({ context: fullEntitiesContext, subscribe: true }) _entityReg!: EntityRegistryEntry[]; @@ -468,10 +480,10 @@ export default class HaAutomationTriggerRow extends LitElement { fireEvent(this, "duplicate"); break; case 4: - fireEvent(this, "set-clipboard", { trigger: this.trigger }); + this._setClipboard(); break; case 5: - fireEvent(this, "set-clipboard", { trigger: this.trigger }); + this._setClipboard(); fireEvent(this, "value-changed", { value: null }); break; case 6: @@ -491,6 +503,13 @@ export default class HaAutomationTriggerRow extends LitElement { } } + private _setClipboard() { + this._clipboard = { + ...this._clipboard, + trigger: this.trigger, + }; + } + private _onDelete() { showConfirmationDialog(this, { title: this.hass.localize( diff --git a/src/panels/config/automation/trigger/ha-automation-trigger.ts b/src/panels/config/automation/trigger/ha-automation-trigger.ts index 66d59f3552..31acd3b0c4 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger.ts @@ -27,7 +27,7 @@ import "../../../../components/ha-button-menu"; import "../../../../components/ha-button"; import type { HaSelect } from "../../../../components/ha-select"; import "../../../../components/ha-svg-icon"; -import { Trigger, Clipboard } from "../../../../data/automation"; +import { Trigger, AutomationClipboard } from "../../../../data/automation"; import { TRIGGER_TYPES } from "../../../../data/trigger"; import { sortableStyles } from "../../../../resources/ha-sortable-style"; import { SortableInstance } from "../../../../resources/sortable"; @@ -42,6 +42,8 @@ import "./types/ha-automation-trigger-geo_location"; import "./types/ha-automation-trigger-homeassistant"; import "./types/ha-automation-trigger-mqtt"; import "./types/ha-automation-trigger-numeric_state"; +import "./types/ha-automation-trigger-persistent_notification"; +import "./types/ha-automation-trigger-conversation"; import "./types/ha-automation-trigger-state"; import "./types/ha-automation-trigger-sun"; import "./types/ha-automation-trigger-tag"; @@ -50,6 +52,7 @@ import "./types/ha-automation-trigger-time"; import "./types/ha-automation-trigger-time_pattern"; import "./types/ha-automation-trigger-webhook"; import "./types/ha-automation-trigger-zone"; +import { storage } from "../../../../common/decorators/storage"; const PASTE_VALUE = "__paste__"; @@ -65,7 +68,13 @@ export default class HaAutomationTrigger extends LitElement { @property({ type: Boolean }) public reOrderMode = false; - @property() public clipboard?: Clipboard; + @storage({ + key: "automationClipboard", + state: true, + subscribe: true, + storage: "sessionStorage", + }) + public _clipboard?: AutomationClipboard; private _focusLastTriggerOnChange = false; @@ -154,13 +163,13 @@ export default class HaAutomationTrigger extends LitElement { > - ${this.clipboard?.trigger + ${this._clipboard?.trigger ? html` ${this.hass.localize( "ui.panel.config.automation.editor.triggers.paste" )} (${this.hass.localize( - `ui.panel.config.automation.editor.triggers.type.${this.clipboard.trigger.platform}.label` + `ui.panel.config.automation.editor.triggers.type.${this._clipboard.trigger.platform}.label` )}) { + return { command: "" }; + } + + protected render() { + const { command } = this.trigger; + const commands = command ? ensureArray(command) : []; + + return html`${commands.length + ? commands.map( + (option, index) => html` + + + + ` + ) + : nothing} + `; + } + + private _handleKeyAdd(ev: KeyboardEvent) { + ev.stopPropagation(); + if (ev.key !== "Enter") { + return; + } + this._addOption(); + } + + private _addOption() { + const input = this._optionInput; + if (!input?.value) { + return; + } + fireEvent(this, "value-changed", { + value: { + ...this.trigger, + command: this.trigger.command.length + ? [ + ...(Array.isArray(this.trigger.command) + ? this.trigger.command + : [this.trigger.command]), + input.value, + ] + : input.value, + }, + }); + input.value = ""; + } + + private async _updateOption(ev: Event) { + const index = (ev.target as any).index; + const command = [...this.trigger.command]; + command.splice(index, 1, (ev.target as HaTextField).value); + fireEvent(this, "value-changed", { + value: { ...this.trigger, command }, + }); + } + + private async _removeOption(ev: Event) { + const index = (ev.target as any).parentElement.index; + if ( + !(await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.conversation.delete" + ), + text: this.hass.localize( + "ui.panel.config.automation.editor.triggers.type.conversation.confirm_delete" + ), + destructive: true, + })) + ) { + return; + } + let command: string[] | string; + if (!Array.isArray(this.trigger.command)) { + command = ""; + } else { + command = [...this.trigger.command]; + command.splice(index, 1); + } + fireEvent(this, "value-changed", { + value: { ...this.trigger, command }, + }); + } + + static get styles(): CSSResultGroup { + return css` + .layout { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + justify-content: flex-start; + } + .option { + margin-top: 4px; + } + mwc-button { + margin-left: 8px; + } + ha-textfield { + display: block; + margin-bottom: 8px; + --textfield-icon-trailing-padding: 0; + } + ha-textfield > ha-icon-button { + position: relative; + right: -8px; + --mdc-icon-button-size: 36px; + --mdc-icon-size: 20px; + color: var(--secondary-text-color); + inset-inline-start: initial; + inset-inline-end: -8px; + direction: var(--direction); + } + #option_input { + margin-top: 8px; + } + .header { + margin-top: 8px; + margin-bottom: 8px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-conversation": HaConversationTrigger; + } +} diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-persistent_notification.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-persistent_notification.ts new file mode 100644 index 0000000000..0e942c1860 --- /dev/null +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-persistent_notification.ts @@ -0,0 +1,117 @@ +import memoizeOne from "memoize-one"; + +import { css, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import "../../../../../components/ha-button-menu"; +import "../../../../../components/ha-check-list-item"; +import "../../../../../components/ha-icon-button"; +import "../../../../../components/ha-textfield"; +import { PersistentNotificationTrigger } from "../../../../../data/automation"; +import { HomeAssistant } from "../../../../../types"; +import type { TriggerElement } from "../ha-automation-trigger-row"; +import type { LocalizeFunc } from "../../../../../common/translations/localize"; +import type { SchemaUnion } from "../../../../../components/ha-form/types"; + +const DEFAULT_UPDATE_TYPES = ["added", "removed"]; +const DEFAULT_NOTIFICATION_ID = ""; + +@customElement("ha-automation-trigger-persistent_notification") +export class HaPersistentNotificationTrigger + extends LitElement + implements TriggerElement +{ + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public trigger!: PersistentNotificationTrigger; + + @property({ type: Boolean }) public disabled = false; + + private _schema = memoizeOne( + (localize: LocalizeFunc) => + [ + { + name: "notification_id", + required: false, + selector: { text: {} }, + }, + { + name: "update_type", + type: "multi_select", + required: false, + options: [ + [ + "added", + localize( + "ui.panel.config.automation.editor.triggers.type.persistent_notification.update_types.added" + ), + ], + [ + "removed", + localize( + "ui.panel.config.automation.editor.triggers.type.persistent_notification.update_types.removed" + ), + ], + [ + "current", + localize( + "ui.panel.config.automation.editor.triggers.type.persistent_notification.update_types.current" + ), + ], + [ + "updated", + localize( + "ui.panel.config.automation.editor.triggers.type.persistent_notification.update_types.updated" + ), + ], + ], + }, + ] as const + ); + + public static get defaultConfig() { + return { + update_type: [...DEFAULT_UPDATE_TYPES], + notification_id: DEFAULT_NOTIFICATION_ID, + }; + } + + protected render() { + const schema = this._schema(this.hass.localize); + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + ev.stopPropagation(); + const newTrigger = ev.detail.value; + fireEvent(this, "value-changed", { value: newTrigger }); + } + + private _computeLabelCallback = ( + schema: SchemaUnion> + ): string => + this.hass.localize( + `ui.panel.config.automation.editor.triggers.type.persistent_notification.${schema.name}` + ); + + static styles = css` + ha-textfield { + display: block; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-automation-trigger-persistent_notification": HaPersistentNotificationTrigger; + } +} diff --git a/src/panels/config/backup/ha-config-backup.ts b/src/panels/config/backup/ha-config-backup.ts index 8d0dfca0d4..07ebf618e6 100644 --- a/src/panels/config/backup/ha-config-backup.ts +++ b/src/panels/config/backup/ha-config-backup.ts @@ -128,6 +128,7 @@ class HaConfigBackup extends LitElement { return html` ) { const blueprint = this._processedBlueprints(this.blueprints).find( - (b) => b.path === ev.detail.id + (b) => b.fullpath === ev.detail.id ); if (blueprint.error) { showAlertDialog(this, { diff --git a/src/panels/config/cloud/account/cloud-account.ts b/src/panels/config/cloud/account/cloud-account.ts index 2be0ccffbd..2f0f3e791d 100644 --- a/src/panels/config/cloud/account/cloud-account.ts +++ b/src/panels/config/cloud/account/cloud-account.ts @@ -79,7 +79,8 @@ export class CloudAccount extends SubscribeMixin(LitElement) { new Date( this._subscription.plan_renewal_date * 1000 ), - this.hass.locale + this.hass.locale, + this.hass.config ) : "" ) diff --git a/src/panels/config/cloud/account/dialog-cloud-tts-try.ts b/src/panels/config/cloud/account/dialog-cloud-tts-try.ts index effed9dea4..bd079a5854 100644 --- a/src/panels/config/cloud/account/dialog-cloud-tts-try.ts +++ b/src/panels/config/cloud/account/dialog-cloud-tts-try.ts @@ -3,7 +3,7 @@ import "@material/mwc-list/mwc-list-item"; import { mdiPlayCircleOutline, mdiRobot } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; -import { LocalStorage } from "../../../../common/decorators/local-storage"; +import { storage } from "../../../../common/decorators/storage"; import { fireEvent } from "../../../../common/dom/fire_event"; import { stopPropagation } from "../../../../common/dom/stop_propagation"; import { computeStateDomain } from "../../../../common/entity/compute_state_domain"; @@ -31,9 +31,19 @@ export class DialogTryTts extends LitElement { @query("#message") private _messageInput?: HaTextArea; - @LocalStorage("cloudTtsTryMessage", false, false) private _message!: string; + @storage({ + key: "cloudTtsTryMessage", + state: false, + subscribe: false, + }) + private _message!: string; - @LocalStorage("cloudTtsTryTarget", false, false) private _target!: string; + @storage({ + key: "cloudTtsTryTarget", + state: false, + subscribe: false, + }) + private _target!: string; public showDialog(params: TryTtsDialogParams) { this._params = params; diff --git a/src/panels/config/cloud/dialog-cloud-certificate/dialog-cloud-certificate.ts b/src/panels/config/cloud/dialog-cloud-certificate/dialog-cloud-certificate.ts index d999a162af..0e170e539b 100644 --- a/src/panels/config/cloud/dialog-cloud-certificate/dialog-cloud-certificate.ts +++ b/src/panels/config/cloud/dialog-cloud-certificate/dialog-cloud-certificate.ts @@ -49,7 +49,8 @@ class DialogCloudCertificate extends LitElement { )} ${formatDateTime( new Date(certificateInfo.expire_date), - this.hass!.locale + this.hass!.locale, + this.hass!.config )}
(${this.hass!.localize( "ui.panel.config.cloud.dialog_certificate.will_be_auto_renewed" diff --git a/src/panels/config/devices/device-detail/integration-elements/mqtt/dialog-mqtt-device-debug-info.ts b/src/panels/config/devices/device-detail/integration-elements/mqtt/dialog-mqtt-device-debug-info.ts index 933ed8769c..d3d15a4a28 100644 --- a/src/panels/config/devices/device-detail/integration-elements/mqtt/dialog-mqtt-device-debug-info.ts +++ b/src/panels/config/devices/device-detail/integration-elements/mqtt/dialog-mqtt-device-debug-info.ts @@ -250,8 +250,18 @@ class DialogMQTTDeviceDebugInfo extends LitElement { haStyleDialog, css` ha-dialog { - --mdc-dialog-max-width: 95%; - --mdc-dialog-min-width: 640px; + --mdc-dialog-max-width: 95vw; + --mdc-dialog-min-width: min(640px, 95vw); + } + @media all and (max-width: 450px), all and (max-height: 500px) { + ha-dialog { + --mdc-dialog-min-width: calc( + 100vw - env(safe-area-inset-right) - env(safe-area-inset-left) + ); + --mdc-dialog-max-width: calc( + 100vw - env(safe-area-inset-right) - env(safe-area-inset-left) + ); + } } ha-switch { margin: 16px; diff --git a/src/panels/config/devices/device-detail/integration-elements/mqtt/mqtt-messages.ts b/src/panels/config/devices/device-detail/integration-elements/mqtt/mqtt-messages.ts index 90bfb086c0..2d3b5acd1f 100644 --- a/src/panels/config/devices/device-detail/integration-elements/mqtt/mqtt-messages.ts +++ b/src/panels/config/devices/device-detail/integration-elements/mqtt/mqtt-messages.ts @@ -55,7 +55,8 @@ class MQTTMessages extends LitElement { ${this.direction} ${formatTimeWithSeconds( new Date(message.time), - this.hass.locale + this.hass.locale, + this.hass.config )}
${this._renderSingleMessage(message)} diff --git a/src/panels/config/devices/device-registry-detail/dialog-device-registry-detail.ts b/src/panels/config/devices/device-registry-detail/dialog-device-registry-detail.ts index 65b4509713..204d115b24 100644 --- a/src/panels/config/devices/device-registry-detail/dialog-device-registry-detail.ts +++ b/src/panels/config/devices/device-registry-detail/dialog-device-registry-detail.ts @@ -67,7 +67,9 @@ class DialogDeviceRegistryDetail extends LitElement {
${this.hass.localize( - "ui.panel.config.devices.enabled_label", + "ui.dialogs.device-registry-detail.enabled_label", "type", this.hass.localize( - `ui.panel.config.devices.type.${ + `ui.dialogs.device-registry-detail.type.${ device.entry_type || "device" }` ) @@ -99,10 +101,10 @@ class DialogDeviceRegistryDetail extends LitElement {
${this._disabledBy && this._disabledBy !== "user" ? this.hass.localize( - "ui.panel.config.devices.enabled_cause", + "ui.dialogs.device-registry-detail.enabled_cause", "type", this.hass.localize( - `ui.panel.config.devices.type.${ + `ui.dialogs.device-registry-detail.type.${ device.entry_type || "device" }` ), @@ -113,7 +115,7 @@ class DialogDeviceRegistryDetail extends LitElement { ) : ""} ${this.hass.localize( - "ui.panel.config.devices.enabled_description" + "ui.dialogs.device-registry-detail.enabled_description" )}
@@ -132,7 +134,7 @@ class DialogDeviceRegistryDetail extends LitElement { @click=${this._updateEntry} .disabled=${this._submitting} > - ${this.hass.localize("ui.panel.config.devices.update")} + ${this.hass.localize("ui.dialogs.device-registry-detail.update")} `; @@ -163,7 +165,7 @@ class DialogDeviceRegistryDetail extends LitElement { } catch (err: any) { this._error = err.message || - this.hass.localize("ui.panel.config.devices.unknown_error"); + this.hass.localize("ui.dialogs.device-registry-detail.unknown_error"); } finally { this._submitting = false; } diff --git a/src/panels/config/entities/entity-registry-settings-editor.ts b/src/panels/config/entities/entity-registry-settings-editor.ts index 84764efc74..a1ce59f5ef 100644 --- a/src/panels/config/entities/entity-registry-settings-editor.ts +++ b/src/panels/config/entities/entity-registry-settings-editor.ts @@ -11,10 +11,12 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; +import { mdiContentCopy } from "@mdi/js"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { fireEvent } from "../../../common/dom/fire_event"; import { stopPropagation } from "../../../common/dom/stop_propagation"; import { computeDomain } from "../../../common/entity/compute_domain"; +import { computeObjectId } from "../../../common/entity/compute_object_id"; import { domainIcon } from "../../../common/entity/domain_icon"; import { supportsFeature } from "../../../common/entity/supports-feature"; import { formatNumber } from "../../../common/number/format_number"; @@ -79,6 +81,8 @@ import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info import { haStyle } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail"; +import { copyToClipboard } from "../../../common/util/copy-clipboard"; +import { showToast } from "../../../util/toast"; const OVERRIDE_DEVICE_CLASSES = { cover: [ @@ -325,8 +329,6 @@ export class EntityRegistrySettingsEditor extends LitElement { const domain = computeDomain(this.entry.entity_id); - const invalidDomainUpdate = computeDomain(this._entityId.trim()) !== domain; - const invalidDefaultCode = domain === "lock" && this._isInvalidDefaultCode( @@ -675,15 +677,23 @@ export class EntityRegistrySettingsEditor extends LitElement { ` : ""} + iconTrailing + > + +
${!this.entry.device_id ? html` { + await copyToClipboard(this._entityId); + showToast(this, { + message: this.hass.localize("ui.common.copied_clipboard"), + }); + } + private _entityIdChanged(ev): void { fireEvent(this, "change"); - this._entityId = ev.target.value; + this._entityId = `${computeDomain(this._origEntityId)}.${ev.target.value}`; } private _deviceClassChanged(ev): void { @@ -1343,6 +1360,20 @@ export class EntityRegistrySettingsEditor extends LitElement { :host { display: block; } + ha-textfield.entityId { + --text-field-prefix-padding-right: 0; + --textfield-icon-trailing-padding: 0; + } + ha-textfield.entityId > ha-icon-button { + position: relative; + right: -8px; + --mdc-icon-button-size: 36px; + --mdc-icon-size: 20px; + color: var(--secondary-text-color); + inset-inline-start: initial; + inset-inline-end: -8px; + direction: var(--direction); + } ha-switch { margin-right: 16px; } diff --git a/src/panels/config/hardware/ha-config-hardware.ts b/src/panels/config/hardware/ha-config-hardware.ts index ee910d622e..a6498ab9bb 100644 --- a/src/panels/config/hardware/ha-config-hardware.ts +++ b/src/panels/config/hardware/ha-config-hardware.ts @@ -170,6 +170,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) { adapters: { date: { locale: this.hass.locale, + config: this.hass.config, }, }, gridLines: { diff --git a/src/panels/config/helpers/dialog-helper-detail.ts b/src/panels/config/helpers/dialog-helper-detail.ts index 7497b3d608..76efa6e17e 100644 --- a/src/panels/config/helpers/dialog-helper-detail.ts +++ b/src/panels/config/helpers/dialog-helper-detail.ts @@ -33,38 +33,59 @@ import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; import { Helper, HelperDomain } from "./const"; -import "./forms/ha-counter-form"; -import "./forms/ha-input_boolean-form"; -import "./forms/ha-input_button-form"; -import "./forms/ha-input_datetime-form"; -import "./forms/ha-input_number-form"; -import "./forms/ha-input_select-form"; -import "./forms/ha-input_text-form"; -import "./forms/ha-schedule-form"; -import "./forms/ha-timer-form"; import type { ShowDialogHelperDetailParams } from "./show-dialog-helper-detail"; type HelperCreators = { - [domain in HelperDomain]: ( - hass: HomeAssistant, - // Not properly typed because there is currently a mismatch for this._item between: - // 1. Type passed to form should be Helper - // 2. Type received by creator should be MutableParams version - // The two are not compatible. - params: any - ) => Promise; + [domain in HelperDomain]: { + create: ( + hass: HomeAssistant, + // Not properly typed because there is currently a mismatch for this._item between: + // 1. Type passed to form should be Helper + // 2. Type received by creator should be MutableParams version + // The two are not compatible. + params: any + ) => Promise; + import: () => Promise; + }; }; const HELPERS: HelperCreators = { - input_boolean: createInputBoolean, - input_button: createInputButton, - input_text: createInputText, - input_number: createInputNumber, - input_datetime: createInputDateTime, - input_select: createInputSelect, - counter: createCounter, - timer: createTimer, - schedule: createSchedule, + input_boolean: { + create: createInputBoolean, + import: () => import("./forms/ha-input_boolean-form"), + }, + input_button: { + create: createInputButton, + import: () => import("./forms/ha-input_button-form"), + }, + input_text: { + create: createInputText, + import: () => import("./forms/ha-input_text-form"), + }, + input_number: { + create: createInputNumber, + import: () => import("./forms/ha-input_number-form"), + }, + input_datetime: { + create: createInputDateTime, + import: () => import("./forms/ha-input_datetime-form"), + }, + input_select: { + create: createInputSelect, + import: () => import("./forms/ha-input_select-form"), + }, + counter: { + create: createCounter, + import: () => import("./forms/ha-counter-form"), + }, + timer: { + create: createTimer, + import: () => import("./forms/ha-timer-form"), + }, + schedule: { + create: createSchedule, + import: () => import("./forms/ha-schedule-form"), + }, }; @customElement("dialog-helper-detail") @@ -85,6 +106,8 @@ export class DialogHelperDetail extends LitElement { @state() private _helperFlows?: string[]; + @state() private _loading = false; + private _params?: ShowDialogHelperDetailParams; public async showDialog(params: ShowDialogHelperDetailParams): Promise { @@ -140,7 +163,7 @@ export class DialogHelperDetail extends LitElement { ${this.hass!.localize("ui.common.back")} `; - } else if (this._helperFlows === undefined) { + } else if (this._loading || this._helperFlows === undefined) { content = html``; } else { const items: [string, string][] = []; @@ -250,7 +273,7 @@ export class DialogHelperDetail extends LitElement { this._submitting = true; this._error = ""; try { - await HELPERS[this._domain](this.hass, this._item); + await HELPERS[this._domain].create(this.hass, this._item); this.closeDialog(); } catch (err: any) { this._error = err.message || "Unknown error"; @@ -259,14 +282,22 @@ export class DialogHelperDetail extends LitElement { } } - private _domainPicked(ev: CustomEvent): void { + private async _domainPicked( + ev: CustomEvent + ): Promise { if (!shouldHandleRequestSelectedEvent(ev)) { return; } const domain = (ev.currentTarget! as any).domain; if (domain in HELPERS) { - this._domain = domain; + this._loading = true; + try { + await HELPERS[domain].import(); + this._domain = domain; + } finally { + this._loading = false; + } this._focusForm(); } else { showConfigFlowDialog(this, { diff --git a/src/panels/config/helpers/forms/ha-schedule-form.ts b/src/panels/config/helpers/forms/ha-schedule-form.ts index 1568452504..e3116d5b21 100644 --- a/src/panels/config/helpers/forms/ha-schedule-form.ts +++ b/src/panels/config/helpers/forms/ha-schedule-form.ts @@ -286,9 +286,9 @@ class HaScheduleForm extends LitElement { const value = [...this[`_${day}`]]; const newValue = { ...this._item }; - const endFormatted = formatTime24h(end); + const endFormatted = formatTime24h(end, this.hass.locale, this.hass.config); value.push({ - from: formatTime24h(start), + from: formatTime24h(start, this.hass.locale, this.hass.config), to: !isSameDay(start, end) || endFormatted === "0:00" ? "24:00" @@ -313,7 +313,7 @@ class HaScheduleForm extends LitElement { const value = this[`_${day}`][parseInt(index)]; const newValue = { ...this._item }; - const endFormatted = formatTime24h(end); + const endFormatted = formatTime24h(end, this.hass.locale, this.hass.config); newValue[day][index] = { from: value.from, to: @@ -338,9 +338,9 @@ class HaScheduleForm extends LitElement { const newDay = weekdays[start.getDay()]; const newValue = { ...this._item }; - const endFormatted = formatTime24h(end); + const endFormatted = formatTime24h(end, this.hass.locale, this.hass.config); const event = { - from: formatTime24h(start), + from: formatTime24h(start, this.hass.locale, this.hass.config), to: !isSameDay(start, end) || endFormatted === "0:00" ? "24:00" diff --git a/src/panels/config/integrations/dialog-add-integration.ts b/src/panels/config/integrations/dialog-add-integration.ts index 1798614a68..02935336c0 100644 --- a/src/panels/config/integrations/dialog-add-integration.ts +++ b/src/panels/config/integrations/dialog-add-integration.ts @@ -1,6 +1,7 @@ import "@material/mwc-button"; import "@material/mwc-list/mwc-list"; import Fuse from "fuse.js"; +import { HassConfig } from "home-assistant-js-websocket"; import { css, html, @@ -158,7 +159,7 @@ class AddIntegrationDialog extends LitElement { ( i: Brands, h: Integrations, - components: HomeAssistant["config"]["components"], + components: HassConfig["components"], localize: LocalizeFunc, filter?: string ): IntegrationListItem[] => { diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 672b6c26a8..cc4819c05f 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -898,10 +898,14 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { if (!this.domain || !isComponentLoaded(this.hass, "diagnostics")) { return; } - this._diagnosticHandler = await fetchDiagnosticHandler( - this.hass, - this.domain - ); + try { + this._diagnosticHandler = await fetchDiagnosticHandler( + this.hass, + this.domain + ); + } catch (err: any) { + // No issue, as diagnostics are not required + } } private async _handleEnableDebugLogging() { diff --git a/src/panels/config/integrations/ha-config-integrations-dashboard.ts b/src/panels/config/integrations/ha-config-integrations-dashboard.ts index 5524c31d26..179167cd22 100644 --- a/src/panels/config/integrations/ha-config-integrations-dashboard.ts +++ b/src/panels/config/integrations/ha-config-integrations-dashboard.ts @@ -376,10 +376,9 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
`} - -
- ${this._showIgnored - ? ignoredConfigEntries.map( + ${this._showIgnored + ? html`
+ ${ignoredConfigEntries.map( (entry: ConfigEntryExtended) => html` ` - ) - : ""} - ${configEntriesInProgress.length - ? configEntriesInProgress.map( + )} +
` + : ""} + ${configEntriesInProgress.length + ? html`
+ ${configEntriesInProgress.map( (flow: DataEntryFlowProgressExtended) => html` ` - ) - : ""} - ${this._showDisabled - ? disabledConfigEntries.map( + )} +
` + : ""} + ${this._showDisabled + ? html`
+ ${disabledConfigEntries.map( (entry: ConfigEntryExtended) => html` ` - ) - : ""} + )} +
` + : ""} +
${integrations.length ? integrations.map( ([domain, items]) => @@ -744,6 +749,8 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); grid-gap: 16px 16px; padding: 8px 16px 16px; + } + .container:last-of-type { margin-bottom: 64px; } .container > * { diff --git a/src/panels/config/integrations/ha-config-integrations.ts b/src/panels/config/integrations/ha-config-integrations.ts index 7604d69d36..42ff9330b4 100644 --- a/src/panels/config/integrations/ha-config-integrations.ts +++ b/src/panels/config/integrations/ha-config-integrations.ts @@ -135,13 +135,13 @@ class HaConfigIntegrations extends SubscribeMixin(HassRouterPage) { integrations.add(flow.handler); } }); - await this.hass.loadBackendTranslation( + const localize = await this.hass.loadBackendTranslation( "config", Array.from(integrations) ); this._configEntriesInProgress = flowsInProgress.map((flow) => ({ ...flow, - localized_title: localizeConfigFlowTitle(this.hass.localize, flow), + localized_title: localizeConfigFlowTitle(localize, flow), })); }), ]; diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index 9a620e3ed3..7866cd8343 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -1,19 +1,29 @@ import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; import "@material/mwc-button"; import "@material/mwc-list"; +import "@material/mwc-ripple"; +import type { Ripple } from "@material/mwc-ripple"; +import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers"; +import { mdiCloud, mdiPackageVariant } from "@mdi/js"; import { - mdiCogOutline, - mdiDevices, - mdiHandExtendedOutline, - mdiPuzzleOutline, - mdiShapeOutline, -} from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property } from "lit/decorators"; + CSSResultGroup, + LitElement, + TemplateResult, + css, + html, + nothing, +} from "lit"; +import { + customElement, + eventOptions, + property, + queryAsync, + state, +} from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; +import { computeRTL } from "../../../common/util/compute_rtl"; import "../../../components/ha-card"; -import "../../../components/ha-icon-button"; import "../../../components/ha-icon-next"; import "../../../components/ha-list-item"; import "../../../components/ha-svg-icon"; @@ -47,8 +57,12 @@ export class HaIntegrationCard extends LitElement { @property() public logInfo?: IntegrationLogInfo; + @queryAsync("mwc-ripple") private _ripple!: Promise; + + @state() private _shouldRenderRipple = false; + protected render(): TemplateResult { - const state = this._getState(this.items); + const entryState = this._getState(this.items); const debugLoggingEnabled = this.logInfo && this.logInfo.level === LogSeverity.DEBUG; @@ -57,22 +71,35 @@ export class HaIntegrationCard extends LitElement { - + + ${this._shouldRenderRipple ? html`` : ""} - + .label=${this.hass.localize( + "ui.panel.config.integrations.config_entry.configure" + )} + > @@ -102,18 +131,14 @@ export class HaIntegrationCard extends LitElement { const services = !devices.some((device) => device.entry_type !== "service"); return html` -
+
${devices.length > 0 ? html` - - + ${this.hass.localize( `ui.panel.config.integrations.config_entry.${ services ? "services" : "devices" @@ -121,40 +146,57 @@ export class HaIntegrationCard extends LitElement { "count", devices.length )} - - + ` : entities.length > 0 ? html` - - + ${this.hass.localize( `ui.panel.config.integrations.config_entry.entities`, "count", entities.length )} - - + ` : html` - - + ${this.hass.localize( `ui.panel.config.integrations.config_entry.entries`, "count", this.items.length )} - - + `} +
+ ${this.manifest && !this.manifest.is_built_in + ? html` + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.custom_integration" + )} + ` + : nothing} + ${this.manifest && this.manifest.iot_class?.startsWith("cloud_") + ? html`
+ + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.depends_on_cloud" + )} +
` + : nothing} +
`; } @@ -164,14 +206,14 @@ export class HaIntegrationCard extends LitElement { if (configEntry.length === 1) { return configEntry[0].state; } - let state: ConfigEntry["state"]; + let entryState: ConfigEntry["state"]; for (const entry of configEntry) { if (ERROR_STATES.includes(entry.state)) { return entry.state; } - state = entry.state; + entryState = entry.state; } - return state!; + return entryState!; } ); @@ -206,6 +248,36 @@ export class HaIntegrationCard extends LitElement { } ); + private _rippleHandlers: RippleHandlers = new RippleHandlers(() => { + this._shouldRenderRipple = true; + return this._ripple; + }); + + @eventOptions({ passive: true }) + private handleRippleActivate(evt?: Event) { + this._rippleHandlers.startPress(evt); + } + + private handleRippleDeactivate() { + this._rippleHandlers.endPress(); + } + + private handleRippleFocus() { + this._rippleHandlers.startFocus(); + } + + private handleRippleBlur() { + this._rippleHandlers.endFocus(); + } + + protected handleRippleMouseEnter() { + this._rippleHandlers.startHover(); + } + + protected handleRippleMouseLeave() { + this._rippleHandlers.endHover(); + } + static get styles(): CSSResultGroup { return [ haStyle, @@ -213,12 +285,25 @@ export class HaIntegrationCard extends LitElement { ha-card { display: flex; flex-direction: column; + justify-content: space-between; height: 100%; overflow: hidden; --state-color: var(--divider-color, #e0e0e0); --ha-card-border-color: var(--state-color); --state-message-color: var(--state-color); } + .ripple-anchor { + flex-grow: 1; + position: relative; + } + ha-integration-header { + height: 100%; + } + .card-actions { + display: flex; + align-items: center; + justify-content: space-between; + } .debug-logging { --state-color: var(--warning-color); --text-on-state-color: var(--primary-text-color); @@ -251,9 +336,32 @@ export class HaIntegrationCard extends LitElement { text-decoration: none; color: var(--primary-text-color); } - a ha-icon-button { + a ha-icon-next { color: var(--secondary-text-color); } + .icons { + display: flex; + } + .icon { + border-radius: 50%; + color: var(--text-primary-color); + padding: 4px; + margin-left: 8px; + } + .icon.cloud { + background: var(--info-color); + } + .icon.custom { + background: var(--warning-color); + } + .icon ha-svg-icon { + width: 16px; + height: 16px; + display: block; + } + simple-tooltip { + white-space: nowrap; + } `, ]; } diff --git a/src/panels/config/integrations/ha-integration-header.ts b/src/panels/config/integrations/ha-integration-header.ts index b151114c61..16085d5ead 100644 --- a/src/panels/config/integrations/ha-integration-header.ts +++ b/src/panels/config/integrations/ha-integration-header.ts @@ -1,11 +1,7 @@ -import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; -import { mdiCloud, mdiPackageVariant } from "@mdi/js"; -import { css, html, LitElement, TemplateResult } from "lit"; +import { LitElement, TemplateResult, css, html } from "lit"; import { customElement, property } from "lit/decorators"; -import { classMap } from "lit/directives/class-map"; -import { computeRTL } from "../../../common/util/compute_rtl"; import "../../../components/ha-svg-icon"; -import { domainToName, IntegrationManifest } from "../../../data/integration"; +import { IntegrationManifest, domainToName } from "../../../data/integration"; import { HomeAssistant } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; @@ -23,8 +19,6 @@ export class HaIntegrationHeader extends LitElement { @property({ attribute: false }) public manifest?: IntegrationManifest; - @property({ attribute: false }) public debugLoggingEnabled?: boolean; - protected render(): TemplateResult { let primary: string; let secondary: string | undefined; @@ -43,31 +37,8 @@ export class HaIntegrationHeader extends LitElement { primary = domainName; } - const icons: [string, string][] = []; - - if (this.manifest) { - if (!this.manifest.is_built_in) { - icons.push([ - mdiPackageVariant, - this.hass.localize( - "ui.panel.config.integrations.config_entry.custom_integration" - ), - ]); - } - - if (this.manifest.iot_class?.startsWith("cloud_")) { - icons.push([ - mdiCloud, - this.hass.localize( - "ui.panel.config.integrations.config_entry.depends_on_cloud" - ), - ]); - } - } - return html` ${!this.banner ? "" : html``} -
- ${icons.length === 0 - ? "" - : html` -
- ${icons.map( - ([icon, description]) => html` - - - ${description} - - ` - )} -
- `}
${primary}
${secondary}
@@ -139,14 +84,13 @@ export class HaIntegrationHeader extends LitElement { .header { display: flex; position: relative; - padding-top: 0px; - padding-bottom: 8px; + padding-top: 16px; + padding-bottom: 16px; padding-inline-start: 16px; padding-inline-end: 8px; direction: var(--direction); } .header img { - margin-top: 16px; margin-inline-start: initial; margin-inline-end: 16px; width: 40px; @@ -166,11 +110,13 @@ export class HaIntegrationHeader extends LitElement { text-overflow: ellipsis; } .header-button { - margin-top: 8px; + display: flex; + align-items: center; + justify-content: center; + width: 36px; } .primary { font-size: 16px; - margin-top: 16px; font-weight: 400; word-break: break-word; color: var(--primary-text-color); @@ -179,41 +125,6 @@ export class HaIntegrationHeader extends LitElement { font-size: 14px; color: var(--secondary-text-color); } - .icons { - background: var(--warning-color); - border: 1px solid var(--card-background-color); - border-radius: 14px; - color: var(--text-primary-color); - position: absolute; - left: 40px; - top: 40px; - display: flex; - padding: 4px; - inset-inline-start: 40px; - inset-inline-end: initial; - } - .icons.cloud { - background: var(--info-color); - } - .icons.double { - background: var(--warning-color); - left: 28px; - inset-inline-start: 28px; - inset-inline-end: initial; - } - .icons ha-svg-icon { - width: 16px; - height: 16px; - display: block; - } - .icons span:not(:first-child) ha-svg-icon { - margin-left: 4px; - margin-inline-start: 4px; - margin-inline-end: inherit; - } - simple-tooltip { - white-space: nowrap; - } `; } diff --git a/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts b/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts index 297ad0c9d9..032ebf38ed 100644 --- a/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts +++ b/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts @@ -1,7 +1,7 @@ import "@material/mwc-button"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; -import { LocalStorage } from "../../../../../common/decorators/local-storage"; +import { storage } from "../../../../../common/decorators/storage"; import "../../../../../components/ha-card"; import "../../../../../components/ha-code-editor"; import "../../../../../components/ha-formfield"; @@ -21,19 +21,39 @@ class HaPanelDevMqtt extends LitElement { @property({ type: Boolean }) public narrow!: boolean; - @LocalStorage("panel-dev-mqtt-topic-ls", true, false) + @storage({ + key: "panel-dev-mqtt-topic-ls", + state: true, + subscribe: false, + }) private _topic = ""; - @LocalStorage("panel-dev-mqtt-payload-ls", true, false) + @storage({ + key: "panel-dev-mqtt-payload-ls", + state: true, + subscribe: false, + }) private _payload = ""; - @LocalStorage("panel-dev-mqtt-qos-ls", true, false) + @storage({ + key: "panel-dev-mqtt-qos-ls", + state: true, + subscribe: false, + }) private _qos = "0"; - @LocalStorage("panel-dev-mqtt-retain-ls", true, false) + @storage({ + key: "panel-dev-mqtt-retain-ls", + state: true, + subscribe: false, + }) private _retain = false; - @LocalStorage("panel-dev-mqtt-allow-template-ls", true, false) + @storage({ + key: "panel-dev-mqtt-allow-template-ls", + state: true, + subscribe: false, + }) private _allowTemplate = false; protected render(): TemplateResult { diff --git a/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts b/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts index ebc0e1a123..b1bf864377 100644 --- a/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts +++ b/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts @@ -8,7 +8,7 @@ import { formatTime } from "../../../../../common/datetime/format_time"; import { MQTTMessage, subscribeMQTTTopic } from "../../../../../data/mqtt"; import { HomeAssistant } from "../../../../../types"; import "@material/mwc-list/mwc-list-item"; -import { LocalStorage } from "../../../../../common/decorators/local-storage"; +import { storage } from "../../../../../common/decorators/storage"; import "../../../../../components/ha-formfield"; import "../../../../../components/ha-switch"; @@ -18,13 +18,25 @@ const qosLevel = ["0", "1", "2"]; class MqttSubscribeCard extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @LocalStorage("panel-dev-mqtt-topic-subscribe", true, false) + @storage({ + key: "panel-dev-mqtt-topic-subscribe", + state: true, + subscribe: false, + }) private _topic = ""; - @LocalStorage("panel-dev-mqtt-qos-subscribe", true, false) + @storage({ + key: "panel-dev-mqtt-qos-subscribe", + state: true, + subscribe: false, + }) private _qos = "0"; - @LocalStorage("panel-dev-mqtt-json-format", true, false) + @storage({ + key: "panel-dev-mqtt-json-format", + state: true, + subscribe: false, + }) private _json_format = false; @state() private _subscribed?: () => void; @@ -105,7 +117,7 @@ class MqttSubscribeCard extends LitElement { "topic", msg.message.topic, "time", - formatTime(msg.time, this.hass!.locale) + formatTime(msg.time, this.hass!.locale, this.hass!.config) )}
${msg.payload}
diff --git a/src/panels/config/logs/dialog-system-log-detail.ts b/src/panels/config/logs/dialog-system-log-detail.ts index 49891035ef..4856efdb98 100644 --- a/src/panels/config/logs/dialog-system-log-detail.ts +++ b/src/panels/config/logs/dialog-system-log-detail.ts @@ -145,12 +145,20 @@ class DialogSystemLogDetail extends LitElement { ${item.count > 0 ? html` First occurred: - ${formatSystemLogTime(item.first_occurred, this.hass!.locale)} + ${formatSystemLogTime( + item.first_occurred, + this.hass!.locale, + this.hass!.config + )} (${item.count} occurrences)
` : ""} Last logged: - ${formatSystemLogTime(item.timestamp, this.hass!.locale)} + ${formatSystemLogTime( + item.timestamp, + this.hass!.locale, + this.hass!.config + )}

${item.message.length > 1 ? html` diff --git a/src/panels/config/logs/system-log-card.ts b/src/panels/config/logs/system-log-card.ts index beec07abaf..e05da8a75d 100644 --- a/src/panels/config/logs/system-log-card.ts +++ b/src/panels/config/logs/system-log-card.ts @@ -38,14 +38,22 @@ export class SystemLogCard extends LitElement { } private _timestamp(item: LoggedError): string { - return formatSystemLogTime(item.timestamp, this.hass!.locale); + return formatSystemLogTime( + item.timestamp, + this.hass.locale, + this.hass.config + ); } private _multipleMessages(item: LoggedError): string { return this.hass.localize( "ui.panel.config.logs.multiple_messages", "time", - formatSystemLogTime(item.first_occurred, this.hass!.locale), + formatSystemLogTime( + item.first_occurred, + this.hass.locale, + this.hass.config + ), "counter", item.count ); diff --git a/src/panels/config/logs/util.ts b/src/panels/config/logs/util.ts index 3eb92fcd96..4cf0d7f704 100644 --- a/src/panels/config/logs/util.ts +++ b/src/panels/config/logs/util.ts @@ -1,13 +1,18 @@ +import { HassConfig } from "home-assistant-js-websocket"; import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time"; import { formatTimeWithSeconds } from "../../../common/datetime/format_time"; import { FrontendLocaleData } from "../../../data/translation"; -export const formatSystemLogTime = (date, locale: FrontendLocaleData) => { +export const formatSystemLogTime = ( + date, + locale: FrontendLocaleData, + config: HassConfig +) => { const today = new Date().setHours(0, 0, 0, 0); const dateTime = new Date(date * 1000); const dateTimeDay = new Date(date * 1000).setHours(0, 0, 0, 0); return dateTimeDay < today - ? formatDateTimeWithSeconds(dateTime, locale) - : formatTimeWithSeconds(dateTime, locale); + ? formatDateTimeWithSeconds(dateTime, locale, config) + : formatTimeWithSeconds(dateTime, locale, config); }; diff --git a/src/panels/config/repairs/dialog-repairs-issue.ts b/src/panels/config/repairs/dialog-repairs-issue.ts index 3aae980d29..4e628ccbd0 100644 --- a/src/panels/config/repairs/dialog-repairs-issue.ts +++ b/src/panels/config/repairs/dialog-repairs-issue.ts @@ -100,7 +100,8 @@ class DialogRepairsIssue extends LitElement { ${this._issue.created ? formatDateNumeric( new Date(this._issue.created), - this.hass.locale + this.hass.locale, + this.hass.config ) : ""}
diff --git a/src/panels/config/repairs/dialog-system-information.ts b/src/panels/config/repairs/dialog-system-information.ts index ce2bfefd5a..bc3b1e0181 100644 --- a/src/panels/config/repairs/dialog-system-information.ts +++ b/src/panels/config/repairs/dialog-system-information.ts @@ -349,7 +349,11 @@ class DialogSystemInformation extends LitElement { `} `; } else if (info.type === "date") { - value = formatDateTime(new Date(info.value), this.hass.locale); + value = formatDateTime( + new Date(info.value), + this.hass.locale, + this.hass.config + ); } } else { value = domainInfo.info[key]; @@ -425,7 +429,11 @@ class DialogSystemInformation extends LitElement { } else if (info.type === "failed") { value = `failed to load: ${info.error}`; } else if (info.type === "date") { - value = formatDateTime(new Date(info.value), this.hass.locale); + value = formatDateTime( + new Date(info.value), + this.hass.locale, + this.hass.config + ); } } else { value = domainInfo.info[key]; diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index 11762b419b..df9f6be040 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -118,7 +118,11 @@ class HaSceneDashboard extends LitElement { return html` ${last_activated && !isUnavailableState(last_activated) ? dayDifference > 3 - ? formatShortDateTime(date, this.hass.locale) + ? formatShortDateTime( + date, + this.hass.locale, + this.hass.config + ) : relativeTime(date, this.hass.locale) : this.hass.localize("ui.components.relative_time.never")} `; diff --git a/src/panels/config/script/blueprint-script-editor.ts b/src/panels/config/script/blueprint-script-editor.ts index 453a639746..7a71170b98 100644 --- a/src/panels/config/script/blueprint-script-editor.ts +++ b/src/panels/config/script/blueprint-script-editor.ts @@ -114,6 +114,7 @@ export class HaBlueprintScriptEditor extends LitElement { .selector=${value.selector} .key=${key} .disabled=${this.disabled} + .required=${value?.default === undefined} .value=${(this.config.use_blueprint.input && this.config.use_blueprint.input[key]) ?? value?.default} @@ -121,7 +122,7 @@ export class HaBlueprintScriptEditor extends LitElement { >` : html` + ${this._errors || stateObj?.state === UNAVAILABLE + ? html` + + ${this._errors || this._validationErrors} + + ` + : ""} + ${this._readOnly + ? html` + ${this.hass.localize("ui.panel.config.script.editor.read_only")} + + ${this.hass.localize("ui.panel.config.script.editor.migrate")} + + ` + : ""} ${this._mode === "gui" ? html`
- ${this._errors - ? html` - - ${this._errors} - - ` - : ""}
- ${"use_blueprint" in this._config + ${useBlueprint ? html` - ${this.hass.localize( - "ui.panel.config.script.editor.read_only" - )} - - ${this.hass.localize( - "ui.panel.config.script.editor.migrate" - )} - - ` - : ""} - ${this._errors - ? html` - ${this._errors} - ` - : ""} + ent.platform === "script" && ent.unique_id === this.scriptId + ); + this._entityId = entity?.entity_id; + this._checkValidation(); }, (resp) => { const entity = this.entityRegistry.find( @@ -477,6 +491,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { if (changedProps.has("entityId") && this.entityId) { getScriptStateConfig(this.hass, this.entityId).then((c) => { this._config = this._normalizeConfig(c.config); + this._checkValidation(); }); const regEntry = this.entityRegistry.find( (ent) => ent.entity_id === this.entityId @@ -500,6 +515,28 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { return config; } + private async _checkValidation() { + this._validationErrors = undefined; + if (!this._entityId || !this._config) { + return; + } + const stateObj = this.hass.states[this._entityId]; + if (stateObj?.state !== UNAVAILABLE) { + return; + } + const validation = await validateConfig(this.hass, { + action: this._config.sequence, + }); + this._validationErrors = Object.entries(validation).map(([key, value]) => + value.valid + ? "" + : html`${this.hass.localize( + `ui.panel.config.automation.editor.${key}s.header` + )}: + ${value.error}
` + ); + } + private _computeLabelCallback = ( schema: SchemaUnion>, data: HaFormDataContainer diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index f5d80a60be..7433bb98e5 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -12,6 +12,8 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { differenceInDays } from "date-fns/esm"; +import { styleMap } from "lit/directives/style-map"; +import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { relativeTime } from "../../../common/datetime/relative_time"; import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event"; @@ -44,9 +46,11 @@ import { HomeAssistant, Route } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { showToast } from "../../../util/toast"; import { configSections } from "../ha-panel-config"; +import { showNewAutomationDialog } from "../automation/show-dialog-new-automation"; import { EntityRegistryEntry } from "../../../data/entity_registry"; import { findRelated } from "../../../data/search"; import { fetchBlueprints } from "../../../data/blueprint"; +import { UNAVAILABLE } from "../../../data/entity"; @customElement("ha-script-picker") class HaScriptPicker extends LitElement { @@ -98,7 +102,13 @@ class HaScriptPicker extends LitElement { ), type: "icon", template: (_icon, script) => - html``, + html``, }, name: { title: this.hass.localize("ui.panel.config.script.picker.headers.name"), @@ -118,7 +128,11 @@ class HaScriptPicker extends LitElement { ${this.hass.localize("ui.card.automation.last_triggered")}: ${script.attributes.last_triggered ? dayDifference > 3 - ? formatShortDateTime(date, this.hass.locale) + ? formatShortDateTime( + date, + this.hass.locale, + this.hass.config + ) : relativeTime(date, this.hass.locale) : this.hass.localize("ui.components.relative_time.never")}
@@ -139,7 +153,7 @@ class HaScriptPicker extends LitElement { return html` ${last_triggered ? dayDifference > 3 - ? formatShortDateTime(date, this.hass.locale) + ? formatShortDateTime(date, this.hass.locale, this.hass.config) : relativeTime(date, this.hass.locale) : this.hass.localize("ui.components.relative_time.never")} `; @@ -238,19 +252,19 @@ class HaScriptPicker extends LitElement { @related-changed=${this._relatedFilterChanged} > - - - - - + + + `; } @@ -308,6 +322,14 @@ class HaScriptPicker extends LitElement { } } + private _createNew() { + if (isComponentLoaded(this.hass, "blueprint")) { + showNewAutomationDialog(this, { mode: "script" }); + } else { + navigate("/config/script/edit/new"); + } + } + private _runScript = async (script: any) => { const entry = this.entityRegistry.find( (e) => e.entity_id === script.entity_id diff --git a/src/panels/config/script/ha-script-trace.ts b/src/panels/config/script/ha-script-trace.ts index 54bb1542d2..dd410b745a 100644 --- a/src/panels/config/script/ha-script-trace.ts +++ b/src/panels/config/script/ha-script-trace.ts @@ -191,7 +191,8 @@ export class HaScriptTrace extends LitElement { html`` )} diff --git a/src/panels/config/script/manual-script-editor.ts b/src/panels/config/script/manual-script-editor.ts index 333c052500..d7d7ced9cf 100644 --- a/src/panels/config/script/manual-script-editor.ts +++ b/src/panels/config/script/manual-script-editor.ts @@ -1,13 +1,10 @@ import "@material/mwc-button/mwc-button"; import { mdiHelpCircle } from "@mdi/js"; -import deepClone from "deep-clone-simple"; import { css, CSSResultGroup, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; -import { LocalStorage } from "../../../common/decorators/local-storage"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-card"; import "../../../components/ha-icon-button"; -import { Clipboard } from "../../../data/automation"; import { Action, ScriptConfig } from "../../../data/script"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; @@ -26,9 +23,6 @@ export class HaManualScriptEditor extends LitElement { @property({ attribute: false }) public config!: ScriptConfig; - @LocalStorage("automationClipboard", true, false, window.sessionStorage) - private _clipboard: Clipboard = {}; - protected render() { return html` ${this.disabled @@ -65,8 +59,6 @@ export class HaManualScriptEditor extends LitElement { .hass=${this.hass} .narrow=${this.narrow} .disabled=${this.disabled} - @set-clipboard=${this._setClipboard} - .clipboard=${this._clipboard} > `; } @@ -78,11 +70,6 @@ export class HaManualScriptEditor extends LitElement { }); } - private _setClipboard(ev: CustomEvent): void { - ev.stopPropagation(); - this._clipboard = { ...this._clipboard, ...deepClone(ev.detail) }; - } - private _duplicate() { fireEvent(this, "duplicate"); } diff --git a/src/panels/config/storage/dialog-mount-view.ts b/src/panels/config/storage/dialog-mount-view.ts index 624d4889a6..e0b0b6302d 100644 --- a/src/panels/config/storage/dialog-mount-view.ts +++ b/src/panels/config/storage/dialog-mount-view.ts @@ -1,8 +1,10 @@ import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; +import { mdiHelpCircle } from "@mdi/js"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-form/ha-form"; +import "../../../components/ha-icon-button"; import { extractApiErrorMessage } from "../../../data/hassio/common"; import { createSupervisorMount, @@ -17,12 +19,15 @@ import { HomeAssistant } from "../../../types"; import { MountViewDialogParams } from "./show-dialog-view-mount"; import { LocalizeFunc } from "../../../common/translations/localize"; import type { SchemaUnion } from "../../../components/ha-form/types"; +import { documentationUrl } from "../../../util/documentation-url"; +import { computeRTLDirection } from "../../../common/util/compute_rtl"; const mountSchema = memoizeOne( ( localize: LocalizeFunc, existing?: boolean, - mountType?: SupervisorMountType + mountType?: SupervisorMountType, + showCIFSVersion?: boolean ) => [ { @@ -86,6 +91,41 @@ const mountSchema = memoizeOne( ] as const) : mountType === "cifs" ? ([ + ...(showCIFSVersion + ? ([ + { + name: "version", + required: true, + selector: { + select: { + options: [ + { + label: localize( + "ui.panel.config.storage.network_mounts.cifs_versions.auto" + ), + value: "auto", + }, + { + label: localize( + "ui.panel.config.storage.network_mounts.cifs_versions.legacy", + { version: "2.0" } + ), + value: "2.0", + }, + { + label: localize( + "ui.panel.config.storage.network_mounts.cifs_versions.legacy", + { version: "1.0" } + ), + value: "1.0", + }, + ], + mode: "dropdown", + }, + }, + }, + ] as const) + : ([] as const)), { name: "share", required: true, @@ -118,8 +158,12 @@ class ViewMountDialog extends LitElement { @state() private _validationError?: Record; + @state() private _validationWarning?: Record; + @state() private _existing?: boolean; + @state() private _showCIFSVersion?: boolean; + @state() private _reloadMounts?: () => void; public async showDialog( @@ -128,6 +172,13 @@ class ViewMountDialog extends LitElement { this._data = dialogParams.mount; this._existing = dialogParams.mount !== undefined; this._reloadMounts = dialogParams.reloadMounts; + if ( + dialogParams.mount?.type === "cifs" && + dialogParams.mount.version && + dialogParams.mount.version !== "auto" + ) { + this._showCIFSVersion = true; + } } public closeDialog(): void { @@ -135,7 +186,9 @@ class ViewMountDialog extends LitElement { this._waiting = undefined; this._error = undefined; this._validationError = undefined; + this._validationWarning = undefined; this._existing = undefined; + this._showCIFSVersion = undefined; this._reloadMounts = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -158,6 +211,33 @@ class ViewMountDialog extends LitElement { )} @closed=${this.closeDialog} > + + ${this._existing + ? this.hass.localize( + "ui.panel.config.storage.network_mounts.update_title" + ) + : this.hass.localize( + "ui.panel.config.storage.network_mounts.add_title" + )} + + + + + ${this._error ? html`${this._error}` : nothing} @@ -166,12 +246,15 @@ class ViewMountDialog extends LitElement { .schema=${mountSchema( this.hass.localize, this._existing, - this._data?.type + this._data?.type, + this._showCIFSVersion )} .error=${this._validationError} + .warning=${this._validationWarning} .computeLabel=${this._computeLabelCallback} .computeHelper=${this._computeHelperCallback} .computeError=${this._computeErrorCallback} + .computeWarning=${this._computeWarningCallback} @value-changed=${this._valueChanged} dialogInitialFocus > @@ -225,12 +308,29 @@ class ViewMountDialog extends LitElement { `ui.panel.config.storage.network_mounts.errors.${error}` ) || error; + private _computeWarningCallback = (warning: string): string => + this.hass.localize( + // @ts-ignore + `ui.panel.config.storage.network_mounts.warnings.${warning}` + ) || warning; + private _valueChanged(ev: CustomEvent) { this._validationError = {}; + this._validationWarning = {}; this._data = ev.detail.value; if (this._data?.name && !/^\w+$/.test(this._data.name)) { this._validationError.name = "invalid_name"; } + if (this._data?.type === "cifs" && !this._data.version) { + this._data.version = "auto"; + } + if ( + this._data?.type === "cifs" && + this._data.version && + ["1.0", "2.0"].includes(this._data.version) + ) { + this._validationWarning.version = "not_recomeded_cifs_version"; + } } private async _connectMount() { @@ -245,6 +345,9 @@ class ViewMountDialog extends LitElement { } catch (err: any) { this._error = extractApiErrorMessage(err); this._waiting = false; + if (this._data!.type === "cifs" && !this._showCIFSVersion) { + this._showCIFSVersion = true; + } return; } if (this._reloadMounts) { @@ -274,6 +377,9 @@ class ViewMountDialog extends LitElement { haStyle, haStyleDialog, css` + ha-icon-button { + color: var(--primary-text-color); + } .delete-btn { --mdc-theme-primary: var(--error-color); } diff --git a/src/panels/config/users/dialog-add-user.ts b/src/panels/config/users/dialog-add-user.ts index c8154991ea..345dfcfc1a 100644 --- a/src/panels/config/users/dialog-add-user.ts +++ b/src/panels/config/users/dialog-add-user.ts @@ -1,5 +1,4 @@ import "@material/mwc-button"; -import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, @@ -14,6 +13,7 @@ import "../../../components/ha-circular-progress"; import { createCloseHeading } from "../../../components/ha-dialog"; import "../../../components/ha-formfield"; import "../../../components/ha-switch"; +import type { HaSwitch } from "../../../components/ha-switch"; import { createAuthForUser } from "../../../data/auth"; import { createUser, @@ -25,6 +25,8 @@ import { import { ValueChangedEvent, HomeAssistant } from "../../../types"; import { haStyleDialog } from "../../../resources/styles"; import { AddUserDialogParams } from "./show-dialog-add-user"; +import "../../../components/ha-textfield"; +import type { HaTextField } from "../../../components/ha-textfield"; @customElement("dialog-add-user") export class DialogAddUser extends LitElement { @@ -97,7 +99,7 @@ export class DialogAddUser extends LitElement {
${this._error ? html`
${this._error}
` : ""} ${this._allowChangeName - ? html` ` + >` : ""} - + > - + > - + >
): void { this._error = undefined; - const name = (ev.target as any).name; - this[`_${name}`] = ev.detail.value; + const target = ev.target as HaTextField; + this[`_${target.name}`] = target.value; } - private async _adminChanged(ev): Promise { - this._isAdmin = ev.target.checked; + private async _adminChanged(ev: Event): Promise { + const target = ev.target as HaSwitch; + this._isAdmin = target.checked; } - private _localOnlyChanged(ev): void { - this._localOnly = ev.target.checked; + private _localOnlyChanged(ev: Event): void { + const target = ev.target as HaSwitch; + this._localOnly = target.checked; } - private async _createUser(ev) { + private async _createUser(ev: Event) { ev.preventDefault(); if (!this._name || !this._username || !this._password) { return; @@ -299,6 +298,10 @@ export class DialogAddUser extends LitElement { display: flex; padding: 8px 0; } + ha-textfield { + display: block; + margin-bottom: 16px; + } `, ]; } diff --git a/src/panels/config/voice-assistants/debug/assist-pipeline-debug.ts b/src/panels/config/voice-assistants/debug/assist-pipeline-debug.ts index 638fad5c3c..9fb7782474 100644 --- a/src/panels/config/voice-assistants/debug/assist-pipeline-debug.ts +++ b/src/panels/config/voice-assistants/debug/assist-pipeline-debug.ts @@ -35,6 +35,8 @@ export class AssistPipelineDebug extends LitElement { @state() private _events?: PipelineRunEvent[]; + private _unsubRefreshEventsID?: number; + protected render() { return html` ${formatDateTimeWithSeconds( new Date(run.timestamp), - this.hass.locale + this.hass.locale, + this.hass.config )} ` )} @@ -93,11 +96,27 @@ export class AssistPipelineDebug extends LitElement { } protected willUpdate(changedProperties) { + let clearRefresh = false; + if (changedProperties.has("pipelineId")) { this._fetchRuns(); + clearRefresh = true; } if (changedProperties.has("_runId")) { this._fetchEvents(); + clearRefresh = true; + } + if (clearRefresh && this._unsubRefreshEventsID) { + clearTimeout(this._unsubRefreshEventsID); + this._unsubRefreshEventsID = undefined; + } + } + + public disconnectedCallback(): void { + super.disconnectedCallback(); + if (this._unsubRefreshEventsID) { + clearTimeout(this._unsubRefreshEventsID); + this._unsubRefreshEventsID = undefined; } } @@ -143,6 +162,17 @@ export class AssistPipelineDebug extends LitElement { title: "Failed to fetch events", text: e.message, }); + return; + } + if ( + this._events?.length && + // If the last event is not a finish run event, the run is still ongoing. + // Refresh events automatically. + !["run-end", "error"].includes(this._events[this._events.length - 1].type) + ) { + this._unsubRefreshEventsID = window.setTimeout(() => { + this._fetchEvents(); + }, 2000); } } diff --git a/src/panels/config/voice-assistants/debug/assist-render-pipeline-run.ts b/src/panels/config/voice-assistants/debug/assist-render-pipeline-run.ts index 739a829d75..f30d5cf5d7 100644 --- a/src/panels/config/voice-assistants/debug/assist-render-pipeline-run.ts +++ b/src/panels/config/voice-assistants/debug/assist-render-pipeline-run.ts @@ -200,7 +200,7 @@ export class AssistPipelineDebug extends LitElement {
- Speech-to-Text + Speech-to-text ${renderProgress(this.hass, this.pipelineRun, "stt")}
${this.pipelineRun.stt @@ -274,7 +274,7 @@ export class AssistPipelineDebug extends LitElement {
- Text-to-Speech + Text-to-speech ${renderProgress(this.hass, this.pipelineRun, "tts")}
${this.pipelineRun.tts diff --git a/src/panels/developer-tools/assist/developer-tools-assist.ts b/src/panels/developer-tools/assist/developer-tools-assist.ts new file mode 100644 index 0000000000..9080e13394 --- /dev/null +++ b/src/panels/developer-tools/assist/developer-tools-assist.ts @@ -0,0 +1,241 @@ +import { dump } from "js-yaml"; +import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import "../../../components/ha-button"; +import "../../../components/ha-code-editor"; +import "../../../components/ha-language-picker"; +import "../../../components/ha-textarea"; +import type { HaTextArea } from "../../../components/ha-textarea"; +import { + AssitDebugResult, + debugAgent, + listAgents, +} from "../../../data/conversation"; +import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import { formatLanguageCode } from "../../../common/language/format_language"; +import { storage } from "../../../common/decorators/storage"; + +type SentenceParsingResult = { + sentence: string; + language: string; + result: AssitDebugResult | null; +}; + +@customElement("developer-tools-assist") +class HaPanelDevAssist extends SubscribeMixin(LitElement) { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public narrow!: boolean; + + @state() supportedLanguages?: string[]; + + @storage({ + key: "assist_debug_language", + state: true, + subscribe: false, + storage: "localStorage", + }) + _language?: string; + + @state() _results: SentenceParsingResult[] = []; + + @query("#sentences-input") _sentencesInput!: HaTextArea; + + @state() _validInput = false; + + private _languageChanged(ev) { + this._language = ev.detail.value; + } + + private _handleKeyDown(e: KeyboardEvent) { + if (e.code !== "Enter" || e.shiftKey) { + return; + } + e.preventDefault(); + this._parse(); + } + + private _textAreaInput(ev) { + const value = ev.target.value; + const valid = Boolean(value); + if (valid !== this._validInput) { + this._validInput = valid; + } + } + + private async _parse() { + const sentences = this._sentencesInput.value + .split("\n") + .filter((a) => a !== ""); + const { results } = await debugAgent(this.hass, sentences, this._language!); + + this._sentencesInput.value = ""; + + const newResults: SentenceParsingResult[] = []; + sentences.forEach((sentence, index) => { + const result = results[index]; + + newResults.push({ + sentence, + language: this._language!, + result, + }); + }); + this._results = [...newResults, ...this._results]; + } + + private async _fetchLanguages() { + const { agents } = await listAgents(this.hass); + const assistAgent = agents.find((agent) => agent.id === "homeassistant"); + this.supportedLanguages = + assistAgent?.supported_languages === "*" + ? undefined + : assistAgent?.supported_languages; + + if ( + !this._language && + this.supportedLanguages?.includes(this.hass.locale.language) + ) { + this._language = this.hass.locale.language; + } else if (!this._language) { + this._language = "en"; + } + } + + protected firstUpdated(): void { + this._fetchLanguages(); + } + + protected render() { + return html` +
+ +
+

+ Enter sentences and see how they will be parsed by Home Assistant. + Each line will be processed as individual sentence. Intents will + not be executed on your instance. +

+ ${this.supportedLanguages + ? html` + + ` + : nothing} + +
+
+ + Parse sentences + +
+
+ + ${this._results.map((r) => { + const { sentence, result, language } = r; + const matched = result != null; + + return html` + +
+
+

${sentence}

+

${matched ? "✅" : "❌"}

+
+
+ Language: ${formatLanguageCode(language, this.hass.locale)} + (${language}) +
+ ${result + ? html` + + ` + : html` + No intent matched + `} +
+
+ `; + })} +
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .content { + padding: 28px 20px 16px; + padding: max(28px, calc(12px + env(safe-area-inset-top))) + max(20px, calc(4px + env(safe-area-inset-right))) + max(16px, env(safe-area-inset-bottom)) + max(20px, calc(4px + env(safe-area-inset-left))); + max-width: 1040px; + margin: 0 auto; + } + .description { + margin: 0; + margin-bottom: 16px; + } + ha-textarea { + width: 100%; + } + .card-actions { + text-align: right; + } + .form { + margin-bottom: 16px; + } + .result { + margin-bottom: 16px; + } + .sentence { + font-weight: 500; + margin-bottom: 8px; + display: flex; + flex-direction: row; + justify-content: space-between; + } + .sentence p { + margin: 0; + } + .info p { + margin: 0; + } + ha-code-editor, + ha-alert { + display: block; + margin-top: 16px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "developer-tools-assist": HaPanelDevAssist; + } +} diff --git a/src/panels/developer-tools/developer-tools-router.ts b/src/panels/developer-tools/developer-tools-router.ts index b614f5e817..5b8baf3899 100644 --- a/src/panels/developer-tools/developer-tools-router.ts +++ b/src/panels/developer-tools/developer-tools-router.ts @@ -45,6 +45,10 @@ class DeveloperToolsRouter extends HassRouterPage { tag: "developer-yaml-config", load: () => import("./yaml_configuration/developer-yaml-config"), }, + assist: { + tag: "developer-tools-assist", + load: () => import("./assist/developer-tools-assist"), + }, }, }; diff --git a/src/panels/developer-tools/event/event-subscribe-card.ts b/src/panels/developer-tools/event/event-subscribe-card.ts index 0b0ce881b6..d1d7afc0aa 100644 --- a/src/panels/developer-tools/event/event-subscribe-card.ts +++ b/src/panels/developer-tools/event/event-subscribe-card.ts @@ -80,7 +80,8 @@ class EventSubscribeCard extends LitElement { )} ${formatTime( new Date(event.event.time_fired), - this.hass!.locale + this.hass!.locale, + this.hass!.config )}: + Assist
; + + @storage({ + key: "panel-dev-service-state-service-data", + state: true, + subscribe: false, + }) private _serviceData?: ServiceAction = { service: "", target: {}, data: {} }; - @LocalStorage("panel-dev-service-state-yaml-mode", true, false) + @storage({ + key: "panel-dev-service-state-yaml-mode", + state: true, + subscribe: false, + }) private _yamlMode = false; - @query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor; + @query("#yaml-editor") private _yamlEditor?: HaYamlEditor; protected firstUpdated(params) { super.firstUpdated(params); @@ -101,6 +111,7 @@ class HaPanelDevService extends LitElement { @value-changed=${this._serviceChanged} >
- + ${this._response + ? html`
+ +
+ +
+
+
` + : nothing} ${(this._yamlMode ? fields : this._filterSelectorFields(fields)).length ? html`
${this._yamlMode - ? html`
+ ? html`

${target ? html` @@ -309,10 +336,20 @@ class HaPanelDevService extends LitElement { if (!this._serviceData?.service) { return; } + const [domain, service] = this._serviceData.service.split(".", 2); + const script: Action[] = []; + if ("response" in this.hass.services[domain][service]) { + script.push({ + ...this._serviceData, + response_variable: "service_result", + }); + script.push({ stop: "done", response_variable: "service_result" }); + } else { + script.push(this._serviceData); + } try { - await callExecuteScript(this.hass, [this._serviceData]); + this._response = (await callExecuteScript(this.hass, script)).response; } catch (err: any) { - const [domain, service] = this._serviceData.service.split(".", 2); if ( err.error?.code === ERR_CONNECTION_LOST && serviceCallWillDisconnect(domain, service) @@ -346,7 +383,27 @@ class HaPanelDevService extends LitElement { } private _checkUiSupported() { - if (this._serviceData && hasTemplate(this._serviceData)) { + const fields = this._fields( + this.hass.services, + this._serviceData?.service + ).fields; + if ( + this._serviceData && + (Object.entries(this._serviceData).some( + ([key, val]) => key !== "data" && hasTemplate(val) + ) || + (this._serviceData.data && + Object.entries(this._serviceData.data).some(([key, val]) => { + const field = fields.find((f) => f.key === key); + if ( + field?.selector && + ("template" in field.selector || "object" in field.selector) + ) { + return false; + } + return hasTemplate(val); + }))) + ) { this._yamlMode = true; this._uiAvailable = false; } else { diff --git a/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts b/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts index 2096b136d6..8dd31d35c0 100644 --- a/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts +++ b/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts @@ -158,7 +158,11 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { > ${growth} ${unit} - ${formatDateTime(new Date(stat.start), this.hass.locale)} + ${formatDateTime( + new Date(stat.start), + this.hass.locale, + this.hass.config + )} @@ -213,7 +217,8 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { ${formatDateTime( new Date(this._chosenStat!.start), - this.hass.locale + this.hass.locale, + this.hass.config )}

@@ -223,7 +228,8 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { ${formatDateTime( new Date(this._chosenStat!.end), - this.hass.locale + this.hass.locale, + this.hass.config )}
diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index b21abece9f..fe0a0c158c 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -10,6 +10,7 @@ import { RenderTemplateResult, subscribeRenderTemplate, } from "../../../data/ws-templates"; +import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; @@ -142,6 +143,9 @@ class HaPanelDevTemplate extends LitElement { "ui.panel.developer-tools.tabs.templates.reset" )} + + ${this.hass.localize("ui.common.clear")} +
@@ -378,11 +382,42 @@ class HaPanelDevTemplate extends LitElement { localStorage["panel-dev-template-template"] = this._template; } - private _restoreDemo() { + private async _restoreDemo() { + if ( + !(await showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.developer-tools.tabs.templates.confirm_reset" + ), + warning: true, + })) + ) { + return; + } this._template = DEMO_TEMPLATE; this._subscribeTemplate(); delete localStorage["panel-dev-template-template"]; } + + private async _clear() { + if ( + !(await showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.developer-tools.tabs.templates.confirm_clear" + ), + warning: true, + })) + ) { + return; + } + this._unsubscribeTemplate(); + this._template = ""; + // Reset to empty result. Setting to 'undefined' results in a different visual + // behaviour compared to manually emptying the template input box. + this._templateResult = { + result: "", + listeners: { all: false, entities: [], domains: [], time: false }, + }; + } } declare global { diff --git a/src/panels/energy/strategies/energy-strategy.ts b/src/panels/energy/strategies/energy-strategy.ts index de5d6aca44..42b0c6678a 100644 --- a/src/panels/energy/strategies/energy-strategy.ts +++ b/src/panels/energy/strategies/energy-strategy.ts @@ -141,6 +141,11 @@ export class EnergyStrategy { view_layout: { position: "sidebar" }, collection_key: "energy_dashboard", }); + view.cards!.push({ + type: "energy-self-sufficiency-gauge", + view_layout: { position: "sidebar" }, + collection_key: "energy_dashboard", + }); } // Only include if we have a grid diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts index 08b8f0d90c..94d5ab6c15 100644 --- a/src/panels/history/ha-panel-history.ts +++ b/src/panels/history/ha-panel-history.ts @@ -1,14 +1,5 @@ import { mdiFilterRemove, mdiRefresh } from "@mdi/js"; -import { - addDays, - differenceInHours, - endOfToday, - endOfWeek, - endOfYesterday, - startOfToday, - startOfWeek, - startOfYesterday, -} from "date-fns/esm"; +import { differenceInHours } from "date-fns/esm"; import { HassServiceTarget, UnsubscribeFunc, @@ -16,8 +7,7 @@ import { import { css, html, LitElement, PropertyValues } from "lit"; import { property, query, state } from "lit/decorators"; import { ensureArray } from "../../common/array/ensure-array"; -import { firstWeekdayIndex } from "../../common/datetime/first_weekday"; -import { LocalStorage } from "../../common/decorators/local-storage"; +import { storage } from "../../common/decorators/storage"; import { navigate } from "../../common/navigate"; import { constructUrlCurrentPath } from "../../common/url/construct-url"; import { @@ -31,10 +21,11 @@ import "../../components/chart/state-history-charts"; import type { StateHistoryCharts } from "../../components/chart/state-history-charts"; import "../../components/ha-circular-progress"; import "../../components/ha-date-range-picker"; -import type { DateRangePickerRanges } from "../../components/ha-date-range-picker"; import "../../components/ha-icon-button"; +import "../../components/ha-icon-button-arrow-prev"; import "../../components/ha-menu-button"; import "../../components/ha-target-picker"; +import "../../components/ha-top-app-bar-fixed"; import { AreaDeviceLookup, AreaEntityLookup, @@ -55,8 +46,6 @@ import { import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { haStyle } from "../../resources/styles"; import { HomeAssistant } from "../../types"; -import "../../components/ha-top-app-bar-fixed"; -import "../../components/ha-icon-button-arrow-prev"; class HaPanelHistory extends SubscribeMixin(LitElement) { @property({ attribute: false }) hass!: HomeAssistant; @@ -69,15 +58,17 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { @state() private _endDate: Date; - @LocalStorage("historyPickedValue", true, false) + @storage({ + key: "historyPickedValue", + state: true, + subscribe: false, + }) private _targetPickerValue?: HassServiceTarget; @state() private _isLoading = false; @state() private _stateHistory?: HistoryResult; - @state() private _ranges?: DateRangePickerRanges; - @state() private _deviceEntityLookup?: DeviceEntityLookup; @state() private _areaEntityLookup?: AreaEntityLookup; @@ -178,7 +169,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { ?disabled=${this._isLoading} .startDate=${this._startDate} .endDate=${this._endDate} - .ranges=${this._ranges} @change=${this._dateRangeChanged} > @@ -220,24 +211,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { return; } - const today = new Date(); - const weekStartsOn = firstWeekdayIndex(this.hass.locale); - const weekStart = startOfWeek(today, { weekStartsOn }); - const weekEnd = endOfWeek(today, { weekStartsOn }); - - this._ranges = { - [this.hass.localize("ui.components.date-range-picker.ranges.today")]: [ - startOfToday(), - endOfToday(), - ], - [this.hass.localize("ui.components.date-range-picker.ranges.yesterday")]: - [startOfYesterday(), endOfYesterday()], - [this.hass.localize("ui.components.date-range-picker.ranges.this_week")]: - [weekStart, weekEnd], - [this.hass.localize("ui.components.date-range-picker.ranges.last_week")]: - [addDays(weekStart, -7), addDays(weekEnd, -7)], - }; - const searchParams = extractSearchParamsObject(); const entityIds = searchParams.entity_id; const deviceIds = searchParams.device_id; diff --git a/src/panels/logbook/ha-logbook-renderer.ts b/src/panels/logbook/ha-logbook-renderer.ts index e93d3858d4..83293b562b 100644 --- a/src/panels/logbook/ha-logbook-renderer.ts +++ b/src/panels/logbook/ha-logbook-renderer.ts @@ -192,7 +192,11 @@ class HaLogbookRenderer extends LitElement { new Date(previous.when * 1000).toDateString()) ? html`

- ${formatDate(new Date(item.when * 1000), this.hass.locale)} + ${formatDate( + new Date(item.when * 1000), + this.hass.locale, + this.hass.config + )}

` : nothing} @@ -229,7 +233,8 @@ class HaLogbookRenderer extends LitElement { ${formatTimeWithSeconds( new Date(item.when * 1000), - this.hass.locale + this.hass.locale, + this.hass.config )} - diff --git a/src/panels/logbook/ha-panel-logbook.ts b/src/panels/logbook/ha-panel-logbook.ts index aba47b77d4..9a93813793 100644 --- a/src/panels/logbook/ha-panel-logbook.ts +++ b/src/panels/logbook/ha-panel-logbook.ts @@ -1,16 +1,6 @@ import { mdiRefresh } from "@mdi/js"; -import { - addDays, - endOfToday, - endOfWeek, - endOfYesterday, - startOfToday, - startOfWeek, - startOfYesterday, -} from "date-fns/esm"; import { css, html, LitElement, PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { firstWeekdayIndex } from "../../common/datetime/first_weekday"; import { navigate } from "../../common/navigate"; import { constructUrlCurrentPath } from "../../common/url/construct-url"; import { @@ -20,7 +10,6 @@ import { } from "../../common/url/search-params"; import "../../components/entity/ha-entity-picker"; import "../../components/ha-date-range-picker"; -import type { DateRangePickerRanges } from "../../components/ha-date-range-picker"; import "../../components/ha-icon-button"; import "../../components/ha-icon-button-arrow-prev"; import "../../components/ha-menu-button"; @@ -40,8 +29,6 @@ export class HaPanelLogbook extends LitElement { @state() _entityIds?: string[]; - @state() private _ranges?: DateRangePickerRanges; - @state() private _showBack?: boolean; @@ -91,7 +78,6 @@ export class HaPanelLogbook extends LitElement { .hass=${this.hass} .startDate=${this._time.range[0]} .endDate=${this._time.range[1]} - .ranges=${this._ranges} @change=${this._dateRangeChanged} > @@ -123,24 +109,6 @@ export class HaPanelLogbook extends LitElement { return; } - const today = new Date(); - const weekStartsOn = firstWeekdayIndex(this.hass.locale); - const weekStart = startOfWeek(today, { weekStartsOn }); - const weekEnd = endOfWeek(today, { weekStartsOn }); - - this._ranges = { - [this.hass.localize("ui.components.date-range-picker.ranges.today")]: [ - startOfToday(), - endOfToday(), - ], - [this.hass.localize("ui.components.date-range-picker.ranges.yesterday")]: - [startOfYesterday(), endOfYesterday()], - [this.hass.localize("ui.components.date-range-picker.ranges.this_week")]: - [weekStart, weekEnd], - [this.hass.localize("ui.components.date-range-picker.ranges.last_week")]: - [addDays(weekStart, -7), addDays(weekEnd, -7)], - }; - this._applyURLParams(); } diff --git a/src/panels/lovelace/cards/energy/hui-energy-compare-card.ts b/src/panels/lovelace/cards/energy/hui-energy-compare-card.ts index 552680e2e9..4c545668fe 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-compare-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-compare-card.ts @@ -60,18 +60,27 @@ export class HuiEnergyCompareCard ${this.hass.localize("ui.panel.energy.compare.info", { start: html`${formatDate(this._start!, this.hass.locale)}${dayDifference > 0 + >${formatDate( + this._start!, + this.hass.locale, + this.hass.config + )}${dayDifference > 0 ? ` - - ${formatDate(this._end || endOfDay(new Date()), this.hass.locale)}` + ${formatDate( + this._end || endOfDay(new Date()), + this.hass.locale, + this.hass.config + )}` : ""}`, end: html`${formatDate( this._startCompare, - this.hass.locale + this.hass.locale, + this.hass.config )}${dayDifference > 0 ? ` - - ${formatDate(this._endCompare, this.hass.locale)}` + ${formatDate(this._endCompare, this.hass.locale, this.hass.config)}` : ""}`, })} diff --git a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts index 8fa32d49b1..68ac83ab67 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts @@ -12,7 +12,7 @@ import { isToday, startOfToday, } from "date-fns"; -import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -112,6 +112,7 @@ export class HuiEnergyGasGraphCard this._start, this._end, this.hass.locale, + this.hass.config, this._unit, this._compareStart, this._compareEnd @@ -137,6 +138,7 @@ export class HuiEnergyGasGraphCard start: Date, end: Date, locale: FrontendLocaleData, + config: HassConfig, unit?: string, compareStart?: Date, compareEnd?: Date @@ -167,7 +169,8 @@ export class HuiEnergyGasGraphCard suggestedMax: end.getTime(), adapters: { date: { - locale: locale, + locale, + config, }, }, ticks: { @@ -214,6 +217,10 @@ export class HuiEnergyGasGraphCard plugins: { tooltip: { position: "nearest", + filter: (val) => val.formattedValue !== "0", + itemSort: function (a, b) { + return b.datasetIndex - a.datasetIndex; + }, callbacks: { title: (datasets) => { if (dayDifference > 0) { @@ -221,10 +228,11 @@ export class HuiEnergyGasGraphCard } const date = new Date(datasets[0].parsed.x); return `${ - compare ? `${formatDateShort(date, locale)}: ` : "" - }${formatTime(date, locale)} – ${formatTime( + compare ? `${formatDateShort(date, locale, config)}: ` : "" + }${formatTime(date, locale, config)} – ${formatTime( addHours(date, 1), - locale + locale, + config )}`; }, label: (context) => diff --git a/src/panels/lovelace/cards/energy/hui-energy-self-sufficiency-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-self-sufficiency-gauge-card.ts new file mode 100644 index 0000000000..d1b3221f6f --- /dev/null +++ b/src/panels/lovelace/cards/energy/hui-energy-self-sufficiency-gauge-card.ts @@ -0,0 +1,257 @@ +import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; +import { mdiInformation } from "@mdi/js"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; +import "../../../../components/ha-card"; +import "../../../../components/ha-gauge"; +import "../../../../components/ha-svg-icon"; +import { + EnergyData, + energySourcesByType, + getEnergyDataCollection, +} from "../../../../data/energy"; +import { calculateStatisticsSumGrowth } from "../../../../data/recorder"; +import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; +import type { HomeAssistant } from "../../../../types"; +import type { LovelaceCard } from "../../types"; +import { severityMap } from "../hui-gauge-card"; +import type { EnergySelfSufficiencyGaugeCardConfig } from "../types"; + +@customElement("hui-energy-self-sufficiency-gauge-card") +class HuiEnergySelfSufficiencyGaugeCard + extends SubscribeMixin(LitElement) + implements LovelaceCard +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @state() private _config?: EnergySelfSufficiencyGaugeCardConfig; + + @state() private _data?: EnergyData; + + protected hassSubscribeRequiredHostProps = ["_config"]; + + public hassSubscribe(): UnsubscribeFunc[] { + return [ + getEnergyDataCollection(this.hass!, { + key: this._config?.collection_key, + }).subscribe((data) => { + this._data = data; + }), + ]; + } + + public getCardSize(): number { + return 4; + } + + public setConfig(config: EnergySelfSufficiencyGaugeCardConfig): void { + this._config = config; + } + + protected render() { + if (!this._config || !this.hass) { + return nothing; + } + + if (!this._data) { + return html`${this.hass.localize( + "ui.panel.lovelace.cards.energy.loading" + )}`; + } + + const prefs = this._data.prefs; + const types = energySourcesByType(prefs); + + // The strategy only includes this card if we have a grid. + const hasConsumption = true; + + const hasSolarProduction = types.solar !== undefined; + const hasBattery = types.battery !== undefined; + const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0; + + const totalFromGrid = + calculateStatisticsSumGrowth( + this._data.stats, + types.grid![0].flow_from.map((flow) => flow.stat_energy_from) + ) ?? 0; + + let totalSolarProduction: number | null = null; + + if (hasSolarProduction) { + totalSolarProduction = + calculateStatisticsSumGrowth( + this._data.stats, + types.solar!.map((source) => source.stat_energy_from) + ) || 0; + } + + let totalBatteryIn: number | null = null; + let totalBatteryOut: number | null = null; + + if (hasBattery) { + totalBatteryIn = + calculateStatisticsSumGrowth( + this._data.stats, + types.battery!.map((source) => source.stat_energy_to) + ) || 0; + totalBatteryOut = + calculateStatisticsSumGrowth( + this._data.stats, + types.battery!.map((source) => source.stat_energy_from) + ) || 0; + } + + let returnedToGrid: number | null = null; + + if (hasReturnToGrid) { + returnedToGrid = + calculateStatisticsSumGrowth( + this._data.stats, + types.grid![0].flow_to.map((flow) => flow.stat_energy_to) + ) || 0; + } + + let solarConsumption: number | null = null; + if (hasSolarProduction) { + solarConsumption = + (totalSolarProduction || 0) - + (returnedToGrid || 0) - + (totalBatteryIn || 0); + } + + let batteryFromGrid: null | number = null; + let batteryToGrid: null | number = null; + if (solarConsumption !== null && solarConsumption < 0) { + // What we returned to the grid and what went in to the battery is more than produced, + // so we have used grid energy to fill the battery + // or returned battery energy to the grid + if (hasBattery) { + batteryFromGrid = solarConsumption * -1; + if (batteryFromGrid > totalFromGrid) { + batteryToGrid = Math.min(0, batteryFromGrid - totalFromGrid); + batteryFromGrid = totalFromGrid; + } + } + solarConsumption = 0; + } + + let batteryConsumption: number | null = null; + if (hasBattery) { + batteryConsumption = (totalBatteryOut || 0) - (batteryToGrid || 0); + } + + const gridConsumption = Math.max(0, totalFromGrid - (batteryFromGrid || 0)); + + const totalHomeConsumption = Math.max( + 0, + gridConsumption + (solarConsumption || 0) + (batteryConsumption || 0) + ); + + let value: number | undefined; + if ( + totalFromGrid !== null && + totalHomeConsumption !== null && + totalHomeConsumption > 0 + ) { + value = (1 - totalFromGrid / totalHomeConsumption) * 100; + } + + return html` + + ${value !== undefined + ? html` + + + + ${this.hass.localize( + "ui.panel.lovelace.cards.energy.self_sufficiency_gauge.card_indicates_self_sufficiency_quota" + )} + + + +
+ ${this.hass.localize( + "ui.panel.lovelace.cards.energy.self_sufficiency_gauge.self_sufficiency_quota" + )} +
+ ` + : this.hass.localize( + "ui.panel.lovelace.cards.energy.self_sufficiency_gauge.self_sufficiency_could_not_calc" + )} +
+ `; + } + + private _computeSeverity(numberValue: number): string { + if (numberValue > 75) { + return severityMap.green; + } + if (numberValue < 50) { + return severityMap.yellow; + } + return severityMap.normal; + } + + static get styles(): CSSResultGroup { + return css` + ha-card { + height: 100%; + overflow: hidden; + padding: 16px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + box-sizing: border-box; + } + + ha-gauge { + width: 100%; + max-width: 250px; + direction: ltr; + } + + .name { + text-align: center; + line-height: initial; + color: var(--primary-text-color); + width: 100%; + font-size: 15px; + margin-top: 8px; + } + + ha-svg-icon { + position: absolute; + right: 4px; + top: 4px; + color: var(--secondary-text-color); + } + simple-tooltip > span { + font-size: 12px; + line-height: 12px; + } + simple-tooltip { + width: 80%; + max-width: 250px; + top: 8px !important; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-energy-self-sufficiency-gauge-card": HuiEnergySelfSufficiencyGaugeCard; + } +} diff --git a/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts index 0365164fb7..4f5e6cbfdf 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts @@ -68,10 +68,11 @@ class HuiEnergySolarGaugeCard return nothing; } - const totalSolarProduction = calculateStatisticsSumGrowth( - this._data.stats, - types.solar.map((source) => source.stat_energy_from) - ); + const totalSolarProduction = + calculateStatisticsSumGrowth( + this._data.stats, + types.solar.map((source) => source.stat_energy_from) + ) || 0; const productionReturnedToGrid = calculateStatisticsSumGrowth( this._data.stats, diff --git a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts index 6c693b4b46..778ccb77d1 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts @@ -12,7 +12,7 @@ import { isToday, startOfToday, } from "date-fns/esm"; -import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -111,6 +111,7 @@ export class HuiEnergySolarGraphCard this._start, this._end, this.hass.locale, + this.hass.config, this._compareStart, this._compareEnd )} @@ -135,6 +136,7 @@ export class HuiEnergySolarGraphCard start: Date, end: Date, locale: FrontendLocaleData, + config: HassConfig, compareStart?: Date, compareEnd?: Date ): ChartOptions => { @@ -164,7 +166,8 @@ export class HuiEnergySolarGraphCard suggestedMax: end.getTime(), adapters: { date: { - locale: locale, + locale, + config, }, }, ticks: { @@ -210,6 +213,10 @@ export class HuiEnergySolarGraphCard plugins: { tooltip: { position: "nearest", + filter: (val) => val.formattedValue !== "0", + itemSort: function (a, b) { + return b.datasetIndex - a.datasetIndex; + }, callbacks: { title: (datasets) => { if (dayDifference > 0) { @@ -217,10 +224,11 @@ export class HuiEnergySolarGraphCard } const date = new Date(datasets[0].parsed.x); return `${ - compare ? `${formatDateShort(date, locale)}: ` : "" - }${formatTime(date, locale)} – ${formatTime( + compare ? `${formatDateShort(date, locale, config)}: ` : "" + }${formatTime(date, locale, config)} – ${formatTime( addHours(date, 1), - locale + locale, + config )}`; }, label: (context) => diff --git a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts index 693f5deb83..b804dedc0a 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts @@ -12,7 +12,7 @@ import { isToday, startOfToday, } from "date-fns/esm"; -import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -105,6 +105,7 @@ export class HuiEnergyUsageGraphCard this._start, this._end, this.hass.locale, + this.hass.config, this._compareStart, this._compareEnd )} @@ -129,6 +130,7 @@ export class HuiEnergyUsageGraphCard start: Date, end: Date, locale: FrontendLocaleData, + config: HassConfig, compareStart?: Date, compareEnd?: Date ): ChartOptions => { @@ -158,7 +160,8 @@ export class HuiEnergyUsageGraphCard suggestedMax: end.getTime(), adapters: { date: { - locale: locale, + locale, + config, }, }, ticks: { @@ -206,6 +209,18 @@ export class HuiEnergyUsageGraphCard tooltip: { position: "nearest", filter: (val) => val.formattedValue !== "0", + itemSort: function (a: any, b: any) { + if (a.raw?.y > 0 && b.raw?.y < 0) { + return -1; + } + if (b.raw?.y > 0 && a.raw?.y < 0) { + return 1; + } + if (a.raw?.y > 0) { + return b.datasetIndex - a.datasetIndex; + } + return a.datasetIndex - b.datasetIndex; + }, callbacks: { title: (datasets) => { if (dayDifference > 0) { @@ -213,10 +228,11 @@ export class HuiEnergyUsageGraphCard } const date = new Date(datasets[0].parsed.x); return `${ - compare ? `${formatDateShort(date, locale)}: ` : "" - }${formatTime(date, locale)} – ${formatTime( + compare ? `${formatDateShort(date, locale, config)}: ` : "" + }${formatTime(date, locale, config)} – ${formatTime( addHours(date, 1), - locale + locale, + config )}`; }, label: (context) => diff --git a/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts index 709921d45c..41d4029c04 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts @@ -12,7 +12,7 @@ import { isToday, startOfToday, } from "date-fns"; -import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -112,6 +112,7 @@ export class HuiEnergyWaterGraphCard this._start, this._end, this.hass.locale, + this.hass.config, this._unit, this._compareStart, this._compareEnd @@ -137,6 +138,7 @@ export class HuiEnergyWaterGraphCard start: Date, end: Date, locale: FrontendLocaleData, + config: HassConfig, unit?: string, compareStart?: Date, compareEnd?: Date @@ -167,7 +169,8 @@ export class HuiEnergyWaterGraphCard suggestedMax: end.getTime(), adapters: { date: { - locale: locale, + locale, + config, }, }, ticks: { @@ -214,6 +217,10 @@ export class HuiEnergyWaterGraphCard plugins: { tooltip: { position: "nearest", + filter: (val) => val.formattedValue !== "0", + itemSort: function (a, b) { + return b.datasetIndex - a.datasetIndex; + }, callbacks: { title: (datasets) => { if (dayDifference > 0) { @@ -221,10 +228,11 @@ export class HuiEnergyWaterGraphCard } const date = new Date(datasets[0].parsed.x); return `${ - compare ? `${formatDateShort(date, locale)}: ` : "" - }${formatTime(date, locale)} – ${formatTime( + compare ? `${formatDateShort(date, locale, config)}: ` : "" + }${formatTime(date, locale, config)} – ${formatTime( addHours(date, 1), - locale + locale, + config )}`; }, label: (context) => diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 6f00fc3dfa..670e69b0e9 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -2,14 +2,18 @@ import { consume } from "@lit-labs/context"; import "@material/mwc-ripple"; import type { Ripple } from "@material/mwc-ripple"; import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers"; -import { HassEntities, HassEntity } from "home-assistant-js-websocket"; import { - CSSResultGroup, - LitElement, - PropertyValues, + HassConfig, + HassEntities, + HassEntity, +} from "home-assistant-js-websocket"; +import { css, + CSSResultGroup, html, + LitElement, nothing, + PropertyValues, } from "lit"; import { customElement, eventOptions, queryAsync, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; @@ -17,36 +21,39 @@ import { styleMap } from "lit/directives/style-map"; import { DOMAINS_TOGGLE } from "../../../common/const"; import { transform } from "../../../common/decorators/transform"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; +import { fireEvent } from "../../../common/dom/fire_event"; import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateDisplaySingleEntity } from "../../../common/entity/compute_state_display"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; -import { stateColorCss } from "../../../common/entity/state_color"; +import { + stateColorCss, + stateColorBrightness, +} from "../../../common/entity/state_color"; import { isValidEntityId } from "../../../common/entity/valid_entity_id"; import { iconColorCSS } from "../../../common/style/icon_color_css"; +import { LocalizeFunc } from "../../../common/translations/localize"; import "../../../components/ha-card"; import { HVAC_ACTION_TO_MODE } from "../../../data/climate"; import { + configContext, entitiesContext, localeContext, localizeContext, statesContext, themesContext, } from "../../../data/context"; -import { LightEntity } from "../../../data/light"; +import { EntityRegistryDisplayEntry } from "../../../data/entity_registry"; import { ActionHandlerEvent } from "../../../data/lovelace"; +import { FrontendLocaleData } from "../../../data/translation"; +import { Themes } from "../../../data/ws-themes"; import { HomeAssistant } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; import { findEntities } from "../common/find-entities"; -import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { ButtonCardConfig } from "./types"; -import { LocalizeFunc } from "../../../common/translations/localize"; -import { FrontendLocaleData } from "../../../data/translation"; -import { Themes } from "../../../data/ws-themes"; -import { EntityRegistryDisplayEntry } from "../../../data/entity_registry"; @customElement("hui-button-card") export class HuiButtonCard extends LitElement implements LovelaceCard { @@ -103,6 +110,10 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { @consume({ context: localeContext, subscribe: true }) _locale!: FrontendLocaleData; + @state() + @consume({ context: configContext, subscribe: true }) + _hassConfig!: HassConfig; + @consume({ context: entitiesContext, subscribe: true }) @transform({ transformer: function (this: HuiButtonCard, value) { @@ -204,9 +215,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { .state=${stateObj} style=${styleMap({ color: colored ? this._computeColor(stateObj) : undefined, - filter: colored - ? this._computeBrightness(stateObj) - : undefined, + filter: colored ? stateColorBrightness(stateObj) : undefined, height: this._config.icon_height ? this._config.icon_height : "", @@ -223,6 +232,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { this._localize, stateObj, this._locale, + this._hassConfig, this._entity )} ` @@ -327,14 +337,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { ]; } - private _computeBrightness(stateObj: HassEntity | LightEntity): string { - if (stateObj.attributes.brightness) { - const brightness = stateObj.attributes.brightness; - return `brightness(${(brightness + 245) / 5}%)`; - } - return ""; - } - private _computeColor(stateObj: HassEntity): string | undefined { if (stateObj.attributes.rgb_color) { return `rgb(${stateObj.attributes.rgb_color.join(",")})`; @@ -354,7 +356,10 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { } private _handleAction(ev: ActionHandlerEvent) { - handleAction(this, this.hass!, this._config!, ev.detail.action!); + fireEvent(this, "hass-action", { + config: this._config!, + action: ev.detail.action, + }); } } diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index 4d948cd0d7..201c4b9dd6 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -4,18 +4,22 @@ import { CSSResultGroup, html, LitElement, - PropertyValues, nothing, + PropertyValues, } from "lit"; import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { fireEvent } from "../../../common/dom/fire_event"; +import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display"; import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; -import { stateColorCss } from "../../../common/entity/state_color"; +import { + stateColorCss, + stateColorBrightness, +} from "../../../common/entity/state_color"; import { isValidEntityId } from "../../../common/entity/valid_entity_id"; import { formatNumber, @@ -27,29 +31,18 @@ import "../../../components/ha-card"; import "../../../components/ha-icon"; import { HVAC_ACTION_TO_MODE } from "../../../data/climate"; import { isUnavailableState } from "../../../data/entity"; -import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display"; -import { LightEntity } from "../../../data/light"; import { HomeAssistant } from "../../../types"; import { computeCardSize } from "../common/compute-card-size"; import { findEntities } from "../common/find-entities"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createHeaderFooterElement } from "../create-element/create-header-footer-element"; -import { - LovelaceCard, - LovelaceCardEditor, - LovelaceHeaderFooter, -} from "../types"; +import { LovelaceCard, LovelaceHeaderFooter } from "../types"; import { HuiErrorCard } from "./hui-error-card"; import { EntityCardConfig } from "./types"; @customElement("hui-entity-card") export class HuiEntityCard extends LitElement implements LovelaceCard { - public static async getConfigElement(): Promise { - await import("../editor/config-elements/hui-entity-card-editor"); - return document.createElement("hui-entity-card-editor"); - } - public static getStubConfig( hass: HomeAssistant, entities: string[], @@ -70,6 +63,11 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { }; } + public static async getConfigForm() { + return (await import("../editor/config-elements/hui-entity-card-editor")) + .default; + } + @property({ attribute: false }) public hass?: HomeAssistant; @state() private _config?: EntityCardConfig; @@ -147,7 +145,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { data-state=${stateObj.state} style=${styleMap({ color: colored ? this._computeColor(stateObj) : undefined, - filter: colored ? this._computeBrightness(stateObj) : undefined, + filter: colored ? stateColorBrightness(stateObj) : undefined, height: this._config.icon_height ? this._config.icon_height : "", @@ -163,6 +161,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities, this._config.attribute! ) @@ -180,6 +179,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities )}${showUnit @@ -216,14 +216,6 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { return undefined; } - private _computeBrightness(stateObj: HassEntity | LightEntity): string { - if (stateObj.attributes.brightness) { - const brightness = stateObj.attributes.brightness; - return `brightness(${(brightness + 245) / 5}%)`; - } - return ""; - } - protected shouldUpdate(changedProps: PropertyValues): boolean { // Side Effect used to update footer hass while keeping optimizations if (this._footerElement) { diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index f22ac84267..7e1bbedb0c 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -215,7 +215,8 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { background: var(--divider-color); border-radius: 14px; padding: 4px; - margin: -4px 0; + margin-top: -4px; + margin-bottom: 8px; } .entity div { width: 100%; @@ -337,6 +338,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { this.hass!.localize, stateObj, this.hass!.locale, + this.hass!.config, this.hass!.entities )}
diff --git a/src/panels/lovelace/cards/hui-history-graph-card.ts b/src/panels/lovelace/cards/hui-history-graph-card.ts index 64e7809e18..5219027fc9 100644 --- a/src/panels/lovelace/cards/hui-history-graph-card.ts +++ b/src/panels/lovelace/cards/hui-history-graph-card.ts @@ -205,6 +205,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { .historyData=${this._stateHistory} .names=${this._names} up-to-now + .hoursToShow=${this._hoursToShow} .showNames=${this._config.show_names !== undefined ? this._config.show_names : true} diff --git a/src/panels/lovelace/cards/hui-humidifier-card.ts b/src/panels/lovelace/cards/hui-humidifier-card.ts index a99331e67e..57e9ee8ee5 100644 --- a/src/panels/lovelace/cards/hui-humidifier-card.ts +++ b/src/panels/lovelace/cards/hui-humidifier-card.ts @@ -1,25 +1,30 @@ -import { mdiDotsVertical } from "@mdi/js"; +import { mdiDotsVertical, mdiPower, mdiWaterPercent } from "@mdi/js"; import "@thomasloven/round-slider"; import { HassEntity } from "home-assistant-js-websocket"; import { - css, CSSResultGroup, - html, LitElement, PropertyValues, - svg, + css, + html, nothing, + svg, } from "lit"; -import { customElement, property, state } from "lit/decorators"; +import { customElement, property, query, state } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; import { classMap } from "lit/directives/class-map"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { fireEvent } from "../../../common/dom/fire_event"; import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display"; +import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateName } from "../../../common/entity/compute_state_name"; +import { stateColorCss } from "../../../common/entity/state_color"; +import { formatNumber } from "../../../common/number/format_number"; import { computeRTLDirection } from "../../../common/util/compute_rtl"; import "../../../components/ha-card"; +import type { HaCard } from "../../../components/ha-card"; import "../../../components/ha-icon-button"; -import { isUnavailableState } from "../../../data/entity"; +import { UNAVAILABLE, isUnavailableState } from "../../../data/entity"; import { HumidifierEntity } from "../../../data/humidifier"; import { HomeAssistant } from "../../../types"; import { findEntities } from "../common/find-entities"; @@ -59,8 +64,10 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { @state() private _setHum?: number; + @query("ha-card") private _card?: HaCard; + public getCardSize(): number { - return 6; + return 7; } public setConfig(config: HumidifierCardConfig): void { @@ -88,6 +95,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { const name = this._config!.name || computeStateName(this.hass!.states[this._config!.entity]); + const targetHumidity = stateObj.attributes.humidity !== null && Number.isFinite(Number(stateObj.attributes.humidity)) @@ -102,7 +110,6 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { ? html` ` : html` `; - const setValues = html` - - - ${isUnavailableState(stateObj.state) || - setHumidity === undefined || - setHumidity === null - ? "" - : svg` - ${setHumidity.toFixed()} - - % - - `} + const currentHumidity = svg` + + + ${ + stateObj.state !== UNAVAILABLE && + stateObj.attributes.current_humidity != null && + !isNaN(stateObj.attributes.current_humidity) + ? svg` + ${formatNumber( + stateObj.attributes.current_humidity, + this.hass.locale + )} + + % + + ` + : nothing + } - `; - const currentMode = html` - - - ${this.hass!.localize(`state.default.${stateObj.state}`)} - ${stateObj.attributes.mode && !isUnavailableState(stateObj.state) - ? html` - - - ${computeAttributeValueDisplay( - this.hass.localize, - stateObj, - this.hass.locale, - this.hass.entities, - "mode" - )} - ` - : ""} - + `; + + const setValues = svg` + + + + ${ + stateObj.state !== UNAVAILABLE && setHumidity != null + ? formatNumber(setHumidity, this.hass.locale, { + maximumFractionDigits: 0, + }) + : nothing + } + + + ${ + stateObj.attributes.action + ? computeAttributeValueDisplay( + this.hass.localize, + stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities, + "action" + ) + : computeStateDisplay( + this.hass.localize, + stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities + ) + } + ${ + stateObj.state !== UNAVAILABLE && stateObj.attributes.mode + ? html` + - + ${computeAttributeValueDisplay( + this.hass.localize, + stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities, + "mode" + )} + ` + : nothing + } + + `; return html` - + ${slider}
- - ${setValues} - - ${currentMode} +
${currentHumidity} ${setValues}
-
${name}
+
+
+ + + + +
+ ${name} +
`; @@ -209,6 +286,15 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { ) { applyThemesOnElement(this, this.hass.themes, this._config.theme); } + + const stateObj = this.hass.states[this._config.entity]; + if (!stateObj) { + return; + } + + if (!oldHass || oldHass.states[this._config.entity] !== stateObj) { + this._rescale_svg(); + } } public willUpdate(changedProps: PropertyValues) { @@ -228,6 +314,27 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { } } + private _rescale_svg() { + // Set the viewbox of the SVG containing the set temperature to perfectly + // fit the text + // That way it will auto-scale correctly + // This is not done to the SVG containing the current temperature, because + // it should not be centered on the text, but only on the value + const card = this._card; + if (card) { + card.updateComplete.then(() => { + const svgRoot = this.shadowRoot!.querySelector("#set-values")!; + const box = svgRoot.querySelector("g")!.getBBox()!; + svgRoot.setAttribute( + "viewBox", + `${box.x} ${box!.y} ${box.width} ${box.height}` + ); + svgRoot.setAttribute("width", `${box.width}`); + svgRoot.setAttribute("height", `${box.height}`); + }); + } + } + private _getSetHum(stateObj: HassEntity): undefined | number { if (isUnavailableState(stateObj.state)) { return undefined; @@ -247,8 +354,14 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { }); } - private _toggle(): void { - this.hass!.callService("humidifier", "toggle", { + private _turnOn(): void { + this.hass!.callService("humidifier", "turn_on", { + entity_id: this._config!.entity, + }); + } + + private _turnOff(): void { + this.hass!.callService("humidifier", "turn_off", { entity_id: this._config!.entity, }); } @@ -272,6 +385,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { --name-font-size: 1.2rem; --brightness-font-size: 1.2rem; --rail-border-color: transparent; + --mode-color: var(--state-inactive-color); } .more-info { @@ -279,11 +393,11 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { cursor: pointer; top: 0; right: 0; + inset-inline-end: 0px; + inset-inline-start: initial; border-radius: 100%; color: var(--secondary-text-color); - z-index: 25; - inset-inline-start: initial; - inset-inline-end: 0; + z-index: 1; direction: var(--direction); } @@ -299,7 +413,6 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { justify-content: center; padding: 16px; position: relative; - direction: ltr; } #slider { @@ -312,13 +425,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { round-slider { --round-slider-path-color: var(--slider-track-color); - --round-slider-bar-color: var(--primary-color); - padding-bottom: 10%; - } - - .round-slider_off { - --round-slider-path-color: var(--slider-track-color); - --round-slider-bar-color: var(--disabled-text-color); + --round-slider-bar-color: var(--mode-color); padding-bottom: 10%; } @@ -332,37 +439,28 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { top: 20px; text-align: center; overflow-wrap: break-word; + pointer-events: none; } #humidity { - max-width: 80%; - transform: translate(0, 350%); + position: absolute; + transform: translate(-50%, -50%); + width: 100%; + height: 50%; + top: 45%; + left: 50%; + direction: ltr; } #set-values { - font-size: 13px; - font-family: var(--paper-font-body1_-_font-family); - font-weight: var(--paper-font-body1_-_font-weight); + max-width: 80%; + transform: translate(0, -50%); + font-size: 20px; } #set-mode { fill: var(--secondary-text-color); - font-size: 4px; - } - - .toggle-button { - color: var(--primary-text-color); - width: 60%; - height: auto; - position: absolute; - max-width: calc(100% - 40px); - box-sizing: border-box; - border-radius: 100%; - top: 39%; - left: 50%; - transform: translate(-50%, -50%); - --mdc-icon-button-size: 100%; - --mdc-icon-size: 100%; + font-size: 16px; } #info { @@ -374,6 +472,16 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { font-size: var(--name-font-size); } + #modes > * { + color: var(--disabled-text-color); + cursor: pointer; + display: inline-block; + } + + #modes .selected-icon { + color: var(--mode-color); + } + text { fill: var(--primary-text-color); } diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index e41aef6afe..c2715b8007 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -30,6 +30,7 @@ import { hasConfigOrEntityChanged } from "../common/has-changed"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LightCardConfig } from "./types"; +import { stateColorBrightness } from "../../../common/entity/state_color"; @customElement("hui-light-card") export class HuiLightCard extends LitElement implements LovelaceCard { @@ -161,6 +162,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities )}
@@ -238,8 +240,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { if (stateObj.state === "off" || !stateObj.attributes.brightness) { return ""; } - const brightness = stateObj.attributes.brightness; - return `brightness(${(brightness + 245) / 5}%)`; + return stateColorBrightness(stateObj); } private _computeColor(stateObj: LightEntity): string { diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index d84694d048..ec9dfa6138 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -374,11 +374,19 @@ class HuiMapCard extends LitElement implements LovelaceCard { if ((config.hours_to_show! ?? DEFAULT_HOURS_TO_SHOW) > 144) { // if showing > 6 days in the history trail, show the full // date and time - p.tooltip = formatDateTime(t, this.hass.locale); + p.tooltip = formatDateTime(t, this.hass.locale, this.hass.config); } else if (isToday(t)) { - p.tooltip = formatTimeWithSeconds(t, this.hass.locale); + p.tooltip = formatTimeWithSeconds( + t, + this.hass.locale, + this.hass.config + ); } else { - p.tooltip = formatTimeWeekday(t, this.hass.locale); + p.tooltip = formatTimeWeekday( + t, + this.hass.locale, + this.hass.config + ); } points.push(p); } diff --git a/src/panels/lovelace/cards/hui-picture-card.ts b/src/panels/lovelace/cards/hui-picture-card.ts index c72bf72bc9..843d447a67 100644 --- a/src/panels/lovelace/cards/hui-picture-card.ts +++ b/src/panels/lovelace/cards/hui-picture-card.ts @@ -3,19 +3,22 @@ import { CSSResultGroup, html, LitElement, - PropertyValues, nothing, + PropertyValues, } from "lit"; import { customElement, property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { ifDefined } from "lit/directives/if-defined"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import "../../../components/ha-card"; +import { computeImageUrl, ImageEntity } from "../../../data/image"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; +import { hasConfigChanged } from "../common/has-changed"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { PictureCardConfig } from "./types"; @@ -30,8 +33,6 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { return { type: "picture", image: "https://demo.home-assistant.io/stub_config/t-shirt-promo.png", - tap_action: { action: "none" }, - hold_action: { action: "none" }, }; } @@ -44,7 +45,7 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { } public setConfig(config: PictureCardConfig): void { - if (!config || !config.image) { + if (!config || (!config.image && !config.image_entity)) { throw new Error("Image required"); } @@ -52,10 +53,21 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { } protected shouldUpdate(changedProps: PropertyValues): boolean { - if (changedProps.size === 1 && changedProps.has("hass")) { - return !changedProps.get("hass"); + if (!this._config || hasConfigChanged(this, changedProps)) { + return true; } - return true; + if (this._config.image_entity && changedProps.has("hass")) { + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + if ( + !oldHass || + oldHass.states[this._config.image_entity] !== + this.hass!.states[this._config.image_entity] + ) { + return true; + } + } + + return false; } protected updated(changedProps: PropertyValues): void { @@ -83,6 +95,17 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { return nothing; } + let stateObj: ImageEntity | undefined; + + if (this._config.image_entity) { + stateObj = this.hass.states[this._config.image_entity] as ImageEntity; + if (!stateObj) { + return html` + ${createEntityNotFoundWarning(this.hass, this._config.image_entity)} + `; + } + } + return html` ${this._config.alt_text} `; diff --git a/src/panels/lovelace/cards/hui-picture-elements-card.ts b/src/panels/lovelace/cards/hui-picture-elements-card.ts index fad22f2c66..15c7f02e1f 100644 --- a/src/panels/lovelace/cards/hui-picture-elements-card.ts +++ b/src/panels/lovelace/cards/hui-picture-elements-card.ts @@ -9,6 +9,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import "../../../components/ha-card"; +import { ImageEntity, computeImageUrl } from "../../../data/image"; import { HomeAssistant } from "../../../types"; import { findEntities } from "../common/find-entities"; import { LovelaceElement, LovelaceElementConfig } from "../elements/types"; @@ -62,7 +63,12 @@ class HuiPictureElementsCard extends LitElement implements LovelaceCard { if (!config) { throw new Error("Invalid configuration"); } else if ( - !(config.image || config.camera_image || config.state_image) || + !( + config.image || + config.image_entity || + config.camera_image || + config.state_image + ) || (config.state_image && !config.entity) ) { throw new Error("Image required"); @@ -115,12 +121,17 @@ class HuiPictureElementsCard extends LitElement implements LovelaceCard { return nothing; } + let stateObj: ImageEntity | undefined; + if (this._config.image_entity) { + stateObj = this.hass.states[this._config.image_entity] as ImageEntity; + } + return html`
${entityState}
`; } + const domain = computeDomain(this._config.entity); + return html`
${this._config.title - ? html`
${this._config.title}
` + ? html`
${this._config.title}
` : ""}
${this._entitiesDialog!.map((entityConf) => @@ -257,6 +277,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { this.hass!.localize, stateObj, this.hass!.locale, + this.hass!.config, this.hass!.entities )}`} > @@ -280,6 +301,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { this.hass!.localize, stateObj, this.hass!.locale, + this.hass!.config, this.hass!.entities )}
diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts index b186cf8e59..b48978274c 100644 --- a/src/panels/lovelace/cards/hui-thermostat-card.ts +++ b/src/panels/lovelace/cards/hui-thermostat-card.ts @@ -166,16 +166,19 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { style="font-size: 13px;" > ${ - stateObj.attributes.current_temperature !== null && + stateObj.state !== UNAVAILABLE && + stateObj.attributes.current_temperature != null && !isNaN(stateObj.attributes.current_temperature) - ? svg`${formatNumber( - stateObj.attributes.current_temperature, - this.hass.locale - )} - - ${this.hass.config.unit_system.temperature} - ` - : "" + ? svg` + ${formatNumber( + stateObj.attributes.current_temperature, + this.hass.locale + )} + + ${this.hass.config.unit_system.temperature} + + ` + : nothing } @@ -186,42 +189,14 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { ${ - stateObj.state === UNAVAILABLE - ? this.hass.localize("state.default.unavailable") - : this._setTemp === undefined || this._setTemp === null - ? "" - : Array.isArray(this._setTemp) - ? this._stepSize === 1 + stateObj.state !== UNAVAILABLE && this._setTemp != null + ? Array.isArray(this._setTemp) ? svg` - ${formatNumber(this._setTemp[0], this.hass.locale, { - maximumFractionDigits: 0, - })} - - ${formatNumber(this._setTemp[1], this.hass.locale, { - maximumFractionDigits: 0, - })} - ` - : svg` - ${formatNumber(this._setTemp[0], this.hass.locale, { - minimumFractionDigits: 1, - maximumFractionDigits: 1, - })} - - ${formatNumber(this._setTemp[1], this.hass.locale, { - minimumFractionDigits: 1, - maximumFractionDigits: 1, - })} - ` - : this._stepSize === 1 - ? svg` - ${formatNumber(this._setTemp, this.hass.locale, { - maximumFractionDigits: 0, - })} - ` - : svg` - ${formatNumber(this._setTemp, this.hass.locale, { - minimumFractionDigits: 1, - maximumFractionDigits: 1, - })} - ` + ${this._formatSetTemp(this._setTemp[0])} - + ${this._formatSetTemp(this._setTemp[1])} + ` + : this._formatSetTemp(this._setTemp) + : nothing } ${ - stateObj.attributes.hvac_action + stateObj.state !== UNAVAILABLE && stateObj.attributes.hvac_action ? computeAttributeValueDisplay( this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities, "hvac_action" ) @@ -242,10 +218,12 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities ) } ${ + stateObj.state !== UNAVAILABLE && stateObj.attributes.preset_mode && stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE ? html` @@ -254,11 +232,12 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities, "preset_mode" )} ` - : "" + : nothing } @@ -371,6 +350,17 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { } } + private _formatSetTemp(temp: number) { + return this._stepSize === 1 + ? formatNumber(temp, this.hass!.locale, { + maximumFractionDigits: 0, + }) + : formatNumber(temp, this.hass!.locale, { + minimumFractionDigits: 1, + maximumFractionDigits: 1, + }); + } + private _rescale_svg() { // Set the viewbox of the SVG containing the set temperature to perfectly // fit the text diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index 5f2b43b83a..3471b6348b 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -3,11 +3,11 @@ import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers"; import { mdiExclamationThick, mdiHelp } from "@mdi/js"; import { HassEntity } from "home-assistant-js-websocket"; import { - css, CSSResultGroup, - html, LitElement, TemplateResult, + css, + html, nothing, } from "lit"; import { @@ -37,13 +37,14 @@ import "../../../components/tile/ha-tile-image"; import "../../../components/tile/ha-tile-info"; import { cameraUrlWithWidthHeight } from "../../../data/camera"; import { - computeCoverPositionStateDisplay, CoverEntity, + computeCoverPositionStateDisplay, } from "../../../data/cover"; import { isUnavailableState } from "../../../data/entity"; -import { computeFanSpeedStateDisplay, FanEntity } from "../../../data/fan"; -import { LightEntity } from "../../../data/light"; -import { ActionHandlerEvent } from "../../../data/lovelace"; +import { FanEntity, computeFanSpeedStateDisplay } from "../../../data/fan"; +import type { HumidifierEntity } from "../../../data/humidifier"; +import type { LightEntity } from "../../../data/light"; +import type { ActionHandlerEvent } from "../../../data/lovelace"; import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor"; import { HomeAssistant } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; @@ -51,15 +52,15 @@ import { findEntities } from "../common/find-entities"; import { handleAction } from "../common/handle-action"; import "../components/hui-timestamp-display"; import { createTileFeatureElement } from "../create-element/create-tile-feature-element"; -import { LovelaceTileFeatureConfig } from "../tile-features/types"; -import { +import type { LovelaceTileFeatureConfig } from "../tile-features/types"; +import type { LovelaceCard, LovelaceCardEditor, LovelaceTileFeature, } from "../types"; -import { HuiErrorCard } from "./hui-error-card"; +import type { HuiErrorCard } from "./hui-error-card"; import { computeTileBadge } from "./tile/badges/tile-badge"; -import { ThermostatCardConfig, TileCardConfig } from "./types"; +import type { ThermostatCardConfig, TileCardConfig } from "./types"; const TIMESTAMP_STATE_DOMAINS = ["button", "input_button", "scene"]; @@ -228,6 +229,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard { this.hass!.localize, stateObj, this.hass!.locale, + this.hass!.config, this.hass!.entities ); @@ -240,6 +242,16 @@ export class HuiTileCard extends LitElement implements LovelaceCard { return `${stateDisplay} ⸱ ${positionStateDisplay}`; } } + + if (domain === "humidifier" && stateActive(stateObj)) { + const humidity = (stateObj as HumidifierEntity).attributes.humidity; + if (humidity) { + return `${stateDisplay} ⸱ ${Math.round(humidity)}${blankBeforePercent( + this.hass!.locale + )}%`; + } + } + return stateDisplay; } diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.ts b/src/panels/lovelace/cards/hui-weather-forecast-card.ts index 066d14fa02..85f29fc0a4 100644 --- a/src/panels/lovelace/cards/hui-weather-forecast-card.ts +++ b/src/panels/lovelace/cards/hui-weather-forecast-card.ts @@ -39,6 +39,7 @@ import { loadPolyfillIfNeeded } from "../../../resources/resize-observer.polyfil import { createEntityNotFoundWarning } from "../components/hui-warning"; import type { LovelaceCard, LovelaceCardEditor } from "../types"; import type { WeatherForecastCardConfig } from "./types"; +import { formatDateWeekdayShort } from "../../../common/datetime/format_date"; const DAY_IN_MILLISECONDS = 86400000; @@ -222,6 +223,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities )}
@@ -319,13 +321,15 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { ? html` ${formatTime( new Date(item.datetime), - this.hass!.locale + this.hass!.locale, + this.hass!.config )} ` : html` - ${new Date(item.datetime).toLocaleDateString( - this.hass!.language, - { weekday: "short" } + ${formatDateWeekdayShort( + new Date(item.datetime), + this.hass!.locale, + this.hass!.config )} `}
diff --git a/src/panels/lovelace/cards/tile/badges/tile-badge-climate.ts b/src/panels/lovelace/cards/tile/badges/tile-badge-climate.ts index 81e7346983..03c959eec6 100644 --- a/src/panels/lovelace/cards/tile/badges/tile-badge-climate.ts +++ b/src/panels/lovelace/cards/tile/badges/tile-badge-climate.ts @@ -2,6 +2,7 @@ import { mdiClockOutline, mdiFan, mdiFire, + mdiHeatWave, mdiPower, mdiSnowflake, mdiWaterPercent, @@ -21,6 +22,7 @@ export const CLIMATE_HVAC_ACTION_ICONS: Record = { heating: mdiFire, idle: mdiClockOutline, off: mdiPower, + preheating: mdiHeatWave, }; export const CLIMATE_HVAC_ACTION_MODE: Record = { @@ -30,6 +32,7 @@ export const CLIMATE_HVAC_ACTION_MODE: Record = { heating: "heat", idle: "off", off: "off", + preheating: "heat", }; export const computeClimateBadge: ComputeBadgeFunction = (stateObj) => { diff --git a/src/panels/lovelace/cards/tile/badges/tile-badge-humidifier.ts b/src/panels/lovelace/cards/tile/badges/tile-badge-humidifier.ts new file mode 100644 index 0000000000..c4901e0b2d --- /dev/null +++ b/src/panels/lovelace/cards/tile/badges/tile-badge-humidifier.ts @@ -0,0 +1,41 @@ +import { + mdiArrowDownBold, + mdiArrowUpBold, + mdiClockOutline, + mdiPower, +} from "@mdi/js"; +import { stateColorCss } from "../../../../../common/entity/state_color"; +import { + HumidifierAction, + HumidifierEntity, + HumidifierState, +} from "../../../../../data/humidifier"; +import { ComputeBadgeFunction } from "./tile-badge"; + +export const HUMIDIFIER_ACTION_ICONS: Record = { + drying: mdiArrowDownBold, + humidifying: mdiArrowUpBold, + idle: mdiClockOutline, + off: mdiPower, +}; + +export const HUMIDIFIER_ACTION_MODE: Record = + { + drying: "on", + humidifying: "on", + idle: "off", + off: "off", + }; + +export const computeHumidifierBadge: ComputeBadgeFunction = (stateObj) => { + const hvacAction = (stateObj as HumidifierEntity).attributes.action; + + if (!hvacAction || hvacAction === "off") { + return undefined; + } + + return { + iconPath: HUMIDIFIER_ACTION_ICONS[hvacAction], + color: stateColorCss(stateObj, HUMIDIFIER_ACTION_MODE[hvacAction]), + }; +}; diff --git a/src/panels/lovelace/cards/tile/badges/tile-badge.ts b/src/panels/lovelace/cards/tile/badges/tile-badge.ts index e303c49129..5281f3591d 100644 --- a/src/panels/lovelace/cards/tile/badges/tile-badge.ts +++ b/src/panels/lovelace/cards/tile/badges/tile-badge.ts @@ -5,6 +5,7 @@ import { UNAVAILABLE, UNKNOWN } from "../../../../../data/entity"; import { HomeAssistant } from "../../../../../types"; import { computeClimateBadge } from "./tile-badge-climate"; import { computePersonBadge } from "./tile-badge-person"; +import { computeHumidifierBadge } from "./tile-badge-humidifier"; export type TileBadge = { color?: string; @@ -34,6 +35,8 @@ export const computeTileBadge: ComputeBadgeFunction = (stateObj, hass) => { return computePersonBadge(stateObj, hass); case "climate": return computeClimateBadge(stateObj, hass); + case "humidifier": + return computeHumidifierBadge(stateObj, hass); default: return undefined; } diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 0e65d9a46a..082b6c4b53 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -159,6 +159,13 @@ export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig { collection_key?: string; } +export interface EnergySelfSufficiencyGaugeCardConfig + extends LovelaceCardConfig { + type: "energy-self-sufficiency-gauge"; + title?: string; + collection_key?: string; +} + export interface EnergyGridGaugeCardConfig extends LovelaceCardConfig { type: "energy-grid-result-gauge"; title?: string; @@ -328,6 +335,7 @@ export interface StatisticCardConfig extends LovelaceCardConfig { export interface PictureCardConfig extends LovelaceCardConfig { image?: string; + image_entity?: string; tap_action?: ActionConfig; hold_action?: ActionConfig; double_tap_action?: ActionConfig; @@ -338,6 +346,7 @@ export interface PictureCardConfig extends LovelaceCardConfig { export interface PictureElementsCardConfig extends LovelaceCardConfig { title?: string; image?: string; + image_entity?: string; camera_image?: string; camera_view?: HuiImage["cameraView"]; state_image?: Record; diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 99bfbdc634..65b66c3ce1 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -20,6 +20,7 @@ import { AlarmPanelCardConfig, EntitiesCardConfig, HumidifierCardConfig, + PictureCardConfig, PictureEntityCardConfig, ThermostatCardConfig, } from "../cards/types"; @@ -125,6 +126,12 @@ export const computeCards = ( entity: entityId, }; cards.push(cardConfig); + } else if (domain === "image") { + const cardConfig: PictureCardConfig = { + type: "picture", + image_entity: entityId, + }; + cards.push(cardConfig); } else if (domain === "climate") { const cardConfig: ThermostatCardConfig = { type: "thermostat", diff --git a/src/panels/lovelace/common/handle-action.ts b/src/panels/lovelace/common/handle-action.ts index ddbbc838f3..83deadf5db 100644 --- a/src/panels/lovelace/common/handle-action.ts +++ b/src/panels/lovelace/common/handle-action.ts @@ -4,6 +4,7 @@ import { forwardHaptic } from "../../../data/haptics"; import { domainToName } from "../../../data/integration"; import { ActionConfig } from "../../../data/lovelace"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; +import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/show-ha-voice-command-dialog"; import { HomeAssistant } from "../../../types"; import { showToast } from "../../../util/toast"; import { toggleEntity } from "./entity/toggle-entity"; @@ -14,16 +15,19 @@ declare global { } } +export type ActionConfigParams = { + entity?: string; + camera_image?: string; + image_entity?: string; + hold_action?: ActionConfig; + tap_action?: ActionConfig; + double_tap_action?: ActionConfig; +}; + export const handleAction = async ( node: HTMLElement, hass: HomeAssistant, - config: { - entity?: string; - camera_image?: string; - hold_action?: ActionConfig; - tap_action?: ActionConfig; - double_tap_action?: ActionConfig; - }, + config: ActionConfigParams, action: string ): Promise => { let actionConfig: ActionConfig | undefined; @@ -84,9 +88,11 @@ export const handleAction = async ( switch (actionConfig.action) { case "more-info": { - if (config.entity || config.camera_image) { + if (config.entity || config.camera_image || config.image_entity) { fireEvent(node, "hass-more-info", { - entityId: config.entity ? config.entity : config.camera_image!, + entityId: (config.entity || + config.camera_image || + config.image_entity)!, }); } else { showToast(node, { @@ -153,6 +159,13 @@ export const handleAction = async ( forwardHaptic("light"); break; } + case "assist": { + showVoiceCommandDialog(node, hass, { + start_listening: actionConfig.start_listening, + pipeline_id: actionConfig.pipeline_id, + }); + break; + } case "fire-dom-event": { fireEvent(node, "ll-custom", actionConfig); } diff --git a/src/panels/lovelace/components/hui-action-editor.ts b/src/panels/lovelace/components/hui-action-editor.ts index 4077ae9a09..20d5e0f597 100644 --- a/src/panels/lovelace/components/hui-action-editor.ts +++ b/src/panels/lovelace/components/hui-action-editor.ts @@ -3,6 +3,8 @@ import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; import { stopPropagation } from "../../../common/dom/stop_propagation"; +import "../../../components/ha-assist-pipeline-picker"; +import { HaFormSchema, SchemaUnion } from "../../../components/ha-form/types"; import "../../../components/ha-help-tooltip"; import "../../../components/ha-navigation-picker"; import "../../../components/ha-service-control"; @@ -24,9 +26,31 @@ const DEFAULT_ACTIONS: UiAction[] = [ "navigate", "url", "call-service", + "assist", "none", ]; +const ASSIST_SCHEMA = [ + { + type: "grid", + name: "", + schema: [ + { + name: "pipeline_id", + selector: { + assist_pipeline: {}, + }, + }, + { + name: "start_listening", + selector: { + boolean: {}, + }, + }, + ], + }, +] as const satisfies readonly HaFormSchema[]; + @customElement("hui-action-editor") export class HuiActionEditor extends LitElement { @property() public config?: ActionConfig; @@ -101,7 +125,7 @@ export class HuiActionEditor extends LitElement { ? html` ` - : ""} + : nothing}
${this.config?.action === "navigate" ? html` @@ -114,7 +138,7 @@ export class HuiActionEditor extends LitElement { @value-changed=${this._navigateValueChanged} > ` - : ""} + : nothing} ${this.config?.action === "url" ? html` ` - : ""} + : nothing} ${this.config?.action === "call-service" ? html` ` - : ""} + : nothing} + ${this.config?.action === "assist" + ? html` + + + ` + : nothing} `; } @@ -182,7 +218,7 @@ export class HuiActionEditor extends LitElement { return; } const target = ev.target! as EditorTarget; - const value = ev.target.value; + const value = ev.target.value ?? ev.target.checked; if (this[`_${target.configValue}`] === value) { return; } @@ -193,6 +229,21 @@ export class HuiActionEditor extends LitElement { } } + private _formValueChanged(ev): void { + ev.stopPropagation(); + const value = ev.detail.value; + + fireEvent(this, "value-changed", { + value: value, + }); + } + + private _computeFormLabel(schema: SchemaUnion) { + return this.hass?.localize( + `ui.panel.lovelace.editor.action-editor.${schema.name}` + ); + } + private _serviceValueChanged(ev: CustomEvent) { ev.stopPropagation(); const value = { @@ -240,17 +291,25 @@ export class HuiActionEditor extends LitElement { width: 100%; } ha-service-control, - ha-navigation-picker { + ha-navigation-picker, + ha-form { display: block; } ha-textfield, ha-service-control, - ha-navigation-picker { + ha-navigation-picker, + ha-form { margin-top: 8px; } ha-service-control { --service-control-padding: 0; } + ha-formfield { + display: flex; + height: 56px; + align-items: center; + --mdc-typography-body2-font-size: 1em; + } `; } } diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index 18c32eafa4..37d5a4d27e 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -2,25 +2,37 @@ import "@material/mwc-button"; import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { mdiArrowDown, mdiArrowUp, mdiDotsVertical } from "@mdi/js"; +import deepClone from "deep-clone-simple"; import { - css, CSSResultGroup, - html, LitElement, PropertyValues, TemplateResult, + css, + html, + nothing, } from "lit"; import { customElement, property, queryAssignedNodes } from "lit/decorators"; +import { storage } from "../../../common/decorators/storage"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-button-menu"; import "../../../components/ha-icon-button"; -import { saveConfig } from "../../../data/lovelace"; -import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; +import { LovelaceCardConfig, saveConfig } from "../../../data/lovelace"; +import { + showAlertDialog, + showPromptDialog, +} from "../../../dialogs/generic/show-dialog-box"; import { HomeAssistant } from "../../../types"; import { showSaveSuccessToast } from "../../../util/toast-saved-success"; import { computeCardSize } from "../common/compute-card-size"; import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; -import { addCard, deleteCard, moveCard, swapCard } from "../editor/config-util"; +import { + addCard, + deleteCard, + moveCard, + moveCardToPosition, + swapCard, +} from "../editor/config-util"; import { showSelectViewDialog } from "../editor/select-view/show-select-view-dialog"; import { Lovelace, LovelaceCard } from "../types"; @@ -34,6 +46,16 @@ export class HuiCardOptions extends LitElement { @queryAssignedNodes() private _assignedNodes?: NodeListOf; + @property({ type: Boolean }) public showPosition = false; + + @storage({ + key: "lovelaceClipboard", + state: false, + subscribe: false, + storage: "sessionStorage", + }) + protected _clipboard?: LovelaceCardConfig; + public getCardSize() { return this._assignedNodes ? computeCardSize(this._assignedNodes[0]) : 1; } @@ -58,7 +80,7 @@ export class HuiCardOptions extends LitElement { "ui.panel.lovelace.editor.edit_card.edit" )} -
+
+ ${this.showPosition + ? html` +
${this.path![1] + 1}
+
` + : nothing} + ${this.hass!.localize( + "ui.panel.lovelace.editor.edit_card.copy" + )} + ${this.hass!.localize( + "ui.panel.lovelace.editor.edit_card.cut" + )} ${this.hass!.localize( "ui.panel.lovelace.editor.edit_card.delete" @@ -135,6 +177,24 @@ export class HuiCardOptions extends LitElement { align-items: center; } + .right { + display: flex; + align-items: center; + } + + .position-badge { + display: block; + width: 24px; + line-height: 24px; + box-sizing: border-box; + border-radius: 50%; + font-weight: 500; + text-align: center; + font-size: 14px; + background-color: var(--app-header-edit-background-color, #455a64); + color: var(--app-header-edit-text-color, white); + } + ha-icon-button { color: var(--primary-text-color); } @@ -163,7 +223,13 @@ export class HuiCardOptions extends LitElement { this._duplicateCard(); break; case 2: - this._deleteCard(); + this._copyCard(); + break; + case 3: + this._cutCard(); + break; + case 4: + this._deleteCard(true); break; } } @@ -183,6 +249,17 @@ export class HuiCardOptions extends LitElement { fireEvent(this, "ll-edit-card", { path: this.path! }); } + private _cutCard(): void { + this._copyCard(); + this._deleteCard(false); + } + + private _copyCard(): void { + const cardConfig = + this.lovelace!.config.views[this.path![0]].cards![this.path![1]]; + this._clipboard = deepClone(cardConfig); + } + private _cardUp(): void { const lovelace = this.lovelace!; const path = this.path!; @@ -199,6 +276,30 @@ export class HuiCardOptions extends LitElement { ); } + private async _changeCardPosition(): Promise { + const lovelace = this.lovelace!; + const path = this.path!; + + const positionString = await showPromptDialog(this, { + title: this.hass!.localize( + "ui.panel.lovelace.editor.change_position.title" + ), + text: this.hass!.localize( + "ui.panel.lovelace.editor.change_position.text" + ), + inputType: "number", + placeholder: String(path[1] + 1), + }); + + if (!positionString) return; + + const position = parseInt(positionString); + + if (isNaN(position)) return; + + lovelace.saveConfig(moveCardToPosition(lovelace.config, path, position)); + } + private _moveCard(): void { showSelectViewDialog(this, { lovelaceConfig: this.lovelace!.config, @@ -236,8 +337,8 @@ export class HuiCardOptions extends LitElement { }); } - private _deleteCard(): void { - fireEvent(this, "ll-delete-card", { path: this.path! }); + private _deleteCard(confirm: boolean): void { + fireEvent(this, "ll-delete-card", { path: this.path!, confirm }); } } diff --git a/src/panels/lovelace/components/hui-energy-period-selector.ts b/src/panels/lovelace/components/hui-energy-period-selector.ts index c4054e7e7c..9cd6666cd3 100644 --- a/src/panels/lovelace/components/hui-energy-period-selector.ts +++ b/src/panels/lovelace/components/hui-energy-period-selector.ts @@ -21,6 +21,7 @@ import { import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { calcDate } from "../../../common/datetime/calc_date"; import { firstWeekdayIndex } from "../../../common/datetime/first_weekday"; import { formatDate, @@ -105,17 +106,27 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
${this._period === "day" - ? formatDate(this._startDate, this.hass.locale) + ? formatDate(this._startDate, this.hass.locale, this.hass.config) : this._period === "month" - ? formatDateMonthYear(this._startDate, this.hass.locale) + ? formatDateMonthYear( + this._startDate, + this.hass.locale, + this.hass.config + ) : this._period === "year" - ? formatDateYear(this._startDate, this.hass.locale) + ? formatDateYear( + this._startDate, + this.hass.locale, + this.hass.config + ) : `${formatDateShort( this._startDate, - this.hass.locale + this.hass.locale, + this.hass.config )} – ${formatDateShort( this._endDate || new Date(), - this.hass.locale + this.hass.locale, + this.hass.config )}`} string; + [key: string]: ( + ts: Date, + lang: FrontendLocaleData, + config: HassConfig + ) => string; } = { date: formatDate, datetime: formatDateTime, @@ -63,7 +68,9 @@ class HuiTimestampDisplay extends LitElement { return html` ${this._relative} `; } if (format in FORMATS) { - return html` ${FORMATS[format](this.ts, this.hass.locale)} `; + return html` + ${FORMATS[format](this.ts, this.hass.locale, this.hass.config)} + `; } return html`${this.hass.localize( "ui.panel.lovelace.components.timestamp-display.invalid_format" diff --git a/src/panels/lovelace/create-element/create-card-element.ts b/src/panels/lovelace/create-element/create-card-element.ts index 98390dc9bf..3ecd7b4a16 100644 --- a/src/panels/lovelace/create-element/create-card-element.ts +++ b/src/panels/lovelace/create-element/create-card-element.ts @@ -53,6 +53,8 @@ const LAZY_LOAD_TYPES = { import("../cards/energy/hui-energy-grid-neutrality-gauge-card"), "energy-solar-consumed-gauge": () => import("../cards/energy/hui-energy-solar-consumed-gauge-card"), + "energy-self-sufficiency-gauge": () => + import("../cards/energy/hui-energy-self-sufficiency-gauge-card"), "energy-solar-graph": () => import("../cards/energy/hui-energy-solar-graph-card"), "energy-sources-table": () => diff --git a/src/panels/lovelace/editor/card-editor/hui-card-element-editor.ts b/src/panels/lovelace/editor/card-editor/hui-card-element-editor.ts index 08a0b9bd42..be036fb404 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-element-editor.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-element-editor.ts @@ -1,7 +1,7 @@ import { customElement } from "lit/decorators"; import type { LovelaceCardConfig } from "../../../../data/lovelace"; import { getCardElementClass } from "../../create-element/create-card-element"; -import type { LovelaceCardEditor } from "../../types"; +import type { LovelaceCardEditor, LovelaceConfigForm } from "../../types"; import { HuiElementEditor } from "../hui-element-editor"; @customElement("hui-card-element-editor") @@ -16,6 +16,17 @@ export class HuiCardElementEditor extends HuiElementEditor { return undefined; } + + protected async getConfigForm(): Promise { + const elClass = await getCardElementClass(this.configElementType!); + + // Check if a schema exists + if (elClass && elClass.getConfigForm) { + return elClass.getConfigForm(); + } + + return undefined; + } } declare global { diff --git a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts index 16f2a4cead..415b89b993 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts @@ -15,6 +15,7 @@ import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; import { until } from "lit/directives/until"; import memoizeOne from "memoize-one"; +import { storage } from "../../../../common/decorators/storage"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-circular-progress"; import "../../../../components/search-input"; @@ -49,6 +50,14 @@ interface CardElement { export class HuiCardPicker extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant; + @storage({ + key: "lovelaceClipboard", + state: true, + subscribe: true, + storage: "sessionStorage", + }) + private _clipboard?: LovelaceCardConfig; + @state() private _cards: CardElement[] = []; public lovelace?: LovelaceConfig; @@ -114,6 +123,37 @@ export class HuiCardPicker extends LitElement { })} >
+ ${this._clipboard + ? html` + ${until( + this._renderCardElement( + { + type: this._clipboard.type, + showElement: true, + isCustom: false, + name: this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.paste" + ), + description: `${this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.paste_description", + { + type: this._clipboard.type, + } + )}`, + }, + this._clipboard + ), + html` +
+ +
+ ` + )} + ` + : nothing} ${this._filterCards(this._cards, this._filter).map( (cardElement: CardElement) => cardElement.element )} @@ -272,7 +312,10 @@ export class HuiCardPicker extends LitElement { } } - private async _renderCardElement(card: Card): Promise { + private async _renderCardElement( + card: Card, + config?: LovelaceCardConfig + ): Promise { let { type } = card; const { showElement, isCustom, name, description } = card; const customCard = isCustom ? getCustomCardEntry(type) : undefined; @@ -281,15 +324,17 @@ export class HuiCardPicker extends LitElement { } let element: LovelaceCard | undefined; - let cardConfig: LovelaceCardConfig = { type }; + let cardConfig: LovelaceCardConfig = config ?? { type }; if (this.hass && this.lovelace) { - cardConfig = await getCardStubConfig( - this.hass, - type, - this._unusedEntities!, - this._usedEntities! - ); + if (!config) { + cardConfig = await getCardStubConfig( + this.hass, + type, + this._unusedEntities!, + this._usedEntities! + ); + } if (showElement) { try { diff --git a/src/panels/lovelace/editor/config-elements/hui-conditional-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-conditional-card-editor.ts index 69c48fb105..b2c8566af6 100644 --- a/src/panels/lovelace/editor/config-elements/hui-conditional-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-conditional-card-editor.ts @@ -1,6 +1,8 @@ import "@material/mwc-list/mwc-list-item"; import "@material/mwc-tab-bar/mwc-tab-bar"; import "@material/mwc-tab/mwc-tab"; +import { mdiContentCopy } from "@mdi/js"; +import deepClone from "deep-clone-simple"; import type { MDCTabBarActivatedEvent } from "@material/tab-bar"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; @@ -13,6 +15,7 @@ import { optional, string, } from "superstruct"; +import { storage } from "../../../../common/decorators/storage"; import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; import { stopPropagation } from "../../../../common/dom/stop_propagation"; import "../../../../components/entity/ha-entity-picker"; @@ -56,6 +59,14 @@ export class HuiConditionalCardEditor @property({ attribute: false }) public lovelace?: LovelaceConfig; + @storage({ + key: "lovelaceClipboard", + state: false, + subscribe: false, + storage: "sessionStorage", + }) + protected _clipboard?: LovelaceCardConfig; + @state() private _config?: ConditionalCardConfig; @state() private _GUImode = true; @@ -114,6 +125,14 @@ export class HuiConditionalCardEditor : "ui.panel.lovelace.editor.edit_card.show_visual_editor" )} + + ${this.hass!.localize( "ui.panel.lovelace.editor.card.conditional.change_type" @@ -238,6 +257,13 @@ export class HuiConditionalCardEditor fireEvent(this, "config-changed", { config: this._config }); } + protected _handleCopyCard() { + if (!this._config) { + return; + } + this._clipboard = deepClone(this._config.card); + } + private _handleCardChanged(ev: HASSDomEvent): void { ev.stopPropagation(); if (!this._config) { diff --git a/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts index 3379792b6f..4c93882620 100644 --- a/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts @@ -1,16 +1,12 @@ -import { html, LitElement, nothing } from "lit"; -import { customElement, property, state } from "lit/decorators"; import { assert, assign, boolean, object, optional, string } from "superstruct"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-form/ha-form"; -import type { SchemaUnion } from "../../../../components/ha-form/types"; -import type { HomeAssistant } from "../../../../types"; -import type { EntityCardConfig } from "../../cards/types"; +import { LocalizeFunc } from "../../../../common/translations/localize"; +import { HaFormSchema } from "../../../../components/ha-form/types"; +import { EntityCardConfig } from "../../cards/types"; import { headerFooterConfigStructs } from "../../header-footer/structs"; -import type { LovelaceCardEditor } from "../../types"; +import { LovelaceConfigForm } from "../../types"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; -const cardConfigStruct = assign( +const struct = assign( baseLovelaceCardConfig, object({ entity: optional(string()), @@ -54,67 +50,19 @@ const SCHEMA = [ { name: "state_color", selector: { boolean: {} } }, ], }, -] as const; - -@customElement("hui-entity-card-editor") -export class HuiEntityCardEditor - extends LitElement - implements LovelaceCardEditor -{ - @property({ attribute: false }) public hass?: HomeAssistant; - - @state() private _config?: EntityCardConfig; - - public setConfig(config: EntityCardConfig): void { - assert(config, cardConfigStruct); - this._config = config; - } - - protected render() { - if (!this.hass || !this._config) { - return nothing; - } - - return html` - - `; - } - - private _valueChanged(ev: CustomEvent): void { - const config = ev.detail.value; - Object.keys(config).forEach((k) => config[k] === "" && delete config[k]); - fireEvent(this, "config-changed", { config }); - } - - private _computeLabelCallback = (schema: SchemaUnion) => { - if (schema.name === "entity") { - return this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.entity" - ); - } +] as HaFormSchema[]; +const entityCardConfigForm: LovelaceConfigForm = { + schema: SCHEMA, + assertConfig: (config: EntityCardConfig) => assert(config, struct), + computeLabel: (schema: HaFormSchema, localize: LocalizeFunc) => { if (schema.name === "theme") { - return `${this.hass!.localize( + return `${localize( "ui.panel.lovelace.editor.card.generic.theme" - )} (${this.hass!.localize( - "ui.panel.lovelace.editor.card.config.optional" - )})`; + )} (${localize("ui.panel.lovelace.editor.card.config.optional")})`; } + return localize(`ui.panel.lovelace.editor.card.generic.${schema.name}`); + }, +}; - return this.hass!.localize( - `ui.panel.lovelace.editor.card.generic.${schema.name}` - ); - }; -} - -declare global { - interface HTMLElementTagNameMap { - "hui-entity-card-editor": HuiEntityCardEditor; - } -} +export default entityCardConfigForm; diff --git a/src/panels/lovelace/editor/config-elements/hui-form-editor.ts b/src/panels/lovelace/editor/config-elements/hui-form-editor.ts new file mode 100644 index 0000000000..9db046adfe --- /dev/null +++ b/src/panels/lovelace/editor/config-elements/hui-form-editor.ts @@ -0,0 +1,82 @@ +import { CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter"; +import { LocalizeFunc } from "../../../../common/translations/localize"; +import "../../../../components/ha-form/ha-form"; +import type { HaFormSchema } from "../../../../components/ha-form/types"; +import { LovelaceCardConfig } from "../../../../data/lovelace"; +import type { HomeAssistant } from "../../../../types"; +import type { LovelaceGenericElementEditor } from "../../types"; +import { configElementStyle } from "./config-elements-style"; + +@customElement("hui-form-editor") +export class HuiFormEditor + extends LitElement + implements LovelaceGenericElementEditor +{ + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public schema!: HaFormSchema[]; + + @state() private _config?: LovelaceCardConfig; + + public assertConfig(_config: LovelaceCardConfig): void { + return undefined; + } + + public setConfig(config: LovelaceCardConfig): void { + this.assertConfig(config); + this._config = config; + } + + protected render() { + if (!this._config) { + return nothing; + } + + return html` + + `; + } + + public computeLabel = ( + _schema: HaFormSchema, + _localize: LocalizeFunc + ): string | undefined => undefined; + + public computeHelper = ( + _schema: HaFormSchema, + _localize: LocalizeFunc + ): string | undefined => undefined; + + private _computeLabelCallback = (schema: HaFormSchema) => + this.computeLabel(schema, this.hass.localize) || + this.hass.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ) || + capitalizeFirstLetter(schema.name.split("_").join(" ")); + + private _computeHelperCallback = (schema: HaFormSchema) => + this.computeHelper(schema, this.hass.localize); + + private _valueChanged(ev: CustomEvent): void { + const config = ev.detail.value; + fireEvent(this, "config-changed", { config }); + } + + static styles: CSSResultGroup = configElementStyle; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-form-editor": HuiFormEditor; + } +} diff --git a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts index 29e001688d..1ce62d6a78 100644 --- a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts @@ -1,22 +1,21 @@ -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { assert, assign, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; +import { SchemaUnion } from "../../../../components/ha-form/types"; import "../../../../components/ha-theme-picker"; -import { ActionConfig } from "../../../../data/lovelace"; import { HomeAssistant } from "../../../../types"; import { PictureCardConfig } from "../../cards/types"; import "../../components/hui-action-editor"; import { LovelaceCardEditor } from "../../types"; import { actionConfigStruct } from "../structs/action-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; -import { EditorTarget } from "../types"; -import { configElementStyle } from "./config-elements-style"; const cardConfigStruct = assign( baseLovelaceCardConfig, object({ image: optional(string()), + image_entity: optional(string()), tap_action: optional(actionConfigStruct), hold_action: optional(actionConfigStruct), theme: optional(string()), @@ -24,6 +23,21 @@ const cardConfigStruct = assign( }) ); +const SCHEMA = [ + { name: "image", selector: { text: {} } }, + { name: "image_entity", selector: { entity: { domain: "image" } } }, + { name: "alt_text", selector: { text: {} } }, + { name: "theme", selector: { theme: {} } }, + { + name: "tap_action", + selector: { ui_action: {} }, + }, + { + name: "hold_action", + selector: { ui_action: {} }, + }, +] as const; + @customElement("hui-picture-card-editor") export class HuiPictureCardEditor extends LitElement @@ -38,129 +52,45 @@ export class HuiPictureCardEditor this._config = config; } - get _image(): string { - return this._config!.image || ""; - } - - get _tap_action(): ActionConfig { - return this._config!.tap_action || { action: "none" }; - } - - get _hold_action(): ActionConfig { - return this._config!.hold_action || { action: "none" }; - } - - get _theme(): string { - return this._config!.theme || ""; - } - - get _alt_text(): string { - return this._config!.alt_text || ""; - } - protected render() { if (!this.hass || !this._config) { return nothing; } - const actions = ["navigate", "url", "call-service", "none"]; - return html` -
- - - - - -
+ `; } private _valueChanged(ev: CustomEvent): void { - if (!this._config || !this.hass) { - return; - } - const target = ev.target! as EditorTarget; - const value = ev.detail?.value ?? target.value; - - if (this[`_${target.configValue}`] === value) { - return; - } - if (target.configValue) { - if (value !== false && !value) { - this._config = { ...this._config }; - delete this._config[target.configValue!]; - } else { - this._config = { - ...this._config, - [target.configValue!]: value, - }; - } - } - fireEvent(this, "config-changed", { config: this._config }); + fireEvent(this, "config-changed", { config: ev.detail.value }); } - static get styles(): CSSResultGroup { - return [ - configElementStyle, - css` - ha-textfield { - display: block; - margin-bottom: 8px; - } - `, - ]; - } + private _computeLabelCallback = (schema: SchemaUnion) => { + switch (schema.name) { + case "theme": + return `${this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.theme" + )} (${this.hass!.localize( + "ui.panel.lovelace.editor.card.config.optional" + )})`; + default: + return ( + this.hass!.localize( + `ui.panel.lovelace.editor.card.picture-card.${schema.name}` + ) || + this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ) + ); + } + }; } declare global { diff --git a/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts index 76c0634698..c6c3d12146 100644 --- a/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts @@ -22,6 +22,7 @@ const cardConfigStruct = assign( title: optional(string()), entity: optional(string()), image: optional(string()), + image_entity: optional(string()), camera_image: optional(string()), camera_view: optional(string()), aspect_ratio: optional(string()), @@ -35,6 +36,7 @@ const cardConfigStruct = assign( const SCHEMA = [ { name: "title", selector: { text: {} } }, { name: "image", selector: { text: {} } }, + { name: "image_entity", selector: { entity: { domain: "image" } } }, { name: "camera_image", selector: { entity: { domain: "camera" } } }, { name: "", diff --git a/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts index 9809a3ac6a..79dafd0aaf 100644 --- a/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts @@ -1,6 +1,14 @@ -import { mdiArrowLeft, mdiArrowRight, mdiDelete, mdiPlus } from "@mdi/js"; +import { + mdiArrowLeft, + mdiArrowRight, + mdiDelete, + mdiContentCut, + mdiContentCopy, + mdiPlus, +} from "@mdi/js"; import "@polymer/paper-tabs"; import "@polymer/paper-tabs/paper-tab"; +import deepClone from "deep-clone-simple"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { @@ -12,6 +20,7 @@ import { optional, string, } from "superstruct"; +import { storage } from "../../../../common/decorators/storage"; import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-icon-button"; import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace"; @@ -43,6 +52,14 @@ export class HuiStackCardEditor @property({ attribute: false }) public lovelace?: LovelaceConfig; + @storage({ + key: "lovelaceClipboard", + state: false, + subscribe: false, + storage: "sessionStorage", + }) + protected _clipboard?: LovelaceCardConfig; + @state() protected _config?: StackCardConfig; @state() protected _selectedCard = 0; @@ -129,6 +146,22 @@ export class HuiStackCardEditor .move=${1} > + + + + + !metaDatas.some((metaData) => statisticsMetaHasType( metaData, supportedStatTypeMap[stat_type] @@ -246,12 +246,10 @@ export class HuiStatisticsGraphCardEditor ); const configured_stat_types = this._config!.stat_types ? ensureArray(this._config.stat_types) - : stat_types.filter( - (stat_type) => - stat_type !== "change" && - this._metaDatas?.every((metaData) => - statisticsMetaHasType(metaData, stat_type) - ) + : stat_types.filter((stat_type) => + this._metaDatas?.some((metaData) => + statisticsMetaHasType(metaData, stat_type) + ) ); const data = { chart_type: "line", @@ -320,9 +318,7 @@ export class HuiStatisticsGraphCardEditor : undefined; if (config.stat_types && config.entities.length) { config.stat_types = ensureArray(config.stat_types).filter((stat_type) => - metadata!.every((metaData) => - statisticsMetaHasType(metaData, stat_type) - ) + metadata!.some((metaData) => statisticsMetaHasType(metaData, stat_type)) ); if (!config.stat_types.length) { delete config.stat_types; diff --git a/src/panels/lovelace/editor/config-util.ts b/src/panels/lovelace/editor/config-util.ts index bf15aafbdc..ce8f11bfbb 100644 --- a/src/panels/lovelace/editor/config-util.ts +++ b/src/panels/lovelace/editor/config-util.ts @@ -185,6 +185,35 @@ export const swapCard = ( }; }; +export const moveCardToPosition = ( + config: LovelaceConfig, + path: [number, number], + position: number +) => { + const view = config.views[path[0]]; + + const oldIndex = path[1]; + const newIndex = Math.max(Math.min(position - 1, view.cards!.length - 1), 0); + + const newCards = [...view.cards!]; + + const card = newCards[oldIndex]; + newCards.splice(oldIndex, 1); + newCards.splice(newIndex, 0, card); + + const newView = { + ...view, + cards: newCards, + }; + + return { + ...config, + views: config.views.map((origView, index) => + index === path[0] ? newView : origView + ), + }; +}; + export const moveCard = ( config: LovelaceConfig, fromPath: [number, number], diff --git a/src/panels/lovelace/editor/hui-element-editor.ts b/src/panels/lovelace/editor/hui-element-editor.ts index b7b2dbf324..f711c63210 100644 --- a/src/panels/lovelace/editor/hui-element-editor.ts +++ b/src/panels/lovelace/editor/hui-element-editor.ts @@ -8,13 +8,13 @@ import { PropertyValues, TemplateResult, } from "lit"; -import { property, state, query } from "lit/decorators"; +import { property, query, state } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; import { handleStructError } from "../../../common/structs/handle-errors"; import { deepEqual } from "../../../common/util/deep-equal"; +import "../../../components/ha-alert"; import "../../../components/ha-circular-progress"; import "../../../components/ha-code-editor"; -import "../../../components/ha-alert"; import type { HaCodeEditor } from "../../../components/ha-code-editor"; import type { LovelaceCardConfig, @@ -23,11 +23,15 @@ import type { import type { HomeAssistant } from "../../../types"; import type { LovelaceRowConfig } from "../entity-rows/types"; import { LovelaceHeaderFooterConfig } from "../header-footer/types"; -import type { LovelaceGenericElementEditor } from "../types"; +import { LovelaceTileFeatureConfig } from "../tile-features/types"; +import type { + LovelaceConfigForm, + LovelaceGenericElementEditor, +} from "../types"; +import type { HuiFormEditor } from "./config-elements/hui-form-editor"; import "./config-elements/hui-generic-entity-row-editor"; import { GUISupportError } from "./gui-support-error"; import { EditSubElementEvent, GUIModeChangedEvent } from "./types"; -import { LovelaceTileFeatureConfig } from "../tile-features/types"; export interface ConfigChangedEvent { config: @@ -182,6 +186,10 @@ export abstract class HuiElementEditor extends LitElement { return undefined; } + protected async getConfigForm(): Promise { + return undefined; + } + protected get configElementType(): string | undefined { return this.value ? (this.value as any).type : undefined; } @@ -328,6 +336,25 @@ export abstract class HuiElementEditor extends LitElement { this._loading = true; configElement = await this.getConfigElement(); + if (!configElement) { + const form = await this.getConfigForm(); + if (form) { + await import("./config-elements/hui-form-editor"); + configElement = document.createElement("hui-form-editor"); + const { schema, assertConfig, computeLabel, computeHelper } = form; + (configElement as HuiFormEditor).schema = schema; + if (computeLabel) { + (configElement as HuiFormEditor).computeLabel = computeLabel; + } + if (computeHelper) { + (configElement as HuiFormEditor).computeHelper = computeHelper; + } + if (assertConfig) { + (configElement as HuiFormEditor).assertConfig = assertConfig; + } + } + } + if (configElement) { configElement.hass = this.hass; if ("lovelace" in configElement) { diff --git a/src/panels/lovelace/editor/structs/action-struct.ts b/src/panels/lovelace/editor/structs/action-struct.ts index e8a6ec82be..512e45aa84 100644 --- a/src/panels/lovelace/editor/structs/action-struct.ts +++ b/src/panels/lovelace/editor/structs/action-struct.ts @@ -51,6 +51,12 @@ const actionConfigStructNavigate = object({ confirmation: optional(actionConfigStructConfirmation), }); +const actionConfigStructAssist = type({ + action: literal("assist"), + pipeline_id: optional(string()), + start_listening: optional(boolean()), +}); + const actionConfigStructCustom = type({ action: literal("fire-dom-event"), }); @@ -63,6 +69,7 @@ export const actionConfigStructType = object({ "call-service", "url", "navigate", + "assist", ]), confirmation: optional(actionConfigStructConfirmation), }); @@ -82,6 +89,9 @@ export const actionConfigStruct = dynamic((value) => { case "url": { return actionConfigStructUrl; } + case "assist": { + return actionConfigStructAssist; + } } } diff --git a/src/panels/lovelace/editor/tile-feature-editor/hui-tile-feature-element-editor.ts b/src/panels/lovelace/editor/tile-feature-editor/hui-tile-feature-element-editor.ts index 573d1e075c..58fb381399 100644 --- a/src/panels/lovelace/editor/tile-feature-editor/hui-tile-feature-element-editor.ts +++ b/src/panels/lovelace/editor/tile-feature-editor/hui-tile-feature-element-editor.ts @@ -4,7 +4,10 @@ import { LovelaceTileFeatureConfig, LovelaceTileFeatureContext, } from "../../tile-features/types"; -import type { LovelaceTileFeatureEditor } from "../../types"; +import type { + LovelaceConfigForm, + LovelaceTileFeatureEditor, +} from "../../types"; import { HuiElementEditor } from "../hui-element-editor"; @customElement("hui-tile-feature-element-editor") @@ -24,6 +27,17 @@ export class HuiTileFeatureElementEditor extends HuiElementEditor< return undefined; } + + protected async getConfigForm(): Promise { + const elClass = await getTileFeatureElementClass(this.configElementType!); + + // Check if a schema exists + if (elClass && elClass.getConfigForm) { + return elClass.getConfigForm(); + } + + return undefined; + } } declare global { diff --git a/src/panels/lovelace/elements/hui-image-element.ts b/src/panels/lovelace/elements/hui-image-element.ts index e54e23fb68..e86058a183 100644 --- a/src/panels/lovelace/elements/hui-image-element.ts +++ b/src/panels/lovelace/elements/hui-image-element.ts @@ -1,6 +1,7 @@ import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; +import { ImageEntity, computeImageUrl } from "../../../data/image"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; import { computeTooltip } from "../common/compute-tooltip"; @@ -34,12 +35,16 @@ export class HuiImageElement extends LitElement implements LovelaceElement { if (!this._config || !this.hass) { return nothing; } + let stateObj: ImageEntity | undefined; + if (this._config.image_entity) { + stateObj = this.hass.states[this._config.image_entity] as ImageEntity; + } return html` @@ -57,7 +59,9 @@ class HuiDateEntityRow extends LitElement implements LovelaceRow { } private _dateChanged(ev: CustomEvent<{ value: string }>): void { - setDateValue(this.hass!, this._config!.entity, ev.detail.value); + if (ev.detail.value) { + setDateValue(this.hass!, this._config!.entity, ev.detail.value); + } } } diff --git a/src/panels/lovelace/entity-rows/hui-datetime-entity-row.ts b/src/panels/lovelace/entity-rows/hui-datetime-entity-row.ts index e4aecd4cbf..b215f51121 100644 --- a/src/panels/lovelace/entity-rows/hui-datetime-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-datetime-entity-row.ts @@ -10,7 +10,7 @@ import { import { customElement, property, state } from "lit/decorators"; import "../../../components/ha-date-input"; import { format } from "date-fns"; -import { isUnavailableState } from "../../../data/entity"; +import { isUnavailableState, UNAVAILABLE } from "../../../data/entity"; import { setDateTimeValue } from "../../../data/datetime"; import type { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; @@ -52,11 +52,13 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { `; } - const unavailable = isUnavailableState(stateObj.state); + const unavailable = stateObj.state === UNAVAILABLE; - const dateObj = unavailable ? undefined : new Date(stateObj.state); - const time = dateObj ? format(dateObj, "HH:mm:ss") : ""; - const date = dateObj ? format(dateObj, "yyyy-MM-dd") : ""; + const dateObj = isUnavailableState(stateObj.state) + ? undefined + : new Date(stateObj.state); + const time = dateObj ? format(dateObj, "HH:mm:ss") : undefined; + const date = dateObj ? format(dateObj, "yyyy-MM-dd") : undefined; return html` ): void { - const stateObj = this.hass!.states[this._config!.entity]; - const dateObj = new Date(stateObj.state); - const newTime = ev.detail.value.split(":").map(Number); - dateObj.setHours(newTime[0], newTime[1], newTime[2]); + if (ev.detail.value) { + const stateObj = this.hass!.states[this._config!.entity]; + const dateObj = new Date(stateObj.state); + const newTime = ev.detail.value.split(":").map(Number); + dateObj.setHours(newTime[0], newTime[1], newTime[2]); - setDateTimeValue(this.hass!, stateObj.entity_id, dateObj); + setDateTimeValue(this.hass!, stateObj.entity_id, dateObj); + } } private _dateChanged(ev: CustomEvent<{ value: string }>): void { - const stateObj = this.hass!.states[this._config!.entity]; - const dateObj = new Date(stateObj.state); - const newDate = ev.detail.value.split("-").map(Number); - dateObj.setFullYear(newDate[0], newDate[1] - 1, newDate[2]); + if (ev.detail.value) { + const stateObj = this.hass!.states[this._config!.entity]; + const dateObj = new Date(stateObj.state); + const newDate = ev.detail.value.split("-").map(Number); + dateObj.setFullYear(newDate[0], newDate[1] - 1, newDate[2]); - setDateTimeValue(this.hass!, stateObj.entity_id, dateObj); + setDateTimeValue(this.hass!, stateObj.entity_id, dateObj); + } } static get styles(): CSSResultGroup { diff --git a/src/panels/lovelace/entity-rows/hui-group-entity-row.ts b/src/panels/lovelace/entity-rows/hui-group-entity-row.ts index 9dad7f8e6d..146943251e 100644 --- a/src/panels/lovelace/entity-rows/hui-group-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-group-entity-row.ts @@ -70,6 +70,7 @@ class HuiGroupEntityRow extends LitElement implements LovelaceRow { this.hass!.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities )}
diff --git a/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts b/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts index f2666cfe99..4463710945 100644 --- a/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts @@ -1,11 +1,18 @@ -import { html, LitElement, PropertyValues, nothing } from "lit"; +import { + CSSResultGroup, + LitElement, + PropertyValues, + css, + html, + nothing, +} from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/entity/ha-entity-toggle"; +import "../../../components/ha-humidifier-state"; import { HumidifierEntity } from "../../../data/humidifier"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-generic-entity-row"; -import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { EntityConfig, LovelaceRow } from "./types"; @@ -43,31 +50,20 @@ class HuiHumidifierEntityRow extends LitElement implements LovelaceRow { } return html` - - + + + `; } + + static get styles(): CSSResultGroup { + return css` + ha-humidifier-state { + text-align: right; + } + `; + } } declare global { diff --git a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts index ab81ff7eb1..6488d33843 100644 --- a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts @@ -100,6 +100,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities, stateObj.state )} diff --git a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts index 2dc020a493..f2f481ad46 100644 --- a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts @@ -193,6 +193,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities )} > diff --git a/src/panels/lovelace/entity-rows/hui-number-entity-row.ts b/src/panels/lovelace/entity-rows/hui-number-entity-row.ts index 0046379e3f..bd55652798 100644 --- a/src/panels/lovelace/entity-rows/hui-number-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-number-entity-row.ts @@ -104,6 +104,7 @@ class HuiNumberEntityRow extends LitElement implements LovelaceRow { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities, stateObj.state )} diff --git a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts index c263519425..2d7d3c51e1 100644 --- a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts @@ -82,6 +82,7 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { this.hass!.localize, stateObj, this.hass!.locale, + this.hass!.config, this.hass!.entities, option )} diff --git a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts index ce291b44cc..0d536ca9f9 100644 --- a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts @@ -83,6 +83,7 @@ class HuiSensorEntityRow extends LitElement implements LovelaceRow { this.hass!.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities )}
diff --git a/src/panels/lovelace/entity-rows/hui-simple-entity-row.ts b/src/panels/lovelace/entity-rows/hui-simple-entity-row.ts index 98e1b44a43..23b4cb8720 100644 --- a/src/panels/lovelace/entity-rows/hui-simple-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-simple-entity-row.ts @@ -53,6 +53,7 @@ class HuiSimpleEntityRow extends LitElement implements LovelaceRow { this.hass!.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities )} diff --git a/src/panels/lovelace/entity-rows/hui-time-entity-row.ts b/src/panels/lovelace/entity-rows/hui-time-entity-row.ts index 66cf5c68b1..932fe99f66 100644 --- a/src/panels/lovelace/entity-rows/hui-time-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-time-entity-row.ts @@ -1,7 +1,7 @@ import { html, LitElement, nothing, PropertyValues, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import "../../../components/ha-date-input"; -import { isUnavailableState } from "../../../data/entity"; +import { isUnavailableState, UNAVAILABLE } from "../../../data/entity"; import { setTimeValue } from "../../../data/time"; import type { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; @@ -42,12 +42,14 @@ class HuiTimeEntityRow extends LitElement implements LovelaceRow { `; } - const unavailable = isUnavailableState(stateObj.state); + const unavailable = stateObj.state === UNAVAILABLE; return html` ): void { - const stateObj = this.hass!.states[this._config!.entity]; - setTimeValue(this.hass!, stateObj.entity_id, ev.detail.value); + if (ev.detail.value) { + const stateObj = this.hass!.states[this._config!.entity]; + setTimeValue(this.hass!, stateObj.entity_id, ev.detail.value); + } } } diff --git a/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts b/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts index 7ea096e9ad..611fd2bdd5 100644 --- a/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts @@ -65,6 +65,7 @@ class HuiToggleEntityRow extends LitElement implements LovelaceRow { this.hass!.localize, stateObj, this.hass!.locale, + this.hass.config, this.hass!.entities )}
diff --git a/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts b/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts index 088869be37..f8badf7b10 100644 --- a/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts @@ -148,6 +148,7 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities ) : html` diff --git a/src/panels/lovelace/special-rows/hui-attribute-row.ts b/src/panels/lovelace/special-rows/hui-attribute-row.ts index b0e95de02f..b8db4ebf56 100644 --- a/src/panels/lovelace/special-rows/hui-attribute-row.ts +++ b/src/panels/lovelace/special-rows/hui-attribute-row.ts @@ -75,6 +75,7 @@ class HuiAttributeRow extends LitElement implements LovelaceRow { this.hass.localize, stateObj, this.hass.locale, + this.hass.config, this.hass.entities, this._config.attribute, attribute diff --git a/src/panels/lovelace/tile-features/hui-alarm-modes-tile-feature.ts b/src/panels/lovelace/tile-features/hui-alarm-modes-tile-feature.ts index d6641b5d38..1675e1b913 100644 --- a/src/panels/lovelace/tile-features/hui-alarm-modes-tile-feature.ts +++ b/src/panels/lovelace/tile-features/hui-alarm-modes-tile-feature.ts @@ -18,10 +18,10 @@ import { ALARM_MODES, } from "../../../data/alarm_control_panel"; import { UNAVAILABLE } from "../../../data/entity"; -import { showEnterCodeDialogDialog } from "../../../dialogs/more-info/components/alarm_control_panel/show-enter-code-dialog"; import { HomeAssistant } from "../../../types"; import { LovelaceTileFeature, LovelaceTileFeatureEditor } from "../types"; import { AlarmModesFileFeatureConfig } from "./types"; +import { showEnterCodeDialogDialog } from "../../../dialogs/enter-code/show-enter-code-dialog"; export const supportsAlarmModesTileFeature = (stateObj: HassEntity) => { const domain = computeDomain(stateObj.entity_id); diff --git a/src/panels/lovelace/tile-features/hui-fan-speed-tile-feature.ts b/src/panels/lovelace/tile-features/hui-fan-speed-tile-feature.ts index fb0f23bf29..da69431ad0 100644 --- a/src/panels/lovelace/tile-features/hui-fan-speed-tile-feature.ts +++ b/src/panels/lovelace/tile-features/hui-fan-speed-tile-feature.ts @@ -59,6 +59,7 @@ class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature { this.hass!.localize, this.stateObj!, this.hass!.locale, + this.hass!.config, this.hass!.entities, speed ); diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index c99e6a51c9..180c923d2e 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -1,4 +1,6 @@ import { HassEntity } from "home-assistant-js-websocket"; +import { LocalizeFunc } from "../../common/translations/localize"; +import { HaFormSchema } from "../../components/ha-form/types"; import { LovelaceBadgeConfig, LovelaceCardConfig, @@ -45,6 +47,19 @@ export interface LovelaceCard extends HTMLElement { setConfig(config: LovelaceCardConfig): void; } +export interface LovelaceConfigForm { + schema: HaFormSchema[]; + assertConfig?: (config: LovelaceCardConfig) => void; + computeLabel?: ( + schema: HaFormSchema, + localize: LocalizeFunc + ) => string | undefined; + computeHelper?: ( + schema: HaFormSchema, + localize: LocalizeFunc + ) => string | undefined; +} + export interface LovelaceCardConstructor extends Constructor { getStubConfig?: ( hass: HomeAssistant, @@ -52,6 +67,7 @@ export interface LovelaceCardConstructor extends Constructor { entitiesFallback: string[] ) => LovelaceCardConfig; getConfigElement?: () => LovelaceCardEditor; + getConfigForm?: () => LovelaceConfigForm; } export interface LovelaceHeaderFooterConstructor @@ -104,11 +120,15 @@ export interface LovelaceTileFeature extends HTMLElement { export interface LovelaceTileFeatureConstructor extends Constructor { - getConfigElement?: () => LovelaceTileFeatureEditor; getStubConfig?: ( hass: HomeAssistant, stateObj?: HassEntity ) => LovelaceTileFeatureConfig; + getConfigElement?: () => LovelaceTileFeatureEditor; + getConfigForm?: () => { + schema: HaFormSchema[]; + assertConfig?: (config: LovelaceCardConfig) => void; + }; isSupported?: (stateObj?: HassEntity) => boolean; } diff --git a/src/panels/lovelace/views/hui-masonry-view.ts b/src/panels/lovelace/views/hui-masonry-view.ts index 20cf91f640..45d3d3f1a0 100644 --- a/src/panels/lovelace/views/hui-masonry-view.ts +++ b/src/panels/lovelace/views/hui-masonry-view.ts @@ -90,9 +90,12 @@ export class MasonryView extends LitElement implements LovelaceViewElement { protected render(): TemplateResult { return html` ${this.badges.length > 0 - ? html`
${this.badges}
` + ? html`
${this.badges}
` : ""} -
+
${this.lovelace?.editMode ? html`
+
${this.lovelace?.editMode ? html` { - confDeleteCard(this, this.hass!, this.lovelace!, ev.detail.path); + if (ev.detail.confirm) { + confDeleteCard(this, this.hass!, this.lovelace!, ev.detail.path); + } else { + const newLovelace = deleteCard(this.lovelace!.config, ev.detail.path); + this.lovelace.saveConfig(newLovelace); + } }); } diff --git a/src/panels/media-browser/ha-bar-media-player.ts b/src/panels/media-browser/ha-bar-media-player.ts index d4f276e3ec..a365bb9720 100644 --- a/src/panels/media-browser/ha-bar-media-player.ts +++ b/src/panels/media-browser/ha-bar-media-player.ts @@ -464,7 +464,7 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) { (entity) => computeStateDomain(entity) === "media_player" && supportsFeature(entity, MediaPlayerEntityFeature.BROWSE_MEDIA) && - !this.hass.entities[entity.entity_id].hidden + !this.hass.entities[entity.entity_id]?.hidden ); } diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index 3bd5da6fc9..e71291bec8 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -9,7 +9,7 @@ import { TemplateResult, } from "lit"; import { customElement, property, query, state } from "lit/decorators"; -import { LocalStorage } from "../../common/decorators/local-storage"; +import { storage } from "../../common/decorators/storage"; import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event"; import { navigate } from "../../common/navigate"; import "../../components/ha-menu-button"; @@ -71,7 +71,11 @@ class PanelMediaBrowser extends LitElement { }, ]; - @LocalStorage("mediaBrowseEntityId", true, false) + @storage({ + key: "mediaBrowseEntityId", + state: true, + subscribe: false, + }) private _entityId = BROWSER_PLAYER; @query("ha-media-player-browse") private _browser!: HaMediaPlayerBrowse; diff --git a/src/panels/profile/ha-long-lived-access-token-dialog.ts b/src/panels/profile/ha-long-lived-access-token-dialog.ts index c16cb7b332..7841e4918a 100644 --- a/src/panels/profile/ha-long-lived-access-token-dialog.ts +++ b/src/panels/profile/ha-long-lived-access-token-dialog.ts @@ -1,4 +1,5 @@ import "@material/mwc-button"; +import { mdiContentCopy } from "@mdi/js"; import { css, CSSResultGroup, @@ -11,9 +12,13 @@ import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; import { createCloseHeading } from "../../components/ha-dialog"; import "../../components/ha-textfield"; +import "../../components/ha-icon-button"; import { haStyleDialog } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; import { LongLivedAccessTokenDialogParams } from "./show-long-lived-access-token-dialog"; +import type { HaTextField } from "../../components/ha-textfield"; +import { copyToClipboard } from "../../common/util/copy-clipboard"; +import { showToast } from "../../util/toast"; const QR_LOGO_URL = "/static/icons/favicon-192x192.png"; @@ -55,8 +60,15 @@ export class HaLongLivedAccessTokenDialog extends LitElement { "ui.panel.profile.long_lived_access_tokens.prompt_copy_token" )} type="text" + iconTrailing readOnly - > + > + +
${this._qrCode ? this._qrCode @@ -71,6 +83,14 @@ export class HaLongLivedAccessTokenDialog extends LitElement { `; } + private async _copyToken(ev): Promise { + const textField = ev.target.parentElement as HaTextField; + await copyToClipboard(textField.value); + showToast(this, { + message: this.hass.localize("ui.common.copied_clipboard"), + }); + } + private async _generateQR() { const qrcode = await import("qrcode"); const canvas = await qrcode.toCanvas(this._params!.token, { @@ -111,6 +131,17 @@ export class HaLongLivedAccessTokenDialog extends LitElement { } ha-textfield { display: block; + --textfield-icon-trailing-padding: 0; + } + ha-textfield > ha-icon-button { + position: relative; + right: -8px; + --mdc-icon-button-size: 36px; + --mdc-icon-size: 20px; + color: var(--secondary-text-color); + inset-inline-start: initial; + inset-inline-end: -8px; + direction: var(--direction); } `, ]; diff --git a/src/panels/profile/ha-panel-profile.ts b/src/panels/profile/ha-panel-profile.ts index 628c44ef38..47bccf93ea 100644 --- a/src/panels/profile/ha-panel-profile.ts +++ b/src/panels/profile/ha-panel-profile.ts @@ -28,6 +28,7 @@ import "./ha-pick-number-format-row"; import "./ha-pick-theme-row"; import "./ha-pick-time-format-row"; import "./ha-pick-date-format-row"; +import "./ha-pick-time-zone-row"; import "./ha-push-notifications-row"; import "./ha-refresh-tokens-card"; import "./ha-set-suspend-row"; @@ -101,6 +102,10 @@ class HaPanelProfile extends LitElement { .narrow=${this.narrow} .hass=${this.hass} > + ${Object.values(DateFormat).map((format) => { - const formattedDate = formatDateNumeric(date, { - ...this.hass.locale, - date_format: format, - }); + const formattedDate = formatDateNumeric( + date, + { + ...this.hass.locale, + date_format: format, + }, + this.hass.config + ); const value = this.hass.localize( `ui.panel.profile.date_format.formats.${format}` ); diff --git a/src/panels/profile/ha-pick-time-format-row.ts b/src/panels/profile/ha-pick-time-format-row.ts index e890e10474..b9d24d597f 100644 --- a/src/panels/profile/ha-pick-time-format-row.ts +++ b/src/panels/profile/ha-pick-time-format-row.ts @@ -35,10 +35,14 @@ class TimeFormatRow extends LitElement { naturalMenuWidth > ${Object.values(TimeFormat).map((format) => { - const formattedTime = formatTime(date, { - ...this.hass.locale, - time_format: format, - }); + const formattedTime = formatTime( + date, + { + ...this.hass.locale, + time_format: format, + }, + this.hass.config + ); const value = this.hass.localize( `ui.panel.profile.time_format.formats.${format}` ); diff --git a/src/panels/profile/ha-pick-time-zone-row.ts b/src/panels/profile/ha-pick-time-zone-row.ts new file mode 100644 index 0000000000..c1bd070406 --- /dev/null +++ b/src/panels/profile/ha-pick-time-zone-row.ts @@ -0,0 +1,76 @@ +import "@material/mwc-list/mwc-list-item"; +import { html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import { formatDateTimeNumeric } from "../../common/datetime/format_date_time"; +import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-card"; +import "../../components/ha-select"; +import "../../components/ha-settings-row"; +import { TimeZone } from "../../data/translation"; +import { HomeAssistant } from "../../types"; + +@customElement("ha-pick-time-zone-row") +class TimeZoneRow extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public narrow!: boolean; + + protected render(): TemplateResult { + const date = new Date(); + return html` + + + ${this.hass.localize("ui.panel.profile.time_zone.header")} + + + ${this.hass.localize("ui.panel.profile.time_zone.description")} + + + ${Object.values(TimeZone).map((format) => { + const formattedTime = formatDateTimeNumeric( + date, + { + ...this.hass.locale, + time_zone: format, + }, + this.hass.config + ); + return html` + ${this.hass.localize( + `ui.panel.profile.time_zone.options.${format}`, + { + timezone: (format === "server" + ? this.hass.config.time_zone + : Intl.DateTimeFormat?.().resolvedOptions?.().timeZone || + "" + ).replace("_", " "), + } + )} + ${formattedTime} + `; + })} + + + `; + } + + private async _handleFormatSelection(ev) { + fireEvent(this, "hass-time-zone-select", ev.target.value); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-pick-time-zone-row": TimeZoneRow; + } +} diff --git a/src/resources/codemirror.ts b/src/resources/codemirror.ts index c419dd4a0c..d21e3ee511 100644 --- a/src/resources/codemirror.ts +++ b/src/resources/codemirror.ts @@ -180,7 +180,7 @@ export const haTheme = EditorView.theme({ ".cm-gutters": { backgroundColor: - "var(--code-editor-gutter-color, var(--mdc-text-field-fill-color, whitesmoke))", + "var(--code-editor-gutter-color, var(--secondary-background-color, whitesmoke))", color: "var(--paper-dialog-color, var(--secondary-text-color))", border: "none", borderRight: diff --git a/src/resources/intl-polyfill-legacy.ts b/src/resources/intl-polyfill-legacy.ts index c3072b0e90..01a057d75a 100644 --- a/src/resources/intl-polyfill-legacy.ts +++ b/src/resources/intl-polyfill-legacy.ts @@ -15,3 +15,5 @@ import "@formatjs/intl-datetimeformat/locale-data/en"; import "@formatjs/intl-datetimeformat/add-all-tz"; import "@formatjs/intl-displaynames/polyfill"; import "@formatjs/intl-displaynames/locale-data/en"; +import "@formatjs/intl-listformat/polyfill"; +import "@formatjs/intl-listformat/locale-data/en"; diff --git a/src/resources/intl-polyfill.ts b/src/resources/intl-polyfill.ts index 8b09aea77a..b98fc722b8 100644 --- a/src/resources/intl-polyfill.ts +++ b/src/resources/intl-polyfill.ts @@ -3,6 +3,7 @@ import { shouldPolyfill as shouldPolyfillDisplayName } from "@formatjs/intl-disp import { shouldPolyfill as shouldPolyfillLocale } from "@formatjs/intl-locale/should-polyfill"; import { shouldPolyfill as shouldPolyfillPluralRules } from "@formatjs/intl-pluralrules/should-polyfill"; import { shouldPolyfill as shouldPolyfillRelativeTime } from "@formatjs/intl-relativetimeformat/should-polyfill"; +import { shouldPolyfill as shouldPolyfillListFormat } from "@formatjs/intl-listformat/should-polyfill"; import { getLocalLanguage } from "../util/common-translation"; import { polyfillLocaleData } from "./locale-data-polyfill"; @@ -29,6 +30,9 @@ const polyfillIntl = async () => { if (shouldPolyfillDisplayName(locale)) { polyfills.push(import("@formatjs/intl-displaynames/polyfill-force")); } + if (shouldPolyfillListFormat(locale)) { + polyfills.push(import("@formatjs/intl-listformat/polyfill-force")); + } if (polyfills.length === 0) { return; } diff --git a/src/resources/locale-data-polyfill.ts b/src/resources/locale-data-polyfill.ts index 2b8b424af0..e408947a65 100644 --- a/src/resources/locale-data-polyfill.ts +++ b/src/resources/locale-data-polyfill.ts @@ -53,6 +53,17 @@ export const polyfillLocaleData = async (language: string) => { // @ts-ignore Intl.DisplayNames.__addLocaleData(await result.json()); } + if ( + Intl.ListFormat && + // @ts-ignore + typeof Intl.ListFormat.__addLocaleData === "function" + ) { + const result = await fetch( + `${__STATIC_PATH__}locale-data/intl-listformat/${language}.json` + ); + // @ts-ignore + Intl.ListFormat.__addLocaleData(await result.json()); + } } catch (e) { // Ignore } diff --git a/src/resources/markdown_worker.ts b/src/resources/markdown-worker.ts similarity index 100% rename from src/resources/markdown_worker.ts rename to src/resources/markdown-worker.ts diff --git a/src/resources/render-markdown.ts b/src/resources/render-markdown.ts index fb9b8cf438..85c584c939 100644 --- a/src/resources/render-markdown.ts +++ b/src/resources/render-markdown.ts @@ -1,5 +1,5 @@ import { Remote, wrap } from "comlink"; -import type { Api } from "./markdown_worker"; +import type { Api } from "./markdown-worker"; type RenderMarkdownType = Api["renderMarkdown"]; type RenderMarkdownParamTypes = Parameters; @@ -12,8 +12,12 @@ export const renderMarkdown = async ( hassOptions?: RenderMarkdownParamTypes[2] ): Promise> => { if (!worker) { - worker = wrap(new Worker(new URL("./markdown_worker", import.meta.url))); + worker = wrap( + new Worker( + /* webpackChunkName: "markdown-worker" */ + new URL("./markdown-worker", import.meta.url) + ) + ); } - return worker.renderMarkdown(content, markedOptions, hassOptions); }; diff --git a/src/resources/resize-observer.polyfill.ts b/src/resources/resize-observer.polyfill.ts index dc5eec0273..5023714d60 100644 --- a/src/resources/resize-observer.polyfill.ts +++ b/src/resources/resize-observer.polyfill.ts @@ -5,7 +5,7 @@ export const loadPolyfillIfNeeded = async () => { } catch (e) { window.ResizeObserver = ( await import( - "@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js" + "@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver" ) ).default; } diff --git a/src/resources/styles.ts b/src/resources/styles.ts index ccec54cc01..37b4f65577 100644 --- a/src/resources/styles.ts +++ b/src/resources/styles.ts @@ -3,7 +3,7 @@ import { css } from "lit"; export const darkStyles = { "primary-background-color": "#111111", "card-background-color": "#1c1c1c", - "secondary-background-color": "#202020", + "secondary-background-color": "#282828", "primary-text-color": "#e1e1e1", "secondary-text-color": "#9b9b9b", "disabled-text-color": "#6f6f6f", diff --git a/src/resources/svg-arc.ts b/src/resources/svg-arc.ts new file mode 100644 index 0000000000..67e29b5157 --- /dev/null +++ b/src/resources/svg-arc.ts @@ -0,0 +1,67 @@ +type Vector = [number, number]; +type Matrix = [Vector, Vector]; + +const rotateVector = ([[a, b], [c, d]]: Matrix, [x, y]: Vector): Vector => [ + a * x + b * y, + c * x + d * y, +]; +const createRotateMatrix = (x: number): Matrix => [ + [Math.cos(x), -Math.sin(x)], + [Math.sin(x), Math.cos(x)], +]; +const addVector = ([a1, a2]: Vector, [b1, b2]: Vector): Vector => [ + a1 + b1, + a2 + b2, +]; + +export const toRadian = (angle: number) => (angle / 180) * Math.PI; + +type ArcOptions = { + x: number; + y: number; + r: number; + start: number; + end: number; + rotate?: number; +}; + +export const arc = (options: ArcOptions) => { + const { x, y, r, start, end, rotate = 0 } = options; + const cx = x; + const cy = y; + const rx = r; + const ry = r; + const t1 = toRadian(start); + const t2 = toRadian(end); + const delta = (t2 - t1) % (2 * Math.PI); + const phi = toRadian(rotate); + + const rotMatrix = createRotateMatrix(phi); + const [sX, sY] = addVector( + rotateVector(rotMatrix, [rx * Math.cos(t1), ry * Math.sin(t1)]), + [cx, cy] + ); + const [eX, eY] = addVector( + rotateVector(rotMatrix, [ + rx * Math.cos(t1 + delta), + ry * Math.sin(t1 + delta), + ]), + [cx, cy] + ); + const fA = delta > Math.PI ? 1 : 0; + const fS = delta > 0 ? 1 : 0; + + return [ + "M", + sX, + sY, + "A", + rx, + ry, + (phi / (2 * Math.PI)) * 360, + fA, + fS, + eX, + eY, + ].join(" "); +}; diff --git a/src/state-summary/state-card-content.js b/src/state-summary/state-card-content.js index 21ca9d08c1..b1669ae994 100644 --- a/src/state-summary/state-card-content.js +++ b/src/state-summary/state-card-content.js @@ -4,6 +4,7 @@ import dynamicContentUpdater from "../common/dom/dynamic_content_updater"; import { stateCardType } from "../common/entity/state_card_type"; import "./state-card-button"; import "./state-card-climate"; +import "./state-card-humidifier"; import "./state-card-configurator"; import "./state-card-cover"; import "./state-card-display"; diff --git a/src/state-summary/state-card-display.ts b/src/state-summary/state-card-display.ts index 9214ecc1fb..a168d05711 100755 --- a/src/state-summary/state-card-display.ts +++ b/src/state-summary/state-card-display.ts @@ -42,7 +42,7 @@ export class StateCardDisplay extends LitElement { this.stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && !isUnavailableState(this.stateObj.state) - ? html` diff --git a/src/state-summary/state-card-humidifier.js b/src/state-summary/state-card-humidifier.js new file mode 100644 index 0000000000..a99b6a539d --- /dev/null +++ b/src/state-summary/state-card-humidifier.js @@ -0,0 +1,55 @@ +import "@polymer/iron-flex-layout/iron-flex-layout-classes"; +import { html } from "@polymer/polymer/lib/utils/html-tag"; +/* eslint-plugin-disable lit */ +import { PolymerElement } from "@polymer/polymer/polymer-element"; +import "../components/entity/state-info"; +import "../components/ha-humidifier-state"; + +class StateCardHumidifier extends PolymerElement { + static get template() { + return html` + + + +
+ ${this.stateInfoTemplate} + +
+ `; + } + + static get stateInfoTemplate() { + return html` + + `; + } + + static get properties() { + return { + hass: Object, + stateObj: Object, + inDialog: { + type: Boolean, + value: false, + }, + }; + } +} +customElements.define("state-card-humidifier", StateCardHumidifier); diff --git a/src/state-summary/state-card-input_number.ts b/src/state-summary/state-card-input_number.ts index d455a5a862..6cd218fb03 100644 --- a/src/state-summary/state-card-input_number.ts +++ b/src/state-summary/state-card-input_number.ts @@ -72,6 +72,7 @@ class StateCardInputNumber extends LitElement { this.hass.localize, this.stateObj, this.hass.locale, + this.hass.config, this.hass.entities, this.stateObj.state )} diff --git a/src/state-summary/state-card-lock.js b/src/state-summary/state-card-lock.js index d0280b7a1d..94ee6e27aa 100644 --- a/src/state-summary/state-card-lock.js +++ b/src/state-summary/state-card-lock.js @@ -3,8 +3,10 @@ import "@polymer/iron-flex-layout/iron-flex-layout-classes"; import { html } from "@polymer/polymer/lib/utils/html-tag"; /* eslint-plugin-disable lit */ import { PolymerElement } from "@polymer/polymer/polymer-element"; +import { supportsFeature } from "../common/entity/supports-feature"; import "../components/entity/state-info"; import LocalizeMixin from "../mixins/localize-mixin"; +import { LockEntityFeature } from "../data/lock"; /* * @appliesMixin LocalizeMixin @@ -19,10 +21,19 @@ class StateCardLock extends LocalizeMixin(PolymerElement) { height: 37px; margin-right: -0.57em; } + [hidden] { + display: none !important; + }
${this.stateInfoTemplate} + [[localize('ui.card.lock.open')]] >(superClass: T) => + class extends superClass { + protected firstUpdated(changedProps: PropertyValues) { + super.firstUpdated(changedProps); + this.addEventListener("hass-action", (ev) => this._handleAction(ev)); + } + + private async _handleAction( + ev: HASSDomEvent<{ config: ActionConfigParams; action: string }> + ) { + if (!this.hass) return; + handleAction(this, this.hass, ev.detail.config, ev.detail.action); + } + }; diff --git a/src/state/connection-mixin.ts b/src/state/connection-mixin.ts index b02e26d837..c2c81cc87f 100644 --- a/src/state/connection-mixin.ts +++ b/src/state/connection-mixin.ts @@ -23,6 +23,7 @@ import { NumberFormat, DateFormat, TimeFormat, + TimeZone, } from "../data/translation"; import { subscribePanels } from "../data/ws-panels"; import { translationMetadata } from "../resources/translations-metadata"; @@ -63,6 +64,7 @@ export const connectionMixin = >( number_format: NumberFormat.language, time_format: TimeFormat.language, date_format: DateFormat.language, + time_zone: TimeZone.local, first_weekday: FirstWeekday.language, }, resources: null as any, diff --git a/src/state/hass-element.ts b/src/state/hass-element.ts index e6e5b9f67a..e017d5c3ef 100644 --- a/src/state/hass-element.ts +++ b/src/state/hass-element.ts @@ -8,6 +8,7 @@ import { HassBaseEl } from "./hass-base-mixin"; import { loggingMixin } from "./logging-mixin"; import { contextMixin } from "./context-mixin"; import MoreInfoMixin from "./more-info-mixin"; +import ActionMixin from "./action-mixin"; import NotificationMixin from "./notification-mixin"; import { panelTitleMixin } from "./panel-title-mixin"; import SidebarMixin from "./sidebar-mixin"; @@ -23,6 +24,7 @@ export class HassElement extends ext(HassBaseEl, [ ThemesMixin, TranslationsMixin, MoreInfoMixin, + ActionMixin, SidebarMixin, DisconnectToastMixin, connectionMixin, diff --git a/src/state/quick-bar-mixin.ts b/src/state/quick-bar-mixin.ts index 04659ac940..0ccaf9f091 100644 --- a/src/state/quick-bar-mixin.ts +++ b/src/state/quick-bar-mixin.ts @@ -1,5 +1,5 @@ import type { PropertyValues } from "lit"; -import tinykeys from "tinykeys"; +import { tinykeys } from "tinykeys"; import { isComponentLoaded } from "../common/config/is_component_loaded"; import { mainWindow } from "../common/dom/get_main_window"; import { diff --git a/src/state/translations-mixin.ts b/src/state/translations-mixin.ts index 149c478925..66b86a140a 100644 --- a/src/state/translations-mixin.ts +++ b/src/state/translations-mixin.ts @@ -15,6 +15,7 @@ import { TimeFormat, DateFormat, TranslationCategory, + TimeZone, } from "../data/translation"; import { translationMetadata } from "../resources/translations-metadata"; import { Constructor, HomeAssistant } from "../types"; @@ -41,6 +42,9 @@ declare global { "hass-date-format-select": { date_format: DateFormat; }; + "hass-time-zone-select": { + time_zone: TimeZone; + }; "hass-first-weekday-select": { first_weekday: FirstWeekday; }; @@ -89,6 +93,9 @@ export default >(superClass: T) => this.addEventListener("hass-date-format-select", (e) => { this._selectDateFormat((e as CustomEvent).detail, true); }); + this.addEventListener("hass-time-zone-select", (e) => { + this._selectTimeZone((e as CustomEvent).detail, true); + }); this.addEventListener("hass-first-weekday-select", (e) => { this._selectFirstWeekday((e as CustomEvent).detail, true); }); @@ -137,6 +144,13 @@ export default >(superClass: T) => // We just got date_format from backend, no need to save back this._selectDateFormat(locale.date_format, false); } + if ( + locale?.time_zone && + this.hass!.locale.time_zone !== locale.time_zone + ) { + // We just got time_zone from backend, no need to save back + this._selectTimeZone(locale.time_zone, false); + } if ( locale?.first_weekday && this.hass!.locale.first_weekday !== locale.first_weekday @@ -203,6 +217,15 @@ export default >(superClass: T) => } } + private _selectTimeZone(time_zone: TimeZone, saveToBackend: boolean) { + this._updateHass({ + locale: { ...this.hass!.locale, time_zone }, + }); + if (saveToBackend) { + saveTranslationPreferences(this.hass!, this.hass!.locale); + } + } + private _selectFirstWeekday( first_weekday: FirstWeekday, saveToBackend: boolean diff --git a/src/translations/en.json b/src/translations/en.json old mode 100755 new mode 100644 index a9f9608190..2f71088f55 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -131,6 +131,7 @@ }, "humidifier": { "humidity": "Target humidity", + "state": "State", "mode": "Mode", "target_humidity_entity": "{name} target humidity", "on_entity": "{name} on" @@ -147,7 +148,8 @@ "lock": { "code": "[%key:ui::card::alarm_control_panel::code%]", "lock": "Lock", - "unlock": "Unlock" + "unlock": "Unlock", + "open": "Open" }, "media_player": { "source": "Source", @@ -396,7 +398,8 @@ }, "theme-picker": { "theme": "Theme", - "no_theme": "No theme" + "no_theme": "No theme", + "default": "[%key:ui::panel::profile::themes::default%]" }, "language-picker": { "language": "Language", @@ -929,8 +932,8 @@ "light": { "edit_mode": "Edit favorite colors", "toggle": "Toggle", - "change_color": "Change color", - "change_color_temp": "Change color temperature", + "color": "Color", + "color_temp": "Temperature", "set_white": "Set white", "select_effect": "Select effect", "brightness": "Brightness", @@ -978,6 +981,11 @@ "disarm_action": "Disarm", "arm_title": "Arm", "arm_action": "Arm" + }, + "lock": { + "open": "Open", + "lock": "Lock", + "unlock": "Unlock" } }, "entity_registry": { @@ -1096,6 +1104,20 @@ "no_aliases": "Configure aliases and expose settings for voice assistants" } }, + "device-registry-detail": { + "name": "[%key:ui::panel::config::devices::name%]", + "enabled_label": "[%key:ui::panel::config::devices::enabled_label%]", + "enabled_cause": "[%key:ui::panel::config::devices::enabled_cause%]", + "type": { + "device_heading": "[%key:ui::panel::config::devices::type::device_heading%]", + "device": "[%key:ui::panel::config::devices::type::device%]", + "service_heading": "[%key:ui::panel::config::devices::type::service_heading%]", + "service": "[%key:ui::panel::config::devices::type::service%]" + }, + "enabled_description": "[%key:ui::panel::config::devices::enabled_description%]", + "update": "[%key:ui::panel::config::devices::update%]", + "unknown_error": "[%key:ui::panel::config::devices::unknown_error%]" + }, "voice-settings": { "expose_header": "Expose", "aliases_header": "Aliases", @@ -1687,7 +1709,7 @@ "energy_stat": "Energy returned to the grid", "cost_para": "Do you get money back when you return energy to the grid?", "no_cost": "I do not get money back", - "cost_stat": "Use an entity tracking the total recieved money", + "cost_stat": "Use an entity tracking the total received money", "cost_stat_input": "Entity with the total compensation", "cost_entity": "Use an entity with current rate", "cost_entity_input": "Entity with the current rate", @@ -2185,6 +2207,7 @@ "disable": "[%key:ui::common::disable%]", "disabled": "Automation is disabled", "read_only": "This automation cannot be edited from the UI, because it is not stored in the automations.yaml file, or doesn't have an ID.", + "unavailable": "Automation is unavailable", "migrate": "Migrate", "duplicate": "[%key:ui::common::duplicate%]", "run": "[%key:ui::panel::config::automation::editor::actions::run%]", @@ -2286,7 +2309,10 @@ "event_data": "Event data", "context_users": "Limit to events triggered by", "context_user_picked": "User firing event", - "context_user_pick": "Select user" + "context_user_pick": "Select user", + "description": { + "full": "When {eventTypes} event is fired" + } }, "geo_location": { "label": "Geolocation", @@ -2308,12 +2334,19 @@ "label": "Home Assistant", "event": "Event:", "start": "Start", - "shutdown": "Shutdown" + "shutdown": "Shutdown", + "description": { + "started": "When Home Assistant is started", + "shutdown": "When Home Assistant is shutdown" + } }, "mqtt": { "label": "MQTT", "topic": "Topic", - "payload": "Payload (optional)" + "payload": "Payload (optional)", + "description": { + "full": "When an MQTT message has been received" + } }, "numeric_state": { "label": "Numeric state", @@ -2325,27 +2358,61 @@ "type_value": "Fixed number", "type_input": "Numeric value of another entity" }, + "persistent_notification": { + "label": "Persistent notification", + "notification_id": "Notification Id", + "update_type": "Update type", + "update_types": { + "added": "added", + "removed": "removed", + "current": "current", + "updated": "updated" + } + }, "sun": { "label": "Sun", "event": "[%key:ui::panel::config::automation::editor::triggers::type::homeassistant::event%]", "sunrise": "Sunrise", "sunset": "Sunset", - "offset": "Offset (optional)" + "offset": "Offset (optional)", + "description": { + "sets": "When the sun sets{hasDuration, select, \n true { offset by {duration}} \n other {}\n }", + "rises": "When the sun rises{hasDuration, select, \n true { offset by {duration}} \n other {}\n }" + } + }, + "conversation": { + "label": "Sentence", + "add_sentence": "Add sentence", + "delete": "Delete sentence", + "confirm_delete": "Are you sure you want to delete this sentence?", + "description": { + "empty": "When a sentence is said", + "full": "When the sentence {sentence} is said" + } }, "tag": { - "label": "Tag" + "label": "Tag", + "description": { + "full": "When a tag is scanned" + } }, "template": { "label": "Template", "value_template": "Value template", - "for": "For" + "for": "For", + "description": { + "full": "When a template triggers{hasDuration, select, \n true { for {duration}} \n other {}\n }" + } }, "time": { "type_value": "Fixed time", "type_input": "Value of a date/time helper or timestamp-class sensor", "label": "Time", "at": "At time", - "mode": "Mode" + "mode": "Mode", + "description": { + "full": "When the time is equal to {time}" + } }, "time_pattern": { "label": "Time Pattern", @@ -2359,7 +2426,10 @@ "local_only": "Only accessible from the local network", "webhook_id": "Webhook ID", "webhook_id_helper": "Treat this ID like a password: keep it secret, and make it hard to guess.", - "webhook_settings": "Webhook Settings" + "webhook_settings": "Webhook Settings", + "description": { + "full": "When a Webhook payload has been received" + } }, "zone": { "label": "Zone", @@ -2496,9 +2566,13 @@ "delete_confirm_text": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm_text%]", "unsupported_action": "No visual editor support for action: {action}", "type_select": "Action type", + "continue_on_error": "Continue on error", "type": { "service": { - "label": "Call service" + "label": "Call service", + "response_variable": "Response variable", + "has_optional_response": "This service can return a response, if you want to use the response, enter the name of a variable the response will be saved in", + "has_response": "This service returns a response, enter the name of a variable the response will be saved in" }, "play_media": { "label": "Play media" @@ -2583,6 +2657,7 @@ "stop": { "label": "Stop", "stop": "Reason for stopping", + "response_variable": "The name of the variable to use as response", "error": "Stop because of an unexpected error" }, "parallel": { @@ -2692,6 +2767,21 @@ "delete": "[%key:ui::common::delete%]", "duplicate": "[%key:ui::common::duplicate%]" }, + "dialog_new": { + "header": "Create script", + "create_empty": "Create new script", + "create_empty_description": "Start with an empty script from scratch", + "create_blueprint": "[%key:ui::panel::config::automation::dialog_new::create_blueprint%]", + "create_blueprint_description": "[%key:ui::panel::config::automation::dialog_new::create_blueprint_description%]", + "blueprint_source": { + "author": "[%key:ui::panel::config::automation::dialog_new::blueprint_source::author%]", + "local": "[%key:ui::panel::config::automation::dialog_new::blueprint_source::local%]", + "community": "[%key:ui::panel::config::automation::dialog_new::blueprint_source::community%]", + "homeassistant": "[%key:ui::panel::config::automation::dialog_new::blueprint_source::homeassistant%]" + }, + "discover_blueprint_tip": "[%key:ui::panel::config::automation::dialog_new::discover_blueprint_tip%]" + }, + "editor": { "alias": "Name", "icon": "Icon", @@ -2702,6 +2792,7 @@ "show_trace": "[%key:ui::panel::config::automation::editor::show_trace%]", "show_info": "[%key:ui::panel::config::automation::editor::show_info%]", "read_only": "This script cannot be edited from the UI, because it is not stored in the ''scripts.yaml'' file.", + "unavailable": "Script is unavailable", "migrate": "Migrate", "duplicate": "[%key:ui::common::duplicate%]", "header": "Script: {name}", @@ -3488,11 +3579,11 @@ }, "thread": { "other_networks": "Other networks", - "my_network": "My network", + "my_network": "Preferred network", "no_preferred_network": "You don't have a preferred network yet.", "add_open_thread_border_router": "Add an OpenThread border router", "reset_border_router": "Reset border router", - "add_to_my_network": "Add to my network", + "add_to_my_network": "Add to preferred network", "no_routers_otbr_network": "No border routers where found, maybe the border router is not configured correctly. You can try to reset it to the factory settings.", "add_dataset_from_tlv": "Add dataset from TLV", "add_dataset": "Add Thread dataset", @@ -4055,6 +4146,7 @@ "add_title": "Add network storage", "update_title": "Update network storage", "no_mounts": "No connected network storage", + "documentation": "Documentation", "not_supported": { "title": "The operating system does not support network storage", "supervised": "Network storage is not supported on this host", @@ -4070,6 +4162,10 @@ "nfs": "Network File Share (NFS)", "cifs": "Samba/Windows (CIFS)" }, + "cifs_versions": { + "auto": "Auto (2.1+)", + "legacy": "Legacy ({version})" + }, "options": { "name": { "title": "Name", @@ -4099,6 +4195,10 @@ "title": "Usage", "description": "This determines how the share is intended to be used" }, + "version": { + "title": "Samba/Windows (CIFS) version", + "description": "This choses the version of the protocol to use" + }, "username": { "title": "Username", "description": "This is your username for the samba share" @@ -4113,6 +4213,9 @@ "errors": { "reload": "Could not reload mount {mount}", "invalid_name": "Invalid name, can only contain alphanumeric characters and underscores" + }, + "warnings": { + "not_recomeded_cifs_version": "The selected CIFS version is not recommended to use, you should only use this if you have problems with auto and your server does not support version 2.1 or newer" } } }, @@ -4218,6 +4321,11 @@ "not_produced_solar_energy": "You have not produced any solar energy", "self_consumed_solar_could_not_calc": "Self-consumed solar energy couldn't be calculated" }, + "self_sufficiency_gauge": { + "card_indicates_self_sufficiency_quota": "This card indicates how self-sufficient your home is.", + "self_sufficiency_quota": "Self-sufficiency quota", + "self_sufficiency_could_not_calc": "Self-sufficiency quota couldn't be calculated" + }, "grid_neutrality_gauge": { "energy_dependency": "This card indicates your net energy usage.", "color_explain": "If the needle is in the purple, you returned more energy to the grid than you consumed from it. If it's in the blue, you consumed more energy from the grid than you returned.", @@ -4363,18 +4471,25 @@ "edit": "Edit", "clear": "Clear", "delete": "Delete card", + "copy": "Copy card", + "cut": "Cut card", "duplicate": "Duplicate card", "move": "Move to view", "move_up": "Move card up", "move_down": "Move card down", "move_before": "Move card before", "move_after": "Move card after", + "change_position": "Change card position", "options": "More options", "search_cards": "Search cards" }, "move_card": { "header": "Choose a view to move the card to" }, + "change_position": { + "title": "Change card position", + "text": "What position do you want to move your card to?" + }, "select_view": { "header": "Choose a view", "dashboard_label": "Dashboard", @@ -4405,12 +4520,15 @@ "action-editor": { "navigation_path": "Navigation Path", "url_path": "URL Path", + "start_listening": "Start listening", + "pipeline_id": "Assistant", "actions": { "default_action": "Default Action", "call-service": "Call Service", "more-info": "More Info", "toggle": "Toggle", "navigate": "Navigate", + "assist": "Assist", "url": "URL", "none": "No Action" } @@ -4598,6 +4716,7 @@ "aspect_ratio": "Aspect Ratio", "attribute": "Attribute", "camera_image": "Camera Entity", + "image_entity": "Image Entity", "camera_view": "Camera View", "double_tap_action": "Double Tap Action", "entities": "Entities", @@ -4613,6 +4732,8 @@ "manual_description": "Need to add a custom card or just want to manually write the YAML?", "minimum": "Minimum", "name": "Name", + "paste": "Paste from Clipboard", + "paste_description": "Paste a {type} card from the clipboard", "refresh_interval": "Refresh Interval", "show_icon": "Show Icon?", "show_name": "Show Name?", @@ -4928,6 +5049,15 @@ "24": "24 hours" } }, + "time_zone": { + "header": "Time Zone", + "dropdown_label": "Time zone", + "description": "Choose the time zone to use for displaying times.", + "options": { + "local": "Use your local time zone ({timezone})", + "server": "Use server time zone ({timezone})" + } + }, "date_format": { "header": "Date Format", "dropdown_label": "Date format", @@ -5204,6 +5334,7 @@ "title": "Services", "description": "The service dev tool allows you to call any available service in Home Assistant.", "call_service": "Call Service", + "response": "Response", "column_parameter": "Parameter", "column_description": "Description", "column_example": "Example", @@ -5240,6 +5371,8 @@ "description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.", "editor": "Template editor", "reset": "Reset to demo template", + "confirm_reset": "Do you want to reset your current template back to the demo template?", + "confirm_clear": "Do you want to clear your current template?", "result_type": "Result type", "jinja_documentation": "Jinja2 template documentation", "template_extensions": "Home Assistant template extensions", @@ -5713,10 +5846,20 @@ "error": "[%key:ui::panel::my::error%]", "error_addon_not_found": "Add-on not found", "error_repository_not_found": "The required repository for this Add-on was not found", - "error_addon_not_started": "The requested add-on is not running. Please start it first", "error_addon_not_installed": "The requested add-on is not installed. Please install it first", "error_addon_no_ingress": "The requested add-on does not support ingress" }, + "ingress": { + "error_addon_info": "Unable to fetch add-on info to start Ingress", + "error_addon_not_installed": "The add-on is not installed. Please install it first", + "error_addon_not_supported": "This add-on does not support Ingress", + "error_addon_not_running": "Add-on is not running. Do you want to start it now?", + "start_addon": "Start add-on", + "error_starting_addon": "Error starting the add-on", + "error_creating_session": "Unable to create an Ingress session", + "error_addon_not_ready": "The add-on seems to not be ready, it might still be starting. Do you want to try again?", + "retry": "Retry" + }, "system": { "log": { "log_provider": "Log Provider", @@ -5815,6 +5958,8 @@ "download_backup": "Download backup", "create_backup": "Create backup", "create": "Create", + "location": "Location", + "data_disk": "Data disk", "created": "Created", "name": "Backup name", "type": "Backup type", diff --git a/src/util/common-translation.ts b/src/util/common-translation.ts index be14097dd8..041d1fa537 100644 --- a/src/util/common-translation.ts +++ b/src/util/common-translation.ts @@ -77,6 +77,7 @@ export async function getUserLocale( const number_format = result?.number_format; const time_format = result?.time_format; const date_format = result?.date_format; + const time_zone = result?.time_zone; const first_weekday = result?.first_weekday; if (language) { const availableLanguage = findAvailableLanguage(language); @@ -85,7 +86,8 @@ export async function getUserLocale( language: availableLanguage, number_format, time_format, - date_format: date_format, + date_format, + time_zone, first_weekday, }; } @@ -93,7 +95,8 @@ export async function getUserLocale( return { number_format, time_format, - date_format: date_format, + date_format, + time_zone, first_weekday, }; } diff --git a/test/common/datetime/format_date.ts b/test/common/datetime/format_date.ts index 9d6841ea7d..d399b58e25 100644 --- a/test/common/datetime/format_date.ts +++ b/test/common/datetime/format_date.ts @@ -6,20 +6,27 @@ import { TimeFormat, FirstWeekday, DateFormat, + TimeZone, } from "../../../src/data/translation"; +import { demoConfig } from "../../../src/fake_data/demo_config"; describe("formatDate", () => { const dateObj = new Date(2017, 10, 18, 11, 12, 13, 1400); it("Formats English dates", () => { assert.strictEqual( - formatDate(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.language, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatDate( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "November 18, 2017" ); }); diff --git a/test/common/datetime/format_date_time.ts b/test/common/datetime/format_date_time.ts index 5632d60615..2f7988e5d3 100644 --- a/test/common/datetime/format_date_time.ts +++ b/test/common/datetime/format_date_time.ts @@ -9,30 +9,42 @@ import { TimeFormat, FirstWeekday, DateFormat, + TimeZone, } from "../../../src/data/translation"; +import { demoConfig } from "../../../src/fake_data/demo_config"; describe("formatDateTime", () => { const dateObj = new Date(2017, 10, 18, 23, 12, 13, 400); it("Formats English date times", () => { assert.strictEqual( - formatDateTime(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.am_pm, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatDateTime( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.am_pm, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "November 18, 2017 at 11:12 PM" ); assert.strictEqual( - formatDateTime(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.twenty_four, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatDateTime( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.twenty_four, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "November 18, 2017 at 23:12" ); }); @@ -43,23 +55,34 @@ describe("formatDateTimeWithSeconds", () => { it("Formats English date times with seconds", () => { assert.strictEqual( - formatDateTimeWithSeconds(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.am_pm, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatDateTimeWithSeconds( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.am_pm, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "November 18, 2017 at 11:12:13 PM" ); assert.strictEqual( - formatDateTimeWithSeconds(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.twenty_four, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatDateTimeWithSeconds( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.twenty_four, + date_format: DateFormat.language, + time_zone: TimeZone.local, + + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "November 18, 2017 at 23:12:13" ); }); diff --git a/test/common/datetime/format_time.ts b/test/common/datetime/format_time.ts index 3c7e7899c7..3dbbcfd0a8 100644 --- a/test/common/datetime/format_time.ts +++ b/test/common/datetime/format_time.ts @@ -10,30 +10,42 @@ import { TimeFormat, FirstWeekday, DateFormat, + TimeZone, } from "../../../src/data/translation"; +import { demoConfig } from "../../../src/fake_data/demo_config"; describe("formatTime", () => { const dateObj = new Date(2017, 10, 18, 23, 12, 13, 1400); it("Formats English times", () => { assert.strictEqual( - formatTime(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.am_pm, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatTime( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.am_pm, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "11:12 PM" ); assert.strictEqual( - formatTime(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.twenty_four, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatTime( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.twenty_four, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "23:12" ); }); @@ -44,23 +56,33 @@ describe("formatTimeWithSeconds", () => { it("Formats English times with seconds", () => { assert.strictEqual( - formatTimeWithSeconds(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.am_pm, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatTimeWithSeconds( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.am_pm, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "11:12:13 PM" ); assert.strictEqual( - formatTimeWithSeconds(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.twenty_four, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatTimeWithSeconds( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.twenty_four, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "23:12:13" ); }); @@ -71,23 +93,33 @@ describe("formatTimeWeekday", () => { it("Formats English times", () => { assert.strictEqual( - formatTimeWeekday(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.am_pm, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatTimeWeekday( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.am_pm, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "Saturday 11:12 PM" ); assert.strictEqual( - formatTimeWeekday(dateObj, { - language: "en", - number_format: NumberFormat.language, - time_format: TimeFormat.twenty_four, - date_format: DateFormat.language, - first_weekday: FirstWeekday.language, - }), + formatTimeWeekday( + dateObj, + { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.twenty_four, + date_format: DateFormat.language, + time_zone: TimeZone.local, + first_weekday: FirstWeekday.language, + }, + demoConfig + ), "Saturday 23:12" ); }); diff --git a/test/common/datetime/relative_time.ts b/test/common/datetime/relative_time.ts index ebdf39185b..a99ed08765 100644 --- a/test/common/datetime/relative_time.ts +++ b/test/common/datetime/relative_time.ts @@ -6,6 +6,7 @@ import { TimeFormat, FirstWeekday, DateFormat, + TimeZone, } from "../../../src/data/translation"; describe("relativeTime", () => { @@ -14,6 +15,7 @@ describe("relativeTime", () => { number_format: NumberFormat.language, time_format: TimeFormat.language, date_format: DateFormat.language, + time_zone: TimeZone.local, first_weekday: FirstWeekday.language, }; @@ -22,6 +24,7 @@ describe("relativeTime", () => { number_format: NumberFormat.language, time_format: TimeFormat.language, date_format: DateFormat.language, + time_zone: TimeZone.local, first_weekday: FirstWeekday.monday, }; diff --git a/test/common/entity/compute_state_display.ts b/test/common/entity/compute_state_display.ts index f09df55c5d..2cfc130a56 100644 --- a/test/common/entity/compute_state_display.ts +++ b/test/common/entity/compute_state_display.ts @@ -7,7 +7,9 @@ import { TimeFormat, FirstWeekday, DateFormat, + TimeZone, } from "../../../src/data/translation"; +import { demoConfig } from "../../../src/fake_data/demo_config"; let localeData: FrontendLocaleData; @@ -22,6 +24,7 @@ describe("computeStateDisplay", () => { number_format: NumberFormat.comma_decimal, time_format: TimeFormat.am_pm, date_format: DateFormat.language, + time_zone: TimeZone.local, first_weekday: FirstWeekday.language, }; }); @@ -33,7 +36,7 @@ describe("computeStateDisplay", () => { attributes: {}, }; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "component.binary_sensor.entity_component._.state.off" ); }); @@ -47,7 +50,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "component.binary_sensor.state.moisture.off" ); }); @@ -67,7 +70,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(altLocalize, stateObj, localeData, {}), + computeStateDisplay(altLocalize, stateObj, localeData, demoConfig, {}), "component.binary_sensor.state.invalid_device_class.off" ); }); @@ -81,7 +84,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "123 m" ); }); @@ -95,7 +98,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "1,234.5 m" ); }); @@ -109,7 +112,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "1,234.5" ); }); @@ -129,7 +132,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(altLocalize, stateObj, localeData, {}), + computeStateDisplay(altLocalize, stateObj, localeData, demoConfig, {}), "state.default.unknown" ); }); @@ -149,7 +152,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(altLocalize, stateObj, localeData, {}), + computeStateDisplay(altLocalize, stateObj, localeData, demoConfig, {}), "state.default.unavailable" ); }); @@ -169,7 +172,7 @@ describe("computeStateDisplay", () => { attributes: {}, }; assert.strictEqual( - computeStateDisplay(altLocalize, stateObj, localeData, {}), + computeStateDisplay(altLocalize, stateObj, localeData, demoConfig, {}), "component.sensor.entity_component._.state.custom_state" ); }); @@ -191,14 +194,14 @@ describe("computeStateDisplay", () => { }; it("Uses am/pm time format", () => { assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "November 18, 2017 at 11:12 PM" ); }); it("Uses 24h time format", () => { localeData.time_format = TimeFormat.twenty_four; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "November 18, 2017 at 23:12" ); }); @@ -220,7 +223,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "November 18, 2017" ); }); @@ -243,14 +246,14 @@ describe("computeStateDisplay", () => { it("Uses am/pm time format", () => { localeData.time_format = TimeFormat.am_pm; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "11:12 PM" ); }); it("Uses 24h time format", () => { localeData.time_format = TimeFormat.twenty_four; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}), + computeStateDisplay(localize, stateObj, localeData, demoConfig, {}), "23:12" ); }); @@ -277,6 +280,7 @@ describe("computeStateDisplay", () => { localize, stateObj, localeData, + demoConfig, {}, "2021-07-04 15:40:03" ), @@ -290,6 +294,7 @@ describe("computeStateDisplay", () => { localize, stateObj, localeData, + demoConfig, {}, "2021-07-04 15:40:03" ), @@ -314,7 +319,14 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}, "2021-07-04"), + computeStateDisplay( + localize, + stateObj, + localeData, + demoConfig, + {}, + "2021-07-04" + ), "July 4, 2021" ); }); @@ -337,14 +349,28 @@ describe("computeStateDisplay", () => { it("Uses am/pm time format", () => { localeData.time_format = TimeFormat.am_pm; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}, "17:05:07"), + computeStateDisplay( + localize, + stateObj, + localeData, + demoConfig, + {}, + "17:05:07" + ), "5:05 PM" ); }); it("Uses 24h time format", () => { localeData.time_format = TimeFormat.twenty_four; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, {}, "17:05:07"), + computeStateDisplay( + localize, + stateObj, + localeData, + demoConfig, + {}, + "17:05:07" + ), "17:05" ); }); @@ -363,7 +389,7 @@ describe("computeStateDisplay", () => { attributes: {}, }; assert.strictEqual( - computeStateDisplay(altLocalize, stateObj, localeData, {}), + computeStateDisplay(altLocalize, stateObj, localeData, demoConfig, {}), "state.default.unavailable" ); }); @@ -378,7 +404,7 @@ describe("computeStateDisplay", () => { attributes: {}, }; assert.strictEqual( - computeStateDisplay(altLocalize, stateObj, localeData, {}), + computeStateDisplay(altLocalize, stateObj, localeData, demoConfig, {}), "My Custom State" ); }); @@ -396,7 +422,7 @@ describe("computeStateDisplay", () => { }, }; assert.strictEqual( - computeStateDisplay(localize, stateObj, localeData, entities), + computeStateDisplay(localize, stateObj, localeData, demoConfig, entities), "component.custom_integration.entity.sensor.custom_translation.state.custom_state" ); }); diff --git a/test/common/string/format_number.ts b/test/common/string/format_number.ts index 986ab9a01b..2004b06234 100644 --- a/test/common/string/format_number.ts +++ b/test/common/string/format_number.ts @@ -12,6 +12,7 @@ import { TimeFormat, FirstWeekday, DateFormat, + TimeZone, } from "../../../src/data/translation"; describe("formatNumber", () => { @@ -21,6 +22,7 @@ describe("formatNumber", () => { number_format: NumberFormat.language, time_format: TimeFormat.language, date_format: DateFormat.language, + time_zone: TimeZone.local, first_weekday: FirstWeekday.language, }; diff --git a/test/webpack.config.js b/test/webpack.config.js index 20e1e163e3..ad593b58aa 100644 --- a/test/webpack.config.js +++ b/test/webpack.config.js @@ -1,8 +1,13 @@ import webpack from "../build-scripts/webpack.cjs"; -export default webpack.createAppConfig({ +const config = webpack.createAppConfig({ isProdBuild: false, latestBuild: true, isStatsBuild: false, isTestBuild: true, }); + +// instant-mocha forces a CJS library, so cannot output ESM +config.output.module = false; + +export default config; diff --git a/tsconfig.json b/tsconfig.json index 13af372a4c..a5bac525d4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,8 @@ { "compilerOptions": { // Language Options - "target": "ES2017", - "lib": [ - "ES2017", - "DOM", - "DOM.Iterable", - "WebWorker" - ], + "target": "ES2021", + "lib": ["ES2021", "DOM", "DOM.Iterable", "WebWorker"], "experimentalDecorators": true, // Modules "module": "ESNext", @@ -43,4 +38,4 @@ } ] } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index b7e8cd082f..f82ee35bb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,119 +38,119 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/code-frame@npm:7.21.4" +"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/code-frame@npm:7.22.5" dependencies: - "@babel/highlight": ^7.18.6 - checksum: e5390e6ec1ac58dcef01d4f18eaf1fd2f1325528661ff6d4a5de8979588b9f5a8e852a54a91b923846f7a5c681b217f0a45c2524eb9560553160cd963b7d592c + "@babel/highlight": ^7.22.5 + checksum: cfe804f518f53faaf9a1d3e0f9f74127ab9a004912c3a16fda07fb6a633393ecb9918a053cb71804204c1b7ec3d49e1699604715e2cfb0c9f7bc4933d324ebb6 languageName: node linkType: hard -"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.22.0, @babel/compat-data@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/compat-data@npm:7.22.3" - checksum: eb001646f41459f42ccb0d39ee8bb3c3c495bc297234817044c0002689c625e3159a6678c53fd31bd98cf21f31472b73506f350fc6906e3bdfa49cb706e2af8d +"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/compat-data@npm:7.22.5" + checksum: eb1a47ebf79ae268b4a16903e977be52629339806e248455eb9973897c503a04b701f36a9de64e19750d6e081d0561e77a514c8dc470babbeba59ae94298ed18 languageName: node linkType: hard -"@babel/core@npm:7.22.1, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.3": - version: 7.22.1 - resolution: "@babel/core@npm:7.22.1" +"@babel/core@npm:7.22.5, @babel/core@npm:^7.11.1, @babel/core@npm:^7.12.3": + version: 7.22.5 + resolution: "@babel/core@npm:7.22.5" dependencies: "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.22.0 - "@babel/helper-compilation-targets": ^7.22.1 - "@babel/helper-module-transforms": ^7.22.1 - "@babel/helpers": ^7.22.0 - "@babel/parser": ^7.22.0 - "@babel/template": ^7.21.9 - "@babel/traverse": ^7.22.1 - "@babel/types": ^7.22.0 + "@babel/code-frame": ^7.22.5 + "@babel/generator": ^7.22.5 + "@babel/helper-compilation-targets": ^7.22.5 + "@babel/helper-module-transforms": ^7.22.5 + "@babel/helpers": ^7.22.5 + "@babel/parser": ^7.22.5 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.5 + "@babel/types": ^7.22.5 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.2 semver: ^6.3.0 - checksum: bbe45e791f223a7e692d2ea6597a73f48050abd24b119c85c48ac6504c30ce63343a2ea3f79b5847bf4b409ddd8a68b6cdc4f0272ded1d2ef6f6b1e9663432f0 + checksum: 173ae426958c90c7bbd7de622c6f13fcab8aef0fac3f138e2d47bc466d1cd1f86f71ca82ae0acb9032fd8794abed8efb56fea55c031396337eaec0d673b69d56 languageName: node linkType: hard -"@babel/generator@npm:^7.22.0": - version: 7.22.3 - resolution: "@babel/generator@npm:7.22.3" +"@babel/generator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/generator@npm:7.22.5" dependencies: - "@babel/types": ^7.22.3 + "@babel/types": ^7.22.5 "@jridgewell/gen-mapping": ^0.3.2 "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: ccb6426ca5b5a38f0d47a3ac9628e223d2aaaa489cbf90ffab41468795c22afe86855f68a58667f0f2673949f1810d4d5a57b826c17984eab3e28fdb34a909e6 + checksum: efa64da70ca88fe69f05520cf5feed6eba6d30a85d32237671488cc355fdc379fe2c3246382a861d49574c4c2f82a317584f8811e95eb024e365faff3232b49d languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" +"@babel/helper-annotate-as-pure@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" dependencies: - "@babel/types": ^7.18.6 - checksum: 88ccd15ced475ef2243fdd3b2916a29ea54c5db3cd0cfabf9d1d29ff6e63b7f7cd1c27264137d7a40ac2e978b9b9a542c332e78f40eb72abe737a7400788fc1b + "@babel/types": ^7.22.5 + checksum: 53da330f1835c46f26b7bf4da31f7a496dee9fd8696cca12366b94ba19d97421ce519a74a837f687749318f94d1a37f8d1abcbf35e8ed22c32d16373b2f6198d languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.18.6": - version: 7.21.5 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.21.5" +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.5" dependencies: - "@babel/types": ^7.21.5 - checksum: 9a033d3d7a6409256272ea6fc03731511af9f936ee0b161ace05d171d7bd5adf455dc85f80437d92277462f6bd2af9af1f2d1967edc21ca4d5966ac0a09cf61d + "@babel/types": ^7.22.5 + checksum: d753acac62399fc6dd354cf1b9441bde0c331c2fe792a4c14904c5e5eafc3cac79478f6aa038e8a51c1148b0af6710a2e619855e4b5d54497ac972eaffed5884 languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.18.9, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.1": - version: 7.22.1 - resolution: "@babel/helper-compilation-targets@npm:7.22.1" +"@babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-compilation-targets@npm:7.22.5" dependencies: - "@babel/compat-data": ^7.22.0 - "@babel/helper-validator-option": ^7.21.0 + "@babel/compat-data": ^7.22.5 + "@babel/helper-validator-option": ^7.22.5 browserslist: ^4.21.3 lru-cache: ^5.1.1 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: a686a01bd3288cf95ca26faa27958d34c04e2501c4b0858c3a6558776dec20317b5635f33d64c5a635b6fbdfe462a85c30d4bfa0ae7e7ffe3467e4d06442d7c8 + checksum: a479460615acffa0f4fd0a29b740eafb53a93694265207d23a6038ccd18d183a382cacca515e77b7c9b042c3ba80b0aca0da5f1f62215140e81660d2cf721b68 languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.21.0, @babel/helper-create-class-features-plugin@npm:^7.22.1": - version: 7.22.1 - resolution: "@babel/helper-create-class-features-plugin@npm:7.22.1" +"@babel/helper-create-class-features-plugin@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-create-class-features-plugin@npm:7.22.5" dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-environment-visitor": ^7.22.1 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-member-expression-to-functions": ^7.22.0 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/helper-replace-supers": ^7.22.1 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-member-expression-to-functions": ^7.22.5 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.5 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: a132d940c345effc55f4d018db4d113be56528cc5f9bdc12d14da311d27febdde9c606c62e81d17c7ab06b44fb7995d6116ed2aceee75ffa6c5e4e2da3c106ba + checksum: f1e91deae06dbee6dd956c0346bca600adfbc7955427795d9d8825f0439a3c3290c789ba2b4a02a1cdf6c1a1bd163dfa16d3d5e96b02a8efb639d2a774e88ed9 languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.1": - version: 7.22.1 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.1" +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.5" dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-annotate-as-pure": ^7.22.5 regexpu-core: ^5.3.1 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 52d875762110d5dac41ce21fa30a2aaa47c119ca58add190a5123b7a843da096854c0b6358c327b8e0dc2f2219a47eace69332d8a26f165f529ec402a4e6f974 + checksum: 94932145beeb1f91856be25fea8de30b4b81b63fbc7c5a207ed97a5ddc34cd1e9b04041ed28bd24ec09cdcfbb62e8d66f820e4fe864672afe0aa2f357c784e11 languageName: node linkType: hard @@ -170,251 +170,246 @@ __metadata: languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.18.9, @babel/helper-environment-visitor@npm:^7.22.1": - version: 7.22.1 - resolution: "@babel/helper-environment-visitor@npm:7.22.1" - checksum: a6b4bb5505453bff95518d361ac1de393f0029aeb8b690c70540f4317934c53c43cc4afcda8c752ffa8c272e63ed6b929a56eca28e4978424177b24238b21bf9 +"@babel/helper-environment-visitor@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-environment-visitor@npm:7.22.5" + checksum: 248532077d732a34cd0844eb7b078ff917c3a8ec81a7f133593f71a860a582f05b60f818dc5049c2212e5baa12289c27889a4b81d56ef409b4863db49646c4b1 languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.18.9, @babel/helper-function-name@npm:^7.19.0, @babel/helper-function-name@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-function-name@npm:7.21.0" +"@babel/helper-function-name@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-function-name@npm:7.22.5" dependencies: - "@babel/template": ^7.20.7 - "@babel/types": ^7.21.0 - checksum: d63e63c3e0e3e8b3138fa47b0cd321148a300ef12b8ee951196994dcd2a492cc708aeda94c2c53759a5c9177fffaac0fd8778791286746f72a000976968daf4e + "@babel/template": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: 6b1f6ce1b1f4e513bf2c8385a557ea0dd7fa37971b9002ad19268ca4384bbe90c09681fe4c076013f33deabc63a53b341ed91e792de741b4b35e01c00238177a languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-hoist-variables@npm:7.18.6" +"@babel/helper-hoist-variables@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-hoist-variables@npm:7.22.5" dependencies: - "@babel/types": ^7.18.6 - checksum: fd9c35bb435fda802bf9ff7b6f2df06308a21277c6dec2120a35b09f9de68f68a33972e2c15505c1a1a04b36ec64c9ace97d4a9e26d6097b76b4396b7c5fa20f + "@babel/types": ^7.22.5 + checksum: 394ca191b4ac908a76e7c50ab52102669efe3a1c277033e49467913c7ed6f7c64d7eacbeabf3bed39ea1f41731e22993f763b1edce0f74ff8563fd1f380d92cc languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.22.0": - version: 7.22.3 - resolution: "@babel/helper-member-expression-to-functions@npm:7.22.3" +"@babel/helper-member-expression-to-functions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-member-expression-to-functions@npm:7.22.5" dependencies: - "@babel/types": ^7.22.3 - checksum: c31b7c8096e722ab7717a1e2343b26afa469172aeb1a8643e9387a14bb50d77dd032fafedf282fcde37b90dcadd2e770c0dfea745a3b1de893d607f2ccba7c0f + "@babel/types": ^7.22.5 + checksum: 4bd5791529c280c00743e8bdc669ef0d4cd1620d6e3d35e0d42b862f8262bc2364973e5968007f960780344c539a4b9cf92ab41f5b4f94560a9620f536de2a39 languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.18.6, @babel/helper-module-imports@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/helper-module-imports@npm:7.21.4" +"@babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.18.6, @babel/helper-module-imports@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-module-imports@npm:7.22.5" dependencies: - "@babel/types": ^7.21.4 - checksum: bd330a2edaafeb281fbcd9357652f8d2666502567c0aad71db926e8499c773c9ea9c10dfaae30122452940326d90c8caff5c649ed8e1bf15b23f858758d3abc6 + "@babel/types": ^7.22.5 + checksum: 9ac2b0404fa38b80bdf2653fbeaf8e8a43ccb41bd505f9741d820ed95d3c4e037c62a1bcdcb6c9527d7798d2e595924c4d025daed73283badc180ada2c9c49ad languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.5, @babel/helper-module-transforms@npm:^7.22.1": - version: 7.22.1 - resolution: "@babel/helper-module-transforms@npm:7.22.1" +"@babel/helper-module-transforms@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-module-transforms@npm:7.22.5" dependencies: - "@babel/helper-environment-visitor": ^7.22.1 - "@babel/helper-module-imports": ^7.21.4 - "@babel/helper-simple-access": ^7.21.5 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/helper-validator-identifier": ^7.19.1 - "@babel/template": ^7.21.9 - "@babel/traverse": ^7.22.1 - "@babel/types": ^7.22.0 - checksum: dfa084211a93c9f0174ab07385fdbf7831bbf5c1ff3d4f984effc489f48670825ad8b817b9e9d2ec6492fde37ed6518c15944e9dd7a60b43a3d9874c9250f5f8 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-module-imports": ^7.22.5 + "@babel/helper-simple-access": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.5 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: 8985dc0d971fd17c467e8b84fe0f50f3dd8610e33b6c86e5b3ca8e8859f9448bcc5c84e08a2a14285ef388351c0484797081c8f05a03770bf44fc27bf4900e68 languageName: node linkType: hard -"@babel/helper-optimise-call-expression@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" +"@babel/helper-optimise-call-expression@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" dependencies: - "@babel/types": ^7.18.6 - checksum: e518fe8418571405e21644cfb39cf694f30b6c47b10b006609a92469ae8b8775cbff56f0b19732343e2ea910641091c5a2dc73b56ceba04e116a33b0f8bd2fbd + "@babel/types": ^7.22.5 + checksum: c70ef6cc6b6ed32eeeec4482127e8be5451d0e5282d5495d5d569d39eb04d7f1d66ec99b327f45d1d5842a9ad8c22d48567e93fc502003a47de78d122e355f7c languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.21.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.21.5 - resolution: "@babel/helper-plugin-utils@npm:7.21.5" - checksum: 6f086e9a84a50ea7df0d5639c8f9f68505af510ea3258b3c8ac8b175efdfb7f664436cb48996f71791a1350ba68f47ad3424131e8e718c5e2ad45564484cbb36 +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.22.5 + resolution: "@babel/helper-plugin-utils@npm:7.22.5" + checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" +"@babel/helper-remap-async-to-generator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-remap-async-to-generator@npm:7.22.5" dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-wrap-function": ^7.18.9 - "@babel/types": ^7.18.9 + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-wrap-function": ^7.22.5 + "@babel/types": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0 - checksum: 4be6076192308671b046245899b703ba090dbe7ad03e0bea897bb2944ae5b88e5e85853c9d1f83f643474b54c578d8ac0800b80341a86e8538264a725fbbefec + checksum: 1e51dcff1c22e97ea3d22034b77788048eb6d8c6860325bd7a1046b7a7135730cefd93b5c96fd9839d76031095d5ffb6f0cd6ee90a5d69a4c7de980d7f4623d9 languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.18.6, @babel/helper-replace-supers@npm:^7.20.7, @babel/helper-replace-supers@npm:^7.22.1": - version: 7.22.1 - resolution: "@babel/helper-replace-supers@npm:7.22.1" +"@babel/helper-replace-supers@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-replace-supers@npm:7.22.5" dependencies: - "@babel/helper-environment-visitor": ^7.22.1 - "@babel/helper-member-expression-to-functions": ^7.22.0 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/template": ^7.21.9 - "@babel/traverse": ^7.22.1 - "@babel/types": ^7.22.0 - checksum: 4179090f7010cf9456d17ec354df10f4f647d9b57f6e0b021519d504afca977f67ca39ffe04b47369ea671a744309d0d58f436cb4c18aef000f1df3c0e1162ba + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-member-expression-to-functions": ^7.22.5 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: af29deff6c6dc3fa2d1a517390716aa3f4d329855e8689f1d5c3cb07c1b898e614a5e175f1826bb58e9ff1480e6552885a71a9a0ba5161787aaafa2c79b216cc languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-simple-access@npm:7.21.5" +"@babel/helper-simple-access@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-simple-access@npm:7.22.5" dependencies: - "@babel/types": ^7.21.5 - checksum: ad212beaa24be3864c8c95bee02f840222457ccf5419991e2d3e3e39b0f75b77e7e857e0bf4ed428b1cd97acefc87f3831bdb0b9696d5ad0557421f398334fc3 + "@babel/types": ^7.22.5 + checksum: fe9686714caf7d70aedb46c3cce090f8b915b206e09225f1e4dbc416786c2fdbbee40b38b23c268b7ccef749dd2db35f255338fb4f2444429874d900dede5ad2 languageName: node linkType: hard -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0": - version: 7.20.0 - resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.20.0" +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" dependencies: - "@babel/types": ^7.20.0 - checksum: 34da8c832d1c8a546e45d5c1d59755459ffe43629436707079989599b91e8c19e50e73af7a4bd09c95402d389266731b0d9c5f69e372d8ebd3a709c05c80d7dd + "@babel/types": ^7.22.5 + checksum: 1012ef2295eb12dc073f2b9edf3425661e9b8432a3387e62a8bc27c42963f1f216ab3124228015c748770b2257b4f1fda882ca8fa34c0bf485e929ae5bc45244 languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-split-export-declaration@npm:7.18.6" +"@babel/helper-split-export-declaration@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-split-export-declaration@npm:7.22.5" dependencies: - "@babel/types": ^7.18.6 - checksum: c6d3dede53878f6be1d869e03e9ffbbb36f4897c7cc1527dc96c56d127d834ffe4520a6f7e467f5b6f3c2843ea0e81a7819d66ae02f707f6ac057f3d57943a2b + "@babel/types": ^7.22.5 + checksum: d10e05a02f49c1f7c578cea63d2ac55356501bbf58856d97ac9bfde4957faee21ae97c7f566aa309e38a256eef58b58e5b670a7f568b362c00e93dfffe072650 languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-string-parser@npm:7.21.5" - checksum: 36c0ded452f3858e67634b81960d4bde1d1cd2a56b82f4ba2926e97864816021c885f111a7cf81de88a0ed025f49d84a393256700e9acbca2d99462d648705d8 +"@babel/helper-string-parser@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-string-parser@npm:7.22.5" + checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": - version: 7.19.1 - resolution: "@babel/helper-validator-identifier@npm:7.19.1" - checksum: 0eca5e86a729162af569b46c6c41a63e18b43dbe09fda1d2a3c8924f7d617116af39cac5e4cd5d431bb760b4dca3c0970e0c444789b1db42bcf1fa41fbad0a3a +"@babel/helper-validator-identifier@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-validator-identifier@npm:7.22.5" + checksum: 7f0f30113474a28298c12161763b49de5018732290ca4de13cdaefd4fd0d635a6fe3f6686c37a02905fb1e64f21a5ee2b55140cf7b070e729f1bd66866506aea languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-validator-option@npm:7.21.0" - checksum: 8ece4c78ffa5461fd8ab6b6e57cc51afad59df08192ed5d84b475af4a7193fc1cb794b59e3e7be64f3cdc4df7ac78bf3dbb20c129d7757ae078e6279ff8c2f07 +"@babel/helper-validator-option@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-validator-option@npm:7.22.5" + checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 languageName: node linkType: hard -"@babel/helper-wrap-function@npm:^7.18.9": - version: 7.20.5 - resolution: "@babel/helper-wrap-function@npm:7.20.5" +"@babel/helper-wrap-function@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-wrap-function@npm:7.22.5" dependencies: - "@babel/helper-function-name": ^7.19.0 - "@babel/template": ^7.18.10 - "@babel/traverse": ^7.20.5 - "@babel/types": ^7.20.5 - checksum: 11a6fc28334368a193a9cb3ad16f29cd7603bab958433efc82ebe59fa6556c227faa24f07ce43983f7a85df826f71d441638442c4315e90a554fe0a70ca5005b + "@babel/helper-function-name": ^7.22.5 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: a4ba2d7577ad3ce92fadaa341ffce3b0e4b389808099b07c80847f9be0852f4b42344612bc1b3d1b796ffb75be56d5957c5c56a1734f6aee5ccbb7cd9ab12691 languageName: node linkType: hard -"@babel/helpers@npm:^7.22.0": - version: 7.22.3 - resolution: "@babel/helpers@npm:7.22.3" +"@babel/helpers@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helpers@npm:7.22.5" dependencies: - "@babel/template": ^7.21.9 - "@babel/traverse": ^7.22.1 - "@babel/types": ^7.22.3 - checksum: 385289ee8b87cf9af448bbb9fcf747f6e67600db5f7f64eb4ad97761ee387819bf2212b6a757008286c6bfacf4f3fc0b6de88686f2e517a70fb59996bdfbd1e9 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: a96e785029dff72f171190943df895ab0f76e17bf3881efd630bc5fae91215042d1c2e9ed730e8e4adf4da6f28b24bd1f54ed93b90ffbca34c197351872a084e languageName: node linkType: hard -"@babel/highlight@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/highlight@npm:7.18.6" +"@babel/highlight@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/highlight@npm:7.22.5" dependencies: - "@babel/helper-validator-identifier": ^7.18.6 + "@babel/helper-validator-identifier": ^7.22.5 chalk: ^2.0.0 js-tokens: ^4.0.0 - checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 + checksum: f61ae6de6ee0ea8d9b5bcf2a532faec5ab0a1dc0f7c640e5047fc61630a0edb88b18d8c92eb06566d30da7a27db841aca11820ecd3ebe9ce514c9350fbed39c4 languageName: node linkType: hard -"@babel/parser@npm:^7.18.4, @babel/parser@npm:^7.21.9, @babel/parser@npm:^7.22.0": - version: 7.22.3 - resolution: "@babel/parser@npm:7.22.3" +"@babel/parser@npm:^7.18.4, @babel/parser@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/parser@npm:7.22.5" bin: parser: ./bin/babel-parser.js - checksum: 016f2960212fd86817e15d90b9b3450d2b51af189b1c17a20ade5735d9ec69e76e29def317e09188ecd5fa6eab4f9ab72d88b8b829c1b2994400a6a2c2dc1958 + checksum: 470ebba516417ce8683b36e2eddd56dcfecb32c54b9bb507e28eb76b30d1c3e618fd0cfeee1f64d8357c2254514e1a19e32885cfb4e73149f4ae875436a6d59c languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0 - checksum: 845bd280c55a6a91d232cfa54eaf9708ec71e594676fe705794f494bb8b711d833b752b59d1a5c154695225880c23dbc9cab0e53af16fd57807976cd3ff41b8d + checksum: 1e353a060fb2cd8f1256d28cd768f16fb02513f905b9b6d656fb0242c96c341a196fa188b27c2701506a6e27515359fbcc1a5ca7fa8b9b530cf88fbd137baefc languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.3" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/plugin-transform-optional-chaining": ^7.22.3 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/plugin-transform-optional-chaining": ^7.22.5 peerDependencies: "@babel/core": ^7.13.0 - checksum: d786e4d89c0674cab4fb65e804920782b2ff8319a3e6c561c81b0265451f4ac9f8ce1f9699303398636352b5177730e31c219a086b72980bf39f98faadeab3c1 + checksum: 16e7a5f3bf2f2ac0ca032a70bf0ebd7e886d84dbb712b55c0643c04c495f0f221fbcbca14b5f8f8027fa6c87a3dafae0934022ad2b409384af6c5c356495b7bd languageName: node linkType: hard -"@babel/plugin-proposal-decorators@npm:7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-proposal-decorators@npm:7.22.3" +"@babel/plugin-proposal-decorators@npm:7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-proposal-decorators@npm:7.22.5" dependencies: - "@babel/helper-create-class-features-plugin": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-replace-supers": ^7.22.1 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/plugin-syntax-decorators": ^7.22.3 + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.5 + "@babel/plugin-syntax-decorators": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e68d9311924095d6e35a8ed52dfaa06ec8a24bbfa2ca2a241a9ef31a543b014eaf32e6434b737cf87368ffc3051d80091d8640554f99f34211caaa7e90985ea9 + checksum: b3807b92b6ffcaba7519a9b2bb59e4b5530873234cd170ff5727414939334fbcae17bbe523df846a103e2fc8ed2d2890d0d9408f073cfc1e90c28ab565c358e5 languageName: node linkType: hard -"@babel/plugin-proposal-private-property-in-object@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-create-class-features-plugin": ^7.21.0 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-private-property-in-object": ^7.14.5 +"@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2": + version: 7.21.0-placeholder-for-preset-env.2 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: add881a6a836635c41d2710551fdf777e2c07c0b691bf2baacc5d658dd64107479df1038680d6e67c468bfc6f36fb8920025d6bac2a1df0a81b867537d40ae78 + checksum: d97745d098b835d55033ff3a7fb2b895b9c5295b08a5759e4f20df325aa385a3e0bc9bd5ad8f2ec554a44d4e6525acfc257b8c5848a1345cb40f26a30e277e91 languageName: node linkType: hard @@ -463,14 +458,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-decorators@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-syntax-decorators@npm:7.22.3" +"@babel/plugin-syntax-decorators@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-decorators@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 1886778ae5284202329e60f5be3322307dc77d0764a75e23f8fc222bc05409f4153035eb817cabd40265cc1c20eb201ca539c8cd2e883b312024dbb6480cdd95 + checksum: 643c75a3b603320c499a0542ca97b5cced81e99de02ae9cbfca1a1ec6d938467546a65023b13df742e1b2f94ffe352ddfe908d14b9303fae7514ed9325886a97 languageName: node linkType: hard @@ -496,25 +491,25 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.12.1, @babel/plugin-syntax-import-assertions@npm:^7.20.0": - version: 7.20.0 - resolution: "@babel/plugin-syntax-import-assertions@npm:7.20.0" +"@babel/plugin-syntax-import-assertions@npm:^7.12.1, @babel/plugin-syntax-import-assertions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.19.0 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6a86220e0aae40164cd3ffaf80e7c076a1be02a8f3480455dddbae05fda8140f429290027604df7a11b3f3f124866e8a6d69dbfa1dda61ee7377b920ad144d5b + checksum: 2b8b5572db04a7bef1e6cd20debf447e4eef7cb012616f5eceb8fa3e23ce469b8f76ee74fd6d1e158ba17a8f58b0aec579d092fb67c5a30e83ccfbc5754916c1 languageName: node linkType: hard -"@babel/plugin-syntax-import-attributes@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-syntax-import-attributes@npm:7.22.3" +"@babel/plugin-syntax-import-attributes@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 48cf66ba1b6772134f4e638c42ff51a0e8037cea540733642146e031641641e8a03e4f43e6be613e2313d174f95d9b3a1f14f41db0a9fa78a8330282b5aad03c + checksum: 197b3c5ea2a9649347f033342cb222ab47f4645633695205c0250c6bf2af29e643753b8bb24a2db39948bef08e7c540babfd365591eb57fc110cb30b425ffc47 languageName: node linkType: hard @@ -540,14 +535,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/plugin-syntax-jsx@npm:7.21.4" +"@babel/plugin-syntax-jsx@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bb7309402a1d4e155f32aa0cf216e1fa8324d6c4cfd248b03280028a015a10e46b6efd6565f515f8913918a3602b39255999c06046f7d4b8a5106be2165d724a + checksum: 8829d30c2617ab31393d99cec2978e41f014f4ac6f01a1cecf4c4dd8320c3ec12fdc3ce121126b2d8d32f6887e99ca1a0bad53dedb1e6ad165640b92b24980ce languageName: node linkType: hard @@ -639,14 +634,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.20.0": - version: 7.21.4 - resolution: "@babel/plugin-syntax-typescript@npm:7.21.4" +"@babel/plugin-syntax-typescript@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a59ce2477b7ae8c8945dc37dda292fef9ce46a6507b3d76b03ce7f3a6c9451a6567438b20a78ebcb3955d04095fd1ccd767075a863f79fcc30aa34dcfa441fe0 + checksum: 8ab7718fbb026d64da93681a57797d60326097fd7cb930380c8bffd9eb101689e90142c760a14b51e8e69c88a73ba3da956cb4520a3b0c65743aee5c71ef360a languageName: node linkType: hard @@ -662,634 +657,634 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.21.5" +"@babel/plugin-transform-arrow-functions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c7c281cdf37c33a584102d9fd1793e85c96d4d320cdfb7c43f1ce581323d057f13b53203994fcc7ee1f8dc1ff013498f258893aa855a06c6f830fcc4c33d6e44 + checksum: 35abb6c57062802c7ce8bd96b2ef2883e3124370c688bbd67609f7d2453802fb73944df8808f893b6c67de978eb2bcf87bbfe325e46d6f39b5fcb09ece11d01a languageName: node linkType: hard -"@babel/plugin-transform-async-generator-functions@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-async-generator-functions@npm:7.22.3" +"@babel/plugin-transform-async-generator-functions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.22.5" dependencies: - "@babel/helper-environment-visitor": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-remap-async-to-generator": ^7.18.9 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-remap-async-to-generator": ^7.22.5 "@babel/plugin-syntax-async-generators": ^7.8.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0ea339f9e484df0b72eb962dca81f5e6291d674eb4de7af2cde2a7e2ff728fbc4fdad662f2e77bf5bdbd2b628e111b9a7c71c3165684147ca1bf1f891fc30a4b + checksum: 32890b69ec5627eb46ee8e084bddc6b98d85b66cae5e015f3a23924611a759789d2ff836406605f5293b5c2bad306b20cb1f5b7a46ed549b07bfec634bcd31f9 languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" +"@babel/plugin-transform-async-to-generator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.22.5" dependencies: - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-remap-async-to-generator": ^7.18.9 + "@babel/helper-module-imports": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-remap-async-to-generator": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fe9ee8a5471b4317c1b9ea92410ace8126b52a600d7cfbfe1920dcac6fb0fad647d2e08beb4fd03c630eb54430e6c72db11e283e3eddc49615c68abd39430904 + checksum: b95f23f99dcb379a9f0a1c2a3bbea3f8dc0e1b16dc1ac8b484fe378370169290a7a63d520959a9ba1232837cf74a80e23f6facbe14fd42a3cda6d3c2d7168e62 languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" +"@babel/plugin-transform-block-scoped-functions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0a0df61f94601e3666bf39f2cc26f5f7b22a94450fb93081edbed967bd752ce3f81d1227fefd3799f5ee2722171b5e28db61379234d1bb85b6ec689589f99d7e + checksum: 416b1341858e8ca4e524dee66044735956ced5f478b2c3b9bc11ec2285b0c25d7dbb96d79887169eb938084c95d0a89338c8b2fe70d473bd9dc92e5d9db1732c languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-block-scoping@npm:7.21.0" +"@babel/plugin-transform-block-scoping@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-block-scoping@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 15aacaadbecf96b53a750db1be4990b0d89c7f5bc3e1794b63b49fb219638c1fd25d452d15566d7e5ddf5b5f4e1a0a0055c35c1c7aee323c7b114bf49f66f4b0 + checksum: 26987002cfe6e24544e60fa35f07052b6557f590c1a1cc5cf35d6dc341d7fea163c1222a2d70d5d2692f0b9860d942fd3ba979848b2995d4debffa387b9b19ae languageName: node linkType: hard -"@babel/plugin-transform-class-properties@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-class-properties@npm:7.22.3" +"@babel/plugin-transform-class-properties@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-class-properties@npm:7.22.5" dependencies: - "@babel/helper-create-class-features-plugin": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4037397badb5d537d87c092da99a0278f735e66dc667a31495aa2dd5fcf1315bfe8981773d2ce502ff8048c68ab32a7c3019df714945520443e28380fa5e7f74 + checksum: b830152dfc2ff2f647f0abe76e6251babdfbef54d18c4b2c73a6bf76b1a00050a5d998dac80dc901a48514e95604324943a9dd39317073fe0928b559e0e0c579 languageName: node linkType: hard -"@babel/plugin-transform-class-static-block@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-class-static-block@npm:7.22.3" +"@babel/plugin-transform-class-static-block@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-class-static-block@npm:7.22.5" dependencies: - "@babel/helper-create-class-features-plugin": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-class-static-block": ^7.14.5 peerDependencies: "@babel/core": ^7.12.0 - checksum: f407a3354ee0720803cd3366d7d081643d37201892243deed1aa76eb9330c11bf4e548441fa6a77637262a1b61890c1aacea176ad828650b8eb3f5b4d2da9c97 + checksum: bc48b92dbaf625a14f2bf62382384eef01e0515802426841636ae9146e27395d068c7a8a45e9e15699491b0a01d990f38f179cbc9dc89274a393f85648772f12 languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-classes@npm:7.21.0" +"@babel/plugin-transform-classes@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-classes@npm:7.22.5" dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-compilation-targets": ^7.20.7 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-replace-supers": ^7.20.7 - "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-compilation-targets": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.5 globals: ^11.1.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 088ae152074bd0e90f64659169255bfe50393e637ec8765cb2a518848b11b0299e66b91003728fd0a41563a6fdc6b8d548ece698a314fd5447f5489c22e466b7 + checksum: 124b1b79180524cc9d08155cecde92c7f2ab0db02cbe0f8befa187ef3c7320909ce1a6d6daf5ce73e8330f9b40cf9991f424c6e572b8dddc1f14e2758fa80d20 languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/plugin-transform-computed-properties@npm:7.21.5" +"@babel/plugin-transform-computed-properties@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-computed-properties@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/template": ^7.20.7 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/template": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e819780ab30fc40d7802ffb75b397eff63ca4942a1873058f81c80f660189b78e158fa03fd3270775f0477c4c33cee3d8d40270e64404bbf24aa6cdccb197e7b + checksum: c2a77a0f94ec71efbc569109ec14ea2aa925b333289272ced8b33c6108bdbb02caf01830ffc7e49486b62dec51911924d13f3a76f1149f40daace1898009e131 languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.21.3": - version: 7.21.3 - resolution: "@babel/plugin-transform-destructuring@npm:7.21.3" +"@babel/plugin-transform-destructuring@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-destructuring@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 43ebbe0bfa20287e34427be7c2200ce096c20913775ea75268fb47fe0e55f9510800587e6052c42fe6dffa0daaad95dd465c3e312fd1ef9785648384c45417ac + checksum: 76f6ea2aee1fcfa1c3791eb7a5b89703c6472650b993e8666fff0f1d6e9d737a84134edf89f63c92297f3e75064c1263219463b02dd9bc7434b6e5b9935e3f20 languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @babel/plugin-transform-dotall-regex@npm:^7.4.4": - version: 7.18.6 - resolution: "@babel/plugin-transform-dotall-regex@npm:7.18.6" +"@babel/plugin-transform-dotall-regex@npm:^7.22.5, @babel/plugin-transform-dotall-regex@npm:^7.4.4": + version: 7.22.5 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.22.5" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: cbe5d7063eb8f8cca24cd4827bc97f5641166509e58781a5f8aa47fb3d2d786ce4506a30fca2e01f61f18792783a5cb5d96bf5434c3dd1ad0de8c9cc625a53da + checksum: 409b658d11e3082c8f69e9cdef2d96e4d6d11256f005772425fb230cc48fd05945edbfbcb709dab293a1a2f01f9c8a5bb7b4131e632b23264039d9f95864b453 languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.18.9" +"@babel/plugin-transform-duplicate-keys@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 220bf4a9fec5c4d4a7b1de38810350260e8ea08481bf78332a464a21256a95f0df8cd56025f346238f09b04f8e86d4158fafc9f4af57abaef31637e3b58bd4fe + checksum: bb1280fbabaab6fab2ede585df34900712698210a3bd413f4df5bae6d8c24be36b496c92722ae676a7a67d060a4624f4d6c23b923485f906bfba8773c69f55b4 languageName: node linkType: hard -"@babel/plugin-transform-dynamic-import@npm:^7.22.1": - version: 7.22.1 - resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.1" +"@babel/plugin-transform-dynamic-import@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-dynamic-import": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e322a08f01cedddcd7c70aa6a74342e900df39ab13dbaa2c8175af660b1786dd26b582546fc37e16bec47181931963e173ff53ffd7c41d5f54687da5f8d457bb + checksum: 186a6d59f36eb3c5824739fc9c22ed0f4ca68e001662aa3a302634346a8b785cb9579b23b0c158f4570604d697d19598ca09b58c60a7fa2894da1163c4eb1907 languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.18.6" +"@babel/plugin-transform-exponentiation-operator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.22.5" dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7f70222f6829c82a36005508d34ddbe6fd0974ae190683a8670dd6ff08669aaf51fef2209d7403f9bd543cb2d12b18458016c99a6ed0332ccedb3ea127b01229 + checksum: f2d660c1b1d51ad5fec1cd5ad426a52187204068c4158f8c4aa977b31535c61b66898d532603eef21c15756827be8277f724c869b888d560f26d7fe848bb5eae languageName: node linkType: hard -"@babel/plugin-transform-export-namespace-from@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.3" +"@babel/plugin-transform-export-namespace-from@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7bb031ea6e05e8090ac18dc03c62527be29f541e9ec0c93031d77d4540c736b43384a2f2a9aef1f72b7867989f1ce2aaefb325dbc7cc49c59f55aed87a96d488 + checksum: 3d197b788758044983c96b9c49bed4b456055f35a388521a405968db0f6e2ffb6fd59110e3931f4dcc5e126ae9e5e00e154a0afb47a7ea359d8d0dea79f480d7 languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/plugin-transform-for-of@npm:7.21.5" +"@babel/plugin-transform-for-of@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-for-of@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b6666b24e8ca1ffbf7452a0042149724e295965aad55070dc9ee992451d69d855fc9db832c1c5fb4d3dc532f4a18a2974d5f8524f5c2250dda888d05f6f3cadb + checksum: d7b8d4db010bce7273674caa95c4e6abd909362866ce297e86a2ecaa9ae636e05d525415811db9b3c942155df7f3651d19b91dd6c41f142f7308a97c7cb06023 languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-function-name@npm:7.18.9" +"@babel/plugin-transform-function-name@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-function-name@npm:7.22.5" dependencies: - "@babel/helper-compilation-targets": ^7.18.9 - "@babel/helper-function-name": ^7.18.9 - "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-compilation-targets": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 62dd9c6cdc9714704efe15545e782ee52d74dc73916bf954b4d3bee088fb0ec9e3c8f52e751252433656c09f744b27b757fc06ed99bcde28e8a21600a1d8e597 + checksum: cff3b876357999cb8ae30e439c3ec6b0491a53b0aa6f722920a4675a6dd5b53af97a833051df4b34791fe5b3dd326ccf769d5c8e45b322aa50ee11a660b17845 languageName: node linkType: hard -"@babel/plugin-transform-json-strings@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-json-strings@npm:7.22.3" +"@babel/plugin-transform-json-strings@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-json-strings@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-json-strings": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2b09a549bdd80020b390dbc91aaf0be624e42fff64026a38abad1ec6c7714551edad8a84edb555288778aa9e3bb20e9df535587466b30347b63532fb1f404731 + checksum: 4e00b902487a670b6c8948f33f9108133fd745cf9d1478aca515fb460b9b2f12e137988ebc1663630fb82070a870aed8b0c1aa4d007a841c18004619798f255c languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-literals@npm:7.18.9" +"@babel/plugin-transform-literals@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-literals@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3458dd2f1a47ac51d9d607aa18f3d321cbfa8560a985199185bed5a906bb0c61ba85575d386460bac9aed43fdd98940041fae5a67dff286f6f967707cff489f8 + checksum: ec37cc2ffb32667af935ab32fe28f00920ec8a1eb999aa6dc6602f2bebd8ba205a558aeedcdccdebf334381d5c57106c61f52332045730393e73410892a9735b languageName: node linkType: hard -"@babel/plugin-transform-logical-assignment-operators@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.3" +"@babel/plugin-transform-logical-assignment-operators@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b2452c6804aa440bd3fc662ee4f477c3acfa4a7f768ac66a6084a9e0774ac52cfff7cc6ea72495cc4e0728d2d7f98b65555927484dc96e9564adf1bcc5aa956e + checksum: 18748e953c08f64885f18c224eac58df10a13eac4d845d16b5d9b6276907da7ca2530dfebe6ed41cdc5f8a75d9db3e36d8eb54ddce7cd0364af1cab09b435302 languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-member-expression-literals@npm:7.18.6" +"@babel/plugin-transform-member-expression-literals@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 35a3d04f6693bc6b298c05453d85ee6e41cc806538acb6928427e0e97ae06059f97d2f07d21495fcf5f70d3c13a242e2ecbd09d5c1fcb1b1a73ff528dcb0b695 + checksum: ec4b0e07915ddd4fda0142fd104ee61015c208608a84cfa13643a95d18760b1dc1ceb6c6e0548898b8c49e5959a994e46367260176dbabc4467f729b21868504 languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.20.11": - version: 7.20.11 - resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11" +"@babel/plugin-transform-modules-amd@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-amd@npm:7.22.5" dependencies: - "@babel/helper-module-transforms": ^7.20.11 - "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-module-transforms": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 23665c1c20c8f11c89382b588fb9651c0756d130737a7625baeaadbd3b973bc5bfba1303bedffa8fb99db1e6d848afb01016e1df2b69b18303e946890c790001 + checksum: 7da4c4ebbbcf7d182abb59b2046b22d86eee340caf8a22a39ef6a727da2d8acfec1f714fcdcd5054110b280e4934f735e80a6848d192b6834c5d4459a014f04d languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.21.5" +"@babel/plugin-transform-modules-commonjs@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.5" dependencies: - "@babel/helper-module-transforms": ^7.21.5 - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-simple-access": ^7.21.5 + "@babel/helper-module-transforms": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-simple-access": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d9ff7a21baaa60c08a0c86c5e468bb4b2bd85caf51ba78712d8f45e9afa2498d50d6cdf349696e08aa820cafed65f19b70e5938613db9ebb095f7aba1127f282 + checksum: 2067aca8f6454d54ffcce69b02c457cfa61428e11372f6a1d99ff4fcfbb55c396ed2ca6ca886bf06c852e38c1a205b8095921b2364fd0243f3e66bc1dda61caa languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.3" +"@babel/plugin-transform-modules-systemjs@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.5" dependencies: - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-module-transforms": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-validator-identifier": ^7.19.1 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-module-transforms": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a12a063dad61cccf50686d3f394f9f3f6c06261160c897a4b3423309aa7c40d37bd88126cf8535701f3490b99ac93b34c947f664465d63a74477ba66ab1d82e9 + checksum: 04f4178589543396b3c24330a67a59c5e69af5e96119c9adda730c0f20122deaff54671ebbc72ad2df6495a5db8a758bd96942de95fba7ad427de9c80b1b38c8 languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-modules-umd@npm:7.18.6" +"@babel/plugin-transform-modules-umd@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-umd@npm:7.22.5" dependencies: - "@babel/helper-module-transforms": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-module-transforms": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c3b6796c6f4579f1ba5ab0cdcc73910c1e9c8e1e773c507c8bb4da33072b3ae5df73c6d68f9126dab6e99c24ea8571e1563f8710d7c421fac1cde1e434c20153 + checksum: 46622834c54c551b231963b867adbc80854881b3e516ff29984a8da989bd81665bd70e8cba6710345248e97166689310f544aee1a5773e262845a8f1b3e5b8b4 languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.3" +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0 - checksum: baf3d8d785ab36df2d7396b8a255e1209eecf83ad5334121fbb9e966a95353fe2100dd3683436f4c74b3c848ec0b34817491c4d14b074e3e539e2040076173d8 + checksum: 3ee564ddee620c035b928fdc942c5d17e9c4b98329b76f9cefac65c111135d925eb94ed324064cd7556d4f5123beec79abea1d4b97d1c8a2a5c748887a2eb623 languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-new-target@npm:7.22.3" +"@babel/plugin-transform-new-target@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-new-target@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a28043575aae52127b7287711cf0b244a28279464d979858408ca6197169b6f7e6341e5b4554a894d409245fcd696c9bf38d5f1f1c64f84a82f479bf35659920 + checksum: 6b72112773487a881a1d6ffa680afde08bad699252020e86122180ee7a88854d5da3f15d9bca3331cf2e025df045604494a8208a2e63b486266b07c14e2ffbf3 languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.3" +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 404c3c7eb8b99f226ce40147d350ad3df55b38ffe39856356f7cfbbb1626ce060bc1daff0663c090d53160d39fdb26ea67ca291d47211ff7746a8a0c3bbc1639 + checksum: e6a059169d257fc61322d0708edae423072449b7c33de396261e68dee582aec5396789a1c22bce84e5bd88a169623c2e750b513fc222930979e6accd52a44bf2 languageName: node linkType: hard -"@babel/plugin-transform-numeric-separator@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-numeric-separator@npm:7.22.3" +"@babel/plugin-transform-numeric-separator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-numeric-separator": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2cbcf21d040cb9ab6ded383586620f3a84e8619fecdc222d8b3d462c706b1e8ceae2dddf530d9177291c155c80dd67100142e76a11f1e230aeda6d90273a2890 + checksum: 9e7837d4eae04f211ebaa034fe5003d2927b6bf6d5b9dc09f2b1183c01482cdde5a75b8bd5c7ff195c2abc7b923339eb0b2a9d27cb78359d38248a3b2c2367c4 languageName: node linkType: hard -"@babel/plugin-transform-object-rest-spread@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-object-rest-spread@npm:7.22.3" +"@babel/plugin-transform-object-rest-spread@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.22.5" dependencies: - "@babel/compat-data": ^7.22.3 - "@babel/helper-compilation-targets": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/compat-data": ^7.22.5 + "@babel/helper-compilation-targets": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-object-rest-spread": ^7.8.3 - "@babel/plugin-transform-parameters": ^7.22.3 + "@babel/plugin-transform-parameters": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 43f4cb8eb60e76bb506cab86a6c9a98b2f4f986296a20a01f93c6a7bf3835621a22e3e85eaca094c86b03580f93e583391f4c0da0af0c9408ff1a2b35f2e96ca + checksum: 3b5e091f0dc67108f2e41ed5a97e15bbe4381a19d9a7eea80b71c7de1d8169fd28784e1e41a3d2ad12709ab212e58fc481282a5bb65d591fae7b443048de3330 languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-object-super@npm:7.18.6" +"@babel/plugin-transform-object-super@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-object-super@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/helper-replace-supers": ^7.18.6 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0fcb04e15deea96ae047c21cb403607d49f06b23b4589055993365ebd7a7d7541334f06bf9642e90075e66efce6ebaf1eb0ef066fbbab802d21d714f1aac3aef + checksum: b71887877d74cb64dbccb5c0324fa67e31171e6a5311991f626650e44a4083e5436a1eaa89da78c0474fb095d4ec322d63ee778b202d33aa2e4194e1ed8e62d7 languageName: node linkType: hard -"@babel/plugin-transform-optional-catch-binding@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.3" +"@babel/plugin-transform-optional-catch-binding@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e766bd2ac74fcd2226c8816500c788268a1fa5200498a288f10854253addb36871cd7b415e20736819e7fcb996a0e33312bc1ce6db9b540ec9dd7b946cb37dda + checksum: b0e8b4233ff06b5c9d285257f49c5bd441f883189b24282e6200f9ebdf5db29aeeebbffae57fbbcd5df9f4387b3e66e5d322aaae5652a78e89685ddbae46bbd1 languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.22.3" +"@babel/plugin-transform-optional-chaining@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 "@babel/plugin-syntax-optional-chaining": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9b50a28b59250ecabeae928b8237c596e6f81f5fcdacd03a99a3777bbfea2447773936f4b5091e63b2d46e707429d9bdf22fcd0fb4b05a702bf08f554bea3ae2 + checksum: 57b9c05fb22ae881b8a334b184fc6ee966661ed5d1eb4eed8c2fb9a12e68150d90b229efcb1aa777e246999830844fee06d7365f8bb4bb262fdcd23876ff3ea2 languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-parameters@npm:7.22.3" +"@babel/plugin-transform-parameters@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-parameters@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 68a30f630f5d99be7675fab23d009205302f33f1eac015418d5344983fe8f97f4eae0130f6e4f3c21b8dd8971d516346fba90b01ba3c2c15f23b47c6f4b7161a + checksum: b44f89cf97daf23903776ba27c2ab13b439d80d8c8a95be5c476ab65023b1e0c0e94c28d3745f3b60a58edc4e590fa0cd4287a0293e51401ca7d29a2ddb13b8e languageName: node linkType: hard -"@babel/plugin-transform-private-methods@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-private-methods@npm:7.22.3" +"@babel/plugin-transform-private-methods@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-private-methods@npm:7.22.5" dependencies: - "@babel/helper-create-class-features-plugin": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 048501cfdf86c3de5750dc0728cf73d65f1ec4ad932e16e55b238ac0b5b805882c1fbbdb63077d95ce8beadae840cee11b767cc6918264517245e7f04baf9f63 + checksum: 321479b4fcb6d3b3ef622ab22fd24001e43d46e680e8e41324c033d5810c84646e470f81b44cbcbef5c22e99030784f7cac92f1829974da7a47a60a7139082c3 languageName: node linkType: hard -"@babel/plugin-transform-private-property-in-object@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-private-property-in-object@npm:7.22.3" +"@babel/plugin-transform-private-property-in-object@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.22.5" dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-create-class-features-plugin": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ea3c347687672119305ba2f2ef7ca66905c1713c6652bb01deacc057178bedcf07c46b6b75e1fe8688ffed8fcabe312a735eeb0fef21dd9ab61a61db23ef6ba5 + checksum: 9ac019fb2772f3af6278a7f4b8b14b0663accb3fd123d87142ceb2fbc57fd1afa07c945d1329029b026b9ee122096ef71a3f34f257a9e04cf4245b87298c38b4 languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-property-literals@npm:7.18.6" +"@babel/plugin-transform-property-literals@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-property-literals@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 1c16e64de554703f4b547541de2edda6c01346dd3031d4d29e881aa7733785cd26d53611a4ccf5353f4d3e69097bb0111c0a93ace9e683edd94fea28c4484144 + checksum: 796176a3176106f77fcb8cd04eb34a8475ce82d6d03a88db089531b8f0453a2fb8b0c6ec9a52c27948bc0ea478becec449893741fc546dfc3930ab927e3f9f2e languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/plugin-transform-regenerator@npm:7.21.5" +"@babel/plugin-transform-regenerator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-regenerator@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 regenerator-transform: ^0.15.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5291f6871276f57a6004f16d50ae9ad57f22a6aa2a183b8c84de8126f1066c6c9f9bbeadb282b5207fa9e7b0f57e40a8421d46cb5c60caf7e2848e98224d5639 + checksum: f7c5ca5151321963df777cc02725d10d1ccc3b3b8323da0423aecd9ac6144cbdd2274af5281a5580db2fc2f8b234e318517b5d76b85669118906533a559f2b6a languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-reserved-words@npm:7.18.6" +"@babel/plugin-transform-reserved-words@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-reserved-words@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0738cdc30abdae07c8ec4b233b30c31f68b3ff0eaa40eddb45ae607c066127f5fa99ddad3c0177d8e2832e3a7d3ad115775c62b431ebd6189c40a951b867a80c + checksum: 3ffd7dbc425fe8132bfec118b9817572799cab1473113a635d25ab606c1f5a2341a636c04cf6b22df3813320365ed5a965b5eeb3192320a10e4cc2c137bd8bfc languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:7.22.4": - version: 7.22.4 - resolution: "@babel/plugin-transform-runtime@npm:7.22.4" +"@babel/plugin-transform-runtime@npm:7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-runtime@npm:7.22.5" dependencies: - "@babel/helper-module-imports": ^7.21.4 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-module-imports": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 babel-plugin-polyfill-corejs2: ^0.4.3 babel-plugin-polyfill-corejs3: ^0.8.1 babel-plugin-polyfill-regenerator: ^0.5.0 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e51400ffeccfc8875c6d136510aa6145e44d825ee7fb52da462401388b4303a6a274ca94fad4aa46b06870c6fdc6141dafa51f681423160d924a21212daa8792 + checksum: 52cf177045b5f61a6cfc36b45aa7629586dc00a28371a09ef03e877a627f520efd51817ad8cceabaaa25f266e069859b36a5ac5018afeaa7f37aafa9325df4d8 languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-shorthand-properties@npm:7.18.6" +"@babel/plugin-transform-shorthand-properties@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b8e4e8acc2700d1e0d7d5dbfd4fdfb935651913de6be36e6afb7e739d8f9ca539a5150075a0f9b79c88be25ddf45abb912fe7abf525f0b80f5b9d9860de685d7 + checksum: a5ac902c56ea8effa99f681340ee61bac21094588f7aef0bc01dff98246651702e677552fa6d10e548c4ac22a3ffad047dd2f8c8f0540b68316c2c203e56818b languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-spread@npm:7.20.7" +"@babel/plugin-transform-spread@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-spread@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8ea698a12da15718aac7489d4cde10beb8a3eea1f66167d11ab1e625033641e8b328157fd1a0b55dd6531933a160c01fc2e2e61132a385cece05f26429fd0cc2 + checksum: 5587f0deb60b3dfc9b274e269031cc45ec75facccf1933ea2ea71ced9fd3ce98ed91bb36d6cd26817c14474b90ed998c5078415f0eab531caf301496ce24c95c languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-sticky-regex@npm:7.18.6" +"@babel/plugin-transform-sticky-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 68ea18884ae9723443ffa975eb736c8c0d751265859cd3955691253f7fee37d7a0f7efea96c8a062876af49a257a18ea0ed5fea0d95a7b3611ce40f7ee23aee3 + checksum: 63b2c575e3e7f96c32d52ed45ee098fb7d354b35c2223b8c8e76840b32cc529ee0c0ceb5742fd082e56e91e3d82842a367ce177e82b05039af3d602c9627a729 languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-template-literals@npm:7.18.9" +"@babel/plugin-transform-template-literals@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-template-literals@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3d2fcd79b7c345917f69b92a85bdc3ddd68ce2c87dc70c7d61a8373546ccd1f5cb8adc8540b49dfba08e1b82bb7b3bbe23a19efdb2b9c994db2db42906ca9fb2 + checksum: 27e9bb030654cb425381c69754be4abe6a7c75b45cd7f962cd8d604b841b2f0fb7b024f2efc1c25cc53f5b16d79d5e8cfc47cacbdaa983895b3aeefa3e7e24ff languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.18.9" +"@babel/plugin-transform-typeof-symbol@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.18.9 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e754e0d8b8a028c52e10c148088606e3f7a9942c57bd648fc0438e5b4868db73c386a5ed47ab6d6f0594aae29ee5ffc2ffc0f7ebee7fae560a066d6dea811cd4 + checksum: 82a53a63ffc3010b689ca9a54e5f53b2718b9f4b4a9818f36f9b7dba234f38a01876680553d2716a645a61920b5e6e4aaf8d4a0064add379b27ca0b403049512 languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.21.3": - version: 7.21.3 - resolution: "@babel/plugin-transform-typescript@npm:7.21.3" +"@babel/plugin-transform-typescript@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-typescript@npm:7.22.5" dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-create-class-features-plugin": ^7.21.0 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-typescript": ^7.20.0 + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-typescript": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c16fd577bf43f633deb76fca2a8527d8ae25968c8efdf327c1955472c3e0257e62992473d1ad7f9ee95379ce2404699af405ea03346055adadd3478ad0ecd117 + checksum: d12f1ca1ef1f2a54432eb044d2999705d1205ebe211c2a7f05b12e8eb2d2a461fd7657b5486b2f2f1efe7c0c0dc8e80725b767073d40fe4ae059a7af057b05e4 languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.21.5" +"@babel/plugin-transform-unicode-escapes@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6504d642d0449a275191b624bd94d3e434ae154e610bf2f0e3c109068b287d2474f68e1da64b47f21d193cd67b27ee4643877d530187670565cac46e29fd257d + checksum: da5e85ab3bb33a75cbf6181bfd236b208dc934702fd304db127232f17b4e0f42c6d3f238de8589470b4190906967eea8ca27adf3ae9d8ee4de2a2eae906ed186 languageName: node linkType: hard -"@babel/plugin-transform-unicode-property-regex@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.22.3" +"@babel/plugin-transform-unicode-property-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.22.5" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 666592f5f5496e7dc267fb32e5c0d1ca620a5a1b7dcfec4fec517a625d2413213f795d3905aea05f45639285578ef13351cedfc5b699aaa482841866089863f6 + checksum: 2495e5f663cb388e3d888b4ba3df419ac436a5012144ac170b622ddfc221f9ea9bdba839fa2bc0185cb776b578030666406452ec7791cbf0e7a3d4c88ae9574c languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-unicode-regex@npm:7.18.6" +"@babel/plugin-transform-unicode-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.22.5" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d9e18d57536a2d317fb0b7c04f8f55347f3cfacb75e636b4c6fa2080ab13a3542771b5120e726b598b815891fc606d1472ac02b749c69fd527b03847f22dc25e + checksum: 6b5d1404c8c623b0ec9bd436c00d885a17d6a34f3f2597996343ddb9d94f6379705b21582dfd4cec2c47fd34068872e74ab6b9580116c0566b3f9447e2a7fa06 languageName: node linkType: hard -"@babel/plugin-transform-unicode-sets-regex@npm:^7.22.3": - version: 7.22.3 - resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.22.3" +"@babel/plugin-transform-unicode-sets-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.22.5" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0 - checksum: 992f6ae2764b1ee3bea9d948132daed22f042517cd6109a8fd2c52c03e7b930fac5a9e6e28130b0ed5a6f1cbf809c9735985352de0484b4c95fb0f6dd88614a2 + checksum: c042070f980b139547f8b0179efbc049ac5930abec7fc26ed7a41d89a048d8ab17d362200e204b6f71c3c20d6991a0e74415e1a412a49adc8131c2a40c04822e languageName: node linkType: hard -"@babel/preset-env@npm:7.22.4, @babel/preset-env@npm:^7.11.0": - version: 7.22.4 - resolution: "@babel/preset-env@npm:7.22.4" +"@babel/preset-env@npm:7.22.5, @babel/preset-env@npm:^7.11.0": + version: 7.22.5 + resolution: "@babel/preset-env@npm:7.22.5" dependencies: - "@babel/compat-data": ^7.22.3 - "@babel/helper-compilation-targets": ^7.22.1 - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-validator-option": ^7.21.0 - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.22.3 - "@babel/plugin-proposal-private-property-in-object": ^7.21.0 + "@babel/compat-data": ^7.22.5 + "@babel/helper-compilation-targets": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-option": ^7.22.5 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.22.5 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.22.5 + "@babel/plugin-proposal-private-property-in-object": 7.21.0-placeholder-for-preset-env.2 "@babel/plugin-syntax-async-generators": ^7.8.4 "@babel/plugin-syntax-class-properties": ^7.12.13 "@babel/plugin-syntax-class-static-block": ^7.14.5 "@babel/plugin-syntax-dynamic-import": ^7.8.3 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - "@babel/plugin-syntax-import-assertions": ^7.20.0 - "@babel/plugin-syntax-import-attributes": ^7.22.3 + "@babel/plugin-syntax-import-assertions": ^7.22.5 + "@babel/plugin-syntax-import-attributes": ^7.22.5 "@babel/plugin-syntax-import-meta": ^7.10.4 "@babel/plugin-syntax-json-strings": ^7.8.3 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 @@ -1301,56 +1296,56 @@ __metadata: "@babel/plugin-syntax-private-property-in-object": ^7.14.5 "@babel/plugin-syntax-top-level-await": ^7.14.5 "@babel/plugin-syntax-unicode-sets-regex": ^7.18.6 - "@babel/plugin-transform-arrow-functions": ^7.21.5 - "@babel/plugin-transform-async-generator-functions": ^7.22.3 - "@babel/plugin-transform-async-to-generator": ^7.20.7 - "@babel/plugin-transform-block-scoped-functions": ^7.18.6 - "@babel/plugin-transform-block-scoping": ^7.21.0 - "@babel/plugin-transform-class-properties": ^7.22.3 - "@babel/plugin-transform-class-static-block": ^7.22.3 - "@babel/plugin-transform-classes": ^7.21.0 - "@babel/plugin-transform-computed-properties": ^7.21.5 - "@babel/plugin-transform-destructuring": ^7.21.3 - "@babel/plugin-transform-dotall-regex": ^7.18.6 - "@babel/plugin-transform-duplicate-keys": ^7.18.9 - "@babel/plugin-transform-dynamic-import": ^7.22.1 - "@babel/plugin-transform-exponentiation-operator": ^7.18.6 - "@babel/plugin-transform-export-namespace-from": ^7.22.3 - "@babel/plugin-transform-for-of": ^7.21.5 - "@babel/plugin-transform-function-name": ^7.18.9 - "@babel/plugin-transform-json-strings": ^7.22.3 - "@babel/plugin-transform-literals": ^7.18.9 - "@babel/plugin-transform-logical-assignment-operators": ^7.22.3 - "@babel/plugin-transform-member-expression-literals": ^7.18.6 - "@babel/plugin-transform-modules-amd": ^7.20.11 - "@babel/plugin-transform-modules-commonjs": ^7.21.5 - "@babel/plugin-transform-modules-systemjs": ^7.22.3 - "@babel/plugin-transform-modules-umd": ^7.18.6 - "@babel/plugin-transform-named-capturing-groups-regex": ^7.22.3 - "@babel/plugin-transform-new-target": ^7.22.3 - "@babel/plugin-transform-nullish-coalescing-operator": ^7.22.3 - "@babel/plugin-transform-numeric-separator": ^7.22.3 - "@babel/plugin-transform-object-rest-spread": ^7.22.3 - "@babel/plugin-transform-object-super": ^7.18.6 - "@babel/plugin-transform-optional-catch-binding": ^7.22.3 - "@babel/plugin-transform-optional-chaining": ^7.22.3 - "@babel/plugin-transform-parameters": ^7.22.3 - "@babel/plugin-transform-private-methods": ^7.22.3 - "@babel/plugin-transform-private-property-in-object": ^7.22.3 - "@babel/plugin-transform-property-literals": ^7.18.6 - "@babel/plugin-transform-regenerator": ^7.21.5 - "@babel/plugin-transform-reserved-words": ^7.18.6 - "@babel/plugin-transform-shorthand-properties": ^7.18.6 - "@babel/plugin-transform-spread": ^7.20.7 - "@babel/plugin-transform-sticky-regex": ^7.18.6 - "@babel/plugin-transform-template-literals": ^7.18.9 - "@babel/plugin-transform-typeof-symbol": ^7.18.9 - "@babel/plugin-transform-unicode-escapes": ^7.21.5 - "@babel/plugin-transform-unicode-property-regex": ^7.22.3 - "@babel/plugin-transform-unicode-regex": ^7.18.6 - "@babel/plugin-transform-unicode-sets-regex": ^7.22.3 + "@babel/plugin-transform-arrow-functions": ^7.22.5 + "@babel/plugin-transform-async-generator-functions": ^7.22.5 + "@babel/plugin-transform-async-to-generator": ^7.22.5 + "@babel/plugin-transform-block-scoped-functions": ^7.22.5 + "@babel/plugin-transform-block-scoping": ^7.22.5 + "@babel/plugin-transform-class-properties": ^7.22.5 + "@babel/plugin-transform-class-static-block": ^7.22.5 + "@babel/plugin-transform-classes": ^7.22.5 + "@babel/plugin-transform-computed-properties": ^7.22.5 + "@babel/plugin-transform-destructuring": ^7.22.5 + "@babel/plugin-transform-dotall-regex": ^7.22.5 + "@babel/plugin-transform-duplicate-keys": ^7.22.5 + "@babel/plugin-transform-dynamic-import": ^7.22.5 + "@babel/plugin-transform-exponentiation-operator": ^7.22.5 + "@babel/plugin-transform-export-namespace-from": ^7.22.5 + "@babel/plugin-transform-for-of": ^7.22.5 + "@babel/plugin-transform-function-name": ^7.22.5 + "@babel/plugin-transform-json-strings": ^7.22.5 + "@babel/plugin-transform-literals": ^7.22.5 + "@babel/plugin-transform-logical-assignment-operators": ^7.22.5 + "@babel/plugin-transform-member-expression-literals": ^7.22.5 + "@babel/plugin-transform-modules-amd": ^7.22.5 + "@babel/plugin-transform-modules-commonjs": ^7.22.5 + "@babel/plugin-transform-modules-systemjs": ^7.22.5 + "@babel/plugin-transform-modules-umd": ^7.22.5 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.22.5 + "@babel/plugin-transform-new-target": ^7.22.5 + "@babel/plugin-transform-nullish-coalescing-operator": ^7.22.5 + "@babel/plugin-transform-numeric-separator": ^7.22.5 + "@babel/plugin-transform-object-rest-spread": ^7.22.5 + "@babel/plugin-transform-object-super": ^7.22.5 + "@babel/plugin-transform-optional-catch-binding": ^7.22.5 + "@babel/plugin-transform-optional-chaining": ^7.22.5 + "@babel/plugin-transform-parameters": ^7.22.5 + "@babel/plugin-transform-private-methods": ^7.22.5 + "@babel/plugin-transform-private-property-in-object": ^7.22.5 + "@babel/plugin-transform-property-literals": ^7.22.5 + "@babel/plugin-transform-regenerator": ^7.22.5 + "@babel/plugin-transform-reserved-words": ^7.22.5 + "@babel/plugin-transform-shorthand-properties": ^7.22.5 + "@babel/plugin-transform-spread": ^7.22.5 + "@babel/plugin-transform-sticky-regex": ^7.22.5 + "@babel/plugin-transform-template-literals": ^7.22.5 + "@babel/plugin-transform-typeof-symbol": ^7.22.5 + "@babel/plugin-transform-unicode-escapes": ^7.22.5 + "@babel/plugin-transform-unicode-property-regex": ^7.22.5 + "@babel/plugin-transform-unicode-regex": ^7.22.5 + "@babel/plugin-transform-unicode-sets-regex": ^7.22.5 "@babel/preset-modules": ^0.1.5 - "@babel/types": ^7.22.4 + "@babel/types": ^7.22.5 babel-plugin-polyfill-corejs2: ^0.4.3 babel-plugin-polyfill-corejs3: ^0.8.1 babel-plugin-polyfill-regenerator: ^0.5.0 @@ -1358,7 +1353,7 @@ __metadata: semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 68ae8b712e7548cb0aa593019bf22ed473bd83887c621c1f820ef0af99958d48b687c01b8aee16035cbc70edae1edc703b892e6efed14b95c8435343a2cb2bda + checksum: 6d9d09010ababef2ab48c8830770b2a8f45d6cce51db0924a98b0d95a5b1248a99ee07ee61cb5446d8b05b562db99a8af30b3ed194546419fb9b2889b8fd1ed3 languageName: node linkType: hard @@ -1377,18 +1372,18 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:7.21.5": - version: 7.21.5 - resolution: "@babel/preset-typescript@npm:7.21.5" +"@babel/preset-typescript@npm:7.22.5": + version: 7.22.5 + resolution: "@babel/preset-typescript@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-validator-option": ^7.21.0 - "@babel/plugin-syntax-jsx": ^7.21.4 - "@babel/plugin-transform-modules-commonjs": ^7.21.5 - "@babel/plugin-transform-typescript": ^7.21.3 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-option": ^7.22.5 + "@babel/plugin-syntax-jsx": ^7.22.5 + "@babel/plugin-transform-modules-commonjs": ^7.22.5 + "@babel/plugin-transform-typescript": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e7b35c435139eec1d6bd9f57e8f3eb79bfc2da2c57a34ad9e9ea848ba4ecd72791cf4102df456604ab07c7f4518525b0764754b6dd5898036608b351e0792448 + checksum: 7be1670cb4404797d3a473bd72d66eb2b3e0f2f8a672a5e40bdb0812cc66085ec84bcd7b896709764cabf042fdc6b7f2d4755ac7cce10515eb596ff61dab5154 languageName: node linkType: hard @@ -1399,52 +1394,52 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.22.3, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4": - version: 7.22.3 - resolution: "@babel/runtime@npm:7.22.3" +"@babel/runtime@npm:7.22.5, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4": + version: 7.22.5 + resolution: "@babel/runtime@npm:7.22.5" dependencies: regenerator-runtime: ^0.13.11 - checksum: 8fc50785ca4cba749fed90bffca7e6fd52d4709755654e95b8d2a945fc034b56fb8c2e8a0183fea7c4abb86bf5fa77678c0ea35163b6920649833d180c34c234 + checksum: 12a50b7de2531beef38840d17af50c55a094253697600cee255311222390c68eed704829308d4fd305e1b3dfbce113272e428e9d9d45b1730e0fede997eaceb1 languageName: node linkType: hard -"@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.21.9": - version: 7.21.9 - resolution: "@babel/template@npm:7.21.9" +"@babel/template@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/template@npm:7.22.5" dependencies: - "@babel/code-frame": ^7.21.4 - "@babel/parser": ^7.21.9 - "@babel/types": ^7.21.5 - checksum: 6ec2c60d4d53b2a9230ab82c399ba6525df87e9a4e01e4b111e071cbad283b1362e7c99a1bc50027073f44f2de36a495a89c27112c4e7efe7ef9c8d9c84de2ec + "@babel/code-frame": ^7.22.5 + "@babel/parser": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: c5746410164039aca61829cdb42e9a55410f43cace6f51ca443313f3d0bdfa9a5a330d0b0df73dc17ef885c72104234ae05efede37c1cc8a72dc9f93425977a3 languageName: node linkType: hard -"@babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.22.1": - version: 7.22.1 - resolution: "@babel/traverse@npm:7.22.1" +"@babel/traverse@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/traverse@npm:7.22.5" dependencies: - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.22.0 - "@babel/helper-environment-visitor": ^7.22.1 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.22.0 - "@babel/types": ^7.22.0 + "@babel/code-frame": ^7.22.5 + "@babel/generator": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.5 + "@babel/parser": ^7.22.5 + "@babel/types": ^7.22.5 debug: ^4.1.0 globals: ^11.1.0 - checksum: 5761837f9ce9b6ec2fe851ce76c6048d4fc11fc5be13218b7492849e42497ea957dafd2b84ab673aaabf31ac26ddc79c298d2a0fcff79ebdfc5c204cb35071a1 + checksum: 560931422dc1761f2df723778dcb4e51ce0d02e560cf2caa49822921578f49189a5a7d053b78a32dca33e59be886a6b2200a6e24d4ae9b5086ca0ba803815694 languageName: node linkType: hard -"@babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.5, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.4, @babel/types@npm:^7.21.5, @babel/types@npm:^7.22.0, @babel/types@npm:^7.22.3, @babel/types@npm:^7.22.4, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.22.4 - resolution: "@babel/types@npm:7.22.4" +"@babel/types@npm:^7.22.5, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.22.5 + resolution: "@babel/types@npm:7.22.5" dependencies: - "@babel/helper-string-parser": ^7.21.5 - "@babel/helper-validator-identifier": ^7.19.1 + "@babel/helper-string-parser": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.5 to-fast-properties: ^2.0.0 - checksum: ffe36bb4f4a99ad13c426a98c3b508d70736036cae4e471d9c862e3a579847ed4f480686af0fce2633f6f7c0f0d3bf02da73da36e7edd3fde0b2061951dcba9a + checksum: c13a9c1dc7d2d1a241a2f8363540cb9af1d66e978e8984b400a20c4f38ba38ca29f06e26a0f2d49a70bad9e57615dac09c35accfddf1bb90d23cd3e0a0bab892 languageName: node linkType: hard @@ -1455,9 +1450,9 @@ __metadata: languageName: node linkType: hard -"@codemirror/autocomplete@npm:6.7.1": - version: 6.7.1 - resolution: "@codemirror/autocomplete@npm:6.7.1" +"@codemirror/autocomplete@npm:6.8.1": + version: 6.8.1 + resolution: "@codemirror/autocomplete@npm:6.8.1" dependencies: "@codemirror/language": ^6.0.0 "@codemirror/state": ^6.0.0 @@ -1468,7 +1463,7 @@ __metadata: "@codemirror/state": ^6.0.0 "@codemirror/view": ^6.0.0 "@lezer/common": ^1.0.0 - checksum: 5f1331cceb6a7b0bd4dc9bad6025d16d1c415bfc2fba29b452f9f5501abd34b75a240e3a20fb9fed92a82a666e54562c32cfe220892713bfbd5a5a16fa004cd8 + checksum: 8599cd91defa3fea5276a7f9aff43ced323d9c4401dfb867e43608ba72ded48cb458256c5c784949a6332c0c20ba2fedac16a5708335cd809d269e4ea5076957 languageName: node linkType: hard @@ -1484,9 +1479,9 @@ __metadata: languageName: node linkType: hard -"@codemirror/language@npm:6.7.0, @codemirror/language@npm:^6.0.0": - version: 6.7.0 - resolution: "@codemirror/language@npm:6.7.0" +"@codemirror/language@npm:6.8.0, @codemirror/language@npm:^6.0.0": + version: 6.8.0 + resolution: "@codemirror/language@npm:6.8.0" dependencies: "@codemirror/state": ^6.0.0 "@codemirror/view": ^6.0.0 @@ -1494,7 +1489,7 @@ __metadata: "@lezer/highlight": ^1.0.0 "@lezer/lr": ^1.0.0 style-mod: ^4.0.0 - checksum: 673905e9eb80f039a5e6c59a8aeca217e124a9a03734848043192aeff9e5b3a82f150559f7bd637ee197c4b2171eb5b04e757d717922128ea4fecca1ac6ecac4 + checksum: 64408d996641931fa4c6b892e17ee1fdaee0f63d3d84c019a6ea7b1e6d1c774f92357b95c2ebaed60545062b795b72d0a058c03578b2bf4023c87726e97b5d2f languageName: node linkType: hard @@ -1507,14 +1502,14 @@ __metadata: languageName: node linkType: hard -"@codemirror/search@npm:6.4.0": - version: 6.4.0 - resolution: "@codemirror/search@npm:6.4.0" +"@codemirror/search@npm:6.5.0": + version: 6.5.0 + resolution: "@codemirror/search@npm:6.5.0" dependencies: "@codemirror/state": ^6.0.0 "@codemirror/view": ^6.0.0 crelt: ^1.0.5 - checksum: 441e04fc896ac984f224e3adb20bc8a6c63d929778335c70d2cb1e3843674c7998db93e2ab1cd05e8276cb3819766cd23951eec748fdf8e66e3611bd9a55aab5 + checksum: 2e9f2344b7dbd4bad79058c105d8cbd02b2bf94c27495310f0e3b6e999010aa080dceea47ef46e35439cc9e131b47c46f7d2eda700ef491b5f2f34bbc8e145ab languageName: node linkType: hard @@ -1525,14 +1520,14 @@ __metadata: languageName: node linkType: hard -"@codemirror/view@npm:6.12.0, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.6.0": - version: 6.12.0 - resolution: "@codemirror/view@npm:6.12.0" +"@codemirror/view@npm:6.14.0, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.6.0": + version: 6.14.0 + resolution: "@codemirror/view@npm:6.14.0" dependencies: "@codemirror/state": ^6.1.4 style-mod: ^4.0.0 w3c-keyname: ^2.2.4 - checksum: 512cbc9c05ac2cfa738cdf7aac711847b44e24ff5869f31839a9fcc11da6a512448fa9bc980535d55b897de80d49e744336a2724ca1fe3dae8bfcb31e339fe64 + checksum: f8fbb8e8cf1bc23de8cd64b1e645112d13f72cd2f1609fb9047d616908c2189ff518b89f21484371e7a37ba1804288452558e96488791f0c850f62b8e28dc163 languageName: node linkType: hard @@ -1587,144 +1582,155 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.42.0": - version: 8.42.0 - resolution: "@eslint/js@npm:8.42.0" - checksum: 750558843ac458f7da666122083ee05306fc087ecc1e5b21e7e14e23885775af6c55bcc92283dff1862b7b0d8863ec676c0f18c7faf1219c722fe91a8ece56b6 +"@eslint/js@npm:8.43.0": + version: 8.43.0 + resolution: "@eslint/js@npm:8.43.0" + checksum: 580487a09c82ac169744d36e4af77bc4f582c9a37749d1e9481eb93626c8f3991b2390c6e4e69e5642e3b6e870912b839229a0e23594fae348156ea5a8ed7e2e languageName: node linkType: hard -"@formatjs/ecma402-abstract@npm:1.15.0": - version: 1.15.0 - resolution: "@formatjs/ecma402-abstract@npm:1.15.0" +"@formatjs/ecma402-abstract@npm:1.17.0": + version: 1.17.0 + resolution: "@formatjs/ecma402-abstract@npm:1.17.0" dependencies: - "@formatjs/intl-localematcher": 0.2.32 + "@formatjs/intl-localematcher": 0.4.0 tslib: ^2.4.0 - checksum: c9feca174f9490026ef75b2de363d17fcac57848fb73bc8001a5c6c733db33a6674cdd506d69414067bd4ad670587f721d1e446b134e38e998b5f44b0c1412d3 + checksum: cc45d238e541076cb27b9cf02d8b97f789d1744b60218da6d31793204850c159e85f5b2557de3905a365eefd52a1c2e7f1febb9e1f009bad23d5eca17b3de6c8 languageName: node linkType: hard -"@formatjs/fast-memoize@npm:2.0.1": - version: 2.0.1 - resolution: "@formatjs/fast-memoize@npm:2.0.1" - dependencies: - tslib: ^2.4.0 - checksum: e434cdc53354666459c47556c403f0ed3391ebab0e851a64e5622d8d81e3b684a74a09c4bf5189885c66e743004601f64e2e2c8c70adf6b00071d4afea20f69d - languageName: node - linkType: hard - -"@formatjs/icu-messageformat-parser@npm:2.4.0": - version: 2.4.0 - resolution: "@formatjs/icu-messageformat-parser@npm:2.4.0" - dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - "@formatjs/icu-skeleton-parser": 1.4.0 - tslib: ^2.4.0 - checksum: 9bf9537b30e6f542a2f3d6763c6baf10010d3fc8e82a7a5a3899b1eaa38f3338ba9f59959fff5837bbd9154e44cf23e0f5503a969e80cce1fa57c2bb6c17ac22 - languageName: node - linkType: hard - -"@formatjs/icu-skeleton-parser@npm:1.4.0": - version: 1.4.0 - resolution: "@formatjs/icu-skeleton-parser@npm:1.4.0" - dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - tslib: ^2.4.0 - checksum: 00f016b4d9b446c395ec88d979baeaef97ed2006848b888ea0a6a44e08b875b7f16a2e4b54297161ecf7d8be64736ac4168c140ab42006b0b13274a955c0f26a - languageName: node - linkType: hard - -"@formatjs/intl-datetimeformat@npm:6.8.0": - version: 6.8.0 - resolution: "@formatjs/intl-datetimeformat@npm:6.8.0" - dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - "@formatjs/intl-localematcher": 0.2.32 - tslib: ^2.4.0 - checksum: 1ad7fb6d5a9eb8ae70758f5402c8e4dbdba6ff4282ca08e76cf3f7ed622b5d691343956cc9439dc8c5b4755f232714eb0c8ae25679fe550b36896fb873b00161 - languageName: node - linkType: hard - -"@formatjs/intl-displaynames@npm:6.3.2": - version: 6.3.2 - resolution: "@formatjs/intl-displaynames@npm:6.3.2" - dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - "@formatjs/intl-localematcher": 0.2.32 - tslib: ^2.4.0 - checksum: fa736c6c6bd40782a630ee9dc1bb36be2bffddfa66adcca554417f6ce5f9d4bf84422c1b47b308278f74b52b12f7e0f8fce064678b96299a7f9d9f1bb4f69b98 - languageName: node - linkType: hard - -"@formatjs/intl-enumerator@npm:1.3.0": - version: 1.3.0 - resolution: "@formatjs/intl-enumerator@npm:1.3.0" - dependencies: - tslib: ^2.4.0 - checksum: 99160b87e7b986528241e4eb4cb06f5523747ad3858e4d1ce0b4c787798cf3414f466a73e4420a8e94e5afc52de4f3e790a8ed91cb79790078f2359df7c4a12a - languageName: node - linkType: hard - -"@formatjs/intl-getcanonicallocales@npm:2.2.0": +"@formatjs/fast-memoize@npm:2.2.0": version: 2.2.0 - resolution: "@formatjs/intl-getcanonicallocales@npm:2.2.0" + resolution: "@formatjs/fast-memoize@npm:2.2.0" dependencies: tslib: ^2.4.0 - checksum: 71942361942a987703669bd7ce472ab68aa514e532bd019abb9fbb0636fbb2840447b90cf702ae800ccad428b32b3f2861140b172653b62549d417fc2d2d598e + checksum: 8697fe72a7ece252d600a7d08105f2a2f758e2dd96f54ac0a4c508b1205a559fc08835635e1f8e5ca9dcc3ee61ce1fca4a0e7047b402f29fc96051e293a280ff languageName: node linkType: hard -"@formatjs/intl-locale@npm:3.3.0": - version: 3.3.0 - resolution: "@formatjs/intl-locale@npm:3.3.0" +"@formatjs/icu-messageformat-parser@npm:2.6.0": + version: 2.6.0 + resolution: "@formatjs/icu-messageformat-parser@npm:2.6.0" dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - "@formatjs/intl-enumerator": 1.3.0 - "@formatjs/intl-getcanonicallocales": 2.2.0 + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/icu-skeleton-parser": 1.6.0 tslib: ^2.4.0 - checksum: 4932b2543291e1854c557045feaf20a6c339963ca2810bc1b08db3b1eb98534c058b96b7b2513d9f0ea92ad72bc14dc77c742fe2c79fd569782ec14617218685 + checksum: 67e76416a381663f62cb7ceaa699b3dc4505b9bfd8dda31950b8fa159e9abc1aae85d2ffa08760448083f113bdabca1653796e988b7a12eef891260726e56ed7 languageName: node linkType: hard -"@formatjs/intl-localematcher@npm:0.2.32": - version: 0.2.32 - resolution: "@formatjs/intl-localematcher@npm:0.2.32" +"@formatjs/icu-skeleton-parser@npm:1.6.0": + version: 1.6.0 + resolution: "@formatjs/icu-skeleton-parser@npm:1.6.0" dependencies: + "@formatjs/ecma402-abstract": 1.17.0 tslib: ^2.4.0 - checksum: 477e18aabaf2e6e90fc12952a3cb6c0ebb40ad99414d6b9d2501c6348fbad58cacb433ec6630955cfd1491ea7630f32a9dc280bb27d0fb8a784251404a54140a + checksum: e0a2e251358fb62cc45ad4783f57ed63db361334634557d5dcf3992b98c2c5409146d890b465321f91d2255eda5d8fdbfc9962e9f2e3fa7b10a738abfbe8ebc0 languageName: node linkType: hard -"@formatjs/intl-numberformat@npm:8.5.0": - version: 8.5.0 - resolution: "@formatjs/intl-numberformat@npm:8.5.0" +"@formatjs/intl-datetimeformat@npm:6.10.0": + version: 6.10.0 + resolution: "@formatjs/intl-datetimeformat@npm:6.10.0" dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - "@formatjs/intl-localematcher": 0.2.32 + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/intl-localematcher": 0.4.0 tslib: ^2.4.0 - checksum: 60a9b4f3740ac1d85e932a4585455142a4c152a70a0a2028ab37ddcfd18c3d753b9609ea6984a0d487ad303995485a50cfd6b7ca1db963b5434ca63d62ad0b60 + checksum: a0f915dd0d179dfeb0dfa585c7e939ef4b51a90cf04b9522d14de8acc9fb18e7af30898cc215c134955f31aa244d1f59ca677b9769fa9ee539148f6f52ceaa1d languageName: node linkType: hard -"@formatjs/intl-pluralrules@npm:5.2.2": - version: 5.2.2 - resolution: "@formatjs/intl-pluralrules@npm:5.2.2" +"@formatjs/intl-displaynames@npm:6.5.0": + version: 6.5.0 + resolution: "@formatjs/intl-displaynames@npm:6.5.0" dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - "@formatjs/intl-localematcher": 0.2.32 + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/intl-localematcher": 0.4.0 tslib: ^2.4.0 - checksum: 4fa72cdad265f19511d4728a15d40e6877d49f1e28ad7053eb566e696597a37e1111be8412b5927dffe792ed35c8fe5c7f3181ccb06b6f149cd75209cb14b967 + checksum: d071f8459796240575e9911052b7116a6e2e43687607b0a5d1ac8ceddbaa85324af78694226c37c8172c0f3e7d6b793f506c5758b6bc50b2110516902b532e12 languageName: node linkType: hard -"@formatjs/intl-relativetimeformat@npm:11.2.2": - version: 11.2.2 - resolution: "@formatjs/intl-relativetimeformat@npm:11.2.2" +"@formatjs/intl-enumerator@npm:1.3.2": + version: 1.3.2 + resolution: "@formatjs/intl-enumerator@npm:1.3.2" dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - "@formatjs/intl-localematcher": 0.2.32 tslib: ^2.4.0 - checksum: 6c9376140bb58193e1c2788a2559af24c150c39fe16249199719ee63474fdb113a2974252514e61120d9ad6daa5d174b442c30af85c3740f1e90f3c822448d8d + checksum: 37435354fcab9f69390402b1bcc917e83d0e35869cd5a06b584ca84ef62b179a4b94b9ee94febb59ab503a9d1587babcdcbebc2d4ece146fbc02bef41f722314 + languageName: node + linkType: hard + +"@formatjs/intl-getcanonicallocales@npm:2.2.1": + version: 2.2.1 + resolution: "@formatjs/intl-getcanonicallocales@npm:2.2.1" + dependencies: + tslib: ^2.4.0 + checksum: 9842f21308ee7cc46bf5a7f54c3ae66ebcaf107226ea43c11571eab0d8b36fdfa81521bf3210a4f385e2ca3302d9a61378e375946a930687278432bc482c9f8e + languageName: node + linkType: hard + +"@formatjs/intl-listformat@npm:7.4.0": + version: 7.4.0 + resolution: "@formatjs/intl-listformat@npm:7.4.0" + dependencies: + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/intl-localematcher": 0.4.0 + tslib: ^2.4.0 + checksum: a2deed31cce57f249e470f54675286c36edc2f5ec1d63a2f36e6315a0154ca06404fa5c00aaefc2c52af57d3d471b17c217e885cc9565e5f54c36509af37fe12 + languageName: node + linkType: hard + +"@formatjs/intl-locale@npm:3.3.2": + version: 3.3.2 + resolution: "@formatjs/intl-locale@npm:3.3.2" + dependencies: + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/intl-enumerator": 1.3.2 + "@formatjs/intl-getcanonicallocales": 2.2.1 + tslib: ^2.4.0 + checksum: 41d2ab272ba759671cfa1720b2c43f8e261694f68d114ff976f2b9749ac3afad2181aa1b16799740082aa05bedd6f23b0ab67c65f48bfa0097b42582789e9a4d + languageName: node + linkType: hard + +"@formatjs/intl-localematcher@npm:0.4.0": + version: 0.4.0 + resolution: "@formatjs/intl-localematcher@npm:0.4.0" + dependencies: + tslib: ^2.4.0 + checksum: c65108e9a81c3733d2b6240ceedc846d0ae59c3606041cb5cc71c13453cdabe295b0dc8559dc4a8acaafdc45876807bd5e9ef37a3ec1cb864e78db655d434b66 + languageName: node + linkType: hard + +"@formatjs/intl-numberformat@npm:8.7.0": + version: 8.7.0 + resolution: "@formatjs/intl-numberformat@npm:8.7.0" + dependencies: + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/intl-localematcher": 0.4.0 + tslib: ^2.4.0 + checksum: 987bd3e6a48e6efa054d9f526b3cba01675a6c96ffc6d652f6053f3dd1cbae5e56ac784718d135a2e2cd6c7afab4f89c0a592a28e2ff93134baeddc1fa1aff30 + languageName: node + linkType: hard + +"@formatjs/intl-pluralrules@npm:5.2.4": + version: 5.2.4 + resolution: "@formatjs/intl-pluralrules@npm:5.2.4" + dependencies: + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/intl-localematcher": 0.4.0 + tslib: ^2.4.0 + checksum: a55445724a65c7c82896792babb2522a03774f3dd370bda2c8a76103528f71e101be84636ae4b161f68fc68afb63f1e61a3bf7489c07ffa722c8213199113814 + languageName: node + linkType: hard + +"@formatjs/intl-relativetimeformat@npm:11.2.4": + version: 11.2.4 + resolution: "@formatjs/intl-relativetimeformat@npm:11.2.4" + dependencies: + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/intl-localematcher": 0.4.0 + tslib: ^2.4.0 + checksum: 8a99abd4fa7a4dec6d46140940709d83c094f4dce5e331f0556e72d75f375f6758d3fcfaee9e78cd313dcded17fd6788814a4a0bee542e83ec74cef71d35e2bf languageName: node linkType: hard @@ -1775,13 +1781,6 @@ __metadata: languageName: node linkType: hard -"@gar/promisify@npm:^1.1.3": - version: 1.1.3 - resolution: "@gar/promisify@npm:1.1.3" - checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 - languageName: node - linkType: hard - "@gfx/zopfli@npm:^1.0.15": version: 1.0.15 resolution: "@gfx/zopfli@npm:1.0.15" @@ -1988,7 +1987,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/source-map@npm:^0.3.2": +"@jridgewell/source-map@npm:^0.3.3": version: 0.3.3 resolution: "@jridgewell/source-map@npm:0.3.3" dependencies: @@ -2039,9 +2038,9 @@ __metadata: linkType: hard "@lezer/common@npm:^1.0.0": - version: 1.0.2 - resolution: "@lezer/common@npm:1.0.2" - checksum: bbcc58e07be02652bf0700d2856042ec089d5be0b95893d628b3e18192ade864fac83b61b19653e10b9f1472261a178b12318d934e9004edd5483a577c0db56b + version: 1.0.3 + resolution: "@lezer/common@npm:1.0.3" + checksum: cc90dc2f0aeaebeb3fe886cbd27f8b1e8bee817d8c2efff178604807debd68c5db820fd23afb830962780063d21891afbdf564420221faca2822e77bc6327184 languageName: node linkType: hard @@ -2055,21 +2054,21 @@ __metadata: linkType: hard "@lezer/lr@npm:^1.0.0": - version: 1.3.4 - resolution: "@lezer/lr@npm:1.3.4" + version: 1.3.6 + resolution: "@lezer/lr@npm:1.3.6" dependencies: "@lezer/common": ^1.0.0 - checksum: 58bc25a9ba891dc6ca713fc8768706935e65d6e54d79a8ddb40c742cc799e87eddf4f49a6d6566a649c4726a9ab79a4200d36c9351608285a9bee6cdf3b33341 + checksum: b2bbcfecc01bd9c801f3ee636ceda333adbbea1f274017cec6f315a23346e7a035a984f325d4f1cd14b157d74d28badda6f794514c29a0b078f7fb3357cdfc32 languageName: node linkType: hard -"@lit-labs/context@npm:0.3.2": - version: 0.3.2 - resolution: "@lit-labs/context@npm:0.3.2" +"@lit-labs/context@npm:0.3.3": + version: 0.3.3 + resolution: "@lit-labs/context@npm:0.3.3" dependencies: "@lit/reactive-element": ^1.5.0 lit: ^2.7.0 - checksum: 55920366798a3337a455c627c0b6911c7b78dee94a783ad77edb9a9e237a2e48201d6cf869f3d0b805316e5c8e8fb817f52f663bc556dd40ca6e8b3168662daf + checksum: 3607a7f965f22072feeef8db791e37be45921d9168ea3f432e160cb1fb337de19b2b41a2cd8db5d4fd2675d704d567a24695b796d0b14590616e9232f27579d3 languageName: node linkType: hard @@ -2089,31 +2088,31 @@ __metadata: languageName: node linkType: hard -"@lit-labs/virtualizer@npm:2.0.2": - version: 2.0.2 - resolution: "@lit-labs/virtualizer@npm:2.0.2" +"@lit-labs/virtualizer@npm:2.0.3": + version: 2.0.3 + resolution: "@lit-labs/virtualizer@npm:2.0.3" dependencies: lit: ^2.7.0 tslib: ^2.0.3 - checksum: 419aedf72f2b08f8fda43d9729810d5c7f34f470484bd9dff2df49d42cc56e57fcdfd98f69dd84e582d07fd2f372b90077cf42e12c4464b2c04c83755eebb495 + checksum: 594b89aca53210a6c0127c331fd05b795074df41aba086b63cb13ad5990e6962b86ca8403fe3a673e3bf46735e2def75d5412afe582702346fbd92a3331d34e1 languageName: node linkType: hard "@lit/reactive-element@npm:^1.3.0, @lit/reactive-element@npm:^1.5.0, @lit/reactive-element@npm:^1.6.0": - version: 1.6.1 - resolution: "@lit/reactive-element@npm:1.6.1" + version: 1.6.2 + resolution: "@lit/reactive-element@npm:1.6.2" dependencies: "@lit-labs/ssr-dom-shim": ^1.0.0 - checksum: fab0bcfdade9c26af2ad5115fb564bcf8ba0732a3a8be86c157851c2771e3fdc65ab38f2cd60fa121946058bf6e487461fd217f87b01f96e88ee7a95d5d866ba + checksum: 011a3ef0933fda86ec726d29ebc14e829e2f1ba23eca8ed8ed4d5c6f2a102c55cc6986000c5f4c8c3d0c549bc671f5d84d00ce91adc5bbd95970eec3662c0a92 languageName: node linkType: hard -"@lrnwebcomponents/simple-tooltip@npm:7.0.2": - version: 7.0.2 - resolution: "@lrnwebcomponents/simple-tooltip@npm:7.0.2" +"@lrnwebcomponents/simple-tooltip@npm:7.0.5": + version: 7.0.5 + resolution: "@lrnwebcomponents/simple-tooltip@npm:7.0.5" dependencies: - lit: ^2.7.4 - checksum: 2f8642c7b503e6093d01b5fcadea461de062c9380df6454f04a08e5b8f4ef36e0cbd52a9f0842238554f6881802895fb442f21c0a4b6345bebd99b43f18a366c + lit: ^2.7.5 + checksum: 57a50ed30be103f8fbe37fbef33bb4071f8c2bbecba6964643404532883f3479fe034ee64b1d71d11d7ea60ee63e39bb5ef5f53dc0b9c88d6ec525627135602b languageName: node linkType: hard @@ -3142,14 +3141,14 @@ __metadata: languageName: node linkType: hard -"@material/web@npm:=1.0.0-pre.9": - version: 1.0.0-pre.9 - resolution: "@material/web@npm:1.0.0-pre.9" +"@material/web@npm:=1.0.0-pre.11": + version: 1.0.0-pre.11 + resolution: "@material/web@npm:1.0.0-pre.11" dependencies: lit: ^2.7.4 safevalues: ^0.4.3 tslib: ^2.4.0 - checksum: c4c6a5328448ee55a6f68260bbadecda2cabd031bf81502934fff95422e63b6903f9737e35ae3cd7e87f6f146a13dc3f492e92fc4e3cef3caee8d0fd4e177a7a + checksum: 6c08d5d1b159472032d8a274eb29c22229dbcd29a80c478bbf680906903a9542dab9916d547e40c3b607a0ecfe51484bc8681b8c2f1fcd521b2b20fe23d84a66 languageName: node linkType: hard @@ -3167,16 +3166,6 @@ __metadata: languageName: node linkType: hard -"@mrmlnc/readdir-enhanced@npm:^2.2.1": - version: 2.2.1 - resolution: "@mrmlnc/readdir-enhanced@npm:2.2.1" - dependencies: - call-me-maybe: ^1.0.1 - glob-to-regexp: ^0.3.0 - checksum: d3b82b29368821154ce8e10bef5ccdbfd070d3e9601643c99ea4607e56f3daeaa4e755dd6d2355da20762c695c1b0570543d9f84b48f70c211ec09c4aaada2e1 - languageName: node - linkType: hard - "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -3194,13 +3183,6 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.stat@npm:^1.1.2": - version: 1.1.3 - resolution: "@nodelib/fs.stat@npm:1.1.3" - checksum: 318deab369b518a34778cdaa0054dd28a4381c0c78e40bbd20252f67d084b1d7bf9295fea4423de2c19ac8e1a34f120add9125f481b2a710f7068bcac7e3e305 - languageName: node - linkType: hard - "@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" @@ -3211,50 +3193,37 @@ __metadata: languageName: node linkType: hard -"@npmcli/fs@npm:^2.1.0": - version: 2.1.2 - resolution: "@npmcli/fs@npm:2.1.2" +"@npmcli/fs@npm:^3.1.0": + version: 3.1.0 + resolution: "@npmcli/fs@npm:3.1.0" dependencies: - "@gar/promisify": ^1.1.3 semver: ^7.3.5 - checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225 + checksum: a50a6818de5fc557d0b0e6f50ec780a7a02ab8ad07e5ac8b16bf519e0ad60a144ac64f97d05c443c3367235d337182e1d012bbac0eb8dbae8dc7b40b193efd0e languageName: node linkType: hard -"@npmcli/move-file@npm:^2.0.0": - version: 2.0.1 - resolution: "@npmcli/move-file@npm:2.0.1" +"@octokit/auth-oauth-device@npm:5.0.2": + version: 5.0.2 + resolution: "@octokit/auth-oauth-device@npm:5.0.2" dependencies: - mkdirp: ^1.0.4 - rimraf: ^3.0.2 - checksum: 52dc02259d98da517fae4cb3a0a3850227bdae4939dda1980b788a7670636ca2b4a01b58df03dd5f65c1e3cb70c50fa8ce5762b582b3f499ec30ee5ce1fd9380 - languageName: node - linkType: hard - -"@octokit/auth-oauth-device@npm:4.0.4": - version: 4.0.4 - resolution: "@octokit/auth-oauth-device@npm:4.0.4" - dependencies: - "@octokit/oauth-methods": ^2.0.0 - "@octokit/request": ^6.0.0 - "@octokit/types": ^9.0.0 + "@octokit/oauth-methods": ^3.0.1 + "@octokit/request": ^7.0.0 + "@octokit/types": ^10.0.0 universal-user-agent: ^6.0.0 - checksum: 245e1fd77684d131793a52a45133ad2514dbb14e5bb6af99eeaccff58662b567327196a721e296ef5734c0988e85a7536ef22bbf04613b302eba595995c5c751 + checksum: b625a2d7604351e52df46d3fdad04d1eb2ec68f80bce065047691ea83044967ef1e7dd0a70e9f8aab818d8c5ecf7f2550d2aa029ffdba85e0ff8c0ce2e25736a languageName: node linkType: hard "@octokit/auth-token@npm:^3.0.0": - version: 3.0.3 - resolution: "@octokit/auth-token@npm:3.0.3" - dependencies: - "@octokit/types": ^9.0.0 - checksum: 9b3f569cec1b7e0aa88ab6da68aed4b49b6652261bd957257541fabaf6a4d4ed99f908153cc3dd2fe15b8b0ccaff8caaafaa50bb1a4de3925b0954a47cca1900 + version: 3.0.4 + resolution: "@octokit/auth-token@npm:3.0.4" + checksum: 42f533a873d4192e6df406b3176141c1f95287423ebdc4cf23a38bb77ee00ccbc0e60e3fbd5874234fc2ed2e67bbc6035e3b0561dacc1d078adb5c4ced3579e3 languageName: node linkType: hard "@octokit/core@npm:^4.2.1": - version: 4.2.1 - resolution: "@octokit/core@npm:4.2.1" + version: 4.2.4 + resolution: "@octokit/core@npm:4.2.4" dependencies: "@octokit/auth-token": ^3.0.0 "@octokit/graphql": ^5.0.0 @@ -3263,56 +3232,67 @@ __metadata: "@octokit/types": ^9.0.0 before-after-hook: ^2.2.0 universal-user-agent: ^6.0.0 - checksum: f82d52e937e12da1c7c163341c845b8e27e7fa75678f5e5954e6fa017a94f1833d6e5c4e43f0be796fbfea9dc5e1137087f655dbd5acb3d57879e1b28568e0a9 + checksum: ac8ab47440a31b0228a034aacac6994b64d6b073ad5b688b4c5157fc5ee0d1af1c926e6087bf17fd7244ee9c5998839da89065a90819bde4a97cb77d4edf58a6 languageName: node linkType: hard "@octokit/endpoint@npm:^7.0.0": - version: 7.0.5 - resolution: "@octokit/endpoint@npm:7.0.5" + version: 7.0.6 + resolution: "@octokit/endpoint@npm:7.0.6" dependencies: "@octokit/types": ^9.0.0 is-plain-object: ^5.0.0 universal-user-agent: ^6.0.0 - checksum: 81c9e9eabf50e48940cceff7c4d7fbc9327190296507cfe8a199ea00cd492caf8f18a841caf4e3619828924b481996eb16091826db6b5a649bee44c8718ecaa9 + checksum: 7caebf30ceec50eb7f253341ed419df355232f03d4638a95c178ee96620400db7e4a5e15d89773fe14db19b8653d4ab4cc81b2e93ca0c760b4e0f7eb7ad80301 + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^8.0.0": + version: 8.0.1 + resolution: "@octokit/endpoint@npm:8.0.1" + dependencies: + "@octokit/types": ^10.0.0 + is-plain-object: ^5.0.0 + universal-user-agent: ^6.0.0 + checksum: 0cff7c972d8304cb59c4cc28016c15bca05e6d7e9e2d9b00af88ce05bf9abdfdb17025f38080162a71ea15b21c740bcb5079361396f18a24bbe55134c504a581 languageName: node linkType: hard "@octokit/graphql@npm:^5.0.0": - version: 5.0.5 - resolution: "@octokit/graphql@npm:5.0.5" + version: 5.0.6 + resolution: "@octokit/graphql@npm:5.0.6" dependencies: "@octokit/request": ^6.0.0 "@octokit/types": ^9.0.0 universal-user-agent: ^6.0.0 - checksum: eb2d1a6305a3d1f55ff0ce92fb88b677f0bb789757152d58a79ef61171fb65ecf6fe18d6c27e236c0cee6a0c2600c2cb8370f5ac7184f8e9361c085aa4555bb1 + checksum: 7be545d348ef31dcab0a2478dd64d5746419a2f82f61459c774602bcf8a9b577989c18001f50b03f5f61a3d9e34203bdc021a4e4d75ff2d981e8c9c09cf8a65c languageName: node linkType: hard -"@octokit/oauth-authorization-url@npm:^5.0.0": - version: 5.0.0 - resolution: "@octokit/oauth-authorization-url@npm:5.0.0" - checksum: bc457c4af9559e9e8f752e643fc9d116247f4e4246e69959d99b9e39196c93d7af53c1c8e3bd946bd0e4fc29f7ba27efe9bced8525ffa41fe45ef56a8281014b +"@octokit/oauth-authorization-url@npm:^6.0.2": + version: 6.0.2 + resolution: "@octokit/oauth-authorization-url@npm:6.0.2" + checksum: 0f11169a3eeb782cc08312c923de1a702b25ae033b972ba40380b6d72cb3f684543c8b6a5cf6f05936fdc6b8892070d4f7581138d8efc1b4c4a55ae6d7762327 languageName: node linkType: hard -"@octokit/oauth-methods@npm:^2.0.0": - version: 2.0.5 - resolution: "@octokit/oauth-methods@npm:2.0.5" +"@octokit/oauth-methods@npm:^3.0.1": + version: 3.0.1 + resolution: "@octokit/oauth-methods@npm:3.0.1" dependencies: - "@octokit/oauth-authorization-url": ^5.0.0 - "@octokit/request": ^6.2.3 - "@octokit/request-error": ^3.0.3 - "@octokit/types": ^9.0.0 + "@octokit/oauth-authorization-url": ^6.0.2 + "@octokit/request": ^7.0.0 + "@octokit/request-error": ^4.0.1 + "@octokit/types": ^10.0.0 btoa-lite: ^1.0.0 - checksum: dda36fdc85f1b9a4322e47d88b13183f86fd7e03e66e787ced7be41ea4a1a64f610e2d1a75f82c7ec7ff5c4a76e07a696c2e7232bf944f04fe64aac5c26bd484 + checksum: ad327084f97d2f3be270d8957545dbd06c35df3e99d8e58702217beb7ac3574c361b49dfe28ba5d96b7f1911ac9c8e26ae07d6180a0598eef8b7fab4b0fe4ad5 languageName: node linkType: hard -"@octokit/openapi-types@npm:^17.2.0": - version: 17.2.0 - resolution: "@octokit/openapi-types@npm:17.2.0" - checksum: 29995e34f98d9d64ba234d64a7ae9486c66d2bb6ac0d30d9a42decdbb4b03b13e811769b1e1505a1748ff20c22d35724985e6c128cd11a3f14f8322201520093 +"@octokit/openapi-types@npm:^18.0.0": + version: 18.0.0 + resolution: "@octokit/openapi-types@npm:18.0.0" + checksum: d487d6c6c1965e583eee417d567e4fe3357a98953fc49bce1a88487e7908e9b5dbb3e98f60dfa340e23b1792725fbc006295aea071c5667a813b9c098185b56f languageName: node linkType: hard @@ -3338,30 +3318,30 @@ __metadata: linkType: hard "@octokit/plugin-rest-endpoint-methods@npm:^7.1.2": - version: 7.1.2 - resolution: "@octokit/plugin-rest-endpoint-methods@npm:7.1.2" + version: 7.2.3 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:7.2.3" dependencies: - "@octokit/types": ^9.2.3 - deprecation: ^2.3.1 + "@octokit/types": ^10.0.0 peerDependencies: "@octokit/core": ">=3" - checksum: 159d29bf28d7aecbe39f08c25cf376d39b6c90ce17e50a55eafb44f3e4b9e1053a300c1edd72f308ae386146a17cbad46c410c1cfd000b048adf9c21d6922a1a + checksum: 21dfb98514dbe900c29cddb13b335bbce43d613800c6b17eba3c1fd31d17e69c1960f3067f7bf864bb38fdd5043391f4a23edee42729d8c7fbabd00569a80336 languageName: node linkType: hard -"@octokit/plugin-retry@npm:5.0.0": - version: 5.0.0 - resolution: "@octokit/plugin-retry@npm:5.0.0" +"@octokit/plugin-retry@npm:5.0.4": + version: 5.0.4 + resolution: "@octokit/plugin-retry@npm:5.0.4" dependencies: - "@octokit/types": ^9.0.0 + "@octokit/request-error": ^4.0.1 + "@octokit/types": ^10.0.0 bottleneck: ^2.15.3 peerDependencies: "@octokit/core": ">=3" - checksum: 3ffea3da379f724f476ba053de3b43c6509684c33cd7a5015b936ea77324cf5801259e20867ab5dccee07af6c23000ccb13c924ce4eef1f4d28e69e4c9056beb + checksum: 0c5645613f7ff758ac126da11ba20b4d49e4067676e30808f5ee3ee471adbd2ccfdea2200adfa5a4663b207964b3d60987f4c5e8682fb275bf134b33f2ef5178 languageName: node linkType: hard -"@octokit/request-error@npm:^3.0.0, @octokit/request-error@npm:^3.0.3": +"@octokit/request-error@npm:^3.0.0": version: 3.0.3 resolution: "@octokit/request-error@npm:3.0.3" dependencies: @@ -3372,9 +3352,20 @@ __metadata: languageName: node linkType: hard -"@octokit/request@npm:^6.0.0, @octokit/request@npm:^6.2.3": - version: 6.2.5 - resolution: "@octokit/request@npm:6.2.5" +"@octokit/request-error@npm:^4.0.1": + version: 4.0.2 + resolution: "@octokit/request-error@npm:4.0.2" + dependencies: + "@octokit/types": ^10.0.0 + deprecation: ^2.0.0 + once: ^1.4.0 + checksum: 9510078f718be08cf74e7b04b45f67aa545a388787192ff1cd3ca0d066499963d641bf1fea76ef47d2657a55f0cf3e612e46bbf4f33a96436415d30a2e3bb00a + languageName: node + linkType: hard + +"@octokit/request@npm:^6.0.0": + version: 6.2.8 + resolution: "@octokit/request@npm:6.2.8" dependencies: "@octokit/endpoint": ^7.0.0 "@octokit/request-error": ^3.0.0 @@ -3382,19 +3373,32 @@ __metadata: is-plain-object: ^5.0.0 node-fetch: ^2.6.7 universal-user-agent: ^6.0.0 - checksum: 856451ea8cc6b1dd0f6e350a141e65c318b5e3a25b8dea373d3afd115f9a3077535a0330f5d90e9db81dc3234dba1dd64edd31e68f639553baa10b4d02b99498 + checksum: 3747106f50d7c462131ff995b13defdd78024b7becc40283f4ac9ea0af2391ff33a0bb476a05aa710346fe766d20254979079a1d6f626112015ba271fe38f3e2 languageName: node linkType: hard -"@octokit/rest@npm:19.0.11": - version: 19.0.11 - resolution: "@octokit/rest@npm:19.0.11" +"@octokit/request@npm:^7.0.0": + version: 7.0.0 + resolution: "@octokit/request@npm:7.0.0" + dependencies: + "@octokit/endpoint": ^8.0.0 + "@octokit/request-error": ^4.0.1 + "@octokit/types": ^10.0.0 + is-plain-object: ^5.0.0 + universal-user-agent: ^6.0.0 + checksum: d3b8ac25c3702bb69c5b345f7a9f16b158209db7e244cc2d60dbcbfbaf1edec8252d78885de3607ee85eb86db7c1d2e07fa2515ba6e25cf2880440c0df5e918a + languageName: node + linkType: hard + +"@octokit/rest@npm:19.0.13": + version: 19.0.13 + resolution: "@octokit/rest@npm:19.0.13" dependencies: "@octokit/core": ^4.2.1 "@octokit/plugin-paginate-rest": ^6.1.2 "@octokit/plugin-request-log": ^1.0.4 "@octokit/plugin-rest-endpoint-methods": ^7.1.2 - checksum: 147518ad51d214ead88adc717b5fdc4f33317949d58c124f4069bdf07d2e6b49fa66861036b9e233aed71fcb88ff367a6da0357653484e466175ab4fb7183b3b + checksum: ca1553e3fe46efabffef60e68e4a228d4cc0f0d545daf7f019560f666d3e934c6f3a6402a42bbd786af4f3c0a6e69380776312f01b7d52998fe1bbdd1b068f69 languageName: node linkType: hard @@ -3405,19 +3409,28 @@ __metadata: languageName: node linkType: hard -"@octokit/types@npm:^9.0.0, @octokit/types@npm:^9.2.3": - version: 9.2.3 - resolution: "@octokit/types@npm:9.2.3" +"@octokit/types@npm:^10.0.0": + version: 10.0.0 + resolution: "@octokit/types@npm:10.0.0" dependencies: - "@octokit/openapi-types": ^17.2.0 - checksum: 6806413089f20a8302237ef85aa2e83bace7499e95fdc3db2d304f9e6dc6e87fb6766452f92e08ddf475046b69753e11beabaeff6733c38bdaf3e21dfd7d3341 + "@octokit/openapi-types": ^18.0.0 + checksum: 8aafba2ff0cd2435fb70c291bf75ed071c0fa8a865cf6169648732068a35dec7b85a345851f18920ec5f3e94ee0e954988485caac0da09ec3f6781cc44fe153a + languageName: node + linkType: hard + +"@octokit/types@npm:^9.0.0, @octokit/types@npm:^9.2.3": + version: 9.3.2 + resolution: "@octokit/types@npm:9.3.2" + dependencies: + "@octokit/openapi-types": ^18.0.0 + checksum: f55d096aaed3e04b8308d4422104fb888f355988056ba7b7ef0a4c397b8a3e54290d7827b06774dbe0c9ce55280b00db486286954f9c265aa6b03091026d9da8 languageName: node linkType: hard "@open-wc/dedupe-mixin@npm:^1.3.0": - version: 1.3.1 - resolution: "@open-wc/dedupe-mixin@npm:1.3.1" - checksum: f0771906242cfd506531f3de65d44dbb069b0b61388612f91ad16b91838d216347d71144e4718dfaf9890a33ab0c7b625b0e6d979e09a288f81acb09f239f55d + version: 1.4.0 + resolution: "@open-wc/dedupe-mixin@npm:1.4.0" + checksum: 25ae5d1c76ffecec74cb38948ee093e8b9fcfdde274ad176e288070370c4fe8182880d50e42b0403ac5449f12a9ebfa508df42772b15910c19177a0a63f47010 languageName: node linkType: hard @@ -3865,9 +3878,9 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-commonjs@npm:25.0.0": - version: 25.0.0 - resolution: "@rollup/plugin-commonjs@npm:25.0.0" +"@rollup/plugin-commonjs@npm:25.0.2": + version: 25.0.2 + resolution: "@rollup/plugin-commonjs@npm:25.0.2" dependencies: "@rollup/pluginutils": ^5.0.1 commondir: ^1.0.1 @@ -3880,7 +3893,7 @@ __metadata: peerDependenciesMeta: rollup: optional: true - checksum: f014f98f161ba4573e5101cf262c152bdcd8cfebf9cf4872f8bb657d20e20d8e2dc51af5339fe10772d19007e63044bd79881af0db4fe9babe9e96d0c0eeb328 + checksum: d8c4e22d264c5b9286f697653c2f9288149c0fe0169d49b83b5dc8b542f96cc0b9113da3f30d241f2f277513fbf3f8c207d95def5559eb3800190f1872add794 languageName: node linkType: hard @@ -4023,12 +4036,12 @@ __metadata: languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^10.0.2, @sinonjs/fake-timers@npm:^10.2.0": - version: 10.2.0 - resolution: "@sinonjs/fake-timers@npm:10.2.0" +"@sinonjs/fake-timers@npm:^10.0.2, @sinonjs/fake-timers@npm:^10.3.0": + version: 10.3.0 + resolution: "@sinonjs/fake-timers@npm:10.3.0" dependencies: "@sinonjs/commons": ^3.0.0 - checksum: 586c76e1dd90d03b0c4e754f2011325b38ac6055878c81c52434c900f36d9d245438c96ef69e08e28d9fbecf2335fb347b67850962d8b6e539dd7359d8c62802 + checksum: 614d30cb4d5201550c940945d44c9e0b6d64a888ff2cd5b357f95ad6721070d6b8839cd10e15b76bf5e14af0bcc1d8f9ec00d49a46318f1f669a4bec1d7f3148 languageName: node linkType: hard @@ -4122,12 +4135,12 @@ __metadata: linkType: hard "@types/chrome@npm:*": - version: 0.0.236 - resolution: "@types/chrome@npm:0.0.236" + version: 0.0.237 + resolution: "@types/chrome@npm:0.0.237" dependencies: "@types/filesystem": "*" "@types/har-format": "*" - checksum: 64cc6f8ac5c23f5dc6a3e1137721dda52e6401b1e1a8f9d16bfca7236bd5a118be6746324fd9e33630d98863370137ff0749b9949ededb9f46accb187f333896 + checksum: 9dfb0070065ca4667ef2fa9ec45af2f604b9ba98b6429d38607c6bfcc8dc2178e83eea4eeedbe88a8299dfe9dda029c9341e454c4eff7b715b90e9d5fc7b990d languageName: node linkType: hard @@ -4203,12 +4216,12 @@ __metadata: linkType: hard "@types/eslint@npm:*": - version: 8.37.0 - resolution: "@types/eslint@npm:8.37.0" + version: 8.40.2 + resolution: "@types/eslint@npm:8.40.2" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: 06d3b3fba12004294591b5c7a52e3cec439472195da54e096076b1f2ddfbb8a445973b9681046dd530a6ac31eca502f635abc1e3ce37d03513089358e6f822ee + checksum: a4780e45e677e3af21c44a900846996cb6d9ae8f71d51940942a047163ae93a05444392c005f491ed46aa169f3b25f8be125ab42c5d8bdb571154bf62a7c828a languageName: node linkType: hard @@ -4300,9 +4313,9 @@ __metadata: linkType: hard "@types/har-format@npm:*": - version: 1.2.10 - resolution: "@types/har-format@npm:1.2.10" - checksum: 14c0118d809e77a0bac9deec0a87159c28beab21105cfce3aa291b51e28d4c07142851c4e6b93b7ecb4ea237fe07d39ba98a42bb2de2f5891144733411ac76b7 + version: 1.2.11 + resolution: "@types/har-format@npm:1.2.11" + checksum: f36add1ac253c99b594231783cc9ca75b6226df60ba2924351dcad4d0fcd0d7e62188c530a981b4dad8c2a9fcf10ea312f8c75ebc7ddbfae7e1a979635b04780 languageName: node linkType: hard @@ -4344,9 +4357,9 @@ __metadata: linkType: hard "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": - version: 7.0.11 - resolution: "@types/json-schema@npm:7.0.11" - checksum: 527bddfe62db9012fccd7627794bd4c71beb77601861055d87e3ee464f2217c85fca7a4b56ae677478367bbd248dbde13553312b7d4dbc702a2f2bbf60c4018d + version: 7.0.12 + resolution: "@types/json-schema@npm:7.0.12" + checksum: 00239e97234eeb5ceefb0c1875d98ade6e922bfec39dd365ec6bd360b5c2f825e612ac4f6e5f1d13601b8b30f378f15e6faa805a3a732f4a1bbe61915163d293 languageName: node linkType: hard @@ -4389,12 +4402,12 @@ __metadata: languageName: node linkType: hard -"@types/leaflet-draw@npm:1.0.6": - version: 1.0.6 - resolution: "@types/leaflet-draw@npm:1.0.6" +"@types/leaflet-draw@npm:1.0.7": + version: 1.0.7 + resolution: "@types/leaflet-draw@npm:1.0.7" dependencies: "@types/leaflet": "*" - checksum: 6520de61f26f4f845fea3faa43077515d2eac58274e98d61b26ca0885d9002fdeb131375e85f4e503daa6b4ec27f0837a1ecddacbba78aea4764084a424bf93e + checksum: 8b2ab83fea3ae95cd8ce652eeb00386f14a3be5d94df96f5f5cb46054e228fbac179751b4e3d35347bcde7fa01dccbe1255601f44a0ec41f16bd1e51b3d8d5a2 languageName: node linkType: hard @@ -4417,9 +4430,9 @@ __metadata: linkType: hard "@types/lodash@npm:*": - version: 4.14.194 - resolution: "@types/lodash@npm:4.14.194" - checksum: 113f34831c461469d91feca2dde737f88487732898b4d25e9eb23b087bb193985f864d1e1e0f3b777edc5022e460443588b6000a3b2348c966f72d17eedc35ea + version: 4.14.195 + resolution: "@types/lodash@npm:4.14.195" + checksum: 39b75ca635b3fa943d17d3d3aabc750babe4c8212485a4df166fe0516e39288e14b0c60afc6e21913cc0e5a84734633c71e617e2bd14eaa1cf51b8d7799c432e languageName: node linkType: hard @@ -4459,9 +4472,9 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 20.2.1 - resolution: "@types/node@npm:20.2.1" - checksum: ed774afa6e9b4ad7868ed0182a8ca40ad0dd54815a70d3051b23fa850f3bca6bea4d0cb55e1fc769666786ac2cc4c1b37aeade313cb4c4634133f18ebcded496 + version: 20.3.1 + resolution: "@types/node@npm:20.3.1" + checksum: 63a393ab6d947be17320817b35d7277ef03728e231558166ed07ee30b09fd7c08861be4d746f10fdc63ca7912e8cd023939d4eab887ff6580ff704ff24ed810c languageName: node linkType: hard @@ -4649,23 +4662,23 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.1": - version: 8.5.4 - resolution: "@types/ws@npm:8.5.4" +"@types/ws@npm:^8.5.5": + version: 8.5.5 + resolution: "@types/ws@npm:8.5.5" dependencies: "@types/node": "*" - checksum: fefbad20d211929bb996285c4e6f699b12192548afedbe4930ab4384f8a94577c9cd421acaad163cacd36b88649509970a05a0b8f20615b30c501ed5269038d1 + checksum: d00bf8070e6938e3ccf933010921c6ce78ac3606696ce37a393b27a9a603f7bd93ea64f3c5fa295a2f743575ba9c9a9fdb904af0f5fe2229bf2adf0630386e4a languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:5.59.8": - version: 5.59.8 - resolution: "@typescript-eslint/eslint-plugin@npm:5.59.8" +"@typescript-eslint/eslint-plugin@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.60.0" dependencies: "@eslint-community/regexpp": ^4.4.0 - "@typescript-eslint/scope-manager": 5.59.8 - "@typescript-eslint/type-utils": 5.59.8 - "@typescript-eslint/utils": 5.59.8 + "@typescript-eslint/scope-manager": 5.60.0 + "@typescript-eslint/type-utils": 5.60.0 + "@typescript-eslint/utils": 5.60.0 debug: ^4.3.4 grapheme-splitter: ^1.0.4 ignore: ^5.2.0 @@ -4678,43 +4691,43 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 3e05cd06149ec3741c3c2fb638e2d19a55687b4614a5c8820433db82997687650297e51c17828d320162ccf4241798cf5712c405561e7605cb17e984a6967f7b + checksum: 61dd70a1ea9787e69d0d4cd14f6a4c94ba786b535a3f519ade7926d965ee1d4f8fefa8bf0224ee57c5c6517eec3674c0fd06f9226536aa428c2bdddeed1e70f4 languageName: node linkType: hard -"@typescript-eslint/parser@npm:5.59.8": - version: 5.59.8 - resolution: "@typescript-eslint/parser@npm:5.59.8" +"@typescript-eslint/parser@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/parser@npm:5.60.0" dependencies: - "@typescript-eslint/scope-manager": 5.59.8 - "@typescript-eslint/types": 5.59.8 - "@typescript-eslint/typescript-estree": 5.59.8 + "@typescript-eslint/scope-manager": 5.60.0 + "@typescript-eslint/types": 5.60.0 + "@typescript-eslint/typescript-estree": 5.60.0 debug: ^4.3.4 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: bac9f09d8552086ceb882a7b87ce4d98dfaa41579249216c75d97e3fc07af33cddc4cbbd07a127a5823c826a258882643aaf658bec19cb2a434002b55c5f0d12 + checksum: 94e7931a5b356b16638b281b8e1d661f8b1660f0c75a323537f68b311dae91b7a575a0a019d4ea05a79cc5d42b5cb41cc367205691cdfd292ef96a3b66b1e58b languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.59.8": - version: 5.59.8 - resolution: "@typescript-eslint/scope-manager@npm:5.59.8" +"@typescript-eslint/scope-manager@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/scope-manager@npm:5.60.0" dependencies: - "@typescript-eslint/types": 5.59.8 - "@typescript-eslint/visitor-keys": 5.59.8 - checksum: e1e810ee991cfeb433330b04ee949bb6784abe4dbdb7d9480aa7a7536671b4fec914b7803edf662516c8ecb1b31dcff126797f9923270a529c26e2b00b0ea96f + "@typescript-eslint/types": 5.60.0 + "@typescript-eslint/visitor-keys": 5.60.0 + checksum: b21ee1ef57be948a806aa31fd65a9186766b3e1a727030dc47025edcadc54bd1aa6133a439acd5f44a93e2b983dd55bc5571bb01cb834461dab733682d66256a languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.59.8": - version: 5.59.8 - resolution: "@typescript-eslint/type-utils@npm:5.59.8" +"@typescript-eslint/type-utils@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/type-utils@npm:5.60.0" dependencies: - "@typescript-eslint/typescript-estree": 5.59.8 - "@typescript-eslint/utils": 5.59.8 + "@typescript-eslint/typescript-estree": 5.60.0 + "@typescript-eslint/utils": 5.60.0 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -4722,23 +4735,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: d9fde31397da0f0e62a5568f64bad99d06bcd324b7e3aac7fd997a3d045a0fe4c084b2e85d440e0a39645acd2269ad6593f196399c2c0f880d293417fec894e3 + checksum: b90ce97592f2db899d88d7a325fec4d2ea11a7b8b4306787310890c27fb51862a6c003675252e9dc465908f791ad5320ea7307260ecd10e89ca1d209fbf8616d languageName: node linkType: hard -"@typescript-eslint/types@npm:5.59.8": - version: 5.59.8 - resolution: "@typescript-eslint/types@npm:5.59.8" - checksum: 559473d5601c849eb0da1874a2ac67c753480beed484ad6f6cda62fa6023273f2c3005c7f2864d9c2afb7c6356412d0d304b57db10c53597207f18a7f6cd4f18 +"@typescript-eslint/types@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/types@npm:5.60.0" + checksum: 48f29e5c084c5663cfed1a6c4458799a6690a213e7861a24501f9b96698ae59e89a1df1c77e481777e4da78f1b0a5573a549f7b8880e3f4071a7a8b686254db8 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.59.8": - version: 5.59.8 - resolution: "@typescript-eslint/typescript-estree@npm:5.59.8" +"@typescript-eslint/typescript-estree@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.60.0" dependencies: - "@typescript-eslint/types": 5.59.8 - "@typescript-eslint/visitor-keys": 5.59.8 + "@typescript-eslint/types": 5.60.0 + "@typescript-eslint/visitor-keys": 5.60.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -4747,142 +4760,158 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: d93371cc866f573a6a1ddc0eb10d498a8e59f36763a99ce21da0737fff2b4c942eef1587216aad273f8d896ebc0b19003677cba63a27d2646aa2c087638963eb + checksum: 0f4f342730ead42ba60b5fca4bf1950abebd83030010c38b5df98ff9fd95d0ce1cfc3974a44c90c65f381f4f172adcf1a540e018d7968cc845d937bf6c734dae languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.59.8": - version: 5.59.8 - resolution: "@typescript-eslint/utils@npm:5.59.8" +"@typescript-eslint/utils@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/utils@npm:5.60.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@types/json-schema": ^7.0.9 "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.59.8 - "@typescript-eslint/types": 5.59.8 - "@typescript-eslint/typescript-estree": 5.59.8 + "@typescript-eslint/scope-manager": 5.60.0 + "@typescript-eslint/types": 5.60.0 + "@typescript-eslint/typescript-estree": 5.60.0 eslint-scope: ^5.1.1 semver: ^7.3.7 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: cbaa057485c7f52c45d0dfb4f5a8e9273abccb1c52dcb4426a79f9e71d2c1062cf2525bad6d4aca5ec42db3fe723d749843bcade5a323bde7fbe4b5d5b5d5c3b + checksum: cbe56567f0b53e24ad7ef7d2fb4cdc8596e2559c21ee639aa0560879b6216208550e51e9d8ae4b388ff21286809c6dc985cec66738294871051396a8ae5bccbc languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.59.8": - version: 5.59.8 - resolution: "@typescript-eslint/visitor-keys@npm:5.59.8" +"@typescript-eslint/visitor-keys@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.60.0" dependencies: - "@typescript-eslint/types": 5.59.8 + "@typescript-eslint/types": 5.60.0 eslint-visitor-keys: ^3.3.0 - checksum: 6bfa7918dbb0e08d8a7404aeeef7bcd1a85736dc8d01614d267c0c5ec10f94d2746b50a945bf5c82c54fda67926e8deaeba8565c919da17f725fc11209ef8987 + checksum: d39b2485d030f9755820d0f6f3748a8ec44e1ca23cb36ddcba67a9eb1f258c8ec83c61fc015c50e8f4a00d05df62d719dbda445625e3e71a64a659f1d248157e languageName: node linkType: hard -"@vaadin/combo-box@npm:24.0.8": - version: 24.0.8 - resolution: "@vaadin/combo-box@npm:24.0.8" +"@vaadin/a11y-base@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/a11y-base@npm:24.1.1" dependencies: "@open-wc/dedupe-mixin": ^1.3.0 "@polymer/polymer": ^3.0.0 - "@vaadin/component-base": ~24.0.8 - "@vaadin/field-base": ~24.0.8 - "@vaadin/input-container": ~24.0.8 - "@vaadin/item": ~24.0.8 - "@vaadin/lit-renderer": ~24.0.8 - "@vaadin/overlay": ~24.0.8 - "@vaadin/vaadin-lumo-styles": ~24.0.8 - "@vaadin/vaadin-material-styles": ~24.0.8 - "@vaadin/vaadin-themable-mixin": ~24.0.8 - checksum: 14bea4c273a96393e8cd3338f0aee6da417026d5712adc82e4dc7f2964e9ccb3b338f950788bc22ec7e7805ababb98f624d44415d969788c0ea896cdc7480084 + "@vaadin/component-base": ~24.1.1 + lit: ^2.0.0 + checksum: 1b83e3e44ebc8c395a5ba9a6bc92d42aeb6afce3bcd6a1c492bbc7eb166a228474bf7dc56fb6509d47618fdd87107c60e4ebc60d2aab3c72dcc1392465fd7ac1 languageName: node linkType: hard -"@vaadin/component-base@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/component-base@npm:24.0.8" +"@vaadin/combo-box@npm:24.1.1": + version: 24.1.1 + resolution: "@vaadin/combo-box@npm:24.1.1" + dependencies: + "@open-wc/dedupe-mixin": ^1.3.0 + "@polymer/polymer": ^3.0.0 + "@vaadin/a11y-base": ~24.1.1 + "@vaadin/component-base": ~24.1.1 + "@vaadin/field-base": ~24.1.1 + "@vaadin/input-container": ~24.1.1 + "@vaadin/item": ~24.1.1 + "@vaadin/lit-renderer": ~24.1.1 + "@vaadin/overlay": ~24.1.1 + "@vaadin/vaadin-lumo-styles": ~24.1.1 + "@vaadin/vaadin-material-styles": ~24.1.1 + "@vaadin/vaadin-themable-mixin": ~24.1.1 + checksum: 0011a1271ebe41c6eaf5d5cf5f15c7cfdfab46534c70b4bdbcc0b1afdd6e83e0c7983d3927db6df4c7669269d7909a3d886586696b9d75d291a6a7876db22ce6 + languageName: node + linkType: hard + +"@vaadin/component-base@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/component-base@npm:24.1.1" dependencies: "@open-wc/dedupe-mixin": ^1.3.0 "@polymer/polymer": ^3.0.0 "@vaadin/vaadin-development-mode-detector": ^2.0.0 "@vaadin/vaadin-usage-statistics": ^2.1.0 lit: ^2.0.0 - checksum: 6406ee2f2bdca164a15480f870611caa91f4b5cfd083de2b838ddf2e6438415d1fce236edebfc8509057ee64f1f1c59712af49e2b41d938a6fa6bc6593b0b00e + checksum: 34c698266897cf7c3c5f8b8798468f5035ae764b1743e6a93f5ea1921b3d29e642db51e4d6d71c6594ba03dee14fa0704b34ccb70b7f50ecbfd07677bde231ac languageName: node linkType: hard -"@vaadin/field-base@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/field-base@npm:24.0.8" +"@vaadin/field-base@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/field-base@npm:24.1.1" dependencies: "@open-wc/dedupe-mixin": ^1.3.0 "@polymer/polymer": ^3.0.0 - "@vaadin/component-base": ~24.0.8 + "@vaadin/a11y-base": ~24.1.1 + "@vaadin/component-base": ~24.1.1 lit: ^2.0.0 - checksum: 7603960e86e705ead7bddaa24252cf733b43b8f08a4b0d4f7c7941f01a2c45fc6a8a50d98ccc49a50a5e1e40b7bbd6327db6762a6fa449a3b31c2934d46f64b0 + checksum: b16d5579d4a5f43a62df431b7e7e3107bf5a8062ad681b6ce0c1c345dc56ce4f0ae4f0909e2e9ff0fb05d9f2f4b54e12f17b75a680c69b9f24f8f82b63c0b234 languageName: node linkType: hard -"@vaadin/icon@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/icon@npm:24.0.8" +"@vaadin/icon@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/icon@npm:24.1.1" dependencies: "@polymer/polymer": ^3.0.0 - "@vaadin/component-base": ~24.0.8 - "@vaadin/vaadin-lumo-styles": ~24.0.8 - "@vaadin/vaadin-themable-mixin": ~24.0.8 + "@vaadin/component-base": ~24.1.1 + "@vaadin/vaadin-lumo-styles": ~24.1.1 + "@vaadin/vaadin-themable-mixin": ~24.1.1 lit: ^2.0.0 - checksum: bfec0d0048649817dd40f5aec2ad3c94d51557161c5af5f04aebadf56d6546b34a5b29013008207e71deaa4ccf9aad0b5a82beb00541f246cb0707396b72e327 + checksum: be3f8986e04f163791c0fdbc51c5d7c8074b12548f151b58a3f357ab639cc5c0c53b3375ded7936cd8c618df19dcfef4c616735b24e81da6ae1fca753d4c0774 languageName: node linkType: hard -"@vaadin/input-container@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/input-container@npm:24.0.8" +"@vaadin/input-container@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/input-container@npm:24.1.1" dependencies: "@polymer/polymer": ^3.0.0 - "@vaadin/component-base": ~24.0.8 - "@vaadin/vaadin-lumo-styles": ~24.0.8 - "@vaadin/vaadin-material-styles": ~24.0.8 - "@vaadin/vaadin-themable-mixin": ~24.0.8 - checksum: d323b2e5b74c72107a9cb6f833bd20b09b2f419d2fbac45136acd535e50ac78d216779ed56aaf510fc73d28677e850d2ed2588f44189289687625cb51a0617d2 + "@vaadin/component-base": ~24.1.1 + "@vaadin/vaadin-lumo-styles": ~24.1.1 + "@vaadin/vaadin-material-styles": ~24.1.1 + "@vaadin/vaadin-themable-mixin": ~24.1.1 + checksum: b8934fae0f5578b78f4ee05c506b59e66c247e3fdf6d42b1ba7d198d77af170907b1f3cd98ee5ce7bd540d0f5c1f4c08d8febdfa35f7231ed56d289b0eb7432b languageName: node linkType: hard -"@vaadin/item@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/item@npm:24.0.8" +"@vaadin/item@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/item@npm:24.1.1" dependencies: "@open-wc/dedupe-mixin": ^1.3.0 "@polymer/polymer": ^3.0.0 - "@vaadin/component-base": ~24.0.8 - "@vaadin/vaadin-lumo-styles": ~24.0.8 - "@vaadin/vaadin-material-styles": ~24.0.8 - "@vaadin/vaadin-themable-mixin": ~24.0.8 - checksum: 7d2ebcd7b9a04dd093c22fdf4e5fe8e37f96c5bafd480f4cd69471d12d675d8fd966f3cea2b82f3fb7bd3f77a1d1f05cc07d7799345df0885ad3cea63d579c20 + "@vaadin/a11y-base": ~24.1.1 + "@vaadin/component-base": ~24.1.1 + "@vaadin/vaadin-lumo-styles": ~24.1.1 + "@vaadin/vaadin-material-styles": ~24.1.1 + "@vaadin/vaadin-themable-mixin": ~24.1.1 + checksum: a87529f5c0c385920d36173b670afbfaaa83d0c171cea048daf7986fbdfb7d82cfef651bc806043ca007a61c1f92b47bba489240b0917d15c75ad49fabab2153 languageName: node linkType: hard -"@vaadin/lit-renderer@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/lit-renderer@npm:24.0.8" +"@vaadin/lit-renderer@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/lit-renderer@npm:24.1.1" dependencies: lit: ^2.0.0 - checksum: d624c779e2b4682923322cb952255490b5f58f392bfd24121fffc347f8cb89be9e90de760c24e846bc98e0f9bb76b658ed99a5446dfaa8f81e61270f8adcf06f + checksum: 17a22abce1654c9b6c86e8a113778d61d5780e45164d3362741b00f47e061cfd88521127802f16ce2ad3ba92ed1535829c8b154183cc6f4fbececdbbb70f4233 languageName: node linkType: hard -"@vaadin/overlay@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/overlay@npm:24.0.8" +"@vaadin/overlay@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/overlay@npm:24.1.1" dependencies: "@open-wc/dedupe-mixin": ^1.3.0 "@polymer/polymer": ^3.0.0 - "@vaadin/component-base": ~24.0.8 - "@vaadin/vaadin-lumo-styles": ~24.0.8 - "@vaadin/vaadin-material-styles": ~24.0.8 - "@vaadin/vaadin-themable-mixin": ~24.0.8 - checksum: f274cfd0cd2bd0f2a75b0f4f494e1d82e0947d7faaa8cb791801c0b2aa90020113ece50fa9736dc59015436ea07c053261efa096ca39ed458c072a7fb98b8110 + "@vaadin/a11y-base": ~24.1.1 + "@vaadin/component-base": ~24.1.1 + "@vaadin/vaadin-lumo-styles": ~24.1.1 + "@vaadin/vaadin-material-styles": ~24.1.1 + "@vaadin/vaadin-themable-mixin": ~24.1.1 + checksum: d0def2106e4fff7d7c49931e9b917c68994f371a0246e076442e33d97ac7a25341d9794aee9e41c7c05c94111dacac74cb5648f1814fb45f11cf37ffe6850fa1 languageName: node linkType: hard @@ -4893,34 +4922,34 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-lumo-styles@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/vaadin-lumo-styles@npm:24.0.8" +"@vaadin/vaadin-lumo-styles@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/vaadin-lumo-styles@npm:24.1.1" dependencies: "@polymer/polymer": ^3.0.0 - "@vaadin/icon": ~24.0.8 - "@vaadin/vaadin-themable-mixin": ~24.0.8 - checksum: 351516cf34d95d3800f82d067ef8c539d67a8cde4c0becbc5bc2467f805999c3ba66d9ba3e493aff3708cde3684be937ed766828af7ad956c9ec48f6209650f7 + "@vaadin/icon": ~24.1.1 + "@vaadin/vaadin-themable-mixin": ~24.1.1 + checksum: ab344ce558de8f1075de6290517169bd3e95cf5038549b987ca7cfb14a9798ca12573e099958fecd518dd74f2bcfa76030dfc5d3b6e46b34dff10e4d675182c5 languageName: node linkType: hard -"@vaadin/vaadin-material-styles@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/vaadin-material-styles@npm:24.0.8" +"@vaadin/vaadin-material-styles@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/vaadin-material-styles@npm:24.1.1" dependencies: "@polymer/polymer": ^3.0.0 - "@vaadin/vaadin-themable-mixin": ~24.0.8 - checksum: 667719adb4efbf9544025a2c59887a359321190e15c5cd902f17c53b7138a23720ddbf0986b2315884163c92e61c1847419844b604651efb0118bc985af47645 + "@vaadin/vaadin-themable-mixin": ~24.1.1 + checksum: 601e345da8858a62804b1afde06a79f93e9b5c41fcc263bb692b6db167937e3dd1b754b502ceeaf4054586d3e04a68a167ba7bc2719ec4ad8ac10005795d9a6e languageName: node linkType: hard -"@vaadin/vaadin-themable-mixin@npm:24.0.8, @vaadin/vaadin-themable-mixin@npm:~24.0.8": - version: 24.0.8 - resolution: "@vaadin/vaadin-themable-mixin@npm:24.0.8" +"@vaadin/vaadin-themable-mixin@npm:24.1.1, @vaadin/vaadin-themable-mixin@npm:~24.1.1": + version: 24.1.1 + resolution: "@vaadin/vaadin-themable-mixin@npm:24.1.1" dependencies: "@open-wc/dedupe-mixin": ^1.3.0 lit: ^2.0.0 - checksum: 5ffcacd49f2f42e01fee55033375ebcf46e93abcb2c7df02b460b635dfb3d7bef5629e7fdafb0813027a4239d6e11ff1ddca31f9281abf5a5f08db8c5418807d + checksum: 5066300dcf6c987e43bb9c2e16d75198188220dfbde0c76d3d875444200f05b4de70d50fd3d082c0ac0b5075953835d1499ed01d51236e06a56d5ca9e6d25e4c languageName: node linkType: hard @@ -5043,6 +5072,13 @@ __metadata: languageName: node linkType: hard +"@vscode/web-custom-data@npm:^0.4.2": + version: 0.4.6 + resolution: "@vscode/web-custom-data@npm:0.4.6" + checksum: 2d87f3e50dc6eeacdbbca224f1c8837154acd6e1588f733ff812423e72f0a14d819740bbb91732a9c09978faa0fcfe1be339a17fd0beb130e51784752cef4b4f + languageName: node + linkType: hard + "@vue/compiler-sfc@npm:2.7.14": version: 2.7.14 resolution: "@vue/compiler-sfc@npm:2.7.14" @@ -5326,36 +5362,36 @@ __metadata: languageName: node linkType: hard -"@webpack-cli/configtest@npm:^2.1.0": - version: 2.1.0 - resolution: "@webpack-cli/configtest@npm:2.1.0" +"@webpack-cli/configtest@npm:^2.1.1": + version: 2.1.1 + resolution: "@webpack-cli/configtest@npm:2.1.1" peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x - checksum: b875fccd8be9a936924e24986725823347703e3eb72ea884e74669ca20f007704e859855a6a05940d5d3805ce2fc08b183a0f1658d5395b5454b3f5f88293081 + checksum: 9f9f9145c2d05471fc83d426db1df85cf49f329836b0c4b9f46b6948bed4b013464c00622b136d2a0a26993ce2306976682592245b08ee717500b1db45009a72 languageName: node linkType: hard -"@webpack-cli/info@npm:^2.0.1": - version: 2.0.1 - resolution: "@webpack-cli/info@npm:2.0.1" +"@webpack-cli/info@npm:^2.0.2": + version: 2.0.2 + resolution: "@webpack-cli/info@npm:2.0.2" peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x - checksum: b8fba49fee10d297c2affb0b064c9a81e9038d75517c6728fb85f9fb254cae634e5d33e696dac5171e6944ae329d85fddac72f781c7d833f7e9dfe43151ce60d + checksum: 8f9a178afca5c82e113aed1efa552d64ee5ae4fdff63fe747c096a981ec74f18a5d07bd6e89bbe6715c3e57d96eea024a410e58977169489fe1df044c10dd94e languageName: node linkType: hard -"@webpack-cli/serve@npm:^2.0.4": - version: 2.0.4 - resolution: "@webpack-cli/serve@npm:2.0.4" +"@webpack-cli/serve@npm:^2.0.5": + version: 2.0.5 + resolution: "@webpack-cli/serve@npm:2.0.5" peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x peerDependenciesMeta: webpack-dev-server: optional: true - checksum: 561ea2e6eb551415f0b1675393a8480e1201293fe37eae334cbb1fdc466986668cca76ca1ca327ada9b498eae27cbecef0793e3bb5677288f1a5216cad414efe + checksum: 75f0e54681796d567a71ac3e2781d2901a8d8cf1cdfc82f261034dddac59a8343e8c3bc5e32b4bb9d6766759ba49fb29a5cd86ef1701d79c506fe886bb63ac75 languageName: node linkType: hard @@ -5408,12 +5444,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.5.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0": - version: 8.8.2 - resolution: "acorn@npm:8.8.2" +"acorn@npm:^8.5.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.2": + version: 8.9.0 + resolution: "acorn@npm:8.9.0" bin: acorn: bin/acorn - checksum: f790b99a1bf63ef160c967e23c46feea7787e531292bb827126334612c234ed489a0dc2c7ba33156416f0ffa8d25bf2b0fdb7f35c2ba60eb3e960572bece4001 + checksum: 25dfb94952386ecfb847e61934de04a4e7c2dc21c2e700fc4e2ef27ce78cb717700c4c4f279cd630bb4774948633c3859fc16063ec8573bda4568e0a312e6744 languageName: node linkType: hard @@ -5716,11 +5752,11 @@ __metadata: linkType: hard "aria-query@npm:^5.1.3": - version: 5.1.3 - resolution: "aria-query@npm:5.1.3" + version: 5.2.1 + resolution: "aria-query@npm:5.2.1" dependencies: - deep-equal: ^2.0.5 - checksum: 929ff95f02857b650fb4cbcd2f41072eee2f46159a6605ea03bf63aa572e35ffdff43d69e815ddc462e16e07de8faba3978afc2813650b4448ee18c9895d982b + dequal: ^2.0.3 + checksum: fdb7a337d97acf4dae831e4c2c786233aca5ccb779a02c10fe65a65af9849f6e9868073593313ab52b7b0d9817e05cfb22a5cd43ecf22a8e7f2abea2268bdac9 languageName: node linkType: hard @@ -6023,9 +6059,9 @@ __metadata: linkType: hard "axe-core@npm:^4.3.3": - version: 4.7.1 - resolution: "axe-core@npm:4.7.1" - checksum: ff6fb92d6cadb749977af72b7d28009dec2b1842d4fdc4114a295ce973a39d0ac477e541be360eb9482a8d63f55840196813d7d892c0bd8437f52d9f7349c788 + version: 4.7.2 + resolution: "axe-core@npm:4.7.2" + checksum: 5d86fa0f45213b0e54cbb5d713ce885c4a8fe3a72b92dd915a47aa396d6fd149c4a87fec53aa978511f6d941402256cfeb26f2db35129e370f25a453c688655a languageName: node linkType: hard @@ -6312,16 +6348,16 @@ __metadata: linkType: hard "browserslist@npm:^4.14.5, browserslist@npm:^4.21.3, browserslist@npm:^4.21.5": - version: 4.21.5 - resolution: "browserslist@npm:4.21.5" + version: 4.21.9 + resolution: "browserslist@npm:4.21.9" dependencies: - caniuse-lite: ^1.0.30001449 - electron-to-chromium: ^1.4.284 - node-releases: ^2.0.8 - update-browserslist-db: ^1.0.10 + caniuse-lite: ^1.0.30001503 + electron-to-chromium: ^1.4.431 + node-releases: ^2.0.12 + update-browserslist-db: ^1.0.11 bin: browserslist: cli.js - checksum: 9755986b22e73a6a1497fd8797aedd88e04270be33ce66ed5d85a1c8a798292a65e222b0f251bafa1c2522261e237d73b08b58689d4920a607e5a53d56dc4706 + checksum: 80d3820584e211484ad1b1a5cfdeca1dd00442f47be87e117e1dda34b628c87e18b81ae7986fa5977b3e6a03154f6d13cd763baa6b8bf5dd9dd19f4926603698 languageName: node linkType: hard @@ -6393,29 +6429,23 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^16.1.0": - version: 16.1.3 - resolution: "cacache@npm:16.1.3" +"cacache@npm:^17.0.0": + version: 17.1.3 + resolution: "cacache@npm:17.1.3" dependencies: - "@npmcli/fs": ^2.1.0 - "@npmcli/move-file": ^2.0.0 - chownr: ^2.0.0 - fs-minipass: ^2.1.0 - glob: ^8.0.1 - infer-owner: ^1.0.4 + "@npmcli/fs": ^3.1.0 + fs-minipass: ^3.0.0 + glob: ^10.2.2 lru-cache: ^7.7.1 - minipass: ^3.1.6 + minipass: ^5.0.0 minipass-collect: ^1.0.2 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 - mkdirp: ^1.0.4 p-map: ^4.0.0 - promise-inflight: ^1.0.1 - rimraf: ^3.0.2 - ssri: ^9.0.0 + ssri: ^10.0.0 tar: ^6.1.11 - unique-filename: ^2.0.0 - checksum: d91409e6e57d7d9a3a25e5dcc589c84e75b178ae8ea7de05cbf6b783f77a5fae938f6e8fda6f5257ed70000be27a681e1e44829251bfffe4c10216002f8f14e6 + unique-filename: ^3.0.0 + checksum: 385756781e1e21af089160d89d7462b7ed9883c978e848c7075b90b73cb823680e66092d61513050164588387d2ca87dd6d910e28d64bc13a9ac82cd8580c796 languageName: node linkType: hard @@ -6456,13 +6486,6 @@ __metadata: languageName: node linkType: hard -"call-me-maybe@npm:^1.0.1": - version: 1.0.2 - resolution: "call-me-maybe@npm:1.0.2" - checksum: 42ff2d0bed5b207e3f0122589162eaaa47ba618f79ad2382fe0ba14d9e49fbf901099a6227440acc5946f86a4953e8aa2d242b330b0a5de4d090bb18f8935cae - languageName: node - linkType: hard - "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -6501,10 +6524,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001449": - version: 1.0.30001488 - resolution: "caniuse-lite@npm:1.0.30001488" - checksum: ef0caf2914f9fca700b75d22921f500241f4e988ded9985e62737136031787052185d8136a65a3a6d6d12b559cf75ab99f5488931f8bd060f1b7810a2c1ee1d1 +"caniuse-lite@npm:^1.0.30001503": + version: 1.0.30001504 + resolution: "caniuse-lite@npm:1.0.30001504" + checksum: 0256f8ef2f5d6d1559198967d7325952e6451e79ff1b92d3d6ba1ec43efedf49fcd3fbb0735ebed0bfd96c6c6a49730e169535e273c60795d23ef25bd37e3e3d languageName: node linkType: hard @@ -7098,18 +7121,18 @@ __metadata: linkType: hard "core-js-compat@npm:^3.30.1, core-js-compat@npm:^3.30.2": - version: 3.30.2 - resolution: "core-js-compat@npm:3.30.2" + version: 3.31.0 + resolution: "core-js-compat@npm:3.31.0" dependencies: browserslist: ^4.21.5 - checksum: 4c81d635559eebc2f81db60f5095a235f580a2f90698113c4124c72761393592b139e30974cce6095a9a6aad6bb3cd467b24b20c32e77ed24ca74eb5944d0638 + checksum: 5c76ac5e4ab39480391f93a5aef14a2cfa188cda7bd6a7b8532de1f8bc5d89099a5025b2640d2ef70a2928614792363dcbcf8bd254aa7b2e11b85aeed7ac460f languageName: node linkType: hard -"core-js@npm:3.30.2": - version: 3.30.2 - resolution: "core-js@npm:3.30.2" - checksum: 73d47e2b9d9f502800973982d08e995bbf04832e20b04e04be31dd7607247158271315e9328788a2408190e291c7ffbefad141167b1e57dea9f983e1e723541e +"core-js@npm:3.31.0": + version: 3.31.0 + resolution: "core-js@npm:3.31.0" + checksum: f7cf9b3010f7ca99c026d95b61743baca1a85512742ed2b67e8f65a72ac4f4fe0b90b00057783e886bdd39d3a295f42f845d33e7cba3973ed263df978343ab79 languageName: node linkType: hard @@ -7268,32 +7291,6 @@ __metadata: languageName: node linkType: hard -"deep-equal@npm:^2.0.5": - version: 2.2.1 - resolution: "deep-equal@npm:2.2.1" - dependencies: - array-buffer-byte-length: ^1.0.0 - call-bind: ^1.0.2 - es-get-iterator: ^1.1.3 - get-intrinsic: ^1.2.0 - is-arguments: ^1.1.1 - is-array-buffer: ^3.0.2 - is-date-object: ^1.0.5 - is-regex: ^1.1.4 - is-shared-array-buffer: ^1.0.2 - isarray: ^2.0.5 - object-is: ^1.1.5 - object-keys: ^1.1.1 - object.assign: ^4.1.4 - regexp.prototype.flags: ^1.5.0 - side-channel: ^1.0.4 - which-boxed-primitive: ^1.0.2 - which-collection: ^1.0.1 - which-typed-array: ^1.1.9 - checksum: 561f0e001a07b2f1b80ff914d0b3d76964bbfc102f34c2128bc8039c0050e63b1a504a8911910e011d8cd1cd4b600a9686c049e327f4ef94420008efc42d25f4 - languageName: node - linkType: hard - "deep-equal@npm:~1.0.1": version: 1.0.1 resolution: "deep-equal@npm:1.0.1" @@ -7467,13 +7464,20 @@ __metadata: languageName: node linkType: hard -"deprecation@npm:^2.0.0, deprecation@npm:^2.3.1": +"deprecation@npm:^2.0.0": version: 2.3.1 resolution: "deprecation@npm:2.3.1" checksum: f56a05e182c2c195071385455956b0c4106fe14e36245b00c689ceef8e8ab639235176a96977ba7c74afb173317fac2e0ec6ec7a1c6d1e6eaa401c586c714132 languageName: node linkType: hard +"dequal@npm:^2.0.3": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90 + languageName: node + linkType: hard + "destroy@npm:1.2.0, destroy@npm:^1.0.4": version: 1.2.0 resolution: "destroy@npm:1.2.0" @@ -7645,10 +7649,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.284": - version: 1.4.402 - resolution: "electron-to-chromium@npm:1.4.402" - checksum: d2e6473921df875169d58e6317bcb64f1d88127fbd841799218720458e1e6d30e5a3abaac97d20c5bf02c3c2246f3cc701d6503713e3f14ff656a87068489d1e +"electron-to-chromium@npm:^1.4.431": + version: 1.4.433 + resolution: "electron-to-chromium@npm:1.4.433" + checksum: 106e3bc2fb4ee5eddd4b141363900d5cd731c7579aa6bebd02509c52d6b598a1684aba1b75791e838dfa54dec0a40ddd17ea01199041ea46310aafb206395e43 languageName: node linkType: hard @@ -7716,13 +7720,13 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.14.1": - version: 5.14.1 - resolution: "enhanced-resolve@npm:5.14.1" +"enhanced-resolve@npm:^5.15.0": + version: 5.15.0 + resolution: "enhanced-resolve@npm:5.15.0" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: ad2a31928b6649eed40d364838449587f731baa63863e83d2629bebaa8be1eabac18b90f89c1784bc805b0818363e99b22547159edd485d7e5ccf18cdc640642 + checksum: fbd8cdc9263be71cc737aa8a7d6c57b43d6aa38f6cc75dde6fcd3598a130cc465f979d2f4d01bb3bf475acb43817749c79f8eef9be048683602ca91ab52e4f11 languageName: node linkType: hard @@ -7807,27 +7811,10 @@ __metadata: languageName: node linkType: hard -"es-get-iterator@npm:^1.1.3": - version: 1.1.3 - resolution: "es-get-iterator@npm:1.1.3" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 - has-symbols: ^1.0.3 - is-arguments: ^1.1.1 - is-map: ^2.0.2 - is-set: ^2.0.2 - is-string: ^1.0.7 - isarray: ^2.0.5 - stop-iteration-iterator: ^1.0.0 - checksum: 8fa118da42667a01a7c7529f8a8cca514feeff243feec1ce0bb73baaa3514560bd09d2b3438873cf8a5aaec5d52da248131de153b28e2638a061b6e4df13267d - languageName: node - linkType: hard - "es-module-lexer@npm:^1.0.0, es-module-lexer@npm:^1.2.1": - version: 1.2.1 - resolution: "es-module-lexer@npm:1.2.1" - checksum: c4145b853e1491eaa5d591e4580926d242978c38071ad3d09165c3b6d50314cc0ae3bf6e1dec81a9e53768b9299df2063d2e4a67d7742a5029ddeae6c4fc26f0 + version: 1.3.0 + resolution: "es-module-lexer@npm:1.3.0" + checksum: 48fd9f504a9d2a894126f75c8b7ccc6273a289983e9b67255f165bfd9ae765d50100218251e94e702ca567826905ea2f7b3b4a0c4d74d3ce99cce3a2a606a238 languageName: node linkType: hard @@ -8062,9 +8049,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-lit-a11y@npm:2.4.1": - version: 2.4.1 - resolution: "eslint-plugin-lit-a11y@npm:2.4.1" +"eslint-plugin-lit-a11y@npm:3.0.0": + version: 3.0.0 + resolution: "eslint-plugin-lit-a11y@npm:3.0.0" dependencies: aria-query: ^5.1.3 axe-core: ^4.3.3 @@ -8079,7 +8066,7 @@ __metadata: requireindex: ~1.2.0 peerDependencies: eslint: ">= 5" - checksum: 1e3caf3dbe16545bbdde096dea1c6ac47b1c366ac7014ccda392c6a62faecaf8c066b5a2f22e7f39c2618a4f14b91158bffbcb48c7f305ce92769f0e9e9bb257 + checksum: c7836aa88714a2fc0388216d44b8a1251edadc6a540cdc054cb4951b56eaa5a017cb51cc37ed8f33e79e8e139cb0c9208ee488af100f76e31c381b66488fc783 languageName: node linkType: hard @@ -8164,14 +8151,14 @@ __metadata: languageName: node linkType: hard -"eslint@npm:8.42.0": - version: 8.42.0 - resolution: "eslint@npm:8.42.0" +"eslint@npm:8.43.0": + version: 8.43.0 + resolution: "eslint@npm:8.43.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.4.0 "@eslint/eslintrc": ^2.0.3 - "@eslint/js": 8.42.0 + "@eslint/js": 8.43.0 "@humanwhocodes/config-array": ^0.11.10 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 @@ -8209,7 +8196,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 07105397b5f2ff4064b983b8971e8c379ec04b1dfcc9d918976b3e00377189000161dac991d82ba14f8759e466091b8c71146f602930ca810c290ee3fcb3faf0 + checksum: 55654ce00b0d128822b57526e40473d0497c7c6be3886afdc0b41b6b0dfbd34d0eae8159911b18451b4db51a939a0e6d2e117e847ae419086884fc3d4fe23c7c languageName: node linkType: hard @@ -8380,6 +8367,13 @@ __metadata: languageName: node linkType: hard +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 3d21519a4f8207c99f7457287291316306255a328770d320b401114ec8481986e4e467e854cb9914dd965e0a1ca810a23ccb559c642c88f4c7f55c55778a9b48 + languageName: node + linkType: hard + "express@npm:^4.17.3": version: 4.18.2 resolution: "express@npm:4.18.2" @@ -8507,20 +8501,6 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^2.2.6": - version: 2.2.7 - resolution: "fast-glob@npm:2.2.7" - dependencies: - "@mrmlnc/readdir-enhanced": ^2.2.1 - "@nodelib/fs.stat": ^1.1.2 - glob-parent: ^3.1.0 - is-glob: ^4.0.0 - merge2: ^1.2.3 - micromatch: ^3.1.10 - checksum: 304ccff1d437fcc44ae0168b0c3899054b92e0fd6af6ad7c3ccc82ab4ddd210b99c7c739d60ee3686da2aa165cd1a31810b31fd91f7c2a575d297342a9fc0534 - languageName: node - linkType: hard - "fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.2, fast-glob@npm:^3.2.9": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" @@ -8889,7 +8869,7 @@ __metadata: languageName: node linkType: hard -"fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": +"fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" dependencies: @@ -8898,6 +8878,15 @@ __metadata: languageName: node linkType: hard +"fs-minipass@npm:^3.0.0": + version: 3.0.2 + resolution: "fs-minipass@npm:3.0.2" + dependencies: + minipass: ^5.0.0 + checksum: e9cc0e1f2d01c6f6f62f567aee59530aba65c6c7b2ae88c5027bc34c711ebcfcfaefd0caf254afa6adfe7d1fba16bc2537508a6235196bac7276747d078aef0a + languageName: node + linkType: hard + "fs-mkdirp-stream@npm:^1.0.0": version: 1.0.0 resolution: "fs-mkdirp-stream@npm:1.0.0" @@ -8908,10 +8897,10 @@ __metadata: languageName: node linkType: hard -"fs-monkey@npm:^1.0.3": - version: 1.0.3 - resolution: "fs-monkey@npm:1.0.3" - checksum: cf50804833f9b88a476911ae911fe50f61a98d986df52f890bd97e7262796d023698cb2309fa9b74fdd8974f04315b648748a0a8ee059e7d5257b293bfc409c0 +"fs-monkey@npm:^1.0.4": + version: 1.0.4 + resolution: "fs-monkey@npm:1.0.4" + checksum: 8b254c982905c0b7e028eab22b410dc35a5c0019c1c860456f5f54ae6a61666e1cb8c6b700d6c88cc873694c00953c935847b9959cc4dcf274aacb8673c1e8bf languageName: node linkType: hard @@ -9145,13 +9134,6 @@ __metadata: languageName: node linkType: hard -"glob-to-regexp@npm:^0.3.0": - version: 0.3.0 - resolution: "glob-to-regexp@npm:0.3.0" - checksum: d34b3219d860042d508c4893b67617cd16e2668827e445ff39cff9f72ef70361d3dc24f429e003cdfb6607c75c9664b8eadc41d2eeb95690af0b0d3113c1b23b - languageName: node - linkType: hard - "glob-to-regexp@npm:^0.4.1": version: 0.4.1 resolution: "glob-to-regexp@npm:0.4.1" @@ -9174,9 +9156,9 @@ __metadata: languageName: node linkType: hard -"glob@npm:10.2.6": - version: 10.2.6 - resolution: "glob@npm:10.2.6" +"glob@npm:10.3.0, glob@npm:^10.2.2": + version: 10.3.0 + resolution: "glob@npm:10.3.0" dependencies: foreground-child: ^3.1.0 jackspeak: ^2.0.3 @@ -9185,7 +9167,7 @@ __metadata: path-scurry: ^1.7.0 bin: glob: dist/cjs/src/bin.js - checksum: 94c5964bfa9df95207a69a3bd9b07b99ea7b5ba1f36dd73a8914378cee9436a205b9b5bdff58872abc238684ea7f4b4936e932155b8885250818bcc8d5321ddf + checksum: 6fa4ac0a86ffec1c5715a2e6fbdd63e1e7f1c2c8f5db08cc3256cdfcb81093678e7c80a3d100b502a1b9d141369ecf87bc24fe2bcb72acec7b14626d358a4eb0 languageName: node linkType: hard @@ -9217,7 +9199,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^8.0.1, glob@npm:^8.0.3": +"glob@npm:^8.0.3": version: 8.1.0 resolution: "glob@npm:8.1.0" dependencies: @@ -9304,15 +9286,15 @@ __metadata: linkType: hard "globby@npm:^13.1.2": - version: 13.1.4 - resolution: "globby@npm:13.1.4" + version: 13.2.0 + resolution: "globby@npm:13.2.0" dependencies: dir-glob: ^3.0.1 fast-glob: ^3.2.11 ignore: ^5.2.0 merge2: ^1.4.1 slash: ^4.0.0 - checksum: e8bc13879972082d590cd1b0e27080d90d2e12fff7eeb2cee9329c29115ace14cc5b9f899e3d6beb136ba826307a727016658919a6f383e1511d698acee81741 + checksum: 0a3dd786571788adef1c894f22112834cff5bbe061ae6e0a01c5118c39d44b3f1937ef1dae3f8b9bc24756eba84a0923e565b1ad9a4ec52831d7e2a04c035e75 languageName: node linkType: hard @@ -9596,10 +9578,10 @@ __metadata: languageName: node linkType: hard -"hls.js@npm:1.4.4": - version: 1.4.4 - resolution: "hls.js@npm:1.4.4" - checksum: 57b1a3607d240ddfa5a145b5075af22e2f27d55fae9330af2520f762651cf0d8811234e01b7b38e04c9e40f371a6350821c013c79ecb5ee5c3fff260f4fca996 +"hls.js@npm:1.4.6": + version: 1.4.6 + resolution: "hls.js@npm:1.4.6" + checksum: 7a44107c92e52859bfd5a37b6ea647cfea20848466286c1280b2cf956cfaccf8131fa54867cd8edfb60042a238025fb8788263fd9dfb9d1798ddbdc08f5aab50 languageName: node linkType: hard @@ -9607,28 +9589,29 @@ __metadata: version: 0.0.0-use.local resolution: "home-assistant-frontend@workspace:." dependencies: - "@babel/core": 7.22.1 - "@babel/plugin-proposal-decorators": 7.22.3 - "@babel/plugin-transform-runtime": 7.22.4 - "@babel/preset-env": 7.22.4 - "@babel/preset-typescript": 7.21.5 - "@babel/runtime": 7.22.3 + "@babel/core": 7.22.5 + "@babel/plugin-proposal-decorators": 7.22.5 + "@babel/plugin-transform-runtime": 7.22.5 + "@babel/preset-env": 7.22.5 + "@babel/preset-typescript": 7.22.5 + "@babel/runtime": 7.22.5 "@braintree/sanitize-url": 6.0.2 - "@codemirror/autocomplete": 6.7.1 + "@codemirror/autocomplete": 6.8.1 "@codemirror/commands": 6.2.4 - "@codemirror/language": 6.7.0 + "@codemirror/language": 6.8.0 "@codemirror/legacy-modes": 6.3.2 - "@codemirror/search": 6.4.0 + "@codemirror/search": 6.5.0 "@codemirror/state": 6.2.1 - "@codemirror/view": 6.12.0 + "@codemirror/view": 6.14.0 "@egjs/hammerjs": 2.0.17 - "@formatjs/intl-datetimeformat": 6.8.0 - "@formatjs/intl-displaynames": 6.3.2 - "@formatjs/intl-getcanonicallocales": 2.2.0 - "@formatjs/intl-locale": 3.3.0 - "@formatjs/intl-numberformat": 8.5.0 - "@formatjs/intl-pluralrules": 5.2.2 - "@formatjs/intl-relativetimeformat": 11.2.2 + "@formatjs/intl-datetimeformat": 6.10.0 + "@formatjs/intl-displaynames": 6.5.0 + "@formatjs/intl-getcanonicallocales": 2.2.1 + "@formatjs/intl-listformat": 7.4.0 + "@formatjs/intl-locale": 3.3.2 + "@formatjs/intl-numberformat": 8.7.0 + "@formatjs/intl-pluralrules": 5.2.4 + "@formatjs/intl-relativetimeformat": 11.2.4 "@fullcalendar/core": 6.1.8 "@fullcalendar/daygrid": 6.1.8 "@fullcalendar/interaction": 6.1.8 @@ -9636,10 +9619,10 @@ __metadata: "@fullcalendar/timegrid": 6.1.8 "@koa/cors": 4.0.0 "@lezer/highlight": 1.1.6 - "@lit-labs/context": 0.3.2 + "@lit-labs/context": 0.3.3 "@lit-labs/motion": 1.0.3 - "@lit-labs/virtualizer": 2.0.2 - "@lrnwebcomponents/simple-tooltip": 7.0.2 + "@lit-labs/virtualizer": 2.0.3 + "@lrnwebcomponents/simple-tooltip": 7.0.5 "@material/chips": =14.0.0-canary.53b3cad2f.0 "@material/data-table": =14.0.0-canary.53b3cad2f.0 "@material/mwc-button": 0.27.0 @@ -9665,12 +9648,12 @@ __metadata: "@material/mwc-top-app-bar": 0.27.0 "@material/mwc-top-app-bar-fixed": 0.27.0 "@material/top-app-bar": =14.0.0-canary.53b3cad2f.0 - "@material/web": =1.0.0-pre.9 + "@material/web": =1.0.0-pre.11 "@mdi/js": 7.2.96 "@mdi/svg": 7.2.96 - "@octokit/auth-oauth-device": 4.0.4 - "@octokit/plugin-retry": 5.0.0 - "@octokit/rest": 19.0.11 + "@octokit/auth-oauth-device": 5.0.2 + "@octokit/plugin-retry": 5.0.4 + "@octokit/rest": 19.0.13 "@open-wc/dev-server-hmr": 0.1.4 "@polymer/app-layout": 3.1.0 "@polymer/iron-flex-layout": 3.0.1 @@ -9684,7 +9667,7 @@ __metadata: "@polymer/paper-toast": 3.0.1 "@polymer/polymer": 3.5.1 "@rollup/plugin-babel": 6.0.3 - "@rollup/plugin-commonjs": 25.0.0 + "@rollup/plugin-commonjs": 25.0.2 "@rollup/plugin-json": 6.0.0 "@rollup/plugin-node-resolve": 15.1.0 "@rollup/plugin-replace": 5.0.2 @@ -9697,7 +9680,7 @@ __metadata: "@types/html-minifier-terser": 7.0.0 "@types/js-yaml": 4.0.5 "@types/leaflet": 1.9.3 - "@types/leaflet-draw": 1.0.6 + "@types/leaflet-draw": 1.0.7 "@types/marked": 4.3.1 "@types/mocha": 10.0.1 "@types/qrcode": 1.5.0 @@ -9705,10 +9688,10 @@ __metadata: "@types/sortablejs": 1.15.1 "@types/tar": 6.1.5 "@types/webspeechapi": 0.0.29 - "@typescript-eslint/eslint-plugin": 5.59.8 - "@typescript-eslint/parser": 5.59.8 - "@vaadin/combo-box": 24.0.8 - "@vaadin/vaadin-themable-mixin": 24.0.8 + "@typescript-eslint/eslint-plugin": 5.60.0 + "@typescript-eslint/parser": 5.60.0 + "@vaadin/combo-box": 24.1.1 + "@vaadin/vaadin-themable-mixin": 24.1.1 "@vibrant/color": 3.2.1-alpha.1 "@vibrant/core": 3.2.1-alpha.1 "@vibrant/quantizer-mmcq": 3.2.1-alpha.1 @@ -9723,14 +9706,14 @@ __metadata: chai: 4.3.7 chart.js: 3.3.2 comlink: 4.4.1 - core-js: 3.30.2 + core-js: 3.31.0 cropperjs: 1.5.13 date-fns: 2.30.0 date-fns-tz: 2.0.0 deep-clone-simple: 1.1.1 deep-freeze: 0.0.1 del: 7.0.0 - eslint: 8.42.0 + eslint: 8.43.0 eslint-config-airbnb-base: 15.0.0 eslint-config-airbnb-typescript: 17.0.0 eslint-config-prettier: 8.8.0 @@ -9738,14 +9721,14 @@ __metadata: eslint-plugin-disable: 2.0.3 eslint-plugin-import: 2.27.5 eslint-plugin-lit: 1.8.3 - eslint-plugin-lit-a11y: 2.4.1 + eslint-plugin-lit-a11y: 3.0.0 eslint-plugin-unused-imports: 2.0.0 eslint-plugin-wc: 1.5.0 esprima: 4.0.1 fancy-log: 2.0.0 fs-extra: 11.1.1 fuse.js: 6.6.2 - glob: 10.2.6 + glob: 10.3.0 google-timezones-json: 1.1.0 gulp: 4.0.2 gulp-flatmap: 1.0.2 @@ -9753,20 +9736,20 @@ __metadata: gulp-merge-json: 2.1.2 gulp-rename: 2.0.0 gulp-zopfli-green: 6.0.1 - hls.js: 1.4.4 - home-assistant-js-websocket: 8.0.1 + hls.js: 1.4.6 + home-assistant-js-websocket: 8.1.0 html-minifier-terser: 7.2.0 husky: 8.0.3 idb-keyval: 6.2.1 instant-mocha: 1.5.1 - intl-messageformat: 10.3.5 + intl-messageformat: 10.5.0 js-yaml: 4.1.0 jszip: 3.10.1 leaflet: 1.9.4 leaflet-draw: 1.0.4 lint-staged: 13.2.2 lit: 2.7.5 - lit-analyzer: 1.2.1 + lit-analyzer: 2.0.0-pre.3 lodash.template: 4.5.0 magic-string: 0.30.0 map-stream: 0.0.7 @@ -9788,21 +9771,21 @@ __metadata: rollup: 2.79.1 rollup-plugin-string: 3.0.0 rollup-plugin-terser: 7.0.2 - rollup-plugin-visualizer: 5.9.0 + rollup-plugin-visualizer: 5.9.2 rrule: 2.7.2 serve-handler: 6.1.5 - sinon: 15.1.0 + sinon: 15.2.0 sortablejs: 1.15.0 source-map-url: 0.4.1 superstruct: 1.0.3 systemjs: 6.14.1 tar: 6.1.15 terser-webpack-plugin: 5.3.9 - tinykeys: 1.4.0 - ts-lit-plugin: 1.2.1 - tsparticles-engine: 2.9.3 - tsparticles-preset-links: 2.9.3 - typescript: 4.9.5 + tinykeys: 2.1.0 + ts-lit-plugin: 2.0.0-pre.1 + tsparticles-engine: 2.10.1 + tsparticles-preset-links: 2.10.1 + typescript: 5.1.3 unfetch: 5.0.0 vinyl-buffer: 1.0.1 vinyl-source-stream: 2.0.0 @@ -9810,9 +9793,9 @@ __metadata: vis-network: 9.1.6 vue: 2.7.14 vue2-daterange-picker: 0.6.8 - webpack: 5.84.1 - webpack-cli: 5.1.1 - webpack-dev-server: 4.15.0 + webpack: 5.88.0 + webpack-cli: 5.1.4 + webpack-dev-server: 4.15.1 webpack-manifest-plugin: 5.0.0 webpackbar: 5.0.2 weekstart: 2.0.0 @@ -9827,10 +9810,10 @@ __metadata: languageName: unknown linkType: soft -"home-assistant-js-websocket@npm:8.0.1": - version: 8.0.1 - resolution: "home-assistant-js-websocket@npm:8.0.1" - checksum: e8b2204d58b2b1fbdf26ca1ad196fcc02ec5d18e6d867179f27246a9f2d4fe5f91de9dbbe7b82806c19dcb0af0e2b77fb48d393668b5c8c0844c201a16832023 +"home-assistant-js-websocket@npm:8.1.0": + version: 8.1.0 + resolution: "home-assistant-js-websocket@npm:8.1.0" + checksum: 74f9afc5affe491921d7fd9e743c1a6841cb0409c2c5454fff266d14ea893f5be476cb85f584ff1836722de77cfe2777d4133890f00fc1983ad448eba50f6240 languageName: node linkType: hard @@ -9863,9 +9846,9 @@ __metadata: linkType: hard "html-entities@npm:^2.3.2": - version: 2.3.3 - resolution: "html-entities@npm:2.3.3" - checksum: 92521501da8aa5f66fee27f0f022d6e9ceae62667dae93aa6a2f636afa71ad530b7fb24a18d4d6c124c9885970cac5f8a52dbf1731741161002816ae43f98196 + version: 2.3.6 + resolution: "html-entities@npm:2.3.6" + checksum: 559a88dc3a2059b1e8882940dcaf996ea9d8151b9a780409ff223a79dc1d42ee8bb19b3365064f241f2e2543b0f90612d63f9b8e36d14c4c7fbb73540a8f41cb languageName: node linkType: hard @@ -9913,7 +9896,7 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.1.0": +"http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 @@ -10159,13 +10142,6 @@ __metadata: languageName: node linkType: hard -"infer-owner@npm:^1.0.4": - version: 1.0.4 - resolution: "infer-owner@npm:1.0.4" - checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 - languageName: node - linkType: hard - "inflight@npm:^1.0.4": version: 1.0.6 resolution: "inflight@npm:1.0.6" @@ -10214,7 +10190,7 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5": +"internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.5": version: 1.0.5 resolution: "internal-slot@npm:1.0.5" dependencies: @@ -10239,15 +10215,15 @@ __metadata: languageName: node linkType: hard -"intl-messageformat@npm:10.3.5": - version: 10.3.5 - resolution: "intl-messageformat@npm:10.3.5" +"intl-messageformat@npm:10.5.0": + version: 10.5.0 + resolution: "intl-messageformat@npm:10.5.0" dependencies: - "@formatjs/ecma402-abstract": 1.15.0 - "@formatjs/fast-memoize": 2.0.1 - "@formatjs/icu-messageformat-parser": 2.4.0 + "@formatjs/ecma402-abstract": 1.17.0 + "@formatjs/fast-memoize": 2.2.0 + "@formatjs/icu-messageformat-parser": 2.6.0 tslib: ^2.4.0 - checksum: a52526a02ee54fda870e5a83900ba089332c5a820bec033ffd5c422e68e90269e36e2aa144dc3728a43b5df506d8a5e261e162227410463d5d8a03864f39170e + checksum: 164c49028b8bf2685f57f8f018d9a2c1d827e94b1c300ebf9df50b6aef25adedb3bf511b3cf603364d67257634b57926935066b2f3715ccb6c2af683cc0815a6 languageName: node linkType: hard @@ -10280,9 +10256,9 @@ __metadata: linkType: hard "ipaddr.js@npm:^2.0.1": - version: 2.0.1 - resolution: "ipaddr.js@npm:2.0.1" - checksum: dd194a394a843d470f88d17191b0948f383ed1c8e320813f850c336a0fcb5e9215d97ec26ca35ab4fbbd31392c8b3467f3e8344628029ed3710b2ff6b5d1034e + version: 2.1.0 + resolution: "ipaddr.js@npm:2.1.0" + checksum: 807a054f2bd720c4d97ee479d6c9e865c233bea21f139fb8dabd5a35c4226d2621c42e07b4ad94ff3f82add926a607d8d9d37c625ad0319f0e08f9f2bd1968e2 languageName: node linkType: hard @@ -10314,16 +10290,6 @@ __metadata: languageName: node linkType: hard -"is-arguments@npm:^1.1.1": - version: 1.1.1 - resolution: "is-arguments@npm:1.1.1" - dependencies: - call-bind: ^1.0.2 - has-tostringtag: ^1.0.0 - checksum: 7f02700ec2171b691ef3e4d0e3e6c0ba408e8434368504bb593d0d7c891c0dbfda6d19d30808b904a6cb1929bca648c061ba438c39f296c2a8ca083229c49f27 - languageName: node - linkType: hard - "is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2": version: 3.0.2 resolution: "is-array-buffer@npm:3.0.2" @@ -10429,7 +10395,7 @@ __metadata: languageName: node linkType: hard -"is-date-object@npm:^1.0.1, is-date-object@npm:^1.0.5": +"is-date-object@npm:^1.0.1": version: 1.0.5 resolution: "is-date-object@npm:1.0.5" dependencies: @@ -10576,13 +10542,6 @@ __metadata: languageName: node linkType: hard -"is-map@npm:^2.0.1, is-map@npm:^2.0.2": - version: 2.0.2 - resolution: "is-map@npm:2.0.2" - checksum: ace3d0ecd667bbdefdb1852de601268f67f2db725624b1958f279316e13fecb8fa7df91fd60f690d7417b4ec180712f5a7ee967008e27c65cfd475cc84337728 - languageName: node - linkType: hard - "is-module@npm:^1.0.0": version: 1.0.0 resolution: "is-module@npm:1.0.0" @@ -10736,13 +10695,6 @@ __metadata: languageName: node linkType: hard -"is-set@npm:^2.0.1, is-set@npm:^2.0.2": - version: 2.0.2 - resolution: "is-set@npm:2.0.2" - checksum: b64343faf45e9387b97a6fd32be632ee7b269bd8183701f3b3f5b71a7cf00d04450ed8669d0bd08753e08b968beda96fca73a10fd0ff56a32603f64deba55a57 - languageName: node - linkType: hard - "is-shared-array-buffer@npm:^1.0.2": version: 1.0.2 resolution: "is-shared-array-buffer@npm:1.0.2" @@ -10836,13 +10788,6 @@ __metadata: languageName: node linkType: hard -"is-weakmap@npm:^2.0.1": - version: 2.0.1 - resolution: "is-weakmap@npm:2.0.1" - checksum: 1222bb7e90c32bdb949226e66d26cb7bce12e1e28e3e1b40bfa6b390ba3e08192a8664a703dff2a00a84825f4e022f9cd58c4599ff9981ab72b1d69479f4f7f6 - languageName: node - linkType: hard - "is-weakref@npm:^1.0.2": version: 1.0.2 resolution: "is-weakref@npm:1.0.2" @@ -10852,16 +10797,6 @@ __metadata: languageName: node linkType: hard -"is-weakset@npm:^2.0.1": - version: 2.0.2 - resolution: "is-weakset@npm:2.0.2" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.1 - checksum: 5d8698d1fa599a0635d7ca85be9c26d547b317ed8fd83fc75f03efbe75d50001b5eececb1e9971de85fcde84f69ae6f8346bc92d20d55d46201d328e4c74a367 - languageName: node - linkType: hard - "is-windows@npm:^1.0.1, is-windows@npm:^1.0.2": version: 1.0.2 resolution: "is-windows@npm:1.0.2" @@ -10892,13 +10827,6 @@ __metadata: languageName: node linkType: hard -"isarray@npm:^2.0.5": - version: 2.0.5 - resolution: "isarray@npm:2.0.5" - checksum: bd5bbe4104438c4196ba58a54650116007fa0262eccef13a4c55b2e09a5b36b59f1e75b9fcc49883dd9d4953892e6fc007eef9e9155648ceea036e184b0f930a - languageName: node - linkType: hard - "isbinaryfile@npm:^5.0.0": version: 5.0.0 resolution: "isbinaryfile@npm:5.0.0" @@ -10930,21 +10858,21 @@ __metadata: linkType: hard "jackspeak@npm:^2.0.3": - version: 2.2.0 - resolution: "jackspeak@npm:2.2.0" + version: 2.2.1 + resolution: "jackspeak@npm:2.2.1" dependencies: "@isaacs/cliui": ^8.0.2 "@pkgjs/parseargs": ^0.11.0 dependenciesMeta: "@pkgjs/parseargs": optional: true - checksum: d8cd5be4f0e89cef04add5b0b068162a086bdb1ca68113ed729e99489b7865ca3edcc6430d6fd20c430e15382929ef5f3c7ec36e6aa7c17be23cac116f92dcff + checksum: e29291c0d0f280a063fa18fbd1e891ab8c2d7519fd34052c0ebde38538a15c603140d60c2c7f432375ff7ee4c5f1c10daa8b2ae19a97c3d4affe308c8360c1df languageName: node linkType: hard "jake@npm:^10.8.5": - version: 10.8.6 - resolution: "jake@npm:10.8.6" + version: 10.8.7 + resolution: "jake@npm:10.8.7" dependencies: async: ^3.2.3 chalk: ^4.0.2 @@ -10952,7 +10880,7 @@ __metadata: minimatch: ^3.1.2 bin: jake: bin/cli.js - checksum: eebebd3ca62a01ced630afc116f429d727d34bebe58a9424c0d5a0618ad6c1db893163fb4fbcdff01f34d34d6d63c0dd2448de598270bcd27d9440630de4aeea + checksum: a23fd2273fb13f0d0d845502d02c791fd55ef5c6a2d207df72f72d8e1eac6d2b8ffa6caf660bc8006b3242e0daaa88a3ecc600194d72b5c6016ad56e9cd43553 languageName: node linkType: hard @@ -11425,21 +11353,22 @@ __metadata: languageName: node linkType: hard -"lit-analyzer@npm:1.2.1": - version: 1.2.1 - resolution: "lit-analyzer@npm:1.2.1" +"lit-analyzer@npm:2.0.0-pre.3, lit-analyzer@npm:^2.0.0-pre.3": + version: 2.0.0-pre.3 + resolution: "lit-analyzer@npm:2.0.0-pre.3" dependencies: + "@vscode/web-custom-data": ^0.4.2 chalk: ^2.4.2 didyoumean2: 4.1.0 - fast-glob: ^2.2.6 + fast-glob: ^3.2.11 parse5: 5.1.0 - ts-simple-type: ~1.0.5 + ts-simple-type: ~2.0.0-next.0 vscode-css-languageservice: 4.3.0 vscode-html-languageservice: 3.1.0 - web-component-analyzer: ~1.1.1 + web-component-analyzer: ^2.0.0-next.5 bin: lit-analyzer: cli.js - checksum: b89646033b45262a863bf32d8bf177bfa4f22cde4e2c3f2cd006abdd68aeab434505f67c3c5ed213d8a5936d063ec2845efb15b0968ec9cf9e0863e53e6c118c + checksum: c6dcf657a0030342d183fcd9d550753bd5dd0692b478aff271085a5a0e7e08aff39cc6dde3d547ffca72897975ef07aac7de8a97e0060d2db70a85350412efae languageName: node linkType: hard @@ -11474,7 +11403,7 @@ __metadata: languageName: node linkType: hard -"lit@npm:2.7.5, lit@npm:^2.0.0, lit@npm:^2.0.0-rc.2, lit@npm:^2.2.1, lit@npm:^2.7.0, lit@npm:^2.7.4": +"lit@npm:2.7.5, lit@npm:^2.0.0, lit@npm:^2.0.0-rc.2, lit@npm:^2.2.1, lit@npm:^2.7.0, lit@npm:^2.7.4, lit@npm:^2.7.5": version: 2.7.5 resolution: "lit@npm:2.7.5" dependencies: @@ -11694,9 +11623,9 @@ __metadata: linkType: hard "lru-cache@npm:^9.1.1": - version: 9.1.1 - resolution: "lru-cache@npm:9.1.1" - checksum: 4d703bb9b66216bbee55ead82a9682820a2b6acbdfca491b235390b1ef1056000a032d56dfb373fdf9ad4492f1fa9d04cc9a05a77f25bd7ce6901d21ad9b68b7 + version: 9.1.2 + resolution: "lru-cache@npm:9.1.2" + checksum: d3415634be3908909081fc4c56371a8d562d9081eba70543d86871b978702fffd0e9e362b83921b27a29ae2b37b90f55675aad770a54ac83bb3e4de5049d4b15 languageName: node linkType: hard @@ -11736,27 +11665,26 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.3": - version: 10.2.1 - resolution: "make-fetch-happen@npm:10.2.1" +"make-fetch-happen@npm:^11.0.3": + version: 11.1.1 + resolution: "make-fetch-happen@npm:11.1.1" dependencies: agentkeepalive: ^4.2.1 - cacache: ^16.1.0 - http-cache-semantics: ^4.1.0 + cacache: ^17.0.0 + http-cache-semantics: ^4.1.1 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.0 is-lambda: ^1.0.1 lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-fetch: ^2.0.3 + minipass: ^5.0.0 + minipass-fetch: ^3.0.0 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 socks-proxy-agent: ^7.0.0 - ssri: ^9.0.0 - checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c + ssri: ^10.0.0 + checksum: 7268bf274a0f6dcf0343829489a4506603ff34bd0649c12058753900b0eb29191dce5dba12680719a5d0a983d3e57810f594a12f3c18494e93a1fbc6348a4540 languageName: node linkType: hard @@ -11821,11 +11749,11 @@ __metadata: linkType: hard "memfs@npm:^3.4.3, memfs@npm:^3.5.0": - version: 3.5.1 - resolution: "memfs@npm:3.5.1" + version: 3.5.3 + resolution: "memfs@npm:3.5.3" dependencies: - fs-monkey: ^1.0.3 - checksum: fcd037566a4bbb00d61dc991858395ccc06267ab5fe9471aeff28433f2a210bf5dd999e64e8b5473f8244f00dfb7ff3221b5c2fe41ff98af1439e5e2168fc410 + fs-monkey: ^1.0.4 + checksum: 18dfdeacad7c8047b976a6ccd58bc98ba76e122ad3ca0e50a21837fe2075fc0d9aafc58ab9cf2576c2b6889da1dd2503083f2364191b695273f40969db2ecc44 languageName: node linkType: hard @@ -11857,7 +11785,7 @@ __metadata: languageName: node linkType: hard -"merge2@npm:^1.2.3, merge2@npm:^1.3.0, merge2@npm:^1.4.1": +"merge2@npm:^1.3.0, merge2@npm:^1.4.1": version: 1.4.1 resolution: "merge2@npm:1.4.1" checksum: 7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 @@ -12025,18 +11953,18 @@ __metadata: languageName: node linkType: hard -"minipass-fetch@npm:^2.0.3": - version: 2.1.2 - resolution: "minipass-fetch@npm:2.1.2" +"minipass-fetch@npm:^3.0.0": + version: 3.0.3 + resolution: "minipass-fetch@npm:3.0.3" dependencies: encoding: ^0.1.13 - minipass: ^3.1.6 + minipass: ^5.0.0 minipass-sized: ^1.0.3 minizlib: ^2.1.2 dependenciesMeta: encoding: optional: true - checksum: 3f216be79164e915fc91210cea1850e488793c740534985da017a4cbc7a5ff50506956d0f73bb0cb60e4fe91be08b6b61ef35101706d3ef5da2c8709b5f08f91 + checksum: af5ab2552a16fcf505d35fd7ffb84b57f4a0eeb269e6e1d9a2a75824dda48b36e527083250b7cca4a4def21d9544e2ade441e4730e233c0bc2133f6abda31e18 languageName: node linkType: hard @@ -12067,7 +11995,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": +"minipass@npm:^3.0.0": version: 3.3.6 resolution: "minipass@npm:3.3.6" dependencies: @@ -12128,7 +12056,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": +"mkdirp@npm:^1.0.3": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" bin: @@ -12342,13 +12270,14 @@ __metadata: linkType: hard "node-gyp@npm:latest": - version: 9.3.1 - resolution: "node-gyp@npm:9.3.1" + version: 9.4.0 + resolution: "node-gyp@npm:9.4.0" dependencies: env-paths: ^2.2.0 + exponential-backoff: ^3.1.1 glob: ^7.1.4 graceful-fs: ^4.2.6 - make-fetch-happen: ^10.0.3 + make-fetch-happen: ^11.0.3 nopt: ^6.0.0 npmlog: ^6.0.0 rimraf: ^3.0.2 @@ -12357,14 +12286,14 @@ __metadata: which: ^2.0.2 bin: node-gyp: bin/node-gyp.js - checksum: b860e9976fa645ca0789c69e25387401b4396b93c8375489b5151a6c55cf2640a3b6183c212b38625ef7c508994930b72198338e3d09b9d7ade5acc4aaf51ea7 + checksum: 78b404e2e0639d64e145845f7f5a3cb20c0520cdaf6dda2f6e025e9b644077202ea7de1232396ba5bde3fee84cdc79604feebe6ba3ec84d464c85d407bb5da99 languageName: node linkType: hard -"node-releases@npm:^2.0.8": - version: 2.0.11 - resolution: "node-releases@npm:2.0.11" - checksum: ade1c8e19852aa7d7b45691c2708e6275703dd4994b16bc191cdbf66add29ccf87c595ecdb03a39db54a8aaba645f228bccd7d9477e4066f1d97a94f857dae9d +"node-releases@npm:^2.0.12": + version: 2.0.12 + resolution: "node-releases@npm:2.0.12" + checksum: b8c56db82c4642a0f443332b331a4396dae452a2ac5a65c8dbd93ef89ecb2fbb0da9d42ac5366d4764973febadca816cf7587dad492dce18d2a6b2af59cda260 languageName: node linkType: hard @@ -12516,16 +12445,6 @@ __metadata: languageName: node linkType: hard -"object-is@npm:^1.1.5": - version: 1.1.5 - resolution: "object-is@npm:1.1.5" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - checksum: 989b18c4cba258a6b74dc1d74a41805c1a1425bce29f6cabb50dcb1a6a651ea9104a1b07046739a49a5bb1bc49727bcb00efd5c55f932f6ea04ec8927a7901fe - languageName: node - linkType: hard - "object-keys@npm:^1.1.1": version: 1.1.1 resolution: "object-keys@npm:1.1.1" @@ -13240,13 +13159,13 @@ __metadata: linkType: hard "postcss@npm:^8.4.14": - version: 8.4.23 - resolution: "postcss@npm:8.4.23" + version: 8.4.24 + resolution: "postcss@npm:8.4.24" dependencies: nanoid: ^3.3.6 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 8bb9d1b2ea6e694f8987d4f18c94617971b2b8d141602725fedcc2222fdc413b776a6e1b969a25d627d7b2681ca5aabb56f59e727ef94072e1b6ac8412105a2f + checksum: 814e2126dacfea313588eda09cc99a9b4c26ec55c059188aa7a916d20d26d483483106dc5ff9e560731b59f45c5bb91b945dfadc670aed875cc90ddbbf4e787d languageName: node linkType: hard @@ -13308,13 +13227,6 @@ __metadata: languageName: node linkType: hard -"promise-inflight@npm:^1.0.1": - version: 1.0.1 - resolution: "promise-inflight@npm:1.0.1" - checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 - languageName: node - linkType: hard - "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -13372,13 +13284,6 @@ __metadata: languageName: node linkType: hard -"punycode@npm:1.3.2": - version: 1.3.2 - resolution: "punycode@npm:1.3.2" - checksum: b8807fd594b1db33335692d1f03e8beeddde6fda7fbb4a2e32925d88d20a3aa4cd8dcc0c109ccaccbd2ba761c208dfaaada83007087ea8bfb0129c9ef1b99ed6 - languageName: node - linkType: hard - "punycode@npm:2.3.0, punycode@npm:^2.1.0, punycode@npm:^2.1.1": version: 2.3.0 resolution: "punycode@npm:2.3.0" @@ -13386,7 +13291,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^1.3.2": +"punycode@npm:^1.3.2, punycode@npm:^1.4.1": version: 1.4.1 resolution: "punycode@npm:1.4.1" checksum: fa6e698cb53db45e4628559e557ddaf554103d2a96a1d62892c8f4032cd3bc8871796cae9eabc1bc700e2b6677611521ce5bb1d9a27700086039965d0cf34518 @@ -13425,10 +13330,12 @@ __metadata: languageName: node linkType: hard -"querystring@npm:0.2.0": - version: 0.2.0 - resolution: "querystring@npm:0.2.0" - checksum: 8258d6734f19be27e93f601758858c299bdebe71147909e367101ba459b95446fbe5b975bf9beb76390156a592b6f4ac3a68b6087cea165c259705b8b4e56a69 +"qs@npm:^6.11.0": + version: 6.11.2 + resolution: "qs@npm:6.11.2" + dependencies: + side-channel: ^1.0.4 + checksum: e812f3c590b2262548647d62f1637b6989cc56656dc960b893fe2098d96e1bd633f36576f4cd7564dfbff9db42e17775884db96d846bebe4f37420d073ecdc0b languageName: node linkType: hard @@ -13610,7 +13517,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.4.3, regexp.prototype.flags@npm:^1.5.0": +"regexp.prototype.flags@npm:^1.4.3": version: 1.5.0 resolution: "regexp.prototype.flags@npm:1.5.0" dependencies: @@ -13933,9 +13840,9 @@ __metadata: languageName: node linkType: hard -"rollup-plugin-visualizer@npm:5.9.0": - version: 5.9.0 - resolution: "rollup-plugin-visualizer@npm:5.9.0" +"rollup-plugin-visualizer@npm:5.9.2": + version: 5.9.2 + resolution: "rollup-plugin-visualizer@npm:5.9.2" dependencies: open: ^8.4.0 picomatch: ^2.3.1 @@ -13948,7 +13855,7 @@ __metadata: optional: true bin: rollup-plugin-visualizer: dist/bin/cli.js - checksum: 362d4fac0295c14bd205dbc85c20c31f4b6c47604868da21d9565ed47e0333759f08b9fe0acb82f78221f5173ea01e4eb70d47351eb6012216afe71b5492ed5f + checksum: ab2adf322e3b20bffc94a8dc804f46be8840a9fcbab4f872dcc2dec205cdd7752e4d2d90cfcf00783bfb5209c5a8bb4e591984e8b61bca41fd048fb7deb0ed4e languageName: node linkType: hard @@ -14066,26 +13973,26 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.1.1, schema-utils@npm:^3.1.2": - version: 3.1.2 - resolution: "schema-utils@npm:3.1.2" +"schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0": + version: 3.3.0 + resolution: "schema-utils@npm:3.3.0" dependencies: "@types/json-schema": ^7.0.8 ajv: ^6.12.5 ajv-keywords: ^3.5.2 - checksum: 39683edfe3beff018cdb1ae4fa296fc55cea13a080aa2b4d9351895cd64b22ba4d87e2e548c2a2ac1bc76e60980670adb0f413a58104479f1a0c12e5663cb8ca + checksum: ea56971926fac2487f0757da939a871388891bc87c6a82220d125d587b388f1704788f3706e7f63a7b70e49fc2db974c41343528caea60444afd5ce0fe4b85c0 languageName: node linkType: hard "schema-utils@npm:^4.0.0": - version: 4.0.1 - resolution: "schema-utils@npm:4.0.1" + version: 4.2.0 + resolution: "schema-utils@npm:4.2.0" dependencies: "@types/json-schema": ^7.0.9 ajv: ^8.9.0 ajv-formats: ^2.1.1 ajv-keywords: ^5.1.0 - checksum: 745e7293c6b6c84940de16753c207311da821aa9911b9e2d158cfd9ffc5bf1f880147abbbe775b96cb8cd3c7f48890950fe0164f54eed9a8aabb948ebf8a3fdd + checksum: 26a0463d47683258106e6652e9aeb0823bf0b85843039e068b57da1892f7ae6b6b1094d48e9ed5ba5cbe9f7166469d880858b9d91abe8bd249421eb813850cde languageName: node linkType: hard @@ -14133,13 +14040,13 @@ __metadata: linkType: hard "semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7": - version: 7.5.1 - resolution: "semver@npm:7.5.1" + version: 7.5.2 + resolution: "semver@npm:7.5.2" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: d16dbedad53c65b086f79524b9ef766bf38670b2395bdad5c957f824dcc566b624988013564f4812bcace3f9d405355c3635e2007396a39d1bffc71cfec4a2fc + checksum: 3fdf5d1e6f170fe8bcc41669e31787649af91af7f54f05c71d0865bb7aa27e8b92f68b3e6b582483e2c1c648008bc84249d2cd86301771fe5cbf7621d1fe5375 languageName: node linkType: hard @@ -14331,17 +14238,17 @@ __metadata: languageName: node linkType: hard -"sinon@npm:15.1.0": - version: 15.1.0 - resolution: "sinon@npm:15.1.0" +"sinon@npm:15.2.0": + version: 15.2.0 + resolution: "sinon@npm:15.2.0" dependencies: "@sinonjs/commons": ^3.0.0 - "@sinonjs/fake-timers": ^10.2.0 + "@sinonjs/fake-timers": ^10.3.0 "@sinonjs/samsam": ^8.0.0 diff: ^5.1.0 nise: ^5.1.4 supports-color: ^7.2.0 - checksum: 3f0eab3e293c6206cca4a31f3c2836be86889f16bf6f54e2429550c1d07d0e7e9327cdbe6b7b69662c152a63e9f2661334c266932ba8f3206190009d58cb4f06 + checksum: 1641b9af8a73ba57c73c9b6fd955a2d062a5d78cce719887869eca45faf33b0fd20cabfeffdfd856bb35bfbd3d49debb2d954ff6ae5e9825a3da5ff4f604ab6c languageName: node linkType: hard @@ -14638,12 +14545,12 @@ __metadata: languageName: node linkType: hard -"ssri@npm:^9.0.0": - version: 9.0.1 - resolution: "ssri@npm:9.0.1" +"ssri@npm:^10.0.0": + version: 10.0.4 + resolution: "ssri@npm:10.0.4" dependencies: - minipass: ^3.1.1 - checksum: fb58f5e46b6923ae67b87ad5ef1c5ab6d427a17db0bead84570c2df3cd50b4ceb880ebdba2d60726588272890bae842a744e1ecce5bd2a2a582fccd5068309eb + minipass: ^5.0.0 + checksum: fb14da9f8a72b04eab163eb13a9dda11d5962cd2317f85457c4e0b575e9a6e0e3a6a87b5bf122c75cb36565830cd5f263fb457571bf6f1587eb5f95d095d6165 languageName: node linkType: hard @@ -14685,15 +14592,6 @@ __metadata: languageName: node linkType: hard -"stop-iteration-iterator@npm:^1.0.0": - version: 1.0.0 - resolution: "stop-iteration-iterator@npm:1.0.0" - dependencies: - internal-slot: ^1.0.4 - checksum: d04173690b2efa40e24ab70e5e51a3ff31d56d699550cfad084104ab3381390daccb36652b25755e420245f3b0737de66c1879eaa2a8d4fc0a78f9bf892fcb42 - languageName: node - linkType: hard - "stream-exhaust@npm:^1.0.1": version: 1.0.2 resolution: "stream-exhaust@npm:1.0.2" @@ -14861,11 +14759,11 @@ __metadata: linkType: hard "strip-ansi@npm:^7.0.1": - version: 7.0.1 - resolution: "strip-ansi@npm:7.0.1" + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" dependencies: ansi-regex: ^6.0.1 - checksum: 257f78fa433520e7f9897722731d78599cb3fce29ff26a20a5e12ba4957463b50a01136f37c43707f4951817a75e90820174853d6ccc240997adc5df8f966039 + checksum: 859c73fcf27869c22a4e4d8c6acfe690064659e84bef9458aa6d13719d09ca88dcfd40cbf31fd0be63518ea1a643fe070b4827d353e09533a5b0b9fd4553d64d languageName: node linkType: hard @@ -15088,16 +14986,16 @@ __metadata: linkType: hard "terser@npm:^5.0.0, terser@npm:^5.15.1, terser@npm:^5.16.8": - version: 5.17.4 - resolution: "terser@npm:5.17.4" + version: 5.18.0 + resolution: "terser@npm:5.18.0" dependencies: - "@jridgewell/source-map": ^0.3.2 - acorn: ^8.5.0 + "@jridgewell/source-map": ^0.3.3 + acorn: ^8.8.2 commander: ^2.20.0 source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: 4bb4bbee170bee4cf897545b602999e0b74d2cd035387514c6859fae6a71d623f8d1319de47bcf6a157358355cc7afaa62a5d5661bfc72968d13b35113022486 + checksum: d01eb9805a978b3338b68fd2d9e35c1cd4cad78ea093dc92c7b3c38965232f0af0f95e0c6d90920ecf600a74135c608aebae26302c036c01393a590e1918bb90 languageName: node linkType: hard @@ -15182,10 +15080,10 @@ __metadata: languageName: node linkType: hard -"tinykeys@npm:1.4.0": - version: 1.4.0 - resolution: "tinykeys@npm:1.4.0" - checksum: 1a285204116f1150d761a5c34bac9340e9c66a2b1a619311c3e0581168bc086a0ba9ae844774b82cb3393a121def5738c1f33f426135052d5acbf7863e9268d2 +"tinykeys@npm:2.1.0": + version: 2.1.0 + resolution: "tinykeys@npm:2.1.0" + checksum: 49e8f7cf09c29372758f434d072429ed5a93cd3148d218af9938ccbbac4e15a2697c93a34dcfe7138a11e569a2145b35709947e5d2b74e48a8e17c575b8beb7f languageName: node linkType: hard @@ -15304,19 +15202,20 @@ __metadata: languageName: node linkType: hard -"ts-lit-plugin@npm:1.2.1": - version: 1.2.1 - resolution: "ts-lit-plugin@npm:1.2.1" +"ts-lit-plugin@npm:2.0.0-pre.1": + version: 2.0.0-pre.1 + resolution: "ts-lit-plugin@npm:2.0.0-pre.1" dependencies: - lit-analyzer: 1.2.1 - checksum: 3ba191d8924b18ba1aa1072db82cd10bca19f20693d98735dc1bbf3692ec759f2d4c728b789a1c1ade4d96e5ddf25e574fdba7c5e42b557c7e82da7a1ad298d7 + lit-analyzer: ^2.0.0-pre.3 + web-component-analyzer: ^2.0.0-next.5 + checksum: d9c8b3c0d8867e9564c7a3083accaf9f8b48b04c8f42e32c224806a7a606983abb3b7acb912e57d0d8f90ff650745bb1af18b1e379316b433838e37eddccfe1e languageName: node linkType: hard -"ts-simple-type@npm:~1.0.5": - version: 1.0.7 - resolution: "ts-simple-type@npm:1.0.7" - checksum: 3cffb45eab7ab7fd963e2765914c41488d9611dc0619334ac1cf01bc2b02cf9746adf12172d785894474c6c5f3cfbf8212e675e456362373a0c2a61441ad8572 +"ts-simple-type@npm:2.0.0-next.0, ts-simple-type@npm:~2.0.0-next.0": + version: 2.0.0-next.0 + resolution: "ts-simple-type@npm:2.0.0-next.0" + checksum: 025c5c1e4f2f7f2627300b2605a0346d5007f9c3d20d075807b01b3ae8179261e6be2d471f74948f3bae3208ca042203d97e80b984d6cb133396c5c4a3af5301 languageName: node linkType: hard @@ -15340,95 +15239,95 @@ __metadata: linkType: hard "tslib@npm:^2.0.1, tslib@npm:^2.0.2, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": - version: 2.5.2 - resolution: "tslib@npm:2.5.2" - checksum: 4d3c1e238b94127ed0e88aa0380db3c2ddae581dc0f4bae5a982345e9f50ee5eda90835b8bfba99b02df10a5734470be197158c36f9129ac49fdc14a6a9da222 + version: 2.5.3 + resolution: "tslib@npm:2.5.3" + checksum: 88902b309afaf83259131c1e13da1dceb0ad1682a213143a1346a649143924d78cf3760c448b84d796938fd76127183894f8d85cbb3bf9c4fddbfcc140c0003c languageName: node linkType: hard -"tsparticles-engine@npm:2.9.3, tsparticles-engine@npm:^2.9.3": - version: 2.9.3 - resolution: "tsparticles-engine@npm:2.9.3" - checksum: 767b6c1568f9428706e62bd15fe91f290fd1a86e9641f00d848006165e829d414e954b95833a4360f2b0a9a2699fa6d340bb007cce22412b48b7692d4a6489f5 +"tsparticles-engine@npm:2.10.1, tsparticles-engine@npm:^2.10.1": + version: 2.10.1 + resolution: "tsparticles-engine@npm:2.10.1" + checksum: 5662c453b1e4c37c608435393a8c5ae03bd9b301dc488a3909bdddf321f38c486b6076d61641add3e2d8764c6e8eb28021e1b424da3240b6efa723c6e8bcbc43 languageName: node linkType: hard -"tsparticles-interaction-particles-links@npm:^2.9.3": - version: 2.9.3 - resolution: "tsparticles-interaction-particles-links@npm:2.9.3" +"tsparticles-interaction-particles-links@npm:^2.10.1": + version: 2.10.1 + resolution: "tsparticles-interaction-particles-links@npm:2.10.1" dependencies: - tsparticles-engine: ^2.9.3 - checksum: be551275a6fd4bb543b4a5eb5105df8f53aed799527e9e032ab282f383367a90b002e8d134432bd8828ef0c06ddd4c826a1bcc158335bf81e83c39b042358322 + tsparticles-engine: ^2.10.1 + checksum: 994fcac1cfcae34409095437db7684cc9f74b4e084995da310ea7f1a63c0a97030cac8fde47a7a54b5e8644e26a3b72fb5e6d229e10758eb1e6fcf52800b8e83 languageName: node linkType: hard -"tsparticles-move-base@npm:^2.9.3": - version: 2.9.3 - resolution: "tsparticles-move-base@npm:2.9.3" +"tsparticles-move-base@npm:^2.10.1": + version: 2.10.1 + resolution: "tsparticles-move-base@npm:2.10.1" dependencies: - tsparticles-engine: ^2.9.3 - checksum: 06b579ce3873daa8f706261c28c0fb5b9b60bf9682da6eb0ca4488e60b06e08d122b9e20613a65db2e7b4c535728dc54a24fcc8942fa79935fd5d4315de44e11 + tsparticles-engine: ^2.10.1 + checksum: 26fb88e7622dfbede4d0177d01645917b01611bf8be32508588e7d8d6e28fcaf58daf900095a7947382d59fe9fcd420563cc6fc6a50c67c9c7ea7236c601d9b7 languageName: node linkType: hard -"tsparticles-preset-links@npm:2.9.3": - version: 2.9.3 - resolution: "tsparticles-preset-links@npm:2.9.3" +"tsparticles-preset-links@npm:2.10.1": + version: 2.10.1 + resolution: "tsparticles-preset-links@npm:2.10.1" dependencies: - tsparticles-engine: ^2.9.3 - tsparticles-interaction-particles-links: ^2.9.3 - tsparticles-move-base: ^2.9.3 - tsparticles-shape-circle: ^2.9.3 - tsparticles-updater-color: ^2.9.3 - tsparticles-updater-opacity: ^2.9.3 - tsparticles-updater-out-modes: ^2.9.3 - tsparticles-updater-size: ^2.9.3 - checksum: fc867d5a7efc0d596a1cfe06159f8e872ff806eafd37a3b07d94537d0432c4839dab3b9c10797341a8526ca395c0e5cd4e91b551c3f4b55da298d2d6b5a938ce + tsparticles-engine: ^2.10.1 + tsparticles-interaction-particles-links: ^2.10.1 + tsparticles-move-base: ^2.10.1 + tsparticles-shape-circle: ^2.10.1 + tsparticles-updater-color: ^2.10.1 + tsparticles-updater-opacity: ^2.10.1 + tsparticles-updater-out-modes: ^2.10.1 + tsparticles-updater-size: ^2.10.1 + checksum: 0678ad77ae9022f3ceac6b462ad4e049e2a0f5559b035690698609df081d7d6307fb93c38d07f3fe6e0f517635bd6a4831b705b7ba6fc33f79a22d4a3ddf65fe languageName: node linkType: hard -"tsparticles-shape-circle@npm:^2.9.3": - version: 2.9.3 - resolution: "tsparticles-shape-circle@npm:2.9.3" +"tsparticles-shape-circle@npm:^2.10.1": + version: 2.10.1 + resolution: "tsparticles-shape-circle@npm:2.10.1" dependencies: - tsparticles-engine: ^2.9.3 - checksum: f770e6edd10e1935402ef19b1f5c051758c3a1f1722b64dae4f215f178bb83211abe8abde26bc87c220db1969457bc7729a77f5e3fcd00e44e7103bc10a13336 + tsparticles-engine: ^2.10.1 + checksum: 49c2a865745c6217b08006c11f134e64792e1d62abbc1dc6f2484165b6b13b6bd7205cd10171418697680e513bd3c8d128c7d9877c61540722250bb8e1693f02 languageName: node linkType: hard -"tsparticles-updater-color@npm:^2.9.3": - version: 2.9.3 - resolution: "tsparticles-updater-color@npm:2.9.3" +"tsparticles-updater-color@npm:^2.10.1": + version: 2.10.1 + resolution: "tsparticles-updater-color@npm:2.10.1" dependencies: - tsparticles-engine: ^2.9.3 - checksum: e602b4754c6bb11ec2475939131e4fff815b07dc0431f4df34b9fdd82567fd299f5648f61e323258126856a3b71f87e77e5cf97e3100fb3929204e53a726c01f + tsparticles-engine: ^2.10.1 + checksum: 6858b429fd59f244251584346d47748216774a9012a6ffb9a614f87ca83b957a6da2b0dffb1c9e5238c84c043c29a23eb828568baf4ddde76c73707a1a0652ba languageName: node linkType: hard -"tsparticles-updater-opacity@npm:^2.9.3": - version: 2.9.3 - resolution: "tsparticles-updater-opacity@npm:2.9.3" +"tsparticles-updater-opacity@npm:^2.10.1": + version: 2.10.1 + resolution: "tsparticles-updater-opacity@npm:2.10.1" dependencies: - tsparticles-engine: ^2.9.3 - checksum: 4bc735e014219615256469fc05330140e7d5d43e85a91c8400045c812108f73b3549f7d50b92b985cf12264e81bb5cf0e2093e4ba16adcb0c420bb9e71dc801e + tsparticles-engine: ^2.10.1 + checksum: f5a418dfe922db99cd6ee75a7accb901eebb4e4f34ef411be46a1d88c8893816b4f8487f7b100a0a1cda749fdecf1d052efaae994a8639a464b6d6829ec9ee8b languageName: node linkType: hard -"tsparticles-updater-out-modes@npm:^2.9.3": - version: 2.9.3 - resolution: "tsparticles-updater-out-modes@npm:2.9.3" +"tsparticles-updater-out-modes@npm:^2.10.1": + version: 2.10.1 + resolution: "tsparticles-updater-out-modes@npm:2.10.1" dependencies: - tsparticles-engine: ^2.9.3 - checksum: 2c695211254b39831987a465b35711899f7e13fc7512aac83b4019a948c22eab15c71ad61627cca34ea6b18b7370f88d5b5d07f743dab8d3ec3eb0b99b4ff684 + tsparticles-engine: ^2.10.1 + checksum: 0f78d2aae5fa03f1af32dbe9eab326d6ef59b4e63226a84a5f8ce91efb1beecd13e0465068de9c9cd70ba830c212552fbe3b01fd21c674368ba39071af335e9b languageName: node linkType: hard -"tsparticles-updater-size@npm:^2.9.3": - version: 2.9.3 - resolution: "tsparticles-updater-size@npm:2.9.3" +"tsparticles-updater-size@npm:^2.10.1": + version: 2.10.1 + resolution: "tsparticles-updater-size@npm:2.10.1" dependencies: - tsparticles-engine: ^2.9.3 - checksum: 13e0231a7469cd83ff74a20766980c8bce4480fc0e91841103431acf7df051e58ead2391f58947d03307bc20c6d3a0065c9a675f7c2f603b82fceffccc4e7c77 + tsparticles-engine: ^2.10.1 + checksum: c77ce515aa765f4e069d232ca7407105d86c432042593fffdd6ae33948f84a654cba7e41c0aef00c2327ed3b5715ab07e057153b93afe5c7a2a5917eee786e85 languageName: node linkType: hard @@ -15529,43 +15428,43 @@ __metadata: languageName: node linkType: hard -"typescript@npm:4.9.5": - version: 4.9.5 - resolution: "typescript@npm:4.9.5" +"typescript@npm:5.1.3": + version: 5.1.3 + resolution: "typescript@npm:5.1.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db + checksum: d9d51862d98efa46534f2800a1071a613751b1585dc78884807d0c179bcd93d6e9d4012a508e276742f5f33c480adefc52ffcafaf9e0e00ab641a14cde9a31c7 languageName: node linkType: hard -"typescript@npm:^3.8.3": - version: 3.9.10 - resolution: "typescript@npm:3.9.10" +"typescript@npm:~4.4.3": + version: 4.4.4 + resolution: "typescript@npm:4.4.4" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 46c842e2cd4797b88b66ef06c9c41dd21da48b95787072ccf39d5f2aa3124361bc4c966aa1c7f709fae0509614d76751455b5231b12dbb72eb97a31369e1ff92 + checksum: 89ecb8436bb48ef5594d49289f5f89103071716b6e4844278f4fb3362856e31203e187a9c76d205c3f0b674d221a058fd28310dbcbcf5d95e9a57229bb5203f1 languageName: node linkType: hard -"typescript@patch:typescript@4.9.5#~builtin": - version: 4.9.5 - resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=289587" +"typescript@patch:typescript@5.1.3#~builtin": + version: 5.1.3 + resolution: "typescript@patch:typescript@npm%3A5.1.3#~builtin::version=5.1.3&hash=5da071" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 1f8f3b6aaea19f0f67cba79057674ba580438a7db55057eb89cc06950483c5d632115c14077f6663ea76fd09fce3c190e6414bb98582ec80aa5a4eaf345d5b68 + checksum: 6f0a9dca6bf4ce9dcaf4e282aade55ef4c56ecb5fb98d0a4a5c0113398815aea66d871b5611e83353e5953a19ed9ef103cf5a76ac0f276d550d1e7cd5344f61e languageName: node linkType: hard -"typescript@patch:typescript@^3.8.3#~builtin": - version: 3.9.10 - resolution: "typescript@patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=3bd3d3" +"typescript@patch:typescript@~4.4.3#~builtin": + version: 4.4.4 + resolution: "typescript@patch:typescript@npm%3A4.4.4#~builtin::version=4.4.4&hash=bbeadb" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: dc7141ab555b23a8650a6787f98845fc11692063d02b75ff49433091b3af2fe3d773650dea18389d7c21f47d620fb3b110ea363dab4ab039417a6ccbbaf96fc2 + checksum: 3d1b04449662193544b81d055479d03b4c5dca95f1a82f8922596f089d894c9fefbe16639d1d9dfe26a7054419645530cef44001bc17aed1fe1eb3c237e9b3c7 languageName: node linkType: hard @@ -15677,21 +15576,21 @@ __metadata: languageName: node linkType: hard -"unique-filename@npm:^2.0.0": - version: 2.0.1 - resolution: "unique-filename@npm:2.0.1" +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" dependencies: - unique-slug: ^3.0.0 - checksum: 807acf3381aff319086b64dc7125a9a37c09c44af7620bd4f7f3247fcd5565660ac12d8b80534dcbfd067e6fe88a67e621386dd796a8af828d1337a8420a255f + unique-slug: ^4.0.0 + checksum: 8e2f59b356cb2e54aab14ff98a51ac6c45781d15ceaab6d4f1c2228b780193dc70fae4463ce9e1df4479cb9d3304d7c2043a3fb905bdeca71cc7e8ce27e063df languageName: node linkType: hard -"unique-slug@npm:^3.0.0": - version: 3.0.0 - resolution: "unique-slug@npm:3.0.0" +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" dependencies: imurmurhash: ^0.1.4 - checksum: 49f8d915ba7f0101801b922062ee46b7953256c93ceca74303bd8e6413ae10aa7e8216556b54dc5382895e8221d04f1efaf75f945c2e4a515b4139f77aa6640c + checksum: 0884b58365af59f89739e6f71e3feacb5b1b41f2df2d842d0757933620e6de08eff347d27e9d499b43c40476cbaf7988638d3acb2ffbcb9d35fd035591adfd15 languageName: node linkType: hard @@ -15759,7 +15658,7 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.10": +"update-browserslist-db@npm:^1.0.11": version: 1.0.11 resolution: "update-browserslist-db@npm:1.0.11" dependencies: @@ -15790,12 +15689,12 @@ __metadata: linkType: hard "url@npm:^0.11.0": - version: 0.11.0 - resolution: "url@npm:0.11.0" + version: 0.11.1 + resolution: "url@npm:0.11.1" dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - checksum: 50d100d3dd2d98b9fe3ada48cadb0b08aa6be6d3ac64112b867b56b19be4bfcba03c2a9a0d7922bfd7ac17d4834e88537749fe182430dfd9b68e520175900d90 + punycode: ^1.4.1 + qs: ^6.11.0 + checksum: a7de4b37bbcbe60ef199acda4ce437ef843c0ef3a4b34ec3e3d97e0446a5f50dc7bfeafbe33ad118cf4e5aa04805e1328f0d0126e254f2b77bb8498fa395c596 languageName: node linkType: hard @@ -16042,9 +15941,9 @@ __metadata: linkType: hard "w3c-keyname@npm:^2.2.4": - version: 2.2.7 - resolution: "w3c-keyname@npm:2.2.7" - checksum: 91e057b1ec28e0bafcaf28def12023f0e083fd473c40d0a9c2aa01a975d227200d75ff6d8eb6961bb4608b967b1df1dd86786b52ee9489cb9a2ebeed881a63ae + version: 2.2.8 + resolution: "w3c-keyname@npm:2.2.8" + checksum: 95bafa4c04fa2f685a86ca1000069c1ec43ace1f8776c10f226a73296caeddd83f893db885c2c220ebeb6c52d424e3b54d7c0c1e963bbf204038ff1a944fbb07 languageName: node linkType: hard @@ -16067,18 +15966,18 @@ __metadata: languageName: node linkType: hard -"web-component-analyzer@npm:~1.1.1": - version: 1.1.7 - resolution: "web-component-analyzer@npm:1.1.7" +"web-component-analyzer@npm:^2.0.0-next.5": + version: 2.0.0-next.5 + resolution: "web-component-analyzer@npm:2.0.0-next.5" dependencies: fast-glob: ^3.2.2 - ts-simple-type: ~1.0.5 - typescript: ^3.8.3 + ts-simple-type: 2.0.0-next.0 + typescript: ~4.4.3 yargs: ^15.3.1 bin: wca: cli.js web-component-analyzer: cli.js - checksum: 6c36521b7b79d5547ffdbc359029651ad1d929df6e09f8adfbafb2a34c23199712b7080f08f941f056b6a989718c11eb9221171d97ad397ed8a20cf08dd78e4b + checksum: bc8eaf57884b81d378014376bcab92bce6b127971952831dd5bb06803b590cc99fb6fa17c7476c02ee014dfccfcee80e670d7fdc7aff4aae6aebc2e262055a65 languageName: node linkType: hard @@ -16103,14 +16002,14 @@ __metadata: languageName: node linkType: hard -"webpack-cli@npm:5.1.1": - version: 5.1.1 - resolution: "webpack-cli@npm:5.1.1" +"webpack-cli@npm:5.1.4": + version: 5.1.4 + resolution: "webpack-cli@npm:5.1.4" dependencies: "@discoveryjs/json-ext": ^0.5.0 - "@webpack-cli/configtest": ^2.1.0 - "@webpack-cli/info": ^2.0.1 - "@webpack-cli/serve": ^2.0.4 + "@webpack-cli/configtest": ^2.1.1 + "@webpack-cli/info": ^2.0.2 + "@webpack-cli/serve": ^2.0.5 colorette: ^2.0.14 commander: ^10.0.1 cross-spawn: ^7.0.3 @@ -16131,7 +16030,7 @@ __metadata: optional: true bin: webpack-cli: bin/cli.js - checksum: 7738e6a84a0098886e1e0c0fd0dab44b7dedfbb0580afbb5ef734c5109dcaee80140bebb5d9f4b40f425029563bb09bcbda8b08d904fa14e60ff632e6dcc8a17 + checksum: 3a4ad0d0342a6815c850ee4633cc2a8a5dae04f918e7847f180bf24ab400803cf8a8943707ffbed03eb20fe6ce647f996f60a2aade87b0b4a9954da3da172ce0 languageName: node linkType: hard @@ -16150,9 +16049,9 @@ __metadata: languageName: node linkType: hard -"webpack-dev-server@npm:4.15.0": - version: 4.15.0 - resolution: "webpack-dev-server@npm:4.15.0" +"webpack-dev-server@npm:4.15.1": + version: 4.15.1 + resolution: "webpack-dev-server@npm:4.15.1" dependencies: "@types/bonjour": ^3.5.9 "@types/connect-history-api-fallback": ^1.3.5 @@ -16160,7 +16059,7 @@ __metadata: "@types/serve-index": ^1.9.1 "@types/serve-static": ^1.13.10 "@types/sockjs": ^0.3.33 - "@types/ws": ^8.5.1 + "@types/ws": ^8.5.5 ansi-html-community: ^0.0.8 bonjour-service: ^1.0.11 chokidar: ^3.5.3 @@ -16193,7 +16092,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: 6fe375089b061be2e4ed6d6a8b20743734d304cd0c34757271c6685f97642b028f253c627f899b629c97c067c294484f906e394fd1c104ee795237b8725f2701 + checksum: cd0063b068d2b938fd76c412d555374186ac2fa84bbae098265212ed50a5c15d6f03aa12a5a310c544a242943eb58c0bfde4c296d5c36765c182f53799e1bc71 languageName: node linkType: hard @@ -16210,12 +16109,12 @@ __metadata: linkType: hard "webpack-merge@npm:^5.7.3": - version: 5.8.0 - resolution: "webpack-merge@npm:5.8.0" + version: 5.9.0 + resolution: "webpack-merge@npm:5.9.0" dependencies: clone-deep: ^4.0.1 wildcard: ^2.0.0 - checksum: 88786ab91013f1bd2a683834ff381be81c245a4b0f63304a5103e90f6653f44dab496a0768287f8531761f8ad957d1f9f3ccb2cb55df0de1bd9ee343e079da26 + checksum: 64fe2c23aacc5f19684452a0e84ec02c46b990423aee6fcc5c18d7d471155bd14e9a6adb02bd3656eb3e0ac2532c8e97d69412ad14c97eeafe32fa6d10050872 languageName: node linkType: hard @@ -16236,9 +16135,9 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.84.1": - version: 5.84.1 - resolution: "webpack@npm:5.84.1" +"webpack@npm:5.88.0": + version: 5.88.0 + resolution: "webpack@npm:5.88.0" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^1.0.0 @@ -16249,7 +16148,7 @@ __metadata: acorn-import-assertions: ^1.9.0 browserslist: ^4.14.5 chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.14.1 + enhanced-resolve: ^5.15.0 es-module-lexer: ^1.2.1 eslint-scope: 5.1.1 events: ^3.2.0 @@ -16259,7 +16158,7 @@ __metadata: loader-runner: ^4.2.0 mime-types: ^2.1.27 neo-async: ^2.6.2 - schema-utils: ^3.1.2 + schema-utils: ^3.2.0 tapable: ^2.1.1 terser-webpack-plugin: ^5.3.7 watchpack: ^2.4.0 @@ -16269,7 +16168,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 646b645df5badf2dac2ddd0193c9e9a177d51283d18f918eead36a0cdf7b750c4111d9ac11d9825c1334cbd0a6fb8f82628fbfb90d0066f927265dd11b47b192 + checksum: 9fd1568b34ec2e99ba97c8509a15ab2576ec80c396e7015551ec814b24cfc11de173acba3e114dafe95f1a6d460781b09d6201e6a1fb15110e1d01a09f61a283 languageName: node linkType: hard @@ -16356,18 +16255,6 @@ __metadata: languageName: node linkType: hard -"which-collection@npm:^1.0.1": - version: 1.0.1 - resolution: "which-collection@npm:1.0.1" - dependencies: - is-map: ^2.0.1 - is-set: ^2.0.1 - is-weakmap: ^2.0.1 - is-weakset: ^2.0.1 - checksum: c815bbd163107ef9cb84f135e6f34453eaf4cca994e7ba85ddb0d27cea724c623fae2a473ceccfd5549c53cc65a5d82692de418166df3f858e1e5dc60818581c - languageName: node - linkType: hard - "which-module@npm:^1.0.0": version: 1.0.0 resolution: "which-module@npm:1.0.0" @@ -16823,9 +16710,9 @@ __metadata: linkType: hard "yaml@npm:^2.2.2": - version: 2.2.2 - resolution: "yaml@npm:2.2.2" - checksum: d90c235e099e30094dcff61ba3350437aef53325db4a6bcd04ca96e1bfe7e348b191f6a7a52b5211e2dbc4eeedb22a00b291527da030de7c189728ef3f2b4eb3 + version: 2.3.1 + resolution: "yaml@npm:2.3.1" + checksum: 2c7bc9a7cd4c9f40d3b0b0a98e370781b68b8b7c4515720869aced2b00d92f5da1762b4ffa947f9e795d6cd6b19f410bd4d15fdd38aca7bd96df59bd9486fb54 languageName: node linkType: hard