187 files

This commit is contained in:
Home Assistant Version Control committed 2026-10-09 11:57:31 +00:00
1 parent 28fa34d78a
commit 1e1b12d52b
187 files changed
+40899 -32654

No files matched your search

@@ -3,17 +3,17 @@
"assets": {
"ha-washdata-panel.js": {
"artifact": "ha-washdata-panel.min.js",
"source_sha256": "4d89abc002319d37b42a2644375516a515ffe0607e663326d4ad22f72efd8660",
"artifact_sha256": "6034a4a223c7192064736c11d184317c9885dad819bf74ca0306358673844e30",
"source_bytes": 948888,
"artifact_bytes": 607586
"source_sha256": "566fd32ab6d109ce5558496840b9cf9987d62130a06df2dc84fe601d2e6be009",
"artifact_sha256": "4ccb426ed8a7371e5488bbcf8cdc1dcc4465f8a8cfa99dc2c3e227270a5ac842",
"source_bytes": 990144,
"artifact_bytes": 620530
},
"ha-washdata-card.js": {
"artifact": "ha-washdata-card.min.js",
"source_sha256": "8782b8429957cfdd344efa36ed5fe4334818dbf199b48245f14eba02f1243b0b",
"artifact_sha256": "d6a42777e7102610983ff27638e9b4640cfaa7c797c668532e31c1afaf1a5d70",
"source_bytes": 56140,
"artifact_bytes": 30343
"source_sha256": "688e7370be6ccb260046c25884eb33ef7653dd97e0972fe7fd4856ffbbcc45ce",
"artifact_sha256": "5ca5a6ae8a8540b9aac5797eb9804f3b8f54e7106cad3adf92f71cd8c771697f",
"source_bytes": 59434,
"artifact_bytes": 31553
}
}
}
@@ -53,7 +53,7 @@ const BUTTON_ICONS = {
};
const BUTTON_ORDER = ["pause", "resume", "terminate", "record_start", "record_stop", "program", "open_panel"];
const ACTIVE_STATES = ["running", "paused", "user_paused", "ending", "starting", "anti_wrinkle", "rinse"];
const ACTIVE_STATES = ["running", "paused", "user_paused", "ending", "starting", "anti_wrinkle"];
// States where button.py's PauseCycleButton is available, so the card must not
// grey out Pause where the backend would accept it. An auto-detected "paused"
// is pausable: async_pause_cycle turns it into a user pause that survives the
@@ -82,8 +82,16 @@ function loadConstants(hass) {
const _panelTrans = {}; // lang -> dict
const _panelTransPending = {}; // lang -> Promise
// The card is loaded as ha-washdata-card.js?v=<cache buster>; reuse it for the
// translation files, as the panel does. Without it HA serves them with a 31-day
// cache, so an upgraded card kept last release's strings (audit UI-17).
const _CARD_VERSION = (() => {
try { return new URL(import.meta.url).searchParams.get("v") || ""; }
catch (_) { return ""; }
})();
function _panelTransUrl(lang) {
return "/" + DOMAIN + "/panel-translations/" + encodeURIComponent(lang) + ".json";
const base = "/" + DOMAIN + "/panel-translations/" + encodeURIComponent(lang) + ".json";
return _CARD_VERSION ? base + "?v=" + encodeURIComponent(_CARD_VERSION) : base;
}
async function _fetchPanelLang(lang) {
if (!lang) return null;
@@ -191,6 +199,8 @@ class WashDataCard extends HTMLElement {
f.showSparkline ? "spark" : "",
f.buttons.join("+"),
layout === "glance" ? String(this._glanceEntities().length) : "",
// _attachGestures makes the card focusable only when it has a tap action.
c.tap_action && c.tap_action.action === "none" ? "notap" : "",
].join("|");
}
@@ -292,7 +302,26 @@ class WashDataCard extends HTMLElement {
}
// ── Entity discovery ──────────────────────────────────────────────────────
// Memoised per registry object: HA replaces `hass.entities` only when the entity
// registry changes, but calls `set hass` on every state change in the house, and
// each call walked the whole registry once per card (audit UI-16). Results from
// the suffix fallback read live states, so only registry-resolved roles are kept.
_resolveRoles(primaryEntity) {
const reg = (this._hass && this._hass.entities) || null;
const key = primaryEntity || (this._cfg && this._cfg.entity) || "";
let c = this._rolesCache;
if (!c || c.reg !== reg || c.cfg !== this._cfg) {
c = this._rolesCache = { reg, cfg: this._cfg, map: new Map() };
}
if (c.map.has(key)) return c.map.get(key);
const roles = this._computeRoles(primaryEntity);
// A role the suffix fallback had to look for in live states may appear later
// without a registry change, so such a result is recomputed, never cached.
if (reg && roles._deviceId && !roles._fromStates) c.map.set(key, roles);
return roles;
}
_computeRoles(primaryEntity) {
const hass = this._hass;
const cfg = this._cfg || {};
const reg = (hass && hass.entities) || {};
@@ -333,9 +362,11 @@ class WashDataCard extends HTMLElement {
// Suffix fallback for the common case where the primary is the state sensor
// but the registry lookup came up empty (e.g. template entity, no device).
let fromStates = false;
if (!roles.time || !roles.progress || !roles.program) {
const m = /^sensor\.(.+)_state$/.exec(primary);
if (m) {
fromStates = true;
const base = "sensor." + m[1] + "_";
const st = hass && hass.states;
const guess = (suffix) => (st && st[base + suffix] ? base + suffix : null);
@@ -348,6 +379,7 @@ class WashDataCard extends HTMLElement {
roles._buttons = buttons;
roles._select = selectEntity;
roles._deviceId = deviceId;
roles._fromStates = fromStates;
return roles;
}
@@ -580,6 +612,7 @@ class WashDataCard extends HTMLElement {
_baseStyle() {
return (
":host{display:block;height:100%}" +
"[role=button]:focus-visible{outline:2px solid var(--primary-color);outline-offset:2px}" +
"ha-card{padding:0;background:var(--ha-card-background,var(--card-background-color,white));" +
"border-radius:var(--ha-card-border-radius,12px);box-shadow:var(--ha-card-box-shadow,none);" +
"overflow:hidden;cursor:pointer;height:100%;box-sizing:border-box;" +
@@ -611,7 +644,21 @@ class WashDataCard extends HTMLElement {
return this._buildTile();
}
_attachGestures(cardEl) {
_attachGestures(cardEl, keyEl = cardEl) {
// Keyboard: focusable with Enter / Space running the tap action, as HA's own
// tile card does (audit UI-17: pointer-only until 0.5.8). `keyEl` carries the
// button role: a card holding its own buttons or program selector passes a
// part without them, since a role="button" may not contain interactive controls.
const tapCfg = (this._cfg && this._cfg.tap_action) || { action: "more-info" };
if (tapCfg.action !== "none") {
keyEl.tabIndex = 0;
keyEl.setAttribute("role", "button");
keyEl.addEventListener("keydown", (ev) => {
if (ev.target !== keyEl || (ev.key !== "Enter" && ev.key !== " ")) return;
ev.preventDefault();
this._executeAction((this._cfg && this._cfg.tap_action) || { action: "more-info" });
});
}
cardEl.addEventListener("pointerdown", this._onPointerDown);
cardEl.addEventListener("pointermove", this._onPointerMove);
cardEl.addEventListener("pointerup", this._onPointerUp);
@@ -668,7 +715,8 @@ class WashDataCard extends HTMLElement {
(flags.showSparkline ? '<canvas id="spark"></canvas>' : "") +
(flags.buttons.length ? '<div class="acts" id="acts"></div>' : "") +
"</div></ha-card>";
this._attachGestures(this.shadowRoot.getElementById("card"));
const card = this.shadowRoot.getElementById("card");
this._attachGestures(card, flags.buttons.length ? card.querySelector(".top") : card);
if (flags.buttons.length) this._buildButtons(this.shadowRoot.getElementById("acts"), flags.buttons);
}
@@ -682,6 +730,9 @@ class WashDataCard extends HTMLElement {
sel.id = "prog-select";
sel.addEventListener("click", (ev) => ev.stopPropagation());
sel.addEventListener("pointerdown", (ev) => ev.stopPropagation());
// As the buttons below: a pointerup reaching the card ran tap_action, whose
// more-info dialog closed the dropdown before a program could be picked (#468).
sel.addEventListener("pointerup", (ev) => ev.stopPropagation());
sel.addEventListener("change", (ev) => {
ev.stopPropagation();
this._onProgramChange(ev.target.value);
@@ -812,7 +863,12 @@ class WashDataCard extends HTMLElement {
else if (vm.timeText) parts.push(vm.timeText);
else if (vm.pct !== null) parts.push(vm.pct + "%");
}
if (flags.showAnomaly && vm.anomaly) parts.push(this._t("card.running_long", null, "running long"));
if (flags.showAnomaly && vm.anomaly) {
// #452: a stalled cycle (halted on its standby draw) is not one running long.
parts.push(vm.anomaly.kind === "stalled"
? this._t("card.stalled", null, "Stalled")
: this._t("card.running_long", null, "running long"));
}
stateEl.textContent = parts.join(" • ");
}
@@ -872,7 +928,9 @@ class WashDataCard extends HTMLElement {
// and a zero; above it the decimal is noise.
if (vm.isRunning && vm.powerW !== null)
addChip((vm.powerW >= 100 ? Math.round(vm.powerW) : vm.powerW.toFixed(1)) + " W");
if (flags.showAnomaly && vm.anomaly) {
if (flags.showAnomaly && vm.anomaly && vm.anomaly.kind === "stalled") {
addChip(this._t("card.stalled", null, "Stalled"), true); // #452
} else if (flags.showAnomaly && vm.anomaly) {
const ratio = vm.anomaly.ratio ? " (" + Math.round((vm.anomaly.ratio - 1) * 100) + "%)" : "";
addChip(this._t("card.running_long", null, "Running long") + ratio, true);
}
+1 -1
View File
@@ -8,4 +8,4 @@
* This program comes with ABSOLUTELY NO WARRANTY. See the GNU Affero General
* Public License for details: <https://www.gnu.org/licenses/>.
*/
const b="ha-washdata-card",v="ha-washdata-card-editor",E="ha_washdata",$=500,K=250,X=10,j={state:"washer_state",program:"washer_program",time:"time_remaining",progress:"cycle_progress",power:"current_power",energy:"energy_total",phase:"current_phase"},N={pause:"pause_cycle",resume:"resume_cycle",terminate:"force_end_cycle",record_start:"record_start",record_stop:"record_stop"},W={pause:"mdi:pause",resume:"mdi:play",terminate:"mdi:stop",record_start:"mdi:record-circle-outline",record_stop:"mdi:stop-circle-outline",open_panel:"mdi:open-in-new"},D=["pause","resume","terminate","record_start","record_stop","program","open_panel"],H=["running","paused","user_paused","ending","starting","anti_wrinkle","rinse"],z=["running","starting","paused","ending"],T=["off","unknown","unavailable","idle"],U=60,R=new WeakMap;function F(d){const t=d&&d.connection;if(!t)return Promise.resolve({stateColors:{}});if(R.has(t))return R.get(t);const e=d.callWS({type:E+"/get_constants"}).then(i=>({stateColors:i&&i.state_colors||{}})).catch(()=>({stateColors:{}}));return R.set(t,e),e}const y={},A={};function G(d){return"/"+E+"/panel-translations/"+encodeURIComponent(d)+".json"}async function q(d){if(!d)return null;const t=[d],e=d.indexOf("-");e>0&&t.push(d.slice(0,e));for(const i of t)try{const s=await fetch(G(i));if(s.ok){const n=await s.json();if(n&&typeof n=="object")return n}}catch{}return null}async function S(d){if(!d||y[d])return;if(A[d]){await A[d];return}const t=q(d).then(e=>{e&&(y[d]=e)});A[d]=t,await t}class V extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this._cfg=null,this._hass=null,this._builtSig=null,this._constantsLoaded=!1,this._langEnsured="",this._constants=null,this._spark=[],this._sparkState=null,this._holdTimer=null,this._holdTriggered=!1,this._tapTimer=null,this._lastTapTime=0,this._pointerStart=null,this._pointerCanceled=!1,this._onPointerDown=this._onPointerDown.bind(this),this._onPointerMove=this._onPointerMove.bind(this),this._onPointerUp=this._onPointerUp.bind(this),this._onPointerCancel=this._onPointerCancel.bind(this)}static getStubConfig(){return{entity:"sensor.washing_machine_state",layout:"tile",tap_action:{action:"more-info"},hold_action:{action:"none"},double_tap_action:{action:"none"}}}static getConfigElement(){return document.createElement(v)}setConfig(t){if(!t)throw new Error("Invalid configuration");const e=t.layout||"tile";if(e!=="glance"&&!t.entity)throw new Error("Please define an entity");if(e==="glance"&&!(Array.isArray(t.entities)&&t.entities.length)&&!t.entity)throw new Error("Please define entities for the glance layout");this._cfg={...t,layout:e},this._builtSig!==this._structureSig()&&(this._builtSig=null,this.shadowRoot&&(this.shadowRoot.innerHTML="")),this._render()}_structureSig(){const t=this._cfg||{},e=this._flags(),i=t.layout||"tile";return[i,e.showBar?"bar":"",e.showSparkline?"spark":"",e.buttons.join("+"),i==="glance"?String(this._glanceEntities().length):""].join("|")}set hass(t){this._hass=t,this._ensureResources(),this._render()}_detailRows(){const t=this._flags();let e=2;return t.buttons.length&&(e+=1),t.showSparkline&&(e+=1),e}getCardSize(){const t=this._cfg&&this._cfg.layout;return t==="detail"?this._detailRows():t==="glance"&&this._glanceEntities().length||1}getGridOptions(){const t=this._cfg&&this._cfg.layout;if(t==="detail"){const e=this._detailRows();return{rows:e,min_rows:e,columns:12,min_columns:6}}return t==="glance"?{rows:Math.max(1,this._glanceEntities().length),min_rows:1,columns:12,min_columns:6}:{rows:1,min_rows:1,columns:6,min_columns:3}}disconnectedCallback(){this._clearHoldTimer(),this._tapTimer&&(window.clearTimeout(this._tapTimer),this._tapTimer=null)}_lang(){const t=this._hass&&this._hass.locale&&this._hass.locale.language||this._hass&&this._hass.language||"en";return typeof t=="string"&&t?t:"en"}_tLookup(t,e){const i=y[e];if(!i)return null;const s=t.split(".").reduce((n,o)=>n&&n[o]!==void 0?n[o]:null,i);return s&&typeof s=="string"?s:null}_t(t,e,i){const s=this._lang();let n=s&&this._tLookup(t,s)||this._tLookup(t,"en")||i||t;if(e)for(const[o,a]of Object.entries(e))n=n.replace(new RegExp("\\{"+o+"\\}","g"),String(a));return n}_ensureResources(){if(!this._hass)return;const t=this._lang(),e=!this._constantsLoaded,i=t!==this._langEnsured;if(!e&&!i)return;const s=[];e&&(this._constantsLoaded=!0,s.push(F(this._hass).then(n=>{this._constants=n}),S("en"))),i&&(this._langEnsured=t,t&&t!=="en"&&s.push(S(t))),Promise.all(s).then(()=>this._update()).catch(()=>{})}_resolveRoles(t){const e=this._hass,i=this._cfg||{},s=e&&e.entities||{},n=t||i.entity,o=s[n],a=i.device_id||o&&o.device_id||null,r={state:n},l={};let h=null;if(a)for(const[c,u]of Object.entries(s)){if(!u||u.device_id!==a||u.platform&&u.platform!==E)continue;const p=u.translation_key;if(c.startsWith("sensor."))for(const[f,g]of Object.entries(j))p===g&&!r[f]&&(r[f]=c);for(const[f,g]of Object.entries(N))p===g&&!l[f]&&(l[f]=c);!h&&c.startsWith("select.")&&(h=c)}if(i.program_entity&&(Line truncated
const b="ha-washdata-card",v="ha-washdata-card-editor",E="ha_washdata",K=500,X=250,Q=10,N={state:"washer_state",program:"washer_program",time:"time_remaining",progress:"cycle_progress",power:"current_power",energy:"energy_total",phase:"current_phase"},W={pause:"pause_cycle",resume:"resume_cycle",terminate:"force_end_cycle",record_start:"record_start",record_stop:"record_stop"},H={pause:"mdi:pause",resume:"mdi:play",terminate:"mdi:stop",record_start:"mdi:record-circle-outline",record_stop:"mdi:stop-circle-outline",open_panel:"mdi:open-in-new"},D=["pause","resume","terminate","record_start","record_stop","program","open_panel"],z=["running","paused","user_paused","ending","starting","anti_wrinkle"],U=["running","starting","paused","ending"],S=["off","unknown","unavailable","idle"],F=60,R=new WeakMap;function G(d){const t=d&&d.connection;if(!t)return Promise.resolve({stateColors:{}});if(R.has(t))return R.get(t);const e=d.callWS({type:E+"/get_constants"}).then(s=>({stateColors:s&&s.state_colors||{}})).catch(()=>({stateColors:{}}));return R.set(t,e),e}const y={},A={},O=(()=>{try{return new URL(import.meta.url).searchParams.get("v")||""}catch{return""}})();function q(d){const t="/"+E+"/panel-translations/"+encodeURIComponent(d)+".json";return O?t+"?v="+encodeURIComponent(O):t}async function V(d){if(!d)return null;const t=[d],e=d.indexOf("-");e>0&&t.push(d.slice(0,e));for(const s of t)try{const i=await fetch(q(s));if(i.ok){const n=await i.json();if(n&&typeof n=="object")return n}}catch{}return null}async function T(d){if(!d||y[d])return;if(A[d]){await A[d];return}const t=V(d).then(e=>{e&&(y[d]=e)});A[d]=t,await t}class Y extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this._cfg=null,this._hass=null,this._builtSig=null,this._constantsLoaded=!1,this._langEnsured="",this._constants=null,this._spark=[],this._sparkState=null,this._holdTimer=null,this._holdTriggered=!1,this._tapTimer=null,this._lastTapTime=0,this._pointerStart=null,this._pointerCanceled=!1,this._onPointerDown=this._onPointerDown.bind(this),this._onPointerMove=this._onPointerMove.bind(this),this._onPointerUp=this._onPointerUp.bind(this),this._onPointerCancel=this._onPointerCancel.bind(this)}static getStubConfig(){return{entity:"sensor.washing_machine_state",layout:"tile",tap_action:{action:"more-info"},hold_action:{action:"none"},double_tap_action:{action:"none"}}}static getConfigElement(){return document.createElement(v)}setConfig(t){if(!t)throw new Error("Invalid configuration");const e=t.layout||"tile";if(e!=="glance"&&!t.entity)throw new Error("Please define an entity");if(e==="glance"&&!(Array.isArray(t.entities)&&t.entities.length)&&!t.entity)throw new Error("Please define entities for the glance layout");this._cfg={...t,layout:e},this._builtSig!==this._structureSig()&&(this._builtSig=null,this.shadowRoot&&(this.shadowRoot.innerHTML="")),this._render()}_structureSig(){const t=this._cfg||{},e=this._flags(),s=t.layout||"tile";return[s,e.showBar?"bar":"",e.showSparkline?"spark":"",e.buttons.join("+"),s==="glance"?String(this._glanceEntities().length):"",t.tap_action&&t.tap_action.action==="none"?"notap":""].join("|")}set hass(t){this._hass=t,this._ensureResources(),this._render()}_detailRows(){const t=this._flags();let e=2;return t.buttons.length&&(e+=1),t.showSparkline&&(e+=1),e}getCardSize(){const t=this._cfg&&this._cfg.layout;return t==="detail"?this._detailRows():t==="glance"&&this._glanceEntities().length||1}getGridOptions(){const t=this._cfg&&this._cfg.layout;if(t==="detail"){const e=this._detailRows();return{rows:e,min_rows:e,columns:12,min_columns:6}}return t==="glance"?{rows:Math.max(1,this._glanceEntities().length),min_rows:1,columns:12,min_columns:6}:{rows:1,min_rows:1,columns:6,min_columns:3}}disconnectedCallback(){this._clearHoldTimer(),this._tapTimer&&(window.clearTimeout(this._tapTimer),this._tapTimer=null)}_lang(){const t=this._hass&&this._hass.locale&&this._hass.locale.language||this._hass&&this._hass.language||"en";return typeof t=="string"&&t?t:"en"}_tLookup(t,e){const s=y[e];if(!s)return null;const i=t.split(".").reduce((n,o)=>n&&n[o]!==void 0?n[o]:null,s);return i&&typeof i=="string"?i:null}_t(t,e,s){const i=this._lang();let n=i&&this._tLookup(t,i)||this._tLookup(t,"en")||s||t;if(e)for(const[o,a]of Object.entries(e))n=n.replace(new RegExp("\\{"+o+"\\}","g"),String(a));return n}_ensureResources(){if(!this._hass)return;const t=this._lang(),e=!this._constantsLoaded,s=t!==this._langEnsured;if(!e&&!s)return;const i=[];e&&(this._constantsLoaded=!0,i.push(G(this._hass).then(n=>{this._constants=n}),T("en"))),s&&(this._langEnsured=t,t&&t!=="en"&&i.push(T(t))),Promise.all(i).then(()=>this._update()).catch(()=>{})}_resolveRoles(t){const e=this._hass&&this._hass.entities||null,s=t||this._cfg&&this._cfg.entity||"";let i=this._rolesCache;if((!i||i.reg!==e||i.cfg!==this._cfg)&&(i=this._rolesCache={reg:e,cfg:this._cfg,map:new Map}),i.map.has(s))return i.map.get(s);const n=this._computeRoles(t);return e&&n._deviceId&&!nLine truncated
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+158 -180
View File
@@ -1,4 +1,4 @@
// AUTO-GENERATED — do not edit; run devtools/generate_ws_types.py
// AUTO-GENERATED - do not edit; run devtools/generate_ws_types.py
// WashData WebSocket API type contract (Group H1).
//
// Response payloads for every `ha_washdata/*` WebSocket command, plus the
@@ -53,9 +53,11 @@ export interface DeviceInfo {
current_program: string | null;
time_remaining_s: number | null;
total_duration_s: number | null;
expected_duration_s: number | null;
current_power_w: number | null;
cycle_progress_pct: number | null;
envelope_position: number | null;
match_uncertainty: MatchUncertainty | null;
suggestions_count: number;
suggestion_keys: string[];
feedback_count: number;
@@ -73,26 +75,6 @@ export interface DismissAllFeedbacksResponse {
dismissed: number;
}
export interface DtwScores {
l1_score: number;
ddtw_score: number;
ensemble_score: number;
blend_weight: number;
blended_score: number;
}
export interface DtwStage2Scores {
correlation: number;
mae_score: number;
score: number;
}
export interface DtwStage4Scores {
duration_agreement: number;
energy_agreement: number;
final_score: number;
}
export interface ExportConfigResponse {
json_data: string;
}
@@ -103,7 +85,6 @@ export interface GetConstantsResponse {
device_types: Record<string, unknown>[];
state_colors: Record<string, unknown>;
ml_lab_enabled: boolean;
ml_suggestions_enabled: boolean;
ml_training_available: boolean;
PROFILE_MIN_WARMUP_CYCLES: unknown;
store_online_available: boolean;
@@ -113,12 +94,25 @@ export interface GetConstantsResponse {
pg_match_defaults: Record<string, unknown>;
}
export interface GetCycleContextResponse {
cycle_id: string;
available: boolean;
reason: string | null;
entity_id: string | null;
before_s: number;
after_s: number;
trace_end_s: number;
after_end_s: number;
before: ((number | null)[])[];
after: ((number | null)[])[];
}
export interface GetCyclePowerDataResponse {
cycle_id?: string;
samples?: number[][];
sample_count?: number;
decimated?: boolean;
full_duration_s?: number;
cycle_id: string;
samples: number[][];
sample_count: number;
decimated: boolean;
full_duration_s: number;
start_time?: string | null;
end_time?: string | null;
duration?: number | null;
@@ -141,6 +135,8 @@ export interface GetDeviceCyclesResponse {
backfill_cycles: Record<string, unknown>[];
total: number;
has_more: boolean;
imported_total: number;
imported_has_more: boolean;
}
export interface GetDevicesResponse {
@@ -149,20 +145,7 @@ export interface GetDevicesResponse {
export interface GetDiagnosticsResponse {
stats: Record<string, unknown>;
}
export interface GetDtwDebugResponse {
cycle_id: unknown;
profile_name: string;
grid_n: number;
cycle_duration_s: number;
profile_duration_s: number;
cycle_trace: number[][];
profile_trace: number[][];
stage2: DtwStage2Scores;
dtw: DtwScores;
stage4: DtwStage4Scores;
warp_path: number[][];
import_undo: Record<string, unknown> | null;
}
export interface GetExportInventoryResponse {
@@ -179,11 +162,14 @@ export interface GetLogsResponse {
export interface GetMaintenanceLogResponse {
log: Record<string, unknown>[];
due: unknown;
due: string[];
event_types: string[];
reminders: Record<string, unknown>;
reminders: Record<string, number>;
cycles_since: Record<string, number>;
custom_tasks: MaintenanceTask[];
status: MaintenanceStatusRow[];
lifetime_cycle_count: number;
limits: MaintenanceLimits;
}
export interface GetMatchDebugResponse {
@@ -196,12 +182,10 @@ export interface GetMlComparisonResponse {
enabled?: boolean;
error?: string;
cycles?: Record<string, unknown>[];
settings_comparison?: Record<string, unknown>;
cycle_count?: number;
evaluated_count?: number;
model_source?: Record<string, unknown>;
profile_stats?: Record<string, unknown>;
ml_suggestions_enabled?: boolean;
}
export interface GetMlTrainingStatusResponse {
@@ -214,7 +198,7 @@ export interface GetMlTrainingStatusResponse {
interval_days: number;
hour: number;
on_device_models: Record<string, unknown>;
matching: Record<string, unknown>;
last_run: Record<string, unknown>;
}
export interface GetOptionsResponse {
@@ -223,10 +207,10 @@ export interface GetOptionsResponse {
}
export interface GetPanelConfigResponse {
panel?: Record<string, unknown>;
is_admin?: boolean;
user?: Record<string, unknown>;
prefs?: Record<string, unknown>;
panel: Record<string, unknown>;
is_admin: boolean;
user: Record<string, unknown>;
prefs: Record<string, unknown>;
rbac?: Record<string, unknown>;
users?: Record<string, unknown>[];
}
@@ -242,16 +226,14 @@ export interface GetPlaygroundSettingsResponse {
publishable: string[];
preset_limit: number;
classic_suggestions: Record<string, unknown>;
ml_suggestions: Record<string, unknown> | null;
ml_suggestions_enabled: boolean;
}
export interface GetPowerHistoryResponse {
cycle_active?: boolean;
cycle_elapsed_s?: number;
live?: number[][];
raw?: number[][];
restart_gaps?: unknown[];
cycle_active: boolean;
cycle_elapsed_s: number;
live: number[][];
raw: number[][];
restart_gaps: unknown[];
cycle_start_iso?: string;
}
@@ -266,7 +248,6 @@ export interface GetProfileEnvelopeResponse {
export interface GetProfileGroupsResponse {
groups: ProfileGroupInfo[];
min_cohesion: number;
suggestions: Record<string, unknown>[];
}
export interface GetProfilePhasesResponse {
@@ -280,10 +261,11 @@ export interface GetProfilesResponse {
coverage_gaps: Record<string, unknown>;
profile_advisories: Record<string, unknown>[];
profile_terminal: Record<string, unknown>;
profile_matcher_counts: Record<string, number>;
}
export interface GetRecordingStateResponse {
state?: string;
state: string;
duration_s?: number;
sample_count?: number;
start_time?: string | null;
@@ -338,13 +320,53 @@ export interface HistoryImportRecorderResponse {
truncated: boolean;
}
export interface ImportConfigResponse {
success: boolean;
restore_point_saved: boolean;
}
export interface ImportConfigSelectiveResponse {
success: boolean;
summary: Record<string, unknown>;
}
export interface ListTasksResponse {
tasks: TaskSnapshot[];
export interface MaintenanceLimits {
tasks_max: number;
name_max: number;
cycles_max: number;
days_max: number;
}
export interface MaintenanceStatusRow {
id: string;
custom: boolean;
name: string | null;
cycles_interval: number;
days_interval: number;
cycles_since: number;
days_since: number | null;
due: boolean;
}
export interface MaintenanceTask {
id: string;
name: string;
cycles: number;
days: number;
since: string;
since_cycle_count: number;
}
export interface MaintenanceTaskResponse {
success: boolean;
task: MaintenanceTask;
}
export interface MatchUncertainty {
top: string;
runner_up: string | null;
margin: number | null;
sure_pct: number;
}
export interface OkResponse {
@@ -380,43 +402,6 @@ export interface ProfileGroupInfo {
cohesive: boolean;
}
export interface RunPlaygroundCycleDetailResponse {
cycle_id?: unknown;
label?: string | null;
duration_s?: number | null;
config_summary?: Record<string, unknown>;
series?: Record<string, unknown>[];
events?: Record<string, unknown>[];
alerts?: Record<string, unknown>[];
outcome?: Record<string, unknown>;
error?: string;
}
export interface RunPlaygroundHistoryResponse {
rows?: Record<string, unknown>[];
summary?: Record<string, unknown>;
baseline_rows?: Record<string, unknown>[];
baseline_summary?: Record<string, unknown>;
diff?: Record<string, string[]>;
}
export interface RunPlaygroundSweepResponse {
param?: string;
objective?: string;
points?: Record<string, unknown>[];
current_value?: unknown;
best_value?: unknown;
best_metric?: number | null;
param_x?: string;
param_y?: string;
x_values?: number[];
y_values?: number[];
grid?: unknown[][];
best?: Record<string, unknown>;
current?: Record<string, unknown>;
error?: string;
}
export interface RunSuggestionAnalysisResponse {
success?: boolean;
count?: number;
@@ -454,16 +439,21 @@ export interface StoreConfirmResponse {
export interface StoreDeviceProfilesResponse {
device_id?: string;
items?: unknown[];
error?: string;
disabled?: boolean;
}
export interface StoreDownloadDeviceResponse {
profiles_adopted?: number;
cycles_imported?: number;
cycles_skipped?: number;
phases_applied?: number;
settings?: Record<string, unknown>;
settings_applied?: number;
cancelled?: boolean;
partial?: boolean;
failed_profiles?: number;
error?: string;
disabled?: boolean;
}
export interface StoreImportResponse {
@@ -475,6 +465,7 @@ export interface StoreImportResponse {
export interface StoreItemsResponse {
items?: unknown[];
error?: string;
disabled?: boolean;
}
@@ -491,12 +482,6 @@ export interface StorePrefsResponse {
disabled?: boolean;
}
export interface StoreQualityResponse {
avg?: number | null;
count?: number;
disabled?: boolean;
}
export interface StoreRefreshCatalogResponse {
ok?: boolean;
disabled?: boolean;
@@ -563,6 +548,11 @@ export interface TaskSnapshot {
result?: unknown;
}
export interface UndoImportResponse {
success: boolean;
summary: Record<string, unknown>;
}
// ── Request parameters ─────────────────────────────────────────────────────
export interface GetDevicesRequest {
@@ -572,6 +562,7 @@ export interface GetDeviceCyclesRequest {
entry_id: string;
limit?: number;
offset?: number;
imported_offset?: number;
}
export interface GetOptionsRequest {
@@ -667,6 +658,26 @@ export interface DeleteMaintenanceEventRequest {
event_id: string;
}
export interface AddMaintenanceTaskRequest {
entry_id: string;
name: string;
cycles?: number;
days?: number;
}
export interface UpdateMaintenanceTaskRequest {
entry_id: string;
task_id: string;
name?: string;
cycles?: number;
days?: number;
}
export interface DeleteMaintenanceTaskRequest {
entry_id: string;
task_id: string;
}
export interface SetLifetimeCycleCountRequest {
entry_id: string;
count: number;
@@ -802,6 +813,10 @@ export interface ImportConfigSelectiveRequest {
apply_settings?: boolean;
}
export interface UndoImportRequest {
entry_id: string;
}
export interface GetConstantsRequest {
}
@@ -833,6 +848,13 @@ export interface GetCyclePowerDataRequest {
cycle_id: string;
}
export interface GetCycleContextRequest {
entry_id: string;
cycle_id: string;
before_s?: number;
after_s?: number;
}
export interface TrimCycleRequest {
entry_id: string;
cycle_id: string;
@@ -914,10 +936,6 @@ export interface TriggerMlTrainingRequest {
entry_id: string;
}
export interface RevertMatchingConfigRequest {
entry_id: string;
}
export interface RevertMlModelsRequest {
entry_id: string;
}
@@ -943,36 +961,6 @@ export interface TerminateCycleRequest {
entry_id: string;
}
export interface RunPlaygroundCycleDetailRequest {
entry_id: string;
cycle_id: string;
settings_override?: Record<string, unknown>;
}
export interface RunPlaygroundHistoryRequest {
entry_id: string;
cycle_ids?: string[];
settings_override?: Record<string, unknown>;
concurrency?: number;
}
export interface RunPlaygroundSweepRequest {
entry_id: string;
param: string;
values: number[];
objective: string;
cycle_ids?: string[];
concurrency?: number;
param_y?: string;
values_y?: number[];
}
export interface GetDtwDebugRequest {
entry_id: string;
cycle_id: string;
profile_name?: string | null;
}
export interface GetPlaygroundSettingsRequest {
entry_id: string;
include_suggestions?: boolean;
@@ -989,10 +977,6 @@ export interface DeletePlaygroundPresetRequest {
name: string;
}
export interface ListTasksRequest {
entry_id?: string | null;
}
export interface SubscribeTasksRequest {
entry_id?: string | null;
}
@@ -1009,6 +993,7 @@ export interface StartPlaygroundHistoryRequest {
entry_id: string;
cycle_ids?: string[];
settings_override?: Record<string, unknown>;
count?: number;
}
export interface StartPlaygroundSweepRequest {
@@ -1016,16 +1001,14 @@ export interface StartPlaygroundSweepRequest {
param: string;
values: number[];
objective: string;
param_y?: string | null;
values_y?: number[];
cycle_ids?: string[];
count?: number;
}
export interface StartPlaygroundCycleDetailRequest {
entry_id: string;
cycle_id: string;
settings_override?: Record<string, unknown>;
stress_tail?: boolean;
stress_idle_w?: number | null;
}
export interface HistoryImportBeginRequest {
@@ -1095,11 +1078,17 @@ export interface StoreGetCyclesRequest {
profile_id: string;
}
export interface StoreGetDeviceQualityRequest {
export interface StoreConfirmDeviceRequest {
entry_id: string;
device_id: string;
}
export interface StoreRateDeviceRequest {
entry_id: string;
device_id: string;
rating: number;
}
export interface StoreGetDeviceProfilesRequest {
entry_id: string;
brand: string;
@@ -1118,17 +1107,6 @@ export interface StoreRefreshCatalogRequest {
entry_id: string;
}
export interface StoreConfirmDeviceRequest {
entry_id: string;
device_id: string;
}
export interface StoreRateDeviceRequest {
entry_id: string;
device_id: string;
rating: number;
}
export interface StoreSetOnlineRequest {
entry_id: string;
enabled: boolean;
@@ -1193,6 +1171,9 @@ export interface WashDataWsRequests {
"ha_washdata/get_maintenance_log": GetMaintenanceLogRequest;
"ha_washdata/add_maintenance_event": AddMaintenanceEventRequest;
"ha_washdata/delete_maintenance_event": DeleteMaintenanceEventRequest;
"ha_washdata/add_maintenance_task": AddMaintenanceTaskRequest;
"ha_washdata/update_maintenance_task": UpdateMaintenanceTaskRequest;
"ha_washdata/delete_maintenance_task": DeleteMaintenanceTaskRequest;
"ha_washdata/set_lifetime_cycle_count": SetLifetimeCycleCountRequest;
"ha_washdata/label_cycle": LabelCycleRequest;
"ha_washdata/delete_cycle": DeleteCycleRequest;
@@ -1219,6 +1200,7 @@ export interface WashDataWsRequests {
"ha_washdata/analyze_import": AnalyzeImportRequest;
"ha_washdata/export_config_selective": ExportConfigSelectiveRequest;
"ha_washdata/import_config_selective": ImportConfigSelectiveRequest;
"ha_washdata/undo_import": UndoImportRequest;
"ha_washdata/get_constants": GetConstantsRequest;
"ha_washdata/get_suggestions": GetSuggestionsRequest;
"ha_washdata/apply_suggestions": ApplySuggestionsRequest;
@@ -1226,6 +1208,7 @@ export interface WashDataWsRequests {
"ha_washdata/set_suggestion_lock": SetSuggestionLockRequest;
"ha_washdata/run_suggestion_analysis": RunSuggestionAnalysisRequest;
"ha_washdata/get_cycle_power_data": GetCyclePowerDataRequest;
"ha_washdata/get_cycle_context": GetCycleContextRequest;
"ha_washdata/trim_cycle": TrimCycleRequest;
"ha_washdata/analyze_split": AnalyzeSplitRequest;
"ha_washdata/apply_split": ApplySplitRequest;
@@ -1242,20 +1225,14 @@ export interface WashDataWsRequests {
"ha_washdata/get_ml_comparison": GetMlComparisonRequest;
"ha_washdata/get_ml_training_status": GetMlTrainingStatusRequest;
"ha_washdata/trigger_ml_training": TriggerMlTrainingRequest;
"ha_washdata/revert_matching_config": RevertMatchingConfigRequest;
"ha_washdata/revert_ml_models": RevertMlModelsRequest;
"ha_washdata/set_ml_review": SetMlReviewRequest;
"ha_washdata/pause_cycle": PauseCycleRequest;
"ha_washdata/resume_cycle": ResumeCycleRequest;
"ha_washdata/terminate_cycle": TerminateCycleRequest;
"ha_washdata/run_playground_cycle_detail": RunPlaygroundCycleDetailRequest;
"ha_washdata/run_playground_history": RunPlaygroundHistoryRequest;
"ha_washdata/run_playground_sweep": RunPlaygroundSweepRequest;
"ha_washdata/get_dtw_debug": GetDtwDebugRequest;
"ha_washdata/get_playground_settings": GetPlaygroundSettingsRequest;
"ha_washdata/save_playground_preset": SavePlaygroundPresetRequest;
"ha_washdata/delete_playground_preset": DeletePlaygroundPresetRequest;
"ha_washdata/list_tasks": ListTasksRequest;
"ha_washdata/subscribe_tasks": SubscribeTasksRequest;
"ha_washdata/cancel_task": CancelTaskRequest;
"ha_washdata/get_task_result": GetTaskResultRequest;
@@ -1274,12 +1251,11 @@ export interface WashDataWsRequests {
"ha_washdata/store_list_brands": StoreListBrandsRequest;
"ha_washdata/store_get_profiles": StoreGetProfilesRequest;
"ha_washdata/store_get_cycles": StoreGetCyclesRequest;
"ha_washdata/store_get_device_quality": StoreGetDeviceQualityRequest;
"ha_washdata/store_confirm_device": StoreConfirmDeviceRequest;
"ha_washdata/store_rate_device": StoreRateDeviceRequest;
"ha_washdata/store_get_device_profiles": StoreGetDeviceProfilesRequest;
"ha_washdata/store_get_catalog_entry": StoreGetCatalogEntryRequest;
"ha_washdata/store_refresh_catalog": StoreRefreshCatalogRequest;
"ha_washdata/store_confirm_device": StoreConfirmDeviceRequest;
"ha_washdata/store_rate_device": StoreRateDeviceRequest;
"ha_washdata/store_set_online": StoreSetOnlineRequest;
"ha_washdata/store_set_prefs": StoreSetPrefsRequest;
"ha_washdata/store_import_cycle": StoreImportCycleRequest;
@@ -1310,10 +1286,13 @@ export interface WashDataWsResponses {
"ha_washdata/get_maintenance_log": GetMaintenanceLogResponse;
"ha_washdata/add_maintenance_event": AddMaintenanceEventResponse;
"ha_washdata/delete_maintenance_event": SuccessResponse;
"ha_washdata/add_maintenance_task": MaintenanceTaskResponse;
"ha_washdata/update_maintenance_task": MaintenanceTaskResponse;
"ha_washdata/delete_maintenance_task": SuccessResponse;
"ha_washdata/set_lifetime_cycle_count": SetLifetimeCycleCountResponse;
"ha_washdata/label_cycle": SuccessResponse;
"ha_washdata/delete_cycle": SuccessResponse;
"ha_washdata/auto_label_cycles": SuccessResponse;
"ha_washdata/auto_label_cycles": StartTaskResponse;
"ha_washdata/get_phase_catalog": GetPhaseCatalogResponse;
"ha_washdata/create_phase": SuccessResponse;
"ha_washdata/update_phase": SuccessResponse;
@@ -1331,11 +1310,12 @@ export interface WashDataWsResponses {
"ha_washdata/clear_debug_data": ClearDebugDataResponse;
"ha_washdata/wipe_history": SuccessResponse;
"ha_washdata/export_config": ExportConfigResponse;
"ha_washdata/import_config": SuccessResponse;
"ha_washdata/import_config": ImportConfigResponse;
"ha_washdata/get_export_inventory": GetExportInventoryResponse;
"ha_washdata/analyze_import": AnalyzeImportResponse;
"ha_washdata/export_config_selective": ExportConfigResponse;
"ha_washdata/import_config_selective": ImportConfigSelectiveResponse;
"ha_washdata/undo_import": UndoImportResponse;
"ha_washdata/get_constants": GetConstantsResponse;
"ha_washdata/get_suggestions": GetSuggestionsResponse;
"ha_washdata/apply_suggestions": ApplySuggestionsResponse;
@@ -1343,6 +1323,7 @@ export interface WashDataWsResponses {
"ha_washdata/set_suggestion_lock": SetSuggestionLockResponse;
"ha_washdata/run_suggestion_analysis": RunSuggestionAnalysisResponse;
"ha_washdata/get_cycle_power_data": GetCyclePowerDataResponse;
"ha_washdata/get_cycle_context": GetCycleContextResponse;
"ha_washdata/trim_cycle": StartTaskResponse;
"ha_washdata/analyze_split": AnalyzeSplitResponse;
"ha_washdata/apply_split": StartTaskResponse;
@@ -1359,20 +1340,14 @@ export interface WashDataWsResponses {
"ha_washdata/get_ml_comparison": GetMlComparisonResponse;
"ha_washdata/get_ml_training_status": GetMlTrainingStatusResponse;
"ha_washdata/trigger_ml_training": StartTaskResponse;
"ha_washdata/revert_matching_config": SuccessResponse;
"ha_washdata/revert_ml_models": SuccessResponse;
"ha_washdata/set_ml_review": SuccessResponse;
"ha_washdata/pause_cycle": OkResponse;
"ha_washdata/resume_cycle": OkResponse;
"ha_washdata/terminate_cycle": OkResponse;
"ha_washdata/run_playground_cycle_detail": RunPlaygroundCycleDetailResponse;
"ha_washdata/run_playground_history": RunPlaygroundHistoryResponse;
"ha_washdata/run_playground_sweep": RunPlaygroundSweepResponse;
"ha_washdata/get_dtw_debug": GetDtwDebugResponse;
"ha_washdata/get_playground_settings": GetPlaygroundSettingsResponse;
"ha_washdata/save_playground_preset": PlaygroundPresetsResponse;
"ha_washdata/delete_playground_preset": PlaygroundPresetsResponse;
"ha_washdata/list_tasks": ListTasksResponse;
"ha_washdata/subscribe_tasks": SubscribeTasksResponse;
"ha_washdata/cancel_task": CancelTaskResponse;
"ha_washdata/get_task_result": TaskSnapshot;
@@ -1390,18 +1365,21 @@ export interface WashDataWsResponses {
"ha_washdata/store_search_devices": StoreItemsResponse;
"ha_washdata/store_get_profiles": StoreItemsResponse;
"ha_washdata/store_get_cycles": StoreItemsResponse;
"ha_washdata/store_confirm_device": StoreConfirmResponse;
"ha_washdata/store_rate_device": StoreOnlineResponse;
"ha_washdata/store_import_cycle": StoreImportResponse;
"ha_washdata/store_upload_cycle": StoreUploadResponse;
"ha_washdata/store_list_brands": StoreItemsResponse;
"ha_washdata/store_get_device_quality": StoreQualityResponse;
"ha_washdata/store_confirm_device": StoreConfirmResponse;
"ha_washdata/store_rate_device": StoreOnlineResponse;
"ha_washdata/store_set_online": StoreOnlineResponse;
"ha_washdata/store_set_prefs": StorePrefsResponse;
"ha_washdata/store_get_device_profiles": StoreDeviceProfilesResponse;
"ha_washdata/store_get_catalog_entry": StoreCatalogEntryResponse;
"ha_washdata/store_refresh_catalog": StoreRefreshCatalogResponse;
"ha_washdata/store_upload_device": StoreUploadDeviceResponse;
"ha_washdata/store_download_device": StoreDownloadDeviceResponse;
"ha_washdata/store_download_device": StartTaskResponse;
"ha_washdata/get_shareable_cycles": GetShareableCyclesResponse;
}
export interface WashDataTaskResults {
"store_download": StoreDownloadDeviceResponse;
}