diff --git a/src/panels/config/core/ha-config-section-updates.ts b/src/panels/config/core/ha-config-section-updates.ts
index 3207620850..e0c0c6411e 100644
--- a/src/panels/config/core/ha-config-section-updates.ts
+++ b/src/panels/config/core/ha-config-section-updates.ts
@@ -1,22 +1,35 @@
+import type { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import { HassEntities } from "home-assistant-js-websocket";
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
+import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
import "../../../components/ha-alert";
import "../../../components/ha-bar";
import "../../../components/ha-button-menu";
+import "../../../components/ha-card";
import "../../../components/ha-metric";
+import { extractApiErrorMessage } from "../../../data/hassio/common";
+import {
+ fetchHassioSupervisorInfo,
+ HassioSupervisorInfo,
+ reloadSupervisor,
+ setSupervisorOption,
+ SupervisorOptions,
+} from "../../../data/hassio/supervisor";
import { updateCanInstall, UpdateEntity } from "../../../data/update";
-import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
+import {
+ showAlertDialog,
+ showConfirmationDialog,
+} from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-subpage";
import type { HomeAssistant } from "../../../types";
import { showToast } from "../../../util/toast";
import "../dashboard/ha-config-updates";
-import "./ha-config-analytics";
@customElement("ha-config-section-updates")
class HaConfigSectionUpdates extends LitElement {
@@ -26,8 +39,20 @@ class HaConfigSectionUpdates extends LitElement {
@state() private _showSkipped = false;
+ @state() private _supervisorInfo?: HassioSupervisorInfo;
+
private _notifyUpdates = false;
+ protected firstUpdated(changedProps) {
+ super.firstUpdated(changedProps);
+
+ if (isComponentLoaded(this.hass, "hassio")) {
+ fetchHassioSupervisorInfo(this.hass).then((data) => {
+ this._supervisorInfo = data;
+ });
+ }
+ }
+
protected render(): TemplateResult {
const canInstallUpdates = this._filterUpdateEntitiesWithInstall(
this.hass.states,
@@ -44,18 +69,27 @@ class HaConfigSectionUpdates extends LitElement {
-
+
${this._showSkipped
? this.hass.localize("ui.panel.config.updates.hide_skipped")
: this.hass.localize("ui.panel.config.updates.show_skipped")}
+ ${this._supervisorInfo?.channel !== "dev"
+ ? html`
+
+ ${this._supervisorInfo?.channel === "stable"
+ ? this.hass.localize("ui.panel.config.updates.join_beta")
+ : this.hass.localize("ui.panel.config.updates.leave_beta")}
+
+ `
+ : ""}
@@ -111,8 +145,53 @@ class HaConfigSectionUpdates extends LitElement {
}
}
- private _toggleSkipped(): void {
- this._showSkipped = !this._showSkipped;
+ private _handleAction(ev: CustomEvent) {
+ switch (ev.detail.index) {
+ case 0:
+ this._showSkipped = !this._showSkipped;
+ break;
+ case 1:
+ this._toggleBeta();
+ break;
+ }
+ }
+
+ private async _toggleBeta(): Promise {
+ if (this._supervisorInfo!.channel === "stable") {
+ const confirmed = await showConfirmationDialog(this, {
+ title: this.hass.localize("ui.dialogs.join_beta_channel.title"),
+ text: html`${this.hass.localize("ui.dialogs.join_beta_channel.warning")}
+
+ ${this.hass.localize("ui.dialogs.join_beta_channel.backup")}
+
+ ${this.hass.localize("ui.dialogs.join_beta_channel.release_items")}
+
+ - Home Assistant Core
+ - Home Assistant Supervisor
+ - Home Assistant Operating System
+
+
+ ${this.hass.localize("ui.dialogs.join_beta_channel.confirm")}`,
+ confirmText: this.hass.localize("ui.panel.config.updates.join_beta"),
+ dismissText: this.hass.localize("ui.common.cancel"),
+ });
+
+ if (!confirmed) {
+ return;
+ }
+ }
+
+ try {
+ const data: Partial = {
+ channel: this._supervisorInfo!.channel === "stable" ? "beta" : "stable",
+ };
+ await setSupervisorOption(this.hass, data);
+ await reloadSupervisor(this.hass);
+ } catch (err: any) {
+ showAlertDialog(this, {
+ text: extractApiErrorMessage(err),
+ });
+ }
}
private async _checkUpdates(): Promise {
diff --git a/src/translations/en.json b/src/translations/en.json
index cb884a4a66..80a30f6966 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -1026,6 +1026,13 @@
"docker": "[%key:supervisor::system::supervisor::unhealthy_reason::docker%]",
"untrusted": "[%key:supervisor::system::supervisor::unhealthy_reason::untrusted%]"
}
+ },
+ "join_beta_channel": {
+ "title": "Join the beta channel",
+ "warning": "[%key:supervisor::system::supervisor::beta_warning%]",
+ "backup": "[%key:supervisor::system::supervisor::beta_backup%]",
+ "release_items": "[%key:supervisor::system::supervisor::beta_release_items%]",
+ "confirm": "[%key:supervisor::system::supervisor::beta_join_confirm%]"
}
},
"duration": {
@@ -1169,6 +1176,8 @@
"show": "show",
"show_skipped": "Show skipped",
"hide_skipped": "Hide skipped",
+ "join_beta": "[%key:supervisor::system::supervisor::join_beta_action%]",
+ "leave_beta": "[%key:supervisor::system::supervisor::leave_beta_action%]",
"skipped": "Skipped"
},
"areas": {