Compare commits

..

5 Commits

Author SHA1 Message Date
J. Nick Koston
23ba92e4ad avoid downloading the whole entity registry again as well 2023-02-24 18:29:30 -06:00
J. Nick Koston
c636eacc51 Merge branch 'energy_no_ids' into ii2 2023-02-24 17:34:27 -06:00
J. Nick Koston
2c6acecb60 Merge branch 'dupe_calls' into ii2 2023-02-24 17:14:15 -06:00
J. Nick Koston
225a3c3f50 Avoid fetching all stats metadata when there are no entities
Fetch all the data at once since it is not dependant
2023-02-24 17:12:01 -06:00
J. Nick Koston
19c125f7be Fix duplicate fetch of stats metadata in more info 2023-02-24 16:51:18 -06:00
21 changed files with 173 additions and 400 deletions

View File

@@ -59,6 +59,7 @@
"prefer-destructuring": "off", "prefer-destructuring": "off",
"no-restricted-globals": [2, "event"], "no-restricted-globals": [2, "event"],
"prefer-promise-reject-errors": "off", "prefer-promise-reject-errors": "off",
"no-unsafe-optional-chaining": "warn",
"import/prefer-default-export": "off", "import/prefer-default-export": "off",
"import/no-default-export": "off", "import/no-default-export": "off",
"import/no-unresolved": "off", "import/no-unresolved": "off",

View File

