Releases: 3dg1luk43/ha_washdata
v0.4.4.1
0.4.4.1 - 2026-04-29
✨ Features
- New Device Type: Oven (#137): Added support for electric ovens with defaults tuned for the heating profile of a thermostat-regulated cavity (4-hour active timeout to cover slow roasts and pyrolytic self-clean, 10-minute off-delay and 15-minute min-off-gap to bridge thermostat-driven silences without splitting one bake into multiple cycles, 0.5 Wh start-energy gate to filter incidental fan/light draws, 5.0 % progress-smoothing tolerance for the bistable on/off swings during temperature regulation, 10-minute completion threshold). Includes five built-in phases: Pre-Heat, Heating, Maintaining Temp, Cool Down, and Pyrolytic Clean. Selectable in the device-type dropdown with a
mdi:stoveicon. Issue and feature-request templates updated to list the new option.
🐛 Bug Fixes
- Issue Automation: Comment Responses No Longer Trigger Auto-Close: The issue-automation pipeline closed reports as "no response in 5 days" even when the reporter had answered. The validator only re-checked on issue-body edits, so reporters who replied via a comment (often with a debug log attached) went undetected; the daily auto-closer then closed the issue purely on the age of the bot's first flag. The validator now also fires on comments from the issue reporter and swaps the
more info requiredlabel forawaiting maintainer review, surfacing the response to the maintainer immediately. The auto-closer additionally checks for reporter comments since the bot flag as a backstop, so a stale flag never trumps an actual reply. - Dishwasher Pump-Out Registers as a Ghost Cycle (#43): On dishwashers with passive drying and an end-of-cycle drain pump, the integration was closing the cycle ~12 minutes before the real pump-out and then registering the pump-out itself as a brand-new "starting → running → ending" sequence visible in the UI. Root cause: a brief mid-cycle drain wind-down at ~50% of the expected duration set
_end_spike_seen=True, which pre-armed Smart Termination so it fired at 99% of expected duration before the real pump-out arrived. After Smart Termination closed the cycle, the pump-out (a 16–20 W spike for ~1 min) was then 12 min outside the 600 s suspicious-window for ghost suppression on devices whose previous cycle had finished slightly earlier (e.g. a longer-trained profile), so it slipped through unsuppressed and started a fresh detection. Fix: the end-spike marker is now only armed when the spike occurs at ≥ 85% of the expected duration (mid-cycle drain spikes are ignored for end-spike tracking but still keep the cycle in ENDING via the existinglong_ending_tailpath). Additionally, between 85% expected and the smart-termination wait window (expected + 300 s), the dishwasher passive drying protection now defers the cycle finish whenever no end spike has been seen yet — so the pump-out gets folded into the original cycle instead of being treated as a new cycle. Verified against a real diag-export power trace from issue #43: pre-fix → 1 cycle (233 min) + 1 ghost cycle; post-fix → 1 cycle (234.3 min) + 0 ghost cycles. No behavioural change for non-dishwasher devices, for unmatched cycles (no profile), or for dishwashers whose end spike fires within the last 15% of expected duration. - Translation Placeholder Mismatch in
notify_finish_message(#219): Home Assistant logged a startup validation error for all non-English locales becauseen.jsondescribed the finish notification format with only three placeholders ({device},{duration},{program}), while the German, Czech, and Italian translations already listed all five ({energy_kwh}and{cost}were added in v0.4.4 but the English source string was only partially updated). HA validates every localization against the English reference, so any language whose description matched the updated set was flagged as inconsistent. The English source string is now corrected to include all five placeholders, and the description in all 57 remaining language files has been updated accordingly so placeholder sets are consistent across all locales.
v0.4.4
0.4.4 - 2026-04-27
✨ Features
- Delayed Start Detection (#193): When enabled under Advanced Settings, WashData now detects machines programmed with a delayed start. A brief low-power drain spike (e.g. the initial drain-check a washing machine performs before sleeping) followed by sustained standby power is recognised as the machine waiting to begin its cycle. The state sensor transitions to "Waiting to Start" (
delay_wait) during the delay period — no program time, progress, or ETA is tracked until the real cycle begins. Configurable thresholds: Drain Spike Min/Max Power (defaults 10 W / 80 W), Max Drain Spike Duration (60 s), and a Max Wait Time safety timeout (8 h, after which the state resets to Off). The feature is disabled by default and requiresstart_threshold_wto be set above the drain spike power level to function correctly. - Wall-Clock Time in Trim Cycle Editor (#212): The Trim Cycle graph now shows real wall-clock times (
HH:MM) on the x-axis as the primary label, with the relative offset (+N min) shown below each tick as a secondary reference. The S and E trim-point markers display the exact wall-clock time (HH:MM:SS) instead of a relative offset. The Set Trim Start and Set Trim End forms have been redesigned: both now show the full cycle window (start → end), the current trim position in wall-clock time and minutes, and a single clock-picker field pre-populated with the current position — making it straightforward to set a trim point by entering a known time from another source (e.g. HA history graphs or a smart plug log) without having to calculate offsets manually. - Energy & Cost Placeholders in Finish Notifications (#196): The finish message template now supports two new placeholders:
{energy_kwh}(energy consumed during the cycle, formatted to 3 decimal places) and{cost}(energy cost, formatted to 2 decimal places). To enable{cost}, configure an electricity price under Notifications → Energy Price - either a static value (e.g.0.22) or a Home Assistant entity that holds the current tariff (anysensor.*,input_number.*, ornumber.*with a numeric state). The entity takes precedence if both are set;{cost}resolves to an empty string when no price is configured. Currency is not included - add it directly in your template (e.g.{cost} €). Example:{device} finished. Duration: {duration}m | Energy: {energy_kwh} kWh | Cost: {cost} €. Energy per cycle is now also stored in the cycle history record (energy_wh) computed from the stored power trace at save time, covering all storage paths including the manual recorder. - Multi-Target Notifications (#203): The notification system now supports multiple recipients per event type. The single Notification Service dropdown and Events to Notify checkbox have been replaced with three independent multi-select fields - Cycle Start, Cycle Finish, and Live Progress - each accepting any number of
notify.*services. A family member who only needs to know when laundry is done can be added to Cycle Finish only, while another person who also wants live progress can be added to all three. Existing configurations are migrated automatically: the previous single service is mapped into each list that matches the previously selected events, so no manual reconfiguration is required after upgrade. Notification actions and presence gating continue to work across all targets. - New Device Type: Bread Maker (#190): Added full support for bread makers with optimised defaults (2-hour no-update timeout for long proving periods, 5-minute off-delay for keep-warm, 30-minute completion threshold) and five built-in phases: Kneading, Resting, Proving, Baking, and Keep Warm.
- New Device Type: Pump / Sump Pump (#195): Added a dedicated pump device type (
mdi:water-pump) with defaults tuned for short, high-frequency cycles: 10 s sampling interval (captures sub-30 s runs that the 30 s default misses), 20 s off-delay (pumps stop sharply with no warm-down), 5 s minimum cycle threshold (valid with 1–2 readings), 60 s minimum off-gap (handles pumps cycling every few minutes), and a 0.003 Wh start-energy gate. Includes a Pump Stuck Alert - a configurable threshold (pump_stuck_duration, default 30 min) that fires a singleha_washdata_pump_stuckHA event if a pump cycle runs longer than expected, enabling automations to alert on a jammed motor or continuous-run fault. - Pump Runs (Last 24 h) Sensor (#195): When using the Pump device type, a new
pump_runs_todaysensor (mdi:counter) counts completed pump cycles in a rolling 24-hour window - useful for monitoring sump pump activity during heavy rain or drought periods. - Cycle Counter Sensor (#210): A new
cycle_countsensor (mdi:counter, unit: cycles, state class:total_increasing) tracks the total number of completed cycles stored for the device. Use this in automations to schedule maintenance tasks - such as cleaning filters, refilling consumables, or checking bearing wear - based on a fixed cycle interval rather than a calendar schedule. - Chronometer Countdown Notifications (#194): Added an optional Chronometer Countdown Timer toggle to the Notifications settings. When enabled, each live progress notification push includes
chronometer,when, andcountdownfields pointing to the estimated cycle finish time. The Android companion app renders a live countdown timer on the notification that ticks down automatically between pushes - eliminating the "frozen at X minutes" problem without requiring more frequent server-side sends. Disabled by default; Android companion app only. - Live Diagnostics in HA Diagnostics Export:
async_get_config_entry_diagnosticsnow includes alive_diagnosticssnapshot from a new in-memoryDiagBuffer- a rolling 24-hour ring buffer capturing every raw power reading, every detector state transition, and the last 5000 log lines. Zero disk I/O; vanishes on HA restart. - User-Triggered Pause (#146): Cycles can now be paused and resumed explicitly via a Pause Cycle button entity, a Resume Cycle button entity, or the new
ha_washdata.pause_cycle/ha_washdata.resume_cycleHA services (both acceptdevice_id). While paused, the cycle is protected from watchdog termination (verified_pause = True) - the appliance can sit at zero or low power indefinitely without WashData closing the session. All time metrics (elapsed time, time remaining, total duration, and progress %) exclude the time spent paused, so a cycle paused for 30 minutes does not inflate the estimated total. The primary use case is energy price management: trigger a pause when tariff is high, resume when it drops (e.g. with Cheapest Energy Hours). An optional Cut Power When Pausing toggle (pause_cuts_power, off by default) instructs WashData to also callswitch.turn_off/switch.turn_onon the configured switch entity when pause or resume is triggered - useful for appliances that remember their position after a power cut. The Pause button is only available while a cycle is Running or Starting; the Resume button is only available while the cycle is user-paused. - Door Sensor & Clean State (#153): An optional Door Sensor field (any
binary_sensor,on = open) can now be configured under Advanced Settings. When set, WashData uses door state in two ways. During an active cycle, opening the door automatically setsverified_pause = True- the same protection used by user-triggered pauses - confirming the interruption is intentional and guarding the cycle against watchdog termination (useful for add-clothes machines such as Samsung AddWash). Closing the door does not auto-resume the cycle - this is intentional and is stated explicitly in the field description; the user must press Resume or call the service. After a cycle ends, if the door is still closed the device enters a new Clean state (STATE_CLEAN) indicating laundry is waiting to be unloaded. The state clears as soon as the door is opened. A configurable Laundry Waiting Notification Delay (notify_unload_delay_minutes, default 60 min, set to 0 to disable) fires a single reminder via the existing finish notification channel once the door has remained closed for that many minutes after cycle end - the "your wash has been sitting there for an hour" nag. The reminder message is fully customizable via a Laundry Waiting Message template (notify_unload_message); the default reads"{device} finished {duration}m ago - laundry is still inside."and supports{device}and{duration}placeholders.
🛠️ Improvements
delay_waitState Now Has a Human-Readable Label: Thedelay_waitstate was missing from the sensor state translation map instrings.json, causing the UI to display the raw key. It now renders as "Delay Start".- Bug Report Template: Pump Label Aligned: The device type dropdown in
bug_report.ymlnow reads "Pump / Sump Pump", matching thefeature_request.ymltemplate. - Issue Template: Outdated Version Check: The automated issue validator now fetches the latest stable and pre-release versions from the GitHub Releases API and flags any bug report submitted against an older version. The reporter is asked to confirm the bug still exists on the latest release before the issue is investigated. The version field description now links directly to the Releases page.
- Issue Template: Device Type Sync: Added Bread Maker and Pump / Sump Pump to the Feature Request template's device type dropdown — these device types were supported since v0.4.4 but were missing from that template.
- Issue Automation: Auto-Assign: All newly opened and reopened issues are now automatically assigned to the maintainer, so every report lands in the triage queue without a manual step.
- **Issue Automation: Auto-Close Incomplete Reports...
v0.4.4-alpha
0.4.4 - 2026-04-14
✨ Features
- Energy & Cost Placeholders in Finish Notifications (#196): The finish message template now supports two new placeholders:
{energy_kwh}(energy consumed during the cycle, formatted to 3 decimal places) and{cost}(energy cost, formatted to 2 decimal places). To enable{cost}, configure an electricity price under Notifications → Energy Price - either a static value (e.g.0.22) or a Home Assistant entity that holds the current tariff (anysensor.*,input_number.*, ornumber.*with a numeric state). The entity takes precedence if both are set;{cost}resolves to an empty string when no price is configured. Currency is not included - add it directly in your template (e.g.{cost} €). Example:{device} finished. Duration: {duration}m | Energy: {energy_kwh} kWh | Cost: {cost} €. Energy per cycle is now also stored in the cycle history record (energy_wh) computed from the stored power trace at save time, covering all storage paths including the manual recorder. - Multi-Target Notifications (#203): The notification system now supports multiple recipients per event type. The single Notification Service dropdown and Events to Notify checkbox have been replaced with three independent multi-select fields - Cycle Start, Cycle Finish, and Live Progress - each accepting any number of
notify.*services. A family member who only needs to know when laundry is done can be added to Cycle Finish only, while another person who also wants live progress can be added to all three. Existing configurations are migrated automatically: the previous single service is mapped into each list that matches the previously selected events, so no manual reconfiguration is required after upgrade. Notification actions and presence gating continue to work across all targets. - New Device Type: Bread Maker (#190): Added full support for bread makers with optimised defaults (2-hour no-update timeout for long proving periods, 5-minute off-delay for keep-warm, 30-minute completion threshold) and five built-in phases: Kneading, Resting, Proving, Baking, and Keep Warm.
- New Device Type: Pump / Sump Pump (#195): Added a dedicated pump device type (
mdi:water-pump) with defaults tuned for short, high-frequency cycles: 10 s sampling interval (captures sub-30 s runs that the 30 s default misses), 20 s off-delay (pumps stop sharply with no warm-down), 5 s minimum cycle threshold (valid with 1–2 readings), 60 s minimum off-gap (handles pumps cycling every few minutes), and a 0.003 Wh start-energy gate. Includes a Pump Stuck Alert - a configurable threshold (pump_stuck_duration, default 30 min) that fires a singleha_washdata_pump_stuckHA event if a pump cycle runs longer than expected, enabling automations to alert on a jammed motor or continuous-run fault. - Pump Runs (Last 24 h) Sensor (#195): When using the Pump device type, a new
pump_runs_todaysensor (mdi:counter) counts completed pump cycles in a rolling 24-hour window - useful for monitoring sump pump activity during heavy rain or drought periods. - Cycle Counter Sensor (#210): A new
cycle_countsensor (mdi:counter, unit: cycles, state class:total_increasing) tracks the total number of completed cycles stored for the device. Use this in automations to schedule maintenance tasks - such as cleaning filters, refilling consumables, or checking bearing wear - based on a fixed cycle interval rather than a calendar schedule. - Chronometer Countdown Notifications (#194): Added an optional Chronometer Countdown Timer toggle to the Notifications settings. When enabled, each live progress notification push includes
chronometer,when, andcountdownfields pointing to the estimated cycle finish time. The Android companion app renders a live countdown timer on the notification that ticks down automatically between pushes - eliminating the "frozen at X minutes" problem without requiring more frequent server-side sends. Disabled by default; Android companion app only. - Live Diagnostics in HA Diagnostics Export:
async_get_config_entry_diagnosticsnow includes alive_diagnosticssnapshot from a new in-memoryDiagBuffer- a rolling 24-hour ring buffer capturing every raw power reading, every detector state transition, and the last 5000 log lines. Zero disk I/O; vanishes on HA restart. - User-Triggered Pause (#146): Cycles can now be paused and resumed explicitly via a Pause Cycle button entity, a Resume Cycle button entity, or the new
ha_washdata.pause_cycle/ha_washdata.resume_cycleHA services (both acceptdevice_id). While paused, the cycle is protected from watchdog termination (verified_pause = True) - the appliance can sit at zero or low power indefinitely without WashData closing the session. All time metrics (elapsed time, time remaining, total duration, and progress %) exclude the time spent paused, so a cycle paused for 30 minutes does not inflate the estimated total. The primary use case is energy price management: trigger a pause when tariff is high, resume when it drops (e.g. with Cheapest Energy Hours). An optional Cut Power When Pausing toggle (pause_cuts_power, off by default) instructs WashData to also callswitch.turn_off/switch.turn_onon the configured switch entity when pause or resume is triggered - useful for appliances that remember their position after a power cut. The Pause button is only available while a cycle is Running or Starting; the Resume button is only available while the cycle is user-paused. - Door Sensor & Clean State (#153): An optional Door Sensor field (any
binary_sensor,on = open) can now be configured under Advanced Settings. When set, WashData uses door state in two ways. During an active cycle, opening the door automatically setsverified_pause = True- the same protection used by user-triggered pauses - confirming the interruption is intentional and guarding the cycle against watchdog termination (useful for add-clothes machines such as Samsung AddWash). Closing the door does not auto-resume the cycle - this is intentional and is stated explicitly in the field description; the user must press Resume or call the service. After a cycle ends, if the door is still closed the device enters a new Clean state (STATE_CLEAN) indicating laundry is waiting to be unloaded. The state clears as soon as the door is opened. A configurable Laundry Waiting Notification Delay (notify_unload_delay_minutes, default 60 min, set to 0 to disable) fires a single reminder via the existing finish notification channel once the door has remained closed for that many minutes after cycle end - the "your wash has been sitting there for an hour" nag. The reminder message is fully customizable via a Laundry Waiting Message template (notify_unload_message); the default reads"{device} finished {duration}m ago - laundry is still inside."and supports{device}and{duration}placeholders.
🛠️ Improvements
- Import Accepts HA Diagnostic Export Format: The import feature (Configure → Diagnostic Settings → Import) now accepts three JSON formats without any manual preprocessing: (1) the regular WashData export; (2) the raw diagnostics dict produced by WashData (with
store_exportat the top level, e.g. copy-pasted from a log or API response); and (3) the full HA diagnostics download file (the.jsonfile downloaded via Settings → Devices & Services → (device) → Download Diagnostics), which wraps our data in ahome_assistant/dataenvelope. When importing from a diagnostic source, any fields replaced by the redactor (**REDACTED**) are silently stripped, so sensitive fields such aspower_sensorandnotify_serviceare never overwritten with the placeholder string. - Offline Diagnostic Analyser (
devtools/analyze_diag.py): New developer tool that processes any WashData diagnostics export and prints a side-by-side table of current vs. suggested settings with a per-parameter rationale. Covers power thresholds, energy gates, timing/operational parameters, and matching tolerances - all derived from the device's own recorded cycle data without requiring a running Home Assistant instance. Surfaces suggestions already computed by live HA operation for cross-checking, and includes a cycle-history summary per programme (average duration, standard deviation, variance). Runpython3 devtools/analyze_diag.py <export.json>from the repo root with the venv activated; seedevtools/README.mdfor full usage. - Batch Multi-Cycle Simulation (suggestion engine): The suggestion engine now derives power thresholds, dead-zone, and end-energy gate from the aggregate of all labeled cycles (
run_batch_simulation) rather than only the most recent one. The 5th-percentile minimum-active-power across cycles is used for the stop threshold (more robust against outlier cycles), and the 75th-percentile of early-dip timestamps is used for the running dead zone. A newprocess_cycle_endhook re-runs the batch every 5 newly labeled cycles so suggestions improve progressively as more data accumulates. - Min-Off-Gap Suggestion: The suggestion engine now derives a
min_off_gaprecommendation from observed inter-cycle gaps. The p05 gap × 0.8 is used as the proposed value, floored by the device-type default, so the suggestion is always conservative enough to avoid splitting real cycles. - DataLoader New Export Format Support (benchmark tooling):
tests/benchmarks/parameter_optimizer.DataLoadernow handles both the newdata → store_export → data → past_cyclesexport path and the legacydata → store_data → past_cyclespath, so all user-contributed diagnostic exports incycle_data/are usable by the benchmark suite. - Dishwasher Post-Cycle Ghost Suppression (#43): The ghost suppressor now uses a 10-minute suspicious window f...
v0.4.3.1
0.4.3.1 - 2026-03-16
✨ Features
- Trim Cycle Service: Added a new
trim_cycleHA service call that trims a cycle's stored power trace to a user-specified[trim_start_s, trim_end_s]window. Offsets are renormalized to start at zero, and all cycle metadata —start_time,end_time,duration,signature,sampling_interval— is recomputed and persisted atomically. Useful for removing noisy preamble or lingering standby readings from a recorded cycle. - Trim Cycle UI: Added a "Trim Cycle Data" action under Manage Cycles in the options flow. The UI presents an SVG preview of the full power curve with the kept region highlighted, and separate steps for setting the trim start and end points in whole minutes. Changes can be previewed, reset, and applied without leaving the interface.
🛠️ Improvements
- Power Data Preserved for Pending Feedback: Cycles that have an open feedback request are now exempted from the periodic power-data strip pass in
ProfileStore. Previously, the background cleanup could remove the stored power trace before the user had reviewed the feedback dialog, causing the SVG comparison chart to show no data. Pending cycles are identified by their presence inpending_feedbackand are skipped unconditionally until feedback is resolved. - Streamlined Power Data Decompression: Replaced a duplicate inline decompression loop in
ProfileStorewith a call to the shareddecompress_power_data()utility. The utility correctly handles both the legacy ISO-timestamp format and the newer relative-offset format, removing a subtle inconsistency where the old inline code only handled one variant. - Translation Overhaul: Comprehensively reviewed and rewrote all 60 non-English translation files. Machine-translated strings that were semantically incorrect, awkward, or technically wrong have been replaced with idiomatic translations. Common fixes applied across languages include: missing
{suggestions_count}placeholders in settings descriptions; translated JSON parameter key names inapply_suggestions(keys must stay in English); translated MDI icon names; nonsensical state labels (e.g. "jogging" for "running", "country" for "device state", "fire/combustion" for notification trigger); and leftover untranslated English strings.sr-Latn.jsonwas fully rewritten from scratch — the previous file contained Serbian Cyrillic script instead of Latin script. - Full Diagnostics Export: The HA diagnostics download now returns the complete store export — all profiles (including power samples), all past cycles, envelopes, feedback history, auto-adjustments, suggestions, custom phases, and full
entry_data/entry_optionsconfig. Previously it returned only counts and a single-cycle summary, making it nearly useless for debugging. Personally identifiable fields (power_sensor,notify_service,notify_people,name,unique_id, etc.) are still redacted. - Unix Timestamp Format Recognition:
detect_power_data_formatnow identifies absolute Unix epoch timestamps (> 1e8) as theunix_timestampvariant rather than silently discarding them.migrate_power_data_to_offsetsaccepts this format and converts epoch-based traces to relative offsets by subtracting the cycle start time, so imported or externally generated power traces are handled correctly without data loss. - Translated Service Errors: Service validation errors in
trim_cycle(device not found, integration not loaded, cycle missing, invalid trim range, empty trim window) now raiseServiceValidationErrorwithtranslation_domainandtranslation_keyinstead of plainValueErrorstrings, giving users localized error messages in automations and the developer tools panel. - SVG Safety and Robustness: HTML special characters in SVG titles and no-data labels are now escaped, preventing rendering artefacts when cycle names contain
&,<, or>. The "no power data" placeholder in merge-preview and trim-preview SVGs is now a configurable parameter rather than a hardcoded English string, enabling full localization. - Per-Device Logging Extended:
DeviceLoggerAdaptercoverage extended toProfileStoreandCycleRecorder. All log messages from these components now carry the device name prefix, consistent with the rest of the engine. - Removed Stub UI Option: Removed the "Label Multiple Cycles" entry from the Manage Cycles menu. The option was listed in the UI but had no backing implementation, leading to a silent no-op when selected.
🐛 Bug Fixes
-
Trim Offsets: Negative Input Clamping:
handle_trim_cyclein__init__.pynow clamps bothtrim_start_sandtrim_end_stomax(0.0, ...)before the range-validity check. Previously a negative input bypassed thetrim_invalid_rangeguard and was forwarded to the store unchanged. -
Envelope Persistence After Startup Repair:
async_loadnow callsawait self.async_save()a second time afterasync_rebuild_all_envelopes()completes. Previously, rebuilt envelopes were applied toself._datain memory but never written to durable storage; a subsequent restart would redo the repair and lose any downstream use of the rebuilt data. -
start_timeString Validation inadd_cycle: Stringstart_timevalues are now validated withdt_util.parse_datetime()(falling back to a float conversion attempt) before being stored as canonical ISO timestamps. Previously any non-empty string passed through unconditionally, so a malformed or placeholder value could reachpower_data_to_offsetsand corrupt the stored offset array. -
Test Alignment with
_cleanintest_trailing_zero_impact:duration_beforeand then_trailing == len(…) - len(trimmed)assertion now operate on_clean(pd)rather than the rawpdlist. Malformed rows filtered by_cleaninside_trim_trailing/_trailing_zero_countcaused length mismatches and potential index errors when real-world cycle files contained non-numeric entries. -
lt.jsonSmart-Quote Corruption: The Lithuanian translation file had itstrim_cycleservices block committed with Unicode curly quotes ("/") used as JSON structural delimiters instead of ASCII". This produced anInvalid JSONerror at startup. The block was rewritten with correct ASCII quoting while preserving the Lithuanian„…"content quotes inside string values. -
Profile Statistics Graph Flat Line: Fixed a bug where newly detected cycles showed a flat line at zero and
0.00 kWhin the Profile Statistics graph. The cycle storage routine (_add_cycle_data) incorrectly treated already-converted offset values ([seconds, power]) as unix timestamps, subtracting the cycle start unix timestamp from them a second time. This produced large negative offsets (≈ −1.7 billion seconds) that causednp.interpin the envelope builder to extrapolate to the boundary power value (typically ~0 W) across the entire time grid, rendering a flat zero curve regardless of actual power draw. -
Automatic Recovery of Corrupted Cycles: On startup, the integration now automatically detects and repairs any cycles stored with the corrupted negative offsets (first offset < −10⁸ s is physically impossible for an appliance cycle). The original offsets are recovered by adding the cycle's
start_timeunix timestamp back. Affected cycles are repaired in-place and saved transparently — no manual action required.
v0.4.3
0.4.3 - 2026-03-16
✨ Features
- New Device Types: Added full support for Air Fryer (#133) and Heat Pump (#134), with optimized defaults and custom icons (
mdi:pot-steam,mdi:heat-pump). - Anti-Wrinkle Mode: Added a dedicated anti-wrinkle state for dryers and washer-dryer combos, including state transitions and shielding (#68).
- Card Customization: Added new dashboard card settings including specialized toggles for
Spinning Icon,Show State,Show Program, andShow Details. - Automated Translation Sync: Enhanced
translate.pyto automatically update the frontend card'sTRANSLATIONSobject from language files, providing out-of-the-box localization for all 27+ supported languages. - Inverted External Trigger: Added a new setting to invert the logic of the external cycle end trigger. Users can now choose to complete a cycle when an external binary sensor turns OFF instead of ON.
- Randomized Cache Buster: The dashboard card now uses a timestamp-based cache buster that refreshes every time the integration is loaded, ensuring immediate updates without browser cache clearance.
- Action-Based Notifications: Added notification actions with priority dispatch and fallback routing (actions → notify service → persistent notification).
- Presence-Gated Notifications: Optional home/away gating to defer notifications until a tracked person is home.
- Live Progress Mobile Notifications: Added in-place companion-app live updates (
cycle_live) with per-cycle throttling, overrun protection caps, mobile-only payload routing, and automatic clear on cycle completion. - Feedback Review Power Visualization: Added an inline SVG chart in "Review Learned Feedbacks" that overlays the current cycle trace with learned profile data for faster manual verification.
- Multi-Profile Comparison Graph: Feedback review now renders all candidate profiles in a single combined chart, highlighting the detected profile and showing the actual cycle trace for direct visual comparison.
- Top Match Candidates Summary: Added ranked candidate details (confidence, MAE, correlation, duration ratio) to feedback review to improve correction decisions.
🛠️ Improvements
- Per-Device Log Context: All log messages emitted by the core engine (
WashDataManager,ProfileStore,CycleDetector,LearningManager,CycleRecorder) now include the device name as a[Device Name]prefix. When running two or more devices simultaneously, every log line is immediately attributable to its source without cross-referencing entry IDs. - UI Menu Clarity: All
SelectSelectordropdowns in the configuration flow now useSelectOptionDictwith explicit human-readable labels (e.g. "Split a Cycle (Find gaps)", "Export All Data", "Confirm - Correct Detection"). Previously, raw internal values such as"split"or"auto_label_cycles"were displayed directly in the UI. - Translation Cleanup: Removed stale action option keys from
strings.jsonanden.jsonthat were no longer backed by selectors in the config flow (assign_mode, export/import mode, cycle history editor actions, and several management menu entries). Reduces translator noise and prevents spurious untranslated keys in other languages. - Phase Catalog Translations: Extended
manage_phase_catalogaction labels and descriptions to Swedish, Tamil, Telugu, and Simplified Chinese. - Unified Time Handling: Refactored the core engine to use a single canonical offset-based time format for storage. Includes automatic migration of legacy data to prevent corruption and fixes "offset-naive/offset-aware" comparison bugs (#144).
- Profile Rename Cascade: Renaming a profile now automatically updates all historically recorded cycles and pending feedback requests, maintaining end-to-end data integrity (#154).
- Detection Persistence: Implemented temporal persistence for profile matching. Start notifications now only fire after a match remains stable over several intervals, drastically reducing false or jittery alerts.
- Enhanced Feedback Resolution: Overhauled the feedback resolution flow with new "Delete" and "Ignore" actions, giving users more granular control over learned cycles.
- Sub-State Extraction: The dashboard card now intelligently extracts and displays the specific phase from the state (e.g., showing "Rinsing" instead of "Running (Rinsing)") for a cleaner UI experience.
- History Timeline Restoration: Restored categorical history diagrams for State and Program sensors by implementing the
enumdevice class (#157). - Localized Menus: Updated configuration flow to use
SelectSelector, enabling natively localized menu options across all supported languages. - Notification Event Toggle: Added
notify_fire_eventsoption to control emission of cycle start/end events. - Migration Normalization: Added migration helper defaults for new notification options to ensure deterministic upgrades.
- Notification Options UX: Moved notification settings to a dedicated "Notifications" options step and removed duplicate live-enable controls, using event selection as the single source of truth.
- Live Progress Match-Aware Flow: Live notifications now send a one-time "no profile matched yet" message before detection converges, then switch to periodic progress updates only after a profile duration is available.
- Ultra-Long Cycle Support: Significantly improved handling for modern high-efficiency dishwashers with cycles exceeding 230 minutes.
- Increased
DEFAULT_MAX_DEFERRAL_SECONDSto 4 hours to prevent long silent Eco drying phases from being cut off. - Extended dishwasher-specific
NO_UPDATE_ACTIVE_TIMEOUTto 4 hours. - Increased the default dishwasher
MIN_OFF_GAPto 1 hour to prevent fragmentation when no profile is matched.
- Increased
- Robust Zombie Killer: Refined the "Zombie Killer" hard-limit to be more lenient, now triggering at 300% of expected duration (previously 200%) and requiring at least 4 hours of runtime. This prevents premature termination of long-running appliances while still protecting against runaway ghost cycles.
- Device-Aware Suggestions: The
SuggestionEngineis now aware of the configured device type and uses device-specific safety floors foroff_delayrecommendations, preventing it from suggesting dangerously short timeouts for dishwashers. - Translation Tool Docs: Added documentation for the Home Assistant integration translation helper script.
- Learning Pipeline Context Propagation: Propagated runtime match ranking through manager/learning flow so feedback requests retain candidate context.
- Feedback Chart Readability: Increased chart and legend typography and spacing to improve readability on Home Assistant dialogs.
- Phase Assignment Visualization: Replaced ASCII timeline with interactive SVG power curve chart showing average cycle profile, colored phase spans, and gating line boundaries for better profile phase visualization.
- Phase Catalog Management: Implemented full create/edit/delete capabilities for custom phases in the phase catalog, allowing users to build device-specific phase vocabularies. Default phases can also be edited, with overrides automatically stored in the custom phases list.
- Device-Type Phase Filtering: Phase options in the profile assignment flow are now automatically filtered by the currently selected device type, ensuring only relevant phases appear in dropdowns.
- Cross-Device Catalog View: "Manage Phase Catalog" now displays and groups phases for all supported device types in one place, instead of only the current integration device type.
- Phase Action Wording Cleanup: Updated phase management action labels to clearer wording ("Create New Phase", "Edit Phase", "Delete Phase").
- Current Phase Sensor Exposure: Added a standard device sensor for current phase (
sensor.<device>_current_phase) so active phase is visible in normal entity views without enabling diagnostics. - Phase-Only Offset Input: Simplified phase assignment to use offset-based time entry (minutes from cycle start) instead of timestamp selection, reducing complexity and user error.
- Suggested Settings Discoverability: Improved the Suggested Settings UX with sensor-first guidance, a one-time "suggestions ready" notification when recommendations become available, and an explicit review step before suggested values are staged in Advanced Settings.
- Phase Unique ID Management: Built-in and custom phases are now assigned stable unique IDs. Phase catalog operations (edit, delete) resolve by ID rather than by name, eliminating ambiguity when similarly-named phases exist across different scopes. Includes improved error handling for rename/delete conflicts.
- Duration Consistency Metric: Profile sensor attributes now expose a
consistency_minfield (standard deviation of recorded cycle durations, in minutes), allowing users to diagnose variability in learned profiles directly from the entity state. - Signal Processing Edge-Case Guards: Added guards against non-positive step and gap values in the resampling pipeline, preventing division-by-zero and NaN propagation in high-noise or sparse-sensor environments.
- Cycle Detector Numeric Guards:
update_matchnow validates confidence and expected-duration values withmath.isfinite(), falling back to0.0with a debug log entry instead of propagating NaN or infinity into downstream sensors. - Suggestion Engine Resilience: Added
TypeError/ValueErrorguards when parsing profile data in theSuggestionEngine, preventing crashes when stored profile fields contain unexpected types. - Card Registration Resilience: Dashboard card asset registration now catches all setup exceptions and logs a warning, allowing the rest of the integration to load normally; setup will retry on the next Home Assi...
v0.4.3-pre-release
[0.4.3-PRE-RELEASE] - 2026-03-10
✨ Features
- New Device Types: Added full support for Air Fryer (#133) and Heat Pump (#134), with optimized defaults and custom icons (
mdi:pot-steam,mdi:heat-pump). - Anti-Wrinkle Mode: Added a dedicated anti-wrinkle state for dryers and washer-dryer combos, including state transitions and shielding (#68).
- Slovak Localization: Full support for Slovak language in the integration, diagnostics, and frontend card (#156).
- Traditional Chinese Localization: Added full Traditional Chinese (
zh-Hant) translations for all configuration and options menus. - Card Customization: Added new dashboard card settings including specialized toggles for
Spinning Icon,Show State,Show Program, andShow Details. - Automated Translation Sync: Enhanced
translate.pyto automatically update the frontend card'sTRANSLATIONSobject from language files, providing out-of-the-box localization for all 27+ supported languages. - Inverted External Trigger: Added a new setting to invert the logic of the external cycle end trigger. Users can now choose to complete a cycle when an external binary sensor turns OFF instead of ON.
- Randomized Cache Buster: The dashboard card now uses a timestamp-based cache buster that refreshes every time the integration is loaded, ensuring immediate updates without browser cache clearance.
- Action-Based Notifications: Added notification actions with priority dispatch and fallback routing (actions → notify service → persistent notification).
- Presence-Gated Notifications: Optional home/away gating to defer notifications until a tracked person is home.
- Live Progress Mobile Notifications: Added in-place companion-app live updates (
cycle_live) with per-cycle throttling, overrun protection caps, mobile-only payload routing, and automatic clear on cycle completion. - Feedback Review Power Visualization: Added an inline SVG chart in "Review Learned Feedbacks" that overlays the current cycle trace with learned profile data for faster manual verification.
- Multi-Profile Comparison Graph: Feedback review now renders all candidate profiles in a single combined chart, highlighting the detected profile and showing the actual cycle trace for direct visual comparison.
- Top Match Candidates Summary: Added ranked candidate details (confidence, MAE, correlation, duration ratio) to feedback review to improve correction decisions.
🛠️ Improvements
- UI Menu Clarity: All
SelectSelectordropdowns in the configuration flow now useSelectOptionDictwith explicit human-readable labels (e.g. "Split a Cycle (Find gaps)", "Export All Data", "Confirm - Correct Detection"). Previously, raw internal values such as"split"or"auto_label_cycles"were displayed directly in the UI. - Translation Cleanup: Removed stale action option keys from
strings.jsonanden.jsonthat were no longer backed by selectors in the config flow (assign_mode, export/import mode, cycle history editor actions, and several management menu entries). Reduces translator noise and prevents spurious untranslated keys in other languages. - Phase Catalog Translations: Extended
manage_phase_catalogaction labels and descriptions to Swedish, Tamil, Telugu, and Simplified Chinese. - Unified Time Handling: Refactored the core engine to use a single canonical offset-based time format for storage. Includes automatic migration of legacy data to prevent corruption and fixes "offset-naive/offset-aware" comparison bugs (#144).
- Profile Rename Cascade: Renaming a profile now automatically updates all historically recorded cycles and pending feedback requests, maintaining end-to-end data integrity (#154).
- Detection Persistence: Implemented temporal persistence for profile matching. Start notifications now only fire after a match remains stable over several intervals, drastically reducing false or jittery alerts.
- Enhanced Feedback Resolution: Overhauled the feedback resolution flow with new "Delete" and "Ignore" actions, giving users more granular control over learned cycles.
- Sub-State Extraction: The dashboard card now intelligently extracts and displays the specific phase from the state (e.g., showing "Rinsing" instead of "Running (Rinsing)") for a cleaner UI experience.
- History Timeline Restoration: Restored categorical history diagrams for State and Program sensors by implementing the
enumdevice class (#157). - Localized Menus: Updated configuration flow to use
SelectSelector, enabling natively localized menu options across all supported languages. - Notification Event Toggle: Added
notify_fire_eventsoption to control emission of cycle start/end events. - Migration Normalization: Added migration helper defaults for new notification options to ensure deterministic upgrades.
- Notification Options UX: Moved notification settings to a dedicated "Notifications" options step and removed duplicate live-enable controls, using event selection as the single source of truth.
- Live Progress Match-Aware Flow: Live notifications now send a one-time "no profile matched yet" message before detection converges, then switch to periodic progress updates only after a profile duration is available.
- Ultra-Long Cycle Support: Significantly improved handling for modern high-efficiency dishwashers with cycles exceeding 230 minutes.
- Increased
DEFAULT_MAX_DEFERRAL_SECONDSto 4 hours to prevent long silent Eco drying phases from being cut off. - Extended dishwasher-specific
NO_UPDATE_ACTIVE_TIMEOUTto 4 hours. - Increased the default dishwasher
MIN_OFF_GAPto 1 hour to prevent fragmentation when no profile is matched.
- Increased
- Robust Zombie Killer: Refined the "Zombie Killer" hard-limit to be more lenient, now triggering at 300% of expected duration (previously 200%) and requiring at least 4 hours of runtime. This prevents premature termination of long-running appliances while still protecting against runaway ghost cycles.
- Device-Aware Suggestions: The
SuggestionEngineis now aware of the configured device type and uses device-specific safety floors foroff_delayrecommendations, preventing it from suggesting dangerously short timeouts for dishwashers. - Translation Tool Docs: Added documentation for the Home Assistant integration translation helper script.
- Learning Pipeline Context Propagation: Propagated runtime match ranking through manager/learning flow so feedback requests retain candidate context.
- Feedback Chart Readability: Increased chart and legend typography and spacing to improve readability on Home Assistant dialogs.
- Phase Assignment Visualization: Replaced ASCII timeline with interactive SVG power curve chart showing average cycle profile, colored phase spans, and gating line boundaries for better profile phase visualization.
- Phase Catalog Management: Implemented full create/edit/delete capabilities for custom phases in the phase catalog, allowing users to build device-specific phase vocabularies. Default phases can also be edited, with overrides automatically stored in the custom phases list.
- Device-Type Phase Filtering: Phase options in the profile assignment flow are now automatically filtered by the currently selected device type, ensuring only relevant phases appear in dropdowns.
- Cross-Device Catalog View: "Manage Phase Catalog" now displays and groups phases for all supported device types in one place, instead of only the current integration device type.
- Phase Action Wording Cleanup: Updated phase management action labels to clearer wording ("Create New Phase", "Edit Phase", "Delete Phase").
- Current Phase Sensor Exposure: Added a standard device sensor for current phase (
sensor.<device>_current_phase) so active phase is visible in normal entity views without enabling diagnostics. - Phase-Only Offset Input: Simplified phase assignment to use offset-based time entry (minutes from cycle start) instead of timestamp selection, reducing complexity and user error.
🐛 Bug Fixes
- Manual Recording Revert (#151): Fixed an issue where manual recordings could unexpectedly revert configuration changes.
- Data Import Fix (#152): Resolved a bug that prevented successful data imports into the profile store.
- Profile Store Reliability (#155): Fixed synchronization issues when updating profile statuses and statistics.
- Translation Consistency: Synchronized
en.jsonwithstrings.jsonto ensure a canonical source of truth for translations. - Energy Threshold Defaults: Fixed a bug where
start_energy_thresholdandend_energy_thresholdwere incorrectly defaulting to 0.0W in the detector configuration, which could lead to premature cycle ends in noisy environments. They now correctly respect device-specific constant defaults. - Config Reload Consistency: Added missing energy threshold updates to the configuration reload logic, ensuring settings take effect immediately when changed in the UI.
- Config Flow Null Option Guard: Fixed a crash in the options flow where a
SelectSelectorentry with aNonevalue would cause aKeyErrorduring form processing. Such entries are now silently skipped. - Profile Stats After Deletion: Fixed
async_rebuild_envelopeincorrectly computingmin_durationandmax_durationfrom the outlier-filtered duration set.min/maxnow reflect the true observed range of all cycles; onlyavg_durationuses the IQR-filtered set for robustness. This means deleting an outlier cycle now correctly recalculates the profile's duration range. - Feedback Translation Placeholder Mismatch: Fixed options-flow description placeholders (
{comparison_data}) to prevent missing-value translation errors during feedback review. - Feedback SVG Legend Clipping: Fixed a viewBox heig...
v0.4.2.1
[0.4.2.1] - 2026-02-13
🐛 Fixed
- Manual Recording Trimming: Fixed a bug where manual recordings (e.g., Dishwashers in Eco mode) were internally shortened by incorrectly snapping the cycle duration to the last recorded power reading, losing trailing silence like drying phases.
- Profile Statistics Accuracy: Corrected profile duration calculations to use the authoritative cycle duration instead of data-offset bounds. This fixes incorrect remaining-time predictions and profile "shrinkage" over time.
- Aggressive Tail Trimming: Modified recorder suggestions to be less aggressive. Suggested tail trims are now
0.0for silence periods under 10 minutes, protecting legitimate silent phases in appliances. - Data Optimization Logic: Fixed maintenance logic that was incorrectly snapping durations to the last power reading during start-time shift corrections.
- Envelope Reconstruction: Updated the statistical engine to correctly respect explicit cycle durations even when power sensor updates are sparse or missing at the end of a run.
v0.4.2
[0.4.2] - 2026-02-11
✨ Features
- Advanced Parameter Auto-Suggestion:
- New
SuggestionEnginethat analyzes your appliance's actual power traces to recommend optimal settings. - Automatically suggests values for
start_threshold_w,off_delay,watchdog_interval, and more based on observed behavior. - Periodic background optimization to keep settings tuned as your appliance ages or use patterns change.
- New
- Electric Vehicle (EV) Support:
- Added new "Electric Vehicle" device type with optimized defaults.
- New icons and phase heuristics ("Charging", "Maintenance").
- Divergence Detection:
- Improved matching logic to detect when a cycle starts diverging from its matched profile.
- Automatically reverts to "Detecting..." if confidence drops significantly below the cycle's peak (default 40% drop).
- Card Animation:
- Added native spinning animation to the dashboard card icon when the appliance is running.
- Configurable Stability Thresholds:
- Added
DEFAULT_MATCH_REVERT_RATIOandDEFAULT_DEFER_FINISH_CONFIDENCEtoconst.pyfor easier fine-tuning.
- Added
- Profile-Aware Watchdog:
- The watchdog now uses "look-ahead" logic from the matched profile to prevent premature cycle termination during long legitimate pauses (e.g., dishwasher drying).
- Automatically extends silence timeouts if the cycle is within its expected profile duration.
- Zombie Protection:
- Implemented a hard "Zombie Killer" limit that force-ends cycles exceeding 200% of their expected profile duration (min 2 hours).
- Stuck Power Prevention:
- Automatically resets the power sensor to 0W when a cycle is forced to end by the watchdog or manual stop, fixing issues where the entity remained at a high value.
- Zero-Latency Low-Power Processing:
- Power updates below
min_powernow bypass all debouncing, smoothing, and sampling interval filters, ensuring immediate cycle-end detection.
- Power updates below
- Program Detection Stability:
- Implemented temporal persistence for profile matching: requires 3 consecutive consistent matches before switching from "detecting..." to a profile, or before unmatching a profile.
- Added a minimum confidence gap for mid-cycle profile switching to prevent "flapping" between similar programs.
- Total Duration Sensor:
- New
total_durationsensor providing the predicted total cycle time (Elapsed + Remaining). - Designed specifically to support full progress bars in
timer-bar-card. - Dynamically updates as estimates are refined.
-Languages - Added many new languages
- New
🛠️ Improvements
- Manual Recording Robustness:
- Increased gap threshold to 6 hours to support very long Eco cycles with multi-hour silent phases.
- Unified automatic trimming threshold to 1.0W across the integration.
- Clean Card UI:
- Removed redundant "off" label from completion details when the appliance is inactive.
- Zigbee2MQTT Guidance:
- Added optimized configuration tips for Z2M smart plug users in README.
🐛 Bug Fixes
- Profile Alignment Error (#112):
- Fixed a critical
TypeError: 'float' object is not subscriptablein the profile matching pipeline.
- Fixed a critical
- Terminal State Persistence:
- Fixed a bug where
finishedandinterruptedstates were not resetting tooffafter the intended 30-minute timeout.
- Fixed a bug where
- Profile Shrinking:
- Fixed an issue where maintenance tasks would aggressively trim trailing silence from completed cycles, causing profiles to shorten over time.
- Termination Hangs:
- Restricted "Deferred Finish" logic to require high confidence (> 0.55) or a verified pause, preventing cycles from hanging on mismatched long profiles.
- Long Drying Phase Support:
- Fixed an issue where dishwashers with multi-hour silent drying phases were being split into multiple cycles by the watchdog.
- Recognizes "Verified Pause" from profile envelope to extend silence timeouts.
- Test Stability:
- Resolved several
TypeErrorissues in the test suite and improved mocking reliability for sensors and configs.
- Resolved several
v0.4.1
[0.4.1] - 2026-02-03
✨ Features
- Persistent Terminal States:
- Implemented proper
finished,interrupted, andforce_stoppedstates that persist for 30 minutes after cycle completion. - Improves visibility of cycle outcomes in the UI (users can now see "Finished" instead of just "Off").
- Auto-resets to
offafter 30 minutes, or immediately if a new cycle starts.
- Implemented proper
- Coffee Machine Defaults: Added dedicated defaults for coffee machines (faster sampling, shorter timeouts) to improve detection out-of-the-box.
- French Translation: Added full French localization (thanks to @MaximeNagel).
🛠️ Improvements
- Profile Sorting: Improved sorting for profile lists (natural sort), ensuring correct numeric order (e.g.
1, 2, 10instead of1, 10, 2). - Refactored Device Defaults: Consolidated and cleaned up device-specific default settings logic.
- Test Suite: Enhanced test coverage for cycle state transitions and manager notifications.
🐛 Bug Fixes
- Stuck Power Value: Fixed issue where the power entity would get stuck at the last non-zero value after a cycle ended.
- Timezone Display: Fixed issue where timestamps in specific UI menus were shown in GMT instead of local time.
- Advanced Settings Error: Fixed a crash that prevented advanced settings from being saved in the configuration flow.
- State Logic: Fixed assertions and logic validation for terminal states.
- Notification Tests: Fixed test environment formatting for notification services.
What's Changed
- fix: sort profiles numerically instead of alphabetically by @fredrik-lindseth in #95
- Add french translation for v4.0 by @MaximeNagel in #102
- v0.4.1 by @3dg1luk43 in #104
New Contributors
- @fredrik-lindseth made their first contribution in #95
- @MaximeNagel made their first contribution in #102
Full Changelog: v0.4.0...0.4.1
v0.4.0
[0.4.0] - 2026-01-12
Major Architectural Rewrite ("vNext")
This release marks a complete re-engineering of the HA WashData core, transitioning from simple heuristics to a rigorous signal processing pipeline and robust state machine. While the version number is minor, this is effectively a new engine under the hood.
🎉 Milestones Reached!
- HA WashData is now available in the HACS Default Repository!
- Passed 1,000 active installations across the community.
- Reached 500+ stars on GitHub.
Thank you to everyone who has been patient during development and to all contributors who provided invaluable feedback, bug reports, and feature suggestions. This release wouldn't be possible without you!
Important
Fresh Start Recommended
This release includes significant changes to how cycles are detected and profiles are matched. The new engine depends on clean, accurate data to work properly.
If you're unsure whether your previously recorded cycles were captured correctly (e.g., cycles that ended prematurely, incorrectly merged fragments, or noisy data from before tuning your thresholds), we recommend:
- Delete your existing cycle history via Configure → Manage Cycles → Delete All
- Use the new "Record Cycle" feature to capture fresh, clean training data for each program you use
This ensures the best possible matching accuracy with the new architecture.
Core Architecture: Signal Processing & State Machine
-
New Signal Processing Engine (
signal_processing.py):- Dt-Aware Integration: Replaced simple averaging with trapezoidal Riemann sum integration (
integrate_wh) that respects variable sampling intervals. - Robust Smoothing: Implemented
robust_smooth, a hybrid algorithm combining a Median Filter (spike rejection) with a Time-Aware Exponential Moving Average (EMA) for clean trend detection. - Adaptive Resampling: New primitives (
resample_adaptive,resample_uniform) handle irregular sensor updates and enforce strict gap handling (no interpolation across large gaps). - Idle Baseline Learning: Automatically learns the device's "true zero" using Median Absolute Deviation (MAD), removing the need for manual calibration.
- Dt-Aware Integration: Replaced simple averaging with trapezoidal Riemann sum integration (
-
Finite State Machine (FSM):
- Replaced binary ON/OFF logic with a formal FSM:
OFF→STARTING→RUNNING↔PAUSED→ENDING→OFF. - Dt-Aware Gating: Start/End detection now uses accumulated time/energy gates (e.g., "energy since idle > X Wh") rather than sample counts, making it immune to sensor update frequency.
- Smart Pausing: Distinguishes between "End of Cycle" and "Mid-Cycle Pause" using dynamic thresholds derived from the sensor's sampling cadence (
_p95_dt).
- Replaced binary ON/OFF logic with a formal FSM:
Storage v2 & Migration
- Profile Store v2 (
profile_store.py):- New Schema: Introduced a versioned storage schema (v2) optimized for performance.
- Trace Compression: Historical power traces are now compressed using relative time deltas, significantly reducing disk usage.
- Robust Migration: Included a designated
WashDataStoreengine that automatically upgrades v1 data to v2 without data loss, preserving user labels and corrections.
✨ functionality & Features
- Configurable Sampling Interval: New "Sampling Interval" setting allows users to throttle high-frequency sensors (e.g., 1s updates) to reduce CPU load.
- Precision Configuration: Configuration flow now uses Text Box inputs for all numeric thresholds, offering precise control over parameters like
start_energy_threshold(Wh) anddrop_ratio. - Smart Resume: "Resurrection" logic restores the exact cycle state (including sub-state) after a Home Assistant restart.
- Auto-Labeling: Increased default confidence threshold to 0.75 (from 0.70) to leverage the improved accuracy of the new engine.
- Diagnostic Sensors: Added dynamic diagnostic sensors for each profile (e.g.,
sensor.washdata_..._profile_cotton_count) showing the total cycle count properly. - Statistics: Added "Total Energy" column to the Profile Statistics table, showing the cumulative energy consumed for each profile.
- Low-Rate Polling Support: Optimized default settings for devices with 30-60s update intervals (e.g., Shelly Cloud, Tuya), including a 30s watchdog and 180s off-delay.
- User Experience: moved "Review Learned Feedbacks" to the main menu (bottom) for easier access, and removed confusing options.
🛠️ Technical Improvements
- Logging: Added more granular
termination_reasonlogging (e.g.,smart,timeout,force_stopped) tocycle_detectorandprofile_store. - Timezone Robustness: Complete refactor to use timezone-aware datetimes (
dt_util.now()) exclusively, permanently fixing "offset-naive/offset-aware" comparison errors. - Strict Typing: Codebase now strictly adheres to type hinting, with extensive use of
TypeAliasanddataclassfor internal structures. - Performance: Optimized
last_match_detailssensor attribute to exclude large raw data arrays, preventing Home Assistant state update bloat. - Serialization: Fixed
MatchResultJSON serialization issues that were blocking sensor updates.
🐛 Bug Fixes
- Premature Termination & Dishwasher Logic: Major robustness improvements for dishwashers.
- Implemented "Verified Pause" logic to prevent early termination during long drying phases.
- Added "End Spike Wait Period": Dishwashers now wait up to 5 extra minutes after expected duration to capture final pump-out spikes.
- Increased Smart Termination duration ratio to 0.99 (from 96%) to ensure strictly conservative termination for dishwashers.
- Ghost Cycles: Enhanced filtering and elimination of false detection.
- Persistent Suppression: The "Suspicious Window" (20 min) now persists across restarts (restoring
last_cycle_end), preventing end-spikes from triggering ghosts after reboots. - Tail Preservation: Disabled "zero trimming" for confirmed completed cycles, preventing the "profile shrinking" feedback loop where tails were lost.
- Implemented
completion_min_secondslogic to ignore brief spikes.
- Persistent Suppression: The "Suspicious Window" (20 min) now persists across restarts (restoring
- Start/End Flutter: Start debounce and End repeat counts are now configurable and backed by robust accumulators, eliminating false starts/ends.
- Cycle Detector: Adjusted duration validation logic to strict 90%-125% window for completion.
- Translations: Fixed "intl string context variable not provided" errors in logs by properly passing placeholders to translation engine.
- Debug Sensors: Fixed "Top Candidates" sensor showing "None" due to missing data propagation.
- Code Quality: Addressed various linting issues (indentation, whitespace, unused arguments).
- Crash Fixes: Resolved
UnboundLocalErrorand specific edge-case crashes inprofile_store.pyduring migration. - Critical Fix (Runtime Matching): Fixed an issue where runtime profile matching was blocking the event loop and skipping DTW; now uses the full async pipeline.
- Legacy Data Repair: Added automatic reconstruction of missing
time_gridin old profile envelopes to prevent errors. - Validation: Fixed missing
dtw_bandwidthkey instrings.jsoncausing config flow validation errors. - Maintenance Safety: Fixed aggressive cleanup logic that was deleting empty/new profiles (pending training); these are now safely preserved.
- Test Suite: Fixed verification tests for Smart Termination and Profile Store matching.
📚 Documentation
- Visual Settings Guide: Expanded
SETTINGS_VISUALIZED.mdwith comprehensive documentation for 20+ parameters, organized into logical sections (Signal Conditioning, Detection, Matching, Integrity, Interruption, Learning, Notifications). - Complete Parameter Coverage: All advanced settings now documented with explanations, including
sampling_interval,watchdog_interval,profile_match_threshold,duration_tolerance,learning_confidence,auto_label_confidence, andabrupt_drop_ratio.
🧹 Cleanup & Removals
- Removed
auto_merge_gap_seconds: This setting was never used in the actual merge logic; removed from code, config flow, and translations. - Removed
auto_merge_lookback_hours: Similar unused legacy setting removed from codebase and UI. - Fixed Unused Imports: Cleaned up unused
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIOand duplicate import warnings. - Fixed F-String Warning: Removed empty f-string in config_flow post-process step.
⚠️ Deprecations
- Legacy Logic: Removed "consecutive samples" based detection in favor of time-aware accumulators.
- Sliders: Removed slider inputs in config flow in favor of precise text inputs.