From accfb19626c6de896213cee57fea1d47cfc94c8a Mon Sep 17 00:00:00 2001 From: Piotr Dobrowolski Date: Mon, 27 Aug 2018 18:07:43 +0200 Subject: [PATCH] Initial commit --- .gitignore | 8 +++ Makefile | 26 +++++++ README.md | 41 +++++++++++ assets/logo.png | Bin 0 -> 1554 bytes gui/__init__.py | 0 gui/mainwindow.ui | 171 ++++++++++++++++++++++++++++++++++++++++++++++ i18n/Polish.ts | 61 +++++++++++++++++ luftdaten-tool.py | 109 +++++++++++++++++++++++++++++ requirements.txt | 6 ++ 9 files changed, 422 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 assets/logo.png create mode 100644 gui/__init__.py create mode 100644 gui/mainwindow.ui create mode 100644 i18n/Polish.ts create mode 100644 luftdaten-tool.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..41598c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.ropeproject +*.py[oc] +*.swp + +# Ignore compiled Qt assets +*.qm +gui/*.py +!gui/__init__.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..47ca08e --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..8d82cc4 --- /dev/null +++ b/README.md @@ -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 diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5ae9b7c8e0c4db1beedabdbe406301a46fb138db GIT binary patch literal 1554 zcmeAS@N?(olHy`uVBq!ia0y~yU^oH79Lx+1471we)-W(I7zg--xH2#>{3j7?pZchY zfq_AwB*-tA!C`-c!29_L=MC;F^ardj2tU7X4+8^hou`XqNX4zUcVAArt-!;O!1%}@ z!TtUJEpNa0y%mu;GWFBnw`a<>O6WL9L`+MKgrHlu*H|rC9zUnYi)9AayTD7PwKirm zoGWC$@q)0`FLwQ_&K!TP^|u7L@kyVwe{|s(n}y&j z`GYnK1nOmOaa6FbmCpT8CEC7yhQ#`_0si8N2C$?bDOhdYJb-ZeBK-`QftI%bktOWaertt0``86PX*aZlS-j$!66_`!-2_Vm#|{ z?#b0J)jM?l`fm7U&L7|TUf`AE+!yjk{~kH{T&8NhaNi@pZ%xZY#LXLS@hUBxvVbS9 zbrOU_ zQ)Sz{MgHp#@%cL4GCIH9)BTQd+o&K z)?5i<58~xY)FOUaO?eQ>`uW6%Po)>6yq0b6eE8-y>wd?N){)=tCs?^AsqwO?xKG}3 zx#OZ-Rh!z&eY;Cc-LG0?DlG5po@+W`|8t?dTMi8Kiys-}D%@wO-VTV%I!OTJ~U$e9j}vLw(h_Elw9G@^6X|G)ULkmaIA=tvvmHE8?3gf{lK2C zCF|N%1NQ1YyYXZ#OXd5TH_4a39EthgEHa^}q|YU0x%i6rVoMjjtMRxRvu?x6-|f=h zT`J>pf2Zbu&*#aUxLIX-w)N+=TY|*qh-FK0$iB>d^vCU`B*)P&5*5orm8SfveUjtv zc1c%cro`5Hl|F7oXWvW+Nj7BvJG)qH;qRK~8UA~2`kOp^&XsNy#VE@1+3NZsj@HtE5$m^mog`v&vNtJd)s=xnsaCJ`^L&zZr(p5^6$-we3oC; zjLJXVKCi63_Up~7>{m`swEWo=vwOLmOc6U%f_2Ly1M~GQ)@v4TQ@OTk+r6G;Wg-7I zHR-H6{dD?%m!c_p0o+;6x1zbPR3CKmb=51}R%>4xrep8>qBlwHLB8TC`=$5IGPfPi z3SS};U87;?P&+Be=FYR*;x|`nrRz&>OZd?rYrR8vOa6qtFO(;^&Ww7ke)>>4i_k(F zftJb78fQduWo^I0^2t`#D=%egKIiE#_ZKHlc4?W})vgnJNv)voRZ;sZv&xC9qFR(@ ztulW8TC%C4O=e~W_pO|O$OUYdN_h|0sQg~?_ndZC*3tZc%YM2`p3mR><-54&g(Y{N zthYKaQ@Qe<+)PU$&d|5B{v|HTyHqgwigW72tEc{?_x#(oPvc%u7XR?fT%e{6pde>@idmy!|e1Y7Xt{vsJ9WRutPB`B8wNE+O_-^NF z0gslKC;VT$(6`z>@BPkSTwfo24@nY;Q9M?)-Bz*S#Ofw=}kHy^y&XE aCU$AjJ?nQL&Szj?VDNPHb6Mw<&;$TMR17cx literal 0 HcmV?d00001 diff --git a/gui/__init__.py b/gui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui new file mode 100644 index 0000000..096f345 --- /dev/null +++ b/gui/mainwindow.ui @@ -0,0 +1,171 @@ + + + MainWindow + + + + 0 + 0 + 503 + 530 + + + + + 0 + 0 + + + + Luftdaten.info Flashing Tool + + + + ../assets/logo.png../assets/logo.png + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + + + + Board: + + + + + + + + 0 + 0 + + + + + + + + Firmware version: + + + + + + + + 0 + 0 + + + + true + + + master + + + + + + + + + + + + + 0 + 0 + + + + Upload + + + + + + + Expert mode + + + true + + + + + + + + + + + + + 0 + 0 + + + + + + + Baudrate: + + + + + + + + 0 + 0 + + + + + + + + + 0 + 1 + + + + + + + + + + + + + 0 + 0 + 503 + 29 + + + + + + + + diff --git a/i18n/Polish.ts b/i18n/Polish.ts new file mode 100644 index 0000000..7437ff5 --- /dev/null +++ b/i18n/Polish.ts @@ -0,0 +1,61 @@ + + + + MainWindow + + + No boards found + Nie znaleziono płytki + + + + Others... + Inne... + + + + No device selected. + Nie wybrano urządzenia. + + + + No version selected. + Nie wybrano wersji. + + + + Luftdaten.info Flashing Tool + + + + + Board: + Płytka: + + + + Firmware version: + Wersja oprogramowania: + + + + master + master + + + + Upload + Wgraj + + + + Expert mode + Tryb eksperta + + + + Baudrate: + Prędkość portu: + + + diff --git a/luftdaten-tool.py b/luftdaten-tool.py new file mode 100644 index 0000000..83f31fa --- /dev/null +++ b/luftdaten-tool.py @@ -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_()) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2fc9742 --- /dev/null +++ b/requirements.txt @@ -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