diff --git a/hassio/src/addon-view/hassio-addon-dashboard.ts b/hassio/src/addon-view/hassio-addon-dashboard.ts index 6f4580870f..98a6c8a843 100644 --- a/hassio/src/addon-view/hassio-addon-dashboard.ts +++ b/hassio/src/addon-view/hassio-addon-dashboard.ts @@ -12,15 +12,17 @@ import { navigate } from "../../../src/common/navigate"; import { extractSearchParam } from "../../../src/common/url/search-params"; import "../../../src/components/ha-circular-progress"; import { + fetchAddonInfo, fetchHassioAddonInfo, fetchHassioAddonsInfo, HassioAddonDetails, } from "../../../src/data/hassio/addon"; import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { - fetchHassioSupervisorInfo, - setSupervisorOption, -} from "../../../src/data/hassio/supervisor"; + addStoreRepository, + fetchSupervisorStore, + StoreAddonDetails, +} from "../../../src/data/supervisor/store"; import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { showConfirmationDialog } from "../../../src/dialogs/generic/show-dialog-box"; import "../../../src/layouts/hass-error-screen"; @@ -45,7 +47,9 @@ class HassioAddonDashboard extends LitElement { @property({ attribute: false }) public route!: Route; - @property({ attribute: false }) public addon?: HassioAddonDetails; + @property({ attribute: false }) public addon?: + | HassioAddonDetails + | StoreAddonDetails; @property({ type: Boolean }) public narrow!: boolean; @@ -173,10 +177,10 @@ class HassioAddonDashboard extends LitElement { const requestedAddon = extractSearchParam("addon"); const requestedAddonRepository = extractSearchParam("repository_url"); if (requestedAddonRepository) { - const supervisorInfo = await fetchHassioSupervisorInfo(this.hass); + const storeInfo = await fetchSupervisorStore(this.hass); if ( - !supervisorInfo.addons_repositories.find( - (repo) => repo === requestedAddonRepository + !storeInfo.repositories.find( + (repo) => repo.source === requestedAddonRepository ) ) { if ( @@ -197,12 +201,7 @@ class HassioAddonDashboard extends LitElement { } try { - await setSupervisorOption(this.hass, { - addons_repositories: [ - ...supervisorInfo.addons_repositories, - requestedAddonRepository, - ], - }); + await addStoreRepository(this.hass, requestedAddonRepository); } catch (err: any) { this._error = extractApiErrorMessage(err); } @@ -245,6 +244,8 @@ class HassioAddonDashboard extends LitElement { if (path === "uninstall") { window.history.back(); + } else if (path === "install") { + this.addon = await fetchHassioAddonInfo(this.hass, this.addon!.slug); } else { await this._routeDataChanged(); } @@ -262,8 +263,7 @@ class HassioAddonDashboard extends LitElement { return; } try { - const addoninfo = await fetchHassioAddonInfo(this.hass, addon); - this.addon = addoninfo; + this.addon = await fetchAddonInfo(this.hass, this.supervisor, addon); } catch (err: any) { this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`; this.addon = undefined; diff --git a/hassio/src/addon-view/hassio-addon-router.ts b/hassio/src/addon-view/hassio-addon-router.ts index fe0bad9c00..66cf1fb8af 100644 --- a/hassio/src/addon-view/hassio-addon-router.ts +++ b/hassio/src/addon-view/hassio-addon-router.ts @@ -1,5 +1,6 @@ import { customElement, property } from "lit/decorators"; import { HassioAddonDetails } from "../../../src/data/hassio/addon"; +import { StoreAddonDetails } from "../../../src/data/supervisor/store"; import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { HassRouterPage, @@ -20,7 +21,9 @@ class HassioAddonRouter extends HassRouterPage { @property({ attribute: false }) public supervisor!: Supervisor; - @property({ attribute: false }) public addon!: HassioAddonDetails; + @property({ attribute: false }) public addon!: + | HassioAddonDetails + | StoreAddonDetails; protected routerOptions: RouterOptions = { defaultPage: "info", diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index 71a0a6f225..6675d7fd55 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -59,7 +59,10 @@ import { fetchHassioStats, HassioStats, } from "../../../../src/data/hassio/common"; -import { StoreAddon } from "../../../../src/data/supervisor/store"; +import { + StoreAddon, + StoreAddonDetails, +} from "../../../../src/data/supervisor/store"; import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { showAlertDialog, @@ -100,7 +103,9 @@ class HassioAddonInfo extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public addon!: HassioAddonDetails; + @property({ attribute: false }) public addon!: + | HassioAddonDetails + | StoreAddonDetails; @property({ attribute: false }) public supervisor!: Supervisor; @@ -143,7 +148,7 @@ class HassioAddonInfo extends LitElement { > ` : ""} - ${!this.addon.protected + ${"protected" in this.addon && !this.addon.protected ? html`
- ${this.addon.state === "started" + ${this.addon.version && this.addon.state === "started" ? html` ${this.supervisor.localize("addon.dashboard.hostname")} @@ -669,7 +674,7 @@ class HassioAddonInfo extends LitElement { } private async _loadData(): Promise { - if (this.addon.state === "started") { + if ("state" in this.addon && this.addon.state === "started") { this._metrics = await fetchHassioStats( this.hass, `addons/${this.addon.slug}` @@ -717,18 +722,22 @@ class HassioAddonInfo extends LitElement { } private get _computeIsRunning(): boolean { - return this.addon?.state === "started"; + return (this.addon as HassioAddonDetails)?.state === "started"; } private get _pathWebui(): string | null { - return ( - this.addon.webui && - this.addon.webui.replace("[HOST]", document.location.hostname) + return (this.addon as HassioAddonDetails).webui!.replace( + "[HOST]", + document.location.hostname ); } private get _computeShowWebUI(): boolean | "" | null { - return !this.addon.ingress && this.addon.webui && this._computeIsRunning; + return ( + !this.addon.ingress && + (this.addon as HassioAddonDetails).webui && + this._computeIsRunning + ); } private _openIngress(): void { @@ -754,7 +763,8 @@ class HassioAddonInfo extends LitElement { private async _startOnBootToggled(): Promise { this._error = undefined; const data: HassioAddonSetOptionParams = { - boot: this.addon.boot === "auto" ? "manual" : "auto", + boot: + (this.addon as HassioAddonDetails).boot === "auto" ? "manual" : "auto", }; try { await setHassioAddonOption(this.hass, this.addon.slug, data); @@ -776,7 +786,7 @@ class HassioAddonInfo extends LitElement { private async _watchdogToggled(): Promise { this._error = undefined; const data: HassioAddonSetOptionParams = { - watchdog: !this.addon.watchdog, + watchdog: !(this.addon as HassioAddonDetails).watchdog, }; try { await setHassioAddonOption(this.hass, this.addon.slug, data); @@ -798,7 +808,7 @@ class HassioAddonInfo extends LitElement { private async _autoUpdateToggled(): Promise { this._error = undefined; const data: HassioAddonSetOptionParams = { - auto_update: !this.addon.auto_update, + auto_update: !(this.addon as HassioAddonDetails).auto_update, }; try { await setHassioAddonOption(this.hass, this.addon.slug, data); @@ -820,7 +830,7 @@ class HassioAddonInfo extends LitElement { private async _protectionToggled(): Promise { this._error = undefined; const data: HassioAddonSetSecurityParams = { - protected: !this.addon.protected, + protected: !(this.addon as HassioAddonDetails).protected, }; try { await setHassioAddonSecurity(this.hass, this.addon.slug, data); @@ -842,7 +852,7 @@ class HassioAddonInfo extends LitElement { private async _panelToggled(): Promise { this._error = undefined; const data: HassioAddonSetOptionParams = { - ingress_panel: !this.addon.ingress_panel, + ingress_panel: !(this.addon as HassioAddonDetails).ingress_panel, }; try { await setHassioAddonOption(this.hass, this.addon.slug, data); @@ -870,7 +880,7 @@ class HassioAddonInfo extends LitElement { showHassioMarkdownDialog(this, { title: this.supervisor.localize("addon.dashboard.changelog"), - content: extractChangelog(this.addon, content), + content: extractChangelog(this.addon as HassioAddonDetails, content), }); } catch (err: any) { showAlertDialog(this, { diff --git a/hassio/src/backups/hassio-backups.ts b/hassio/src/backups/hassio-backups.ts index c8e8e5e30c..99d2869f8b 100644 --- a/hassio/src/backups/hassio-backups.ts +++ b/hassio/src/backups/hassio-backups.ts @@ -98,9 +98,8 @@ export class HassioBackups extends LitElement { if (backup.content.addons.length !== 0) { for (const addon of backup.content.addons) { content.push( - this.supervisor.supervisor.addons.find( - (entry) => entry.slug === addon - )?.name || addon + this.supervisor.addon.addons.find((entry) => entry.slug === addon) + ?.name || addon ); } } diff --git a/hassio/src/components/supervisor-backup-content.ts b/hassio/src/components/supervisor-backup-content.ts index 45faee620f..dd5c38a178 100644 --- a/hassio/src/components/supervisor-backup-content.ts +++ b/hassio/src/components/supervisor-backup-content.ts @@ -96,7 +96,7 @@ export class SupervisorBackupContent extends LitElement { : ["ssl", "share", "media", "addons/local"] ); this.addons = _computeAddons( - this.backup ? this.backup.addons : this.supervisor?.supervisor.addons + this.backup ? this.backup.addons : this.supervisor?.addon.addons ); this.backupType = this.backup?.type || "full"; this.backupName = this.backup?.name || ""; diff --git a/hassio/src/dashboard/hassio-addons.ts b/hassio/src/dashboard/hassio-addons.ts index 4e685ef361..cc855ccc69 100644 --- a/hassio/src/dashboard/hassio-addons.ts +++ b/hassio/src/dashboard/hassio-addons.ts @@ -24,7 +24,7 @@ class HassioAddons extends LitElement { ? html`

${this.supervisor.localize("dashboard.addons")}

` : ""}
- ${!this.supervisor.supervisor.addons?.length + ${!this.supervisor.addon.addons.length ? html`
@@ -34,7 +34,7 @@ class HassioAddons extends LitElement {
` - : this.supervisor.supervisor.addons + : this.supervisor.addon.addons .sort((a, b) => caseInsensitiveStringCompare(a.name, b.name)) .map( (addon) => html` diff --git a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts index e701640923..82325db788 100644 --- a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts +++ b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts @@ -87,7 +87,7 @@ class HassioRepositoriesDialog extends LitElement { const repositories = this._filteredRepositories(this._repositories); const usedRepositories = this._filteredUsedRepositories( repositories, - this._dialogParams.supervisor.supervisor.addons + this._dialogParams.supervisor.addon.addons ); return html` - stringCompare(a.name, b.name) - ); + const addonsInfo = await fetchHassioAddonsInfo(this.hass); + this._addons = addonsInfo.addons + .filter((addon) => addon.version) + .sort((a, b) => stringCompare(a.name, b.name)); } else { showAlertDialog(this, { title: this.hass.localize( diff --git a/src/data/hassio/addon.ts b/src/data/hassio/addon.ts index c27d735b0e..5c1e7f49c2 100644 --- a/src/data/hassio/addon.ts +++ b/src/data/hassio/addon.ts @@ -1,7 +1,9 @@ import { atLeastVersion } from "../../common/config/version"; import type { HaFormSchema } from "../../components/ha-form/types"; import { HomeAssistant } from "../../types"; -import { SupervisorArch } from "../supervisor/supervisor"; +import { supervisorApiCall } from "../supervisor/common"; +import { StoreAddonDetails } from "../supervisor/store"; +import { Supervisor, SupervisorArch } from "../supervisor/supervisor"; import { extractApiErrorMessage, hassioApiResultExtractor, @@ -363,3 +365,15 @@ export const uninstallHassioAddon = async ( `hassio/addons/${slug}/uninstall` ); }; + +export const fetchAddonInfo = ( + hass: HomeAssistant, + supervisor: Supervisor, + addonSlug: string +): Promise => + supervisorApiCall( + hass, + !supervisor.addon?.addons.find((addon) => addon.slug === addonSlug) + ? `/store/addons/${addonSlug}` // Use /store/addons when add-on is not installed + : `/addons/${addonSlug}/info` // Use /addons when add-on is installed + ); diff --git a/src/data/hassio/supervisor.ts b/src/data/hassio/supervisor.ts index e08600098b..9f5bcd0f28 100644 --- a/src/data/hassio/supervisor.ts +++ b/src/data/hassio/supervisor.ts @@ -1,7 +1,6 @@ import { atLeastVersion } from "../../common/config/version"; import { HomeAssistant, PanelInfo } from "../../types"; import { SupervisorArch } from "../supervisor/supervisor"; -import { HassioAddonInfo } from "./addon"; import { hassioApiResultExtractor, HassioResponse } from "./common"; export type HassioHomeAssistantInfo = { @@ -22,7 +21,7 @@ export type HassioHomeAssistantInfo = { }; export type HassioSupervisorInfo = { - addons: HassioAddonInfo[]; + addons: string[]; addons_repositories: string[]; arch: SupervisorArch; channel: string; diff --git a/src/data/supervisor/store.ts b/src/data/supervisor/store.ts index e02b46b56f..d71f96401c 100644 --- a/src/data/supervisor/store.ts +++ b/src/data/supervisor/store.ts @@ -1,6 +1,7 @@ import { HomeAssistant } from "../../types"; -import { AddonRepository, AddonStage } from "../hassio/addon"; +import { AddonStage } from "../hassio/addon"; import { supervisorApiCall } from "./common"; +import { SupervisorArch } from "./supervisor"; export interface StoreAddon { advanced: boolean; @@ -12,14 +13,34 @@ export interface StoreAddon { installed: boolean; logo: boolean; name: string; - repository: AddonRepository; + repository: string; slug: string; stage: AddonStage; update_available: boolean; url: string; - version: string | null; version_latest: string; + version: null; } + +export interface StoreAddonDetails extends StoreAddon { + apparmor: boolean; + arch: SupervisorArch[]; + auth_api: boolean; + detached: boolean; + docker_api: boolean; + documentation: boolean; + full_access: boolean; + hassio_api: boolean; + hassio_role: string; + homeassistant_api: boolean; + host_network: boolean; + host_pid: boolean; + ingress: boolean; + long_description: string; + rating: number; + signed: boolean; +} + interface StoreRepository { maintainer: string; name: string; diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 150eee54e4..ee987044ab 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -34,7 +34,7 @@ import "../../components/ha-circular-progress"; import "../../components/ha-header-bar"; import "../../components/ha-icon-button"; import "../../components/ha-textfield"; -import { fetchHassioSupervisorInfo } from "../../data/hassio/supervisor"; +import { fetchHassioAddonsInfo } from "../../data/hassio/addon"; import { domainToName } from "../../data/integration"; import { getPanelNameTranslationKey } from "../../data/panel"; import { PageNavigation } from "../../layouts/hass-tabs-subpage"; @@ -586,7 +586,7 @@ export class QuickBar extends LitElement { const sectionItems = this._generateNavigationConfigSectionCommands(); const supervisorItems: BaseNavigationCommand[] = []; if (isComponentLoaded(this.hass, "hassio")) { - const supervisorInfo = await fetchHassioSupervisorInfo(this.hass); + const addonsInfo = await fetchHassioAddonsInfo(this.hass); supervisorItems.push({ path: "/hassio/store", primaryText: this.hass.localize( @@ -599,7 +599,7 @@ export class QuickBar extends LitElement { "ui.dialogs.quick-bar.commands.navigation.addon_dashboard" ), }); - for (const addon of supervisorInfo.addons) { + for (const addon of addonsInfo.addons.filter((a) => a.version)) { supervisorItems.push({ path: `/hassio/addon/${addon.slug}`, primaryText: this.hass.localize( diff --git a/src/panels/config/logs/ha-config-logs.ts b/src/panels/config/logs/ha-config-logs.ts index 1981a08a25..d8f811fcf4 100644 --- a/src/panels/config/logs/ha-config-logs.ts +++ b/src/panels/config/logs/ha-config-logs.ts @@ -6,7 +6,7 @@ import { extractSearchParam } from "../../../common/url/search-params"; import "../../../components/ha-button-menu"; import "../../../components/search-input"; import { LogProvider } from "../../../data/error_log"; -import { fetchHassioSupervisorInfo } from "../../../data/hassio/supervisor"; +import { fetchHassioAddonsInfo } from "../../../data/hassio/addon"; import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../resources/styles"; @@ -167,13 +167,15 @@ export class HaConfigLogs extends LitElement { private async _getInstalledAddons() { try { - const supervisorInfo = await fetchHassioSupervisorInfo(this.hass); + const addonsInfo = await fetchHassioAddonsInfo(this.hass); this._logProviders = [ ...this._logProviders, - ...supervisorInfo.addons.map((addon) => ({ - key: addon.slug, - name: addon.name, - })), + ...addonsInfo.addons + .filter((addon) => addon.version) + .map((addon) => ({ + key: addon.slug, + name: addon.name, + })), ]; } catch (err) { // Ignore, nothing the user can do anyway