From 7c4b9a0410da28c1da5dbfd5c807d91420fe50bc Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 1 Feb 2022 18:18:14 +0100 Subject: [PATCH] 20220201.0 (#11508) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paulus Schoutsen Co-authored-by: Thomas Lovén Co-authored-by: Zack Barett Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Co-authored-by: Philip Allgaier Co-authored-by: Yosi Levy <37745463+yosilevy@users.noreply.github.com> Co-authored-by: J. Nick Koston Co-authored-by: Patrick ZAJDA Co-authored-by: Steve Repsher --- .github/workflows/release.yaml | 2 +- MANIFEST.in | 1 - build-scripts/env.js | 6 +- package.json | 10 +-- pyproject.toml | 3 + script/release | 4 +- script/version_bump.js | 4 +- setup.cfg | 21 +++++ setup.py | 19 ++--- .../chart/state-history-chart-line.ts | 31 ++------ src/components/ha-form/ha-form-integer.ts | 11 +-- src/components/ha-form/ha-form.ts | 2 +- .../media-player/ha-media-player-browse.ts | 5 +- src/data/device_registry.ts | 8 +- src/data/media-player.ts | 13 +--- src/data/media_source.ts | 10 +++ .../config-flow/dialog-data-entry-flow.ts | 24 ++++-- src/dialogs/quick-bar/ha-quick-bar.ts | 15 ++-- src/entrypoints/app.ts | 6 +- src/entrypoints/authorize.ts | 3 + src/entrypoints/custom-panel.ts | 5 +- src/entrypoints/onboarding.ts | 3 + .../config/areas/ha-config-area-page.ts | 6 +- .../config/cloud/account/cloud-google-pref.ts | 1 + .../config/dashboard/ha-config-dashboard.ts | 33 +++++++- .../device-detail/ha-device-info-card.ts | 10 ++- .../dialog-device-registry-detail.ts | 16 +++- .../config/devices/ha-config-device-page.ts | 77 ++++++++++++++++--- .../devices/ha-config-devices-dashboard.ts | 2 +- .../entities/entity-registry-settings.ts | 2 +- .../config/entities/ha-config-entities.ts | 4 +- .../ozw/ozw-config-dashboard.ts | 32 ++++++++ .../zwave/ha-config-zwave.js | 33 ++++++++ .../ha-config-lovelace-dashboards.ts | 33 ++++++-- .../energy/cards/energy-setup-wizard-card.ts | 7 +- .../energy/hui-energy-devices-graph-card.ts | 15 ++++ .../media-browser/ha-bar-media-player.ts | 49 +++++++----- .../media-browser/ha-panel-media-browser.ts | 18 +++++ src/resources/compatibility.ts | 1 + src/translations/en.json | 52 ++++++++----- yarn.lock | 44 +++++------ 41 files changed, 467 insertions(+), 174 deletions(-) create mode 100644 pyproject.toml create mode 100644 setup.cfg diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 87b99837d5..1ae009b033 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -41,7 +41,7 @@ jobs: LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} - name: Build and release package run: | - python3 -m pip install twine + python3 -m pip install twine build export TWINE_USERNAME="__token__" export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}" diff --git a/MANIFEST.in b/MANIFEST.in index 703dc5da5c..384d62c072 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,4 @@ include README.md -include LICENSE.md graft hass_frontend graft hass_frontend_es5 recursive-exclude * *.py[co] diff --git a/build-scripts/env.js b/build-scripts/env.js index bda99cf010..1320c137dd 100644 --- a/build-scripts/env.js +++ b/build-scripts/env.js @@ -26,11 +26,11 @@ module.exports = { }, version() { const version = fs - .readFileSync(path.resolve(paths.polymer_dir, "setup.py"), "utf8") - .match(/\d{8}\.\d+/); + .readFileSync(path.resolve(paths.polymer_dir, "setup.cfg"), "utf8") + .match(/version\W+=\W(\d{8}\.\d)/); if (!version) { throw Error("Version not found"); } - return version[0]; + return version[1]; }, }; diff --git a/package.json b/package.json index fe80d89127..2e31be62fd 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "js-yaml": "^4.1.0", "leaflet": "^1.7.1", "leaflet-draw": "^1.0.4", - "lit": "^2.0.2", + "lit": "^2.1.2", "lit-vaadin-helpers": "^0.2.1", "marked": "^3.0.2", "memoize-one": "^5.2.1", @@ -236,10 +236,10 @@ "resolutions": { "@polymer/polymer": "patch:@polymer/polymer@3.4.1#./.yarn/patches/@polymer/polymer/pr-5569.patch", "@webcomponents/webcomponentsjs": "^2.2.10", - "lit": "^2.0.2", - "lit-html": "2.0.1", - "lit-element": "3.0.1", - "@lit/reactive-element": "1.0.1" + "lit": "^2.1.2", + "lit-html": "2.1.2", + "lit-element": "3.1.2", + "@lit/reactive-element": "1.2.1" }, "main": "src/home-assistant.js", "husky": { diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..cad8f17be0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools~=60.5", "wheel~=0.37.1"] +build-backend = "setuptools.build_meta" diff --git a/script/release b/script/release index f2d6e91a08..3b981bf503 100755 --- a/script/release +++ b/script/release @@ -11,6 +11,6 @@ yarn install script/build_frontend -rm -rf dist -python3 setup.py -q sdist +rm -rf dist home_assistant_frontend.egg-info +python3 -m build python3 -m twine upload dist/* --skip-existing diff --git a/script/version_bump.js b/script/version_bump.js index 35ad851f15..74fc3770e0 100755 --- a/script/version_bump.js +++ b/script/version_bump.js @@ -50,14 +50,14 @@ async function main(args) { return; } - const setup = fs.readFileSync("setup.py", "utf8"); + const setup = fs.readFileSync("setup.cfg", "utf8"); const version = setup.match(/\d{8}\.\d+/)[0]; const newVersion = method(version); console.log("Current version:", version); console.log("New version:", newVersion); - fs.writeFileSync("setup.py", setup.replace(version, newVersion), "utf-8"); + fs.writeFileSync("setup.cfg", setup.replace(version, newVersion), "utf-8"); if (!commit) { return; diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000..11dbe9fcf2 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,21 @@ +[metadata] +name = home-assistant-frontend +version = 20220201.0 +author = The Home Assistant Authors +author_email = hello@home-assistant.io +license = Apache-2.0 +platforms = any +description = The Home Assistant frontend +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/home-assistant/frontend + +[options] +packages = find: +zip_safe = False +include_package_data = True +python_requires = >= 3.4.0 + +[options.packages.find] +include = + hass_frontend* diff --git a/setup.py b/setup.py index 7417efc3bd..69bf65dd8a 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,7 @@ -from setuptools import setup, find_packages +""" +Entry point for setuptools. Required for editable installs. +TODO: Remove file after updating to pip 21.3 +""" +from setuptools import setup -setup( - name="home-assistant-frontend", - version="20220127.0", - description="The Home Assistant frontend", - url="https://github.com/home-assistant/frontend", - author="The Home Assistant Authors", - author_email="hello@home-assistant.io", - license="Apache-2.0", - packages=find_packages(include=["hass_frontend", "hass_frontend.*"]), - include_package_data=True, - zip_safe=False, -) +setup() diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index dde233a2e8..f355595c1d 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -183,12 +183,7 @@ class StateHistoryChartLine extends LitElement { prevValues = datavalues; }; - const addDataSet = ( - nameY: string, - step = false, - fill = false, - color?: string - ) => { + const addDataSet = (nameY: string, fill = false, color?: string) => { if (!color) { color = getGraphColorByIndex(colorIndex, computedStyles); colorIndex++; @@ -198,7 +193,7 @@ class StateHistoryChartLine extends LitElement { fill: fill ? "origin" : false, borderColor: color, backgroundColor: color + "7F", - stepped: step ? "before" : false, + stepped: "before", pointRadius: 0, data: [], }); @@ -239,14 +234,12 @@ class StateHistoryChartLine extends LitElement { addDataSet( `${this.hass.localize("ui.card.climate.current_temperature", { name: name, - })}`, - true + })}` ); if (hasHeat) { addDataSet( `${this.hass.localize("ui.card.climate.heating", { name: name })}`, true, - true, computedStyles.getPropertyValue("--state-climate-heat-color") ); // The "heating" series uses steppedArea to shade the area below the current @@ -256,7 +249,6 @@ class StateHistoryChartLine extends LitElement { addDataSet( `${this.hass.localize("ui.card.climate.cooling", { name: name })}`, true, - true, computedStyles.getPropertyValue("--state-climate-cool-color") ); // The "cooling" series uses steppedArea to shade the area below the current @@ -268,22 +260,19 @@ class StateHistoryChartLine extends LitElement { `${this.hass.localize("ui.card.climate.target_temperature_mode", { name: name, mode: this.hass.localize("ui.card.climate.high"), - })}`, - true + })}` ); addDataSet( `${this.hass.localize("ui.card.climate.target_temperature_mode", { name: name, mode: this.hass.localize("ui.card.climate.low"), - })}`, - true + })}` ); } else { addDataSet( `${this.hass.localize("ui.card.climate.target_temperature_entity", { name: name, - })}`, - true + })}` ); } @@ -318,14 +307,12 @@ class StateHistoryChartLine extends LitElement { addDataSet( `${this.hass.localize("ui.card.humidifier.target_humidity_entity", { name: name, - })}`, - true + })}` ); addDataSet( `${this.hass.localize("ui.card.humidifier.on_entity", { name: name, })}`, - true, true ); @@ -337,9 +324,7 @@ class StateHistoryChartLine extends LitElement { pushData(new Date(entityState.last_changed), series); }); } else { - // Only interpolate for sensors - const isStep = domain !== "sensor"; - addDataSet(name, isStep); + addDataSet(name); let lastValue: number; let lastDate: Date; diff --git a/src/components/ha-form/ha-form-integer.ts b/src/components/ha-form/ha-form-integer.ts index 5ed8abcdaf..e8ec34f13f 100644 --- a/src/components/ha-form/ha-form-integer.ts +++ b/src/components/ha-form/ha-form-integer.ts @@ -1,6 +1,5 @@ import "@material/mwc-textfield"; import type { TextField } from "@material/mwc-textfield"; -import "@material/mwc-slider"; import type { Slider } from "@material/mwc-slider"; import { css, @@ -14,6 +13,7 @@ import { customElement, property, query } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; import { HaCheckbox } from "../ha-checkbox"; import { HaFormElement, HaFormIntegerData, HaFormIntegerSchema } from "./types"; +import "../ha-slider"; @customElement("ha-form-integer") export class HaFormInteger extends LitElement implements HaFormElement { @@ -54,15 +54,16 @@ export class HaFormInteger extends LitElement implements HaFormElement { > ` : ""} - + > `; @@ -168,7 +169,7 @@ export class HaFormInteger extends LitElement implements HaFormElement { .flex { display: flex; } - mwc-slider { + ha-slider { flex: 1; } mwc-textfield { diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index 6a5da55448..e96f5243e5 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -104,7 +104,7 @@ export class HaForm extends LitElement implements HaFormElement { return css` .root { margin-bottom: -24px; - overflow: auto; + overflow: clip visible; } .root > * { display: block; diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 4910271298..6eccdd42a9 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -30,7 +30,6 @@ import { debounce } from "../../common/util/debounce"; import { getSignedPath } from "../../data/auth"; import type { MediaPlayerItem } from "../../data/media-player"; import { - browseLocalMediaPlayer, browseMediaPlayer, BROWSER_PLAYER, MediaClassBrowserSettings, @@ -50,6 +49,7 @@ import "../ha-circular-progress"; import "../ha-icon-button"; import "../ha-svg-icon"; import "../ha-fab"; +import { browseLocalMediaPlayer } from "../../data/media_source"; declare global { interface HASSDomEvents { @@ -417,6 +417,9 @@ export class HaMediaPlayerBrowse extends LitElement { if (!changedProps.has("navigateIds")) { return; } + + this._setError(undefined); + const oldNavigateIds = changedProps.get("navigateIds") as | this["navigateIds"] | undefined; diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts index b2610c109c..65c0bcec56 100644 --- a/src/data/device_registry.ts +++ b/src/data/device_registry.ts @@ -55,7 +55,13 @@ export const computeDeviceName = ( device.name_by_user || device.name || (entities && fallbackDeviceName(hass, entities)) || - hass.localize("ui.panel.config.devices.unnamed_device"); + hass.localize( + "ui.panel.config.devices.unnamed_device", + "type", + hass.localize( + `ui.panel.config.devices.type.${device.entry_type || "device"}` + ) + ); export const devicesInArea = (devices: DeviceRegistryEntry[], areaId: string) => devices.filter((device) => device.area_id === areaId); diff --git a/src/data/media-player.ts b/src/data/media-player.ts index ee15481785..48f3351dbb 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -185,15 +185,6 @@ export const browseMediaPlayer = ( media_content_type: mediaContentType, }); -export const browseLocalMediaPlayer = ( - hass: HomeAssistant, - mediaContentId?: string -): Promise => - hass.callWS({ - type: "media_source/browse_media", - media_content_id: mediaContentId, - }); - export const getCurrentProgress = (stateObj: MediaPlayerEntity): number => { let progress = stateObj.attributes.media_position!; @@ -321,8 +312,8 @@ export const computeMediaControls = ( return buttons.length > 0 ? buttons : undefined; }; -export const formatMediaTime = (seconds: number): string => { - if (!seconds) { +export const formatMediaTime = (seconds: number | undefined): string => { + if (seconds === undefined) { return ""; } diff --git a/src/data/media_source.ts b/src/data/media_source.ts index 0b2b70b989..759be2a7d3 100644 --- a/src/data/media_source.ts +++ b/src/data/media_source.ts @@ -1,4 +1,5 @@ import { HomeAssistant } from "../types"; +import { MediaPlayerItem } from "./media-player"; export interface ResolvedMediaSource { url: string; @@ -13,3 +14,12 @@ export const resolveMediaSource = ( type: "media_source/resolve_media", media_content_id, }); + +export const browseLocalMediaPlayer = ( + hass: HomeAssistant, + mediaContentId?: string +): Promise => + hass.callWS({ + type: "media_source/browse_media", + media_content_id: mediaContentId, + }); diff --git a/src/dialogs/config-flow/dialog-data-entry-flow.ts b/src/dialogs/config-flow/dialog-data-entry-flow.ts index c8273d7825..29bea7491e 100644 --- a/src/dialogs/config-flow/dialog-data-entry-flow.ts +++ b/src/dialogs/config-flow/dialog-data-entry-flow.ts @@ -116,15 +116,14 @@ class DataEntryFlowDialog extends LitElement { params.continueFlowId ); } catch (err: any) { - this._step = undefined; - this._params = undefined; + this.closeDialog(); showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.integrations.config_flow.error" ), - text: this.hass.localize( + text: `${this.hass.localize( "ui.panel.config.integrations.config_flow.could_not_load" - ), + )}: ${err.message || err.body}`, }); return; } @@ -177,6 +176,7 @@ class DataEntryFlowDialog extends LitElement { }); } + this._loading = undefined; this._step = undefined; this._params = undefined; this._devices = undefined; @@ -372,15 +372,14 @@ class DataEntryFlowDialog extends LitElement { try { step = await this._params!.flowConfig.createFlow(this.hass, handler); } catch (err: any) { - this._step = undefined; - this._params = undefined; + this.closeDialog(); showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.integrations.config_flow.error" ), - text: this.hass.localize( + text: `${this.hass.localize( "ui.panel.config.integrations.config_flow.could_not_load" - ), + )}: ${err.message || err.body}`, }); return; } finally { @@ -405,6 +404,15 @@ class DataEntryFlowDialog extends LitElement { this._loading = "loading_step"; try { this._step = await step; + } catch (err: any) { + this.closeDialog(); + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_flow.error" + ), + text: err.message || err.body, + }); + return; } finally { this._loading = undefined; } diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 161a32403c..1f9920fa32 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -185,13 +185,14 @@ export class QuickBar extends LitElement { .label=${this.hass!.localize("ui.common.clear")} .path=${mdiClose} >`} - ${this._narrow && - html` - - `} + ${this._narrow + ? html` + + ` + : ""} ` : ""} diff --git a/src/entrypoints/app.ts b/src/entrypoints/app.ts index d4d1852c20..e93a38769e 100644 --- a/src/entrypoints/app.ts +++ b/src/entrypoints/app.ts @@ -1,7 +1,11 @@ -import { setPassiveTouchGestures } from "@polymer/polymer/lib/utils/settings"; +import { + setPassiveTouchGestures, + setCancelSyntheticClickEvents, +} from "@polymer/polymer/lib/utils/settings"; import "../layouts/home-assistant"; import "../resources/ha-style"; import "../resources/roboto"; import "../util/legacy-support"; setPassiveTouchGestures(true); +setCancelSyntheticClickEvents(false); diff --git a/src/entrypoints/authorize.ts b/src/entrypoints/authorize.ts index 36954a3fcf..47ba124a91 100644 --- a/src/entrypoints/authorize.ts +++ b/src/entrypoints/authorize.ts @@ -1,11 +1,14 @@ // Compat needs to be first import import "../resources/compatibility"; +import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings"; import "../auth/ha-authorize"; import "../resources/ha-style"; import "../resources/roboto"; import "../resources/safari-14-attachshadow-patch"; import "../resources/array.flat.polyfill"; +setCancelSyntheticClickEvents(false); + /* polyfill for paper-dropdown */ setTimeout( () => import("web-animations-js/web-animations-next-lite.min"), diff --git a/src/entrypoints/custom-panel.ts b/src/entrypoints/custom-panel.ts index e2da175ea8..2e09f9931b 100644 --- a/src/entrypoints/custom-panel.ts +++ b/src/entrypoints/custom-panel.ts @@ -1,5 +1,6 @@ // Compat needs to be first import import "../resources/compatibility"; +import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings"; import "../resources/safari-14-attachshadow-patch"; import { PolymerElement } from "@polymer/polymer"; @@ -15,6 +16,8 @@ import { createCustomPanelElement } from "../util/custom-panel/create-custom-pan import { loadCustomPanel } from "../util/custom-panel/load-custom-panel"; import { setCustomPanelProperties } from "../util/custom-panel/set-custom-panel-properties"; +setCancelSyntheticClickEvents(false); + declare global { interface Window { loadES5Adapter: () => Promise; @@ -47,7 +50,7 @@ function initialize( ) { const style = document.createElement("style"); - style.innerHTML = `body { margin:0; } + style.innerHTML = `body { margin:0; } @media (prefers-color-scheme: dark) { body { background-color: #111111; diff --git a/src/entrypoints/onboarding.ts b/src/entrypoints/onboarding.ts index 44a67890ab..78696169ca 100644 --- a/src/entrypoints/onboarding.ts +++ b/src/entrypoints/onboarding.ts @@ -1,11 +1,14 @@ // Compat needs to be first import import "../resources/compatibility"; +import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings"; import "../onboarding/ha-onboarding"; import "../resources/ha-style"; import "../resources/roboto"; import "../resources/safari-14-attachshadow-patch"; import "../resources/array.flat.polyfill"; +setCancelSyntheticClickEvents(false); + declare global { interface Window { stepsPromise: Promise; diff --git a/src/panels/config/areas/ha-config-area-page.ts b/src/panels/config/areas/ha-config-area-page.ts index 0a971181cb..dfb038e375 100644 --- a/src/panels/config/areas/ha-config-area-page.ts +++ b/src/panels/config/areas/ha-config-area-page.ts @@ -315,7 +315,7 @@ class HaConfigAreaPage extends LitElement { ? html` ${groupedAutomations?.length @@ -362,7 +362,7 @@ class HaConfigAreaPage extends LitElement { ? html` ${groupedScenes?.length @@ -401,7 +401,7 @@ class HaConfigAreaPage extends LitElement { ? html` ${groupedScripts?.length diff --git a/src/panels/config/cloud/account/cloud-google-pref.ts b/src/panels/config/cloud/account/cloud-google-pref.ts index e7f1ec6339..7e605d165d 100644 --- a/src/panels/config/cloud/account/cloud-google-pref.ts +++ b/src/panels/config/cloud/account/cloud-google-pref.ts @@ -137,6 +137,7 @@ export class CloudGooglePref extends LitElement { "ui.panel.config.cloud.account.google.enter_pin_info" )} @@ -129,6 +139,26 @@ class HaConfigDashboard extends LitElement { `; } + protected override updated(changedProps: PropertyValues): void { + super.updated(changedProps); + + if (!changedProps.has("supervisorUpdates") || !this._notifyUpdates) { + return; + } + this._notifyUpdates = false; + if (this.supervisorUpdates?.length) { + showToast(this, { + message: this.hass.localize( + "ui.panel.config.updates.updates_refreshed" + ), + }); + } else { + showToast(this, { + message: this.hass.localize("ui.panel.config.updates.no_new_updates"), + }); + } + } + private _showQuickBar(): void { showQuickBar(this, { commandMode: true, @@ -140,6 +170,7 @@ class HaConfigDashboard extends LitElement { switch (ev.detail.index) { case 0: if (isComponentLoaded(this.hass, "hassio")) { + this._notifyUpdates = true; await refreshSupervisorAvailableUpdates(this.hass); fireEvent(this, "ha-refresh-supervisor"); return; diff --git a/src/panels/config/devices/device-detail/ha-device-info-card.ts b/src/panels/config/devices/device-detail/ha-device-info-card.ts index c2541397a3..19176d0fbe 100644 --- a/src/panels/config/devices/device-detail/ha-device-info-card.ts +++ b/src/panels/config/devices/device-detail/ha-device-info-card.ts @@ -23,7 +23,15 @@ export class HaDeviceCard extends LitElement { protected render(): TemplateResult { return html`
${this.device.model 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 8ad148eef4..677820ce12 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 @@ -82,12 +82,26 @@ class DialogDeviceRegistryDetail extends LitElement {
- ${this.hass.localize("ui.panel.config.devices.enabled_label")} + ${this.hass.localize( + "ui.panel.config.devices.enabled_label", + "type", + this.hass.localize( + `ui.panel.config.devices.type.${ + device.entry_type || "device" + }` + ) + )}
${this._disabledBy && this._disabledBy !== "user" ? this.hass.localize( "ui.panel.config.devices.enabled_cause", + "type", + this.hass.localize( + `ui.panel.config.devices.type.${ + device.entry_type || "device" + }` + ), "cause", this.hass.localize( `config_entry.disabled_by.${this._disabledBy}` diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 935c305cc7..58db797b3c 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -296,6 +296,10 @@ export class HaConfigDevicePage extends LitElement { ${this.hass.localize( "ui.panel.config.devices.enabled_cause", + "type", + this.hass.localize( + `ui.panel.config.devices.type.${device.entry_type || "device"}` + ), "cause", this.hass.localize( `ui.panel.config.devices.disabled_by.${device.disabled_by}` @@ -485,17 +489,29 @@ export class HaConfigDevicePage extends LitElement {

${this.hass.localize( - "ui.panel.config.devices.automation.automations" + "ui.panel.config.devices.automation.automations_heading" )} @@ -547,6 +563,12 @@ export class HaConfigDevicePage extends LitElement { "name", this.hass.localize( "ui.panel.config.devices.automation.automations" + ), + "type", + this.hass.localize( + `ui.panel.config.devices.type.${ + device.entry_type || "device" + }` ) )}

@@ -561,7 +583,7 @@ export class HaConfigDevicePage extends LitElement {

${this.hass.localize( - "ui.panel.config.devices.scene.scenes" + "ui.panel.config.devices.scene.scenes_heading" )} @@ -627,6 +661,12 @@ export class HaConfigDevicePage extends LitElement { "name", this.hass.localize( "ui.panel.config.devices.scene.scenes" + ), + "type", + this.hass.localize( + `ui.panel.config.devices.type.${ + device.entry_type || "device" + }` ) )}

@@ -641,17 +681,29 @@ export class HaConfigDevicePage extends LitElement {

${this.hass.localize( - "ui.panel.config.devices.script.scripts" + "ui.panel.config.devices.script.scripts_heading" )} @@ -685,6 +737,12 @@ export class HaConfigDevicePage extends LitElement { "name", this.hass.localize( "ui.panel.config.devices.script.scripts" + ), + "type", + this.hass.localize( + `ui.panel.config.devices.type.${ + device.entry_type || "device" + }` ) )}

@@ -1065,6 +1123,7 @@ export class HaConfigDevicePage extends LitElement { align-self: center; align-items: center; display: flex; + white-space: nowrap; } .column > *:not(:first-child) { diff --git a/src/panels/config/devices/ha-config-devices-dashboard.ts b/src/panels/config/devices/ha-config-devices-dashboard.ts index e195c09825..8b541f711a 100644 --- a/src/panels/config/devices/ha-config-devices-dashboard.ts +++ b/src/panels/config/devices/ha-config-devices-dashboard.ts @@ -338,7 +338,7 @@ export class HaConfigDeviceDashboard extends LitElement { ${this.hass.localize("ui.panel.config.devices.disabled")} ` - : "", + : "—", }; } return columns; diff --git a/src/panels/config/entities/entity-registry-settings.ts b/src/panels/config/entities/entity-registry-settings.ts index 798d9e1412..a62338b472 100644 --- a/src/panels/config/entities/entity-registry-settings.ts +++ b/src/panels/config/entities/entity-registry-settings.ts @@ -42,7 +42,7 @@ import type { HomeAssistant } from "../../../types"; import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail"; const OVERRIDE_DEVICE_CLASSES = { - cover: ["window", "door", "garage"], + cover: ["window", "door", "garage", "gate"], binary_sensor: ["window", "door", "garage_door", "opening"], }; diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 47271028c8..fb91132d8c 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -237,7 +237,9 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { template: (disabled_by) => this.hass.localize( `ui.panel.config.devices.disabled_by.${disabled_by}` - ) || disabled_by, + ) || + disabled_by || + "—", }, status: { title: this.hass.localize( diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts index ade3da83aa..dcdb81518d 100644 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts @@ -21,6 +21,7 @@ import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant, Route } from "../../../../../types"; import "../../../ha-config-section"; +import "../../../../../components/ha-alert"; export const ozwTabs: PageNavigation[] = []; @@ -64,6 +65,30 @@ class OZWConfigDashboard extends LitElement { .tabs=${ozwTabs} back-path="/config/integrations" > + + The OpenZWave integration is deprecated and will no longer receive any + updates. The technical dependencies will render this integration + unusable in the near future. We strongly advise you to migrate to the + new + Z-Wave JS integration. + + learn more + + +
${this.hass.localize("ui.panel.config.ozw.select_instance.header")} @@ -162,6 +187,13 @@ class OZWConfigDashboard extends LitElement { :host([narrow]) ha-config-section { margin-top: -20px; } + ha-alert { + display: block; + margin: 16px; + } + ha-alert a { + text-decoration: none; + } ha-card { overflow: hidden; } diff --git a/src/panels/config/integrations/integration-panels/zwave/ha-config-zwave.js b/src/panels/config/integrations/integration-panels/zwave/ha-config-zwave.js index f72844f373..91081d5158 100644 --- a/src/panels/config/integrations/integration-panels/zwave/ha-config-zwave.js +++ b/src/panels/config/integrations/integration-panels/zwave/ha-config-zwave.js @@ -12,6 +12,7 @@ import { computeStateName } from "../../../../../common/entity/compute_state_nam import { sortStatesByName } from "../../../../../common/entity/states_sort_by_name"; import "../../../../../components/buttons/ha-call-service-button"; import "../../../../../components/ha-card"; +import "../../../../../components/ha-alert"; import "../../../../../components/ha-icon"; import "../../../../../components/ha-icon-button"; import "../../../../../components/ha-icon-button-arrow-prev"; @@ -43,6 +44,14 @@ class HaConfigZwave extends LocalizeMixin(EventsMixin(PolymerElement)) { border-bottom: 1px solid var(--divider-color); } + ha-alert { + display: block; + margin: 16px; + } + ha-alert a { + text-decoration: none; + } + .content { margin-top: 24px; } @@ -101,6 +110,30 @@ class HaConfigZwave extends LocalizeMixin(EventsMixin(PolymerElement)) { + + This Z-Wave integration is deprecated and will no longer receive any + updates. The technical dependencies will render this integration + unusable in the near future. We strongly advise you to migrate to the + new + Z-Wave JS integration. + + learn more + + + + template: (icon, dashboard) => icon - ? html` ` + ? html` + + ` : html``, }, title: { @@ -64,7 +75,6 @@ export class HaConfigLovelaceDashboards extends LitElement { ), sortable: true, filterable: true, - direction: "asc", grows: true, template: (title, dashboard: any) => { const titleTemplate = html` @@ -194,12 +204,8 @@ export class HaConfigLovelaceDashboards extends LitElement { url_path: "lovelace", mode: defaultMode, filename: defaultMode === "yaml" ? "ui-lovelace.yaml" : "", + iconColor: "var(--primary-color)", }, - ...dashboards.map((dashboard) => ({ - filename: "", - ...dashboard, - default: defaultUrlPath === dashboard.url_path, - })), ]; if (isComponentLoaded(this.hass, "energy")) { result.push({ @@ -209,8 +215,19 @@ export class HaConfigLovelaceDashboards extends LitElement { mode: "storage", url_path: "energy", filename: "", + iconColor: "var(--label-badge-yellow)", }); } + + result.push( + ...dashboards + .sort((a, b) => stringCompare(a.title, b.title)) + .map((dashboard) => ({ + filename: "", + ...dashboard, + default: defaultUrlPath === dashboard.url_path, + })) + ); return result; }); diff --git a/src/panels/energy/cards/energy-setup-wizard-card.ts b/src/panels/energy/cards/energy-setup-wizard-card.ts index 57a1ee52e7..2ab0b23500 100644 --- a/src/panels/energy/cards/energy-setup-wizard-card.ts +++ b/src/panels/energy/cards/energy-setup-wizard-card.ts @@ -51,7 +51,12 @@ export class EnergySetupWizard extends LitElement implements LovelaceCard { protected render(): TemplateResult { return html` -

Step ${this._step + 1} of 5

+

+ ${this.hass.localize("ui.panel.energy.setup.step", { + step: this._step + 1, + steps: 5, + })} +

${this._step === 0 ? html` 35 ? "month" : dayDifference > 2 ? "day" : "hour" ); + const startMinHour = addHours(energyData.start, -1); + + Object.values(this._data).forEach((stat) => { + // if the start of the first value is after the requested period, we have the first data point, and should add a zero point + if (stat.length && new Date(stat[0].start) > startMinHour) { + stat.unshift({ + ...stat[0], + start: startMinHour.toISOString(), + end: startMinHour.toISOString(), + sum: 0, + state: 0, + }); + } + }); + const data: Array>["data"]> = []; const borderColor: string[] = []; const backgroundColor: string[] = []; diff --git a/src/panels/media-browser/ha-bar-media-player.ts b/src/panels/media-browser/ha-bar-media-player.ts index 1a2f67d0e7..65a02e1fc6 100644 --- a/src/panels/media-browser/ha-bar-media-player.ts +++ b/src/panels/media-browser/ha-bar-media-player.ts @@ -115,7 +115,9 @@ class BarMediaPlayer extends LitElement { protected render(): TemplateResult { const isBrowser = this.entityId === BROWSER_PLAYER; const stateObj = this._stateObj; - const controls = !this.narrow + const controls = !stateObj + ? undefined + : !this.narrow ? computeMediaControls(stateObj) : (stateObj.state === "playing" && (supportsFeature(stateObj, SUPPORT_PAUSE) || @@ -144,13 +146,16 @@ class BarMediaPlayer extends LitElement { }, ] : [{}]; - const mediaDescription = computeMediaDescription(stateObj); - const mediaDuration = formatMediaTime(stateObj!.attributes.media_duration!); - const mediaTitleClean = cleanupMediaTitle(stateObj.attributes.media_title); + const mediaDescription = stateObj ? computeMediaDescription(stateObj) : ""; + const mediaDuration = formatMediaTime(stateObj?.attributes.media_duration); + const mediaTitleClean = cleanupMediaTitle( + stateObj?.attributes.media_title || "" + ); - const mediaArt = - stateObj.attributes.entity_picture_local || - stateObj.attributes.entity_picture; + const mediaArt = stateObj + ? stateObj.attributes.entity_picture_local || + stateObj.attributes.entity_picture + : undefined; return html`
this._updateProgressBar(), @@ -296,21 +301,20 @@ class BarMediaPlayer extends LitElement { ); } else if ( this._progressInterval && - (!this._showProgressBar || stateObj.state !== "playing") + (!this._showProgressBar || stateObj?.state !== "playing") ) { clearInterval(this._progressInterval); this._progressInterval = undefined; } } - private get _stateObj(): MediaPlayerEntity { - if (this._browserPlayer) { - return this._browserPlayer.toStateObj(); + private get _stateObj(): MediaPlayerEntity | undefined { + if (this.entityId === BROWSER_PLAYER) { + return this._browserPlayer + ? this._browserPlayer.toStateObj() + : BrowserMediaPlayer.idleStateObj(); } - return ( - (this.hass!.states[this.entityId] as MediaPlayerEntity | undefined) || - BrowserMediaPlayer.idleStateObj() - ); + return this.hass!.states[this.entityId] as MediaPlayerEntity | undefined; } private _openMoreInfo() { @@ -328,6 +332,7 @@ class BarMediaPlayer extends LitElement { const stateObj = this._stateObj; return ( + stateObj && (stateObj.state === "playing" || stateObj.state === "paused") && "media_duration" in stateObj.attributes && "media_position" in stateObj.attributes @@ -343,19 +348,21 @@ class BarMediaPlayer extends LitElement { } private _updateProgressBar(): void { - if (!this._progressBar || !this._currentProgress) { + const stateObj = this._stateObj; + + if (!this._progressBar || !this._currentProgress || !stateObj) { return; } - if (!this._stateObj.attributes.media_duration) { + if (!stateObj.attributes.media_duration) { this._progressBar.progress = 0; this._currentProgress.innerHTML = ""; return; } - const currentProgress = getCurrentProgress(this._stateObj); + const currentProgress = getCurrentProgress(stateObj); this._progressBar.progress = - currentProgress / this._stateObj.attributes.media_duration; + currentProgress / stateObj.attributes.media_duration; if (this._currentProgress) { this._currentProgress.innerHTML = formatMediaTime(currentProgress); diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index ef8a9ba04a..95931759cb 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -28,6 +28,7 @@ import { haStyle } from "../../resources/styles"; import type { HomeAssistant, Route } from "../../types"; import "./ha-bar-media-player"; import { showWebBrowserPlayMediaDialog } from "./show-media-player-dialog"; +import { showAlertDialog } from "../../dialogs/generic/show-dialog-box"; @customElement("ha-panel-media-browser") class PanelMediaBrowser extends LitElement { @@ -112,6 +113,23 @@ class PanelMediaBrowser extends LitElement { .split("/"); if (routePlayer !== this._entityId) { + // Detect if picked player doesn't exist (anymore) + // Can happen if URL bookmarked or stored in local storage + if ( + routePlayer !== BROWSER_PLAYER && + this.hass.states[routePlayer] === undefined + ) { + navigate(`/media-browser/${BROWSER_PLAYER}`, { replace: true }); + showAlertDialog(this, { + text: this.hass.localize( + "ui.panel.media-browser.error.player_not_exist", + { + name: routePlayer, + } + ), + }); + return; + } this._entityId = routePlayer; } diff --git a/src/resources/compatibility.ts b/src/resources/compatibility.ts index 03a0b23fec..07f80f0ff8 100644 --- a/src/resources/compatibility.ts +++ b/src/resources/compatibility.ts @@ -1,3 +1,4 @@ +// Caution before editing - For latest builds, this module is replaced with emptiness and thus not imported (see build-scripts/bundle.js) import "core-js"; import "regenerator-runtime/runtime"; import "lit/polyfill-support"; diff --git a/src/translations/en.json b/src/translations/en.json index 672b952ef8..ba969edb2a 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -733,6 +733,7 @@ "cover": { "door": "Door", "garage": "Garage door", + "gate": "Gate", "window": "Window" } }, @@ -1011,6 +1012,8 @@ "description": "You need to run the Home Assistant operating system to be able to check and install updates from the Home Assistant user interface." }, "check_updates": "Check for updates", + "no_new_updates": "No new updates found", + "updates_refreshed": "Updates refreshed", "title": "{count} {count, plural,\n one {update}\n other {updates}\n}", "unable_to_fetch": "Unable to load updates", "version_available": "Version {version_available} is available", @@ -2205,18 +2208,18 @@ } }, "devices": { - "add_prompt": "No {name} have been added using this device yet. You can add one by clicking the + button above.", + "add_prompt": "No {name} have been added using this {type} yet. You can add one by clicking the + button above.", "caption": "Devices", "description": "Manage configured devices", - "device_info": "Device info", + "device_info": "{type} info", "edit_settings": "Edit settings", - "unnamed_device": "Unnamed device", + "unnamed_device": "Unnamed {type}", "unknown_error": "Unknown error", "name": "Name", "update": "Update", "no_devices": "No devices", - "enabled_label": "Enable device", - "enabled_cause": "The device is disabled by {cause}.", + "enabled_label": "Enable {type}", + "enabled_cause": "The {type} is disabled by {cause}.", "disabled_by": { "user": "User", "integration": "Integration", @@ -2226,12 +2229,19 @@ "open_configuration_url_device": "Visit device", "open_configuration_url_service": "Visit service", "download_diagnostics": "Download diagnostics", + "type": { + "device_heading": "Device", + "device": "device", + "service_heading": "Service", + "service": "service" + }, "automation": { - "automations": "Automations", + "automations_heading": "Automations", + "automations": "automations", "no_automations": "No automations", "unknown_automation": "Unknown automation", - "create": "Create automation with device", - "create_disable": "Can't create automation with disabled device", + "create": "Create automation with {type}", + "create_disable": "Can't create automation with disabled {type}", "triggers": { "caption": "Do something when…", "no_triggers": "No triggers", @@ -2250,19 +2260,21 @@ "no_device_automations": "There are no automations available for this device." }, "script": { - "scripts": "Scripts", + "scripts_heading": "Scripts", + "scripts": "scripts", "no_scripts": "No scripts", - "create": "Create script with device", - "create_disable": "Can't create script with disabled device" + "create": "Create script with {type}", + "create_disable": "Can't create script with disabled {type}" }, "scene": { - "scenes": "Scenes", + "scenes_heading": "Scenes", + "scenes": "scenes", "no_scenes": "No scenes", - "create": "Create scene with device", - "create_disable": "Can't create scene with disabled device" + "create": "Create scene with {type}", + "create_disable": "Can't create scene with disabled {type}" }, "cant_edit": "You can only edit items that are created in the UI.", - "device_not_found": "Device not found.", + "device_not_found": "Device / service not found.", "entities": { "entities": "Entities", "control": "Controls", @@ -2274,8 +2286,6 @@ "hide_disabled": "Hide disabled", "disabled_entities": "+{count} {count, plural,\n one {disabled entity}\n other {disabled entities}\n}" }, - "scripts": "Scripts", - "scenes": "Scenes", "confirm_rename_entity_ids": "Do you also want to rename the entity IDs of your entities?", "confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to update them yourself to use the new entity IDs!", "confirm_disable_config_entry": "There are no more devices for the config entry {entry_name}, do you want to instead disable the config entry?", @@ -3644,6 +3654,11 @@ "delete_prompt": "Delete this message?", "delete_button": "Delete" }, + "media-browser": { + "error": { + "player_not_exist": "Media player {name} does not exist" + } + }, "map": { "edit_zones": "Edit Zones" }, @@ -4096,7 +4111,8 @@ "setup": { "next": "Next", "back": "Back", - "done": "Show me my energy dashboard!" + "done": "Show me my energy dashboard!", + "step": "Step {step} of {steps}" }, "charts": { "stat_house_energy_meter": "Total energy consumption", diff --git a/yarn.lock b/yarn.lock index 4117444880..4c4a088a29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1958,10 +1958,10 @@ __metadata: languageName: node linkType: hard -"@lit/reactive-element@npm:1.0.1": - version: 1.0.1 - resolution: "@lit/reactive-element@npm:1.0.1" - checksum: 82f8eb195acb766413fa37dafab8dc853547e5fbb901318d9911b3f9410a15ceea37c9a4b0954ab37710e91140af243148af9f72ec3bd14fe71e1d03faaaad7a +"@lit/reactive-element@npm:1.2.1": + version: 1.2.1 + resolution: "@lit/reactive-element@npm:1.2.1" + checksum: 58f1b62c54b1899180b11cd44009ee91ac35099f9016259d3b9cc2f969fc920ab7e7ec32a816986ce89a27c632e25568542c93fdfed6730c19c850f7db0ba4cf languageName: node linkType: hard @@ -9187,7 +9187,7 @@ fsevents@^1.2.7: leaflet: ^1.7.1 leaflet-draw: ^1.0.4 lint-staged: ^11.1.2 - lit: ^2.0.2 + lit: ^2.1.2 lit-analyzer: ^1.2.1 lit-vaadin-helpers: ^0.2.1 lodash.template: ^4.5.0 @@ -10776,22 +10776,22 @@ fsevents@^1.2.7: languageName: node linkType: hard -"lit-element@npm:3.0.1": - version: 3.0.1 - resolution: "lit-element@npm:3.0.1" +"lit-element@npm:3.1.2": + version: 3.1.2 + resolution: "lit-element@npm:3.1.2" dependencies: - "@lit/reactive-element": ^1.0.0 - lit-html: ^2.0.0 - checksum: 3735cd1b96efb44f1ecdcb07406604d7c6feeab07f81cfc904a0f9ab806fab1211e8bf68827513b12e12e61448e3cd6508664d433abd3f0c5c4408b0a6b2d7a6 + "@lit/reactive-element": ^1.1.0 + lit-html: ^2.1.0 + checksum: 56ae568369af7c51cfe7187136ffb0782308ed3a12aa664085bd6761ff89fb60d3ac2ef286ce15154a2eb179d89f8655f7adaa18b8c083091c03dc75dd2ebf16 languageName: node linkType: hard -"lit-html@npm:2.0.1": - version: 2.0.1 - resolution: "lit-html@npm:2.0.1" +"lit-html@npm:2.1.2": + version: 2.1.2 + resolution: "lit-html@npm:2.1.2" dependencies: "@types/trusted-types": ^2.0.2 - checksum: 7ee9e909ec59009539d5b2d7bd07ceb6e182ed5c6535f36da5265dd2f5dc39f9e5f445e8272953a26948ff5724cf110836c792c439883527a2a3b46ecdbbb395 + checksum: 0c87d83b3577dbb0a2c50743296b6600a6872eac2f23b48da7ba3e604b8c72a8b0e1e48cfe0e00dd6f4ca921ab57f97e20709756f2115077478ffb05efa6cea0 languageName: node linkType: hard @@ -10804,14 +10804,14 @@ fsevents@^1.2.7: languageName: node linkType: hard -"lit@npm:^2.0.2": - version: 2.0.2 - resolution: "lit@npm:2.0.2" +"lit@npm:^2.1.2": + version: 2.1.2 + resolution: "lit@npm:2.1.2" dependencies: - "@lit/reactive-element": ^1.0.0 - lit-element: ^3.0.0 - lit-html: ^2.0.0 - checksum: 52a04b25164da1683c7295b305087794f175165cd1561e03f0c50ae998823f0e26101c82add05dea773e35472fbe3ed84486a4685294897f588d126fe8419e05 + "@lit/reactive-element": ^1.1.0 + lit-element: ^3.1.0 + lit-html: ^2.1.0 + checksum: dc3d6f30d508e48ad665a5777383c26055ae6514a7bd6a5745ac75cfeeeff8db49aa3b0f32db1a19e43bd7eec2bccd7db8523d5ed445ca0035fe37fddd9fb646 languageName: node linkType: hard