Compare commits
37 Commits
Author | SHA1 | Date |
---|---|---|
Jesse Hills | df4b36d80a | |
dependabot[bot] | df73bb5585 | |
dependabot[bot] | 77c8d64110 | |
dependabot[bot] | f51fd45f11 | |
dependabot[bot] | aef82a0761 | |
dependabot[bot] | bd67d926f0 | |
dependabot[bot] | 18176f051a | |
dependabot[bot] | b4aaa8a5d7 | |
Otto Winter | 1c24e594e9 | |
dependabot[bot] | b1bfb387d4 | |
dependabot[bot] | a3e9099ed7 | |
dependabot[bot] | a591997e72 | |
dependabot[bot] | 09e119a610 | |
dependabot[bot] | adb44b7008 | |
dependabot[bot] | df83104724 | |
Otto winter | 93b19f8f9e | |
Otto winter | 528ef7655e | |
Otto winter | 1d3e589722 | |
Otto winter | 7a65462464 | |
Otto winter | 152e7491a7 | |
Otto Winter | e7ea643a36 | |
Otto Winter | b9dd803e1a | |
Otto Winter | 23a6bae0ed | |
dependabot[bot] | 9d57fbbff3 | |
dependabot[bot] | 2598775f01 | |
Otto Winter | 493717c725 | |
Otto Winter | 26de901da7 | |
Otto Winter | d732be0068 | |
Bas | 4221529467 | |
Otto Winter | cf1d2a2e2e | |
Otto Winter | 7b21f8ef45 | |
Otto Winter | c49ec75f06 | |
Otto Winter | b0aeedc573 | |
Alex | 2f7b469718 | |
Guillermo Ruffino | 294c6ad459 | |
gordon-zhao | 8fc5341336 | |
Fabian Affolter | 12ed27be9b |
|
@ -4,3 +4,7 @@ updates:
|
|||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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' }}
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "isort",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^ERROR:\\s+(.+)\\s+(.+)$",
|
||||
"file": 1,
|
||||
"message": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -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.
|
|
@ -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/
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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@:%_+.~#?&/=]*)"
|
||||
)
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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
partitions.bin
BIN
partitions.bin
Binary file not shown.
|
@ -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",
|
||||
]
|
|
@ -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
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
pyinstaller>=3.6,<4
|
||||
pyinstaller>=4.5.1,<5
|
||||
wheel
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
pylint==2.13.4
|
||||
black==22.3.0
|
||||
flake8==4.0.1
|
||||
isort==5.10.1
|
|
@ -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,
|
||||
|
4
setup.py
4
setup.py
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue