示例#1
0
    def __init__(self, parent=None, app=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Dialog)

        # FIXME: dirty hack to solve relative paths in *.ui
        oldcwd = os.getcwd()
        os.chdir(os.path.join(RESOURCES_PATH, 'assets'))
        self.setupUi(self)
        os.chdir(oldcwd)

        self.app = app

        self.translator = QtCore.QTranslator()
        self.i18n_init(QtCore.QLocale.system())

        self.statusbar.showMessage(self.tr("Loading firmware list..."))

        self.versionBox.clear()
        self.firmware_list = FirmwareListThread()
        self.firmware_list.listLoaded.connect(self.populate_versions)
        self.firmware_list.error.connect(self.on_work_error)
        self.firmware_list.start()

        self.enableDiscoveryButton(False)

        self.port_detect = PortDetectThread()
        self.port_detect.portsUpdate.connect(self.populate_boards)
        self.port_detect.error.connect(self.on_work_error)
        self.port_detect.start()

        self.discovery_start()

        self.globalMessage.hide()

        self.uploadProgress.connect(self.on_work_update)
        self.errorSignal.connect(self.on_work_error)
        self.cachedir = tempfile.TemporaryDirectory()
        self.serial = None

        self.logTable.setHorizontalHeaderLabels(['Zeit', 'IP', 'Message'])
        header = self.logTable.horizontalHeader()
        header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
        header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)

        self.logger = LogListenerThread()
        self.logger.logReceived.connect(self.on_logmessage_received)
        self.logger.start()

        self.addIcon(self.fileopenButton, "SP_FileDialogStart")
        self.addIcon(self.discoveryRefreshButton, "SP_BrowserReload")
示例#2
0
    def __init__(self, parent=None, app=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Dialog)

        # FIXME: dirty hack to solve relative paths in *.ui
        oldcwd = os.getcwd()
        os.chdir(os.path.join(RESOURCES_PATH, 'assets'))
        self.setupUi(self)
        os.chdir(oldcwd)

        self.app = app

        self.translator = QtCore.QTranslator()
        self.i18n_init(QtCore.QLocale.system())

        self.statusbar.showMessage(self.tr("Loading firmware list..."))

        self.versionBox.clear()
        self.firmware_list = FirmwareListThread()
        self.firmware_list.listLoaded.connect(self.populate_versions)
        self.firmware_list.error.connect(self.on_work_error)
        self.firmware_list.start()

        self.port_detect = PortDetectThread()
        self.port_detect.portsUpdate.connect(self.populate_boards)
        self.port_detect.error.connect(self.on_work_error)
        self.port_detect.start()

        self.discovery_start()

        self.globalMessage.hide()

        # Hide WIP GUI parts...
        self.on_expertModeBox_clicked()
        self.expertModeBox.hide()
        self.tabWidget.removeTab(self.tabWidget.indexOf(self.serialTab))

        self.uploadProgress.connect(self.on_work_update)
        self.errorSignal.connect(self.on_work_error)

        self.cachedir = tempfile.TemporaryDirectory()
        self.cachedirjson = tempfile.TemporaryDirectory()
        self.cachedirspiffs = tempfile.TemporaryDirectory()

        print(self.cachedir.name)
        print(self.cachedirjson.name)
        print(self.cachedirspiffs.name)
示例#3
0
 def on_discoveryList_itemDoubleClicked(self, index):
     QtGui.QDesktopServices.openUrl(QtCore.QUrl(index.data(ROLE_DEVICE)))
