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>
brewpi.py passed a literal 20 as theta_amb to Pot, and
temp_controller_smith.py's two internal Pot models (and every demo's
Pot/TempController instantiation) silently relied on Pot's default of
20 instead. Add a top-level ambient_temperature key to
config.json(.templ/.sim), thread it through brewpi.py into both the
real plant and the Smith predictor's models via a new theta_amb param
on TempController, and make the demos pass it explicitly too.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[Sensor] - replace TempSensorSim's hardcoded k_noise with configurable
temp_offset/variance, accept **kwargs on Max31865 too so both sensors
share a constructor signature usable from TempSensorFactory.create()
[Temp Controller] - low-pass filter the backward-difference heat-rate
estimate (alpha=0.1) in both temp_controller.py and
temp_controller_smith.py instead of using the raw, noisy derivative
[Pot] - drop kn from demo_pot.py's params, matching the unused-field
removal already made to pot.py itself
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[brewpi] - drop trace vars for the dtheta_ist_* attributes removed
when temp_controller_smith.py dropped Kalman filtering
[Pot] - drop unused kn field (never read since the noise-injection
code that used it was dropped)
[GUI] - default WS connection to ws://localhost:8765
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ws/message.py, ws/server/ws_server_multi_user.py: drop the loop= kwarg
and replace asyncio.wait(coroutines) with asyncio.gather(), both of
which were removed/forbidden in Python 3.10+.
- brewpi/requirements.txt: pin websockets==10.4, the latest version that
still supports this code's serve()/handler API.
- config.json.sim: move Model to the top level and add the missing gain
field so it matches the schema brewpi.py and config.json.templ expect.
- brewpi/brewpi.py: add -c/-m/-s/-d CLI args for selecting config/model
files, simulation mode, and debug.
- .gitignore: exclude __pycache__, logs, the local config.json, and
editor/venv directories.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Splits the server, GUI client, and websocket layer into their own
packages with dedicated requirements.txt files, and adds a README
documenting the project's architecture and usage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>