Compare commits

..

1 Commits

Author SHA1 Message Date
Paul Bottein
885a590443 Create button heading badge 2025-10-10 17:09:22 +02:00
20 changed files with 590 additions and 702 deletions

View File

@@ -36,14 +36,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
uses: github/codeql-action/init@64d10c13136e1c5bce3e5fbde8d4906eeaafc885 # v3.30.6
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
uses: github/codeql-action/autobuild@64d10c13136e1c5bce3e5fbde8d4906eeaafc885 # v3.30.6
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -57,4 +57,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
uses: github/codeql-action/analyze@64d10c13136e1c5bce3e5fbde8d4906eeaafc885 # v3.30.6

View File

@@ -55,7 +55,7 @@ jobs:
script/release
- name: Upload release assets
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
uses: softprops/action-gh-release@62c96d0c4e8a889135c1f3a25910db8dbe0e85f7 # v2.3.4
with:
files: |
dist/*.whl
@@ -108,7 +108,7 @@ jobs:
- name: Tar folder
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
- name: Upload release asset
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
uses: softprops/action-gh-release@62c96d0c4e8a889135c1f3a25910db8dbe0e85f7 # v2.3.4
with:
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
@@ -137,6 +137,6 @@ jobs:
- name: Tar folder
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
- name: Upload release asset
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
uses: softprops/action-gh-release@62c96d0c4e8a889135c1f3a25910db8dbe0e85f7 # v2.3.4
with:
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz

View File

@@ -34,18 +34,18 @@
"@codemirror/legacy-modes": "6.5.2",
"@codemirror/search": "6.5.11",
"@codemirror/state": "6.5.2",
"@codemirror/view": "6.38.5",
"@codemirror/view": "6.38.4",
"@date-fns/tz": "1.4.1",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.18.2",
"@formatjs/intl-displaynames": "6.8.13",
"@formatjs/intl-durationformat": "0.7.6",
"@formatjs/intl-datetimeformat": "6.18.1",
"@formatjs/intl-displaynames": "6.8.12",
"@formatjs/intl-durationformat": "0.7.5",
"@formatjs/intl-getcanonicallocales": "2.5.6",
"@formatjs/intl-listformat": "7.7.13",
"@formatjs/intl-locale": "4.2.13",
"@formatjs/intl-numberformat": "8.15.6",
"@formatjs/intl-pluralrules": "5.4.6",
"@formatjs/intl-relativetimeformat": "11.4.13",
"@formatjs/intl-listformat": "7.7.12",
"@formatjs/intl-locale": "4.2.12",
"@formatjs/intl-numberformat": "8.15.5",
"@formatjs/intl-pluralrules": "5.4.5",
"@formatjs/intl-relativetimeformat": "11.4.12",
"@fullcalendar/core": "6.1.19",
"@fullcalendar/daygrid": "6.1.19",
"@fullcalendar/interaction": "6.1.19",
@@ -99,7 +99,7 @@
"barcode-detector": "3.0.6",
"color-name": "2.0.2",
"comlink": "4.4.2",
"core-js": "3.46.0",
"core-js": "3.45.1",
"cropperjs": "1.6.2",
"culori": "4.0.2",
"date-fns": "4.1.0",
@@ -114,7 +114,7 @@
"hls.js": "1.6.13",
"home-assistant-js-websocket": "9.5.0",
"idb-keyval": "6.2.2",
"intl-messageformat": "10.7.18",
"intl-messageformat": "10.7.17",
"js-yaml": "4.1.0",
"leaflet": "1.9.4",
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",
@@ -122,7 +122,7 @@
"lit": "3.3.1",
"lit-html": "3.3.1",
"luxon": "3.7.2",
"marked": "16.4.0",
"marked": "16.3.0",
"memoize-one": "6.0.0",
"node-vibrant": "4.0.3",
"object-hash": "3.0.0",
@@ -135,7 +135,7 @@
"stacktrace-js": "2.0.2",
"superstruct": "2.0.2",
"tinykeys": "3.0.0",
"ua-parser-js": "2.0.6",
"ua-parser-js": "2.0.5",
"vue": "2.7.16",
"vue2-daterange-picker": "0.6.8",
"weekstart": "2.0.0",
@@ -152,7 +152,7 @@
"@babel/helper-define-polyfill-provider": "0.6.5",
"@babel/plugin-transform-runtime": "7.28.3",
"@babel/preset-env": "7.28.3",
"@bundle-stats/plugin-webpack-filter": "4.21.5",
"@bundle-stats/plugin-webpack-filter": "4.21.4",
"@lokalise/node-api": "15.3.0",
"@octokit/auth-oauth-device": "8.0.2",
"@octokit/plugin-retry": "8.0.2",

View File

@@ -21,6 +21,7 @@ import "../ha-combo-box-item";
import "../ha-generic-picker";
import type { HaGenericPicker } from "../ha-generic-picker";
import "../ha-icon-button";
import "../ha-input-helper-text";
import type {
PickerComboBoxItem,
PickerComboBoxSearchFn,
@@ -476,7 +477,6 @@ export class HaStatisticPicker extends LitElement {
.hideClearIcon=${this.hideClearIcon}
.searchFn=${this._searchFn}
.valueRenderer=${this._valueRenderer}
.helper=${this.helper}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>

View File

@@ -102,7 +102,6 @@ export type EnergySolarForecasts = Record<string, EnergySolarForecast>;
export interface DeviceConsumptionEnergyPreference {
// This is an ever increasing value
stat_consumption: string;
stat_power?: string;
name?: string;
included_in_stat?: string;
}
@@ -131,17 +130,11 @@ export interface FlowToGridSourceEnergyPreference {
number_energy_price: number | null;
}
export interface GridPowerSourceEnergyPreference {
// W meter
stat_power: string;
}
export interface GridSourceTypeEnergyPreference {
type: "grid";
flow_from: FlowFromGridSourceEnergyPreference[];
flow_to: FlowToGridSourceEnergyPreference[];
power?: GridPowerSourceEnergyPreference[];
cost_adjustment_day: number;
}
@@ -150,7 +143,6 @@ export interface SolarSourceTypeEnergyPreference {
type: "solar";
stat_energy_from: string;
stat_power?: string;
config_entry_solar_forecast: string[] | null;
}
@@ -158,7 +150,6 @@ export interface BatterySourceTypeEnergyPreference {
type: "battery";
stat_energy_from: string;
stat_energy_to: string;
stat_power?: string;
}
export interface GasSourceTypeEnergyPreference {
type: "gas";

View File

@@ -167,12 +167,15 @@ class MoreInfoMediaPlayer extends LitElement {
}
return html`<ha-md-button-menu positioning="popover">
<ha-icon-button
<ha-button
slot="trigger"
.title=${this.hass.localize(`ui.card.media_player.source`)}
.path=${mdiLoginVariant}
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(`ui.card.media_player.source`)}
>
</ha-icon-button>
<ha-svg-icon .path=${mdiLoginVariant}></ha-svg-icon>
</ha-button>
${this.stateObj.attributes.source_list!.map(
(source) =>
html`<ha-md-menu-item
@@ -200,12 +203,15 @@ class MoreInfoMediaPlayer extends LitElement {
}
return html`<ha-md-button-menu positioning="popover">
<ha-icon-button
<ha-button
slot="trigger"
.title=${this.hass.localize(`ui.card.media_player.sound_mode`)}
.path=${mdiMusicNoteEighth}
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(`ui.card.media_player.sound_mode`)}
>
</ha-icon-button>
<ha-svg-icon .path=${mdiMusicNoteEighth}></ha-svg-icon>
</ha-button>
${this.stateObj.attributes.sound_mode_list!.map(
(soundMode) =>
html`<ha-md-menu-item
@@ -231,17 +237,21 @@ class MoreInfoMediaPlayer extends LitElement {
const groupMembers = this.stateObj.attributes.group_members;
const hasMultipleMembers = groupMembers && groupMembers?.length > 1;
return html`<ha-icon-button
return html`<ha-button
class="grouping"
@click=${this._showGroupMediaPlayers}
.title=${this.hass.localize("ui.card.media_player.join")}
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize("ui.card.media_player.join")}
>
<div class="grouping">
<div>
<ha-svg-icon .path=${mdiSpeakerMultiple}></ha-svg-icon>
${hasMultipleMembers
? html`<span class="badge">${groupMembers?.length || 4}</span>`
? html`<span class="badge"> ${groupMembers?.length || 4} </span>`
: nothing}
</div>
</ha-icon-button>`;
</ha-button>`;
}
protected _renderEmptyCover(title: string, icon?: string) {
@@ -404,39 +414,48 @@ class MoreInfoMediaPlayer extends LitElement {
${!isUnavailableState(stateObj.state) &&
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA)
? html`
<ha-icon-button
<ha-button
@click=${this._showBrowseMedia}
.title=${this.hass.localize(
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(
"ui.card.media_player.browse_media"
)}
.path=${mdiPlayBoxMultiple}
>
</ha-icon-button>
<ha-svg-icon .path=${mdiPlayBoxMultiple}></ha-svg-icon>
</ha-button>
`
: nothing}
${this._renderGrouping()} ${this._renderSourceControl()}
${this._renderSoundMode()}
${turnOn
? html`<ha-icon-button
? html`<ha-button
action=${turnOn.action}
@click=${this._handleClick}
.title=${this.hass.localize(
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(
`ui.card.media_player.${turnOn.action}`
)}
.path=${turnOn.icon}
>
</ha-icon-button>`
<ha-svg-icon .path=${turnOn.icon}></ha-svg-icon>
</ha-button>`
: nothing}
${turnOff
? html`<ha-icon-button
? html`<ha-button
action=${turnOff.action}
@click=${this._handleClick}
.title=${this.hass.localize(
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(
`ui.card.media_player.${turnOff.action}`
)}
.path=${turnOff.icon}
>
</ha-icon-button>`
<ha-svg-icon .path=${turnOff.icon}></ha-svg-icon>
</ha-button>`
: nothing}
</div>
</div>
@@ -560,7 +579,7 @@ class MoreInfoMediaPlayer extends LitElement {
font-size: var(--ha-font-size-xs);
background-color: var(--primary-color);
padding: 0 4px;
color: var(--text-primary-color);
color: var(--primary-text-color);
}
.position-bar {
@@ -597,15 +616,15 @@ class MoreInfoMediaPlayer extends LitElement {
justify-content: space-around;
}
.controls-row ha-icon-button {
color: var(--secondary-text-color);
.controls-row ha-button {
width: 32px;
}
.controls-row ha-svg-icon {
color: var(--ha-color-on-neutral-quiet);
}
.grouping {
.grouping::part(label) {
position: relative;
}

View File

@@ -26,7 +26,6 @@ import type {
EnergySource,
FlowFromGridSourceEnergyPreference,
FlowToGridSourceEnergyPreference,
GridPowerSourceEnergyPreference,
GridSourceTypeEnergyPreference,
} from "../../../../data/energy";
import {
@@ -48,7 +47,6 @@ import { documentationUrl } from "../../../../util/documentation-url";
import {
showEnergySettingsGridFlowFromDialog,
showEnergySettingsGridFlowToDialog,
showEnergySettingsGridPowerDialog,
} from "../dialogs/show-dialogs-energy";
import "./ha-energy-validation-result";
import { energyCardStyles } from "./styles";
@@ -228,58 +226,6 @@ export class EnergyGridSettings extends LitElement {
>
</div>
<h3>
${this.hass.localize("ui.panel.config.energy.grid.grid_power")}
</h3>
${gridSource.power?.map((power) => {
const entityState = this.hass.states[power.stat_power];
return html`
<div class="row" .source=${power}>
${entityState?.attributes.icon
? html`<ha-icon
.icon=${entityState.attributes.icon}
></ha-icon>`
: html`<ha-svg-icon
.path=${mdiTransmissionTower}
></ha-svg-icon>`}
<span class="content"
>${getStatisticLabel(
this.hass,
power.stat_power,
this.statsMetadata?.[power.stat_power]
)}</span
>
<ha-icon-button
.label=${this.hass.localize(
"ui.panel.config.energy.grid.edit_power"
)}
@click=${this._editPowerSource}
.path=${mdiPencil}
></ha-icon-button>
<ha-icon-button
.label=${this.hass.localize(
"ui.panel.config.energy.grid.delete_power"
)}
@click=${this._deletePowerSource}
.path=${mdiDelete}
></ha-icon-button>
</div>
`;
})}
<div class="row border-bottom">
<ha-svg-icon .path=${mdiTransmissionTower}></ha-svg-icon>
<ha-button
@click=${this._addPowerSource}
appearance="filled"
size="small"
>
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon
>${this.hass.localize(
"ui.panel.config.energy.grid.add_power"
)}</ha-button
>
</div>
<h3>
${this.hass.localize(
"ui.panel.config.energy.grid.grid_carbon_footprint"
@@ -553,97 +499,6 @@ export class EnergyGridSettings extends LitElement {
await this._savePreferences(cleanedPreferences);
}
private _addPowerSource() {
const gridSource = this.preferences.energy_sources.find(
(src) => src.type === "grid"
) as GridSourceTypeEnergyPreference | undefined;
showEnergySettingsGridPowerDialog(this, {
grid_source: gridSource,
saveCallback: async (power) => {
let preferences: EnergyPreferences;
if (!gridSource) {
preferences = {
...this.preferences,
energy_sources: [
...this.preferences.energy_sources,
{
...emptyGridSourceEnergyPreference(),
power: [power],
},
],
};
} else {
preferences = {
...this.preferences,
energy_sources: this.preferences.energy_sources.map((src) =>
src.type === "grid"
? { ...src, power: [...(gridSource.power || []), power] }
: src
),
};
}
await this._savePreferences(preferences);
},
});
}
private _editPowerSource(ev) {
const origSource: GridPowerSourceEnergyPreference =
ev.currentTarget.closest(".row").source;
const gridSource = this.preferences.energy_sources.find(
(src) => src.type === "grid"
) as GridSourceTypeEnergyPreference | undefined;
showEnergySettingsGridPowerDialog(this, {
source: { ...origSource },
grid_source: gridSource,
saveCallback: async (source) => {
const power =
energySourcesByType(this.preferences).grid![0].power || [];
const preferences: EnergyPreferences = {
...this.preferences,
energy_sources: this.preferences.energy_sources.map((src) =>
src.type === "grid"
? {
...src,
power: power.map((p) => (p === origSource ? source : p)),
}
: src
),
};
await this._savePreferences(preferences);
},
});
}
private async _deletePowerSource(ev) {
const sourceToDelete: GridPowerSourceEnergyPreference =
ev.currentTarget.closest(".row").source;
if (
!(await showConfirmationDialog(this, {
title: this.hass.localize("ui.panel.config.energy.delete_source"),
}))
) {
return;
}
const power =
energySourcesByType(this.preferences).grid![0].power?.filter(
(p) => p !== sourceToDelete
) || [];
const preferences: EnergyPreferences = {
...this.preferences,
energy_sources: this.preferences.energy_sources.map((source) =>
source.type === "grid" ? { ...source, power } : source
),
};
const cleanedPreferences = this._removeEmptySources(preferences);
await this._savePreferences(cleanedPreferences);
}
private _removeEmptySources(preferences: EnergyPreferences) {
// Check if grid sources became an empty type and remove if so
preferences.energy_sources = preferences.energy_sources.reduce<
@@ -652,8 +507,7 @@ export class EnergyGridSettings extends LitElement {
if (
source.type !== "grid" ||
source.flow_from.length > 0 ||
source.flow_to.length > 0 ||
(source.power && source.power.length > 0)
source.flow_to.length > 0
) {
acc.push(source);
}

View File

@@ -18,7 +18,6 @@ import type { HomeAssistant } from "../../../../types";
import type { EnergySettingsBatteryDialogParams } from "./show-dialogs-energy";
const energyUnitClasses = ["energy"];
const powerUnitClasses = ["power"];
@customElement("dialog-energy-battery-settings")
export class DialogEnergyBatterySettings
@@ -33,14 +32,10 @@ export class DialogEnergyBatterySettings
@state() private _energy_units?: string[];
@state() private _power_units?: string[];
@state() private _error?: string;
private _excludeList?: string[];
private _excludeListPower?: string[];
public async showDialog(
params: EnergySettingsBatteryDialogParams
): Promise<void> {
@@ -51,9 +46,6 @@ export class DialogEnergyBatterySettings
this._energy_units = (
await getSensorDeviceClassConvertibleUnits(this.hass, "energy")
).units;
this._power_units = (
await getSensorDeviceClassConvertibleUnits(this.hass, "power")
).units;
const allSources: string[] = [];
this._params.battery_sources.forEach((entry) => {
allSources.push(entry.stat_energy_from);
@@ -64,9 +56,6 @@ export class DialogEnergyBatterySettings
id !== this._source?.stat_energy_from &&
id !== this._source?.stat_energy_to
);
this._excludeListPower = this._params.battery_sources
.map((entry) => entry.stat_power)
.filter((id) => id && id !== this._source?.stat_power) as string[];
}
public closeDialog() {
@@ -83,6 +72,8 @@ export class DialogEnergyBatterySettings
return nothing;
}
const pickableUnit = this._energy_units?.join(", ") || "";
return html`
<ha-dialog
open
@@ -94,6 +85,12 @@ export class DialogEnergyBatterySettings
@closed=${this.closeDialog}
>
${this._error ? html`<p class="error">${this._error}</p>` : ""}
<div>
${this.hass.localize(
"ui.panel.config.energy.battery.dialog.entity_para",
{ unit: pickableUnit }
)}
</div>
<ha-statistic-picker
.hass=${this.hass}
@@ -108,10 +105,6 @@ export class DialogEnergyBatterySettings
this._source.stat_energy_from,
]}
@value-changed=${this._statisticToChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.battery.dialog.energy_helper_into",
{ unit: this._energy_units?.join(", ") || "" }
)}
dialogInitialFocus
></ha-statistic-picker>
@@ -128,25 +121,6 @@ export class DialogEnergyBatterySettings
this._source.stat_energy_to,
]}
@value-changed=${this._statisticFromChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.battery.dialog.energy_helper_out",
{ unit: this._energy_units?.join(", ") || "" }
)}
></ha-statistic-picker>
<ha-statistic-picker
.hass=${this.hass}
.includeUnitClass=${powerUnitClasses}
.value=${this._source.stat_power}
.label=${this.hass.localize(
"ui.panel.config.energy.battery.dialog.power"
)}
.excludeStatistics=${this._excludeListPower}
@value-changed=${this._powerChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.battery.dialog.power_helper",
{ unit: this._power_units?.join(", ") || "" }
)}
></ha-statistic-picker>
<ha-button
@@ -176,10 +150,6 @@ export class DialogEnergyBatterySettings
this._source = { ...this._source!, stat_energy_from: ev.detail.value };
}
private _powerChanged(ev: CustomEvent<{ value: string }>) {
this._source = { ...this._source!, stat_power: ev.detail.value };
}
private async _save() {
try {
await this._params!.saveCallback(this._source!);
@@ -198,11 +168,7 @@ export class DialogEnergyBatterySettings
--mdc-dialog-max-width: 430px;
}
ha-statistic-picker {
display: block;
margin-bottom: var(--ha-space-4);
}
ha-statistic-picker:last-of-type {
margin-bottom: 0;
width: 100%;
}
`,
];

View File

@@ -21,7 +21,6 @@ import type { HomeAssistant } from "../../../../types";
import type { EnergySettingsDeviceDialogParams } from "./show-dialogs-energy";
const energyUnitClasses = ["energy"];
const powerUnitClasses = ["power"];
@customElement("dialog-energy-device-settings")
export class DialogEnergyDeviceSettings
@@ -36,14 +35,10 @@ export class DialogEnergyDeviceSettings
@state() private _energy_units?: string[];
@state() private _power_units?: string[];
@state() private _error?: string;
private _excludeList?: string[];
private _excludeListPower?: string[];
private _possibleParents: DeviceConsumptionEnergyPreference[] = [];
public async showDialog(
@@ -55,15 +50,9 @@ export class DialogEnergyDeviceSettings
this._energy_units = (
await getSensorDeviceClassConvertibleUnits(this.hass, "energy")
).units;
this._power_units = (
await getSensorDeviceClassConvertibleUnits(this.hass, "power")
).units;
this._excludeList = this._params.device_consumptions
.map((entry) => entry.stat_consumption)
.filter((id) => id !== this._device?.stat_consumption);
this._excludeListPower = this._params.device_consumptions
.map((entry) => entry.stat_power)
.filter((id) => id && id !== this._device?.stat_power) as string[];
}
private _computePossibleParents() {
@@ -104,6 +93,8 @@ export class DialogEnergyDeviceSettings
return nothing;
}
const pickableUnit = this._energy_units?.join(", ") || "";
return html`
<ha-dialog
open
@@ -117,6 +108,12 @@ export class DialogEnergyDeviceSettings
@closed=${this.closeDialog}
>
${this._error ? html`<p class="error">${this._error}</p>` : ""}
<div>
${this.hass.localize(
"ui.panel.config.energy.device_consumption.dialog.selected_stat_intro",
{ unit: pickableUnit }
)}
</div>
<ha-statistic-picker
.hass=${this.hass}
@@ -128,28 +125,9 @@ export class DialogEnergyDeviceSettings
)}
.excludeStatistics=${this._excludeList}
@value-changed=${this._statisticChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.device_consumption.dialog.selected_stat_intro",
{ unit: this._energy_units?.join(", ") || "" }
)}
dialogInitialFocus
></ha-statistic-picker>
<ha-statistic-picker
.hass=${this.hass}
.includeUnitClass=${powerUnitClasses}
.value=${this._device?.stat_power}
.label=${this.hass.localize(
"ui.panel.config.energy.device_consumption.dialog.device_consumption_power"
)}
.excludeStatistics=${this._excludeListPower}
@value-changed=${this._powerStatisticChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.device_consumption.dialog.selected_stat_intro",
{ unit: this._power_units?.join(", ") || "" }
)}
></ha-statistic-picker>
<ha-textfield
.label=${this.hass.localize(
"ui.panel.config.energy.device_consumption.dialog.display_name"
@@ -232,20 +210,6 @@ export class DialogEnergyDeviceSettings
this._computePossibleParents();
}
private _powerStatisticChanged(ev: CustomEvent<{ value: string }>) {
if (!this._device) {
return;
}
const newDevice = {
...this._device,
stat_power: ev.detail.value,
} as DeviceConsumptionEnergyPreference;
if (!newDevice.stat_power) {
delete newDevice.stat_power;
}
this._device = newDevice;
}
private _nameChanged(ev) {
const newDevice = {
...this._device!,
@@ -281,19 +245,15 @@ export class DialogEnergyDeviceSettings
return [
haStyleDialog,
css`
ha-statistic-picker {
display: block;
margin-bottom: var(--ha-space-2);
}
ha-statistic-picker {
width: 100%;
}
ha-select {
margin-top: var(--ha-space-4);
margin-top: 16px;
width: 100%;
}
ha-textfield {
margin-top: var(--ha-space-4);
margin-top: 16px;
width: 100%;
}
`,

View File

@@ -115,6 +115,8 @@ export class DialogEnergyGridFlowSettings
return nothing;
}
const pickableUnit = this._energy_units?.join(", ") || "";
const unitPriceSensor = this._pickedDisplayUnit
? `${this.hass.config.currency}/${this._pickedDisplayUnit}`
: undefined;
@@ -148,11 +150,19 @@ export class DialogEnergyGridFlowSettings
@closed=${this.closeDialog}
>
${this._error ? html`<p class="error">${this._error}</p>` : ""}
<p>
${this.hass.localize(
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.paragraph`
)}
</p>
<div>
<p>
${this.hass.localize(
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.paragraph`
)}
</p>
<p>
${this.hass.localize(
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.entity_para`,
{ unit: pickableUnit }
)}
</p>
</div>
<ha-statistic-picker
.hass=${this.hass}
@@ -168,10 +178,6 @@ export class DialogEnergyGridFlowSettings
)}
.excludeStatistics=${this._excludeList}
@value-changed=${this._statisticChanged}
.helper=${this.hass.localize(
`ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.entity_para`,
{ unit: this._energy_units?.join(", ") || "" }
)}
dialogInitialFocus
></ha-statistic-picker>
@@ -374,10 +380,6 @@ export class DialogEnergyGridFlowSettings
ha-dialog {
--mdc-dialog-max-width: 430px;
}
ha-statistic-picker {
display: block;
margin: var(--ha-space-4) 0;
}
ha-formfield {
display: block;
}

View File

@@ -1,153 +0,0 @@
import { mdiTransmissionTower } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-statistic-picker";
import "../../../../components/ha-dialog";
import "../../../../components/ha-button";
import type { GridPowerSourceEnergyPreference } from "../../../../data/energy";
import { energyStatisticHelpUrl } from "../../../../data/energy";
import { getSensorDeviceClassConvertibleUnits } from "../../../../data/sensor";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { EnergySettingsGridPowerDialogParams } from "./show-dialogs-energy";
const powerUnitClasses = ["power"];
@customElement("dialog-energy-grid-power-settings")
export class DialogEnergyGridPowerSettings
extends LitElement
implements HassDialog<EnergySettingsGridPowerDialogParams>
{
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _params?: EnergySettingsGridPowerDialogParams;
@state() private _source?: GridPowerSourceEnergyPreference;
@state() private _power_units?: string[];
@state() private _error?: string;
private _excludeListPower?: string[];
public async showDialog(
params: EnergySettingsGridPowerDialogParams
): Promise<void> {
this._params = params;
this._source = params.source ? { ...params.source } : { stat_power: "" };
const initialSourceIdPower = this._source.stat_power;
this._power_units = (
await getSensorDeviceClassConvertibleUnits(this.hass, "power")
).units;
this._excludeListPower = [
...(this._params.grid_source?.power?.map((entry) => entry.stat_power) ||
[]),
].filter((id) => id && id !== initialSourceIdPower) as string[];
}
public closeDialog() {
this._params = undefined;
this._source = undefined;
this._error = undefined;
this._excludeListPower = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
return true;
}
protected render() {
if (!this._params || !this._source) {
return nothing;
}
return html`
<ha-dialog
open
.heading=${html`<ha-svg-icon
.path=${mdiTransmissionTower}
style="--mdc-icon-size: 32px;"
></ha-svg-icon
>${this.hass.localize(
"ui.panel.config.energy.grid.power_dialog.header"
)}`}
@closed=${this.closeDialog}
>
${this._error ? html`<p class="error">${this._error}</p>` : ""}
<ha-statistic-picker
.hass=${this.hass}
.helpMissingEntityUrl=${energyStatisticHelpUrl}
.includeUnitClass=${powerUnitClasses}
.value=${this._source.stat_power}
.label=${this.hass.localize(
"ui.panel.config.energy.grid.power_dialog.power_stat"
)}
.excludeStatistics=${this._excludeListPower}
@value-changed=${this._powerStatisticChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.grid.power_dialog.power_helper",
{ unit: this._power_units?.join(", ") || "" }
)}
dialogInitialFocus
></ha-statistic-picker>
<ha-button
appearance="plain"
@click=${this.closeDialog}
slot="primaryAction"
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button
@click=${this._save}
.disabled=${!this._source.stat_power}
slot="primaryAction"
>
${this.hass.localize("ui.common.save")}
</ha-button>
</ha-dialog>
`;
}
private _powerStatisticChanged(ev: CustomEvent<{ value: string }>) {
this._source = {
...this._source!,
stat_power: ev.detail.value,
};
}
private async _save() {
try {
await this._params!.saveCallback(this._source!);
this.closeDialog();
} catch (err: any) {
this._error = err.message;
}
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
ha-dialog {
--mdc-dialog-max-width: 430px;
}
ha-statistic-picker {
display: block;
margin: var(--ha-space-4) 0;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-energy-grid-power-settings": DialogEnergyGridPowerSettings;
}
}

View File

@@ -28,7 +28,6 @@ import { brandsUrl } from "../../../../util/brands-url";
import type { EnergySettingsSolarDialogParams } from "./show-dialogs-energy";
const energyUnitClasses = ["energy"];
const powerUnitClasses = ["power"];
@customElement("dialog-energy-solar-settings")
export class DialogEnergySolarSettings
@@ -47,14 +46,10 @@ export class DialogEnergySolarSettings
@state() private _energy_units?: string[];
@state() private _power_units?: string[];
@state() private _error?: string;
private _excludeList?: string[];
private _excludeListPower?: string[];
public async showDialog(
params: EnergySettingsSolarDialogParams
): Promise<void> {
@@ -67,15 +62,9 @@ export class DialogEnergySolarSettings
this._energy_units = (
await getSensorDeviceClassConvertibleUnits(this.hass, "energy")
).units;
this._power_units = (
await getSensorDeviceClassConvertibleUnits(this.hass, "power")
).units;
this._excludeList = this._params.solar_sources
.map((entry) => entry.stat_energy_from)
.filter((id) => id !== this._source?.stat_energy_from);
this._excludeListPower = this._params.solar_sources
.map((entry) => entry.stat_power)
.filter((id) => id && id !== this._source?.stat_power) as string[];
}
public closeDialog() {
@@ -92,6 +81,8 @@ export class DialogEnergySolarSettings
return nothing;
}
const pickableUnit = this._energy_units?.join(", ") || "";
return html`
<ha-dialog
open
@@ -103,6 +94,12 @@ export class DialogEnergySolarSettings
@closed=${this.closeDialog}
>
${this._error ? html`<p class="error">${this._error}</p>` : ""}
<div>
${this.hass.localize(
"ui.panel.config.energy.solar.dialog.entity_para",
{ unit: pickableUnit }
)}
</div>
<ha-statistic-picker
.hass=${this.hass}
@@ -114,28 +111,9 @@ export class DialogEnergySolarSettings
)}
.excludeStatistics=${this._excludeList}
@value-changed=${this._statisticChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.solar.dialog.entity_para",
{ unit: this._energy_units?.join(", ") || "" }
)}
dialogInitialFocus
></ha-statistic-picker>
<ha-statistic-picker
.hass=${this.hass}
.includeUnitClass=${powerUnitClasses}
.value=${this._source.stat_power}
.label=${this.hass.localize(
"ui.panel.config.energy.solar.dialog.solar_production_power"
)}
.excludeStatistics=${this._excludeListPower}
@value-changed=${this._powerStatisticChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.solar.dialog.entity_para",
{ unit: this._power_units?.join(", ") || "" }
)}
></ha-statistic-picker>
<h3>
${this.hass.localize(
"ui.panel.config.energy.solar.dialog.solar_production_forecast"
@@ -289,10 +267,6 @@ export class DialogEnergySolarSettings
this._source = { ...this._source!, stat_energy_from: ev.detail.value };
}
private _powerStatisticChanged(ev: CustomEvent<{ value: string }>) {
this._source = { ...this._source!, stat_power: ev.detail.value };
}
private async _save() {
try {
if (!this._forecast) {
@@ -313,10 +287,6 @@ export class DialogEnergySolarSettings
ha-dialog {
--mdc-dialog-max-width: 430px;
}
ha-statistic-picker {
display: block;
margin-bottom: var(--ha-space-4);
}
img {
height: 24px;
margin-right: 16px;

View File

@@ -7,7 +7,6 @@ import type {
FlowFromGridSourceEnergyPreference,
FlowToGridSourceEnergyPreference,
GasSourceTypeEnergyPreference,
GridPowerSourceEnergyPreference,
GridSourceTypeEnergyPreference,
SolarSourceTypeEnergyPreference,
WaterSourceTypeEnergyPreference,
@@ -42,12 +41,6 @@ export interface EnergySettingsGridFlowToDialogParams {
saveCallback: (source: FlowToGridSourceEnergyPreference) => Promise<void>;
}
export interface EnergySettingsGridPowerDialogParams {
source?: GridPowerSourceEnergyPreference;
grid_source?: GridSourceTypeEnergyPreference;
saveCallback: (source: GridPowerSourceEnergyPreference) => Promise<void>;
}
export interface EnergySettingsSolarDialogParams {
info: EnergyInfo;
source?: SolarSourceTypeEnergyPreference;
@@ -159,14 +152,3 @@ export const showEnergySettingsGridFlowToDialog = (
dialogParams: { ...dialogParams, direction: "to" },
});
};
export const showEnergySettingsGridPowerDialog = (
element: HTMLElement,
dialogParams: EnergySettingsGridPowerDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-energy-grid-power-settings",
dialogImport: () => import("./dialog-energy-grid-power-settings"),
dialogParams: dialogParams,
});
};

View File

@@ -1,4 +1,5 @@
import "../heading-badges/hui-entity-heading-badge";
import "../heading-badges/hui-button-heading-badge";
import {
createLovelaceElement,
@@ -6,7 +7,7 @@ import {
} from "./create-element-base";
import type { LovelaceHeadingBadgeConfig } from "../heading-badges/types";
const ALWAYS_LOADED_TYPES = new Set(["error", "entity"]);
const ALWAYS_LOADED_TYPES = new Set(["error", "entity", "button"]);
export const createHeadingBadgeElement = (config: LovelaceHeadingBadgeConfig) =>
createLovelaceElement(

View File

@@ -0,0 +1,238 @@
import { mdiEye, mdiGestureTap, mdiTextShort } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { any, array, assert, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import type { Condition } from "../../common/validate-condition";
import type { UiAction } from "../../components/hui-action-editor";
import type { ButtonHeadingBadgeConfig } from "../../heading-badges/types";
import type { LovelaceGenericElementEditor } from "../../types";
import "../conditions/ha-card-conditions-editor";
import { configElementStyle } from "../config-elements/config-elements-style";
import { actionConfigStruct } from "../structs/action-struct";
export const DEFAULT_CONFIG: Partial<ButtonHeadingBadgeConfig> = {
type: "button",
};
const entityConfigStruct = object({
type: optional(string()),
text: optional(string()),
icon: optional(string()),
tap_action: optional(actionConfigStruct),
hold_action: optional(actionConfigStruct),
double_tap_action: optional(actionConfigStruct),
visibility: optional(array(any())),
});
const ALLOWED_ACTIONS: UiAction[] = [
"navigate",
"url",
"assist",
"call-service",
"none",
];
@customElement("hui-heading-button-editor")
export class HuiHeadingButtonEditor
extends LitElement
implements LovelaceGenericElementEditor
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public preview = false;
@state() private _config?: ButtonHeadingBadgeConfig;
public setConfig(config: ButtonHeadingBadgeConfig): void {
assert(config, entityConfigStruct);
this._config = {
...DEFAULT_CONFIG,
...config,
};
}
private _schema = memoizeOne(
() =>
[
{
name: "content",
type: "expandable",
flatten: true,
iconPath: mdiTextShort,
schema: [
{
name: "",
type: "grid",
schema: [
{
name: "text",
selector: {
text: {},
},
},
{
name: "icon",
selector: { icon: {} },
context: { icon_entity: "entity" },
},
],
},
],
},
{
name: "interactions",
type: "expandable",
flatten: true,
iconPath: mdiGestureTap,
schema: [
{
name: "tap_action",
selector: {
ui_action: {
default_action: "none",
actions: ALLOWED_ACTIONS,
},
},
},
{
name: "",
type: "optional_actions",
flatten: true,
schema: (["hold_action", "double_tap_action"] as const).map(
(action) => ({
name: action,
selector: {
ui_action: {
default_action: "none" as const,
actions: ALLOWED_ACTIONS,
},
},
})
),
},
],
},
] as const satisfies readonly HaFormSchema[]
);
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
const schema = this._schema();
const conditions = this._config.visibility ?? [];
return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
<ha-expansion-panel outlined>
<ha-svg-icon slot="leading-icon" .path=${mdiEye}></ha-svg-icon>
<h3 slot="header">
${this.hass!.localize(
"ui.panel.lovelace.editor.card.heading.entity_config.visibility"
)}
</h3>
<div class="content">
<p class="intro">
${this.hass.localize(
"ui.panel.lovelace.editor.card.heading.entity_config.visibility_explanation"
)}
</p>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${conditions}
@value-changed=${this._conditionChanged}
>
</ha-card-conditions-editor>
</div>
</ha-expansion-panel>
`;
}
private _valueChanged(ev: CustomEvent): void {
ev.stopPropagation();
if (!this._config || !this.hass) {
return;
}
const config = { ...ev.detail.value } as FormData;
fireEvent(this, "config-changed", { config });
}
private _conditionChanged(ev: CustomEvent): void {
ev.stopPropagation();
if (!this._config || !this.hass) {
return;
}
const conditions = ev.detail.value as Condition[];
const newConfig: ButtonHeadingBadgeConfig = {
...this._config,
visibility: conditions,
};
if (newConfig.visibility?.length === 0) {
delete newConfig.visibility;
}
fireEvent(this, "config-changed", { config: newConfig });
}
private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
switch (schema.name) {
case "text":
return this.hass!.localize(
`ui.panel.lovelace.editor.card.heading.button_config.${schema.name}`
);
default:
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
}
};
static get styles() {
return [
configElementStyle,
css`
.container {
display: flex;
flex-direction: column;
}
ha-form {
display: block;
margin-bottom: 24px;
}
.intro {
margin: 0;
color: var(--secondary-text-color);
margin-bottom: 8px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-heading-button-editor": HuiHeadingButtonEditor;
}
}

View File

@@ -0,0 +1,96 @@
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-state-icon";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import "../../../state-display/state-display";
import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action";
import { DEFAULT_CONFIG } from "../editor/heading-badge-editor/hui-entity-heading-badge-editor";
import type {
LovelaceHeadingBadge,
LovelaceHeadingBadgeEditor,
} from "../types";
import type { ButtonHeadingBadgeConfig } from "./types";
const DEFAULT_ACTIONS: Pick<
ButtonHeadingBadgeConfig,
"tap_action" | "hold_action" | "double_tap_action"
> = {
tap_action: { action: "none" },
hold_action: { action: "none" },
double_tap_action: { action: "none" },
};
@customElement("hui-button-heading-badge")
export class HuiButtonHeadingBadge
extends LitElement
implements LovelaceHeadingBadge
{
public static async getConfigElement(): Promise<LovelaceHeadingBadgeEditor> {
await import(
"../editor/heading-badge-editor/hui-button-heading-badge-editor"
);
return document.createElement("hui-heading-button-editor");
}
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: ButtonHeadingBadgeConfig;
@property({ type: Boolean }) public preview = false;
public setConfig(config): void {
this._config = {
...DEFAULT_CONFIG,
...DEFAULT_ACTIONS,
...config,
};
}
get hasAction() {
return (
hasAction(this._config?.tap_action) ||
hasAction(this._config?.hold_action) ||
hasAction(this._config?.double_tap_action)
);
}
private _handleAction(ev: ActionHandlerEvent) {
handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
const config = this._config;
return html`
<ha-button
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config!.hold_action),
hasDoubleClick: hasAction(this._config!.double_tap_action),
})}
>
<ha-icon .icon=${config.icon}></ha-icon>
${this._config.text}
</ha-button>
`;
}
static styles = css`
[role="button"] {
cursor: pointer;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"hui-button-heading-badge": HuiButtonHeadingBadge;
}
}

View File

@@ -26,3 +26,12 @@ export interface EntityHeadingBadgeConfig extends LovelaceHeadingBadgeConfig {
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}
export interface ButtonHeadingBadgeConfig extends LovelaceHeadingBadgeConfig {
type?: "button";
icon: string;
text?: string;
tap_action: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}

View File

@@ -4,7 +4,6 @@ import { isComponentLoaded } from "../../../../common/config/is_component_loaded
import { generateEntityFilter } from "../../../../common/entity/entity_filter";
import type { AreaRegistryEntry } from "../../../../data/area_registry";
import { getEnergyPreferences } from "../../../../data/energy";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type {
LovelaceSectionConfig,
LovelaceSectionRawConfig,
@@ -19,9 +18,8 @@ import type {
TileCardConfig,
WeatherForecastCardConfig,
} from "../../cards/types";
import { getAreas, getFloors } from "../areas/helpers/areas-strategy-helper";
import { getAreas } from "../areas/helpers/areas-strategy-helper";
import type { CommonControlSectionStrategyConfig } from "../usage_prediction/common-controls-section-strategy";
import { getHomeStructure } from "./helpers/home-structure";
export interface HomeMainViewStrategyConfig {
type: "home-main";
@@ -61,67 +59,25 @@ export class HomeMainViewStrategy extends ReactiveElement {
hass: HomeAssistant
): Promise<LovelaceViewConfig> {
const areas = getAreas(hass.areas);
const floors = getFloors(hass.floors);
const home = getHomeStructure(floors, areas);
const floorCount = home.floors.length + (home.areas.length ? 1 : 0);
const areasSection: LovelaceSectionConfig = {
type: "grid",
column_span: 2,
cards: [
{
type: "heading",
heading_style: "title",
heading: hass.localize("ui.panel.lovelace.strategy.home.areas"),
},
...areas.map<AreaCardConfig>((area) =>
computeAreaCard(area.area_id, hass)
),
],
};
// Allow between 2 and 3 columns (the max should be set to define the width of the header)
const maxColumns = 2;
const floorsSections: LovelaceSectionConfig[] = [];
for (const floorStructure of home.floors) {
const floorId = floorStructure.id;
const areaIds = floorStructure.areas;
const floor = hass.floors[floorId];
const cards: LovelaceCardConfig[] = [];
for (const areaId of areaIds) {
cards.push(computeAreaCard(areaId, hass));
}
if (cards.length) {
floorsSections.push({
type: "grid",
column_span: maxColumns,
cards: [
{
type: "heading",
heading:
floorCount > 1
? floor.name
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
heading_style: "title",
},
...cards,
],
});
}
}
if (home.areas.length) {
const cards: LovelaceCardConfig[] = [];
for (const areaId of home.areas) {
cards.push(computeAreaCard(areaId, hass));
}
floorsSections.push({
type: "grid",
column_span: maxColumns,
cards: [
{
type: "heading",
heading:
floorCount > 1
? hass.localize("ui.panel.lovelace.strategy.home.other_areas")
: hass.localize("ui.panel.lovelace.strategy.home.areas"),
heading_style: "title",
},
...cards,
],
});
}
const favoriteSection: LovelaceSectionConfig = {
type: "grid",
column_span: maxColumns,
@@ -278,7 +234,7 @@ export class HomeMainViewStrategy extends ReactiveElement {
favoriteSection.cards && favoriteSection,
commonControlsSection,
summarySection,
...floorsSections,
areasSection,
widgetSection.cards && widgetSection,
] satisfies (LovelaceSectionRawConfig | undefined)[]
).filter(Boolean) as LovelaceSectionRawConfig[];

View File

@@ -3036,15 +3036,6 @@
"grid_carbon_footprint": "Grid carbon footprint",
"remove_co2_signal": "Remove Electricity Maps integration",
"add_co2_signal": "Add Electricity Maps integration",
"grid_power": "Grid power",
"add_power": "Add power sensor",
"edit_power": "Edit power sensor",
"delete_power": "Delete power sensor",
"power_dialog": {
"header": "Configure grid power",
"power_stat": "Power sensor",
"power_helper": "Pick a sensor which measures grid power in either of {unit}. Positive values indicate using electricity from the grid, negative values indicate exporting electricity to the grid."
},
"flow_dialog": {
"from": {
"header": "Configure grid consumption",
@@ -3091,7 +3082,6 @@
"header": "Configure solar panels",
"entity_para": "Pick a sensor which measures solar energy production in either of {unit}.",
"solar_production_energy": "Solar production energy",
"solar_production_power": "Solar production power",
"solar_production_forecast": "Solar production forecast",
"solar_production_forecast_description": "Adding solar production forecast information will allow you to quickly see your expected production for today.",
"dont_forecast_production": "Don't forecast production",
@@ -3109,12 +3099,9 @@
"add_battery_system": "Add battery system",
"dialog": {
"header": "Configure battery system",
"energy_helper_into": "Pick a sensor that measures the electricity flowing into the battery in either of {unit}.",
"energy_helper_out": "Pick a sensor that measures the electricity flowing out of the battery in either of {unit}.",
"energy_into_battery": "Energy charged into the battery",
"energy_out_of_battery": "Energy discharged from the battery",
"power": "Battery power",
"power_helper": "Pick a sensor which measures the electricity flowing into and out of the battery in either of {unit}. Positive values indicate discharging the battery, negative values indicate charging the battery."
"entity_para": "Pick sensors which measure energy going into and coming out of the battery in either of {unit}.",
"energy_into_battery": "Energy going into the battery",
"energy_out_of_battery": "Energy coming out of the battery"
}
},
"gas": {
@@ -3176,8 +3163,7 @@
"header": "Add a device",
"display_name": "Display name",
"device_consumption_energy": "Device energy consumption",
"device_consumption_power": "Device power consumption",
"selected_stat_intro": "Select the sensor that measures the device's electricity usage in either of {unit}.",
"selected_stat_intro": "Select the energy sensor that measures the device's energy usage in either of {unit}.",
"included_in_device": "Upstream device",
"included_in_device_helper": "If this device is already counted by another device (such as a smart switch measured by a smart breaker), selecting the upstream device prevents duplicate energy tracking.",
"no_upstream_devices": "No eligible upstream devices"
@@ -7780,6 +7766,9 @@
"state": "[%key:ui::panel::lovelace::editor::badge::entity::displayed_elements_options::state%]"
}
},
"button_config": {
"text": "Text"
},
"default_heading": "Kitchen"
},
"map": {
@@ -7950,7 +7939,7 @@
"hide_completed": "Hide completed items",
"hide_create": "Hide 'Add item' field",
"hide_section_headers": "Hide section headers",
"hide_section_headers_helper": "Removes the 'Active' and 'Completed' section headers and their overflow menus.",
"hide_section_headers_helper": "Removes the 'Active' and 'Completed' sections with the overflow menus.",
"display_order": "Display order",
"item_tap_action": "Item tap behavior",
"actions": {

210
yarn.lock
View File

@@ -1206,14 +1206,14 @@ __metadata:
languageName: node
linkType: hard
"@bundle-stats/plugin-webpack-filter@npm:4.21.5":
version: 4.21.5
resolution: "@bundle-stats/plugin-webpack-filter@npm:4.21.5"
"@bundle-stats/plugin-webpack-filter@npm:4.21.4":
version: 4.21.4
resolution: "@bundle-stats/plugin-webpack-filter@npm:4.21.4"
dependencies:
tslib: "npm:2.8.1"
peerDependencies:
core-js: ^3.0.0
checksum: 10/a24e4fb0efdd671e8d21116045a22500494198feb451981d40db732cd890b90b7bad9a9f1d7909c8db9a1069f4c6226ae35939b4d865be437ea8e19b989ebcf1
checksum: 10/67e589755ce6702c92db3e9ca6158d0cbdce5a0c1d92508fae61c0424720121e81c6884efd86392f4906db871e4ba6f97111a55976571cdb79720a47149c027b
languageName: node
linkType: hard
@@ -1284,15 +1284,15 @@ __metadata:
languageName: node
linkType: hard
"@codemirror/view@npm:6.38.5, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0":
version: 6.38.5
resolution: "@codemirror/view@npm:6.38.5"
"@codemirror/view@npm:6.38.4, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0":
version: 6.38.4
resolution: "@codemirror/view@npm:6.38.4"
dependencies:
"@codemirror/state": "npm:^6.5.0"
crelt: "npm:^1.0.6"
style-mod: "npm:^4.1.0"
w3c-keyname: "npm:^2.2.4"
checksum: 10/2335b593770042eb3adfe369073432b07cd2d15f1e230ae4dc7be7a7b8edd74e57c13e59b92a11e7e5d59ae030aabf7f55478dfec1cf2a2fe3a1ef3f091676a4
checksum: 10/86b3894e9e7c2113aabb1db8684d0520378339c194fa56a688fc26cd7d40336bb9df1f5f19f68309d95f14b80ecf0b70c0ffe5e43f2ec11c4bab18f2d5ee4494
languageName: node
linkType: hard
@@ -1698,15 +1698,15 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/ecma402-abstract@npm:2.3.6":
version: 2.3.6
resolution: "@formatjs/ecma402-abstract@npm:2.3.6"
"@formatjs/ecma402-abstract@npm:2.3.5":
version: 2.3.5
resolution: "@formatjs/ecma402-abstract@npm:2.3.5"
dependencies:
"@formatjs/fast-memoize": "npm:2.2.7"
"@formatjs/intl-localematcher": "npm:0.6.2"
decimal.js: "npm:^10.4.3"
tslib: "npm:^2.8.0"
checksum: 10/30b1b5cd6b62ba46245f934429936592df5500bc1b089dc92dd49c826757b873dd92c305dcfe370701e4df6b057bf007782113abb9b65db550d73be4961718bc
checksum: 10/254651057170836237dc4f0fbb372157f97133c4dcee414007e0cdb5b589baf0546c2f6337d117b988ee0a4f0a4d8247780aaa9e96b410c568495f162c40dc50
languageName: node
linkType: hard
@@ -1719,68 +1719,68 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/icu-messageformat-parser@npm:2.11.4":
version: 2.11.4
resolution: "@formatjs/icu-messageformat-parser@npm:2.11.4"
"@formatjs/icu-messageformat-parser@npm:2.11.3":
version: 2.11.3
resolution: "@formatjs/icu-messageformat-parser@npm:2.11.3"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/icu-skeleton-parser": "npm:1.8.16"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/icu-skeleton-parser": "npm:1.8.15"
tslib: "npm:^2.8.0"
checksum: 10/2acb100c06c2ade666d72787fb9f9795b1ace41e8e73bfadc2b1a7b8562e81f655e484f0f33d8c39473aa17bf0ad96fb2228871806a9b3dc4f5f876754a0de3a
checksum: 10/339f5ff5ea7417e2db7f01bd41340f78fd5a8e56a66e723272d21ce7ab4b265dcb45748cdca76eac7137e2b5e6767986812b471e011b4602cf7afbc6da57fb98
languageName: node
linkType: hard
"@formatjs/icu-skeleton-parser@npm:1.8.16":
version: 1.8.16
resolution: "@formatjs/icu-skeleton-parser@npm:1.8.16"
"@formatjs/icu-skeleton-parser@npm:1.8.15":
version: 1.8.15
resolution: "@formatjs/icu-skeleton-parser@npm:1.8.15"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
tslib: "npm:^2.8.0"
checksum: 10/428001e5bed81889b276a2356a1393157af91dc59220b765a1a132f6407ac5832b7ac6ae9737674ac38e44035295c0c1c310b2630f383f2b5779ea90bf2849e6
checksum: 10/19825abc1a5eef0288456c08420d06f3da8256fbe81db0b9ead48cacc94954d748c8068988e26d184d38fca2e50c191ecda5a10ff3935529c3134b8d80db0538
languageName: node
linkType: hard
"@formatjs/intl-datetimeformat@npm:6.18.2":
version: 6.18.2
resolution: "@formatjs/intl-datetimeformat@npm:6.18.2"
"@formatjs/intl-datetimeformat@npm:6.18.1":
version: 6.18.1
resolution: "@formatjs/intl-datetimeformat@npm:6.18.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/intl-localematcher": "npm:0.6.2"
decimal.js: "npm:^10.4.3"
tslib: "npm:^2.8.0"
checksum: 10/e6f80d0eb2049564502370839697a18858268a0dff8d199b1908137c4a229b1303131c12b8b8a8e8e259a1feba26dbc25b003b150adabea10d1c43f68086efbe
checksum: 10/66938778ecf37472a7e2f1d9349b0ac249fcbd5d684ae5614dea07287876182429980ba2fe3671224f981065baf017ac955f4b3c1f3c924c89bf2ec82dd1acd8
languageName: node
linkType: hard
"@formatjs/intl-displaynames@npm:6.8.13":
version: 6.8.13
resolution: "@formatjs/intl-displaynames@npm:6.8.13"
"@formatjs/intl-displaynames@npm:6.8.12":
version: 6.8.12
resolution: "@formatjs/intl-displaynames@npm:6.8.12"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/intl-localematcher": "npm:0.6.2"
tslib: "npm:^2.8.0"
checksum: 10/adefd25fa42266c7bc33dd3cd50f3681bdce51d18b32a03c98f8ad7587dfd8b9291345e185a4b16f31f4eee10fc799fd1b6361bdfd3a2c9fe127744e1e0f3b07
checksum: 10/7de27ef7e8cde2febce84d5443f00b70062cbd0c3f1039ce8ed1caacb15c4c7a36da16295f26657d59aa4663141a04d7b1083bfd1eea6a4e8ad9dc6093a2c886
languageName: node
linkType: hard
"@formatjs/intl-durationformat@npm:0.7.6":
version: 0.7.6
resolution: "@formatjs/intl-durationformat@npm:0.7.6"
"@formatjs/intl-durationformat@npm:0.7.5":
version: 0.7.5
resolution: "@formatjs/intl-durationformat@npm:0.7.5"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/intl-localematcher": "npm:0.6.2"
tslib: "npm:^2.8.0"
checksum: 10/442236ba85bcd9cb7296c43a708271fa09f110b1ca9d5899066d00812fc2965eaeaec6b5240be421b80daba62860352131088449ba0fcd2061f671cec6240f0b
checksum: 10/4dc81b112fed25dc8da0a16ddeff033b7c763bf9a1cfd7b1b25c1216f7f147eb67a47059a3cf95b4d4ade150c54a813542b84e69298905a4bc22548d74bf8567
languageName: node
linkType: hard
"@formatjs/intl-enumerator@npm:1.8.12":
version: 1.8.12
resolution: "@formatjs/intl-enumerator@npm:1.8.12"
"@formatjs/intl-enumerator@npm:1.8.11":
version: 1.8.11
resolution: "@formatjs/intl-enumerator@npm:1.8.11"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
tslib: "npm:^2.8.0"
checksum: 10/8dfd7ca5383b4dca530e1df5118a72f71347f4e0daa6131b82dbf7e860a8b96bec0fed43bfa6f6e650e55fa50fcd3e9e3a5253515131b578539d8eaa84630927
checksum: 10/8646a517cd4160c1ceff888ec8fdf652caa3d375fa41231e829c13bc7be0cd156c9642e339b75e9cfa8ef60ae8140c766f9055318c62f1c1d9345f25cdb7f426
languageName: node
linkType: hard
@@ -1793,26 +1793,26 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/intl-listformat@npm:7.7.13":
version: 7.7.13
resolution: "@formatjs/intl-listformat@npm:7.7.13"
"@formatjs/intl-listformat@npm:7.7.12":
version: 7.7.12
resolution: "@formatjs/intl-listformat@npm:7.7.12"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/intl-localematcher": "npm:0.6.2"
tslib: "npm:^2.8.0"
checksum: 10/476d7cffb64eb996a888b1865aa237f04088de60fa7c65b6d073bca8a3c0f4304040ef12f16eafaf6587895976b773607296951afa7f119447d8f9b2c40daa55
checksum: 10/eee910e83ad28b3b3c24ab6e155720187ae5b5ac936ffa2c8ec6cc8c392c194fd5c79a166290da1c6de8dc1857e3d9d11241029832ec88f7a85cce1821b7f067
languageName: node
linkType: hard
"@formatjs/intl-locale@npm:4.2.13":
version: 4.2.13
resolution: "@formatjs/intl-locale@npm:4.2.13"
"@formatjs/intl-locale@npm:4.2.12":
version: 4.2.12
resolution: "@formatjs/intl-locale@npm:4.2.12"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/intl-enumerator": "npm:1.8.12"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/intl-enumerator": "npm:1.8.11"
"@formatjs/intl-getcanonicallocales": "npm:2.5.6"
tslib: "npm:^2.8.0"
checksum: 10/865615561b4bad8b8d7d93539cae7eb3ed2d46b6156486ef3ccb1b8f9f46f075c7cf2f6e5325aba1cf07150e19280858dff7dfd86d530fbf45fd31ea4fabf8d4
checksum: 10/42111a3002a5a2076b3eb012073230f69c62355dc03647bc17f4d0805f39c7e720e2281b359277d020fef623944a5bcc1ddc3dae9a3af74886d876147680147d
languageName: node
linkType: hard
@@ -1825,38 +1825,38 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/intl-numberformat@npm:8.15.6":
version: 8.15.6
resolution: "@formatjs/intl-numberformat@npm:8.15.6"
"@formatjs/intl-numberformat@npm:8.15.5":
version: 8.15.5
resolution: "@formatjs/intl-numberformat@npm:8.15.5"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/intl-localematcher": "npm:0.6.2"
decimal.js: "npm:^10.4.3"
tslib: "npm:^2.8.0"
checksum: 10/674c5fefa0b14fcd7c58d0c0e592b4887dc2563fa5a11d80a0a82328ac12b2bb82b9a5367fa0a4d80060d61d15a1821bca7085e20cad09aa93b87edb3cff68ea
checksum: 10/3440371a43c54cdd2aa3714cb518ad22e491dd19fbc0c046e712dde078d3f6ed709474376863d64d2bddb506957d1cf265d440f6723b88211044a7b56186e550
languageName: node
linkType: hard
"@formatjs/intl-pluralrules@npm:5.4.6":
version: 5.4.6
resolution: "@formatjs/intl-pluralrules@npm:5.4.6"
"@formatjs/intl-pluralrules@npm:5.4.5":
version: 5.4.5
resolution: "@formatjs/intl-pluralrules@npm:5.4.5"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/intl-localematcher": "npm:0.6.2"
decimal.js: "npm:^10.4.3"
tslib: "npm:^2.8.0"
checksum: 10/88aa244e69ccfdf459899f5fa3c64df345f451ef91ce1188eab35b7e37daa225d22120f64be633f2cd8b826ea705d19831915118f555f2d17611ee842a9a86dc
checksum: 10/00f650891893b743d126dd2bf0d17c1b16a8c9e0e0dd94cd0895e66cb556246116263e9603204e1991924814d0ed3a3503765914aff08181d5e4435dfc5e547c
languageName: node
linkType: hard
"@formatjs/intl-relativetimeformat@npm:11.4.13":
version: 11.4.13
resolution: "@formatjs/intl-relativetimeformat@npm:11.4.13"
"@formatjs/intl-relativetimeformat@npm:11.4.12":
version: 11.4.12
resolution: "@formatjs/intl-relativetimeformat@npm:11.4.12"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/intl-localematcher": "npm:0.6.2"
tslib: "npm:^2.8.0"
checksum: 10/c2058d5f29a13aa216d317d309a6ffd7d203f0fe11696b7bd524e17ac3cc22ae50ad56a26dbf18125e4c115a3e75f01e6cf2134a83df6c7916ae6d3fb21a1e9b
checksum: 10/f6adca59738cb7f58d2ea985558d8fc45e567406de6fb6e67894afe790e2a9fa1a19d34853afc36805fa4a3d638e29c62d6c6ba3ec2a85628c240081dcdfebc1
languageName: node
linkType: hard
@@ -6925,10 +6925,10 @@ __metadata:
languageName: node
linkType: hard
"core-js@npm:3.46.0":
version: 3.46.0
resolution: "core-js@npm:3.46.0"
checksum: 10/82993ca487c6cbbf8bbf00e45eeb9705eb63dc2f9c90d7f35696733efbc3f4b52426e1f8dbef0f0b68ea16caa21e4f44cc5490e08120e1cad4a72b031ed8adaa
"core-js@npm:3.45.1":
version: 3.45.1
resolution: "core-js@npm:3.45.1"
checksum: 10/b9dca79b1af8bb4f0d4af0752ea98d694fe157abaf55513fd4084df32dfd4398f0fc57898b32cdb643c1cecb87b9231c2a2ce535797c80ae328eac6d6078ee61
languageName: node
linkType: hard
@@ -9182,25 +9182,25 @@ __metadata:
"@babel/preset-env": "npm:7.28.3"
"@babel/runtime": "npm:7.28.4"
"@braintree/sanitize-url": "npm:7.1.1"
"@bundle-stats/plugin-webpack-filter": "npm:4.21.5"
"@bundle-stats/plugin-webpack-filter": "npm:4.21.4"
"@codemirror/autocomplete": "npm:6.19.0"
"@codemirror/commands": "npm:6.9.0"
"@codemirror/language": "npm:6.11.3"
"@codemirror/legacy-modes": "npm:6.5.2"
"@codemirror/search": "npm:6.5.11"
"@codemirror/state": "npm:6.5.2"
"@codemirror/view": "npm:6.38.5"
"@codemirror/view": "npm:6.38.4"
"@date-fns/tz": "npm:1.4.1"
"@egjs/hammerjs": "npm:2.0.17"
"@formatjs/intl-datetimeformat": "npm:6.18.2"
"@formatjs/intl-displaynames": "npm:6.8.13"
"@formatjs/intl-durationformat": "npm:0.7.6"
"@formatjs/intl-datetimeformat": "npm:6.18.1"
"@formatjs/intl-displaynames": "npm:6.8.12"
"@formatjs/intl-durationformat": "npm:0.7.5"
"@formatjs/intl-getcanonicallocales": "npm:2.5.6"
"@formatjs/intl-listformat": "npm:7.7.13"
"@formatjs/intl-locale": "npm:4.2.13"
"@formatjs/intl-numberformat": "npm:8.15.6"
"@formatjs/intl-pluralrules": "npm:5.4.6"
"@formatjs/intl-relativetimeformat": "npm:11.4.13"
"@formatjs/intl-listformat": "npm:7.7.12"
"@formatjs/intl-locale": "npm:4.2.12"
"@formatjs/intl-numberformat": "npm:8.15.5"
"@formatjs/intl-pluralrules": "npm:5.4.5"
"@formatjs/intl-relativetimeformat": "npm:11.4.12"
"@fullcalendar/core": "npm:6.1.19"
"@fullcalendar/daygrid": "npm:6.1.19"
"@fullcalendar/interaction": "npm:6.1.19"
@@ -9283,7 +9283,7 @@ __metadata:
browserslist-useragent-regexp: "npm:4.1.3"
color-name: "npm:2.0.2"
comlink: "npm:4.4.2"
core-js: "npm:3.46.0"
core-js: "npm:3.45.1"
cropperjs: "npm:1.6.2"
culori: "npm:4.0.2"
date-fns: "npm:4.1.0"
@@ -9317,7 +9317,7 @@ __metadata:
html-minifier-terser: "npm:7.2.0"
husky: "npm:9.1.7"
idb-keyval: "npm:6.2.2"
intl-messageformat: "npm:10.7.18"
intl-messageformat: "npm:10.7.17"
js-yaml: "npm:4.1.0"
jsdom: "npm:27.0.0"
jszip: "npm:3.10.1"
@@ -9332,7 +9332,7 @@ __metadata:
lodash.template: "npm:4.5.0"
luxon: "npm:3.7.2"
map-stream: "npm:0.0.7"
marked: "npm:16.4.0"
marked: "npm:16.3.0"
memoize-one: "npm:6.0.0"
node-vibrant: "npm:4.0.3"
object-hash: "npm:3.0.0"
@@ -9355,7 +9355,7 @@ __metadata:
ts-lit-plugin: "npm:2.0.2"
typescript: "npm:5.9.3"
typescript-eslint: "npm:8.46.0"
ua-parser-js: "npm:2.0.6"
ua-parser-js: "npm:2.0.5"
vite-tsconfig-paths: "npm:5.1.4"
vitest: "npm:3.2.4"
vue: "npm:2.7.16"
@@ -9712,15 +9712,15 @@ __metadata:
languageName: node
linkType: hard
"intl-messageformat@npm:10.7.18":
version: 10.7.18
resolution: "intl-messageformat@npm:10.7.18"
"intl-messageformat@npm:10.7.17":
version: 10.7.17
resolution: "intl-messageformat@npm:10.7.17"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.3.6"
"@formatjs/ecma402-abstract": "npm:2.3.5"
"@formatjs/fast-memoize": "npm:2.2.7"
"@formatjs/icu-messageformat-parser": "npm:2.11.4"
"@formatjs/icu-messageformat-parser": "npm:2.11.3"
tslib: "npm:^2.8.0"
checksum: 10/96650d673912763d21bbfa14b50749b992d45f1901092a020e3155961e3c70f4644dd1731c3ecb1207a1eb94d84bedf4c34b1ac8127c29ad6b015b6a2a4045cb
checksum: 10/4f8c30c998bfc14eb64894414b94a8923045ab31d7bbf0978dab6621c644d451ff5c533c04ce8128163b74dd6d59061ec1ef3acb1cbab3302d31cbdb21947620
languageName: node
linkType: hard
@@ -10974,12 +10974,12 @@ __metadata:
languageName: node
linkType: hard
"marked@npm:16.4.0":
version: 16.4.0
resolution: "marked@npm:16.4.0"
"marked@npm:16.3.0":
version: 16.3.0
resolution: "marked@npm:16.3.0"
bin:
marked: bin/marked.js
checksum: 10/5174b345ccc61e2030c2eb8abb3e5cbebeb6697a6d2b609f64ffa2ff6e482f5f1e1fda1912db19c747f43971b1fa54ae53c1ab1ce5d2f58566d6db4bc3016833
checksum: 10/60497834b9acfb3b3994222509d359ecb9a197c885dfeb77e2050a287cd2f4ab19f00d5597172b47f9e0c54d9e1e13d8b2dd73322b7838599e1f16d1d6283f5b
languageName: node
linkType: hard
@@ -14379,16 +14379,17 @@ __metadata:
languageName: node
linkType: hard
"ua-parser-js@npm:2.0.6":
version: 2.0.6
resolution: "ua-parser-js@npm:2.0.6"
"ua-parser-js@npm:2.0.5":
version: 2.0.5
resolution: "ua-parser-js@npm:2.0.5"
dependencies:
detect-europe-js: "npm:^0.1.2"
is-standalone-pwa: "npm:^0.1.1"
ua-is-frozen: "npm:^0.1.2"
undici: "npm:^7.12.0"
bin:
ua-parser-js: script/cli.js
checksum: 10/b0049d3b272979049c7df6af2ec2ce032e4351316b10c33699f6e3f0bec701336f67530cc3ccb363c554b1bb5047b75d2f46575699afacd6e541762ca3861f4d
checksum: 10/e946cb1c85bfcd0f2d30c7d5e1b605e340bb458432e7e87fc4aa1b2f90117e4220521d4e0bc7dd8c2a5cadd0935dedb5ac434b70efdc0007221288c1d98b3cd5
languageName: node
linkType: hard
@@ -14451,6 +14452,13 @@ __metadata:
languageName: node
linkType: hard
"undici@npm:^7.12.0":
version: 7.16.0
resolution: "undici@npm:7.16.0"
checksum: 10/2bb71672b23d3dc0f56f1b7fb6c936e4487a350db46eaafc03f2f9107f99cdf8e51ecdd32e589e2381ef47a64b6369cfb31f328b2c3ea663023aa47bc5258b9e
languageName: node
linkType: hard
"unicode-canonical-property-names-ecmascript@npm:^2.0.0":
version: 2.0.1
resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.1"