Add mash-schedule (Sud) automation: temp/rate sequencing + stirrer switching

brewpi.py had a dead -m/--model CLI arg and sude/*.json schedules were
pure data with no code to drive them, despite the README documenting
them as if they were already wired up.

Add components/sud.py's Sud, which loads a sude/*.json schedule and
steps through its Rasten (ramp -> hold -> optional wait-for-user ->
next rest), and tasks/sud.py's SudTask, which drives the temperature
controller's theta_soll/heatrate_soll from the current rest and
switches the stirrer between continuous (ramping) and intermittent
(holding, via stirrSpeedRast/stirrDutyRast/stirrCycleTime) operation.
Exposes progress on a new "Sud" WebSocket channel and accepts
Start/Confirm commands. Enabled via a new optional top-level "sud"
config key (see config.json.templ/.sim).

"Reached target" is detected via abs(theta_ist - theta_soll_set) <
tolerance rather than tc.state == HOLD, since the latter can still
read HOLD left over from the previous rest for one tick after a new
target is pushed (tc.state only updates on the controller's own,
independently-scheduled process() tick).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 17:54:42 +02:00
co-authored by Claude Sonnet 4.6
parent 1e89a58370
commit 7e131df4ca
8 changed files with 188 additions and 6 deletions
+8 -1
View File
@@ -8,7 +8,8 @@ from components.sensor import TempSensorFactory
from components.pid import PidFactory
from components.plant import Pot
from components.actor import HeaterFactory, StirrerFactory
from tasks import TaskManager, TempSensorTask, HeaterTask, PotTask, TcTask, StirrerTask, TracerTask
from components.sud import Sud
from tasks import TaskManager, TempSensorTask, HeaterTask, PotTask, TcTask, StirrerTask, TracerTask, SudTask
from tracer import Tracer
import argparse as ap
@@ -61,6 +62,12 @@ if __name__ == '__main__':
stirrer_task = StirrerTask(stirrer, DT_TASK, dispatcher.msgio_get("Stirrer"))
taskmgr.add(stirrer_task)
# Mash schedule (optional)
sud_path = config.get('sud')
if sud_path:
sud = Sud(sud_path)
taskmgr.add(SudTask(sud, tc, stirrer, DT_TASK, dispatcher.msgio_get("Sud")))
# Tracer
taskmgr.add(TracerTask(sensor, heater, tc, trace_tc, DT_TASK_TRACER, dispatcher.msgio_get("Tracer")))