217 files

This commit is contained in:
Home Assistant Version Control
2026-09-16 15:29:53 +00:00
parent 40c604b6dd
commit ef9868f192
217 changed files with 864 additions and 34306 deletions
@@ -56,6 +56,7 @@ interface SettingsResponse {
battery_low_percent?: number;
/** #180: a low battery counts as replaced only once its level is above this. */
battery_recovered_percent?: number;
battery_auto_record_recovery?: boolean;
/** D#182: shopping-search URL with {q} ("" = automatic) + the automatic value in effect. */
part_search_url_template?: string;
part_search_url_default?: string;
@@ -517,6 +518,28 @@ export class MaintenanceSettingsView extends LitElement {
return runWs<T>(this, payload, { fallbackKey, busy, onError: (msg) => this._showToast(msg) });
}
@state() private _compacting = false;
/** #170: renumber every object, task and completion sequentially — an
* explicit, admin-only action behind a confirm (printed booklets keep the
* old numbers, so this is never done silently). */
private async _compactReferenceNumbers(): Promise<void> {
const L = this._lang;
if (this._compacting || !window.confirm(t("settings_compact_refs_confirm", L))) return;
const res = await this._ws<{ objects: number; tasks: number; completions: number }>(
{ type: "maintenance_supporter/reference_numbers/compact" },
"action_error",
(b) => { this._compacting = b; },
);
if (!res) return;
this._showToast(
t("settings_compact_refs_done", L)
.replace("{objects}", String(res.objects))
.replace("{tasks}", String(res.tasks))
.replace("{completions}", String(res.completions)),
);
}
/** Bounded integer settings: an out-of-range entry used to be dropped
* silently while the field kept showing it (bug review 2026-09-04). Now
* it is named in a toast and the field snaps back to the stored value;
@@ -870,6 +893,13 @@ export class MaintenanceSettingsView extends LitElement {
${this._intSetting("battery_recovered_percent", g.battery_recovered_percent ?? 50)}
</label>
<div class="setting-hint">${t("settings_battery_recovered_percent_hint", L)}</div>
<label class="setting-row">
<span class="setting-label">${t("settings_battery_auto_record", L)}</span>
<input type="checkbox" class="auto-record-recovery"
.checked=${g.battery_auto_record_recovery === true}
@change=${(e: Event) => this._updateSetting("battery_auto_record_recovery", (e.target as HTMLInputElement).checked)} />
</label>
<div class="setting-hint">${t("settings_battery_auto_record_hint", L)}</div>
${this._renderBatteryNotesHint(L)}
${this._renderBatteryLifetimes(L)}
<div class="setting-hint">${t("settings_thresholds_hint", L)}</div>
@@ -896,6 +926,15 @@ export class MaintenanceSettingsView extends LitElement {
@change=${(e: Event) => this._updateSetting("ref_numbers_in_lists", (e.target as HTMLInputElement).checked)} />
</label>
<div class="setting-hint">${t("settings_ref_numbers_in_lists_hint", L)}</div>
${this.hass?.user?.is_admin
? html`<div class="setting-row">
<span class="setting-label">${t("settings_compact_refs", L)}</span>
<button class="btn compact-refs" ?disabled=${this._compacting} @click=${this._compactReferenceNumbers}>
<ha-icon icon="mdi:sort-numeric-ascending"></ha-icon> ${t("settings_compact_refs", L)}
</button>
</div>
<div class="setting-hint">${t("settings_compact_refs_hint", L)}</div>`
: nothing}
<label class="setting-row">
<span class="setting-label">${t("settings_currency", L)}</span>
<select .value=${live(b.currency)} @change=${(e: Event) => this._updateSetting("budget_currency", (e.target as HTMLSelectElement).value)}>
@@ -168,6 +168,9 @@ export class MaintenanceTaskDialog extends LitElement {
@state() private _warning = "";
@state() private _entryId = "";
@state() private _taskId: string | null = null; // null = create
/** #181: the battery fleet task takes its quantities from Battery Notes per
* device — the parts picker is hidden for it (links there would be ignored). */
@state() private _isFleetTask = false;
// When openCreate is called without an entry_id and a list of objects is supplied,
// the dialog renders an Object selector dropdown so the user can pick the parent.
@state() private _objectChoices: Array<{ entry_id: string; name: string }> = [];
@@ -350,6 +353,7 @@ export class MaintenanceTaskDialog extends LitElement {
public async openCreate(entryId: string, objects?: Array<{ entry_id: string; object: { name: string } }>): Promise<void> {
this._entryId = entryId;
this._taskId = null;
this._isFleetTask = false;
this._error = "";
this._warning = "";
// If no entryId is preset but caller passed objects, expose them as a dropdown.
@@ -379,6 +383,7 @@ export class MaintenanceTaskDialog extends LitElement {
// edit mode — and picking another object re-pointed _entryId while
// _taskId kept the old task (audit 2026-08-29).
this._objectChoices = [];
this._isFleetTask = task.battery_fleet_task === true;
this._name = task.name;
this._type = task.type;
this._scheduleType = task.schedule_type;
@@ -2675,7 +2680,9 @@ export class MaintenanceTaskDialog extends LitElement {
${this._partsLoadFailed
? html`<div class="field-help parts-load-failed">${t("parts_load_failed", L)}</div>`
: nothing}
${this.parts.length || this._foreignOwners.length
${this._isFleetTask
? html`<div class="field-help parts-fleet-hint">${t("task_parts_fleet_hint", L)}</div>`
: this.parts.length || this._foreignOwners.length
? html`
<div class="field">
<label>${t("consumes_parts_label", L)}</label>