"""Constants for the WashData integration.""" DOMAIN = "ha_washdata" # Configuration keys CONF_POWER_SENSOR = "power_sensor" CONF_NAME = "name" CONF_MIN_POWER = "min_power" CONF_OFF_DELAY = "off_delay" CONF_NOTIFY_SERVICE = "notify_service" # Deprecated - kept for migration only CONF_NOTIFY_ACTIONS = "notify_actions" CONF_NOTIFY_PEOPLE = "notify_people" CONF_NOTIFY_ONLY_WHEN_HOME = "notify_only_when_home" CONF_NOTIFY_FIRE_EVENTS = "notify_fire_events" CONF_NOTIFY_EVENTS = "notify_events" # Deprecated - kept for migration only CONF_NOTIFY_START_SERVICES = "notify_start_services" CONF_NOTIFY_FINISH_SERVICES = "notify_finish_services" CONF_NOTIFY_LIVE_SERVICES = "notify_live_services" CONF_NO_UPDATE_ACTIVE_TIMEOUT = "no_update_active_timeout" CONF_LOW_POWER_NO_UPDATE_TIMEOUT = "low_power_no_update_timeout" CONF_SMOOTHING_WINDOW = "smoothing_window" CONF_SAMPLING_INTERVAL = "sampling_interval" CONF_START_DURATION_THRESHOLD = ( "start_duration_threshold" # Debounce for start detection ) CONF_DEVICE_TYPE = "device_type" CONF_PROFILE_DURATION_TOLERANCE = "profile_duration_tolerance" CONF_INTERRUPTED_MIN_SECONDS = "interrupted_min_seconds" # Internal use only CONF_PROGRESS_RESET_DELAY = "progress_reset_delay" CONF_LEARNING_CONFIDENCE = "learning_confidence" CONF_DURATION_TOLERANCE = "duration_tolerance" CONF_AUTO_LABEL_CONFIDENCE = "auto_label_confidence" CONF_AUTO_MAINTENANCE = "auto_maintenance" CONF_PROFILE_MATCH_INTERVAL = "profile_match_interval" CONF_PROFILE_MATCH_MIN_DURATION_RATIO = "profile_match_min_duration_ratio" CONF_PROFILE_MATCH_MAX_DURATION_RATIO = "profile_match_max_duration_ratio" CONF_MAX_PAST_CYCLES = "max_past_cycles" CONF_MAX_FULL_TRACES_PER_PROFILE = "max_full_traces_per_profile" CONF_MAX_FULL_TRACES_UNLABELED = "max_full_traces_unlabeled" CONF_WATCHDOG_INTERVAL = "watchdog_interval" # Derived from sampling_interval CONF_MATCH_PERSISTENCE = "match_persistence" CONF_COMPLETION_MIN_SECONDS = "completion_min_seconds" CONF_NOTIFY_BEFORE_END_MINUTES = "notify_before_end_minutes" CONF_APPLY_SUGGESTIONS = "apply_suggestions" CONF_RUNNING_DEAD_ZONE = "running_dead_zone" # Seconds after start to ignore power dips CONF_END_REPEAT_COUNT = "end_repeat_count" # Number of times end condition must be met CONF_SHOW_ADVANCED = "show_advanced" # Toggle advanced settings CONF_MIN_OFF_GAP = "min_off_gap" # Minimum gap to separate cycles (seconds) CONF_START_ENERGY_THRESHOLD = "start_energy_threshold" # Wh required to confirm start CONF_END_ENERGY_THRESHOLD = "end_energy_threshold" # Wh allowed during end candidates CONF_START_THRESHOLD_W = "start_threshold_w" # Custom power threshold for STARTING CONF_STOP_THRESHOLD_W = ( "stop_threshold_w" # Custom power threshold for ENDING (hysteresis) ) CONF_EXPOSE_DEBUG_ENTITIES = "expose_debug_entities" # Expose detailed debug sensors CONF_SAVE_DEBUG_TRACES = ( "save_debug_traces" # Improve historical cycle data with rich debug info ) # Cycle interruption detection settings (not exposed in UI, but used internally) CONF_ABRUPT_DROP_WATTS = "abrupt_drop_watts" # Power cliff threshold for interrupted status CONF_ABRUPT_DROP_RATIO = "abrupt_drop_ratio" # Relative drop ratio for interrupted status CONF_ABRUPT_HIGH_LOAD_FACTOR = "abrupt_high_load_factor" # High load factor threshold CONF_AUTO_TUNE_NOISE_EVENTS_THRESHOLD = "auto_tune_noise_events_threshold" # Noise events before auto-tune CONF_EXTERNAL_END_TRIGGER_ENABLED = "external_end_trigger_enabled" # Enable external cycle end trigger CONF_EXTERNAL_END_TRIGGER = "external_end_trigger" # Binary sensor entity for external cycle end CONF_EXTERNAL_END_TRIGGER_INVERTED = "external_end_trigger_inverted" # Invert external trigger logic (trigger on OFF) CONF_ANTI_WRINKLE_ENABLED = "anti_wrinkle_enabled" # Dryer anti-wrinkle shielding CONF_ANTI_WRINKLE_MAX_POWER = "anti_wrinkle_max_power" # W threshold for anti-wrinkle spikes CONF_ANTI_WRINKLE_MAX_DURATION = "anti_wrinkle_max_duration" # Seconds to treat as anti-wrinkle CONF_ANTI_WRINKLE_EXIT_POWER = "anti_wrinkle_exit_power" # W threshold for true-off exit CONF_DELAY_START_DETECT_ENABLED = "delay_start_detect_enabled" # Enable delayed-start detection CONF_DELAY_CONFIRM_SECONDS = "delay_confirm_seconds" # Seconds power must stay in standby band before DELAY_WAIT engages CONF_DELAY_TIMEOUT_HOURS = "delay_timeout_hours" # Safety timeout (hours) while waiting to start # Deprecated since 0.4.5: drain-spike model replaced by band-based DELAY_WAIT. # Kept only so older Store/options blobs don't raise KeyError during migration. CONF_DELAY_DRAIN_MIN_POWER = "delay_drain_min_power" CONF_DELAY_DRAIN_MAX_POWER = "delay_drain_max_power" CONF_DELAY_DRAIN_MAX_DURATION = "delay_drain_max_duration" NOTIFY_EVENT_START = "cycle_start" NOTIFY_EVENT_FINISH = "cycle_finish" NOTIFY_EVENT_LIVE = "cycle_live" NOTIFY_EVENT_CLEAN = "cycle_clean" # Laundry still inside after cycle ends CONF_NOTIFY_TITLE = "notify_title" CONF_NOTIFY_ICON = "notify_icon" CONF_NOTIFY_START_MESSAGE = "notify_start_message" CONF_NOTIFY_FINISH_MESSAGE = "notify_finish_message" CONF_NOTIFY_PRE_COMPLETE_MESSAGE = "notify_pre_complete_message" CONF_NOTIFY_LIVE_INTERVAL_SECONDS = "notify_live_interval_seconds" CONF_NOTIFY_LIVE_OVERRUN_PERCENT = "notify_live_overrun_percent" CONF_NOTIFY_LIVE_CHRONOMETER = "notify_live_chronometer" CONF_NOTIFY_REMINDER_MESSAGE = "notify_reminder_message" # Distinct one-time pre-end alert CONF_NOTIFY_TIMEOUT_SECONDS = "notify_timeout_seconds" # Auto-dismiss after N seconds (0 = never) CONF_NOTIFY_CHANNEL = "notify_channel" # Android channel for status/live/reminder CONF_NOTIFY_FINISH_CHANNEL = "notify_finish_channel" # Distinct Android channel for finished/clean CONF_ENERGY_PRICE_STATIC = "energy_price_static" CONF_ENERGY_PRICE_ENTITY = "energy_price_entity" # Door sensor & pause CONF_DOOR_SENSOR_ENTITY = "door_sensor_entity" # Optional binary_sensor for machine door CONF_PAUSE_CUTS_POWER = "pause_cuts_power" # Also turn off switch entity when pausing CONF_SWITCH_ENTITY = "switch_entity" # Optional switch entity toggled on pause/resume CONF_NOTIFY_UNLOAD_DELAY_MINUTES = "notify_unload_delay_minutes" # Minutes before "laundry waiting" nag CONF_NOTIFY_UNLOAD_MESSAGE = "notify_unload_message" # Template for the clean-laundry nag message # Optional link to an existing HA device (e.g. the smart plug or appliance). # When set, the WashData device is exposed as "Connected via " through # the device registry's via_device relationship. Stores a device registry id. CONF_LINKED_DEVICE = "linked_device" DEFAULT_NOTIFY_TITLE = "WashData: {device}" DEFAULT_NOTIFY_START_MESSAGE = "{device} started." DEFAULT_NOTIFY_FINISH_MESSAGE = "{device} finished. Duration: {duration}m." DEFAULT_NOTIFY_PRE_COMPLETE_MESSAGE = "{device}: Less than {minutes} minutes remaining." DEFAULT_NOTIFY_REMINDER_MESSAGE = "{device}: about {minutes} minutes left." DEFAULT_NOTIFY_LIVE_WAITING_MESSAGE = "{device}: No profile matched yet." DEFAULT_NOTIFY_ONLY_WHEN_HOME = False DEFAULT_NOTIFY_FIRE_EVENTS = True DEFAULT_NOTIFY_LIVE_INTERVAL_SECONDS = 300 DEFAULT_NOTIFY_LIVE_OVERRUN_PERCENT = 20 DEFAULT_NOTIFY_LIVE_CHRONOMETER = False DEFAULT_NOTIFY_TIMEOUT_SECONDS = 0 # 0 = notifications never auto-dismiss DEFAULT_NOTIFY_CHANNEL = "" # Empty = omit channel (companion app default) DEFAULT_NOTIFY_FINISH_CHANNEL = "" # Empty = reuse status channel DEFAULT_NOTIFY_UNLOAD_DELAY_MINUTES = 60 # 1 hour before "still waiting" nag notification DEFAULT_NOTIFY_UNLOAD_MESSAGE = "{device} finished {duration}m ago - laundry is still inside." # Defaults DEFAULT_MIN_POWER = 2.0 # Watts DEFAULT_OFF_DELAY = 180 # Seconds (3 minutes, safer for 60s polling) DEFAULT_NAME = "Washing Machine" # Seconds without updates while active before forced stop (publish-on-change sockets) DEFAULT_NO_UPDATE_ACTIVE_TIMEOUT = 600 # 10 minutes DEFAULT_SMOOTHING_WINDOW = 2 DEFAULT_SAMPLING_INTERVAL = 30.0 # Seconds DEFAULT_START_DURATION_THRESHOLD = 5.0 # Seconds (debounce) DEFAULT_START_ENERGY_THRESHOLD = 0.2 # Wh - Require some energy accumulation before starting DEFAULT_END_ENERGY_THRESHOLD = 0.05 # Wh - Require effectively zero energy to end DEFAULT_DEVICE_TYPE = "washing_machine" DEFAULT_PROFILE_DURATION_TOLERANCE = 0.25 DEFAULT_INTERRUPTED_MIN_SECONDS = 150 # Internal use only, not exposed DEFAULT_PROGRESS_RESET_DELAY = 1800 # Seconds (30 minutes state expiry/unload window) DEFAULT_LEARNING_CONFIDENCE = 0.6 # Minimum confidence to request user verification DEFAULT_DURATION_TOLERANCE = 0.10 # Allow ±10% duration variance before flagging DEFAULT_AUTO_LABEL_CONFIDENCE = 0.9 # High confidence auto-label threshold DEFAULT_AUTO_MAINTENANCE = True # Enable nightly cleanup by default DEFAULT_COMPLETION_MIN_SECONDS = 600 # 10 minutes DEFAULT_NOTIFY_BEFORE_END_MINUTES = 0 # Disabled DEFAULT_PROFILE_MATCH_INTERVAL = ( 300 # Seconds between profile matching attempts (5 minutes) ) DEFAULT_PROFILE_MATCH_MIN_DURATION_RATIO = 0.10 # Allow match after 10% of expected duration DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO = ( 1.3 # Maximum duration ratio (130% of profile) - hidden default ) DEFAULT_MAX_PAST_CYCLES = 200 DEFAULT_MAX_FULL_TRACES_PER_PROFILE = 20 DEFAULT_MAX_FULL_TRACES_UNLABELED = 20 DEFAULT_WATCHDOG_INTERVAL = 30 # Derived: 2 * sampling_interval + 1 DEFAULT_MATCH_PERSISTENCE = 3 DEFAULT_RUNNING_DEAD_ZONE = 3 # Seconds after start to ignore power dips DEFAULT_END_REPEAT_COUNT = 1 # 1 = current behavior (no repeat required) # Matching & Termination Stability DEFAULT_MATCH_REVERT_RATIO = 0.4 # Drop from peak score to revert to detecting DEFAULT_DEFER_FINISH_CONFIDENCE = 0.55 # Minimum confidence to defer cycle finish # Cycle interruption detection defaults (internal) DEFAULT_ABRUPT_DROP_WATTS = 500.0 # Power cliff detection threshold (W) DEFAULT_ABRUPT_DROP_RATIO = 0.6 # 60% drop considered abrupt DEFAULT_ABRUPT_HIGH_LOAD_FACTOR = 5.0 # High load factor threshold DEFAULT_AUTO_TUNE_NOISE_EVENTS_THRESHOLD = 3 # Ghost cycles before threshold adjustment # Anti-wrinkle defaults (advanced; disabled by default) DEFAULT_ANTI_WRINKLE_ENABLED = False DEFAULT_ANTI_WRINKLE_MAX_POWER = 400.0 # W DEFAULT_ANTI_WRINKLE_MAX_DURATION = 60.0 # s DEFAULT_ANTI_WRINKLE_EXIT_POWER = 0.8 # W # Delayed-start detection defaults (disabled by default). # # The detector watches for sustained power between stop_threshold_w and # start_threshold_w (the "standby band"): a machine sitting in that band # for at least DEFAULT_DELAY_CONFIRM_SECONDS is in delayed-start mode, not # off and not running. Short menu-navigation peaks above the band are # ignored because they don't sustain long enough to satisfy the normal # start-duration gate. DEFAULT_DELAY_START_DETECT_ENABLED = False DEFAULT_DELAY_CONFIRM_SECONDS = 60.0 # s — sustained standby before DELAY_WAIT engages DEFAULT_DELAY_TIMEOUT_HOURS = 8.0 # h — give up waiting after this long # Pump Monitor settings (pump device type only) CONF_PUMP_STUCK_DURATION = "pump_stuck_duration" # Seconds before a running pump is flagged as stuck DEFAULT_PUMP_STUCK_DURATION = 1800 # 30 min - typical sump pump runs <60 s; 30 min implies motor is jammed EVENT_PUMP_STUCK = "ha_washdata_pump_stuck" # Fired when stuck-pump threshold is exceeded # Profile Matching Thresholds CONF_PROFILE_MATCH_THRESHOLD = "profile_match_threshold" CONF_PROFILE_UNMATCH_THRESHOLD = "profile_unmatch_threshold" DEFAULT_PROFILE_MATCH_THRESHOLD = 0.4 DEFAULT_PROFILE_UNMATCH_THRESHOLD = 0.35 CONF_DTW_BANDWIDTH = "dtw_bandwidth" DEFAULT_DTW_BANDWIDTH = 0.20 # 20% Sakoe-Chiba constraint CONF_SUPPRESS_FEEDBACK_NOTIFICATIONS = "suppress_feedback_notifications" DEFAULT_SUPPRESS_FEEDBACK_NOTIFICATIONS = False # Show persistent notifications by default # States STATE_OFF = "off" STATE_DELAY_WAIT = "delay_wait" STATE_IDLE = "idle" STATE_STARTING = "starting" STATE_RUNNING = "running" STATE_PAUSED = "paused" STATE_USER_PAUSED = "user_paused" STATE_ENDING = "ending" STATE_FINISHED = "finished" STATE_ANTI_WRINKLE = "anti_wrinkle" STATE_INTERRUPTED = "interrupted" STATE_FORCE_STOPPED = "force_stopped" STATE_RINSE = "rinse" STATE_UNKNOWN = "unknown" STATE_CLEAN = "clean" # Cycle ended but door not yet opened (laundry still inside) # Cycle Status (how the cycle ended) CYCLE_STATUS_COMPLETED = "completed" # Natural completion (power dropped) CYCLE_STATUS_INTERRUPTED = ( "interrupted" # Abnormal/short run or abrupt power cliff (likely user/power abort) ) CYCLE_STATUS_FORCE_STOPPED = "force_stopped" # Watchdog forced end (sensor offline) CYCLE_STATUS_RESUMED = "resumed" # Cycle was restored from storage after restart # Device Types DEVICE_TYPE_WASHING_MACHINE = "washing_machine" DEVICE_TYPE_DRYER = "dryer" DEVICE_TYPE_WASHER_DRYER = "washer_dryer" DEVICE_TYPE_DISHWASHER = "dishwasher" DEVICE_TYPE_COFFEE_MACHINE = "coffee_machine" DEVICE_TYPE_EV = "ev" DEVICE_TYPE_AIR_FRYER = "air_fryer" DEVICE_TYPE_HEAT_PUMP = "heat_pump" DEVICE_TYPE_BREAD_MAKER = "bread_maker" DEVICE_TYPE_PUMP = "pump" DEVICE_TYPE_OVEN = "oven" # Generic / unsupported bucket. Ships intentionally generic defaults that are # not tuned for any specific appliance, so the user must configure thresholds, # timeouts, and matching parameters themselves. Also serves as the runtime # fallback when a deprecated device type is hard-removed (see # DEPRECATED_DEVICE_TYPE_FALLBACK below). No curated phase catalog and no # device-type-specific branches in the runtime, so behavior is whatever the # user dials in. DEVICE_TYPE_OTHER = "other" DEVICE_TYPES = { DEVICE_TYPE_WASHING_MACHINE: "Washing Machine", DEVICE_TYPE_DRYER: "Dryer", DEVICE_TYPE_WASHER_DRYER: "Washer-Dryer Combo", DEVICE_TYPE_DISHWASHER: "Dishwasher", DEVICE_TYPE_COFFEE_MACHINE: "Coffee Machine", DEVICE_TYPE_EV: "Electric Vehicle", DEVICE_TYPE_AIR_FRYER: "Air Fryer", DEVICE_TYPE_HEAT_PUMP: "Heat Pump", DEVICE_TYPE_BREAD_MAKER: "Bread Maker", DEVICE_TYPE_PUMP: "Pump / Sump Pump", DEVICE_TYPE_OVEN: "Oven", DEVICE_TYPE_OTHER: "Other (Advanced)", } # Device types that ship as deprecated. They fail one of WashData's three fit # tests (user-selected discrete program, reproducible power signature, clean # return to OFF) so profile matching and time-remaining estimation produce # noise rather than signal. Kept in DEVICE_TYPES so existing config entries # load unchanged; filtered out of the new-entry picker in the config flow, # shown with a "(deprecated)" suffix when an existing entry already uses one, # and surfaced via a one-shot persistent_notification on integration startup. # Planned hard removal: 0.4.6 (two release cycles after this deprecation). DEPRECATED_DEVICE_TYPES = frozenset({ DEVICE_TYPE_COFFEE_MACHINE, DEVICE_TYPE_EV, DEVICE_TYPE_HEAT_PUMP, DEVICE_TYPE_OVEN, }) # Fallback device_type used at runtime once a deprecated type is hard-removed. # "Other (Advanced)" intentionally ships generic defaults so the integration # does not silently pretend an orphaned entry behaves like a washing machine. # Stored options are preserved as-is, so a user who had hand-tuned thresholds # on the old deprecated type keeps those values; the integration just stops # layering device-specific defaults underneath them. DEPRECATED_DEVICE_TYPE_FALLBACK = DEVICE_TYPE_OTHER # Device Type Defaults # Device Type Defaults (Maps) DEFAULT_NO_UPDATE_ACTIVE_TIMEOUT_BY_DEVICE = { DEVICE_TYPE_DISHWASHER: 14400, # 4 hours (Drying can be long) DEVICE_TYPE_HEAT_PUMP: 14400, # 4 hours (Heat pumps can run a long time with slow updates) DEVICE_TYPE_BREAD_MAKER: 7200, # 2 hours (Proving/Rising is very low-power for extended periods) DEVICE_TYPE_PUMP: DEFAULT_PUMP_STUCK_DURATION + 60, # Must exceed stuck-alarm threshold so the alarm fires before the watchdog DEVICE_TYPE_OVEN: 14400, # 4 hours (Slow roasts and pyrolytic self-clean can run for hours with thermostat-driven silence) } DEFAULT_MAX_DEFERRAL_SECONDS = 14400 # 4 hours max safe deferral # Issue #43: dishwasher end-of-cycle pump-out handling. # # A dishwasher's wash→drying drain wind-down produces brief power spikes mid # ENDING that, prior to the issue #43 fix, would set _end_spike_seen=True and # pre-arm Smart Termination — so the cycle closed at 99% of expected, BEFORE # the real end-of-cycle pump-out at ~99.5% of expected. The pump-out then # registered as a brand-new cycle. # # Two coordinated thresholds gate the fix. They MUST agree: the wait window # in _should_defer_finish (DISHWASHER_END_SPIKE_WAIT_SECONDS) is the upper # bound for keeping the cycle open without an end spike, and Smart # Termination's own wait branch in STATE_ENDING uses the SAME constant so the # two paths release the cycle at the same moment. # # A spike at < DISHWASHER_END_SPIKE_MIN_PROGRESS of expected duration is # ignored for end-spike tracking (the cycle still stays in ENDING via the # existing long_ending_tail path - this only governs the smart-termination # pre-arming). DISHWASHER_END_SPIKE_MIN_PROGRESS = 0.85 # Widened from 300s to 1800s after issue #43 follow-up: # real-world user reports showed Smart Termination misfiring ~4 min before the # end-of-cycle pump-out, and the original 5-min escape hatch wasn't generous # enough to cover that gap before the next reading arrived. 30 min is plenty # to capture even the latest pump-outs while still guaranteeing the cycle # closes eventually for dishwashers that have no pump-out at all. DISHWASHER_END_SPIKE_WAIT_SECONDS = 1800.0 DEFAULT_OFF_DELAY_BY_DEVICE = { DEVICE_TYPE_DISHWASHER: 1800, # 30 min (Drying) DEVICE_TYPE_COFFEE_MACHINE: 300, # 5 min (Warming/Pause handling) DEVICE_TYPE_HEAT_PUMP: 600, # 10 min (Defrosting pauses) DEVICE_TYPE_BREAD_MAKER: 300, # 5 min (Keep-warm phase after baking) DEVICE_TYPE_PUMP: 20, # 20 s (Pumps cut off sharply; no warm-down phase) DEVICE_TYPE_OVEN: 600, # 10 min (Thermostat off-cycles can be long while holding temp) } # Device-specific progress smoothing thresholds (percentage points) # These control how much backward progress is allowed before heavy damping kicks in DEVICE_SMOOTHING_THRESHOLDS = { DEVICE_TYPE_WASHING_MACHINE: 5.0, # Can have repeating phases (rinse cycles) DEVICE_TYPE_DRYER: 3.0, # More linear, less phase repetition DEVICE_TYPE_WASHER_DRYER: 5.0, # Combined washer+dryer, use washer defaults DEVICE_TYPE_DISHWASHER: 5.0, # Similar to washing machine with distinct phases DEVICE_TYPE_COFFEE_MACHINE: 2.0, # Short cycles, rapid transitions, less tolerance DEVICE_TYPE_AIR_FRYER: 2.0, # Constant load with sudden drop DEVICE_TYPE_HEAT_PUMP: 5.0, # Variable load, long periods DEVICE_TYPE_BREAD_MAKER: 5.0, # Large power swings between kneading, proving, baking DEVICE_TYPE_PUMP: 2.0, # Binary on/off spikes; minimal smoothing needed DEVICE_TYPE_OVEN: 5.0, # Bistable thermostat cycling between full heat and 0 W } CONF_VERIFICATION_POLL_INTERVAL = "verification_poll_interval" # Internal setting DEFAULT_VERIFICATION_POLL_INTERVAL = 15 # Seconds (rapid checks after delay) # Device specific completion thresholds (min run time to be considered a valid "completed" cycle) DEVICE_COMPLETION_THRESHOLDS = { DEVICE_TYPE_WASHING_MACHINE: 600, # 10 min DEVICE_TYPE_DRYER: 600, # 10 min DEVICE_TYPE_WASHER_DRYER: 600, # 10 min (same as washer) DEVICE_TYPE_DISHWASHER: 900, # 15 min DEVICE_TYPE_COFFEE_MACHINE: 60, # 1 min (Filter coffee cycle) DEVICE_TYPE_EV: 600, # 10 min DEVICE_TYPE_AIR_FRYER: 300, # 5 min minimum DEVICE_TYPE_HEAT_PUMP: 900, # 15 min minimum DEVICE_TYPE_BREAD_MAKER: 1800, # 30 min (even express bread takes 30+ min) DEVICE_TYPE_PUMP: 5, # 5 s - pump cycles can be under 30 seconds DEVICE_TYPE_OVEN: 600, # 10 min (covers quick reheats and ignores brief preheating tests) } # Default min_off_gap by device type (seconds) # If gap between cycles is larger than this, force new cycle. # If smaller, and we deemed previous as 'ended' but technically could be same, # we might want to handle that (though strict state machine usually suffices if tuned well). # Default min_off_gap by device type (seconds) # Default min_off_gap by device type (seconds) DEFAULT_MIN_OFF_GAP_BY_DEVICE = { DEVICE_TYPE_WASHING_MACHINE: 480, # 8 min (Soak handling) DEVICE_TYPE_DRYER: 300, # 5 min (Cool down gaps?) DEVICE_TYPE_WASHER_DRYER: 600, # 10 min (longer for combined cycles) DEVICE_TYPE_DISHWASHER: 3600, # 1 hour (Drying pauses) DEVICE_TYPE_COFFEE_MACHINE: 120, # 2 min (Session grouping) DEVICE_TYPE_EV: 900, # 15 min (Brief unplug/replug) DEVICE_TYPE_AIR_FRYER: 120, # 2 min (Shaking food) DEVICE_TYPE_HEAT_PUMP: 1800, # 30 min (Defrost cycle / resting gap) DEVICE_TYPE_BREAD_MAKER: 600, # 10 min (Resting between knead/prove keeps same cycle together) DEVICE_TYPE_PUMP: 60, # 1 min (Pumps can cycle every 3-5 min in heavy rain) DEVICE_TYPE_OVEN: 900, # 15 min (Bridge thermostat off-windows so one bake stays a single cycle) } DEFAULT_MIN_OFF_GAP = 60 # Scalar fallback # Default start energy threshold by device type (Wh) # Filter noise spikes (1000W * 0.01s = 0.002Wh). # Must be significant enough to imply mechanical work. DEFAULT_START_ENERGY_THRESHOLDS_BY_DEVICE = { DEVICE_TYPE_WASHING_MACHINE: 0.2, # ~50W for 15s or 200W for 3s DEVICE_TYPE_DRYER: 0.5, # Heater kicks in hard DEVICE_TYPE_WASHER_DRYER: 0.3, # Mix of washer and dryer DEVICE_TYPE_DISHWASHER: 0.2, # Pump/Heater DEVICE_TYPE_COFFEE_MACHINE: 0.05, # Short heater burst DEVICE_TYPE_EV: 0.5, # High power charging DEVICE_TYPE_AIR_FRYER: 0.2, # Heater kicks in DEVICE_TYPE_HEAT_PUMP: 0.2, # Compressor spins up DEVICE_TYPE_BREAD_MAKER: 0.2, # Kneading motor starts (~200W for a few seconds) DEVICE_TYPE_PUMP: 0.003, # ~100W motor for ~0.1 s is enough to confirm a pump cycle DEVICE_TYPE_OVEN: 0.5, # Heating element kicks in hard (~2-3 kW) - high gate filters incidental light/fan draws } # Default sampling interval by device type DEFAULT_SAMPLING_INTERVAL_BY_DEVICE = { # 2s captures the rapid 0<->150W motor/heater oscillation in wet appliances; # the 30s global default discards those spikes and undersamples the cycle. DEVICE_TYPE_WASHING_MACHINE: 2.0, DEVICE_TYPE_WASHER_DRYER: 2.0, DEVICE_TYPE_DISHWASHER: 2.0, DEVICE_TYPE_COFFEE_MACHINE: 10.0, # 10s is sufficient for brew cycles DEVICE_TYPE_PUMP: 10.0, # 10s - pump cycles can be <30 s; 30s default would miss them } # Default profile match min duration ratio by device type DEFAULT_PROFILE_MATCH_MIN_DURATION_RATIO_BY_DEVICE = { DEVICE_TYPE_DISHWASHER: 0.10, } # Storage STORAGE_VERSION = 5 STORAGE_KEY = "ha_washdata" # Notification events EVENT_CYCLE_STARTED = "ha_washdata_cycle_started" EVENT_CYCLE_ENDED = "ha_washdata_cycle_ended" # Signals SIGNAL_WASHER_UPDATE = "ha_washdata_update_{}" # Learning & Feedback SERVICE_SUBMIT_FEEDBACK = ( "ha_washdata.submit_cycle_feedback" # Service to submit feedback ) # Recorder STATE_RECORDING = "recording" CONF_RECORD_MODE = "record_mode" SERVICE_RECORD_START = "record_start" SERVICE_RECORD_STOP = "record_stop" # Thresholds for trim suggestions SHORT_SILENCE_THRESHOLD_S = 600 # 10 minutes TRIM_BUFFER_S = 60.0 # 1 minute buffer