Skip to content

Feat/add mpc algo - #9

Merged
Gamso merged 12 commits into
mainfrom
feat/add-mpc-algo
Jun 13, 2026
Merged

Gamso merged 12 commits into
mainfrom
feat/add-mpc-algo

Conversation

@Gamso

@Gamso Gamso commented Apr 6, 2026 •

Copy link
Copy Markdown
Owner
  • Update docs/mpc_shadow_mode.md to reflect MPC production mode behavior and correct entity IDs
  • Remove mpc_shadow_mode/mpc_shadow_enabled from LEGACY_OBJECT_KEY_MAP in const.py to prevent unintended migration to production switch

Copilot AI review requested due to automatic review settings April 6, 2026 07:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an MPC-based controller path (“shadow” diagnostics plus an optional “production mode” override) to the Smart Fan Controller Home Assistant integration, along with new diagnostics, CSV logging fields, and supporting tests/docs.

Changes:

  • Add MPCShadowController (learned model + MPC-lite scoring) and integrate it into the control loop, optionally overriding the live rule-based fan decision in “production mode”.
  • Extend diagnostics/CSV logging with MPC shadow outputs (status, cost, confidence, predictions, disturbance bias) and add sensors for learned per-mode MPC profiles.
  • Add tests covering MPC shadow behavior (including defrost/HVAC-idle pausing) and update translations/docs.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
custom_components/smart_fan_controller/mpc_shadow.py Adds the MPC shadow/production controller implementation and diagnostics payload.
custom_components/smart_fan_controller/init.py Wires MPC evaluation into the control loop and introduces production override logic.
custom_components/smart_fan_controller/switch.py Adds a config switch to toggle MPC Production Mode.
custom_components/smart_fan_controller/sensor.py Adds MPC shadow diagnostic sensors and MPC learned-profile sensors.
custom_components/smart_fan_controller/data_collection.py Adds MPC shadow fields to CSV logging schema and recording.
custom_components/smart_fan_controller/const.py Adds production-mode option constants and legacy entity-key migration mappings.
custom_components/smart_fan_controller/config_flow.py Exposes MPC production mode in config/options UI.
tests/test_mpc_shadow.py New test suite for MPC shadow diagnostics/guardrails and CSV columns.
tests/test_hvac_idle.py Extends HVAC-idle coverage to include MPC shadow pause/decay behavior.
tests/test_data_collection.py Updates expected CSV row layout for new MPC columns.
README.md Updates docs to describe MPC controller + production mode switch.
docs/mpc_shadow_mode.md Adds MPC shadow mode documentation (currently inconsistent with production mode).
custom_components/smart_fan_controller/translations/en.json Adds translation strings for MPC production mode option.
custom_components/smart_fan_controller/translations/fr.json Adds translation strings for MPC production mode option.
scripts/analyze_mpc_viability.py Adds offline analysis script for comparing live vs MPC shadow behavior.

Comment thread custom_components/smart_fan_controller/__init__.py Outdated
Comment thread custom_components/smart_fan_controller/const.py Outdated
Comment thread custom_components/smart_fan_controller/data_collection.py Outdated
Comment thread scripts/analyze_mpc_viability.py Outdated
Comment thread docs/mpc_shadow_mode.md Outdated
Copilot finished work on behalf of Gamso April 6, 2026 07:46
Copilot finished work on behalf of Gamso April 6, 2026 08:03
@Gamso
Gamso force-pushed the feat/add-mpc-algo branch 2 times, most recently from 56d7179 to 8773bbd Compare April 6, 2026 08:45
- Runs an MPCController in observation mode every control cycle (shadow learning)
- Adds a production mode toggle switch; when enabled, the MPC decision replaces
  the rule-based heuristic for direct fan control
- Shares the ThermalLearning backend with the heuristic controller; idle, defrost,
  and window-open periods are excluded from learning and MPC evaluation
- Exposes 12 diagnostic sensors (status, reason, fan mode, cost, confidence,
  predicted temperatures at 10 and 30 min, dead time, known profiles, disturbance
  bias, match, would-change-now)
- Adds CSV data-collection columns for offline MPC analysis
- Entity migration mapping included so existing HA installations keep history
@Gamso
Gamso force-pushed the feat/add-mpc-algo branch from 8773bbd to e6f9062 Compare April 6, 2026 09:58
Gamso added 4 commits April 12, 2026 14:59
…pe service

Learning data integrity:
- Use median (not mean) for effective slope computation, resisting
  outlier contamination from VTherm EMA inertia after mode transitions
- Add 30-minute learning cooldown after large setpoint drops
  (SETPOINT_DROP_LEARNING_COOLDOWN) to avoid recording stale slopes
- Require fan mode to be active for ≥ 2× dead_time before accepting
  slope samples (MIN_ESTABLISHED_RATIO), ensuring the EMA has fully
  converged to the current mode's thermal response

MPC monotone constraint:
- Enforce slope(mode_i) ≤ slope(mode_i+1) via isotonic forward pass
  when all fan-mode profiles are learned; skip when profiles are partial
