mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-13 20:36:35 +00:00
🌐 Add MVP for translation in the Supervisor panel (#8425)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
5ae10e8516
commit
bea20d0495
@ -85,6 +85,11 @@ gulp.task("copy-translations-app", async () => {
|
|||||||
copyTranslations(staticDir);
|
copyTranslations(staticDir);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("copy-translations-supervisor", async () => {
|
||||||
|
const staticDir = paths.hassio_output_static;
|
||||||
|
copyTranslations(staticDir);
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("copy-static-app", async () => {
|
gulp.task("copy-static-app", async () => {
|
||||||
const staticDir = paths.app_output_static;
|
const staticDir = paths.app_output_static;
|
||||||
// Basic static files
|
// Basic static files
|
||||||
|
@ -10,6 +10,8 @@ require("./gen-icons-json.js");
|
|||||||
require("./webpack.js");
|
require("./webpack.js");
|
||||||
require("./compress.js");
|
require("./compress.js");
|
||||||
require("./rollup.js");
|
require("./rollup.js");
|
||||||
|
require("./gather-static.js");
|
||||||
|
require("./translations.js");
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"develop-hassio",
|
"develop-hassio",
|
||||||
@ -20,6 +22,8 @@ gulp.task(
|
|||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"gen-index-hassio-dev",
|
"gen-index-hassio-dev",
|
||||||
|
"build-supervisor-translations",
|
||||||
|
"copy-translations-supervisor",
|
||||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -32,6 +36,8 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
|
"build-supervisor-translations",
|
||||||
|
"copy-translations-supervisor",
|
||||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||||
"gen-index-hassio-prod",
|
"gen-index-hassio-prod",
|
||||||
...// Don't compress running tests
|
...// Don't compress running tests
|
||||||
|
@ -266,6 +266,7 @@ gulp.task(taskName, function () {
|
|||||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||||
delete data.ui.panel[fragment];
|
delete data.ui.panel[fragment];
|
||||||
});
|
});
|
||||||
|
delete data.supervisor;
|
||||||
return data;
|
return data;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -342,6 +343,62 @@ gulp.task(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gulp.task("build-translation-fragment-supervisor", function () {
|
||||||
|
return gulp
|
||||||
|
.src(fullDir + "/*.json")
|
||||||
|
.pipe(transform((data) => data.supervisor))
|
||||||
|
.pipe(gulp.dest(workDir + "/supervisor"));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("build-translation-flatten-supervisor", function () {
|
||||||
|
return gulp
|
||||||
|
.src(workDir + "/supervisor/*.json")
|
||||||
|
.pipe(
|
||||||
|
transform(function (data) {
|
||||||
|
// Polymer.AppLocalizeBehavior requires flattened json
|
||||||
|
return flatten(data);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(outDir));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("build-translation-write-metadata", function writeMetadata() {
|
||||||
|
return gulp
|
||||||
|
.src(
|
||||||
|
[
|
||||||
|
path.join(paths.translations_src, "translationMetadata.json"),
|
||||||
|
workDir + "/testMetadata.json",
|
||||||
|
workDir + "/translationFingerprints.json",
|
||||||
|
],
|
||||||
|
{ allowEmpty: true }
|
||||||
|
)
|
||||||
|
.pipe(merge({}))
|
||||||
|
.pipe(
|
||||||
|
transform(function (data) {
|
||||||
|
const newData = {};
|
||||||
|
Object.entries(data).forEach(([key, value]) => {
|
||||||
|
// Filter out translations without native name.
|
||||||
|
if (value.nativeName) {
|
||||||
|
newData[key] = value;
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Skipping language ${key}. Native name was not translated.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newData;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
transform((data) => ({
|
||||||
|
fragments: TRANSLATION_FRAGMENTS,
|
||||||
|
translations: data,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.pipe(rename("translationMetadata.json"))
|
||||||
|
.pipe(gulp.dest(workDir));
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"build-translations",
|
"build-translations",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
@ -353,41 +410,20 @@ gulp.task(
|
|||||||
gulp.parallel(...splitTasks),
|
gulp.parallel(...splitTasks),
|
||||||
"build-flattened-translations",
|
"build-flattened-translations",
|
||||||
"build-translation-fingerprints",
|
"build-translation-fingerprints",
|
||||||
function writeMetadata() {
|
"build-translation-write-metadata"
|
||||||
return gulp
|
)
|
||||||
.src(
|
);
|
||||||
[
|
|
||||||
path.join(paths.translations_src, "translationMetadata.json"),
|
gulp.task(
|
||||||
workDir + "/testMetadata.json",
|
"build-supervisor-translations",
|
||||||
workDir + "/translationFingerprints.json",
|
gulp.series(
|
||||||
],
|
"clean-translations",
|
||||||
{ allowEmpty: true }
|
"ensure-translations-build-dir",
|
||||||
)
|
"build-master-translation",
|
||||||
.pipe(merge({}))
|
"build-merged-translations",
|
||||||
.pipe(
|
"build-translation-fragment-supervisor",
|
||||||
transform(function (data) {
|
"build-translation-flatten-supervisor",
|
||||||
const newData = {};
|
"build-translation-fingerprints",
|
||||||
Object.entries(data).forEach(([key, value]) => {
|
"build-translation-write-metadata"
|
||||||
// Filter out translations without native name.
|
|
||||||
if (value.nativeName) {
|
|
||||||
newData[key] = value;
|
|
||||||
} else {
|
|
||||||
console.warn(
|
|
||||||
`Skipping language ${key}. Native name was not translated.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return newData;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(
|
|
||||||
transform((data) => ({
|
|
||||||
fragments: TRANSLATION_FRAGMENTS,
|
|
||||||
translations: data,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
.pipe(rename("translationMetadata.json"))
|
|
||||||
.pipe(gulp.dest(workDir));
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -137,7 +137,12 @@ gulp.task("webpack-watch-hassio", () => {
|
|||||||
isProdBuild: false,
|
isProdBuild: false,
|
||||||
latestBuild: true,
|
latestBuild: true,
|
||||||
})
|
})
|
||||||
).watch({}, doneHandler());
|
).watch({ ignored: /build-translations/ }, doneHandler());
|
||||||
|
|
||||||
|
gulp.watch(
|
||||||
|
path.join(paths.translations_src, "en.json"),
|
||||||
|
gulp.series("build-supervisor-translations", "copy-translations-supervisor")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("webpack-prod-hassio", () =>
|
gulp.task("webpack-prod-hassio", () =>
|
||||||
|
@ -34,6 +34,7 @@ module.exports = {
|
|||||||
|
|
||||||
hassio_dir: path.resolve(__dirname, "../hassio"),
|
hassio_dir: path.resolve(__dirname, "../hassio"),
|
||||||
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
|
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
|
||||||
|
hassio_output_static: path.resolve(__dirname, "../hassio/build/static"),
|
||||||
hassio_output_latest: path.resolve(
|
hassio_output_latest: path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
"../hassio/build/frontend_latest"
|
"../hassio/build/frontend_latest"
|
||||||
|
@ -77,13 +77,16 @@ class HassioAddonStore extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
hassio
|
|
||||||
main-page
|
|
||||||
.tabs=${supervisorTabs}
|
.tabs=${supervisorTabs}
|
||||||
|
main-page
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
<span slot="header">Add-on Store</span>
|
<span slot="header">
|
||||||
|
${this.supervisor.localize("panel.store")}
|
||||||
|
</span>
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
corner="BOTTOM_START"
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
|
@ -62,6 +62,15 @@ class HassioAddonConfig extends LitElement {
|
|||||||
|
|
||||||
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
|
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
|
||||||
|
|
||||||
|
public computeLabel = (entry: HaFormSchema): string => {
|
||||||
|
return (
|
||||||
|
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
||||||
|
?.name ||
|
||||||
|
this.addon.translations.en?.configuration?.[entry.name].name ||
|
||||||
|
entry.name
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
private _filteredShchema = memoizeOne(
|
private _filteredShchema = memoizeOne(
|
||||||
(options: Record<string, unknown>, schema: HaFormSchema[]) => {
|
(options: Record<string, unknown>, schema: HaFormSchema[]) => {
|
||||||
return schema.filter((entry) => entry.name in options || entry.required);
|
return schema.filter((entry) => entry.name in options || entry.required);
|
||||||
@ -102,6 +111,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
? html`<ha-form
|
? html`<ha-form
|
||||||
.data=${this._options!}
|
.data=${this._options!}
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
|
.computeLabel=${this.computeLabel}
|
||||||
.schema=${this._showOptional
|
.schema=${this._showOptional
|
||||||
? this.addon.schema!
|
? this.addon.schema!
|
||||||
: this._filteredShchema(
|
: this._filteredShchema(
|
||||||
|
@ -80,6 +80,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
const addonTabs: PageNavigation[] = [
|
const addonTabs: PageNavigation[] = [
|
||||||
{
|
{
|
||||||
name: "Info",
|
name: "Info",
|
||||||
|
translationKey: "addon.panel.info",
|
||||||
path: `/hassio/addon/${this.addon.slug}/info`,
|
path: `/hassio/addon/${this.addon.slug}/info`,
|
||||||
iconPath: mdiInformationVariant,
|
iconPath: mdiInformationVariant,
|
||||||
},
|
},
|
||||||
@ -88,6 +89,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
if (this.addon.documentation) {
|
if (this.addon.documentation) {
|
||||||
addonTabs.push({
|
addonTabs.push({
|
||||||
name: "Documentation",
|
name: "Documentation",
|
||||||
|
translationKey: "addon.panel.documentation",
|
||||||
path: `/hassio/addon/${this.addon.slug}/documentation`,
|
path: `/hassio/addon/${this.addon.slug}/documentation`,
|
||||||
iconPath: mdiFileDocument,
|
iconPath: mdiFileDocument,
|
||||||
});
|
});
|
||||||
@ -97,11 +99,13 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
addonTabs.push(
|
addonTabs.push(
|
||||||
{
|
{
|
||||||
name: "Configuration",
|
name: "Configuration",
|
||||||
|
translationKey: "addon.panel.configuration",
|
||||||
path: `/hassio/addon/${this.addon.slug}/config`,
|
path: `/hassio/addon/${this.addon.slug}/config`,
|
||||||
iconPath: mdiCogs,
|
iconPath: mdiCogs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Log",
|
name: "Log",
|
||||||
|
translationKey: "addon.panel.log",
|
||||||
path: `/hassio/addon/${this.addon.slug}/logs`,
|
path: `/hassio/addon/${this.addon.slug}/logs`,
|
||||||
iconPath: mdiMathLog,
|
iconPath: mdiMathLog,
|
||||||
}
|
}
|
||||||
@ -113,11 +117,12 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"}
|
.backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"}
|
||||||
.route=${route}
|
.route=${route}
|
||||||
hassio
|
|
||||||
.tabs=${addonTabs}
|
.tabs=${addonTabs}
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
<span slot="header">${this.addon.name}</span>
|
<span slot="header">${this.addon.name}</span>
|
||||||
<hassio-addon-router
|
<hassio-addon-router
|
||||||
|
@ -27,17 +27,15 @@ class HassioAddons extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>Add-ons</h1>
|
<h1>${this.supervisor.localize("dashboard.addons")}</h1>
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
${!this.supervisor.supervisor.addons?.length
|
${!this.supervisor.supervisor.addons?.length
|
||||||
? html`
|
? html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
You don't have any add-ons installed yet. Head over to
|
|
||||||
<button class="link" @click=${this._openStore}>
|
<button class="link" @click=${this._openStore}>
|
||||||
the add-on store
|
${this.supervisor.localize("dashboard.no_addons")}
|
||||||
</button>
|
</button>
|
||||||
to get started!
|
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
@ -58,10 +56,16 @@ class HassioAddons extends LitElement {
|
|||||||
? mdiArrowUpBoldCircle
|
? mdiArrowUpBoldCircle
|
||||||
: mdiPuzzle}
|
: mdiPuzzle}
|
||||||
.iconTitle=${addon.state !== "started"
|
.iconTitle=${addon.state !== "started"
|
||||||
? "Add-on is stopped"
|
? this.supervisor.localize(
|
||||||
|
"dashboard.addon_stopped"
|
||||||
|
)
|
||||||
: addon.update_available!
|
: addon.update_available!
|
||||||
? "New version available"
|
? this.supervisor.localize(
|
||||||
: "Add-on is running"}
|
"dashboard.addon_new_version"
|
||||||
|
)
|
||||||
|
: this.supervisor.localize(
|
||||||
|
"dashboard.addon_running"
|
||||||
|
)}
|
||||||
.iconClass=${addon.update_available
|
.iconClass=${addon.update_available
|
||||||
? addon.state === "started"
|
? addon.state === "started"
|
||||||
? "update"
|
? "update"
|
||||||
|
@ -29,13 +29,16 @@ class HassioDashboard extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
hassio
|
|
||||||
main-page
|
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${supervisorTabs}
|
.tabs=${supervisorTabs}
|
||||||
|
main-page
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
<span slot="header">Dashboard</span>
|
<span slot="header">
|
||||||
|
${this.supervisor.localize("panel.dashboard")}
|
||||||
|
</span>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<hassio-update
|
<hassio-update
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -10,9 +10,11 @@ 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-card";
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/components/ha-settings-row";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
@ -24,7 +26,10 @@ import {
|
|||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
} from "../../../src/data/hassio/supervisor";
|
} from "../../../src/data/hassio/supervisor";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import {
|
||||||
|
Supervisor,
|
||||||
|
supervisorApiWsRequest,
|
||||||
|
} from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
@ -34,6 +39,10 @@ import { HomeAssistant } from "../../../src/types";
|
|||||||
import { showDialogSupervisorCoreUpdate } from "../dialogs/core/show-dialog-core-update";
|
import { showDialogSupervisorCoreUpdate } from "../dialogs/core/show-dialog-core-update";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
|
const computeVersion = (key: string, version: string): string => {
|
||||||
|
return key === "os" ? version : `${key}-${version}`;
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("hassio-update")
|
@customElement("hassio-update")
|
||||||
export class HassioUpdate extends LitElement {
|
export class HassioUpdate extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -59,9 +68,12 @@ export class HassioUpdate extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>
|
<h1>
|
||||||
${updatesAvailable > 1
|
${this.supervisor.localize(
|
||||||
? "Updates Available 🎉"
|
"dashboard.update_available",
|
||||||
: "Update Available 🎉"}
|
"count",
|
||||||
|
updatesAvailable
|
||||||
|
)}
|
||||||
|
🎉
|
||||||
</h1>
|
</h1>
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
${this._renderUpdateCard(
|
${this._renderUpdateCard(
|
||||||
@ -110,14 +122,30 @@ export class HassioUpdate extends LitElement {
|
|||||||
<div class="icon">
|
<div class="icon">
|
||||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="update-heading">${name} ${object.version_latest}</div>
|
<div class="update-heading">${name}</div>
|
||||||
<div class="warning">
|
<ha-settings-row two-line>
|
||||||
You are currently running version ${object.version}
|
<span slot="heading">
|
||||||
</div>
|
${this.supervisor.localize("common.version")}
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${computeVersion(key, object.version!)}
|
||||||
|
</span>
|
||||||
|
</ha-settings-row>
|
||||||
|
|
||||||
|
<ha-settings-row two-line>
|
||||||
|
<span slot="heading">
|
||||||
|
${this.supervisor.localize("common.newest_version")}
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${computeVersion(key, object.version_latest!)}
|
||||||
|
</span>
|
||||||
|
</ha-settings-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
|
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
|
||||||
<mwc-button>Release notes</mwc-button>
|
<mwc-button>
|
||||||
|
${this.supervisor.localize("common.release_notes")}
|
||||||
|
</mwc-button>
|
||||||
</a>
|
</a>
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
.apiPath=${apiPath}
|
.apiPath=${apiPath}
|
||||||
@ -126,7 +154,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
.version=${object.version_latest}
|
.version=${object.version_latest}
|
||||||
@click=${this._confirmUpdate}
|
@click=${this._confirmUpdate}
|
||||||
>
|
>
|
||||||
Update
|
${this.supervisor.localize("common.update")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
@ -141,10 +169,20 @@ export class HassioUpdate extends LitElement {
|
|||||||
}
|
}
|
||||||
item.progress = true;
|
item.progress = true;
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
title: `Update ${item.name}`,
|
title: this.supervisor.localize(
|
||||||
text: `Are you sure you want to update ${item.name} to version ${item.version}?`,
|
"confirm.update.title",
|
||||||
confirmText: "update",
|
"name",
|
||||||
dismissText: "cancel",
|
item.name
|
||||||
|
),
|
||||||
|
text: this.supervisor.localize(
|
||||||
|
"confirm.update.text",
|
||||||
|
"name",
|
||||||
|
item.name,
|
||||||
|
"version",
|
||||||
|
computeVersion(item.key, item.version)
|
||||||
|
),
|
||||||
|
confirmText: this.supervisor.localize("common.update"),
|
||||||
|
dismissText: this.supervisor.localize("common.cancel"),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
@ -152,7 +190,15 @@ export class HassioUpdate extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
|
if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
|
||||||
|
await supervisorApiWsRequest(this.hass.connection, {
|
||||||
|
method: "post",
|
||||||
|
endpoint: item.apiPath.replace("hassio", ""),
|
||||||
|
timeout: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
|
||||||
|
}
|
||||||
fireEvent(this, "supervisor-colllection-refresh", {
|
fireEvent(this, "supervisor-colllection-refresh", {
|
||||||
colllection: item.key,
|
colllection: item.key,
|
||||||
});
|
});
|
||||||
@ -165,7 +211,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
!ignoredStatusCodes.has(err.status_code)
|
!ignoredStatusCodes.has(err.status_code)
|
||||||
) {
|
) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Update failed",
|
title: this.supervisor.localize("error.update_failed"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -190,9 +236,6 @@ export class HassioUpdate extends LitElement {
|
|||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
.warning {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
.card-content {
|
.card-content {
|
||||||
height: calc(100% - 47px);
|
height: calc(100% - 47px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -200,13 +243,13 @@ export class HassioUpdate extends LitElement {
|
|||||||
.card-actions {
|
.card-actions {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
.errors {
|
|
||||||
color: var(--error-color);
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
ha-settings-row {
|
||||||
|
padding: 0;
|
||||||
|
--paper-item-body-two-line-min-height: 32px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ 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 { supervisorCollection } from "../../src/data/supervisor/supervisor";
|
import { Supervisor } 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 "../../src/layouts/hass-loading-screen";
|
||||||
import { HomeAssistant, Route } from "../../src/types";
|
import { HomeAssistant, Route } from "../../src/types";
|
||||||
@ -14,6 +14,8 @@ import { SupervisorBaseElement } from "./supervisor-base-element";
|
|||||||
export class HassioMain extends SupervisorBaseElement {
|
export class HassioMain extends SupervisorBaseElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public panel!: HassioPanelInfo;
|
@property({ attribute: false }) public panel!: HassioPanelInfo;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
@ -72,18 +74,6 @@ export class HassioMain extends SupervisorBaseElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.supervisor || !this.hass) {
|
|
||||||
return html`<hass-loading-screen></hass-loading-screen>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Object.keys(supervisorCollection).some(
|
|
||||||
(colllection) => !this.supervisor![colllection]
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return html`<hass-loading-screen></hass-loading-screen>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hassio-router
|
<hassio-router
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -7,7 +7,10 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
|
import {
|
||||||
|
Supervisor,
|
||||||
|
supervisorCollection,
|
||||||
|
} from "../../src/data/supervisor/supervisor";
|
||||||
import { HomeAssistant, Route } from "../../src/types";
|
import { HomeAssistant, Route } from "../../src/types";
|
||||||
import "./hassio-panel-router";
|
import "./hassio-panel-router";
|
||||||
|
|
||||||
@ -22,6 +25,17 @@ class HassioPanel extends LitElement {
|
|||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html`<hass-loading-screen></hass-loading-screen>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Object.keys(supervisorCollection).some(
|
||||||
|
(colllection) => !this.supervisor[colllection]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return html`<hass-loading-screen></hass-loading-screen>`;
|
||||||
|
}
|
||||||
return html`
|
return html`
|
||||||
<hassio-panel-router
|
<hassio-panel-router
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -3,22 +3,22 @@ import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
|||||||
|
|
||||||
export const supervisorTabs: PageNavigation[] = [
|
export const supervisorTabs: PageNavigation[] = [
|
||||||
{
|
{
|
||||||
name: "Dashboard",
|
translationKey: "panel.dashboard",
|
||||||
path: `/hassio/dashboard`,
|
path: `/hassio/dashboard`,
|
||||||
iconPath: mdiViewDashboard,
|
iconPath: mdiViewDashboard,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Add-on Store",
|
translationKey: "panel.store",
|
||||||
path: `/hassio/store`,
|
path: `/hassio/store`,
|
||||||
iconPath: mdiStore,
|
iconPath: mdiStore,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Snapshots",
|
translationKey: "panel.snapshots",
|
||||||
path: `/hassio/snapshots`,
|
path: `/hassio/snapshots`,
|
||||||
iconPath: mdiBackupRestore,
|
iconPath: mdiBackupRestore,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "System",
|
translationKey: "panel.system",
|
||||||
path: `/hassio/system`,
|
path: `/hassio/system`,
|
||||||
iconPath: mdiCogs,
|
iconPath: mdiCogs,
|
||||||
},
|
},
|
||||||
|
@ -104,13 +104,16 @@ class HassioSnapshots extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
hassio
|
|
||||||
main-page
|
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${supervisorTabs}
|
.tabs=${supervisorTabs}
|
||||||
|
main-page
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
<span slot="header">Snapshots</span>
|
<span slot="header">
|
||||||
|
${this.supervisor.localize("panel.snapshots")}
|
||||||
|
</span>
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
corner="BOTTOM_START"
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { atLeastVersion } from "../../src/common/config/version";
|
import { atLeastVersion } from "../../src/common/config/version";
|
||||||
|
import { computeLocalize } from "../../src/common/translations/localize";
|
||||||
import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon";
|
import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon";
|
||||||
import { HassioResponse } from "../../src/data/hassio/common";
|
import { HassioResponse } from "../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
@ -29,6 +30,7 @@ import {
|
|||||||
} from "../../src/data/supervisor/supervisor";
|
} 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";
|
||||||
|
import { getTranslation } from "../../src/util/common-translation";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@ -40,7 +42,9 @@ declare global {
|
|||||||
export class SupervisorBaseElement extends urlSyncMixin(
|
export class SupervisorBaseElement extends urlSyncMixin(
|
||||||
ProvideHassLitMixin(LitElement)
|
ProvideHassLitMixin(LitElement)
|
||||||
) {
|
) {
|
||||||
@property({ attribute: false }) public supervisor?: Supervisor;
|
@property({ attribute: false }) public supervisor: Partial<Supervisor> = {
|
||||||
|
localize: () => "",
|
||||||
|
};
|
||||||
|
|
||||||
@internalProperty() private _unsubs: Record<string, UnsubscribeFunc> = {};
|
@internalProperty() private _unsubs: Record<string, UnsubscribeFunc> = {};
|
||||||
|
|
||||||
@ -49,6 +53,15 @@ export class SupervisorBaseElement extends urlSyncMixin(
|
|||||||
Collection<unknown>
|
Collection<unknown>
|
||||||
> = {};
|
> = {};
|
||||||
|
|
||||||
|
@internalProperty() private _resources?: Record<string, any>;
|
||||||
|
|
||||||
|
@internalProperty() private _language = "en";
|
||||||
|
|
||||||
|
public connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._initializeLocalize();
|
||||||
|
}
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
Object.keys(this._unsubs).forEach((unsub) => {
|
Object.keys(this._unsubs).forEach((unsub) => {
|
||||||
@ -56,15 +69,50 @@ export class SupervisorBaseElement extends urlSyncMixin(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updated(changedProperties: PropertyValues) {
|
||||||
|
super.updated(changedProperties);
|
||||||
|
|
||||||
|
if (changedProperties.has("_language")) {
|
||||||
|
if (changedProperties.get("_language") !== this._language) {
|
||||||
|
this._initializeLocalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected _updateSupervisor(obj: Partial<Supervisor>): void {
|
protected _updateSupervisor(obj: Partial<Supervisor>): void {
|
||||||
this.supervisor = { ...this.supervisor!, ...obj };
|
this.supervisor = { ...this.supervisor, ...obj };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues): void {
|
protected firstUpdated(changedProps: PropertyValues): void {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
if (this._language !== this.hass.language) {
|
||||||
|
this._language = this.hass.language;
|
||||||
|
}
|
||||||
|
this._initializeLocalize();
|
||||||
this._initSupervisor();
|
this._initSupervisor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _initializeLocalize() {
|
||||||
|
const { language, data } = await getTranslation(
|
||||||
|
null,
|
||||||
|
this._language,
|
||||||
|
"/api/hassio/app/static/translations"
|
||||||
|
);
|
||||||
|
|
||||||
|
this._resources = {
|
||||||
|
[language]: data,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.supervisor = {
|
||||||
|
...this.supervisor,
|
||||||
|
localize: await computeLocalize(
|
||||||
|
this.constructor.prototype,
|
||||||
|
this._language,
|
||||||
|
this._resources
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async _handleSupervisorStoreRefreshEvent(ev) {
|
private async _handleSupervisorStoreRefreshEvent(ev) {
|
||||||
const colllection = ev.detail.colllection;
|
const colllection = ev.detail.colllection;
|
||||||
if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
|
if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
|
||||||
@ -104,52 +152,54 @@ export class SupervisorBaseElement extends urlSyncMixin(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.supervisor === undefined) {
|
Object.keys(this._collections).forEach((collection) => {
|
||||||
Object.keys(this._collections).forEach((collection) =>
|
if (
|
||||||
|
this.supervisor === undefined ||
|
||||||
|
this.supervisor[collection] === undefined
|
||||||
|
) {
|
||||||
this._updateSupervisor({
|
this._updateSupervisor({
|
||||||
[collection]: this._collections[collection].state,
|
[collection]: this._collections[collection].state,
|
||||||
})
|
});
|
||||||
);
|
}
|
||||||
}
|
});
|
||||||
return;
|
} else {
|
||||||
|
const [
|
||||||
|
addon,
|
||||||
|
supervisor,
|
||||||
|
host,
|
||||||
|
core,
|
||||||
|
info,
|
||||||
|
os,
|
||||||
|
network,
|
||||||
|
resolution,
|
||||||
|
store,
|
||||||
|
] = await Promise.all([
|
||||||
|
fetchHassioAddonsInfo(this.hass),
|
||||||
|
fetchHassioSupervisorInfo(this.hass),
|
||||||
|
fetchHassioHostInfo(this.hass),
|
||||||
|
fetchHassioHomeAssistantInfo(this.hass),
|
||||||
|
fetchHassioInfo(this.hass),
|
||||||
|
fetchHassioHassOsInfo(this.hass),
|
||||||
|
fetchNetworkInfo(this.hass),
|
||||||
|
fetchHassioResolution(this.hass),
|
||||||
|
fetchSupervisorStore(this.hass),
|
||||||
|
]);
|
||||||
|
|
||||||
|
this.supervisor = {
|
||||||
|
addon,
|
||||||
|
supervisor,
|
||||||
|
host,
|
||||||
|
core,
|
||||||
|
info,
|
||||||
|
os,
|
||||||
|
network,
|
||||||
|
resolution,
|
||||||
|
store,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addEventListener("supervisor-update", (ev) =>
|
||||||
|
this._updateSupervisor(ev.detail)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [
|
|
||||||
addon,
|
|
||||||
supervisor,
|
|
||||||
host,
|
|
||||||
core,
|
|
||||||
info,
|
|
||||||
os,
|
|
||||||
network,
|
|
||||||
resolution,
|
|
||||||
store,
|
|
||||||
] = await Promise.all([
|
|
||||||
fetchHassioAddonsInfo(this.hass),
|
|
||||||
fetchHassioSupervisorInfo(this.hass),
|
|
||||||
fetchHassioHostInfo(this.hass),
|
|
||||||
fetchHassioHomeAssistantInfo(this.hass),
|
|
||||||
fetchHassioInfo(this.hass),
|
|
||||||
fetchHassioHassOsInfo(this.hass),
|
|
||||||
fetchNetworkInfo(this.hass),
|
|
||||||
fetchHassioResolution(this.hass),
|
|
||||||
fetchSupervisorStore(this.hass),
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.supervisor = {
|
|
||||||
addon,
|
|
||||||
supervisor,
|
|
||||||
host,
|
|
||||||
core,
|
|
||||||
info,
|
|
||||||
os,
|
|
||||||
network,
|
|
||||||
resolution,
|
|
||||||
store,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addEventListener("supervisor-update", (ev) =>
|
|
||||||
this._updateSupervisor(ev.detail)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,16 @@ class HassioSystem extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
hassio
|
|
||||||
main-page
|
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${supervisorTabs}
|
.tabs=${supervisorTabs}
|
||||||
|
main-page
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
<span slot="header">System</span>
|
<span slot="header">
|
||||||
|
${this.supervisor.localize("panel.system")}
|
||||||
|
</span>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
<hassio-core-info
|
<hassio-core-info
|
||||||
|
@ -8,9 +8,10 @@ export const atLeastVersion = (
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
Number(haMajor) > major ||
|
Number(haMajor) > major ||
|
||||||
(Number(haMajor) === major && (patch === undefined
|
(Number(haMajor) === major &&
|
||||||
? Number(haMinor) >= minor
|
(patch === undefined
|
||||||
: Number(haMinor) > minor)) ||
|
? Number(haMinor) >= minor
|
||||||
|
: Number(haMinor) > minor)) ||
|
||||||
(patch !== undefined &&
|
(patch !== undefined &&
|
||||||
Number(haMajor) === major &&
|
Number(haMajor) === major &&
|
||||||
Number(haMinor) === minor &&
|
Number(haMinor) === minor &&
|
||||||
|
@ -16,6 +16,10 @@ export type AddonStartup =
|
|||||||
export type AddonState = "started" | "stopped" | null;
|
export type AddonState = "started" | "stopped" | null;
|
||||||
export type AddonRepository = "core" | "local" | string;
|
export type AddonRepository = "core" | "local" | string;
|
||||||
|
|
||||||
|
interface AddonTranslations {
|
||||||
|
[key: string]: Record<string, Record<string, Record<string, string>>>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HassioAddonInfo {
|
export interface HassioAddonInfo {
|
||||||
advanced: boolean;
|
advanced: boolean;
|
||||||
available: boolean;
|
available: boolean;
|
||||||
@ -82,6 +86,7 @@ export interface HassioAddonDetails extends HassioAddonInfo {
|
|||||||
slug: string;
|
slug: string;
|
||||||
startup: AddonStartup;
|
startup: AddonStartup;
|
||||||
stdin: boolean;
|
stdin: boolean;
|
||||||
|
translations: AddonTranslations;
|
||||||
watchdog: null | boolean;
|
watchdog: null | boolean;
|
||||||
webui: null | string;
|
webui: null | string;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Connection, getCollection } from "home-assistant-js-websocket";
|
import { Connection, getCollection } from "home-assistant-js-websocket";
|
||||||
import { Store } from "home-assistant-js-websocket/dist/store";
|
import { Store } from "home-assistant-js-websocket/dist/store";
|
||||||
|
import { LocalizeFunc } from "../../common/translations/localize";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { HassioAddonsInfo } from "../hassio/addon";
|
import { HassioAddonsInfo } from "../hassio/addon";
|
||||||
import { HassioHassOSInfo, HassioHostInfo } from "../hassio/host";
|
import { HassioHassOSInfo, HassioHostInfo } from "../hassio/host";
|
||||||
@ -46,6 +47,7 @@ interface supervisorApiRequest {
|
|||||||
method?: "get" | "post" | "delete" | "put";
|
method?: "get" | "post" | "delete" | "put";
|
||||||
force_rest?: boolean;
|
force_rest?: boolean;
|
||||||
data?: any;
|
data?: any;
|
||||||
|
timeout?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SupervisorEvent {
|
export interface SupervisorEvent {
|
||||||
@ -65,6 +67,7 @@ export interface Supervisor {
|
|||||||
os: HassioHassOSInfo;
|
os: HassioHassOSInfo;
|
||||||
addon: HassioAddonsInfo;
|
addon: HassioAddonsInfo;
|
||||||
store: SupervisorStore;
|
store: SupervisorStore;
|
||||||
|
localize: LocalizeFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const supervisorApiWsRequest = <T>(
|
export const supervisorApiWsRequest = <T>(
|
||||||
|
@ -7,7 +7,7 @@ import { computeLocalize } from "../common/translations/localize";
|
|||||||
import { DEFAULT_PANEL } from "../data/panel";
|
import { DEFAULT_PANEL } from "../data/panel";
|
||||||
import { translationMetadata } from "../resources/translations-metadata";
|
import { translationMetadata } from "../resources/translations-metadata";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { getLocalLanguage, getTranslation } from "../util/hass-translation";
|
import { getTranslation, getLocalLanguage } from "../util/hass-translation";
|
||||||
import { demoConfig } from "./demo_config";
|
import { demoConfig } from "./demo_config";
|
||||||
import { demoPanels } from "./demo_panels";
|
import { demoPanels } from "./demo_panels";
|
||||||
import { demoServices } from "./demo_services";
|
import { demoServices } from "./demo_services";
|
||||||
|
@ -16,6 +16,7 @@ import memoizeOne from "memoize-one";
|
|||||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||||
import { restoreScroll } from "../common/decorators/restore-scroll";
|
import { restoreScroll } from "../common/decorators/restore-scroll";
|
||||||
import { navigate } from "../common/navigate";
|
import { navigate } from "../common/navigate";
|
||||||
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
import { computeRTL } from "../common/util/compute_rtl";
|
import { computeRTL } from "../common/util/compute_rtl";
|
||||||
import "../components/ha-icon";
|
import "../components/ha-icon";
|
||||||
import "../components/ha-icon-button-arrow-prev";
|
import "../components/ha-icon-button-arrow-prev";
|
||||||
@ -40,7 +41,9 @@ export interface PageNavigation {
|
|||||||
class HassTabsSubpage extends LitElement {
|
class HassTabsSubpage extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public hassio = false;
|
@property({ type: Boolean }) public supervisor = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public localizeFunc?: LocalizeFunc;
|
||||||
|
|
||||||
@property({ type: String, attribute: "back-path" }) public backPath?: string;
|
@property({ type: String, attribute: "back-path" }) public backPath?: string;
|
||||||
|
|
||||||
@ -48,9 +51,9 @@ class HassTabsSubpage extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean, attribute: "main-page" }) public mainPage = false;
|
@property({ type: Boolean, attribute: "main-page" }) public mainPage = false;
|
||||||
|
|
||||||
@property() public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
@property() public tabs!: PageNavigation[];
|
@property({ attribute: false }) public tabs!: PageNavigation[];
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||||
|
|
||||||
@ -71,7 +74,8 @@ class HassTabsSubpage extends LitElement {
|
|||||||
showAdvanced: boolean | undefined,
|
showAdvanced: boolean | undefined,
|
||||||
_components,
|
_components,
|
||||||
_language,
|
_language,
|
||||||
_narrow
|
_narrow,
|
||||||
|
localizeFunc
|
||||||
) => {
|
) => {
|
||||||
const shownTabs = tabs.filter(
|
const shownTabs = tabs.filter(
|
||||||
(page) =>
|
(page) =>
|
||||||
@ -91,7 +95,7 @@ class HassTabsSubpage extends LitElement {
|
|||||||
.active=${page === activeTab}
|
.active=${page === activeTab}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.name=${page.translationKey
|
.name=${page.translationKey
|
||||||
? this.hass.localize(page.translationKey)
|
? localizeFunc(page.translationKey)
|
||||||
: page.name}
|
: page.name}
|
||||||
>
|
>
|
||||||
${page.iconPath
|
${page.iconPath
|
||||||
@ -130,7 +134,8 @@ class HassTabsSubpage extends LitElement {
|
|||||||
this.hass.userData?.showAdvanced,
|
this.hass.userData?.showAdvanced,
|
||||||
this.hass.config.components,
|
this.hass.config.components,
|
||||||
this.hass.language,
|
this.hass.language,
|
||||||
this.narrow
|
this.narrow,
|
||||||
|
this.localizeFunc || this.hass.localize
|
||||||
);
|
);
|
||||||
const showTabs = tabs.length > 1 || !this.narrow;
|
const showTabs = tabs.length > 1 || !this.narrow;
|
||||||
return html`
|
return html`
|
||||||
@ -138,7 +143,7 @@ class HassTabsSubpage extends LitElement {
|
|||||||
${this.mainPage
|
${this.mainPage
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-button
|
<ha-menu-button
|
||||||
.hassio=${this.hassio}
|
.hassio=${this.supervisor}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
></ha-menu-button>
|
></ha-menu-button>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LitElement, property, PropertyValues } from "lit-element";
|
import { LitElement, property, PropertyValues } from "lit-element";
|
||||||
import { computeLocalize, LocalizeFunc } from "../common/translations/localize";
|
import { computeLocalize, LocalizeFunc } from "../common/translations/localize";
|
||||||
import { Constructor, Resources } from "../types";
|
import { Constructor, Resources } from "../types";
|
||||||
import { getLocalLanguage, getTranslation } from "../util/hass-translation";
|
import { getTranslation, getLocalLanguage } from "../util/hass-translation";
|
||||||
|
|
||||||
const empty = () => "";
|
const empty = () => "";
|
||||||
|
|
||||||
|
@ -161,8 +161,8 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
const batteryState = batteryEntity
|
const batteryState = batteryEntity
|
||||||
? this.hass.states[batteryEntity.entity_id]
|
? this.hass.states[batteryEntity.entity_id]
|
||||||
: undefined;
|
: undefined;
|
||||||
const batteryIsBinary = batteryState
|
const batteryIsBinary =
|
||||||
&& computeStateDomain(batteryState) === "binary_sensor";
|
batteryState && computeStateDomain(batteryState) === "binary_sensor";
|
||||||
const batteryChargingState = batteryChargingEntity
|
const batteryChargingState = batteryChargingEntity
|
||||||
? this.hass.states[batteryChargingEntity.entity_id]
|
? this.hass.states[batteryChargingEntity.entity_id]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import {
|
import {
|
||||||
mdiInformationOutline,
|
mdiInformationOutline,
|
||||||
mdiClipboardTextMultipleOutline
|
mdiClipboardTextMultipleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import "@polymer/paper-checkbox/paper-checkbox";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
@ -169,7 +169,10 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
<th>[[localize('ui.panel.developer-tools.tabs.states.state')]]</th>
|
<th>[[localize('ui.panel.developer-tools.tabs.states.state')]]</th>
|
||||||
<th hidden$="[[narrow]]">
|
<th hidden$="[[narrow]]">
|
||||||
[[localize('ui.panel.developer-tools.tabs.states.attributes')]]
|
[[localize('ui.panel.developer-tools.tabs.states.attributes')]]
|
||||||
<paper-checkbox checked="{{_showAttributes}}" on-change="{{saveAttributeCheckboxState}}"></paper-checkbox>
|
<paper-checkbox
|
||||||
|
checked="{{_showAttributes}}"
|
||||||
|
on-change="{{saveAttributeCheckboxState}}"
|
||||||
|
></paper-checkbox>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -285,7 +288,9 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
|
|
||||||
_showAttributes: {
|
_showAttributes: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: JSON.parse(localStorage.getItem("devToolsShowAttributes") || true),
|
value: JSON.parse(
|
||||||
|
localStorage.getItem("devToolsShowAttributes") || true
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
_entities: {
|
_entities: {
|
||||||
|
@ -199,7 +199,10 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: var(--ha-picture-card-background-color, rgba(0, 0, 0, 0.3));
|
background-color: var(
|
||||||
|
--ha-picture-card-background-color,
|
||||||
|
rgba(0, 0, 0, 0.3)
|
||||||
|
);
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
|
@ -314,7 +314,10 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: var(--ha-picture-card-background-color, rgba(0, 0, 0, 0.3));
|
background-color: var(
|
||||||
|
--ha-picture-card-background-color,
|
||||||
|
rgba(0, 0, 0, 0.3)
|
||||||
|
);
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
|
@ -12,8 +12,8 @@ import { translationMetadata } from "../resources/translations-metadata";
|
|||||||
import { Constructor, HomeAssistant } from "../types";
|
import { Constructor, HomeAssistant } from "../types";
|
||||||
import { storeState } from "../util/ha-pref-storage";
|
import { storeState } from "../util/ha-pref-storage";
|
||||||
import {
|
import {
|
||||||
getLocalLanguage,
|
|
||||||
getTranslation,
|
getTranslation,
|
||||||
|
getLocalLanguage,
|
||||||
getUserLanguage,
|
getUserLanguage,
|
||||||
} from "../util/hass-translation";
|
} from "../util/hass-translation";
|
||||||
import { HassBaseEl } from "./hass-base-mixin";
|
import { HassBaseEl } from "./hass-base-mixin";
|
||||||
|
@ -3417,5 +3417,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"supervisor": {
|
||||||
|
"addon": {
|
||||||
|
"panel": {
|
||||||
|
"configuration": "Configuration",
|
||||||
|
"documentation": "Documentation",
|
||||||
|
"info": "Info",
|
||||||
|
"log": "Log"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"newest_version": "Newest Version",
|
||||||
|
"release_notes": "Release notes",
|
||||||
|
"update": "Update",
|
||||||
|
"version": "Version"
|
||||||
|
},
|
||||||
|
"confirm": {
|
||||||
|
"update": {
|
||||||
|
"title": "Update ${name}",
|
||||||
|
"text": "Are you sure you want to update {name} to version {version}?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"addon_new_version": "New version available",
|
||||||
|
"addon_running": "Add-on is running",
|
||||||
|
"addon_stopped": "Add-on is stopped",
|
||||||
|
"addons": "Add-ons",
|
||||||
|
"no_addons": "You don't have any add-ons installed yet. Head over to the add-on store to get started!",
|
||||||
|
"update_available": "{count, plural,\n one {Update}\n other {{count} Updates}\n} pending"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"unknown": "Unknown error",
|
||||||
|
"update_failed": "Update failed"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"snapshots": "Snapshots",
|
||||||
|
"store": "Add-on Store",
|
||||||
|
"system": "System"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
src/util/common-translation.ts
Normal file
56
src/util/common-translation.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { translationMetadata } from "../resources/translations-metadata";
|
||||||
|
|
||||||
|
const DEFAULT_BASE_URL = "/static/translations";
|
||||||
|
|
||||||
|
// Store loaded translations in memory so translations are available immediately
|
||||||
|
// when DOM is created in Polymer. Even a cache lookup creates noticeable latency.
|
||||||
|
const translations = {};
|
||||||
|
|
||||||
|
async function fetchTranslation(fingerprint: string, base_url: string) {
|
||||||
|
const response = await fetch(`${base_url}/${fingerprint}`, {
|
||||||
|
credentials: "same-origin",
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Fail to fetch translation ${fingerprint}: HTTP response status is ${response.status}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTranslation(
|
||||||
|
fragment: string | null,
|
||||||
|
language: string,
|
||||||
|
base_url?: string
|
||||||
|
) {
|
||||||
|
const metadata = translationMetadata.translations[language];
|
||||||
|
if (!metadata) {
|
||||||
|
if (language !== "en") {
|
||||||
|
return getTranslation(fragment, "en", base_url);
|
||||||
|
}
|
||||||
|
throw new Error("Language en is not found in metadata");
|
||||||
|
}
|
||||||
|
|
||||||
|
// nl-abcd.jon or logbook/nl-abcd.json
|
||||||
|
const fingerprint = `${fragment ? fragment + "/" : ""}${language}-${
|
||||||
|
metadata.hash
|
||||||
|
}.json`;
|
||||||
|
|
||||||
|
// Fetch translation from the server
|
||||||
|
if (!translations[fingerprint]) {
|
||||||
|
translations[fingerprint] = fetchTranslation(
|
||||||
|
fingerprint,
|
||||||
|
base_url || DEFAULT_BASE_URL
|
||||||
|
)
|
||||||
|
.then((data) => ({ language, data }))
|
||||||
|
.catch((error) => {
|
||||||
|
delete translations[fingerprint];
|
||||||
|
if (language !== "en") {
|
||||||
|
// Couldn't load selected translation. Try a fall back to en before failing.
|
||||||
|
return getTranslation(fragment, "en", base_url);
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return translations[fingerprint];
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { fetchTranslationPreferences } from "../data/translation";
|
import { fetchTranslationPreferences } from "../data/translation";
|
||||||
import { translationMetadata } from "../resources/translations-metadata";
|
import { translationMetadata } from "../resources/translations-metadata";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
import { getTranslation as commonGetTranslation } from "./common-translation";
|
||||||
|
|
||||||
const STORAGE = window.localStorage || {};
|
const STORAGE = window.localStorage || {};
|
||||||
|
|
||||||
@ -93,55 +94,13 @@ export function getLocalLanguage() {
|
|||||||
return "en";
|
return "en";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store loaded translations in memory so translations are available immediately
|
|
||||||
// when DOM is created in Polymer. Even a cache lookup creates noticeable latency.
|
|
||||||
const translations = {};
|
|
||||||
|
|
||||||
async function fetchTranslation(fingerprint) {
|
|
||||||
const response = await fetch(`/static/translations/${fingerprint}`, {
|
|
||||||
credentials: "same-origin",
|
|
||||||
});
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(
|
|
||||||
`Fail to fetch translation ${fingerprint}: HTTP response status is ${response.status}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getTranslation(
|
export async function getTranslation(
|
||||||
fragment: string | null,
|
fragment: string | null,
|
||||||
language: string
|
language: string
|
||||||
) {
|
) {
|
||||||
const metadata = translationMetadata.translations[language];
|
return commonGetTranslation(fragment, language);
|
||||||
if (!metadata) {
|
|
||||||
if (language !== "en") {
|
|
||||||
return getTranslation(fragment, "en");
|
|
||||||
}
|
|
||||||
throw new Error("Language en is not found in metadata");
|
|
||||||
}
|
|
||||||
|
|
||||||
// nl-abcd.jon or logbook/nl-abcd.json
|
|
||||||
const fingerprint = `${fragment ? fragment + "/" : ""}${language}-${
|
|
||||||
metadata.hash
|
|
||||||
}.json`;
|
|
||||||
|
|
||||||
// Fetch translation from the server
|
|
||||||
if (!translations[fingerprint]) {
|
|
||||||
translations[fingerprint] = fetchTranslation(fingerprint)
|
|
||||||
.then((data) => ({ language, data }))
|
|
||||||
.catch((error) => {
|
|
||||||
delete translations[fingerprint];
|
|
||||||
if (language !== "en") {
|
|
||||||
// Couldn't load selected translation. Try a fall back to en before failing.
|
|
||||||
return getTranslation(fragment, "en");
|
|
||||||
}
|
|
||||||
return Promise.reject(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return translations[fingerprint];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load selected translation into memory immediately so it is ready when Polymer
|
// Load selected translation into memory immediately so it is ready when Polymer
|
||||||
// initializes.
|
// initializes.
|
||||||
getTranslation(null, getLocalLanguage());
|
commonGetTranslation(null, getLocalLanguage());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user