Compare commits

..
Author SHA1 Message Date
Zack BarettandJoakim Sørensen a8367f58a1 Update src/panels/lovelace/editor/hui-element-editor.ts
Co-authored-by: Joakim Sørensen <[email protected]>
2021-10-04 17:43:02 -05:00
Zack BarettandJoakim Sørensen bfd6b97f51 Update src/panels/lovelace/editor/hui-element-editor.ts
Co-authored-by: Joakim Sørensen <[email protected]>
2021-10-04 17:43:01 -05:00
Zack BarettandJoakim Sørensen af1e86fe82 Update src/panels/lovelace/editor/config-elements/hui-shopping-list-editor.ts
Co-authored-by: Joakim Sørensen <[email protected]>
2021-10-04 17:43:01 -05:00
Zack Arnett 5b90c8a19c First pass 2021-10-04 17:43:00 -05:00
Bram Kragten cba6bbdc74 Bumped version to 20211004.0 2021-10-04 23:43:13 +02:00
6f4593508b More statistics validation (#10146)
Co-authored-by: Philip Allgaier <[email protected]>
2021-10-04 14:21:21 -07:00
dc3bad56f2 Improve padding/positioning of ha-alert (#10145)
Co-authored-by: Paulus Schoutsen <[email protected]>
2021-10-04 16:27:03 +00:00
Joakim SørensenandGitHub 784e5e6e39 Add more secret string fields (#10149) 2021-10-04 08:42:18 -07:00
Michael IrigoyenandGitHub 13fe62975d Update MDI to v6.2.95 (#10142) 2021-10-04 10:33:25 +02:00
Tobias KündigandGitHub b97fd9918a Add decorator to translationFragment property (#10143) 2021-10-04 10:32:53 +02:00
uvjustinandGitHub dc56c2de52 Bump hls.js to 1.0.11 (#10144) 2021-10-04 10:29:58 +02:00
Philip AllgaierandGitHub 375a5323d5 Prevent wrong colors in history timeline for inverted unavailable states (#10137) 2021-10-03 11:35:53 -07:00
Bram Kragten 8e3011807d Bumped version to 20211002.0 2021-10-02 22:29:56 +02:00
Bram KragtenandGitHub ec7c6ab96c Add if node is secure to zwave js device page (#10135) 2021-10-02 10:20:16 -07:00
Bram KragtenandGitHub 8a4097a366 Fix labels device energy graph (#10134)
* Fix labels device energy graph

* prettier
2021-10-02 08:06:05 -07:00
Bram KragtenandGitHub 792a736e48 Fix fix stats callback (#10123)
* Fix `fix stats` callback

* memoize
2021-10-02 08:05:30 -07:00
Paulus SchoutsenandGitHub cce0a02ebb Add My support for statistics (#10131) 2021-10-02 10:15:26 +02:00
Paulus SchoutsenandGitHub 2ddab4eecc Fix webpack dev server (#10130) 2021-10-01 14:18:53 -07:00
Kyle NiewiadaandGitHub f66755cbf1 Fix inverted motion chart colors (#10128) 2021-10-01 20:12:05 +02:00
Bram KragtenandGitHub 257e60a2b1 Don't bundle locale data, but add to static (#10119) 2021-10-01 07:58:02 -07:00
Philip AllgaierandGitHub 75a3566760 Fixed typo in unsupported unit statistics dialog (#10118) 2021-10-01 00:00:20 +02:00
Joakim SørensenandGitHub 7a9f17e059 Use heading property for data disk dialog (#10115) 2021-09-30 19:08:51 +02:00
36 changed files with 887 additions and 332 deletions
+3 -1
View File
@@ -30,7 +30,7 @@ jobs:
env:
CI: true
- name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-demos
- name: Run eslint
run: yarn run lint:eslint
- name: Run tsc
@@ -53,6 +53,8 @@ jobs:
run: yarn install
env:
CI: true
- name: Build resources
run: ./node_modules/.bin/gulp build-translations build-locale-data
- name: Run Tests
run: yarn run test
build:
-1
View File
@@ -3,7 +3,6 @@
# build
build
build-translations/*
hass_frontend/*
dist
-1
View File
@@ -1,5 +1,4 @@
build
build-translations/*
translations/*
node_modules/*
hass_frontend/*
+4 -2
View File
@@ -5,6 +5,7 @@ const env = require("../env");
require("./clean.js");
require("./translations.js");
require("./locale-data.js");
require("./gen-icons-json.js");
require("./gather-static.js");
require("./compress.js");
@@ -26,7 +27,8 @@ gulp.task(
"gen-icons-json",
"gen-pages-dev",
"gen-index-app-dev",
"build-translations"
"build-translations",
"build-locale-data"
),
"copy-static-app",
env.useWDS()
@@ -44,7 +46,7 @@ gulp.task(
process.env.NODE_ENV = "production";
},
"clean",
gulp.parallel("gen-icons-json", "build-translations"),
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
"copy-static-app",
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
// Don't compress running tests
+2 -2
View File
@@ -18,7 +18,7 @@ gulp.task(
},
"clean-cast",
"translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations"),
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
"copy-static-cast",
"gen-index-cast-dev",
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
@@ -33,7 +33,7 @@ gulp.task(
},
"clean-cast",
"translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations"),
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
"copy-static-cast",
env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast",
"gen-index-cast-prod"
+7 -2
View File
@@ -20,7 +20,12 @@ gulp.task(
},
"clean-demo",
"translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "gen-index-demo-dev", "build-translations"),
gulp.parallel(
"gen-icons-json",
"gen-index-demo-dev",
"build-translations",
"build-locale-data"
),
"copy-static-demo",
env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo"
)
@@ -35,7 +40,7 @@ gulp.task(
"clean-demo",
// Cast needs to be backwards compatible and older HA has no translations
"translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations"),
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
"copy-static-demo",
env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo",
"gen-index-demo-prod"
+2
View File
@@ -51,6 +51,7 @@ gulp.task(
gulp.parallel(
"gen-icons-json",
"build-translations",
"build-locale-data",
"gather-gallery-demos"
),
"copy-static-gallery",
@@ -70,6 +71,7 @@ gulp.task(
gulp.parallel(
"gen-icons-json",
"build-translations",
"build-locale-data",
"gather-gallery-demos"
),
"copy-static-gallery",
+22 -1
View File
@@ -22,11 +22,18 @@ function copyTranslations(staticDir) {
// Translation output
fs.copySync(
polyPath("build-translations/output"),
polyPath("build/translations/output"),
staticPath("translations")
);
}
function copyLocaleData(staticDir) {
const staticPath = genStaticPath(staticDir);
// Locale data output
fs.copySync(polyPath("build/locale-data"), staticPath("locale-data"));
}
function copyMdiIcons(staticDir) {
const staticPath = genStaticPath(staticDir);
@@ -84,6 +91,11 @@ function copyMapPanel(staticDir) {
);
}
gulp.task("copy-locale-data", async () => {
const staticDir = paths.app_output_static;
copyLocaleData(staticDir);
});
gulp.task("copy-translations-app", async () => {
const staticDir = paths.app_output_static;
copyTranslations(staticDir);
@@ -94,6 +106,11 @@ gulp.task("copy-translations-supervisor", async () => {
copyTranslations(staticDir);
});
gulp.task("copy-locale-data-supervisor", async () => {
const staticDir = paths.hassio_output_static;
copyLocaleData(staticDir);
});
gulp.task("copy-static-app", async () => {
const staticDir = paths.app_output_static;
// Basic static files
@@ -103,6 +120,7 @@ gulp.task("copy-static-app", async () => {
copyPolyfills(staticDir);
copyFonts(staticDir);
copyTranslations(staticDir);
copyLocaleData(staticDir);
copyMdiIcons(staticDir);
// Panel assets
@@ -123,6 +141,7 @@ gulp.task("copy-static-demo", async () => {
copyMapPanel(paths.demo_output_static);
copyFonts(paths.demo_output_static);
copyTranslations(paths.demo_output_static);
copyLocaleData(paths.demo_output_static);
copyMdiIcons(paths.demo_output_static);
});
@@ -137,6 +156,7 @@ gulp.task("copy-static-cast", async () => {
copyMapPanel(paths.cast_output_static);
copyFonts(paths.cast_output_static);
copyTranslations(paths.cast_output_static);
copyLocaleData(paths.cast_output_static);
copyMdiIcons(paths.cast_output_static);
});
@@ -152,5 +172,6 @@ gulp.task("copy-static-gallery", async () => {
copyMapPanel(paths.gallery_output_static);
copyFonts(paths.gallery_output_static);
copyTranslations(paths.gallery_output_static);
copyLocaleData(paths.gallery_output_static);
copyMdiIcons(paths.gallery_output_static);
});
+4
View File
@@ -24,6 +24,8 @@ gulp.task(
"gen-index-hassio-dev",
"build-supervisor-translations",
"copy-translations-supervisor",
"build-locale-data",
"copy-locale-data-supervisor",
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
)
);
@@ -38,6 +40,8 @@ gulp.task(
"gen-icons-json",
"build-supervisor-translations",
"copy-translations-supervisor",
"build-locale-data",
"copy-locale-data-supervisor",
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
"gen-index-hassio-prod",
...// Don't compress running tests
+77
View File
@@ -0,0 +1,77 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const del = require("del");
const path = require("path");
const gulp = require("gulp");
const fs = require("fs");
const merge = require("gulp-merge-json");
const rename = require("gulp-rename");
const transform = require("gulp-json-transform");
const paths = require("../paths");
const outDir = "build/locale-data";
gulp.task("clean-locale-data", () => del([outDir]));
gulp.task("ensure-locale-data-build-dir", (done) => {
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
}
done();
});
const modules = {
"intl-relativetimeformat": "RelativeTimeFormat",
"intl-datetimeformat": "DateTimeFormat",
"intl-numberformat": "NumberFormat",
};
gulp.task("create-locale-data", (done) => {
const translationMeta = JSON.parse(
fs.readFileSync(
path.join(paths.translations_src, "translationMetadata.json")
)
);
Object.entries(modules).forEach(([module, className]) => {
Object.keys(translationMeta).forEach((lang) => {
try {
const localeData = String(
fs.readFileSync(
require.resolve(`@formatjs/${module}/locale-data/${lang}.js`)
)
)
.replace(
new RegExp(
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
"im"
),
""
)
.replace(/\)\s*}/im, "");
// make sure we have valid JSON
JSON.parse(localeData);
if (!fs.existsSync(path.join(outDir, module))) {
fs.mkdirSync(path.join(outDir, module), { recursive: true });
}
fs.writeFileSync(
path.join(outDir, `${module}/${lang}.json`),
localeData
);
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND") {
throw e;
}
}
});
done();
});
});
gulp.task(
"build-locale-data",
gulp.series(
"clean-locale-data",
"ensure-locale-data-build-dir",
"create-locale-data"
)
);
+2 -2
View File
@@ -17,7 +17,7 @@ const paths = require("../paths");
const inFrontendDir = "translations/frontend";
const inBackendDir = "translations/backend";
const workDir = "build-translations";
const workDir = "build/translations";
const fullDir = workDir + "/full";
const coreDir = workDir + "/core";
const outDir = workDir + "/output";
@@ -121,7 +121,7 @@ gulp.task("clean-translations", () => del([workDir]));
gulp.task("ensure-translations-build-dir", (done) => {
if (!fs.existsSync(workDir)) {
fs.mkdirSync(workDir);
fs.mkdirSync(workDir, { recursive: true });
}
done();
});
+29 -26
View File
@@ -35,26 +35,29 @@ const isWsl =
* listenHost?: string
* }}
*/
const runDevServer = ({
const runDevServer = async ({
compiler,
contentBase,
port,
listenHost = "localhost",
}) =>
new WebpackDevServer(compiler, {
open: true,
watchContentBase: true,
contentBase,
}).listen(port, listenHost, (err) => {
if (err) {
throw err;
}
// Server listening
log(
"[webpack-dev-server]",
`Project is running at http://localhost:${port}`
);
});
}) => {
const server = new WebpackDevServer(
{
open: true,
host: listenHost,
port,
static: {
directory: contentBase,
watch: true,
},
},
compiler
);
await server.start();
// Server listening
log("[webpack-dev-server]", `Project is running at http://localhost:${port}`);
};
const doneHandler = (done) => (err, stats) => {
if (err) {
@@ -107,13 +110,13 @@ gulp.task("webpack-prod-app", () =>
)
);
gulp.task("webpack-dev-server-demo", () => {
gulp.task("webpack-dev-server-demo", () =>
runDevServer({
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
contentBase: paths.demo_output_root,
port: 8090,
});
});
})
);
gulp.task("webpack-prod-demo", () =>
prodBuild(
@@ -123,15 +126,15 @@ gulp.task("webpack-prod-demo", () =>
)
);
gulp.task("webpack-dev-server-cast", () => {
gulp.task("webpack-dev-server-cast", () =>
runDevServer({
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
contentBase: paths.cast_output_root,
port: 8080,
// Accessible from the network, because that's how Cast hits it.
listenHost: "0.0.0.0",
});
});
})
);
gulp.task("webpack-prod-cast", () =>
prodBuild(
@@ -148,7 +151,7 @@ gulp.task("webpack-watch-hassio", () => {
isProdBuild: false,
latestBuild: true,
})
).watch({ ignored: /build-translations/, poll: isWsl }, doneHandler());
).watch({ ignored: /build/, poll: isWsl }, doneHandler());
gulp.watch(
path.join(paths.translations_src, "en.json"),
@@ -164,14 +167,14 @@ gulp.task("webpack-prod-hassio", () =>
)
);
gulp.task("webpack-dev-server-gallery", () => {
gulp.task("webpack-dev-server-gallery", () =>
runDevServer({
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
contentBase: paths.gallery_output_root,
port: 8100,
});
});
})
);
gulp.task("webpack-prod-gallery", () =>
prodBuild(
+19 -13
View File
@@ -107,19 +107,21 @@ export class DemoHaAlert extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="ha-alert demo">
${alerts.map(
(alert) => html`
<ha-alert
.title=${alert.title || ""}
.alertType=${alert.type}
.dismissable=${alert.dismissable || false}
.actionText=${alert.action || ""}
.rtl=${alert.rtl || false}
>
${alert.description}
</ha-alert>
`
)}
<div class="card-content">
${alerts.map(
(alert) => html`
<ha-alert
.title=${alert.title || ""}
.alertType=${alert.type}
.dismissable=${alert.dismissable || false}
.actionText=${alert.action || ""}
.rtl=${alert.rtl || false}
>
${alert.description}
</ha-alert>
`
)}
</div>
</ha-card>
`;
}
@@ -130,6 +132,10 @@ export class DemoHaAlert extends LitElement {
max-width: 600px;
margin: 24px auto;
}
ha-alert {
display: block;
margin: 24px 0;
}
.condition {
padding: 16px;
display: flex;
+212
View File
@@ -0,0 +1,212 @@
/* eslint-disable lit/no-template-arrow */
import { LitElement, TemplateResult, css, html } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-form/ha-form";
import "../../../src/components/ha-card";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import type { HaFormSchema } from "../../../src/components/ha-form/ha-form";
const SCHEMAS: {
title: string;
translations?: Record<string, string>;
error?: Record<string, string>;
schema: HaFormSchema[];
}[] = [
{
title: "Authentication",
translations: {
username: "Username",
password: "Password",
invalid_login: "Invalid login",
},
error: {
base: "invalid_login",
},
schema: [
{
type: "string",
name: "username",
required: true,
},
{
type: "string",
name: "password",
required: true,
},
],
},
{
title: "One of each",
schema: [
{
type: "constant",
value: "Constant Value",
name: "constant",
required: true,
},
{
type: "boolean",
name: "bool",
optional: true,
default: false,
},
{
type: "integer",
name: "int",
optional: true,
default: 10,
},
{
type: "string",
name: "string",
optional: true,
default: "Default",
},
{
type: "select",
options: [
["default", "default"],
["other", "other"],
],
name: "select",
optional: true,
default: "default",
},
{
type: "multi_select",
options: {
default: "Default",
other: "Other",
},
name: "multi",
optional: true,
default: ["default"],
},
],
},
{
title: "Multi select",
schema: [
{
type: "multi_select",
options: {
default: "Default",
other: "Other",
},
name: "multi",
optional: true,
default: ["default"],
},
{
type: "multi_select",
options: {
default: "Default",
other: "Other",
uno: "mas",
one: "more",
and: "another_one",
option: "1000",
},
name: "multi",
optional: true,
default: ["default"],
},
],
},
];
@customElement("demo-ha-form")
class DemoHaForm extends LitElement {
private lightModeData: any = [];
private darkModeData: any = [];
protected render(): TemplateResult {
return html`
${SCHEMAS.map((info, idx) => {
const translations = info.translations || {};
const computeLabel = (schema) =>
translations[schema.name] || schema.name;
const computeError = (error) => translations[error] || error;
return [
[this.lightModeData, "light"],
[this.darkModeData, "dark"],
].map(
([data, type]) => html`
<div class="row" data-type=${type}>
<ha-card .header=${info.title}>
<div class="card-content">
<ha-form
.data=${data[idx]}
.schema=${info.schema}
.error=${info.error}
.computeError=${computeError}
.computeLabel=${computeLabel}
@value-changed=${(e) => {
data[idx] = e.detail.value;
this.requestUpdate();
}}
></ha-form>
</div>
</ha-card>
<pre>${JSON.stringify(data[idx], undefined, 2)}</pre>
</div>
`
);
})}
`;
}
firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this.shadowRoot!.querySelectorAll("[data-type=dark]").forEach((el) => {
applyThemesOnElement(
el,
{
default_theme: "default",
default_dark_theme: "default",
themes: {},
darkMode: false,
},
"default",
{ dark: true }
);
});
}
static styles = css`
.row {
margin: 0 auto;
max-width: 800px;
display: flex;
padding: 50px;
background-color: var(--primary-background-color);
}
ha-card {
width: 100%;
max-width: 384px;
}
pre {
width: 400px;
margin: 0 16px;
overflow: auto;
color: var(--primary-text-color);
}
@media only screen and (max-width: 800px) {
.row {
flex-direction: column;
}
pre {
margin: 16px 0;
}
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-ha-form": DemoHaForm;
}
}
@@ -60,85 +60,72 @@ class HassioDatadiskDialog extends LitElement {
if (!this.dialogParams) {
return html``;
}
let heading: string;
let content: TemplateResult;
if (this.moving) {
heading = this.dialogParams.supervisor.localize(
"dialog.datadisk_move.moving"
);
content = html`
<ha-circular-progress alt="Moving" size="large" active>
</ha-circular-progress>
<p class="progress-text">
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.moving_desc"
)}
</p>
`;
} else {
heading = this.dialogParams.supervisor.localize(
"dialog.datadisk_move.title"
);
content = html`
${this.devices?.length
? html`
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.description",
{
current_path: this.dialogParams.supervisor.os.data_disk,
time: calculateMoveTime(this.dialogParams.supervisor),
}
)}
<br /><br />
<paper-dropdown-menu
.label=${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.select_device"
)}
@value-changed=${this._select_device}
>
<paper-listbox slot="dropdown-content">
${this.devices.map(
(device) => html`<paper-item>${device}</paper-item>`
)}
</paper-listbox>
</paper-dropdown-menu>
`
: this.devices === undefined
? this.dialogParams.supervisor.localize(
"dialog.datadisk_move.loading_devices"
)
: this.dialogParams.supervisor.localize(
"dialog.datadisk_move.no_devices"
)}
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.cancel"
)}
</mwc-button>
<mwc-button
.disabled=${!this.selectedDevice}
slot="primaryAction"
@click=${this._moveDatadisk}
>
${this.dialogParams.supervisor.localize("dialog.datadisk_move.move")}
</mwc-button>
`;
}
return html`
<ha-dialog
open
scrimClickAction
escapeKeyAction
.heading=${this.moving
? this.dialogParams.supervisor.localize("dialog.datadisk_move.moving")
: this.dialogParams.supervisor.localize("dialog.datadisk_move.title")}
@closed=${this.closeDialog}
?hideActions=${this.moving}
.heading=${heading}
>
${content}
${this.moving
? html` <ha-circular-progress alt="Moving" size="large" active>
</ha-circular-progress>
<p class="progress-text">
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.moving_desc"
)}
</p>`
: html` ${this.devices?.length
? html`
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.description",
{
current_path: this.dialogParams.supervisor.os.data_disk,
time: calculateMoveTime(this.dialogParams.supervisor),
}
)}
<br /><br />
<paper-dropdown-menu
.label=${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.select_device"
)}
@value-changed=${this._select_device}
>
<paper-listbox slot="dropdown-content">
${this.devices.map(
(device) => html`<paper-item>${device}</paper-item>`
)}
</paper-listbox>
</paper-dropdown-menu>
`
: this.devices === undefined
? this.dialogParams.supervisor.localize(
"dialog.datadisk_move.loading_devices"
)
: this.dialogParams.supervisor.localize(
"dialog.datadisk_move.no_devices"
)}
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.cancel"
)}
</mwc-button>
<mwc-button
.disabled=${!this.selectedDevice}
slot="primaryAction"
@click=${this._moveDatadisk}
>
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.move"
)}
</mwc-button>`}
</ha-dialog>
`;
}
+3 -3
View File
@@ -64,8 +64,8 @@
"@material/mwc-tab": "0.25.1",
"@material/mwc-tab-bar": "0.25.1",
"@material/top-app-bar": "13.0.0-canary.65125b3a6.0",
"@mdi/js": "6.1.95",
"@mdi/svg": "6.1.95",
"@mdi/js": "6.2.95",
"@mdi/svg": "6.2.95",
"@polymer/app-layout": "^3.1.0",
"@polymer/iron-flex-layout": "^3.0.1",
"@polymer/iron-icon": "^3.0.1",
@@ -108,7 +108,7 @@
"deep-freeze": "^0.0.1",
"fuse.js": "^6.0.0",
"google-timezones-json": "^1.0.2",
"hls.js": "^1.0.10",
"hls.js": "^1.0.11",
"home-assistant-js-websocket": "^5.11.1",
"idb-keyval": "^5.1.3",
"intl-messageformat": "^9.9.1",
+1 -1
View File
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="home-assistant-frontend",
version="20210930.0",
version="20211004.0",
description="The Home Assistant frontend",
url="https://github.com/home-assistant/frontend",
author="The Home Assistant Authors",
+45 -30
View File
@@ -4,6 +4,7 @@ import { shouldPolyfill as shouldPolyfillRelativeTime } from "@formatjs/intl-rel
import { shouldPolyfill as shouldPolyfillDateTime } from "@formatjs/intl-datetimeformat/lib/should-polyfill";
import IntlMessageFormat from "intl-messageformat";
import { Resources } from "../../types";
import { getLocalLanguage } from "../../util/hass-translation";
export type LocalizeFunc = (key: string, ...args: any[]) => string;
interface FormatType {
@@ -15,34 +16,32 @@ export interface FormatsType {
time: FormatType;
}
const polyfillPluralRules = shouldPolyfillPluralRules();
const polyfillRelativeTime = shouldPolyfillRelativeTime();
const polyfillDateTime = shouldPolyfillDateTime();
const loadedPolyfillLocale = new Set();
const polyfills: Promise<any>[] = [];
if (__BUILD__ === "latest") {
if (shouldPolyfillLocale()) {
polyfills.push(import("@formatjs/intl-locale/polyfill"));
}
if (polyfillPluralRules) {
if (shouldPolyfillPluralRules()) {
polyfills.push(import("@formatjs/intl-pluralrules/polyfill"));
polyfills.push(import("@formatjs/intl-pluralrules/locale-data/en"));
}
if (polyfillRelativeTime) {
if (shouldPolyfillRelativeTime()) {
polyfills.push(import("@formatjs/intl-relativetimeformat/polyfill"));
}
if (polyfillDateTime) {
if (shouldPolyfillDateTime()) {
polyfills.push(import("@formatjs/intl-datetimeformat/polyfill"));
}
}
let polyfillLoaded = polyfills.length === 0;
export const polyfillsLoaded = polyfillLoaded
? undefined
: Promise.all(polyfills).then(() => {
polyfillLoaded = true;
// Load English so it becomes the default
return loadPolyfillLocales("en");
});
export const polyfillsLoaded =
polyfills.length === 0
? undefined
: Promise.all(polyfills).then(() =>
// Load the default language
loadPolyfillLocales(getLocalLanguage())
);
/**
* Adapted from Polymer app-localize-behavior.
@@ -71,11 +70,11 @@ export const computeLocalize = async (
resources: Resources,
formats?: FormatsType
): Promise<LocalizeFunc> => {
if (!polyfillLoaded) {
if (polyfillsLoaded) {
await polyfillsLoaded;
}
loadPolyfillLocales(language);
await loadPolyfillLocales(language);
// Everytime any of the parameters change, invalidate the strings cache.
cache._localizationCache = {};
@@ -129,28 +128,44 @@ export const computeLocalize = async (
};
export const loadPolyfillLocales = async (language: string) => {
if (!polyfillsLoaded) {
if (loadedPolyfillLocale.has(language)) {
return;
}
await polyfillsLoaded;
loadedPolyfillLocale.add(language);
try {
if (polyfillPluralRules) {
await import(
/* webpackExclude: /.+-.+\.js$/ */
`@formatjs/intl-pluralrules/locale-data/${language}`
if (
Intl.NumberFormat &&
// @ts-ignore
typeof Intl.NumberFormat.__addLocaleData === "function"
) {
const result = await fetch(
`/static/locale-data/intl-numberformat/${language}.json`
);
// @ts-ignore
Intl.NumberFormat.__addLocaleData(await result.json());
}
if (polyfillRelativeTime) {
await import(
/* webpackExclude: /.+-.+\.js$/ */
`@formatjs/intl-relativetimeformat/locale-data/${language}`
if (
// @ts-expect-error
Intl.RelativeTimeFormat &&
// @ts-ignore
typeof Intl.RelativeTimeFormat.__addLocaleData === "function"
) {
const result = await fetch(
`/static/locale-data/intl-relativetimeformat/${language}.json`
);
// @ts-ignore
Intl.RelativeTimeFormat.__addLocaleData(await result.json());
}
if (polyfillDateTime) {
await import(
/* webpackExclude: /.+-.+\.js$/ */
`@formatjs/intl-datetimeformat/locale-data/${language}`
if (
Intl.DateTimeFormat &&
// @ts-ignore
typeof Intl.DateTimeFormat.__addLocaleData === "function"
) {
const result = await fetch(
`/static/locale-data/intl-datetimeformat/${language}.json`
);
// @ts-ignore
Intl.DateTimeFormat.__addLocaleData(await result.json());
}
} catch (_e) {
// Ignore
@@ -21,6 +21,7 @@ const BINARY_SENSOR_DEVICE_CLASS_COLOR_INVERTED = new Set([
"garage_door",
"gas",
"lock",
"motion",
"opening",
"problem",
"safety",
@@ -55,7 +56,11 @@ const getColor = (
entityState: HassEntity,
computedStyles: CSSStyleDeclaration
) => {
if (invertOnOff(entityState)) {
// Inversion is only valid for "on" or "off" state
if (
(stateString === "on" || stateString === "off") &&
invertOnOff(entityState)
) {
stateString = stateString === "on" ? "off" : "on";
}
if (stateColorMap.has(stateString)) {
+15 -16
View File
@@ -51,15 +51,11 @@ class HaAlert extends LitElement {
[this.alertType]: true,
})}"
>
<div class="icon">
<div class="icon ${this.title ? "" : "no-title"}">
<ha-svg-icon .path=${ALERT_ICONS[this.alertType]}></ha-svg-icon>
</div>
<div class="content">
<div
class="main-content ${classMap({
"no-title": !this.title,
})}"
>
<div class="main-content">
${this.title ? html`<div class="title">${this.title}</div>` : ""}
<slot></slot>
</div>
@@ -94,7 +90,7 @@ class HaAlert extends LitElement {
static styles = css`
.issue-type {
position: relative;
padding: 4px;
padding: 8px;
display: flex;
margin: 4px 0;
}
@@ -113,11 +109,16 @@ class HaAlert extends LitElement {
border-radius: 4px;
}
.icon {
margin: 4px 8px;
margin-right: 8px;
width: 24px;
}
.main-content.no-title {
margin-top: 6px;
.icon.no-title {
align-self: center;
}
.issue-type.rtl > .icon {
margin-right: 0px;
margin-left: 8px;
width: 24px;
}
.issue-type.rtl > .content {
flex-direction: row-reverse;
@@ -126,24 +127,22 @@ class HaAlert extends LitElement {
.content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.main-content {
overflow-wrap: anywhere;
}
.title {
margin-top: 2px;
font-weight: bold;
margin-top: 6px;
}
mwc-button {
--mdc-theme-primary: var(--primary-text-color);
}
.action {
align-self: center;
mwc-icon-button {
--mdc-icon-button-size: 36px;
}
.issue-type.info > .icon {
color: var(--info-color);
}
+3 -1
View File
@@ -12,6 +12,8 @@ import type {
HaFormStringSchema,
} from "./ha-form";
const MASKED_FIELDS = ["password", "secret", "token"];
@customElement("ha-form-string")
export class HaFormString extends LitElement implements HaFormElement {
@property() public schema!: HaFormStringSchema;
@@ -33,7 +35,7 @@ export class HaFormString extends LitElement implements HaFormElement {
}
protected render(): TemplateResult {
return this.schema.name.includes("password")
return MASKED_FIELDS.some((field) => this.schema.name.includes(field))
? html`
<paper-input
.type=${this._unmaskedPassword ? "text" : "password"}
+29 -5
View File
@@ -77,18 +77,42 @@ export interface StatisticsMetaData {
}
export type StatisticsValidationResult =
| StatisticsValidationResultUnsupportedUnit
| StatisticsValidationResultUnitsChanged;
| StatisticsValidationResultEntityNotRecorded
| StatisticsValidationResultUnsupportedStateClass
| StatisticsValidationResultUnitsChanged
| StatisticsValidationResultUnsupportedUnitMetadata
| StatisticsValidationResultUnsupportedUnitState;
export interface StatisticsValidationResultUnsupportedUnit {
type: "unsupported_unit";
data: { statistic_id: string; device_class: string; state_unit: string };
export interface StatisticsValidationResultEntityNotRecorded {
type: "entity_not_recorded";
data: { statistic_id: string };
}
export interface StatisticsValidationResultUnsupportedStateClass {
type: "unsupported_state_class";
data: { statistic_id: string; state_class: string };
}
export interface StatisticsValidationResultUnitsChanged {
type: "units_changed";
data: { statistic_id: string; state_unit: string; metadata_unit: string };
}
export interface StatisticsValidationResultUnsupportedUnitMetadata {
type: "unsupported_unit_metadata";
data: {
statistic_id: string;
device_class: string;
metadata_unit: string;
supported_unit: string;
};
}
export interface StatisticsValidationResultUnsupportedUnitState {
type: "unsupported_unit_state";
data: { statistic_id: string; device_class: string; metadata_unit: string };
}
export interface StatisticsValidationResults {
[statisticId: string]: StatisticsValidationResult[];
}
+1
View File
@@ -83,6 +83,7 @@ export interface ZWaveJSNodeStatus {
node_id: number;
ready: boolean;
status: number;
is_secure: boolean | string;
}
export interface ZwaveJSNodeMetadata {
+1 -1
View File
@@ -59,7 +59,7 @@ declare global {
class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
@property({ attribute: false }) public hass?: HomeAssistant;
public translationFragment = "page-onboarding";
@property() public translationFragment = "page-onboarding";
@state() private _loading = false;
@@ -116,6 +116,14 @@ export class HaDeviceInfoZWaveJS extends LitElement {
? this.hass.localize("ui.common.yes")
: this.hass.localize("ui.common.no")}
</div>
<div>
${this.hass.localize("ui.panel.config.zwave_js.device_info.is_secure")}:
${this._node.is_secure === true
? this.hass.localize("ui.common.yes")
: this._node.is_secure === false
? this.hass.localize("ui.common.no")
: this.hass.localize("ui.panel.config.zwave_js.device_info.unknown")}
</div>
`;
}
@@ -102,7 +102,7 @@ class PanelDeveloperTools extends LitElement {
}
developer-tools-router {
display: block;
height: calc(100vh - 112px);
height: calc(100vh - 104px);
}
ha-tabs {
margin-left: max(env(safe-area-inset-left), 24px);
@@ -2,6 +2,7 @@ import "@material/mwc-button/mwc-button";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import "../../../components/data-table/ha-data-table";
@@ -16,7 +17,15 @@ import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { showFixStatisticsUnitsChangedDialog } from "./show-dialog-statistics-fix-units-changed";
import { showFixStatisticsUnsupportedUnitMetadataDialog } from "./show-dialog-statistics-fix-unsupported-unit-meta";
const FIX_ISSUES_ORDER = {
entity_not_recorded: 1,
unsupported_unit_state: 2,
unsupported_state_class: 3,
units_changed: 4,
unsupported_unit_metadata: 5,
};
@customElement("developer-tools-statistics")
class HaPanelDevStatistics extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -32,63 +41,70 @@ class HaPanelDevStatistics extends LitElement {
this._validateStatistics();
}
private _columns: DataTableColumnContainer = {
state: {
title: "Entity",
sortable: true,
filterable: true,
grows: true,
template: (entityState, data: any) =>
html`${entityState
? computeStateName(entityState)
: data.statistic_id}`,
},
statistic_id: {
title: "Statistic id",
sortable: true,
filterable: true,
hidden: this.narrow,
width: "30%",
},
unit_of_measurement: {
title: "Unit",
sortable: true,
filterable: true,
width: "10%",
},
issues: {
title: "Issue",
sortable: true,
filterable: true,
direction: "asc",
width: "30%",
template: (issues) =>
html`${issues
? issues.map(
(issue) =>
this.hass.localize(
`ui.panel.developer-tools.tabs.statistics.issues.${issue.type}`,
issue.data
) || issue.type
)
: ""}`,
},
fix: {
title: "",
template: (_, data: any) =>
html`${data.issues
? html`<mwc-button @click=${this._fixIssue} .data=${data.issues}
>Fix issue</mwc-button
>`
: ""}`,
width: "113px",
},
};
/* eslint-disable lit/no-template-arrow */
private _columns = memoizeOne(
(localize): DataTableColumnContainer => ({
state: {
title: "Entity",
sortable: true,
filterable: true,
grows: true,
template: (entityState, data: any) =>
html`${entityState
? computeStateName(entityState)
: data.statistic_id}`,
},
statistic_id: {
title: "Statistic id",
sortable: true,
filterable: true,
hidden: this.narrow,
width: "30%",
},
unit_of_measurement: {
title: "Unit",
sortable: true,
filterable: true,
width: "10%",
},
issues: {
title: "Issue",
sortable: true,
filterable: true,
direction: "asc",
width: "30%",
template: (issues) =>
html`${issues
? issues.map(
(issue) =>
localize(
`ui.panel.developer-tools.tabs.statistics.issues.${issue.type}`,
issue.data
) || issue.type
)
: ""}`,
},
fix: {
title: "",
template: (_, data: any) =>
html`${data.issues
? html`<mwc-button
@click=${(ev) => this._fixIssue(ev)}
.data=${data.issues}
>
Fix issue
</mwc-button>`
: ""}`,
width: "113px",
},
})
);
/* eslint-enable lit/no-template-arrow */
protected render() {
return html`
<ha-data-table
.columns=${this._columns}
.columns=${this._columns(this.hass.localize)}
.data=${this._data}
noDataText="No issues found!"
id="statistic_id"
@@ -111,48 +127,115 @@ class HaPanelDevStatistics extends LitElement {
validateStatistics(this.hass),
]);
this._data = statisticIds.map((statistic) => ({
...statistic,
state: this.hass.states[statistic.statistic_id],
issues: issues[statistic.statistic_id],
}));
const statsIds = new Set();
this._data = statisticIds.map((statistic) => {
statsIds.add(statistic.statistic_id);
return {
...statistic,
state: this.hass.states[statistic.statistic_id],
issues: issues[statistic.statistic_id],
};
});
Object.keys(issues).forEach((statisticId) => {
if (!statsIds.has(statisticId)) {
this._data.push({
statistic_id: statisticId,
unit_of_measurement: "",
state: this.hass.states[statisticId],
issues: issues[statisticId],
});
}
});
}
private _fixIssue(ev) {
const issue = ev.currentTarget.data[0] as StatisticsValidationResult;
if (issue.type === "unsupported_unit") {
showAlertDialog(this, {
title: "Unsupported unit",
text: html`The unit of your entity is not a suppported unit for the
device class of the entity, ${issue.data.device_class}.
<br />Statistics can not be generated until this entity has a
supported unit. <br /><br />If this unit was provided by an
integration, this is a bug. Please report an issue. <br /><br />If you
have set this unit yourself, and want to have statistics generated,
make sure the unit matched the device class. The supported units are
documented in the
<a
href="https://developers.home-assistant.io/docs/core/entity/sensor"
target="_blank"
>
developer documentation</a
>.`,
});
return;
const issues = (ev.currentTarget.data as StatisticsValidationResult[]).sort(
(itemA, itemB) =>
(FIX_ISSUES_ORDER[itemA.type] ?? 99) -
(FIX_ISSUES_ORDER[itemB.type] ?? 99)
);
const issue = issues[0];
switch (issue.type) {
case "entity_not_recorded":
showAlertDialog(this, {
title: "Entity not recorded",
text: html`State changes of this entity are not recorded, therefore,
we can not track long term statistics for it. <br /><br />You
probably excluded this entity, or have just included some
entities.<br /><br />See the
<a
href="https://www.home-assistant.io/integrations/recorder/#configure-filter"
target="_blank"
rel="noreferrer noopener"
>
recorder documentation</a
>
for more information.`,
});
break;
case "unsupported_state_class":
showAlertDialog(this, {
title: "Unsupported state class",
text: html`The state class of this entity, ${issue.data.state_class}
is not supported. <br />Statistics can not be generated until this
entity has a supported state class.<br /><br />If this state class
was provided by an integration, this is a bug. Please report an
issue.<br /><br />If you have set this state class yourself, please
correct it. The different state classes and when to use which can be
found in the
<a
href="https://developers.home-assistant.io/docs/core/entity/sensor/#long-term-statistics"
target="_blank"
rel="noreferrer noopener"
>
developer documentation</a
>.`,
});
break;
case "unsupported_unit_metadata":
showFixStatisticsUnsupportedUnitMetadataDialog(this, {
issue,
fixedCallback: () => {
this._validateStatistics();
},
});
break;
case "unsupported_unit_state":
showAlertDialog(this, {
title: "Unsupported unit",
text: html`The unit of your entity is not a supported unit for the
device class of the entity, ${issue.data.device_class}.
<br />Statistics can not be generated until this entity has a
supported unit.<br /><br />If this unit was provided by an
integration, this is a bug. Please report an issue.<br /><br />If
you have set this unit yourself, and want to have statistics
generated, make sure the unit matches the device class. The
supported units are documented in the
<a
href="https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes"
target="_blank"
rel="noreferrer noopener"
>
developer documentation</a
>.`,
});
break;
case "units_changed":
showFixStatisticsUnitsChangedDialog(this, {
issue,
fixedCallback: () => {
this._validateStatistics();
},
});
break;
default:
showAlertDialog(this, {
title: "Fix issue",
text: "Fixing this issue is not supported yet.",
});
}
if (issue.type === "units_changed") {
showFixStatisticsUnitsChangedDialog(this, {
issue,
fixedCallback: () => {
this._validateStatistics();
},
});
return;
}
showAlertDialog(this, {
title: "Fix issue",
text: "Fixing this issue is not supported yet.",
});
}
static get styles(): CSSResultGroup {
@@ -0,0 +1,79 @@
import "@material/mwc-button/mwc-button";
import { LitElement, TemplateResult, html, CSSResultGroup } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-dialog";
import { fireEvent } from "../../../common/dom/fire_event";
import { haStyle, haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { updateStatisticsMetadata } from "../../../data/history";
import "../../../components/ha-formfield";
import "../../../components/ha-radio";
import { DialogStatisticsUnsupportedUnitMetaParams } from "./show-dialog-statistics-fix-unsupported-unit-meta";
@customElement("dialog-statistics-fix-unsupported-unit-meta")
export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _params?: DialogStatisticsUnsupportedUnitMetaParams;
public showDialog(params: DialogStatisticsUnsupportedUnitMetaParams): void {
this._params = params;
}
public closeDialog(): void {
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult | void {
if (!this._params) {
return html``;
}
return html`
<ha-dialog
open
@closed=${this.closeDialog}
heading="Unsupported unit in recorded statistics"
>
<p>
The unit of the statistics in your database for this entity is not a
supported unit for the device class of the entity,
${this._params.issue.data.device_class}. It should be
${this._params.issue.data.supported_unit}.
</p>
<p>
Do you want to update the unit of the history statistics to
${this._params.issue.data.supported_unit}?
</p>
<mwc-button slot="primaryAction" @click=${this._fixIssue}>
Fix
</mwc-button>
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.close")}
</mwc-button>
</ha-dialog>
`;
}
private async _fixIssue(): Promise<void> {
await updateStatisticsMetadata(
this.hass,
this._params!.issue.data.statistic_id,
this._params!.issue.data.supported_unit
);
this._params?.fixedCallback();
this.closeDialog();
}
static get styles(): CSSResultGroup {
return [haStyle, haStyleDialog];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-statistics-fix-unsupported-unit-meta": DialogStatisticsFixUnsupportedUnitMetadata;
}
}
@@ -0,0 +1,21 @@
import { fireEvent } from "../../../common/dom/fire_event";
import { StatisticsValidationResultUnsupportedUnitMetadata } from "../../../data/history";
export const loadFixUnsupportedUnitMetaDialog = () =>
import("./dialog-statistics-fix-unsupported-unit-meta");
export interface DialogStatisticsUnsupportedUnitMetaParams {
issue: StatisticsValidationResultUnsupportedUnitMetadata;
fixedCallback: () => void;
}
export const showFixStatisticsUnsupportedUnitMetadataDialog = (
element: HTMLElement,
detailParams: DialogStatisticsUnsupportedUnitMetaParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-statistics-fix-unsupported-unit-meta",
dialogImport: loadFixUnsupportedUnitMetaDialog,
dialogParams: detailParams,
});
};
@@ -3,6 +3,7 @@ import {
ChartDataset,
ChartOptions,
ParsedDataType,
ScatterDataPoint,
} from "chart.js";
import { getRelativePosition } from "chart.js/helpers";
import { addHours } from "date-fns";
@@ -21,11 +22,7 @@ import {
import "../../../../components/chart/ha-chart-base";
import type HaChartBase from "../../../../components/chart/ha-chart-base";
import "../../../../components/ha-card";
import {
DeviceConsumptionEnergyPreference,
EnergyData,
getEnergyDataCollection,
} from "../../../../data/energy";
import { EnergyData, getEnergyDataCollection } from "../../../../data/energy";
import {
calculateStatisticSumGrowth,
fetchStatistics,
@@ -52,8 +49,6 @@ export class HuiEnergyDevicesGraphCard
@query("ha-chart-base") private _chart?: HaChartBase;
private _deviceConsumptionPrefs: DeviceConsumptionEnergyPreference[] = [];
public hassSubscribe(): UnsubscribeFunc[] {
return [
getEnergyDataCollection(this.hass, {
@@ -110,11 +105,11 @@ export class HuiEnergyDevicesGraphCard
ticks: {
autoSkip: false,
callback: (index) => {
const devicePref = this._deviceConsumptionPrefs[index];
const entity = this.hass.states[devicePref.stat_consumption];
return entity
? computeStateName(entity)
: devicePref.stat_consumption;
const entityId = (
this._chartData.datasets[0].data[index] as ScatterDataPoint
).y;
const entity = this.hass.states[entityId];
return entity ? computeStateName(entity) : entityId;
},
},
},
@@ -160,8 +155,6 @@ export class HuiEnergyDevicesGraphCard
);
private async _getStatistics(energyData: EnergyData): Promise<void> {
this._deviceConsumptionPrefs = energyData.prefs.device_consumption;
this._data = await fetchStatistics(
this.hass,
addHours(energyData.start, -1),
@@ -1,9 +1,10 @@
import "@polymer/paper-input/paper-input";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { assert, assign, object, optional, string } from "superstruct";
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import { HomeAssistant } from "../../../../types";
import { ShoppingListCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
@@ -50,11 +51,11 @@ export class HuiShoppingListEditor
<div class="card-config">
${!isComponentLoaded(this.hass, "shopping_list")
? html`
<div class="error">
<ha-alert alert-type="error">
${this.hass.localize(
"ui.panel.lovelace.editor.card.shopping-list.integration_not_loaded"
)}
</div>
</ha-alert>
`
: ""}
<paper-input
@@ -99,14 +100,6 @@ export class HuiShoppingListEditor
}
fireEvent(this, "config-changed", { config: this._config });
}
static get styles(): CSSResultGroup {
return css`
.error {
color: var(--error-color);
}
`;
}
}
declare global {
@@ -8,7 +8,7 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
import { property, state, query } from "lit/decorators";
import { property, query, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import { handleStructError } from "../../../common/structs/handle-errors";
import { computeRTL } from "../../../common/util/compute_rtl";
@@ -207,40 +207,45 @@ export abstract class HuiElementEditor<T> extends LitElement {
`}
${this._guiSupported === false && this.configElementType
? html`
<div class="info">
<ha-alert>
${this.hass.localize(
"ui.errors.config.editor_not_available",
"type",
this.configElementType
)}
</div>
</ha-alert>
`
: ""}
${this.hasError
? html`
<div class="error">
${this.hass.localize("ui.errors.config.error_detected")}:
<br />
<ha-alert
.title=${this.hass.localize("ui.errors.config.error_detected")}
alert-type="error"
>
<ul>
${this._errors!.map((error) => html`<li>${error}</li>`)}
</ul>
</div>
</ha-alert>
`
: ""}
${this.hasWarning
? html`
<div class="warning">
${this.hass.localize("ui.errors.config.editor_not_supported")}:
<br />
<ha-alert
.title=${this.hass.localize(
"ui.errors.config.editor_not_supported"
)}
alert-type="warning"
>
${this._warnings!.length > 0 && this._warnings![0] !== undefined
? html` <ul>
${this._warnings!.map(
(warning) => html`<li>${warning}</li>`
)}
</ul>`
? html`
<ul>
${this._warnings!.map(
(warning) => html`<li>${warning}</li>`
)}
</ul>
`
: ""}
${this.hass.localize("ui.errors.config.edit_in_yaml_supported")}
</div>
</ha-alert>
`
: ""}
</div>
+3
View File
@@ -31,6 +31,9 @@ const REDIRECTS: Redirects = {
developer_events: {
redirect: "/developer-tools/event",
},
developer_statistics: {
redirect: "/developer-tools/statistics",
},
config: {
redirect: "/config",
},
+1 -1
View File
@@ -1,4 +1,4 @@
import * as translationMetadata_ from "../../build-translations/translationMetadata.json";
import * as translationMetadata_ from "../../build/translations/translationMetadata.json";
import { TranslationMetadata } from "../types.js";
export const translationMetadata = (translationMetadata_ as any)
+7 -2
View File
@@ -2761,7 +2761,9 @@
"device_config": "Configure Device",
"reinterview_device": "Re-interview Device",
"heal_node": "Heal Device",
"remove_failed": "Remove Failed Device"
"remove_failed": "Remove Failed Device",
"is_secure": "Secure",
"unknown": "Unknown"
},
"node_config": {
"header": "Z-Wave Device Configuration",
@@ -3726,7 +3728,10 @@
"issue": "Issue",
"issues": {
"units_changed": "The unit of this entity changed from ''{metadata_unit}'' to ''{state_unit}''.",
"unsupported_unit": "The unit (''{state_unit}'') of this entity doesn't match a unit of device class ''{device_class}''."
"unsupported_unit_state": "The unit (''{state_unit}'') of this entity doesn't match a unit of device class ''{device_class}''.",
"unsupported_unit_metadata": "The unit (''{state_unit}'') of the recorded statistics doesn't match a unit of device class ''{device_class}''.",
"unsupported_state_class": "The state class ''{state_class}'' of this entity is not supported.",
"entity_not_recorded": "This entity is excluded from being recorded."
},
"fix_issue": {
"units_changed": {
+15 -15
View File
@@ -2783,17 +2783,17 @@ __metadata:
languageName: node
linkType: hard
"@mdi/js@npm:6.1.95":
version: 6.1.95
resolution: "@mdi/js@npm:6.1.95"
checksum: 318899617da78956ae1e0650b83be65aa459f7fdb9f45212fe081776016e3b9881d1cf03458f65a5d86a99c3387caad0d87828808bca87e21db7e69706337016
"@mdi/js@npm:6.2.95":
version: 6.2.95
resolution: "@mdi/js@npm:6.2.95"
checksum: 85f33d0bbb0665ef048dbcae8759eb7b17db35594f1e471c71946fb36ea8416d3c21b3a1013bb587c7293f2c84d97af6ed791a6904625abdae7ec96a75569e5e
languageName: node
linkType: hard
"@mdi/svg@npm:6.1.95":
version: 6.1.95
resolution: "@mdi/svg@npm:6.1.95"
checksum: 55e522e1758509f9a293600b0fdd6fd874aaefe8c4a404391fe11c178b24afd7a37601775af846776ac60899c03d74e39a5e061615bc65918bef8679b3dea968
"@mdi/svg@npm:6.2.95":
version: 6.2.95
resolution: "@mdi/svg@npm:6.2.95"
checksum: c2f14e8b62ae4c31da84af44d13f02b050c9a7759e20f08c9088996b2cafea5ff372c89795d57e976d8a693034964dad8f65bc75eea02834d1d8784e2e22627e
languageName: node
linkType: hard
@@ -8915,10 +8915,10 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"hls.js@npm:^1.0.10":
version: 1.0.10
resolution: "hls.js@npm:1.0.10"
checksum: 56fb2729a55a68a384e3fbba4a6146702533896ac467d556afeefa12c315d64d68c2a21673d345ebacf392172ad7e8429f597a12ed90d2506b77a3093d05e596
"hls.js@npm:^1.0.11":
version: 1.0.11
resolution: "hls.js@npm:1.0.11"
checksum: 0375871cf8ffef3374f44284028d235bb122dc5ee2a36b765124e8caca1ec97ba96738ef8de261fbdecf78669e7bf3fb8aee1070f239c7791add44fad50df180
languageName: node
linkType: hard
@@ -8982,8 +8982,8 @@ fsevents@^1.2.7:
"@material/mwc-tab": 0.25.1
"@material/mwc-tab-bar": 0.25.1
"@material/top-app-bar": 13.0.0-canary.65125b3a6.0
"@mdi/js": 6.1.95
"@mdi/svg": 6.1.95
"@mdi/js": 6.2.95
"@mdi/svg": 6.2.95
"@open-wc/dev-server-hmr": ^0.0.2
"@polymer/app-layout": ^3.1.0
"@polymer/iron-flex-layout": ^3.0.1
@@ -9067,7 +9067,7 @@ fsevents@^1.2.7:
gulp-merge-json: ^1.3.1
gulp-rename: ^2.0.0
gulp-zopfli-green: ^3.0.1
hls.js: ^1.0.10
hls.js: ^1.0.11
home-assistant-js-websocket: ^5.11.1
html-minifier: ^4.0.0
husky: ^1.3.1