- Prevents contaminated profiles from causing MPC to rank a weaker
  fan mode above a stronger one

New service — set_effective_slope:
- Manually override the effective slope for a specific fan mode / HVAC
  mode profile by replacing stored samples with synthetic ones
- Useful for correcting contaminated profiles without full learning reset
Remove the rule-based heuristic controller (controller.py) and make MPCController the sole, always-active decision engine.

Configuration cleanup:
- Remove soft_error, hard_error, learning_enabled, mpc_production_enabled parameters (config flow, translations, options flow)
- Remove production_mode and enabled toggles from MPCController
- Remove step-down limit (_apply_step_down_limit); step-down hold remains
- Empty the switch platform (no more user-facing switches)

Sensor/data cleanup:
- Remove MPC Match, Learned Soft Error, Learned Hard Error sensors
- Remove mpc_match column from CSV data collection
- Remove learning_enabled attribute from ThermalLearning; always on
- Rename data_collection shadow parameter to mpc_decision

Migration cleanup:
- Remove LEGACY_OBJECT_KEY_MAP and extract_object_key_from_unique_id
- Remove _async_migrate_entity_registry and entity registry import
- Users upgrading from a legacy install start fresh

Tests:
- Delete test_entity_migration.py (migration removed)
- Delete heuristic controller tests; replace with MPC-focused test suite
- Rename all test_shadow_* functions to test_mpc_*
- Remove production_mode, enabled, live_decision_fan from all test calls

Documentation:
- Clean README, docs/mpc_mode.md, copilot-instructions, and skill files of any reference to removed features or prior architecture
- route services to the correct config entry via climate_entity
- prevent duplicate climate entries in config flows
- apply learned settings to runtime options and reset auto-apply state on learning reset
- scope sensor entity IDs by climate entity and migrate legacy IDs automatically
- enrich CSV audit rows with live control-loop metrics
- update tests, translations, and README for the new behavior
…guard, code smells

- Extract MPC cost function magic numbers into named module-level constants
  (COMFORT_ERROR_WEIGHT, OVERSHOOT_QUADRATIC_WEIGHT, URGENCY_SENSITIVITY, etc.)
- Add fan_modes ordering validation: warn when learned slopes violate monotonicity
- Fix retry storm on fan command failure: update last_change_time even on error
  to prevent rapid-fire retries when the climate API is broken
- Replace _services_registered flag with hass.services.has_service() for
  race-free service registration across concurrent entry setups
- Fix data_collection rotated path using os.path.splitext() instead of
  fragile str.replace('.csv', '_old.csv')
- Fix thermal_learning serialization key slope_M2 → slope_m2 for naming
  consistency (with backward-compatible deserialization)
- Extract _should_collect_slope_sample() helper from nested learning gate
- Extract _update_sensors() helper to deduplicate sensor update loops
- Delete unused switch.py stub and remove Platform.SWITCH from PLATFORMS
- Add replay_bench.py to replay csv file.
@Gamso
Gamso force-pushed the feat/add-mpc-algo branch from b763fb5 to 8dda89c Compare April 14, 2026 18:40
Repository owner deleted a comment from Copilot AI Apr 14, 2026
Gamso and others added 7 commits April 22, 2026 22:00
- treat idle operating state as HVAC idle during disturbance detection
- preserve sparse learned profiles and enforce monotone known slopes during partial learning
- make replay bench snapshot-aware with time window filtering for real-trace analysis
- add real trace datasets, regression tests, and reference analysis notes
fix: guard against None in data_collection projected_temperature round()
- Implement dynamic, adaptive simulation horizon length based on dead times to eliminate lag blindness
- Decouple dead-time tracking separately for heat and cool modes with robust schema fallback migrations
- Transition mode-ranking weights to physical non-linear power curves [1.0, 1.5, 3.0, 6.0]
- Refactor comfort cost to calculate step-by-step urgency dynamically inside the simulation loop
- Re-establish strict zero comfort cost within target deadbands
…loop abort

The response_events list contains 3-tuples (timestamp, minutes, hvac_mode)
since the hvac_mode field was added, but sensor.py was still unpacking them
as 2-tuples (`for _, t in learning.response_events`), raising a ValueError.

Because _update_sensors() calls sensor.async_write_ha_state() synchronously
before _async_apply_fan_change(), this exception was propagating up through
run_control_loop and aborting the cycle before any fan change could be applied.
This caused the MPC-recommended mode (e.g. superhigh) to never be set on the
climate entity, requiring manual intervention.

- sensor.py: use item[1] indexing instead of 2-tuple unpacking
- thermal_learning.py: remove unused `ts` variable in get_dead_time()
- tests: add regression test covering mixed 2- and 3-tuple response_events
@Gamso
Gamso merged commit d39c800 into main Jun 13, 2026
2 checks passed
@Gamso
Gamso deleted the feat/add-mpc-algo branch July 1, 2026 10:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants