Skip to content

Commit

Permalink
Add update actions card feature (#19110)
Browse files Browse the repository at this point in the history
* Add update tile feature

* Fix translations

* Add confirmation dialog

* Remove unused styles

* Fix gallery

* Update wording

* Update src/translations/en.json
  • Loading branch information
piitaya authored Dec 27, 2023
1 parent df54687 commit 01a1427
Show file tree
Hide file tree
Showing 11 changed files with 483 additions and 38 deletions.
28 changes: 12 additions & 16 deletions gallery/src/pages/more-info/update.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import {
UPDATE_SUPPORT_BACKUP,
UPDATE_SUPPORT_PROGRESS,
UPDATE_SUPPORT_INSTALL,
UPDATE_SUPPORT_RELEASE_NOTES,
} from "../../../../src/data/update";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
Expand All @@ -15,13 +9,14 @@ import {
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
import { LONG_TEXT } from "../../data/text";
import { UpdateEntityFeature } from "../../../../src/data/update";

const base_attributes = {
title: "Awesome",
installed_version: "1.2.2",
latest_version: "1.2.3",
release_url: "https://home-assistant.io",
supported_features: UPDATE_SUPPORT_INSTALL,
supported_features: UpdateEntityFeature.INSTALL,
skipped_version: null,
in_progress: false,
release_summary:
Expand Down Expand Up @@ -61,7 +56,7 @@ const ENTITIES = [
getEntity("update", "update7", "on", {
...base_attributes,
supported_features:
base_attributes.supported_features + UPDATE_SUPPORT_BACKUP,
base_attributes.supported_features + UpdateEntityFeature.BACKUP,
friendly_name: "With backup support",
}),
getEntity("update", "update8", "on", {
Expand All @@ -73,21 +68,21 @@ const ENTITIES = [
...base_attributes,
in_progress: 25,
supported_features:
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 25 in_progress",
}),
getEntity("update", "update10", "on", {
...base_attributes,
in_progress: 50,
supported_features:
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 50 in_progress",
}),
getEntity("update", "update11", "on", {
...base_attributes,
in_progress: 75,
supported_features:
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 75 in_progress",
}),
getEntity("update", "update12", "unavailable", {
Expand All @@ -114,19 +109,19 @@ const ENTITIES = [
...base_attributes,
friendly_name: "Update with release notes",
supported_features:
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
}),
getEntity("update", "update17", "off", {
...base_attributes,
friendly_name: "Update with release notes error",
supported_features:
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
}),
getEntity("update", "update18", "off", {
...base_attributes,
friendly_name: "Update with release notes loading",
supported_features:
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
}),
getEntity("update", "update19", "on", {
...base_attributes,
Expand All @@ -142,9 +137,10 @@ const ENTITIES = [
getEntity("update", "update21", "on", {
...base_attributes,
in_progress: true,
friendly_name: "Update with in_progress true and UPDATE_SUPPORT_PROGRESS",
friendly_name:
"Update with in_progress true and UpdateEntityFeature.PROGRESS",
supported_features:
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
}),
];

Expand Down
18 changes: 10 additions & 8 deletions src/data/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
import { HomeAssistant } from "../types";
import { showToast } from "../util/toast";

export const UPDATE_SUPPORT_INSTALL = 1;
export const UPDATE_SUPPORT_SPECIFIC_VERSION = 2;
export const UPDATE_SUPPORT_PROGRESS = 4;
export const UPDATE_SUPPORT_BACKUP = 8;
export const UPDATE_SUPPORT_RELEASE_NOTES = 16;
export enum UpdateEntityFeature {
INSTALL = 1,
SPECIFIC_VERSION = 2,
PROGRESS = 4,
BACKUP = 8,
RELEASE_NOTES = 16,
}

interface UpdateEntityAttributes extends HassEntityAttributeBase {
auto_update: boolean | null;
Expand All @@ -35,7 +37,7 @@ export interface UpdateEntity extends HassEntityBase {
}

export const updateUsesProgress = (entity: UpdateEntity): boolean =>
supportsFeature(entity, UPDATE_SUPPORT_PROGRESS) &&
supportsFeature(entity, UpdateEntityFeature.PROGRESS) &&
typeof entity.attributes.in_progress === "number";

export const updateCanInstall = (
Expand All @@ -44,7 +46,7 @@ export const updateCanInstall = (
): boolean =>
(entity.state === BINARY_STATE_ON ||
(showSkipped && Boolean(entity.attributes.skipped_version))) &&
supportsFeature(entity, UPDATE_SUPPORT_INSTALL);
supportsFeature(entity, UpdateEntityFeature.INSTALL);

export const updateIsInstalling = (entity: UpdateEntity): boolean =>
updateUsesProgress(entity) || !!entity.attributes.in_progress;
Expand Down Expand Up @@ -176,7 +178,7 @@ export const computeUpdateStateDisplay = (
if (state === "on") {
if (updateIsInstalling(stateObj)) {
const supportsProgress =
supportsFeature(stateObj, UPDATE_SUPPORT_PROGRESS) &&
supportsFeature(stateObj, UpdateEntityFeature.PROGRESS) &&
typeof attributes.in_progress === "number";
if (supportsProgress) {
return hass.localize("ui.card.update.installing_with_progress", {
Expand Down
20 changes: 8 additions & 12 deletions src/dialogs/more-info/controls/more-info-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@ import "../../../components/ha-markdown";
import { isUnavailableState } from "../../../data/entity";
import {
UpdateEntity,
UpdateEntityFeature,
updateIsInstalling,
updateReleaseNotes,
UPDATE_SUPPORT_BACKUP,
UPDATE_SUPPORT_INSTALL,
UPDATE_SUPPORT_PROGRESS,
UPDATE_SUPPORT_RELEASE_NOTES,
UPDATE_SUPPORT_SPECIFIC_VERSION,
} from "../../../data/update";
import type { HomeAssistant } from "../../../types";

Expand Down Expand Up @@ -49,7 +45,7 @@ class MoreInfoUpdate extends LitElement {

return html`
${this.stateObj.attributes.in_progress
? supportsFeature(this.stateObj, UPDATE_SUPPORT_PROGRESS) &&
? supportsFeature(this.stateObj, UpdateEntityFeature.PROGRESS) &&
typeof this.stateObj.attributes.in_progress === "number"
? html`<mwc-linear-progress
.progress=${this.stateObj.attributes.in_progress / 100}
Expand Down Expand Up @@ -101,7 +97,7 @@ class MoreInfoUpdate extends LitElement {
</div>
</div>`
: ""}
${supportsFeature(this.stateObj!, UPDATE_SUPPORT_RELEASE_NOTES) &&
${supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES) &&
!this._error
? !this._releaseNotes
? html`<div class="flex center">
Expand All @@ -117,7 +113,7 @@ class MoreInfoUpdate extends LitElement {
.content=${this.stateObj.attributes.release_summary}
></ha-markdown>`
: ""}
${supportsFeature(this.stateObj, UPDATE_SUPPORT_BACKUP)
${supportsFeature(this.stateObj, UpdateEntityFeature.BACKUP)
? html`<hr />
<ha-formfield
.label=${this.hass.localize(
Expand Down Expand Up @@ -155,7 +151,7 @@ class MoreInfoUpdate extends LitElement {
)}
</mwc-button>
`}
${supportsFeature(this.stateObj, UPDATE_SUPPORT_INSTALL)
${supportsFeature(this.stateObj, UpdateEntityFeature.INSTALL)
? html`
<mwc-button
@click=${this._handleInstall}
Expand All @@ -174,7 +170,7 @@ class MoreInfoUpdate extends LitElement {
}

protected firstUpdated(): void {
if (supportsFeature(this.stateObj!, UPDATE_SUPPORT_RELEASE_NOTES)) {
if (supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES)) {
updateReleaseNotes(this.hass, this.stateObj!.entity_id)
.then((result) => {
this._releaseNotes = result;
Expand All @@ -186,7 +182,7 @@ class MoreInfoUpdate extends LitElement {
}

get _shouldCreateBackup(): boolean | null {
if (!supportsFeature(this.stateObj!, UPDATE_SUPPORT_BACKUP)) {
if (!supportsFeature(this.stateObj!, UpdateEntityFeature.BACKUP)) {
return null;
}
const checkbox = this.shadowRoot?.querySelector("ha-checkbox");
Expand All @@ -206,7 +202,7 @@ class MoreInfoUpdate extends LitElement {
}

if (
supportsFeature(this.stateObj!, UPDATE_SUPPORT_SPECIFIC_VERSION) &&
supportsFeature(this.stateObj!, UpdateEntityFeature.SPECIFIC_VERSION) &&
this.stateObj!.attributes.latest_version
) {
installData.version = this.stateObj!.attributes.latest_version;
Expand Down
92 changes: 92 additions & 0 deletions src/dialogs/update_backup/dialog-update-backup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-button";
import { createCloseHeading } from "../../components/ha-dialog";
import { HomeAssistant } from "../../types";
import { UpdateBackupDialogParams } from "./show-update-backup-dialog";

@customElement("dialog-update-backup")
class DialogBox extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@state() private _params?: UpdateBackupDialogParams;

public async showDialog(params: UpdateBackupDialogParams): Promise<void> {
this._params = params;
}

protected render() {
if (!this._params) {
return nothing;
}

return html`
<ha-dialog
open
@closed=${this._cancel}
defaultAction="ignore"
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.dialogs.update_backup.title")
)}
>
<p>${this.hass.localize("ui.dialogs.update_backup.text")}</p>
<ha-button @click=${this._no} slot="secondaryAction">
${this.hass!.localize("ui.common.no")}
</ha-button>
<ha-button @click=${this._yes} slot="primaryAction">
${this.hass.localize("ui.dialogs.update_backup.create")}
</ha-button>
</ha-dialog>
`;
}

private _no(): void {
if (this._params!.submit) {
this._params!.submit(false);
}
this.closeDialog();
}

private _yes(): void {
if (this._params!.submit) {
this._params!.submit(true);
}
this.closeDialog();
}

private _cancel(): void {
this._params?.cancel?.();
this.closeDialog();
}

public closeDialog(): void {
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}

static get styles(): CSSResultGroup {
return css`
p {
margin: 0;
color: var(--primary-text-color);
}
ha-dialog {
/* Place above other dialogs */
--dialog-z-index: 104;
}
@media all and (min-width: 600px) {
ha-dialog {
--mdc-dialog-min-width: 400px;
}
}
`;
}
}

declare global {
interface HTMLElementTagNameMap {
"dialog-update-backup": DialogBox;
}
}
35 changes: 35 additions & 0 deletions src/dialogs/update_backup/show-update-backup-dialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { fireEvent } from "../../common/dom/fire_event";

export interface UpdateBackupDialogParams {
submit?: (response: boolean) => void;
cancel?: () => void;
}

export const showUpdateBackupDialogParams = (
element: HTMLElement,
dialogParams: UpdateBackupDialogParams
) =>
new Promise<boolean | null>((resolve) => {
const origCancel = dialogParams.cancel;
const origSubmit = dialogParams.submit;

fireEvent(element, "show-dialog", {
dialogTag: "dialog-update-backup",
dialogImport: () => import("./dialog-update-backup"),
dialogParams: {
...dialogParams,
cancel: () => {
resolve(null);
if (origCancel) {
origCancel();
}
},
submit: (response: boolean) => {
resolve(response);
if (origSubmit) {
origSubmit(response);
}
},
},
});
});
Loading

0 comments on commit 01a1427

Please sign in to comment.