Compare commits
7 Commits
v1.1.0
...
e06cc1d4fc
Author | SHA1 | Date | |
---|---|---|---|
e06cc1d4fc | |||
aae5152288 | |||
cd3688b0b3 | |||
774d2a1e83 | |||
58366036a2 | |||
47c498dddb | |||
2f4c3103b8 |
@@ -10,6 +10,10 @@ Maybe it's straightforward or obvious, but just for completeness: the name comes
|
|||||||
1. The direct bond to tasmota (written for its "API" if one can call the HTTP endpoints an API)
|
1. The direct bond to tasmota (written for its "API" if one can call the HTTP endpoints an API)
|
||||||
2. The ability to turn on and off tasmota devices ("on" and "off" pronounced directly one after the other sounds (a bit) like "onov")
|
2. The ability to turn on and off tasmota devices ("on" and "off" pronounced directly one after the other sounds (a bit) like "onov")
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## CLI Usage
|
## CLI Usage
|
||||||
|
|
||||||
|
BIN
assets/gui_tab1.png
Normal file
BIN
assets/gui_tab1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 180 KiB |
BIN
assets/gui_tab2.png
Normal file
BIN
assets/gui_tab2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 142 KiB |
@@ -1,40 +1,134 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Tasmotonov GUI - A simple Qt wrapper around the tasmotonov.py script
|
||||||
|
Copyright (C) 2025 Benjamin Burkhardt
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import tasmotonov
|
||||||
|
import configparser
|
||||||
from PySide6.QtUiTools import QUiLoader
|
from PySide6.QtUiTools import QUiLoader
|
||||||
from PySide6.QtWidgets import QApplication,QFileDialog
|
from PySide6.QtWidgets import QApplication,QFileDialog
|
||||||
from PySide6.QtCore import QFile, QUrl, QIODevice
|
from PySide6.QtCore import QFile, QUrl, QIODevice
|
||||||
|
|
||||||
|
# set some standard values
|
||||||
filename = ""
|
filename = ""
|
||||||
action = "toggle"
|
action = "toggle"
|
||||||
|
|
||||||
def tab1_load_file():
|
def load_config(file="config.ini"): # load config from configparser.ConfigParser object
|
||||||
|
to_load = configparser.ConfigParser()
|
||||||
|
to_load.read(file)
|
||||||
|
global filename, action
|
||||||
|
if "DEFAULT" in to_load:
|
||||||
|
if "file" in to_load['DEFAULT']:
|
||||||
|
filename = to_load['DEFAULT']['file']
|
||||||
|
if filename != "":
|
||||||
|
try:
|
||||||
|
tab1_load_file()
|
||||||
|
except FileNotFoundError:
|
||||||
|
tab1_clear()
|
||||||
|
if "inline" in to_load['DEFAULT']:
|
||||||
|
window.tab2_plainTextEdit.setPlainText(to_load['DEFAULT']['inline'])
|
||||||
|
if "action" in to_load['DEFAULT']:
|
||||||
|
a_new = to_load['DEFAULT']['action']
|
||||||
|
if a_new != "":
|
||||||
|
action = a_new
|
||||||
|
# else: no config there yet
|
||||||
|
def save_config(file="config.ini"):
|
||||||
|
window.tab3_textBrowser.append("Saving configuration!")
|
||||||
|
global filename, action
|
||||||
|
to_save = configparser.ConfigParser()
|
||||||
|
to_save['DEFAULT'] = {}
|
||||||
|
to_save['DEFAULT']['file'] = str(filename)
|
||||||
|
to_save['DEFAULT']['inline'] = str(window.tab2_plainTextEdit.toPlainText())
|
||||||
|
to_save['DEFAULT']['action'] = str(action)
|
||||||
|
with open(file, 'w') as configfile:
|
||||||
|
to_save.write(configfile)
|
||||||
|
|
||||||
|
# tab1 slots
|
||||||
|
def tab1_load_file_pressed():
|
||||||
global filename
|
global filename
|
||||||
dialog = QFileDialog()
|
dialog = QFileDialog()
|
||||||
dialog.setFileMode(QFileDialog.AnyFile)
|
dialog.setFileMode(QFileDialog.AnyFile)
|
||||||
#fileNames = QStringList()
|
|
||||||
if dialog.exec():
|
if dialog.exec():
|
||||||
filename = dialog.selectedFiles()[0]
|
filename = dialog.selectedFiles()[0]
|
||||||
|
tab1_load_file()
|
||||||
|
def tab1_load_file():
|
||||||
|
global filename
|
||||||
window.tab1_label.setText(f"File loaded: {filename}")
|
window.tab1_label.setText(f"File loaded: {filename}")
|
||||||
|
window.tab3_textBrowser.append(f"File loaded: {filename}")
|
||||||
window.tab1_label.show()
|
window.tab1_label.show()
|
||||||
window.tab1_filecontent_textBrowser.setSource(QUrl(filename))
|
window.tab1_filecontent_textBrowser.setSource(QUrl(filename))
|
||||||
|
window.tab1_label_help.show()
|
||||||
|
# clear listWidget and add the items
|
||||||
|
window.tab1_listWidget.clear()
|
||||||
|
window.tab1_listWidget.addItems(tasmotonov.TasmotonovRunner("file", filename, action, False, False).get_addresses())
|
||||||
|
def tab1_clear():
|
||||||
|
global filename
|
||||||
|
filename = ""
|
||||||
|
window.tab1_label.setText(f"File loaded: ")
|
||||||
|
window.tab3_textBrowser.append(f"File unloaded")
|
||||||
|
window.tab1_label.hide()
|
||||||
|
window.tab1_label_help.hide()
|
||||||
|
window.tab1_filecontent_textBrowser.clear()
|
||||||
|
window.tab1_listWidget.clear()
|
||||||
def tab1_action():
|
def tab1_action():
|
||||||
if filename != "":
|
if filename != "":
|
||||||
p = subprocess.Popen(["python3", "tasmotonov.py", "file", filename, action], stdout=subprocess.PIPE)
|
tasmotonov_runner = tasmotonov.TasmotonovRunner("file", filename, action, False, False)
|
||||||
out, err = p.communicate()
|
tasmotonov_runner.run()
|
||||||
window.tab3_textBrowser.append("==== RUNNING ====\n" + str(out) + "\n=================\n")
|
window.tab3_textBrowser.append("\n==== RUNNING ====\n\n" + str(tasmotonov_runner.logger.log_string) + "\n=================\n")
|
||||||
|
tasmotonov_runner.logger.log_string = ""
|
||||||
else:
|
else:
|
||||||
window.tab3_textBrowser.append("Will not run, no file selected!")
|
window.tab3_textBrowser.append("Will not run, no file selected!")
|
||||||
|
def tab1_single_action():
|
||||||
|
if filename != "":
|
||||||
|
tasmotonov_runner = tasmotonov.TasmotonovRunner("file", filename, action, False, False)
|
||||||
|
tasmotonov_runner.run_single(window.tab1_listWidget.currentRow())
|
||||||
|
window.tab3_textBrowser.append(str(tasmotonov_runner.logger.log_string))
|
||||||
|
|
||||||
|
# tab2 slots
|
||||||
|
def tab2_plainTextEdit_change():
|
||||||
|
# clear listWidget
|
||||||
|
window.tab2_listWidget.clear()
|
||||||
|
content = window.tab2_plainTextEdit.toPlainText()
|
||||||
|
if content != "":
|
||||||
|
tasmotonov_runner = tasmotonov.TasmotonovRunner("inline", content, action, False, False)
|
||||||
|
window.tab2_listWidget.addItems(tasmotonov_runner.get_addresses())
|
||||||
|
window.tab3_textBrowser.append(str(tasmotonov_runner.logger.log_string))
|
||||||
def tab2_action():
|
def tab2_action():
|
||||||
content = window.tab2_plainTextEdit.toPlainText()
|
content = window.tab2_plainTextEdit.toPlainText()
|
||||||
if content != "":
|
if content != "":
|
||||||
p = subprocess.Popen(["python3", "tasmotonov.py", "inline", content, action], stdout=subprocess.PIPE)
|
tasmotonov_runner = tasmotonov.TasmotonovRunner("inline", content, action, False, False)
|
||||||
out, err = p.communicate()
|
tasmotonov_runner.run()
|
||||||
window.tab3_textBrowser.append("==== RUNNING ====\n" + str(out) + "\n=================\n")
|
window.tab3_textBrowser.append("\n==== RUNNING ====\n\n" + str(tasmotonov_runner.logger.log_string) + "\n=================\n")
|
||||||
print(out)
|
|
||||||
else:
|
else:
|
||||||
window.tab3_textBrowser.append("Will not run, no input given!")
|
window.tab3_textBrowser.append("Will not run, no input given!")
|
||||||
|
def tab2_single_action():
|
||||||
|
content = window.tab2_plainTextEdit.toPlainText()
|
||||||
|
tasmotonov_runner = tasmotonov.TasmotonovRunner("inline", content, action, False, False)
|
||||||
|
if len(tasmotonov_runner.get_addresses()) != 0:
|
||||||
|
tasmotonov_runner.run_single(window.tab2_listWidget.currentRow())
|
||||||
|
window.tab3_textBrowser.append(str(tasmotonov_runner.logger.log_string))
|
||||||
|
|
||||||
|
# tab3 slots
|
||||||
|
def tab3_clear():
|
||||||
|
window.tab3_textBrowser.clear()
|
||||||
|
|
||||||
|
# other slots
|
||||||
def select_on():
|
def select_on():
|
||||||
global action
|
global action
|
||||||
action = "on"
|
action = "on"
|
||||||
@@ -51,7 +145,7 @@ def select_toggle():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
ui_file_name = "tasmotonov.ui"
|
ui_file_name = "tasmotonov-gui.ui"
|
||||||
ui_file = QFile(ui_file_name)
|
ui_file = QFile(ui_file_name)
|
||||||
if not ui_file.open(QIODevice.ReadOnly):
|
if not ui_file.open(QIODevice.ReadOnly):
|
||||||
print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
|
print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
|
||||||
@@ -64,14 +158,29 @@ if __name__ == "__main__":
|
|||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
window.tab1_label.hide()
|
window.tab1_label.hide()
|
||||||
window.tab1_load_file_pushButton.clicked.connect(tab1_load_file)
|
window.tab1_label_help.hide()
|
||||||
|
|
||||||
|
window.tab1_load_file_pushButton.clicked.connect(tab1_load_file_pressed)
|
||||||
|
window.tab1_clear_pushButton.clicked.connect(tab1_clear)
|
||||||
window.tab1_action_pushButton.clicked.connect(tab1_action)
|
window.tab1_action_pushButton.clicked.connect(tab1_action)
|
||||||
|
window.tab1_single_action_pushButton.clicked.connect(tab1_single_action)
|
||||||
|
|
||||||
|
window.tab2_plainTextEdit.textChanged.connect(tab2_plainTextEdit_change)
|
||||||
window.tab2_action_pushButton.clicked.connect(tab2_action)
|
window.tab2_action_pushButton.clicked.connect(tab2_action)
|
||||||
|
window.tab2_single_action_pushButton.clicked.connect(tab2_single_action)
|
||||||
|
|
||||||
window.radioButton_on.clicked.connect(select_on)
|
window.radioButton_on.clicked.connect(select_on)
|
||||||
window.radioButton_off.clicked.connect(select_off)
|
window.radioButton_off.clicked.connect(select_off)
|
||||||
window.radioButton_toggle.clicked.connect(select_toggle)
|
window.radioButton_toggle.clicked.connect(select_toggle)
|
||||||
|
|
||||||
|
window.tab3_clear_button.clicked.connect(tab3_clear)
|
||||||
|
|
||||||
|
load_config()
|
||||||
|
|
||||||
window.show()
|
window.show()
|
||||||
|
|
||||||
sys.exit(app.exec())
|
# execute app loop and define exit strategy
|
||||||
|
code = app.exec()
|
||||||
|
save_config()
|
||||||
|
sys.exit(code)
|
||||||
|
|
||||||
|
@@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>630</width>
|
<width>1166</width>
|
||||||
<height>404</height>
|
<height>546</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
<enum>QTabWidget::TabShape::Rounded</enum>
|
<enum>QTabWidget::TabShape::Rounded</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item row="0" column="1">
|
<item>
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="tabPosition">
|
<property name="tabPosition">
|
||||||
<enum>QTabWidget::TabPosition::East</enum>
|
<enum>QTabWidget::TabPosition::East</enum>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<enum>QTabWidget::TabShape::Rounded</enum>
|
<enum>QTabWidget::TabShape::Rounded</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>1</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="usesScrollButtons">
|
<property name="usesScrollButtons">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@@ -43,17 +43,34 @@
|
|||||||
<string>From File</string>
|
<string>From File</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="3" column="0">
|
<item row="8" column="0">
|
||||||
<widget class="QTextBrowser" name="tab1_filecontent_textBrowser">
|
<widget class="QPushButton" name="tab1_action_pushButton">
|
||||||
<property name="frameShadow">
|
<property name="minimumSize">
|
||||||
<enum>QFrame::Shadow::Sunken</enum>
|
<size>
|
||||||
|
<width>414</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="openExternalLinks">
|
<property name="text">
|
||||||
<bool>true</bool>
|
<string>Run bulk action (specified below)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="8" column="1">
|
||||||
|
<widget class="QPushButton" name="tab1_single_action_pushButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Run single action (specified below)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLabel" name="tab1_label_help">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select an item and run an action:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="tab1_label">
|
<widget class="QLabel" name="tab1_label">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@@ -69,28 +86,77 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QPushButton" name="tab1_action_pushButton">
|
<widget class="QTextBrowser" name="tab1_filecontent_textBrowser">
|
||||||
<property name="text">
|
<property name="sizePolicy">
|
||||||
<string>Run!</string>
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>414</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Shadow::Sunken</enum>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="6" column="1">
|
||||||
|
<widget class="QListWidget" name="tab1_listWidget"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
<widget class="QPushButton" name="tab1_load_file_pushButton">
|
<widget class="QPushButton" name="tab1_load_file_pushButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Load file</string>
|
<string>Load file</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QPushButton" name="tab1_clear_pushButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Clear</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab2_from_inline">
|
<widget class="QWidget" name="tab2_from_inline">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>From Inline</string>
|
<string>From Inline</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item>
|
<item row="1" column="1">
|
||||||
|
<widget class="QListWidget" name="tab2_listWidget"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPushButton" name="tab2_single_action_pushButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Run single action (specified below)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QPushButton" name="tab2_action_pushButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Run bulk action (specified below)!</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="tab2_label_help_single">
|
||||||
|
<property name="text">
|
||||||
|
<string>Single actions: select the address and click the button below</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" rowspan="2">
|
||||||
<widget class="QPlainTextEdit" name="tab2_plainTextEdit">
|
<widget class="QPlainTextEdit" name="tab2_plainTextEdit">
|
||||||
<property name="lineWrapMode">
|
<property name="lineWrapMode">
|
||||||
<enum>QPlainTextEdit::LineWrapMode::WidgetWidth</enum>
|
<enum>QPlainTextEdit::LineWrapMode::WidgetWidth</enum>
|
||||||
@@ -106,13 +172,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="tab2_action_pushButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Run!</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab3_output">
|
<widget class="QWidget" name="tab3_output">
|
||||||
@@ -128,13 +187,27 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTextBrowser" name="tab3_textBrowser"/>
|
<widget class="QTextBrowser" name="tab3_textBrowser">
|
||||||
|
<property name="acceptDrops">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="tab3_clear_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Clear</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="radioButton_on">
|
<widget class="QRadioButton" name="radioButton_on">
|
Reference in New Issue
Block a user