Adds File menu actions to save the running Sud's schedule to a local sud.json and to replace it from one, via the Sud websocket channel's new Download/Upload messages. File dialogs are opened up front on the GUI thread; the actual read/write happens off the websocket recv callback, which runs on a worker thread and must not touch Qt itself. main_window.py is regenerated from brewpi.ui via pyuic5 5.15.11, which also picks up an unrelated pre-existing drift (the default URI had fallen out of sync with the .ui source).
209 lines
7.7 KiB
Python
Executable File
209 lines
7.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from main_window import Ui_MainWindow
|
|
from PyQt5 import QtWidgets, QtGui
|
|
import json
|
|
import sys
|
|
from ws.client.ws_client import WsClient
|
|
from ws.message import MessageDispatcherSync
|
|
from queue import Queue
|
|
import asyncio
|
|
|
|
|
|
class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|
def __init__(self):
|
|
QtWidgets.QMainWindow.__init__(self)
|
|
self.setWindowIcon(QtGui.QIcon('res/beer.png'))
|
|
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
self.msg_dispatch = MessageDispatcherSync(auto_subscribe=True)
|
|
self.ws_client = WsClient(listener=self.msg_dispatch, loop=loop)
|
|
self.msg_pot = self.msg_dispatch.msgio_get('Pot')
|
|
self.msg_sensor = self.msg_dispatch.msgio_get('Sensor')
|
|
self.msg_heater = self.msg_dispatch.msgio_get('Heater')
|
|
self.msg_stirrer = self.msg_dispatch.msgio_get('Stirrer')
|
|
self.msg_tempctrl = self.msg_dispatch.msgio_get('TempCtrl')
|
|
self.msg_sud = self.msg_dispatch.msgio_get('Sud')
|
|
|
|
self.msg_pot.set_recv_handler(self.on_pot_changed)
|
|
self.msg_sensor.set_recv_handler(self.on_sensor_changed)
|
|
self.msg_heater.set_recv_handler(self.on_heater_changed)
|
|
self.msg_stirrer.set_recv_handler(self.on_stirrer_changed)
|
|
self.msg_tempctrl.set_recv_handler(self.on_tempctrl_changed)
|
|
self.msg_sud.set_recv_handler(self.on_sud_changed)
|
|
|
|
self.setupUi(self)
|
|
self.actionStart.triggered.connect(self.connect)
|
|
self.actionStop.triggered.connect(self.disconnect)
|
|
self.actionSudDownload.triggered.connect(self.on_action_sud_download)
|
|
self.actionSudUpload.triggered.connect(self.on_action_sud_upload)
|
|
self.Slider_pwr_soll.valueChanged.connect(self.on_slider_pwr_soll_changed)
|
|
self.Slider_temp_soll.valueChanged.connect(self.on_slider_temp_soll_changed)
|
|
self.Slider_speed_soll.valueChanged.connect(self.on_slider_stirrer_speed_soll_changed)
|
|
|
|
self.doubleSpinBox_heatrate_soll.valueChanged.connect(self.on_heatrate_soll_changed)
|
|
|
|
self.checkbox_heater_activate.stateChanged.connect(self.on_checkbox_changed)
|
|
self.checkbox_stirrer_activate.stateChanged.connect(self.on_checkbox_stirrer_activate_changed)
|
|
|
|
self.send_data = Queue()
|
|
self.slider_pwr_initial_update = True
|
|
self.slider_temp_soll_initial_update = True
|
|
self.slider_speed_initial_update = True
|
|
self.checkbox_heater_activate_initial_update = True
|
|
self.checkbox_stirrer_activate_initial_update = True
|
|
self.heatrate_soll_initial_update = True
|
|
self.sud_download_path = None
|
|
|
|
def connect(self):
|
|
self.slider_pwr_initial_update = True
|
|
self.slider_temp_soll_initial_update = True
|
|
self.slider_speed_initial_update = True
|
|
self.checkbox_heater_activate_initial_update = True
|
|
self.checkbox_stirrer_activate_initial_update = True
|
|
self.heatrate_soll_initial_update = True
|
|
self.ws_client.connect(uri=self.plainTextUri.toPlainText())
|
|
|
|
def disconnect(self):
|
|
self.ws_client.disconnect()
|
|
|
|
def on_slider_temp_soll_changed(self, value):
|
|
print("on_slider_temp_soll_changed {}".format(value))
|
|
self.msg_tempctrl.send({'Soll': {'Temp': value}})
|
|
|
|
def on_heatrate_soll_changed(self, value):
|
|
value = round(value, 1)
|
|
print("on_textbox_heatrate_soll_changed {}".format(value))
|
|
self.msg_tempctrl.send({'Soll': {'Rate': value}})
|
|
|
|
def on_slider_pwr_soll_changed(self, value):
|
|
print("on_slider_pwr_soll_changed {}".format(value))
|
|
self.msg_heater.send({'Power': value})
|
|
|
|
def on_checkbox_changed(self, value):
|
|
print("on_checkbox_changed {}".format(value))
|
|
self.msg_heater.send({'Activate': int(value == 2)})
|
|
|
|
def on_slider_stirrer_speed_soll_changed(self, value):
|
|
print("on_slider_stirrer_speed_soll_changed {}".format(value))
|
|
self.msg_stirrer.send({'Speed': value})
|
|
|
|
def on_checkbox_stirrer_activate_changed(self, value):
|
|
print("on_checkbox_stirrer_activate_changed {}".format(value))
|
|
self.msg_stirrer.send({'Activate': int(value == 2)})
|
|
|
|
def on_action_sud_download(self):
|
|
# Pick the destination up front, on the GUI thread - the actual
|
|
# write happens in on_sud_changed(), which runs off a worker thread
|
|
# and must not touch Qt (e.g. open a file dialog) itself.
|
|
path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Download Sud", filter="JSON files (*.json)")
|
|
if not path:
|
|
return
|
|
self.sud_download_path = path
|
|
self.msg_sud.send({'Download': True})
|
|
|
|
def on_action_sud_upload(self):
|
|
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Upload Sud", filter="JSON files (*.json)")
|
|
if not path:
|
|
return
|
|
with open(path) as f:
|
|
data = json.load(f)
|
|
self.msg_sud.send({'Upload': data})
|
|
|
|
def on_pot_changed(self, msg):
|
|
print("on_pot_changed {}".format(msg))
|
|
for key in msg:
|
|
if "Power" in key:
|
|
self.lcdNumber_power_pot.display(msg['Power'])
|
|
|
|
def on_sensor_changed(self, msg):
|
|
print("on_sensor_changed {}".format(msg))
|
|
|
|
def on_tempctrl_changed(self, msg):
|
|
print("on_tempctrl_changed {}".format(msg))
|
|
for key in msg:
|
|
if 'State' in key:
|
|
self.label_state.setText(msg['State'])
|
|
elif "Soll" in key:
|
|
submsg = msg['Soll']
|
|
if 'Temp' in submsg:
|
|
self.lcdNumber_temp_soll.display(str(submsg['Temp']))
|
|
if self.slider_temp_soll_initial_update:
|
|
self.Slider_temp_soll.setValue(int(round(submsg['Temp'])))
|
|
self.slider_temp_soll_initial_update = False
|
|
if 'Rate' in submsg:
|
|
subsubmsg = submsg['Rate']
|
|
for subkey in subsubmsg:
|
|
if "Current" in subkey:
|
|
self.lcdNumber_heatrate_soll.display(str(subsubmsg['Current']))
|
|
if "Set" in subkey:
|
|
if self.heatrate_soll_initial_update:
|
|
self.heatrate_soll_initial_update = False
|
|
self.doubleSpinBox_heatrate_soll.setValue(subsubmsg['Set'])
|
|
if "Ist" in key:
|
|
submsg = msg['Ist']
|
|
if 'Temp' in submsg:
|
|
self.lcdNumber_temp_ist.display(str(submsg['Temp']))
|
|
if 'Rate' in submsg:
|
|
self.lcdNumber_heatrate_ist.display(str(submsg['Rate']))
|
|
|
|
def on_heater_changed(self, msg):
|
|
print("on_heater_changed {}".format(msg))
|
|
for key in msg:
|
|
if "Activate" in key:
|
|
if self.checkbox_heater_activate_initial_update:
|
|
self.checkbox_heater_activate.setCheckState(2 if msg['Activate'] == 1 else 0)
|
|
self.checkbox_heater_activate_initial_update = False
|
|
elif "Power" in key:
|
|
self.lcdNumber_power_heater.display(msg['Power'])
|
|
if self.slider_pwr_initial_update:
|
|
self.Slider_pwr_soll.setValue(int(round(msg['Power'])))
|
|
self.slider_pwr_initial_update = False
|
|
elif "Capabilities" in key:
|
|
submsg = msg['Capabilities']
|
|
if "Power" in submsg:
|
|
submsg = submsg['Power']
|
|
self.Slider_pwr_soll.setMinimum(int(submsg['Min']))
|
|
self.Slider_pwr_soll.setMaximum(int(submsg['Max']))
|
|
|
|
def on_stirrer_changed(self, msg):
|
|
print("on_stirrer_changed {}".format(msg))
|
|
for key in msg:
|
|
if "Activate" in key:
|
|
if self.checkbox_stirrer_activate_initial_update:
|
|
self.checkbox_stirrer_activate.setCheckState(2 if msg['Activate'] == 1 else 0)
|
|
self.checkbox_stirrer_activate_initial_update = False
|
|
elif "Speed" in key:
|
|
if self.slider_speed_initial_update:
|
|
self.Slider_speed_soll.setValue(int(round(msg['Speed'])))
|
|
self.slider_speed_initial_update = False
|
|
elif "Capabilities" in key:
|
|
submsg = msg['Capabilities']
|
|
if "Power" in submsg:
|
|
submsg = submsg['Power']
|
|
self.Slider_speed_soll.setMinimum(int(submsg['Min']))
|
|
self.Slider_speed_soll.setMaximum(int(submsg['Max']))
|
|
|
|
def on_sud_changed(self, msg):
|
|
print("on_sud_changed {}".format(msg))
|
|
for key in msg:
|
|
if "Json" in key and self.sud_download_path:
|
|
with open(self.sud_download_path, 'w') as f:
|
|
json.dump(msg['Json'], f, indent='\t')
|
|
self.sud_download_path = None
|
|
elif "Name" in key:
|
|
self.statusBar().showMessage("Sud schedule updated: {}".format(msg['Name']), 5000)
|
|
|
|
def closeEvent(self, event):
|
|
print("Exitting gracefully!")
|
|
self.ws_client.disconnect()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
win = Window()
|
|
win.show()
|
|
sys.exit(app.exec_())
|
|
|
|
print ("End of program.")
|