diff --git a/pythermostat/pythermostat/gui/view/net_settings_input_diag.py b/pythermostat/pythermostat/gui/view/net_settings_input_diag.py
new file mode 100644
index 0000000..1ef4f61
--- /dev/null
+++ b/pythermostat/pythermostat/gui/view/net_settings_input_diag.py
@@ -0,0 +1,36 @@
+from PyQt6 import QtWidgets
+from PyQt6.QtWidgets import QAbstractButton
+from PyQt6.QtCore import pyqtSignal, pyqtSlot
+
+
+class NetSettingsInputDiag(QtWidgets.QInputDialog):
+ set_ipv4_act = pyqtSignal(str)
+
+ def __init__(self, current_ipv4_settings):
+ super().__init__()
+ self.setWindowTitle("Network Settings")
+ self.setLabelText(
+ "Set the Thermostat's IPv4 address, netmask and gateway (optional)"
+ )
+ self.setTextValue(current_ipv4_settings)
+ self._new_ipv4 = ""
+
+ @pyqtSlot(str)
+ def set_ipv4(ipv4_settings):
+ self._new_ipv4 = ipv4_settings
+
+ sure = QtWidgets.QMessageBox(self)
+ sure.setWindowTitle("Set network?")
+ sure.setText(
+ f"Setting this as network and disconnecting:
{ipv4_settings}"
+ )
+
+ sure.buttonClicked.connect(self._emit_sig)
+ sure.show()
+
+ self.textValueSelected.connect(set_ipv4)
+ self.show()
+
+ @pyqtSlot(QAbstractButton)
+ def _emit_sig(self, _):
+ self.set_ipv4_act.emit(self._new_ipv4)
diff --git a/pythermostat/pythermostat/gui/view/thermostat_settings_menu.py b/pythermostat/pythermostat/gui/view/thermostat_settings_menu.py
new file mode 100644
index 0000000..09555e0
--- /dev/null
+++ b/pythermostat/pythermostat/gui/view/thermostat_settings_menu.py
@@ -0,0 +1,215 @@
+import logging
+from PyQt6 import QtWidgets, QtGui, QtCore
+from PyQt6.QtCore import pyqtSignal, pyqtSlot, QSignalBlocker
+from qasync import asyncSlot
+from pythermostat.gui.view.net_settings_input_diag import NetSettingsInputDiag
+from pythermostat.gui.model.thermostat import ThermostatConnectionState
+
+
+class ThermostatSettingsMenu(QtWidgets.QMenu):
+ def __init__(self, thermostat, info_box, style):
+ super().__init__()
+ self._thermostat = thermostat
+ self._info_box = info_box
+ self._style = style
+ self.setTitle("Thermostat settings")
+
+ self.hw_rev_data = dict()
+ self._thermostat.hw_rev_update.connect(self.hw_rev)
+ self._thermostat.connection_state_update.connect(
+ self.thermostat_state_change_handler
+ )
+
+ self.fan_group = QtWidgets.QWidget()
+ self.fan_group.setEnabled(False)
+ self.fan_group.setMinimumSize(QtCore.QSize(40, 0))
+ self.fan_layout = QtWidgets.QHBoxLayout(self.fan_group)
+ self.fan_layout.setSpacing(9)
+ self.fan_lbl = QtWidgets.QLabel(parent=self.fan_group)
+ self.fan_lbl.setMinimumSize(QtCore.QSize(40, 0))
+ self.fan_lbl.setMaximumSize(QtCore.QSize(40, 16777215))
+ self.fan_lbl.setBaseSize(QtCore.QSize(40, 0))
+ self.fan_layout.addWidget(self.fan_lbl)
+ self.fan_power_slider = QtWidgets.QSlider(parent=self.fan_group)
+ self.fan_power_slider.setMinimumSize(QtCore.QSize(200, 0))
+ self.fan_power_slider.setMaximumSize(QtCore.QSize(200, 16777215))
+ self.fan_power_slider.setBaseSize(QtCore.QSize(200, 0))
+ self.fan_power_slider.setRange(1, 100)
+ self.fan_power_slider.setOrientation(QtCore.Qt.Orientation.Horizontal)
+ self.fan_layout.addWidget(self.fan_power_slider)
+ self.fan_auto_box = QtWidgets.QCheckBox(parent=self.fan_group)
+ self.fan_auto_box.setMinimumSize(QtCore.QSize(70, 0))
+ self.fan_auto_box.setMaximumSize(QtCore.QSize(70, 16777215))
+ self.fan_layout.addWidget(self.fan_auto_box)
+ self.fan_pwm_warning = QtWidgets.QLabel(parent=self.fan_group)
+ self.fan_pwm_warning.setMinimumSize(QtCore.QSize(16, 0))
+ self.fan_layout.addWidget(self.fan_pwm_warning)
+
+ self.fan_power_slider.valueChanged.connect(self.fan_set_request)
+ self.fan_auto_box.stateChanged.connect(self.fan_auto_set_request)
+ self._thermostat.fan_update.connect(self.fan_update)
+
+ self.fan_lbl.setToolTip("Adjust the fan")
+ self.fan_lbl.setText("Fan:")
+ self.fan_auto_box.setText("Auto")
+
+ fan = QtWidgets.QWidgetAction(self)
+ fan.setDefaultWidget(self.fan_group)
+ self.addAction(fan)
+ self.fan = fan
+
+ self.actionReset = QtGui.QAction("Reset Thermostat", self)
+ self.actionReset.triggered.connect(self.reset_request)
+ self.addAction(self.actionReset)
+
+ self.actionEnter_DFU_Mode = QtGui.QAction("Enter DFU Mode", self)
+ self.actionEnter_DFU_Mode.triggered.connect(self.dfu_request)
+ self.addAction(self.actionEnter_DFU_Mode)
+
+ self.actionnet_settings_input_diag = QtGui.QAction("Set IPV4 Settings", self)
+ self.actionnet_settings_input_diag.triggered.connect(self.net_settings_request)
+ self.addAction(self.actionnet_settings_input_diag)
+
+ @asyncSlot(bool)
+ async def load(_):
+ await self._thermostat.load_cfg()
+
+ self._info_box.display_info_box(
+ "Config loaded", "All channel configs have been loaded from flash."
+ )
+
+ self.actionLoad_all_configs = QtGui.QAction("Load Config", self)
+ self.actionLoad_all_configs.triggered.connect(load)
+ self.addAction(self.actionLoad_all_configs)
+
+ @asyncSlot(bool)
+ async def save(_):
+ await self._thermostat.save_cfg()
+
+ self._info_box.display_info_box(
+ "Config saved", "All channel configs have been saved to flash."
+ )
+
+ self.actionSave_all_configs = QtGui.QAction("Save Config", self)
+ self.actionSave_all_configs.triggered.connect(save)
+ self.addAction(self.actionSave_all_configs)
+
+ def about_thermostat():
+ QtWidgets.QMessageBox.about(
+ self,
+ "About Thermostat",
+ f"""
+