Skip to content

v0.4.4-alpha

Pre-release
Pre-release

Choose a tag to compare

@3dg1luk43 3dg1luk43 released this 20 Apr 20:35

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 (any sensor.*, input_number.*, or number.* 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 single ha_washdata_pump_stuck HA 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_today sensor (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_count sensor (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, and countdown fields 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_diagnostics now includes a live_diagnostics snapshot from a new in-memory DiagBuffer - 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_cycle HA services (both accept device_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 call switch.turn_off / switch.turn_on on 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 sets verified_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_export at the top level, e.g. copy-pasted from a log or API response); and (3) the full HA diagnostics download file (the .json file downloaded via Settings → Devices & Services → (device) → Download Diagnostics), which wraps our data in a home_assistant / data envelope. When importing from a diagnostic source, any fields replaced by the redactor (**REDACTED**) are silently stripped, so sensitive fields such as power_sensor and notify_service are 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). Run python3 devtools/analyze_diag.py <export.json> from the repo root with the venv activated; see devtools/README.md for 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 new process_cycle_end hook 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_gap recommendation 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.DataLoader now handles both the new data → store_export → data → past_cycles export path and the legacy data → store_data → past_cycles path, so all user-contributed diagnostic exports in cycle_data/ are usable by the benchmark suite.
  • Dishwasher Post-Cycle Ghost Suppression (#43): The ghost suppressor now uses a 10-minute suspicious window for dishwashers (vs. 3 minutes for other devices) to catch drain pump-outs that fire 3–8 minutes after the main cycle ends. Within that window, a pump-out is suppressed faster: 3 minutes elapsed + 1 minute of silence, instead of the standard 10-minute/5-minute thresholds. A secondary fallback guard in _on_cycle_end independently suppresses any cycle starting within 10 minutes of the previous cycle end if it lasted under 5 minutes and consumed less than 1 Wh.
  • Confidence Improves With Confirmed Cycles (#199): When a profile has ≥2 confirmed labeled cycles, the envelope's averaged power curve is now used as the matching reference instead of the original single sample cycle. Because the envelope represents the average of all user-confirmed runs, repeated correct detections now produce progressively higher (and more stable) confidence scores rather than staying frozen at the first sample's similarity level.
  • Suppress Feedback Notifications (#199): Added a new Suppress Feedback Notifications toggle under Advanced Settings. When enabled, WashData still tracks pending feedback and records confirmations internally, but stops creating persistent "Verify Cycle" notifications in the HA sidebar - useful for devices like dishwashers that are always detected correctly and need no ongoing prompting.
  • Feedback Threshold Settings Now Exposed in UI (#199): Auto-Label Confidence and Feedback Request Confidence are now available as editable fields under Advanced Settings. Previously these keys existed internally but could only be changed by directly editing config entries. Lowering Auto-Label Confidence below the observed match score allows consistent detections to be auto-labeled without any user prompt.
  • Profile Granularity Guide (#201): The Create New Profile form now shows inline guidance explaining what the matcher uses (power shape, duration, total energy) and what to expect from different levels of profile detail - including which program combinations are reliably auto-detected (e.g. wash-only vs wash+dry), which may need manual confirmation at first (temperature-only variants of the same programme), and how to approach a complex machine like a washer-dryer combo. The README also has a new "Profile Granularity" section (§ 3 of the Getting Started guide) with the same information in a more detailed, tabular form.

🐛 Bug Fixes

  • Pause/Resume Buttons Always Shown as Unavailable: The Pause Cycle and Resume Cycle button entities never registered state-change callbacks, so their available property was evaluated only once at HA startup (when no cycle is active → False) and never refreshed. Both buttons now subscribe to SIGNAL_WASHER_UPDATE via async_added_to_hass and call async_write_ha_state() on each manager update, making them correctly enable when a cycle is running and disable when it ends.
  • Start Notification Fired Up to 36 Minutes Late (#206): The cycle-start push notification was gated on profile detection completing, meaning it did not fire until 3 consecutive profile matches had been confirmed at 5-minute intervals (≥15 minutes minimum, often 30–36 minutes for dishwashers due to their 10% minimum-duration ratio before the first match attempt). The HA bus event (ha_washdata_cycle_started) already fired immediately - only the push notification via notify.* services was delayed. The notification is now dispatched at the moment the appliance transitions to RUNNING, regardless of whether a program has been identified yet. Users who included {program} in their start message template will now see "detecting..." until a match is confirmed; the default message has been updated to "{device} started." (without {program}) to avoid the awkward placeholder. A restart-recovery fallback remains in the profile-matching loop in case the notification was missed due to an HA restart before the first cycle snapshot was saved.
  • Dishwasher ECO Cycle Killed During Passive Drying (#43): Two separate bugs caused unmatched dishwasher cycles to be terminated hours before the physical run finished, leaving a 60–90 minute gap of missing data and triggering spurious pump-out ghost cycles.
    • Bug A - Watchdog force-ended unmatched cycles too early: The 4-hour (14400 s) device-specific silence floor - intended to cover dishwasher passive open-door drying - was only applied when a profile had already been matched. An unmatched cycle (program == "detecting...") fell back to the 1-hour (3600 s) base timeout. Because dishwasher ECO programs use very little power and can go completely silent for 1–2 hours while drying, the watchdog killed the cycle roughly 1 hour after the last sensor update - while the appliance was still running. The floor is now applied unconditionally regardless of match state.
    • Bug B - Cycle detector held ENDING state open for hours after pump-out: After the watchdog was fixed, the CycleDetector itself held the cycle in ENDING state until min_off_gap (up to 9000 s = 2.5 h) had elapsed after the pump-out spike. For an unmatched cycle that has already seen a terminal end spike, the full gap is unnecessary. The effective_off_delay is now capped at 1800 s (30 min) in this case, so the cycle closes cleanly ~30 minutes after the pump-out rather than sitting open for hours.
  • Wipe ALL Data Now Clears Suggested Settings (#185): "Wipe ALL data for this device" previously left the suggested_settings sensor populated with stale values. The wipe operation now also clears suggestions, envelopes, feedback history, pending feedback, and auto-adjustment logs - all data derived from the cycle history that was just deleted.
  • Applied Suggestions No Longer Reappear (#185): After a user applied suggested settings, the sensor count stayed at the old value and the suggestions reappeared immediately on the next cycle because the engine re-computes them from the same data. Suggestions are now cleared from the store when the user saves the options form after confirming them. Additionally, the suggestion engine now skips generating a suggestion for any key whose recommended value already matches the current config option, so accepted suggestions stay silent until the underlying data actually shifts.
  • Corrected Cycle Duration Not Saved (#155): When a user selected the "Correct" action in the learning feedback dialog and changed the cycle duration, the correction was applied but the profile's avg_duration statistic was not always updated. If the cycle had no power data (e.g. imported or stripped by retention), async_rebuild_envelope skipped the stats update entirely and returned early, leaving the profile showing the old wrong duration. The stats update (min/max/avg) is now computed directly from the stored cycle durations even when the envelope shape cannot be built. Additionally, if no target profile can be determined (both the form field and the pending feedback's detected profile are absent), the duration correction is now applied directly to the cycle using its existing profile, and a warning is logged - previously it was silently dropped.
  • Current Phase Stays On Post-Cycle (#192): The "Current Phase" sensor kept showing the last detected phase (e.g. "Spin") after a cycle ended instead of resetting to "Off". _last_match_result - which carries the matched phase label - was passed to the learning manager at cycle end but never cleared in the state teardown block. The sensor's phase_description property reads _last_match_result.matched_phase first, so it remained stuck on the last phase until the next periodic _update_estimates call (up to 5 minutes later). _last_match_result is now cleared in the same teardown block as the other cycle state, immediately after the learning call that needs it.
  • Publish-on-Change Sensor Cycle Close Delay (#197): Three related bugs caused cycles to remain open for up to 58 minutes after an appliance physically finished when the power sensor only publishes on value change (e.g. LightwaveRF). Once the appliance stabilised at a standby value below the stop threshold, the sensor went completely silent and the cycle could not close promptly.
    • Bug A - No-Update Timeout ignored for low-power silence: The user-configurable No-Update Timeout (no_update_active_timeout) only applied to high-power states. For low-power silence the watchdog used a separate 3 600 s (low_power_no_update_timeout) ceiling, so a user who set "No-Update Timeout" to 140 s had no effect at all on how quickly the cycle closed after the appliance reached standby. The low-power watchdog now also honours no_update_active_timeout: once real-update silence exceeds this threshold (and no verified pause is active), a 0 W keepalive is injected on every watchdog tick to advance the off-delay accumulator. With the default 116 s watchdog interval, cycles now close within ~6 minutes of going to standby. Verified pauses (e.g. dishwasher drying confirmed by envelope alignment) continue to be exempted so legitimate long silent phases are unaffected.
    • Bug B - Profile match always returned expected_duration = 0.0: Profiles created before avg_duration tracking was introduced (or imported without duration data) stored avg_duration = 0. The snapshot-building loop passed this zero to the analysis worker, which propagated it as the match result's expected_duration. Every match attempt logged a "update_match: invalid raw_expected_duration" warning and the time-remaining estimate stayed at zero throughout the cycle. The snapshot builder now falls back through profile.avg_duration → cycle.duration → len(segment) × dt so a zero value is always replaced with a computed estimate.
    • Bug C - update_match log always said (> 6h) for zero durations: The validation branch that rejected expected_duration <= 0 shared a single log message with the > 6h branch, making it impossible to tell from logs whether the rejection was a zero/negative value or an implausibly large one. The two conditions are now logged separately as (<= 0) and (> 6h).
  • Duplicate Notifications Suppressed: When multiple notification targets were configured, WashData could dispatch the same push notification more than once for a single event if a service appeared in more than one list. _dispatch_notification now deduplicates the resolved service set before sending, so each notify.* service receives at most one call per event regardless of how many event-type lists it appears in.
  • Stale Notification Services Cleared on Save: Removing a notify.* service from one of the multi-target lists and saving options could leave the old entry in the in-memory runtime set, continuing to fire notifications to the removed service until the next HA restart. The options-flow reload path now fully rebuilds _notify_start_services, _notify_finish_services, and _notify_live_services from the saved config on every options save, ensuring removed services take effect immediately.

New Contributors