diff --git a/hassio/src/update-available/update-available-card.ts b/hassio/src/update-available/update-available-card.ts index 5c1ef47b7f..46e0bc64cc 100644 --- a/hassio/src/update-available/update-available-card.ts +++ b/hassio/src/update-available/update-available-card.ts @@ -132,7 +132,13 @@ class UpdateAvailableCard extends LitElement { ${this._error ? html`${this._error}` : ""} - ${this._action === null + ${this._version === this._version_latest + ? html`

+ ${this.supervisor.localize("update_available.no_update", { + name: this._name, + })} +

` + : this._action === null ? html` ${this._changelogContent ? html` @@ -177,7 +183,7 @@ class UpdateAvailableCard extends LitElement { )}

`} - ${this._action === null + ${this._version !== this._version_latest && this._action === null ? html`
${changelog @@ -224,6 +230,9 @@ class UpdateAvailableCard extends LitElement { } get _shouldCreateBackup(): boolean { + if (this._updateType && !["core", "addon"].includes(this._updateType)) { + return false; + } const checkbox = this.shadowRoot?.querySelector("ha-checkbox"); if (checkbox) { return checkbox.checked; diff --git a/setup.py b/setup.py index 189ceda3a9..f70ca702e7 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20211212.0", + version="20211215.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/frontend", author="The Home Assistant Authors", diff --git a/src/common/const.ts b/src/common/const.ts index 6f4c6319c5..ef88067ac2 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -208,6 +208,7 @@ export const DOMAINS_HIDE_DEFAULT_MORE_INFO = [ export const DOMAINS_INPUT_ROW = [ "cover", "fan", + "group", "humidifier", "input_boolean", "input_datetime", diff --git a/src/components/ha-alert.ts b/src/components/ha-alert.ts index b6f2f52a46..aa6848a48b 100644 --- a/src/components/ha-alert.ts +++ b/src/components/ha-alert.ts @@ -46,6 +46,7 @@ class HaAlert extends LitElement { rtl: this.rtl, [this.alertType]: true, })}" + role="alert" >
diff --git a/src/components/ha-form/ha-form-string.ts b/src/components/ha-form/ha-form-string.ts index bdc6fe9e60..5b40a1cfb7 100644 --- a/src/components/ha-form/ha-form-string.ts +++ b/src/components/ha-form/ha-form-string.ts @@ -66,7 +66,7 @@ export class HaFormString extends LitElement implements HaFormElement { ${isPassword ? html` { + this._externalConfig = conf; + }); + } + subscribeNotifications(this.hass.connection, (notifications) => { this._notifications = notifications; }); - - // Temporary workaround for a bug in Android. Can be removed in Home Assistant 2022.2 - if (this.hass.auth.external) { - getExternalConfig(this.hass.auth.external); - } } protected updated(changedProps) { @@ -364,6 +372,7 @@ class HaSidebar extends LitElement { : this._renderPanels(beforeSpacer)} ${this._renderSpacer()} ${this._renderPanels(afterSpacer)} + ${this._renderExternalConfiguration()} `; } @@ -393,7 +402,7 @@ class HaSidebar extends LitElement { ) { return html` @@ -529,7 +538,7 @@ class HaSidebar extends LitElement { href="/profile" data-panel="panel" tabindex="-1" - aria-role="option" + role="option" aria-label=${this.hass.localize("panel.profile")} @mouseenter=${this._itemMouseEnter} @mouseleave=${this._itemMouseLeave} @@ -548,6 +557,43 @@ class HaSidebar extends LitElement { `; } + private _renderExternalConfiguration() { + return html`${!this.hass.user?.is_admin && + this._externalConfig && + this._externalConfig.hasSettingsScreen + ? html` + + + + + ${this.hass.localize("ui.sidebar.external_app_configuration")} + + + + ` + : ""}`; + } + + private _handleExternalAppConfiguration(ev: Event) { + ev.preventDefault(); + this.hass.auth.external!.fireMessage({ + type: "config_screen/show", + }); + } + private get _tooltip() { return this.shadowRoot!.querySelector(".tooltip")! as HTMLDivElement; } diff --git a/src/data/media-player.ts b/src/data/media-player.ts index f0431b7f57..ee4245f1b9 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -156,6 +156,7 @@ export interface MediaPlayerThumbnail { export interface ControlButton { icon: string; + // Used as key for action as well as tooltip and aria-label translation key action: string; } diff --git a/src/dialogs/more-info/controls/more-info-media_player.ts b/src/dialogs/more-info/controls/more-info-media_player.ts index 8f27cdb034..e836e3dd28 100644 --- a/src/dialogs/more-info/controls/more-info-media_player.ts +++ b/src/dialogs/more-info/controls/more-info-media_player.ts @@ -65,6 +65,9 @@ class MoreInfoMediaPlayer extends LitElement { action=${control.action} @click=${this._handleClick} .path=${control.icon} + .label=${this.hass.localize( + `ui.card.media_player.${control.action}` + )} > ` diff --git a/src/onboarding/onboarding-create-user.ts b/src/onboarding/onboarding-create-user.ts index bec0c77479..c511d34a46 100644 --- a/src/onboarding/onboarding-create-user.ts +++ b/src/onboarding/onboarding-create-user.ts @@ -95,8 +95,11 @@ class OnboardingCreateUser extends LitElement { private _handleValueChanged( ev: PolymerChangedEvent ): void { + const nameChanged = ev.detail.value.name !== this._newUser.name; this._newUser = ev.detail.value; - this._maybePopulateUsername(); + if (nameChanged) { + this._maybePopulateUsername(); + } this._formError.password_confirm = this._newUser.password !== this._newUser.password_confirm ? this.localize( diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts index 5df15283b7..4a6e5a86ef 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts @@ -17,9 +17,9 @@ import { const stateConditionStruct = object({ condition: literal("state"), - entity_id: string(), + entity_id: optional(string()), attribute: optional(string()), - state: string(), + state: optional(string()), for: optional(union([string(), forDictStruct])), }); diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts index f48310da8e..c38e78d6a4 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts @@ -28,7 +28,7 @@ const stateTriggerStruct = assign( baseTriggerStruct, object({ platform: literal("state"), - entity_id: string(), + entity_id: optional(string()), attribute: optional(string()), from: optional(string()), to: optional(string()), diff --git a/src/panels/config/dashboard/ha-config-navigation.ts b/src/panels/config/dashboard/ha-config-navigation.ts index 5f29dadc89..66c47ccb64 100644 --- a/src/panels/config/dashboard/ha-config-navigation.ts +++ b/src/panels/config/dashboard/ha-config-navigation.ts @@ -31,7 +31,7 @@ class HaConfigNavigation extends LitElement { : canShowPage(this.hass, page) ) ? html` - + diff --git a/src/panels/developer-tools/state/developer-tools-state.js b/src/panels/developer-tools/state/developer-tools-state.js index 6a664ee61b..6887354c93 100644 --- a/src/panels/developer-tools/state/developer-tools-state.js +++ b/src/panels/developer-tools/state/developer-tools-state.js @@ -143,7 +143,12 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {

[[localize('ui.panel.developer-tools.tabs.states.current_entities')]]

- +

[[localize('ui.panel.developer-tools.tabs.states.description1')]]
[[localize('ui.panel.developer-tools.tabs.states.description2')]] @@ -353,6 +358,11 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) { "computeEntities(hass, _entityFilter, _stateFilter, _attributeFilter)", }, + _expanded: { + type: Boolean, + value: false, + }, + narrow: { type: Boolean, reflectToAttribute: true, @@ -376,6 +386,7 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) { this._entity = state; this._state = state.state; this._stateAttributes = dump(state.attributes); + this._expanded = true; ev.preventDefault(); } @@ -393,6 +404,11 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) { this._entity = state; this._state = state.state; this._stateAttributes = dump(state.attributes); + this._expanded = true; + } + + expandedChanged(ev) { + this._expanded = ev.detail.expanded; } entityMoreInfo(ev) { diff --git a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts index 5e19672487..da21cc8004 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts @@ -26,7 +26,7 @@ import { rgb2hex, rgb2lab, } from "../../../../common/color/convert-color"; -import { labDarken } from "../../../../common/color/lab"; +import { labBrighten, labDarken } from "../../../../common/color/lab"; import { EnergyData, getEnergyDataCollection, @@ -247,10 +247,15 @@ export class HuiEnergyGasGraphCard const data: ChartDataset<"bar" | "line">[] = []; const entity = this.hass.states[source.stat_energy_from]; - const borderColor = + const modifiedColor = idx > 0 - ? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(gasColor)), idx))) - : gasColor; + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(gasColor)), idx) + : labDarken(rgb2lab(hex2rgb(gasColor)), idx) + : undefined; + const borderColor = modifiedColor + ? rgb2hex(lab2rgb(modifiedColor)) + : gasColor; let prevValue: number | null = null; let prevStart: string | null = null; diff --git a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts index 98305f7c2a..584da90561 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts @@ -1,9 +1,3 @@ -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { UnsubscribeFunc } from "home-assistant-js-websocket"; -import memoizeOne from "memoize-one"; -import { classMap } from "lit/directives/class-map"; -import "../../../../components/ha-card"; import { ChartData, ChartDataset, @@ -17,16 +11,26 @@ import { isToday, startOfToday, } from "date-fns"; -import { HomeAssistant } from "../../../../types"; -import { LovelaceCard } from "../../types"; -import { EnergySolarGraphCardConfig } from "../types"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import memoizeOne from "memoize-one"; import { hex2rgb, lab2rgb, rgb2hex, rgb2lab, } from "../../../../common/color/convert-color"; -import { labDarken } from "../../../../common/color/lab"; +import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { formatTime } from "../../../../common/datetime/format_time"; +import { computeStateName } from "../../../../common/entity/compute_state_name"; +import { + formatNumber, + numberFormatToLocale, +} from "../../../../common/number/format_number"; +import "../../../../components/chart/ha-chart-base"; +import "../../../../components/ha-card"; import { EnergyData, EnergySolarForecasts, @@ -34,15 +38,11 @@ import { getEnergySolarForecasts, SolarSourceTypeEnergyPreference, } from "../../../../data/energy"; -import { computeStateName } from "../../../../common/entity/compute_state_name"; -import "../../../../components/chart/ha-chart-base"; -import { - formatNumber, - numberFormatToLocale, -} from "../../../../common/number/format_number"; -import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; import { FrontendLocaleData } from "../../../../data/translation"; -import { formatTime } from "../../../../common/datetime/format_time"; +import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; +import { HomeAssistant } from "../../../../types"; +import { LovelaceCard } from "../../types"; +import { EnergySolarGraphCardConfig } from "../types"; @customElement("hui-energy-solar-graph-card") export class HuiEnergySolarGraphCard @@ -258,10 +258,15 @@ export class HuiEnergySolarGraphCard const data: ChartDataset<"bar" | "line">[] = []; const entity = this.hass.states[source.stat_energy_from]; - const borderColor = + const modifiedColor = idx > 0 - ? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(solarColor)), idx))) - : solarColor; + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(solarColor)), idx) + : labDarken(rgb2lab(hex2rgb(solarColor)), idx) + : undefined; + const borderColor = modifiedColor + ? rgb2hex(lab2rgb(modifiedColor)) + : solarColor; let prevValue: number | null = null; let prevStart: string | null = null; diff --git a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts index bd6a0f3756..5c1b21a992 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts @@ -17,7 +17,7 @@ import { rgb2lab, hex2rgb, } from "../../../../common/color/convert-color"; -import { labDarken } from "../../../../common/color/lab"; +import { labBrighten, labDarken } from "../../../../common/color/lab"; import { computeStateName } from "../../../../common/entity/compute_state_name"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/statistics-chart"; @@ -170,12 +170,17 @@ export class HuiEnergySourcesTableCard this._data!.stats[source.stat_energy_from] ) || 0; totalSolar += energy; - const color = + + const modifiedColor = idx > 0 - ? rgb2hex( - lab2rgb(labDarken(rgb2lab(hex2rgb(solarColor)), idx)) - ) - : solarColor; + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(solarColor)), idx) + : labDarken(rgb2lab(hex2rgb(solarColor)), idx) + : undefined; + const color = modifiedColor + ? rgb2hex(lab2rgb(modifiedColor)) + : solarColor; + return html`