示例#4
0
class MainWindow(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
    uploadProgress = QtCore.Signal([str, int])
    errorSignal = QtCore.Signal([str])
    uploadThread = None
    zeroconf_discovery = None
    boards_detected = False

    def __init__(self, parent=None, app=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Dialog)

        # FIXME: dirty hack to solve relative paths in *.ui
        oldcwd = os.getcwd()
        os.chdir(os.path.join(RESOURCES_PATH, 'assets'))
        self.setupUi(self)
        os.chdir(oldcwd)

        self.app = app

        self.translator = QtCore.QTranslator()
        self.i18n_init(QtCore.QLocale.system())

        self.statusbar.showMessage(self.tr("Loading firmware list..."))

        self.versionBox.clear()
        self.firmware_list = FirmwareListThread()
        self.firmware_list.listLoaded.connect(self.populate_versions)
        self.firmware_list.error.connect(self.on_work_error)
        self.firmware_list.start()

        self.port_detect = PortDetectThread()
        self.port_detect.portsUpdate.connect(self.populate_boards)
        self.port_detect.error.connect(self.on_work_error)
        self.port_detect.start()

        self.discovery_start()

        self.globalMessage.hide()

        # Hide WIP GUI parts...
        self.on_expertModeBox_clicked()
        self.expertModeBox.hide()
        self.tabWidget.removeTab(self.tabWidget.indexOf(self.serialTab))

        self.uploadProgress.connect(self.on_work_update)
        self.errorSignal.connect(self.on_work_error)

        self.cachedir = tempfile.TemporaryDirectory()

    def show_global_message(self, title, message):
        self.globalMessage.show()
        self.globalMessageTitle.setText(title)
        self.globalMessageText.setText(message)

    def on_work_update(self, status, progress):
        self.statusbar.showMessage(status)
        self.progressBar.setValue(progress)

    def on_work_error(self, message):
        self.statusbar.showMessage(message)

    @property
    def version(self):
        return airrohrFlasher.__version__

    @property
    def build_id(self):
        try:
            from airrohrFlasher._buildid import commit, builddate
        except ImportError:
            import datetime
            commit = 'devel'
            builddate = datetime.datetime.now().strftime('%Y%m%d')

        return '{}-{}/{}'.format(self.version, commit, builddate)

    def i18n_init(self, locale):
        """Initializes i18n to specified QLocale"""

        self.app.removeTranslator(self.translator)
        lang = QtCore.QLocale.languageToString(locale.language())
        self.translator.load(os.path.join(RESOURCES_PATH, 'i18n',
                                          lang + '.qm'))
        self.app.installTranslator(self.translator)
        self.retranslateUi(self)

    def retranslateUi(self, win):
        super(MainWindow, self).retranslateUi(win)

        win.setWindowTitle(win.windowTitle().format(version=self.version))
        win.buildLabel.setText(
            win.buildLabel.text().format(build_id=self.build_id))

    def populate_versions(self, files):
        """Loads available firmware versions into versionbox widget"""

        for fname in files:
            if not fname.endswith('.bin'):
                continue

            item = QtGui.QStandardItem(fname)
            item.setData(UPDATE_REPOSITORY + fname, ROLE_DEVICE)
            self.versionBox.model().appendRow(item)

        self.statusbar.clearMessage()

    def populate_boards(self, ports):
        """Populates board selection combobox from list of pyserial
        ListPortInfo objects"""

        self.boardBox.clear()

        prefered, others = self.group_ports(ports)

        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)

            # No prefered boards has been found so far and there is a
            # suggested driver download URL available
            if not self.boards_detected and DRIVERS_URL:
                self.show_global_message(
                    self.tr('No boards found'),
                    self.tr(
                        'Have you installed <a href="{drivers_url}">'
                        'the drivers</a>?').format(drivers_url=DRIVERS_URL))
        else:
            self.globalMessage.hide()
            self.boards_detected = True

        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

    @QtCore.Slot()
    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

        sel = self.versionBox.model().item(self.versionBox.currentIndex())
        if sel:
            orig_version = sel.text()
        else:
            orig_version = ''

        if version == orig_version:
            # Editable combobox has been unchanged
            binary_uri = self.versionBox.currentData(ROLE_DEVICE)
        elif version.startswith(ALLOWED_PROTO):
            # User has provided a download URL
            binary_uri = version
        elif os.path.exists(version):
            binary_uri = version
        else:
            self.statusbar.showMessage(
                self.tr("Invalid version / file does not exist"))
            return

        if self.flash_board.running():
            self.statusbar.showMessage(self.tr("Work in progess..."))
            return

        self.flash_board(self.uploadProgress,
                         device,
                         binary_uri,
                         error=self.errorSignal)

    def cache_download(self, progress, binary_uri):
        """Downloads and caches file with status reports via Qt Signals"""
        cache_fname = os.path.join(
            self.cachedir.name,
            hashlib.sha256(binary_uri.encode('utf-8')).hexdigest())

        if os.path.exists(cache_fname):
            return cache_fname

        with open(cache_fname, 'wb') as fd:
            progress.emit(self.tr('Downloading...'), 0)
            response = requests.get(binary_uri, stream=True)
            total_length = response.headers.get('content-length')

            dl = 0
            total_length = int(total_length or 0)
            for data in response.iter_content(chunk_size=4096):
                dl += len(data)
                fd.write(data)

                if total_length:
                    progress.emit(self.tr('Downloading...'),
                                  (100 * dl) // total_length)

        return cache_fname

    @QuickThread.wrap
    def erase_board(self, progress, device, baudrate=460800):

        progress.emit(self.tr('Connecting...'), 0)
        init_baud = min(ESPLoader.ESP_ROM_BAUD, baudrate)
        esp = ESPLoader.detect_chip(device, init_baud, 'default_reset', False)

        progress.emit(
            self.tr('Connected. Chip type: {chip_type}').format(
                chip_type=esp.get_chip_description()), 0)
        esp = esp.run_stub()
        esp.change_baud(baudrate)
        esp.erase_flash()
        progress.emit(self.tr('Erasing complete!'), 100)

    @QtCore.Slot()
    def on_eraseButton_clicked(self):
        self.statusbar.clearMessage()
        device = self.boardBox.currentData(ROLE_DEVICE)

        if not device:
            self.statusbar.showMessage(self.tr("No device selected."))
            return

        if self.erase_board.running():
            self.statusbar.showMessage(self.tr("Erasing in progress..."))
            return

        self.erase_board(self.uploadProgress, device, error=self.errorSignal)

    @QuickThread.wrap
    def flash_board(self, progress, device, binary_uri, baudrate=460800):
        if binary_uri.startswith(ALLOWED_PROTO):
            binary_uri = self.cache_download(progress, binary_uri)

        progress.emit(self.tr('Connecting...'), 0)

        init_baud = min(ESPLoader.ESP_ROM_BAUD, baudrate)
        esp = ESPLoader.detect_chip(device, init_baud, 'default_reset', False)

        progress.emit(
            self.tr('Connected. Chip type: {chip_type}').format(
                chip_type=esp.get_chip_description()), 0)
        esp = esp.run_stub()
        esp.change_baud(baudrate)

        with open(binary_uri, 'rb') as fd:
            uncimage = fd.read()

        image = zlib.compress(uncimage, 9)

        address = 0x0
        blocks = esp.flash_defl_begin(len(uncimage), len(image), address)

        seq = 0
        written = 0
        t = time.time()
        while len(image) > 0:
            current_addr = address + seq * esp.FLASH_WRITE_SIZE
            progress.emit(
                self.tr('Writing at 0x{address:08x}...').format(
                    address=current_addr), 100 * (seq + 1) // blocks)

            block = image[0:esp.FLASH_WRITE_SIZE]
            esp.flash_defl_block(block, seq, timeout=3.0)
            image = image[esp.FLASH_WRITE_SIZE:]
            seq += 1
            written += len(block)
        t = time.time() - t

        progress.emit(
            self.tr('Finished in {time:.2f} seconds. Sensor ID: {sensor_id}').
            format(time=t, sensor_id=esp.chip_id()), 100)

    @QtCore.Slot()
    def on_expertModeBox_clicked(self):
        self.expertForm.setVisible(self.expertModeBox.checkState())
        # self.centralwidget.setFixedHeight(
        #     self.centralwidget.sizeHint().height())
        # self.setFixedHeight(self.sizeHint().height())

    # Zeroconf page
    def discovery_start(self):
        if self.zeroconf_discovery:
            self.zeroconf_discovery.stop()

        self.zeroconf_discovery = ZeroconfDiscoveryThread()
        self.zeroconf_discovery.deviceDiscovered.connect(
            self.on_zeroconf_discovered)
        self.zeroconf_discovery.start()

    def on_zeroconf_discovered(self, name, address, info):
        """Called on every zeroconf discovered device"""
        if (name.startswith('Feinstaubsensor') or name.startswith('NAM')
                or name.startswith('Smogomierz')
                or name.startswith('airrohr')):
            item = QtWidgets.QListWidgetItem('{}: {}'.format(
                address,
                name.split('.')[0]))
            item.setData(ROLE_DEVICE,
                         'http://{}:{}'.format(address, info.port))
            self.discoveryList.addItem(item)

    @QtCore.Slot(QtWidgets.QListWidgetItem)
    def on_discoveryList_itemDoubleClicked(self, index):
        QtGui.QDesktopServices.openUrl(QtCore.QUrl(index.data(ROLE_DEVICE)))

    @QtCore.Slot()
    def on_discoveryRefreshButton_clicked(self):
        self.discoveryList.clear()
        self.discovery_start()
示例#5
0
 def on_discoveryBrowser_clicked(self):
     data = self.discoveryList.selectionModel().selectedRows()[0]
     url = "http://" + data.data(DATA_ADDR)
     QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))
