Add support package download to cloud (#24051)

This commit is contained in:
Bram Kragten 2025-02-04 18:06:41 +01:00 committed by GitHub
parent 11ae3a77e8
commit 904ee2e418
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 271 additions and 7 deletions

View File

@ -181,3 +181,6 @@ export const updateCloudGoogleEntityConfig = (
export const cloudSyncGoogleAssistant = (hass: HomeAssistant) =>
hass.callApi("POST", "cloud/google_actions/sync");
export const fetchSupportPackage = (hass: HomeAssistant) =>
hass.callApi<string>("GET", "cloud/support_package");

View File

@ -1,15 +1,15 @@
import "@material/mwc-button";
import { mdiDeleteForever, mdiDotsVertical, mdiDownload } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mdiDeleteForever, mdiDotsVertical } from "@mdi/js";
import { formatDateTime } from "../../../../common/datetime/format_date_time";
import { fireEvent } from "../../../../common/dom/fire_event";
import { debounce } from "../../../../common/util/debounce";
import "../../../../components/ha-alert";
import "../../../../components/ha-card";
import "../../../../components/ha-tip";
import "../../../../components/ha-list-item";
import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-list-item";
import "../../../../components/ha-tip";
import type {
CloudStatusLoggedIn,
SubscriptionInfo,
@ -32,6 +32,7 @@ import "./cloud-ice-servers-pref";
import "./cloud-remote-pref";
import "./cloud-tts-pref";
import "./cloud-webhooks";
import { showSupportPackageDialog } from "./show-dialog-cloud-support-package";
@customElement("cloud-account")
export class CloudAccount extends SubscribeMixin(LitElement) {
@ -52,7 +53,7 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
.narrow=${this.narrow}
header="Home Assistant Cloud"
>
<ha-button-menu slot="toolbar-icon" @action=${this._deleteCloudData}>
<ha-button-menu slot="toolbar-icon" @action=${this._handleMenuAction}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
@ -65,6 +66,12 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
)}
<ha-svg-icon slot="graphic" .path=${mdiDeleteForever}></ha-svg-icon>
</ha-list-item>
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.cloud.account.download_support_package"
)}
<ha-svg-icon slot="graphic" .path=${mdiDownload}></ha-svg-icon>
</ha-list-item>
</ha-button-menu>
<div class="content">
<ha-config-section .isWide=${this.isWide}>
@ -286,6 +293,16 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
fireEvent(this, "ha-refresh-cloud-status");
}
private _handleMenuAction(ev) {
switch (ev.detail.index) {
case 0:
this._deleteCloudData();
break;
case 1:
this._downloadSupportPackage();
}
}
private async _deleteCloudData() {
const confirm = await showConfirmationDialog(this, {
title: this.hass.localize(
@ -316,6 +333,10 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
}
}
private async _downloadSupportPackage() {
showSupportPackageDialog(this);
}
static get styles() {
return [
haStyle,

View File

@ -0,0 +1,206 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import { mdiClose } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-circular-progress";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-markdown-element";
import "../../../../components/ha-md-dialog";
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
import "../../../../components/ha-select";
import "../../../../components/ha-textarea";
import { fetchSupportPackage } from "../../../../data/cloud";
import type { HomeAssistant } from "../../../../types";
import { fileDownload } from "../../../../util/file_download";
@customElement("dialog-cloud-support-package")
export class DialogSupportPackage extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _open = false;
@state() private _supportPackage?: string;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
public showDialog() {
this._open = true;
this._loadSupportPackage();
}
private _dialogClosed(): void {
this._open = false;
this._supportPackage = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
public closeDialog() {
this._dialog?.close();
return true;
}
protected render() {
if (!this._open) {
return nothing;
}
return html`
<ha-md-dialog open @closed=${this._dialogClosed}>
<ha-dialog-header slot="headline">
<ha-icon-button
slot="navigationIcon"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
@click=${this.closeDialog}
></ha-icon-button>
<span slot="title">Download support package</span>
</ha-dialog-header>
<div slot="content">
${this._supportPackage
? html`<ha-markdown-element
.content=${this._supportPackage}
breaks
></ha-markdown-element>`
: html`
<div class="progress-container">
<ha-circular-progress indeterminate></ha-circular-progress>
Generating preview...
</div>
`}
</div>
<div class="footer" slot="actions">
<ha-alert>
This file may contain personal data about your home. Avoid sharing
them with unverified or untrusted parties.
</ha-alert>
<hr />
<div class="actions">
<ha-button @click=${this.closeDialog}>Close</ha-button>
<ha-button @click=${this._download}>Download</ha-button>
</div>
</div>
</ha-md-dialog>
`;
}
private async _loadSupportPackage() {
this._supportPackage = await fetchSupportPackage(this.hass);
}
private async _download() {
fileDownload(
"data:text/plain;charset=utf-8," +
encodeURIComponent(this._supportPackage || ""),
"support-package.md"
);
}
static styles = css`
ha-md-dialog {
min-width: 90vw;
min-height: 90vh;
}
.progress-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: calc(90vh - 260px);
width: 100%;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-md-dialog {
min-width: 100vw;
min-height: 100vh;
}
.progress-container {
height: calc(100vh - 260px);
}
}
.footer {
flex-direction: column;
}
.actions {
display: flex;
gap: 8px;
justify-content: flex-end;
}
hr {
border: none;
border-top: 1px solid var(--divider-color);
width: calc(100% + 48px);
margin-right: -24px;
margin-left: -24px;
}
table,
th,
td {
border: none;
}
table {
width: 100%;
display: table;
border-collapse: collapse;
border-spacing: 0;
}
table tr {
border-bottom: none;
}
table > tbody > tr:nth-child(odd) {
background-color: rgba(var(--rgb-primary-text-color), 0.04);
}
table > tbody > tr > td {
border-radius: 0;
}
table > tbody > tr {
-webkit-transition: background-color 0.25s ease;
transition: background-color 0.25s ease;
}
table > tbody > tr:hover {
background-color: rgba(var(--rgb-primary-text-color), 0.08);
}
tr {
border-bottom: 1px solid var(--divider-color);
}
td,
th {
padding: 15px 5px;
display: table-cell;
text-align: left;
vertical-align: middle;
border-radius: 2px;
}
details {
background-color: var(--secondary-background-color);
padding: 16px 24px;
margin: 8px 0;
border: 1px solid var(--divider-color);
border-radius: 16px;
}
summary {
font-weight: bold;
cursor: pointer;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"dialog-cloud-support-package": DialogSupportPackage;
}
}

View File

@ -0,0 +1,12 @@
import { fireEvent } from "../../../../common/dom/fire_event";
export const loadSupportPackageDialog = () =>
import("./dialog-cloud-support-package");
export const showSupportPackageDialog = (element: HTMLElement): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-cloud-support-package",
dialogImport: loadSupportPackageDialog,
dialogParams: {},
});
};

View File

@ -1,6 +1,6 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list";
import { mdiDeleteForever, mdiDotsVertical } from "@mdi/js";
import { mdiDeleteForever, mdiDotsVertical, mdiDownload } from "@mdi/js";
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
@ -27,6 +27,7 @@ import "../../../../layouts/hass-subpage";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import "../../ha-config-section";
import { showSupportPackageDialog } from "../account/show-dialog-cloud-support-package";
@customElement("cloud-login")
export class CloudLogin extends LitElement {
@ -57,7 +58,7 @@ export class CloudLogin extends LitElement {
.narrow=${this.narrow}
header="Home Assistant Cloud"
>
<ha-button-menu slot="toolbar-icon" @action=${this._deleteCloudData}>
<ha-button-menu slot="toolbar-icon" @action=${this._handleMenuAction}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
@ -70,6 +71,12 @@ export class CloudLogin extends LitElement {
)}
<ha-svg-icon slot="graphic" .path=${mdiDeleteForever}></ha-svg-icon>
</ha-list-item>
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.cloud.account.download_support_package"
)}
<ha-svg-icon slot="graphic" .path=${mdiDownload}></ha-svg-icon>
</ha-list-item>
</ha-button-menu>
<div class="content">
<ha-config-section .isWide=${this.isWide}>
@ -348,6 +355,16 @@ export class CloudLogin extends LitElement {
fireEvent(this, "flash-message-changed", { value: "" });
}
private _handleMenuAction(ev) {
switch (ev.detail.index) {
case 0:
this._deleteCloudData();
break;
case 1:
this._downloadSupportPackage();
}
}
private async _deleteCloudData() {
const confirm = await showConfirmationDialog(this, {
title: this.hass.localize(
@ -377,6 +394,10 @@ export class CloudLogin extends LitElement {
}
}
private async _downloadSupportPackage() {
showSupportPackageDialog(this);
}
static get styles() {
return [
haStyle,

View File

@ -4592,6 +4592,7 @@
"account_created": "Account created! Check your email for instructions on how to activate your account."
},
"account": {
"download_support_package": "Download support package",
"reset_cloud_data": "Reset cloud data",
"reset_data_confirm_title": "Reset cloud data?",
"reset_data_confirm_text": "This will reset all your cloud settings. This includes your remote connection, Google Assistant and Amazon Alexa integrations. This action cannot be undone.",