Compare commits

...

47 Commits
v1.1.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
Otto Winter 1d33132878
Bump version to v1.3.0 2020-07-30 13:28:12 +02:00
Otto Winter 1546b4aede
Merge branch 'master' of https://github.com/OttoWinter/esphomeflasher 2020-07-30 13:26:38 +02:00
Otto Winter 80facb3a07
Update build system (#24) 2020-07-30 13:26:27 +02:00
Otto Winter 64369944ae
Update copyright to 2020 2020-07-30 12:03:40 +02:00
Otto Winter 3e59270244
Add dependabot 2020-07-30 12:03:12 +02:00
cbialobos da530cd18b
Fix alignment error message (#21) 2020-07-13 23:19:47 +02:00
Otto Winter 0220e98b8d
Add logic to revert to 115200 upload rate if faster one fails (#16) 2020-07-13 23:19:36 +02:00
Otto Winter 127731e221
Bump version to 1.2.0 2019-10-24 16:10:54 +02:00
Otto Winter 342d6895a1
GitHub actions (#5)
* Add github actions

* Updates

* Try fix

* Try fix 2

* Try fix 3

* Windows x86, ubuntu fix

* Disable ubuntu

Builds keep failing, looks like a bug in github actions ubuntu image/azure

* Fix esptool 2.8

* Faster upload (4x)

* Change baud rate for logs
2019-10-24 16:10:10 +02:00
Otto Winter c3541a9ba0
Add build instructions 2019-06-09 16:14:52 +02:00
27 changed files with 913 additions and 195 deletions

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: "daily"

144
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,144 @@
name: Build Workflow
on:
push:
branches: [main]
pull_request:
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: actions/upload-artifact@v2
with:
name: Windows
path: dist/ESPHome-Flasher.exe
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: actions/upload-artifact@v2
with:
name: Windows-x86
path: dist/ESPHome-Flasher.exe
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: actions/upload-artifact@v2
with:
name: Ubuntu
path: dist/ESPHome-Flasher
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
- name: 'Upload Artifact'
uses: actions/upload-artifact@v2
with:
name: macOS
path: dist.tar
build-pypi:
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 sdist
run: python setup.py sdist bdist_wheel
- name: See dist directory
run: ls dist
- uses: actions/upload-artifact@v2
with:
name: sdist
path: dist/esphomeflasher-*.tar.gz
- uses: actions/upload-artifact@v2
with:
name: bdist_wheel
path: dist/esphomeflasher-*.whl

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.

12
.gitignore vendored
View File

@ -104,3 +104,15 @@ venv.bak/
.mypy_cache/
config/
.DS_Store
/.idea/
.vscode/*
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/

3
MANIFEST.in Normal file
View File

@ -0,0 +1,3 @@
include LICENSE
include README.md
include requirements.txt

View File

@ -1,15 +1,20 @@
# esphomeflasher
# Deprecated
esphomeflasher is a utility app for the [esphomelib](https://esphomelib.com/esphomeyaml/index.html)
framework and is designed to make flashing ESPs with esphomelib as simple as possible by:
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/)
framework and is designed to make flashing ESPs with ESPHome as simple as possible by:
* Having pre-built binaries for most operating systems.
* Hiding all non-essential options for flashing. All necessary options for flashing
(bootloader, flash mode) are automatically extracted from the binary.
This project was originally intended to be a simple command-line tool,
but then I decided that a GUI would be nice. As I don't like writing graphical
front end code, the GUI largely is based on the
front end code, the GUI largely is based on the
[NodeMCU PyFlasher](https://github.com/marcelstoer/nodemcu-pyflasher)
project.
@ -18,8 +23,8 @@ library by espressif.
## Installation
Es doesn't have to be installed, just double-click it and it'll start.
Check the [releases section](https://github.com/OttoWinter/esphomeflasher/releases)
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.
## Installation Using `pip`
@ -27,7 +32,7 @@ for downloads for your platform.
If you want to install this application from `pip`:
- Install Python 3.x
- Install [wxPython 4.x](https://wxpython.org/) manually or run `pip3 install wxpython`
- Install [wxPython 4.x](https://wxpython.org/) manually or run `pip3 install wxpython` (see also linux notes below)
- Install this project using `pip3 install esphomeflasher`
- Start the GUI using `esphomeflasher`. Alternatively, you can use the command line interface (
type `esphomeflasher -h` for info)
@ -42,6 +47,20 @@ If you want to build this application yourself you need to:
- Start the GUI using `esphomeflasher`. Alternatively, you can use the command line interface (
type `esphomeflasher -h` for info)
## Linux Notes
Installing wxpython for linux can be a bit challenging (especially when you don't want to install from source).
You can use the following command to install a wxpython suitable with your OS:
```bash
# Go to https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ and select the correct OS type
# here, we assume ubuntu 18.03 bionic
pip3 install -U \
-f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 \
wxPython
```
## License
[MIT](http://opensource.org/licenses/MIT) © Marcel Stör, Otto Winter

13
build-instructions.md Normal file
View File

@ -0,0 +1,13 @@
# macOS
`pyinstaller -F -w -n ESPHome-Flasher -i icon.icns esphomeflasher/__main__.py`
# Windows
1. Start up VM
2. Install Python (3) from App Store
3. Download esphome-flasher from GitHub
4. `pip install -e.` and `pip install pyinstaller`
5. Check with `python -m esphomeflasher.__main__`
6. `python -m PyInstaller.__main__ -F -w -n ESPHome-Flasher -i icon.ico esphomeflasher\__main__.py`
7. Go to `dist` folder, check ESPHome-Flasher.exe works.

View File

@ -1,48 +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')
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:
@ -50,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]
@ -66,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):
@ -86,55 +108,81 @@ 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
flash_size = detect_flash_size(stub_chip)
print(" - Flash Size: {}".format(flash_size))
if args.upload_baud_rate != 115200:
try:
stub_chip.change_baud(args.upload_baud_rate)
except esptool.FatalError as err:
raise EsphomeflasherError(
f"Error changing ESP upload baud rate: {err}"
) from err
mock_args = configure_write_flash_args(info, firmware, flash_size,
args.bootloader, args.partitions,
args.otadata)
# Check if the higher baud rate works
try:
flash_size = detect_flash_size(stub_chip)
except EsphomeflasherError:
# Go back to old baud rate by recreating chip instance
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)
print(" - Flash Mode: {}".format(mock_args.flash_mode))
print(" - Flash Frequency: {}Hz".format(mock_args.flash_freq.upper()))
if flash_size is None:
flash_size = detect_flash_size(stub_chip)
print(f" - Flash Size: {flash_size}")
mock_args = configure_write_flash_args(
info, firmware, flash_size, args.bootloader, args.partitions, args.otadata
)
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()
@ -142,6 +190,14 @@ def run_esphomeflasher(argv):
print("Done! Flashing is complete!")
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
@ -22,9 +22,11 @@ class MockEsptoolArgs(object):
self.no_stub = False
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
@ -33,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
@ -52,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
@ -79,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):
@ -120,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
@ -141,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)
@ -152,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)
@ -192,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.1.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.0/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")
@ -223,7 +240,7 @@ class MainFrame(wx.Frame):
serial_boxsizer = wx.BoxSizer(wx.HORIZONTAL)
serial_boxsizer.Add(self.choice, 1, wx.EXPAND)
serial_boxsizer.AddStretchSpacer(0)
serial_boxsizer.Add(reload_button, 0, wx.ALIGN_RIGHT, 20)
serial_boxsizer.Add(reload_button, 0, wx.ALIGN_NOT, 20)
button = wx.Button(panel, -1, "Flash ESP")
button.Bind(wx.EVT_BUTTON, on_clicked)
@ -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

BIN
icon.icns Normal file

Binary file not shown.

BIN
icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

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",
]

3
requirements.txt Normal file
View File

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

2
requirements_build.txt Normal file
View File

@ -0,0 +1,2 @@
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

@ -1,5 +1,7 @@
#!/usr/bin/env python
"""esphomeflasher setup script."""
import os
from setuptools import setup, find_packages
from esphomeflasher import const
@ -8,12 +10,12 @@ PROJECT_NAME = 'esphomeflasher'
PROJECT_PACKAGE_NAME = 'esphomeflasher'
PROJECT_LICENSE = 'MIT'
PROJECT_AUTHOR = 'ESPHome'
PROJECT_COPYRIGHT = '2019, ESPHome'
PROJECT_COPYRIGHT = '2021, ESPHome'
PROJECT_URL = 'https://esphome.io/guides/faq.html'
PROJECT_EMAIL = 'contact@esphome.io'
PROJECT_GITHUB_USERNAME = 'esphome'
PROJECT_GITHUB_REPOSITORY = 'esphomeflasher'
PROJECT_GITHUB_REPOSITORY = 'esphome-flasher'
PYPI_URL = 'https://pypi.python.org/pypi/{}'.format(PROJECT_PACKAGE_NAME)
GITHUB_PATH = '{}/{}'.format(PROJECT_GITHUB_USERNAME, PROJECT_GITHUB_REPOSITORY)
@ -21,11 +23,14 @@ GITHUB_URL = 'https://github.com/{}'.format(GITHUB_PATH)
DOWNLOAD_URL = '{}/archive/{}.zip'.format(GITHUB_URL, const.__version__)
REQUIRES = [
'wxpython>=4.0,<5.0',
'esptool==2.6',
'requests>=2.0,<3',
]
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'requirements.txt')) as requirements_txt:
REQUIRES = requirements_txt.read().splitlines()
with open(os.path.join(here, 'README.md')) as readme:
LONG_DESCRIPTION = readme.read()
setup(
name=PROJECT_PACKAGE_NAME,
@ -35,18 +40,20 @@ setup(
download_url=DOWNLOAD_URL,
author=PROJECT_AUTHOR,
author_email=PROJECT_EMAIL,
description="ESP8266/ESP32 firmware flasher for esphomelib",
description="ESP8266/ESP32 firmware flasher for ESPHome",
include_package_data=True,
zip_safe=False,
platforms='any',
test_suite='tests',
python_requires='>=3.5',
python_requires='>=3.7,<4.0',
install_requires=REQUIRES,
long_description=LONG_DESCRIPTION,
long_description_content_type='text/markdown',
keywords=['home', 'automation'],
entry_points={
'console_scripts': [
'esphomeflasher = esphomeflasher.__main__:main'
]
},
packages=find_packages()
packages=find_packages(include="esphomerelease.*")
)