Compare commits

...

37 Commits
v1.3.0 ... main

Author SHA1 Message Date
Jesse Hills df4b36d80a
Update README.md 2023-07-04 11:40:01 +12:00
dependabot[bot] df73bb5585
Bump actions/cache from 2 to 3.0.1 (#118)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-04 19:37:50 +02:00
dependabot[bot] 77c8d64110
Bump actions/setup-python from 2.3.1 to 3.1.0 (#120)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-04 19:37:43 +02:00
dependabot[bot] f51fd45f11
Bump pylint from 2.12.2 to 2.13.4 (#119)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-04 19:28:25 +02:00
dependabot[bot] aef82a0761
Bump actions/checkout from 2.4.0 to 3 (#108)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-04 19:17:16 +02:00
dependabot[bot] bd67d926f0
Bump black from 22.1.0 to 22.3.0 (#116)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-04 19:16:56 +02:00
dependabot[bot] 18176f051a
Bump black from 21.12b0 to 22.1.0 (#104)
Bumps [black](https://github.com/psf/black) from 21.12b0 to 22.1.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/commits/22.1.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-31 20:08:04 +01:00
dependabot[bot] b4aaa8a5d7
Bump pylint from 2.11.1 to 2.12.2 (#93)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-08 14:09:43 +01:00
Otto Winter 1c24e594e9
Fix CI needs apt update sometimes (#100) 2022-01-08 14:01:47 +01:00
dependabot[bot] b1bfb387d4
Bump actions/setup-python from 2.2.2 to 2.3.1 (#91)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-08 13:35:35 +01:00
dependabot[bot] a3e9099ed7
Bump isort from 5.10.0 to 5.10.1 (#86)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-08 13:35:29 +01:00
dependabot[bot] a591997e72
Bump black from 21.10b0 to 21.12b0 (#94)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-08 13:35:19 +01:00
dependabot[bot] 09e119a610
Bump isort from 5.9.3 to 5.10.0 (#85) 2021-11-03 20:44:30 +01:00
dependabot[bot] adb44b7008
Bump actions/checkout from 2.3.5 to 2.4.0 (#84)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2.3.5 to 2.4.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.3.5...v2.4.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-02 22:42:59 +01:00
dependabot[bot] df83104724
Bump black from 21.9b0 to 21.10b0 (#83) 2021-11-01 20:10:42 +01:00
Otto winter 93b19f8f9e
Fix lock 2021-10-29 13:19:35 +02:00
Otto winter 528ef7655e
Add stale/lock bots 2021-10-29 13:16:43 +02:00
Otto winter 1d3e589722
Constrain release workflow to created event 2021-10-29 12:56:15 +02:00
Otto winter 7a65462464
Bump version to 1.4.0 2021-10-29 12:50:12 +02:00
Otto winter 152e7491a7
gitignore vscode 2021-10-29 12:49:46 +02:00
Otto Winter e7ea643a36
Add python linting (#80) 2021-10-29 12:48:46 +02:00
Otto Winter b9dd803e1a
Bump requests from 2.24.0 to 2.26.0 (#81) 2021-10-29 12:43:49 +02:00
Otto Winter 23a6bae0ed
Bump python for assets to 3.9 (#79) 2021-10-29 12:39:15 +02:00
dependabot[bot] 9d57fbbff3
Bump actions/setup-python from 1 to 2.2.2 (#78)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 12:12:31 +02:00
dependabot[bot] 2598775f01
Bump actions/checkout from 1 to 2.3.5 (#77)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-29 12:12:24 +02:00
Otto Winter 493717c725
Dependabot enable GH Actions (#76) 2021-10-29 12:07:12 +02:00
Otto Winter 26de901da7
Add automated release workflow (#75) 2021-10-29 12:04:24 +02:00
Otto Winter d732be0068
Fix build workflow executing twice for internal PRs (#74) 2021-10-29 11:15:22 +02:00
Bas 4221529467
Make MacOS build executable (#54)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-10-29 11:14:25 +02:00
Otto Winter cf1d2a2e2e
Fix main branch rename partitions url (#73) 2021-10-29 11:13:46 +02:00
Otto Winter 7b21f8ef45
Bump esptool from 2.8 to 3.2 (#71) 2021-10-29 11:13:29 +02:00
Otto Winter c49ec75f06
Bump pyinstaller from 3.6 to 4.5.1 (#72) 2021-10-29 11:12:47 +02:00
Otto Winter b0aeedc573
Bump wxPython from 4.0.0 to 4.1.1 (#70) 2021-10-29 11:06:32 +02:00
Alex 2f7b469718
Update partitions (#67) 2021-10-29 10:54:14 +02:00
Guillermo Ruffino 294c6ad459
Bump version to v1.3.1 2021-01-19 15:52:57 -03:00
gordon-zhao 8fc5341336
BugFix: Upload speed fallback logic (#44)
- Fix the problem that the error from detect_flash_size() doesn't get caught (because detect_flash_size() also tries to catch esptool.FatalError, and it will raise the error again as EsphomeflasherError)
- Fix typo
2021-01-19 15:44:07 -03:00
Fabian Affolter 12ed27be9b
Fix typo (#41) 2021-01-19 15:43:19 -03:00
23 changed files with 705 additions and 207 deletions

View File

@ -4,3 +4,7 @@ updates:
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: "daily"

View File

@ -2,6 +2,7 @@ name: Build Workflow
on:
push:
branches: [main]
pull_request:
jobs:
@ -9,11 +10,11 @@ jobs:
runs-on: windows-2019
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v1
uses: actions/setup-python@v3.1.0
with:
python-version: '3.7'
python-version: '3.9'
architecture: 'x64'
- name: Install requirements
run: |
@ -31,11 +32,11 @@ jobs:
runs-on: windows-2019
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v1
uses: actions/setup-python@v3.1.0
with:
python-version: '3.7'
python-version: '3.9'
architecture: 'x86'
- name: Install requirements
run: |
@ -55,13 +56,14 @@ jobs:
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v1
uses: actions/setup-python@v3.1.0
with:
python-version: '3.7'
python-version: '3.9'
- name: Install dependencies
run: |
sudo apt update
sudo apt install libgtk-3-dev libnotify-dev libsdl2-dev
pip install -U \
-f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 \
@ -85,11 +87,11 @@ jobs:
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v1
uses: actions/setup-python@v3.1.0
with:
python-version: '3.7'
python-version: '3.9'
- name: Install requirements
run: |
pip install -r requirements.txt -r requirements_build.txt
@ -99,25 +101,27 @@ jobs:
python -m PyInstaller.__main__ -F -w -n ESPHome-Flasher -i icon.icns esphomeflasher/__main__.py
- name: See dist directory
run: ls dist
- name: Move app
- name: Tar files
run: |
mv dist/ESPHome-Flasher.app dist/ESPHome-Flasher-dev-macOS.app
- uses: actions/upload-artifact@v2
tar -C dist -cvf dist.tar ESPHome-Flasher.app
- name: 'Upload Artifact'
uses: actions/upload-artifact@v2
with:
name: macOS
path: dist/
path: dist.tar
build-pypi:
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v1
uses: actions/setup-python@v3.1.0
with:
python-version: '3.7'
python-version: '3.9'
- name: Install dependencies
run: |
sudo apt update
sudo apt install libgtk-3-dev libnotify-dev libsdl2-dev
pip install -U \
-f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 \

71
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,71 @@
name: Lint workflow
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
ci:
name: ${{ matrix.name }}
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
include:
- id: flake8
name: Lint with flake8
- id: pylint
name: Lint with pylint
- id: black
name: Check formatting with black
- id: isort
name: Check import order with isort
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: '3.7'
- name: Install apt dependencies
run: |
sudo apt update
sudo apt install libgtk-3-dev libnotify-dev libsdl2-dev
- name: Get pip cache dir
id: pip-cache
run: |
echo "::set-output name=dir::$(pip cache dir)"
- name: Restore PIP cache
uses: actions/cache@v3.0.1
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: pip-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt', 'requirements_test.txt') }}
restore-keys: |
pip-${{ steps.python.outputs.python-version }}-
- name: Set up Python environment
run: |
pip3 install -U \
-f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 \
wxPython
pip3 install -r requirements.txt -r requirements_test.txt
pip3 install -e .
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/flake8.json"
echo "::add-matcher::.github/workflows/matchers/pylint.json"
echo "::add-matcher::.github/workflows/matchers/isort.json"
- run: flake8 esphomeflasher
if: ${{ matrix.id == 'flake8' }}
- run: pylint esphomeflasher
if: ${{ matrix.id == 'pylint' }}
- run: black --check --diff --color esphomeflasher
if: ${{ matrix.id == 'black' }}
- run: isort --check --diff esphomeflasher
if: ${{ matrix.id == 'isort' }}

27
.github/workflows/lock.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Lock
on:
schedule:
- cron: '30 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
with:
pr-inactive-days: "7"
pr-lock-reason: ""
exclude-any-pr-labels: keep-open
issue-inactive-days: "7"
issue-lock-reason: ""
exclude-any-issue-labels: keep-open

30
.github/workflows/matchers/flake8.json vendored Normal file
View File

@ -0,0 +1,30 @@
{
"problemMatcher": [
{
"owner": "flake8-error",
"severity": "error",
"pattern": [
{
"regexp": "^(.*):(\\d+):(\\d+):\\s([EF]\\d{3}\\s.*)$",
"file": 1,
"line": 2,
"column": 3,
"message": 4
}
]
},
{
"owner": "flake8-warning",
"severity": "warning",
"pattern": [
{
"regexp": "^(.*):(\\d+):(\\d+):\\s([CDNW]\\d{3}\\s.*)$",
"file": 1,
"line": 2,
"column": 3,
"message": 4
}
]
}
]
}

14
.github/workflows/matchers/isort.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
"problemMatcher": [
{
"owner": "isort",
"pattern": [
{
"regexp": "^ERROR:\\s+(.+)\\s+(.+)$",
"file": 1,
"message": 2
}
]
}
]
}

32
.github/workflows/matchers/pylint.json vendored Normal file
View File

@ -0,0 +1,32 @@
{
"problemMatcher": [
{
"owner": "pylint-error",
"severity": "error",
"pattern": [
{
"regexp": "^(.+):(\\d+):(\\d+):\\s(([EF]\\d{4}):\\s.+)$",
"file": 1,
"line": 2,
"column": 3,
"message": 4,
"code": 5
}
]
},
{
"owner": "pylint-warning",
"severity": "warning",
"pattern": [
{
"regexp": "^(.+):(\\d+):(\\d+):\\s(([CRW]\\d{4}):\\s.+)$",
"file": 1,
"line": 2,
"column": 3,
"message": 4,
"code": 5
}
]
}
]
}

121
.github/workflows/release-assets.yml vendored Normal file
View File

@ -0,0 +1,121 @@
name: Release Assets Workflow
on:
release:
type: [created]
jobs:
build-windows:
runs-on: windows-2019
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v3.1.0
with:
python-version: '3.9'
architecture: 'x64'
- name: Install requirements
run: |
pip install -r requirements.txt -r requirements_build.txt
pip install -e .
- name: Run PyInstaller
run: |
python -m PyInstaller.__main__ -F -w -n ESPHome-Flasher -i icon.ico esphomeflasher\__main__.py
- uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: dist/ESPHome-Flasher.exe
asset_name: ESPHome-Flasher-$tag-Windows-x64.exe
tag: ${{ github.ref }}
overwrite: true
build-windows-x86:
runs-on: windows-2019
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v3.1.0
with:
python-version: '3.9'
architecture: 'x86'
- name: Install requirements
run: |
pip install -r requirements.txt -r requirements_build.txt
pip install -e .
- name: Run PyInstaller
run: |
python -m PyInstaller.__main__ -F -w -n ESPHome-Flasher -i icon.ico esphomeflasher\__main__.py
- name: See dist directory
run: ls dist
- uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: dist/ESPHome-Flasher.exe
asset_name: ESPHome-Flasher-$tag-Windows-x86.exe
tag: ${{ github.ref }}
overwrite: true
build-ubuntu:
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v3.1.0
with:
python-version: '3.9'
- name: Install dependencies
run: |
sudo apt update
sudo apt install libgtk-3-dev libnotify-dev libsdl2-dev
pip install -U \
-f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 \
wxPython
- name: Install requirements
run: |
pip install -r requirements.txt -r requirements_build.txt
pip install -e .
- name: Run PyInstaller
run: |
python -m PyInstaller.__main__ -F -w -n ESPHome-Flasher -i icon.ico esphomeflasher/__main__.py
- name: See dist directory
run: ls dist
- uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: dist/ESPHome-Flasher
asset_name: ESPHome-Flasher-$tag-Ubuntu-x64.exec
tag: ${{ github.ref }}
overwrite: true
build-macos:
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v3.1.0
with:
python-version: '3.9'
- name: Install requirements
run: |
pip install -r requirements.txt -r requirements_build.txt
pip install -e .
- name: Run PyInstaller
run: |
python -m PyInstaller.__main__ -F -w -n ESPHome-Flasher -i icon.icns esphomeflasher/__main__.py
- name: See dist directory
run: ls dist
- name: Tar files
run: |
tar -C dist -cvf dist.tar ESPHome-Flasher.app
- uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: dist.tar
asset_name: ESPHome-Flasher-$tag-macOS.tar
tag: ${{ github.ref }}
overwrite: true

38
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Stale
on:
schedule:
- cron: '30 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
days-before-pr-stale: 90
days-before-pr-close: 7
days-before-issue-stale: 90
days-before-issue-close: 7
remove-stale-when-updated: true
stale-pr-label: "stale"
exempt-pr-labels: "no-stale"
stale-issue-label: "stale"
exempt-issue-labels: "no-stale"
stale-pr-message: >
There hasn't been any activity on this pull request recently. This
pull request has been automatically marked as stale because of that
and will be closed if no further activity occurs within 7 days.
Thank you for your contributions.
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.

9
.gitignore vendored
View File

@ -107,3 +107,12 @@ config/
.DS_Store
/.idea/
.vscode/*
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/

View File

@ -1,3 +1,8 @@
# Deprecated
This project is deprecated in favour of browser based flashing with [ESP Web Tools](https://github.com/esphome/esp-web-tools). For example: https://web.esphome.io
# ESPHome-Flasher
ESPHome-Flasher is a utility app for the [ESPHome](https://esphome.io/)
@ -18,7 +23,7 @@ library by espressif.
## Installation
Es doesn't have to be installed, just double-click it and it'll start.
It doesn't have to be installed, just double-click it and it'll start.
Check the [releases section](https://github.com/esphome/esphome-flasher/releases)
for downloads for your platform.

View File

@ -1,51 +1,70 @@
from __future__ import print_function
import argparse
from datetime import datetime
import sys
import time
from datetime import datetime
import esptool
import serial
from esphomeflasher import const
from esphomeflasher.common import ESP32ChipInfo, EsphomeflasherError, chip_run_stub, \
configure_write_flash_args, detect_chip, detect_flash_size, read_chip_info
from esphomeflasher.const import ESP32_DEFAULT_BOOTLOADER_FORMAT, ESP32_DEFAULT_OTA_DATA, \
ESP32_DEFAULT_PARTITIONS
from esphomeflasher.common import (
ESP32ChipInfo,
EsphomeflasherError,
chip_run_stub,
configure_write_flash_args,
detect_chip,
detect_flash_size,
read_chip_info,
)
from esphomeflasher.const import (
ESP32_DEFAULT_BOOTLOADER_FORMAT,
ESP32_DEFAULT_OTA_DATA,
ESP32_DEFAULT_PARTITIONS,
)
from esphomeflasher.helpers import list_serial_ports
def parse_args(argv):
parser = argparse.ArgumentParser(prog='esphomeflasher {}'.format(const.__version__))
parser.add_argument('-p', '--port',
help="Select the USB/COM port for uploading.")
parser = argparse.ArgumentParser(prog=f"esphomeflasher {const.__version__}")
parser.add_argument("-p", "--port", help="Select the USB/COM port for uploading.")
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('--esp8266', action='store_true')
group.add_argument('--esp32', action='store_true')
group.add_argument('--upload-baud-rate', type=int, default=460800,
help="Baud rate to upload with (not for logging)")
parser.add_argument('--bootloader',
help="(ESP32-only) The bootloader to flash.",
default=ESP32_DEFAULT_BOOTLOADER_FORMAT)
parser.add_argument('--partitions',
help="(ESP32-only) The partitions to flash.",
default=ESP32_DEFAULT_PARTITIONS)
parser.add_argument('--otadata',
help="(ESP32-only) The otadata file to flash.",
default=ESP32_DEFAULT_OTA_DATA)
parser.add_argument('--no-erase',
help="Do not erase flash before flashing",
action='store_true')
parser.add_argument('--show-logs', help="Only show logs", action='store_true')
parser.add_argument('binary', help="The binary image to flash.")
group.add_argument("--esp8266", action="store_true")
group.add_argument("--esp32", action="store_true")
group.add_argument(
"--upload-baud-rate",
type=int,
default=460800,
help="Baud rate to upload with (not for logging)",
)
parser.add_argument(
"--bootloader",
help="(ESP32-only) The bootloader to flash.",
default=ESP32_DEFAULT_BOOTLOADER_FORMAT,
)
parser.add_argument(
"--partitions",
help="(ESP32-only) The partitions to flash.",
default=ESP32_DEFAULT_PARTITIONS,
)
parser.add_argument(
"--otadata",
help="(ESP32-only) The otadata file to flash.",
default=ESP32_DEFAULT_OTA_DATA,
)
parser.add_argument(
"--no-erase", help="Do not erase flash before flashing", action="store_true"
)
parser.add_argument("--show-logs", help="Only show logs", action="store_true")
parser.add_argument("binary", help="The binary image to flash.")
return parser.parse_args(argv[1:])
def select_port(args):
if args.port is not None:
print(u"Using '{}' as serial port.".format(args.port))
print(f"Using '{args.port}' as serial port.")
return args.port
ports = list_serial_ports()
if not ports:
@ -53,10 +72,10 @@ def select_port(args):
if len(ports) != 1:
print("Found more than one serial port:")
for port, desc in ports:
print(u" * {} ({})".format(port, desc))
print(f" * {port} ({desc})")
print("Please choose one with the --port argument.")
raise EsphomeflasherError
print(u"Auto-detected serial port: {}".format(ports[0][0]))
print(f"Auto-detected serial port: {ports[0][0]}")
return ports[0][0]
@ -69,14 +88,14 @@ def show_logs(serial_port):
except serial.SerialException:
print("Serial port closed!")
return
text = raw.decode(errors='ignore')
line = text.replace('\r', '').replace('\n', '')
time = datetime.now().time().strftime('[%H:%M:%S]')
message = time + line
text = raw.decode(errors="ignore")
line = text.replace("\r", "").replace("\n", "")
time_ = datetime.now().time().strftime("[%H:%M:%S]")
message = time_ + line
try:
print(message)
except UnicodeEncodeError:
print(message.encode('ascii', 'backslashreplace'))
print(message.encode("ascii", "backslashreplace"))
def run_esphomeflasher(argv):
@ -89,27 +108,29 @@ def run_esphomeflasher(argv):
return
try:
firmware = open(args.binary, 'rb')
# pylint: disable=consider-using-with
firmware = open(args.binary, "rb")
except IOError as err:
raise EsphomeflasherError("Error opening binary: {}".format(err))
raise EsphomeflasherError(f"Error opening binary: {err}") from err
chip = detect_chip(port, args.esp8266, args.esp32)
info = read_chip_info(chip)
print()
print("Chip Info:")
print(" - Chip Family: {}".format(info.family))
print(" - Chip Model: {}".format(info.model))
print(f" - Chip Family: {info.family}")
print(f" - Chip Model: {info.model}")
if isinstance(info, ESP32ChipInfo):
print(" - Number of Cores: {}".format(info.num_cores))
print(" - Max CPU Frequency: {}".format(info.cpu_frequency))
print(" - Has Bluetooth: {}".format('YES' if info.has_bluetooth else 'NO'))
print(" - Has Embedded Flash: {}".format('YES' if info.has_embedded_flash else 'NO'))
print(" - Has Factory-Calibrated ADC: {}".format(
'YES' if info.has_factory_calibrated_adc else 'NO'))
print(f" - Number of Cores: {info.num_cores}")
print(f" - Max CPU Frequency: {info.cpu_frequency}")
print(f" - Has Bluetooth: {'YES' if info.has_bluetooth else 'NO'}")
print(f" - Has Embedded Flash: {'YES' if info.has_embedded_flash else 'NO'}")
print(
f" - Has Factory-Calibrated ADC: {'YES' if info.has_factory_calibrated_adc else 'NO'}"
)
else:
print(" - Chip ID: {:08X}".format(info.chip_id))
print(f" - Chip ID: {info.chip_id:08X}")
print(" - MAC Address: {}".format(info.mac))
print(f" - MAC Address: {info.mac}")
stub_chip = chip_run_stub(chip)
flash_size = None
@ -118,14 +139,19 @@ def run_esphomeflasher(argv):
try:
stub_chip.change_baud(args.upload_baud_rate)
except esptool.FatalError as err:
raise EsphomeflasherError("Error changing ESP upload baud rate: {}".format(err))
raise EsphomeflasherError(
f"Error changing ESP upload baud rate: {err}"
) from err
# Check if the higher baud rate works
try:
flash_size = detect_flash_size(stub_chip)
except esptool.FatalError as err:
except EsphomeflasherError:
# Go back to old baud rate by recreating chip instance
print("Chip does not support baud rate {}, changing to 115200".format(args.upload_baud_Rate))
print(
f"Chip does not support baud rate {args.upload_baud_rate}, changing to 115200"
)
# pylint: disable=protected-access
stub_chip._port.close()
chip = detect_chip(port, args.esp8266, args.esp32)
stub_chip = chip_run_stub(chip)
@ -133,31 +159,30 @@ def run_esphomeflasher(argv):
if flash_size is None:
flash_size = detect_flash_size(stub_chip)
print(f" - Flash Size: {flash_size}")
print(" - Flash Size: {}".format(flash_size))
mock_args = configure_write_flash_args(
info, firmware, flash_size, args.bootloader, args.partitions, args.otadata
)
mock_args = configure_write_flash_args(info, firmware, flash_size,
args.bootloader, args.partitions,
args.otadata)
print(" - Flash Mode: {}".format(mock_args.flash_mode))
print(" - Flash Frequency: {}Hz".format(mock_args.flash_freq.upper()))
print(f" - Flash Mode: {mock_args.flash_mode}")
print(f" - Flash Frequency: {mock_args.flash_freq.upper()}Hz")
try:
stub_chip.flash_set_parameters(esptool.flash_size_bytes(flash_size))
except esptool.FatalError as err:
raise EsphomeflasherError("Error setting flash parameters: {}".format(err))
raise EsphomeflasherError(f"Error setting flash parameters: {err}") from err
if not args.no_erase:
try:
esptool.erase_flash(stub_chip, mock_args)
except esptool.FatalError as err:
raise EsphomeflasherError("Error while erasing flash: {}".format(err))
raise EsphomeflasherError(f"Error while erasing flash: {err}") from err
try:
esptool.write_flash(stub_chip, mock_args)
except esptool.FatalError as err:
raise EsphomeflasherError("Error while writing flash: {}".format(err))
raise EsphomeflasherError(f"Error while writing flash: {err}") from err
print("Hard Resetting...")
stub_chip.hard_reset()
@ -166,10 +191,13 @@ def run_esphomeflasher(argv):
print()
if args.upload_baud_rate != 115200:
# pylint: disable=protected-access
stub_chip._port.baudrate = 115200
time.sleep(0.05) # get rid of crap sent during baud rate change
# pylint: disable=protected-access
stub_chip._port.flushInput()
# pylint: disable=protected-access
show_logs(stub_chip._port)

View File

@ -11,7 +11,7 @@ class EsphomeflasherError(Exception):
pass
class MockEsptoolArgs(object):
class MockEsptoolArgs:
def __init__(self, flash_size, addr_filename, flash_mode, flash_freq):
self.compress = True
self.no_compress = False
@ -23,9 +23,10 @@ class MockEsptoolArgs(object):
self.verify = False
self.erase_all = False
self.encrypt = False
self.encrypt_files = None
class ChipInfo(object):
class ChipInfo:
def __init__(self, family, model, mac):
self.family = family
self.model = model
@ -34,17 +35,25 @@ class ChipInfo(object):
def as_dict(self):
return {
'family': self.family,
'model': self.model,
'mac': self.mac,
'is_esp32': self.is_esp32,
"family": self.family,
"model": self.model,
"mac": self.mac,
"is_esp32": self.is_esp32,
}
class ESP32ChipInfo(ChipInfo):
def __init__(self, model, mac, num_cores, cpu_frequency, has_bluetooth, has_embedded_flash,
has_factory_calibrated_adc):
super(ESP32ChipInfo, self).__init__("ESP32", model, mac)
def __init__(
self,
model,
mac,
num_cores,
cpu_frequency,
has_bluetooth,
has_embedded_flash,
has_factory_calibrated_adc,
):
super().__init__("ESP32", model, mac)
self.num_cores = num_cores
self.cpu_frequency = cpu_frequency
self.has_bluetooth = has_bluetooth
@ -53,26 +62,30 @@ class ESP32ChipInfo(ChipInfo):
def as_dict(self):
data = ChipInfo.as_dict(self)
data.update({
'num_cores': self.num_cores,
'cpu_frequency': self.cpu_frequency,
'has_bluetooth': self.has_bluetooth,
'has_embedded_flash': self.has_embedded_flash,
'has_factory_calibrated_adc': self.has_factory_calibrated_adc,
})
data.update(
{
"num_cores": self.num_cores,
"cpu_frequency": self.cpu_frequency,
"has_bluetooth": self.has_bluetooth,
"has_embedded_flash": self.has_embedded_flash,
"has_factory_calibrated_adc": self.has_factory_calibrated_adc,
}
)
return data
class ESP8266ChipInfo(ChipInfo):
def __init__(self, model, mac, chip_id):
super(ESP8266ChipInfo, self).__init__("ESP8266", model, mac)
super().__init__("ESP8266", model, mac)
self.chip_id = chip_id
def as_dict(self):
data = ChipInfo.as_dict(self)
data.update({
'chip_id': self.chip_id,
})
data.update(
{
"chip_id": self.chip_id,
}
)
return data
@ -80,38 +93,47 @@ def read_chip_property(func, *args, **kwargs):
try:
return prevent_print(func, *args, **kwargs)
except esptool.FatalError as err:
raise EsphomeflasherError("Reading chip details failed: {}".format(err))
raise EsphomeflasherError(f"Reading chip details failed: {err}") from err
def read_chip_info(chip):
mac = ':'.join('{:02X}'.format(x) for x in read_chip_property(chip.read_mac))
mac = ":".join(f"{x:02X}" for x in read_chip_property(chip.read_mac))
if isinstance(chip, esptool.ESP32ROM):
model = read_chip_property(chip.get_chip_description)
features = read_chip_property(chip.get_chip_features)
num_cores = 2 if 'Dual Core' in features else 1
frequency = next((x for x in ('160MHz', '240MHz') if x in features), '80MHz')
has_bluetooth = 'BT' in features
has_embedded_flash = 'Embedded Flash' in features
has_factory_calibrated_adc = 'VRef calibration in efuse' in features
return ESP32ChipInfo(model, mac, num_cores, frequency, has_bluetooth,
has_embedded_flash, has_factory_calibrated_adc)
elif isinstance(chip, esptool.ESP8266ROM):
num_cores = 2 if "Dual Core" in features else 1
frequency = next((x for x in ("160MHz", "240MHz") if x in features), "80MHz")
has_bluetooth = "BT" in features
has_embedded_flash = "Embedded Flash" in features
has_factory_calibrated_adc = "VRef calibration in efuse" in features
return ESP32ChipInfo(
model,
mac,
num_cores,
frequency,
has_bluetooth,
has_embedded_flash,
has_factory_calibrated_adc,
)
if isinstance(chip, esptool.ESP8266ROM):
model = read_chip_property(chip.get_chip_description)
chip_id = read_chip_property(chip.chip_id)
return ESP8266ChipInfo(model, mac, chip_id)
raise EsphomeflasherError("Unknown chip type {}".format(type(chip)))
raise EsphomeflasherError(f"Unknown chip type {type(chip)}")
def chip_run_stub(chip):
try:
return chip.run_stub()
except esptool.FatalError as err:
raise EsphomeflasherError("Error putting ESP in stub flash mode: {}".format(err))
raise EsphomeflasherError(
f"Error putting ESP in stub flash mode: {err}"
) from err
def detect_flash_size(stub_chip):
flash_id = read_chip_property(stub_chip.flash_id)
return esptool.DETECTED_FLASH_SIZES.get(flash_id >> 16, '4MB')
return esptool.DETECTED_FLASH_SIZES.get(flash_id >> 16, "4MB")
def read_firmware_info(firmware):
@ -121,16 +143,16 @@ def read_firmware_info(firmware):
magic, _, flash_mode_raw, flash_size_freq = struct.unpack("BBBB", header)
if magic != esptool.ESPLoader.ESP_IMAGE_MAGIC:
raise EsphomeflasherError(
"The firmware binary is invalid (magic byte={:02X}, should be {:02X})"
"".format(magic, esptool.ESPLoader.ESP_IMAGE_MAGIC))
f"The firmware binary is invalid (magic byte={magic:02X}, should be {esptool.ESPLoader.ESP_IMAGE_MAGIC:02X})"
)
flash_freq_raw = flash_size_freq & 0x0F
flash_mode = {0: 'qio', 1: 'qout', 2: 'dio', 3: 'dout'}.get(flash_mode_raw)
flash_freq = {0: '40m', 1: '26m', 2: '20m', 0xF: '80m'}.get(flash_freq_raw)
flash_mode = {0: "qio", 1: "qout", 2: "dio", 3: "dout"}.get(flash_mode_raw)
flash_freq = {0: "40m", 1: "26m", 2: "20m", 0xF: "80m"}.get(flash_freq_raw)
return flash_mode, flash_freq
def open_downloadable_binary(path):
if hasattr(path, 'seek'):
if hasattr(path, "seek"):
path.seek(0)
return path
@ -142,10 +164,12 @@ def open_downloadable_binary(path):
response.raise_for_status()
except requests.exceptions.Timeout as err:
raise EsphomeflasherError(
"Timeout while retrieving firmware file '{}': {}".format(path, err))
f"Timeout while retrieving firmware file '{path}': {err}"
) from err
except requests.exceptions.RequestException as err:
raise EsphomeflasherError(
"Error while retrieving firmware file '{}': {}".format(path, err))
f"Error while retrieving firmware file '{path}': {err}"
) from err
binary = io.BytesIO()
binary.write(response.content)
@ -153,26 +177,29 @@ def open_downloadable_binary(path):
return binary
try:
return open(path, 'rb')
return open(path, "rb")
except IOError as err:
raise EsphomeflasherError("Error opening binary '{}': {}".format(path, err))
raise EsphomeflasherError(f"Error opening binary '{path}': {err}") from err
def format_bootloader_path(path, flash_mode, flash_freq):
return path.replace('$FLASH_MODE$', flash_mode).replace('$FLASH_FREQ$', flash_freq)
return path.replace("$FLASH_MODE$", flash_mode).replace("$FLASH_FREQ$", flash_freq)
def configure_write_flash_args(info, firmware_path, flash_size,
bootloader_path, partitions_path, otadata_path):
def configure_write_flash_args(
info, firmware_path, flash_size, bootloader_path, partitions_path, otadata_path
):
addr_filename = []
firmware = open_downloadable_binary(firmware_path)
flash_mode, flash_freq = read_firmware_info(firmware)
if isinstance(info, ESP32ChipInfo):
if flash_freq in ('26m', '20m'):
if flash_freq in ("26m", "20m"):
raise EsphomeflasherError(
"No bootloader available for flash frequency {}".format(flash_freq))
f"No bootloader available for flash frequency {flash_freq}"
)
bootloader = open_downloadable_binary(
format_bootloader_path(bootloader_path, flash_mode, flash_freq))
format_bootloader_path(bootloader_path, flash_mode, flash_freq)
)
partitions = open_downloadable_binary(partitions_path)
otadata = open_downloadable_binary(otadata_path)
@ -193,11 +220,15 @@ def detect_chip(port, force_esp8266=False, force_esp32=False):
try:
chip = esptool.ESPLoader.detect_chip(port)
except esptool.FatalError as err:
raise EsphomeflasherError("ESP Chip Auto-Detection failed: {}".format(err))
if "Wrong boot mode detected" in str(err):
msg = "ESP is not in flash boot mode. If your board has a flashing pin, try again while keeping it pressed."
else:
msg = f"ESP Chip Auto-Detection failed: {err}"
raise EsphomeflasherError(msg) from err
try:
chip.connect()
except esptool.FatalError as err:
raise EsphomeflasherError("Error connecting to ESP: {}".format(err))
raise EsphomeflasherError(f"Error connecting to ESP: {err}") from err
return chip

View File

@ -1,11 +1,17 @@
import re
__version__ = "1.3.0"
__version__ = "1.4.0"
ESP32_DEFAULT_OTA_DATA = 'https://raw.githubusercontent.com/espressif/arduino-esp32/1.0.0/tools/partitions/boot_app0.bin'
ESP32_DEFAULT_BOOTLOADER_FORMAT = 'https://raw.githubusercontent.com/espressif/arduino-esp32/' \
'1.0.4/tools/sdk/bin/bootloader_$FLASH_MODE$_$FLASH_FREQ$.bin'
ESP32_DEFAULT_PARTITIONS = 'https://raw.githubusercontent.com/esphome/esphomeflasher/master/partitions.bin'
ESP32_DEFAULT_OTA_DATA = "https://raw.githubusercontent.com/espressif/arduino-esp32/1.0.0/tools/partitions/boot_app0.bin"
ESP32_DEFAULT_BOOTLOADER_FORMAT = (
"https://raw.githubusercontent.com/espressif/arduino-esp32/"
"1.0.4/tools/sdk/bin/bootloader_$FLASH_MODE$_$FLASH_FREQ$.bin"
)
ESP32_DEFAULT_PARTITIONS = (
"https://raw.githubusercontent.com/esphome/esphomeflasher/main/partitions.bin"
)
# https://stackoverflow.com/a/3809435/8924614
HTTP_REGEX = re.compile(r'https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)')
HTTP_REGEX = re.compile(
r"https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)"
)

View File

@ -2,37 +2,40 @@
import re
import sys
import threading
from io import TextIOBase
import wx
import wx.adv
from wx.lib.embeddedimage import PyEmbeddedImage
import wx.lib.inspection
import wx.lib.mixins.inspection
from wx.lib.embeddedimage import PyEmbeddedImage
from esphomeflasher.helpers import list_serial_ports
# pylint: disable=no-member
COLOR_RE = re.compile(r'(?:\033)(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))')
COLOR_RE = re.compile(r"(?:\033)(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))")
COLORS = {
'black': wx.BLACK,
'red': wx.RED,
'green': wx.GREEN,
'yellow': wx.YELLOW,
'blue': wx.BLUE,
'magenta': wx.Colour(255, 0, 255),
'cyan': wx.CYAN,
'white': wx.WHITE,
"black": wx.BLACK,
"red": wx.RED,
"green": wx.GREEN,
"yellow": wx.YELLOW,
"blue": wx.BLUE,
"magenta": wx.Colour(255, 0, 255),
"cyan": wx.CYAN,
"white": wx.WHITE,
}
FORE_COLORS = {**COLORS, None: wx.WHITE}
BACK_COLORS = {**COLORS, None: wx.BLACK}
# See discussion at http://stackoverflow.com/q/41101897/131929
class RedirectText:
class RedirectText(TextIOBase):
def __init__(self, text_ctrl):
super().__init__()
self._out = text_ctrl
self._i = 0
self._line = ''
self._line = ""
self._bold = False
self._italic = False
self._underline = False
@ -60,7 +63,7 @@ class RedirectText:
self._add_content(self._line[pos:j])
pos = match.end()
for code in match.group(1).split(';'):
for code in match.group(1).split(";"):
code = int(code)
if code == 0:
self._bold = False
@ -86,62 +89,66 @@ class RedirectText:
elif code == 24:
self._underline = False
elif code == 30:
self._foreground = 'black'
self._foreground = "black"
elif code == 31:
self._foreground = 'red'
self._foreground = "red"
elif code == 32:
self._foreground = 'green'
self._foreground = "green"
elif code == 33:
self._foreground = 'yellow'
self._foreground = "yellow"
elif code == 34:
self._foreground = 'blue'
self._foreground = "blue"
elif code == 35:
self._foreground = 'magenta'
self._foreground = "magenta"
elif code == 36:
self._foreground = 'cyan'
self._foreground = "cyan"
elif code == 37:
self._foreground = 'white'
self._foreground = "white"
elif code == 39:
self._foreground = None
elif code == 40:
self._background = 'black'
self._background = "black"
elif code == 41:
self._background = 'red'
self._background = "red"
elif code == 42:
self._background = 'green'
self._background = "green"
elif code == 43:
self._background = 'yellow'
self._background = "yellow"
elif code == 44:
self._background = 'blue'
self._background = "blue"
elif code == 45:
self._background = 'magenta'
self._background = "magenta"
elif code == 46:
self._background = 'cyan'
self._background = "cyan"
elif code == 47:
self._background = 'white'
self._background = "white"
elif code == 49:
self._background = None
self._add_content(self._line[pos:])
def write(self, string):
# pylint: disable=invalid-name
for s in string:
if s == '\r':
if s == "\r":
current_value = self._out.GetValue()
last_newline = current_value.rfind("\n")
wx.CallAfter(self._out.Remove, last_newline + 1, len(current_value))
# self._line += '\n'
self._write_line()
self._line = ''
self._line = ""
continue
self._line += s
if s == '\n':
if s == "\n":
self._write_line()
self._line = ''
self._line = ""
continue
def flush(self):
pass
def writable(self):
return True
def isatty(self) -> bool:
return True
class FlashingThread(threading.Thread):
@ -157,19 +164,25 @@ class FlashingThread(threading.Thread):
try:
from esphomeflasher.__main__ import run_esphomeflasher
argv = ['esphomeflasher', '--port', self._port, self._firmware]
argv = ["esphomeflasher", "--port", self._port, self._firmware]
if self._show_logs:
argv.append('--show-logs')
argv.append("--show-logs")
run_esphomeflasher(argv)
except Exception as e:
print("Unexpected error: {}".format(e))
except Exception as err:
print(f"Unexpected error: {err}")
raise
class MainFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, size=(725, 650),
style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
wx.Frame.__init__(
self,
parent,
-1,
title,
size=(725, 650),
style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE,
)
self._firmware = None
self._port = None
@ -193,7 +206,7 @@ class MainFrame(wx.Frame):
def on_logs_clicked(event):
self.console_ctrl.SetValue("")
worker = FlashingThread(self, 'dummy', self._port, show_logs=True)
worker = FlashingThread(self, "dummy", self._port, show_logs=True)
worker.start()
def on_select_port(event):
@ -212,8 +225,12 @@ class MainFrame(wx.Frame):
self.choice = wx.Choice(panel, choices=self._get_serial_ports())
self.choice.Bind(wx.EVT_CHOICE, on_select_port)
bmp = Reload.GetBitmap()
reload_button = wx.BitmapButton(panel, id=wx.ID_ANY, bitmap=bmp,
size=(bmp.GetWidth() + 7, bmp.GetHeight() + 7))
reload_button = wx.BitmapButton(
panel,
id=wx.ID_ANY,
bitmap=bmp,
size=(bmp.GetWidth() + 7, bmp.GetHeight() + 7),
)
reload_button.Bind(wx.EVT_BUTTON, on_reload)
reload_button.SetToolTip("Reload serial device list")
@ -231,9 +248,17 @@ class MainFrame(wx.Frame):
logs_button = wx.Button(panel, -1, "View Logs")
logs_button.Bind(wx.EVT_BUTTON, on_logs_clicked)
self.console_ctrl = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL)
self.console_ctrl.SetFont(wx.Font((0, 13), wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_NORMAL))
self.console_ctrl = wx.TextCtrl(
panel, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL
)
self.console_ctrl.SetFont(
wx.Font(
(0, 13),
wx.FONTFAMILY_TELETYPE,
wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_NORMAL,
)
)
self.console_ctrl.SetBackgroundColour(wx.BLACK)
self.console_ctrl.SetForegroundColour(wx.WHITE)
self.console_ctrl.SetDefaultStyle(wx.TextAttr(wx.WHITE))
@ -243,18 +268,25 @@ class MainFrame(wx.Frame):
console_label = wx.StaticText(panel, label="Console")
fgs.AddMany([
# Port selection row
port_label, (serial_boxsizer, 1, wx.EXPAND),
# Firmware selection row (growable)
file_label, (file_picker, 1, wx.EXPAND),
# Flash ESP button
(wx.StaticText(panel, label="")), (button, 1, wx.EXPAND),
# View Logs button
(wx.StaticText(panel, label="")), (logs_button, 1, wx.EXPAND),
# Console View (growable)
(console_label, 1, wx.EXPAND), (self.console_ctrl, 1, wx.EXPAND),
])
fgs.AddMany(
[
# Port selection row
port_label,
(serial_boxsizer, 1, wx.EXPAND),
# Firmware selection row (growable)
file_label,
(file_picker, 1, wx.EXPAND),
# Flash ESP button
(wx.StaticText(panel, label="")),
(button, 1, wx.EXPAND),
# View Logs button
(wx.StaticText(panel, label="")),
(logs_button, 1, wx.EXPAND),
# Console View (growable)
(console_label, 1, wx.EXPAND),
(self.console_ctrl, 1, wx.EXPAND),
]
)
fgs.AddGrowableRow(4, 1)
fgs.AddGrowableCol(1, 1)
hbox.Add(fgs, proportion=2, flag=wx.ALL | wx.EXPAND, border=15)
@ -262,7 +294,7 @@ class MainFrame(wx.Frame):
def _get_serial_ports(self):
ports = []
for port, desc in list_serial_ports():
for port, _ in list_serial_ports():
ports.append(port)
if not self._port and ports:
self._port = ports[0]
@ -279,6 +311,7 @@ class MainFrame(wx.Frame):
class App(wx.App, wx.lib.mixins.inspection.InspectionMixin):
# pylint: disable=invalid-name
def OnInit(self):
wx.SystemOptions.SetOption("mac.window-plain-transition", 1)
self.SetAppName("esphome-flasher (Based on NodeMCU PyFlasher)")
@ -308,7 +341,8 @@ Exit = PyEmbeddedImage(
"3Rl+LVvOwG1syMBrYcbwfetmhmsOdgy/795iuMXEwnDh89c2oJ7jIL0AAQR2wQRgXvgKNAfo"
"qRIlJfk2NR42Rj5gEmb5+4/h35+/DJ+/fmd4DUyNN4B+v/DlWwcwcTWzA9PXQqBegACCGwAK"
"ERD+zsBgwszOXirEwe7OzvCP5y/QCx/+/v/26vfv/R///O0GOvkII1AdKxCDDAAIIEZKszNA"
"gAEA1sFjF+2KokIAAAAASUVORK5CYII=")
"gAEA1sFjF+2KokIAAAAASUVORK5CYII="
)
Reload = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBj"
@ -328,7 +362,8 @@ Reload = PyEmbeddedImage(
"wS9NKRdXQr6kgeuBfwEbWdzTvan9igAAADV0RVh0Y29tbWVudABSZWZyZXNoIGZyb20gSWNv"
"biBHYWxsZXJ5IGh0dHA6Ly9pY29uZ2FsLmNvbS/RLzdIAAAAJXRFWHRkYXRlOmNyZWF0ZQAy"
"MDExLTA4LTIxVDE0OjAxOjU2LTA2OjAwdNJAnQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxMS0w"
"OC0yMVQxNDowMTo1Ni0wNjowMAWP+CEAAAAASUVORK5CYII=")
"OC0yMVQxNDowMTo1Ni0wNjowMAWP+CEAAAAASUVORK5CYII="
)
def main():

View File

@ -5,17 +5,19 @@ import sys
import serial
DEVNULL = open(os.devnull, 'w')
# pylint: disable=unspecified-encoding,consider-using-with
DEVNULL = open(os.devnull, "w")
def list_serial_ports():
# from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
from serial.tools.list_ports import comports
result = []
for port, desc, info in comports():
if not port or "VID:PID" not in info:
continue
split_desc = desc.split(' - ')
split_desc = desc.split(" - ")
if len(split_desc) == 2 and split_desc[0] == split_desc[1]:
desc = split_desc[0]
result.append((port, desc))
@ -31,7 +33,6 @@ def prevent_print(func, *args, **kwargs):
except serial.SerialException as err:
from esphomeflasher.common import EsphomeflasherError
raise EsphomeflasherError("Serial port closed: {}".format(err))
raise EsphomeflasherError(f"Serial port closed: {err}") from err
finally:
sys.stdout = orig_sys_stdout
pass

Binary file not shown.

24
pyproject.toml Normal file
View File

@ -0,0 +1,24 @@
[tool.isort]
profile = "black"
multi_line_output = 3
[tool.black]
target-version = ['py37']
[tool.pylint.MASTER]
reports = 'no'
disable = [
"too-many-branches",
"missing-function-docstring",
"missing-module-docstring",
"too-many-statements",
"import-outside-toplevel",
"line-too-long",
"missing-class-docstring",
"too-few-public-methods",
"too-many-arguments",
"too-many-instance-attributes",
"too-many-locals",
"unused-argument",
"cyclic-import",
]

View File

@ -1,3 +1,3 @@
wxpython>=4.0,<5.0
esptool==2.8
requests>=2.24.0,<3
wxpython>=4.1.1,<5.0
esptool==3.2
requests>=2.26.0,<3

View File

@ -1,2 +1,2 @@
pyinstaller>=3.6,<4
pyinstaller>=4.5.1,<5
wheel

4
requirements_test.txt Normal file
View File

@ -0,0 +1,4 @@
pylint==2.13.4
black==22.3.0
flake8==4.0.1
isort==5.10.1

14
setup.cfg Normal file
View File

@ -0,0 +1,14 @@
[flake8]
max-line-length = 120
# Following 4 for black compatibility
# E501: line too long
# W503: Line break occurred before a binary operator
# E203: Whitespace before ':'
# D202 No blank lines allowed after function docstring
ignore =
E501,
W503,
E203,
D202,

View File

@ -10,7 +10,7 @@ PROJECT_NAME = 'esphomeflasher'
PROJECT_PACKAGE_NAME = 'esphomeflasher'
PROJECT_LICENSE = 'MIT'
PROJECT_AUTHOR = 'ESPHome'
PROJECT_COPYRIGHT = '2020, ESPHome'
PROJECT_COPYRIGHT = '2021, ESPHome'
PROJECT_URL = 'https://esphome.io/guides/faq.html'
PROJECT_EMAIL = 'contact@esphome.io'
@ -45,7 +45,7 @@ setup(
zip_safe=False,
platforms='any',
test_suite='tests',
python_requires='>=3.5,<4.0',
python_requires='>=3.7,<4.0',
install_requires=REQUIRES,
long_description=LONG_DESCRIPTION,
long_description_content_type='text/markdown',