diff --git a/package.json b/package.json index b9628ec2a2..1b7cb54886 100644 --- a/package.json +++ b/package.json @@ -43,24 +43,24 @@ "@fullcalendar/interaction": "5.1.0", "@fullcalendar/list": "5.1.0", "@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.6.0#./.yarn/patches/@lit-labs/virtualizer/0.7.0.patch", - "@material/chips": "=12.0.0-canary.1a8d06483.0", - "@material/data-table": "=12.0.0-canary.1a8d06483.0", - "@material/mwc-button": "0.22.0-canary.cc04657a.0", - "@material/mwc-checkbox": "0.22.0-canary.cc04657a.0", - "@material/mwc-circular-progress": "0.22.0-canary.cc04657a.0", - "@material/mwc-dialog": "0.22.0-canary.cc04657a.0", - "@material/mwc-fab": "0.22.0-canary.cc04657a.0", - "@material/mwc-formfield": "0.22.0-canary.cc04657a.0", - "@material/mwc-icon-button": "0.22.0-canary.cc04657a.0", - "@material/mwc-linear-progress": "0.22.0-canary.cc04657a.0", - "@material/mwc-list": "0.22.0-canary.cc04657a.0", - "@material/mwc-menu": "0.22.0-canary.cc04657a.0", - "@material/mwc-radio": "0.22.0-canary.cc04657a.0", - "@material/mwc-ripple": "0.22.0-canary.cc04657a.0", - "@material/mwc-switch": "0.22.0-canary.cc04657a.0", - "@material/mwc-tab": "0.22.0-canary.cc04657a.0", - "@material/mwc-tab-bar": "0.22.0-canary.cc04657a.0", - "@material/top-app-bar": "=12.0.0-canary.1a8d06483.0", + "@material/chips": "12.0.0-canary.22d29cbb4.0", + "@material/data-table": "12.0.0-canary.22d29cbb4.0", + "@material/mwc-button": "0.22.1", + "@material/mwc-checkbox": "0.22.1", + "@material/mwc-circular-progress": "0.22.1", + "@material/mwc-dialog": "0.22.1", + "@material/mwc-fab": "0.22.1", + "@material/mwc-formfield": "0.22.1", + "@material/mwc-icon-button": "0.22.1", + "@material/mwc-linear-progress": "0.22.1", + "@material/mwc-list": "0.22.1", + "@material/mwc-menu": "0.22.1", + "@material/mwc-radio": "0.22.1", + "@material/mwc-ripple": "0.22.1", + "@material/mwc-switch": "0.22.1", + "@material/mwc-tab": "0.22.1", + "@material/mwc-tab-bar": "0.22.1", + "@material/top-app-bar": "12.0.0-canary.22d29cbb4.0", "@mdi/js": "5.9.55", "@mdi/svg": "5.9.55", "@polymer/app-layout": "^3.1.0", @@ -89,8 +89,8 @@ "@polymer/paper-tooltip": "^3.0.1", "@polymer/polymer": "3.4.1", "@thomasloven/round-slider": "0.5.2", - "@vaadin/vaadin-combo-box": "^5.0.10", - "@vaadin/vaadin-date-picker": "^4.0.7", + "@vaadin/vaadin-combo-box": "^20.0.1", + "@vaadin/vaadin-date-picker": "^20.0.1", "@vibrant/color": "^3.2.1-alpha.1", "@vibrant/core": "^3.2.1-alpha.1", "@vibrant/quantizer-mmcq": "^3.2.1-alpha.1", @@ -107,7 +107,7 @@ "fuse.js": "^6.0.0", "google-timezones-json": "^1.0.2", "hls.js": "^1.0.7", - "home-assistant-js-websocket": "^5.10.0", + "home-assistant-js-websocket": "^5.11.1", "idb-keyval": "^5.0.5", "intl-messageformat": "^9.6.16", "js-yaml": "^4.1.0", diff --git a/setup.py b/setup.py index 87b564b33c..758a5198f2 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20210726.0", + version="20210730.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/frontend", author="The Home Assistant Authors", diff --git a/src/common/const.ts b/src/common/const.ts index 2bd338c895..dd4eace281 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -63,6 +63,7 @@ export const FIXED_DEVICE_CLASS_ICONS = { humidity: "hass:water-percent", illuminance: "hass:brightness-5", temperature: "hass:thermometer", + monetary: "mdi:cash", pressure: "hass:gauge", power: "hass:flash", power_factor: "hass:angle-acute", diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts index c5eb00c2d7..cb31f5ec10 100644 --- a/src/common/entity/compute_state_display.ts +++ b/src/common/entity/compute_state_display.ts @@ -21,6 +21,16 @@ export const computeStateDisplay = ( } if (stateObj.attributes.unit_of_measurement) { + if (stateObj.attributes.device_class === "monetary") { + try { + return formatNumber(compareState, locale, { + style: "currency", + currency: stateObj.attributes.unit_of_measurement, + }); + } catch (_err) { + // fallback to default + } + } return `${formatNumber(compareState, locale)} ${ stateObj.attributes.unit_of_measurement }`; diff --git a/src/common/string/format_number.ts b/src/common/string/format_number.ts index 2cfa22458b..01279cb715 100644 --- a/src/common/string/format_number.ts +++ b/src/common/string/format_number.ts @@ -1,4 +1,22 @@ import { FrontendLocaleData, NumberFormat } from "../../data/translation"; +import { round } from "../number/round"; + +export const numberFormatToLocale = ( + localeOptions: FrontendLocaleData +): string | string[] | undefined => { + switch (localeOptions.number_format) { + case NumberFormat.comma_decimal: + return ["en-US", "en"]; // Use United States with fallback to English formatting 1,234,567.89 + case NumberFormat.decimal_comma: + return ["de", "es", "it"]; // Use German with fallback to Spanish then Italian formatting 1.234.567,89 + case NumberFormat.space_comma: + return ["fr", "sv", "cs"]; // Use French with fallback to Swedish and Czech formatting 1 234 567,89 + case NumberFormat.system: + return undefined; + default: + return localeOptions.language; + } +}; /** * Formats a number based on the user's preference with thousands separator(s) and decimal character for better legibility. @@ -9,27 +27,12 @@ import { FrontendLocaleData, NumberFormat } from "../../data/translation"; */ export const formatNumber = ( num: string | number, - locale?: FrontendLocaleData, + localeOptions?: FrontendLocaleData, options?: Intl.NumberFormatOptions ): string => { - let format: string | string[] | undefined; - - switch (locale?.number_format) { - case NumberFormat.comma_decimal: - format = ["en-US", "en"]; // Use United States with fallback to English formatting 1,234,567.89 - break; - case NumberFormat.decimal_comma: - format = ["de", "es", "it"]; // Use German with fallback to Spanish then Italian formatting 1.234.567,89 - break; - case NumberFormat.space_comma: - format = ["fr", "sv", "cs"]; // Use French with fallback to Swedish and Czech formatting 1 234 567,89 - break; - case NumberFormat.system: - format = undefined; - break; - default: - format = locale?.language; - } + const locale = localeOptions + ? numberFormatToLocale(localeOptions) + : undefined; // Polyfill for Number.isNaN, which is more reliable than the global isNaN() Number.isNaN = @@ -39,13 +42,13 @@ export const formatNumber = ( }; if ( + localeOptions?.number_format !== NumberFormat.none && !Number.isNaN(Number(num)) && - Intl && - locale?.number_format !== NumberFormat.none + Intl ) { try { return new Intl.NumberFormat( - format, + locale, getDefaultFormatOptions(num, options) ).format(Number(num)); } catch (error) { @@ -58,7 +61,12 @@ export const formatNumber = ( ).format(Number(num)); } } - return num.toString(); + if (typeof num === "string") { + return num; + } + return `${round(num, options?.maximumFractionDigits).toString()}${ + options?.style === "currency" ? ` ${options.currency}` : "" + }`; }; /** diff --git a/src/components/buttons/ha-progress-button.ts b/src/components/buttons/ha-progress-button.ts index 259ee75310..ed6ec6ec86 100644 --- a/src/components/buttons/ha-progress-button.ts +++ b/src/components/buttons/ha-progress-button.ts @@ -5,7 +5,7 @@ import { customElement, property, query } from "lit/decorators"; import "../ha-circular-progress"; @customElement("ha-progress-button") -class HaProgressButton extends LitElement { +export class HaProgressButton extends LitElement { @property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public progress = false; diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 63b41aef70..2ff0550efa 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -55,7 +55,7 @@ export default class HaChartBase extends LitElement { this._setupChart(); return; } - if (changedProps.has("type")) { + if (changedProps.has("chartType")) { this.chart.config.type = this.chartType; } if (changedProps.has("data")) { @@ -136,12 +136,9 @@ export default class HaChartBase extends LitElement { )} - ${this._tooltip.footer - ? // footer has white-space: pre; - // prettier-ignore - html`` @@ -155,7 +152,17 @@ export default class HaChartBase extends LitElement { .querySelector("canvas")! .getContext("2d")!; - this.chart = new (await import("../../resources/chartjs")).Chart(ctx, { + const ChartConstructor = (await import("../../resources/chartjs")).Chart; + + const computedStyles = getComputedStyle(this); + + ChartConstructor.defaults.borderColor = + computedStyles.getPropertyValue("--divider-color"); + ChartConstructor.defaults.color = computedStyles.getPropertyValue( + "--secondary-text-color" + ); + + this.chart = new ChartConstructor(ctx, { type: this.chartType, data: this.data, options: this._createOptions(), @@ -275,7 +282,7 @@ export default class HaChartBase extends LitElement { border-radius: 50%; display: inline-block; height: 16px; - margin-right: 4px; + margin-right: 6px; width: 16px; flex-shrink: 0; box-sizing: border-box; @@ -283,9 +290,10 @@ export default class HaChartBase extends LitElement { .chartTooltip .bullet { align-self: baseline; } + :host([rtl]) .chartLegend .bullet, :host([rtl]) .chartTooltip .bullet { margin-right: inherit; - margin-left: 4px; + margin-left: 6px; } .chartTooltip { padding: 8px; @@ -317,6 +325,7 @@ export default class HaChartBase extends LitElement { white-space: pre-line; align-items: center; line-height: 16px; + padding: 4px 0; } .chartTooltip .title { text-align: center; @@ -324,7 +333,6 @@ export default class HaChartBase extends LitElement { } .chartTooltip .footer { font-weight: 500; - white-space: pre; } .chartTooltip .beforeBody { text-align: center; diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index 30e6dff80f..b2bddfba4f 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -2,6 +2,7 @@ import type { ChartData, ChartDataset, ChartOptions } from "chart.js"; import { html, LitElement, PropertyValues } from "lit"; import { property, state } from "lit/decorators"; import { getColorByIndex } from "../../common/color/colors"; +import { numberFormatToLocale } from "../../common/string/format_number"; import { LineChartEntity, LineChartState } from "../../data/history"; import { HomeAssistant } from "../../types"; import "./ha-chart-base"; @@ -109,6 +110,8 @@ class StateHistoryChartLine extends LitElement { hitRadius: 5, }, }, + // @ts-expect-error + locale: numberFormatToLocale(this.hass.locale), }; } if (changedProps.has("data")) { diff --git a/src/components/chart/state-history-chart-timeline.ts b/src/components/chart/state-history-chart-timeline.ts index 2ce6277871..db81411b6a 100644 --- a/src/components/chart/state-history-chart-timeline.ts +++ b/src/components/chart/state-history-chart-timeline.ts @@ -5,6 +5,7 @@ import { customElement, property, state } from "lit/decorators"; import { getColorByIndex } from "../../common/color/colors"; import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time"; import { computeDomain } from "../../common/entity/compute_domain"; +import { numberFormatToLocale } from "../../common/string/format_number"; import { computeRTL } from "../../common/util/compute_rtl"; import { TimelineEntity } from "../../data/history"; import { HomeAssistant } from "../../types"; @@ -186,6 +187,8 @@ export class StateHistoryChartTimeline extends LitElement { propagate: true, }, }, + // @ts-expect-error + locale: numberFormatToLocale(this.hass.locale), }; } if (changedProps.has("data")) { diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index 13e5e92a58..68431696ba 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -16,6 +16,7 @@ import { customElement, property, state } from "lit/decorators"; import { getColorByIndex } from "../../common/color/colors"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { computeStateName } from "../../common/entity/compute_state_name"; +import { numberFormatToLocale } from "../../common/string/format_number"; import { Statistics, statisticsHaveType, @@ -37,8 +38,8 @@ class StatisticsChart extends LitElement { @property({ type: Array }) public statTypes: Array = [ "sum", "min", - "max", "mean", + "max", ]; @property() public chartType: ChartType = "line"; @@ -57,7 +58,7 @@ class StatisticsChart extends LitElement { if (!this.hasUpdated) { this._createOptions(); } - if (changedProps.has("statisticsData")) { + if (changedProps.has("statisticsData") || changedProps.has("statTypes")) { this._generateData(); } } @@ -119,7 +120,7 @@ class StatisticsChart extends LitElement { : {}, }, time: { - tooltipFormat: "datetimeseconds", + tooltipFormat: "datetime", }, }, y: { @@ -157,10 +158,15 @@ class StatisticsChart extends LitElement { hitRadius: 5, }, }, + // @ts-expect-error + locale: numberFormatToLocale(this.hass.locale), }; } private _generateData() { + if (!this.statisticsData) { + return; + } let colorIndex = 0; const statisticsData = Object.values(this.statisticsData); const totalDataSets: ChartDataset<"line">[] = []; @@ -228,21 +234,21 @@ class StatisticsChart extends LitElement { prevValues = dataValues; }; + const color = getColorByIndex(colorIndex); + colorIndex++; + const addDataSet = ( nameY: string, + borderColor: string, + backgroundColor: string, step = false, - fill = false, - color?: string + fill?: boolean | number | string ) => { - if (!color) { - color = getColorByIndex(colorIndex); - colorIndex++; - } statDataSets.push({ label: nameY, - fill: fill ? "origin" : false, - borderColor: color, - backgroundColor: color + "7F", + fill: fill || false, + borderColor, + backgroundColor: backgroundColor, stepped: step ? "before" : false, pointRadius: 0, data: [], @@ -251,26 +257,60 @@ class StatisticsChart extends LitElement { const statTypes: this["statTypes"] = []; - this.statTypes.forEach((type) => { + const sortedTypes = [...this.statTypes].sort((a, _b) => { + if (a === "min") { + return -1; + } + if (a === "max") { + return +1; + } + return 0; + }); + + const drawBands = + this.statTypes.includes("mean") && statisticsHaveType(stats, "mean"); + + sortedTypes.forEach((type) => { if (statisticsHaveType(stats, type)) { statTypes.push(type); addDataSet( `${name} (${this.hass.localize( `ui.components.statistics_charts.statistic_types.${type}` )})`, - false + drawBands && (type === "min" || type === "max") + ? color + "7F" + : color, + color + "7F", + false, + drawBands + ? type === "min" + ? "+1" + : type === "max" + ? "-1" + : false + : false ); } }); + let prevDate: Date | null = null; // Process chart data. stats.forEach((stat) => { + const date = new Date(stat.start); + if (prevDate === date) { + return; + } + prevDate = date; const dataValues: Array = []; statTypes.forEach((type) => { - const val = stat[type]; + let val: number | null; + if (type === "sum") { + val = stat.state; + } else { + val = stat[type]; + } dataValues.push(val !== null ? Math.round(val * 100) / 100 : null); }); - const date = new Date(stat.start); pushData(date, dataValues); }); diff --git a/src/components/currency-datalist.ts b/src/components/currency-datalist.ts new file mode 100644 index 0000000000..7133aa560b --- /dev/null +++ b/src/components/currency-datalist.ts @@ -0,0 +1,168 @@ +export const createCurrencyListEl = () => { + const list = document.createElement("datalist"); + list.id = "currencies"; + for (const currency of [ + "AED", + "AFN", + "ALL", + "AMD", + "ANG", + "AOA", + "ARS", + "AUD", + "AWG", + "AZN", + "BAM", + "BBD", + "BDT", + "BGN", + "BHD", + "BIF", + "BMD", + "BND", + "BOB", + "BRL", + "BSD", + "BTN", + "BWP", + "BYR", + "BZD", + "CAD", + "CDF", + "CHF", + "CLP", + "CNY", + "COP", + "CRC", + "CUP", + "CVE", + "CZK", + "DJF", + "DKK", + "DOP", + "DZD", + "EGP", + "ERN", + "ETB", + "EUR", + "FJD", + "FKP", + "GBP", + "GEL", + "GHS", + "GIP", + "GMD", + "GNF", + "GTQ", + "GYD", + "HKD", + "HNL", + "HRK", + "HTG", + "HUF", + "IDR", + "ILS", + "INR", + "IQD", + "IRR", + "ISK", + "JMD", + "JOD", + "JPY", + "KES", + "KGS", + "KHR", + "KMF", + "KPW", + "KRW", + "KWD", + "KYD", + "KZT", + "LAK", + "LBP", + "LKR", + "LRD", + "LSL", + "LTL", + "LYD", + "MAD", + "MDL", + "MGA", + "MKD", + "MMK", + "MNT", + "MOP", + "MRO", + "MUR", + "MVR", + "MWK", + "MXN", + "MYR", + "MZN", + "NAD", + "NGN", + "NIO", + "NOK", + "NPR", + "NZD", + "OMR", + "PAB", + "PEN", + "PGK", + "PHP", + "PKR", + "PLN", + "PYG", + "QAR", + "RON", + "RSD", + "RUB", + "RWF", + "SAR", + "SBD", + "SCR", + "SDG", + "SEK", + "SGD", + "SHP", + "SLL", + "SOS", + "SRD", + "SSP", + "STD", + "SYP", + "SZL", + "THB", + "TJS", + "TMT", + "TND", + "TOP", + "TRY", + "TTD", + "TWD", + "TZS", + "UAH", + "UGX", + "USD", + "UYU", + "UZS", + "VEF", + "VND", + "VUV", + "WST", + "XAF", + "XCD", + "XOF", + "XPF", + "YER", + "ZAR", + "ZMK", + "ZWL", + ]) { + const option = document.createElement("option"); + option.value = currency; + option.innerHTML = currency; + list.appendChild(option); + } + return list; +}; diff --git a/src/components/entity/ha-entities-picker.ts b/src/components/entity/ha-entities-picker.ts index fff8041e72..19c20886fa 100644 --- a/src/components/entity/ha-entities-picker.ts +++ b/src/components/entity/ha-entities-picker.ts @@ -131,6 +131,9 @@ class HaEntitiesPickerLight extends LitElement { private async _addEntity(event: PolymerChangedEvent) { event.stopPropagation(); const toAdd = event.detail.value; + if (!toAdd) { + return; + } (event.currentTarget as any).value = ""; if (!toAdd) { return; diff --git a/src/components/entity/ha-statistic-picker.ts b/src/components/entity/ha-statistic-picker.ts index 912188ea8d..aa09f92d25 100644 --- a/src/components/entity/ha-statistic-picker.ts +++ b/src/components/entity/ha-statistic-picker.ts @@ -22,46 +22,12 @@ import { compare } from "../../common/string/compare"; import { getStatisticIds, StatisticsMetaData } from "../../data/history"; import { PolymerChangedEvent } from "../../polymer-types"; import { HomeAssistant } from "../../types"; +import { documentationUrl } from "../../util/documentation-url"; import "../ha-combo-box"; import type { HaComboBox } from "../ha-combo-box"; import "../ha-svg-icon"; import "./state-badge"; -// vaadin-combo-box-item - -const rowRenderer: ComboBoxLitRenderer<{ - id: string; - name: string; - state?: HassEntity; -}> = (item) => html` - - - - - ${item.name} - ${item.id} - - `; - @customElement("ha-statistic-picker") export class HaStatisticPicker extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -99,6 +65,53 @@ export class HaStatisticPicker extends LitElement { private _init = false; + private _rowRenderer: ComboBoxLitRenderer<{ + id: string; + name: string; + state?: HassEntity; + }> = (item) => html` + + + + + ${item.name} + ${item.id === "" || item.id === "__missing" + ? html`${this.hass.localize( + "ui.components.statistic-picker.learn_more" + )}` + : item.id} + + `; + private _getStatistics = memoizeOne( ( statisticIds: StatisticsMetaData[], @@ -110,7 +123,7 @@ export class HaStatisticPicker extends LitElement { { id: "", name: this.hass.localize( - "ui.components.statistics-picker.no_statistics" + "ui.components.statistic-picker.no_statistics" ), }, ]; @@ -142,10 +155,27 @@ export class HaStatisticPicker extends LitElement { }); }); - if (output.length === 1) { - return output; + if (!output.length) { + return [ + { + id: "", + name: this.hass.localize("ui.components.statistic-picker.no_match"), + }, + ]; } - return output.sort((a, b) => compare(a.name || "", b.name || "")); + + if (output.length > 1) { + output.sort((a, b) => compare(a.name || "", b.name || "")); + } + + output.push({ + id: "__missing", + name: this.hass.localize( + "ui.components.statistic-picker.missing_entity" + ), + }); + + return output; } ); @@ -195,7 +225,7 @@ export class HaStatisticPicker extends LitElement { ? this.hass.localize("ui.components.statistic-picker.statistic") : this.label} .value=${this._value} - .renderer=${rowRenderer} + .renderer=${this._rowRenderer} .disabled=${this.disabled} item-value-path="id" item-id-path="id" @@ -216,7 +246,10 @@ export class HaStatisticPicker extends LitElement { private _statisticChanged(ev: PolymerChangedEvent) { ev.stopPropagation(); - const newValue = ev.detail.value; + let newValue = ev.detail.value; + if (newValue === "__missing") { + newValue = ""; + } if (newValue !== this._value) { this._setValue(newValue); diff --git a/src/components/entity/ha-statistics-picker.ts b/src/components/entity/ha-statistics-picker.ts index 3ad6af9203..22a6593ffd 100644 --- a/src/components/entity/ha-statistics-picker.ts +++ b/src/components/entity/ha-statistics-picker.ts @@ -90,6 +90,9 @@ class HaStatisticsPicker extends LitElement { private async _addStatistic(event: PolymerChangedEvent) { event.stopPropagation(); const toAdd = event.detail.value; + if (!toAdd) { + return; + } (event.currentTarget as any).value = ""; if (!toAdd) { return; diff --git a/src/components/ha-gauge.ts b/src/components/ha-gauge.ts index e5d785b435..25dbae2b5f 100644 --- a/src/components/ha-gauge.ts +++ b/src/components/ha-gauge.ts @@ -13,6 +13,11 @@ const getAngle = (value: number, min: number, max: number) => { return (percentage * 180) / 100; }; +export interface LevelDefinition { + level: number; + stroke: string; +} + @customElement("ha-gauge") export class Gauge extends LitElement { @property({ type: Number }) public min = 0; @@ -21,8 +26,14 @@ export class Gauge extends LitElement { @property({ type: Number }) public value = 0; + @property({ type: String }) public valueText?: string; + @property() public locale!: FrontendLocaleData; + @property({ type: Boolean }) public needle?: boolean; + + @property() public levels?: LevelDefinition[]; + @property() public label = ""; @state() private _angle = 0; @@ -51,22 +62,61 @@ export class Gauge extends LitElement { protected render() { return svg` - - + >` + : "" + } + + ${ + this.levels + ? this.levels + .sort((a, b) => a.level - b.level) + .map((level) => { + const angle = getAngle(level.level, this.min, this.max); + return svg``; + }) + : "" + } + ${ + this.needle + ? svg` + ` + : svg`` + } ${ // Workaround for https://github.com/home-assistant/frontend/issues/6467 isSafari @@ -83,7 +133,9 @@ export class Gauge extends LitElement { - ${formatNumber(this.value, this.locale)} ${this.label} + ${this.valueText || formatNumber(this.value, this.locale)} ${ + this.label + } `; } @@ -117,6 +169,15 @@ export class Gauge extends LitElement { transform-origin: 50% 100%; transition: all 1s ease 0s; } + .needle { + fill: var(--primary-text-color); + transform-origin: 50% 100%; + transition: all 1s ease 0s; + } + .level { + fill: none; + stroke-width: 15; + } .gauge { display: block; } diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 0a56b4df48..e234408f3e 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -53,9 +53,10 @@ const SHOW_AFTER_SPACER = ["config", "developer-tools", "hassio"]; const SUPPORT_SCROLL_IF_NEEDED = "scrollIntoViewIfNeeded" in document.body; const SORT_VALUE_URL_PATHS = { - map: 1, - logbook: 2, - history: 3, + energy: 1, + map: 2, + logbook: 3, + history: 4, "developer-tools": 9, hassio: 10, config: 11, diff --git a/src/data/core.ts b/src/data/core.ts index 667a90cd61..32006fbb7e 100644 --- a/src/data/core.ts +++ b/src/data/core.ts @@ -10,6 +10,7 @@ export interface ConfigUpdateValues { time_zone: string; external_url?: string | null; internal_url?: string | null; + currency?: string | null; } export interface CheckConfigResult { diff --git a/src/data/currency.ts b/src/data/currency.ts new file mode 100644 index 0000000000..d8ddf7933e --- /dev/null +++ b/src/data/currency.ts @@ -0,0 +1,8 @@ +export const SYMBOL_TO_ISO = { + $: "USD", + "€": "EUR", + "¥": "JPY", + "£": "GBP", + "₽": "RUB", + "₹": "INR", +}; diff --git a/src/data/energy.ts b/src/data/energy.ts index 4b8536424c..216bf566ff 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -85,7 +85,6 @@ type EnergySource = | GridSourceTypeEnergyPreference; export interface EnergyPreferences { - currency: string; energy_sources: EnergySource[]; device_consumption: DeviceConsumptionEnergyPreference[]; } diff --git a/src/data/history.ts b/src/data/history.ts index 71cc46e231..481ddf7792 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -302,12 +302,9 @@ export const fetchStatistics = ( export const calculateStatisticSumGrowth = ( values: StatisticValue[] ): number | null => { - if (values.length === 0) { + if (!values || values.length < 2) { return null; } - if (values.length === 1) { - return values[0].sum; - } const endSum = values[values.length - 1].sum; if (endSum === null) { return null; @@ -323,19 +320,22 @@ export const calculateStatisticsSumGrowth = ( data: Statistics, stats: string[] ): number | null => { - let totalGrowth = 0; + let totalGrowth: number | null = null; for (const stat of stats) { if (!(stat in data)) { - return null; + continue; } const statGrowth = calculateStatisticSumGrowth(data[stat]); if (statGrowth === null) { - return null; + continue; + } + if (totalGrowth === null) { + totalGrowth = statGrowth; + } else { + totalGrowth += statGrowth; } - - totalGrowth += statGrowth; } return totalGrowth; @@ -345,3 +345,128 @@ export const statisticsHaveType = ( stats: StatisticValue[], type: StatisticType ) => stats.some((stat) => stat[type] !== null); + +/** + * Get the earliest start from a list of statistics. + */ +const getMinStatisticStart = (stats: StatisticValue[][]): string | null => { + let earliestString: string | null = null; + let earliestTime: Date | null = null; + + for (const stat of stats) { + if (stat.length === 0) { + continue; + } + const curTime = new Date(stat[0].start); + + if (earliestString === null) { + earliestString = stat[0].start; + earliestTime = curTime; + continue; + } + + if (curTime < earliestTime!) { + earliestString = stat[0].start; + earliestTime = curTime; + } + } + + return earliestString; +}; + +// Merge multiple sum statistics into one +const mergeSumStatistics = (stats: StatisticValue[][]) => { + const result: { start: string; sum: number }[] = []; + + const statsCopy: StatisticValue[][] = stats.map((stat) => [...stat]); + + while (statsCopy.some((stat) => stat.length > 0)) { + const earliestStart = getMinStatisticStart(statsCopy)!; + + let sum = 0; + + for (const stat of statsCopy) { + if (stat.length === 0) { + continue; + } + if (stat[0].start !== earliestStart) { + continue; + } + const statVal = stat.shift()!; + if (!statVal.sum) { + continue; + } + sum += statVal.sum; + } + + result.push({ + start: earliestStart, + sum, + }); + } + + return result; +}; + +/** + * Get the growth of a statistic over the given period while applying a + * per-period percentage. + */ +export const calculateStatisticsSumGrowthWithPercentage = ( + percentageStat: StatisticValue[], + sumStats: StatisticValue[][] +): number | null => { + let sum: number | null = null; + + if (sumStats.length === 0) { + return null; + } + + const sumStatsToProcess = mergeSumStatistics(sumStats); + const percentageStatToProcess = [...percentageStat]; + + let lastSum: number | null = null; + + // pre-populate lastSum with last sum statistic _before_ the first percentage statistic + for (const stat of sumStatsToProcess) { + if (new Date(stat.start) >= new Date(percentageStat[0].start)) { + break; + } + lastSum = stat.sum; + } + + while (percentageStatToProcess.length > 0) { + if (!sumStatsToProcess.length) { + return sum; + } + + // If they are not equal, pop the value that is earlier in time + if (sumStatsToProcess[0].start !== percentageStatToProcess[0].start) { + if ( + new Date(sumStatsToProcess[0].start) < + new Date(percentageStatToProcess[0].start) + ) { + sumStatsToProcess.shift(); + } else { + percentageStatToProcess.shift(); + } + continue; + } + + const sumStatValue = sumStatsToProcess.shift()!; + const percentageStatValue = percentageStatToProcess.shift()!; + + if (lastSum !== null) { + const sumGrowth = sumStatValue.sum! - lastSum; + if (sum === null) { + sum = sumGrowth * (percentageStatValue.mean! / 100); + } else { + sum += sumGrowth * (percentageStatValue.mean! / 100); + } + } + + lastSum = sumStatValue.sum; + } + + return sum; +}; diff --git a/src/data/zwave_js.ts b/src/data/zwave_js.ts index 16751e26b2..b443d62fbc 100644 --- a/src/data/zwave_js.ts +++ b/src/data/zwave_js.ts @@ -83,6 +83,12 @@ export interface ZWaveJSHealNetworkStatusMessage { heal_node_status: { [key: number]: string }; } +export interface ZWaveJSRemovedNode { + node_id: number; + manufacturer: string; + label: string; +} + export enum NodeStatus { Unknown, Asleep, @@ -178,6 +184,32 @@ export const reinterviewNode = ( } ); +export const healNode = ( + hass: HomeAssistant, + entry_id: string, + node_id: number +): Promise => + hass.callWS({ + type: "zwave_js/heal_node", + entry_id: entry_id, + node_id: node_id, + }); + +export const removeFailedNode = ( + hass: HomeAssistant, + entry_id: string, + node_id: number, + callbackFunction: (message: any) => void +): Promise => + hass.connection.subscribeMessage( + (message: any) => callbackFunction(message), + { + type: "zwave_js/remove_failed_node", + entry_id: entry_id, + node_id: node_id, + } + ); + export const healNetwork = ( hass: HomeAssistant, entry_id: string diff --git a/src/fake_data/demo_config.ts b/src/fake_data/demo_config.ts index 68a99ee8e2..91052868ea 100644 --- a/src/fake_data/demo_config.ts +++ b/src/fake_data/demo_config.ts @@ -22,4 +22,5 @@ export const demoConfig: HassConfig = { state: STATE_RUNNING, internal_url: "http://homeassistant.local:8123", external_url: null, + currency: "USD", }; diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-time.ts b/src/panels/config/automation/condition/types/ha-automation-condition-time.ts index 5b0e56b350..e606e31eac 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-time.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-time.ts @@ -132,6 +132,7 @@ export class HaTimeCondition extends LitElement implements ConditionElement { .value=${before?.startsWith("input_datetime.") ? before : ""} @value-changed=${this._valueChanged} .hass=${this.hass} + allow-custom-entity >` : html` +
+
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.currency" + )}
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.find_currency_value" + )} +
+ + +
@@ -157,10 +188,16 @@ class ConfigCoreForm extends LitElement { protected firstUpdated(changedProps) { super.firstUpdated(changedProps); - const input = this.shadowRoot!.querySelector( + + const tzInput = this.shadowRoot!.querySelector( "[name=timeZone]" ) as PaperInputElement; - input.inputElement.appendChild(createTimezoneListEl()); + tzInput.inputElement.appendChild(createTimezoneListEl()); + + const cInput = this.shadowRoot!.querySelector( + "[name=currency]" + ) as PaperInputElement; + cInput.inputElement.appendChild(createCurrencyListEl()); } private _markerLocation = memoizeOne( @@ -178,6 +215,12 @@ class ConfigCoreForm extends LitElement { ] ); + private get _currencyValue() { + return this._currency !== undefined + ? this._currency + : this.hass.config.currency; + } + private get _elevationValue() { return this._elevation !== undefined ? this._elevation @@ -200,7 +243,15 @@ class ConfigCoreForm extends LitElement { private _handleChange(ev: PolymerChangedEvent) { const target = ev.currentTarget as PaperInputElement; - this[`_${target.name}`] = target.value; + let value = target.value; + + if (target.name === "currency" && value) { + if (value in SYMBOL_TO_ISO) { + value = SYMBOL_TO_ISO[value]; + } + } + + this[`_${target.name}`] = value; } private _locationChanged(ev) { @@ -223,12 +274,13 @@ class ConfigCoreForm extends LitElement { await saveCoreConfig(this.hass, { latitude: location[0], longitude: location[1], + currency: this._currencyValue, elevation: Number(this._elevationValue), unit_system: this._unitSystemValue, time_zone: this._timeZoneValue, }); } catch (err) { - alert("FAIL"); + alert(`Error saving config: ${err.message}`); } finally { this._working = false; } @@ -258,6 +310,10 @@ class ConfigCoreForm extends LitElement { .card-actions { text-align: right; } + + a { + color: var(--primary-color); + } `; } } diff --git a/src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-actions-zwave_js.ts b/src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-actions-zwave_js.ts index 8695c556b1..cd6b6e5f7d 100644 --- a/src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-actions-zwave_js.ts +++ b/src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-actions-zwave_js.ts @@ -16,6 +16,8 @@ import { import { haStyle } from "../../../../../../resources/styles"; import { HomeAssistant } from "../../../../../../types"; import { showZWaveJSReinterviewNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-reinterview-node"; +import { showZWaveJSHealNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node"; +import { showZWaveJSRemoveFailedNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node"; @customElement("ha-device-actions-zwave_js") export class HaDeviceActionsZWaveJS extends LitElement { @@ -56,6 +58,14 @@ export class HaDeviceActionsZWaveJS extends LitElement { "ui.panel.config.zwave_js.device_info.reinterview_device" )} + + ${this.hass.localize("ui.panel.config.zwave_js.device_info.heal_node")} + + + ${this.hass.localize( + "ui.panel.config.zwave_js.device_info.remove_failed" + )} + `; } @@ -69,6 +79,27 @@ export class HaDeviceActionsZWaveJS extends LitElement { }); } + private async _healNodeClicked() { + if (!this._nodeId || !this._entryId) { + return; + } + showZWaveJSHealNodeDialog(this, { + entry_id: this._entryId, + node_id: this._nodeId, + device: this.device, + }); + } + + private async _removeFailedNode() { + if (!this._nodeId || !this._entryId) { + return; + } + showZWaveJSRemoveFailedNodeDialog(this, { + entry_id: this._entryId, + node_id: this._nodeId, + }); + } + static get styles(): CSSResultGroup { return [ haStyle, diff --git a/src/panels/config/energy/components/ha-energy-device-settings.ts b/src/panels/config/energy/components/ha-energy-device-settings.ts index 2ec08d014a..16b7dc38d2 100644 --- a/src/panels/config/energy/components/ha-energy-device-settings.ts +++ b/src/panels/config/energy/components/ha-energy-device-settings.ts @@ -19,6 +19,7 @@ import { } from "../../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; +import { documentationUrl } from "../../../../util/documentation-url"; import { showEnergySettingsDeviceDialog } from "../dialogs/show-dialogs-energy"; import { energyCardStyles } from "./styles"; @@ -33,12 +34,29 @@ export class EnergyDeviceSettings extends LitElement { return html`

- Monitor individual - devices + + ${this.hass.localize( + "ui.panel.config.energy.device_consumption.title" + )}

-

Monitor individual devices.

+

+ ${this.hass.localize( + "ui.panel.config.energy.device_consumption.sub" + )} + ${this.hass.localize( + "ui.panel.config.energy.device_consumption.learn_more" + )} +

Devices

${this.preferences.device_consumption.map((device) => { const entityState = this.hass.states[device.stat_consumption]; diff --git a/src/panels/config/energy/components/ha-energy-grid-settings.ts b/src/panels/config/energy/components/ha-energy-grid-settings.ts index 999445595e..2146c9f7f4 100644 --- a/src/panels/config/energy/components/ha-energy-grid-settings.ts +++ b/src/panels/config/energy/components/ha-energy-grid-settings.ts @@ -24,6 +24,7 @@ import { energySourcesByType, FlowFromGridSourceEnergyPreference, FlowToGridSourceEnergyPreference, + GridSourceTypeEnergyPreference, saveEnergyPreferences, } from "../../../../data/energy"; import { showConfigFlowDialog } from "../../../../dialogs/config-flow/show-dialog-config-flow"; @@ -33,6 +34,7 @@ import { } from "../../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; +import { documentationUrl } from "../../../../util/documentation-url"; import { showEnergySettingsGridFlowFromDialog, showEnergySettingsGridFlowToDialog, @@ -62,12 +64,25 @@ export class EnergyGridSettings extends LitElement { return html`

- ${this.hass.localize("ui.panel.config.energy.grid.title")} + + ${this.hass.localize("ui.panel.config.energy.grid.title")}

-

${this.hass.localize("ui.panel.config.energy.grid.sub")}

+

+ ${this.hass.localize("ui.panel.config.energy.grid.sub")} + ${this.hass.localize( + "ui.panel.config.energy.grid.learn_more" + )} +

Grid consumption

${gridSource.flow_from.map((flow) => { const entityState = this.hass.states[flow.stat_energy_from]; @@ -200,19 +215,33 @@ export class EnergyGridSettings extends LitElement { private _addFromSource() { showEnergySettingsGridFlowFromDialog(this, { - currency: this.preferences.currency, - saveCallback: async (source) => { - const flowFrom = energySourcesByType(this.preferences).grid![0] - .flow_from; + saveCallback: async (flow) => { + let preferences: EnergyPreferences; + const gridSource = this.preferences.energy_sources.find( + (src) => src.type === "grid" + ) as GridSourceTypeEnergyPreference | undefined; - const preferences: EnergyPreferences = { - ...this.preferences, - energy_sources: this.preferences.energy_sources.map((src) => - src.type === "grid" - ? { ...src, flow_from: [...flowFrom, source] } - : src - ), - }; + if (!gridSource) { + preferences = { + ...this.preferences, + energy_sources: [ + ...this.preferences.energy_sources, + { + ...emptyGridSourceEnergyPreference(), + flow_from: [flow], + }, + ], + }; + } else { + preferences = { + ...this.preferences, + energy_sources: this.preferences.energy_sources.map((src) => + src.type === "grid" + ? { ...src, flow_from: [...gridSource.flow_from, flow] } + : src + ), + }; + } await this._savePreferences(preferences); }, }); @@ -220,16 +249,33 @@ export class EnergyGridSettings extends LitElement { private _addToSource() { showEnergySettingsGridFlowToDialog(this, { - currency: this.preferences.currency, - saveCallback: async (source) => { - const flowTo = energySourcesByType(this.preferences).grid![0].flow_to; + saveCallback: async (flow) => { + let preferences: EnergyPreferences; + const gridSource = this.preferences.energy_sources.find( + (src) => src.type === "grid" + ) as GridSourceTypeEnergyPreference | undefined; - const preferences: EnergyPreferences = { - ...this.preferences, - energy_sources: this.preferences.energy_sources.map((src) => - src.type === "grid" ? { ...src, flow_to: [...flowTo, source] } : src - ), - }; + if (!gridSource) { + preferences = { + ...this.preferences, + energy_sources: [ + ...this.preferences.energy_sources, + { + ...emptyGridSourceEnergyPreference(), + flow_to: [flow], + }, + ], + }; + } else { + preferences = { + ...this.preferences, + energy_sources: this.preferences.energy_sources.map((src) => + src.type === "grid" + ? { ...src, flow_to: [...gridSource.flow_to, flow] } + : src + ), + }; + } await this._savePreferences(preferences); }, }); @@ -239,7 +285,6 @@ export class EnergyGridSettings extends LitElement { const origSource: FlowFromGridSourceEnergyPreference = ev.currentTarget.closest(".row").source; showEnergySettingsGridFlowFromDialog(this, { - currency: this.preferences.currency, source: { ...origSource }, saveCallback: async (source) => { const flowFrom = energySourcesByType(this.preferences).grid![0] @@ -267,7 +312,6 @@ export class EnergyGridSettings extends LitElement { const origSource: FlowToGridSourceEnergyPreference = ev.currentTarget.closest(".row").source; showEnergySettingsGridFlowToDialog(this, { - currency: this.preferences.currency, source: { ...origSource }, saveCallback: async (source) => { const flowTo = energySourcesByType(this.preferences).grid![0].flow_to; diff --git a/src/panels/config/energy/components/ha-energy-solar-settings.ts b/src/panels/config/energy/components/ha-energy-solar-settings.ts index a9023dc5ba..5af7e71a16 100644 --- a/src/panels/config/energy/components/ha-energy-solar-settings.ts +++ b/src/panels/config/energy/components/ha-energy-solar-settings.ts @@ -19,6 +19,7 @@ import { } from "../../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; +import { documentationUrl } from "../../../../util/documentation-url"; import { showEnergySettingsSolarDialog } from "../dialogs/show-dialogs-energy"; import { energyCardStyles } from "./styles"; @@ -37,14 +38,24 @@ export class EnergySolarSettings extends LitElement { return html`

- Configure solar - panels + + ${this.hass.localize("ui.panel.config.energy.solar.title")}

- Let Home Assistant monitor your solar panels and give you insight on - their performace. + ${this.hass.localize("ui.panel.config.energy.solar.sub")} + ${this.hass.localize( + "ui.panel.config.energy.solar.learn_more" + )}

Solar production

${solarSources.map((source) => { diff --git a/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts index 47826b1836..3705b4b6a2 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-device-settings.ts @@ -58,12 +58,11 @@ export class DialogEnergyDeviceSettings @closed=${this.closeDialog} > ${this._error ? html`

${this._error}

` : ""} -

Track your devices Learn more

diff --git a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts index 607e3384fb..0963c6beed 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts @@ -85,11 +85,11 @@ export class DialogEnergyGridFlowSettings @closed=${this.closeDialog} > ${this._error ? html`

${this._error}

` : ""} -

+

${this.hass.localize( `ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.paragraph` )} -

+
${this.hass.localize( `ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.cost_number_suffix`, - { currency: this._params.currency } + { currency: this.hass.config.currency } )} ` @@ -212,7 +212,15 @@ export class DialogEnergyGridFlowSettings ${this.hass.localize("ui.common.cancel")} - + ${this.hass.localize("ui.common.save")} @@ -231,32 +239,42 @@ export class DialogEnergyGridFlowSettings } private _numberPriceChanged(ev: CustomEvent) { - this._source!.number_energy_price = Number(ev.detail.value); - this._source!.entity_energy_price = null; this._costStat = null; + this._source = { + ...this._source!, + number_energy_price: Number(ev.detail.value), + entity_energy_price: null, + }; } private _priceStatChanged(ev: CustomEvent) { this._costStat = ev.detail.value; - this._source!.entity_energy_price = null; - this._source!.number_energy_price = null; + this._source = { + ...this._source!, + entity_energy_price: null, + number_energy_price: null, + }; } private _priceEntityChanged(ev: CustomEvent) { - this._source!.entity_energy_price = ev.detail.value; - this._source!.number_energy_price = null; this._costStat = null; + this._source = { + ...this._source!, + entity_energy_price: ev.detail.value, + number_energy_price: null, + }; } private _statisticChanged(ev: CustomEvent<{ value: string }>) { - this._source![ - this._params!.direction === "from" ? "stat_energy_from" : "stat_energy_to" - ] = ev.detail.value; - this._source![ - this._params!.direction === "from" + this._source = { + ...this._source!, + [this._params!.direction === "from" + ? "stat_energy_from" + : "stat_energy_to"]: ev.detail.value, + [this._params!.direction === "from" ? "entity_energy_from" - : "entity_energy_to" - ] = ev.detail.value; + : "entity_energy_to"]: ev.detail.value, + }; } private async _save() { @@ -277,6 +295,9 @@ export class DialogEnergyGridFlowSettings return [ haStyleDialog, css` + ha-dialog { + --mdc-dialog-max-width: 430px; + } ha-formfield { display: block; } diff --git a/src/panels/config/energy/dialogs/dialog-energy-solar-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-solar-settings.ts index 17f68591ef..2e55ba598d 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-solar-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-solar-settings.ts @@ -75,7 +75,6 @@ export class DialogEnergySolarSettings @closed=${this.closeDialog} > ${this._error ? html`

${this._error}

` : ""} -

Solar production for the win! Learn more

${this.hass.localize("ui.common.cancel")}
- + ${this.hass.localize("ui.common.save")} @@ -192,7 +195,7 @@ export class DialogEnergySolarSettings } private _statisticChanged(ev: CustomEvent<{ value: string }>) { - this._source!.stat_energy_from = ev.detail.value; + this._source = { ...this._source!, stat_energy_from: ev.detail.value }; } private async _save() { @@ -212,6 +215,9 @@ export class DialogEnergySolarSettings haStyle, haStyleDialog, css` + ha-dialog { + --mdc-dialog-max-width: 430px; + } img { height: 24px; margin-right: 16px; diff --git a/src/panels/config/energy/dialogs/show-dialogs-energy.ts b/src/panels/config/energy/dialogs/show-dialogs-energy.ts index 62f9d4a48d..f471cce7a7 100644 --- a/src/panels/config/energy/dialogs/show-dialogs-energy.ts +++ b/src/panels/config/energy/dialogs/show-dialogs-energy.ts @@ -10,7 +10,6 @@ export interface EnergySettingsGridFlowDialogParams { source?: | FlowFromGridSourceEnergyPreference | FlowToGridSourceEnergyPreference; - currency: string; direction: "from" | "to"; saveCallback: ( source: @@ -21,13 +20,11 @@ export interface EnergySettingsGridFlowDialogParams { export interface EnergySettingsGridFlowFromDialogParams { source?: FlowFromGridSourceEnergyPreference; - currency: string; saveCallback: (source: FlowFromGridSourceEnergyPreference) => Promise; } export interface EnergySettingsGridFlowToDialogParams { source?: FlowToGridSourceEnergyPreference; - currency: string; saveCallback: (source: FlowToGridSourceEnergyPreference) => Promise; } diff --git a/src/panels/config/energy/ha-config-energy.ts b/src/panels/config/energy/ha-config-energy.ts index 3b81c275ca..a15a6c4179 100644 --- a/src/panels/config/energy/ha-config-energy.ts +++ b/src/panels/config/energy/ha-config-energy.ts @@ -1,25 +1,17 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; - import "../../../components/ha-svg-icon"; -import { - EnergyPreferences, - getEnergyPreferences, - saveEnergyPreferences, -} from "../../../data/energy"; - +import { EnergyPreferences, getEnergyPreferences } from "../../../data/energy"; import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route } from "../../../types"; import { configSections } from "../ha-panel-config"; +import "./components/ha-energy-device-settings"; import "./components/ha-energy-grid-settings"; import "./components/ha-energy-solar-settings"; -import "./components/ha-energy-device-settings"; -import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; -const INITIAL_CONFIG = { - currency: "€", +const INITIAL_CONFIG: EnergyPreferences = { energy_sources: [], device_consumption: [], }; @@ -72,18 +64,6 @@ class HaConfigEnergy extends LitElement { .route=${this.route} .tabs=${configSections.experiences} > - -
- - - - Save -
-
+ ${!this._status + ? html` +
+ +
+

+ ${this.hass.localize( + "ui.panel.config.zwave_js.heal_node.introduction", + { + device: html`${computeDeviceName(this.device, this.hass!)}`, + } + )} +

+
+
+

+ + ${this.hass.localize( + "ui.panel.config.zwave_js.heal_node.traffic_warning" + )} + +

+ + ${this.hass.localize( + "ui.panel.config.zwave_js.heal_node.start_heal" + )} + + ` + : ``} + ${this._status === "started" + ? html` +
+ +
+

+ ${this.hass.localize( + "ui.panel.config.zwave_js.heal_node.in_progress", + { + device: html`${computeDeviceName(this.device, this.hass!)}`, + } + )} +

+
+
+ + ${this.hass.localize("ui.common.close")} + + ` + : ``} + ${this._status === "failed" + ? html` +
+ +
+

+ ${this.hass.localize( + "ui.panel.config.zwave_js.heal_node.healing_failed", + { + device: html`${computeDeviceName(this.device, this.hass!)}`, + } + )} +

+

+ ${this._error + ? html` ${this._error} ` + : ` + ${this.hass.localize( + "ui.panel.config.zwave_js.heal_node.healing_failed_check_logs" + )} + `} +

+
+
+ + ${this.hass.localize("ui.common.close")} + + ` + : ``} + ${this._status === "finished" + ? html` +
+ +
+

+ ${this.hass.localize( + "ui.panel.config.zwave_js.heal_node.healing_complete", + { + device: html`${computeDeviceName(this.device, this.hass!)}`, + } + )} +

+
+
+ + ${this.hass.localize("ui.panel.config.zwave_js.common.close")} + + ` + : ``} + ${this._status === "network-healing" + ? html` +
+ +
+

+ ${this.hass.localize( + "ui.panel.config.zwave_js.heal_node.network_heal_in_progress" + )} +

+
+
+ + ${this.hass.localize("ui.panel.config.zwave_js.common.close")} + + ` + : ``} + + `; + } + + private async _fetchData(): Promise { + if (!this.hass) { + return; + } + const network: ZWaveJSNetwork = await fetchNetworkStatus( + this.hass!, + this.entry_id! + ); + if (network.controller.is_heal_network_active) { + this._status = "network-healing"; + } + } + + private async _startHeal(): Promise { + if (!this.hass) { + return; + } + this._status = "started"; + try { + this._status = (await healNode(this.hass, this.entry_id!, this.node_id!)) + ? "finished" + : "failed"; + } catch (error) { + this._error = error.message; + this._status = "failed"; + } + } + + static get styles(): CSSResultGroup { + return [ + haStyleDialog, + css` + .success { + color: var(--success-color); + } + + .failed { + color: var(--error-color); + } + + .flex-container { + display: flex; + align-items: center; + } + + ha-svg-icon { + width: 68px; + height: 48px; + } + + ha-svg-icon.introduction { + color: var(--primary-color); + } + + .flex-container ha-svg-icon, + .flex-container ha-circular-progress { + margin-right: 20px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-zwave_js-heal-node": DialogZWaveJSHealNode; + } +} diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-failed-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-failed-node.ts new file mode 100644 index 0000000000..ca9cce32f6 --- /dev/null +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-failed-node.ts @@ -0,0 +1,237 @@ +import "@material/mwc-button/mwc-button"; +import { mdiCheckCircle, mdiCloseCircle, mdiRobotDead } from "@mdi/js"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import "../../../../../components/ha-circular-progress"; +import { createCloseHeading } from "../../../../../components/ha-dialog"; +import { + removeFailedNode, + ZWaveJSRemovedNode, +} from "../../../../../data/zwave_js"; +import { haStyleDialog } from "../../../../../resources/styles"; +import { HomeAssistant } from "../../../../../types"; +import { ZWaveJSRemoveFailedNodeDialogParams } from "./show-dialog-zwave_js-remove-failed-node"; + +@customElement("dialog-zwave_js-remove-failed-node") +class DialogZWaveJSRemoveFailedNode extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private entry_id?: string; + + @state() private node_id?: number; + + @state() private _status = ""; + + @state() private _error?: any; + + @state() private _node?: ZWaveJSRemovedNode; + + private _subscribed?: Promise; + + public disconnectedCallback(): void { + super.disconnectedCallback(); + this._unsubscribe(); + } + + public async showDialog( + params: ZWaveJSRemoveFailedNodeDialogParams + ): Promise { + this.entry_id = params.entry_id; + this.node_id = params.node_id; + } + + public closeDialog(): void { + this._unsubscribe(); + this.entry_id = undefined; + this._status = ""; + + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + public closeDialogFinished(): void { + history.back(); + this.closeDialog(); + } + + protected render(): TemplateResult { + if (!this.entry_id || !this.node_id) { + return html``; + } + + return html` + + ${this._status === "" + ? html` +
+ +
+ ${this.hass.localize( + "ui.panel.config.zwave_js.remove_failed_node.introduction" + )} +
+
+ + ${this.hass.localize( + "ui.panel.config.zwave_js.remove_failed_node.remove_device" + )} + + ` + : ``} + ${this._status === "started" + ? html` +
+ +
+

+ + ${this.hass.localize( + "ui.panel.config.zwave_js.remove_failed_node.in_progress" + )} + +

+
+
+ ` + : ``} + ${this._status === "failed" + ? html` +
+ +
+

+ ${this.hass.localize( + "ui.panel.config.zwave_js.remove_failed_node.removal_failed" + )} +

+ ${this._error + ? html`

${this._error.message}

` + : ``} +
+
+ + ${this.hass.localize("ui.common.close")} + + ` + : ``} + ${this._status === "finished" + ? html` +
+ +
+

+ ${this.hass.localize( + "ui.panel.config.zwave_js.remove_failed_node.removal_finished", + "id", + this._node!.node_id + )} +

+
+
+ + ${this.hass.localize("ui.common.close")} + + ` + : ``} +
+ `; + } + + private _startExclusion(): void { + if (!this.hass) { + return; + } + this._status = "started"; + this._subscribed = removeFailedNode( + this.hass, + this.entry_id!, + this.node_id!, + (message: any) => this._handleMessage(message) + ).catch((error) => { + this._status = "failed"; + this._error = error; + }); + } + + private _handleMessage(message: any): void { + if (message.event === "exclusion started") { + this._status = "started"; + } + if (message.event === "node removed") { + this._status = "finished"; + this._node = message.node; + this._unsubscribe(); + } + } + + private async _unsubscribe(): Promise { + if (this._subscribed) { + const unsubFunc = await this._subscribed; + if (unsubFunc instanceof Function) { + unsubFunc(); + } + this._subscribed = undefined; + } + if (this._status !== "finished") { + this._status = ""; + } + } + + static get styles(): CSSResultGroup { + return [ + haStyleDialog, + css` + .success { + color: var(--success-color); + } + + .failed { + color: var(--warning-color); + } + + .flex-container { + display: flex; + align-items: center; + } + + ha-svg-icon { + width: 68px; + height: 48px; + } + + .flex-container ha-circular-progress, + .flex-container ha-svg-icon { + margin-right: 20px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-zwave_js-remove-failed-node": DialogZWaveJSRemoveFailedNode; + } +} diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node.ts new file mode 100644 index 0000000000..646b9f1b3e --- /dev/null +++ b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node.ts @@ -0,0 +1,21 @@ +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { DeviceRegistryEntry } from "../../../../../data/device_registry"; + +export interface ZWaveJSHealNodeDialogParams { + entry_id: string; + node_id: number; + device: DeviceRegistryEntry; +} + +export const loadHealNodeDialog = () => import("./dialog-zwave_js-heal-node"); + +export const showZWaveJSHealNodeDialog = ( + element: HTMLElement, + healNodeDialogParams: ZWaveJSHealNodeDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-zwave_js-heal-node", + dialogImport: loadHealNodeDialog, + dialogParams: healNodeDialogParams, + }); +}; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node.ts new file mode 100644 index 0000000000..e64f0ee483 --- /dev/null +++ b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node.ts @@ -0,0 +1,20 @@ +import { fireEvent } from "../../../../../common/dom/fire_event"; + +export interface ZWaveJSRemoveFailedNodeDialogParams { + entry_id: string; + node_id: number; +} + +export const loadRemoveFailedNodeDialog = () => + import("./dialog-zwave_js-remove-failed-node"); + +export const showZWaveJSRemoveFailedNodeDialog = ( + element: HTMLElement, + removeFailedNodeDialogParams: ZWaveJSRemoveFailedNodeDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-zwave_js-remove-failed-node", + dialogImport: loadRemoveFailedNodeDialog, + dialogParams: removeFailedNodeDialogParams, + }); +}; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts index 0e380a18b3..9edf199887 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts @@ -131,7 +131,9 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) { private _downloadLogs() { fileDownload( this, - `data:text/plain;charset=utf-8,${encodeURI(this._textarea!.value)}`, + `data:text/plain;charset=utf-8,${encodeURIComponent( + this._textarea!.value + )}`, `zwave_js.log` ); } diff --git a/src/panels/developer-tools/service/developer-tools-service.ts b/src/panels/developer-tools/service/developer-tools-service.ts index 282337d0c0..8ca0492192 100644 --- a/src/panels/developer-tools/service/developer-tools-service.ts +++ b/src/panels/developer-tools/service/developer-tools-service.ts @@ -9,7 +9,8 @@ import { computeDomain } from "../../../common/entity/compute_domain"; import { computeObjectId } from "../../../common/entity/compute_object_id"; import { hasTemplate } from "../../../common/string/has-template"; import { extractSearchParam } from "../../../common/url/search-params"; -import "../../../components/buttons/ha-progress-button"; +import { HaProgressButton } from "../../../components/buttons/ha-progress-button"; + import "../../../components/entity/ha-entity-picker"; import "../../../components/ha-card"; import "../../../components/ha-expansion-panel"; @@ -135,11 +136,15 @@ class HaPanelDevService extends LitElement { >` : ""}
- + ${this.hass.localize( "ui.panel.developer-tools.tabs.services.call_service" )} - +
@@ -295,7 +300,8 @@ class HaPanelDevService extends LitElement { } ); - private async _callService() { + private async _callService(ev) { + const button = ev.currentTarget as HaProgressButton; if (!this._serviceData?.service) { return; } @@ -310,6 +316,7 @@ class HaPanelDevService extends LitElement { return; } forwardHaptic("failure"); + button.actionError(); showToast(this, { message: this.hass.localize( @@ -318,7 +325,9 @@ class HaPanelDevService extends LitElement { this._serviceData.service ) + ` ${err.message}`, }); + return; } + button.actionSuccess(); } private _toggleYaml() { diff --git a/src/panels/energy/cards/energy-setup-wizard-card.ts b/src/panels/energy/cards/energy-setup-wizard-card.ts index 315fd79ffc..aa5178ac5f 100644 --- a/src/panels/energy/cards/energy-setup-wizard-card.ts +++ b/src/panels/energy/cards/energy-setup-wizard-card.ts @@ -20,8 +20,7 @@ export class EnergySetupWizard extends LitElement implements LovelaceCard { @state() private _step = 0; - private _preferences: EnergyPreferences = { - currency: "€", + @state() private _preferences: EnergyPreferences = { energy_sources: [], device_consumption: [], }; @@ -42,9 +41,6 @@ export class EnergySetupWizard extends LitElement implements LovelaceCard { protected render(): TemplateResult { return html` -

${this.hass.localize("ui.panel.energy.setup.header")}

-

${this.hass.localize("ui.panel.energy.setup.slogan")}

-

Step ${this._step + 1} of 3

${this._step === 0 ? html` `}
${this._step > 0 - ? html`${this.hass.localize("ui.panel.energy.setup.back")}` : html`
`} ${this._step < 2 - ? html`${this.hass.localize("ui.panel.energy.setup.next")}` - : html` + : html` ${this.hass.localize("ui.panel.energy.setup.done")} `}
diff --git a/src/panels/energy/strategies/energy-strategy.ts b/src/panels/energy/strategies/energy-strategy.ts index 7e19dbfd3a..86ff55d8d1 100644 --- a/src/panels/energy/strategies/energy-strategy.ts +++ b/src/panels/energy/strategies/energy-strategy.ts @@ -1,4 +1,8 @@ -import { EnergyPreferences, getEnergyPreferences } from "../../../data/energy"; +import { + EnergyPreferences, + getEnergyPreferences, + GridSourceTypeEnergyPreference, +} from "../../../data/energy"; import { LovelaceViewConfig } from "../../../data/lovelace"; import { LovelaceViewStrategy } from "../../lovelace/strategies/get-strategy"; @@ -39,9 +43,10 @@ export class EnergyStrategy { view.type = "sidebar"; - const hasGrid = energyPrefs.energy_sources.some( + const hasGrid = energyPrefs.energy_sources.find( (source) => source.type === "grid" - ); + ) as GridSourceTypeEnergyPreference; + const hasReturn = hasGrid && hasGrid.flow_to.length; const hasSolar = energyPrefs.energy_sources.some( (source) => source.type === "solar" ); @@ -49,8 +54,8 @@ export class EnergyStrategy { // Only include if we have a grid source. if (hasGrid) { view.cards!.push({ - title: "Electricity", - type: "energy-summary-graph", + title: "Energy usage", + type: "energy-usage-graph", prefs: energyPrefs, }); } @@ -67,30 +72,21 @@ export class EnergyStrategy { // Only include if we have a grid. if (hasGrid) { view.cards!.push({ - title: "Costs", - type: "energy-costs-table", - prefs: energyPrefs, - }); - } - - // Only include if we have at least 1 device in the config. - if (energyPrefs.device_consumption.length) { - view.cards!.push({ - title: "Monitor individual devices", - type: "energy-devices-graph", - prefs: energyPrefs, - }); - } - - // Only include if we have a grid. - if (hasGrid) { - view.cards!.push({ - type: "energy-usage", + title: "Energy distribution", + type: "energy-distribution", prefs: energyPrefs, view_layout: { position: "sidebar" }, }); } + if (hasGrid || hasSolar) { + view.cards!.push({ + title: "Sources", + type: "energy-sources-table", + prefs: energyPrefs, + }); + } + // Only include if we have a solar source. if (hasSolar) { view.cards!.push({ @@ -100,6 +96,15 @@ export class EnergyStrategy { }); } + // Only include if we have a grid source & return. + if (hasReturn) { + view.cards!.push({ + type: "energy-grid-neutrality-gauge", + prefs: energyPrefs, + view_layout: { position: "sidebar" }, + }); + } + // Only include if we have a grid if (hasGrid) { view.cards!.push({ @@ -109,11 +114,14 @@ export class EnergyStrategy { }); } - view.cards!.push({ - type: "energy-summary", - prefs: energyPrefs, - view_layout: { position: "sidebar" }, - }); + // Only include if we have at least 1 device in the config. + if (energyPrefs.device_consumption.length) { + view.cards!.push({ + title: "Monitor individual devices", + type: "energy-devices-graph", + prefs: energyPrefs, + }); + } return view; } diff --git a/src/panels/lovelace/cards/hui-energy-carbon-consumed-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts similarity index 60% rename from src/panels/lovelace/cards/hui-energy-carbon-consumed-gauge-card.ts rename to src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts index 7a3c5f68ba..84a2e54206 100644 --- a/src/panels/lovelace/cards/hui-energy-carbon-consumed-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts @@ -1,22 +1,24 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { styleMap } from "lit/directives/style-map"; -import { round } from "../../../common/number/round"; -import { subscribeOne } from "../../../common/util/subscribe-one"; -import "../../../components/ha-card"; -import "../../../components/ha-gauge"; -import { getConfigEntries } from "../../../data/config_entries"; -import { energySourcesByType } from "../../../data/energy"; -import { subscribeEntityRegistry } from "../../../data/entity_registry"; +import { round } from "../../../../common/number/round"; +import { subscribeOne } from "../../../../common/util/subscribe-one"; +import "../../../../components/ha-card"; +import "../../../../components/ha-gauge"; +import { getConfigEntries } from "../../../../data/config_entries"; +import { energySourcesByType } from "../../../../data/energy"; +import { subscribeEntityRegistry } from "../../../../data/entity_registry"; import { calculateStatisticsSumGrowth, + calculateStatisticsSumGrowthWithPercentage, fetchStatistics, Statistics, -} from "../../../data/history"; -import type { HomeAssistant } from "../../../types"; -import type { LovelaceCard } from "../types"; -import { severityMap } from "./hui-gauge-card"; -import type { EnergyCarbonGaugeCardConfig } from "./types"; +} from "../../../../data/history"; +import type { HomeAssistant } from "../../../../types"; +import { createEntityNotFoundWarning } from "../../components/hui-warning"; +import type { LovelaceCard } from "../../types"; +import { severityMap } from "../hui-gauge-card"; +import type { EnergyCarbonGaugeCardConfig } from "../types"; @customElement("hui-energy-carbon-consumed-gauge-card") class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard { @@ -41,7 +43,6 @@ class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard { if (!this.hasUpdated) { this._getStatistics(); - this._fetchCO2SignalEntity(); } } @@ -50,24 +51,20 @@ class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard { return html``; } - if (!this._stats || this._co2SignalEntity === undefined) { - return html`Loading...`; + if (this._co2SignalEntity === null) { + return html``; } - if (!this._co2SignalEntity) { - return html``; + if (!this._stats || !this._co2SignalEntity) { + return html`Loading...`; } const co2State = this.hass.states[this._co2SignalEntity]; if (!co2State) { - return html`No CO2 Signal entity found.`; - } - - const co2percentage = Number(co2State.state); - - if (isNaN(co2percentage)) { - return html``; + return html` + ${createEntityNotFoundWarning(this.hass, this._co2SignalEntity)} + `; } const prefs = this._config!.prefs; @@ -78,56 +75,65 @@ class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard { types.grid![0].flow_from.map((flow) => flow.stat_energy_from) ); - const totalSolarProduction = types.solar - ? calculateStatisticsSumGrowth( - this._stats, - types.solar.map((source) => source.stat_energy_from) - ) - : undefined; + let value: number | undefined; - const totalGridReturned = calculateStatisticsSumGrowth( - this._stats, - types.grid![0].flow_to.map((flow) => flow.stat_energy_to) - ); + if (this._co2SignalEntity in this._stats && totalGridConsumption) { + const highCarbonEnergy = + calculateStatisticsSumGrowthWithPercentage( + this._stats[this._co2SignalEntity], + types + .grid![0].flow_from.map( + (flow) => this._stats![flow.stat_energy_from] + ) + .filter(Boolean) + ) || 0; - if (totalGridConsumption === null) { - return html`Couldn't calculate the total grid consumption.`; + const totalSolarProduction = types.solar + ? calculateStatisticsSumGrowth( + this._stats, + types.solar.map((source) => source.stat_energy_from) + ) + : undefined; + + const totalGridReturned = calculateStatisticsSumGrowth( + this._stats, + types.grid![0].flow_to.map((flow) => flow.stat_energy_to) + ); + + const totalEnergyConsumed = + totalGridConsumption + + Math.max(0, (totalSolarProduction || 0) - (totalGridReturned || 0)); + + value = round((1 - highCarbonEnergy / totalEnergyConsumed) * 100); } - const highCarbonEnergy = (totalGridConsumption * co2percentage) / 100; - - const totalEnergyConsumed = - totalGridConsumption + - (totalSolarProduction || 0) - - (totalGridReturned || 0); - - const value = round((highCarbonEnergy / totalEnergyConsumed) * 100); - return html` - - -
High-carbon energy consumed
+ ${value !== undefined + ? html` +
Non-fossil energy consumed
` + : html`Consumed non-fossil energy couldn't be calculated`}
`; } private _computeSeverity(numberValue: number): string { - if (numberValue > 50) { + if (numberValue < 10) { return severityMap.red; } - if (numberValue > 30) { + if (numberValue < 30) { return severityMap.yellow; } - if (numberValue < 10) { + if (numberValue > 75) { return severityMap.green; } return severityMap.normal; @@ -166,6 +172,12 @@ class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard { } private async _getStatistics(): Promise { + await this._fetchCO2SignalEntity(); + + if (this._co2SignalEntity === null) { + return; + } + const startDate = new Date(); startDate.setHours(0, 0, 0, 0); startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint @@ -187,6 +199,10 @@ class HuiEnergyCarbonGaugeCard extends LitElement implements LovelaceCard { } } + if (this._co2SignalEntity) { + statistics.push(this._co2SignalEntity); + } + this._stats = await fetchStatistics( this.hass!, startDate, diff --git a/src/panels/lovelace/cards/hui-energy-devices-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts similarity index 77% rename from src/panels/lovelace/cards/hui-energy-devices-graph-card.ts rename to src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts index 2897dc386b..f9d8c1cb5d 100644 --- a/src/panels/lovelace/cards/hui-energy-devices-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts @@ -14,18 +14,22 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import { getColorByIndex } from "../../../common/color/colors"; -import { computeStateName } from "../../../common/entity/compute_state_name"; -import "../../../components/chart/ha-chart-base"; -import "../../../components/ha-card"; +import { getColorByIndex } from "../../../../common/color/colors"; +import { computeStateName } from "../../../../common/entity/compute_state_name"; +import { + formatNumber, + numberFormatToLocale, +} from "../../../../common/string/format_number"; +import "../../../../components/chart/ha-chart-base"; +import "../../../../components/ha-card"; import { calculateStatisticSumGrowth, fetchStatistics, Statistics, -} from "../../../data/history"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard } from "../types"; -import { EnergyDevicesGraphCardConfig } from "./types"; +} from "../../../../data/history"; +import { HomeAssistant } from "../../../../types"; +import { LovelaceCard } from "../../types"; +import { EnergyDevicesGraphCardConfig } from "../types"; @customElement("hui-energy-devices-graph-card") export class HuiEnergyDevicesGraphCard @@ -106,7 +110,10 @@ export class HuiEnergyDevicesGraphCard } return html` - + + ${this._config.title + ? html`

${this._config.title}

` + : ""}
- `${context.dataset.label}: ${ - Math.round(context.parsed.x * 100) / 100 - } kWh`, + `${context.dataset.label}: ${formatNumber( + context.parsed.x, + this.hass.locale + )} kWh`, }, }, }, + // @ts-expect-error + locale: numberFormatToLocale(this.hass.locale), }; } @@ -178,10 +188,6 @@ export class HuiEnergyDevicesGraphCard const statisticsData = Object.values(this._data!); let endTime: Date; - if (statisticsData.length === 0) { - return; - } - endTime = new Date( Math.max( ...statisticsData.map((stats) => @@ -190,7 +196,7 @@ export class HuiEnergyDevicesGraphCard ) ); - if (endTime > new Date()) { + if (!endTime || endTime > new Date()) { endTime = new Date(); } @@ -207,27 +213,30 @@ export class HuiEnergyDevicesGraphCard }, ]; - Object.entries(this._data).forEach(([id, statistics], idx) => { - const entity = this.hass.states[id]; - const label = entity ? computeStateName(entity) : id; + for (let idx = 0; idx < prefs.device_consumption.length; idx++) { + const device = prefs.device_consumption[idx]; + const entity = this.hass.states[device.stat_consumption]; + const label = entity ? computeStateName(entity) : device.stat_consumption; const color = getColorByIndex(idx); borderColor.push(color); backgroundColor.push(color + "7F"); - const value = calculateStatisticSumGrowth(statistics); + const value = + device.stat_consumption in this._data + ? calculateStatisticSumGrowth(this._data[device.stat_consumption]) + : 0; data.push({ // @ts-expect-error y: label, x: value || 0, }); - }); + } data.sort((a, b) => b.x - a.x); this._chartData = { - // labels, datasets, }; } @@ -237,6 +246,9 @@ export class HuiEnergyDevicesGraphCard ha-card { height: 100%; } + .card-header { + padding-bottom: 0; + } .content { padding: 16px; } diff --git a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts new file mode 100644 index 0000000000..a3b27745f6 --- /dev/null +++ b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts @@ -0,0 +1,625 @@ +import { + mdiArrowLeft, + mdiArrowRight, + mdiHome, + mdiLeaf, + mdiSolarPower, + mdiTransmissionTower, +} from "@mdi/js"; +import { css, html, LitElement, svg } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { ifDefined } from "lit/directives/if-defined"; +import { formatNumber } from "../../../../common/string/format_number"; +import { subscribeOne } from "../../../../common/util/subscribe-one"; +import "../../../../components/ha-card"; +import "../../../../components/ha-svg-icon"; +import { getConfigEntries } from "../../../../data/config_entries"; +import { energySourcesByType } from "../../../../data/energy"; +import { subscribeEntityRegistry } from "../../../../data/entity_registry"; +import { + calculateStatisticsSumGrowth, + calculateStatisticsSumGrowthWithPercentage, + fetchStatistics, + Statistics, +} from "../../../../data/history"; +import { HomeAssistant } from "../../../../types"; +import { LovelaceCard } from "../../types"; +import { EnergyDistributionCardConfig } from "../types"; + +const CIRCLE_CIRCUMFERENCE = 238.76104; + +@customElement("hui-energy-distribution-card") +class HuiEnergyDistrubutionCard extends LitElement implements LovelaceCard { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _config?: EnergyDistributionCardConfig; + + @state() private _stats?: Statistics; + + @state() private _co2SignalEntity?: string; + + private _fetching = false; + + public setConfig(config: EnergyDistributionCardConfig): void { + this._config = config; + } + + public getCardSize(): Promise | number { + return 3; + } + + public willUpdate(changedProps) { + super.willUpdate(changedProps); + + if (!this._fetching && !this._stats) { + this._fetching = true; + this._getStatistics().then(() => { + this._fetching = false; + }); + } + } + + protected render() { + if (!this._config) { + return html``; + } + + if (!this._stats) { + return html`Loading…`; + } + + const prefs = this._config!.prefs; + const types = energySourcesByType(prefs); + + // The strategy only includes this card if we have a grid. + const hasConsumption = true; + + const hasSolarProduction = types.solar !== undefined; + const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0; + + const totalGridConsumption = + calculateStatisticsSumGrowth( + this._stats, + types.grid![0].flow_from.map((flow) => flow.stat_energy_from) + ) ?? 0; + + let totalSolarProduction: number | null = null; + + if (hasSolarProduction) { + totalSolarProduction = + calculateStatisticsSumGrowth( + this._stats, + types.solar!.map((source) => source.stat_energy_from) + ) || 0; + } + + let productionReturnedToGrid: number | null = null; + + if (hasReturnToGrid) { + productionReturnedToGrid = + calculateStatisticsSumGrowth( + this._stats, + types.grid![0].flow_to.map((flow) => flow.stat_energy_to) + ) || 0; + } + + const solarConsumption = Math.max( + 0, + (totalSolarProduction || 0) - (productionReturnedToGrid || 0) + ); + + const totalHomeConsumption = totalGridConsumption + solarConsumption; + + let homeSolarCircumference: number | undefined; + if (hasSolarProduction) { + homeSolarCircumference = + CIRCLE_CIRCUMFERENCE * (solarConsumption / totalHomeConsumption); + } + + let lowCarbonConsumption: number | undefined; + + let homeLowCarbonCircumference: number | undefined; + let homeHighCarbonCircumference: number | undefined; + + let electricityMapUrl: string | undefined; + + if (this._co2SignalEntity && this._co2SignalEntity in this._stats) { + // Calculate high carbon consumption + const highCarbonConsumption = calculateStatisticsSumGrowthWithPercentage( + this._stats[this._co2SignalEntity], + types + .grid![0].flow_from.map((flow) => this._stats![flow.stat_energy_from]) + .filter(Boolean) + ); + + const co2State = this.hass.states[this._co2SignalEntity]; + + if (co2State) { + electricityMapUrl = `https://www.electricitymap.org/zone/${co2State.attributes.country_code}`; + } + + if (highCarbonConsumption !== null) { + lowCarbonConsumption = totalGridConsumption - highCarbonConsumption; + + homeHighCarbonCircumference = + CIRCLE_CIRCUMFERENCE * (highCarbonConsumption / totalHomeConsumption); + + homeLowCarbonCircumference = + CIRCLE_CIRCUMFERENCE - + (homeSolarCircumference || 0) - + homeHighCarbonCircumference; + } + } + + return html` + +
+ ${lowCarbonConsumption !== undefined || hasSolarProduction + ? html`
+ ${lowCarbonConsumption === undefined + ? html`
` + : html``} + ${hasSolarProduction + ? html`
+ Solar +
+ + ${formatNumber( + totalSolarProduction || 0, + this.hass.locale, + { maximumFractionDigits: 1 } + )} + kWh +
+
` + : ""} +
+
` + : ""} +
+
+
+ + + ${hasReturnToGrid + ? html`` + : ""}${formatNumber( + totalGridConsumption, + this.hass.locale, + { maximumFractionDigits: 1 } + )} + kWh + + ${productionReturnedToGrid !== null + ? html` + ${formatNumber( + productionReturnedToGrid, + this.hass.locale, + { maximumFractionDigits: 1 } + )} + kWh + ` + : ""} +
+ Grid +
+
+
+ + ${formatNumber(totalHomeConsumption, this.hass.locale, { + maximumFractionDigits: 1, + })} + kWh + ${homeSolarCircumference !== undefined || + homeLowCarbonCircumference !== undefined + ? html` + ${homeSolarCircumference !== undefined + ? svg`` + : ""} + ${homeLowCarbonCircumference + ? svg`` + : ""} + + ` + : ""} +
+ Home +
+
+
+ + ${hasReturnToGrid && hasSolarProduction + ? svg` ` + : ""} + ${hasSolarProduction + ? svg`` + : ""} + + ${productionReturnedToGrid && hasSolarProduction + ? svg` + + + + ` + : ""} + ${totalSolarProduction + ? svg` + + + + ` + : ""} + ${totalGridConsumption + ? svg` + + + + ` + : ""} + +
+
+
+ `; + } + + private async _getStatistics(): Promise { + const [configEntries, entityRegistryEntries] = await Promise.all([ + getConfigEntries(this.hass), + subscribeOne(this.hass.connection, subscribeEntityRegistry), + ]); + + const co2ConfigEntry = configEntries.find( + (entry) => entry.domain === "co2signal" + ); + + this._co2SignalEntity = undefined; + + if (co2ConfigEntry) { + for (const entry of entityRegistryEntries) { + if (entry.config_entry_id !== co2ConfigEntry.entry_id) { + continue; + } + + // The integration offers 2 entities. We want the % one. + const co2State = this.hass.states[entry.entity_id]; + if (!co2State || co2State.attributes.unit_of_measurement !== "%") { + continue; + } + + this._co2SignalEntity = co2State.entity_id; + break; + } + } + + const startDate = new Date(); + startDate.setHours(0, 0, 0, 0); + startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint + + const statistics: string[] = []; + + if (this._co2SignalEntity !== undefined) { + statistics.push(this._co2SignalEntity); + } + + const prefs = this._config!.prefs; + for (const source of prefs.energy_sources) { + if (source.type === "solar") { + statistics.push(source.stat_energy_from); + continue; + } + + // grid source + for (const flowFrom of source.flow_from) { + statistics.push(flowFrom.stat_energy_from); + } + for (const flowTo of source.flow_to) { + statistics.push(flowTo.stat_energy_to); + } + } + + this._stats = await fetchStatistics( + this.hass!, + startDate, + undefined, + statistics + ); + } + + static styles = css` + :host { + --mdc-icon-size: 24px; + } + .card-content { + position: relative; + } + .lines { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 146px; + display: flex; + justify-content: center; + padding: 0 16px 16px; + box-sizing: border-box; + } + .lines svg { + width: calc(100% - 160px); + height: 100%; + max-width: 340px; + } + .row { + display: flex; + justify-content: space-between; + max-width: 500px; + margin: 0 auto; + } + .circle-container { + display: flex; + flex-direction: column; + align-items: center; + } + .circle-container.low-carbon { + margin-right: 4px; + } + .circle-container.solar { + margin-left: 4px; + height: 130px; + } + .spacer { + width: 84px; + } + .circle { + width: 80px; + height: 80px; + border-radius: 50%; + box-sizing: border-box; + border: 2px solid; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + font-size: 12px; + line-height: 12px; + position: relative; + text-decoration: none; + color: var(--primary-text-color); + } + ha-svg-icon { + padding-bottom: 2px; + } + ha-svg-icon.small { + --mdc-icon-size: 12px; + } + .label { + color: var(--secondary-text-color); + font-size: 12px; + } + line, + path { + stroke: var(--primary-text-color); + stroke-width: 1; + fill: none; + } + .circle svg { + position: absolute; + fill: none; + stroke-width: 4px; + width: 100%; + height: 100%; + } + .low-carbon line { + stroke: var(--energy-non-fossil-color); + } + .low-carbon .circle { + border-color: var(--energy-non-fossil-color); + } + .low-carbon ha-svg-icon { + color: var(--energy-non-fossil-color); + } + circle.low-carbon { + stroke: var(--energy-non-fossil-color); + fill: var(--energy-non-fossil-color); + } + .solar .circle { + border-color: var(--energy-solar-color); + } + circle.solar, + path.solar { + stroke: var(--energy-solar-color); + } + circle.solar { + stroke-width: 4; + fill: var(--energy-solar-color); + } + path.return, + circle.return { + stroke: var(--energy-grid-return-color); + } + circle.return { + stroke-width: 4; + fill: var(--energy-grid-return-color); + } + .return { + color: var(--energy-grid-return-color); + } + .grid .circle { + border-color: var(--energy-grid-consumption-color); + } + .consumption { + color: var(--energy-grid-consumption-color); + } + circle.grid, + path.grid { + stroke: var(--energy-grid-consumption-color); + } + circle.grid { + stroke-width: 4; + fill: var(--energy-grid-consumption-color); + } + .home .circle { + border-width: 0; + border-color: var(--primary-color); + } + .home .circle.border { + border-width: 2px; + } + .circle svg circle { + animation: rotate-in 0.6s ease-in; + transition: stroke-dashoffset 0.4s, stroke-dasharray 0.4s; + fill: none; + } + @keyframes rotate-in { + from { + stroke-dashoffset: 238.76104; + stroke-dasharray: 238.76104; + } + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-energy-distribution-card": HuiEnergyDistrubutionCard; + } +} diff --git a/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts new file mode 100644 index 0000000000..97f8dab168 --- /dev/null +++ b/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts @@ -0,0 +1,177 @@ +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { formatNumber } from "../../../../common/string/format_number"; +import "../../../../components/ha-card"; +import "../../../../components/ha-gauge"; +import type { LevelDefinition } from "../../../../components/ha-gauge"; +import { GridSourceTypeEnergyPreference } from "../../../../data/energy"; +import { + calculateStatisticsSumGrowth, + fetchStatistics, + Statistics, +} from "../../../../data/history"; +import type { HomeAssistant } from "../../../../types"; +import type { LovelaceCard } from "../../types"; +import type { EnergyGridGaugeCardConfig } from "../types"; + +const LEVELS: LevelDefinition[] = [ + { level: -1, stroke: "var(--label-badge-red)" }, + { level: -0.2, stroke: "var(--label-badge-yellow)" }, + { level: 0, stroke: "var(--label-badge-green)" }, +]; + +@customElement("hui-energy-grid-neutrality-gauge-card") +class HuiEnergyGridGaugeCard extends LitElement implements LovelaceCard { + @property({ attribute: false }) public hass?: HomeAssistant; + + @state() private _config?: EnergyGridGaugeCardConfig; + + @state() private _stats?: Statistics; + + public getCardSize(): number { + return 4; + } + + public setConfig(config: EnergyGridGaugeCardConfig): void { + this._config = config; + } + + public willUpdate(changedProps) { + super.willUpdate(changedProps); + + if (!this.hasUpdated) { + this._getStatistics(); + } + } + + protected render(): TemplateResult { + if (!this._config || !this.hass) { + return html``; + } + + if (!this._stats) { + return html`Loading...`; + } + + const prefs = this._config!.prefs; + const gridSource = prefs.energy_sources.find( + (src) => src.type === "grid" + ) as GridSourceTypeEnergyPreference | undefined; + + let value: number | undefined; + + if (!gridSource) { + return html``; + } + + const consumedFromGrid = calculateStatisticsSumGrowth( + this._stats, + gridSource.flow_from.map((flow) => flow.stat_energy_from) + ); + + const returnedToGrid = calculateStatisticsSumGrowth( + this._stats, + gridSource.flow_to.map((flow) => flow.stat_energy_to) + ); + + if (consumedFromGrid !== null && returnedToGrid !== null) { + if (returnedToGrid > consumedFromGrid) { + value = 1 - consumedFromGrid / returnedToGrid; + } else if (returnedToGrid < consumedFromGrid) { + value = (1 - returnedToGrid / consumedFromGrid) * -1; + } else { + value = 0; + } + } + + return html` + + ${value !== undefined + ? html` +
+ ${returnedToGrid! >= consumedFromGrid! + ? "Returned to the grid" + : "Consumed from the grid"} +
` + : "Grid neutrality could not be calculated"} +
+ `; + } + + private async _getStatistics(): Promise { + const startDate = new Date(); + startDate.setHours(0, 0, 0, 0); + startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint + + const statistics: string[] = []; + const prefs = this._config!.prefs; + for (const source of prefs.energy_sources) { + if (source.type === "solar") { + continue; + } + + // grid source + for (const flowFrom of source.flow_from) { + statistics.push(flowFrom.stat_energy_from); + } + for (const flowTo of source.flow_to) { + statistics.push(flowTo.stat_energy_to); + } + } + + this._stats = await fetchStatistics( + this.hass!, + startDate, + undefined, + statistics + ); + } + + static get styles(): CSSResultGroup { + return css` + ha-card { + height: 100%; + overflow: hidden; + padding: 16px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + box-sizing: border-box; + } + + ha-gauge { + width: 100%; + max-width: 250px; + } + + .name { + text-align: center; + line-height: initial; + color: var(--primary-text-color); + width: 100%; + font-size: 15px; + margin-top: 8px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-energy-grid-neutrality-gauge-card": HuiEnergyGridGaugeCard; + } +} diff --git a/src/panels/lovelace/cards/hui-energy-solar-consumed-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts similarity index 79% rename from src/panels/lovelace/cards/hui-energy-solar-consumed-gauge-card.ts rename to src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts index c5e9dab835..3d79fe48ab 100644 --- a/src/panels/lovelace/cards/hui-energy-solar-consumed-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts @@ -1,19 +1,18 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { styleMap } from "lit/directives/style-map"; -import { round } from "../../../common/number/round"; -import "../../../components/ha-card"; -import "../../../components/ha-gauge"; -import { energySourcesByType } from "../../../data/energy"; +import "../../../../components/ha-card"; +import "../../../../components/ha-gauge"; +import { energySourcesByType } from "../../../../data/energy"; import { calculateStatisticsSumGrowth, fetchStatistics, Statistics, -} from "../../../data/history"; -import type { HomeAssistant } from "../../../types"; -import type { LovelaceCard } from "../types"; -import { severityMap } from "./hui-gauge-card"; -import type { EnergySolarGaugeCardConfig } from "./types"; +} from "../../../../data/history"; +import type { HomeAssistant } from "../../../../types"; +import type { LovelaceCard } from "../../types"; +import { severityMap } from "../hui-gauge-card"; +import type { EnergySolarGaugeCardConfig } from "../types"; @customElement("hui-energy-solar-consumed-gauge-card") class HuiEnergySolarGaugeCard extends LitElement implements LovelaceCard { @@ -63,33 +62,42 @@ class HuiEnergySolarGaugeCard extends LitElement implements LovelaceCard { let value: number | undefined; - if (productionReturnedToGrid !== null && totalSolarProduction !== null) { - const cosumedSolar = totalSolarProduction - productionReturnedToGrid; - value = round((cosumedSolar / totalSolarProduction) * 100); + if (productionReturnedToGrid !== null && totalSolarProduction) { + const cosumedSolar = Math.max( + 0, + totalSolarProduction - productionReturnedToGrid + ); + value = (cosumedSolar / totalSolarProduction) * 100; } + return html` - ${value - ? html`
Self consumed solar energy
` - : html`Self consumed solar energy couldn't be calculated`} + : totalSolarProduction === 0 + ? "You have not produced any solar energy" + : "Self consumed solar energy couldn't be calculated"}
`; } private _computeSeverity(numberValue: number): string { - if (numberValue > 50) { + if (numberValue > 75) { return severityMap.green; } + if (numberValue < 50) { + return severityMap.yellow; + } return severityMap.normal; } diff --git a/src/panels/lovelace/cards/hui-energy-solar-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts similarity index 76% rename from src/panels/lovelace/cards/hui-energy-solar-graph-card.ts rename to src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts index c258ac3f79..391585329b 100644 --- a/src/panels/lovelace/cards/hui-energy-solar-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts @@ -8,31 +8,33 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import "../../../components/ha-card"; +import "../../../../components/ha-card"; import { ChartData, ChartDataset, ChartOptions } from "chart.js"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard } from "../types"; -import { EnergySolarGraphCardConfig } from "./types"; -import { fetchStatistics, Statistics } from "../../../data/history"; +import { HomeAssistant } from "../../../../types"; +import { LovelaceCard } from "../../types"; +import { EnergySolarGraphCardConfig } from "../types"; +import { fetchStatistics, Statistics } from "../../../../data/history"; import { hex2rgb, lab2rgb, rgb2hex, rgb2lab, -} from "../../../common/color/convert-color"; -import { labDarken } from "../../../common/color/lab"; -import { SolarSourceTypeEnergyPreference } from "../../../data/energy"; -import { isComponentLoaded } from "../../../common/config/is_component_loaded"; +} from "../../../../common/color/convert-color"; +import { labDarken } from "../../../../common/color/lab"; +import { SolarSourceTypeEnergyPreference } from "../../../../data/energy"; +import { isComponentLoaded } from "../../../../common/config/is_component_loaded"; import { ForecastSolarForecast, getForecastSolarForecasts, -} from "../../../data/forecast_solar"; -import { computeStateName } from "../../../common/entity/compute_state_name"; -import "../../../components/chart/ha-chart-base"; -import "../../../components/ha-switch"; -import "../../../components/ha-formfield"; - -const SOLAR_COLOR = { border: "#FF9800", background: "#ffcb80" }; +} from "../../../../data/forecast_solar"; +import { computeStateName } from "../../../../common/entity/compute_state_name"; +import "../../../../components/chart/ha-chart-base"; +import "../../../../components/ha-switch"; +import "../../../../components/ha-formfield"; +import { + formatNumber, + numberFormatToLocale, +} from "../../../../common/string/format_number"; @customElement("hui-energy-solar-graph-card") export class HuiEnergySolarGraphCard @@ -45,7 +47,9 @@ export class HuiEnergySolarGraphCard @state() private _data?: Statistics; - @state() private _chartData?: ChartData; + @state() private _chartData: ChartData = { + datasets: [], + }; @state() private _forecasts?: Record; @@ -117,37 +121,38 @@ export class HuiEnergySolarGraphCard } return html` - + + ${this._config.title + ? html`

${this._config.title}

` + : ""}
- - ${this._chartData - ? html`` - : ""} +
`; } private _createOptions() { + const startDate = new Date(); + startDate.setHours(0, 0, 0, 0); + const startTime = startDate.getTime(); + this._chartOptions = { parsing: false, animation: false, scales: { x: { type: "time", + suggestedMin: startTime, + suggestedMax: startTime + 24 * 60 * 60 * 1000, adapters: { date: { locale: this.hass.locale, @@ -166,11 +171,16 @@ export class HuiEnergySolarGraphCard : {}, }, time: { - tooltipFormat: "datetimeseconds", + tooltipFormat: "datetime", }, + offset: true, }, y: { type: "linear", + title: { + display: true, + text: "kWh", + }, ticks: { beginAtZero: true, }, @@ -181,7 +191,10 @@ export class HuiEnergySolarGraphCard mode: "nearest", callbacks: { label: (context) => - `${context.dataset.label}: ${context.parsed.y} kWh`, + `${context.dataset.label}: ${formatNumber( + context.parsed.y, + this.hass.locale + )} kWh`, }, }, filler: { @@ -199,13 +212,16 @@ export class HuiEnergySolarGraphCard }, elements: { line: { - tension: 0.4, + tension: 0.3, borderWidth: 1.5, }, + bar: { borderWidth: 1.5, borderRadius: 4 }, point: { hitRadius: 5, }, }, + // @ts-expect-error + locale: numberFormatToLocale(this.hass.locale), }; } @@ -213,6 +229,7 @@ export class HuiEnergySolarGraphCard if (this._fetching) { return; } + const startDate = new Date(); startDate.setHours(0, 0, 0, 0); startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint @@ -252,13 +269,9 @@ export class HuiEnergySolarGraphCard ) as SolarSourceTypeEnergyPreference[]; const statisticsData = Object.values(this._data!); - const datasets: ChartDataset<"line">[] = []; + const datasets: ChartDataset<"bar">[] = []; let endTime: Date; - if (statisticsData.length === 0) { - return; - } - endTime = new Date( Math.max( ...statisticsData.map((stats) => @@ -267,28 +280,29 @@ export class HuiEnergySolarGraphCard ) ); - if (endTime > new Date()) { + if (!endTime || endTime > new Date()) { endTime = new Date(); } + const computedStyles = getComputedStyle(this); + const solarColor = computedStyles + .getPropertyValue("--energy-solar-color") + .trim(); + solarSources.forEach((source, idx) => { - const data: ChartDataset<"line">[] = []; + const data: ChartDataset<"bar" | "line">[] = []; const entity = this.hass.states[source.stat_energy_from]; const borderColor = idx > 0 - ? rgb2hex( - lab2rgb(labDarken(rgb2lab(hex2rgb(SOLAR_COLOR.border)), idx)) - ) - : SOLAR_COLOR.border; + ? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(solarColor)), idx))) + : solarColor; data.push({ label: `Production ${ entity ? computeStateName(entity) : source.stat_energy_from }`, - fill: true, - stepped: false, - borderColor: borderColor, + borderColor, backgroundColor: borderColor + "7F", data: [], }); @@ -309,7 +323,7 @@ export class HuiEnergySolarGraphCard if (prevStart === point.start) { continue; } - const value = Math.round((point.sum - prevValue) * 100) / 100; + const value = point.sum - prevValue; const date = new Date(point.start); data[0].data.push({ x: date.getTime(), @@ -343,12 +357,15 @@ export class HuiEnergySolarGraphCard if (forecastsData) { const forecast: ChartDataset<"line"> = { + type: "line", label: `Forecast ${ entity ? computeStateName(entity) : source.stat_energy_from }`, fill: false, stepped: false, - borderColor: "#000", + borderColor: computedStyles.getPropertyValue( + "--primary-text-color" + ), borderDash: [7, 5], pointRadius: 0, data: [], @@ -382,16 +399,14 @@ export class HuiEnergySolarGraphCard }; } - private _showAllForecastChanged(ev) { - this._showAllForecastData = ev.target.checked; - this._renderChart(); - } - static get styles(): CSSResultGroup { return css` ha-card { height: 100%; } + .card-header { + padding-bottom: 0; + } .content { padding: 16px; } diff --git a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts new file mode 100644 index 0000000000..83252fe9fe --- /dev/null +++ b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts @@ -0,0 +1,427 @@ +// @ts-ignore +import dataTableStyles from "@material/data-table/dist/mdc.data-table.min.css"; +import { + css, + CSSResultGroup, + html, + LitElement, + TemplateResult, + unsafeCSS, +} from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; +import { + rgb2hex, + lab2rgb, + rgb2lab, + hex2rgb, +} from "../../../../common/color/convert-color"; +import { labDarken } from "../../../../common/color/lab"; +import { computeStateName } from "../../../../common/entity/compute_state_name"; +import { formatNumber } from "../../../../common/string/format_number"; +import "../../../../components/chart/statistics-chart"; +import "../../../../components/ha-card"; +import { + EnergyInfo, + energySourcesByType, + getEnergyInfo, +} from "../../../../data/energy"; +import { + calculateStatisticSumGrowth, + fetchStatistics, + Statistics, +} from "../../../../data/history"; +import { HomeAssistant } from "../../../../types"; +import { LovelaceCard } from "../../types"; +import { EnergySourcesTableCardConfig } from "../types"; + +@customElement("hui-energy-sources-table-card") +export class HuiEnergySourcesTableCard + extends LitElement + implements LovelaceCard +{ + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _config?: EnergySourcesTableCardConfig; + + @state() private _stats?: Statistics; + + @state() private _energyInfo?: EnergyInfo; + + public getCardSize(): Promise | number { + return 3; + } + + public setConfig(config: EnergySourcesTableCardConfig): void { + this._config = config; + } + + public willUpdate() { + if (!this.hasUpdated) { + this._getEnergyInfo().then(() => this._getStatistics()); + } + } + + protected render(): TemplateResult { + if (!this.hass || !this._config) { + return html``; + } + + if (!this._stats) { + return html`Loading...`; + } + + let totalGrid = 0; + let totalSolar = 0; + let totalCost = 0; + + const types = energySourcesByType(this._config.prefs); + + const computedStyles = getComputedStyle(this); + const solarColor = computedStyles + .getPropertyValue("--energy-solar-color") + .trim(); + const returnColor = computedStyles + .getPropertyValue("--energy-grid-return-color") + .trim(); + const consumptionColor = computedStyles + .getPropertyValue("--energy-grid-consumption-color") + .trim(); + + const showCosts = + types.grid?.[0].flow_from.some( + (flow) => + flow.stat_cost || flow.entity_energy_price || flow.number_energy_price + ) || + types.grid?.[0].flow_to.some( + (flow) => + flow.stat_compensation || + flow.entity_energy_price || + flow.number_energy_price + ); + + return html` + ${this._config.title + ? html`

${this._config.title}

` + : ""} +
+
+ + + + + + + ${showCosts + ? html` ` + : ""} + + + + ${types.solar?.map((source, idx) => { + const entity = this.hass.states[source.stat_energy_from]; + const energy = + calculateStatisticSumGrowth( + this._stats![source.stat_energy_from] + ) || 0; + totalSolar += energy; + const color = + idx > 0 + ? rgb2hex( + lab2rgb(labDarken(rgb2lab(hex2rgb(solarColor)), idx)) + ) + : solarColor; + return html` + + + + ${showCosts + ? html`` + : ""} + `; + })} + ${types.solar + ? html` + + + + ${showCosts + ? html`` + : ""} + ` + : ""} + ${types.grid?.map( + (source) => html`${source.flow_from.map((flow, idx) => { + const entity = this.hass.states[flow.stat_energy_from]; + const energy = + calculateStatisticSumGrowth( + this._stats![flow.stat_energy_from] + ) || 0; + totalGrid += energy; + const cost_stat = + flow.stat_cost || + this._energyInfo!.cost_sensors[flow.stat_energy_from]; + const cost = cost_stat + ? calculateStatisticSumGrowth(this._stats![cost_stat]) || 0 + : null; + if (cost !== null) { + totalCost += cost; + } + const color = + idx > 0 + ? rgb2hex( + lab2rgb( + labDarken(rgb2lab(hex2rgb(consumptionColor)), idx) + ) + ) + : consumptionColor; + return html` + + + + ${showCosts + ? html` ` + : ""} + `; + })} + ${source.flow_to.map((flow, idx) => { + const entity = this.hass.states[flow.stat_energy_to]; + const energy = + (calculateStatisticSumGrowth( + this._stats![flow.stat_energy_to] + ) || 0) * -1; + totalGrid += energy; + const cost_stat = + flow.stat_compensation || + this._energyInfo!.cost_sensors[flow.stat_energy_to]; + const cost = cost_stat + ? (calculateStatisticSumGrowth(this._stats![cost_stat]) || + 0) * -1 + : null; + if (cost !== null) { + totalCost += cost; + } + const color = + idx > 0 + ? rgb2hex( + lab2rgb(labDarken(rgb2lab(hex2rgb(returnColor)), idx)) + ) + : returnColor; + return html` + + + + ${showCosts + ? html` ` + : ""} + `; + })}` + )} + ${types.grid + ? html` + + + + ${showCosts + ? html`` + : ""} + ` + : ""} + +
+ Source + + Energy + + Cost +
+
+
+ ${entity + ? computeStateName(entity) + : source.stat_energy_from} + + ${formatNumber(energy, this.hass.locale)} kWh +
+ Solar total + + ${formatNumber(totalSolar, this.hass.locale)} kWh +
+
+
+ ${entity + ? computeStateName(entity) + : flow.stat_energy_from} + + ${formatNumber(energy, this.hass.locale)} kWh + + ${cost !== null + ? formatNumber(cost, this.hass.locale, { + style: "currency", + currency: this.hass.config.currency!, + }) + : ""} +
+
+
+ ${entity ? computeStateName(entity) : flow.stat_energy_to} + + ${formatNumber(energy, this.hass.locale)} kWh + + ${cost !== null + ? formatNumber(cost, this.hass.locale, { + style: "currency", + currency: this.hass.config.currency!, + }) + : ""} +
Grid total + ${formatNumber(totalGrid, this.hass.locale)} kWh + + ${formatNumber(totalCost, this.hass.locale, { + style: "currency", + currency: this.hass.config.currency!, + })} +
+
+
+
`; + } + + private async _getEnergyInfo() { + this._energyInfo = await getEnergyInfo(this.hass); + } + + private async _getStatistics(): Promise { + const startDate = new Date(); + startDate.setHours(0, 0, 0, 0); + startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint + + const statistics: string[] = Object.values(this._energyInfo!.cost_sensors); + const prefs = this._config!.prefs; + for (const source of prefs.energy_sources) { + if (source.type === "solar") { + statistics.push(source.stat_energy_from); + } else { + // grid source + for (const flowFrom of source.flow_from) { + statistics.push(flowFrom.stat_energy_from); + if (flowFrom.stat_cost) { + statistics.push(flowFrom.stat_cost); + } + } + for (const flowTo of source.flow_to) { + statistics.push(flowTo.stat_energy_to); + if (flowTo.stat_compensation) { + statistics.push(flowTo.stat_compensation); + } + } + } + } + + this._stats = await fetchStatistics( + this.hass!, + startDate, + undefined, + statistics + ); + } + + static get styles(): CSSResultGroup { + return css` + ${unsafeCSS(dataTableStyles)} + .mdc-data-table { + width: 100%; + border: 0; + } + .mdc-data-table__header-cell, + .mdc-data-table__cell { + color: var(--primary-text-color); + border-bottom-color: var(--divider-color); + } + .mdc-data-table__row:not(.mdc-data-table__row--selected):hover { + background-color: rgba(var(--rgb-primary-text-color), 0.04); + } + .total { + --mdc-typography-body2-font-weight: 500; + } + .total .mdc-data-table__cell { + border-top: 1px solid var(--divider-color); + } + ha-card { + height: 100%; + } + .card-header { + padding-bottom: 0; + } + .content { + padding: 16px; + } + .has-header { + padding-top: 0; + } + .cell-bullet { + width: 32px; + padding-right: 0; + } + .bullet { + border-width: 1px; + border-style: solid; + border-radius: 4px; + height: 16px; + width: 32px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-energy-sources-table-card": HuiEnergySourcesTableCard; + } +} diff --git a/src/panels/lovelace/cards/hui-energy-summary-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts similarity index 70% rename from src/panels/lovelace/cards/hui-energy-summary-graph-card.ts rename to src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts index f0ceab258e..5c455a4865 100644 --- a/src/panels/lovelace/cards/hui-energy-summary-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts @@ -1,3 +1,4 @@ +import { ChartData, ChartDataset, ChartOptions } from "chart.js"; import { css, CSSResultGroup, @@ -8,47 +9,40 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import "../../../components/ha-card"; -import { ChartData, ChartDataset, ChartOptions } from "chart.js"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard } from "../types"; -import { EnergySummaryGraphCardConfig } from "./types"; -import { fetchStatistics, Statistics } from "../../../data/history"; import { hex2rgb, lab2rgb, rgb2hex, rgb2lab, -} from "../../../common/color/convert-color"; -import { labDarken } from "../../../common/color/lab"; -import { computeStateName } from "../../../common/entity/compute_state_name"; -import "../../../components/chart/ha-chart-base"; -import { round } from "../../../common/number/round"; +} from "../../../../common/color/convert-color"; +import { hexBlend } from "../../../../common/color/hex"; +import { labDarken } from "../../../../common/color/lab"; +import { computeStateName } from "../../../../common/entity/compute_state_name"; +import { + formatNumber, + numberFormatToLocale, +} from "../../../../common/string/format_number"; +import "../../../../components/chart/ha-chart-base"; +import "../../../../components/ha-card"; +import { fetchStatistics, Statistics } from "../../../../data/history"; +import { HomeAssistant } from "../../../../types"; +import { LovelaceCard } from "../../types"; +import { EnergyUsageGraphCardConfig } from "../types"; -const NEGATIVE = ["to_grid"]; -const ORDER = { - used_solar: 0, - from_grid: 100, - to_grid: 200, -}; -const COLORS = { - to_grid: { border: "#56d256", background: "#87ceab" }, - from_grid: { border: "#126A9A", background: "#88b5cd" }, - used_solar: { border: "#FF9800", background: "#ffcb80" }, -}; - -@customElement("hui-energy-summary-graph-card") -export class HuiEnergySummaryGraphCard +@customElement("hui-energy-usage-graph-card") +export class HuiEnergyUsageGraphCard extends LitElement implements LovelaceCard { @property({ attribute: false }) public hass!: HomeAssistant; - @state() private _config?: EnergySummaryGraphCardConfig; + @state() private _config?: EnergyUsageGraphCardConfig; @state() private _data?: Statistics; - @state() private _chartData?: ChartData; + @state() private _chartData: ChartData = { + datasets: [], + }; @state() private _chartOptions?: ChartOptions; @@ -82,7 +76,7 @@ export class HuiEnergySummaryGraphCard return 3; } - public setConfig(config: EnergySummaryGraphCardConfig): void { + public setConfig(config: EnergyUsageGraphCardConfig): void { this._config = config; } @@ -96,7 +90,7 @@ export class HuiEnergySummaryGraphCard } const oldConfig = changedProps.get("_config") as - | EnergySummaryGraphCardConfig + | EnergyUsageGraphCardConfig | undefined; if (oldConfig !== this._config) { @@ -116,31 +110,38 @@ export class HuiEnergySummaryGraphCard } return html` - + + ${this._config.title + ? html`

${this._config.title}

` + : ""}
- ${this._chartData - ? html`` - : ""} +
`; } private _createOptions() { + const startDate = new Date(); + startDate.setHours(0, 0, 0, 0); + const startTime = startDate.getTime(); + this._chartOptions = { parsing: false, animation: false, scales: { x: { type: "time", + suggestedMin: startTime, + suggestedMax: startTime + 24 * 60 * 60 * 1000, adapters: { date: { locale: this.hass.locale, @@ -159,15 +160,21 @@ export class HuiEnergySummaryGraphCard : {}, }, time: { - tooltipFormat: "datetimeseconds", + tooltipFormat: "datetime", }, + offset: true, }, y: { stacked: true, type: "linear", + title: { + display: true, + text: "kWh", + }, ticks: { beginAtZero: true, - callback: (value) => Math.abs(round(value)), + callback: (value) => + formatNumber(Math.abs(value), this.hass.locale), }, }, }, @@ -179,7 +186,10 @@ export class HuiEnergySummaryGraphCard filter: (val) => val.formattedValue !== "0", callbacks: { label: (context) => - `${context.dataset.label}: ${Math.abs(context.parsed.y)} kWh`, + `${context.dataset.label}: ${formatNumber( + Math.abs(context.parsed.y), + this.hass.locale + )} kWh`, footer: (contexts) => { let totalConsumed = 0; let totalReturned = 0; @@ -193,9 +203,19 @@ export class HuiEnergySummaryGraphCard } } return [ - `Total consumed: ${totalConsumed.toFixed(2)} kWh`, - `Total returned: ${totalReturned.toFixed(2)} kWh`, - ]; + totalConsumed + ? `Total consumed: ${formatNumber( + totalConsumed, + this.hass.locale + )} kWh` + : "", + totalReturned + ? `Total returned: ${formatNumber( + totalReturned, + this.hass.locale + )} kWh` + : "", + ].filter(Boolean); }, }, }, @@ -213,14 +233,13 @@ export class HuiEnergySummaryGraphCard mode: "nearest", }, elements: { - line: { - tension: 0.4, - borderWidth: 1.5, - }, + bar: { borderWidth: 1.5, borderRadius: 4 }, point: { hitRadius: 5, }, }, + // @ts-expect-error + locale: numberFormatToLocale(this.hass.locale), }; } @@ -280,7 +299,7 @@ export class HuiEnergySummaryGraphCard } const statisticsData = Object.values(this._data!); - const datasets: ChartDataset<"line">[] = []; + const datasets: ChartDataset<"bar">[] = []; let endTime: Date; if (statisticsData.length === 0) { @@ -304,6 +323,23 @@ export class HuiEnergySummaryGraphCard } = {}; const summedData: { [key: string]: { [start: string]: number } } = {}; + const computedStyles = getComputedStyle(this); + const colors = { + to_grid: computedStyles + .getPropertyValue("--energy-grid-return-color") + .trim(), + from_grid: computedStyles + .getPropertyValue("--energy-grid-consumption-color") + .trim(), + used_solar: computedStyles + .getPropertyValue("--energy-solar-color") + .trim(), + }; + + const backgroundColor = computedStyles + .getPropertyValue("--card-background-color") + .trim(); + Object.entries(statistics).forEach(([key, statIds]) => { const sum = ["solar", "to_grid"].includes(key); const add = key !== "solar"; @@ -330,7 +366,7 @@ export class HuiEnergySummaryGraphCard totalStats[stat.start] = stat.start in totalStats ? totalStats[stat.start] + val : val; } - if (add) { + if (add && !(stat.start in set)) { set[stat.start] = val; } prevValue = stat.sum; @@ -367,12 +403,13 @@ export class HuiEnergySummaryGraphCard const uniqueKeys = Array.from(new Set(allKeys)); Object.entries(combinedData).forEach(([type, sources]) => { - const negative = NEGATIVE.includes(type); - Object.entries(sources).forEach(([statId, source], idx) => { - const data: ChartDataset<"line">[] = []; + const data: ChartDataset<"bar">[] = []; const entity = this.hass.states[statId]; - const color = COLORS[type]; + const borderColor = + idx > 0 + ? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(colors[type])), idx))) + : colors[type]; data.push({ label: @@ -381,30 +418,20 @@ export class HuiEnergySummaryGraphCard : entity ? computeStateName(entity) : statId, - fill: true, - stepped: false, - order: ORDER[type] + idx, - borderColor: - idx > 0 - ? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(color.border)), idx))) - : color.border, - backgroundColor: - idx > 0 - ? rgb2hex( - lab2rgb(labDarken(rgb2lab(hex2rgb(color.background)), idx)) - ) - : color.background, - stack: negative ? "negative" : "positive", + borderColor, + backgroundColor: hexBlend(borderColor, backgroundColor, 50), + stack: "stack", data: [], }); // Process chart data. for (const key of uniqueKeys) { - const value = key in source ? Math.round(source[key] * 100) / 100 : 0; + const value = source[key] || 0; const date = new Date(key); + // @ts-expect-error data[0].data.push({ x: date.getTime(), - y: value && negative ? -1 * value : value, + y: value && type === "to_grid" ? -1 * value : value, }); } @@ -423,6 +450,9 @@ export class HuiEnergySummaryGraphCard ha-card { height: 100%; } + .card-header { + padding-bottom: 0; + } .content { padding: 16px; } @@ -435,6 +465,6 @@ export class HuiEnergySummaryGraphCard declare global { interface HTMLElementTagNameMap { - "hui-energy-summary-graph-card": HuiEnergySummaryGraphCard; + "hui-energy-usage-graph-card": HuiEnergyUsageGraphCard; } } diff --git a/src/panels/lovelace/cards/hui-energy-costs-table-card.ts b/src/panels/lovelace/cards/hui-energy-costs-table-card.ts deleted file mode 100644 index 9c3a843c0f..0000000000 --- a/src/panels/lovelace/cards/hui-energy-costs-table-card.ts +++ /dev/null @@ -1,252 +0,0 @@ -// @ts-ignore -import dataTableStyles from "@material/data-table/dist/mdc.data-table.min.css"; -import { - css, - CSSResultGroup, - html, - LitElement, - TemplateResult, - unsafeCSS, -} from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { computeStateName } from "../../../common/entity/compute_state_name"; -import { round } from "../../../common/number/round"; -import "../../../components/chart/statistics-chart"; -import "../../../components/ha-card"; -import { - EnergyInfo, - getEnergyInfo, - GridSourceTypeEnergyPreference, -} from "../../../data/energy"; -import { - calculateStatisticSumGrowth, - fetchStatistics, - Statistics, -} from "../../../data/history"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard } from "../types"; -import { EnergyDevicesGraphCardConfig } from "./types"; - -@customElement("hui-energy-costs-table-card") -export class HuiEnergyCostsTableCard - extends LitElement - implements LovelaceCard -{ - @property({ attribute: false }) public hass!: HomeAssistant; - - @state() private _config?: EnergyDevicesGraphCardConfig; - - @state() private _stats?: Statistics; - - @state() private _energyInfo?: EnergyInfo; - - public getCardSize(): Promise | number { - return 3; - } - - public setConfig(config: EnergyDevicesGraphCardConfig): void { - this._config = config; - } - - public willUpdate() { - if (!this.hasUpdated) { - this._getEnergyInfo().then(() => this._getStatistics()); - } - } - - protected render(): TemplateResult { - if (!this.hass || !this._config) { - return html``; - } - - if (!this._stats) { - return html`Loading...`; - } - - const source = this._config.prefs.energy_sources?.find( - (src) => src.type === "grid" - ) as GridSourceTypeEnergyPreference | undefined; - - if (!source) { - return html`No grid source found.`; - } - - let totalEnergy = 0; - let totalCost = 0; - - return html` -
-
- - - - - - - - - - ${source.flow_from.map((flow) => { - const entity = this.hass.states[flow.stat_energy_from]; - const energy = - calculateStatisticSumGrowth( - this._stats![flow.stat_energy_from] - ) || 0; - totalEnergy += energy; - const cost_stat = - flow.stat_cost || - this._energyInfo!.cost_sensors[flow.stat_energy_from]; - const cost = - (cost_stat && - calculateStatisticSumGrowth(this._stats![cost_stat])) || - 0; - totalCost += cost; - return html` - - - - `; - })} - ${source.flow_to.map((flow) => { - const entity = this.hass.states[flow.stat_energy_to]; - const energy = - (calculateStatisticSumGrowth( - this._stats![flow.stat_energy_to] - ) || 0) * -1; - totalEnergy += energy; - const cost_stat = - flow.stat_compensation || - this._energyInfo!.cost_sensors[flow.stat_energy_to]; - const cost = - ((cost_stat && - calculateStatisticSumGrowth(this._stats![cost_stat])) || - 0) * -1; - totalCost += cost; - return html` - - - - `; - })} - - - - - - -
- Grid source - - Energy - - Cost -
- ${entity ? computeStateName(entity) : flow.stat_energy_from} - - ${round(energy)} kWh - - ${this._config!.prefs.currency} ${cost.toFixed(2)} -
- ${entity ? computeStateName(entity) : flow.stat_energy_to} - - ${round(energy)} kWh - - ${this._config!.prefs.currency} ${cost.toFixed(2)} -
Total - ${round(totalEnergy)} kWh - - ${this._config!.prefs.currency} ${totalCost.toFixed(2)} -
-
-
-
`; - } - - private async _getEnergyInfo() { - this._energyInfo = await getEnergyInfo(this.hass); - } - - private async _getStatistics(): Promise { - const startDate = new Date(); - startDate.setHours(0, 0, 0, 0); - startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint - - const statistics: string[] = Object.values(this._energyInfo!.cost_sensors); - const prefs = this._config!.prefs; - for (const source of prefs.energy_sources) { - if (source.type === "solar") { - continue; - } - - // grid source - for (const flowFrom of source.flow_from) { - statistics.push(flowFrom.stat_energy_from); - if (flowFrom.stat_cost) { - statistics.push(flowFrom.stat_cost); - } - } - for (const flowTo of source.flow_to) { - statistics.push(flowTo.stat_energy_to); - if (flowTo.stat_compensation) { - statistics.push(flowTo.stat_compensation); - } - } - } - - this._stats = await fetchStatistics( - this.hass!, - startDate, - undefined, - statistics - ); - } - - static get styles(): CSSResultGroup { - return css` - ${unsafeCSS(dataTableStyles)} - .mdc-data-table { - width: 100%; - border: 0; - } - .total { - background-color: var(--primary-background-color); - --mdc-typography-body2-font-weight: 500; - } - ha-card { - height: 100%; - } - .content { - padding: 16px; - } - .has-header { - padding-top: 0; - } - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "hui-energy-costs-table-card": HuiEnergyCostsTableCard; - } -} diff --git a/src/panels/lovelace/cards/hui-energy-summary-card.ts b/src/panels/lovelace/cards/hui-energy-summary-card.ts deleted file mode 100644 index 0b5c47ccb3..0000000000 --- a/src/panels/lovelace/cards/hui-energy-summary-card.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { mdiCashMultiple, mdiSolarPower } from "@mdi/js"; -import { css, html, LitElement } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import "../../../components/ha-svg-icon"; -import { - energySourcesByType, - GridSourceTypeEnergyPreference, - SolarSourceTypeEnergyPreference, -} from "../../../data/energy"; -import { - calculateStatisticSumGrowth, - fetchStatistics, - Statistics, -} from "../../../data/history"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard } from "../types"; -import { EnergySummaryCardConfig } from "./types"; -import "../../../components/ha-card"; - -const renderSumStatHelper = ( - data: Statistics, - stats: string[], - unit: string -) => { - let totalGrowth = 0; - - for (const stat of stats) { - if (!(stat in data)) { - return "stat missing"; - } - const statGrowth = calculateStatisticSumGrowth(data[stat]); - - if (statGrowth === null) { - return "incomplete data"; - } - - totalGrowth += statGrowth; - } - - return `${totalGrowth.toFixed(2)} ${unit}`; -}; - -@customElement("hui-energy-summary-card") -class HuiEnergySummaryCard extends LitElement implements LovelaceCard { - @property({ attribute: false }) public hass?: HomeAssistant; - - @state() private _config?: EnergySummaryCardConfig; - - @state() private _data?: Statistics; - - private _fetching = false; - - public setConfig(config: EnergySummaryCardConfig): void { - this._config = config; - } - - public getCardSize(): Promise | number { - return 3; - } - - public willUpdate(changedProps) { - super.willUpdate(changedProps); - - if (!this._fetching && !this._data) { - this._getStatistics(); - } - } - - protected render() { - if (!this._config || !this.hass) { - return html``; - } - - const prefs = this._config!.prefs; - const types = energySourcesByType(prefs); - - const hasConsumption = types.grid !== undefined; - const hasProduction = types.solar !== undefined; - const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0; - const hasCost = - hasConsumption && - types.grid![0].flow_from.some((flow) => flow.stat_cost !== null); - - // total consumption = consumption_from_grid + solar_production - return_to_grid - - return html` - -
- ${!hasConsumption - ? "" - : html` -
- -
Total Consumption
-
- ${!this._data - ? "" - : renderSumStatHelper( - this._data, - types.grid![0].flow_from.map( - (flow) => flow.stat_energy_from - ), - "kWh" - )} -
-
- `} - ${!hasProduction - ? "" - : html` -
- -
Total Production
-
- ${!this._data - ? "" - : renderSumStatHelper( - this._data, - types.solar!.map((source) => source.stat_energy_from), - "kWh" - )} -
-
- `} - ${!hasReturnToGrid - ? "" - : html` -
- -
Production returned to grid
-
- ${!this._data - ? "" - : renderSumStatHelper( - this._data, - types.grid![0].flow_to.map( - (flow) => flow.stat_energy_to - ), - "kWh" - )} -
-
- `} - ${!hasReturnToGrid || !hasProduction - ? "" - : html` -
- -
Amount of produced power self used
-
- ${!this._data - ? "" - : this._renderSolarPowerConsumptionRatio( - types.solar![0], - types.grid![0] - )} -
-
- `} - ${!hasCost - ? "" - : html` -
- -
Total costs of today
-
- ${!this._data - ? "" - : renderSumStatHelper( - this._data, - types - .grid![0].flow_from.map((flow) => flow.stat_cost) - .filter(Boolean) as string[], - prefs.currency - )} -
-
- `} -
-
- `; - } - - // This is superduper temp. - private async _getStatistics(): Promise { - if (this._fetching) { - return; - } - const startDate = new Date(); - startDate.setHours(0, 0, 0, 0); - startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint - - this._fetching = true; - const statistics: string[] = []; - const prefs = this._config!.prefs; - for (const source of prefs.energy_sources) { - if (source.type === "solar") { - statistics.push(source.stat_energy_from); - // Use ws command to get solar forecast - - // if (source.stat_predicted_energy_from) { - // statistics.push(source.stat_predicted_energy_from); - // } - continue; - } - - // grid source - for (const flowFrom of source.flow_from) { - statistics.push(flowFrom.stat_energy_from); - if (flowFrom.stat_cost) { - statistics.push(flowFrom.stat_cost); - } - } - for (const flowTo of source.flow_to) { - statistics.push(flowTo.stat_energy_to); - } - } - - try { - this._data = await fetchStatistics( - this.hass!, - startDate, - undefined, - statistics - ); - } finally { - this._fetching = false; - } - } - - private _renderSolarPowerConsumptionRatio( - solarSource: SolarSourceTypeEnergyPreference, - gridSource: GridSourceTypeEnergyPreference - ) { - let returnToGrid = 0; - - for (const flowTo of gridSource.flow_to) { - if (!flowTo.stat_energy_to || !(flowTo.stat_energy_to in this._data!)) { - continue; - } - const flowReturned = calculateStatisticSumGrowth( - this._data![flowTo.stat_energy_to] - ); - if (flowReturned === null) { - return "incomplete return data"; - } - returnToGrid += flowReturned; - } - - if (!(solarSource.stat_energy_from in this._data!)) { - return "sun stat missing"; - } - - const production = calculateStatisticSumGrowth( - this._data![solarSource.stat_energy_from] - ); - - if (production === null) { - return "incomplete solar data"; - } - - if (production === 0) { - return "-"; - } - - const consumed = Math.max( - Math.min(((production - returnToGrid) / production) * 100, 100), - 0 - ); - - return `${consumed.toFixed(1)}%`; - } - - static styles = css` - .row { - display: flex; - align-items: center; - color: var(--primary-text-color); - } - ha-svg-icon { - padding: 8px; - color: var(--paper-item-icon-color); - } - div { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .label { - flex: 1; - margin-left: 16px; - } - .data { - } - `; -} - -declare global { - interface HTMLElementTagNameMap { - "hui-energy-summary-card": HuiEnergySummaryCard; - } -} diff --git a/src/panels/lovelace/cards/hui-energy-usage-card.ts b/src/panels/lovelace/cards/hui-energy-usage-card.ts deleted file mode 100644 index ce71cbf281..0000000000 --- a/src/panels/lovelace/cards/hui-energy-usage-card.ts +++ /dev/null @@ -1,333 +0,0 @@ -import { mdiHome, mdiLeaf, mdiSolarPower, mdiTransmissionTower } from "@mdi/js"; -import { css, html, LitElement } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { subscribeOne } from "../../../common/util/subscribe-one"; -import "../../../components/ha-svg-icon"; -import { getConfigEntries } from "../../../data/config_entries"; -import { energySourcesByType } from "../../../data/energy"; -import { subscribeEntityRegistry } from "../../../data/entity_registry"; -import { - calculateStatisticsSumGrowth, - fetchStatistics, - Statistics, -} from "../../../data/history"; -import { HomeAssistant } from "../../../types"; -import { LovelaceCard } from "../types"; -import { EnergySummaryCardConfig } from "./types"; -import "../../../components/ha-card"; -import { round } from "../../../common/number/round"; - -@customElement("hui-energy-usage-card") -class HuiEnergyUsageCard extends LitElement implements LovelaceCard { - @property({ attribute: false }) public hass!: HomeAssistant; - - @state() private _config?: EnergySummaryCardConfig; - - @state() private _stats?: Statistics; - - @state() private _co2SignalEntity?: string; - - private _fetching = false; - - public setConfig(config: EnergySummaryCardConfig): void { - this._config = config; - } - - public getCardSize(): Promise | number { - return 3; - } - - public willUpdate(changedProps) { - super.willUpdate(changedProps); - - if (!this._fetching && !this._stats) { - this._fetching = true; - Promise.all([this._getStatistics(), this._fetchCO2SignalEntity()]).then( - () => { - this._fetching = false; - } - ); - } - } - - protected render() { - if (!this._config) { - return html``; - } - - if (!this._stats) { - return html`Loading…`; - } - - const prefs = this._config!.prefs; - const types = energySourcesByType(prefs); - - // The strategy only includes this card if we have a grid. - const hasConsumption = true; - - const hasSolarProduction = types.solar !== undefined; - const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0; - - const totalGridConsumption = calculateStatisticsSumGrowth( - this._stats, - types.grid![0].flow_from.map((flow) => flow.stat_energy_from) - ); - - if (totalGridConsumption === null) { - return html`Total consumption couldn't be calculated`; - } - - let totalSolarProduction: number | null = null; - - if (hasSolarProduction) { - totalSolarProduction = calculateStatisticsSumGrowth( - this._stats, - types.solar!.map((source) => source.stat_energy_from) - ); - - if (totalSolarProduction === null) { - return html`Total production couldn't be calculated`; - } - } - - let productionReturnedToGrid: number | null = null; - - if (hasReturnToGrid) { - productionReturnedToGrid = calculateStatisticsSumGrowth( - this._stats, - types.grid![0].flow_to.map((flow) => flow.stat_energy_to) - ); - - if (productionReturnedToGrid === undefined) { - return html`Production returned to grid couldn't be calculated`; - } - } - - // total consumption = consumption_from_grid + solar_production - return_to_grid - - let co2percentage: number | undefined; - - if (this._co2SignalEntity) { - const co2State = this.hass.states[this._co2SignalEntity]; - if (co2State) { - co2percentage = Number(co2State.state); - if (isNaN(co2percentage)) { - co2percentage = undefined; - } - } - } - - // We are calculating low carbon consumption based on what we got from the grid - // minus what we gave back because what we gave back is low carbon - const relativeGridFlow = - totalGridConsumption - (productionReturnedToGrid || 0); - - let lowCarbonConsumption: number | undefined; - - if (co2percentage !== undefined) { - if (relativeGridFlow > 0) { - lowCarbonConsumption = round(relativeGridFlow * (co2percentage / 100)); - } else { - lowCarbonConsumption = 0; - } - } - - const totalConsumption = - totalGridConsumption + - (totalSolarProduction || 0) - - (productionReturnedToGrid || 0); - - const gridPctLowCarbon = - co2percentage === undefined ? 0 : co2percentage / 100; - const gridPctHighCarbon = 1 - gridPctLowCarbon; - - const homePctSolar = - ((totalSolarProduction || 0) - (productionReturnedToGrid || 0)) / - totalConsumption; - // When we know the ratio solar-grid, we can adjust the low/high carbon - // percentages to reflect that. - const homePctGridLowCarbon = gridPctLowCarbon * (1 - homePctSolar); - const homePctGridHighCarbon = gridPctHighCarbon * (1 - homePctSolar); - - return html` - -
-
- ${co2percentage === undefined - ? "" - : html` -
- Low-carbon -
- - ${co2percentage}% / ${round(lowCarbonConsumption!)} kWh -
-
- `} -
- Solar -
- - ${round(totalSolarProduction || 0)} kWh -
-
-
-
-
-
- - ${round(totalGridConsumption - (productionReturnedToGrid || 0))} - kWh -
    -
  • - Grid high carbon: ${round(gridPctHighCarbon * 100, 1)}% -
  • -
  • Grid low carbon: ${round(gridPctLowCarbon * 100, 1)}%
  • -
-
- Grid -
-
-
- - ${round(totalConsumption)} kWh -
    -
  • - Grid high carbon: ${round(homePctGridHighCarbon * 100)}% -
  • -
  • - Grid low carbon: ${round(homePctGridLowCarbon * 100)}% -
  • -
  • Solar: ${round(homePctSolar * 100)}%
  • -
-
- Home -
-
-
-
- `; - } - - private async _fetchCO2SignalEntity() { - const [configEntries, entityRegistryEntries] = await Promise.all([ - getConfigEntries(this.hass), - subscribeOne(this.hass.connection, subscribeEntityRegistry), - ]); - - const co2ConfigEntry = configEntries.find( - (entry) => entry.domain === "co2signal" - ); - - if (!co2ConfigEntry) { - return; - } - - for (const entry of entityRegistryEntries) { - if (entry.config_entry_id !== co2ConfigEntry.entry_id) { - continue; - } - - // The integration offers 2 entities. We want the % one. - const co2State = this.hass.states[entry.entity_id]; - if (!co2State || co2State.attributes.unit_of_measurement !== "%") { - continue; - } - - this._co2SignalEntity = co2State.entity_id; - break; - } - } - - private async _getStatistics(): Promise { - const startDate = new Date(); - startDate.setHours(0, 0, 0, 0); - startDate.setTime(startDate.getTime() - 1000 * 60 * 60); // subtract 1 hour to get a startpoint - - const statistics: string[] = []; - const prefs = this._config!.prefs; - for (const source of prefs.energy_sources) { - if (source.type === "solar") { - statistics.push(source.stat_energy_from); - continue; - } - - // grid source - for (const flowFrom of source.flow_from) { - statistics.push(flowFrom.stat_energy_from); - } - for (const flowTo of source.flow_to) { - statistics.push(flowTo.stat_energy_to); - } - } - - this._stats = await fetchStatistics( - this.hass!, - startDate, - undefined, - statistics - ); - } - - static styles = css` - :host { - --mdc-icon-size: 26px; - } - .row { - display: flex; - margin-bottom: 30px; - } - .row:last-child { - margin-bottom: 0; - } - .circle-container { - display: flex; - flex-direction: column; - align-items: center; - margin-right: 40px; - } - .circle { - width: 80px; - height: 80px; - border-radius: 50%; - border: 2px solid; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; - font-size: 12px; - } - .label { - color: var(--secondary-text-color); - font-size: 12px; - } - .circle-container:last-child { - margin-right: 0; - } - .circle ul { - display: none; - } - .low-carbon { - border-color: #0da035; - } - .low-carbon ha-svg-icon { - color: #0da035; - } - .solar { - border-color: #ff9800; - } - .grid { - border-color: #134763; - } - .circle-container.home { - margin-left: 120px; - } - `; -} - -declare global { - interface HTMLElementTagNameMap { - "hui-energy-usage-card": HuiEnergyUsageCard; - } -} diff --git a/src/panels/lovelace/cards/hui-gauge-card.ts b/src/panels/lovelace/cards/hui-gauge-card.ts index f87a9081e8..f8ff918de2 100644 --- a/src/panels/lovelace/cards/hui-gauge-card.ts +++ b/src/panels/lovelace/cards/hui-gauge-card.ts @@ -135,6 +135,8 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { style=${styleMap({ "--gauge-color": this._computeSeverity(entityState), })} + .needle=${this._config!.needle} + .levels=${this._config!.needle ? this._severityLevels() : undefined} >
${this._config.name || computeStateName(stateObj)} @@ -200,6 +202,20 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { return severityMap.normal; } + private _severityLevels() { + const sections = this._config!.severity; + + if (!sections) { + return [{ level: 0, stroke: severityMap.normal }]; + } + + const sectionsArray = Object.keys(sections); + return sectionsArray.map((severity) => ({ + level: sections[severity], + stroke: severityMap[severity], + })); + } + private _handleClick(): void { fireEvent(this, "hass-more-info", { entityId: this._config!.entity }); } diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index 50f6ab3627..d660dae680 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -133,6 +133,7 @@ class HuiMapCard extends LitElement implements LovelaceCard { this._config, this._configEntities )} + .zoom=${this._config.default_zoom ?? 14} .paths=${this._getHistoryPaths(this._config, this._history)} .darkMode=${this._config.dark_mode} > diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index b475097634..2b4e90522d 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -19,6 +19,15 @@ import { fetchStatistics, Statistics } from "../../../data/history"; @customElement("hui-statistics-graph-card") export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { + public static async getConfigElement() { + await import("../editor/config-elements/hui-statistics-graph-card-editor"); + return document.createElement("hui-statistics-graph-card-editor"); + } + + public static getStubConfig(): StatisticsGraphCardConfig { + return { type: "statistics-graph", entities: [] }; + } + @property({ attribute: false }) public hass?: HomeAssistant; @state() private _statistics?: Statistics; @@ -105,7 +114,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { | StatisticsGraphCardConfig | undefined; - if (oldConfig?.entities !== this._config.entities) { + if ( + oldConfig?.entities !== this._config.entities || + oldConfig?.days_to_show !== this._config.days_to_show + ) { this._getStatistics(); // statistics are created every hour clearInterval(this._interval); @@ -146,7 +158,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { return; } const startDate = new Date(); - startDate.setHours(-24 * (this._config!.days_to_show || 30)); + startDate.setTime( + startDate.getTime() - + 1000 * 60 * 60 * (24 * (this._config!.days_to_show || 30) + 1) + ); this._fetching = true; try { this._statistics = await fetchStatistics( diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index ebaecf3e9e..4894837543 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -92,31 +92,54 @@ export interface ButtonCardConfig extends LovelaceCardConfig { export interface EnergySummaryCardConfig extends LovelaceCardConfig { type: "energy-summary"; + title?: string; prefs: EnergyPreferences; } -export interface EnergySummaryGraphCardConfig extends LovelaceCardConfig { +export interface EnergyDistributionCardConfig extends LovelaceCardConfig { + type: "energy-distribution"; + title?: string; + prefs: EnergyPreferences; +} +export interface EnergyUsageGraphCardConfig extends LovelaceCardConfig { type: "energy-summary-graph"; + title?: string; prefs: EnergyPreferences; } export interface EnergySolarGraphCardConfig extends LovelaceCardConfig { type: "energy-solar-graph"; + title?: string; prefs: EnergyPreferences; } export interface EnergyDevicesGraphCardConfig extends LovelaceCardConfig { type: "energy-devices-graph"; + title?: string; + prefs: EnergyPreferences; +} + +export interface EnergySourcesTableCardConfig extends LovelaceCardConfig { + type: "energy-sources-table"; + title?: string; prefs: EnergyPreferences; } export interface EnergySolarGaugeCardConfig extends LovelaceCardConfig { type: "energy-solar-consumed-gauge"; + title?: string; + prefs: EnergyPreferences; +} + +export interface EnergyGridGaugeCardConfig extends LovelaceCardConfig { + type: "energy-grid-result-gauge"; + title?: string; prefs: EnergyPreferences; } export interface EnergyCarbonGaugeCardConfig extends LovelaceCardConfig { type: "energy-carbon-consumed-gauge"; + title?: string; prefs: EnergyPreferences; } @@ -147,6 +170,7 @@ export interface GaugeCardConfig extends LovelaceCardConfig { max?: number; severity?: SeverityConfig; theme?: string; + needle?: boolean; } export interface ConfigEntity extends EntityConfig { diff --git a/src/panels/lovelace/create-element/create-card-element.ts b/src/panels/lovelace/create-element/create-card-element.ts index 33871cfe40..e73a60619d 100644 --- a/src/panels/lovelace/create-element/create-card-element.ts +++ b/src/panels/lovelace/create-element/create-card-element.ts @@ -35,18 +35,22 @@ const LAZY_LOAD_TYPES = { "alarm-panel": () => import("../cards/hui-alarm-panel-card"), error: () => import("../cards/hui-error-card"), "empty-state": () => import("../cards/hui-empty-state-card"), - "energy-summary": () => import("../cards/hui-energy-summary-card"), - "energy-summary-graph": () => - import("../cards/hui-energy-summary-graph-card"), - "energy-solar-graph": () => import("../cards/hui-energy-solar-graph-card"), + "energy-usage-graph": () => + import("../cards/energy/hui-energy-usage-graph-card"), + "energy-solar-graph": () => + import("../cards/energy/hui-energy-solar-graph-card"), "energy-devices-graph": () => - import("../cards/hui-energy-devices-graph-card"), - "energy-costs-table": () => import("../cards/hui-energy-costs-table-card"), - "energy-usage": () => import("../cards/hui-energy-usage-card"), + import("../cards/energy/hui-energy-devices-graph-card"), + "energy-sources-table": () => + import("../cards/energy/hui-energy-sources-table-card"), + "energy-distribution": () => + import("../cards/energy/hui-energy-distribution-card"), "energy-solar-consumed-gauge": () => - import("../cards/hui-energy-solar-consumed-gauge-card"), + import("../cards/energy/hui-energy-solar-consumed-gauge-card"), + "energy-grid-neutrality-gauge": () => + import("../cards/energy/hui-energy-grid-neutrality-gauge-card"), "energy-carbon-consumed-gauge": () => - import("../cards/hui-energy-carbon-consumed-gauge-card"), + import("../cards/energy/hui-energy-carbon-consumed-gauge-card"), grid: () => import("../cards/hui-grid-card"), starting: () => import("../cards/hui-starting-card"), "entity-filter": () => import("../cards/hui-entity-filter-card"), diff --git a/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts index 16e1e049c9..3776dee7b0 100644 --- a/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts @@ -1,7 +1,7 @@ import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { assert, number, object, optional, string } from "superstruct"; +import { assert, boolean, number, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { computeRTLDirection } from "../../../../common/util/compute_rtl"; import "../../../../components/ha-formfield"; @@ -23,6 +23,7 @@ const cardConfigStruct = object({ max: optional(number()), severity: optional(object()), theme: optional(string()), + needle: optional(boolean()), }); const includeDomains = ["counter", "input_number", "number", "sensor"]; @@ -137,6 +138,17 @@ export class HuiGaugeCardEditor .configValue=${"max"} @value-changed="${this._valueChanged}" > + + cfg.entity) + : []; + } + + get _title(): string { + return this._config!.title || ""; + } + + get _days_to_show(): number { + return this._config!.days_to_show || 30; + } + + get _chart_type(): StatisticsGraphCardConfig["chart_type"] { + return this._config!.chart_type || "line"; + } + + get _stat_types(): StatisticType[] { + return this._config!.stat_types + ? Array.isArray(this._config!.stat_types) + ? this._config!.stat_types + : [this._config!.stat_types] + : ["mean", "min", "max", "sum"]; + } + + protected render(): TemplateResult { + if (!this.hass || !this._config) { + return html``; + } + + return html` +
+ + +

Show stat types:

+
+ + + + + + + + + + + + +
+
+

Chart type:

+ + + + + + +
+ +
+ `; + } + + private _chartTypeChanged(ev: CustomEvent) { + const input = ev.currentTarget as HaRadio; + fireEvent(this, "config-changed", { + config: { ...this._config!, chart_type: input.value }, + }); + } + + private _statTypesChanged(ev) { + const name = ev.currentTarget.name; + const checked = ev.currentTarget.checked; + if (checked) { + fireEvent(this, "config-changed", { + config: { ...this._config!, stat_types: [...this._stat_types, name] }, + }); + return; + } + const statTypes = [...this._stat_types]; + fireEvent(this, "config-changed", { + config: { + ...this._config!, + stat_types: statTypes.filter((stat) => stat !== name), + }, + }); + } + + private _valueChanged(ev: CustomEvent): void { + if (!this._config || !this.hass) { + return; + } + const target = ev.target! as EditorTarget; + + const newValue = ev.detail?.value || target.value; + + if (this[`_${target.configValue}`] === newValue) { + return; + } + + if (newValue === "") { + this._config = { ...this._config }; + delete this._config[target.configValue!]; + } else { + let value: any = newValue; + if (target.type === "number") { + value = Number(value); + } + this._config = { + ...this._config, + [target.configValue!]: value, + }; + } + + fireEvent(this, "config-changed", { config: this._config }); + } + + static get styles(): CSSResultGroup { + return configElementStyle; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-statistics-graph-card-editor": HuiStatisticsGraphCardEditor; + } +} diff --git a/src/panels/lovelace/editor/lovelace-cards.ts b/src/panels/lovelace/editor/lovelace-cards.ts index 6e29ec6dfd..2a9beb0371 100644 --- a/src/panels/lovelace/editor/lovelace-cards.ts +++ b/src/panels/lovelace/editor/lovelace-cards.ts @@ -33,6 +33,10 @@ export const coreCards: Card[] = [ type: "history-graph", showElement: true, }, + { + type: "statistics-graph", + showElement: false, + }, { type: "humidifier", showElement: true, diff --git a/src/panels/lovelace/views/hui-masonry-view.ts b/src/panels/lovelace/views/hui-masonry-view.ts index 5ce2edf13e..f8e7e1bbe8 100644 --- a/src/panels/lovelace/views/hui-masonry-view.ts +++ b/src/panels/lovelace/views/hui-masonry-view.ts @@ -64,6 +64,8 @@ export class MasonryView extends LitElement implements LovelaceViewElement { private _mqls?: MediaQueryList[]; + private _mqlListenerRef?: () => void; + public constructor() { super(); this.addEventListener("iron-resize", (ev: Event) => ev.stopPropagation()); @@ -77,8 +79,9 @@ export class MasonryView extends LitElement implements LovelaceViewElement { public disconnectedCallback() { super.disconnectedCallback(); this._mqls?.forEach((mql) => { - mql.removeListener(this._updateColumns); + mql.removeListener(this._mqlListenerRef!); }); + this._mqlListenerRef = undefined; this._mqls = undefined; } @@ -112,7 +115,10 @@ export class MasonryView extends LitElement implements LovelaceViewElement { private _initMqls() { this._mqls = [300, 600, 900, 1200].map((width) => { const mql = window.matchMedia(`(min-width: ${width}px)`); - mql.addListener(this._updateColumns.bind(this)); + if (!this._mqlListenerRef) { + this._mqlListenerRef = this._updateColumns.bind(this); + } + mql.addListener(this._mqlListenerRef); return mql; }); } diff --git a/src/panels/lovelace/views/hui-sidebar-view.ts b/src/panels/lovelace/views/hui-sidebar-view.ts index 0b8bd652c5..5792b52b2b 100644 --- a/src/panels/lovelace/views/hui-sidebar-view.ts +++ b/src/panels/lovelace/views/hui-sidebar-view.ts @@ -17,10 +17,9 @@ import type { } from "../../../data/lovelace"; import type { HomeAssistant } from "../../../types"; import { HuiErrorCard } from "../cards/hui-error-card"; +import { HuiCardOptions } from "../components/hui-card-options"; import type { Lovelace, LovelaceCard } from "../types"; -let editCodeLoaded = false; - export class SideBarView extends LitElement implements LovelaceViewElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -36,6 +35,24 @@ export class SideBarView extends LitElement implements LovelaceViewElement { @state() private _config?: LovelaceViewConfig; + private _mqlListenerRef?: () => void; + + private _mql?: MediaQueryList; + + public connectedCallback() { + super.connectedCallback(); + this._mql = window.matchMedia("(min-width: 760px)"); + this._mqlListenerRef = this._createCards.bind(this); + this._mql.addListener(this._mqlListenerRef); + } + + public disconnectedCallback() { + super.disconnectedCallback(); + this._mql?.removeListener(this._mqlListenerRef!); + this._mqlListenerRef = undefined; + this._mql = undefined; + } + public setConfig(config: LovelaceViewConfig): void { this._config = config; } @@ -43,8 +60,7 @@ export class SideBarView extends LitElement implements LovelaceViewElement { public willUpdate(changedProperties: PropertyValues): void { super.willUpdate(changedProperties); - if (this.lovelace?.editMode && !editCodeLoaded) { - editCodeLoaded = true; + if (this.lovelace?.editMode) { import("./default-view-editable"); } @@ -71,7 +87,8 @@ export class SideBarView extends LitElement implements LovelaceViewElement { protected render(): TemplateResult { return html` - ${this.lovelace?.editMode && this.cards.length === 0 +
+ ${this.lovelace?.editMode ? html` { - this.renderRoot.appendChild(mainDiv); - this.renderRoot.appendChild(sidebarDiv); + const container = this.renderRoot.querySelector(".container")!; + container.appendChild(mainDiv); + container.appendChild(sidebarDiv); }); } this.cards.forEach((card: LovelaceCard, idx) => { const cardConfig = this._config?.cards?.[idx]; + let element: LovelaceCard | HuiCardOptions; if (this.isStrategy || !this.lovelace?.editMode) { card.editMode = false; - if (cardConfig?.view_layout?.position !== "sidebar") { - mainDiv.appendChild(card); - } else { - sidebarDiv.appendChild(card); - } - return; - } - - const wrapper = document.createElement("hui-card-options"); - wrapper.hass = this.hass; - wrapper.lovelace = this.lovelace; - wrapper.path = [this.index!, 0]; - card.editMode = true; - wrapper.appendChild(card); - if (cardConfig?.view_layout?.position !== "sidebar") { - mainDiv.appendChild(card); + element = card; } else { - sidebarDiv.appendChild(card); + element = document.createElement("hui-card-options"); + element.hass = this.hass; + element.lovelace = this.lovelace; + element.path = [this.index!, idx]; + card.editMode = true; + element.appendChild(card); + } + if (cardConfig?.view_layout?.position !== "sidebar") { + mainDiv.appendChild(element); + } else { + sidebarDiv.appendChild(element); } }); } @@ -147,13 +168,17 @@ export class SideBarView extends LitElement implements LovelaceViewElement { static get styles(): CSSResultGroup { return css` :host { - display: flex; + display: block; padding-top: 4px; - margin-left: 4px; - margin-right: 4px; height: 100%; box-sizing: border-box; + } + + .container { + display: flex; justify-content: center; + margin-left: 4px; + margin-right: 4px; } #main { @@ -166,27 +191,18 @@ export class SideBarView extends LitElement implements LovelaceViewElement { max-width: 380px; } - :host > div { + .container > div { min-width: 0; box-sizing: border-box; } - :host > div > * { + .container > div > * { display: block; margin: var(--masonry-view-card-margin, 4px 4px 8px); } - @media (max-width: 760px) { - :host { - flex-direction: column; - } - #sidebar { - max-width: unset; - } - } - @media (max-width: 500px) { - :host > div > * { + .container > div > * { margin-left: 0; margin-right: 0; } diff --git a/src/resources/ha-style.ts b/src/resources/ha-style.ts index a4ff54ff8f..9416b15f99 100644 --- a/src/resources/ha-style.ts +++ b/src/resources/ha-style.ts @@ -82,6 +82,14 @@ documentContainer.innerHTML = ` --state-climate-dry-color: #efbd07; --state-climate-idle-color: #8a8a8a; + /* energy */ + --energy-grid-consumption-color: #126a9a; + --energy-grid-return-color: #673ab7; + --energy-solar-color: #ff9800; + --energy-non-fossil-color: #0f9d58; + + --rgb-energy-solar-color: 255, 152, 0; + /* Paper-styles color.html dependency is stripped on build. When a default paper-style color is used, it needs to be copied diff --git a/src/resources/styles.ts b/src/resources/styles.ts index f2b2494416..716de34179 100644 --- a/src/resources/styles.ts +++ b/src/resources/styles.ts @@ -31,6 +31,7 @@ export const darkStyles = { "codemirror-property": "#C792EA", "codemirror-qualifier": "#DECB6B", "codemirror-type": "#DECB6B", + "energy-grid-return-color": "#b39bdb", }; export const derivedStyles = { diff --git a/src/translations/en.json b/src/translations/en.json index c434885e8f..3b02d20f33 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -390,6 +390,13 @@ "failed_create_area": "Failed to create area." } }, + "statistic-picker": { + "statistic": "Statistic", + "no_statistics": "You don't have any statistics", + "no_match": "No matching statistics found", + "missing_entity": "Why is my entity not listed?", + "learn_more": "Learn more about statistics" + }, "addon-picker": { "addon": "Add-on", "error": { @@ -992,8 +999,9 @@ "description": "Monitor your energy production and consumption", "currency": "", "grid": { - "title": "Configure grid", - "sub": "Configure the different tarrifs for the energy you consume from the grid, and, if you return energy to the grid, the energy you return to the grid.", + "title": "Electricity grid", + "sub": "Configure the amount of energy that you consume from the grid and, if you produce energy, give back to the grid. This allows Home Assistant to track your whole home energy usage.", + "learn_more": "More information on how to get started.", "flow_dialog": { "from": { "header": "Configure grid consumption", @@ -1002,7 +1010,7 @@ "cost_para": "Select how Home Assistant should keep track of the costs of the consumed energy.", "no_cost": "Do not track costs", "cost_stat": "Use an entity tracking the total costs", - "cost_stat_input": "Entity keeping track of the total costs", + "cost_stat_input": "Total Costs Entity", "cost_entity": "Use an entity with current price", "cost_entity_input": "Entity with the current price", "cost_number": "Use a static price", @@ -1016,7 +1024,7 @@ "cost_para": "Do you get money back when you return energy to the grid?", "no_cost": "I do not get money back", "cost_stat": "Use an entity tracking the total recieved money", - "cost_stat_input": "Entity keeping track of the total of received money", + "cost_stat_input": "Total Compensation Entity", "cost_entity": "Use an entity with current rate", "cost_entity_input": "Entity with the current rate", "cost_number": "Use a static rate", @@ -1026,12 +1034,17 @@ } }, "solar": { + "title": "Solar Panels", + "sub": "Let Home Assistant monitor your solar panels and give you insight on their performance.", + "learn_more": "More information on how to get started.", "stat_production": "Your solar energy production", "stat_return_to_grid": "Solar energy returned to the grid", "stat_predicted_production": "Prediction of your solar energy production" }, "device_consumption": { - "description": "If you measure the power consumption of individual devices, you can select the entities with the power consumption below", + "title": "Individual devices", + "sub": "Tracking the energy usage of individual devices allows Home Assistant to break down your energy usage by device.", + "learn_more": "More information on how to get started.", "add_stat": "Pick entity to track energy of", "selected_stat": "Tracking energy for" } @@ -1084,9 +1097,11 @@ "unit_system_metric": "Metric", "imperial_example": "Fahrenheit, pounds", "metric_example": "Celsius, kilograms", + "find_currency_value": "Find your value", "save_button": "Save", "external_url": "External URL", - "internal_url": "Internal URL" + "internal_url": "Internal URL", + "currency": "Currency" } } } @@ -2688,7 +2703,9 @@ "node_status": "Node Status", "node_ready": "Node Ready", "device_config": "Configure Device", - "reinterview_device": "Re-interview Device" + "reinterview_device": "Re-interview Device", + "heal_node": "Heal Node", + "remove_failed": "Remove Failed Device" }, "node_config": { "header": "Z-Wave Device Configuration", @@ -2741,6 +2758,14 @@ "exclusion_failed": "The node could not be removed. Please check the logs for more information.", "exclusion_finished": "Node {id} has been removed from your Z-Wave network." }, + "remove_failed_node": { + "title": "Remove a Failed Z-Wave Device", + "introduction": "Remove a failed device from your Z-Wave network. Use this if you are unable to exclude a device normally because it is broken.", + "remove_device": "Remove Device", + "in_progress": "The device removal is in progress.", + "removal_finished": "Node {id} has been removed from your Z-Wave network.", + "removal_failed": "The device could not be removed from your Z-Wave network." + }, "reinterview_node": { "title": "Re-interview a Z-Wave Device", "introduction": "Re-interview a device on your Z-Wave network. Use this feature if your device has missing or incorrect functionality.", @@ -2763,6 +2788,17 @@ "healing_failed": "Healing failed. Additional information may be available in the logs.", "healing_cancelled": "Network healing has been cancelled." }, + "heal_node": { + "title": "Heal a Z-Wave Device", + "introduction": "Tell {device} to update its routes back to the controller. This can help with communication issues if you have recently moved the device or your controller.", + "traffic_warning": "The healing process generates a large amount of traffic on the Z-Wave network. This may cause devices to respond slowly (or not at all) while the heal is in progress.", + "start_heal": "Heal Device", + "healing_failed": "{device} could not be healed.", + "healing_failed_check_logs": "Additional information may be available in the logs.", + "healing_complete": "{device} has been healed.", + "in_progress": "{device} healing is in progress.", + "network_heal_in_progress": "A Z-Wave network heal is already in progress. Please wait for it to finish before healing an individual device." + }, "logs": { "title": "Z-Wave JS Logs", "log_level": "Log Level", @@ -3059,6 +3095,7 @@ }, "gauge": { "name": "Gauge", + "needle_gauge": "Display as needle gauge?", "severity": { "define": "Define Severity?", "green": "Green", @@ -3086,6 +3123,10 @@ "name": "History Graph", "description": "The History Graph card allows you to display a graph for each of the entities listed." }, + "statistics-graph": { + "name": "Statistics Graph", + "description": "The Statistics Graph card allows you to display a graph of the statistics for each of the entities listed." + }, "horizontal-stack": { "name": "Horizontal Stack", "description": "The Horizontal Stack card allows you to stack together multiple cards, so they always sit next to each other in the space of one column." @@ -3112,6 +3153,7 @@ "entity": "Entity", "hold_action": "Hold Action", "hours_to_show": "Hours to Show", + "days_to_show": "Days to Show", "icon": "Icon", "icon_height": "Icon Height", "image": "Image Path", @@ -3685,8 +3727,6 @@ }, "energy": { "setup": { - "header": "Setup your energy dashboard", - "slogan": "The world is heating up. Together we can fix that.", "next": "Next", "back": "Back", "done": "Show me my energy dashboard!" diff --git a/src/util/hass-attributes-util.ts b/src/util/hass-attributes-util.ts index 7a9bc9c232..73fa60cd4e 100644 --- a/src/util/hass-attributes-util.ts +++ b/src/util/hass-attributes-util.ts @@ -60,6 +60,7 @@ const hassAttributeUtil = { "power", "power_factor", "pressure", + "monetary", "signal_strength", "temperature", "timestamp", diff --git a/test-mocha/data/history.spec.ts b/test-mocha/data/history.spec.ts new file mode 100644 index 0000000000..8ba9efd651 --- /dev/null +++ b/test-mocha/data/history.spec.ts @@ -0,0 +1,77 @@ +import { assert } from "chai"; + +import { calculateStatisticsSumGrowthWithPercentage } from "../../src/data/history"; + +describe("calculateStatisticsSumGrowthWithPercentage", () => { + it("Returns null if not enough values", async () => { + assert.strictEqual( + calculateStatisticsSumGrowthWithPercentage([], []), + null + ); + }); + + it("Returns null if not enough values", async () => { + assert.strictEqual( + calculateStatisticsSumGrowthWithPercentage( + [ + { + statistic_id: "sensor.carbon_intensity", + start: "2021-07-28T05:00:00Z", + last_reset: null, + max: 75, + mean: 50, + min: 25, + sum: null, + state: null, + }, + { + statistic_id: "sensor.carbon_intensity", + start: "2021-07-28T07:00:00Z", + last_reset: null, + max: 100, + mean: 75, + min: 50, + sum: null, + state: null, + }, + ], + [ + [ + { + statistic_id: "sensor.peak_consumption", + start: "2021-07-28T04:00:00Z", + last_reset: null, + max: null, + mean: null, + min: null, + sum: 50, + state: null, + }, + { + statistic_id: "sensor.peak_consumption", + start: "2021-07-28T05:00:00Z", + last_reset: null, + max: null, + mean: null, + min: null, + sum: 100, + state: null, + }, + { + statistic_id: "sensor.peak_consumption", + start: "2021-07-28T07:00:00Z", + last_reset: null, + max: null, + mean: null, + min: null, + sum: 200, + state: null, + }, + ], + [], + ] + ), + 100 + ); + }); +}); diff --git a/yarn.lock b/yarn.lock index 63bd07e770..50b861edd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1876,795 +1876,802 @@ __metadata: languageName: node linkType: hard -"@material/animation@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/animation@npm:12.0.0-canary.1a8d06483.0" +"@material/animation@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/animation@npm:12.0.0-canary.22d29cbb4.0" dependencies: tslib: ^2.1.0 - checksum: dda21b9a4c8660e675876951ce8484d501e1b19ffc85eebb4dc0ad530f382c733fc579ac9927dd53fa4c3e050057da35813445d63b348a06f55342678243ae32 + checksum: b618c92d61b82cb989c7a714d041104f12ea50b00de58dd9508ce2113f8eb776c1c6f4e69cca2a76b02c69ba955d9663e5bfcdca7f9523968ea733f66b8bcdeb languageName: node linkType: hard -"@material/base@npm:12.0.0-canary.1a8d06483.0, @material/base@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/base@npm:12.0.0-canary.1a8d06483.0" +"@material/base@npm:12.0.0-canary.22d29cbb4.0, @material/base@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/base@npm:12.0.0-canary.22d29cbb4.0" dependencies: tslib: ^2.1.0 - checksum: 36fcfbb6401a4a044ad4377ac4bee54cc3d657c554ba060a8d70d407d3e928b939b13b5085040561e2adec1598c5e191c3e6cb17a63f427447cbe40012432c10 + checksum: a2fc63d0080f488abe5b79f4ff6acbc58168c5fc66d35bc57c3fb085d12d5ad79ef98101159726655c896a353f6333bede4524470983496cd5a4bd845795b034 languageName: node linkType: hard -"@material/button@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/button@npm:12.0.0-canary.1a8d06483.0" +"@material/button@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/button@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/elevation": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/touch-target": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/touch-target": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: a05725f747eebafcff09e5600a2b1586f59f28d10d75bfeed7228eea799cc4f5f1f4a188058a93bdf90ead87d6a2a6630cb4a01d00f477955a1c3129e677895c + checksum: 8c5eff310a7ce64fd5b8e4efd35c3d11162f8c00925d26c988267ad8de2e2445e5d844a2dc958f2b880f3d14fbd68223d68222a1734942e57e756f4530d27e26 languageName: node linkType: hard -"@material/checkbox@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/checkbox@npm:12.0.0-canary.1a8d06483.0" +"@material/checkbox@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/checkbox@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/touch-target": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/touch-target": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: efff30de456725b314cdb0358be5ef56d34dfdf3022cae5f308e7a9dbcc6850855bf684ef20c9b78de5c7ef69b436d879357c7660a861ddb88b2d4c04cbda037 + checksum: 6fe21a7554b379887ddaf4ea6e78b6e9f46f259f08a688b28f2869f3fb229c334881b6f716947f2f93b0f0cc2229fd7b482cb7a56a12e4bb3680cc71ed30f9a5 languageName: node linkType: hard -"@material/chips@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/chips@npm:12.0.0-canary.1a8d06483.0" +"@material/chips@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/chips@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/checkbox": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/elevation": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/touch-target": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/checkbox": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/touch-target": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 45cffe1991e63edc9aa05bc3e655688b9c5cef76e8d5ebc0813f76123f0e4d5d5d152f3197d96736485f577bf5176f29cc2ae38030342738b8ffa3254093f7c1 + checksum: ea7b9a3e2c6f2a9e56dd521e0838c7a0f4866ccf5a15cb4fb02d453b772ef298c95b256a7088d1b64aee72b8b25b4af6d0b1be50562611223d1847a902dda972 languageName: node linkType: hard -"@material/circular-progress@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/circular-progress@npm:12.0.0-canary.1a8d06483.0" +"@material/circular-progress@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/circular-progress@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/progress-indicator": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/progress-indicator": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 4bfd4bd6438ac19e88a968318b8714efa3f65bf6f939750e7b904e4d56619bb089896580fb829f9c83cd991e947c70efa6ff4acdd06b0e741cd2ca639062ccff + checksum: d9e1187cbc522f81c2dd38854f5c39ad1cac572881df1b742bd9adc80f0bbdb00644c3e8b4ec5b608ef4ee164e67b0bfc0fe0f5528d8a987117dacaeaeaadf2c languageName: node linkType: hard -"@material/data-table@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/data-table@npm:12.0.0-canary.1a8d06483.0" +"@material/data-table@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/data-table@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/checkbox": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/elevation": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/icon-button": 12.0.0-canary.1a8d06483.0 - "@material/linear-progress": 12.0.0-canary.1a8d06483.0 - "@material/list": 12.0.0-canary.1a8d06483.0 - "@material/menu": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/select": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/touch-target": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/checkbox": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/icon-button": 12.0.0-canary.22d29cbb4.0 + "@material/linear-progress": 12.0.0-canary.22d29cbb4.0 + "@material/list": 12.0.0-canary.22d29cbb4.0 + "@material/menu": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/select": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/touch-target": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 61a5abbc681742a1cd259fa52bda067324313658d438cbef86ee35c6a6858ffb41fa082d592fa8c8390104507e1c2629d0e21f7552598ac4ad2d8d3a00c768cb + checksum: a4ff3483f9baf5bc33342503ca30b26d18cdf20335d6138ab1d8f49f2a690cc2f5efecadd44776b537d95d3bee5bfb0942ba81a8e50f0734ee5892eca2db8a43 languageName: node linkType: hard -"@material/density@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/density@npm:12.0.0-canary.1a8d06483.0" +"@material/density@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/density@npm:12.0.0-canary.22d29cbb4.0" dependencies: tslib: ^2.1.0 - checksum: 1279f5a000f43bbb03d43c797a198381110da9baba44757889a69deea580aa25e4f21ffcad2e87a122b6f7c209592fb0a3475e2f9de1b4c2f13e5576f142cb4e + checksum: 29cb900ae2c6ba598a6cd37ea7a1125654b8fcf32d386046f70e2bfdf6fe80ee980e631b77268ad237e926cbe7c8b4723faad309f6b507576efbc21014deee0e languageName: node linkType: hard -"@material/dialog@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/dialog@npm:12.0.0-canary.1a8d06483.0" +"@material/dialog@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/dialog@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/button": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/elevation": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/icon-button": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/touch-target": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/button": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/icon-button": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/touch-target": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: c77f329392e382d6af8c4196f6795c85a77b03e1a8117d9760eec1c1f0c282ea5a821378cfa6ad4f06bec8250c3867df59f3051ccea356912b4a5235a1aa2592 + checksum: 8f30743c42129c3211d04ec4436e745a5fed1e27b09f595f5cba1e9db18b3a16ad1bedfef04c6f3701963a9e178ae9920bf31e3c12f16140d10aa2a3cdf3f392 languageName: node linkType: hard -"@material/dom@npm:12.0.0-canary.1a8d06483.0, @material/dom@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/dom@npm:12.0.0-canary.1a8d06483.0" +"@material/dom@npm:12.0.0-canary.22d29cbb4.0, @material/dom@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/dom@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 5865b2cec09fe111cedb1126af1db346e108327e6de62cf1fe8709db389df3e73bcf7aee6a25d1ed5b1bf24642f8ef16129208c56448990c794b0227d199add3 + checksum: 9199392b23c62bb3edce88b52cd14b785568b689cfa397fb167d67d4c6c4fc401079b78840ebd64bbb8c3db3ca2f98ec0d4bbccbfd9ac7340a3862813fa3aaf3 languageName: node linkType: hard -"@material/elevation@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/elevation@npm:12.0.0-canary.1a8d06483.0" +"@material/elevation@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/elevation@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 9163fc8c7967e327ff0d61548378925f8bc7a06ad7579207cdd6f5ba3d833af0114d533d91c0fb7eafdef244669102c37b8a0cbb626322f68a1ef6d55f203270 + checksum: 83542f35f234baf10b6aac1765be644e3b3b653fa4510a30c4456d487ab41bb13b8b61877ea2a3c672ab7bda711af89d33c37b19a18861f2cd410026b854d946 languageName: node linkType: hard -"@material/feature-targeting@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/feature-targeting@npm:12.0.0-canary.1a8d06483.0" +"@material/feature-targeting@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/feature-targeting@npm:12.0.0-canary.22d29cbb4.0" dependencies: tslib: ^2.1.0 - checksum: 049b6cbb4ed68063ac8cd0f0ea3843fe305e52b9cae4fbdece7f7b5211d71ba50e01702b07804e8a7cd60a32bd93b98dbefdd9796995abb2f901b1fab29a52f5 + checksum: 6b79778485e32b4032e78fca75b6557b5c1357209e42178a0a15177567f93cb204e83d9d326990057a6eb5d2c92bfe05948a72caa4626c9324a82c07893ffa64 languageName: node linkType: hard -"@material/floating-label@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/floating-label@npm:12.0.0-canary.1a8d06483.0" +"@material/floating-label@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/floating-label@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 49a926db76396dbe5b4b00e4526982afdc059617eaad7abbeee7a48fb05b19924feb512a5b1584e0e3a588fe9b2e572f7da60010810f2a72c8ab51ef29904344 + checksum: 609c8fd91c969023d6e7be37aa74a2ab47301c6b5e5e84185f90292fb28e8092c85c779a447ebb7aaf615f2b16210f6fd39e16cf72c5a7de025436efd76636f2 languageName: node linkType: hard -"@material/form-field@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/form-field@npm:12.0.0-canary.1a8d06483.0" +"@material/form-field@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/form-field@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 66591c0e02d1a388c51b3e84464a832e6027d1d53ea90c7f2c381e3518576056320887c724d474093a7e4ab0e47df19d6b4190ef15bd5db6d7301534c1bc8c11 + checksum: e628ca67c7061cd11fddbf259d39bc2071a604f9ac9b2187239b8c954702d17ee3b31b483005e70c03acfa661a45789659221d53afbed7073858972f24db8296 languageName: node linkType: hard -"@material/icon-button@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/icon-button@npm:12.0.0-canary.1a8d06483.0" +"@material/icon-button@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/icon-button@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/touch-target": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: f6c8fd25355a39995fed3afe87d4383cd4ad7dfd93b11ea2000d12294b84c3d6c046b64033fb211c13ab9e515d551e178ec841c3ae7ffdf9dc4824530836d52a + checksum: e743d1cd17e74fdeb9b64c17e7a22edc4c5d23e444e1217845d23cf12d26448eb2a5b84c72f289f164bff3811f24b691d5ba87b7734b202c36b71790209cdb09 languageName: node linkType: hard -"@material/line-ripple@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/line-ripple@npm:12.0.0-canary.1a8d06483.0" +"@material/line-ripple@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/line-ripple@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: e5a7d102819c5d23089afc3d9e85c771ed9f9b7eddce2a0339bb4ed37f3d0d10c36f90370855f9bef2f6e66d421ec082f732f7d930b503779615072299468002 + checksum: dbb5d43fbff73d419e8cddcd66bb4ee70fff36588ee40fae8ffc3633e39e711bf37e1a6042e2913e473adc4fe9479a7cf56a131d0b3a748d02d1fb28dc4ac2f0 languageName: node linkType: hard -"@material/linear-progress@npm:12.0.0-canary.1a8d06483.0, @material/linear-progress@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/linear-progress@npm:12.0.0-canary.1a8d06483.0" +"@material/linear-progress@npm:12.0.0-canary.22d29cbb4.0, @material/linear-progress@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/linear-progress@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/progress-indicator": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/progress-indicator": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 41433c048ffeef51bd8962876f443858e00f3451abdc0b28fcfb2793a2f55653967c61d6f1190f3b735606dfc70cc451932b6d6ab4a2ec0beac35acdeb0f041f + checksum: 78d48593186a45f5ff6b7e5de6fc93b54508d593272348a5b8316695c4fc3bb86cd41df019533d896f0255f202a9d254ac6d409ef7824faade99b7a9b359c667 languageName: node linkType: hard -"@material/list@npm:12.0.0-canary.1a8d06483.0, @material/list@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/list@npm:12.0.0-canary.1a8d06483.0" +"@material/list@npm:12.0.0-canary.22d29cbb4.0, @material/list@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/list@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: df0f753c3cf97bb5e0709dc51aeb79e837cec3adc95db9b25c4d9e0e2e27f0ce1f2e9baf40f0f81631c83ea935d89221c32a1ef6ac9de600476e088a9417b5da + checksum: 46a5845e398080fb1fd5bfcc3770a62e20b063b09effbeb58e8ed54047f9c9c98f77179e5ae9314a978f8a4a8b4b887f97b256fec663b2a2dc62ca456677e3cf languageName: node linkType: hard -"@material/menu-surface@npm:12.0.0-canary.1a8d06483.0, @material/menu-surface@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/menu-surface@npm:12.0.0-canary.1a8d06483.0" +"@material/menu-surface@npm:12.0.0-canary.22d29cbb4.0, @material/menu-surface@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/menu-surface@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/elevation": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 76fce5de3d2103b5108989329431037443567dc74eeff96738004e48ba10694527e74c3b65fbd1b8de06b848cfbc10d598808bc21a3b1d4fa6cb0713c8adb858 + checksum: 5d074bb79a76b312645035f289dff09e4b12e46accc265244d64117fac61ac005342479971c6e4f6c34ac491dad2ddaaa38f5769837390a0a9a0a351c9bd7001 languageName: node linkType: hard -"@material/menu@npm:12.0.0-canary.1a8d06483.0, @material/menu@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/menu@npm:12.0.0-canary.1a8d06483.0" +"@material/menu@npm:12.0.0-canary.22d29cbb4.0, @material/menu@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/menu@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/elevation": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/list": 12.0.0-canary.1a8d06483.0 - "@material/menu-surface": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/list": 12.0.0-canary.22d29cbb4.0 + "@material/menu-surface": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 675a250865033c928d4bdead088f2bb09257f6c30b41008e01f900b0ac0c5c10ce533a2f32f71ad15520dc87b0ed27320e82ef6e938eb816b7462877c42b5ce9 + checksum: 26964feb90014c0d7528ff4f21352e806217a42f9a74b112be81a59e5b8d78bfba5f21501dc6852621b7ffd2819a7f6c3a931954b1caa77764515ee04f65f119 languageName: node linkType: hard -"@material/mwc-base@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-base@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-base@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-base@npm:0.22.1" dependencies: - "@material/base": =12.0.0-canary.1a8d06483.0 - "@material/dom": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 + "@material/base": =12.0.0-canary.22d29cbb4.0 + "@material/dom": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 tslib: ^2.0.1 - checksum: 4e21573d05ea6a511920e37a6ca775f1e7f645f0160dc3f024c18ad6aa746e96e5baaebb93315eb91ceb9e9b48ceeb0ba3c06cc8e7a2049bdefc11e4c8cce75a + checksum: bcff1df68feef92a7657e0a628152eab9851a3e645fd276e196b68123b1704a710805a5674b88e9acaaa570ccff08b4b3c74bf89c5067683d0fd0e46c6210a89 languageName: node linkType: hard -"@material/mwc-button@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-button@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-button@npm:0.22.1, @material/mwc-button@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-button@npm:0.22.1" dependencies: - "@material/mwc-icon": 0.22.0-canary.cc04657a.0 - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/mwc-icon": ^0.22.1 + "@material/mwc-ripple": ^0.22.1 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: 9cab8bf29e4a0b70b7c20c363bae2e2dd48f22442ad78fe027bd11a8494adebc0f7f6563c3d28bc16d0554f9b4eb199246db55c69e639f6f88e11fb707199135 + checksum: 74854fd65e79f49d7bfd7f5335df14ea2bc3b673a9df1ab57daf88d2d1564ce31a0d61979931f05834754af478812e6d4384ef49258a8d6e04dea8d3fc50520c languageName: node linkType: hard -"@material/mwc-checkbox@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-checkbox@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-checkbox@npm:0.22.1, @material/mwc-checkbox@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-checkbox@npm:0.22.1" dependencies: - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/mwc-base": ^0.22.1 + "@material/mwc-ripple": ^0.22.1 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: 10a319ca207f9e4ac680e65d351bc2fbd5b8fa39022cfe9c2cd2a67f66f431404d6109c4217f5e971e791eb6758912e0ccc77593b2d7da8612083c93a20c9f4c + checksum: a9e5d4de1709526eef2cc411ba9fc104aa83626f11f256f3ffa6ffaba80a31e583b48ab1ee7daea5bac9c61772635e5dc30185fa6e7ed1b1ee05cb0baa3124ee languageName: node linkType: hard -"@material/mwc-circular-progress@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-circular-progress@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-circular-progress@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-circular-progress@npm:0.22.1" dependencies: - "@material/circular-progress": =12.0.0-canary.1a8d06483.0 - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/theme": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/circular-progress": =12.0.0-canary.22d29cbb4.0 + "@material/mwc-base": ^0.22.1 + "@material/theme": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: 0a6e7de2540bcdb8595079a7dff6fd535b6b8a47344980babfa9be2b7861f8e203c0effdafa3a2967dc1480bae98fbc4666c1c8a8a0da50b5a9702ec2534e645 + checksum: 18bf0f9ef006d7b57c3a24ce951eb2e4e6d1df0b26a22025278791932d40b26558cc7f3173da01dddc79616273918cc67fa2690a59ebb9a1af9dbe668435ce4a languageName: node linkType: hard -"@material/mwc-dialog@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-dialog@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-dialog@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-dialog@npm:0.22.1" dependencies: - "@material/dialog": =12.0.0-canary.1a8d06483.0 - "@material/dom": =12.0.0-canary.1a8d06483.0 - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/mwc-button": 0.22.0-canary.cc04657a.0 + "@material/dialog": =12.0.0-canary.22d29cbb4.0 + "@material/dom": =12.0.0-canary.22d29cbb4.0 + "@material/mwc-base": ^0.22.1 + "@material/mwc-button": ^0.22.1 blocking-elements: ^0.1.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 wicg-inert: ^3.0.0 - checksum: 5aafec2257fa0dce23f94911be443bf4dd7315c63f052f5452d10dac949f20f4c161cd0f72694661400400c99272886f28058985f71963e8e8bf492553fa39a1 + checksum: 0a025e3716dc0e20e134d4c2b4cd2bcd1324b68f5ae3bc50353f585d7cdba6e5e531befca08a73e56297089ba867a2b65f0fff413d9ef6e7e5aeda6f71dccc81 languageName: node linkType: hard -"@material/mwc-fab@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-fab@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-fab@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-fab@npm:0.22.1" dependencies: - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/mwc-ripple": ^0.22.1 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: 7fabaa77153420dcd4196e9c2ccfba300900248135a56c79c394e3c6ab7cc8b02feb897f777d3926248287be55d7541579e6e92402f539cf83e0f63efccdaccd + checksum: e866f88eddf7f4271e2b4f226a793875471d4862138fded3745db86390c21661e7c8191d31289ce7c67d20530dc4487606a1be6a5b81905b867b27d272e03fae languageName: node linkType: hard -"@material/mwc-formfield@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-formfield@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-formfield@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-formfield@npm:0.22.1" dependencies: - "@material/form-field": =12.0.0-canary.1a8d06483.0 - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/form-field": =12.0.0-canary.22d29cbb4.0 + "@material/mwc-base": ^0.22.1 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: a5756519b4173b444f89342f0ea6f091dd8ec74542ba0415f89c108dfa6a97dca7a37e7acff4e84099dfad06636a09c60c54046010882e3908ea48d343a0943f + checksum: ba915330e6e0a6a5b3a1f37cf97620426822b761282c6d127bcd53c8eec841141013e3da796221263c47654a9eea80604d830589acf8a29502b4a76a19272769 languageName: node linkType: hard -"@material/mwc-icon-button@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-icon-button@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-icon-button@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-icon-button@npm:0.22.1" dependencies: - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - lit-element: ^2.5.0 + "@material/mwc-ripple": ^0.22.1 + lit-element: ^2.5.1 tslib: ^2.0.1 - checksum: 7c6c385d88cf2a3eb4db4f5cf1d88e080dc9cb0350da8d50ac9433f6df90c3bb41c764f53fb420c5f8c6b20ec8359c5a250142bbdfc41228179e919b97206828 + checksum: 821d45563310c969b6a11d4679c68234c0fe085384f035ca7dbdbbcc89f94a3afaa2730316fa5bb96414384e46c8016a13cc7a28da1f928a3bad4a35bf06ea11 languageName: node linkType: hard -"@material/mwc-icon@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-icon@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-icon@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-icon@npm:0.22.1" dependencies: - lit-element: ^2.5.0 + lit-element: ^2.5.1 tslib: ^2.0.1 - checksum: e6b1c9bb9ee0d2d3356511434743261f97dd024c5a733cb6ee842e5ae4ffc89cefd8a57eb594423f857146d83449e62ce43df9795d332077c7b9f4caa5a554f5 + checksum: 4f4aa981766005854d52bdcd102885b9f137fa392646e03f7b76f1af879e230131b331eccec45e7c698315fc89da668fc56e22f8d68f79ee0b493e74d01478e4 languageName: node linkType: hard -"@material/mwc-linear-progress@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-linear-progress@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-linear-progress@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-linear-progress@npm:0.22.1" dependencies: - "@material/linear-progress": =12.0.0-canary.1a8d06483.0 - "@material/theme": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/linear-progress": =12.0.0-canary.22d29cbb4.0 + "@material/mwc-base": ^0.22.1 + "@material/theme": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: 78026c84ec71bdf22b4c8603014ec79d5ddef13370e4d9a34a27ee53077d8129d43603dcf05738278bf284bed2417e34298aa0736a037c54c337d80c54ea724b + checksum: 248074aa2d38782a9cf55584eead50122845090a89cb03d3c0807e423608ebd67212f46868c806b75a42a359a002380fde0ae3cc0321be953b5355c8ca33fa1a languageName: node linkType: hard -"@material/mwc-list@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-list@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-list@npm:0.22.1, @material/mwc-list@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-list@npm:0.22.1" dependencies: - "@material/base": =12.0.0-canary.1a8d06483.0 - "@material/dom": =12.0.0-canary.1a8d06483.0 - "@material/list": =12.0.0-canary.1a8d06483.0 - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/mwc-checkbox": 0.22.0-canary.cc04657a.0 - "@material/mwc-radio": 0.22.0-canary.cc04657a.0 - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/base": =12.0.0-canary.22d29cbb4.0 + "@material/dom": =12.0.0-canary.22d29cbb4.0 + "@material/list": =12.0.0-canary.22d29cbb4.0 + "@material/mwc-base": ^0.22.1 + "@material/mwc-checkbox": ^0.22.1 + "@material/mwc-radio": ^0.22.1 + "@material/mwc-ripple": ^0.22.1 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: bfa8c62ce4a06377542162ce78662a06ab0ab61b51803db4bae4a5156e7b417a259426187d7338a5691cc77005c1abf5b8f5874d46c9f9f88350f261ff8670f9 + checksum: 4f61a99ad47fb87ee40a16027bd0d793c334f9a1c570fce0f304c2844bb3ddf8b3ecd22e32a2f44c7047e47a61a56a57a2ccf37f4a8cb57dfd6025d0f48f320c languageName: node linkType: hard -"@material/mwc-menu@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-menu@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-menu@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-menu@npm:0.22.1" dependencies: - "@material/menu": =12.0.0-canary.1a8d06483.0 - "@material/menu-surface": =12.0.0-canary.1a8d06483.0 - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/mwc-list": 0.22.0-canary.cc04657a.0 - "@material/shape": =12.0.0-canary.1a8d06483.0 - "@material/theme": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/menu": =12.0.0-canary.22d29cbb4.0 + "@material/menu-surface": =12.0.0-canary.22d29cbb4.0 + "@material/mwc-base": ^0.22.1 + "@material/mwc-list": ^0.22.1 + "@material/shape": =12.0.0-canary.22d29cbb4.0 + "@material/theme": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: 2eaa25fd0846a4855124ab05ec24bed177dea0f8aed333eb472ceb9962665217a57dab02a321584322f7f70da85afbebc29c7fd44af12b79c8502611b4988812 + checksum: caa0120bed96e16b981666e6d4a593e8ff1184e10f56ad42143851a0c794b7ed620f4442576c782ec15bef33b0bdf1b7d4a29e6b0af78c732ecc5563a8ef28e5 languageName: node linkType: hard -"@material/mwc-radio@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-radio@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-radio@npm:0.22.1, @material/mwc-radio@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-radio@npm:0.22.1" dependencies: - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - "@material/radio": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 + "@material/mwc-base": ^0.22.1 + "@material/mwc-ripple": ^0.22.1 + "@material/radio": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 tslib: ^2.0.1 - checksum: 19c041f2e9e6159ee5bdfb3a20218aa7e205f40a8da63e23ab148c626729d8853e1fe080a06a161f60c3691c89e2285c8fc781b7cc14644959a770e39ea87974 + checksum: 32f728d4bccfdee78a6a6eb6a550f946bd7a82b019968f4e7473dc97c87176af4530488b3e40ac00afa308df4e8a555dfa09307cc1e17b6937837126a71e5aff languageName: node linkType: hard -"@material/mwc-ripple@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-ripple@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-ripple@npm:0.22.1, @material/mwc-ripple@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-ripple@npm:0.22.1" dependencies: - "@material/dom": =12.0.0-canary.1a8d06483.0 - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/ripple": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/dom": =12.0.0-canary.22d29cbb4.0 + "@material/mwc-base": ^0.22.1 + "@material/ripple": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: 7c09ca245d356f4e53f3f1e1ddcca84d2e280755e9ff287affb31bd1cffd2f31b5bad21f5c2eb8fc02a1aeb2ea2e06b11dcfd6d2cb1f42899a6e935f786f5a8a + checksum: 4377c5b25e69d2fdd172f98cb02eabf15e73af2293338110766024aa5a86b1ffc9876985ad8f731edb8466126c200f8cbcd88b5559a1788366f194942ea96873 languageName: node linkType: hard -"@material/mwc-switch@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-switch@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-switch@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-switch@npm:0.22.1" dependencies: - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - "@material/switch": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 + "@material/mwc-base": ^0.22.1 + "@material/mwc-ripple": ^0.22.1 + "@material/switch": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 tslib: ^2.0.1 - checksum: aea397e88419b1c673230d213a76a4d42b39a5837572131158196dc098cff3a71d219a89e139d3ddf65cf4405f9e473ea19fd7699013547a1db64bb6ad6a312d + checksum: 737ec4fb06eaed5bd9f2cf91afbd7b7dd474a5a7eb3e75c488805e87a8425e43ed68dbecbe9f941a96e069ac6d5f3cb14732f74cb8babe18a69158312e9391d1 languageName: node linkType: hard -"@material/mwc-tab-bar@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-tab-bar@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-tab-bar@npm:0.22.1": + version: 0.22.1 + resolution: "@material/mwc-tab-bar@npm:0.22.1" dependencies: - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/mwc-tab": 0.22.0-canary.cc04657a.0 - "@material/mwc-tab-scroller": 0.22.0-canary.cc04657a.0 - "@material/tab": =12.0.0-canary.1a8d06483.0 - "@material/tab-bar": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 + "@material/mwc-base": ^0.22.1 + "@material/mwc-tab": ^0.22.1 + "@material/mwc-tab-scroller": ^0.22.1 + "@material/tab": =12.0.0-canary.22d29cbb4.0 + "@material/tab-bar": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 tslib: ^2.0.1 - checksum: 836017ada48a4861f7cdbf2cb362a955db80a6e2b8d14fa67349d7411da587e143f9bd81a425b5441baef22697ebe2fa399048b58842069fc19d6c8a1be48c12 + checksum: f23f06a413a66c73c5157f5f455ed48b8be5472919b8631c39087d65d530b93ee9c8ed923dcba63a7dfdab12b0ef85f3dab711a46a4107ac8c069a83c880100f languageName: node linkType: hard -"@material/mwc-tab-indicator@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-tab-indicator@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-tab-indicator@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-tab-indicator@npm:0.22.1" dependencies: - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/tab-indicator": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/mwc-base": ^0.22.1 + "@material/tab-indicator": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: d379732c947f913bbbb075f3e8df65ed3a4dcba6e5a3d81cb73e33234543f6e4e894b21453b99648e2a44ec10d4658b44b6102d52ca8197f8ed8befe5cb29d26 + checksum: f4c79082c381910a3de4edcc8e6d33fc0ddccf51c74fa59abe55b18788414badc68b3377fb99a6f1a319c0ca292fb22521b1af781e938b2a9e3508c9e9daffd2 languageName: node linkType: hard -"@material/mwc-tab-scroller@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-tab-scroller@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-tab-scroller@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-tab-scroller@npm:0.22.1" dependencies: - "@material/dom": =12.0.0-canary.1a8d06483.0 - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/tab-scroller": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 + "@material/dom": =12.0.0-canary.22d29cbb4.0 + "@material/mwc-base": ^0.22.1 + "@material/tab-scroller": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 tslib: ^2.0.1 - checksum: f1408aabe3e5dcee7b1c5eab5d9dcab65f2696d92ee3a7dd1878d5d6e84665ec4c62ea141deeee1a327adc699de45d7d863bce9dcd4d709f5cef259f18882a0d + checksum: a3a111047c67abe578db9f6b483afa2bc02d3a7a3e9e4b04b4d19973dbda170acb334293508d4737207e9832c1cdbbf52433be174b3f660988b67e1bedf191c4 languageName: node linkType: hard -"@material/mwc-tab@npm:0.22.0-canary.cc04657a.0": - version: 0.22.0-canary.cc04657a.0 - resolution: "@material/mwc-tab@npm:0.22.0-canary.cc04657a.0" +"@material/mwc-tab@npm:0.22.1, @material/mwc-tab@npm:^0.22.1": + version: 0.22.1 + resolution: "@material/mwc-tab@npm:0.22.1" dependencies: - "@material/mwc-base": 0.22.0-canary.cc04657a.0 - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - "@material/mwc-tab-indicator": 0.22.0-canary.cc04657a.0 - "@material/tab": =12.0.0-canary.1a8d06483.0 - lit-element: ^2.5.0 - lit-html: ^1.4.0 + "@material/mwc-base": ^0.22.1 + "@material/mwc-ripple": ^0.22.1 + "@material/mwc-tab-indicator": ^0.22.1 + "@material/tab": =12.0.0-canary.22d29cbb4.0 + lit-element: ^2.5.1 + lit-html: ^1.4.1 tslib: ^2.0.1 - checksum: 610c35da217f46b3f4dcaea25d62170113eb0ffc47ad3d066ae9e8e1600ff857f20764a6e9fa35e393f2facbc92f3f6755ed0116e9682b27e66fb83a36c4744b + checksum: 6f19ff9f8317d5875dc417b579cd0eeac052738a2b097ca45f7cc20acb47d7f193dc39a44805d9bb9d6df1f313c1ef4c5859e946cab5e572450f84b4989f60a2 languageName: node linkType: hard -"@material/notched-outline@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/notched-outline@npm:12.0.0-canary.1a8d06483.0" +"@material/notched-outline@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/notched-outline@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/floating-label": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/floating-label": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: d0856c5aeba272df09c5c76e99cc630c1614458a8184959fb4a82d54908db5632f09519080831290d9786e989a92601de432c5a4704b6481664ce3fc22d44150 + checksum: a8500bbdb59d12e1625f16ba6f85d266c6d591ea42f726d4eacd65dddc6455eefcfb0f39011f692b9bee7e98b20a4b6540228526445c9872a4a2cefc6ad7312c languageName: node linkType: hard -"@material/progress-indicator@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/progress-indicator@npm:12.0.0-canary.1a8d06483.0" +"@material/progress-indicator@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/progress-indicator@npm:12.0.0-canary.22d29cbb4.0" dependencies: tslib: ^2.1.0 - checksum: b59d400bd7e11e3c58a27721a8771473d44cc371de41cce417c6e88f6431da08e6d3401e5e2b51cb1ab593a8ad3d927f19518802e9b3a24581d73f6fcfcd290b + checksum: 80f67a094b69022f4ac3dd7aac5cd9c00d453b723722622b6688778a0e8b5ffd95bb8abd5c5ace4627959d376870451f2dd69e39197f5c18c52c3c24fa5a683c languageName: node linkType: hard -"@material/radio@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/radio@npm:12.0.0-canary.1a8d06483.0" +"@material/radio@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/radio@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/touch-target": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/touch-target": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 2f0a6f3dd862bafb7638c97fa1681a3f2266e2c705eb7c91f648d440521e86d9bd50a36f0216cbb2335cf715e6df9a20b3c4e126359e340518d015959717ae2b + checksum: 531ea652b1c1df6a367a2565ea0e79df106b3d408fbab3bd277dbe1dcdd8a05ccea9fcb3febec940bd200ff9ca915cac6dcdcb134fdb9064f099f98aa725ef78 languageName: node linkType: hard -"@material/ripple@npm:12.0.0-canary.1a8d06483.0, @material/ripple@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/ripple@npm:12.0.0-canary.1a8d06483.0" +"@material/ripple@npm:12.0.0-canary.22d29cbb4.0, @material/ripple@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/ripple@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: d3610bdaa2ec1b647842242dbbf8dec8b27e1e8a35ceb088903540bb4b550b7971183a7cf434e64f7755f5e91ea9a50cfffcf05eac692942a7feeca6759b7e23 + checksum: 21db3d2dc89a648abd6c2111e36c1b95d07e17a473ea152f0a884c3712bbeedeb80daf6849f0098fd20b82fc3e859307395fece0203b4d611bc03d312b4bff3f languageName: node linkType: hard -"@material/rtl@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/rtl@npm:12.0.0-canary.1a8d06483.0" +"@material/rtl@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/rtl@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: f5c7a4bd398707e53a717d391f684a571b89e32f541b795d1bc5fc3d74d0272f09ac7eb07366879cc05b11fd85fa1f022360fca6fd6339541f326bcac1e2d430 + checksum: 3f51ae15556beec7dca39acccd13da1332329d2fc7831e436b149c51bbf7340f9be5f02a206defb4de5ce54fa44376086f986956c20ea515cb75f8ff4fac9b59 languageName: node linkType: hard -"@material/select@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/select@npm:12.0.0-canary.1a8d06483.0" +"@material/select@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/select@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/floating-label": 12.0.0-canary.1a8d06483.0 - "@material/line-ripple": 12.0.0-canary.1a8d06483.0 - "@material/list": 12.0.0-canary.1a8d06483.0 - "@material/menu": 12.0.0-canary.1a8d06483.0 - "@material/menu-surface": 12.0.0-canary.1a8d06483.0 - "@material/notched-outline": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/floating-label": 12.0.0-canary.22d29cbb4.0 + "@material/line-ripple": 12.0.0-canary.22d29cbb4.0 + "@material/list": 12.0.0-canary.22d29cbb4.0 + "@material/menu": 12.0.0-canary.22d29cbb4.0 + "@material/menu-surface": 12.0.0-canary.22d29cbb4.0 + "@material/notched-outline": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 6a5483d3497a9ba835df56ca28e059b6f6693d363e6baea67106405ca19485fc517d2824bcb2ef64b94e836a9fb8977552243db9be7be858e7dfb9c1af51d2ab + checksum: e2f71996ce36dcb7bd17ac60cd40c4c3b5c03101f457f060af3355f893cf7b9e23fb77b495fdf97c62ac44ffde37c5a70d5e7e086341e88fdb22965a44521ef4 languageName: node linkType: hard -"@material/shape@npm:12.0.0-canary.1a8d06483.0, @material/shape@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/shape@npm:12.0.0-canary.1a8d06483.0" +"@material/shape@npm:12.0.0-canary.22d29cbb4.0, @material/shape@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/shape@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: c6be54e5153082ab4e1eccd2896ebe8782ab13c47f4bf9b3ebb4092676061f1b65fbea2b1659d2338d36e79b3776d8512d1ec19b13764ab6b711d810902a05b6 + checksum: 7af7d2f433790363c70949a1364c893497e076d64b6d873f504ece8507c81ccb38c12454e54087ddfd5e505288df62f80a288d080cd341dfe1919628ed65d37f languageName: node linkType: hard -"@material/switch@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/switch@npm:12.0.0-canary.1a8d06483.0" +"@material/switch@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/switch@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/elevation": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 5420ff748c6092d4f67040d507ed44cc9528bb014919475967ade4fa9a5ccc83ed5c8b9895653874bd8e1dc77d2e2a08d752e44a83a0680b37134df90a1b225e + checksum: 75291fe99122d765babbf96187de40c4c51cbd839b214c3944ff864ce9c9ed73c8455293a76c4c3e1698694c5e93a9921352610a444e8dfa9dc920229c39e7b4 languageName: node linkType: hard -"@material/tab-bar@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/tab-bar@npm:12.0.0-canary.1a8d06483.0" +"@material/tab-bar@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/tab-bar@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/density": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/tab": 12.0.0-canary.1a8d06483.0 - "@material/tab-scroller": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/density": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/tab": 12.0.0-canary.22d29cbb4.0 + "@material/tab-indicator": 12.0.0-canary.22d29cbb4.0 + "@material/tab-scroller": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 7a6be3b3779309417823d70772dba984e391080e3ff6e506f86024e62c5077e093184411a72ee87dfb8dc63cedf840916887432504d868cf198afbef6cb35de0 + checksum: c9f7cea1158fce88a4e2a38757e1a362e69edea748bc580afb8d256978b0d09f11b5d819224c5274f7d165ae08e7d1bc77a7d7e5b622916b3eb79ecae3058c7c languageName: node linkType: hard -"@material/tab-indicator@npm:12.0.0-canary.1a8d06483.0, @material/tab-indicator@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/tab-indicator@npm:12.0.0-canary.1a8d06483.0" +"@material/tab-indicator@npm:12.0.0-canary.22d29cbb4.0, @material/tab-indicator@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/tab-indicator@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: a157c80d9ec4c396f888aa6ef299973474c24c997ddb128e50f777333be3cd91f53caa9404af4a728482c285fa79886ccd54d4fbfa7e91b0f32f6a4a1de89e90 + checksum: d07c36863c8aed45c63e8f1f02c07e9e312144017828f29a95733ad2273f39b52ffaeaf706ee3f472333a756476475e3c927078b11c9eff25ee1113576996cd5 languageName: node linkType: hard -"@material/tab-scroller@npm:12.0.0-canary.1a8d06483.0, @material/tab-scroller@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/tab-scroller@npm:12.0.0-canary.1a8d06483.0" +"@material/tab-scroller@npm:12.0.0-canary.22d29cbb4.0, @material/tab-scroller@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/tab-scroller@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/dom": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/tab": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/dom": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/tab": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 870e28bb4508997ba22175bfdaeac7a0eb8d23007c608fdd4e40d1ac10f30e73daebd9099a41210ca723f62514b594f6ef6635f8ce7f9b1155827a80c0e6e92e + checksum: d7abb5e8ee9f1da768592f918819ebd459deec710bb400651af65de1534deee225f2b4cc470dc89dd11102c971276fc87a85f7249219fc50ce5c1911c3b6fcc9 languageName: node linkType: hard -"@material/tab@npm:12.0.0-canary.1a8d06483.0, @material/tab@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/tab@npm:12.0.0-canary.1a8d06483.0" +"@material/tab@npm:12.0.0-canary.22d29cbb4.0, @material/tab@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/tab@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/tab-indicator": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/tab-indicator": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 90e008c397cc9994eb694190ff8df62bb91a3aa0266a9e7ecd0200f18113b78b824ecc390fe530b417d514cf2b99d681c53409b7161da1b091e12ec7a5552334 + checksum: 10d0bb2ebd0da8b4eb03761397beecc88645718723386c4f0f0c264f5737243a4335e3b8d50047ea68bede4adbe56cc90d10ab9f7d75a3d1d995dca509ebb74c languageName: node linkType: hard -"@material/theme@npm:12.0.0-canary.1a8d06483.0, @material/theme@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/theme@npm:12.0.0-canary.1a8d06483.0" +"@material/theme@npm:12.0.0-canary.22d29cbb4.0, @material/theme@npm:=12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/theme@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: cb6bd4562ed038ef9c56227ec840f05f0cbef06687f2a4e9ad89aefac16325681d6a5f53dc12828fd787ddd8d58950c69e0933a413aa55d6d8ec97ce5a63dbfb + checksum: 69727d0a7b5c6b4be438c2ff874f9d3f0db75e26fb0b98acc57f5c1b65c2571fbf86ea559ee3cbdf5cebf45700d18efffa87fbf8fa72512eeb567a74b22ad8d9 languageName: node linkType: hard -"@material/top-app-bar@npm:=12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/top-app-bar@npm:12.0.0-canary.1a8d06483.0" +"@material/top-app-bar@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/top-app-bar@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/animation": 12.0.0-canary.1a8d06483.0 - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/elevation": 12.0.0-canary.1a8d06483.0 - "@material/ripple": 12.0.0-canary.1a8d06483.0 - "@material/rtl": 12.0.0-canary.1a8d06483.0 - "@material/shape": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 - "@material/typography": 12.0.0-canary.1a8d06483.0 + "@material/animation": 12.0.0-canary.22d29cbb4.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/elevation": 12.0.0-canary.22d29cbb4.0 + "@material/ripple": 12.0.0-canary.22d29cbb4.0 + "@material/rtl": 12.0.0-canary.22d29cbb4.0 + "@material/shape": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 + "@material/typography": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: d608552fd00260b1a79756d02b421f2f8b516028742934d52a5920a439687c6c0bf5d231c2eafa1e1e63b0721899d1bdc1e192b73730c923bca7a26ec2abb91d + checksum: 7769215628c86a353565f647c3dcdf99aba2baf0eade345bd8fd695a93a8348224bc569c5e2ff22123e91e31cbed847c54a246418cc93c6ce6e162d7c66655cd languageName: node linkType: hard -"@material/touch-target@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/touch-target@npm:12.0.0-canary.1a8d06483.0" +"@material/touch-target@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/touch-target@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/base": 12.0.0-canary.1a8d06483.0 - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 + "@material/base": 12.0.0-canary.22d29cbb4.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 16e0ba9051b8823f6c1126faf95e20a06078b9408321be374f0dbb8f0808cd6f3e39cdc19fc4824bf1aa09149028ffea068cd680e85edd338990580e9290cd36 + checksum: 4108809ad673383cf162318ab9f69c3faeacddc9c4130d0d540575b40bc56f17119d20b199b947059e5db576fc65902d0c43bd156564536692641853e4fb002f languageName: node linkType: hard -"@material/typography@npm:12.0.0-canary.1a8d06483.0": - version: 12.0.0-canary.1a8d06483.0 - resolution: "@material/typography@npm:12.0.0-canary.1a8d06483.0" +"@material/typography@npm:12.0.0-canary.22d29cbb4.0": + version: 12.0.0-canary.22d29cbb4.0 + resolution: "@material/typography@npm:12.0.0-canary.22d29cbb4.0" dependencies: - "@material/feature-targeting": 12.0.0-canary.1a8d06483.0 - "@material/theme": 12.0.0-canary.1a8d06483.0 + "@material/feature-targeting": 12.0.0-canary.22d29cbb4.0 + "@material/theme": 12.0.0-canary.22d29cbb4.0 tslib: ^2.1.0 - checksum: 99d54dc2b985526dddc09336cc36c02e5b2ff97bf1210e7ef5484e96e81ee6e7fb0dd8995ff22a9420f51c4b67b059f3413c4f058250fa20976989aca7faa60e + checksum: f87fbdb008dbac707ec0544c5f6bf0da21590cb5641423c2d2031013f8d23d53cc7f23067f3987768dc7b0cddff48d717df24046ecef79336e30f1e449cab4eb languageName: node linkType: hard @@ -3967,68 +3974,67 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-button@npm:^2.4.0": - version: 2.4.0 - resolution: "@vaadin/vaadin-button@npm:2.4.0" +"@vaadin/vaadin-button@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-button@npm:20.0.1" dependencies: "@polymer/polymer": ^3.0.0 - "@vaadin/vaadin-control-state-mixin": ^2.2.1 - "@vaadin/vaadin-element-mixin": ^2.4.1 - "@vaadin/vaadin-lumo-styles": ^1.3.3 - "@vaadin/vaadin-material-styles": ^1.2.0 - "@vaadin/vaadin-themable-mixin": ^1.6.1 - checksum: f0b79e3b8e2e83c90fde9a6b7730ccfed26b4b75bb5c54f5b1d55e1efa3ae4905f5e5d0218743824569f54324056422b69979e1b1e50d34c9d69322845083735 + "@vaadin/vaadin-control-state-mixin": 20.0.1 + "@vaadin/vaadin-element-mixin": 20.0.1 + "@vaadin/vaadin-lumo-styles": 20.0.1 + "@vaadin/vaadin-material-styles": 20.0.1 + "@vaadin/vaadin-themable-mixin": 20.0.1 + checksum: 096a669d76f6b3587c7d3849a68aa05123248924a388568b69ae45970b7155af1abca83085a84a66d22e397f0d88acc9c2b289eeff9bc4a758b75fe945fc4a1f languageName: node linkType: hard -"@vaadin/vaadin-combo-box@npm:^5.0.10": - version: 5.4.7 - resolution: "@vaadin/vaadin-combo-box@npm:5.4.7" +"@vaadin/vaadin-combo-box@npm:^20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-combo-box@npm:20.0.1" dependencies: "@polymer/iron-a11y-announcer": ^3.0.0 - "@polymer/iron-a11y-keys-behavior": ^3.0.0 "@polymer/iron-list": ^3.0.0 "@polymer/iron-resizable-behavior": ^3.0.0 "@polymer/polymer": ^3.0.0 - "@vaadin/vaadin-control-state-mixin": ^2.2.2 - "@vaadin/vaadin-element-mixin": ^2.4.1 - "@vaadin/vaadin-item": ^2.3.0 - "@vaadin/vaadin-lumo-styles": ^1.1.1 - "@vaadin/vaadin-material-styles": ^1.1.2 - "@vaadin/vaadin-overlay": ^3.5.0 - "@vaadin/vaadin-text-field": ^2.8.0 - "@vaadin/vaadin-themable-mixin": ^1.6.1 - checksum: f606af912f2db8e2725086540f13a0e199ca150dc6cd2abccf68f7ddaa903d59dfbc3ad29f31b89b520b739ccc6af6740b1e87f54f6b7c9461e26377f599d8d4 + "@vaadin/vaadin-control-state-mixin": 20.0.1 + "@vaadin/vaadin-element-mixin": 20.0.1 + "@vaadin/vaadin-item": 20.0.1 + "@vaadin/vaadin-lumo-styles": 20.0.1 + "@vaadin/vaadin-material-styles": 20.0.1 + "@vaadin/vaadin-overlay": 20.0.1 + "@vaadin/vaadin-text-field": 20.0.1 + "@vaadin/vaadin-themable-mixin": 20.0.1 + checksum: ec4cf4d687fe3ee1cf7b2fbbb6c65d5701f30179d41d591ce75664864afc15f6ecd95f9baf595c2179a0a4211a9b46fd42b214d20b60c45bfdd80c05ca5e24b2 languageName: node linkType: hard -"@vaadin/vaadin-control-state-mixin@npm:^2.2.1, @vaadin/vaadin-control-state-mixin@npm:^2.2.2": - version: 2.2.4 - resolution: "@vaadin/vaadin-control-state-mixin@npm:2.2.4" +"@vaadin/vaadin-control-state-mixin@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-control-state-mixin@npm:20.0.1" dependencies: "@polymer/polymer": ^3.0.0 - checksum: 7efdc2e8f546164983f1dfba2c153f4801e18de2f5abefe7260d3bde2d71ad1c9214afed316b6d2d79752cd72d9987d430c3400bd5995f5bc9e0146844e302ab + checksum: dfb7c89cdc5f88497194b0f3b61a08c70abf3e5633f302feb4b5a42557fd270f02e59b586a47dd482dffa76dee820422e388c2bee3ca9bfedea48da01592ff74 languageName: node linkType: hard -"@vaadin/vaadin-date-picker@npm:^4.0.7": - version: 4.4.1 - resolution: "@vaadin/vaadin-date-picker@npm:4.4.1" +"@vaadin/vaadin-date-picker@npm:^20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-date-picker@npm:20.0.1" dependencies: "@polymer/iron-a11y-announcer": ^3.0.0 "@polymer/iron-a11y-keys-behavior": ^3.0.0 "@polymer/iron-media-query": ^3.0.0 "@polymer/iron-resizable-behavior": ^3.0.0 - "@polymer/polymer": ^3.0.0 - "@vaadin/vaadin-button": ^2.4.0 - "@vaadin/vaadin-control-state-mixin": ^2.2.2 - "@vaadin/vaadin-element-mixin": ^2.4.1 - "@vaadin/vaadin-lumo-styles": ^1.6.0 - "@vaadin/vaadin-material-styles": ^1.3.2 - "@vaadin/vaadin-overlay": ^3.5.0 - "@vaadin/vaadin-text-field": ^2.8.0 - "@vaadin/vaadin-themable-mixin": ^1.6.1 - checksum: da3bc0b7e3ce22ae1887e9fc79c4fdefaf179fb721b03d9cdb9fc336287c6c1c18889504b6d185b59c1644ca4c1e875e6b845cf29ab0c811b51456b87962c7a6 + "@polymer/polymer": ^3.2.0 + "@vaadin/vaadin-button": 20.0.1 + "@vaadin/vaadin-control-state-mixin": 20.0.1 + "@vaadin/vaadin-element-mixin": 20.0.1 + "@vaadin/vaadin-lumo-styles": 20.0.1 + "@vaadin/vaadin-material-styles": 20.0.1 + "@vaadin/vaadin-overlay": 20.0.1 + "@vaadin/vaadin-text-field": 20.0.1 + "@vaadin/vaadin-themable-mixin": 20.0.1 + checksum: 058e1cae51414c664fac5679ebea435ae4f8da716915c8c0aa9f4c7683e7bd48ce94e6f059dea9d7cf67e87554a098ab7cfcd955afdc4d85291ba0c0c6447236 languageName: node linkType: hard @@ -4039,84 +4045,86 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-element-mixin@npm:^2.4.0, @vaadin/vaadin-element-mixin@npm:^2.4.1": - version: 2.4.2 - resolution: "@vaadin/vaadin-element-mixin@npm:2.4.2" +"@vaadin/vaadin-element-mixin@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-element-mixin@npm:20.0.1" dependencies: "@polymer/polymer": ^3.0.0 "@vaadin/vaadin-development-mode-detector": ^2.0.0 "@vaadin/vaadin-usage-statistics": ^2.1.0 - checksum: 6bc9a4226e5cfd5b3dabd7bda5a95f3582ab045892dd941430245ce78d4361c20ea3c4c24e1f490d1f5635b76ac962aeb7503b38dd1e09c937d2ae97d1d95d93 + checksum: 34f66bc3332a57672645f00ac55327624a55c686eaaa0a19a6a830d93229c7a5e16a2421c8b9919d729540d829e289f68105bd6754c953fe9c94b38ab547416f languageName: node linkType: hard -"@vaadin/vaadin-item@npm:^2.3.0": - version: 2.3.0 - resolution: "@vaadin/vaadin-item@npm:2.3.0" +"@vaadin/vaadin-item@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-item@npm:20.0.1" dependencies: "@polymer/polymer": ^3.0.0 - "@vaadin/vaadin-element-mixin": ^2.4.1 - "@vaadin/vaadin-lumo-styles": ^1.1.0 - "@vaadin/vaadin-material-styles": ^1.1.0 - "@vaadin/vaadin-themable-mixin": ^1.6.1 - checksum: 5effafdf4a5135a8af92b86dc29d510331bff2814347568c69a61c7d2012714311e9c3be2761f0a2f599849828f1e5918d979840905c149ce244a75255f0f76e + "@vaadin/vaadin-element-mixin": 20.0.1 + "@vaadin/vaadin-lumo-styles": 20.0.1 + "@vaadin/vaadin-material-styles": 20.0.1 + "@vaadin/vaadin-themable-mixin": 20.0.1 + checksum: 904a549574c7c7c23a98d5ec4067d262ff612eb561f60a9fea9bdf70eb65fe5d297877139ab2398b746a84eb1bfbb8d3270494c067b44a11ef70625fa9dd0177 languageName: node linkType: hard -"@vaadin/vaadin-lumo-styles@npm:^1.1.0, @vaadin/vaadin-lumo-styles@npm:^1.1.1, @vaadin/vaadin-lumo-styles@npm:^1.3.0, @vaadin/vaadin-lumo-styles@npm:^1.3.3, @vaadin/vaadin-lumo-styles@npm:^1.6.0": - version: 1.6.1 - resolution: "@vaadin/vaadin-lumo-styles@npm:1.6.1" +"@vaadin/vaadin-lumo-styles@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-lumo-styles@npm:20.0.1" dependencies: "@polymer/iron-icon": ^3.0.0 "@polymer/iron-iconset-svg": ^3.0.0 "@polymer/polymer": ^3.0.0 - checksum: e6ae75a68f626ff877a8c4f6a14b12529f168308c8e198b24e91db46657dcec06a3e3df1a77678e9c1a7ac81568592483e3508c8af89d5b578cf6db9814e097e + "@vaadin/vaadin-themable-mixin": 20.0.1 + checksum: bd6be1aa9a2339db332beda63433f301c2f7495070b968da1ff27033c111a8c1cd23c55090832f15b69f04b5b8a65c8ee3feba480eb876fb4fc4d165b4568bdb languageName: node linkType: hard -"@vaadin/vaadin-material-styles@npm:^1.1.0, @vaadin/vaadin-material-styles@npm:^1.1.2, @vaadin/vaadin-material-styles@npm:^1.2.0, @vaadin/vaadin-material-styles@npm:^1.3.2": - version: 1.3.2 - resolution: "@vaadin/vaadin-material-styles@npm:1.3.2" +"@vaadin/vaadin-material-styles@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-material-styles@npm:20.0.1" dependencies: "@polymer/polymer": ^3.0.0 - checksum: 4fe2cc038ec4dcc6de37263aaf90435f3987cf8a6ed09bf797a845235a6c7a24d9af2e77c0597fb17ec8887b723e59305f88bf19d265c6899c6dd60b5e3b0aee + "@vaadin/vaadin-themable-mixin": 20.0.1 + checksum: 885ba2637157e07fd78829113d3e50eb0346e363435691acd431893b77edf5ae2863fe2a2163eaad4b36e84383b8b73db0e0ebb6f815c7608d3775c0260ae250 languageName: node linkType: hard -"@vaadin/vaadin-overlay@npm:^3.5.0": - version: 3.5.1 - resolution: "@vaadin/vaadin-overlay@npm:3.5.1" +"@vaadin/vaadin-overlay@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-overlay@npm:20.0.1" dependencies: "@polymer/polymer": ^3.0.0 - "@vaadin/vaadin-element-mixin": ^2.4.0 - "@vaadin/vaadin-lumo-styles": ^1.3.0 - "@vaadin/vaadin-material-styles": ^1.2.0 - "@vaadin/vaadin-themable-mixin": ^1.6.1 - checksum: dcfba6b65bc0001e8d16278c6e63707629f7480f6b0553f4f3338ec79e4edfa3ecf7f27fcfc022cab9e08fdcdf2a5c263d817fd55402a554d28362a333fcd0b6 + "@vaadin/vaadin-element-mixin": 20.0.1 + "@vaadin/vaadin-lumo-styles": 20.0.1 + "@vaadin/vaadin-material-styles": 20.0.1 + "@vaadin/vaadin-themable-mixin": 20.0.1 + checksum: c3ef33e5149381e69d4f64b3500ba342dbc7cf244025a071533f971f0c5c49d92d9ca797c635933842642ff2fa248403a20d8f5a758c0434709878b35cf2324f languageName: node linkType: hard -"@vaadin/vaadin-text-field@npm:^2.8.0": - version: 2.8.4 - resolution: "@vaadin/vaadin-text-field@npm:2.8.4" +"@vaadin/vaadin-text-field@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-text-field@npm:20.0.1" dependencies: "@polymer/polymer": ^3.0.0 - "@vaadin/vaadin-control-state-mixin": ^2.2.1 - "@vaadin/vaadin-element-mixin": ^2.4.1 - "@vaadin/vaadin-lumo-styles": ^1.6.0 - "@vaadin/vaadin-material-styles": ^1.3.2 - "@vaadin/vaadin-themable-mixin": ^1.6.1 - checksum: fb7994b99b641f8570b5960f6ec40de4422caeca29a43bd760edbb95a39b87d2dfeeeeb21eb4e0d4eeaa33b11cc41a087c5a49c78c602f5ac951effdcc902693 + "@vaadin/vaadin-control-state-mixin": 20.0.1 + "@vaadin/vaadin-element-mixin": 20.0.1 + "@vaadin/vaadin-lumo-styles": 20.0.1 + "@vaadin/vaadin-material-styles": 20.0.1 + "@vaadin/vaadin-themable-mixin": 20.0.1 + checksum: 5bcba688a66a873de5586f6a42d005cd48a0f4d9143c6d23bbd4baaba196427679a74cadcc93a61a878dc33cecb8bef732896f1e8cb6de829dcc68aac9ce6ded languageName: node linkType: hard -"@vaadin/vaadin-themable-mixin@npm:^1.6.1": - version: 1.6.2 - resolution: "@vaadin/vaadin-themable-mixin@npm:1.6.2" +"@vaadin/vaadin-themable-mixin@npm:20.0.1": + version: 20.0.1 + resolution: "@vaadin/vaadin-themable-mixin@npm:20.0.1" dependencies: "@polymer/polymer": ^3.0.0 lit-element: ^2.0.0 - checksum: f8a0aff260ab2c512c4bbec3fcaa1c07e64244a45b971a108c827d94009223e0006150b0c006cd95477a9337a406b44046fc21e15a1e33d07fdc1f396ab7b387 + checksum: 1a0bcab0dbaec2f6ea03480ccd3957f5eb1145bdbcabb2bc8fe9168c279626ab665b55f4f4f8f1de0a79d272e42034a04a7e88b6e4d5b2e2196b6252be6c952b languageName: node linkType: hard @@ -8829,24 +8837,24 @@ fsevents@~2.3.1: "@fullcalendar/list": 5.1.0 "@koa/cors": ^3.1.0 "@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.6.0#./.yarn/patches/@lit-labs/virtualizer/0.7.0.patch" - "@material/chips": =12.0.0-canary.1a8d06483.0 - "@material/data-table": =12.0.0-canary.1a8d06483.0 - "@material/mwc-button": 0.22.0-canary.cc04657a.0 - "@material/mwc-checkbox": 0.22.0-canary.cc04657a.0 - "@material/mwc-circular-progress": 0.22.0-canary.cc04657a.0 - "@material/mwc-dialog": 0.22.0-canary.cc04657a.0 - "@material/mwc-fab": 0.22.0-canary.cc04657a.0 - "@material/mwc-formfield": 0.22.0-canary.cc04657a.0 - "@material/mwc-icon-button": 0.22.0-canary.cc04657a.0 - "@material/mwc-linear-progress": 0.22.0-canary.cc04657a.0 - "@material/mwc-list": 0.22.0-canary.cc04657a.0 - "@material/mwc-menu": 0.22.0-canary.cc04657a.0 - "@material/mwc-radio": 0.22.0-canary.cc04657a.0 - "@material/mwc-ripple": 0.22.0-canary.cc04657a.0 - "@material/mwc-switch": 0.22.0-canary.cc04657a.0 - "@material/mwc-tab": 0.22.0-canary.cc04657a.0 - "@material/mwc-tab-bar": 0.22.0-canary.cc04657a.0 - "@material/top-app-bar": =12.0.0-canary.1a8d06483.0 + "@material/chips": 12.0.0-canary.22d29cbb4.0 + "@material/data-table": 12.0.0-canary.22d29cbb4.0 + "@material/mwc-button": 0.22.1 + "@material/mwc-checkbox": 0.22.1 + "@material/mwc-circular-progress": 0.22.1 + "@material/mwc-dialog": 0.22.1 + "@material/mwc-fab": 0.22.1 + "@material/mwc-formfield": 0.22.1 + "@material/mwc-icon-button": 0.22.1 + "@material/mwc-linear-progress": 0.22.1 + "@material/mwc-list": 0.22.1 + "@material/mwc-menu": 0.22.1 + "@material/mwc-radio": 0.22.1 + "@material/mwc-ripple": 0.22.1 + "@material/mwc-switch": 0.22.1 + "@material/mwc-tab": 0.22.1 + "@material/mwc-tab-bar": 0.22.1 + "@material/top-app-bar": 12.0.0-canary.22d29cbb4.0 "@mdi/js": 5.9.55 "@mdi/svg": 5.9.55 "@open-wc/dev-server-hmr": ^0.0.2 @@ -8892,8 +8900,8 @@ fsevents@~2.3.1: "@types/webspeechapi": ^0.0.29 "@typescript-eslint/eslint-plugin": ^4.28.3 "@typescript-eslint/parser": ^4.28.3 - "@vaadin/vaadin-combo-box": ^5.0.10 - "@vaadin/vaadin-date-picker": ^4.0.7 + "@vaadin/vaadin-combo-box": ^20.0.1 + "@vaadin/vaadin-date-picker": ^20.0.1 "@vibrant/color": ^3.2.1-alpha.1 "@vibrant/core": ^3.2.1-alpha.1 "@vibrant/quantizer-mmcq": ^3.2.1-alpha.1 @@ -8933,7 +8941,7 @@ fsevents@~2.3.1: gulp-rename: ^2.0.0 gulp-zopfli-green: ^3.0.1 hls.js: ^1.0.7 - home-assistant-js-websocket: ^5.10.0 + home-assistant-js-websocket: ^5.11.1 html-minifier: ^4.0.0 husky: ^1.3.1 idb-keyval: ^5.0.5 @@ -9003,10 +9011,10 @@ fsevents@~2.3.1: languageName: unknown linkType: soft -"home-assistant-js-websocket@npm:^5.10.0": - version: 5.10.0 - resolution: "home-assistant-js-websocket@npm:5.10.0" - checksum: 86c11025bbc1146dbc1e22492324170993c540d7744b89f19471a6e5400051fee053dd13cc1b3e46d0c61858ee2cba739681f820dc2b73cddd258e4ff04829f8 +"home-assistant-js-websocket@npm:^5.11.1": + version: 5.11.1 + resolution: "home-assistant-js-websocket@npm:5.11.1" + checksum: 6766cff890831741cfb9ebc5372ed465be578897a8c763daac09a7673d804fa591658542ca1e93619104486956972c94b7f6f9098caaddbf36f89c7808db563d languageName: node linkType: hard