124 files
This commit is contained in:
@@ -43,6 +43,19 @@ interface SettingsResponse {
|
||||
panel_title: string;
|
||||
/** Opt-in copy of the shipped Assist sentences into the config dir. */
|
||||
install_assist_sentences?: boolean;
|
||||
/** #146: household "low" floors (percent) for discovery + battery fleet. */
|
||||
default_consumable_threshold?: number;
|
||||
battery_low_percent?: number;
|
||||
/** Computed: what Battery Notes reports (null when not installed). */
|
||||
battery_notes?: {
|
||||
default: number;
|
||||
devices: number;
|
||||
overrides: { name: string; device_id: string | null; threshold: number }[];
|
||||
more: number;
|
||||
} | null;
|
||||
/** #145: how task rows show Complete / Skip. */
|
||||
row_action_style?: "buttons_compact" | "buttons" | "icons";
|
||||
row_action_notice_pending?: boolean;
|
||||
};
|
||||
notifications: {
|
||||
due_soon_enabled: boolean;
|
||||
@@ -241,6 +254,37 @@ export class MaintenanceSettingsView extends LitElement {
|
||||
this._vacWindowEnd = v.window_end;
|
||||
}
|
||||
|
||||
/** #146 follow-up: what Battery Notes currently reports, with the rule
|
||||
* spelled out — warn-toned when its default sits above our floor (then a
|
||||
* lowered floor would silently change nothing for noted batteries). */
|
||||
private _renderBatteryNotesHint(L: string) {
|
||||
const bn = this._settings?.general?.battery_notes;
|
||||
if (!bn || !bn.devices) return nothing;
|
||||
const floor = this._settings?.general?.battery_low_percent ?? 20;
|
||||
const warn = bn.default > floor;
|
||||
const summary = t("bn_summary", L)
|
||||
.replace("{name}", "Battery Notes")
|
||||
.replace("{pct}", String(bn.default))
|
||||
.replace("{n}", String(bn.devices));
|
||||
const rule = (warn ? t("bn_above_floor", L).replace("{name}", "Battery Notes") : t("bn_floor_decides", L))
|
||||
.replace("{floor}", String(floor));
|
||||
return html`
|
||||
<div class="bn-note${warn ? " warn" : ""}">
|
||||
<ha-icon icon="${warn ? "mdi:alert-outline" : "mdi:battery-heart-variant"}"></ha-icon>
|
||||
<span>
|
||||
<a class="bn-link" href="/config/integrations/integration/battery_notes">${summary.split(":")[0]}</a>${summary.slice(summary.indexOf(":"))}
|
||||
${bn.overrides.length
|
||||
? html` · ${t("bn_overrides", L).replace("{n}", String(bn.overrides.length + bn.more))}
|
||||
${bn.overrides.map((o, i) => html`${i ? " · " : " "}${o.device_id
|
||||
? html`<a class="bn-link" href="/config/devices/device/${o.device_id}">${o.name}</a>`
|
||||
: o.name} (${o.threshold} %)`)}
|
||||
${bn.more ? html` <span class="bn-more">${t("bn_more", L).replace("{n}", String(bn.more))}</span>` : nothing}`
|
||||
: nothing}
|
||||
<span class="bn-sub">${rule}</span>
|
||||
</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private async _updateSetting(key: string, value: unknown): Promise<void> {
|
||||
try {
|
||||
const result = await this.hass.connection.sendMessagePromise({
|
||||
@@ -594,6 +638,32 @@ export class MaintenanceSettingsView extends LitElement {
|
||||
if (v >= 0 && v <= 365) this._updateSetting("default_warning_days", v);
|
||||
}} />
|
||||
</label>
|
||||
<label class="setting-row">
|
||||
<span class="setting-label">${t("settings_consumable_threshold", L)}</span>
|
||||
<input type="number" min="1" max="90" .value=${String(g.default_consumable_threshold ?? 10)}
|
||||
@change=${(e: Event) => {
|
||||
const v = parseInt((e.target as HTMLInputElement).value, 10);
|
||||
if (v >= 1 && v <= 90) this._updateSetting("default_consumable_threshold", v);
|
||||
}} />
|
||||
</label>
|
||||
<label class="setting-row">
|
||||
<span class="setting-label">${t("settings_battery_low_percent", L)}</span>
|
||||
<input type="number" min="1" max="90" .value=${String(g.battery_low_percent ?? 20)}
|
||||
@change=${(e: Event) => {
|
||||
const v = parseInt((e.target as HTMLInputElement).value, 10);
|
||||
if (v >= 1 && v <= 90) this._updateSetting("battery_low_percent", v);
|
||||
}} />
|
||||
</label>
|
||||
${this._renderBatteryNotesHint(L)}
|
||||
<div class="setting-hint">${t("settings_thresholds_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")}
|
||||
@change=${(e: Event) => this._updateSetting("row_action_style", (e.target as HTMLSelectElement).value)}>
|
||||
${(["buttons_compact", "buttons", "icons"] as const).map((v) => html`
|
||||
<option value=${v} ?selected=${(g.row_action_style || "buttons_compact") === v}>${t(`row_actions_${v}`, L)}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
<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)}>
|
||||
@@ -1667,6 +1737,21 @@ export class MaintenanceSettingsView extends LitElement {
|
||||
// --- Styles ---
|
||||
|
||||
static styles = css`
|
||||
.bn-note {
|
||||
display: flex; align-items: flex-start; gap: 10px;
|
||||
margin: 6px 0 10px; padding: 10px 12px; border-radius: 8px;
|
||||
background: rgba(3, 169, 244, 0.08);
|
||||
border: 1px solid rgba(3, 169, 244, 0.25);
|
||||
font-size: 13.5px; line-height: 1.45;
|
||||
}
|
||||
.bn-note ha-icon { --mdc-icon-size: 20px; flex: none; color: var(--primary-color); margin-top: 1px; }
|
||||
.bn-note.warn { background: rgba(255, 167, 38, 0.08); border-color: rgba(255, 167, 38, 0.35); }
|
||||
.bn-note.warn ha-icon { color: var(--warning-color, #ffa726); }
|
||||
.bn-link { color: var(--primary-color); text-decoration: none; }
|
||||
.bn-link:hover { text-decoration: underline; }
|
||||
.bn-more, .bn-sub { color: var(--secondary-text-color); }
|
||||
.bn-sub { display: block; font-size: 12.5px; margin-top: 2px; }
|
||||
|
||||
:host { display: block; }
|
||||
|
||||
.settings-loading {
|
||||
|
||||
+26
-23
@@ -596,40 +596,40 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
||||
`
|
||||
: html`
|
||||
<div class="actions primary-row">
|
||||
<button class="btn primary" @click=${this._onComplete} ?disabled=${this._busy}>
|
||||
<ha-icon icon="mdi:check"></ha-icon>
|
||||
<ha-button appearance="accent" variant="success" @click=${this._onComplete} .disabled=${this._busy}>
|
||||
<ha-icon slot="start" icon="mdi:check"></ha-icon>
|
||||
${t("complete", L) || "Complete"}
|
||||
</button>
|
||||
<button class="btn" @click=${() => { this._showSkip = true; }} ?disabled=${this._busy}>
|
||||
<ha-icon icon="mdi:skip-next"></ha-icon>
|
||||
</ha-button>
|
||||
<ha-button appearance="plain" variant="neutral" @click=${() => { this._showSkip = true; }} .disabled=${this._busy}>
|
||||
<ha-icon slot="start" icon="mdi:skip-next"></ha-icon>
|
||||
${t("skip", L) || "Skip"}
|
||||
</button>
|
||||
<button class="btn" @click=${() => { this._showReset = true; }} ?disabled=${this._busy}>
|
||||
<ha-icon icon="mdi:restart"></ha-icon>
|
||||
</ha-button>
|
||||
<ha-button appearance="plain" variant="neutral" @click=${() => { this._showReset = true; }} .disabled=${this._busy}>
|
||||
<ha-icon slot="start" icon="mdi:restart"></ha-icon>
|
||||
${t("reset", L) || "Reset"}
|
||||
</button>
|
||||
</ha-button>
|
||||
</div>
|
||||
${isAdmin
|
||||
? html`
|
||||
<div class="actions secondary-row">
|
||||
<button class="btn ghost" @click=${this._onEdit} ?disabled=${this._busy}>
|
||||
<ha-icon icon="mdi:pencil"></ha-icon>
|
||||
<ha-button size="small" appearance="outlined" variant="neutral" @click=${this._onEdit} .disabled=${this._busy}>
|
||||
<ha-icon slot="start" icon="mdi:pencil"></ha-icon>
|
||||
${t("edit", L) || "Edit"}
|
||||
</button>
|
||||
<button class="btn ghost" @click=${this._onQr} ?disabled=${this._busy}>
|
||||
<ha-icon icon="mdi:qrcode"></ha-icon>
|
||||
</ha-button>
|
||||
<ha-button size="small" appearance="outlined" variant="neutral" @click=${this._onQr} .disabled=${this._busy}>
|
||||
<ha-icon slot="start" icon="mdi:qrcode"></ha-icon>
|
||||
${t("qr_code", L) || "QR"}
|
||||
</button>
|
||||
<button class="btn ghost"
|
||||
</ha-button>
|
||||
<ha-button size="small" appearance="outlined" variant="neutral"
|
||||
@click=${task.archived ? this._onUnarchive : this._onArchive}
|
||||
?disabled=${this._busy}>
|
||||
<ha-icon icon="${task.archived ? 'mdi:archive-arrow-up-outline' : 'mdi:archive-outline'}"></ha-icon>
|
||||
.disabled=${this._busy}>
|
||||
<ha-icon slot="start" icon="${task.archived ? 'mdi:archive-arrow-up-outline' : 'mdi:archive-outline'}"></ha-icon>
|
||||
${task.archived ? (t("unarchive", L) || "Unarchive") : (t("archive", L) || "Archive")}
|
||||
</button>
|
||||
<button class="btn ghost danger" @click=${this._onDelete} ?disabled=${this._busy}>
|
||||
<ha-icon icon="mdi:delete"></ha-icon>
|
||||
</ha-button>
|
||||
<ha-button size="small" appearance="outlined" variant="danger" class="danger" @click=${this._onDelete} .disabled=${this._busy}>
|
||||
<ha-icon slot="start" icon="mdi:delete"></ha-icon>
|
||||
${t("delete", L) || "Delete"}
|
||||
</button>
|
||||
</ha-button>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
@@ -704,6 +704,7 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
||||
.actions { display: flex; gap: 8px; }
|
||||
.actions.primary-row { gap: 6px; }
|
||||
.actions.primary-row .btn { flex: 1; }
|
||||
.actions.primary-row ha-button { flex: 1; }
|
||||
/* Edit + QR are admin-tools — left-align as a group; Delete is destructive
|
||||
so it gets pushed to the far right with margin-left:auto for visual
|
||||
separation. Earlier this row was flex-end which left a strange empty
|
||||
@@ -712,9 +713,11 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
||||
padding-top: 8px; border-top: 1px solid var(--divider-color);
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.actions.secondary-row .btn.danger {
|
||||
.actions.secondary-row .btn.danger,
|
||||
.actions.secondary-row ha-button.danger {
|
||||
margin-left: auto;
|
||||
}
|
||||
.actions.secondary-row ha-button { --ha-button-font-size: 13px; }
|
||||
.btn {
|
||||
padding: 8px 12px; font-size: 14px;
|
||||
border-radius: 6px; cursor: pointer;
|
||||
|
||||
Reference in New Issue
Block a user