Compare commits

..

13 Commits

Author SHA1 Message Date
Bram Kragten
8a4b96f1ff Bumped version to 20241106.1 2024-11-07 21:40:38 +01:00
Paul Bottein
17b6bf0673 Enable auto-scroll for drag and drop (#22725) 2024-11-07 21:39:45 +01:00
Bram Kragten
387392713c move download logs button, switch between raw and normal logs (#22721) 2024-11-07 21:39:44 +01:00
Wendelin
125ad9c794 Fix logs live-indicator on older boots (#22719) 2024-11-07 21:39:43 +01:00
Bram Kragten
ae33b10cb2 Add support for helper text in form boolean (#22711) 2024-11-07 21:39:43 +01:00
Wendelin
1181ddcbbf Fix hassio logs for core < 2024.11 (#22708) 2024-11-07 21:39:42 +01:00
Paul Bottein
f7103febdf Fix typo for fixed background attribute (#22707) 2024-11-07 21:39:41 +01:00
Bram Kragten
6e8c1f1a63 Update value of password field on change event (#22706) 2024-11-07 21:39:40 +01:00
Bram Kragten
ce39b1a2c8 20241106.0 (#22695) 2024-11-06 14:05:23 +01:00
Paul Bottein
a6971d61d1 20241105.0 (#22679) 2024-11-05 18:47:04 +01:00
Bram Kragten
452cfee2cd Merge branch 'dev' 2024-11-04 19:04:13 +01:00
Paul Bottein
deb077b5e9 20241031.0 (#22613) 2024-10-31 15:14:02 +01:00
Paul Bottein
79e223cf8e 20241030.0 (#22599) 2024-10-30 16:35:54 +01:00
22 changed files with 333 additions and 952 deletions

View File

@@ -47,7 +47,6 @@ class HassioAddonLogDashboard extends LitElement {
.localizeFunc=${this.supervisor.localize}
.header=${this.addon.name}
.provider=${this.addon.slug}
show
.filter=${this._filter}
>
</error-log-card>

View File

@@ -35,14 +35,14 @@
"@codemirror/state": "6.4.1",
"@codemirror/view": "6.34.1",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.16.3",
"@formatjs/intl-displaynames": "6.8.3",
"@formatjs/intl-getcanonicallocales": "2.5.2",
"@formatjs/intl-listformat": "7.7.3",
"@formatjs/intl-locale": "4.2.3",
"@formatjs/intl-numberformat": "8.14.3",
"@formatjs/intl-pluralrules": "5.3.3",
"@formatjs/intl-relativetimeformat": "11.4.3",
"@formatjs/intl-datetimeformat": "6.16.1",
"@formatjs/intl-displaynames": "6.8.1",
"@formatjs/intl-getcanonicallocales": "2.5.1",
"@formatjs/intl-listformat": "7.7.1",
"@formatjs/intl-locale": "4.2.1",
"@formatjs/intl-numberformat": "8.14.1",
"@formatjs/intl-pluralrules": "5.3.1",
"@formatjs/intl-relativetimeformat": "11.4.1",
"@fullcalendar/core": "6.1.15",
"@fullcalendar/daygrid": "6.1.15",
"@fullcalendar/interaction": "6.1.15",
@@ -115,7 +115,7 @@
"hls.js": "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch",
"home-assistant-js-websocket": "9.4.0",
"idb-keyval": "6.2.1",
"intl-messageformat": "10.7.5",
"intl-messageformat": "10.7.3",
"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",

View File

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

View File

@@ -117,8 +117,8 @@ export class HaPasswordField extends LitElement {
.autocapitalize=${this.autocapitalize}
.type=${this._unmaskedPassword ? "text" : "password"}
.suffix=${html`<div style="width: 24px"></div>`}
@input=${this._handleInputChange}
@change=${this._reDispatchEvent}
@input=${this._handleInputEvent}
@change=${this._handleChangeEvent}
></ha-textfield>
<ha-icon-button
toggles
@@ -153,11 +153,16 @@ export class HaPasswordField extends LitElement {
}
@eventOptions({ passive: true })
private _handleInputChange(ev) {
private _handleInputEvent(ev) {
this.value = ev.target.value;
}
@eventOptions({ passive: true })
private _handleChangeEvent(ev) {
this.value = ev.target.value;
this._reDispatchEvent(ev);
}
private _reDispatchEvent(oldEvent: Event) {
const newEvent = new Event(oldEvent.type, oldEvent);
this.dispatchEvent(newEvent);

View File

@@ -135,6 +135,10 @@ export class HaSortable extends LitElement {
const Sortable = (await import("../resources/sortable")).default;
const options: SortableInstance.Options = {
scroll: true,
// Force the autoscroll fallback because it works better than the native one
forceAutoScrollFallback: true,
scrollSpeed: 20,
animation: 150,
...this.options,
onChoose: this._handleChoose,

View File

@@ -185,6 +185,15 @@ export const fetchHassioInfo = async (
export const fetchHassioBoots = async (hass: HomeAssistant) =>
hass.callApi<HassioResponse<HassioBoots>>("GET", `hassio/host/logs/boots`);
export const fetchHassioLogsLegacy = async (
hass: HomeAssistant,
provider: string
) =>
hass.callApi<string>(
"GET",
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs`
);
export const fetchHassioLogs = async (
hass: HomeAssistant,
provider: string,

View File

@@ -209,17 +209,6 @@ export interface ZWaveJSNodeStatus {
has_firmware_update_cc: boolean;
}
export type ZWaveJSNodeCapabilities = {
[endpoint: number]: ZWaveJSEndpointCapability[];
};
export interface ZWaveJSEndpointCapability {
id: number;
name: string;
version: number;
is_secure: boolean;
}
export interface ZwaveJSNodeMetadata {
node_id: number;
exclusion: string;
@@ -415,25 +404,6 @@ export interface RequestedGrant {
clientSideAuth: boolean;
}
export const invokeZWaveCCApi = (
hass: HomeAssistant,
device_id: string,
command_class: number,
endpoint: number | undefined,
method_name: string,
parameters: any[],
wait_for_result?: boolean
): Promise<unknown> =>
hass.callWS({
type: "zwave_js/invoke_cc_api",
device_id,
command_class,
endpoint,
method_name,
parameters,
wait_for_result,
});
export const fetchZwaveNetworkStatus = (
hass: HomeAssistant,
device_or_entry_id: {
@@ -609,15 +579,6 @@ export const fetchZwaveNodeStatus = (
device_id,
});
export const fetchZwaveNodeCapabilities = (
hass: HomeAssistant,
device_id: string
): Promise<ZWaveJSNodeCapabilities> =>
hass.callWS({
type: "zwave_js/node_capabilities",
device_id,
});
export const subscribeZwaveNodeStatus = (
hass: HomeAssistant,
device_id: string,

View File

@@ -5,7 +5,6 @@ import {
mdiHospitalBox,
mdiInformation,
mdiUpload,
mdiWrench,
} from "@mdi/js";
import { getConfigEntries } from "../../../../../../data/config_entries";
import type { DeviceRegistryEntry } from "../../../../../../data/device_registry";
@@ -99,13 +98,6 @@ export const getZwaveDeviceActions = async (
showZWaveJSNodeStatisticsDialog(el, {
device,
}),
},
{
label: hass.localize(
"ui.panel.config.zwave_js.device_info.installer_settings"
),
icon: mdiWrench,
href: `/config/zwave_js/node_installer/${device.id}?config_entry=${entryId}`,
}
);
}

View File

@@ -1,152 +0,0 @@
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../../components/buttons/ha-progress-button";
import type { DeviceRegistryEntry } from "../../../../../../data/device_registry";
import type { HomeAssistant } from "../../../../../../types";
import { invokeZWaveCCApi } from "../../../../../../data/zwave_js";
import "../../../../../../components/ha-textfield";
import "../../../../../../components/ha-select";
import "../../../../../../components/ha-list-item";
import "../../../../../../components/ha-alert";
import "../../../../../../components/ha-formfield";
import "../../../../../../components/ha-switch";
import type { HaProgressButton } from "../../../../../../components/buttons/ha-progress-button";
import type { HaSelect } from "../../../../../../components/ha-select";
import type { HaTextField } from "../../../../../../components/ha-textfield";
import type { HaSwitch } from "../../../../../../components/ha-switch";
import { extractApiErrorMessage } from "../../../../../../data/hassio/common";
@customElement("zwave_js-capability-control-multilevel_switch")
class ZWaveJSCapabilityMultiLevelSwitch extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public device!: DeviceRegistryEntry;
@property({ type: Number }) public endpoint!: number;
@property({ type: Number }) public command_class!: number;
@property({ type: Number }) public version!: number;
@state() private _error?: string;
protected render() {
return html`
<h3>
${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.title"
)}
</h3>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
<ha-select
.label=${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.direction"
)}
id="direction"
>
<ha-list-item .value=${"up"} selected
>${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.up"
)}</ha-list-item
>
<ha-list-item .value=${"down"}
>${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.down"
)}</ha-list-item
>
</ha-select>
<ha-formfield
.label=${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.ignore_start_level"
)}
>
<ha-switch id="ignore_start_level"></ha-switch>
</ha-formfield>
<ha-textfield
type="number"
id="start_level"
value="0"
.label=${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.start_level"
)}
></ha-textfield>
<div class="actions">
<ha-progress-button
.control=${"startLevelChange"}
@click=${this._controlTransition}
>
${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.start_transition"
)}
</ha-progress-button>
<ha-progress-button
.control=${"stopLevelChange"}
@click=${this._controlTransition}
>
${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.stop_transition"
)}
</ha-progress-button>
</div>
`;
}
private async _controlTransition(ev: any) {
const control = ev.currentTarget!.control;
const button = ev.currentTarget as HaProgressButton;
button.progress = true;
const direction = (this.shadowRoot!.getElementById("direction") as HaSelect)
.value;
const ignoreStartLevel = (
this.shadowRoot!.getElementById("ignore_start_level") as HaSwitch
).checked;
const startLevel = Number(
(this.shadowRoot!.getElementById("start_level") as HaTextField).value
);
try {
button.actionSuccess();
await invokeZWaveCCApi(
this.hass,
this.device.id,
this.command_class,
this.endpoint,
control,
[{ direction, ignoreStartLevel, startLevel }],
true
);
} catch (err) {
button.actionError();
this._error = this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.control_failed",
{ error: extractApiErrorMessage(err) }
);
}
button.progress = false;
}
static styles = css`
ha-select,
ha-formfield,
ha-textfield {
display: block;
margin-bottom: 8px;
}
.actions {
display: flex;
justify-content: flex-end;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"zwave_js-capability-control-multilevel_switch": ZWaveJSCapabilityMultiLevelSwitch;
}
}

View File

@@ -1,241 +0,0 @@
import { LitElement, css, html } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import type { DeviceRegistryEntry } from "../../../../../../data/device_registry";
import type { HomeAssistant } from "../../../../../../types";
import { invokeZWaveCCApi } from "../../../../../../data/zwave_js";
import "../../../../../../components/ha-button";
import "../../../../../../components/buttons/ha-progress-button";
import "../../../../../../components/ha-textfield";
import "../../../../../../components/ha-select";
import "../../../../../../components/ha-list-item";
import "../../../../../../components/ha-alert";
import type { HaSelect } from "../../../../../../components/ha-select";
import type { HaTextField } from "../../../../../../components/ha-textfield";
import { extractApiErrorMessage } from "../../../../../../data/hassio/common";
import type { HaProgressButton } from "../../../../../../components/buttons/ha-progress-button";
// enum with special states
enum SpecialState {
frost_protection = "Frost Protection",
energy_saving = "Energy Saving",
unused = "Unused",
}
const SETBACK_TYPE_OPTIONS = ["none", "temporary", "permanent"];
@customElement("zwave_js-capability-control-thermostat_setback")
class ZWaveJSCapabilityThermostatSetback extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public device!: DeviceRegistryEntry;
@property({ type: Number }) public endpoint!: number;
@property({ type: Number }) public command_class!: number;
@property({ type: Number }) public version!: number;
@state() private _disableSetbackState = false;
@query("#setback_type") private _setbackTypeInput!: HaSelect;
@query("#setback_state") private _setbackStateInput!: HaTextField;
@query("#setback_special_state")
private _setbackSpecialStateSelect!: HaSelect;
@state() private _error?: string;
@state() private _loading = true;
protected render() {
return html`
<h3>
${this.hass.localize(
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.title`
)}
</h3>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
<ha-select
.label=${this.hass.localize(
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_type.label`
)}
id="setback_type"
.value=${"0"}
.disabled=${this._loading}
>
${SETBACK_TYPE_OPTIONS.map(
(translationKey, index) =>
html`<ha-list-item .value=${String(index)}>
${this.hass.localize(
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_type.${translationKey}`
)}
</ha-list-item>`
)}
</ha-select>
<div class="setback-state">
<ha-textfield
type="number"
id="setback_state"
value="0"
.label=${this.hass.localize(
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_state_label`
)}
min="-12.8"
max="12.0"
step=".1"
.helper=${this.hass.localize(
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_state_helper`
)}
.disabled=${this._disableSetbackState || this._loading}
></ha-textfield>
<ha-select
.label=${this.hass.localize(
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_special_state.label`
)}
id="setback_special_state"
@change=${this._changeSpecialState}
.disabled=${this._loading}
>
<ha-list-item selected> </ha-list-item>
${Object.entries(SpecialState).map(
([translationKey, value]) =>
html`<ha-list-item .value=${value}>
${this.hass.localize(
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_special_state.${translationKey}`
)}
</ha-list-item>`
)}
</ha-select>
</div>
<div class="actions">
<ha-button
class="clear-button"
@click=${this._clear}
.disabled=${this._loading}
>${this.hass.localize("ui.common.clear")}</ha-button
>
<ha-progress-button
@click=${this._saveSetback}
.disabled=${this._loading}
>
${this.hass.localize("ui.common.save")}
</ha-progress-button>
</div>
`;
}
protected firstUpdated() {
this._loadSetback();
}
private async _loadSetback() {
this._loading = true;
try {
const { setbackType, setbackState } = (await invokeZWaveCCApi(
this.hass,
this.device.id,
this.command_class,
this.endpoint,
"get",
[],
true
)) as { setbackType: number; setbackState: number | SpecialState };
this._setbackTypeInput.value = String(setbackType);
if (typeof setbackState === "number") {
this._setbackStateInput.value = String(setbackState);
this._setbackSpecialStateSelect.value = "";
} else {
this._setbackSpecialStateSelect.value = setbackState;
}
} catch (err) {
this._error = this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.get_setback_failed",
{ error: extractApiErrorMessage(err) }
);
}
this._loading = false;
}
private _changeSpecialState() {
this._disableSetbackState = !!this._setbackSpecialStateSelect.value;
}
private async _saveSetback(ev: CustomEvent) {
const button = ev.currentTarget as HaProgressButton;
button.progress = true;
this._error = undefined;
const setbackType = this._setbackTypeInput.value;
let setbackState: number | string = Number(this._setbackStateInput.value);
if (this._setbackSpecialStateSelect.value) {
setbackState = this._setbackSpecialStateSelect.value;
}
try {
await invokeZWaveCCApi(
this.hass,
this.device.id,
this.command_class,
this.endpoint,
"set",
[Number(setbackType), setbackState],
true
);
button.actionSuccess();
} catch (err) {
button.actionError();
this._error = this.hass.localize(
"ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.save_setback_failed",
{ error: extractApiErrorMessage(err) }
);
}
button.progress = false;
}
private _clear() {
this._loadSetback();
}
static styles = css`
:host {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
:host > ha-select {
width: 100%;
}
.actions {
width: 100%;
display: flex;
justify-content: flex-end;
}
.actions .clear-button {
--mdc-theme-primary: var(--red-color);
}
.setback-state {
width: 100%;
display: flex;
gap: 16px;
}
.setback-state ha-select,
ha-textfield {
flex: 1;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"zwave_js-capability-control-thermostat_setback": ZWaveJSCapabilityThermostatSetback;
}
}

View File

@@ -48,10 +48,6 @@ class ZWaveJSConfigRouter extends HassRouterPage {
tag: "zwave_js-node-config",
load: () => import("./zwave_js-node-config"),
},
node_installer: {
tag: "zwave_js-node-installer",
load: () => import("./zwave_js-node-installer"),
},
logs: {
tag: "zwave_js-logs",
load: () => import("./zwave_js-logs"),

View File

@@ -1,210 +0,0 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-list/mwc-list-item";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { dynamicElement } from "../../../../../common/dom/dynamic-element-directive";
import "../../../../../components/ha-card";
import { computeDeviceName } from "../../../../../data/device_registry";
import type {
ZWaveJSNodeCapabilities,
ZwaveJSNodeMetadata,
} from "../../../../../data/zwave_js";
import {
fetchZwaveNodeCapabilities,
fetchZwaveNodeMetadata,
} from "../../../../../data/zwave_js";
import "../../../../../layouts/hass-error-screen";
import "../../../../../layouts/hass-loading-screen";
import "../../../../../layouts/hass-subpage";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant, Route } from "../../../../../types";
import "../../../ha-config-section";
import "./capability-controls/zwave_js-capability-control-multilevel-switch";
import "./capability-controls/zwave_js-capability-control-thermostat-setback";
const CAPABILITY_CONTROLS = {
38: "multilevel_switch",
71: "thermostat_setback",
};
@customElement("zwave_js-node-installer")
class ZWaveJSNodeInstaller extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public route!: Route;
@property({ type: Boolean }) public narrow = false;
@property({ type: Boolean }) public isWide = false;
@property() public configEntryId?: string;
@property() public deviceId!: string;
@state() private _nodeMetadata?: ZwaveJSNodeMetadata;
@state() private _capabilities?: ZWaveJSNodeCapabilities;
@state() private _error?: string;
public connectedCallback(): void {
super.connectedCallback();
this.deviceId = this.route.path.substr(1);
}
protected updated(changedProps: PropertyValues): void {
if (!this._capabilities || changedProps.has("deviceId")) {
this._fetchData();
}
}
protected render(): TemplateResult {
if (this._error) {
return html`<hass-error-screen
.hass=${this.hass}
.error=${this.hass.localize(
`ui.panel.config.zwave_js.node_config.error_${this._error}`
)}
></hass-error-screen>`;
}
if (!this._capabilities || !this._nodeMetadata) {
return html`<hass-loading-screen></hass-loading-screen>`;
}
const device = this.hass.devices[this.deviceId];
const endpoints = Object.entries(this._capabilities).filter(
([_endpoint, capabilities]) => {
const filteredCapabilities = capabilities.filter(
(capability) => capability.id in CAPABILITY_CONTROLS
);
return filteredCapabilities.length > 0;
}
);
return html`
<hass-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
>
<ha-config-section
.narrow=${this.narrow}
.isWide=${this.isWide}
vertical
>
<div slot="header">
${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.header"
)}
</div>
<div slot="introduction">
${device
? html`
<div class="device-info">
<h2>${computeDeviceName(device, this.hass)}</h2>
<p>${device.manufacturer} ${device.model}</p>
</div>
`
: ``}
${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.introduction"
)}
</div>
${endpoints.length
? endpoints.map(
([endpoint, capabilities]) => html`
<h3>
${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.endpoint"
)}:
${endpoint}
</h3>
<ha-card>
${capabilities.map(
(capability) => html`
${capability.id in CAPABILITY_CONTROLS
? html` <div class="capability">
<h4>
${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.command_class"
)}:
${capability.name}
</h4>
${dynamicElement(
`zwave_js-capability-control-${CAPABILITY_CONTROLS[capability.id]}`,
{
hass: this.hass,
device: device,
endpoint: endpoint,
command_class: capability.id,
version: capability.version,
is_secure: capability.is_secure,
}
)}
</div>`
: nothing}
`
)}
</ha-card>
`
)
: html`<ha-card class="empty"
>${this.hass.localize(
"ui.panel.config.zwave_js.node_installer.no_settings"
)}</ha-card
>`}
</ha-config-section>
</hass-subpage>
`;
}
private async _fetchData() {
if (!this.configEntryId) {
return;
}
const device = this.hass.devices[this.deviceId];
if (!device) {
this._error = "device_not_found";
return;
}
[this._nodeMetadata, this._capabilities] = await Promise.all([
fetchZwaveNodeMetadata(this.hass, device.id),
fetchZwaveNodeCapabilities(this.hass, device.id),
]);
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
ha-card {
margin-bottom: 40px;
margin-top: 0;
}
.capability {
border-bottom: 1px solid var(--divider-color);
padding: 4px 16px;
}
.capability:last-child {
border-bottom: none;
}
.empty {
margin-top: 32px;
padding: 24px 16px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zwave_js-node-installer": ZWaveJSNodeInstaller;
}
}

View File

@@ -11,6 +11,7 @@ import {
mdiRefresh,
mdiWrap,
mdiWrapDisabled,
mdiFolderTextOutline,
} from "@mdi/js";
import {
css,
@@ -49,6 +50,7 @@ import {
fetchHassioBoots,
fetchHassioLogs,
fetchHassioLogsFollow,
fetchHassioLogsLegacy,
getHassioLogDownloadLinesUrl,
getHassioLogDownloadUrl,
} from "../../../data/hassio/supervisor";
@@ -57,7 +59,7 @@ import {
downloadFileSupported,
fileDownload,
} from "../../../util/file_download";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { fireEvent, type HASSDomEvent } from "../../../common/dom/fire_event";
import type { ConnectionStatus } from "../../../data/connection-status";
import { atLeastVersion } from "../../../common/config/version";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
@@ -78,9 +80,10 @@ class ErrorLogCard extends LitElement {
@property() public header?: string;
@property() public provider!: string;
@property() public provider?: string;
@property({ type: Boolean, attribute: true }) public show = false;
@property({ attribute: "allow-switch", type: Boolean }) public allowSwitch =
false;
@query(".error-log") private _logElement?: HTMLElement;
@@ -129,26 +132,32 @@ class ErrorLogCard extends LitElement {
@state() private _wrapLines = true;
@state() private _downloadSupported;
@state() private _downloadSupported?: boolean;
@state() private _logsFileLink;
@state() private _logsFileLink?: string;
protected render(): TemplateResult {
const streaming =
this._streamSupported &&
this.provider &&
isComponentLoaded(this.hass, "hassio") &&
this._loadingState !== "loading";
const hasBoots = this._streamSupported && Array.isArray(this._boots);
const localize = this.localizeFunc || this.hass.localize;
return html`
<div class="error-log-intro">
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
<ha-card outlined class=${classMap({ hidden: this.show === false })}>
<ha-card outlined>
<div class="header">
<h1 class="card-header">
${this.header || localize("ui.panel.config.logs.show_full_logs")}
</h1>
<div class="action-buttons">
${this._streamSupported &&
Array.isArray(this._boots) &&
this._showBootsSelect
${hasBoots && this._showBootsSelect
? html`
<ha-assist-chip
.title=${localize(
@@ -174,7 +183,7 @@ class ErrorLogCard extends LitElement {
id="boots-menu"
positioning="fixed"
>
${this._boots.map(
${this._boots!.map(
(boot) => html`
<ha-md-menu-item
.value=${boot}
@@ -231,27 +240,40 @@ class ErrorLogCard extends LitElement {
`ui.panel.config.logs.${this._wrapLines ? "full_width" : "wrap_lines"}`
)}
></ha-icon-button>
${!this._streamSupported || this._error
${!streaming || this._error
? html`<ha-icon-button
.path=${mdiRefresh}
@click=${this._loadLogs}
.label=${localize("ui.common.refresh")}
></ha-icon-button>`
: nothing}
${this._streamSupported && Array.isArray(this._boots)
${(this.allowSwitch && this.provider === "core") || hasBoots
? html`
<ha-button-menu @action=${this._handleOverflowAction}>
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
</ha-icon-button>
<ha-list-item graphic="icon">
<ha-svg-icon
slot="graphic"
.path=${mdiFormatListNumbered}
></ha-svg-icon>
${localize(
`ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots`
)}
</ha-list-item>
${this.allowSwitch && this.provider === "core"
? html`<ha-list-item graphic="icon">
<ha-svg-icon
slot="graphic"
.path=${mdiFolderTextOutline}
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.logs.show_condensed_logs"
)}
</ha-list-item>`
: nothing}
${hasBoots
? html`<ha-list-item graphic="icon">
<ha-svg-icon
slot="graphic"
.path=${mdiFormatListNumbered}
></ha-svg-icon>
${localize(
`ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots`
)}
</ha-list-item>`
: nothing}
</ha-button-menu>
`
: nothing}
@@ -304,47 +326,34 @@ class ErrorLogCard extends LitElement {
slot="trailingIcon"
></ha-svg-icon>
</ha-button>
${this._streamSupported &&
this._loadingState !== "loading" &&
!this._error
${streaming && this._boot === 0 && !this._error
? html`<div class="live-indicator">
<ha-svg-icon path=${mdiCircle}></ha-svg-icon>
Live
</div>`
: nothing}
</ha-card>
${this.show === false
? html`
${this._downloadSupported
? html`
<ha-button outlined @click=${this._downloadLogs}>
<ha-svg-icon .path=${mdiDownload}></ha-svg-icon>
${localize("ui.panel.config.logs.download_logs")}
</ha-button>
`
: nothing}
<mwc-button raised @click=${this._showLogs}>
${localize("ui.panel.config.logs.load_logs")}
</mwc-button>
`
: nothing}
</div>
`;
}
public connectedCallback() {
super.connectedCallback();
protected willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (changedProps.has("provider")) {
this._boot = 0;
this._loadLogs();
}
if (this.hasUpdated) {
return;
}
this._streamSupported = atLeastVersion(this.hass.config.version, 2024, 11);
this._downloadSupported = downloadFileSupported(this.hass);
// just needs to be loaded once, because only the host endpoints provide boots information
this._loadBoots();
if (this._streamSupported === undefined) {
this._streamSupported = atLeastVersion(
this.hass.config.version,
2024,
11
);
}
if (this._downloadSupported === undefined && this.hass) {
this._downloadSupported = downloadFileSupported(this.hass);
}
window.addEventListener("connection-status", this._handleConnectionStatus);
this.hass.loadFragmentTranslation("config");
}
protected firstUpdated(changedProps: PropertyValues) {
@@ -354,28 +363,11 @@ class ErrorLogCard extends LitElement {
this._scrolledToTopController.callback = this._handleTopScroll;
this._scrolledToTopController.observe(this._scrollTopMarkerElement!);
window.addEventListener("connection-status", this._handleConnectionStatus);
if (this.hass?.config.recovery_mode || this.show) {
this.hass.loadFragmentTranslation("config");
}
// just needs to be loaded once, because only the host endpoints provide boots information
this._loadBoots();
}
protected updated(changedProps) {
super.updated(changedProps);
if (
(changedProps.has("show") && this.show) ||
(changedProps.has("provider") && this.show)
) {
this._boot = 0;
this._loadLogs();
}
if (this._newLogsIndicator && this._scrolledToBottomController.value) {
this._newLogsIndicator = false;
}
@@ -409,7 +401,7 @@ class ErrorLogCard extends LitElement {
}
private async _downloadLogs(): Promise<void> {
if (this._streamSupported) {
if (this._streamSupported && this.provider) {
showDownloadLogsDialog(this, {
header: this.header,
provider: this.provider,
@@ -431,10 +423,6 @@ class ErrorLogCard extends LitElement {
}
}
private _showLogs(): void {
this.show = true;
}
private async _loadLogs(): Promise<void> {
this._error = undefined;
this._loadingState = "loading";
@@ -446,15 +434,16 @@ class ErrorLogCard extends LitElement {
try {
if (this._logStreamAborter) {
this._logStreamAborter.abort();
this._logStreamAborter = undefined;
}
this._logStreamAborter = new AbortController();
if (
this._streamSupported &&
isComponentLoaded(this.hass, "hassio") &&
this.provider
) {
this._logStreamAborter = new AbortController();
// check if there are any logs at all
const testResponse = await fetchHassioLogs(
this.hass,
@@ -545,8 +534,7 @@ class ErrorLogCard extends LitElement {
this._streamSupported = false;
let logs = "";
if (isComponentLoaded(this.hass, "hassio") && this.provider) {
const repsonse = await fetchHassioLogs(this.hass, this.provider);
logs = await repsonse.text();
logs = await fetchHassioLogsLegacy(this.hass, this.provider);
} else {
logs = await fetchErrorLog(this.hass);
}
@@ -598,60 +586,62 @@ class ErrorLogCard extends LitElement {
if (ev.detail === "disconnected" && this._logStreamAborter) {
this._logStreamAborter.abort();
}
if (ev.detail === "connected" && this.show) {
if (ev.detail === "connected") {
this._loadLogs();
}
};
private async _loadMoreLogs() {
if (
this._firstCursor &&
this._loadingPrevState !== "loading" &&
this._loadingState === "loaded" &&
this._logElement
!this._firstCursor ||
this._loadingPrevState === "loading" ||
this._loadingState !== "loaded" ||
!this._logElement ||
!this.provider
) {
const scrolledToBottom = this._scrolledToBottomController.value;
const scrollPositionFromBottom =
this._logElement.scrollHeight - this._logElement.scrollTop;
this._loadingPrevState = "loading";
const response = await fetchHassioLogs(
this.hass,
this.provider,
`entries=${this._firstCursor}:-100:100`,
this._boot
);
return;
}
const scrolledToBottom = this._scrolledToBottomController.value;
const scrollPositionFromBottom =
this._logElement.scrollHeight - this._logElement.scrollTop;
this._loadingPrevState = "loading";
const response = await fetchHassioLogs(
this.hass,
this.provider,
`entries=${this._firstCursor}:-100:100`,
this._boot
);
if (response.headers.has("X-First-Cursor")) {
if (this._firstCursor === response.headers.get("X-First-Cursor")!) {
this._loadingPrevState = "end";
return;
}
this._firstCursor = response.headers.get("X-First-Cursor")!;
}
const body = await response.text();
if (body) {
const lines = body
.split("\n")
.filter((line) => line.trim() !== "")
.reverse();
this._ansiToHtmlElement?.parseLinesToColoredPre(lines, true);
this._numberOfLines! += lines.length;
this._loadingPrevState = "loaded";
} else {
if (response.headers.has("X-First-Cursor")) {
if (this._firstCursor === response.headers.get("X-First-Cursor")!) {
this._loadingPrevState = "end";
return;
}
this._firstCursor = response.headers.get("X-First-Cursor")!;
}
if (scrolledToBottom) {
this._scrollToBottom();
} else if (this._loadingPrevState !== "end" && this._logElement) {
window.requestAnimationFrame(() => {
this._logElement!.scrollTop =
this._logElement!.scrollHeight - scrollPositionFromBottom;
});
}
const body = await response.text();
if (body) {
const lines = body
.split("\n")
.filter((line) => line.trim() !== "")
.reverse();
this._ansiToHtmlElement?.parseLinesToColoredPre(lines, true);
this._numberOfLines! += lines.length;
this._loadingPrevState = "loaded";
} else {
this._loadingPrevState = "end";
}
if (scrolledToBottom) {
this._scrollToBottom();
} else if (this._loadingPrevState !== "end" && this._logElement) {
window.requestAnimationFrame(() => {
this._logElement!.scrollTop =
this._logElement!.scrollHeight - scrollPositionFromBottom;
});
}
}
@@ -693,7 +683,15 @@ class ErrorLogCard extends LitElement {
}
private _handleOverflowAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
let index = ev.detail.index;
if (this.provider === "core") {
index--;
}
switch (index) {
case -1:
// @ts-ignore
fireEvent(this, "switch-log-view");
break;
case 0:
this._showBootsSelect = !this._showBootsSelect;
break;

View File

@@ -3,20 +3,20 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { navigate } from "../../../common/navigate";
import { extractSearchParam } from "../../../common/url/search-params";
import "../../../components/ha-button-menu";
import "../../../components/ha-button";
import "../../../components/ha-button-menu";
import "../../../components/search-input";
import type { LogProvider } from "../../../data/error_log";
import { fetchHassioAddonsInfo } from "../../../data/hassio/addon";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-subpage";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import "./error-log-card";
import "./system-log-card";
import type { SystemLogCard } from "./system-log-card";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import { navigate } from "../../../common/navigate";
const logProviders: LogProvider[] = [
{
@@ -57,6 +57,8 @@ export class HaConfigLogs extends LitElement {
@state() private _filter = extractSearchParam("filter") || "";
@state() private _detail = false;
@query("system-log-card") private systemLog?: SystemLogCard;
@state() private _selectedLogProvider = "core";
@@ -141,7 +143,7 @@ export class HaConfigLogs extends LitElement {
: ""}
${search}
<div class="content">
${this._selectedLogProvider === "core"
${this._selectedLogProvider === "core" && !this._detail
? html`
<system-log-card
.hass=${this.hass}
@@ -149,23 +151,28 @@ export class HaConfigLogs extends LitElement {
(p) => p.key === this._selectedLogProvider
)!.name}
.filter=${this._filter}
@switch-log-view=${this._showDetail}
></system-log-card>
`
: ""}
<error-log-card
.hass=${this.hass}
.header=${this._logProviders.find(
(p) => p.key === this._selectedLogProvider
)!.name}
.filter=${this._filter}
.provider=${this._selectedLogProvider}
.show=${this._selectedLogProvider !== "core"}
></error-log-card>
: html`<error-log-card
.hass=${this.hass}
.header=${this._logProviders.find(
(p) => p.key === this._selectedLogProvider
)!.name}
.filter=${this._filter}
.provider=${this._selectedLogProvider}
@switch-log-view=${this._showDetail}
allow-switch
></error-log-card>`}
</div>
</hass-subpage>
`;
}
private _showDetail() {
this._detail = !this._detail;
}
private _selectProvider(ev) {
this._selectedLogProvider = (ev.currentTarget as any).provider;
this._filter = "";

View File

@@ -1,16 +1,20 @@
import { mdiRefresh } from "@mdi/js";
import "@material/mwc-list/mwc-list";
import { mdiDotsVertical, mdiDownload, mdiRefresh, mdiText } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/buttons/ha-progress-button";
import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-circular-progress";
import "../../../components/ha-icon-button";
import "../../../components/ha-list-item";
import { getSignedPath } from "../../../data/auth";
import { getErrorLogDownloadUrl } from "../../../data/error_log";
import { domainToName } from "../../../data/integration";
import type { LoggedError } from "../../../data/system_log";
import {
@@ -19,6 +23,7 @@ import {
isCustomIntegrationError,
} from "../../../data/system_log";
import type { HomeAssistant } from "../../../types";
import { fileDownload } from "../../../util/file_download";
import { showSystemLogDetailDialog } from "./show-dialog-system-log-detail";
import { formatSystemLogTime } from "./util";
@@ -104,11 +109,34 @@ export class SystemLogCard extends LitElement {
: html`
<div class="header">
<h1 class="card-header">${this.header || "Logs"}</h1>
<ha-icon-button
.path=${mdiRefresh}
@click=${this.fetchData}
.label=${this.hass.localize("ui.common.refresh")}
></ha-icon-button>
<div class="header-buttons">
<ha-icon-button
.path=${mdiDownload}
@click=${this._downloadLogs}
.label=${this.hass.localize(
"ui.panel.config.logs.download_logs"
)}
></ha-icon-button>
<ha-icon-button
.path=${mdiRefresh}
@click=${this.fetchData}
.label=${this.hass.localize("ui.common.refresh")}
></ha-icon-button>
<ha-button-menu @action=${this._handleOverflowAction}>
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
</ha-icon-button>
<ha-list-item graphic="icon">
<ha-svg-icon
slot="graphic"
.path=${mdiText}
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.logs.show_full_logs"
)}
</ha-list-item>
</ha-button-menu>
</div>
</div>
${this._items.length === 0
? html`
@@ -195,6 +223,19 @@ export class SystemLogCard extends LitElement {
}
}
private _handleOverflowAction() {
// @ts-ignore
fireEvent(this, "switch-log-view");
}
private async _downloadLogs() {
const timeString = new Date().toISOString().replace(/:/g, "-");
const downloadUrl = getErrorLogDownloadUrl;
const logFileName = `home-assistant_${timeString}.log`;
const signedUrl = await getSignedPath(this.hass, downloadUrl);
fileDownload(signedUrl.path, logFileName);
}
private _openLog(ev: Event): void {
const item = (ev.currentTarget as any).logItem;
showSystemLogDetailDialog(this, { item });
@@ -203,7 +244,7 @@ export class SystemLogCard extends LitElement {
static get styles(): CSSResultGroup {
return css`
ha-card {
padding-top: 16px;
padding-top: 8px;
}
.header {
@@ -212,6 +253,11 @@ export class SystemLogCard extends LitElement {
padding: 0 16px;
}
.header-buttons {
display: flex;
align-items: center;
}
.card-header {
color: var(--ha-card-header-color, var(--primary-text-color));
font-family: var(--ha-card-header-font-family, inherit);
@@ -243,6 +289,10 @@ export class SystemLogCard extends LitElement {
color: var(--warning-color);
}
.card-content {
border-top: 1px solid var(--divider-color);
}
.card-actions,
.empty-content {
direction: var(--direction);

View File

@@ -31,7 +31,7 @@ export class HuiRecoveryModeCard extends LitElement implements LovelaceCard {
"ui.panel.lovelace.cards.recovery-mode.description"
)}
</div>
<error-log-card .hass=${this.hass}></error-log-card>
<error-log-card .hass=${this.hass} provider="core"></error-log-card>
</ha-card>
`;
}

View File

@@ -47,7 +47,7 @@ class HuiViewContainer extends LitElement {
private _isFixedBackground(background?: BackgroundConfig) {
if (typeof background === "string") {
return background.includes(" fixed");
return background.split(" ").includes("fixed");
}
return false;
}
@@ -126,7 +126,7 @@ class HuiViewContainer extends LitElement {
);
background-attachment: scroll !important;
}
:host(:not(fixed-background)) {
:host(:not([fixed-background])) {
background: var(
--view-background,
var(--lovelace-background, var(--primary-background-color))

View File

@@ -1,10 +1,11 @@
import type Sortable from "sortablejs";
import SortableCore, {
OnSpill,
AutoScroll,
OnSpill,
} from "sortablejs/modular/sortable.core.esm";
SortableCore.mount(OnSpill, new AutoScroll());
SortableCore.mount(OnSpill);
SortableCore.mount(new AutoScroll());
export default SortableCore as typeof Sortable;

View File

@@ -158,6 +158,7 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
},
callApi: async (method, path, parameters, headers) =>
hassCallApi(auth, method, path, parameters, headers),
// callApiRaw introduced in 2024.11
callApiRaw: async (method, path, parameters, headers, signal) =>
hassCallApiRaw(auth, method, path, parameters, headers, signal),
fetchWithAuth: (

View File

@@ -2470,9 +2470,9 @@
"search": "Search logs",
"failed_get_logs": "Failed to get {provider} logs, {error}",
"no_issues_search": "No issues found for search term ''{term}''",
"load_logs": "Load full logs",
"load_logs": "Load logs",
"nr_of_lines": "Number of lines",
"loading_log": "Loading full log…",
"loading_log": "Loading log…",
"no_errors": "No errors have been reported",
"no_issues": "There are no new issues!",
"clear": "Clear",
@@ -2489,7 +2489,8 @@
},
"custom_integration": "custom integration",
"error_from_custom_integration": "This error originated from a custom integration.",
"show_full_logs": "Show full logs",
"show_full_logs": "Show raw logs",
"show_condensed_logs": "Show condensed logs",
"select_number_of_lines": "Select number of lines to download",
"lines": "Lines",
"download_logs": "Download logs",
@@ -4841,7 +4842,6 @@
"node_id": "ID",
"node_ready": "Ready",
"device_config": "Configure",
"installer_settings": "Installer settings",
"reinterview_device": "Re-interview",
"rebuild_routes": "Rebuild routes",
"remove_failed": "Remove failed",
@@ -5133,45 +5133,6 @@
"subscribed_to_logs": "Subscribed to Z-Wave JS log messages…",
"log_level_changed": "Log Level changed to: {level}",
"download_logs": "Download logs"
},
"node_installer": {
"header": "Installer Settings",
"introduction": "Configure your device installer settings.",
"endpoint": "Endpoint",
"no_settings": "This device does not have any installer settings.",
"command_class": "Command Class",
"capability_controls": {
"thermostat_setback": {
"title": "Thermostat Setback",
"setback_state_label": "Setback in 1/10 degrees (Kelvin)",
"setback_state_helper": "Min: -12.8, max: 12.0",
"setback_special_state": {
"label": "Setback special state",
"frost_protection": "Frost protection",
"energy_saving": "Energy saving",
"unused": "Unused"
},
"setback_type": {
"label": "Setback Type",
"none": "None",
"temporary": "Temporary",
"permanent": "Permanent"
},
"get_setback_failed": "Failed to get setback state. {error}",
"save_setback_failed": "Failed to save setback state. {error}"
},
"multilevel_switch": {
"title": "Transition",
"direction": "Direction",
"up": "Up",
"down": "Down",
"ignore_start_level": "Ignore start level",
"start_level": "Start level",
"start_transition": "Start transition",
"stop_transition": "Stop transition",
"control_failed": "Failed to control transition. {error}"
}
}
}
},
"matter": {

View File

@@ -259,7 +259,7 @@ export interface HomeAssistant {
parameters?: Record<string, any>,
headers?: Record<string, string>
): Promise<T>;
callApiRaw(
callApiRaw( // introduced in 2024.11
method: "GET" | "POST" | "PUT" | "DELETE",
path: string,
parameters?: Record<string, any>,

186
yarn.lock
View File

@@ -1413,150 +1413,150 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/ecma402-abstract@npm:2.2.3":
version: 2.2.3
resolution: "@formatjs/ecma402-abstract@npm:2.2.3"
"@formatjs/ecma402-abstract@npm:2.2.1":
version: 2.2.1
resolution: "@formatjs/ecma402-abstract@npm:2.2.1"
dependencies:
"@formatjs/fast-memoize": "npm:2.2.3"
"@formatjs/intl-localematcher": "npm:0.5.7"
"@formatjs/fast-memoize": "npm:2.2.2"
"@formatjs/intl-localematcher": "npm:0.5.6"
tslib: "npm:2"
checksum: 10/d39e9f0d36c296a635f52aa35e07a67b6aa90383a30a046a0508e5d730676399fd0e67188eff463fe2a4d5febc9f567af45788fdf881e070910be7eb9294dd8c
checksum: 10/8c281e14cb5f12b8697225be6b0ac13d057911e257d3c23928aad985b535df90b7bb2a235aab22753a6e57aef98f00b826514fc3703e69018ccc98c8d9848f38
languageName: node
linkType: hard
"@formatjs/fast-memoize@npm:2.2.3":
version: 2.2.3
resolution: "@formatjs/fast-memoize@npm:2.2.3"
"@formatjs/fast-memoize@npm:2.2.2":
version: 2.2.2
resolution: "@formatjs/fast-memoize@npm:2.2.2"
dependencies:
tslib: "npm:2"
checksum: 10/a9634acb5e03d051e09881eea5484ab02271f7d6b5f96ae9485674ab3c359aa881bc45fc07a1181ae4b2d6e288dadc169f578d142d698913ebbefa373014cac2
checksum: 10/c6e958753eb41bb0875734762a44126a0d570706a31b32bb409e759cd372184c28e294b02fce0b0f0999c171ef717d513eaf7936862c498d78428b97db446ff8
languageName: node
linkType: hard
"@formatjs/icu-messageformat-parser@npm:2.9.3":
version: 2.9.3
resolution: "@formatjs/icu-messageformat-parser@npm:2.9.3"
"@formatjs/icu-messageformat-parser@npm:2.9.1":
version: 2.9.1
resolution: "@formatjs/icu-messageformat-parser@npm:2.9.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/icu-skeleton-parser": "npm:1.8.7"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/icu-skeleton-parser": "npm:1.8.5"
tslib: "npm:2"
checksum: 10/b24a3db43e4bf612107e981d5b40c077543d2266a08aac5cf01d5f65bf60527d5d16795e2e30063cb180b1d36d401944cd2ffb3a19d79b0cd28fa59751d19b7c
checksum: 10/f52c7c55b1dfc141910089a0494abd98d1c13c0a359cfb3bfa0668a5e2015c0c579bf161978fdb3ab40fa9a7374a37ac062f8710ed285429bf60abde4a5d1183
languageName: node
linkType: hard
"@formatjs/icu-skeleton-parser@npm:1.8.7":
version: 1.8.7
resolution: "@formatjs/icu-skeleton-parser@npm:1.8.7"
"@formatjs/icu-skeleton-parser@npm:1.8.5":
version: 1.8.5
resolution: "@formatjs/icu-skeleton-parser@npm:1.8.5"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/ecma402-abstract": "npm:2.2.1"
tslib: "npm:2"
checksum: 10/1a39815e5048f3c12a8d6a5b553271437b62e302724fc15c3b6967dc3e24823fcd9b8d3231a064991e163c147e54e588c571a092d557e93e78e738d218c6ef43
checksum: 10/5b9c57f80b751483bef8897ff9607a9eb215fd7a8d8ae9fa5c631edf6d16fa4532c853395f20b7f3f38d6d4d1a35b98cd06421291203c7ad333f52077ef2a406
languageName: node
linkType: hard
"@formatjs/intl-datetimeformat@npm:6.16.3":
version: 6.16.3
resolution: "@formatjs/intl-datetimeformat@npm:6.16.3"
"@formatjs/intl-datetimeformat@npm:6.16.1":
version: 6.16.1
resolution: "@formatjs/intl-datetimeformat@npm:6.16.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/intl-localematcher": "npm:0.5.7"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/intl-localematcher": "npm:0.5.6"
tslib: "npm:2"
checksum: 10/4e213611b92eda40aa6053b9458be71fb752f020616bb0e93fc681efc4fc408dfec408ae33ded8678887730f8ee766568f90b6ca57de6e9d8f1de45dda794f08
checksum: 10/494868322d396e0eede6a27c16047858944f42fd3b45cf5d155f963df62e694b842ac0bef07e23aa73fa55cf143956d642d05ea62a3e762632101451975b5fc4
languageName: node
linkType: hard
"@formatjs/intl-displaynames@npm:6.8.3":
version: 6.8.3
resolution: "@formatjs/intl-displaynames@npm:6.8.3"
"@formatjs/intl-displaynames@npm:6.8.1":
version: 6.8.1
resolution: "@formatjs/intl-displaynames@npm:6.8.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/intl-localematcher": "npm:0.5.7"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/intl-localematcher": "npm:0.5.6"
tslib: "npm:2"
checksum: 10/46c8d6e6d6d56d5f495c0bfb5784687a0af1ffd9eaeb72c1d9db8e21f8c7eeec346198871f8fe39f6eebfb19d6c3e46cbf92e213e6a6f0dfdb2f55fe96d43bcc
checksum: 10/627fc625e14b4d1bea5b2bf41e40050eb9775d0f66780e155719e21c062f9b3331d08b488ebcd3608c60999498af5a39e67cb5fd2a6d54a0e7395d7a63bfe643
languageName: node
linkType: hard
"@formatjs/intl-enumerator@npm:1.8.3":
version: 1.8.3
resolution: "@formatjs/intl-enumerator@npm:1.8.3"
"@formatjs/intl-enumerator@npm:1.8.1":
version: 1.8.1
resolution: "@formatjs/intl-enumerator@npm:1.8.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/ecma402-abstract": "npm:2.2.1"
tslib: "npm:2"
checksum: 10/a51ed7e15835cc1612282de46139d0f49553f004439a728a9118d1b9b15a3d05916e8aad4001e18c4909a3d4287fc07c921540c5ba8f32499f3243ac50d68a42
checksum: 10/0e4250de905e757fb88d6ff072968c72ed3a39de8ddaed73c38c0099825f11530c9b8e224573ae6e46cf49f1318e463f40ba2cdfa25cb7415382ba952b570bdc
languageName: node
linkType: hard
"@formatjs/intl-getcanonicallocales@npm:2.5.2":
version: 2.5.2
resolution: "@formatjs/intl-getcanonicallocales@npm:2.5.2"
"@formatjs/intl-getcanonicallocales@npm:2.5.1":
version: 2.5.1
resolution: "@formatjs/intl-getcanonicallocales@npm:2.5.1"
dependencies:
tslib: "npm:2"
checksum: 10/0d1738181911635d91d4a788d663fadd1aa045f40f0f05ac8b04adc06cd4f5ee3c50aa7c3a50c63ba7572f23e336720340c8240d6070d899e56adf25d0388f1b
checksum: 10/5e83c0b3574333e5027c3c4f74ea20800e50e36fb8efa69361457b57f618738f478b5d22777ba30a2b7a15bdff60101d8119169c909b33577244747d52e59614
languageName: node
linkType: hard
"@formatjs/intl-listformat@npm:7.7.3":
version: 7.7.3
resolution: "@formatjs/intl-listformat@npm:7.7.3"
"@formatjs/intl-listformat@npm:7.7.1":
version: 7.7.1
resolution: "@formatjs/intl-listformat@npm:7.7.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/intl-localematcher": "npm:0.5.7"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/intl-localematcher": "npm:0.5.6"
tslib: "npm:2"
checksum: 10/52ae02202a2bb0d8c16ea9a8f142d616e6ecb8400aa96ca618896cf529a3e3f5d88d64cb2644ad6a4ba7e17ee013d8fb3463419802afab5ca25afa51151ab62c
checksum: 10/a64581f1d2e8e0c0c83c5d56334a3e3786ed251e1a882d7610d2588d8602eacb32c9167032891e2796c30df3437c9ce52c7284786dca6f1f44250301060169ea
languageName: node
linkType: hard
"@formatjs/intl-locale@npm:4.2.3":
version: 4.2.3
resolution: "@formatjs/intl-locale@npm:4.2.3"
"@formatjs/intl-locale@npm:4.2.1":
version: 4.2.1
resolution: "@formatjs/intl-locale@npm:4.2.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/intl-enumerator": "npm:1.8.3"
"@formatjs/intl-getcanonicallocales": "npm:2.5.2"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/intl-enumerator": "npm:1.8.1"
"@formatjs/intl-getcanonicallocales": "npm:2.5.1"
tslib: "npm:2"
checksum: 10/dab4090653e62f1c3453c074c3047d0e22ee6a3d33ac00afa45f1541b8686b453c671755e8faeeb1253b61131071c02506b56c094941efd6195d40163007182e
checksum: 10/4cba0fbeded2c7c5806528806f176cb833c43765bf1717470f4e001ab42581d5f0b52bf1893afef9597fba96dc3d4659507e490030f231523d460ec6686b9562
languageName: node
linkType: hard
"@formatjs/intl-localematcher@npm:0.5.7":
version: 0.5.7
resolution: "@formatjs/intl-localematcher@npm:0.5.7"
"@formatjs/intl-localematcher@npm:0.5.6":
version: 0.5.6
resolution: "@formatjs/intl-localematcher@npm:0.5.6"
dependencies:
tslib: "npm:2"
checksum: 10/52201f12212e7e9cba1a4f99020da587b13e44e06e03c4ccd4e5ac0829b411e73dfe0904a9039ef81eeabeea04ed8cfae9e727e6791acd0230745b7bd3ad059e
checksum: 10/14eac6bb25dcfeedd7960f44dec5a137999729da00b294ddf1133abe760ced4342f37734bc750b4c47f8dd8d5633a7da38d274503f80d7e965bb1f6fb6f2988c
languageName: node
linkType: hard
"@formatjs/intl-numberformat@npm:8.14.3":
version: 8.14.3
resolution: "@formatjs/intl-numberformat@npm:8.14.3"
"@formatjs/intl-numberformat@npm:8.14.1":
version: 8.14.1
resolution: "@formatjs/intl-numberformat@npm:8.14.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/intl-localematcher": "npm:0.5.7"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/intl-localematcher": "npm:0.5.6"
tslib: "npm:2"
checksum: 10/7a4e52ace65589ceb441032a09b88616e71ba4220605e498b1a064f43672cad5cca8c98b72446ffd7d57ef098c658c245c08a16623e0b1bc10940ff7e71069c7
checksum: 10/51152d1b9607a35c64e6089e44b90c7ec90be3b1925ba47ffc559ddb4fd72afae76e83af3d436831ea0fc47dc0e9fee9cd3d576280440f2dce03cb6bd24e0bed
languageName: node
linkType: hard
"@formatjs/intl-pluralrules@npm:5.3.3":
version: 5.3.3
resolution: "@formatjs/intl-pluralrules@npm:5.3.3"
"@formatjs/intl-pluralrules@npm:5.3.1":
version: 5.3.1
resolution: "@formatjs/intl-pluralrules@npm:5.3.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/intl-localematcher": "npm:0.5.7"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/intl-localematcher": "npm:0.5.6"
tslib: "npm:2"
checksum: 10/3679c63aa2b9dde474572998b829ecb5d134f1efe508e1e5a06089480bbff9f2216235a7d5745c434030dc17c8a83385f0639a71a22f1d648fcbc6fff90f57e3
checksum: 10/fc83c3547a9f0af6331c2970f265234fde967848ff738730f2e87ce816636d8778ead1185f5ecccc692cb8b63c11412dc85deac9d3425f44fe3a6a6c30c8b776
languageName: node
linkType: hard
"@formatjs/intl-relativetimeformat@npm:11.4.3":
version: 11.4.3
resolution: "@formatjs/intl-relativetimeformat@npm:11.4.3"
"@formatjs/intl-relativetimeformat@npm:11.4.1":
version: 11.4.1
resolution: "@formatjs/intl-relativetimeformat@npm:11.4.1"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/intl-localematcher": "npm:0.5.7"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/intl-localematcher": "npm:0.5.6"
tslib: "npm:2"
checksum: 10/7c7548ba133031873683a37566d646e4e3f50ea979773de199b41769df23648be2b44b53975809bd53f97a95d1d44038c0a09b1c031e05f0de6f7bba843b1aad
checksum: 10/80817403301baed257fbd8c793b9ed077a2e6dd0414a6895b5bfde3619aebc818f30535da9b560a6186fac783cf09561c495d2c6568a980bd635736194655af5
languageName: node
linkType: hard
@@ -8759,14 +8759,14 @@ __metadata:
"@codemirror/state": "npm:6.4.1"
"@codemirror/view": "npm:6.34.1"
"@egjs/hammerjs": "npm:2.0.17"
"@formatjs/intl-datetimeformat": "npm:6.16.3"
"@formatjs/intl-displaynames": "npm:6.8.3"
"@formatjs/intl-getcanonicallocales": "npm:2.5.2"
"@formatjs/intl-listformat": "npm:7.7.3"
"@formatjs/intl-locale": "npm:4.2.3"
"@formatjs/intl-numberformat": "npm:8.14.3"
"@formatjs/intl-pluralrules": "npm:5.3.3"
"@formatjs/intl-relativetimeformat": "npm:11.4.3"
"@formatjs/intl-datetimeformat": "npm:6.16.1"
"@formatjs/intl-displaynames": "npm:6.8.1"
"@formatjs/intl-getcanonicallocales": "npm:2.5.1"
"@formatjs/intl-listformat": "npm:7.7.1"
"@formatjs/intl-locale": "npm:4.2.1"
"@formatjs/intl-numberformat": "npm:8.14.1"
"@formatjs/intl-pluralrules": "npm:5.3.1"
"@formatjs/intl-relativetimeformat": "npm:11.4.1"
"@fullcalendar/core": "npm:6.1.15"
"@fullcalendar/daygrid": "npm:6.1.15"
"@fullcalendar/interaction": "npm:6.1.15"
@@ -8898,7 +8898,7 @@ __metadata:
husky: "npm:9.1.6"
idb-keyval: "npm:6.2.1"
instant-mocha: "npm:1.5.3"
intl-messageformat: "npm:10.7.5"
intl-messageformat: "npm:10.7.3"
js-yaml: "npm:4.1.0"
jszip: "npm:3.10.1"
leaflet: "npm:1.9.4"
@@ -9348,15 +9348,15 @@ __metadata:
languageName: node
linkType: hard
"intl-messageformat@npm:10.7.5":
version: 10.7.5
resolution: "intl-messageformat@npm:10.7.5"
"intl-messageformat@npm:10.7.3":
version: 10.7.3
resolution: "intl-messageformat@npm:10.7.3"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.3"
"@formatjs/fast-memoize": "npm:2.2.3"
"@formatjs/icu-messageformat-parser": "npm:2.9.3"
"@formatjs/ecma402-abstract": "npm:2.2.1"
"@formatjs/fast-memoize": "npm:2.2.2"
"@formatjs/icu-messageformat-parser": "npm:2.9.1"
tslib: "npm:2"
checksum: 10/8880448d62bd0260eafd4ee7ccfabaea573476f28e6d6bf47e027ee9c1d46d4919a076df7abedaf282422ff80ade02b5c637c69cdf739ee405e4837098bac37e
checksum: 10/e387f7f37a295d9d386af0c6392ba135a4580e86177161f1f400d470fed1f8c7b3cb6c724cbc2f50a7ded2e20f202977d8bf5e2bbc626f72016a5b5b6752b76d
languageName: node
linkType: hard