@@ -2,7 +2,6 @@ const webpack = require("webpack");
const path = require("path"); const path = require("path");
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const log = require("fancy-log"); const log = require("fancy-log");
const WebpackBar = require("webpackbar"); const WebpackBar = require("webpackbar");
const paths = require("./paths.js"); const paths = require("./paths.js");
@@ -76,7 +75,6 @@ const createWebpackConfig = ({
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
}, },
plugins: [ plugins: [
new CompressionWebpackPlugin(),
!isStatsBuild && new WebpackBar({ fancy: !isProdBuild }), !isStatsBuild && new WebpackBar({ fancy: !isProdBuild }),
new WebpackManifestPlugin({ new WebpackManifestPlugin({
// Only include the JS of entrypoints // Only include the JS of entrypoints

View File

@@ -100,7 +100,6 @@
"app-datepicker": "^5.1.0", "app-datepicker": "^5.1.0",
"chart.js": "^3.3.2", "chart.js": "^3.3.2",
"comlink": "^4.4.1", "comlink": "^4.4.1",
"compression-webpack-plugin": "^10.0.0",
"core-js": "^3.28.0", "core-js": "^3.28.0",
"cropperjs": "^1.5.13", "cropperjs": "^1.5.13",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
@@ -135,10 +134,10 @@
"tsparticles-preset-links": "^2.9.3", "tsparticles-preset-links": "^2.9.3",
"unfetch": "^5.0.0", "unfetch": "^5.0.0",
"vis-data": "^7.1.4", "vis-data": "^7.1.4",
"vis-network": "^9.1.4", "vis-network": "^9.1.2",
"vue": "^2.7.14", "vue": "^2.7.14",
"vue2-daterange-picker": "^0.6.8", "vue2-daterange-picker": "^0.6.8",
"weekstart": "^2.0.0", "weekstart": "^1.1.0",
"workbox-cacheable-response": "^6.5.4", "workbox-cacheable-response": "^6.5.4",
"workbox-core": "^6.5.4", "workbox-core": "^6.5.4",
"workbox-expiration": "^6.5.4", "workbox-expiration": "^6.5.4",
@@ -189,7 +188,7 @@
"babel-loader": "^9.1.2", "babel-loader": "^9.1.2",
"chai": "^4.3.7", "chai": "^4.3.7",
"del": "^7.0.0", "del": "^7.0.0",
"eslint": "^8.35.0", "eslint": "^8.34.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-prettier": "^8.6.0", "eslint-config-prettier": "^8.6.0",
@@ -233,7 +232,7 @@
"serve": "^11.3.2", "serve": "^11.3.2",
"sinon": "^15.0.1", "sinon": "^15.0.1",
"source-map-url": "^0.4.1", "source-map-url": "^0.4.1",
"systemjs": "^6.14.0", "systemjs": "^6.13.0",
"tar": "^6.1.13", "tar": "^6.1.13",
"terser-webpack-plugin": "^5.3.6", "terser-webpack-plugin": "^5.3.6",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20230224.0" version = "20230223.0"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "The Home Assistant frontend" description = "The Home Assistant frontend"
readme = "README.md" readme = "README.md"

View File

@@ -1,6 +1,6 @@
import { css, html, LitElement, nothing } from "lit"; /* eslint-disable lit/prefer-static-styles */
import { html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import type { HaFormSchema } from "../components/ha-form/types"; import type { HaFormSchema } from "../components/ha-form/types";
import { autocompleteLoginFields } from "../data/auth"; import { autocompleteLoginFields } from "../data/auth";
@@ -29,14 +29,18 @@ export class HaPasswordManagerPolyfill extends LitElement {
@property({ attribute: false }) public boundingRect?: DOMRect; @property({ attribute: false }) public boundingRect?: DOMRect;
private _styleElement?: HTMLStyleElement; protected createRenderRoot() {
// Add under document body so the element isn't placed inside any shadow roots
return document.body;
}
public connectedCallback() { private get styles() {
super.connectedCallback(); return `
this._styleElement = document.createElement("style");
this._styleElement.textContent = css`
.password-manager-polyfill { .password-manager-polyfill {
position: absolute; position: absolute;
top: ${this.boundingRect?.y || 148}px;
left: calc(50% - ${(this.boundingRect?.width || 360) / 2}px);
width: ${this.boundingRect?.width || 360}px;
opacity: 0; opacity: 0;
z-index: -1; z-index: -1;
} }
@@ -50,22 +54,10 @@ export class HaPasswordManagerPolyfill extends LitElement {
width: 0; width: 0;
height: 0; height: 0;
} }
`.toString(); `;
document.head.append(this._styleElement);
} }
public disconnectedCallback() { protected render(): TemplateResult {
super.disconnectedCallback();
this._styleElement?.remove();
delete this._styleElement;
}
protected createRenderRoot() {
// Add under document body so the element isn't placed inside any shadow roots
return document.body;
}
protected render() {
if ( if (
this.step && this.step &&
this.step.type === "form" && this.step.type === "form" &&
@@ -75,11 +67,6 @@ export class HaPasswordManagerPolyfill extends LitElement {
return html` return html`
<form <form
class="password-manager-polyfill" class="password-manager-polyfill"
style=${styleMap({
top: `${this.boundingRect?.y || 148}px`,
left: `calc(50% - ${(this.boundingRect?.width || 360) / 2}px)`,
width: `${this.boundingRect?.width || 360}px`,
})}
aria-hidden="true" aria-hidden="true"
@submit=${this._handleSubmit} @submit=${this._handleSubmit}
> >
@@ -87,13 +74,16 @@ export class HaPasswordManagerPolyfill extends LitElement {
this.render_input(input) this.render_input(input)
)} )}
<input type="submit" /> <input type="submit" />
<style>
${this.styles}
</style>
</form> </form>
`; `;
} }
return nothing; return html``;
} }
private render_input(schema: HaFormSchema) { private render_input(schema: HaFormSchema): TemplateResult | string {
const inputType = schema.name.includes("password") ? "password" : "text"; const inputType = schema.name.includes("password") ? "password" : "text";
if (schema.type !== "string") { if (schema.type !== "string") {
return ""; return "";

View File

@@ -24,6 +24,7 @@ import {
mdiDatabase, mdiDatabase,
mdiEarHearing, mdiEarHearing,
mdiEye, mdiEye,
mdiFan,
mdiFlash, mdiFlash,
mdiFlower, mdiFlower,
mdiFormatListBulleted, mdiFormatListBulleted,
@@ -90,6 +91,7 @@ export const FIXED_DOMAIN_ICONS = {
conversation: mdiMicrophoneMessage, conversation: mdiMicrophoneMessage,
counter: mdiCounter, counter: mdiCounter,
demo: mdiHomeAssistant, demo: mdiHomeAssistant,
fan: mdiFan,
google_assistant: mdiGoogleAssistant, google_assistant: mdiGoogleAssistant,
group: mdiGoogleCirclesCommunities, group: mdiGoogleCirclesCommunities,
homeassistant: mdiHomeAssistant, homeassistant: mdiHomeAssistant,

View File

@@ -15,8 +15,6 @@ import {
mdiCheckCircleOutline, mdiCheckCircleOutline,
mdiClock, mdiClock,
mdiCloseCircleOutline, mdiCloseCircleOutline,
mdiFan,
mdiFanOff,
mdiGestureTapButton, mdiGestureTapButton,
mdiLanConnect, mdiLanConnect,
mdiLanDisconnect, mdiLanDisconnect,
@@ -110,9 +108,6 @@ export const domainIconWithoutDefault = (
} }
return compareState === "not_home" ? mdiAccountArrowRight : mdiAccount; return compareState === "not_home" ? mdiAccountArrowRight : mdiAccount;
case "fan":
return compareState === "off" ? mdiFanOff : mdiFan;
case "humidifier": case "humidifier":
return compareState === "off" ? mdiAirHumidifierOff : mdiAirHumidifier; return compareState === "off" ? mdiAirHumidifierOff : mdiAirHumidifier;

View File

@@ -44,6 +44,7 @@ export class HaBar extends LitElement {
} }
rect:last-child { rect:last-child {
fill: var(--ha-bar-primary-color, var(--primary-color)); fill: var(--ha-bar-primary-color, var(--primary-color));
rx: var(--ha-bar-border-radius, 4px);
} }
svg { svg {
border-radius: var(--ha-bar-border-radius, 4px); border-radius: var(--ha-bar-border-radius, 4px);

View File

@@ -62,7 +62,7 @@ class HaLabelBadge extends LitElement {
height: var(--ha-label-badge-size, 2.5em); height: var(--ha-label-badge-size, 2.5em);
line-height: var(--ha-label-badge-size, 2.5em); line-height: var(--ha-label-badge-size, 2.5em);
font-size: var(--ha-label-badge-font-size, 1.5em); font-size: var(--ha-label-badge-font-size, 1.5em);
border-radius: var(--ha-label-badge-border-radius, 50%); border-radius: 50%;
border: 0.1em solid var(--ha-label-badge-color, var(--primary-color)); border: 0.1em solid var(--ha-label-badge-color, var(--primary-color));
color: var(--label-badge-text-color, rgb(76, 76, 76)); color: var(--label-badge-text-color, rgb(76, 76, 76));

View File

@@ -30,30 +30,6 @@ export class HaListItem extends ListItemBase {
margin-inline-end: 0px !important; margin-inline-end: 0px !important;
direction: var(--direction); direction: var(--direction);
} }
:host([multiline-secondary]) {
height: auto;
}
:host([multiline-secondary]) .mdc-deprecated-list-item__text {
padding: 8px 0;
}
:host([multiline-secondary]) .mdc-deprecated-list-item__secondary-text {
text-overflow: initial;
white-space: normal;
overflow: auto;
display: inline-block;
margin-top: 10px;
}
:host([multiline-secondary]) .mdc-deprecated-list-item__primary-text {
margin-top: 10px;
}
:host([multiline-secondary])
.mdc-deprecated-list-item__secondary-text::before {
display: none;
}
:host([multiline-secondary])
.mdc-deprecated-list-item__primary-text::before {
display: none;
}
`, `,
]; ];
} }

View File

@@ -193,12 +193,6 @@ export interface EnergyPreferencesValidation {
device_consumption: EnergyValidationIssue[][]; device_consumption: EnergyValidationIssue[][];
} }
export interface EnergyInfoAndCO2Signal {
energyInfo: EnergyInfo;
co2SignalEntity: string | undefined;
co2SignalConfigEntry: ConfigEntry | undefined;
}
export const getEnergyInfo = (hass: HomeAssistant) => export const getEnergyInfo = (hass: HomeAssistant) =>
hass.callWS<EnergyInfo>({ hass.callWS<EnergyInfo>({
type: "energy/info", type: "energy/info",
@@ -338,9 +332,13 @@ export const getReferencedStatisticIds = (
return statIDs; return statIDs;
}; };
const getEnergyInfoAndCO2Signal = async ( const getEnergyData = async (
hass: HomeAssistant hass: HomeAssistant,
): Promise<EnergyInfoAndCO2Signal> => { prefs: EnergyPreferences,
start: Date,
end?: Date,
compare?: boolean
): Promise<EnergyData> => {
const [configEntries, info] = await Promise.all([ const [configEntries, info] = await Promise.all([
getConfigEntries(hass, { domain: "co2signal" }), getConfigEntries(hass, { domain: "co2signal" }),
getEnergyInfo(hass), getEnergyInfo(hass),
@@ -352,13 +350,14 @@ const getEnergyInfoAndCO2Signal = async (
let co2SignalEntity: string | undefined; let co2SignalEntity: string | undefined;
if (co2SignalConfigEntry) { if (co2SignalConfigEntry) {
for (const entity of Object.values(hass.entities)) { for (const entityId of Object.keys(hass.entities)) {
const entity = hass.entities[entityId];
if (entity.platform !== "co2signal") { if (entity.platform !== "co2signal") {
continue; continue;
} }
// The integration offers 2 entities. We want the % one. // The integration offers 2 entities. We want the % one.
const co2State = hass.states[entity.entity_id]; const co2State = hass.states[entityId];
if (!co2State || co2State.attributes.unit_of_measurement !== "%") { if (!co2State || co2State.attributes.unit_of_measurement !== "%") {
continue; continue;
} }
@@ -368,24 +367,6 @@ const getEnergyInfoAndCO2Signal = async (
} }
} }
return <EnergyInfoAndCO2Signal>{
energyInfo: info,
co2SignalEntity: co2SignalEntity,
co2SignalConfigEntry: co2SignalConfigEntry,
};
};
const getEnergyDataWithInfo = async (
hass: HomeAssistant,
energyInfoAndCO2Signal: EnergyInfoAndCO2Signal,
prefs: EnergyPreferences,
start: Date,
end?: Date,
compare?: boolean
): Promise<EnergyData> => {
const info = energyInfoAndCO2Signal.energyInfo;
const co2SignalEntity = energyInfoAndCO2Signal.co2SignalEntity;
const co2SignalConfigEntry = energyInfoAndCO2Signal.co2SignalConfigEntry;
const consumptionStatIDs: string[] = []; const consumptionStatIDs: string[] = [];
for (const source of prefs.energy_sources) { for (const source of prefs.energy_sources) {
// grid source // grid source
@@ -421,7 +402,7 @@ const getEnergyDataWithInfo = async (
volume: lengthUnit === "km" ? "L" : "gal", volume: lengthUnit === "km" ? "L" : "gal",
}; };
const _energyStats: Statistics | Promise<Statistics> = energyStatIds.length const _energyStats = energyStatIds.length
? fetchStatistics( ? fetchStatistics(
hass!, hass!,
startMinHour, startMinHour,
@@ -431,9 +412,9 @@ const getEnergyDataWithInfo = async (
energyUnits, energyUnits,
["sum"] ["sum"]
) )
: {}; : Promise.resolve({});
const _waterStats: Statistics | Promise<Statistics> = waterStatIds.length const _waterStats = waterStatIds.length
? fetchStatistics( ? await fetchStatistics(
hass!, hass!,
startMinHour, startMinHour,
end, end,
@@ -442,13 +423,13 @@ const getEnergyDataWithInfo = async (
waterUnits, waterUnits,
["sum"] ["sum"]
) )
: {}; : Promise.resolve({});
let statsCompare; let statsCompare;
let startCompare; let startCompare;
let endCompare; let endCompare;
let _energyStatsCompare: Statistics | Promise<Statistics> = {}; let _energyStatsCompare = Promise.resolve({});
let _waterStatsCompare: Statistics | Promise<Statistics> = {}; let _waterStatsCompare = Promise.resolve({});
if (compare) { if (compare) {
if (dayDifference > 27 && dayDifference < 32) { if (dayDifference > 27 && dayDifference < 32) {
@@ -484,10 +465,12 @@ const getEnergyDataWithInfo = async (
} }
} }
let _fossilEnergyConsumption: undefined | Promise<FossilEnergyConsumption>; let _fossilEnergyConsumption:
| Promise<undefined>
| Promise<FossilEnergyConsumption> = Promise.resolve(undefined);
let _fossilEnergyConsumptionCompare: let _fossilEnergyConsumptionCompare:
| undefined | Promise<undefined>
| Promise<FossilEnergyConsumption>; | Promise<FossilEnergyConsumption> = Promise.resolve(undefined);
if (co2SignalEntity !== undefined) { if (co2SignalEntity !== undefined) {
_fossilEnergyConsumption = getFossilEnergyConsumption( _fossilEnergyConsumption = getFossilEnergyConsumption(
hass!, hass!,
@@ -510,11 +493,9 @@ const getEnergyDataWithInfo = async (
} }
const statsMetadata: Record<string, StatisticsMetaData> = {}; const statsMetadata: Record<string, StatisticsMetaData> = {};
const _getStatisticMetadata: const _getStatisticMetadata: Promise<StatisticsMetaData[]> = allStatIDs.length
| Promise<StatisticsMetaData[]>
| StatisticsMetaData[] = allStatIDs.length
? getStatisticMetadata(hass, allStatIDs) ? getStatisticMetadata(hass, allStatIDs)
: []; : Promise.resolve([]);
const [ const [
energyStats, energyStats,
waterStats, waterStats,
@@ -615,9 +596,6 @@ export const getEnergyDataCollection = (
energyCollectionKeys.push(options.key); energyCollectionKeys.push(options.key);
let energyInfoAndCO2Signal: EnergyInfoAndCO2Signal | undefined;
let forceRefreshEnergyInfo = false;
const collection = getCollection<EnergyData>( const collection = getCollection<EnergyData>(
hass.connection, hass.connection,
key, key,
@@ -645,20 +623,14 @@ export const getEnergyDataCollection = (
} }
nextFetch.setMinutes(20, 0, 0); nextFetch.setMinutes(20, 0, 0);
collection._refreshTimeout = window.setTimeout(() => { collection._refreshTimeout = window.setTimeout(
forceRefreshEnergyInfo = true; () => collection.refresh(),
collection.refresh(); nextFetch.getTime() - Date.now()
}, nextFetch.getTime() - Date.now()); );
} }
if (!energyInfoAndCO2Signal || forceRefreshEnergyInfo) { return getEnergyData(
energyInfoAndCO2Signal = await getEnergyInfoAndCO2Signal(hass);
forceRefreshEnergyInfo = false;
}
return getEnergyDataWithInfo(
hass, hass,
energyInfoAndCO2Signal,
collection.prefs, collection.prefs,
collection.start, collection.start,
collection.end, collection.end,

View File

@@ -10,13 +10,13 @@ export interface ThreadRouter {
} }
export interface ThreadDataSet { export interface ThreadDataSet {
created: string; created;
dataset_id: string; dataset_id;
preferred: boolean; extended_pan_id;
source: string;
network_name: string; network_name: string;
extended_pan_id?: string; pan_id;
pan_id?: string; preferred: boolean;
source;
} }
export interface ThreadRouterDiscoveryEvent { export interface ThreadRouterDiscoveryEvent {
@@ -61,29 +61,3 @@ export const listThreadDataSets = (
hass.callWS({ hass.callWS({
type: "thread/list_datasets", type: "thread/list_datasets",
}); });
export const getThreadDataSetTLV = (
hass: HomeAssistant,
dataset_id: string
): Promise<{ tlv: string }> =>
hass.callWS({ type: "thread/get_dataset_tlv", dataset_id });
export const addThreadDataSet = (
hass: HomeAssistant,
source: string,
tlv: string
): Promise<void> =>
hass.callWS({
type: "thread/add_dataset_tlv",
source,
tlv,
});
export const removeThreadDataSet = (
hass: HomeAssistant,
dataset_id: string
): Promise<void> =>
hass.callWS({
type: "thread/delete_dataset",
dataset_id,
});

View File

@@ -7,7 +7,6 @@ import { fireEvent } from "../../common/dom/fire_event";
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
import "../../components/ha-circular-progress"; import "../../components/ha-circular-progress";
import { createCloseHeading } from "../../components/ha-dialog"; import { createCloseHeading } from "../../components/ha-dialog";
import "../../components/ha-expansion-panel";
import "../../components/ha-list-item"; import "../../components/ha-list-item";
import { import {
extractApiErrorMessage, extractApiErrorMessage,
@@ -107,7 +106,6 @@ class DialogRestart extends LitElement {
<ha-list-item <ha-list-item
graphic="avatar" graphic="avatar"
twoline twoline
multiline-secondary
hasMeta hasMeta
@request-selected=${this._reload} @request-selected=${this._reload}
> >
@@ -130,7 +128,6 @@ class DialogRestart extends LitElement {
<ha-list-item <ha-list-item
graphic="avatar" graphic="avatar"
twoline twoline
multiline-secondary
hasMeta hasMeta
@request-selected=${this._restart} @request-selected=${this._restart}
> >
@@ -146,20 +143,17 @@ class DialogRestart extends LitElement {
)} )}
</span> </span>
</ha-list-item> </ha-list-item>
</mwc-list>
${showRebootShutdown ${showRebootShutdown
? html` ? html`
<ha-expansion-panel <div class="divider"></div>
.header=${this.hass.localize( <p class="section">
${this.hass.localize(
"ui.dialogs.restart.advanced_options" "ui.dialogs.restart.advanced_options"
)} )}
> </p>
<mwc-list>
<ha-list-item <ha-list-item
graphic="avatar" graphic="avatar"
twoline twoline
multiline-secondary
hasMeta hasMeta
@request-selected=${this._hostReboot} @request-selected=${this._hostReboot}
> >
@@ -180,7 +174,6 @@ class DialogRestart extends LitElement {
<ha-list-item <ha-list-item
graphic="avatar" graphic="avatar"
twoline twoline
multiline-secondary
hasMeta hasMeta
@request-selected=${this._hostShutdown} @request-selected=${this._hostShutdown}
> >
@@ -198,10 +191,9 @@ class DialogRestart extends LitElement {
)} )}
</span> </span>
</ha-list-item> </ha-list-item>
</mwc-list>
</ha-expansion-panel>
` `
: null} : null}
</mwc-list>
`} `}
</ha-dialog> </ha-dialog>
`; `;
@@ -348,23 +340,6 @@ class DialogRestart extends LitElement {
ha-dialog { ha-dialog {
--dialog-content-padding: 0; --dialog-content-padding: 0;
} }
@media all and (min-width: 550px) {
ha-dialog {
--mdc-dialog-min-width: 500px;
--mdc-dialog-max-width: 500px;
}
}
ha-expansion-panel {
border-top: 1px solid var(--divider-color);
margin-bottom: 10px;
box-shadow: none;
--expansion-panel-content-padding: 0;
--expansion-panel-summary-padding: 0
var(--mdc-list-side-padding, 20px);
--ha-card-border-radius: 0;
}
.icon-background { .icon-background {
border-radius: 50%; border-radius: 50%;
color: #fff; color: #fff;

View File

@@ -288,8 +288,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
} }
private precisionLabel(precision?: number, stateValue?: string) { private precisionLabel(precision?: number, stateValue?: string) {
const stateValueNumber = Number(stateValue); const value = stateValue ?? 0;
const value = !isNaN(stateValueNumber) ? stateValueNumber : 0;
return formatNumber(value, this.hass.locale, { return formatNumber(value, this.hass.locale, {
minimumFractionDigits: precision, minimumFractionDigits: precision,
maximumFractionDigits: precision, maximumFractionDigits: precision,

View File

@@ -278,7 +278,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
` `
: ""} : ""}
<div class="content"> <div class="content">
${boardName || isComponentLoaded(this.hass, "hassio") ${boardName
? html` ? html`
<ha-card outlined> <ha-card outlined>
<div class="card-content"> <div class="card-content">
@@ -293,9 +293,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
: ""} : ""}
<span class="primary-text"> <span class="primary-text">
${boardName || ${boardName ||
this.hass.localize( this.hass.localize("ui.panel.config.hardware.board")}
"ui.panel.config.hardware.generic_hardware"
)}
</span> </span>
${boardId ${boardId
? html` ? html`

View File

@@ -1,10 +1,5 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { import { mdiDevices, mdiDotsVertical, mdiInformationOutline } from "@mdi/js";
mdiDeleteOutline,
mdiDevices,
mdiDotsVertical,
mdiInformationOutline,
} from "@mdi/js";
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
@@ -16,19 +11,13 @@ import { getSignedPath } from "../../../../../data/auth";
import { getConfigEntryDiagnosticsDownloadUrl } from "../../../../../data/diagnostics"; import { getConfigEntryDiagnosticsDownloadUrl } from "../../../../../data/diagnostics";
import { getOTBRInfo } from "../../../../../data/otbr"; import { getOTBRInfo } from "../../../../../data/otbr";
import { import {
addThreadDataSet,
listThreadDataSets, listThreadDataSets,
removeThreadDataSet,
subscribeDiscoverThreadRouters, subscribeDiscoverThreadRouters,
ThreadDataSet, ThreadDataSet,
ThreadRouter, ThreadRouter,
} from "../../../../../data/thread"; } from "../../../../../data/thread";
import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow"; import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow";
import { import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
showAlertDialog,
showConfirmationDialog,
showPromptDialog,
} from "../../../../../dialogs/generic/show-dialog-box";
import "../../../../../layouts/hass-subpage"; import "../../../../../layouts/hass-subpage";
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
import { haStyle } from "../../../../../resources/styles"; import { haStyle } from "../../../../../resources/styles";
@@ -77,11 +66,6 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
)} )}
</mwc-list-item> </mwc-list-item>
</a> </a>
<mwc-list-item @click=${this._addTLV}
>${this.hass.localize(
"ui.panel.config.thread.add_dataset_from_tlv"
)}</mwc-list-item
>
<mwc-list-item @click=${this._addOTBR} <mwc-list-item @click=${this._addOTBR}
>${this.hass.localize( >${this.hass.localize(
"ui.panel.config.thread.add_open_thread_border_router" "ui.panel.config.thread.add_open_thread_border_router"
@@ -124,21 +108,12 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
return html`<ha-card> return html`<ha-card>
<div class="card-header"> <div class="card-header">
${network.name}${network.dataset ${network.name}${network.dataset
? html`<div> ? html`<ha-icon-button
<ha-icon-button
.networkDataset=${network.dataset} .networkDataset=${network.dataset}
.path=${mdiInformationOutline} .path=${mdiInformationOutline}
@click=${this._showDatasetInfo} @click=${this._showDatasetInfo}
></ha-icon-button
>${!network.dataset.preferred && !network.routers?.length
? html`<ha-icon-button
.networkDataset=${network.dataset}
.path=${mdiDeleteOutline}
@click=${this._removeDataset}
></ha-icon-button>` ></ha-icon-button>`
: ""} : ""}
</div>`
: ""}
</div> </div>
${network.routers?.length ${network.routers?.length
? html`<div class="card-content routers"> ? html`<div class="card-content routers">
@@ -179,10 +154,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
const dataset = (ev.currentTarget as any).networkDataset as ThreadDataSet; const dataset = (ev.currentTarget as any).networkDataset as ThreadDataSet;
if (isComponentLoaded(this.hass, "otbr")) { if (isComponentLoaded(this.hass, "otbr")) {
const otbrInfo = await getOTBRInfo(this.hass); const otbrInfo = await getOTBRInfo(this.hass);
if ( if (otbrInfo.active_dataset_tlvs.includes(dataset.extended_pan_id)) {
dataset.extended_pan_id &&
otbrInfo.active_dataset_tlvs?.includes(dataset.extended_pan_id)
) {
showAlertDialog(this, { showAlertDialog(this, {
title: dataset.network_name, title: dataset.network_name,
text: html`Network name: ${dataset.network_name}<br /> text: html`Network name: ${dataset.network_name}<br />
@@ -295,57 +267,6 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
}); });
} }
private async _addTLV() {
const tlv = await showPromptDialog(this, {
title: this.hass.localize("ui.panel.config.thread.add_dataset"),
inputLabel: this.hass.localize(
"ui.panel.config.thread.add_dataset_label"
),
confirmText: this.hass.localize(
"ui.panel.config.thread.add_dataset_button"
),
});
if (!tlv) {
return;
}
try {
await addThreadDataSet(this.hass, "manual", tlv);
} catch (err: any) {
showAlertDialog(this, {
title: "Error",
text: err.message || err,
});
}
this._refresh();
}
private async _removeDataset(ev: Event) {
const dataset = (ev.currentTarget as any).networkDataset as ThreadDataSet;
const confirm = await showConfirmationDialog(this, {
title: this.hass.localize(
"ui.panel.config.thread.confirm_delete_dataset",
{ name: dataset.network_name }
),
text: this.hass.localize(
"ui.panel.config.thread.confirm_delete_dataset_text"
),
destructive: true,
confirmText: this.hass.localize("ui.common.delete"),
});
if (!confirm) {
return;
}
try {
await removeThreadDataSet(this.hass, dataset.dataset_id);
} catch (err: any) {
showAlertDialog(this, {
title: "Error",
text: err.message || err,
});
}
this._refresh();
}
static styles = [ static styles = [
haStyle, haStyle,
css` css`

View File

@@ -72,7 +72,7 @@ class DialogZHAReconfigureDevice extends LitElement {
this.hass, this.hass,
this.hass.localize(`ui.dialogs.zha_reconfigure_device.heading`) + this.hass.localize(`ui.dialogs.zha_reconfigure_device.heading`) +
": " + ": " +
(this._params.device.user_given_name || this._params.device.name) (this._params?.device.user_given_name || this._params?.device.name)
)} )}
> >
${!this._status ${!this._status

View File

@@ -183,7 +183,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
// new format // new format
let segments = this._config!.segments; let segments = this._config!.segments;
if (segments) { if (segments) {
segments = [...segments].sort((a, b) => a.from - b.from); segments = [...segments].sort((a, b) => a?.from - b?.from);
for (let i = 0; i < segments.length; i++) { for (let i = 0; i < segments.length; i++) {
const segment = segments[i]; const segment = segments[i];

View File

@@ -1038,9 +1038,9 @@
"restart": { "restart": {
"title": "Restart Home Assistant", "title": "Restart Home Assistant",
"description": "Interrupts all running automations and scripts.", "description": "Interrupts all running automations and scripts.",
"confirm_title": "Restart Home Assistant?", "confirm_title": "Restart?",
"confirm_description": "This will interrupt all running automations and scripts.", "confirm_description": "This will interrupt all running automations and scripts.",
"confirm_action": "Restart", "confirm_action": "Restart Home Assistant",
"failed": "Failed to restart Home Assistant" "failed": "Failed to restart Home Assistant"
}, },
"reboot": { "reboot": {
@@ -1801,7 +1801,7 @@
"reboot_moved_link": "Go to system page.", "reboot_moved_link": "Go to system page.",
"processor": "Processor", "processor": "Processor",
"memory": "Memory", "memory": "Memory",
"generic_hardware": "Generic Hardware", "board": "Board",
"documentation": "Documentation", "documentation": "Documentation",
"configure": "Configure", "configure": "Configure",
"documentation_description": "Find extra information about your device", "documentation_description": "Find extra information about your device",
@@ -3288,12 +3288,6 @@
"my_network": "My network", "my_network": "My network",
"no_preferred_network": "You don't have a preferred network yet.", "no_preferred_network": "You don't have a preferred network yet.",
"add_open_thread_border_router": "Add an OpenThread border router", "add_open_thread_border_router": "Add an OpenThread border router",
"add_dataset_from_tlv": "Add dataset from TLV",
"add_dataset": "Add Thread dataset",
"add_dataset_label": "Operational dataset TLV",
"add_dataset_button": "Add dataset",
"confirm_delete_dataset": "Delete {name} dataset?",
"confirm_delete_dataset_text": "This network will be removed from Home Assistant.",
"no_border_routers": "No border routers found", "no_border_routers": "No border routers found",
"border_routers": "{count} border {count, plural,\n one {router}\n other {routers}\n}", "border_routers": "{count} border {count, plural,\n one {router}\n other {routers}\n}",
"managed_by_home_assistant": "Managed by Home Assistant", "managed_by_home_assistant": "Managed by Home Assistant",

View File

@@ -19,7 +19,6 @@ module.exports = {
"**/*.json": "js", "**/*.json": "js",
"**/*.css": "js", "**/*.css": "js",
}, },
compress: true,
middleware: [cors()], middleware: [cors()],
plugins: rollupWDSPlugins, plugins: rollupWDSPlugins,
}; };

View File

@@ -1457,9 +1457,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@eslint/eslintrc@npm:^2.0.0": "@eslint/eslintrc@npm:^1.4.1":
version: 2.0.0 version: 1.4.1
resolution: "@eslint/eslintrc@npm:2.0.0" resolution: "@eslint/eslintrc@npm:1.4.1"
dependencies: dependencies:
ajv: ^6.12.4 ajv: ^6.12.4
debug: ^4.3.2 debug: ^4.3.2
@@ -1470,14 +1470,7 @@ __metadata:
js-yaml: ^4.1.0 js-yaml: ^4.1.0
minimatch: ^3.1.2 minimatch: ^3.1.2
strip-json-comments: ^3.1.1 strip-json-comments: ^3.1.1
checksum: 31119c8ca06723d80384f18f5c78e0530d8e6306ad36379868650131a8b10dd7cffd7aff79a5deb3a2e9933660823052623d268532bae9538ded53d5b19a69a6 checksum: cd3e5a8683db604739938b1c1c8b77927dc04fce3e28e0c88e7f2cd4900b89466baf83dfbad76b2b9e4d2746abdd00dd3f9da544d3e311633d8693f327d04cd7
languageName: node
linkType: hard
"@eslint/js@npm:8.35.0":
version: 8.35.0
resolution: "@eslint/js@npm:8.35.0"
checksum: 6687ceff659a6d617e37823f809dc9c4b096535961a81acead27d26b1a51a4cf608a5e59d831ddd57f24f6f8bb99340a4a0e19f9c99b390fbb4b275f51ed5f5e
languageName: node languageName: node
linkType: hard linkType: hard
@@ -6860,18 +6853,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"compression-webpack-plugin@npm:^10.0.0":
version: 10.0.0
resolution: "compression-webpack-plugin@npm:10.0.0"
dependencies:
schema-utils: ^4.0.0
serialize-javascript: ^6.0.0
peerDependencies:
webpack: ^5.1.0
checksum: 2ac9079b7ab87141639c62ddbb2820a06f105198e27ef4c3860da3186bdbefc00d1e206969833ce7a4b7b26161ddbec7b8d20d30f9f9c1d494818b9b86f0d5cc
languageName: node
linkType: hard
"compression@npm:1.7.3": "compression@npm:1.7.3":
version: 1.7.3 version: 1.7.3
resolution: "compression@npm:1.7.3" resolution: "compression@npm:1.7.3"
@@ -8092,12 +8073,11 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"eslint@npm:^8.35.0": "eslint@npm:^8.34.0":
version: 8.35.0 version: 8.34.0
resolution: "eslint@npm:8.35.0" resolution: "eslint@npm:8.34.0"
dependencies: dependencies:
"@eslint/eslintrc": ^2.0.0 "@eslint/eslintrc": ^1.4.1
"@eslint/js": 8.35.0
"@humanwhocodes/config-array": ^0.11.8 "@humanwhocodes/config-array": ^0.11.8
"@humanwhocodes/module-importer": ^1.0.1 "@humanwhocodes/module-importer": ^1.0.1
"@nodelib/fs.walk": ^1.2.8 "@nodelib/fs.walk": ^1.2.8
@@ -8111,7 +8091,7 @@ __metadata:
eslint-utils: ^3.0.0 eslint-utils: ^3.0.0
eslint-visitor-keys: ^3.3.0 eslint-visitor-keys: ^3.3.0
espree: ^9.4.0 espree: ^9.4.0
esquery: ^1.4.2 esquery: ^1.4.0
esutils: ^2.0.2 esutils: ^2.0.2
fast-deep-equal: ^3.1.3 fast-deep-equal: ^3.1.3
file-entry-cache: ^6.0.1 file-entry-cache: ^6.0.1
@@ -8138,7 +8118,7 @@ __metadata:
text-table: ^0.2.0 text-table: ^0.2.0
bin: bin:
eslint: bin/eslint.js eslint: bin/eslint.js
checksum: 6212173691d90b1bc94dd3d640e1f210374b30c3905fc0a15e501cf71c6ca52aa3d80ea7a9a245adaaed26d6019169e01fb6881b3f2885b188d37069c749308c checksum: 4e13e9eb05ac2248efbb6acae0b2325091235d5c47ba91a4775c7d6760778cbcd358a773ebd42f4629d2ad89e27c02f5d66eb1f737d75d9f5fc411454f83b2e5
languageName: node languageName: node
linkType: hard linkType: hard
@@ -8163,12 +8143,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"esquery@npm:^1.4.2": "esquery@npm:^1.4.0":
version: 1.4.2 version: 1.4.0
resolution: "esquery@npm:1.4.2" resolution: "esquery@npm:1.4.0"
dependencies: dependencies:
estraverse: ^5.1.0 estraverse: ^5.1.0
checksum: 2f4ad89c5aafaca61cc2c15e256190f0d6deb4791cae6552d3cb4b1eb8867958cdf27a56aaa3272ff17435e3eaa19ee0d4129fac336ca6373d7354d7b5da7966 checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210
languageName: node languageName: node
linkType: hard linkType: hard
@@ -9638,7 +9618,6 @@ fsevents@~2.3.2:
chai: ^4.3.7 chai: ^4.3.7
chart.js: ^3.3.2 chart.js: ^3.3.2
comlink: ^4.4.1 comlink: ^4.4.1
compression-webpack-plugin: ^10.0.0
core-js: ^3.28.0 core-js: ^3.28.0
cropperjs: ^1.5.13 cropperjs: ^1.5.13
date-fns: ^2.29.3 date-fns: ^2.29.3
@@ -9646,7 +9625,7 @@ fsevents@~2.3.2:
deep-clone-simple: ^1.1.1 deep-clone-simple: ^1.1.1
deep-freeze: ^0.0.1 deep-freeze: ^0.0.1
del: ^7.0.0 del: ^7.0.0
eslint: ^8.35.0 eslint: ^8.34.0
eslint-config-airbnb-base: ^15.0.0 eslint-config-airbnb-base: ^15.0.0
eslint-config-airbnb-typescript: ^17.0.0 eslint-config-airbnb-typescript: ^17.0.0
eslint-config-prettier: ^8.6.0 eslint-config-prettier: ^8.6.0
@@ -9713,7 +9692,7 @@ fsevents@~2.3.2:
sortablejs: ^1.15.0 sortablejs: ^1.15.0
source-map-url: ^0.4.1 source-map-url: ^0.4.1
superstruct: ^1.0.3 superstruct: ^1.0.3
systemjs: ^6.14.0 systemjs: ^6.13.0
tar: ^6.1.13 tar: ^6.1.13
terser-webpack-plugin: ^5.3.6 terser-webpack-plugin: ^5.3.6
tinykeys: ^1.4.0 tinykeys: ^1.4.0
@@ -9725,7 +9704,7 @@ fsevents@~2.3.2:
vinyl-buffer: ^1.0.1 vinyl-buffer: ^1.0.1
vinyl-source-stream: ^2.0.0 vinyl-source-stream: ^2.0.0
vis-data: ^7.1.4 vis-data: ^7.1.4
vis-network: ^9.1.4 vis-network: ^9.1.2
vue: ^2.7.14 vue: ^2.7.14
vue2-daterange-picker: ^0.6.8 vue2-daterange-picker: ^0.6.8
webpack: =5.72.1 webpack: =5.72.1
@@ -9733,7 +9712,7 @@ fsevents@~2.3.2:
webpack-dev-server: ^4.11.1 webpack-dev-server: ^4.11.1
webpack-manifest-plugin: ^5.0.0 webpack-manifest-plugin: ^5.0.0
webpackbar: ^5.0.2 webpackbar: ^5.0.2
weekstart: ^2.0.0 weekstart: ^1.1.0
workbox-build: ^6.5.4 workbox-build: ^6.5.4
workbox-cacheable-response: ^6.5.4 workbox-cacheable-response: ^6.5.4
workbox-core: ^6.5.4 workbox-core: ^6.5.4
@@ -15069,10 +15048,10 @@ fsevents@~2.3.2:
languageName: node languageName: node
linkType: hard linkType: hard
"systemjs@npm:^6.14.0": "systemjs@npm:^6.13.0":
version: 6.14.0 version: 6.13.0
resolution: "systemjs@npm:6.14.0" resolution: "systemjs@npm:6.13.0"
checksum: df82c38a5f23012dfee693e97dacfc48eef787a80cc41bc3bd594567ba9441c6392978841c6555d88df7451a2e22c8df9fcbf9c88c78f9f9b9027393ac38d84b checksum: df8d7374249778291f3a85278fdb3e1b9d81ac07767b0a7f9edeca0ee45d847c19bceb01522c817605e2908d32a4fcfed6bacd707bfb7bd577774ab900d3707d
languageName: node languageName: node
linkType: hard linkType: hard
@@ -16086,18 +16065,18 @@ typescript@^3.8.3:
languageName: node languageName: node
linkType: hard linkType: hard
"vis-network@npm:^9.1.4": "vis-network@npm:^9.1.2":
version: 9.1.4 version: 9.1.2
resolution: "vis-network@npm:9.1.4" resolution: "vis-network@npm:9.1.2"
peerDependencies: peerDependencies:
"@egjs/hammerjs": ^2.0.0 "@egjs/hammerjs": ^2.0.0
component-emitter: ^1.3.0 component-emitter: ^1.3.0
keycharm: ^0.2.0 || ^0.3.0 || ^0.4.0 keycharm: ^0.2.0 || ^0.3.0 || ^0.4.0
timsort: ^0.3.0 timsort: ^0.3.0
uuid: ^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 uuid: ^3.4.0 || ^7.0.0 || ^8.0.0
vis-data: ^7.0.0 vis-data: ^7.0.0
vis-util: ^5.0.1 vis-util: ^5.0.1
checksum: 1c3ce02d251bf04ce3284301d597ee2630ad2a76de6c168c78df4dccf8ec4f4d058d67a1d78d507b763ff909d04d0d1afadcb3c3daf664e5090720b25a00aa2f checksum: 763bee104c9d69f6b229d10675f712f824568f28dd13540d35d0f1f1fc58fcb9dc27cc12da80f6e874076a7cb75793d168b49c3c7783f8cb06f57931d9edfe0e
languageName: node languageName: node
linkType: hard linkType: hard
@@ -16440,10 +16419,10 @@ typescript@^3.8.3:
languageName: node languageName: node
linkType: hard linkType: hard
"weekstart@npm:^2.0.0": "weekstart@npm:^1.1.0":
version: 2.0.0 version: 1.1.0
resolution: "weekstart@npm:2.0.0" resolution: "weekstart@npm:1.1.0"
checksum: 9a27d7fe30d847997d50006c814e68ad9b105858a43029b2312bf5757dd49839865a64ad28345d2dcb1b9f25b72f0de81e23d8db50362890b253486defff69e6 checksum: afce96e0b95809a30f00fa02b13a0927324d9f76b9c10ce6b3de9bbd5926615156f8a0526c63e2bd1cabdc8ec3da68b8df8d6608b6364ded11b5da300a8cfcb4
languageName: node languageName: node
linkType: hard linkType: hard