144
.github/workflows/ci.yaml
vendored
|
|
@ -2,150 +2,44 @@ name: ESPSomfy-RTS
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
|
||||||
ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json"
|
|
||||||
ARDUINO_CLI_VERSION: "0.x"
|
|
||||||
ARDUINO_ESP32_VERSION: "2.0.10"
|
|
||||||
ARDUINO_JSON_VERSION: "6.21.3"
|
|
||||||
ESPTOOL_VERSION: "4.6"
|
|
||||||
LITTLEFS_VERSION: "v2.5.1"
|
|
||||||
MKLITTLEFS_VERSION: "3.1.0"
|
|
||||||
PUB_SUB_CLIENT_VERSION: "2.8.0"
|
|
||||||
PYTHON_VERSION: "3.10"
|
|
||||||
SMARTRC_CC1101_VERSION: "2.5.7"
|
|
||||||
WEB_SOCKET_VERSION: "2.4.0"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
littlefs:
|
build:
|
||||||
name: LittleFS
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Checkout mklittlefs
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: earlephilhower/mklittlefs
|
|
||||||
path: mklittlefs
|
|
||||||
ref: ${{ env.MKLITTLEFS_VERSION }}
|
|
||||||
|
|
||||||
- name: Checkout LittleFS
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: littlefs-project/littlefs
|
|
||||||
path: mklittlefs/littlefs
|
|
||||||
ref: ${{ env.LITTLEFS_VERSION }}
|
|
||||||
|
|
||||||
- name: Build mklittlefs
|
|
||||||
run: |
|
|
||||||
make -C mklittlefs
|
|
||||||
|
|
||||||
- name: Create LittleFS
|
|
||||||
run: |
|
|
||||||
./mklittlefs/mklittlefs --create data --size 1441792 SomfyController.littlefs.bin
|
|
||||||
|
|
||||||
- name: Upload binaries
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: LittleFS
|
|
||||||
path: SomfyController.littlefs.bin
|
|
||||||
retention-days: 5
|
|
||||||
|
|
||||||
arduino:
|
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
needs: [littlefs]
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- board: esp32
|
- env: esp32dev
|
||||||
addr_bootloader: 0x1000
|
|
||||||
chip: ESP32
|
|
||||||
fqbn: esp32:esp32:esp32
|
|
||||||
name: ESP32
|
name: ESP32
|
||||||
- board: lolin_c3_mini
|
|
||||||
addr_bootloader: 0x0
|
|
||||||
chip: ESP32-C3
|
|
||||||
fqbn: esp32:esp32:lolin_c3_mini
|
|
||||||
name: LOLIN-C3-mini
|
|
||||||
- board: lolin_s2_mini
|
|
||||||
addr_bootloader: 0x1000
|
|
||||||
chip: ESP32-S2
|
|
||||||
fqbn: esp32:esp32:lolin_s2_mini
|
|
||||||
name: LOLIN-S2-mini
|
|
||||||
- board: lolin_s3_mini
|
|
||||||
addr_bootloader: 0x0
|
|
||||||
chip: ESP32-S3
|
|
||||||
fqbn: esp32:esp32:lolin_s3_mini
|
|
||||||
name: LOLIN-S3-mini
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
path: SomfyController
|
python-version: "3.12"
|
||||||
|
|
||||||
- name: Get LittleFS
|
- name: Install PlatformIO
|
||||||
uses: actions/download-artifact@v3
|
run: pip install platformio
|
||||||
with:
|
|
||||||
name: LittleFS
|
|
||||||
|
|
||||||
- name: Install Python ${{ env.PYTHON_VERSION }}
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
|
||||||
|
|
||||||
- name: Upgrade pip
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip --version
|
|
||||||
|
|
||||||
- name: Install ESPTool
|
|
||||||
run: |
|
|
||||||
pip install esptool==${{ env.ESPTOOL_VERSION }}
|
|
||||||
|
|
||||||
- name: Install Arduino CLI
|
|
||||||
uses: arduino/setup-arduino-cli@v1
|
|
||||||
with:
|
|
||||||
version: ${{ env.ARDUINO_CLI_VERSION }}
|
|
||||||
|
|
||||||
- name: Configure Arduino CLI
|
|
||||||
run: |
|
|
||||||
arduino-cli core update-index
|
|
||||||
arduino-cli core install esp32:esp32@${{ env.ARDUINO_ESP32_VERSION }}
|
|
||||||
|
|
||||||
- name: Configure Arduino Libraries
|
|
||||||
run: |
|
|
||||||
arduino-cli lib install ArduinoJson@${{ env.ARDUINO_JSON_VERSION }}
|
|
||||||
arduino-cli lib install PubSubClient@${{ env.PUB_SUB_CLIENT_VERSION }}
|
|
||||||
arduino-cli lib install SmartRC-CC1101-Driver-Lib@${{ env.SMARTRC_CC1101_VERSION }}
|
|
||||||
arduino-cli lib install WebSockets@${{ env.WEB_SOCKET_VERSION }}
|
|
||||||
|
|
||||||
- name: Build ${{ matrix.name }}
|
- name: Build ${{ matrix.name }}
|
||||||
run: |
|
run: pio run -e ${{ matrix.env }}
|
||||||
mkdir -p build
|
|
||||||
arduino-cli compile --clean --output-dir build --fqbn ${{ matrix.fqbn }} --warnings default ./SomfyController
|
|
||||||
|
|
||||||
- name: ${{ matrix.name }} Image
|
- name: Build LittleFS image
|
||||||
run: |
|
run: pio run -e ${{ matrix.env }} -t buildfs
|
||||||
python -m esptool --chip ${{ matrix.chip }} \
|
|
||||||
merge_bin -o build/SomfyController.onboard.bin \
|
|
||||||
${{ matrix.addr_bootloader }} build/SomfyController.ino.bootloader.bin \
|
|
||||||
0x8000 build/SomfyController.ino.partitions.bin \
|
|
||||||
0x10000 build/SomfyController.ino.bin \
|
|
||||||
0x290000 SomfyController.littlefs.bin
|
|
||||||
|
|
||||||
- name: Upload ${{ matrix.name }}
|
- name: Upload firmware
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
build/SomfyController.ino.bin
|
.pio/build/${{ matrix.env }}/firmware.bin
|
||||||
build/SomfyController.ino.bootloader.bin
|
.pio/build/${{ matrix.env }}/firmware.elf
|
||||||
build/SomfyController.ino.partitions.bin
|
.pio/build/${{ matrix.env }}/partitions.bin
|
||||||
build/SomfyController.onboard.bin
|
.pio/build/${{ matrix.env }}/bootloader.bin
|
||||||
|
.pio/build/${{ matrix.env }}/littlefs.bin
|
||||||
retention-days: 5
|
retention-days: 5
|
||||||
|
|
|
||||||
194
.github/workflows/release.yaml
vendored
|
|
@ -1,27 +1,26 @@
|
||||||
name: ESPSomfy-RTS Release
|
name: ESPSomfy-RTS Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
env:
|
|
||||||
ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json"
|
|
||||||
ARDUINO_CLI_VERSION: "0.x"
|
|
||||||
ARDUINO_ESP32_VERSION: "2.0.17"
|
|
||||||
ARDUINO_JSON_VERSION: "6.21.5"
|
|
||||||
ESPTOOL_VERSION: "4.7"
|
|
||||||
LITTLEFS_VERSION: "v2.5.1"
|
|
||||||
MKLITTLEFS_VERSION: "3.1.0"
|
|
||||||
PUB_SUB_CLIENT_VERSION: "2.8.0"
|
|
||||||
PYTHON_VERSION: "3.10"
|
|
||||||
SMARTRC_CC1101_VERSION: "2.5.7"
|
|
||||||
WEB_SOCKET_VERSION: "2.4.0"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
littlefs:
|
build:
|
||||||
name: LittleFS
|
permissions: write-all
|
||||||
|
name: ${{ matrix.name }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- env: esp32dev
|
||||||
|
name: ESP32
|
||||||
|
chip: ESP32
|
||||||
|
addr_bootloader: "0x1000"
|
||||||
|
fwname: SomfyController.esp32.bin
|
||||||
|
obname: SomfyController.onboard.esp32.bin
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Get Release
|
- name: Get Release
|
||||||
id: get_release
|
id: get_release
|
||||||
|
|
@ -32,34 +31,31 @@ jobs:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Checkout mklittlefs
|
- name: Set up Python
|
||||||
uses: actions/checkout@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
repository: earlephilhower/mklittlefs
|
python-version: "3.12"
|
||||||
path: mklittlefs
|
|
||||||
ref: ${{ env.MKLITTLEFS_VERSION }}
|
|
||||||
|
|
||||||
- name: Checkout LittleFS
|
- name: Install PlatformIO
|
||||||
uses: actions/checkout@v4
|
run: pip install platformio
|
||||||
with:
|
|
||||||
repository: littlefs-project/littlefs
|
|
||||||
path: mklittlefs/littlefs
|
|
||||||
ref: ${{ env.LITTLEFS_VERSION }}
|
|
||||||
|
|
||||||
- name: Build mklittlefs
|
- name: Build firmware
|
||||||
|
run: pio run -e ${{ matrix.env }}
|
||||||
|
|
||||||
|
- name: Build LittleFS image
|
||||||
|
run: pio run -e ${{ matrix.env }} -t buildfs
|
||||||
|
|
||||||
|
- name: Create onboard image
|
||||||
run: |
|
run: |
|
||||||
make -C mklittlefs
|
python -m esptool --chip ${{ matrix.chip }} \
|
||||||
|
merge_bin -o ${{ matrix.obname }} \
|
||||||
|
${{ matrix.addr_bootloader }} .pio/build/${{ matrix.env }}/bootloader.bin \
|
||||||
|
0x8000 .pio/build/${{ matrix.env }}/partitions.bin \
|
||||||
|
0x10000 .pio/build/${{ matrix.env }}/firmware.bin \
|
||||||
|
0x310000 .pio/build/${{ matrix.env }}/littlefs.bin
|
||||||
|
|
||||||
- name: Create LittleFS
|
- name: Compress onboard image
|
||||||
run: |
|
run: zip ${{ matrix.obname }}.zip ${{ matrix.obname }}
|
||||||
./mklittlefs/mklittlefs --create data --size 1441792 SomfyController.littlefs.bin
|
|
||||||
|
|
||||||
- name: Upload binaries
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: LittleFS
|
|
||||||
path: SomfyController.littlefs.bin
|
|
||||||
retention-days: 5
|
|
||||||
|
|
||||||
- name: Upload LittleFS
|
- name: Upload LittleFS
|
||||||
uses: shogo82148/actions-upload-release-asset@v1.7.5
|
uses: shogo82148/actions-upload-release-asset@v1.7.5
|
||||||
|
|
@ -67,129 +63,20 @@ jobs:
|
||||||
github_token: ${{ github.token }}
|
github_token: ${{ github.token }}
|
||||||
upload_url: ${{ steps.get_release.outputs.upload_url }}
|
upload_url: ${{ steps.get_release.outputs.upload_url }}
|
||||||
asset_name: SomfyController.littlefs.bin
|
asset_name: SomfyController.littlefs.bin
|
||||||
asset_path: SomfyController.littlefs.bin
|
asset_path: .pio/build/${{ matrix.env }}/littlefs.bin
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
arduino:
|
- name: Upload firmware
|
||||||
permissions: write-all
|
|
||||||
name: ${{ matrix.name }}
|
|
||||||
needs: [littlefs]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- board: esp32
|
|
||||||
addr_bootloader: 0x1000
|
|
||||||
chip: ESP32
|
|
||||||
fqbn: esp32:esp32:esp32wrover:PartitionScheme=default,FlashMode=qio,FlashFreq=80,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
|
|
||||||
# esp32:esp32:esp32wrover:PartitionScheme=default,FlashMode=qio,FlashFreq=80,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
|
|
||||||
name: ESP32
|
|
||||||
obname: SomfyController.onboard.esp32.bin
|
|
||||||
fwname: SomfyController.ino.esp32.bin
|
|
||||||
- board: esp32c3
|
|
||||||
addr_bootloader: 0x0
|
|
||||||
chip: ESP32-C3
|
|
||||||
fqbn: esp32:esp32:esp32c3:JTAGAdapter=default,CDCOnBoot=cdc,PartitionScheme=default,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
|
|
||||||
# esp32:esp32:esp32c3:JTAGAdapter=default,CDCOnBoot=default,PartitionScheme=default,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
|
|
||||||
name: ESP32C3
|
|
||||||
obname: SomfyController.onboard.esp32c3.bin
|
|
||||||
fwname: SomfyController.ino.esp32c3.bin
|
|
||||||
- board: esp32s2
|
|
||||||
addr_bootloader: 0x1000
|
|
||||||
chip: ESP32-S2
|
|
||||||
fqbn: esp32:esp32:esp32s2:JTAGAdapter=default,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
|
|
||||||
# esp32:esp32:esp32s2:JTAGAdapter=default,CDCOnBoot=default,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
|
|
||||||
name: ESP32S2
|
|
||||||
obname: SomfyController.onboard.esp32s2.bin
|
|
||||||
fwname: SomfyController.ino.esp32s2.bin
|
|
||||||
- board: esp32s3
|
|
||||||
addr_bootloader: 0x0
|
|
||||||
chip: ESP32-S3
|
|
||||||
fqbn: esp32:esp32:esp32s3:JTAGAdapter=default,PSRAM=disabled,FlashMode=qio,FlashSize=4M,LoopCore=1,EventsCore=1,USBMode=hwcdc,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PartitionScheme=default,CPUFreq=240,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
|
|
||||||
# esp32:esp32:esp32s3:JTAGAdapter=default,PSRAM=disabled,FlashMode=qio,FlashSize=4M,LoopCore=1,EventsCore=1,USBMode=hwcdc,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,PartitionScheme=default,CPUFreq=240,UploadSpeed=921600,DebugLevel=none,EraseFlash=none
|
|
||||||
name: ESP32S3
|
|
||||||
fwname: SomfyController.ino.esp32s3.bin
|
|
||||||
obname: SomfyController.onboard.esp32s3.bin
|
|
||||||
steps:
|
|
||||||
- name: Get Release
|
|
||||||
id: get_release
|
|
||||||
uses: bruceadams/get-release@v1.3.2
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
|
||||||
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: SomfyController
|
|
||||||
|
|
||||||
- name: Get LittleFS
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: LittleFS
|
|
||||||
|
|
||||||
- name: Install Python ${{ env.PYTHON_VERSION }}
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
|
||||||
|
|
||||||
- name: Upgrade pip
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip --version
|
|
||||||
|
|
||||||
- name: Install ESPTool
|
|
||||||
run: |
|
|
||||||
pip install esptool==${{ env.ESPTOOL_VERSION }}
|
|
||||||
|
|
||||||
- name: Install Arduino CLI
|
|
||||||
uses: arduino/setup-arduino-cli@v1
|
|
||||||
with:
|
|
||||||
version: ${{ env.ARDUINO_CLI_VERSION }}
|
|
||||||
|
|
||||||
- name: Configure Arduino CLI
|
|
||||||
run: |
|
|
||||||
arduino-cli core update-index
|
|
||||||
arduino-cli core install esp32:esp32@${{ env.ARDUINO_ESP32_VERSION }}
|
|
||||||
|
|
||||||
- name: Configure Arduino Libraries
|
|
||||||
run: |
|
|
||||||
arduino-cli lib install ArduinoJson@${{ env.ARDUINO_JSON_VERSION }}
|
|
||||||
arduino-cli lib install PubSubClient@${{ env.PUB_SUB_CLIENT_VERSION }}
|
|
||||||
arduino-cli lib install SmartRC-CC1101-Driver-Lib@${{ env.SMARTRC_CC1101_VERSION }}
|
|
||||||
arduino-cli lib install WebSockets@${{ env.WEB_SOCKET_VERSION }}
|
|
||||||
|
|
||||||
- name: Build ${{ matrix.name }}
|
|
||||||
run: |
|
|
||||||
mkdir -p build${{ matrix.name }}
|
|
||||||
arduino-cli compile --clean --output-dir build${{ matrix.name }} --fqbn ${{ matrix.fqbn }} --warnings none ./SomfyController
|
|
||||||
|
|
||||||
- name: ${{ matrix.name }} Image
|
|
||||||
run: |
|
|
||||||
python -m esptool --chip ${{ matrix.chip }} \
|
|
||||||
merge_bin -o ${{ matrix.obname }} \
|
|
||||||
${{ matrix.addr_bootloader }} build${{ matrix.name }}/SomfyController.ino.bootloader.bin \
|
|
||||||
0x8000 build${{ matrix.name }}/SomfyController.ino.partitions.bin \
|
|
||||||
0x10000 build${{ matrix.name }}/SomfyController.ino.bin \
|
|
||||||
0x290000 SomfyController.littlefs.bin
|
|
||||||
|
|
||||||
- name: Upload Firmware ${{ matrix.name }}
|
|
||||||
uses: shogo82148/actions-upload-release-asset@v1.7.5
|
uses: shogo82148/actions-upload-release-asset@v1.7.5
|
||||||
with:
|
with:
|
||||||
github_token: ${{ github.token }}
|
github_token: ${{ github.token }}
|
||||||
upload_url: ${{ steps.get_release.outputs.upload_url }}
|
upload_url: ${{ steps.get_release.outputs.upload_url }}
|
||||||
asset_name: ${{ matrix.fwname }}
|
asset_name: ${{ matrix.fwname }}
|
||||||
asset_path: build${{ matrix.name }}/SomfyController.ino.bin
|
asset_path: .pio/build/${{ matrix.env }}/firmware.bin
|
||||||
|
overwrite: true
|
||||||
|
|
||||||
- name: ${{ matrix.name }} Compress Onboard Image
|
- name: Upload onboard image
|
||||||
run: |
|
|
||||||
zip ${{ matrix.obname }}.zip ./${{ matrix.obname }}
|
|
||||||
|
|
||||||
- name: Upload Onboard ${{ matrix.name }}
|
|
||||||
uses: shogo82148/actions-upload-release-asset@v1.7.5
|
uses: shogo82148/actions-upload-release-asset@v1.7.5
|
||||||
# env:
|
|
||||||
# GITHUB_TOKEN: ${{ github.token }}
|
|
||||||
with:
|
with:
|
||||||
github_token: ${{ github.token }}
|
github_token: ${{ github.token }}
|
||||||
upload_url: ${{ steps.get_release.outputs.upload_url }}
|
upload_url: ${{ steps.get_release.outputs.upload_url }}
|
||||||
|
|
@ -197,4 +84,3 @@ jobs:
|
||||||
asset_path: ${{ matrix.obname }}.zip
|
asset_path: ${{ matrix.obname }}.zip
|
||||||
overwrite: true
|
overwrite: true
|
||||||
asset_content_type: application/zip
|
asset_content_type: application/zip
|
||||||
|
|
||||||
|
|
|
||||||
9
.gitignore
vendored
|
|
@ -7,4 +7,11 @@ debug.cfg
|
||||||
SomfyController.ino.XIAO_ESP32S3.bin
|
SomfyController.ino.XIAO_ESP32S3.bin
|
||||||
SomfyController.ino.esp32c3.bin
|
SomfyController.ino.esp32c3.bin
|
||||||
SomfyController.ino.esp32s2.bin
|
SomfyController.ino.esp32s2.bin
|
||||||
.vscode/settings.json
|
.vscode/
|
||||||
|
.pio/
|
||||||
|
.claude/
|
||||||
|
data/
|
||||||
|
build/
|
||||||
|
coredump_report.txt
|
||||||
|
coredump.bin
|
||||||
|
logs/
|
||||||
|
|
@ -82,3 +82,9 @@ Configuration of the Transceiver is done with the ELECHOUSE_CC1101 library which
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pio pkg exec -p tool-esptoolpy -- esptool.py --port COM9 read_flash 0x3F0000 0x10000 coredump.bin
|
||||||
|
|
||||||
|
|
||||||
|
C:\Users\oem\.platformio\packages\framework-espidf\export.ps1
|
||||||
|
esp-coredump info_corefile --core coredump.bin --core-format=raw --gdb C:\Users\oem\.platformio\packages\toolchain-xtensa-esp32\bin\xtensa-esp32-elf-gdb.exe .pio\build\esp32dev\firmware.elf > coredump_report.txt
|
||||||
|
|
||||||
|
|
|
||||||
203
Sockets.cpp
|
|
@ -1,203 +0,0 @@
|
||||||
#include <Arduino.h>
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <WebSocketsServer.h>
|
|
||||||
#include <esp_task_wdt.h>
|
|
||||||
#include "Sockets.h"
|
|
||||||
#include "ConfigSettings.h"
|
|
||||||
#include "Somfy.h"
|
|
||||||
#include "Network.h"
|
|
||||||
#include "GitOTA.h"
|
|
||||||
|
|
||||||
extern ConfigSettings settings;
|
|
||||||
extern Network net;
|
|
||||||
extern SomfyShadeController somfy;
|
|
||||||
extern SocketEmitter sockEmit;
|
|
||||||
extern GitUpdater git;
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketsServer sockServer = WebSocketsServer(8080);
|
|
||||||
|
|
||||||
#define MAX_SOCK_RESPONSE 2048
|
|
||||||
static char g_response[MAX_SOCK_RESPONSE];
|
|
||||||
|
|
||||||
bool room_t::isJoined(uint8_t num) {
|
|
||||||
for(uint8_t i = 0; i < sizeof(this->clients); i++) {
|
|
||||||
if(this->clients[i] == num) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool room_t::join(uint8_t num) {
|
|
||||||
if(this->isJoined(num)) return true;
|
|
||||||
for(uint8_t i = 0; i < sizeof(this->clients); i++) {
|
|
||||||
if(this->clients[i] == 255) {
|
|
||||||
this->clients[i] = num;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool room_t::leave(uint8_t num) {
|
|
||||||
if(!this->isJoined(num)) return false;
|
|
||||||
for(uint8_t i = 0; i < sizeof(this->clients); i++) {
|
|
||||||
if(this->clients[i] == num) this->clients[i] = 255;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
void room_t::clear() {
|
|
||||||
memset(this->clients, 255, sizeof(this->clients));
|
|
||||||
}
|
|
||||||
uint8_t room_t::activeClients() {
|
|
||||||
uint8_t n = 0;
|
|
||||||
for(uint8_t i = 0; i < sizeof(this->clients); i++) {
|
|
||||||
if(this->clients[i] != 255) n++;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
/*********************************************************************
|
|
||||||
* ClientSocketEvent class members
|
|
||||||
********************************************************************/
|
|
||||||
/*
|
|
||||||
void ClientSocketEvent::prepareMessage(const char *evt, const char *payload) {
|
|
||||||
if(strlen(payload) + 5 >= sizeof(this->msg)) Serial.printf("Socket buffer overflow %d > 2048\n", strlen(payload) + 5 + strlen(evt));
|
|
||||||
snprintf(this->msg, sizeof(this->msg), "42[%s,%s]", evt, payload);
|
|
||||||
}
|
|
||||||
void ClientSocketEvent::prepareMessage(const char *evt, JsonDocument &doc) {
|
|
||||||
memset(this->msg, 0x00, sizeof(this->msg));
|
|
||||||
snprintf(this->msg, sizeof(this->msg), "42[%s,", evt);
|
|
||||||
serializeJson(doc, &this->msg[strlen(this->msg)], sizeof(this->msg) - strlen(this->msg) - 2);
|
|
||||||
strcat(this->msg, "]");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
* SocketEmitter class members
|
|
||||||
********************************************************************/
|
|
||||||
void SocketEmitter::startup() {
|
|
||||||
|
|
||||||
}
|
|
||||||
void SocketEmitter::begin() {
|
|
||||||
sockServer.begin();
|
|
||||||
sockServer.enableHeartbeat(20000, 10000, 3);
|
|
||||||
sockServer.onEvent(this->wsEvent);
|
|
||||||
Serial.println("Socket Server Started...");
|
|
||||||
//settings.printAvailHeap();
|
|
||||||
}
|
|
||||||
void SocketEmitter::loop() {
|
|
||||||
this->initClients();
|
|
||||||
sockServer.loop();
|
|
||||||
}
|
|
||||||
JsonSockEvent *SocketEmitter::beginEmit(const char *evt) {
|
|
||||||
this->json.beginEvent(&sockServer, evt, g_response, sizeof(g_response));
|
|
||||||
return &this->json;
|
|
||||||
}
|
|
||||||
void SocketEmitter::endEmit(uint8_t num) { this->json.endEvent(num); sockServer.loop(); }
|
|
||||||
void SocketEmitter::endEmitRoom(uint8_t room) {
|
|
||||||
if(room < SOCK_MAX_ROOMS) {
|
|
||||||
room_t *r = &this->rooms[room];
|
|
||||||
for(uint8_t i = 0; i < sizeof(r->clients); i++) {
|
|
||||||
if(r->clients[i] != 255) this->json.endEvent(r->clients[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint8_t SocketEmitter::activeClients(uint8_t room) {
|
|
||||||
if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
void SocketEmitter::initClients() {
|
|
||||||
for(uint8_t i = 0; i < sizeof(this->newClients); i++) {
|
|
||||||
uint8_t num = this->newClients[i];
|
|
||||||
if(num != 255) {
|
|
||||||
if(sockServer.clientIsConnected(num)) {
|
|
||||||
Serial.printf("Initializing Socket Client %u\n", num);
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
settings.emitSockets(num);
|
|
||||||
somfy.emitState(num);
|
|
||||||
git.emitUpdateCheck(num);
|
|
||||||
net.emitSockets(num);
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
}
|
|
||||||
this->newClients[i] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void SocketEmitter::delayInit(uint8_t num) {
|
|
||||||
for(uint8_t i=0; i < sizeof(this->newClients); i++) {
|
|
||||||
if(this->newClients[i] == num) break;
|
|
||||||
else if(this->newClients[i] == 255) {
|
|
||||||
this->newClients[i] = num;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void SocketEmitter::end() {
|
|
||||||
sockServer.close();
|
|
||||||
for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++)
|
|
||||||
this->rooms[i].clear();
|
|
||||||
}
|
|
||||||
void SocketEmitter::disconnect() { sockServer.disconnect(); }
|
|
||||||
void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
|
|
||||||
switch(type) {
|
|
||||||
case WStype_ERROR:
|
|
||||||
if(length > 0)
|
|
||||||
Serial.printf("Socket Error: %s\n", payload);
|
|
||||||
else
|
|
||||||
Serial.println("Socket Error: \n");
|
|
||||||
break;
|
|
||||||
case WStype_DISCONNECTED:
|
|
||||||
if(length > 0)
|
|
||||||
Serial.printf("Socket [%u] Disconnected!\n [%s]", num, payload);
|
|
||||||
else
|
|
||||||
Serial.printf("Socket [%u] Disconnected!\n", num);
|
|
||||||
for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++) {
|
|
||||||
sockEmit.rooms[i].leave(num);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WStype_CONNECTED:
|
|
||||||
{
|
|
||||||
IPAddress ip = sockServer.remoteIP(num);
|
|
||||||
Serial.printf("Socket [%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
|
|
||||||
// Send all the current shade settings to the client.
|
|
||||||
sockServer.sendTXT(num, "Connected");
|
|
||||||
//sockServer.loop();
|
|
||||||
sockEmit.delayInit(num);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WStype_TEXT:
|
|
||||||
if(strncmp((char *)payload, "join:", 5) == 0) {
|
|
||||||
// In this instance the client wants to join a room. Let's do some
|
|
||||||
// work to get the ordinal of the room that the client wants to join.
|
|
||||||
uint8_t roomNum = atoi((char *)&payload[5]);
|
|
||||||
Serial.printf("Client %u joining room %u\n", num, roomNum);
|
|
||||||
if(roomNum < SOCK_MAX_ROOMS) sockEmit.rooms[roomNum].join(num);
|
|
||||||
}
|
|
||||||
else if(strncmp((char *)payload, "leave:", 6) == 0) {
|
|
||||||
uint8_t roomNum = atoi((char *)&payload[6]);
|
|
||||||
Serial.printf("Client %u leaving room %u\n", num, roomNum);
|
|
||||||
if(roomNum < SOCK_MAX_ROOMS) sockEmit.rooms[roomNum].leave(num);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Serial.printf("Socket [%u] text: %s\n", num, payload);
|
|
||||||
}
|
|
||||||
// send message to client
|
|
||||||
// webSocket.sendTXT(num, "message here");
|
|
||||||
|
|
||||||
// send data to all connected clients
|
|
||||||
// sockServer.broadcastTXT("message here");
|
|
||||||
break;
|
|
||||||
case WStype_BIN:
|
|
||||||
Serial.printf("[%u] get binary length: %u\n", num, length);
|
|
||||||
//hexdump(payload, length);
|
|
||||||
|
|
||||||
// send message to client
|
|
||||||
// sockServer.sendBIN(num, payload, length);
|
|
||||||
break;
|
|
||||||
case WStype_PONG:
|
|
||||||
//Serial.printf("Pong from %u\n", num);
|
|
||||||
break;
|
|
||||||
case WStype_PING:
|
|
||||||
//Serial.printf("Ping from %u\n", num);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
50
Web.h
|
|
@ -1,50 +0,0 @@
|
||||||
#include <WebServer.h>
|
|
||||||
#include "Somfy.h"
|
|
||||||
#ifndef webserver_h
|
|
||||||
#define webserver_h
|
|
||||||
class Web {
|
|
||||||
public:
|
|
||||||
bool uploadSuccess = false;
|
|
||||||
void sendCORSHeaders(WebServer &server);
|
|
||||||
void sendCacheHeaders(uint32_t seconds=604800);
|
|
||||||
void startup();
|
|
||||||
void handleLogin(WebServer &server);
|
|
||||||
void handleLogout(WebServer &server);
|
|
||||||
void handleStreamFile(WebServer &server, const char *filename, const char *encoding);
|
|
||||||
void handleController(WebServer &server);
|
|
||||||
void handleLoginContext(WebServer &server);
|
|
||||||
void handleGetRepeaters(WebServer &server);
|
|
||||||
void handleGetRooms(WebServer &server);
|
|
||||||
void handleGetShades(WebServer &server);
|
|
||||||
void handleGetGroups(WebServer &server);
|
|
||||||
void handleShadeCommand(WebServer &server);
|
|
||||||
void handleRepeatCommand(WebServer &server);
|
|
||||||
void handleGroupCommand(WebServer &server);
|
|
||||||
void handleTiltCommand(WebServer &server);
|
|
||||||
void handleDiscovery(WebServer &server);
|
|
||||||
void handleNotFound(WebServer &server);
|
|
||||||
void handleRoom(WebServer &server);
|
|
||||||
void handleShade(WebServer &server);
|
|
||||||
void handleGroup(WebServer &server);
|
|
||||||
void handleSetPositions(WebServer &server);
|
|
||||||
void handleSetSensor(WebServer &server);
|
|
||||||
void handleDownloadFirmware(WebServer &server);
|
|
||||||
void handleBackup(WebServer &server, bool attach = false);
|
|
||||||
void handleReboot(WebServer &server);
|
|
||||||
void handleDeserializationError(WebServer &server, DeserializationError &err);
|
|
||||||
void begin();
|
|
||||||
void loop();
|
|
||||||
void end();
|
|
||||||
// Web Handlers
|
|
||||||
bool createAPIToken(const IPAddress ipAddress, char *token);
|
|
||||||
bool createAPIToken(const char *payload, char *token);
|
|
||||||
bool createAPIPinToken(const IPAddress ipAddress, const char *pin, char *token);
|
|
||||||
bool createAPIPasswordToken(const IPAddress ipAddress, const char *username, const char *password, char *token);
|
|
||||||
bool isAuthenticated(WebServer &server, bool cfg = false);
|
|
||||||
|
|
||||||
//void chunkRoomsResponse(WebServer &server, const char *elem = nullptr);
|
|
||||||
//void chunkShadesResponse(WebServer &server, const char *elem = nullptr);
|
|
||||||
//void chunkGroupsResponse(WebServer &server, const char *elem = nullptr);
|
|
||||||
//void chunkGroupResponse(WebServer &server, SomfyGroup *, const char *prefix = nullptr);
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
1
data-src/appversion
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
2.4.8
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
|
@ -8,9 +8,9 @@
|
||||||
<meta name="apple-mobile-web-app-title" content="ESPSomfy RTS App">
|
<meta name="apple-mobile-web-app-title" content="ESPSomfy RTS App">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
|
||||||
<link rel="stylesheet" href="main.css?v=2.4.7c" type="text/css" />
|
<link rel="stylesheet" href="main.css?v=2.4.8c" type="text/css" />
|
||||||
<link rel="stylesheet" href="widgets.css?v=2.4.7c" type="text/css" />
|
<link rel="stylesheet" href="widgets.css?v=2.4.8c" type="text/css" />
|
||||||
<link rel="stylesheet" href="icons.css?v=2.4.7c" type="text/css" />
|
<link rel="stylesheet" href="icons.css?v=2.4.8c" type="text/css" />
|
||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
|
|
||||||
<!-- iPad retina icon -->
|
<!-- iPad retina icon -->
|
||||||
|
|
@ -114,7 +114,7 @@
|
||||||
rel="apple-touch-startup-image">
|
rel="apple-touch-startup-image">
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript" src="index.js?v=2.4.7c"></script>
|
<script type="text/javascript" src="index.js?v=2.4.8c"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="divContainer" class="container main" data-auth="false">
|
<div id="divContainer" class="container main" data-auth="false">
|
||||||
|
|
@ -246,6 +246,8 @@
|
||||||
<span id="spanMaxMemory" style="text-align:right;width:120px;"></span>
|
<span id="spanMaxMemory" style="text-align:right;width:120px;"></span>
|
||||||
<span style="text-align:right;display:inline-block;color:#00bcd4;">Min: </span>
|
<span style="text-align:right;display:inline-block;color:#00bcd4;">Min: </span>
|
||||||
<span id="spanMinMemory" style="text-align:right;width:120px;"></span>
|
<span id="spanMinMemory" style="text-align:right;width:120px;"></span>
|
||||||
|
<span style="text-align:right;display:inline-block;color:#00bcd4;">Uptime: </span>
|
||||||
|
<span id="spanUptime" style="text-align:right;width:120px;"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
|
|
@ -1270,7 +1270,7 @@ var security = new Security();
|
||||||
|
|
||||||
class General {
|
class General {
|
||||||
initialized = false;
|
initialized = false;
|
||||||
appVersion = 'v2.4.7';
|
appVersion = 'v2.4.8';
|
||||||
reloadApp = false;
|
reloadApp = false;
|
||||||
init() {
|
init() {
|
||||||
if (this.initialized) return;
|
if (this.initialized) return;
|
||||||
|
|
@ -4335,7 +4335,7 @@ class Firmware {
|
||||||
if (typeof overlay !== 'undefined') overlay.remove();
|
if (typeof overlay !== 'undefined') overlay.remove();
|
||||||
reject({ htmlError: status, service: 'GET /backup' });
|
reject({ htmlError: status, service: 'GET /backup' });
|
||||||
};
|
};
|
||||||
xhr.open('GET', baseUrl.length > 0 ? `${baseUrl}/backup` : '/backup', true);
|
xhr.open('GET', baseUrl.length > 0 ? `${baseUrl}/backup?attach=true` : '/backup?attach=true', true);
|
||||||
xhr.send();
|
xhr.send();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -4385,6 +4385,20 @@ class Firmware {
|
||||||
if (sp) sp.innerHTML = mem.max.fmt('#,##0');
|
if (sp) sp.innerHTML = mem.max.fmt('#,##0');
|
||||||
sp = document.getElementById('spanMinMemory');
|
sp = document.getElementById('spanMinMemory');
|
||||||
if (sp) sp.innerHTML = mem.min.fmt('#,##0');
|
if (sp) sp.innerHTML = mem.min.fmt('#,##0');
|
||||||
|
sp = document.getElementById('spanUptime');
|
||||||
|
if (sp) {
|
||||||
|
let t = Math.floor(mem.uptime / 1000);
|
||||||
|
let d = Math.floor(t / 86400); t %= 86400;
|
||||||
|
let h = Math.floor(t / 3600); t %= 3600;
|
||||||
|
let m = Math.floor(t / 60);
|
||||||
|
let s = t % 60;
|
||||||
|
let parts = [];
|
||||||
|
if (d > 0) parts.push(d + 'd');
|
||||||
|
if (h > 0) parts.push(h + 'h');
|
||||||
|
if (m > 0) parts.push(m + 'm');
|
||||||
|
if (s > 0 || parts.length === 0) parts.push(s + 's');
|
||||||
|
sp.innerHTML = parts.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
2.4.7
|
|
||||||
6
huge_app.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
|
otadata, data, ota, 0xe000, 0x2000,
|
||||||
|
app0, app, ota_0, 0x10000, 0x300000,
|
||||||
|
spiffs, data, spiffs, 0x310000,0xE0000,
|
||||||
|
coredump, data, coredump,0x3F0000,0x10000,
|
||||||
|
37
include/README
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the convention is to give header files names that end with `.h'.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||||
46
lib/README
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into the executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a separate directory
|
||||||
|
("lib/your_library_name/[Code]").
|
||||||
|
|
||||||
|
For example, see the structure of the following example libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
Example contents of `src/main.c` using Foo and Bar:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries by scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||||
7
min_spiffs.csv
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
|
otadata, data, ota, 0xe000, 0x2000,
|
||||||
|
app0, app, ota_0, 0x10000, 0x1E0000,
|
||||||
|
app1, app, ota_1, 0x1F0000,0x1E0000,
|
||||||
|
spiffs, data, spiffs, 0x3D0000,0x20000,
|
||||||
|
coredump, data, coredump,0x3F0000,0x10000,
|
||||||
|
290
minify.py
Normal file
|
|
@ -0,0 +1,290 @@
|
||||||
|
"""
|
||||||
|
PlatformIO pre-build script: minify & gzip web assets.
|
||||||
|
|
||||||
|
Copies files from data-src/ → data/
|
||||||
|
• HTML – whitespace & comment removal, then gzip
|
||||||
|
• CSS – whitespace & comment removal, then gzip
|
||||||
|
• JS – whitespace & comment removal, then gzip
|
||||||
|
• PNG – copied as-is (optipng if available)
|
||||||
|
• Other – copied as-is
|
||||||
|
|
||||||
|
Usage in platformio.ini
|
||||||
|
-----------------------
|
||||||
|
extra_scripts = pre:minify.py
|
||||||
|
|
||||||
|
Directory layout
|
||||||
|
----------------
|
||||||
|
project/
|
||||||
|
├── data-src/ ← original, human-readable assets
|
||||||
|
│ ├── index.html
|
||||||
|
│ ├── css/
|
||||||
|
│ ├── js/
|
||||||
|
│ └── img/
|
||||||
|
├── data/ ← auto-generated (gitignore this!)
|
||||||
|
├── minify.py
|
||||||
|
└── platformio.ini
|
||||||
|
|
||||||
|
The script runs automatically before LittleFS / SPIFFS image is built.
|
||||||
|
It also runs once at the start of every build so the data/ dir is always fresh.
|
||||||
|
|
||||||
|
Dependencies: none (pure Python ≥ 3.7).
|
||||||
|
Optional: optipng (for PNG optimisation)
|
||||||
|
"""
|
||||||
|
|
||||||
|
Import("env") # PlatformIO macro – gives us the build environment
|
||||||
|
|
||||||
|
import gzip
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Config
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
SRC_DIR_NAME = "data-src"
|
||||||
|
DST_DIR_NAME = "data"
|
||||||
|
|
||||||
|
# Extensions that will be minified + gzipped
|
||||||
|
MINIFY_AND_GZIP = {".html", ".htm", ".css", ".js", ".json", ".svg", ".xml"}
|
||||||
|
|
||||||
|
# Extensions that optipng can optimise
|
||||||
|
PNG_EXTENSIONS = {".png"}
|
||||||
|
|
||||||
|
# Everything else is copied verbatim
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def _project_dir():
|
||||||
|
return env.subst("$PROJECT_DIR")
|
||||||
|
|
||||||
|
|
||||||
|
def _src_dir():
|
||||||
|
return os.path.join(_project_dir(), SRC_DIR_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
def _dst_dir():
|
||||||
|
return os.path.join(_project_dir(), DST_DIR_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Minifiers (pure Python, no npm needed)
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
def minify_html(text: str) -> str:
|
||||||
|
"""Simple but effective HTML minifier."""
|
||||||
|
# Remove HTML comments (but keep IE conditional comments)
|
||||||
|
text = re.sub(r"<!--(?!\[if).*?-->", "", text, flags=re.DOTALL)
|
||||||
|
# Collapse whitespace between tags
|
||||||
|
text = re.sub(r">\s+<", "> <", text)
|
||||||
|
# Collapse runs of whitespace into a single space
|
||||||
|
text = re.sub(r"\s{2,}", " ", text)
|
||||||
|
# Remove whitespace around = in attributes
|
||||||
|
text = re.sub(r'\s*=\s*', '=', text)
|
||||||
|
# Strip leading/trailing whitespace per line, rejoin
|
||||||
|
lines = [line.strip() for line in text.splitlines() if line.strip()]
|
||||||
|
return " ".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def minify_css(text: str) -> str:
|
||||||
|
"""Remove comments, collapse whitespace in CSS."""
|
||||||
|
# Remove comments
|
||||||
|
text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
|
||||||
|
# Remove whitespace around : ; { } ,
|
||||||
|
text = re.sub(r"\s*([:{};,])\s*", r"\1", text)
|
||||||
|
# Collapse remaining whitespace
|
||||||
|
text = re.sub(r"\s{2,}", " ", text)
|
||||||
|
# Strip leading/trailing
|
||||||
|
return text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
# def minify_js(text: str) -> str:
|
||||||
|
# """
|
||||||
|
# Light JS minifier – removes comments and collapses whitespace.
|
||||||
|
# For heavy minification install terser and the script will use it
|
||||||
|
# automatically (see _try_terser below).
|
||||||
|
# """
|
||||||
|
# # Remove single-line comments (careful with URLs – :// )
|
||||||
|
# text = re.sub(r"(?<!:)//(?!/)[^\n]*", "", text)
|
||||||
|
# # Remove multi-line comments
|
||||||
|
# text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
|
||||||
|
# # Collapse whitespace
|
||||||
|
# text = re.sub(r"\s{2,}", " ", text)
|
||||||
|
# lines = [line.strip() for line in text.splitlines() if line.strip()]
|
||||||
|
# return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def minify_json(text: str) -> str:
|
||||||
|
"""Compact JSON by removing unnecessary whitespace."""
|
||||||
|
import json
|
||||||
|
try:
|
||||||
|
data = json.loads(text)
|
||||||
|
return json.dumps(data, separators=(",", ":"), ensure_ascii=False)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def minify_svg(text: str) -> str:
|
||||||
|
"""Minimal SVG minifier – comments + whitespace."""
|
||||||
|
text = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)
|
||||||
|
text = re.sub(r">\s+<", "><", text)
|
||||||
|
text = re.sub(r"\s{2,}", " ", text)
|
||||||
|
return text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
MINIFIERS = {
|
||||||
|
# ".html": minify_html,
|
||||||
|
# ".htm": minify_html,
|
||||||
|
# ".css": minify_css,
|
||||||
|
# ".js": minify_js,
|
||||||
|
# ".json": minify_json,
|
||||||
|
# ".svg": minify_svg,
|
||||||
|
# ".xml": minify_svg, # same approach works for generic XML
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Optional external tools (used when available)
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
def _has_command(cmd: str) -> bool:
|
||||||
|
return shutil.which(cmd) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def _try_terser(src_path: str, dst_path: str) -> bool:
|
||||||
|
"""Use terser for JS if installed (npm i -g terser)."""
|
||||||
|
if not _has_command("terser"):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
["terser", src_path, "-o", dst_path, "--compress", "--mangle"],
|
||||||
|
check=True, capture_output=True,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _try_optipng(path: str) -> None:
|
||||||
|
"""Optimise PNG in-place if optipng is available."""
|
||||||
|
if _has_command("optipng"):
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
["optipng", "-quiet", "-o2", path],
|
||||||
|
check=True, capture_output=True,
|
||||||
|
)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Core logic
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
def process_file(src_path: str, dst_path: str) -> dict:
|
||||||
|
"""
|
||||||
|
Process a single file: minify, gzip or copy.
|
||||||
|
Returns a small stats dict.
|
||||||
|
"""
|
||||||
|
ext = os.path.splitext(src_path)[1].lower()
|
||||||
|
original_size = os.path.getsize(src_path)
|
||||||
|
stats = {"src": src_path, "original": original_size, "final": 0, "action": "copy"}
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||||
|
|
||||||
|
# ── Text assets: minify + gzip ──────────
|
||||||
|
if ext in MINIFY_AND_GZIP:
|
||||||
|
# Try terser for JS first
|
||||||
|
if ext == ".js" and _try_terser(src_path, dst_path + ".tmp"):
|
||||||
|
with open(dst_path + ".tmp", "rb") as f:
|
||||||
|
minified = f.read()
|
||||||
|
os.remove(dst_path + ".tmp")
|
||||||
|
stats["action"] = "terser+gzip"
|
||||||
|
else:
|
||||||
|
with open(src_path, "r", encoding="utf-8", errors="ignore") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
minifier = MINIFIERS.get(ext)
|
||||||
|
if minifier:
|
||||||
|
content = minifier(content)
|
||||||
|
stats["action"] = "minify+gzip"
|
||||||
|
else:
|
||||||
|
stats["action"] = "gzip"
|
||||||
|
|
||||||
|
minified = content.encode("utf-8")
|
||||||
|
|
||||||
|
# Write gzipped version
|
||||||
|
gz_path = dst_path + ".gz"
|
||||||
|
with gzip.open(gz_path, "wb", compresslevel=9) as gz:
|
||||||
|
gz.write(minified)
|
||||||
|
|
||||||
|
stats["final"] = os.path.getsize(gz_path)
|
||||||
|
return stats
|
||||||
|
|
||||||
|
# ── PNG: copy + optional optipng ────────
|
||||||
|
if ext in PNG_EXTENSIONS:
|
||||||
|
shutil.copy2(src_path, dst_path)
|
||||||
|
_try_optipng(dst_path)
|
||||||
|
stats["final"] = os.path.getsize(dst_path)
|
||||||
|
stats["action"] = "optipng" if _has_command("optipng") else "copy"
|
||||||
|
return stats
|
||||||
|
|
||||||
|
# ── Everything else: plain copy ─────────
|
||||||
|
shutil.copy2(src_path, dst_path)
|
||||||
|
stats["final"] = os.path.getsize(dst_path)
|
||||||
|
return stats
|
||||||
|
|
||||||
|
|
||||||
|
def minify_all():
|
||||||
|
src_dir = _src_dir()
|
||||||
|
dst_dir = _dst_dir()
|
||||||
|
|
||||||
|
if not os.path.isdir(src_dir):
|
||||||
|
print(f"[minify] WARNING: '{SRC_DIR_NAME}/' not found – skipping.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"[minify] {SRC_DIR_NAME}/ → {DST_DIR_NAME}/")
|
||||||
|
|
||||||
|
# Clean destination
|
||||||
|
if os.path.exists(dst_dir):
|
||||||
|
shutil.rmtree(dst_dir)
|
||||||
|
os.makedirs(dst_dir, exist_ok=True)
|
||||||
|
|
||||||
|
total_original = 0
|
||||||
|
total_final = 0
|
||||||
|
file_count = 0
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(src_dir):
|
||||||
|
for fname in sorted(files):
|
||||||
|
# Skip hidden files and editor temp files
|
||||||
|
if fname.startswith(".") or fname.endswith("~"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
src_path = os.path.join(root, fname)
|
||||||
|
rel_path = os.path.relpath(src_path, src_dir)
|
||||||
|
dst_path = os.path.join(dst_dir, rel_path)
|
||||||
|
|
||||||
|
stats = process_file(src_path, dst_path)
|
||||||
|
|
||||||
|
pct = (1 - stats["final"] / stats["original"]) * 100 if stats["original"] > 0 else 0
|
||||||
|
print(
|
||||||
|
f" {rel_path:<40s} "
|
||||||
|
f"{stats['original']:>8,d} → {stats['final']:>8,d} B "
|
||||||
|
f"({pct:+.0f}%) [{stats['action']}]"
|
||||||
|
)
|
||||||
|
|
||||||
|
total_original += stats["original"]
|
||||||
|
total_final += stats["final"]
|
||||||
|
file_count += 1
|
||||||
|
|
||||||
|
print(f"[minify] {file_count} files processed")
|
||||||
|
print(
|
||||||
|
f"[minify] Total: {total_original:,d} → {total_final:,d} bytes "
|
||||||
|
f"(saved {total_original - total_final:,d} bytes, "
|
||||||
|
f"{(1 - total_final / total_original) * 100:.0f}%)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# PlatformIO hooks
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Run at the start of every build
|
||||||
|
minify_all()
|
||||||
39
platformio.ini
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[platformio]
|
||||||
|
default_envs = esp32dev
|
||||||
|
|
||||||
|
; Shared settings for all environments
|
||||||
|
[env]
|
||||||
|
platform = espressif32
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
bblanchon/ArduinoJson@^7.2.2
|
||||||
|
lsatan/SmartRC-CC1101-Driver-Lib@^2.5.7
|
||||||
|
knolleary/PubSubClient@^2.8
|
||||||
|
esp32async/ESPAsyncWebServer@^3.10.3
|
||||||
|
esp32async/AsyncTCP@^3.4.10
|
||||||
|
extra_scripts = pre:minify.py
|
||||||
|
board_build.partitions = huge_app.csv
|
||||||
|
board_build.filesystem = littlefs
|
||||||
|
build_flags =
|
||||||
|
-DCONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=1
|
||||||
|
-DCONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=1
|
||||||
|
-DCONFIG_ESP_COREDUMP_CHECKSUM_CRC32=1
|
||||||
|
-DCONFIG_ESP_TASK_WDT_PANIC=1
|
||||||
|
-DCONFIG_ESP_COREDUMP_DECODE_INFO=1
|
||||||
|
monitor_speed = 115200
|
||||||
|
monitor_filters =
|
||||||
|
time
|
||||||
|
esp32_exception_decoder
|
||||||
|
log2file
|
||||||
|
|
||||||
|
[env:esp32dev]
|
||||||
|
board = esp32dev
|
||||||
|
|
||||||
|
[env:esp32devdbg]
|
||||||
|
board = esp32dev
|
||||||
|
build_type = debug
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -22,10 +22,15 @@ bool ConfigFile::begin(const char* filename, bool readOnly) {
|
||||||
this->_opened = true;
|
this->_opened = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool ConfigFile::beginRAM(String *buf) {
|
||||||
|
_ramBuf = buf;
|
||||||
|
_opened = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
void ConfigFile::end() {
|
void ConfigFile::end() {
|
||||||
if(this->isOpen()) {
|
if(this->isOpen()) {
|
||||||
if(!this->readOnly) this->file.flush();
|
if(_ramBuf) { _ramBuf = nullptr; }
|
||||||
this->file.close();
|
else { if(!this->readOnly) this->file.flush(); this->file.close(); }
|
||||||
}
|
}
|
||||||
this->_opened = false;
|
this->_opened = false;
|
||||||
}
|
}
|
||||||
|
|
@ -187,10 +192,16 @@ bool ConfigFile::readVarString(char *buff, size_t len) {
|
||||||
bool ConfigFile::writeString(const char *val, size_t len, const char tok) {
|
bool ConfigFile::writeString(const char *val, size_t len, const char tok) {
|
||||||
if(!this->isOpen()) return false;
|
if(!this->isOpen()) return false;
|
||||||
int slen = strlen(val);
|
int slen = strlen(val);
|
||||||
|
if(_ramBuf) {
|
||||||
|
if(slen > 0) _ramBuf->concat(val);
|
||||||
|
while(slen < (int)len - 1) { _ramBuf->concat(' '); slen++; }
|
||||||
|
if(tok != CFG_TOK_NONE) _ramBuf->concat(tok);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if(slen > 0)
|
if(slen > 0)
|
||||||
if(this->file.write((uint8_t *)val, slen) != slen) return false;
|
if(this->file.write((uint8_t *)val, slen) != slen) return false;
|
||||||
// Now we need to pad the end of the string so that it is of a fixed length.
|
// Now we need to pad the end of the string so that it is of a fixed length.
|
||||||
while(slen < len - 1) {
|
while(slen < (int)len - 1) {
|
||||||
this->file.write(' ');
|
this->file.write(' ');
|
||||||
slen++;
|
slen++;
|
||||||
}
|
}
|
||||||
|
|
@ -202,6 +213,13 @@ bool ConfigFile::writeString(const char *val, size_t len, const char tok) {
|
||||||
bool ConfigFile::writeVarString(const char *val, const char tok) {
|
bool ConfigFile::writeVarString(const char *val, const char tok) {
|
||||||
if(!this->isOpen()) return false;
|
if(!this->isOpen()) return false;
|
||||||
int slen = strlen(val);
|
int slen = strlen(val);
|
||||||
|
if(_ramBuf) {
|
||||||
|
_ramBuf->concat((char)CFG_TOK_QUOTE);
|
||||||
|
if(slen > 0) _ramBuf->concat(val);
|
||||||
|
_ramBuf->concat((char)CFG_TOK_QUOTE);
|
||||||
|
if(tok != CFG_TOK_NONE) _ramBuf->concat(tok);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
this->writeChar(CFG_TOK_QUOTE);
|
this->writeChar(CFG_TOK_QUOTE);
|
||||||
if(slen > 0) if(this->file.write((uint8_t *)val, slen) != slen) return false;
|
if(slen > 0) if(this->file.write((uint8_t *)val, slen) != slen) return false;
|
||||||
this->writeChar(CFG_TOK_QUOTE);
|
this->writeChar(CFG_TOK_QUOTE);
|
||||||
|
|
@ -210,6 +228,7 @@ bool ConfigFile::writeVarString(const char *val, const char tok) {
|
||||||
}
|
}
|
||||||
bool ConfigFile::writeChar(const char val) {
|
bool ConfigFile::writeChar(const char val) {
|
||||||
if(!this->isOpen()) return false;
|
if(!this->isOpen()) return false;
|
||||||
|
if(_ramBuf) { _ramBuf->concat(val); return true; }
|
||||||
if(this->file.write(static_cast<uint8_t>(val)) == 1) return true;
|
if(this->file.write(static_cast<uint8_t>(val)) == 1) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -31,12 +31,14 @@ struct config_header_t {
|
||||||
class ConfigFile {
|
class ConfigFile {
|
||||||
protected:
|
protected:
|
||||||
File file;
|
File file;
|
||||||
|
String *_ramBuf = nullptr;
|
||||||
bool readOnly = false;
|
bool readOnly = false;
|
||||||
bool begin(const char *filename, bool readOnly = false);
|
bool begin(const char *filename, bool readOnly = false);
|
||||||
uint32_t startRecPos = 0;
|
uint32_t startRecPos = 0;
|
||||||
bool _opened = false;
|
bool _opened = false;
|
||||||
public:
|
public:
|
||||||
config_header_t header;
|
config_header_t header;
|
||||||
|
bool beginRAM(String *buf);
|
||||||
void end();
|
void end();
|
||||||
bool isOpen();
|
bool isOpen();
|
||||||
bool seekRecordByIndex(uint16_t ndx);
|
bool seekRecordByIndex(uint16_t ndx);
|
||||||
|
|
@ -85,13 +85,6 @@ bool appver_t::toJSON(JsonObject &obj) {
|
||||||
obj["suffix"] = this->suffix;
|
obj["suffix"] = this->suffix;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void appver_t::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("name", this->name);
|
|
||||||
json.addElem("major", this->major);
|
|
||||||
json.addElem("minor", this->minor);
|
|
||||||
json.addElem("build", this->build);
|
|
||||||
json.addElem("suffix", this->suffix);
|
|
||||||
}
|
|
||||||
void appver_t::toJSON(JsonSockEvent *json) {
|
void appver_t::toJSON(JsonSockEvent *json) {
|
||||||
json->addElem("name", this->name);
|
json->addElem("name", this->name);
|
||||||
json->addElem("major", this->major);
|
json->addElem("major", this->major);
|
||||||
|
|
@ -249,14 +242,6 @@ bool ConfigSettings::toJSON(JsonObject &obj) {
|
||||||
obj["checkForUpdate"] = this->checkForUpdate;
|
obj["checkForUpdate"] = this->checkForUpdate;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void ConfigSettings::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("ssdpBroadcast", this->ssdpBroadcast);
|
|
||||||
json.addElem("hostname", this->hostname);
|
|
||||||
json.addElem("connType", static_cast<uint8_t>(this->connType));
|
|
||||||
json.addElem("chipModel", this->chipModel);
|
|
||||||
json.addElem("checkForUpdate", this->checkForUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConfigSettings::requiresAuth() { return this->Security.type != security_types::None; }
|
bool ConfigSettings::requiresAuth() { return this->Security.type != security_types::None; }
|
||||||
bool ConfigSettings::fromJSON(JsonObject &obj) {
|
bool ConfigSettings::fromJSON(JsonObject &obj) {
|
||||||
if(obj.containsKey("ssdpBroadcast")) this->ssdpBroadcast = obj["ssdpBroadcast"];
|
if(obj.containsKey("ssdpBroadcast")) this->ssdpBroadcast = obj["ssdpBroadcast"];
|
||||||
|
|
@ -308,18 +293,6 @@ bool MQTTSettings::begin() {
|
||||||
this->load();
|
this->load();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void MQTTSettings::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("enabled", this->enabled);
|
|
||||||
json.addElem("pubDisco", this->pubDisco);
|
|
||||||
json.addElem("protocol", this->protocol);
|
|
||||||
json.addElem("hostname", this->hostname);
|
|
||||||
json.addElem("port", (uint32_t)this->port);
|
|
||||||
json.addElem("username", this->username);
|
|
||||||
json.addElem("password", this->password);
|
|
||||||
json.addElem("rootTopic", this->rootTopic);
|
|
||||||
json.addElem("discoTopic", this->discoTopic);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MQTTSettings::toJSON(JsonObject &obj) {
|
bool MQTTSettings::toJSON(JsonObject &obj) {
|
||||||
obj["enabled"] = this->enabled;
|
obj["enabled"] = this->enabled;
|
||||||
obj["pubDisco"] = this->pubDisco;
|
obj["pubDisco"] = this->pubDisco;
|
||||||
|
|
@ -418,11 +391,6 @@ bool NTPSettings::fromJSON(JsonObject &obj) {
|
||||||
this->parseValueString(obj, "posixZone", this->posixZone, sizeof(this->posixZone));
|
this->parseValueString(obj, "posixZone", this->posixZone, sizeof(this->posixZone));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void NTPSettings::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("ntpServer", this->ntpServer);
|
|
||||||
json.addElem("posixZone", this->posixZone);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NTPSettings::toJSON(JsonObject &obj) {
|
bool NTPSettings::toJSON(JsonObject &obj) {
|
||||||
obj["ntpServer"] = this->ntpServer;
|
obj["ntpServer"] = this->ntpServer;
|
||||||
obj["posixZone"] = this->posixZone;
|
obj["posixZone"] = this->posixZone;
|
||||||
|
|
@ -459,16 +427,6 @@ bool IPSettings::toJSON(JsonObject &obj) {
|
||||||
obj["dns2"] = this->dns2 == ipEmpty ? "" : this->dns2.toString();
|
obj["dns2"] = this->dns2 == ipEmpty ? "" : this->dns2.toString();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void IPSettings::toJSON(JsonResponse &json) {
|
|
||||||
IPAddress ipEmpty(0,0,0,0);
|
|
||||||
json.addElem("dhcp", this->dhcp);
|
|
||||||
json.addElem("ip", this->ip.toString().c_str());
|
|
||||||
json.addElem("gateway", this->gateway.toString().c_str());
|
|
||||||
json.addElem("subnet", this->subnet.toString().c_str());
|
|
||||||
json.addElem("dns1", this->dns1.toString().c_str());
|
|
||||||
json.addElem("dns2", this->dns2.toString().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IPSettings::save() {
|
bool IPSettings::save() {
|
||||||
pref.begin("IP");
|
pref.begin("IP");
|
||||||
pref.clear();
|
pref.clear();
|
||||||
|
|
@ -529,14 +487,6 @@ bool SecuritySettings::toJSON(JsonObject &obj) {
|
||||||
obj["permissions"] = this->permissions;
|
obj["permissions"] = this->permissions;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void SecuritySettings::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("type", static_cast<uint8_t>(this->type));
|
|
||||||
json.addElem("username", this->username);
|
|
||||||
json.addElem("password", this->password);
|
|
||||||
json.addElem("pin", this->pin);
|
|
||||||
json.addElem("permissions", this->permissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SecuritySettings::save() {
|
bool SecuritySettings::save() {
|
||||||
pref.begin("SEC");
|
pref.begin("SEC");
|
||||||
pref.clear();
|
pref.clear();
|
||||||
|
|
@ -590,13 +540,6 @@ bool WifiSettings::toJSON(JsonObject &obj) {
|
||||||
obj["hidden"] = this->hidden;
|
obj["hidden"] = this->hidden;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void WifiSettings::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("ssid", this->ssid);
|
|
||||||
json.addElem("passphrase", this->passphrase);
|
|
||||||
json.addElem("roaming", this->roaming);
|
|
||||||
json.addElem("hidden", this->hidden);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WifiSettings::save() {
|
bool WifiSettings::save() {
|
||||||
pref.begin("WIFI");
|
pref.begin("WIFI");
|
||||||
pref.clear();
|
pref.clear();
|
||||||
|
|
@ -697,16 +640,6 @@ bool EthernetSettings::toJSON(JsonObject &obj) {
|
||||||
obj["MDIOPin"] = this->MDIOPin;
|
obj["MDIOPin"] = this->MDIOPin;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void EthernetSettings::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("boardType", this->boardType);
|
|
||||||
json.addElem("phyAddress", this->phyAddress);
|
|
||||||
json.addElem("CLKMode", static_cast<uint8_t>(this->CLKMode));
|
|
||||||
json.addElem("phyType", static_cast<uint8_t>(this->phyType));
|
|
||||||
json.addElem("PWRPin", this->PWRPin);
|
|
||||||
json.addElem("MDCPin", this->MDCPin);
|
|
||||||
json.addElem("MDIOPin", this->MDIOPin);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EthernetSettings::usesPin(uint8_t pin) {
|
bool EthernetSettings::usesPin(uint8_t pin) {
|
||||||
if((this->CLKMode == 0 || this->CLKMode == 1) && pin == 0) return true;
|
if((this->CLKMode == 0 || this->CLKMode == 1) && pin == 0) return true;
|
||||||
else if(this->CLKMode == 2 && pin == 16) return true;
|
else if(this->CLKMode == 2 && pin == 16) return true;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#ifndef configsettings_h
|
#ifndef configsettings_h
|
||||||
#define configsettings_h
|
#define configsettings_h
|
||||||
#include "WResp.h"
|
#include "WResp.h"
|
||||||
#define FW_VERSION "v2.4.7"
|
#define FW_VERSION "v2.4.8"
|
||||||
enum class conn_types_t : byte {
|
enum class conn_types_t : byte {
|
||||||
unset = 0x00,
|
unset = 0x00,
|
||||||
wifi = 0x01,
|
wifi = 0x01,
|
||||||
|
|
@ -34,7 +34,7 @@ struct appver_t {
|
||||||
char suffix[4] = "";
|
char suffix[4] = "";
|
||||||
void parse(const char *ver);
|
void parse(const char *ver);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
void toJSON(JsonSockEvent *json);
|
void toJSON(JsonSockEvent *json);
|
||||||
int8_t compare(appver_t &ver);
|
int8_t compare(appver_t &ver);
|
||||||
void copy(appver_t &ver);
|
void copy(appver_t &ver);
|
||||||
|
|
@ -46,7 +46,7 @@ class BaseSettings {
|
||||||
bool loadFile(const char* filename);
|
bool loadFile(const char* filename);
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
bool parseIPAddress(JsonObject &obj, const char *prop, IPAddress *);
|
bool parseIPAddress(JsonObject &obj, const char *prop, IPAddress *);
|
||||||
bool parseValueString(JsonObject &obj, const char *prop, char *dest, size_t size);
|
bool parseValueString(JsonObject &obj, const char *prop, char *dest, size_t size);
|
||||||
int parseValueInt(JsonObject &obj, const char *prop, int defVal);
|
int parseValueInt(JsonObject &obj, const char *prop, int defVal);
|
||||||
|
|
@ -61,7 +61,7 @@ class NTPSettings: BaseSettings {
|
||||||
char posixZone[64] = "";
|
char posixZone[64] = "";
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
bool apply();
|
bool apply();
|
||||||
bool begin();
|
bool begin();
|
||||||
bool save();
|
bool save();
|
||||||
|
|
@ -79,7 +79,7 @@ class WifiSettings: BaseSettings {
|
||||||
bool begin();
|
bool begin();
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
String mapEncryptionType(int type);
|
String mapEncryptionType(int type);
|
||||||
bool ssidExists(const char *ssid);
|
bool ssidExists(const char *ssid);
|
||||||
void printNetworks();
|
void printNetworks();
|
||||||
|
|
@ -102,7 +102,7 @@ class EthernetSettings: BaseSettings {
|
||||||
bool begin();
|
bool begin();
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
bool load();
|
bool load();
|
||||||
bool save();
|
bool save();
|
||||||
void print();
|
void print();
|
||||||
|
|
@ -120,7 +120,7 @@ class IPSettings: BaseSettings {
|
||||||
bool begin();
|
bool begin();
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
bool load();
|
bool load();
|
||||||
bool save();
|
bool save();
|
||||||
void print();
|
void print();
|
||||||
|
|
@ -145,7 +145,7 @@ class SecuritySettings: BaseSettings {
|
||||||
bool load();
|
bool load();
|
||||||
void print();
|
void print();
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
};
|
};
|
||||||
class MQTTSettings: BaseSettings {
|
class MQTTSettings: BaseSettings {
|
||||||
|
|
@ -163,7 +163,7 @@ class MQTTSettings: BaseSettings {
|
||||||
bool save();
|
bool save();
|
||||||
bool load();
|
bool load();
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
};
|
};
|
||||||
class ConfigSettings: BaseSettings {
|
class ConfigSettings: BaseSettings {
|
||||||
|
|
@ -187,7 +187,7 @@ class ConfigSettings: BaseSettings {
|
||||||
bool requiresAuth();
|
bool requiresAuth();
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
bool begin();
|
bool begin();
|
||||||
bool save();
|
bool save();
|
||||||
bool load();
|
bool load();
|
||||||
|
|
@ -72,22 +72,6 @@ void GitRelease::setAssetProperty(const char *key, const char *val) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void GitRelease::toJSON(JsonResponse &json) {
|
|
||||||
Timestamp ts;
|
|
||||||
char buff[20];
|
|
||||||
sprintf(buff, "%llu", this->id);
|
|
||||||
json.addElem("id", buff);
|
|
||||||
json.addElem("name", this->name);
|
|
||||||
json.addElem("date", ts.getISOTime(this->releaseDate));
|
|
||||||
json.addElem("draft", this->draft);
|
|
||||||
json.addElem("preRelease", this->preRelease);
|
|
||||||
json.addElem("main", this->main);
|
|
||||||
json.addElem("hasFS", this->hasFS);
|
|
||||||
json.addElem("hwVersions", this->hwVersions);
|
|
||||||
json.beginObject("version");
|
|
||||||
this->version.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
#define ERR_CLIENT_OFFSET -50
|
#define ERR_CLIENT_OFFSET -50
|
||||||
|
|
||||||
int16_t GitRepo::getReleases(uint8_t num) {
|
int16_t GitRepo::getReleases(uint8_t num) {
|
||||||
|
|
@ -230,22 +214,6 @@ int16_t GitRepo::getReleases(uint8_t num) {
|
||||||
settings.printAvailHeap();
|
settings.printAvailHeap();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void GitRepo::toJSON(JsonResponse &json) {
|
|
||||||
json.beginObject("fwVersion");
|
|
||||||
settings.fwVersion.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
json.beginObject("appVersion");
|
|
||||||
settings.appVersion.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
json.beginArray("releases");
|
|
||||||
for(uint8_t i = 0; i < GIT_MAX_RELEASES + 1; i++) {
|
|
||||||
if(this->releases[i].id == 0) continue;
|
|
||||||
json.beginObject();
|
|
||||||
this->releases[i].toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
json.endArray();
|
|
||||||
}
|
|
||||||
#define UPDATE_ERR_OFFSET 20
|
#define UPDATE_ERR_OFFSET 20
|
||||||
#define ERR_DOWNLOAD_HTTP -40
|
#define ERR_DOWNLOAD_HTTP -40
|
||||||
#define ERR_DOWNLOAD_BUFFER -41
|
#define ERR_DOWNLOAD_BUFFER -41
|
||||||
|
|
@ -310,23 +278,6 @@ void GitUpdater::setCurrentRelease(GitRepo &repo) {
|
||||||
}
|
}
|
||||||
this->emitUpdateCheck();
|
this->emitUpdateCheck();
|
||||||
}
|
}
|
||||||
void GitUpdater::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("available", this->updateAvailable);
|
|
||||||
json.addElem("status", this->status);
|
|
||||||
json.addElem("error", (int32_t)this->error);
|
|
||||||
json.addElem("cancelled", this->cancelled);
|
|
||||||
json.addElem("checkForUpdate", settings.checkForUpdate);
|
|
||||||
json.addElem("inetAvailable", this->inetAvailable);
|
|
||||||
json.beginObject("fwVersion");
|
|
||||||
settings.fwVersion.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
json.beginObject("appVersion");
|
|
||||||
settings.appVersion.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
json.beginObject("latest");
|
|
||||||
this->latest.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
void GitUpdater::emitUpdateCheck(uint8_t num) {
|
void GitUpdater::emitUpdateCheck(uint8_t num) {
|
||||||
JsonSockEvent *json = sockEmit.beginEmit("fwStatus");
|
JsonSockEvent *json = sockEmit.beginEmit("fwStatus");
|
||||||
json->beginObject();
|
json->beginObject();
|
||||||
|
|
@ -28,13 +28,13 @@ class GitRelease {
|
||||||
appver_t version;
|
appver_t version;
|
||||||
void setReleaseProperty(const char *key, const char *val);
|
void setReleaseProperty(const char *key, const char *val);
|
||||||
void setAssetProperty(const char *key, const char *val);
|
void setAssetProperty(const char *key, const char *val);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
};
|
};
|
||||||
class GitRepo {
|
class GitRepo {
|
||||||
public:
|
public:
|
||||||
int16_t getReleases(uint8_t num = GIT_MAX_RELEASES);
|
int16_t getReleases(uint8_t num = GIT_MAX_RELEASES);
|
||||||
GitRelease releases[GIT_MAX_RELEASES + 1];
|
GitRelease releases[GIT_MAX_RELEASES + 1];
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
};
|
};
|
||||||
class GitUpdater {
|
class GitUpdater {
|
||||||
public:
|
public:
|
||||||
|
|
@ -58,7 +58,7 @@ class GitUpdater {
|
||||||
void setFirmwareFile();
|
void setFirmwareFile();
|
||||||
void setCurrentRelease(GitRepo &repo);
|
void setCurrentRelease(GitRepo &repo);
|
||||||
void loop();
|
void loop();
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
bool recoverFilesystem();
|
bool recoverFilesystem();
|
||||||
int checkInternet();
|
int checkInternet();
|
||||||
void emitUpdateCheck(uint8_t num=255);
|
void emitUpdateCheck(uint8_t num=255);
|
||||||
|
|
@ -691,6 +691,7 @@ void Network::emitHeap(uint8_t num) {
|
||||||
json->addElem("free", freeHeap);
|
json->addElem("free", freeHeap);
|
||||||
json->addElem("min", minHeap);
|
json->addElem("min", minHeap);
|
||||||
json->addElem("total", ESP.getHeapSize());
|
json->addElem("total", ESP.getHeapSize());
|
||||||
|
json->addElem("uptime", (uint64_t)millis());
|
||||||
json->endObject();
|
json->endObject();
|
||||||
if(num == 255 && bTimeEmit && bValEmit) {
|
if(num == 255 && bTimeEmit && bValEmit) {
|
||||||
sockEmit.endEmit(num);
|
sockEmit.endEmit(num);
|
||||||
221
src/Sockets.cpp
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <esp_task_wdt.h>
|
||||||
|
#include "Sockets.h"
|
||||||
|
#include "ConfigSettings.h"
|
||||||
|
#include "Somfy.h"
|
||||||
|
#include "Network.h"
|
||||||
|
#include "GitOTA.h"
|
||||||
|
|
||||||
|
extern ConfigSettings settings;
|
||||||
|
extern Network net;
|
||||||
|
extern SomfyShadeController somfy;
|
||||||
|
extern SocketEmitter sockEmit;
|
||||||
|
extern GitUpdater git;
|
||||||
|
|
||||||
|
AsyncWebServer wsServer(8080);
|
||||||
|
AsyncWebSocket ws("/");
|
||||||
|
|
||||||
|
#define MAX_WS_CLIENTS 5
|
||||||
|
static uint32_t clientMap[MAX_WS_CLIENTS] = {0,0,0,0,0};
|
||||||
|
|
||||||
|
static uint8_t mapClientId(uint32_t asyncId) {
|
||||||
|
for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++)
|
||||||
|
if(clientMap[i] == asyncId) return i;
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
static uint32_t getAsyncId(uint8_t slot) {
|
||||||
|
if(slot < MAX_WS_CLIENTS) return clientMap[slot];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static uint8_t addClient(uint32_t asyncId) {
|
||||||
|
for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++) {
|
||||||
|
if(clientMap[i] == 0) { clientMap[i] = asyncId; return i; }
|
||||||
|
}
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
static void removeClient(uint32_t asyncId) {
|
||||||
|
for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++)
|
||||||
|
if(clientMap[i] == asyncId) clientMap[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_SOCK_RESPONSE 2048
|
||||||
|
static char g_response[MAX_SOCK_RESPONSE];
|
||||||
|
|
||||||
|
bool room_t::isJoined(uint8_t num) {
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->clients); i++) {
|
||||||
|
if(this->clients[i] == num) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool room_t::join(uint8_t num) {
|
||||||
|
if(this->isJoined(num)) return true;
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->clients); i++) {
|
||||||
|
if(this->clients[i] == 255) {
|
||||||
|
this->clients[i] = num;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool room_t::leave(uint8_t num) {
|
||||||
|
if(!this->isJoined(num)) return false;
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->clients); i++) {
|
||||||
|
if(this->clients[i] == num) this->clients[i] = 255;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void room_t::clear() {
|
||||||
|
memset(this->clients, 255, sizeof(this->clients));
|
||||||
|
}
|
||||||
|
uint8_t room_t::activeClients() {
|
||||||
|
uint8_t n = 0;
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->clients); i++) {
|
||||||
|
if(this->clients[i] != 255) n++;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* SocketEmitter class members
|
||||||
|
********************************************************************/
|
||||||
|
void SocketEmitter::startup() {
|
||||||
|
|
||||||
|
}
|
||||||
|
void SocketEmitter::begin() {
|
||||||
|
ws.onEvent(SocketEmitter::wsEvent);
|
||||||
|
wsServer.addHandler(&ws);
|
||||||
|
wsServer.begin();
|
||||||
|
Serial.println("Socket Server Started...");
|
||||||
|
}
|
||||||
|
void SocketEmitter::loop() {
|
||||||
|
ws.cleanupClients();
|
||||||
|
this->initClients();
|
||||||
|
}
|
||||||
|
JsonSockEvent *SocketEmitter::beginEmit(const char *evt) {
|
||||||
|
this->json.beginEvent(&ws, evt, g_response, sizeof(g_response));
|
||||||
|
return &this->json;
|
||||||
|
}
|
||||||
|
void SocketEmitter::endEmit(uint8_t num) {
|
||||||
|
if(num == 255) {
|
||||||
|
this->json.endEvent(0);
|
||||||
|
} else {
|
||||||
|
uint32_t asyncId = getAsyncId(num);
|
||||||
|
this->json.endEvent(asyncId);
|
||||||
|
}
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
}
|
||||||
|
void SocketEmitter::endEmitRoom(uint8_t room) {
|
||||||
|
if(room < SOCK_MAX_ROOMS) {
|
||||||
|
room_t *r = &this->rooms[room];
|
||||||
|
for(uint8_t i = 0; i < sizeof(r->clients); i++) {
|
||||||
|
if(r->clients[i] != 255) {
|
||||||
|
uint32_t asyncId = getAsyncId(r->clients[i]);
|
||||||
|
if(asyncId != 0) this->json.endEvent(asyncId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t SocketEmitter::activeClients(uint8_t room) {
|
||||||
|
if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void SocketEmitter::initClients() {
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->newClients); i++) {
|
||||||
|
uint8_t slot = this->newClients[i];
|
||||||
|
if(slot != 255) {
|
||||||
|
uint32_t asyncId = getAsyncId(slot);
|
||||||
|
if(asyncId != 0 && ws.hasClient(asyncId)) {
|
||||||
|
Serial.printf("Initializing Socket Client %u (asyncId=%lu)\n", slot, asyncId);
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
settings.emitSockets(slot);
|
||||||
|
if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
|
||||||
|
somfy.emitState(slot);
|
||||||
|
if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
|
||||||
|
git.emitUpdateCheck(slot);
|
||||||
|
if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
|
||||||
|
net.emitSockets(slot);
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
}
|
||||||
|
this->newClients[i] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SocketEmitter::delayInit(uint8_t num) {
|
||||||
|
for(uint8_t i=0; i < sizeof(this->newClients); i++) {
|
||||||
|
if(this->newClients[i] == num) break;
|
||||||
|
else if(this->newClients[i] == 255) {
|
||||||
|
this->newClients[i] = num;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SocketEmitter::end() {
|
||||||
|
ws.closeAll();
|
||||||
|
wsServer.end();
|
||||||
|
for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++)
|
||||||
|
this->rooms[i].clear();
|
||||||
|
memset(clientMap, 0, sizeof(clientMap));
|
||||||
|
}
|
||||||
|
void SocketEmitter::disconnect() {
|
||||||
|
ws.closeAll();
|
||||||
|
memset(clientMap, 0, sizeof(clientMap));
|
||||||
|
}
|
||||||
|
void SocketEmitter::wsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
||||||
|
uint32_t asyncId = client->id();
|
||||||
|
switch(type) {
|
||||||
|
case WS_EVT_CONNECT:
|
||||||
|
{
|
||||||
|
uint8_t slot = addClient(asyncId);
|
||||||
|
if(slot == 255) {
|
||||||
|
Serial.printf("Socket: No free client slots, closing %lu\n", asyncId);
|
||||||
|
client->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IPAddress ip = client->remoteIP();
|
||||||
|
Serial.printf("Socket [%lu] Connected from %d.%d.%d.%d (slot %u)\n", asyncId, ip[0], ip[1], ip[2], ip[3], slot);
|
||||||
|
client->text("Connected");
|
||||||
|
client->setCloseClientOnQueueFull(false);
|
||||||
|
sockEmit.delayInit(slot);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WS_EVT_DISCONNECT:
|
||||||
|
{
|
||||||
|
uint8_t slot = mapClientId(asyncId);
|
||||||
|
Serial.printf("Socket [%lu] Disconnected (slot %u)\n", asyncId, slot);
|
||||||
|
if(slot != 255) {
|
||||||
|
for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++)
|
||||||
|
sockEmit.rooms[i].leave(slot);
|
||||||
|
}
|
||||||
|
removeClient(asyncId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WS_EVT_DATA:
|
||||||
|
{
|
||||||
|
AwsFrameInfo *info = (AwsFrameInfo*)arg;
|
||||||
|
if(info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
|
||||||
|
uint8_t slot = mapClientId(asyncId);
|
||||||
|
data[len] = 0;
|
||||||
|
if(strncmp((char*)data, "join:", 5) == 0) {
|
||||||
|
uint8_t roomNum = atoi((char*)&data[5]);
|
||||||
|
Serial.printf("Client %u joining room %u\n", slot, roomNum);
|
||||||
|
if(roomNum < SOCK_MAX_ROOMS && slot != 255) sockEmit.rooms[roomNum].join(slot);
|
||||||
|
}
|
||||||
|
else if(strncmp((char*)data, "leave:", 6) == 0) {
|
||||||
|
uint8_t roomNum = atoi((char*)&data[6]);
|
||||||
|
Serial.printf("Client %u leaving room %u\n", slot, roomNum);
|
||||||
|
if(roomNum < SOCK_MAX_ROOMS && slot != 255) sockEmit.rooms[roomNum].leave(slot);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.printf("Socket [%lu] text: %s\n", asyncId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WS_EVT_ERROR:
|
||||||
|
Serial.printf("Socket [%lu] Error\n", asyncId);
|
||||||
|
break;
|
||||||
|
case WS_EVT_PONG:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include <WebSocketsServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include "WResp.h"
|
#include "WResp.h"
|
||||||
#ifndef sockets_h
|
#ifndef sockets_h
|
||||||
#define sockets_h
|
#define sockets_h
|
||||||
|
|
@ -21,7 +21,6 @@ class SocketEmitter {
|
||||||
void delayInit(uint8_t num);
|
void delayInit(uint8_t num);
|
||||||
public:
|
public:
|
||||||
JsonSockEvent json;
|
JsonSockEvent json;
|
||||||
//ClientSocketEvent evt;
|
|
||||||
room_t rooms[SOCK_MAX_ROOMS];
|
room_t rooms[SOCK_MAX_ROOMS];
|
||||||
uint8_t activeClients(uint8_t room);
|
uint8_t activeClients(uint8_t room);
|
||||||
void initClients();
|
void initClients();
|
||||||
|
|
@ -33,6 +32,6 @@ class SocketEmitter {
|
||||||
JsonSockEvent * beginEmit(const char *evt);
|
JsonSockEvent * beginEmit(const char *evt);
|
||||||
void endEmit(uint8_t num = 255);
|
void endEmit(uint8_t num = 255);
|
||||||
void endEmitRoom(uint8_t num);
|
void endEmitRoom(uint8_t num);
|
||||||
static void wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length);
|
static void wsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <ELECHOUSE_CC1101_SRC_DRV.h>
|
#include <ELECHOUSE_CC1101_SRC_DRV.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <WebServer.h>
|
|
||||||
#include <esp_task_wdt.h>
|
#include <esp_task_wdt.h>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "ConfigSettings.h"
|
#include "ConfigSettings.h"
|
||||||
|
|
@ -156,9 +155,9 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
||||||
this->proto = radio_proto::RTV;
|
this->proto = radio_proto::RTV;
|
||||||
this->cmd = (somfy_commands)(this->encKey - 148);
|
this->cmd = (somfy_commands)(this->encKey - 148);
|
||||||
}
|
}
|
||||||
else if(this->encKey > 133) {
|
else if(this->encKey >= 133) {
|
||||||
this->proto = radio_proto::RTW;
|
this->proto = radio_proto::RTW;
|
||||||
this->cmd = (somfy_commands)(this->encKey - 133);
|
this->cmd = this->encKey == 133 ? somfy_commands::My : (somfy_commands)(this->encKey - 133);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else this->proto = radio_proto::RTS;
|
else this->proto = radio_proto::RTS;
|
||||||
|
|
@ -633,8 +632,10 @@ void SomfyShadeController::commit() {
|
||||||
void SomfyShadeController::writeBackup() {
|
void SomfyShadeController::writeBackup() {
|
||||||
if(git.lockFS) return;
|
if(git.lockFS) return;
|
||||||
esp_task_wdt_reset(); // Make sure we don't reset inadvertently.
|
esp_task_wdt_reset(); // Make sure we don't reset inadvertently.
|
||||||
|
this->backupData = "";
|
||||||
|
this->backupData.reserve(16384);
|
||||||
ShadeConfigFile file;
|
ShadeConfigFile file;
|
||||||
file.begin("/controller.backup", false);
|
file.beginRAM(&this->backupData);
|
||||||
file.backup(this);
|
file.backup(this);
|
||||||
file.end();
|
file.end();
|
||||||
}
|
}
|
||||||
|
|
@ -1445,9 +1446,9 @@ void SomfyRoom::unpublish() {
|
||||||
}
|
}
|
||||||
void SomfyShade::publishState() {
|
void SomfyShade::publishState() {
|
||||||
if(mqtt.connected()) {
|
if(mqtt.connected()) {
|
||||||
this->publish("position", this->transformPosition(this->currentPos), true);
|
this->publish("position", (uint8_t)50, true);
|
||||||
this->publish("direction", this->direction, true);
|
this->publish("direction", (int8_t)0, true);
|
||||||
this->publish("target", this->transformPosition(this->target), true);
|
this->publish("target", (uint8_t)50, true);
|
||||||
this->publish("lastRollingCode", this->lastRollingCode);
|
this->publish("lastRollingCode", this->lastRollingCode);
|
||||||
this->publish("mypos", this->transformPosition(this->myPos), true);
|
this->publish("mypos", this->transformPosition(this->myPos), true);
|
||||||
this->publish("myTiltPos", this->transformPosition(this->myTiltPos), true);
|
this->publish("myTiltPos", this->transformPosition(this->myTiltPos), true);
|
||||||
|
|
@ -1794,7 +1795,7 @@ bool SomfyGroup::publish(const char *topic, bool val, bool retain) {
|
||||||
float SomfyShade::p_currentPos(float pos) {
|
float SomfyShade::p_currentPos(float pos) {
|
||||||
float old = this->currentPos;
|
float old = this->currentPos;
|
||||||
this->currentPos = pos;
|
this->currentPos = pos;
|
||||||
if(floor(old) != floor(pos)) this->publish("position", this->transformPosition(static_cast<uint8_t>(floor(this->currentPos))));
|
if(floor(old) != floor(pos)) this->publish("position", (uint8_t)50);
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
float SomfyShade::p_currentTiltPos(float pos) {
|
float SomfyShade::p_currentTiltPos(float pos) {
|
||||||
|
|
@ -2888,9 +2889,21 @@ void SomfyShade::moveToMyPosition() {
|
||||||
}
|
}
|
||||||
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
||||||
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
||||||
|
Serial.print("Send command start\n");
|
||||||
// This sendCommand function will always be called externally. sendCommand at the remote level
|
// This sendCommand function will always be called externally. sendCommand at the remote level
|
||||||
// is expected to be called internally when the motor needs commanded.
|
// is expected to be called internally when the motor needs commanded.
|
||||||
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
|
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
|
||||||
|
// If same direction command sent while already moving, stop the state tracking.
|
||||||
|
// The real motor stops on its own when it receives the same direction again.
|
||||||
|
if((cmd == somfy_commands::Up && this->direction == -1) ||
|
||||||
|
(cmd == somfy_commands::Down && this->direction == 1)) {
|
||||||
|
Serial.println("Same command as dir");
|
||||||
|
SomfyRemote::sendCommand(cmd, repeat);
|
||||||
|
this->p_target(this->currentPos);
|
||||||
|
this->p_tiltTarget(this->currentTiltPos);
|
||||||
|
this->setMovement(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(cmd == somfy_commands::Up) {
|
if(cmd == somfy_commands::Up) {
|
||||||
if(this->tiltType == tilt_types::euromode) {
|
if(this->tiltType == tilt_types::euromode) {
|
||||||
// In euromode we need to long press for 2 seconds on the
|
// In euromode we need to long press for 2 seconds on the
|
||||||
|
|
@ -2930,9 +2943,13 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
|
||||||
else if(cmd == somfy_commands::My) {
|
else if(cmd == somfy_commands::My) {
|
||||||
if(this->isToggle() || this->shadeType == shade_types::drycontact)
|
if(this->isToggle() || this->shadeType == shade_types::drycontact)
|
||||||
SomfyRemote::sendCommand(cmd, repeat);
|
SomfyRemote::sendCommand(cmd, repeat);
|
||||||
else if(this->shadeType == shade_types::drycontact2) return;
|
else if(this->shadeType == shade_types::drycontact2){
|
||||||
|
Serial.print("Send command start 1\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
else if(this->isIdle()) {
|
else if(this->isIdle()) {
|
||||||
this->moveToMyPosition();
|
this->moveToMyPosition();
|
||||||
|
Serial.print("Send command end 2\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -2951,6 +2968,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
|
||||||
else {
|
else {
|
||||||
SomfyRemote::sendCommand(cmd, repeat, stepSize);
|
SomfyRemote::sendCommand(cmd, repeat, stepSize);
|
||||||
}
|
}
|
||||||
|
Serial.print("Send command end\n");
|
||||||
}
|
}
|
||||||
void SomfyGroup::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
void SomfyGroup::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
||||||
void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
||||||
|
|
@ -3309,72 +3327,6 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
void SomfyShade::toJSONRef(JsonResponse &json) {
|
|
||||||
json.addElem("shadeId", this->getShadeId());
|
|
||||||
json.addElem("roomId", this->roomId);
|
|
||||||
json.addElem("name", this->name);
|
|
||||||
json.addElem("remoteAddress", (uint32_t)this->m_remoteAddress);
|
|
||||||
json.addElem("paired", this->paired);
|
|
||||||
json.addElem("shadeType", static_cast<uint8_t>(this->shadeType));
|
|
||||||
json.addElem("flipCommands", this->flipCommands);
|
|
||||||
json.addElem("flipPosition", this->flipCommands);
|
|
||||||
json.addElem("bitLength", this->bitLength);
|
|
||||||
json.addElem("proto", static_cast<uint8_t>(this->proto));
|
|
||||||
json.addElem("flags", this->flags);
|
|
||||||
json.addElem("sunSensor", this->hasSunSensor());
|
|
||||||
json.addElem("hasLight", this->hasLight());
|
|
||||||
json.addElem("repeats", this->repeats);
|
|
||||||
//SomfyRemote::toJSON(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SomfyShade::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("shadeId", this->getShadeId());
|
|
||||||
json.addElem("roomId", this->roomId);
|
|
||||||
json.addElem("name", this->name);
|
|
||||||
json.addElem("remoteAddress", (uint32_t)this->m_remoteAddress);
|
|
||||||
json.addElem("upTime", (uint32_t)this->upTime);
|
|
||||||
json.addElem("downTime", (uint32_t)this->downTime);
|
|
||||||
json.addElem("paired", this->paired);
|
|
||||||
json.addElem("lastRollingCode", (uint32_t)this->lastRollingCode);
|
|
||||||
json.addElem("position", this->transformPosition(this->currentPos));
|
|
||||||
json.addElem("tiltType", static_cast<uint8_t>(this->tiltType));
|
|
||||||
json.addElem("tiltPosition", this->transformPosition(this->currentTiltPos));
|
|
||||||
json.addElem("tiltDirection", this->tiltDirection);
|
|
||||||
json.addElem("tiltTime", (uint32_t)this->tiltTime);
|
|
||||||
json.addElem("stepSize", (uint32_t)this->stepSize);
|
|
||||||
json.addElem("tiltTarget", this->transformPosition(this->tiltTarget));
|
|
||||||
json.addElem("target", this->transformPosition(this->target));
|
|
||||||
json.addElem("myPos", this->transformPosition(this->myPos));
|
|
||||||
json.addElem("myTiltPos", this->transformPosition(this->myTiltPos));
|
|
||||||
json.addElem("direction", this->direction);
|
|
||||||
json.addElem("shadeType", static_cast<uint8_t>(this->shadeType));
|
|
||||||
json.addElem("bitLength", this->bitLength);
|
|
||||||
json.addElem("proto", static_cast<uint8_t>(this->proto));
|
|
||||||
json.addElem("flags", this->flags);
|
|
||||||
json.addElem("flipCommands", this->flipCommands);
|
|
||||||
json.addElem("flipPosition", this->flipPosition);
|
|
||||||
json.addElem("inGroup", this->isInGroup());
|
|
||||||
json.addElem("sunSensor", this->hasSunSensor());
|
|
||||||
json.addElem("light", this->hasLight());
|
|
||||||
json.addElem("repeats", this->repeats);
|
|
||||||
json.addElem("sortOrder", this->sortOrder);
|
|
||||||
json.addElem("gpioUp", this->gpioUp);
|
|
||||||
json.addElem("gpioDown", this->gpioDown);
|
|
||||||
json.addElem("gpioMy", this->gpioMy);
|
|
||||||
json.addElem("gpioLLTrigger", ((this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0) ? false : true);
|
|
||||||
json.addElem("simMy", this->simMy());
|
|
||||||
json.beginArray("linkedRemotes");
|
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
|
||||||
SomfyLinkedRemote &lremote = this->linkedRemotes[i];
|
|
||||||
if(lremote.getRemoteAddress() != 0) {
|
|
||||||
json.beginObject();
|
|
||||||
lremote.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
json.endArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bool SomfyShade::toJSON(JsonObject &obj) {
|
bool SomfyShade::toJSON(JsonObject &obj) {
|
||||||
//Serial.print("Serializing Shade:");
|
//Serial.print("Serializing Shade:");
|
||||||
|
|
@ -3442,12 +3394,6 @@ bool SomfyRoom::toJSON(JsonObject &obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
void SomfyRoom::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("roomId", this->roomId);
|
|
||||||
json.addElem("name", this->name);
|
|
||||||
json.addElem("sortOrder", this->sortOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SomfyGroup::fromJSON(JsonObject &obj) {
|
bool SomfyGroup::fromJSON(JsonObject &obj) {
|
||||||
if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
|
if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
|
||||||
if(obj.containsKey("roomId")) this->roomId = obj["roomId"];
|
if(obj.containsKey("roomId")) this->roomId = obj["roomId"];
|
||||||
|
|
@ -3469,50 +3415,6 @@ bool SomfyGroup::fromJSON(JsonObject &obj) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void SomfyGroup::toJSON(JsonResponse &json) {
|
|
||||||
this->updateFlags();
|
|
||||||
json.addElem("groupId", this->getGroupId());
|
|
||||||
json.addElem("roomId", this->roomId);
|
|
||||||
json.addElem("name", this->name);
|
|
||||||
json.addElem("remoteAddress", (uint32_t)this->m_remoteAddress);
|
|
||||||
json.addElem("lastRollingCode", (uint32_t)this->lastRollingCode);
|
|
||||||
json.addElem("bitLength", this->bitLength);
|
|
||||||
json.addElem("proto", static_cast<uint8_t>(this->proto));
|
|
||||||
json.addElem("sunSensor", this->hasSunSensor());
|
|
||||||
json.addElem("flipCommands", this->flipCommands);
|
|
||||||
json.addElem("flags", this->flags);
|
|
||||||
json.addElem("repeats", this->repeats);
|
|
||||||
json.addElem("sortOrder", this->sortOrder);
|
|
||||||
json.beginArray("linkedShades");
|
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
|
|
||||||
uint8_t shadeId = this->linkedShades[i];
|
|
||||||
if(shadeId > 0 && shadeId < 255) {
|
|
||||||
SomfyShade *shade = somfy.getShadeById(shadeId);
|
|
||||||
if(shade) {
|
|
||||||
json.beginObject();
|
|
||||||
shade->toJSONRef(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
json.endArray();
|
|
||||||
}
|
|
||||||
void SomfyGroup::toJSONRef(JsonResponse &json) {
|
|
||||||
this->updateFlags();
|
|
||||||
json.addElem("groupId", this->getGroupId());
|
|
||||||
json.addElem("roomId", this->roomId);
|
|
||||||
json.addElem("name", this->name);
|
|
||||||
json.addElem("remoteAddress", (uint32_t)this->m_remoteAddress);
|
|
||||||
json.addElem("lastRollingCode", (uint32_t)this->lastRollingCode);
|
|
||||||
json.addElem("bitLength", this->bitLength);
|
|
||||||
json.addElem("proto", static_cast<uint8_t>(this->proto));
|
|
||||||
json.addElem("sunSensor", this->hasSunSensor());
|
|
||||||
json.addElem("flipCommands", this->flipCommands);
|
|
||||||
json.addElem("flags", this->flags);
|
|
||||||
json.addElem("repeats", this->repeats);
|
|
||||||
json.addElem("sortOrder", this->sortOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bool SomfyGroup::toJSON(JsonObject &obj) {
|
bool SomfyGroup::toJSON(JsonObject &obj) {
|
||||||
this->updateFlags();
|
this->updateFlags();
|
||||||
|
|
@ -3544,10 +3446,6 @@ bool SomfyGroup::toJSON(JsonObject &obj) {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void SomfyRemote::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("remoteAddress", (uint32_t)this->getRemoteAddress());
|
|
||||||
json.addElem("lastRollingCode", (uint32_t)this->lastRollingCode);
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
bool SomfyRemote::toJSON(JsonObject &obj) {
|
bool SomfyRemote::toJSON(JsonObject &obj) {
|
||||||
//obj["remotePrefId"] = this->getRemotePrefId();
|
//obj["remotePrefId"] = this->getRemotePrefId();
|
||||||
|
|
@ -3571,6 +3469,7 @@ void SomfyShadeController::emitState(uint8_t num) {
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
SomfyShade *shade = &this->shades[i];
|
SomfyShade *shade = &this->shades[i];
|
||||||
if(shade->getShadeId() == 255) continue;
|
if(shade->getShadeId() == 255) continue;
|
||||||
|
esp_task_wdt_reset();
|
||||||
shade->emitState(num);
|
shade->emitState(num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4111,26 +4010,6 @@ uint16_t SomfyRemote::setRollingCode(uint16_t code) {
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
void SomfyShadeController::toJSONRooms(JsonResponse &json) {
|
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
|
||||||
SomfyRoom *room = &this->rooms[i];
|
|
||||||
if(room->roomId != 0) {
|
|
||||||
json.beginObject();
|
|
||||||
room->toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void SomfyShadeController::toJSONShades(JsonResponse &json) {
|
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
|
||||||
SomfyShade &shade = this->shades[i];
|
|
||||||
if(shade.getShadeId() != 255) {
|
|
||||||
json.beginObject();
|
|
||||||
shade.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) {
|
bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) {
|
||||||
|
|
@ -4185,21 +4064,6 @@ bool SomfyShadeController::toJSONGroups(JsonArray &arr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
void SomfyShadeController::toJSONGroups(JsonResponse &json) {
|
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
|
|
||||||
SomfyGroup &group = this->groups[i];
|
|
||||||
if(group.getGroupId() != 255) {
|
|
||||||
json.beginObject();
|
|
||||||
group.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void SomfyShadeController::toJSONRepeaters(JsonResponse &json) {
|
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
|
||||||
if(somfy.repeaters[i] != 0) json.addElem((uint32_t)somfy.repeaters[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void SomfyShadeController::loop() {
|
void SomfyShadeController::loop() {
|
||||||
this->transceiver.loop();
|
this->transceiver.loop();
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
|
|
@ -4683,11 +4547,6 @@ void Transceiver::disableReceive(void) {
|
||||||
interruptPin = 0;
|
interruptPin = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
void Transceiver::toJSON(JsonResponse& json) {
|
|
||||||
json.beginObject("config");
|
|
||||||
this->config.toJSON(json);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
bool Transceiver::toJSON(JsonObject& obj) {
|
bool Transceiver::toJSON(JsonObject& obj) {
|
||||||
//Serial.println("Setting Transceiver Json");
|
//Serial.println("Setting Transceiver Json");
|
||||||
|
|
@ -4766,22 +4625,6 @@ void transceiver_config_t::fromJSON(JsonObject& obj) {
|
||||||
*/
|
*/
|
||||||
Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
|
Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
|
||||||
}
|
}
|
||||||
void transceiver_config_t::toJSON(JsonResponse &json) {
|
|
||||||
json.addElem("type", this->type);
|
|
||||||
json.addElem("TXPin", this->TXPin);
|
|
||||||
json.addElem("RXPin", this->RXPin);
|
|
||||||
json.addElem("SCKPin", this->SCKPin);
|
|
||||||
json.addElem("MOSIPin", this->MOSIPin);
|
|
||||||
json.addElem("MISOPin", this->MISOPin);
|
|
||||||
json.addElem("CSNPin", this->CSNPin);
|
|
||||||
json.addElem("rxBandwidth", this->rxBandwidth); // float
|
|
||||||
json.addElem("frequency", this->frequency); // float
|
|
||||||
json.addElem("deviation", this->deviation); // float
|
|
||||||
json.addElem("txPower", this->txPower);
|
|
||||||
json.addElem("proto", static_cast<uint8_t>(this->proto));
|
|
||||||
json.addElem("enabled", this->enabled);
|
|
||||||
json.addElem("radioInit", this->radioInit);
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
void transceiver_config_t::toJSON(JsonObject& obj) {
|
void transceiver_config_t::toJSON(JsonObject& obj) {
|
||||||
obj["type"] = this->type;
|
obj["type"] = this->type;
|
||||||
|
|
@ -30,10 +30,10 @@ enum class radio_proto : byte { // Ordinal byte 0-255
|
||||||
};
|
};
|
||||||
enum class somfy_commands : byte {
|
enum class somfy_commands : byte {
|
||||||
Unknown0 = 0x0,
|
Unknown0 = 0x0,
|
||||||
My = 0x1,
|
My = 0x2,//DOWN
|
||||||
Up = 0x2,
|
Up = 0x1,
|
||||||
MyUp = 0x3,
|
MyUp = 0x4, //up
|
||||||
Down = 0x4,
|
Down = 0x3,
|
||||||
MyDown = 0x5,
|
MyDown = 0x5,
|
||||||
UpDown = 0x6,
|
UpDown = 0x6,
|
||||||
MyUpDown = 0x7,
|
MyUpDown = 0x7,
|
||||||
|
|
@ -209,7 +209,6 @@ class SomfyRoom {
|
||||||
void clear();
|
void clear();
|
||||||
bool save();
|
bool save();
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
void emitState(const char *evt = "roomState");
|
void emitState(const char *evt = "roomState");
|
||||||
void emitState(uint8_t num, const char *evt = "roomState");
|
void emitState(uint8_t num, const char *evt = "roomState");
|
||||||
void publish();
|
void publish();
|
||||||
|
|
@ -239,7 +238,6 @@ class SomfyRemote {
|
||||||
uint8_t repeats = 1;
|
uint8_t repeats = 1;
|
||||||
virtual bool isLastCommand(somfy_commands cmd);
|
virtual bool isLastCommand(somfy_commands cmd);
|
||||||
char *getRemotePrefId() {return m_remotePrefId;}
|
char *getRemotePrefId() {return m_remotePrefId;}
|
||||||
virtual void toJSON(JsonResponse &json);
|
|
||||||
virtual void setRemoteAddress(uint32_t address);
|
virtual void setRemoteAddress(uint32_t address);
|
||||||
virtual uint32_t getRemoteAddress();
|
virtual uint32_t getRemoteAddress();
|
||||||
virtual uint16_t getNextRollingCode();
|
virtual uint16_t getNextRollingCode();
|
||||||
|
|
@ -304,9 +302,7 @@ class SomfyShade : public SomfyRemote {
|
||||||
SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES];
|
SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES];
|
||||||
bool paired = false;
|
bool paired = false;
|
||||||
int8_t validateJSON(JsonObject &obj);
|
int8_t validateJSON(JsonObject &obj);
|
||||||
void toJSONRef(JsonResponse &json);
|
|
||||||
int8_t fromJSON(JsonObject &obj);
|
int8_t fromJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json) override;
|
|
||||||
|
|
||||||
char name[21] = "";
|
char name[21] = "";
|
||||||
void setShadeId(uint8_t id) { shadeId = id; }
|
void setShadeId(uint8_t id) { shadeId = id; }
|
||||||
|
|
@ -393,9 +389,6 @@ class SomfyGroup : public SomfyRemote {
|
||||||
void clear();
|
void clear();
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
//bool toJSON(JsonObject &obj);
|
//bool toJSON(JsonObject &obj);
|
||||||
void toJSON(JsonResponse &json);
|
|
||||||
void toJSONRef(JsonResponse &json);
|
|
||||||
|
|
||||||
bool linkShade(uint8_t shadeId);
|
bool linkShade(uint8_t shadeId);
|
||||||
bool unlinkShade(uint8_t shadeId);
|
bool unlinkShade(uint8_t shadeId);
|
||||||
bool hasShadeId(uint8_t shadeId);
|
bool hasShadeId(uint8_t shadeId);
|
||||||
|
|
@ -485,7 +478,6 @@ struct transceiver_config_t {
|
||||||
*/
|
*/
|
||||||
void fromJSON(JsonObject& obj);
|
void fromJSON(JsonObject& obj);
|
||||||
//void toJSON(JsonObject& obj);
|
//void toJSON(JsonObject& obj);
|
||||||
void toJSON(JsonResponse& json);
|
|
||||||
void save();
|
void save();
|
||||||
void load();
|
void load();
|
||||||
void apply();
|
void apply();
|
||||||
|
|
@ -500,7 +492,6 @@ class Transceiver {
|
||||||
transceiver_config_t config;
|
transceiver_config_t config;
|
||||||
bool printBuffer = false;
|
bool printBuffer = false;
|
||||||
//bool toJSON(JsonObject& obj);
|
//bool toJSON(JsonObject& obj);
|
||||||
void toJSON(JsonResponse& json);
|
|
||||||
bool fromJSON(JsonObject& obj);
|
bool fromJSON(JsonObject& obj);
|
||||||
bool save();
|
bool save();
|
||||||
bool begin();
|
bool begin();
|
||||||
|
|
@ -557,10 +548,6 @@ class SomfyShadeController {
|
||||||
SomfyGroup groups[SOMFY_MAX_GROUPS];
|
SomfyGroup groups[SOMFY_MAX_GROUPS];
|
||||||
bool linkRepeater(uint32_t address);
|
bool linkRepeater(uint32_t address);
|
||||||
bool unlinkRepeater(uint32_t address);
|
bool unlinkRepeater(uint32_t address);
|
||||||
void toJSONShades(JsonResponse &json);
|
|
||||||
void toJSONRooms(JsonResponse &json);
|
|
||||||
void toJSONGroups(JsonResponse &json);
|
|
||||||
void toJSONRepeaters(JsonResponse &json);
|
|
||||||
uint8_t repeaterCount();
|
uint8_t repeaterCount();
|
||||||
uint8_t roomCount();
|
uint8_t roomCount();
|
||||||
uint8_t shadeCount();
|
uint8_t shadeCount();
|
||||||
|
|
@ -578,6 +565,7 @@ class SomfyShadeController {
|
||||||
void processWaitingFrame();
|
void processWaitingFrame();
|
||||||
void commit();
|
void commit();
|
||||||
void writeBackup();
|
void writeBackup();
|
||||||
|
String backupData;
|
||||||
bool loadShadesFile(const char *filename);
|
bool loadShadesFile(const char *filename);
|
||||||
#ifdef USE_NVS
|
#ifdef USE_NVS
|
||||||
bool loadLegacy();
|
bool loadLegacy();
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
|
||||||
|
#include "esp_log.h"
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
#include <esp_task_wdt.h>
|
#include <esp_task_wdt.h>
|
||||||
|
|
@ -9,6 +11,7 @@
|
||||||
#include "Somfy.h"
|
#include "Somfy.h"
|
||||||
#include "MQTT.h"
|
#include "MQTT.h"
|
||||||
#include "GitOTA.h"
|
#include "GitOTA.h"
|
||||||
|
#include "esp_core_dump.h"
|
||||||
|
|
||||||
ConfigSettings settings;
|
ConfigSettings settings;
|
||||||
Web webServer;
|
Web webServer;
|
||||||
|
|
@ -20,11 +23,55 @@ MQTTClass mqtt;
|
||||||
GitUpdater git;
|
GitUpdater git;
|
||||||
|
|
||||||
uint32_t oldheap = 0;
|
uint32_t oldheap = 0;
|
||||||
void setup() {
|
|
||||||
|
void listDir(const char *dirname, uint8_t levels) {
|
||||||
|
Serial.printf("Listing: %s\n", dirname);
|
||||||
|
File root = LittleFS.open(dirname);
|
||||||
|
if (!root || !root.isDirectory()) {
|
||||||
|
Serial.println("Failed to open directory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File file = root.openNextFile();
|
||||||
|
while (file) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
Serial.printf(" DIR : %s\n", file.name());
|
||||||
|
if (levels) listDir(file.path(), levels - 1);
|
||||||
|
} else {
|
||||||
|
Serial.printf(" FILE: %-30s %d bytes\n", file.name(), file.size());
|
||||||
|
}
|
||||||
|
file = root.openNextFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println("Startup/Boot....");
|
Serial.println("Startup/Boot....");
|
||||||
|
esp_core_dump_summary_t summary;
|
||||||
|
if (esp_core_dump_get_summary(&summary) == ESP_OK) {
|
||||||
|
Serial.println("*** Previous crash coredump found ***");
|
||||||
|
Serial.printf(" Task: %s\n", summary.exc_task);
|
||||||
|
Serial.printf(" PC: 0x%08x\n", summary.exc_pc);
|
||||||
|
Serial.printf(" Cause: %d\n", summary.ex_info.exc_cause);
|
||||||
|
Serial.printf(" Backtrace:");
|
||||||
|
for (int i = 0; i < summary.exc_bt_info.depth; i++) {
|
||||||
|
Serial.printf(" 0x%08x", summary.exc_bt_info.bt[i]);
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
Serial.println("Mounting File System...");
|
Serial.println("Mounting File System...");
|
||||||
|
|
||||||
|
|
||||||
|
if (LittleFS.begin()) {
|
||||||
|
Serial.printf("\nTotal: %d bytes\n", LittleFS.totalBytes());
|
||||||
|
Serial.printf("Used: %d bytes\n", LittleFS.usedBytes());
|
||||||
|
Serial.printf("Free: %d bytes\n", LittleFS.totalBytes() - LittleFS.usedBytes());
|
||||||
|
Serial.println();
|
||||||
|
listDir("/", 3);
|
||||||
|
} else {
|
||||||
|
Serial.println("LittleFS mount failed!");
|
||||||
|
}
|
||||||
|
|
||||||
if(LittleFS.begin()) Serial.println("File system mounted successfully");
|
if(LittleFS.begin()) Serial.println("File system mounted successfully");
|
||||||
else Serial.println("Error mounting file system");
|
else Serial.println("Error mounting file system");
|
||||||
settings.begin();
|
settings.begin();
|
||||||
|
|
@ -37,7 +84,7 @@ void setup() {
|
||||||
net.setup();
|
net.setup();
|
||||||
somfy.begin();
|
somfy.begin();
|
||||||
//git.checkForUpdate();
|
//git.checkForUpdate();
|
||||||
esp_task_wdt_init(7, true); //enable panic so ESP32 restarts
|
esp_task_wdt_init(15, true); //enable panic so ESP32 restarts
|
||||||
esp_task_wdt_add(NULL); //add current thread to WDT watch
|
esp_task_wdt_add(NULL); //add current thread to WDT watch
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#include "WResp.h"
|
#include "WResp.h"
|
||||||
void JsonSockEvent::beginEvent(WebSocketsServer *server, const char *evt, char *buff, size_t buffSize) {
|
void JsonSockEvent::beginEvent(AsyncWebSocket *server, const char *evt, char *buff, size_t buffSize) {
|
||||||
this->server = server;
|
this->server = server;
|
||||||
this->buff = buff;
|
this->buff = buff;
|
||||||
this->buffSize = buffSize;
|
this->buffSize = buffSize;
|
||||||
|
|
@ -15,10 +15,10 @@ void JsonSockEvent::closeEvent() {
|
||||||
this->_nocomma = true;
|
this->_nocomma = true;
|
||||||
this->_closed = true;
|
this->_closed = true;
|
||||||
}
|
}
|
||||||
void JsonSockEvent::endEvent(uint8_t num) {
|
void JsonSockEvent::endEvent(uint32_t clientId) {
|
||||||
this->closeEvent();
|
this->closeEvent();
|
||||||
if(num == 255) this->server->broadcastTXT(this->buff);
|
if(clientId == 0) this->server->textAll(this->buff);
|
||||||
else this->server->sendTXT(num, this->buff);
|
else this->server->text(clientId, this->buff);
|
||||||
}
|
}
|
||||||
void JsonSockEvent::_safecat(const char *val, bool escape) {
|
void JsonSockEvent::_safecat(const char *val, bool escape) {
|
||||||
size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
|
size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
|
||||||
|
|
@ -33,30 +33,32 @@ void JsonSockEvent::_safecat(const char *val, bool escape) {
|
||||||
else strcat(this->buff, val);
|
else strcat(this->buff, val);
|
||||||
if(escape) strcat(this->buff, "\"");
|
if(escape) strcat(this->buff, "\"");
|
||||||
}
|
}
|
||||||
void JsonResponse::beginResponse(WebServer *server, char *buff, size_t buffSize) {
|
void AsyncJsonResp::beginResponse(AsyncWebServerRequest *request, char *buff, size_t buffSize) {
|
||||||
this->server = server;
|
this->_request = request;
|
||||||
this->buff = buff;
|
this->buff = buff;
|
||||||
this->buffSize = buffSize;
|
this->buffSize = buffSize;
|
||||||
this->buff[0] = 0x00;
|
this->buff[0] = 0x00;
|
||||||
this->_nocomma = true;
|
this->_nocomma = true;
|
||||||
server->setContentLength(CONTENT_LENGTH_UNKNOWN);
|
this->_headersSent = false;
|
||||||
|
this->_stream = request->beginResponseStream("application/json");
|
||||||
}
|
}
|
||||||
void JsonResponse::endResponse() {
|
void AsyncJsonResp::endResponse() {
|
||||||
if(strlen(buff)) this->send();
|
if(strlen(this->buff)) this->flush();
|
||||||
server->sendContent("", 0);
|
if(this->_request && this->_stream) {
|
||||||
|
this->_request->send(this->_stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void JsonResponse::send() {
|
void AsyncJsonResp::flush() {
|
||||||
if(!this->_headersSent) server->send_P(200, "application/json", this->buff);
|
if(this->_stream && strlen(this->buff) > 0) {
|
||||||
else server->sendContent(this->buff);
|
this->_stream->print(this->buff);
|
||||||
//Serial.printf("Sent %d bytes %d\n", strlen(this->buff), this->buffSize);
|
|
||||||
this->buff[0] = 0x00;
|
this->buff[0] = 0x00;
|
||||||
this->_headersSent = true;
|
}
|
||||||
}
|
}
|
||||||
void JsonResponse::_safecat(const char *val, bool escape) {
|
void AsyncJsonResp::_safecat(const char *val, bool escape) {
|
||||||
size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
|
size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
|
||||||
if(escape) len += 2;
|
if(escape) len += 2;
|
||||||
if(len >= this->buffSize) {
|
if(len >= this->buffSize) {
|
||||||
this->send();
|
this->flush();
|
||||||
}
|
}
|
||||||
if(escape) strcat(this->buff, "\"");
|
if(escape) strcat(this->buff, "\"");
|
||||||
if(escape) this->escapeString(val, &this->buff[strlen(this->buff)]);
|
if(escape) this->escapeString(val, &this->buff[strlen(this->buff)]);
|
||||||
|
|
@ -130,8 +132,9 @@ void JsonFormatter::addElem(const char *name, uint32_t nval) { sprintf(this->_nu
|
||||||
void JsonFormatter::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); }
|
void JsonFormatter::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); }
|
||||||
void JsonFormatter::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); }
|
void JsonFormatter::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); }
|
||||||
void JsonFormatter::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); }
|
void JsonFormatter::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); }
|
||||||
void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); }
|
|
||||||
*/
|
*/
|
||||||
|
void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); }
|
||||||
|
|
||||||
void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); }
|
void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); }
|
||||||
|
|
||||||
void JsonFormatter::_safecat(const char *val, bool escape) {
|
void JsonFormatter::_safecat(const char *val, bool escape) {
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
#include <WebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <WebSocketsServer.h>
|
|
||||||
#include "Somfy.h"
|
#include "Somfy.h"
|
||||||
#ifndef wresp_h
|
#ifndef wresp_h
|
||||||
#define wresp_h
|
#define wresp_h
|
||||||
|
|
@ -51,24 +50,26 @@ class JsonFormatter {
|
||||||
void addElem(const char* name, uint32_t lval);
|
void addElem(const char* name, uint32_t lval);
|
||||||
void addElem(const char* name, bool bval);
|
void addElem(const char* name, bool bval);
|
||||||
void addElem(const char *name, const char *val);
|
void addElem(const char *name, const char *val);
|
||||||
|
void addElem(const char* name, uint64_t lval);
|
||||||
};
|
};
|
||||||
class JsonResponse : public JsonFormatter {
|
class AsyncJsonResp : public JsonFormatter {
|
||||||
protected:
|
protected:
|
||||||
void _safecat(const char *val, bool escape = false) override;
|
void _safecat(const char *val, bool escape = false) override;
|
||||||
|
AsyncWebServerRequest *_request = nullptr;
|
||||||
|
AsyncResponseStream *_stream = nullptr;
|
||||||
public:
|
public:
|
||||||
WebServer *server;
|
void beginResponse(AsyncWebServerRequest *request, char *buff, size_t buffSize);
|
||||||
void beginResponse(WebServer *server, char *buff, size_t buffSize);
|
|
||||||
void endResponse();
|
void endResponse();
|
||||||
void send();
|
void flush();
|
||||||
};
|
};
|
||||||
class JsonSockEvent : public JsonFormatter {
|
class JsonSockEvent : public JsonFormatter {
|
||||||
protected:
|
protected:
|
||||||
bool _closed = false;
|
bool _closed = false;
|
||||||
void _safecat(const char *val, bool escape = false) override;
|
void _safecat(const char *val, bool escape = false) override;
|
||||||
public:
|
public:
|
||||||
WebSocketsServer *server = nullptr;
|
AsyncWebSocket *server = nullptr;
|
||||||
void beginEvent(WebSocketsServer *server, const char *evt, char *buff, size_t buffSize);
|
void beginEvent(AsyncWebSocket *server, const char *evt, char *buff, size_t buffSize);
|
||||||
void endEvent(uint8_t clientNum = 255);
|
void endEvent(uint32_t clientId = 0);
|
||||||
void closeEvent();
|
void closeEvent();
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
2306
src/Web.cpp
Normal file
40
src/Web.h
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <AsyncJson.h>
|
||||||
|
#include "Somfy.h"
|
||||||
|
#ifndef webserver_h
|
||||||
|
#define webserver_h
|
||||||
|
class Web {
|
||||||
|
public:
|
||||||
|
bool uploadSuccess = false;
|
||||||
|
void startup();
|
||||||
|
void begin();
|
||||||
|
void loop();
|
||||||
|
// Auth helpers
|
||||||
|
bool createAPIToken(const IPAddress ipAddress, char *token);
|
||||||
|
bool createAPIToken(const char *payload, char *token);
|
||||||
|
bool createAPIPinToken(const IPAddress ipAddress, const char *pin, char *token);
|
||||||
|
bool createAPIPasswordToken(const IPAddress ipAddress, const char *username, const char *password, char *token);
|
||||||
|
bool isAuthenticated(AsyncWebServerRequest *request, bool cfg = false);
|
||||||
|
|
||||||
|
// Async API handlers
|
||||||
|
void handleDiscovery(AsyncWebServerRequest *request);
|
||||||
|
void handleGetRooms(AsyncWebServerRequest *request);
|
||||||
|
void handleGetShades(AsyncWebServerRequest *request);
|
||||||
|
void handleGetGroups(AsyncWebServerRequest *request);
|
||||||
|
void handleController(AsyncWebServerRequest *request);
|
||||||
|
void handleRoom(AsyncWebServerRequest *request);
|
||||||
|
void handleShade(AsyncWebServerRequest *request);
|
||||||
|
void handleGroup(AsyncWebServerRequest *request);
|
||||||
|
void handleLogin(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
void handleShadeCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
void handleGroupCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
void handleTiltCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
void handleRepeatCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
void handleSetPositions(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
void handleSetSensor(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
void handleDownloadFirmware(AsyncWebServerRequest *request);
|
||||||
|
void handleBackup(AsyncWebServerRequest *request);
|
||||||
|
void handleReboot(AsyncWebServerRequest *request);
|
||||||
|
void handleNotFound(AsyncWebServerRequest *request);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
11
test/README
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||||