diff --git a/hassio/src/addon-store/hassio-addon-store.ts b/hassio/src/addon-store/hassio-addon-store.ts index 6add263da7..0f67342110 100644 --- a/hassio/src/addon-store/hassio-addon-store.ts +++ b/hassio/src/addon-store/hassio-addon-store.ts @@ -192,8 +192,10 @@ class HassioAddonStore extends LitElement { } private async _loadData() { - fireEvent(this, "supervisor-store-refresh", { store: "addon" }); - fireEvent(this, "supervisor-store-refresh", { store: "supervisor" }); + fireEvent(this, "supervisor-colllection-refresh", { colllection: "addon" }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: "supervisor", + }); } private async _filterChanged(e) { diff --git a/hassio/src/addon-view/hassio-addon-dashboard.ts b/hassio/src/addon-view/hassio-addon-dashboard.ts index 4c01e88c87..ebbaf9254b 100644 --- a/hassio/src/addon-view/hassio-addon-dashboard.ts +++ b/hassio/src/addon-view/hassio-addon-dashboard.ts @@ -190,7 +190,9 @@ class HassioAddonDashboard extends LitElement { const path: string = pathSplit[pathSplit.length - 1]; if (["uninstall", "install", "update", "start", "stop"].includes(path)) { - fireEvent(this, "supervisor-store-refresh", { store: "supervisor" }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: "supervisor", + }); } if (path === "uninstall") { diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index dfb1386662..51f02f651c 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -25,6 +25,7 @@ import { TemplateResult, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; +import memoizeOne from "memoize-one"; import { atLeastVersion } from "../../../../src/common/config/version"; import { fireEvent } from "../../../../src/common/dom/fire_event"; import { navigate } from "../../../../src/common/navigate"; @@ -57,6 +58,7 @@ import { fetchHassioStats, HassioStats, } from "../../../../src/data/hassio/common"; +import { StoreAddon } from "../../../../src/data/supervisor/store"; import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { showAlertDialog, @@ -148,7 +150,16 @@ class HassioAddonInfo extends LitElement { @internalProperty() private _error?: string; + private _addonStoreInfo = memoizeOne( + (slug: string, storeAddons: StoreAddon[]) => + storeAddons.find((addon) => addon.slug === slug) + ); + protected render(): TemplateResult { + const addonStoreInfo = + !this.addon.detached && !this.addon.available + ? this._addonStoreInfo(this.addon.slug, this.supervisor.store.addons) + : undefined; const metrics = [ { description: "Add-on CPU Usage", @@ -176,30 +187,33 @@ class HassioAddonInfo extends LitElement { icon=${mdiArrowUpBoldCircle} iconClass="update" > - ${!this.addon.available + ${!this.addon.available && addonStoreInfo ? !addonArchIsSupported( this.supervisor.info.supported_arch, this.addon.arch ) ? html` -

+

This add-on is not compatible with the processor of your device or the operating system you have installed on your device.

` : html` -

+

You are running Home Assistant ${this.supervisor.core.version}, to update to this version of the add-on you need at least version - ${this.addon.homeassistant} of Home Assistant + ${addonStoreInfo.homeassistant} of Home Assistant

` : ""}
- + Update ${this.addon.changelog @@ -551,7 +565,7 @@ class HassioAddonInfo extends LitElement {
${this._error ? html`
${this._error}
` : ""} - ${!this.addon.available + ${!this.addon.version && addonStoreInfo && !this.addon.available ? !addonArchIsSupported( this.supervisor.info.supported_arch, this.addon.arch @@ -567,8 +581,8 @@ class HassioAddonInfo extends LitElement {

You are running Home Assistant ${this.supervisor.core.version}, to install this add-on you - need at least version ${this.addon.homeassistant} of Home - Assistant + need at least version ${addonStoreInfo!.homeassistant} of + Home Assistant

` : ""} diff --git a/hassio/src/dashboard/hassio-update.ts b/hassio/src/dashboard/hassio-update.ts index 488e541f2f..730d2a245a 100644 --- a/hassio/src/dashboard/hassio-update.ts +++ b/hassio/src/dashboard/hassio-update.ts @@ -148,7 +148,9 @@ export class HassioUpdate extends LitElement { } try { await this.hass.callApi>("POST", item.apiPath); - fireEvent(this, "supervisor-store-refresh", { store: item.key }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: item.key, + }); } catch (err) { // Only show an error if the status code was not expected (user behind proxy) // or no status at all(connection terminated) diff --git a/hassio/src/hassio-main.ts b/hassio/src/hassio-main.ts index 31de2006d7..5283a8cb08 100644 --- a/hassio/src/hassio-main.ts +++ b/hassio/src/hassio-main.ts @@ -3,7 +3,7 @@ import { atLeastVersion } from "../../src/common/config/version"; import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element"; import { fireEvent } from "../../src/common/dom/fire_event"; import { HassioPanelInfo } from "../../src/data/hassio/supervisor"; -import { supervisorStore } from "../../src/data/supervisor/supervisor"; +import { supervisorCollection } from "../../src/data/supervisor/supervisor"; import { makeDialogManager } from "../../src/dialogs/make-dialog-manager"; import "../../src/layouts/hass-loading-screen"; import { HomeAssistant, Route } from "../../src/types"; @@ -77,7 +77,9 @@ export class HassioMain extends SupervisorBaseElement { } if ( - Object.keys(supervisorStore).some((store) => !this.supervisor![store]) + Object.keys(supervisorCollection).some( + (colllection) => !this.supervisor![colllection] + ) ) { return html``; } diff --git a/hassio/src/supervisor-base-element.ts b/hassio/src/supervisor-base-element.ts index 9abdf473a5..8522614ff7 100644 --- a/hassio/src/supervisor-base-element.ts +++ b/hassio/src/supervisor-base-element.ts @@ -19,12 +19,13 @@ import { fetchHassioInfo, fetchHassioSupervisorInfo, } from "../../src/data/hassio/supervisor"; +import { fetchSupervisorStore } from "../../src/data/supervisor/store"; import { getSupervisorEventCollection, subscribeSupervisorEvents, Supervisor, SupervisorObject, - supervisorStore, + supervisorCollection, } from "../../src/data/supervisor/supervisor"; import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin"; import { urlSyncMixin } from "../../src/state/url-sync-mixin"; @@ -32,7 +33,7 @@ import { urlSyncMixin } from "../../src/state/url-sync-mixin"; declare global { interface HASSDomEvents { "supervisor-update": Partial; - "supervisor-store-refresh": { store: SupervisorObject }; + "supervisor-colllection-refresh": { colllection: SupervisorObject }; } } @@ -65,40 +66,40 @@ export class SupervisorBaseElement extends urlSyncMixin( } private async _handleSupervisorStoreRefreshEvent(ev) { - const store = ev.detail.store; + const colllection = ev.detail.colllection; if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) { - this._collections[store].refresh(); + this._collections[colllection].refresh(); return; } const response = await this.hass.callApi>( "GET", - `hassio${supervisorStore[store]}` + `hassio${supervisorCollection[colllection]}` ); - this._updateSupervisor({ [store]: response.data }); + this._updateSupervisor({ [colllection]: response.data }); } private async _initSupervisor(): Promise { this.addEventListener( - "supervisor-store-refresh", + "supervisor-colllection-refresh", this._handleSupervisorStoreRefreshEvent ); if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) { - Object.keys(supervisorStore).forEach((store) => { - this._unsubs[store] = subscribeSupervisorEvents( + Object.keys(supervisorCollection).forEach((colllection) => { + this._unsubs[colllection] = subscribeSupervisorEvents( this.hass, - (data) => this._updateSupervisor({ [store]: data }), - store, - supervisorStore[store] + (data) => this._updateSupervisor({ [colllection]: data }), + colllection, + supervisorCollection[colllection] ); - if (this._collections[store]) { - this._collections[store].refresh(); + if (this._collections[colllection]) { + this._collections[colllection].refresh(); } else { - this._collections[store] = getSupervisorEventCollection( + this._collections[colllection] = getSupervisorEventCollection( this.hass.connection, - store, - supervisorStore[store] + colllection, + supervisorCollection[colllection] ); } }); @@ -122,6 +123,7 @@ export class SupervisorBaseElement extends urlSyncMixin( os, network, resolution, + store, ] = await Promise.all([ fetchHassioAddonsInfo(this.hass), fetchHassioSupervisorInfo(this.hass), @@ -131,6 +133,7 @@ export class SupervisorBaseElement extends urlSyncMixin( fetchHassioHassOsInfo(this.hass), fetchNetworkInfo(this.hass), fetchHassioResolution(this.hass), + fetchSupervisorStore(this.hass), ]); this.supervisor = { @@ -142,6 +145,7 @@ export class SupervisorBaseElement extends urlSyncMixin( os, network, resolution, + store, }; this.addEventListener("supervisor-update", (ev) => diff --git a/hassio/src/system/hassio-core-info.ts b/hassio/src/system/hassio-core-info.ts index 985a242773..c07ea824f5 100644 --- a/hassio/src/system/hassio-core-info.ts +++ b/hassio/src/system/hassio-core-info.ts @@ -167,7 +167,9 @@ class HassioCoreInfo extends LitElement { try { await updateCore(this.hass); - fireEvent(this, "supervisor-store-refresh", { store: "core" }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: "core", + }); } catch (err) { showAlertDialog(this, { title: "Failed to update Home Assistant Core", diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts index 69245d8663..2abe3a74cb 100644 --- a/hassio/src/system/hassio-host-info.ts +++ b/hassio/src/system/hassio-host-info.ts @@ -340,7 +340,7 @@ class HassioHostInfo extends LitElement { try { await updateOS(this.hass); - fireEvent(this, "supervisor-store-refresh", { store: "os" }); + fireEvent(this, "supervisor-colllection-refresh", { colllection: "os" }); } catch (err) { showAlertDialog(this, { title: "Failed to update", @@ -369,7 +369,9 @@ class HassioHostInfo extends LitElement { if (hostname && hostname !== curHostname) { try { await changeHostOptions(this.hass, { hostname }); - fireEvent(this, "supervisor-store-refresh", { store: "host" }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: "host", + }); } catch (err) { showAlertDialog(this, { title: "Setting hostname failed", @@ -382,7 +384,9 @@ class HassioHostInfo extends LitElement { private async _importFromUSB(): Promise { try { await configSyncOS(this.hass); - fireEvent(this, "supervisor-store-refresh", { store: "host" }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: "host", + }); } catch (err) { showAlertDialog(this, { title: "Failed to import from USB", @@ -393,7 +397,9 @@ class HassioHostInfo extends LitElement { private async _loadData(): Promise { if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) { - fireEvent(this, "supervisor-store-refresh", { store: "network" }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: "network", + }); } else { const network = await fetchNetworkInfo(this.hass); fireEvent(this, "supervisor-update", { network }); diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts index ebc96ab32a..3afc373e0b 100644 --- a/hassio/src/system/hassio-supervisor-info.ts +++ b/hassio/src/system/hassio-supervisor-info.ts @@ -317,7 +317,9 @@ class HassioSupervisorInfo extends LitElement { private async _reloadSupervisor(): Promise { await reloadSupervisor(this.hass); - fireEvent(this, "supervisor-store-refresh", { store: "supervisor" }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: "supervisor", + }); } private async _supervisorRestart(ev: CustomEvent): Promise { @@ -366,7 +368,9 @@ class HassioSupervisorInfo extends LitElement { try { await updateSupervisor(this.hass); - fireEvent(this, "supervisor-store-refresh", { store: "supervisor" }); + fireEvent(this, "supervisor-colllection-refresh", { + colllection: "supervisor", + }); } catch (err) { showAlertDialog(this, { title: "Failed to update the supervisor", diff --git a/src/data/hassio/addon.ts b/src/data/hassio/addon.ts index b17f327097..06a4760d42 100644 --- a/src/data/hassio/addon.ts +++ b/src/data/hassio/addon.ts @@ -4,20 +4,33 @@ import { HomeAssistant } from "../../types"; import { SupervisorArch } from "../supervisor/supervisor"; import { hassioApiResultExtractor, HassioResponse } from "./common"; +export type AddonStage = "stable" | "experimental" | "deprecated"; +export type AddonAppArmour = "disable" | "default" | "profile"; +export type AddonRole = "default" | "homeassistant" | "manager" | "admin"; +export type AddonStartup = + | "initialize" + | "system" + | "services" + | "application" + | "once"; +export type AddonState = "started" | "stopped" | null; +export type AddonRepository = "core" | "local" | string; + export interface HassioAddonInfo { advanced: boolean; available: boolean; build: boolean; description: string; detached: boolean; + homeassistant: string; icon: boolean; installed: boolean; logo: boolean; name: string; - repository: "core" | "local" | string; + repository: AddonRepository; slug: string; - stage: "stable" | "experimental" | "deprecated"; - state: "started" | "stopped" | null; + stage: AddonStage; + state: AddonState; update_available: boolean; url: string | null; version_latest: string; @@ -25,7 +38,7 @@ export interface HassioAddonInfo { } export interface HassioAddonDetails extends HassioAddonInfo { - apparmor: "disable" | "default" | "profile"; + apparmor: AddonAppArmour; arch: SupervisorArch[]; audio_input: null | string; audio_output: null | string; @@ -43,10 +56,9 @@ export interface HassioAddonDetails extends HassioAddonInfo { full_access: boolean; gpio: boolean; hassio_api: boolean; - hassio_role: "default" | "homeassistant" | "manager" | "admin"; + hassio_role: AddonRole; hostname: string; homeassistant_api: boolean; - homeassistant: string; host_dbus: boolean; host_ipc: boolean; host_network: boolean; @@ -68,7 +80,7 @@ export interface HassioAddonDetails extends HassioAddonInfo { schema: HaFormSchema[] | null; services_role: string[]; slug: string; - startup: "initialize" | "system" | "services" | "application" | "once"; + startup: AddonStartup; stdin: boolean; watchdog: null | boolean; webui: null | string; diff --git a/src/data/supervisor/store.ts b/src/data/supervisor/store.ts new file mode 100644 index 0000000000..c917388faf --- /dev/null +++ b/src/data/supervisor/store.ts @@ -0,0 +1,51 @@ +import { atLeastVersion } from "../../common/config/version"; +import { HomeAssistant } from "../../types"; +import { AddonRepository, AddonStage } from "../hassio/addon"; +import { hassioApiResultExtractor, HassioResponse } from "../hassio/common"; + +export interface StoreAddon { + advanced: boolean; + available: boolean; + build: boolean; + description: string; + homeassistant: string | null; + icon: boolean; + installed: boolean; + logo: boolean; + name: string; + repository: AddonRepository; + slug: string; + stage: AddonStage; + update_available: boolean; + url: string; + version: string | null; + version_latest: string; +} +interface StoreRepository { + maintainer: string; + name: string; + slug: string; + source: string; + url: string; +} + +export interface SupervisorStore { + addons: StoreAddon[]; + repositories: StoreRepository[]; +} + +export const fetchSupervisorStore = async ( + hass: HomeAssistant +): Promise => { + if (atLeastVersion(hass.config.version, 2021, 2, 4)) { + return await hass.callWS({ + type: "supervisor/api", + endpoint: "/store", + method: "get", + }); + } + + return hassioApiResultExtractor( + await hass.callApi>("GET", `hassio/store`) + ); +}; diff --git a/src/data/supervisor/supervisor.ts b/src/data/supervisor/supervisor.ts index c884c79e0a..4f1eff928a 100644 --- a/src/data/supervisor/supervisor.ts +++ b/src/data/supervisor/supervisor.ts @@ -10,13 +10,14 @@ import { HassioInfo, HassioSupervisorInfo, } from "../hassio/supervisor"; +import { SupervisorStore } from "./store"; export const supervisorWSbaseCommand = { type: "supervisor/api", method: "GET", }; -export const supervisorStore = { +export const supervisorCollection = { host: "/host/info", supervisor: "/supervisor/info", info: "/info", @@ -25,6 +26,7 @@ export const supervisorStore = { resolution: "/resolution/info", os: "/os/info", addon: "/addons", + store: "/store", }; export type SupervisorArch = "armhf" | "armv7" | "aarch64" | "i386" | "amd64"; @@ -36,7 +38,8 @@ export type SupervisorObject = | "network" | "resolution" | "os" - | "addon"; + | "addon" + | "store"; interface supervisorApiRequest { endpoint: string; @@ -61,6 +64,7 @@ export interface Supervisor { resolution: HassioResolution; os: HassioHassOSInfo; addon: HassioAddonsInfo; + store: SupervisorStore; } export const supervisorApiWsRequest = ( @@ -81,7 +85,7 @@ async function processEvent( if (Object.keys(event.data).length === 0) { const data = await supervisorApiWsRequest(conn, { - endpoint: supervisorStore[key], + endpoint: supervisorCollection[key], }); store.setState(data); return;