0 - ? rgb2hex( - lab2rgb( - labDarken(rgb2lab(hex2rgb(batteryFromColor)), idx) - ) - ) - : batteryFromColor; - const toColor = + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(batteryFromColor)), idx) + : labDarken(rgb2lab(hex2rgb(batteryFromColor)), idx) + : undefined; + const fromColor = modifiedFromColor + ? rgb2hex(lab2rgb(modifiedFromColor)) + : batteryFromColor; + const modifiedToColor = idx > 0 - ? rgb2hex( - lab2rgb( - labDarken(rgb2lab(hex2rgb(batteryToColor)), idx) - ) - ) - : batteryToColor; + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(batteryToColor)), idx) + : labDarken(rgb2lab(hex2rgb(batteryToColor)), idx) + : undefined; + const toColor = modifiedToColor + ? rgb2hex(lab2rgb(modifiedToColor)) + : batteryToColor; + return html`
0 - ? rgb2hex( - lab2rgb( - labDarken(rgb2lab(hex2rgb(consumptionColor)), idx) - ) - ) - : consumptionColor; + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(consumptionColor)), idx) + : labDarken(rgb2lab(hex2rgb(consumptionColor)), idx) + : undefined; + const color = modifiedColor + ? rgb2hex(lab2rgb(modifiedColor)) + : consumptionColor; + return html`
0 - ? rgb2hex( - lab2rgb(labDarken(rgb2lab(hex2rgb(returnColor)), idx)) - ) - : returnColor; + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(returnColor)), idx) + : labDarken(rgb2lab(hex2rgb(returnColor)), idx) + : undefined; + const color = modifiedColor + ? rgb2hex(lab2rgb(modifiedColor)) + : returnColor; + return html`
0 - ? rgb2hex( - lab2rgb(labDarken(rgb2lab(hex2rgb(gasColor)), idx)) - ) - : gasColor; + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(gasColor)), idx) + : labDarken(rgb2lab(hex2rgb(gasColor)), idx) + : undefined; + const color = modifiedColor + ? rgb2hex(lab2rgb(modifiedColor)) + : gasColor; + return html`
{ const data: ChartDataset<"bar">[] = []; const entity = this.hass.states[statId]; - const borderColor = + + const modifiedColor = idx > 0 - ? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(colors[type])), idx))) - : colors[type]; + ? this.hass.themes.darkMode + ? labBrighten(rgb2lab(hex2rgb(colors[type])), idx) + : labDarken(rgb2lab(hex2rgb(colors[type])), idx) + : undefined; + const borderColor = modifiedColor + ? rgb2hex(lab2rgb(modifiedColor)) + : colors[type]; data.push({ label: diff --git a/src/panels/lovelace/cards/hui-media-control-card.ts b/src/panels/lovelace/cards/hui-media-control-card.ts index f579172e9a..36bb87268b 100644 --- a/src/panels/lovelace/cards/hui-media-control-card.ts +++ b/src/panels/lovelace/cards/hui-media-control-card.ts @@ -235,6 +235,9 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
diff --git a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts index 1b99c687e0..5935905dbf 100644 --- a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts @@ -30,6 +30,7 @@ import "../../../components/ha-slider"; import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; import { computeMediaDescription, + ControlButton, MediaPlayerEntity, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, @@ -108,6 +109,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { } const entityState = stateObj.state; + const controlButton = this._computeControlButton(stateObj); const buttons = html` ${!this._narrow && @@ -116,6 +118,9 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { ? html` ` @@ -130,7 +135,10 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { supportsFeature(stateObj, SUPPORT_PAUSE))) ? html` ` @@ -140,6 +148,9 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { ? html` ` @@ -162,6 +173,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { ? html` ` @@ -175,6 +187,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { ? html` ` @@ -193,6 +206,13 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { .path=${stateObj.attributes.is_volume_muted ? mdiVolumeOff : mdiVolumeHigh} + .label=${this.hass.localize( + `ui.card.media_player.${ + stateObj.attributes.is_volume_muted + ? "media_volume_mute" + : "media_volume_unmute" + }` + )} @click=${this._toggleMute} > ` @@ -214,10 +234,16 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { ? html` ` @@ -249,14 +275,14 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { this._veryNarrow = (this.clientWidth || 0) < 225; } - private _computeControlIcon(stateObj: HassEntity): string { + private _computeControlButton(stateObj: HassEntity): ControlButton { return stateObj.state === "on" - ? mdiPlayPause + ? { icon: mdiPlayPause, action: "media_play_pause" } : stateObj.state !== "playing" - ? mdiPlay + ? { icon: mdiPlay, action: "media_play" } : supportsFeature(stateObj, SUPPORT_PAUSE) - ? mdiPause - : mdiStop; + ? { icon: mdiPause, action: "media_pause" } + : { icon: mdiStop, action: "media_stop" }; } private _togglePower(): void { diff --git a/src/translations/en.json b/src/translations/en.json index 79154aff3d..102da654c6 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -195,8 +195,14 @@ "turn_off": "Turn off", "media_play": "Play", "media_play_pause": "Play/pause", - "media_next_track": "Next", - "media_previous_track": "Previous", + "media_pause": "Pause", + "media_stop": "Stop", + "media_next_track": "Next track", + "media_previous_track": "Previous track", + "media_volume_up": "Volume up", + "media_volume_down": "Volume down", + "media_volume_mute": "Volume mute", + "media_volume_unmute": "Volume unmute", "text_to_speak": "Text to speak" }, "persistent_notification": { @@ -689,7 +695,7 @@ "close_cover": "Close cover", "open_tilt_cover": "Open cover tilt", "close_tilt_cover": "Close cover tilt", - "stop_cover": "Stop cover from moving" + "stop_cover": "Stop cover" } }, "entity_registry": { @@ -922,6 +928,7 @@ "dismiss": "Dismiss" }, "sidebar": { + "external_app_configuration": "App Configuration", "sidebar_toggle": "Sidebar Toggle", "done": "Done", "hide_panel": "Hide panel", @@ -4269,7 +4276,8 @@ "create_backup": "Create backup before updating", "description": "You have {version} installed. Click update to update to version {newest_version}", "updating": "Updating {name} to version {version}", - "creating_backup": "Creating backup of {name}" + "creating_backup": "Creating backup of {name}", + "no_update": "No update available for {name}" }, "confirm": { "restart": {