Initial commit

This commit is contained in:
Piotr Dobrowolski 2018-08-27 18:07:43 +02:00
commit accfb19626
9 changed files with 422 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.ropeproject
*.py[oc]
*.swp
# Ignore compiled Qt assets
*.qm
gui/*.py
!gui/__init__.py

26
Makefile Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

0
gui/__init__.py Normal file
View File

171
gui/mainwindow.ui Normal file
View 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
View 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
View 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
View 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