示例#6
0
class MainWindow(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
    uploadProgress = QtCore.Signal([str, int])
    errorSignal = QtCore.Signal([str])
    uploadThread = None
    zeroconf_discovery = None
    boards_detected = False

    def __init__(self, parent=None, app=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Dialog)

        # FIXME: dirty hack to solve relative paths in *.ui
        oldcwd = os.getcwd()
        os.chdir(os.path.join(RESOURCES_PATH, 'assets'))
        self.setupUi(self)
        os.chdir(oldcwd)

        self.app = app

        self.translator = QtCore.QTranslator()
        self.i18n_init(QtCore.QLocale.system())

        self.statusbar.showMessage(self.tr("Loading firmware list..."))

        self.versionBox.clear()
        self.firmware_list = FirmwareListThread()
        self.firmware_list.listLoaded.connect(self.populate_versions)
        self.firmware_list.error.connect(self.on_work_error)
        self.firmware_list.start()

        self.enableDiscoveryButton(False)

        self.port_detect = PortDetectThread()
        self.port_detect.portsUpdate.connect(self.populate_boards)
        self.port_detect.error.connect(self.on_work_error)
        self.port_detect.start()

        self.discovery_start()

        self.globalMessage.hide()

        self.uploadProgress.connect(self.on_work_update)
        self.errorSignal.connect(self.on_work_error)
        self.cachedir = tempfile.TemporaryDirectory()
        self.serial = None

        self.logTable.setHorizontalHeaderLabels(['Zeit', 'IP', 'Message'])
        header = self.logTable.horizontalHeader()
        header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
        header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)

        self.logger = LogListenerThread()
        self.logger.logReceived.connect(self.on_logmessage_received)
        self.logger.start()

        self.addIcon(self.fileopenButton, "SP_FileDialogStart")
        self.addIcon(self.discoveryRefreshButton, "SP_BrowserReload")

    def addIcon(self, widget, iconname):
        widget.setIcon(self.style().standardIcon(getattr(QStyle, iconname)))

    def show_global_message(self, title, message):
        self.globalMessage.show()
        self.globalMessageTitle.setText(title)
        self.globalMessageText.setText(message)

    def on_work_update(self, status, progress):
        self.statusbar.showMessage(status)
        self.progressBar.setValue(progress)

    def on_work_error(self, message):
        self.statusbar.showMessage(message)

    @property
    def version(self):
        return airrohrFlasher.__version__

    @property
    def build_id(self):
        try:
            from airrohrFlasher._buildid import commit, builddate
        except ImportError:
            import datetime
            commit = 'devel'
            builddate = datetime.datetime.now().strftime('%Y%m%d')

        return '{}-{}/{}'.format(self.version, commit, builddate)

    def i18n_init(self, locale):
        """Initializes i18n to specified QLocale"""

        self.app.removeTranslator(self.translator)
        lang = QtCore.QLocale.languageToString(locale.language())
        self.translator.load(os.path.join(RESOURCES_PATH, 'i18n',
                                          lang + '.qm'))
        self.app.installTranslator(self.translator)
        self.retranslateUi(self)

    def retranslateUi(self, win):
        super(MainWindow, self).retranslateUi(win)

        win.setWindowTitle(win.windowTitle().format(version=self.version))
        win.buildLabel.setText(
            win.buildLabel.text().format(build_id=self.build_id))

    def populate_versions(self, files):
        """Loads available firmware versions into versionbox widget"""

        for fname in files:
            item = QtGui.QStandardItem(fname[0] + " (" + fname[1] + ")")
            item.setData(fname[2], ROLE_DEVICE)
            self.versionBox.model().appendRow(item)

        self.statusbar.clearMessage()

    def populate_boards(self, ports):
        """Populates board selection combobox from list of pyserial
        ListPortInfo objects"""

        #self.boardBox.clear()

        prefered, others = self.group_ports(ports)
        for b in others:
            self.statusbar.showMessage("Not Supported: %s:%s %s" %
                                       (hex(b.vid), hex(b.pid), str(b)))
            print("Filtered: " + str(b))

        for b in prefered:
            print("Found: " + str(b))
            rowPosition = self.discoveryList.rowCount()
            self.discoveryList.insertRow(rowPosition)
            data = QTableWidgetItem(b.device)
            data.setData(ROLE_DEVICE, TYP_USB)
            data.setData(DATA_ADDR, b.device)
            data.setData(DATA_NAME, b.description)
            data.setData(DATA_INFO, "")
            self.discoveryList.setItem(rowPosition, 0, data)
            self.discoveryList.setItem(rowPosition, 1,
                                       QTableWidgetItem(b.description))
            self.discoveryList.setItem(rowPosition, 2, QTableWidgetItem(""))

            #TODO
            #     if not prefered:
            #            sep = QtGui.QStandardItem(self.tr('No boards found'))
            ##           sep.setEnabled(False)
            #         self.boardBox.model().appendRow(sep)

            # No prefered boards has been found so far and there is a
            # suggested driver download URL available
            if not self.boards_detected and DRIVERS_URL:
                self.show_global_message(
                    self.tr('No boards found'),
                    self.tr(
                        'Have you installed <a href="{drivers_url}">'
                        'the drivers</a>?').format(drivers_url=DRIVERS_URL))
        else:
            self.globalMessage.hide()
            self.boards_detected = True

        # 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

    @QtCore.Slot()
    def on_serialSendButton_clicked(self):
        # TODO Check if it is connected
        s = self.serialOutText.text()
        self.serial.writeData(s.encode('utf-8'))
        self.serialOutText.setText("")

    @QtCore.Slot()
    def on_serialOutText_returnPressed(self):
        s = self.serialOutText.text()
        self.serial.writeData(s.encode('utf-8'))
        self.serialOutText.setText("")

    @QtCore.Slot(bool)
    def on_serialConnectButton_clicked(self, checked):
        if not checked:
            if self.serial:
                self.serial.close()
                self.statusbar.showMessage(self.tr("Disconnected."))
                return

        data = self.discoveryList.selectionModel().selectedRows()[0]
        device = data.data(DATA_ADDR)
        self.serialTextEdit.setText("")
        if not device:
            self.statusbar.showMessage(self.tr("No device selected."))
            return

        self.serial = QtSerialPort.QSerialPort(
            device,
            baudRate=QtSerialPort.QSerialPort.Baud115200,
            readyRead=self.receive)
        if self.serial.open(QtCore.QIODevice.ReadWrite):
            self.statusbar.showMessage(self.tr("Connected."))
        else:
            self.statusbar.showMessage(
                self.tr("Error while opening com port."))

    @QtCore.Slot()
    def receive(self):
        while self.serial.canReadLine():
            text = self.serial.readLine().data().decode()
            text = text.rstrip('\r\n')
            self.serialTextEdit.append(text)

    @QtCore.Slot()
    def on_fileuploadButton_clicked(self):
        self.statusbar.showMessage(self.tr("Villeicht"))
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fileName, _ = QFileDialog.getOpenFileName(
            self,
            "QFileDialog.getOpenFileName()",
            "",
            "Config-File (config.json);;CSS-File (*.css);;All Files (*)",
            options=options)
        if fileName:
            print(fileName)
            data = self.discoveryList.selectionModel().selectedRows()[0]
            device = data.data(DATA_ADDR)
            print(device)
            with serial.Serial(device, 115200, timeout=3) as ser:
                ser.write("xdebug".encode('utf-8'))
                s = ser.readline().decode('utf-8').rstrip('\r\n')
                print(s)
                if (s != "Debugmodus aktiviert"):
                    s = ser.readline().decode('utf-8').rstrip('\r\n')
                print(s)
                if (s != "Debugmodus aktiviert"):
                    self.statusbar.showMessage(
                        self.tr("Aktivierung des Debugmodus fehlgeschlagen!"))
                    return
                ser.write("_".encode('utf-8'))
                s = ser.readline().decode('utf-8').rstrip('\r\n')
                if (s != "TRANSFER ACTIVE"):
                    self.statusbar.showMessage(
                        self.tr("Aktivierung des Transfers fehlgeschlagen!"))
                    return
                with open(fileName, "rb") as f:
                    size = os.fstat(f.fileno()).st_size
                    b64 = base64.b64encode(f.read())
                    s = "PUT " + str(size) + " config.json\r\n"
                    ser.write(s.encode('iso-8859-1'))
                    print(b64)
                    print(s)
                    print("Len " + str(len(b64)))
                    count = ser.write(b64)
                    print("Writting " + str(count) + " bytes (expected: " +
                          str(len(b64)))
                    ser.flush()
                    s = ser.readline().decode('utf-8').rstrip('\r\n')
                    print(s)
                    s = ser.readline().decode('utf-8').rstrip('\r\n')
                    print(s)
                ser.write("x".encode('utf-8'))
                s = ser.readline().decode('utf-8').rstrip('\r\n')

    @QtCore.Slot()
    def on_flashButton_clicked(self):
        self.statusbar.clearMessage()

        data = self.discoveryList.selectionModel().selectedRows()[0]
        typ = data.data(ROLE_DEVICE)
        if (typ == TYP_USB):
            device = data.data(DATA_ADDR)
            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

            sel = self.versionBox.model().item(self.versionBox.currentIndex())
            if sel:
                orig_version = sel.text()
            else:
                orig_version = ''

            if version == orig_version:
                # Editable combobox has been unchanged
                binary_uri = self.versionBox.currentData(ROLE_DEVICE)
            elif version.startswith(ALLOWED_PROTO):
                # User has provided a download URL
                binary_uri = version
            elif os.path.exists(version):
                binary_uri = version
            else:
                self.statusbar.showMessage(
                    self.tr("Invalid version / file does not exist"))
                return

            if self.flash_board.running():
                self.statusbar.showMessage(self.tr("Work in progess..."))
                return

            self.flash_board(self.uploadProgress,
                             device,
                             binary_uri,
                             error=self.errorSignal)

        if (typ == TYP_REMOTE):
            try:
                progress = self.uploadProgress
                version = self.versionBox.currentText()
                sel = self.versionBox.model().item(
                    self.versionBox.currentIndex())
                if sel:
                    orig_version = sel.text()
                else:
                    orig_version = ''

                if version == orig_version:
                    # Editable combobox has been unchanged
                    binary_uri = self.versionBox.currentData(ROLE_DEVICE)
                elif version.startswith(ALLOWED_PROTO):
                    # User has provided a download URL
                    binary_uri = version
                elif os.path.exists(version):
                    binary_uri = version
                else:
                    self.statusbar.showMessage(
                        self.tr("Invalid version / file does not exist"))
                    return

                if binary_uri.startswith(ALLOWED_PROTO):
                    binary_uri = self.cache_download(progress, binary_uri)

                QtWidgets.QApplication.setOverrideCursor(Qt.WaitCursor)
                info = data.data(DATA_INFO)

                flashModus = ""
                if b'FlashModus' in info.properties:
                    flashModus = info.properties.get(b'FlashModus')

                url = "http://" + data.data(DATA_ADDR) + "/firmware"
                auth = HTTPBasicAuth('admin', 'admin')
                if (flashModus == "Arduino_Esp8266_2.6"):
                    files = {'firmware': open(binary_uri, 'rb')}
                elif (flashModus == "Arduino_Esp8266_2.5" or flashModus == ""):
                    files = {'file': open(binary_uri, 'rb')}

                values = {}
                progress.emit(self.tr('Uploading...'), 1)
                r = requests.post(url, files=files, data=values, auth=auth)
                if (r.status_code == 200):
                    string = re.sub('<.*?>', '', r.text)
                    progress.emit(
                        self.tr("Finish. {text}").format(text=string), 100)
                else:
                    progress.emit(
                        self.tr('Error {code} : {text}').format(
                            code=str(r.status_code), text=r.text), 1)
            finally:
                QtWidgets.QApplication.restoreOverrideCursor()

    def cache_download(self, progress, binary_uri):
        """Downloads and caches file with status reports via Qt Signals"""
        cache_fname = os.path.join(
            self.cachedir.name,
            hashlib.sha256(binary_uri.encode('utf-8')).hexdigest())

        if os.path.exists(cache_fname):
            return cache_fname

        with open(cache_fname, 'wb') as fd:
            progress.emit(self.tr('Downloading...'), 0)
            response = requests.get(binary_uri, stream=True)
            total_length = response.headers.get('content-length')

            dl = 0
            total_length = int(total_length or 0)
            for data in response.iter_content(chunk_size=4096):
                dl += len(data)
                fd.write(data)

                if total_length:
                    progress.emit(self.tr('Downloading...'),
                                  (100 * dl) // total_length)

        return cache_fname

    @QuickThread.wrap
    def erase_board(self, progress, device, baudrate=460800):

        progress.emit(self.tr('Connecting...'), 0)
        init_baud = min(ESPLoader.ESP_ROM_BAUD, baudrate)
        esp = ESPLoader.detect_chip(device, init_baud, 'default_reset', False)

        progress.emit(
            self.tr('Connected. Chip type: {chip_type}').format(
                chip_type=esp.get_chip_description()), 0)
        esp = esp.run_stub()
        esp.change_baud(baudrate)
        esp.erase_flash()
        progress.emit(self.tr('Erasing complete!'), 100)

    @QtCore.Slot()
    def on_eraseButton_clicked(self):
        self.statusbar.clearMessage()
        data = self.discoveryList.selectionModel().selectedRows()[0]
        device = data.data(DATA_ADDR)

        if self.erase_board.running():
            self.statusbar.showMessage(self.tr("Erasing in progress..."))
            return

        self.erase_board(self.uploadProgress, device, error=self.errorSignal)

    @QuickThread.wrap
    def flash_board(self, progress, device, binary_uri, baudrate=460800):
        if binary_uri.startswith(ALLOWED_PROTO):
            binary_uri = self.cache_download(progress, binary_uri)

        progress.emit(self.tr('Connecting...'), 0)

        init_baud = min(ESPLoader.ESP_ROM_BAUD, baudrate)
        esp = ESPLoader.detect_chip(device, init_baud, 'default_reset', False)

        progress.emit(
            self.tr('Connected. Chip type: {chip_type}').format(
                chip_type=esp.get_chip_description()), 0)
        esp = esp.run_stub()
        esp.change_baud(baudrate)

        t = time.time()
        if zipfile.is_zipfile(binary_uri):
            with zipfile.ZipFile(binary_uri) as myzip:
                for fname in myzip.namelist():
                    if fname.startswith("0x"):
                        addr = int(fname, 16)
                        with myzip.open(fname) as myfile:
                            data = myfile.read()
                            print("Segment: " + fname + " / " + str(addr) +
                                  " Size: " + str(len(data)))
                            self.flashBlock(data, progress, esp, addr)
                    else:
                        print("Cannot handle " + fname)

        else:
            with open(binary_uri, 'rb') as fd:
                uncimage = fd.read()
            self.flashBlock(uncimage, progress, esp, 0x0)
        t = time.time() - t

        esp.flash_finish(True)

        progress.emit(
            self.tr('Finished in {time:.2f} seconds.').format(time=t), 100)

    def flashBlock(self, uncimage, progress, esp, address):
        image = zlib.compress(uncimage, 9)

        blocks = esp.flash_defl_begin(len(uncimage), len(image), address)
        seq = 0
        written = 0
        while len(image) > 0:
            current_addr = address + seq * esp.FLASH_WRITE_SIZE
            progress.emit(
                self.tr('Writing at 0x{address:08x}...').format(
                    address=current_addr), 100 * (seq + 1) // blocks)

            block = image[0:esp.FLASH_WRITE_SIZE]
            esp.flash_defl_block(block, seq, timeout=3.0)
            image = image[esp.FLASH_WRITE_SIZE:]
            seq += 1
            written += len(block)

    # Zeroconf page
    def discovery_start(self):
        if self.zeroconf_discovery:
            self.zeroconf_discovery.stop()

        self.zeroconf_discovery = ZeroconfDiscoveryThread()
        self.zeroconf_discovery.deviceDiscovered.connect(
            self.on_zeroconf_discovered)
        self.zeroconf_discovery.start()
        self.discoveryList.setRowCount(0)
        self.discoveryList.setSizeAdjustPolicy(
            QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.discoveryList.setHorizontalHeaderLabels(['IP', 'Name', 'Version'])
        header = self.discoveryList.horizontalHeader()
        header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
        header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)

    def on_logmessage_received(self, addr, data):
        print(data)
        print(addr)
        rowPosition = 0  # self.logTable.rowCount()
        self.logTable.insertRow(rowPosition)

        self.logTable.setItem(rowPosition, 0, QTableWidgetItem(""))
        self.logTable.setItem(rowPosition, 1, QTableWidgetItem(data))
        self.logTable.setItem(rowPosition, 2, QTableWidgetItem(addr))

    def on_zeroconf_discovered(self, name, address, info):
        """Called on every zeroconf discovered device"""
        try:
            if (name.lower().startswith('ly-dcc-')):
                rowPosition = self.discoveryList.rowCount()
                self.discoveryList.insertRow(rowPosition)

                data = QTableWidgetItem(address)
                data.setData(ROLE_DEVICE, TYP_REMOTE)
                data.setData(DATA_ADDR, address)
                data.setData(DATA_NAME, name)
                data.setData(DATA_INFO, info)
                self.discoveryList.setItem(rowPosition, 0, data)
                self.discoveryList.setItem(
                    rowPosition, 1, QTableWidgetItem(name.split('.')[0]))
                self.discoveryList.setItem(
                    rowPosition, 2,
                    QTableWidgetItem(
                        info.properties.get(b"Version").decode('utf-8')))
        except:
            print("Error")

    def enableDiscoveryButton(self, selectedTyp):
        self.discoveryBrowser.setEnabled(selectedTyp == TYP_REMOTE)
        self.flashButton.setEnabled(selectedTyp == TYP_REMOTE
                                    or selectedTyp == TYP_USB)
        self.eraseButton.setEnabled(selectedTyp == TYP_USB)
        self.fileuploadButton.setEnabled(selectedTyp == TYP_USB)
        self.fileuploadButton.hide()
        #self.versionBox.setEnabled(selectedTyp == TYP_REMOTE or selectedTyp == TYP_USB)
        self.enableLoggingButton.setEnabled(selectedTyp == TYP_REMOTE)
        #self.fileopenButton.setEnabled(selectedTyp == TYP_USB)
        self.serialConnectButton.setEnabled(selectedTyp == TYP_USB)

    @QtCore.Slot()
    def on_discoveryBrowser_clicked(self):
        data = self.discoveryList.selectionModel().selectedRows()[0]
        url = "http://" + data.data(DATA_ADDR)
        QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))

    @QtCore.Slot()
    def on_enableLoggingButton_clicked(self):
        data = self.discoveryList.selectionModel().selectedRows()[0]
        url = "http://" + data.data(
            DATA_ADDR) + "/set?id=sys&key=log&value=bcast"
        r = requests.get(url)
        if (r.status_code == 200):
            self.statusbar.showMessage(self.tr("Started."))
        else:
            self.statusbar.showMessage(
                self.tr('Error {code} : {text}').format(code=str(status_code),
                                                        text=r.text))

    @QtCore.Slot()
    def on_discoveryList_itemSelectionChanged(self):
        rows = self.discoveryList.selectionModel().selectedRows()
        typ = rows[0].data(ROLE_DEVICE) if (len(rows) > 0) else TYP_UNKNOWN
        self.enableDiscoveryButton(typ)

    @QtCore.Slot()
    def on_fileopenButton_clicked(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fileName, _ = QFileDialog.getOpenFileName(
            self,
            "QFileDialog.getOpenFileName()",
            "",
            "Fimrware (*.bin *.ly32);;ESP8266 Firmware (*.bin);;ESP32 Firmware (*.ly32);;All Files (*)",
            options=options)
        if fileName:
            item = QtGui.QStandardItem("File: " + fileName)
            item.setData(fileName, ROLE_DEVICE)
            self.versionBox.model().insertRow(0, item)
            self.versionBox.setCurrentIndex(0)
            # idx = self.versionBox.currentIndex()
            # self.versionBox.selected
            # print(idx)
            # self.versionBox.setText(filename)

    @QtCore.Slot()
    def on_discoveryRefreshButton_clicked(self):
        self.discovery_start()
        self.port_detect.restart()
示例#7
0
    def __init__(self, parent=None, app=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Dialog)

        # FIXME: dirty hack to solve relative paths in *.ui
        oldcwd = os.getcwd()
        os.chdir(os.path.join(RESOURCES_PATH, 'assets'))
        self.setupUi(self)
        os.chdir(oldcwd)

        self.app = app

        self.translator = QtCore.QTranslator()
        self.i18n_init(QtCore.QLocale.system())

        self.statusbar.showMessage(self.tr("Loading firmware list..."))

        self.versionBox.clear()
        self.firmware_list = FirmwareListThread()
        self.firmware_list.listLoaded.connect(self.populate_versions)
        self.firmware_list.error.connect(self.on_work_error)
        self.firmware_list.start()

        self.port_detect = PortDetectThread()
        self.port_detect.portsUpdate.connect(self.populate_boards)
        self.port_detect.error.connect(self.on_work_error)
        self.port_detect.start()

        self.discovery_start()

        self.globalMessage.hide()

        # Hide WIP GUI parts...
        self.on_expertModeBox_clicked()
        self.expertModeBox.hide()
        self.tabWidget.removeTab(self.tabWidget.indexOf(self.serialTab))

        self.uploadProgress.connect(self.on_work_update)
        self.configProgress.connect(self.on_config_update)
        self.eraseProgress.connect(self.on_erase_update)
        self.errorSignal.connect(self.on_work_error)

        self.cachedir = tempfile.TemporaryDirectory()
        self.cachedirjson = tempfile.TemporaryDirectory()
        self.cachedirspiffs = tempfile.TemporaryDirectory()

        print(self.cachedir.name)
        print(self.cachedirjson.name)
        print(self.cachedirspiffs.name)

        self.sensorsList = [
            "SDS011", "SPS30", "BME280", "BMP180", "BMP280", "DHT22",
            "DNMS (noise)"
        ]
        self.languagesList = ["EN", "FR", "DE"]
        self.populate_sensors1(self.sensorsList)
        self.populate_sensors2(self.sensorsList)
        self.populate_languages(self.languagesList)

        self.sensor1Box.setCurrentIndex(0)
        self.sensor2Box.setCurrentIndex(2)
        self.languageBox.setCurrentIndex(0)

        self.customName.setPlaceholderText("Default = airRohr")

        self.wifiSSID.setPlaceholderText("Please double check...")
        self.wifiPW.setPlaceholderText("Please double check...")

        self.configjson = json.loads('{}')
        self.sensorID = 0
        self.customNameSave = ""
示例#8
0
class MainWindow(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
    uploadProgress = QtCore.Signal([str, int])
    configProgress = QtCore.Signal([str, int])
    eraseProgress = QtCore.Signal([str, int])
    errorSignal = QtCore.Signal([str])
    uploadThread = None
    zeroconf_discovery = None
    boards_detected = False
    jsonFinal = ""
    spiffsBinary = b''

    def __init__(self, parent=None, app=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Dialog)

        # FIXME: dirty hack to solve relative paths in *.ui
        oldcwd = os.getcwd()
        os.chdir(os.path.join(RESOURCES_PATH, 'assets'))
        self.setupUi(self)
        os.chdir(oldcwd)

        self.app = app

        self.translator = QtCore.QTranslator()
        self.i18n_init(QtCore.QLocale.system())

        self.statusbar.showMessage(self.tr("Loading firmware list..."))

        self.versionBox.clear()
        self.firmware_list = FirmwareListThread()
        self.firmware_list.listLoaded.connect(self.populate_versions)
        self.firmware_list.error.connect(self.on_work_error)
        self.firmware_list.start()

        self.port_detect = PortDetectThread()
        self.port_detect.portsUpdate.connect(self.populate_boards)
        self.port_detect.error.connect(self.on_work_error)
        self.port_detect.start()

        self.discovery_start()

        self.globalMessage.hide()

        # Hide WIP GUI parts...
        self.on_expertModeBox_clicked()
        self.expertModeBox.hide()
        self.tabWidget.removeTab(self.tabWidget.indexOf(self.serialTab))

        self.uploadProgress.connect(self.on_work_update)
        self.configProgress.connect(self.on_config_update)
        self.eraseProgress.connect(self.on_erase_update)
        self.errorSignal.connect(self.on_work_error)

        self.cachedir = tempfile.TemporaryDirectory()
        self.cachedirjson = tempfile.TemporaryDirectory()
        self.cachedirspiffs = tempfile.TemporaryDirectory()

        print(self.cachedir.name)
        print(self.cachedirjson.name)
        print(self.cachedirspiffs.name)

        self.sensorsList = [
            "SDS011", "SPS30", "BME280", "BMP180", "BMP280", "DHT22",
            "DNMS (noise)"
        ]
        self.languagesList = ["EN", "FR", "DE"]
        self.populate_sensors1(self.sensorsList)
        self.populate_sensors2(self.sensorsList)
        self.populate_languages(self.languagesList)

        self.sensor1Box.setCurrentIndex(0)
        self.sensor2Box.setCurrentIndex(2)
        self.languageBox.setCurrentIndex(0)

        self.customName.setPlaceholderText("Default = airRohr")

        self.wifiSSID.setPlaceholderText("Please double check...")
        self.wifiPW.setPlaceholderText("Please double check...")

        self.configjson = json.loads('{}')
        self.sensorID = 0
        self.customNameSave = ""

        # String		current_lang
        # String		wlanssid
        # Password		wlanpwd
        # String		www_username
        # Password		www_password
        # String		fs_ssid
        # Password		fs_pwd
        # Bool		www_basicauth_enabled
        # Bool		dht_read
        # Bool		htu21d_read
        # Bool		ppd_read
        # Bool		sds_read
        # Bool		pms_read
        # Bool		hpm_read
        # Bool		npm_read
        # Bool		sps30_read
        # Bool		bmp_read
        # Bool		bmx280_read
        # Bool		sht3x_read
        # Bool		ds18b20_read
        # Bool		dnms_read
        # String		dnms_correction
        # String		temp_correction
        # Bool		gps_read
        # Bool		send2dusti
        # Bool		ssl_dusti
        # Bool		send2madavi
        # Bool		ssl_madavi
        # Bool		send2sensemap
        # Bool		send2fsapp
        # Bool		send2aircms
        # Bool		send2csv
        # Bool		auto_update
        # Bool		use_beta
        # Bool		has_display
        # Bool		has_sh1106
        # Bool		has_flipped_display
        # Bool		has_lcd1602
        # Bool		has_lcd1602_27
        # Bool		has_lcd2004
        # Bool		has_lcd2004_27
        # Bool		display_wifi_info
        # Bool		display_device_info
        # UInt		debug
        # Time		sending_intervall_ms
        # Time		time_for_wifi_config
        # String		senseboxid
        # Bool		send2custom
        # String		host_custom
        # String		url_custom
        # UInt		port_custom
        # String		user_custom
        # Password		pwd_custom
        # Bool		ssl_custom
        # Bool		send2influx
        # String		host_influx
        # String		url_influx
        # UInt		port_influx
        # String		user_influx
        # Password		pwd_influx
        # String		measurement_name_influx
        # Bool		ssl_influx

    def show_global_message(self, title, message):
        self.globalMessage.show()
        self.globalMessageTitle.setText(title)
        self.globalMessageText.setText(message)

    def on_work_update(self, status, progress):
        self.statusbar.showMessage(status)
        self.progressBar.setValue(progress)

    def on_config_update(self, status, progress):
        self.statusbar.showMessage(status)
        self.progressBar_config.setValue(progress)

    def on_erase_update(self, status, progress):
        self.statusbar.showMessage(status)
        self.progressBar_erase.setValue(progress)

    def on_work_error(self, message):
        self.statusbar.showMessage(message)

    @property
    def version(self):
        return airrohrFlasher.__version__

    @property
    def build_id(self):
        try:
            from airrohrFlasher._buildid import commit, builddate
        except ImportError:
            import datetime
            commit = 'devel'
            builddate = datetime.datetime.now().strftime('%Y%m%d')

        return '{}-{}/{}'.format(self.version, commit, builddate)

    def i18n_init(self, locale):
        """Initializes i18n to specified QLocale"""

        self.app.removeTranslator(self.translator)
        lang = QtCore.QLocale.languageToString(locale.language())
        self.translator.load(os.path.join(RESOURCES_PATH, 'i18n',
                                          lang + '.qm'))
        self.app.installTranslator(self.translator)
        self.retranslateUi(self)

    def retranslateUi(self, win):
        super(MainWindow, self).retranslateUi(win)

        win.setWindowTitle(win.windowTitle().format(version=self.version))
        win.buildLabel.setText(
            win.buildLabel.text().format(build_id=self.build_id))

    def populate_versions(self, files):
        """Loads available firmware versions into versionbox widget"""

        for fname in files:
            if not fname.endswith('.bin'):
                continue

            item = QtGui.QStandardItem(fname)
            item.setData(UPDATE_REPOSITORY + fname, ROLE_DEVICE)
            self.versionBox.model().appendRow(item)

        self.statusbar.clearMessage()

    def populate_sensors1(self, sensors):
        for sensor in sensors:
            item = QtGui.QStandardItem(sensor)
            #item.setData(UPDATE_REPOSITORY + fname, ROLE_DEVICE)
            self.sensor1Box.model().appendRow(item)

    def populate_sensors2(self, sensors):
        for sensor in sensors:
            item = QtGui.QStandardItem(sensor)
            #item.setData(UPDATE_REPOSITORY + fname, ROLE_DEVICE)
            self.sensor2Box.model().appendRow(item)

    def populate_languages(self, languages):
        for language in languages:
            item = QtGui.QStandardItem(language)
            #item.setData(UPDATE_REPOSITORY + fname, ROLE_DEVICE)
            self.languageBox.model().appendRow(item)

    def populate_boards(self, ports):
        """Populates board selection combobox from list of pyserial
        ListPortInfo objects"""

        self.boardBox.clear()

        prefered, others = self.group_ports(ports)

        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)

            # No prefered boards has been found so far and there is a
            # suggested driver download URL available
            if not self.boards_detected and DRIVERS_URL:
                self.show_global_message(
                    self.tr('No boards found'),
                    self.tr(
                        'Have you installed <a href="{drivers_url}">'
                        'the drivers</a>?').format(drivers_url=DRIVERS_URL))
        else:
            self.globalMessage.hide()
            self.boards_detected = True

        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 is_json(self, myjson):
        try:
            json_object = json.loads(myjson)
        except ValueError as e:
            return False
        return True

        #Configuration saver

    def switcher(self, value):
        if value == "DHT22":
            self.configjson['dht_read'] = True
        elif value == "PPD42":
            self.configjson['ppd_read'] = True
        elif value == "SDS011":
            self.configjson['sds_read'] = True
        elif value == "PMSx003":
            self.configjson['pms_read'] = True
        elif value == "HPM":
            self.configjson['hpm_read'] = True
        elif value == "NPM":
            self.configjson['npm_read'] = True
        elif value == "SPS30":
            self.configjson['sps30_read'] = True
        elif value == "BMP":
            self.configjson['bmp_read'] = True
        elif value == "BME280":
            self.configjson['bmx280_read'] = True
        elif value == "SHT3X":
            self.configjson['sht3x_read'] = True
        elif value == "DS18B20":
            self.configjson['ds18b20_read'] = True
        elif value == "DNMS (noise)":
            self.configjson['dnms_read'] = True
        else:
            self.statusbar.showMessage(self.tr("Invalid sensor name."))
            return

    @QtCore.Slot()
    def on_wifiButton_clicked(self):
        self.statusbar.clearMessage()

        device = self.boardBox.currentData(ROLE_DEVICE)

        configstring = '{"SOFTWARE_VERSION":"flashingtool","current_lang":"","wlanssid":"","wlanpwd":"","www_username":"******","www_password":"","fs_ssid":"","fs_pwd":"","www_basicauth_enabled":false,"dht_read":false,"htu21d_read":false,"ppd_read":false,"sds_read":false,"pms_read":false,"hpm_read":false,"npm_read":false,"sps30_read":false,"bmp_read":false,"bmx280_read":false,"sht3x_read":false,"ds18b20_read":false,"dnms_read":false,"dnms_correction":"0.0","temp_correction":"0.0","gps_read":false,"send2dusti":true,"ssl_dusti":false,"send2madavi":true,"ssl_madavi":false,"send2sensemap":false,"send2fsapp":false,"send2aircms":false,"send2csv":false,"auto_update":true,"use_beta":false,"has_display":false,"has_sh1106":false,"has_flipped_display":false,"has_lcd1602":false,"has_lcd1602_27":false,"has_lcd2004":false,"has_lcd2004_27":false,"display_wifi_info":true,"display_device_info":true,"debug":3,"sending_intervall_ms":145000,"time_for_wifi_config":600000,"senseboxid":"","send2custom":false,"host_custom":"192.168.234.1","url_custom":"/data.php","port_custom":80,"user_custom":"","pwd_custom":"","ssl_custom":false,"send2influx":false,"host_influx":"influx.server","url_influx":"/write?db=sensorcommunity","port_influx":8086,"user_influx":"","pwd_influx":"","measurement_name_influx":"feinstaub","ssl_influx":false}'
        self.configjson = json.loads(configstring)
        ssid = self.wifiSSID.text()
        pw = self.wifiPW.text()
        pw_empty = self.wifiPW_empty.isChecked()
        apssid = self.customName.text()
        sensor1 = self.sensorsList[self.sensor1Box.currentIndex()]
        sensor2 = self.sensorsList[self.sensor2Box.currentIndex()]
        language = self.languagesList[self.languageBox.currentIndex()]

        if language not in self.languagesList:
            self.statusbar.showMessage(self.tr("Invalid language."))
            return

        if not ssid:
            self.statusbar.showMessage(self.tr("No SSID typed."))
            return

        if not pw and not pw_empty:
            self.statusbar.showMessage(self.tr("No password typed."))
            return

        if pw_empty:
            pw = ""

        if sensor1 == sensor2:
            self.statusbar.showMessage(self.tr("2 times the same sensor."))
            return

        if not apssid:
            # self.configjson['fs_ssid'] = "airRohr-" + str(self.sensorID)
            self.configjson['fs_ssid'] = ""
            # print(self.configjson['fs_ssid'])
        else:
            self.configjson['fs_ssid'] = apssid + "-" + str(self.sensorID)
            self.customNameSave = apssid
            print(self.configjson['fs_ssid'])

        self.switcher(sensor1)
        self.switcher(sensor2)

        self.configjson['wlanssid'] = ssid
        self.configjson['wlanpwd'] = pw
        self.configjson['current_lang'] = language

        jsonTest = json.dumps(self.configjson)

        if not self.is_json(jsonTest):
            self.statusbar.showMessage(self.tr("Created invalid json."))
            return
        else:
            self.jsonFinal = json.dumps(self.configjson)
            self.statusbar.showMessage(self.tr("Created valid json."))
            print(self.jsonFinal)

            self.statusbar.showMessage(
                self.tr("Opening temporary json directory."))

            jsonfile = open(self.cachedirjson.name + "/config.json", "w")
            self.statusbar.showMessage(
                self.tr("Write json in temporay json directory."))
            jsonfile.write(self.jsonFinal)
            jsonfile.close()

            self.statusbar.showMessage(self.tr("Make SPIFFS bin"))

            args = []
            args.extend([
                "spiffsgen.py", "--page-size", "256", "--block-size", "8192",
                "--meta-len=0", "0x100000"
            ])

            args.append("--no-magic-len")
            args.append("--aligned-obj-ix-tables")

            args.extend([
                self.cachedirjson.name,
                self.cachedirspiffs.name + "/spiffs.bin"
            ])

            sys.argv = args
            spiffsgen.main()
            self.statusbar.showMessage(self.tr("spiffs.bin done!"))

            self.statusbar.clearMessage()
            device = self.boardBox.currentData(ROLE_DEVICE)

            if not device:
                self.statusbar.showMessage(self.tr("No device selected."))
                return

            if self.write_config.running():
                self.statusbar.showMessage(self.tr("Work in progess..."))
                return

            self.write_config(self.configProgress,
                              device,
                              self.cachedirspiffs.name + "/spiffs.bin",
                              error=self.errorSignal)

    @QuickThread.wrap
    def write_config(self, progress, device, path, baudrate=460800):

        progress.emit(self.tr('Connecting...'), 0)

        init_baud = min(ESPLoader.ESP_ROM_BAUD, baudrate)
        esp = ESPLoader.detect_chip(device, init_baud, 'default_reset', False)

        progress.emit(
            self.tr('Connected. Chip type: {chip_type}').format(
                chip_type=esp.get_chip_description()), 0)
        esp = esp.run_stub()
        esp.change_baud(baudrate)

        with open(path, 'rb') as fd:
            uncimagespiffs = fd.read()

        imagespiffs = zlib.compress(uncimagespiffs, 0)

        address = 0x100000
        blocks = esp.flash_defl_begin(len(uncimagespiffs), len(imagespiffs),
                                      address)

        seq = 0
        written = 0
        t = time.time()
        while len(imagespiffs) > 0:

            current_addr = address + seq * esp.FLASH_WRITE_SIZE
            progress.emit(
                self.tr('Writing at 0x{address:08x}...').format(
                    address=current_addr), 100 * (seq + 1) // blocks)

            block = imagespiffs[0:esp.FLASH_WRITE_SIZE]
            esp.flash_defl_block(block, seq, timeout=3.0)
            imagespiffs = imagespiffs[esp.FLASH_WRITE_SIZE:]
            seq += 1
            written += len(block)
            #print("iteration "+str(seq))

        t = time.time() - t

        progress.emit(
            self.tr('Finished in {time:.2f} seconds. Sensor ID: {sensor_id}').
            format(time=t, sensor_id=esp.chip_id()), 100)

        esp.flash_finish(True)

    @QtCore.Slot()
    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

        sel = self.versionBox.model().item(self.versionBox.currentIndex())
        if sel:
            orig_version = sel.text()
        else:
            orig_version = ''

        if version == orig_version:
            # Editable combobox has been unchanged
            binary_uri = self.versionBox.currentData(ROLE_DEVICE)
        elif version.startswith(ALLOWED_PROTO):
            # User has provided a download URL
            binary_uri = version
        elif os.path.exists(version):
            binary_uri = version
        else:
            self.statusbar.showMessage(
                self.tr("Invalid version / file does not exist"))
            return

        if self.flash_board.running():
            self.statusbar.showMessage(self.tr("Work in progess..."))
            return

        self.flash_board(self.uploadProgress,
                         device,
                         binary_uri,
                         error=self.errorSignal)

    def cache_download(self, progress, binary_uri):
        """Downloads and caches file with status reports via Qt Signals"""
        cache_fname = os.path.join(
            self.cachedir.name,
            hashlib.sha256(binary_uri.encode('utf-8')).hexdigest())

        #print(self.cachedir)

        if os.path.exists(cache_fname):
            return cache_fname

        with open(cache_fname, 'wb') as fd:
            progress.emit(self.tr('Downloading...'), 0)
            response = requests.get(binary_uri, stream=True)
            total_length = response.headers.get('content-length')

            dl = 0
            total_length = int(total_length or 0)
            for data in response.iter_content(chunk_size=4096):
                dl += len(data)
                fd.write(data)

                if total_length:
                    progress.emit(self.tr('Downloading...'),
                                  (100 * dl) // total_length)

        return cache_fname

    @QuickThread.wrap
    def erase_board(self, progress, device, baudrate=460800):

        progress.emit(self.tr('Connecting...'), 0)
        init_baud = min(ESPLoader.ESP_ROM_BAUD, baudrate)
        esp = ESPLoader.detect_chip(device, init_baud, 'default_reset', False)

        progress.emit(
            self.tr('Connected. Chip type: {chip_type}').format(
                chip_type=esp.get_chip_description()), 0)
        esp = esp.run_stub()
        esp.change_baud(baudrate)
        esp.erase_flash()
        progress.emit(self.tr('Erasing complete!'), 100)

    @QtCore.Slot()
    def on_eraseButton_clicked(self):
        self.statusbar.clearMessage()
        device = self.boardBox.currentData(ROLE_DEVICE)

        if not device:
            self.statusbar.showMessage(self.tr("No device selected."))
            return

        if self.erase_board.running():
            self.statusbar.showMessage(self.tr("Erasing in progress..."))
            return

        self.erase_board(self.eraseProgress, device, error=self.errorSignal)

    @QuickThread.wrap
    def flash_board(self, progress, device, binary_uri, baudrate=460800):

        if binary_uri.startswith(ALLOWED_PROTO):
            binary_uri = self.cache_download(progress, binary_uri)

        progress.emit(self.tr('Connecting...'), 0)

        init_baud = min(ESPLoader.ESP_ROM_BAUD, baudrate)
        esp = ESPLoader.detect_chip(device, init_baud, 'default_reset', False)

        progress.emit(
            self.tr('Connected. Chip type: {chip_type}').format(
                chip_type=esp.get_chip_description()), 0)
        esp = esp.run_stub()
        esp.change_baud(baudrate)

        with open(binary_uri, 'rb') as fd:
            uncimage = fd.read()

        image = zlib.compress(uncimage, 9)

        address = 0x0
        blocks = esp.flash_defl_begin(len(uncimage), len(image), address)

        seq = 0
        written = 0
        t = time.time()
        while len(image) > 0:
            current_addr = address + seq * esp.FLASH_WRITE_SIZE
            progress.emit(
                self.tr('Writing at 0x{address:08x}...').format(
                    address=current_addr), 100 * (seq + 1) // blocks)

            block = image[0:esp.FLASH_WRITE_SIZE]
            esp.flash_defl_block(block, seq, timeout=3.0)
            image = image[esp.FLASH_WRITE_SIZE:]
            seq += 1
            written += len(block)
        t = time.time() - t

        progress.emit(
            self.tr('Finished in {time:.2f} seconds. Sensor ID: {sensor_id}').
            format(time=t, sensor_id=esp.chip_id()), 100)

        esp.flash_finish(True)

    @QtCore.Slot()
    def on_expertModeBox_clicked(self):
        self.expertForm.setVisible(self.expertModeBox.checkState())
        # self.centralwidget.setFixedHeight(
        #     self.centralwidget.sizeHint().height())
        # self.setFixedHeight(self.sizeHint().height())

    # Zeroconf page
    def discovery_start(self):
        if self.zeroconf_discovery:
            self.zeroconf_discovery.stop()

        self.zeroconf_discovery = ZeroconfDiscoveryThread()
        self.zeroconf_discovery.deviceDiscovered.connect(
            self.on_zeroconf_discovered)
        self.zeroconf_discovery.start()

    def on_zeroconf_discovered(self, name, address, info):
        """Called on every zeroconf discovered device"""
        # if (name.lower().startswith('feinstaubsensor')
        #         or name.lower().startswith('nam')
        #         or name.lower().startswith('smogomierz')
        #         or name.lower().startswith('airrohr')
        #         or name.lower().startswith(self.customNameSave)):
        item = QtWidgets.QListWidgetItem('{}: {}'.format(
            address,
            name.split('.')[0]))
        item.setData(ROLE_DEVICE, 'http://{}:{}'.format(address, info.port))
        self.discoveryList.addItem(item)

    @QtCore.Slot(QtWidgets.QListWidgetItem)
    def on_discoveryList_itemDoubleClicked(self, index):
        QtGui.QDesktopServices.openUrl(QtCore.QUrl(index.data(ROLE_DEVICE)))

    @QtCore.Slot()
    def on_discoveryRefreshButton_clicked(self):
        self.discoveryList.clear()
        self.discovery_start()