From b51d802b3195f797f9b0775119684f3afe8f17cf Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Sun, 21 Jun 2026 10:34:28 +0200 Subject: [PATCH] gui: don't echo manual-mode sliders/checkboxes back to the server on sync On (re)connect, the manual-mode controls (heater power/activate, temp soll, heatrate soll, stirrer speed/activate) sync themselves once to the server's current value via setValue()/setCheckState(). Without blockSignals(), that programmatic update also fires valueChanged/ stateChanged, echoing the value straight back as a command. For the heater power slider this was the cause of the "stale power > 0 after restart" bug: if the slider happened to sync to a nonzero power (e.g. reconnecting mid-heat), the echoed {'Power': ...} set tasks/heater.py's power_soll, which power_soll = max(power_soll, power_actor) then latches as a floor that never drops back down on its own - the heater would then stay at least that hot regardless of what the actual controller/Sud wanted afterwards. --- client/brewpi_gui.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/client/brewpi_gui.py b/client/brewpi_gui.py index dbead21..a067baf 100755 --- a/client/brewpi_gui.py +++ b/client/brewpi_gui.py @@ -489,7 +489,14 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): self.plot_temp_soll = submsg['Temp'] self.lcdNumber_temp_soll.display(str(submsg['Temp'])) if self.slider_temp_soll_initial_update: + # blockSignals: setValue() here is just syncing the + # slider's display to the server's current value, not + # a user edit - without this it would also fire + # valueChanged and echo the value straight back as a + # command. + self.Slider_temp_soll.blockSignals(True) self.Slider_temp_soll.setValue(int(round(submsg['Temp']))) + self.Slider_temp_soll.blockSignals(False) self.slider_temp_soll_initial_update = False if 'Rate' in submsg: subsubmsg = submsg['Rate'] @@ -500,7 +507,9 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): if "Set" in subkey: if self.heatrate_soll_initial_update: self.heatrate_soll_initial_update = False + self.doubleSpinBox_heatrate_soll.blockSignals(True) self.doubleSpinBox_heatrate_soll.setValue(subsubmsg['Set']) + self.doubleSpinBox_heatrate_soll.blockSignals(False) if "Ist" in key: submsg = msg['Ist'] if 'Temp' in submsg: @@ -515,7 +524,9 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): for key in msg: if "Activate" in key: if self.checkbox_heater_activate_initial_update: + self.checkbox_heater_activate.blockSignals(True) self.checkbox_heater_activate.setCheckState(2 if msg['Activate'] == 1 else 0) + self.checkbox_heater_activate.blockSignals(False) self.checkbox_heater_activate_initial_update = False elif "PowerSet" in key: self.plot_power_set = msg['PowerSet'] @@ -523,7 +534,16 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): self.plot_power_eff = msg['Power'] self.lcdNumber_power_heater.display(msg['Power']) if self.slider_pwr_initial_update: + # blockSignals: this is a display-only sync of the + # slider to the heater's current power, not a manual + # override - letting valueChanged fire here would echo + # it back as {'Power': ...}, which tasks/heater.py's + # power_soll = max(power_soll, power_actor) latches as a + # floor that never goes back down on its own. That's the + # "stale power > 0 after reconnect" bug. + self.Slider_pwr_soll.blockSignals(True) self.Slider_pwr_soll.setValue(int(round(msg['Power']))) + self.Slider_pwr_soll.blockSignals(False) self.slider_pwr_initial_update = False elif "Capabilities" in key: submsg = msg['Capabilities'] @@ -537,11 +557,15 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): for key in msg: if "Activate" in key: if self.checkbox_stirrer_activate_initial_update: + self.checkbox_stirrer_activate.blockSignals(True) self.checkbox_stirrer_activate.setCheckState(2 if msg['Activate'] == 1 else 0) + self.checkbox_stirrer_activate.blockSignals(False) self.checkbox_stirrer_activate_initial_update = False elif "Speed" in key: if self.slider_speed_initial_update: + self.Slider_speed_soll.blockSignals(True) self.Slider_speed_soll.setValue(int(round(msg['Speed']))) + self.Slider_speed_soll.blockSignals(False) self.slider_speed_initial_update = False elif "Capabilities" in key: submsg = msg['Capabilities']