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 Assistant restart.
- Live Notification Interval Constraint: Added minimum-value constraint documentation for
notify_live_interval_secondsacross all supported languages, guiding users away from excessively short polling intervals. - Diagnostics Sensitive Data Redaction: Config entry diagnostics now automatically redact personally identifiable fields (
notify_service,notify_people,notify_actions,power_sensor,external_end_trigger) before the report is generated. - Services Description Consistency: Updated
services.yamldescriptions to consistently refer to "WashData" instead of "washing machine", accurately reflecting multi-device support. - Enhanced Issue Templates: Overhauled bug report and feature request templates to cover all 8 supported device types (Washing Machine, Dryer, Washer-Dryer Combo, Dishwasher, Coffee Machine, Electric Vehicle, Air Fryer, Heat Pump). Improved debug log guidance to clearly distinguish copy-paste (short logs) from file attachment (long logs), and added a separate note explaining that HA diagnostics exports are distinct from debug logs and must be attached as files. Added a pre-submission checklist requiring reporters to confirm they searched for duplicates and filled in all applicable fields. Translation contributions are now handled via a dedicated Pull Request template instead of an issue template.
- Issue Triage Automation: Added a GitHub Actions workflow that automatically validates new and edited issues, comments with targeted guidance on any missing required sections, and applies a
needs-more-infolabel — keeping the issue queue actionable without manual triage. Supports adry_runinput for safe testing without posting comments or modifying labels. - Auto-Close Done Issues: Added a GitHub Actions workflow that automatically closes open issues labelled
doneafter 5 days of inactivity, provided the last comment is from the maintainer. Posts a closing comment with reopen instructions before closing.
🐛 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 height mismatch that could render legend content outside the visible area.
- Global Phase Duplication in Selectors: Fixed edit/delete phase dialogs duplicating "All Devices" phases once per device type by introducing scoped deduplication and explicit scope keys.
- Phase Scope Resolution in Edit/Delete: Fixed phase edit/delete operations to resolve against the selected phase scope (
device_type) so similarly named phases remain deterministic. - Notification Action Script Context: Fixed Home Assistant script action execution by passing a valid
Contexttoscript.async_run, resolving runtime errors like "Running script requires passing in a context". - Notification Dispatch Ordering: Fixed a routing regression where configured actions could suppress notify-service delivery; actions and mobile notifications now run together as expected.
- Live State Coverage: Fixed live-progress gating to continue updates during
STATE_ENDING(not onlyRUNNING/PAUSED) until cycle completion. - Legacy Phase Diagnostic Confusion: Removed stale diagnostic phase entity behavior and added cleanup of the old
wash_phaseregistry entry to prevent misleading duplicate/legacy phase sensors. - Orphaned Diagnostic Entity Cleanup: Added automatic registry reconciliation for diagnostic entities on startup and profile updates, removing stale unavailable duplicates (including old
profile_count_*entries left behind by profile renames) without manual per-device cleanup. - End-Spike Revert to Running: Fixed a bug where a power burst during the
ENDINGstate (e.g. the final pump-out on a dishwasher) could incorrectly transition the device back torunning, delaying the reported cycle end by several minutes. Thelong_ending_tailguard (ignore any spike after 120 s inENDING) now applies to all device types, not only dishwashers. _time_in_stateReset After Config Reload: Fixed a bug where reloading the integration configuration reset the in-state timer to zero. On the next power spike the guard checked0 s >= 120 sand failed, causing a falseending → runningtransition. The timer is now recomputed from the persistedstate_enter_timeon restore so the guard works correctly across reloads.- Cycle Split "Unknown Error" (#167): Fixed a
TypeError: argument must be strcrash inanalyze_split_synccaused by a dead-codedt_util.parse_datetime(cycle["start_time"])call that failed whenstart_timeis adatetimeobject rather than an ISO string. The parsed value was never used; the line has been removed. - Unknown Cycle Statistics Empty (#168): Fixed two root causes for statistics (graph and energy) being blank after assigning an unknown cycle to a new profile.
_rebuild_envelope_syncnow uses the shared_decompress_power_data()helper instead of raw iteration withfloat()conversion. The raw conversion silently discarded every data point for cycles stored in the legacy ISO-timestamp format, producing an empty envelope and a blank graph.create_profile_standalonenow labels the reference cycle with the new profile name (when it is currently unlabeled) and immediately rebuilds the envelope, so statistics are populated as soon as the profile is created — without requiring a separate feedback-correction step.
- Merge Preview Graph Empty: Fixed the cycle merge preview showing a blank graph. When cycles have no recorded power data, a proper "No power data available for preview" placeholder is rendered instead of a broken empty image. The config flow also falls back to italic text if the SVG cannot be built.
- Broken Duration After Merge: Fixed merged cycles showing wildly incorrect durations (e.g.
-29555478m). Two root causes were addressed:- Sort order: cycles were sorted lexicographically by ISO timestamp string, which gives wrong chronological order when start times use different UTC offset representations (e.g.
+01:00vs+02:00). Sorting now uses the parsed UTC timestamp. - Corrupt
last_t_abs: the end timestamp was taken from the last element of the power-data list, which could be a corrupted or out-of-order entry near Unix epoch. The merge now takes the maximum absolute timestamp across all collected data points, and falls back to the cycle's storedend_timefield when no power data is available at all.
- Sort order: cycles were sorted lexicographically by ISO timestamp string, which gives wrong chronological order when start times use different UTC offset representations (e.g.
manual_durationPersisting After Merge: The merged cycle no longer inherits the source cycle'smanual_durationoverride, ensuring the freshly computed duration is always displayed.- Merge Preview SVG Encoding: Fixed the merge-preview placeholder SVG (shown when cycles contain no recorded power data) to properly XML-escape the title and label strings. Profile names or translated strings containing
&,<, or>no longer produce malformed SVG output. - Translation Corrections (Bosnian, Traditional Chinese): Removed stray zero-width space characters (U+200B) from the Bosnian
no_power_previewstring, and corrected the Traditional Chineseno_power_previewvalue which was incorrectly using Simplified Chinese characters (没有/预览/数据→沒有/預覽/數據).
🧪 Tests
- HA Test Harness Adoption: Replaced
MagicMock-based hass objects with realHomeAssistantinstances frompytest_homeassistant_custom_componentacross all new test modules. OnlyProfileStoreandCycleDetectorare patched as true external I/O boundaries. - Event Payload & Ghost Cycle Tests (
test_manager_event_payload_and_ghosts.py): CoversEVENT_CYCLE_ENDEDpayload field exclusion (power_data,debug_data,power_trace) and ghost-cycle energy threshold detection using real HA event bus listeners. - Migration Harness Tests (
test_migration_harness.py): Validatesasync_migrate_entryfield movement (data → options), idempotency on re-run, and no-op behaviour when already at the latest schema version. - Pre-Completion Notification Tests (
test_manager_precompletion_harness.py): Pins the ambiguity gate — notifications are suppressed when_last_match_ambiguous=True, sent exactly once when unambiguous, and not re-sent on subsequent calls. - Match Persistence / Transition Tests (
test_manager_matching_harness.py): Covers the full persistence-counter state machine inside_async_do_perform_matching: single-call accumulation, below-threshold staying atdetecting..., threshold commit, profile-change counter reset, high-confidence override bypassing persistence, and ambiguous-result gating. - Live Notification Harness (
test_manager_live_notifications.py): Added focused coverage for mobile-only routing, payload keys, overrun cap enforcement, away-mode deferred live coalescing, clear-on-end behavior,STATE_ENDINGsupport, one-time pre-match waiting message, and post-match periodic update activation. - Phase Catalog Atomic Operations (
test_issue_166_phase_catalog.py): Validates that phase renames and deletions behave atomically with unique-ID resolution, preventing unintended collisions or cascading updates across scopes. - Profile Sensor Attributes (
test_profile_sensor_attributes.py): Covers the exposure of new sensor attributes includingconsistency_minon profile sensors. - Diagnostic Entity Cleanup (
test_diagnostic_entity_cleanup.py): Validates automatic removal of orphaned diagnostic entities on startup, covering stale profile count sensors, legacywash_phaseentries, and debug entity visibility toggling based onexpose_debug_entities.
New Contributors
- @rrooggiieerr made their first contribution in #149
Full Changelog: v0.4.2.1...v0.4.3