Initial commit
This commit is contained in:
commit
accfb19626
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.ropeproject
|
||||
*.py[oc]
|
||||
*.swp
|
||||
|
||||
# Ignore compiled Qt assets
|
||||
*.qm
|
||||
gui/*.py
|
||||
!gui/__init__.py
|
26
Makefile
Normal file
26
Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
UI_FILES = $(wildcard gui/*.ui)
|
||||
TS_FILES = $(wildcard i18n/*.ts)
|
||||
PY_FILES = $(wildcard *.py) $(wildcard gui/*.py)
|
||||
|
||||
UI_COMPILED = $(UI_FILES:.ui=.py)
|
||||
TS_COMPILED = $(TS_FILES:.ts=.qm)
|
||||
|
||||
%.py: %.ui
|
||||
pyuic5 $< > $@
|
||||
|
||||
%.qm: %.ts
|
||||
lrelease $<
|
||||
|
||||
all: $(UI_COMPILED) $(TS_COMPILED)
|
||||
|
||||
clean:
|
||||
rm $(UI_COMPILED)
|
||||
rm $(TS_COMPILED)
|
||||
|
||||
run: all
|
||||
python3 luftdaten-tool.py
|
||||
|
||||
i18n-update:
|
||||
@for f in $(TS_FILES) ; do \
|
||||
pylupdate5 *.py gui/*.py -ts $$f -verbose; \
|
||||
done
|
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
||||
luftdaten.info flashing tool
|
||||
============================
|
||||
|
||||
Binary builds
|
||||
-------------
|
||||
|
||||
Our main target is having working prebuilt binaries for users to simply
|
||||
download and run, to avoid all the setup below.
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
Both build & runtime requirements are defined in `requirements.txt` file. In
|
||||
order to install these use the following command:
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
To manage dynamic UI and translation binaries generation we use a very simple
|
||||
GNU make-based build system.
|
||||
|
||||
To simply build everything needed to run `luftdaten-tool.py` run:
|
||||
|
||||
make
|
||||
|
||||
To build and run use:
|
||||
|
||||
make run
|
||||
|
||||
To remove all build artifacts use:
|
||||
|
||||
make clean
|
||||
|
||||
All requirements are set up using wildcards, so, in theory, `Makefile` shouldn't
|
||||
need much changes in near future.
|
||||
|
||||
Translations
|
||||
------------
|
||||
|
||||
In order to rebuild `*.ts` files use:
|
||||
|
||||
make i18n-update
|
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
0
gui/__init__.py
Normal file
0
gui/__init__.py
Normal file
171
gui/mainwindow.ui
Normal file
171
gui/mainwindow.ui
Normal file
@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>503</width>
|
||||
<height>530</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Luftdaten.info Flashing Tool</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
<normaloff>../assets/logo.png</normaloff>../assets/logo.png</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QWidget" name="formWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Board:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="boardBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Firmware version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="versionBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentText">
|
||||
<string>master</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QProgressBar" name="progressBar"/>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="uploadButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Upload</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="expertModeBox">
|
||||
<property name="text">
|
||||
<string>Expert mode</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="expertForm" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Baudrate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>503</width>
|
||||
<height>29</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
61
i18n/Polish.ts
Normal file
61
i18n/Polish.ts
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS><TS version="2.0" language="pl" sourcelanguage="">
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../luftdaten-tool.py" line="59"/>
|
||||
<source>No boards found</source>
|
||||
<translation>Nie znaleziono płytki</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../luftdaten-tool.py" line="64"/>
|
||||
<source>Others...</source>
|
||||
<translation>Inne...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../luftdaten-tool.py" line="96"/>
|
||||
<source>No device selected.</source>
|
||||
<translation>Nie wybrano urządzenia.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../luftdaten-tool.py" line="100"/>
|
||||
<source>No version selected.</source>
|
||||
<translation>Nie wybrano wersji.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.py" line="126"/>
|
||||
<source>Luftdaten.info Flashing Tool</source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.py" line="127"/>
|
||||
<source>Board:</source>
|
||||
<translation>Płytka:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.py" line="128"/>
|
||||
<source>Firmware version:</source>
|
||||
<translation>Wersja oprogramowania:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.py" line="129"/>
|
||||
<source>master</source>
|
||||
<translation>master</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.py" line="130"/>
|
||||
<source>Upload</source>
|
||||
<translation>Wgraj</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.py" line="131"/>
|
||||
<source>Expert mode</source>
|
||||
<translation>Tryb eksperta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.py" line="132"/>
|
||||
<source>Baudrate:</source>
|
||||
<translation>Prędkość portu:</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
109
luftdaten-tool.py
Normal file
109
luftdaten-tool.py
Normal file
@ -0,0 +1,109 @@
|
||||
# -* encoding: utf-8 *-
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||
|
||||
from gui import mainwindow
|
||||
|
||||
|
||||
PREFERED_PORTS = [
|
||||
# CH341
|
||||
(0x1A86, 0x7523),
|
||||
|
||||
# CP2102
|
||||
(0x10c4, 0xea60),
|
||||
]
|
||||
|
||||
ROLE_DEVICE = QtCore.Qt.UserRole + 1
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
def __init__(self, parent=None, app=None):
|
||||
super(MainWindow, self).__init__(parent)
|
||||
|
||||
# FIXME: dirty hack to solve relative paths in *.ui
|
||||
oldcwd = os.getcwd()
|
||||
os.chdir('assets')
|
||||
self.setupUi(self)
|
||||
os.chdir(oldcwd)
|
||||
|
||||
self.app = app
|
||||
self.translator = QtCore.QTranslator()
|
||||
|
||||
self.i18n_init(QtCore.QLocale.system())
|
||||
self.populate_boards(serial.tools.list_ports.comports())
|
||||
|
||||
self.on_expertModeBox_clicked()
|
||||
|
||||
def i18n_init(self, locale):
|
||||
self.app.removeTranslator(self.translator)
|
||||
self.translator.load(os.path.join('i18n', QtCore.QLocale.languageToString(locale.language())+ '.qm'))
|
||||
self.app.installTranslator(self.translator)
|
||||
self.retranslateUi(self)
|
||||
|
||||
def populate_boards(self, ports):
|
||||
prefered, others = self.group_ports(serial.tools.list_ports.comports())
|
||||
|
||||
for b in prefered:
|
||||
item = QtGui.QStandardItem(
|
||||
'{0.description} ({0.device})'.format(b))
|
||||
item.setData(b.device, ROLE_DEVICE)
|
||||
self.boardBox.model().appendRow(item)
|
||||
|
||||
if not prefered:
|
||||
sep = QtGui.QStandardItem(self.tr('No boards found'))
|
||||
sep.setEnabled(False)
|
||||
self.boardBox.model().appendRow(sep)
|
||||
|
||||
if others:
|
||||
sep = QtGui.QStandardItem(self.tr('Others...'))
|
||||
sep.setEnabled(False)
|
||||
self.boardBox.model().appendRow(sep)
|
||||
|
||||
for b in others:
|
||||
item = QtGui.QStandardItem(
|
||||
'{0.description} ({0.device})'.format(b))
|
||||
item.setData(b.device, ROLE_DEVICE)
|
||||
self.boardBox.model().appendRow(item)
|
||||
|
||||
def group_ports(self, ports):
|
||||
prefered = []
|
||||
others = []
|
||||
|
||||
for p in ports:
|
||||
if (p.vid, p.pid) in PREFERED_PORTS:
|
||||
prefered.append(p)
|
||||
else:
|
||||
others.append(p)
|
||||
return prefered, others
|
||||
|
||||
def on_actionExit_triggered(self):
|
||||
"""This handles activation of "Exit" menu action"""
|
||||
self.app.exit()
|
||||
|
||||
def on_uploadButton_clicked(self):
|
||||
self.statusbar.clearMessage()
|
||||
|
||||
device = self.boardBox.currentData(ROLE_DEVICE)
|
||||
version = self.versionBox.currentText()
|
||||
|
||||
if not device:
|
||||
self.statusbar.showMessage(self.tr("No device selected."))
|
||||
return
|
||||
|
||||
if not version:
|
||||
self.statusbar.showMessage(self.tr("No version selected."))
|
||||
return
|
||||
|
||||
def on_expertModeBox_clicked(self):
|
||||
self.expertForm.setVisible(self.expertModeBox.checkState())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = MainWindow(app=app)
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
ecdsa==0.13
|
||||
esptool==2.5.0
|
||||
pyaes==1.6.1
|
||||
PyQt5==5.11.2
|
||||
PyQt5-sip==4.19.12
|
||||
pyserial==3.4
|
Loading…
x
Reference in New Issue
Block a user