From d0a137e39ebe9a74ad468d19ca309e5a2a60fa46 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Tue, 30 Jun 2026 19:45:13 +0200 Subject: [PATCH] config: seed plant params from Pot.water_mass at server startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sud._parse_data now falls through to pot_config for all hardware keys (mass, material, L, Td) — EMPTY_SUD no longer shadows them. brewpi.py seeds pot/tc plant params from Pot.water_mass on startup so the simulated plant is configured before any sud is loaded. Co-Authored-By: Claude Sonnet 4.6 --- components/sud.py | 21 ++++----------------- server/brewpi.py | 24 ++++++++++-------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/components/sud.py b/components/sud.py index b258f2a..5a5e9a2 100644 --- a/components/sud.py +++ b/components/sud.py @@ -61,19 +61,6 @@ EMPTY_SUD = { 'Name': '', 'Description': '', 'pot': { - 'mass': 0, - 'material': None, - # Pot energy loss coefficient [W/(kg*K)] and transport propagation - # delay [s] - see derive_plant_params(). Defaults match the generic - # values brewpi.py used to hardcode for every brew alike. - 'L': 0.2, - 'Td': 30, - # Initial grain_mass/water_mass - what's actually in the pot before - # the brew starts (e.g. water added but no malt yet), as opposed to - # default.step's pot.grain_mass/pot.water_mass, which is just inert - # template filler for steps that don't override it. Lets a caller - # derive starting plant params directly (see SudTask.recv()'s Load - # handler) without having to parse the first step out of the schedule. 'grain_mass': 0, 'water_mass': 0, }, @@ -123,10 +110,10 @@ class Sud(AttributeChange): pot_data = data.get('pot', {}) pot_mass = pot_data.get('mass', pot_config.get('mass', 0)) pot_material = pot_data.get('material', pot_config.get('material')) - L = pot_data.get('L', pot_config.get('L', EMPTY_SUD['pot']['L'])) - Td = pot_data.get('Td', pot_config.get('Td', EMPTY_SUD['pot']['Td'])) - grain_mass = pot_data.get('grain_mass', EMPTY_SUD['pot']['grain_mass']) - water_mass = pot_data.get('water_mass', EMPTY_SUD['pot']['water_mass']) + L = pot_data.get('L', pot_config.get('L', 0.2)) + Td = pot_data.get('Td', pot_config.get('Td', 30)) + grain_mass = pot_data.get('grain_mass', 0) + water_mass = pot_data.get('water_mass', 0) return name, description, schedule, pot_mass, pot_material, L, Td, grain_mass, water_mass diff --git a/server/brewpi.py b/server/brewpi.py index fbc889c..40e9362 100755 --- a/server/brewpi.py +++ b/server/brewpi.py @@ -92,14 +92,6 @@ if __name__ == '__main__': sensor_task = TempSensorTask(sensor, DT_TASK, dispatcher.msgio_get("Sensor")) taskmgr.add(sensor_task) - # Pot - deliberately left without plant params (M/C/L/Td) here: a - # Sud's own doc is the only source for those now (see tasks/sud.py's - # SudTask.apply_plant_params(), called immediately on Load), so the - # simulated Pot stays inert (Pot.is_configured() is False, PotTask - # skips process() - see there) until one actually is loaded (PotReal - # just ignores this - see there). Ambient is independent of any Sud - # (a global setting, changeable live via the System channel - see - # on_system_recv() below), so it's set right away regardless. pot.set_ambient_temperature(theta_amb) taskmgr.add(PotTask(pot, DT_TASK, dispatcher.msgio_get("Pot"))) @@ -107,18 +99,22 @@ if __name__ == '__main__': heater_task = HeaterTask(heater, DT_TASK, dispatcher.msgio_get("Heater")) taskmgr.add(heater_task) - # Temperature Controller - PID gains are server/hardware-level - # config, independent of any Sud, so they're set right away; "Normal" - # has no internal model/ambient at all (see temp_controller.py vs. - # temp_controller_smith.py.), and Smith's model plant params are - - # like the real Pot's above - deliberately left unset here, only - # ever coming from a Sud's own doc. tc = PidFactory.create(config['TempCtrl']['pid_type'], DT) tc.set_params(config['TempCtrl']) tc.set_ambient_temperature(theta_amb) tc_task = TcTask(tc, DT_TASK, dispatcher.msgio_get("TempCtrl")) taskmgr.add(tc_task) + # Seed plant params from config so pot/tc are configured before any sud + # is loaded - Pot.water_mass in config is how much water is already in + # the kettle at startup (e.g. strike water pre-filled). A loaded sud's + # own apply_plant_params() will override this on the first step. + startup_water = config.get('Pot', {}).get('water_mass', 0) + if startup_water > 0: + startup_params = sud.derive_plant_params(0, startup_water) + pot.set_plant_params(startup_params) + tc.set_model_plant_params(startup_params) + # Stirrer stirrer = StirrerFactory.create(config['Stirrer']['type'], DT, config['Stirrer']) stirrer_task = StirrerTask(stirrer, DT_TASK, dispatcher.msgio_get("Stirrer"))