diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..b7b4ca2ddc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: weekly + time: "06:00" + open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4a67ddd5a0..c0f6369fb2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,9 +19,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: yarn @@ -43,9 +43,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: yarn @@ -62,9 +62,9 @@ jobs: needs: [lint, test] steps: - name: Check out files from GitHub - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: yarn @@ -81,9 +81,9 @@ jobs: needs: [lint, test] steps: - name: Check out files from GitHub - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f03f0dcbd0..a15a50c233 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@v2 + uses: actions/checkout@v3 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -36,14 +36,14 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -57,4 +57,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/demo.yaml b/.github/workflows/demo.yaml index 3896f292c0..05fd6a28e0 100644 --- a/.github/workflows/demo.yaml +++ b/.github/workflows/demo.yaml @@ -14,9 +14,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 4f7a0efb2d..e49906d686 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@v2.0.1 + - uses: dessant/lock-threads@v3.0.0 with: github-token: ${{ github.token }} issue-lock-inactive-days: "30" diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml new file mode 100644 index 0000000000..e272963950 --- /dev/null +++ b/.github/workflows/nightly.yaml @@ -0,0 +1,63 @@ +name: Nightly + +on: + workflow_dispatch: + schedule: + - cron: "0 1 * * *" + +env: + PYTHON_VERSION: 3.8 + NODE_VERSION: 14 + NODE_OPTIONS: --max_old_space_size=6144 + +permissions: + actions: none + +jobs: + nightly: + name: Nightly + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + + - name: Set up Python ${{ env.PYTHON_VERSION }} + uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Set up Node ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Install dependencies + run: yarn install + + - name: Download translations + run: ./script/translations_download + env: + LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} + + - name: Bump version + run: script/version_bump.js nightly + + - name: Build nightly Python wheels + run: | + pip install build + yarn install + + script/build_frontend + + rm -rf dist home_assistant_frontend.egg-info + python3 -m build + + - name: Upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist/home_assistant_frontend*.whl + if-no-files-found: error diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a556ef94a7..b3e7cf3d94 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -24,18 +24,18 @@ jobs: contents: write # Required to upload release assets steps: - name: Checkout the repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Verify version uses: home-assistant/actions/helpers/verify-version@master - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/translations.yaml b/.github/workflows/translations.yaml index 3224317199..0a798b1dc4 100644 --- a/.github/workflows/translations.yaml +++ b/.github/workflows/translations.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Upload Translations run: | diff --git a/build-scripts/env.js b/build-scripts/env.js index b12f20582f..e2eff4a4fa 100644 --- a/build-scripts/env.js +++ b/build-scripts/env.js @@ -27,7 +27,7 @@ module.exports = { version() { const version = fs .readFileSync(path.resolve(paths.polymer_dir, "pyproject.toml"), "utf8") - .match(/version\W+=\W"(\d{8}\.\d)"/); + .match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/); if (!version) { throw Error("Version not found"); } diff --git a/demo/src/configs/kernehed/entities.ts b/demo/src/configs/kernehed/entities.ts index f7020ee719..193a05179d 100644 --- a/demo/src/configs/kernehed/entities.ts +++ b/demo/src/configs/kernehed/entities.ts @@ -797,7 +797,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => attributes: { battery_level: 34, on: true, - friendly_name: "altan_motion_sensor", + friendly_name: "Porch motion sensor", device_class: "motion", }, }, @@ -818,7 +818,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => attributes: { battery_level: 74, on: true, - friendly_name: "badrumssensor", + friendly_name: "Bathroom motion sensor", device_class: "motion", }, }, @@ -829,7 +829,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => battery_level: 47, on: true, dark: true, - friendly_name: "R\u00f6relsesensor k\u00e4llaren 1", + friendly_name: "Basement motion sensor", device_class: "motion", icon: "mdi:walk", }, @@ -863,7 +863,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => attributes: { battery_level: 60, on: true, - friendly_name: "R\u00f6relsesensor skafferiet", + friendly_name: "Pantry motion sensor", device_class: "motion", icon: "mdi:walk", }, @@ -875,7 +875,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => battery_level: 60, on: true, dark: true, - friendly_name: "R\u00f6relsesensor k\u00e4llaren 2", + friendly_name: "Stair motion sensor", device_class: "motion", icon: "mdi:walk", }, @@ -887,7 +887,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () => battery_level: 47, on: true, dark: true, - friendly_name: "B\u00e4nksensor", + friendly_name: "Bench sensor", device_class: "motion", }, }, diff --git a/demo/src/configs/kernehed/lovelace.ts b/demo/src/configs/kernehed/lovelace.ts index b8550d2b62..5d8cc442e4 100644 --- a/demo/src/configs/kernehed/lovelace.ts +++ b/demo/src/configs/kernehed/lovelace.ts @@ -277,7 +277,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ ], show_header_toggle: false, type: "entities", - title: "Bandbredd", + title: "Bandwidth", }, // { // title: "Updater", diff --git a/package.json b/package.json index 76028454a6..b0f426ea1c 100644 --- a/package.json +++ b/package.json @@ -72,8 +72,8 @@ "@material/mwc-textfield": "0.25.3", "@material/mwc-top-app-bar-fixed": "^0.25.3", "@material/top-app-bar": "14.0.0-canary.261f2db59.0", - "@mdi/js": "6.7.96", - "@mdi/svg": "6.7.96", + "@mdi/js": "6.9.96", + "@mdi/svg": "6.9.96", "@polymer/app-layout": "^3.1.0", "@polymer/iron-flex-layout": "^3.0.1", "@polymer/iron-icon": "^3.0.1", diff --git a/pyproject.toml b/pyproject.toml index 9d88995977..08a642995e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20220630.0" +version = "20220705.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/script/version_bump.js b/script/version_bump.js index 7796f1451a..a1c8f9c9be 100755 --- a/script/version_bump.js +++ b/script/version_bump.js @@ -24,10 +24,15 @@ function auto(version) { return patch(version); } +function nightly() { + return `${today()}.dev`; +} + const methods = { patch, today, auto, + nightly, }; async function main(args) { @@ -57,7 +62,11 @@ async function main(args) { console.log("Current version:", version); console.log("New version:", newVersion); - fs.writeFileSync("pyproject.toml", setup.replace(version, newVersion), "utf-8"); + fs.writeFileSync( + "pyproject.toml", + setup.replace(version, newVersion), + "utf-8" + ); if (!commit) { return; diff --git a/src/components/chart/state-history-charts.ts b/src/components/chart/state-history-charts.ts index b00fb19cdf..415ecbb4e7 100644 --- a/src/components/chart/state-history-charts.ts +++ b/src/components/chart/state-history-charts.ts @@ -186,6 +186,7 @@ class StateHistoryCharts extends LitElement { line-height: 60px; color: var(--secondary-text-color); } + .container { max-height: var(--history-max-height); } diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts index 152e7a9a6f..a8f9938332 100644 --- a/src/components/ha-code-editor.ts +++ b/src/components/ha-code-editor.ts @@ -209,7 +209,7 @@ export class HaCodeEditor extends ReactiveElement { private _entityCompletions( context: CompletionContext ): CompletionResult | null | Promise { - const entityWord = context.matchBefore(/[a-z_]{3,}\./); + const entityWord = context.matchBefore(/[a-z_]{3,}\.\w*/); if ( !entityWord || @@ -227,7 +227,7 @@ export class HaCodeEditor extends ReactiveElement { return { from: Number(entityWord.from), options: states, - span: /^\w*.\w*$/, + span: /^[a-z_]{3,}\.\w*$/, }; } @@ -257,7 +257,7 @@ export class HaCodeEditor extends ReactiveElement { private async _mdiCompletions( context: CompletionContext ): Promise { - const match = context.matchBefore(/mdi:/); + const match = context.matchBefore(/mdi:\S*/); if (!match || (match.from === match.to && !context.explicit)) { return null; @@ -268,7 +268,7 @@ export class HaCodeEditor extends ReactiveElement { return { from: Number(match.from), options: iconItems, - span: /^\w*.\w*$/, + span: /^mdi:\S*$/, }; } diff --git a/src/components/ha-target-picker.ts b/src/components/ha-target-picker.ts index 7545a19a36..8df2bea571 100644 --- a/src/components/ha-target-picker.ts +++ b/src/components/ha-target-picker.ts @@ -42,8 +42,8 @@ import "./entity/ha-entity-picker"; import type { HaEntityPickerEntityFilterFunc } from "./entity/ha-entity-picker"; import "./ha-area-picker"; import "./ha-icon-button"; -import "./ha-svg-icon"; import "./ha-input-helper-text"; +import "./ha-svg-icon"; @customElement("ha-target-picker") export class HaTargetPicker extends SubscribeMixin(LitElement) { @@ -119,55 +119,68 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) { if (!this._areas || !this._devices || !this._entities) { return html``; } - return html`
- ${this.horizontal ? this._renderChips() : this._renderItems()} - ${this._renderPicker()} - ${this.horizontal ? this._renderItems() : this._renderChips()} -
`; + return html` + ${this.horizontal + ? html` +
+ ${this._renderChips()} ${this._renderPicker()} +
+ ${this._renderItems()} + ` + : html` +
+ ${this._renderItems()} ${this._renderPicker()} + ${this._renderChips()} +
+ `} + `; } private _renderItems() { - return html`
- ${this.value?.area_id - ? ensureArray(this.value.area_id).map((area_id) => { - const area = this._areas![area_id]; - return this._renderChip( - "area_id", - area_id, - area?.name || area_id, - undefined, - mdiSofa - ); - }) - : ""} - ${this.value?.device_id - ? ensureArray(this.value.device_id).map((device_id) => { - const device = this._devices![device_id]; - return this._renderChip( - "device_id", - device_id, - device ? computeDeviceName(device, this.hass) : device_id, - undefined, - mdiDevices - ); - }) - : ""} - ${this.value?.entity_id - ? ensureArray(this.value.entity_id).map((entity_id) => { - const entity = this.hass.states[entity_id]; - return this._renderChip( - "entity_id", - entity_id, - entity ? computeStateName(entity) : entity_id, - entity - ); - }) - : ""} -
`; + return html` +
+ ${this.value?.area_id + ? ensureArray(this.value.area_id).map((area_id) => { + const area = this._areas![area_id]; + return this._renderChip( + "area_id", + area_id, + area?.name || area_id, + undefined, + mdiSofa + ); + }) + : ""} + ${this.value?.device_id + ? ensureArray(this.value.device_id).map((device_id) => { + const device = this._devices![device_id]; + return this._renderChip( + "device_id", + device_id, + device ? computeDeviceName(device, this.hass) : device_id, + undefined, + mdiDevices + ); + }) + : ""} + ${this.value?.entity_id + ? ensureArray(this.value.entity_id).map((entity_id) => { + const entity = this.hass.states[entity_id]; + return this._renderChip( + "entity_id", + entity_id, + entity ? computeStateName(entity) : entity_id, + entity + ); + }) + : ""} +
+ `; } private _renderChips() { - return html`
+ return html` +
${this.helper ? html`${this.helper}` - : ""} `; + : ""} + `; } private async _showPicker(ev) { @@ -320,51 +334,54 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) { private _renderPicker() { switch (this._addMode) { case "area_id": - return html``; + return html` + + `; case "device_id": - return html``; + return html` + + `; case "entity_id": - return html``; + return html` + + `; } return html``; } @@ -553,15 +570,11 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) { static get styles(): CSSResultGroup { return css` ${unsafeCSS(chipStyles)} - .hidden-picker { - height: 0px; - display: inline-block; - overflow: hidden; - position: absolute; - } .horizontal-container { display: flex; flex-wrap: wrap; + min-height: 56px; + align-items: center; } .mdc-chip { color: var(--primary-text-color); diff --git a/src/onboarding/ha-onboarding.ts b/src/onboarding/ha-onboarding.ts index 3c05e5b680..28de65a955 100644 --- a/src/onboarding/ha-onboarding.ts +++ b/src/onboarding/ha-onboarding.ts @@ -124,7 +124,6 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); this._fetchOnboardingSteps(); - this._fetchInstallationType(); import("./onboarding-integrations"); import("./onboarding-core-config"); registerServiceWorker(this, false); @@ -215,6 +214,9 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { }); history.replaceState(null, "", location.pathname); await this._connectHass(auth); + } else { + // User creating screen needs to know the installation type. + this._fetchInstallationType(); } this._steps = steps; diff --git a/src/onboarding/onboarding-core-config.ts b/src/onboarding/onboarding-core-config.ts index b18f14653f..2abb8cc8c6 100644 --- a/src/onboarding/onboarding-core-config.ts +++ b/src/onboarding/onboarding-core-config.ts @@ -378,6 +378,10 @@ class OnboardingCoreConfig extends LitElement { color: var(--secondary-text-color); } + ha-locations-editor { + height: 200px; + } + .flex { flex: 1; } diff --git a/src/onboarding/onboarding-integrations.ts b/src/onboarding/onboarding-integrations.ts index 1c21b94008..ff89d020c0 100644 --- a/src/onboarding/onboarding-integrations.ts +++ b/src/onboarding/onboarding-integrations.ts @@ -30,7 +30,13 @@ import { HomeAssistant } from "../types"; import "./action-badge"; import "./integration-badge"; -const HIDDEN_DOMAINS = new Set(["hassio", "met", "radio_browser", "rpi_power"]); +const HIDDEN_DOMAINS = new Set([ + "hassio", + "met", + "radio_browser", + "rpi_power", + "sun", +]); @customElement("onboarding-integrations") class OnboardingIntegrations extends LitElement { @@ -75,7 +81,10 @@ class OnboardingIntegrations extends LitElement { // Render discovered and existing entries together sorted by localized title. const entries: Array<[string, TemplateResult]> = this._entries.map( (entry) => { - const title = domainToName(this.hass.localize, entry.domain); + const title = + entry.title || + domainToName(this.hass.localize, entry.domain) || + entry.domain; return [ title, html` diff --git a/src/panels/config/cloud/account/cloud-account.ts b/src/panels/config/cloud/account/cloud-account.ts index 46293adf67..e3a8fbd494 100644 --- a/src/panels/config/cloud/account/cloud-account.ts +++ b/src/panels/config/cloud/account/cloud-account.ts @@ -1,7 +1,5 @@ import "@material/mwc-button"; -import type { ActionDetail } from "@material/mwc-list"; import "@material/mwc-list/mwc-list-item"; -import { mdiDotsVertical } from "@mdi/js"; import "@polymer/paper-item/paper-item-body"; import { css, html, LitElement, PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators"; @@ -11,9 +9,7 @@ import { computeRTLDirection } from "../../../../common/util/compute_rtl"; import { debounce } from "../../../../common/util/debounce"; import "../../../../components/buttons/ha-call-api-button"; import "../../../../components/ha-alert"; -import "../../../../components/ha-button-menu"; import "../../../../components/ha-card"; -import "../../../../components/ha-icon-button"; import { cloudLogout, CloudStatusLoggedIn, @@ -23,6 +19,7 @@ import { import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; import "../../../../layouts/hass-subpage"; import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; +import { haStyle } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; import "../../ha-config-section"; import "./cloud-alexa-pref"; @@ -52,23 +49,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) { .narrow=${this.narrow} header="Home Assistant Cloud" > - - - - - ${this.hass.localize("ui.panel.config.cloud.account.sign_out")} - - -
Home Assistant Cloud @@ -156,6 +136,11 @@ export class CloudAccount extends SubscribeMixin(LitElement) { )} + + ${this.hass.localize( + "ui.panel.config.cloud.account.sign_out" + )} +
@@ -279,18 +264,15 @@ export class CloudAccount extends SubscribeMixin(LitElement) { } } - private async _handleMenuAction(ev: CustomEvent) { - switch (ev.detail.index) { - case 0: - showConfirmationDialog(this, { - text: this.hass.localize( - "ui.panel.config.cloud.account.sign_out_confirm" - ), - confirmText: this.hass!.localize("ui.common.yes"), - dismissText: this.hass!.localize("ui.common.no"), - confirm: () => this._logoutFromCloud(), - }); - } + private async _signOut() { + showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.config.cloud.account.sign_out_confirm" + ), + confirmText: this.hass!.localize("ui.common.yes"), + dismissText: this.hass!.localize("ui.common.no"), + confirm: () => this._logoutFromCloud(), + }); } private async _logoutFromCloud() { @@ -303,41 +285,39 @@ export class CloudAccount extends SubscribeMixin(LitElement) { } static get styles() { - return css` - [slot="introduction"] { - margin: -1em 0; - } - [slot="introduction"] a { - color: var(--primary-color); - } - .content { - padding-bottom: 24px; - } - .account-row { - display: flex; - padding: 0 16px; - } - .card-actions { - display: flex; - flex-direction: row-reverse; - } - .card-actions a { - text-decoration: none; - } - mwc-button { - align-self: center; - } - .wrap { - white-space: normal; - } - .status { - text-transform: capitalize; - padding: 16px; - } - a { - color: var(--primary-color); - } - `; + return [ + haStyle, + css` + [slot="introduction"] { + margin: -1em 0; + } + [slot="introduction"] a { + color: var(--primary-color); + } + .content { + padding-bottom: 24px; + } + .account-row { + display: flex; + padding: 0 16px; + } + .card-actions { + display: flex; + flex-direction: row-reverse; + justify-content: space-between; + } + mwc-button { + align-self: center; + } + .wrap { + white-space: normal; + } + .status { + text-transform: capitalize; + padding: 16px; + } + `, + ]; } } diff --git a/src/panels/config/cloud/account/cloud-tts-pref.ts b/src/panels/config/cloud/account/cloud-tts-pref.ts index c5eaea962f..6856595d96 100644 --- a/src/panels/config/cloud/account/cloud-tts-pref.ts +++ b/src/panels/config/cloud/account/cloud-tts-pref.ts @@ -185,6 +185,10 @@ export class CloudTTSPref extends LitElement { right: auto; left: 24px; } + .card-actions { + display: flex; + flex-direction: row-reverse; + } `; } } diff --git a/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts b/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts index d0843bc2ac..180d33adb0 100644 --- a/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts +++ b/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts @@ -2,7 +2,6 @@ import { getConfigEntries } from "../../../../../../data/config_entries"; import { DeviceRegistryEntry } from "../../../../../../data/device_registry"; import { fetchZwaveIsAnyFirmwareUpdateInProgress, - fetchZwaveNodeFirmwareUpdateCapabilities, fetchZwaveNodeIsFirmwareUpdateInProgress, fetchZwaveNodeStatus, } from "../../../../../../data/zwave_js"; @@ -87,20 +86,13 @@ export const getZwaveDeviceActions = async ( return actions; } - const [ - firmwareUpdateCapabilities, - isAnyFirmwareUpdateInProgress, - isNodeFirmwareUpdateInProgress, - ] = await Promise.all([ - fetchZwaveNodeFirmwareUpdateCapabilities(hass, device.id), - fetchZwaveIsAnyFirmwareUpdateInProgress(hass, entryId), - fetchZwaveNodeIsFirmwareUpdateInProgress(hass, device.id), - ]); + const [isAnyFirmwareUpdateInProgress, isNodeFirmwareUpdateInProgress] = + await Promise.all([ + fetchZwaveIsAnyFirmwareUpdateInProgress(hass, entryId), + fetchZwaveNodeIsFirmwareUpdateInProgress(hass, device.id), + ]); - if ( - firmwareUpdateCapabilities.firmware_upgradable && - (!isAnyFirmwareUpdateInProgress || isNodeFirmwareUpdateInProgress) - ) { + if (!isAnyFirmwareUpdateInProgress || isNodeFirmwareUpdateInProgress) { actions.push({ label: hass.localize( "ui.panel.config.zwave_js.device_info.update_firmware" @@ -117,7 +109,6 @@ export const getZwaveDeviceActions = async ( ) { showZWaveJUpdateFirmwareNodeDialog(el, { device, - firmwareUpdateCapabilities, }); } }, diff --git a/src/panels/config/info/ha-config-info.ts b/src/panels/config/info/ha-config-info.ts index 459531f6f1..3c87a70dd7 100644 --- a/src/panels/config/info/ha-config-info.ts +++ b/src/panels/config/info/ha-config-info.ts @@ -165,13 +165,6 @@ class HaConfigInfo extends LitElement { ` )} -

- ${this.hass.localize( - "ui.panel.config.info.path_configuration", - "path", - hass.config.config_dir - )} -

${!customUiList.length ? "" : html` @@ -202,7 +195,7 @@ class HaConfigInfo extends LitElement { if (((window as any).CUSTOM_UI_LIST || []).length !== customUI.length) { this.requestUpdate(); } - }, 1000); + }, 2000); if (isComponentLoaded(this.hass, "hassio")) { this._loadSupervisorInfo(); @@ -291,12 +284,6 @@ class HaConfigInfo extends LitElement { } } - .config-path { - color: var(--secondary-text-color); - text-align: center; - font-style: italic; - } - .custom-ui { color: var(--secondary-text-color); text-align: center; diff --git a/src/panels/config/integrations/ha-config-integrations.ts b/src/panels/config/integrations/ha-config-integrations.ts index 6ae2d3d81f..beb0bb0cb0 100644 --- a/src/panels/config/integrations/ha-config-integrations.ts +++ b/src/panels/config/integrations/ha-config-integrations.ts @@ -766,7 +766,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { display: flex; justify-content: flex-end; width: 100%; - margin-right: 8px; align-items: center; height: 56px; position: sticky; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts index b0f76d331d..00e4bfb437 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts @@ -6,7 +6,6 @@ import "@material/mwc-linear-progress/mwc-linear-progress"; import { mdiCheckCircle, mdiCloseCircle, mdiFileUpload } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import memoizeOne from "memoize-one"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { createCloseHeading } from "../../../../../components/ha-dialog"; @@ -26,7 +25,6 @@ import { ZWaveJSNodeFirmwareUpdateFinishedMessage, ZWaveJSNodeFirmwareUpdateProgressMessage, ZWaveJSNodeStatusUpdatedMessage, - ZWaveJSNodeFirmwareUpdateCapabilities, ZWaveJSNodeStatus, } from "../../../../../data/zwave_js"; import { haStyleDialog } from "../../../../../resources/styles"; @@ -66,12 +64,9 @@ class DialogZWaveJSUpdateFirmwareNode extends LitElement { private _deviceName?: string; - private _firmwareUpdateCapabilities?: ZWaveJSNodeFirmwareUpdateCapabilities; - public showDialog(params: ZWaveJSUpdateFirmwareNodeDialogParams): void { this._deviceName = computeDeviceName(params.device, this.hass!); this.device = params.device; - this._firmwareUpdateCapabilities = params.firmwareUpdateCapabilities; this._fetchData(); this._subscribeNodeStatus(); } @@ -84,7 +79,6 @@ class DialogZWaveJSUpdateFirmwareNode extends LitElement { this._updateFinishedMessage = this._firmwareFile = this._nodeStatus = - this._firmwareUpdateCapabilities = undefined; this._firmwareTarget = 0; this._uploading = this._updateInProgress = false; @@ -92,34 +86,21 @@ class DialogZWaveJSUpdateFirmwareNode extends LitElement { fireEvent(this, "dialog-closed", { dialog: this.localName }); } - private _schema = memoizeOne( - ( - firmwareUpdateCapabilities: ZWaveJSNodeFirmwareUpdateCapabilities - ): HaFormIntegerSchema => { - if (!firmwareUpdateCapabilities.firmware_upgradable) { - // We should never get here, this is to pass type checks - throw new Error(); - } - return { - name: "firmware_target", - type: "integer", - valueMin: Math.min(...firmwareUpdateCapabilities.firmware_targets), - valueMax: Math.max(...firmwareUpdateCapabilities.firmware_targets), - }; - } - ); - protected render(): TemplateResult { if ( !this.device || !this._nodeStatus || - !this._firmwareUpdateCapabilities || - !this._firmwareUpdateCapabilities.firmware_upgradable || this._updateInProgress === undefined ) { return html``; } + const schema: HaFormIntegerSchema = { + name: "firmware_target", + type: "integer", + valueMin: 0, + }; + const beginFirmwareUpdateHTML = html` - ${this._firmwareUpdateCapabilities.firmware_targets.length > 1 - ? html`

- ${this.hass.localize( - "ui.panel.config.zwave_js.update_firmware.firmware_target_intro" - )} -

- ` - : ""} +

+ ${this.hass.localize( + "ui.panel.config.zwave_js.update_firmware.firmware_target_intro" + )} +

+ ${beginFirmwareUpdateHTML}`} -

- ${this.hass.localize( - "ui.panel.config.zwave_js.update_firmware.finished_status.try_again" - )} -

- ${beginFirmwareUpdateHTML} `} `; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-update-firmware-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-update-firmware-node.ts index dc45f309f6..69c7cc4729 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-update-firmware-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-update-firmware-node.ts @@ -1,10 +1,8 @@ import { fireEvent } from "../../../../../common/dom/fire_event"; import { DeviceRegistryEntry } from "../../../../../data/device_registry"; -import { ZWaveJSNodeFirmwareUpdateCapabilities } from "../../../../../data/zwave_js"; export interface ZWaveJSUpdateFirmwareNodeDialogParams { device: DeviceRegistryEntry; - firmwareUpdateCapabilities: ZWaveJSNodeFirmwareUpdateCapabilities; } export const loadUpdateFirmwareNodeDialog = () => diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index 7832d9116c..22bcf8e876 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -107,6 +107,8 @@ export class HaSceneEditor extends SubscribeMixin( @state() private _entities: string[] = []; + private _single_entities: string[] = []; + @state() private _devices: string[] = []; @state() @@ -121,7 +123,7 @@ export class HaSceneEditor extends SubscribeMixin( private _unsubscribeEvents?: () => void; - @state() private _deviceEntityLookup: DeviceEntitiesLookup = {}; + private _deviceEntityLookup: DeviceEntitiesLookup = {}; private _activateContextId?: string; @@ -520,9 +522,11 @@ export class HaSceneEditor extends SubscribeMixin( } if (changedProps.has("_entityRegistryEntries")) { + this._deviceEntityLookup = {}; for (const entity of this._entityRegistryEntries) { if ( !entity.device_id || + entity.entity_category || SCENE_IGNORED_DOMAINS.includes(computeDomain(entity.entity_id)) ) { continue; @@ -530,13 +534,10 @@ export class HaSceneEditor extends SubscribeMixin( if (!(entity.device_id in this._deviceEntityLookup)) { this._deviceEntityLookup[entity.device_id] = []; } - if ( - !this._deviceEntityLookup[entity.device_id].includes(entity.entity_id) - ) { - this._deviceEntityLookup[entity.device_id].push(entity.entity_id); - } + this._deviceEntityLookup[entity.device_id].push(entity.entity_id); if ( this._entities.includes(entity.entity_id) && + !this._single_entities.includes(entity.device_id) && !this._devices.includes(entity.device_id) ) { this._devices = [...this._devices, entity.device_id]; @@ -625,12 +626,24 @@ export class HaSceneEditor extends SubscribeMixin( private _initEntities(config: SceneConfig) { this._entities = Object.keys(config.entities); this._entities.forEach((entity) => this._storeState(entity)); + this._single_entities = []; const filteredEntityReg = this._entityRegistryEntries.filter((entityReg) => this._entities.includes(entityReg.entity_id) ); const newDevices: string[] = []; + if (config.metadata) { + Object.keys(config.entities).forEach((entity) => { + if ( + !this._single_entities.includes(entity) && + config.metadata![entity].entity_only + ) { + this._single_entities.push(entity); + } + }); + } + for (const entityReg of filteredEntityReg) { if (!entityReg.device_id) { continue; @@ -654,6 +667,7 @@ export class HaSceneEditor extends SubscribeMixin( return; } this._entities = [...this._entities, entityId]; + this._single_entities.push(entityId); this._storeState(entityId); this._dirty = true; } @@ -664,6 +678,9 @@ export class HaSceneEditor extends SubscribeMixin( this._entities = this._entities.filter( (entityId) => entityId !== deleteEntityId ); + this._single_entities = this._single_entities.filter( + (entityId) => entityId !== deleteEntityId + ); this._dirty = true; } @@ -815,19 +832,15 @@ export class HaSceneEditor extends SubscribeMixin( private _calculateMetaData(): SceneMetaData { const output: SceneMetaData = {}; - for (const entityReg of this._entityRegistryEntries) { - if (!this._entities.includes(entityReg.entity_id)) { - continue; - } - - const entityState = this._getCurrentState(entityReg.entity_id); + for (const entityId of this._single_entities) { + const entityState = this._getCurrentState(entityId); if (!entityState) { continue; } - output[entityReg.entity_id] = { - entity_only: !this._devices.includes(entityReg.device_id!), + output[entityId] = { + entity_only: true, }; } diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts index 9deb2cf1b1..06d8be2834 100644 --- a/src/panels/history/ha-panel-history.ts +++ b/src/panels/history/ha-panel-history.ts @@ -1,4 +1,4 @@ -import { mdiRefresh } from "@mdi/js"; +import { mdiCollapseAll, mdiRefresh } from "@mdi/js"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; import { @@ -10,9 +10,12 @@ import { startOfWeek, startOfYesterday, } from "date-fns/esm"; +import { UnsubscribeFunc } from "home-assistant-js-websocket/dist/types"; import { css, html, LitElement, PropertyValues } from "lit"; import { property, state } from "lit/decorators"; -import { UnsubscribeFunc } from "home-assistant-js-websocket/dist/types"; +import { LocalStorage } from "../../common/decorators/local-storage"; +import { computeDomain } from "../../common/entity/compute_domain"; +import { computeStateName } from "../../common/entity/compute_state_name"; import { navigate } from "../../common/navigate"; import { createSearchParam, @@ -20,46 +23,62 @@ import { } from "../../common/url/search-params"; import { computeRTL } from "../../common/util/compute_rtl"; import "../../components/chart/state-history-charts"; -import "../../components/ha-target-picker"; 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-menu-button"; -import { computeHistory, fetchDateWS } from "../../data/history"; -import "../../layouts/ha-app-layout"; -import { haStyle } from "../../resources/styles"; -import { HomeAssistant } from "../../types"; +import "../../components/ha-target-picker"; +import { + DeviceRegistryEntry, + subscribeDeviceRegistry, +} from "../../data/device_registry"; import { EntityRegistryEntry, subscribeEntityRegistry, } from "../../data/entity_registry"; +import { computeHistory, fetchDateWS } from "../../data/history"; +import "../../layouts/ha-app-layout"; import { SubscribeMixin } from "../../mixins/subscribe-mixin"; -import { computeStateName } from "../../common/entity/compute_state_name"; -import { computeDomain } from "../../common/entity/compute_domain"; +import { haStyle } from "../../resources/styles"; +import { HomeAssistant } from "../../types"; class HaPanelHistory extends SubscribeMixin(LitElement) { - @property() hass!: HomeAssistant; + @property({ attribute: false }) hass!: HomeAssistant; @property({ reflect: true, type: Boolean }) narrow!: boolean; - @property() _startDate: Date; - - @property() _endDate: Date; - - @property() _targetPickerValue?; - - @property() _isLoading = false; - - @property() _stateHistory?; - @property({ reflect: true, type: Boolean }) rtl = false; + @state() private _startDate: Date; + + @state() private _endDate: Date; + + @LocalStorage("historyPickedValue", true, false) private _targetPickerValue?; + + @state() private _isLoading = false; + + @state() private _stateHistory?; + @state() private _ranges?: DateRangePickerRanges; - @state() private _entities?: EntityRegistryEntry[]; + @state() private _devices?: { [deviceId: string]: DeviceRegistryEntry }; - @state() private _stateEntities?: EntityRegistryEntry[]; + @state() private _entities?: { [entityId: string]: EntityRegistryEntry }; + + @state() private _stateEntities?: { [entityId: string]: EntityRegistryEntry }; + + @state() private _deviceIdToEntities?: { + [deviceId: string]: EntityRegistryEntry[]; + }; + + @state() private _areaIdToEntities?: { + [areaId: string]: EntityRegistryEntry[]; + }; + + @state() private _areaIdToDevices?: { + [areaId: string]: DeviceRegistryEntry[]; + }; public constructor() { super(); @@ -76,7 +95,52 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { public hassSubscribe(): UnsubscribeFunc[] { return [ subscribeEntityRegistry(this.hass.connection!, (entities) => { - this._entities = entities; + this._entities = entities.reduce((accumulator, current) => { + accumulator[current.entity_id] = current; + return accumulator; + }, {}); + this._deviceIdToEntities = entities.reduce((accumulator, current) => { + if (!current.device_id) { + return accumulator; + } + let found = accumulator[current.device_id]; + if (found === undefined) { + found = []; + accumulator[current.device_id] = found; + } + found.push(current); + return accumulator; + }, {}); + this._areaIdToEntities = entities.reduce((accumulator, current) => { + if (!current.area_id) { + return accumulator; + } + let found = accumulator[current.area_id]; + if (found === undefined) { + found = []; + accumulator[current.area_id] = found; + } + found.push(current); + return accumulator; + }, {}); + }), + subscribeDeviceRegistry(this.hass.connection!, (devices) => { + this._devices = devices.reduce((accumulator, current) => { + accumulator[current.id] = current; + return accumulator; + }, {}); + this._areaIdToDevices = devices.reduce((accumulator, current) => { + if (!current.area_id) { + return accumulator; + } + let found = accumulator[current.area_id]; + if (found === undefined) { + found = []; + accumulator[current.area_id] = found; + } + found.push(current); + return accumulator; + }, {}); }), ]; } @@ -91,8 +155,18 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { .narrow=${this.narrow} >
${this.hass.localize("panel.history")}
+ ${this._targetPickerValue + ? html` + + ` + : ""}
` + : !this._targetPickerValue + ? html`` : html` `}
- ${this._isLoading - ? html`
- -
` - : html` - - - `} `; } @@ -197,62 +257,75 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { protected updated(changedProps: PropertyValues) { if ( - changedProps.has("_startDate") || - changedProps.has("_endDate") || - changedProps.has("_targetPickerValue") || - changedProps.has("_entities") + this._targetPickerValue && + (changedProps.has("_startDate") || + changedProps.has("_endDate") || + changedProps.has("_targetPickerValue") || + (!this._stateHistory && + (changedProps.has("_entities") || + changedProps.has("_devices") || + changedProps.has("_stateEntities") || + changedProps.has("_deviceIdToEntities") || + changedProps.has("_areaIdToEntities") || + changedProps.has("_areaIdToDevices")))) ) { this._getHistory(); } - if (changedProps.has("hass") || changedProps.has("_entities")) { - const oldHass = changedProps.get("hass") as HomeAssistant | undefined; - if (!oldHass || oldHass.language !== this.hass.language) { - this.rtl = computeRTL(this.hass); - } - if (this._entities) { - const stateEntities: EntityRegistryEntry[] = []; - const regEntityIds = new Set( - this._entities.map((entity) => entity.entity_id) - ); - for (const entityId of Object.keys(this.hass.states)) { - if (regEntityIds.has(entityId)) { - continue; - } - stateEntities.push({ - name: computeStateName(this.hass.states[entityId]), - entity_id: entityId, - platform: computeDomain(entityId), - disabled_by: null, - hidden_by: null, - area_id: null, - config_entry_id: null, - device_id: null, - icon: null, - entity_category: null, - }); + if (!changedProps.has("hass") && !changedProps.has("_entities")) { + return; + } + + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + if (!oldHass || oldHass.language !== this.hass.language) { + this.rtl = computeRTL(this.hass); + } + + if (this._entities) { + const stateEntities: { [entityId: string]: EntityRegistryEntry } = {}; + const regEntityIds = new Set(Object.keys(this._entities)); + for (const entityId of Object.keys(this.hass.states)) { + if (regEntityIds.has(entityId)) { + continue; } - this._stateEntities = stateEntities; + stateEntities[entityId] = { + name: computeStateName(this.hass.states[entityId]), + entity_id: entityId, + platform: computeDomain(entityId), + disabled_by: null, + hidden_by: null, + area_id: null, + config_entry_id: null, + device_id: null, + icon: null, + entity_category: null, + }; } + this._stateEntities = stateEntities; } } - private _refreshHistory() { - this._getHistory(); + private _removeAll() { + this._targetPickerValue = undefined; + this._updatePath(); } private async _getHistory() { this._isLoading = true; const entityIds = this._getEntityIds(); - const dateHistory = - entityIds.length === 0 - ? {} - : await fetchDateWS( - this.hass, - this._startDate, - this._endDate, - entityIds - ); + + if (!entityIds.length) { + this._stateHistory = undefined; + return; + } + + const dateHistory = await fetchDateWS( + this.hass, + this._startDate, + this._endDate, + entityIds + ); + this._stateHistory = computeHistory( this.hass, dateHistory, @@ -261,50 +334,90 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { this._isLoading = false; } - private _filterEntity(entity: EntityRegistryEntry): boolean { - const { area_id, device_id, entity_id } = this._targetPickerValue; - if (area_id !== undefined) { - if (typeof area_id === "string" && area_id === entity.area_id) { - return true; - } - if (Array.isArray(area_id) && area_id.includes(entity.area_id)) { - return true; - } - } - if (device_id !== undefined) { - if (typeof device_id === "string" && device_id === entity.device_id) { - return true; - } - if (Array.isArray(device_id) && device_id.includes(entity.device_id)) { - return true; - } - } - if (entity_id !== undefined) { - if (typeof entity_id === "string" && entity_id === entity.entity_id) { - return true; - } - if (Array.isArray(entity_id) && entity_id.includes(entity.entity_id)) { - return true; - } - } - return false; - } - private _getEntityIds(): string[] { if ( this._targetPickerValue === undefined || this._entities === undefined || - this._stateEntities === undefined + this._stateEntities === undefined || + this._devices === undefined || + this._deviceIdToEntities === undefined || + this._areaIdToEntities === undefined || + this._areaIdToDevices === undefined ) { return []; } - const entityIds = this._entities - .filter((entity) => this._filterEntity(entity)) - .map((entity) => entity.entity_id); - const stateEntityIds = this._stateEntities - .filter((entity) => this._filterEntity(entity)) - .map((entity) => entity.entity_id); - return [...entityIds, ...stateEntityIds]; + const entityIds = new Set(); + let { + area_id: searchingAreaId, + device_id: searchingDeviceId, + entity_id: searchingEntityId, + } = this._targetPickerValue; + + if (searchingAreaId !== undefined) { + searchingAreaId = + typeof searchingAreaId === "string" + ? [searchingAreaId] + : searchingAreaId; + for (const singleSearchingAreaId of searchingAreaId) { + const foundEntities = this._areaIdToEntities[singleSearchingAreaId]; + if (!foundEntities) { + continue; + } + + for (const foundEntity of foundEntities) { + if (foundEntity.entity_category === null) { + entityIds.add(foundEntity.entity_id); + } + } + + const foundDevices = this._areaIdToDevices[singleSearchingAreaId]; + if (foundDevices !== undefined) { + for (const foundDevice of foundDevices) { + const foundDeviceEntities = + this._deviceIdToEntities[foundDevice.id]; + for (const foundDeviceEntity of foundDeviceEntities) { + if ( + (!foundDeviceEntity.area_id || + foundDeviceEntity.area_id === singleSearchingAreaId) && + foundDeviceEntity.entity_category === null + ) { + entityIds.add(foundDeviceEntity.entity_id); + } + } + } + } + } + } + + if (searchingDeviceId !== undefined) { + searchingDeviceId = + typeof searchingDeviceId === "string" + ? [searchingDeviceId] + : searchingDeviceId; + for (const singleSearchingDeviceId of searchingDeviceId) { + const foundEntities = this._deviceIdToEntities[singleSearchingDeviceId]; + if (!foundEntities) { + continue; + } + + for (const foundEntity of foundEntities) { + if (foundEntity.entity_category === null) { + entityIds.add(foundEntity.entity_id); + } + } + } + } + + if (searchingEntityId !== undefined) { + searchingEntityId = + typeof searchingEntityId === "string" + ? [searchingEntityId] + : searchingEntityId; + for (const singleSearchingEntityId of searchingEntityId) { + entityIds.add(singleSearchingEntityId); + } + } + return [...entityIds]; } private _dateRangeChanged(ev) { @@ -321,7 +434,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { private _entitiesChanged(ev) { this._targetPickerValue = ev.detail.value; - this._updatePath(); } @@ -389,7 +501,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { .filters { display: flex; - align-items: flex-end; + align-items: flex-start; padding: 8px 16px 0; } @@ -429,9 +541,21 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { max-width: none; width: 100%; } + + .start-search { + padding-top: 16px; + text-align: center; + color: var(--secondary-text-color); + } `, ]; } } customElements.define("ha-panel-history", HaPanelHistory); + +declare global { + interface HTMLElementTagNameMap { + "ha-panel-history": HaPanelHistory; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index 5d22072f24..cc53fac488 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1593,7 +1593,6 @@ "copy_github": "For GitHub", "description": "Version, loaded integrations and links to documentation", "home_assistant_logo": "Home Assistant logo", - "path_configuration": "Path to configuration.yaml: {path}", "developed_by": "Developed by a bunch of awesome people.", "license": "Published under the Apache 2.0 license", "source": "Source:", @@ -4012,7 +4011,8 @@ "month": "Month", "year": "Year", "previous": "Previous", - "next": "Next" + "next": "Next", + "compare": "Compare Data" } }, "reload_lovelace": "Reload UI" @@ -4537,6 +4537,9 @@ } }, "energy": { + "compare": { + "info": "You are comparing the period {start} with the period {end}" + }, "setup": { "next": "Next", "back": "Back", @@ -4556,6 +4559,11 @@ "energy_sources_table_title": "Sources", "energy_devices_graph_title": "Monitor individual devices" } + }, + "history": { + "start_search": "Start by selecting an area, device or entity above", + "add_all": "Add all entities", + "remove_all": "Remove all selections" } }, "tips": { diff --git a/yarn.lock b/yarn.lock index 492b213e6e..d21c1833a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2975,17 +2975,17 @@ __metadata: languageName: node linkType: hard -"@mdi/js@npm:6.7.96": - version: 6.7.96 - resolution: "@mdi/js@npm:6.7.96" - checksum: 8c8f6acb8fd3f856a92ffe2405e258ee5aa84cf541fda1c0a564c9c8bbf935cf2b6a6100cf97d41e9ada1ccb59e4b138d4c712e075f759d7595e21ef1cff84b5 +"@mdi/js@npm:6.9.96": + version: 6.9.96 + resolution: "@mdi/js@npm:6.9.96" + checksum: 94c43271585981e7ebf8cc5e3ead11915eea1339b79e849b351fbaae58e9626aaca0a8b031f0e80754d880ea6b08fd715906588f3cf980603bb5f6871bff12cc languageName: node linkType: hard -"@mdi/svg@npm:6.7.96": - version: 6.7.96 - resolution: "@mdi/svg@npm:6.7.96" - checksum: 959332009b8833d0347e2dfac86028362a6d11996db850025b7da8c493d7fd341a14d8716ef775dd2ed0492158c77236bcf09adfc4ae77b31044a0a8a26fc74b +"@mdi/svg@npm:6.9.96": + version: 6.9.96 + resolution: "@mdi/svg@npm:6.9.96" + checksum: 3e45f9a6632b0aa22f02ab0023d44131e3aea308bae85160445fe1f8f0b6d61df5c01e2756d18bd1461f7314fbf7c00a397bf99670f55803d90de5fa8e3fe33e languageName: node linkType: hard @@ -9047,8 +9047,8 @@ fsevents@^1.2.7: "@material/mwc-textfield": 0.25.3 "@material/mwc-top-app-bar-fixed": ^0.25.3 "@material/top-app-bar": 14.0.0-canary.261f2db59.0 - "@mdi/js": 6.7.96 - "@mdi/svg": 6.7.96 + "@mdi/js": 6.9.96 + "@mdi/svg": 6.9.96 "@open-wc/dev-server-hmr": ^0.0.2 "@polymer/app-layout": ^3.1.0 "@polymer/iron-flex-layout": ^3.0.1