Use websockets (#8403)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Joakim Sørensen 2021-02-18 18:18:05 +01:00 committed by GitHub
parent 17410874e3
commit 09e7600d86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 868 additions and 142 deletions

View File

@ -48,7 +48,7 @@ class HcCast extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
if (this.lovelaceConfig === undefined) { if (this.lovelaceConfig === undefined) {
return html` <hass-loading-screen no-toolbar></hass-loading-screen>> `; return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
} }
const error = const error =

View File

@ -11,19 +11,18 @@ import {
PropertyValues, PropertyValues,
} from "lit-element"; } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/common/search/search-input"; import "../../../src/common/search/search-input";
import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-svg-icon"; import "../../../src/components/ha-svg-icon";
import { import {
fetchHassioAddonsInfo,
HassioAddonInfo, HassioAddonInfo,
HassioAddonRepository, HassioAddonRepository,
reloadHassioAddons, reloadHassioAddons,
} from "../../../src/data/hassio/addon"; } from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { fetchHassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
import "../../../src/layouts/hass-loading-screen"; import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
@ -51,46 +50,27 @@ const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
class HassioAddonStore extends LitElement { class HassioAddonStore extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean; @property({ type: Boolean }) public narrow!: boolean;
@property({ attribute: false }) public route!: Route; @property({ attribute: false }) public route!: Route;
@property({ attribute: false }) private _addons?: HassioAddonInfo[];
@property({ attribute: false }) private _repos?: HassioAddonRepository[];
@internalProperty() private _filter?: string; @internalProperty() private _filter?: string;
public async refreshData() { public async refreshData() {
this._repos = undefined;
this._addons = undefined;
this._filter = undefined;
await reloadHassioAddons(this.hass); await reloadHassioAddons(this.hass);
await this._loadData(); await this._loadData();
} }
protected render(): TemplateResult { protected render(): TemplateResult {
const repos: TemplateResult[] = []; let repos: TemplateResult[] = [];
if (this._repos) { if (this.supervisor.addon.repositories) {
for (const repo of this._repos) { repos = this.addonRepositories(
const addons = this._addons!.filter( this.supervisor.addon.repositories,
(addon) => addon.repository === repo.slug this.supervisor.addon.addons
); );
if (addons.length === 0) {
continue;
}
repos.push(html`
<hassio-addon-repository
.hass=${this.hass}
.repo=${repo}
.addons=${addons}
.filter=${this._filter!}
></hassio-addon-repository>
`);
}
} }
return html` return html`
@ -159,6 +139,27 @@ class HassioAddonStore extends LitElement {
this._loadData(); this._loadData();
} }
private addonRepositories = memoizeOne(
(repositories: HassioAddonRepository[], addons: HassioAddonInfo[]) => {
return repositories.sort(sortRepos).map((repo) => {
const filteredAddons = addons.filter(
(addon) => addon.repository === repo.slug
);
return filteredAddons.length !== 0
? html`
<hassio-addon-repository
.hass=${this.hass}
.repo=${repo}
.addons=${filteredAddons}
.filter=${this._filter!}
></hassio-addon-repository>
`
: html``;
});
}
);
private _handleAction(ev: CustomEvent<ActionDetail>) { private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) { switch (ev.detail.index) {
case 0: case 0:
@ -181,7 +182,7 @@ class HassioAddonStore extends LitElement {
private async _manageRepositories() { private async _manageRepositories() {
showRepositoriesDialog(this, { showRepositoriesDialog(this, {
repos: this._repos!, repos: this.supervisor.addon.repositories,
loadData: () => this._loadData(), loadData: () => this._loadData(),
}); });
} }
@ -191,18 +192,8 @@ class HassioAddonStore extends LitElement {
} }
private async _loadData() { private async _loadData() {
try { fireEvent(this, "supervisor-store-refresh", { store: "addon" });
const [addonsInfo, supervisor] = await Promise.all([ fireEvent(this, "supervisor-store-refresh", { store: "supervisor" });
fetchHassioAddonsInfo(this.hass),
fetchHassioSupervisorInfo(this.hass),
]);
fireEvent(this, "supervisor-update", { supervisor });
this._repos = addonsInfo.repositories;
this._repos.sort(sortRepos);
this._addons = addonsInfo.addons;
} catch (err) {
alert(extractApiErrorMessage(err));
}
} }
private async _filterChanged(e) { private async _filterChanged(e) {

View File

@ -24,9 +24,7 @@ import {
HassioAddonDetails, HassioAddonDetails,
} from "../../../src/data/hassio/addon"; } from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { fetchHassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-error-screen"; import "../../../src/layouts/hass-error-screen";
import "../../../src/layouts/hass-loading-screen"; import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
@ -192,7 +190,7 @@ class HassioAddonDashboard extends LitElement {
const path: string = pathSplit[pathSplit.length - 1]; const path: string = pathSplit[pathSplit.length - 1];
if (["uninstall", "install", "update", "start", "stop"].includes(path)) { if (["uninstall", "install", "update", "start", "stop"].includes(path)) {
await this._updateSupervisor(); fireEvent(this, "supervisor-store-refresh", { store: "supervisor" });
} }
if (path === "uninstall") { if (path === "uninstall") {
@ -221,18 +219,6 @@ class HassioAddonDashboard extends LitElement {
this.addon = undefined; this.addon = undefined;
} }
} }
private async _updateSupervisor(): Promise<void> {
try {
const supervisor = await fetchHassioSupervisorInfo(this.hass);
fireEvent(this, "supervisor-update", { supervisor });
} catch (err) {
showAlertDialog(this, {
title: "Failed to fetch supervisor information",
text: extractApiErrorMessage(err),
});
}
}
} }
declare global { declare global {

View File

@ -43,10 +43,13 @@ import {
HassioAddonSetOptionParams, HassioAddonSetOptionParams,
HassioAddonSetSecurityParams, HassioAddonSetSecurityParams,
installHassioAddon, installHassioAddon,
restartHassioAddon,
setHassioAddonOption, setHassioAddonOption,
setHassioAddonSecurity, setHassioAddonSecurity,
startHassioAddon, startHassioAddon,
stopHassioAddon,
uninstallHassioAddon, uninstallHassioAddon,
updateHassioAddon,
validateHassioAddonOption, validateHassioAddonOption,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import { import {
@ -196,13 +199,9 @@ class HassioAddonInfo extends LitElement {
: ""} : ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-call-api-button <ha-progress-button @click=${this._updateClicked}>
.hass=${this.hass}
.disabled=${!this.addon.available}
path="hassio/addons/${this.addon.slug}/update"
>
Update Update
</ha-call-api-button> </ha-progress-button>
${this.addon.changelog ${this.addon.changelog
? html` ? html`
<mwc-button @click=${this._openChangelog}> <mwc-button @click=${this._openChangelog}>
@ -579,20 +578,18 @@ class HassioAddonInfo extends LitElement {
${this.addon.version ${this.addon.version
? this._computeIsRunning ? this._computeIsRunning
? html` ? html`
<ha-call-api-button <ha-progress-button
class="warning" class="warning"
.hass=${this.hass} @click=${this._stopClicked}
.path="hassio/addons/${this.addon.slug}/stop"
> >
Stop Stop
</ha-call-api-button> </ha-progress-button>
<ha-call-api-button <ha-progress-button
class="warning" class="warning"
.hass=${this.hass} @click=${this._restartClicked}
.path="hassio/addons/${this.addon.slug}/restart"
> >
Restart Restart
</ha-call-api-button> </ha-progress-button>
` `
: html` : html`
<ha-progress-button @click=${this._startClicked}> <ha-progress-button @click=${this._startClicked}>
@ -883,6 +880,82 @@ class HassioAddonInfo extends LitElement {
button.progress = false; button.progress = false;
} }
private async _stopClicked(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
try {
await stopHassioAddon(this.hass, this.addon.slug);
const eventdata = {
success: true,
response: undefined,
path: "stop",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
showAlertDialog(this, {
title: "Failed to stop addon",
text: extractApiErrorMessage(err),
});
}
button.progress = false;
}
private async _restartClicked(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
try {
await restartHassioAddon(this.hass, this.addon.slug);
const eventdata = {
success: true,
response: undefined,
path: "stop",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
showAlertDialog(this, {
title: "Failed to restart addon",
text: extractApiErrorMessage(err),
});
}
button.progress = false;
}
private async _updateClicked(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: this.addon.name,
text: "Are you sure you want to update this add-on?",
confirmText: "update add-on",
dismissText: "no",
});
if (!confirmed) {
button.progress = false;
return;
}
this._error = undefined;
try {
await updateHassioAddon(this.hass, this.addon.slug);
const eventdata = {
success: true,
response: undefined,
path: "update",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
showAlertDialog(this, {
title: "Failed to update addon",
text: extractApiErrorMessage(err),
});
}
button.progress = false;
}
private async _startClicked(ev: CustomEvent): Promise<void> { private async _startClicked(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any; const button = ev.currentTarget as any;
button.progress = true; button.progress = true;
@ -891,10 +964,10 @@ class HassioAddonInfo extends LitElement {
this.hass, this.hass,
this.addon.slug this.addon.slug
); );
if (!validate.data.valid) { if (!validate.valid) {
await showConfirmationDialog(this, { await showConfirmationDialog(this, {
title: "Failed to start addon - configuration validation failed!", title: "Failed to start addon - configuration validation failed!",
text: validate.data.message.split(" Got ")[0], text: validate.message.split(" Got ")[0],
confirm: () => this._openConfiguration(), confirm: () => this._openConfiguration(),
confirmText: "Go to configuration", confirmText: "Go to configuration",
dismissText: "Cancel", dismissText: "Cancel",

View File

@ -10,6 +10,7 @@ import {
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-svg-icon"; import "../../../src/components/ha-svg-icon";
@ -64,6 +65,7 @@ export class HassioUpdate extends LitElement {
<div class="card-group"> <div class="card-group">
${this._renderUpdateCard( ${this._renderUpdateCard(
"Home Assistant Core", "Home Assistant Core",
"core",
this.supervisor.core, this.supervisor.core,
"hassio/homeassistant/update", "hassio/homeassistant/update",
`https://${ `https://${
@ -72,6 +74,7 @@ export class HassioUpdate extends LitElement {
)} )}
${this._renderUpdateCard( ${this._renderUpdateCard(
"Supervisor", "Supervisor",
"supervisor",
this.supervisor.supervisor, this.supervisor.supervisor,
"hassio/supervisor/update", "hassio/supervisor/update",
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}` `https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
@ -79,6 +82,7 @@ export class HassioUpdate extends LitElement {
${this.supervisor.host.features.includes("hassos") ${this.supervisor.host.features.includes("hassos")
? this._renderUpdateCard( ? this._renderUpdateCard(
"Operating System", "Operating System",
"os",
this.supervisor.os, this.supervisor.os,
"hassio/os/update", "hassio/os/update",
`https://github.com//home-assistant/hassos/releases/tag/${this.supervisor.os.version_latest}` `https://github.com//home-assistant/hassos/releases/tag/${this.supervisor.os.version_latest}`
@ -91,6 +95,7 @@ export class HassioUpdate extends LitElement {
private _renderUpdateCard( private _renderUpdateCard(
name: string, name: string,
key: string,
object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo, object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo,
apiPath: string, apiPath: string,
releaseNotesUrl: string releaseNotesUrl: string
@ -116,6 +121,7 @@ export class HassioUpdate extends LitElement {
<ha-progress-button <ha-progress-button
.apiPath=${apiPath} .apiPath=${apiPath}
.name=${name} .name=${name}
.key=${key}
.version=${object.version_latest} .version=${object.version_latest}
@click=${this._confirmUpdate} @click=${this._confirmUpdate}
> >
@ -142,6 +148,7 @@ export class HassioUpdate extends LitElement {
} }
try { try {
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath); await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
fireEvent(this, "supervisor-store-refresh", { store: item.key });
} catch (err) { } catch (err) {
// Only show an error if the status code was not expected (user behind proxy) // Only show an error if the status code was not expected (user behind proxy)
// or no status at all(connection terminated) // or no status at all(connection terminated)

View File

@ -3,7 +3,9 @@ import { atLeastVersion } from "../../src/common/config/version";
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../src/common/dom/fire_event"; import { fireEvent } from "../../src/common/dom/fire_event";
import { HassioPanelInfo } from "../../src/data/hassio/supervisor"; import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
import { supervisorStore } from "../../src/data/supervisor/supervisor";
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager"; import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
import "../../src/layouts/hass-loading-screen";
import { HomeAssistant, Route } from "../../src/types"; import { HomeAssistant, Route } from "../../src/types";
import "./hassio-router"; import "./hassio-router";
import { SupervisorBaseElement } from "./supervisor-base-element"; import { SupervisorBaseElement } from "./supervisor-base-element";
@ -71,8 +73,15 @@ export class HassioMain extends SupervisorBaseElement {
protected render() { protected render() {
if (!this.supervisor || !this.hass) { if (!this.supervisor || !this.hass) {
return html``; return html`<hass-loading-screen></hass-loading-screen>`;
} }
if (
Object.keys(supervisorStore).some((store) => !this.supervisor![store])
) {
return html`<hass-loading-screen></hass-loading-screen>`;
}
return html` return html`
<hassio-router <hassio-router
.hass=${this.hass} .hass=${this.hass}

View File

@ -53,12 +53,13 @@ class HassioRouter extends HassRouterPage {
const route = el.nodeName === "HASSIO-PANEL" ? this.route : this.routeTail; const route = el.nodeName === "HASSIO-PANEL" ? this.route : this.routeTail;
el.hass = this.hass; el.hass = this.hass;
el.supervisor = this.supervisor;
el.narrow = this.narrow; el.narrow = this.narrow;
el.route = route; el.route = route;
if (el.localName === "hassio-ingress-view") { if (el.localName === "hassio-ingress-view") {
el.ingressPanel = this.panel.config && this.panel.config.ingress; el.ingressPanel = this.panel.config && this.panel.config.ingress;
} else {
el.supervisor = this.supervisor;
} }
} }

View File

@ -1,4 +1,13 @@
import { LitElement, property, PropertyValues } from "lit-element"; import { Collection, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
internalProperty,
LitElement,
property,
PropertyValues,
} from "lit-element";
import { atLeastVersion } from "../../src/common/config/version";
import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon";
import { HassioResponse } from "../../src/data/hassio/common";
import { import {
fetchHassioHassOsInfo, fetchHassioHassOsInfo,
fetchHassioHostInfo, fetchHassioHostInfo,
@ -10,13 +19,20 @@ import {
fetchHassioInfo, fetchHassioInfo,
fetchHassioSupervisorInfo, fetchHassioSupervisorInfo,
} from "../../src/data/hassio/supervisor"; } from "../../src/data/hassio/supervisor";
import { Supervisor } from "../../src/data/supervisor/supervisor"; import {
getSupervisorEventCollection,
subscribeSupervisorEvents,
Supervisor,
SupervisorObject,
supervisorStore,
} from "../../src/data/supervisor/supervisor";
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin"; import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
import { urlSyncMixin } from "../../src/state/url-sync-mixin"; import { urlSyncMixin } from "../../src/state/url-sync-mixin";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
"supervisor-update": Partial<Supervisor>; "supervisor-update": Partial<Supervisor>;
"supervisor-store-refresh": { store: SupervisorObject };
} }
} }
@ -25,6 +41,20 @@ export class SupervisorBaseElement extends urlSyncMixin(
) { ) {
@property({ attribute: false }) public supervisor?: Supervisor; @property({ attribute: false }) public supervisor?: Supervisor;
@internalProperty() private _unsubs: Record<string, UnsubscribeFunc> = {};
@internalProperty() private _collections: Record<
string,
Collection<unknown>
> = {};
public disconnectedCallback() {
super.disconnectedCallback();
Object.keys(this._unsubs).forEach((unsub) => {
this._unsubs[unsub]();
});
}
protected _updateSupervisor(obj: Partial<Supervisor>): void { protected _updateSupervisor(obj: Partial<Supervisor>): void {
this.supervisor = { ...this.supervisor!, ...obj }; this.supervisor = { ...this.supervisor!, ...obj };
} }
@ -32,13 +62,59 @@ export class SupervisorBaseElement extends urlSyncMixin(
protected firstUpdated(changedProps: PropertyValues): void { protected firstUpdated(changedProps: PropertyValues): void {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
this._initSupervisor(); this._initSupervisor();
this.addEventListener("supervisor-update", (ev) => }
this._updateSupervisor(ev.detail)
private async _handleSupervisorStoreRefreshEvent(ev) {
const store = ev.detail.store;
if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
this._collections[store].refresh();
return;
}
const response = await this.hass.callApi<HassioResponse<any>>(
"GET",
`hassio${supervisorStore[store]}`
); );
this._updateSupervisor({ [store]: response.data });
} }
private async _initSupervisor(): Promise<void> { private async _initSupervisor(): Promise<void> {
this.addEventListener(
"supervisor-store-refresh",
this._handleSupervisorStoreRefreshEvent
);
if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
Object.keys(supervisorStore).forEach((store) => {
this._unsubs[store] = subscribeSupervisorEvents(
this.hass,
(data) => this._updateSupervisor({ [store]: data }),
store,
supervisorStore[store]
);
if (this._collections[store]) {
this._collections[store].refresh();
} else {
this._collections[store] = getSupervisorEventCollection(
this.hass.connection,
store,
supervisorStore[store]
);
}
});
if (this.supervisor === undefined) {
Object.keys(this._collections).forEach((collection) =>
this._updateSupervisor({
[collection]: this._collections[collection].state,
})
);
}
return;
}
const [ const [
addon,
supervisor, supervisor,
host, host,
core, core,
@ -47,6 +123,7 @@ export class SupervisorBaseElement extends urlSyncMixin(
network, network,
resolution, resolution,
] = await Promise.all([ ] = await Promise.all([
fetchHassioAddonsInfo(this.hass),
fetchHassioSupervisorInfo(this.hass), fetchHassioSupervisorInfo(this.hass),
fetchHassioHostInfo(this.hass), fetchHassioHostInfo(this.hass),
fetchHassioHomeAssistantInfo(this.hass), fetchHassioHomeAssistantInfo(this.hass),
@ -57,6 +134,7 @@ export class SupervisorBaseElement extends urlSyncMixin(
]); ]);
this.supervisor = { this.supervisor = {
addon,
supervisor, supervisor,
host, host,
core, core,
@ -65,5 +143,9 @@ export class SupervisorBaseElement extends urlSyncMixin(
network, network,
resolution, resolution,
}; };
this.addEventListener("supervisor-update", (ev) =>
this._updateSupervisor(ev.detail)
);
} }
} }

View File

@ -10,6 +10,7 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
@ -166,6 +167,7 @@ class HassioCoreInfo extends LitElement {
try { try {
await updateCore(this.hass); await updateCore(this.hass);
fireEvent(this, "supervisor-store-refresh", { store: "core" });
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Failed to update Home Assistant Core", title: "Failed to update Home Assistant Core",

View File

@ -13,6 +13,7 @@ import {
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button-menu";
@ -26,7 +27,6 @@ import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware";
import { import {
changeHostOptions, changeHostOptions,
configSyncOS, configSyncOS,
fetchHassioHostInfo,
rebootHost, rebootHost,
shutdownHost, shutdownHost,
updateOS, updateOS,
@ -340,6 +340,7 @@ class HassioHostInfo extends LitElement {
try { try {
await updateOS(this.hass); await updateOS(this.hass);
fireEvent(this, "supervisor-store-refresh", { store: "os" });
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Failed to update", title: "Failed to update",
@ -368,8 +369,7 @@ class HassioHostInfo extends LitElement {
if (hostname && hostname !== curHostname) { if (hostname && hostname !== curHostname) {
try { try {
await changeHostOptions(this.hass, { hostname }); await changeHostOptions(this.hass, { hostname });
const host = await fetchHassioHostInfo(this.hass); fireEvent(this, "supervisor-store-refresh", { store: "host" });
fireEvent(this, "supervisor-update", { host });
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Setting hostname failed", title: "Setting hostname failed",
@ -382,8 +382,7 @@ class HassioHostInfo extends LitElement {
private async _importFromUSB(): Promise<void> { private async _importFromUSB(): Promise<void> {
try { try {
await configSyncOS(this.hass); await configSyncOS(this.hass);
const host = await fetchHassioHostInfo(this.hass); fireEvent(this, "supervisor-store-refresh", { store: "host" });
fireEvent(this, "supervisor-update", { host });
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Failed to import from USB", title: "Failed to import from USB",
@ -393,8 +392,12 @@ class HassioHostInfo extends LitElement {
} }
private async _loadData(): Promise<void> { private async _loadData(): Promise<void> {
const network = await fetchNetworkInfo(this.hass); if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
fireEvent(this, "supervisor-update", { network }); fireEvent(this, "supervisor-store-refresh", { store: "network" });
} else {
const network = await fetchNetworkInfo(this.hass);
fireEvent(this, "supervisor-update", { network });
}
} }
static get styles(): CSSResult[] { static get styles(): CSSResult[] {

View File

@ -19,7 +19,6 @@ import {
HassioStats, HassioStats,
} from "../../../src/data/hassio/common"; } from "../../../src/data/hassio/common";
import { import {
fetchHassioSupervisorInfo,
reloadSupervisor, reloadSupervisor,
restartSupervisor, restartSupervisor,
setSupervisorOption, setSupervisorOption,
@ -318,8 +317,7 @@ class HassioSupervisorInfo extends LitElement {
private async _reloadSupervisor(): Promise<void> { private async _reloadSupervisor(): Promise<void> {
await reloadSupervisor(this.hass); await reloadSupervisor(this.hass);
const supervisor = await fetchHassioSupervisorInfo(this.hass); fireEvent(this, "supervisor-store-refresh", { store: "supervisor" });
fireEvent(this, "supervisor-update", { supervisor });
} }
private async _supervisorRestart(ev: CustomEvent): Promise<void> { private async _supervisorRestart(ev: CustomEvent): Promise<void> {
@ -368,6 +366,7 @@ class HassioSupervisorInfo extends LitElement {
try { try {
await updateSupervisor(this.hass); await updateSupervisor(this.hass);
fireEvent(this, "supervisor-store-refresh", { store: "supervisor" });
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Failed to update the supervisor", title: "Failed to update the supervisor",

View File

@ -1,11 +1,15 @@
export const atLeastVersion = ( export const atLeastVersion = (
version: string, version: string,
major: number, major: number,
minor: number minor: number,
patch?: number
): boolean => { ): boolean => {
const [haMajor, haMinor] = version.split(".", 2); const [haMajor, haMinor, haPatch] = version.split(".", 3);
return ( return (
Number(haMajor) > major || Number(haMajor) > major ||
(Number(haMajor) === major && Number(haMinor) >= minor) (Number(haMajor) === major && Number(haMinor) >= minor) ||
(patch !== undefined &&
Number(haMajor) === major && Number(haMinor) === minor &&
Number(haPatch) >= patch)
); );
}; };

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HaFormSchema } from "../../components/ha-form/ha-form"; import { HaFormSchema } from "../../components/ha-form/ha-form";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { SupervisorArch } from "../supervisor/supervisor"; import { SupervisorArch } from "../supervisor/supervisor";
@ -102,10 +103,28 @@ export interface HassioAddonSetOptionParams {
} }
export const reloadHassioAddons = async (hass: HomeAssistant) => { export const reloadHassioAddons = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/addons/reload",
method: "post",
});
return;
}
await hass.callApi<HassioResponse<void>>("POST", `hassio/addons/reload`); await hass.callApi<HassioResponse<void>>("POST", `hassio/addons/reload`);
}; };
export const fetchHassioAddonsInfo = async (hass: HomeAssistant) => { export const fetchHassioAddonsInfo = async (
hass: HomeAssistant
): Promise<HassioAddonsInfo> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/addons",
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioAddonsInfo>>("GET", `hassio/addons`) await hass.callApi<HassioResponse<HassioAddonsInfo>>("GET", `hassio/addons`)
); );
@ -114,7 +133,15 @@ export const fetchHassioAddonsInfo = async (hass: HomeAssistant) => {
export const fetchHassioAddonInfo = async ( export const fetchHassioAddonInfo = async (
hass: HomeAssistant, hass: HomeAssistant,
slug: string slug: string
) => { ): Promise<HassioAddonDetails> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/info`,
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioAddonDetails>>( await hass.callApi<HassioResponse<HassioAddonDetails>>(
"GET", "GET",
@ -149,6 +176,16 @@ export const setHassioAddonOption = async (
slug: string, slug: string,
data: HassioAddonSetOptionParams data: HassioAddonSetOptionParams
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/options`,
method: "post",
data,
});
return;
}
await hass.callApi<HassioResponse<void>>( await hass.callApi<HassioResponse<void>>(
"POST", "POST",
`hassio/addons/${slug}/options`, `hassio/addons/${slug}/options`,
@ -159,21 +196,64 @@ export const setHassioAddonOption = async (
export const validateHassioAddonOption = async ( export const validateHassioAddonOption = async (
hass: HomeAssistant, hass: HomeAssistant,
slug: string slug: string
) => { ): Promise<{ message: string; valid: boolean }> => {
return await hass.callApi< if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
HassioResponse<{ message: string; valid: boolean }> return await hass.callWS({
>("POST", `hassio/addons/${slug}/options/validate`); type: "supervisor/api",
endpoint: `/addons/${slug}/options/validate`,
method: "post",
});
}
return (
await hass.callApi<HassioResponse<{ message: string; valid: boolean }>>(
"POST",
`hassio/addons/${slug}/options/validate`
)
).data;
}; };
export const startHassioAddon = async (hass: HomeAssistant, slug: string) => { export const startHassioAddon = async (hass: HomeAssistant, slug: string) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/start`,
method: "post",
timeout: null,
});
}
return hass.callApi<string>("POST", `hassio/addons/${slug}/start`); return hass.callApi<string>("POST", `hassio/addons/${slug}/start`);
}; };
export const stopHassioAddon = async (hass: HomeAssistant, slug: string) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/stop`,
method: "post",
timeout: null,
});
}
return hass.callApi<string>("POST", `hassio/addons/${slug}/stop`);
};
export const setHassioAddonSecurity = async ( export const setHassioAddonSecurity = async (
hass: HomeAssistant, hass: HomeAssistant,
slug: string, slug: string,
data: HassioAddonSetSecurityParams data: HassioAddonSetSecurityParams
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/security`,
method: "post",
data,
});
return;
}
await hass.callApi<HassioResponse<void>>( await hass.callApi<HassioResponse<void>>(
"POST", "POST",
`hassio/addons/${slug}/security`, `hassio/addons/${slug}/security`,
@ -181,15 +261,61 @@ export const setHassioAddonSecurity = async (
); );
}; };
export const installHassioAddon = async (hass: HomeAssistant, slug: string) => { export const installHassioAddon = async (
return hass.callApi<HassioResponse<void>>( hass: HomeAssistant,
slug: string
): Promise<void> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/install`,
method: "post",
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<void>>(
"POST", "POST",
`hassio/addons/${slug}/install` `hassio/addons/${slug}/install`
); );
}; };
export const restartHassioAddon = async (hass: HomeAssistant, slug: string) => { export const updateHassioAddon = async (
return hass.callApi<HassioResponse<void>>( hass: HomeAssistant,
slug: string
): Promise<void> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/update`,
method: "post",
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<void>>(
"POST",
`hassio/addons/${slug}/update`
);
};
export const restartHassioAddon = async (
hass: HomeAssistant,
slug: string
): Promise<void> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/restart`,
method: "post",
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<void>>(
"POST", "POST",
`hassio/addons/${slug}/restart` `hassio/addons/${slug}/restart`
); );
@ -199,6 +325,16 @@ export const uninstallHassioAddon = async (
hass: HomeAssistant, hass: HomeAssistant,
slug: string slug: string
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/addons/${slug}/uninstall`,
method: "post",
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<void>>( await hass.callApi<HassioResponse<void>>(
"POST", "POST",
`hassio/addons/${slug}/uninstall` `hassio/addons/${slug}/uninstall`

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
export interface HassioResponse<T> { export interface HassioResponse<T> {
@ -33,6 +34,14 @@ export const fetchHassioStats = async (
hass: HomeAssistant, hass: HomeAssistant,
container: string container: string
): Promise<HassioStats> => { ): Promise<HassioStats> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/${container}/stats`,
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioStats>>( await hass.callApi<HassioResponse<HassioStats>>(
"GET", "GET",

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { hassioApiResultExtractor, HassioResponse } from "./common"; import { hassioApiResultExtractor, HassioResponse } from "./common";
@ -5,7 +6,17 @@ interface HassioDockerRegistries {
[key: string]: { username: string; password?: string }; [key: string]: { username: string; password?: string };
} }
export const fetchHassioDockerRegistries = async (hass: HomeAssistant) => { export const fetchHassioDockerRegistries = async (
hass: HomeAssistant
): Promise<HassioDockerRegistries> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/docker/registries`,
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioDockerRegistries>>( await hass.callApi<HassioResponse<HassioDockerRegistries>>(
"GET", "GET",
@ -18,6 +29,16 @@ export const addHassioDockerRegistry = async (
hass: HomeAssistant, hass: HomeAssistant,
data: HassioDockerRegistries data: HassioDockerRegistries
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/docker/registries`,
method: "post",
data,
});
return;
}
await hass.callApi<HassioResponse<HassioDockerRegistries>>( await hass.callApi<HassioResponse<HassioDockerRegistries>>(
"POST", "POST",
"hassio/docker/registries", "hassio/docker/registries",
@ -29,6 +50,15 @@ export const removeHassioDockerRegistry = async (
hass: HomeAssistant, hass: HomeAssistant,
registry: string registry: string
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/docker/registries/${registry}`,
method: "delete",
});
return;
}
await hass.callApi<HassioResponse<void>>( await hass.callApi<HassioResponse<void>>(
"DELETE", "DELETE",
`hassio/docker/registries/${registry}` `hassio/docker/registries/${registry}`

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { hassioApiResultExtractor, HassioResponse } from "./common"; import { hassioApiResultExtractor, HassioResponse } from "./common";
@ -21,7 +22,17 @@ export interface HassioHardwareInfo {
audio: Record<string, unknown>; audio: Record<string, unknown>;
} }
export const fetchHassioHardwareAudio = async (hass: HomeAssistant) => { export const fetchHassioHardwareAudio = async (
hass: HomeAssistant
): Promise<HassioHardwareAudioList> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/hardware/audio`,
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioHardwareAudioList>>( await hass.callApi<HassioResponse<HassioHardwareAudioList>>(
"GET", "GET",
@ -30,7 +41,17 @@ export const fetchHassioHardwareAudio = async (hass: HomeAssistant) => {
); );
}; };
export const fetchHassioHardwareInfo = async (hass: HomeAssistant) => { export const fetchHassioHardwareInfo = async (
hass: HomeAssistant
): Promise<HassioHardwareInfo> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/hardware/info`,
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioHardwareInfo>>( await hass.callApi<HassioResponse<HassioHardwareInfo>>(
"GET", "GET",

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { hassioApiResultExtractor, HassioResponse } from "./common"; import { hassioApiResultExtractor, HassioResponse } from "./common";
@ -23,7 +24,17 @@ export interface HassioHassOSInfo {
version: string | null; version: string | null;
} }
export const fetchHassioHostInfo = async (hass: HomeAssistant) => { export const fetchHassioHostInfo = async (
hass: HomeAssistant
): Promise<HassioHostInfo> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/host/info",
method: "get",
});
}
const response = await hass.callApi<HassioResponse<HassioHostInfo>>( const response = await hass.callApi<HassioResponse<HassioHostInfo>>(
"GET", "GET",
"hassio/host/info" "hassio/host/info"
@ -31,7 +42,17 @@ export const fetchHassioHostInfo = async (hass: HomeAssistant) => {
return hassioApiResultExtractor(response); return hassioApiResultExtractor(response);
}; };
export const fetchHassioHassOsInfo = async (hass: HomeAssistant) => { export const fetchHassioHassOsInfo = async (
hass: HomeAssistant
): Promise<HassioHassOSInfo> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/os/info",
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioHassOSInfo>>( await hass.callApi<HassioResponse<HassioHassOSInfo>>(
"GET", "GET",
@ -41,22 +62,67 @@ export const fetchHassioHassOsInfo = async (hass: HomeAssistant) => {
}; };
export const rebootHost = async (hass: HomeAssistant) => { export const rebootHost = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/host/reboot",
method: "post",
timeout: null,
});
}
return hass.callApi<HassioResponse<void>>("POST", "hassio/host/reboot"); return hass.callApi<HassioResponse<void>>("POST", "hassio/host/reboot");
}; };
export const shutdownHost = async (hass: HomeAssistant) => { export const shutdownHost = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/host/shutdown",
method: "post",
timeout: null,
});
}
return hass.callApi<HassioResponse<void>>("POST", "hassio/host/shutdown"); return hass.callApi<HassioResponse<void>>("POST", "hassio/host/shutdown");
}; };
export const updateOS = async (hass: HomeAssistant) => { export const updateOS = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/os/update",
method: "post",
timeout: null,
});
}
return hass.callApi<HassioResponse<void>>("POST", "hassio/os/update"); return hass.callApi<HassioResponse<void>>("POST", "hassio/os/update");
}; };
export const configSyncOS = async (hass: HomeAssistant) => { export const configSyncOS = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "os/config/sync",
method: "post",
timeout: null,
});
}
return hass.callApi<HassioResponse<void>>("POST", "hassio/os/config/sync"); return hass.callApi<HassioResponse<void>>("POST", "hassio/os/config/sync");
}; };
export const changeHostOptions = async (hass: HomeAssistant, options: any) => { export const changeHostOptions = async (hass: HomeAssistant, options: any) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/host/options",
method: "post",
data: options,
});
}
return hass.callApi<HassioResponse<void>>( return hass.callApi<HassioResponse<void>>(
"POST", "POST",
"hassio/host/options", "hassio/host/options",

View File

@ -1,26 +1,49 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { HassioResponse } from "./common"; import { HassioResponse } from "./common";
import { CreateSessionResponse } from "./supervisor"; import { CreateSessionResponse } from "./supervisor";
export const createHassioSession = async (hass: HomeAssistant) => { function setIngressCookie(session: string): string {
const response = await hass.callApi<HassioResponse<CreateSessionResponse>>( document.cookie = `ingress_session=${session};path=/api/hassio_ingress/;SameSite=Strict${
"POST",
"hassio/ingress/session"
);
document.cookie = `ingress_session=${
response.data.session
};path=/api/hassio_ingress/;SameSite=Strict${
location.protocol === "https:" ? ";Secure" : "" location.protocol === "https:" ? ";Secure" : ""
}`; }`;
return response.data.session; return session;
}
export const createHassioSession = async (
hass: HomeAssistant
): Promise<string> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
const wsResponse: { session: string } = await hass.callWS({
type: "supervisor/api",
endpoint: "/ingress/session",
method: "post",
});
return setIngressCookie(wsResponse.session);
}
const restResponse: { data: { session: string } } = await hass.callApi<
HassioResponse<CreateSessionResponse>
>("POST", "hassio/ingress/session");
return setIngressCookie(restResponse.data.session);
}; };
export const validateHassioSession = async ( export const validateHassioSession = async (
hass: HomeAssistant, hass: HomeAssistant,
session: string session: string
) => ): Promise<void> => {
await hass.callApi<HassioResponse<null>>( if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/ingress/validate_session",
method: "post",
data: session,
});
}
await hass.callApi<HassioResponse<void>>(
"POST", "POST",
"hassio/ingress/validate_session", "hassio/ingress/validate_session",
{ session } { session }
); );
};

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { hassioApiResultExtractor, HassioResponse } from "./common"; import { hassioApiResultExtractor, HassioResponse } from "./common";
@ -51,7 +52,17 @@ export interface NetworkInfo {
docker: DockerNetwork; docker: DockerNetwork;
} }
export const fetchNetworkInfo = async (hass: HomeAssistant) => { export const fetchNetworkInfo = async (
hass: HomeAssistant
): Promise<NetworkInfo> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/network/info",
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<NetworkInfo>>( await hass.callApi<HassioResponse<NetworkInfo>>(
"GET", "GET",
@ -65,6 +76,17 @@ export const updateNetworkInterface = async (
network_interface: string, network_interface: string,
options: Partial<NetworkInterface> options: Partial<NetworkInterface>
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: `/network/interface/${network_interface}/update`,
method: "post",
data: options,
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<NetworkInfo>>( await hass.callApi<HassioResponse<NetworkInfo>>(
"POST", "POST",
`hassio/network/interface/${network_interface}/update`, `hassio/network/interface/${network_interface}/update`,
@ -75,7 +97,16 @@ export const updateNetworkInterface = async (
export const accesspointScan = async ( export const accesspointScan = async (
hass: HomeAssistant, hass: HomeAssistant,
network_interface: string network_interface: string
) => { ): Promise<AccessPoints> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/network/interface/${network_interface}/accesspoints`,
method: "get",
timeout: null,
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<AccessPoints>>( await hass.callApi<HassioResponse<AccessPoints>>(
"GET", "GET",

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { hassioApiResultExtractor, HassioResponse } from "./common"; import { hassioApiResultExtractor, HassioResponse } from "./common";
@ -8,7 +9,17 @@ export interface HassioResolution {
suggestions: string[]; suggestions: string[];
} }
export const fetchHassioResolution = async (hass: HomeAssistant) => { export const fetchHassioResolution = async (
hass: HomeAssistant
): Promise<HassioResolution> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/resolution/info",
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioResolution>>( await hass.callApi<HassioResponse<HassioResolution>>(
"GET", "GET",

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { hassioApiResultExtractor, HassioResponse } from "./common"; import { hassioApiResultExtractor, HassioResponse } from "./common";
@ -33,7 +34,18 @@ export interface HassioPartialSnapshotCreateParams {
password?: string; password?: string;
} }
export const fetchHassioSnapshots = async (hass: HomeAssistant) => { export const fetchHassioSnapshots = async (
hass: HomeAssistant
): Promise<HassioSnapshot[]> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
const data: { snapshots: HassioSnapshot[] } = await hass.callWS({
type: "supervisor/api",
endpoint: `/snapshots`,
method: "get",
});
return data.snapshots;
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<{ snapshots: HassioSnapshot[] }>>( await hass.callApi<HassioResponse<{ snapshots: HassioSnapshot[] }>>(
"GET", "GET",
@ -45,8 +57,15 @@ export const fetchHassioSnapshots = async (hass: HomeAssistant) => {
export const fetchHassioSnapshotInfo = async ( export const fetchHassioSnapshotInfo = async (
hass: HomeAssistant, hass: HomeAssistant,
snapshot: string snapshot: string
) => { ): Promise<HassioSnapshotDetail> => {
if (hass) { if (hass) {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: `/snapshots/${snapshot}/info`,
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioSnapshotDetail>>( await hass.callApi<HassioResponse<HassioSnapshotDetail>>(
"GET", "GET",
@ -63,6 +82,15 @@ export const fetchHassioSnapshotInfo = async (
}; };
export const reloadHassioSnapshots = async (hass: HomeAssistant) => { export const reloadHassioSnapshots = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/snapshots/reload",
method: "post",
});
return;
}
await hass.callApi<HassioResponse<void>>("POST", `hassio/snapshots/reload`); await hass.callApi<HassioResponse<void>>("POST", `hassio/snapshots/reload`);
}; };
@ -70,6 +98,15 @@ export const createHassioFullSnapshot = async (
hass: HomeAssistant, hass: HomeAssistant,
data: HassioFullSnapshotCreateParams data: HassioFullSnapshotCreateParams
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/snapshots/new/full",
method: "post",
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<void>>( await hass.callApi<HassioResponse<void>>(
"POST", "POST",
`hassio/snapshots/new/full`, `hassio/snapshots/new/full`,
@ -81,6 +118,17 @@ export const createHassioPartialSnapshot = async (
hass: HomeAssistant, hass: HomeAssistant,
data: HassioFullSnapshotCreateParams data: HassioFullSnapshotCreateParams
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/snapshots/new/partial",
method: "post",
timeout: null,
data,
});
return;
}
await hass.callApi<HassioResponse<void>>( await hass.callApi<HassioResponse<void>>(
"POST", "POST",
`hassio/snapshots/new/partial`, `hassio/snapshots/new/partial`,

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant, PanelInfo } from "../../types"; import { HomeAssistant, PanelInfo } from "../../types";
import { SupervisorArch } from "../supervisor/supervisor"; import { SupervisorArch } from "../supervisor/supervisor";
import { HassioAddonInfo, HassioAddonRepository } from "./addon"; import { HassioAddonInfo, HassioAddonRepository } from "./addon";
@ -83,18 +84,57 @@ export interface SupervisorOptions {
} }
export const reloadSupervisor = async (hass: HomeAssistant) => { export const reloadSupervisor = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/supervisor/reload",
method: "post",
});
return;
}
await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/reload`); await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/reload`);
}; };
export const restartSupervisor = async (hass: HomeAssistant) => { export const restartSupervisor = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/supervisor/restart",
method: "post",
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/restart`); await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/restart`);
}; };
export const updateSupervisor = async (hass: HomeAssistant) => { export const updateSupervisor = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/supervisor/update",
method: "post",
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/update`); await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/update`);
}; };
export const fetchHassioHomeAssistantInfo = async (hass: HomeAssistant) => { export const fetchHassioHomeAssistantInfo = async (
hass: HomeAssistant
): Promise<HassioHomeAssistantInfo> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/core/info",
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioHomeAssistantInfo>>( await hass.callApi<HassioResponse<HassioHomeAssistantInfo>>(
"GET", "GET",
@ -103,7 +143,17 @@ export const fetchHassioHomeAssistantInfo = async (hass: HomeAssistant) => {
); );
}; };
export const fetchHassioSupervisorInfo = async (hass: HomeAssistant) => { export const fetchHassioSupervisorInfo = async (
hass: HomeAssistant
): Promise<HassioSupervisorInfo> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/supervisor/info",
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioSupervisorInfo>>( await hass.callApi<HassioResponse<HassioSupervisorInfo>>(
"GET", "GET",
@ -112,7 +162,17 @@ export const fetchHassioSupervisorInfo = async (hass: HomeAssistant) => {
); );
}; };
export const fetchHassioInfo = async (hass: HomeAssistant) => { export const fetchHassioInfo = async (
hass: HomeAssistant
): Promise<HassioInfo> => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
return await hass.callWS({
type: "supervisor/api",
endpoint: "/info",
method: "get",
});
}
return hassioApiResultExtractor( return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioInfo>>("GET", "hassio/info") await hass.callApi<HassioResponse<HassioInfo>>("GET", "hassio/info")
); );
@ -129,6 +189,16 @@ export const setSupervisorOption = async (
hass: HomeAssistant, hass: HomeAssistant,
data: SupervisorOptions data: SupervisorOptions
) => { ) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/supervisor/options",
method: "post",
data,
});
return;
}
await hass.callApi<HassioResponse<void>>( await hass.callApi<HassioResponse<void>>(
"POST", "POST",
"hassio/supervisor/options", "hassio/supervisor/options",

View File

@ -1,3 +1,4 @@
import { atLeastVersion } from "../../common/config/version";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { HassioResponse } from "../hassio/common"; import { HassioResponse } from "../hassio/common";
@ -6,5 +7,15 @@ export const restartCore = async (hass: HomeAssistant) => {
}; };
export const updateCore = async (hass: HomeAssistant) => { export const updateCore = async (hass: HomeAssistant) => {
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
await hass.callWS({
type: "supervisor/api",
endpoint: "/core/update",
method: "post",
timeout: null,
});
return;
}
await hass.callApi<HassioResponse<void>>("POST", `hassio/core/update`); await hass.callApi<HassioResponse<void>>("POST", `hassio/core/update`);
}; };

View File

@ -1,3 +1,7 @@
import { Connection, getCollection } from "home-assistant-js-websocket";
import { Store } from "home-assistant-js-websocket/dist/store";
import { HomeAssistant } from "../../types";
import { HassioAddonsInfo } from "../hassio/addon";
import { HassioHassOSInfo, HassioHostInfo } from "../hassio/host"; import { HassioHassOSInfo, HassioHostInfo } from "../hassio/host";
import { NetworkInfo } from "../hassio/network"; import { NetworkInfo } from "../hassio/network";
import { HassioResolution } from "../hassio/resolution"; import { HassioResolution } from "../hassio/resolution";
@ -7,7 +11,46 @@ import {
HassioSupervisorInfo, HassioSupervisorInfo,
} from "../hassio/supervisor"; } from "../hassio/supervisor";
export const supervisorWSbaseCommand = {
type: "supervisor/api",
method: "GET",
};
export const supervisorStore = {
host: "/host/info",
supervisor: "/supervisor/info",
info: "/info",
core: "/core/info",
network: "/network/info",
resolution: "/resolution/info",
os: "/os/info",
addon: "/addons",
};
export type SupervisorArch = "armhf" | "armv7" | "aarch64" | "i386" | "amd64"; export type SupervisorArch = "armhf" | "armv7" | "aarch64" | "i386" | "amd64";
export type SupervisorObject =
| "host"
| "supervisor"
| "info"
| "core"
| "network"
| "resolution"
| "os"
| "addon";
interface supervisorApiRequest {
endpoint: string;
method?: "get" | "post" | "delete" | "put";
force_rest?: boolean;
data?: any;
}
export interface SupervisorEvent {
event: string;
update_key?: SupervisorObject;
data?: any;
[key: string]: any;
}
export interface Supervisor { export interface Supervisor {
host: HassioHostInfo; host: HassioHostInfo;
@ -17,4 +60,77 @@ export interface Supervisor {
network: NetworkInfo; network: NetworkInfo;
resolution: HassioResolution; resolution: HassioResolution;
os: HassioHassOSInfo; os: HassioHassOSInfo;
addon: HassioAddonsInfo;
} }
export const supervisorApiWsRequest = <T>(
conn: Connection,
request: supervisorApiRequest
): Promise<T> =>
conn.sendMessagePromise<T>({ ...supervisorWSbaseCommand, ...request });
async function processEvent(
conn: Connection,
store: Store<any>,
event: SupervisorEvent,
key: string
) {
if (
!event.data ||
event.data.event !== "supervisor-update" ||
event.data.update_key !== key
) {
return;
}
if (Object.keys(event.data.data).length === 0) {
const data = await supervisorApiWsRequest<any>(conn, {
endpoint: supervisorStore[key],
});
store.setState(data);
return;
}
const state = store.state;
if (state === undefined) {
return;
}
store.setState({
...state,
...event.data.data,
});
}
const subscribeSupervisorEventUpdates = (
conn: Connection,
store: Store<unknown>,
key: string
) =>
conn.subscribeEvents(
(event) => processEvent(conn, store, event as SupervisorEvent, key),
"supervisor_event"
);
export const getSupervisorEventCollection = (
conn: Connection,
key: string,
endpoint: string
) =>
getCollection(
conn,
`_supervisor${key}Event`,
() => supervisorApiWsRequest(conn, { endpoint }),
(connection, store) =>
subscribeSupervisorEventUpdates(connection, store, key)
);
export const subscribeSupervisorEvents = (
hass: HomeAssistant,
onChange: (event) => void,
key: string,
endpoint: string
) =>
getSupervisorEventCollection(hass.connection, key, endpoint).subscribe(
onChange
);

View File

@ -1,20 +1,21 @@
import * as assert from "assert"; import * as assert from "assert";
import { createHassioSession } from "../../src/data/hassio/ingress"; import { createHassioSession } from "../../src/data/hassio/ingress";
const sessionID = "fhdsu73rh3io4h8f3irhjel8ousafehf8f3yh";
describe("Create hassio session", function () { describe("Create hassio session", function () {
const hass = {
config: { version: "1.0.0" },
callApi: async function () {
return { data: { session: "fhdsu73rh3io4h8f3irhjel8ousafehf8f3yh" } };
},
};
it("Test create session without HTTPS", async function () { it("Test create session without HTTPS", async function () {
// @ts-ignore // @ts-ignore
global.document = {}; global.document = {};
// @ts-ignore // @ts-ignore
global.location = {}; global.location = {};
await createHassioSession({ // @ts-ignore
// @ts-ignore await createHassioSession(hass);
callApi: async function () {
return { data: { session: sessionID } };
},
});
assert.strictEqual( assert.strictEqual(
// @ts-ignore // @ts-ignore
global.document.cookie, global.document.cookie,
@ -26,12 +27,8 @@ describe("Create hassio session", function () {
global.document = {}; global.document = {};
// @ts-ignore // @ts-ignore
global.location = { protocol: "https:" }; global.location = { protocol: "https:" };
await createHassioSession({ // @ts-ignore
// @ts-ignore await createHassioSession(hass);
callApi: async function () {
return { data: { session: sessionID } };
},
});
assert.strictEqual( assert.strictEqual(
// @ts-ignore // @ts-ignore
global.document.cookie, global.document.cookie,