186 files
This commit is contained in:
+7
-4
@@ -410,15 +410,18 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
|
||||
/** One-click fix for a detected-but-unrecorded swap: record the DETECTED
|
||||
* jump time in Battery Notes, so the forecast re-anchors on the real
|
||||
* replacement instead of the dead battery's date. */
|
||||
* replacement instead of the dead battery's date. Goes through the
|
||||
* backend (#181) so the swap also consumes the type-part cells from
|
||||
* stock, exactly like the Replaced action. */
|
||||
private async _recordJump(entityId: string, jump: { at: number; device_id: string }): Promise<void> {
|
||||
if (this._marking) return;
|
||||
this._marking = true;
|
||||
this._error = "";
|
||||
try {
|
||||
await this.hass.callService("battery_notes", "set_battery_replaced", {
|
||||
device_id: jump.device_id,
|
||||
datetime_replaced: new Date(jump.at * 1000).toISOString(),
|
||||
await this.hass.connection.sendMessagePromise({
|
||||
type: "maintenance_supporter/battery_fleet/record_replacement",
|
||||
entity_id: entityId,
|
||||
replaced_at: new Date(jump.at * 1000).toISOString(),
|
||||
});
|
||||
this._recorded = [...this._recorded, entityId];
|
||||
await this._load();
|
||||
|
||||
@@ -54,6 +54,11 @@ interface SettingsResponse {
|
||||
/** #146: household "low" floors (percent) for discovery + battery fleet. */
|
||||
default_consumable_threshold?: number;
|
||||
battery_low_percent?: number;
|
||||
/** #180: a low battery counts as replaced only once its level is above this. */
|
||||
battery_recovered_percent?: number;
|
||||
/** D#182: shopping-search URL with {q} ("" = automatic) + the automatic value in effect. */
|
||||
part_search_url_template?: string;
|
||||
part_search_url_default?: string;
|
||||
/** D#162 follow-up: per-type lifetime overrides + the computed catalog. */
|
||||
battery_lifetime_months?: Record<string, number>;
|
||||
battery_lifetimes?: {
|
||||
@@ -860,9 +865,23 @@ export class MaintenanceSettingsView extends LitElement {
|
||||
<span class="setting-label">${t("settings_battery_low_percent", L)}</span>
|
||||
${this._intSetting("battery_low_percent", g.battery_low_percent ?? 20)}
|
||||
</label>
|
||||
<label class="setting-row">
|
||||
<span class="setting-label">${t("settings_battery_recovered_percent", L)}</span>
|
||||
${this._intSetting("battery_recovered_percent", g.battery_recovered_percent ?? 50)}
|
||||
</label>
|
||||
<div class="setting-hint">${t("settings_battery_recovered_percent_hint", L)}</div>
|
||||
${this._renderBatteryNotesHint(L)}
|
||||
${this._renderBatteryLifetimes(L)}
|
||||
<div class="setting-hint">${t("settings_thresholds_hint", L)}</div>
|
||||
<label class="setting-row">
|
||||
<span class="setting-label">${t("settings_part_search_url", L)}</span>
|
||||
<!-- D#182: no live() — same typed-input rule as the numbers above; the
|
||||
placeholder shows the automatic template (country, then language). -->
|
||||
<input type="url" class="part-search-url" .value=${g.part_search_url_template ?? ""}
|
||||
placeholder=${g.part_search_url_default ?? ""}
|
||||
@change=${(e: Event) => this._updateSetting("part_search_url_template", (e.target as HTMLInputElement).value.trim())} />
|
||||
</label>
|
||||
<div class="setting-hint">${t("settings_part_search_url_hint", L)}</div>
|
||||
<label class="setting-row">
|
||||
<span class="setting-label">${t("settings_row_actions", L)}</span>
|
||||
<select .value=${live(g.row_action_style || "buttons_compact")}
|
||||
|
||||
@@ -208,6 +208,8 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
@state() private _customIcon = "";
|
||||
@state() private _priority = "normal";
|
||||
@state() private _labels = "";
|
||||
/** D#183: external todo.* lists the task is mirrored into while due. */
|
||||
@state() private _mirrorTodoEntities: string[] = [];
|
||||
@state() private _enabled = true;
|
||||
|
||||
// Trigger fields
|
||||
@@ -415,6 +417,7 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
this._customIcon = task.custom_icon || "";
|
||||
this._priority = task.priority || "normal";
|
||||
this._labels = (task.labels || []).join(", ");
|
||||
this._mirrorTodoEntities = [...(task.mirror_todo_entities || [])];
|
||||
this._enabled = task.enabled !== false;
|
||||
this._lastPerformed = task.last_performed || "";
|
||||
this._nfcTagId = task.nfc_tag_id || "";
|
||||
@@ -571,6 +574,7 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
this._customIcon = "";
|
||||
this._priority = "normal";
|
||||
this._labels = "";
|
||||
this._mirrorTodoEntities = [];
|
||||
this._enabled = true;
|
||||
this._lastPerformed = "";
|
||||
this._nfcTagId = "";
|
||||
@@ -1322,6 +1326,8 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
// D#183: always sent — [] clears the mirror targets.
|
||||
data.mirror_todo_entities = this._mirrorTodoEntities.filter(Boolean);
|
||||
data.enabled = this._enabled;
|
||||
data.last_performed = this._lastPerformed || null;
|
||||
data.nfc_tag_id = this._nfcTagId || null;
|
||||
@@ -2714,6 +2720,34 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
/>
|
||||
<div class="field-help">${t("labels_help", L)}</div>
|
||||
</div>
|
||||
<div class="field mirror-todo-field">
|
||||
${this._entityPickerFallback ? html`
|
||||
<ms-textfield
|
||||
label="${t("task_mirror_todo", L)}"
|
||||
placeholder="todo.family, todo.kids"
|
||||
.value=${this._mirrorTodoEntities.join(", ")}
|
||||
@input=${(e: Event) => {
|
||||
this._mirrorTodoEntities = (e.target as HTMLInputElement).value
|
||||
.split(",").map((s: string) => s.trim()).filter(Boolean);
|
||||
}}
|
||||
></ms-textfield>
|
||||
` : html`
|
||||
<ha-form
|
||||
class="entity-picker-form"
|
||||
.hass=${this.hass}
|
||||
.schema=${[{
|
||||
name: "mirror_todo_entities",
|
||||
selector: { entity: { multiple: true, domain: ["todo"] } },
|
||||
}]}
|
||||
.data=${{ mirror_todo_entities: this._mirrorTodoEntities }}
|
||||
.computeLabel=${() => t("task_mirror_todo", L)}
|
||||
@value-changed=${(e: CustomEvent) => {
|
||||
const ids = ((e.detail.value as { mirror_todo_entities?: string[] }).mirror_todo_entities || []).filter(Boolean);
|
||||
this._mirrorTodoEntities = ids;
|
||||
}}
|
||||
></ha-form>`}
|
||||
<div class="field-help">${t("task_mirror_todo_hint", L)}</div>
|
||||
</div>
|
||||
<div class="select-row">
|
||||
<label>${t("schedule_type", L)}</label>
|
||||
<select
|
||||
|
||||
Reference in New Issue
Block a user