mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-18 14:56:37 +00:00
Use entity component translations for update entity (#18608)
This commit is contained in:
parent
270d463d02
commit
eef024587b
@ -203,6 +203,7 @@ export const DOMAINS_WITH_CARD = [
|
|||||||
"select",
|
"select",
|
||||||
"timer",
|
"timer",
|
||||||
"text",
|
"text",
|
||||||
|
"update",
|
||||||
"vacuum",
|
"vacuum",
|
||||||
"water_heater",
|
"water_heater",
|
||||||
];
|
];
|
||||||
|
@ -2,10 +2,6 @@ import { HassConfig, HassEntity } from "home-assistant-js-websocket";
|
|||||||
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
||||||
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
|
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
|
||||||
import { FrontendLocaleData, TimeZone } from "../../data/translation";
|
import { FrontendLocaleData, TimeZone } from "../../data/translation";
|
||||||
import {
|
|
||||||
UPDATE_SUPPORT_PROGRESS,
|
|
||||||
updateIsInstallingFromAttributes,
|
|
||||||
} from "../../data/update";
|
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import {
|
import {
|
||||||
UNIT_TO_MILLISECOND_CONVERT,
|
UNIT_TO_MILLISECOND_CONVERT,
|
||||||
@ -19,10 +15,9 @@ import {
|
|||||||
getNumberFormatOptions,
|
getNumberFormatOptions,
|
||||||
isNumericFromAttributes,
|
isNumericFromAttributes,
|
||||||
} from "../number/format_number";
|
} from "../number/format_number";
|
||||||
|
import { blankBeforeUnit } from "../translations/blank_before_unit";
|
||||||
import { LocalizeFunc } from "../translations/localize";
|
import { LocalizeFunc } from "../translations/localize";
|
||||||
import { computeDomain } from "./compute_domain";
|
import { computeDomain } from "./compute_domain";
|
||||||
import { supportsFeatureFromAttributes } from "./supports-feature";
|
|
||||||
import { blankBeforeUnit } from "../translations/blank_before_unit";
|
|
||||||
|
|
||||||
export const computeStateDisplaySingleEntity = (
|
export const computeStateDisplaySingleEntity = (
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
@ -208,27 +203,6 @@ export const computeStateDisplayFromEntityAttributes = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "update") {
|
|
||||||
// When updating, and entity does not support % show "Installing"
|
|
||||||
// When updating, and entity does support % show "Installing (xx%)"
|
|
||||||
// When update available, show the version
|
|
||||||
// When the latest version is skipped, show the latest version
|
|
||||||
// When update is not available, show "Up-to-date"
|
|
||||||
// When update is not available and there is no latest_version show "Unavailable"
|
|
||||||
return state === "on"
|
|
||||||
? updateIsInstallingFromAttributes(attributes)
|
|
||||||
? supportsFeatureFromAttributes(attributes, UPDATE_SUPPORT_PROGRESS) &&
|
|
||||||
typeof attributes.in_progress === "number"
|
|
||||||
? localize("ui.card.update.installing_with_progress", {
|
|
||||||
progress: attributes.in_progress,
|
|
||||||
})
|
|
||||||
: localize("ui.card.update.installing")
|
|
||||||
: attributes.latest_version
|
|
||||||
: attributes.skipped_version === attributes.latest_version
|
|
||||||
? attributes.latest_version ?? localize("state.default.unavailable")
|
|
||||||
: localize("ui.card.update.up_to_date");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(entity?.translation_key &&
|
(entity?.translation_key &&
|
||||||
localize(
|
localize(
|
||||||
|
@ -7,10 +7,7 @@ import type {
|
|||||||
import { BINARY_STATE_ON } from "../common/const";
|
import { BINARY_STATE_ON } from "../common/const";
|
||||||
import { computeDomain } from "../common/entity/compute_domain";
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||||
import {
|
import { supportsFeature } from "../common/entity/supports-feature";
|
||||||
supportsFeature,
|
|
||||||
supportsFeatureFromAttributes,
|
|
||||||
} from "../common/entity/supports-feature";
|
|
||||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||||
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
@ -38,13 +35,8 @@ export interface UpdateEntity extends HassEntityBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const updateUsesProgress = (entity: UpdateEntity): boolean =>
|
export const updateUsesProgress = (entity: UpdateEntity): boolean =>
|
||||||
updateUsesProgressFromAttributes(entity.attributes);
|
supportsFeature(entity, UPDATE_SUPPORT_PROGRESS) &&
|
||||||
|
typeof entity.attributes.in_progress === "number";
|
||||||
export const updateUsesProgressFromAttributes = (attributes: {
|
|
||||||
[key: string]: any;
|
|
||||||
}): boolean =>
|
|
||||||
supportsFeatureFromAttributes(attributes, UPDATE_SUPPORT_PROGRESS) &&
|
|
||||||
typeof attributes.in_progress === "number";
|
|
||||||
|
|
||||||
export const updateCanInstall = (
|
export const updateCanInstall = (
|
||||||
entity: UpdateEntity,
|
entity: UpdateEntity,
|
||||||
@ -57,11 +49,6 @@ export const updateCanInstall = (
|
|||||||
export const updateIsInstalling = (entity: UpdateEntity): boolean =>
|
export const updateIsInstalling = (entity: UpdateEntity): boolean =>
|
||||||
updateUsesProgress(entity) || !!entity.attributes.in_progress;
|
updateUsesProgress(entity) || !!entity.attributes.in_progress;
|
||||||
|
|
||||||
export const updateIsInstallingFromAttributes = (attributes: {
|
|
||||||
[key: string]: any;
|
|
||||||
}): boolean =>
|
|
||||||
updateUsesProgressFromAttributes(attributes) || !!attributes.in_progress;
|
|
||||||
|
|
||||||
export const updateReleaseNotes = (hass: HomeAssistant, entityId: string) =>
|
export const updateReleaseNotes = (hass: HomeAssistant, entityId: string) =>
|
||||||
hass.callWS<string | null>({
|
hass.callWS<string | null>({
|
||||||
type: "update/release_notes",
|
type: "update/release_notes",
|
||||||
@ -162,3 +149,47 @@ export const checkForEntityUpdates = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// When updating, and entity does not support % show "Installing"
|
||||||
|
// When updating, and entity does support % show "Installing (xx%)"
|
||||||
|
// When update available, show the version
|
||||||
|
// When the latest version is skipped, show the latest version
|
||||||
|
// When update is not available, show "Up-to-date"
|
||||||
|
// When update is not available and there is no latest_version show "Unavailable"
|
||||||
|
export const computeUpdateStateDisplay = (
|
||||||
|
stateObj: UpdateEntity,
|
||||||
|
hass: HomeAssistant
|
||||||
|
): string => {
|
||||||
|
const state = stateObj.state;
|
||||||
|
const attributes = stateObj.attributes;
|
||||||
|
|
||||||
|
if (state === "off") {
|
||||||
|
const isSkipped =
|
||||||
|
attributes.latest_version &&
|
||||||
|
attributes.skipped_version === attributes.latest_version;
|
||||||
|
if (isSkipped) {
|
||||||
|
return attributes.latest_version!;
|
||||||
|
}
|
||||||
|
return hass.formatEntityState(stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === "on") {
|
||||||
|
if (updateIsInstalling(stateObj)) {
|
||||||
|
const supportsProgress =
|
||||||
|
supportsFeature(stateObj, UPDATE_SUPPORT_PROGRESS) &&
|
||||||
|
typeof typeof attributes.in_progress === "number";
|
||||||
|
if (supportsProgress) {
|
||||||
|
return hass.localize("ui.card.update.installing_with_progress", {
|
||||||
|
progress: attributes.in_progress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return hass.localize("ui.card.update.installing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributes.latest_version) {
|
||||||
|
return attributes.latest_version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hass.formatEntityState(stateObj);
|
||||||
|
};
|
||||||
|
@ -63,8 +63,9 @@ class MoreInfoUpdate extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="key">
|
<div class="key">
|
||||||
${this.hass.localize(
|
${this.hass.formatEntityAttributeName(
|
||||||
"ui.dialogs.more_info_control.update.installed_version"
|
this.stateObj,
|
||||||
|
"installed_version"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
@ -74,8 +75,9 @@ class MoreInfoUpdate extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="key">
|
<div class="key">
|
||||||
${this.hass.localize(
|
${this.hass.formatEntityAttributeName(
|
||||||
"ui.dialogs.more_info_control.update.latest_version"
|
this.stateObj,
|
||||||
|
"latest_version"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
|
@ -47,6 +47,7 @@ import "../tile-features/hui-tile-features";
|
|||||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
import { computeTileBadge } from "./tile/badges/tile-badge";
|
import { computeTileBadge } from "./tile/badges/tile-badge";
|
||||||
import type { ThermostatCardConfig, TileCardConfig } from "./types";
|
import type { ThermostatCardConfig, TileCardConfig } from "./types";
|
||||||
|
import { UpdateEntity, computeUpdateStateDisplay } from "../../../data/update";
|
||||||
|
|
||||||
const TIMESTAMP_STATE_DOMAINS = ["button", "input_button", "scene"];
|
const TIMESTAMP_STATE_DOMAINS = ["button", "input_button", "scene"];
|
||||||
|
|
||||||
@ -260,6 +261,13 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (domain === "update") {
|
||||||
|
return html`${computeUpdateStateDisplay(
|
||||||
|
stateObj as UpdateEntity,
|
||||||
|
this.hass!
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
|
||||||
return this._renderStateContent(stateObj, "state");
|
return this._renderStateContent(stateObj, "state");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ const LAZY_LOAD_TYPES = {
|
|||||||
"text-entity": () => import("../entity-rows/hui-text-entity-row"),
|
"text-entity": () => import("../entity-rows/hui-text-entity-row"),
|
||||||
"time-entity": () => import("../entity-rows/hui-time-entity-row"),
|
"time-entity": () => import("../entity-rows/hui-time-entity-row"),
|
||||||
"timer-entity": () => import("../entity-rows/hui-timer-entity-row"),
|
"timer-entity": () => import("../entity-rows/hui-timer-entity-row"),
|
||||||
|
"update-entity": () => import("../entity-rows/hui-update-entity-row"),
|
||||||
conditional: () => import("../special-rows/hui-conditional-row"),
|
conditional: () => import("../special-rows/hui-conditional-row"),
|
||||||
"weather-entity": () => import("../entity-rows/hui-weather-entity-row"),
|
"weather-entity": () => import("../entity-rows/hui-weather-entity-row"),
|
||||||
divider: () => import("../special-rows/hui-divider-row"),
|
divider: () => import("../special-rows/hui-divider-row"),
|
||||||
@ -73,6 +74,7 @@ const DOMAIN_TO_ELEMENT_TYPE = {
|
|||||||
humidifier: "humidifier",
|
humidifier: "humidifier",
|
||||||
input_boolean: "toggle",
|
input_boolean: "toggle",
|
||||||
input_button: "input-button",
|
input_button: "input-button",
|
||||||
|
input_datetime: "input-datetime",
|
||||||
input_number: "input-number",
|
input_number: "input-number",
|
||||||
input_select: "input-select",
|
input_select: "input-select",
|
||||||
input_text: "input-text",
|
input_text: "input-text",
|
||||||
@ -90,11 +92,11 @@ const DOMAIN_TO_ELEMENT_TYPE = {
|
|||||||
text: "text",
|
text: "text",
|
||||||
time: "time",
|
time: "time",
|
||||||
timer: "timer",
|
timer: "timer",
|
||||||
|
update: "update",
|
||||||
vacuum: "toggle",
|
vacuum: "toggle",
|
||||||
// Temporary. Once climate is rewritten,
|
// Temporary. Once climate is rewritten,
|
||||||
// water heater should get its own row.
|
// water heater should get its own row.
|
||||||
water_heater: "climate",
|
water_heater: "climate",
|
||||||
input_datetime: "input-datetime",
|
|
||||||
weather: "weather",
|
weather: "weather",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
75
src/panels/lovelace/entity-rows/hui-update-entity-row.ts
Normal file
75
src/panels/lovelace/entity-rows/hui-update-entity-row.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import {
|
||||||
|
CSSResultGroup,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { UpdateEntity, computeUpdateStateDisplay } from "../../../data/update";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { EntitiesCardEntityConfig } from "../cards/types";
|
||||||
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
|
import "../components/hui-generic-entity-row";
|
||||||
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
|
import { LovelaceRow } from "./types";
|
||||||
|
|
||||||
|
@customElement("hui-update-entity-row")
|
||||||
|
class HuiUpdateEntityRow extends LitElement implements LovelaceRow {
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _config?: EntitiesCardEntityConfig;
|
||||||
|
|
||||||
|
public setConfig(config: EntitiesCardEntityConfig): void {
|
||||||
|
if (!config) {
|
||||||
|
throw new Error("Invalid configuration");
|
||||||
|
}
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
return hasConfigOrEntityChanged(this, changedProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateObj = this.hass.states[this._config.entity] as
|
||||||
|
| UpdateEntity
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (!stateObj) {
|
||||||
|
return html`
|
||||||
|
<hui-warning>
|
||||||
|
${createEntityNotFoundWarning(this.hass, this._config.entity)}
|
||||||
|
</hui-warning>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
||||||
|
${computeUpdateStateDisplay(stateObj, this.hass)}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
div {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-update-entity-row": HuiUpdateEntityRow;
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import "./state-card-select";
|
|||||||
import "./state-card-text";
|
import "./state-card-text";
|
||||||
import "./state-card-timer";
|
import "./state-card-timer";
|
||||||
import "./state-card-toggle";
|
import "./state-card-toggle";
|
||||||
|
import "./state-card-update";
|
||||||
import "./state-card-vacuum";
|
import "./state-card-vacuum";
|
||||||
import "./state-card-water_heater";
|
import "./state-card-water_heater";
|
||||||
|
|
||||||
|
56
src/state-summary/state-card-update.ts
Executable file
56
src/state-summary/state-card-update.ts
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import { computeUpdateStateDisplay, UpdateEntity } from "../data/update";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
@customElement("state-card-update")
|
||||||
|
export class StateCardUpdate extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: UpdateEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
>
|
||||||
|
</state-info>
|
||||||
|
<div class="state">
|
||||||
|
${computeUpdateStateDisplay(this.stateObj, this.hass)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
state-info {
|
||||||
|
flex: 0 1 fit-content;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
.state {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
margin-inline-start: 16px;
|
||||||
|
margin-inline-end: initial;
|
||||||
|
text-align: var(--float-end, right);
|
||||||
|
min-width: 50px;
|
||||||
|
flex: 0 1 fit-content;
|
||||||
|
word-break: break-word;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
direction: ltr;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -936,8 +936,6 @@
|
|||||||
"setting": "Setting"
|
"setting": "Setting"
|
||||||
},
|
},
|
||||||
"update": {
|
"update": {
|
||||||
"installed_version": "Installed version",
|
|
||||||
"latest_version": "Latest version",
|
|
||||||
"release_announcement": "Read release announcement",
|
"release_announcement": "Read release announcement",
|
||||||
"skip": "Skip",
|
"skip": "Skip",
|
||||||
"clear_skipped": "Clear skipped",
|
"clear_skipped": "Clear skipped",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user