Shut heater off on brew end (DONE) and stop (IDLE)
Adds HeaterTask.shutdown() which switches to open-loop at power 0, disables TC, and broadcasts ClosedLoop=false to clients so the browser checkbox unchecks immediately. SudTask fires it via a new set_on_end() callback whenever state reaches DONE or IDLE, giving both a completed schedule and a manual Stop the same safe behaviour: stirrer and heater both off, no heater left running unattended. Re-enabling after shutdown: check the closed-loop checkbox (sends ClosedLoop=true → TC re-enabled) or adjust the power slider directly; the next brew's Play press already sends ClosedLoop=true automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
This commit is contained in:
@@ -145,6 +145,8 @@ if __name__ == '__main__':
|
|||||||
tc.set_on_changed("y", heater_task.actor)
|
tc.set_on_changed("y", heater_task.actor)
|
||||||
# HeaterTask owns TC enable: enabled in closed-loop mode, disabled in open-loop.
|
# HeaterTask owns TC enable: enabled in closed-loop mode, disabled in open-loop.
|
||||||
heater_task.set_on_closed_loop_changed(tc.set_enabled)
|
heater_task.set_on_closed_loop_changed(tc.set_enabled)
|
||||||
|
# Brew end (DONE/IDLE): shut heater off and broadcast open-loop/power-0 to clients.
|
||||||
|
sud_task.set_on_end(heater_task.shutdown)
|
||||||
|
|
||||||
# Assign temp. sensor readings to tc
|
# Assign temp. sensor readings to tc
|
||||||
sensor_task.set_on_changed("temp", ChangedFloat(tc.set_theta_ist, prec=2).set)
|
sensor_task.set_on_changed("temp", ChangedFloat(tc.set_theta_ist, prec=2).set)
|
||||||
|
|||||||
@@ -24,6 +24,16 @@ class HeaterTask(ATask):
|
|||||||
def set_on_closed_loop_changed(self, callback):
|
def set_on_closed_loop_changed(self, callback):
|
||||||
self._on_closed_loop_changed = callback
|
self._on_closed_loop_changed = callback
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
"""Called when the brew ends (DONE/IDLE). Switches to open-loop at
|
||||||
|
power 0 and disables TC, mirroring what a Stop press should do."""
|
||||||
|
self.closed_loop = False
|
||||||
|
self.power_soll = 0
|
||||||
|
if self._on_closed_loop_changed:
|
||||||
|
self._on_closed_loop_changed(False)
|
||||||
|
asyncio.create_task(self.send({'ClosedLoop': False}))
|
||||||
|
self.power_set_changed(0)
|
||||||
|
|
||||||
def actor(self, y):
|
def actor(self, y):
|
||||||
self.power_actor = max(0, self.device.get_power_max() * y)
|
self.power_actor = max(0, self.device.get_power_max() * y)
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ class SudTask(ATask):
|
|||||||
# Saved on pause so resume can restore the effective setpoint even
|
# Saved on pause so resume can restore the effective setpoint even
|
||||||
# when the current step has temperature=None (inherits from a prior step).
|
# when the current step has temperature=None (inherits from a prior step).
|
||||||
self._paused_temp_soll = None
|
self._paused_temp_soll = None
|
||||||
|
self._on_end = None
|
||||||
msg_handler.set_recv_handler(self.recv)
|
msg_handler.set_recv_handler(self.recv)
|
||||||
|
|
||||||
def apply_plant_params(self, grain_mass, water_mass):
|
def apply_plant_params(self, grain_mass, water_mass):
|
||||||
@@ -217,12 +218,17 @@ class SudTask(ATask):
|
|||||||
'WaitForUser': step.get('user_wait_for_continue', False) if step else None,
|
'WaitForUser': step.get('user_wait_for_continue', False) if step else None,
|
||||||
}}))
|
}}))
|
||||||
|
|
||||||
|
def set_on_end(self, callback):
|
||||||
|
self._on_end = callback
|
||||||
|
|
||||||
def on_state_changed(self, value):
|
def on_state_changed(self, value):
|
||||||
asyncio.create_task(self.send({'State': str(value)}))
|
asyncio.create_task(self.send({'State': str(value)}))
|
||||||
|
|
||||||
if value in (SudState.DONE, SudState.IDLE):
|
if value in (SudState.DONE, SudState.IDLE):
|
||||||
self.stirrer.set_duty_cycle(1.0)
|
self.stirrer.set_duty_cycle(1.0)
|
||||||
self.stirrer.set_speed(0)
|
self.stirrer.set_speed(0)
|
||||||
|
if self._on_end:
|
||||||
|
self._on_end()
|
||||||
|
|
||||||
def on_user_message_changed(self, value):
|
def on_user_message_changed(self, value):
|
||||||
asyncio.create_task(self.send({'UserMessage': value}))
|
asyncio.create_task(self.send({'UserMessage': value}))
|
||||||
|
|||||||
Reference in New Issue
Block a user