├── tools ├── build │ ├── __init__.py │ ├── func.py │ ├── pre_build.py │ ├── version_update.py │ ├── build.py │ └── merge_bin_esp.py ├── webfilesbuilder │ ├── .gitignore │ ├── package.json │ ├── gulpfile.js │ ├── build_html.py │ └── gulpfile_old.js ├── export_compile_commands.py ├── clean_file.sh └── commit.sh ├── src ├── websrc │ ├── size.fs │ ├── .gitignore │ ├── img │ │ └── logo.svg │ ├── html │ │ ├── login.html │ │ ├── security.html │ │ ├── mqtt.html │ │ ├── about.html │ │ ├── vpn.html │ │ ├── general.html │ │ ├── loader.html │ │ ├── zigbee.html │ │ └── network.html │ ├── js │ │ └── i18nextHttpBackend.min.js │ └── json │ │ └── zh.json ├── log.h ├── version.h ├── per.h ├── CMakeLists.txt ├── zb.h ├── main.h ├── log.cpp ├── mqtt.h ├── const │ ├── hw.h │ ├── hw.cpp │ ├── keys.h │ └── keys.cpp ├── web.h ├── etc.h └── per.cpp ├── bin ├── .gitignore ├── partitions.bin └── bootloader_dio_40m.bin ├── lib ├── README.md └── CCTools │ └── library.json ├── CMakeLists.txt ├── .gitmodules ├── .gitignore ├── .vscode └── i18n-ally-custom-framework.yml ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── FUNDING.yml ├── workflows │ ├── test_build_fw.yml │ ├── build_wiki.yml │ ├── merge_comment.yml │ ├── update_devices.yml │ ├── cp_releases.yml │ ├── close_issues.yml │ └── build_fw.yml └── scripts │ └── update_devices.py ├── partitions.csv ├── partitions.default.csv ├── XZG.code-workspace ├── CODE_OF_CONDUCT.md ├── platformio.ini ├── README.md ├── CONTRIBUTING.md └── dependencies.lock /tools/build/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/websrc/size.fs: -------------------------------------------------------------------------------- 1 | 364544 -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | *debug* 2 | XZG*bin -------------------------------------------------------------------------------- /src/websrc/.gitignore: -------------------------------------------------------------------------------- 1 | gzipped/ 2 | */tools/* 3 | min/ -------------------------------------------------------------------------------- /tools/webfilesbuilder/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /bin/partitions.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xyzroe/XZG/main/bin/partitions.bin -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | void logPush(char c); 2 | void logClear(); 3 | String logPrint(); 4 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | git submodule update --init 2 | git submodule update --recursive --remote -------------------------------------------------------------------------------- /bin/bootloader_dio_40m.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xyzroe/XZG/main/bin/bootloader_dio_40m.bin -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | // AUTO GENERATED FILE 2 | #ifndef VERSION 3 | #define VERSION "20250310" 4 | #endif 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | project(XZG) 4 | 5 | -------------------------------------------------------------------------------- /src/per.h: -------------------------------------------------------------------------------- 1 | void handleLongBtn(); 2 | void toggleUsbMode(); 3 | void buttonInit(); 4 | void buttonSetup(); 5 | void buttonLoop(); 6 | bool debounce(); 7 | void btnInterrupt(); 8 | void ledModeSetup(); 9 | void ledPwrSetup(); -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file was automatically generated for projects 2 | # without default 'CMakeLists.txt' file. 3 | 4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) 5 | 6 | idf_component_register(SRCS ${app_sources}) 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/WireGuard"] 2 | path = lib/WireGuard 3 | url = https://github.com/Tinkerforge/WireGuard-ESP32-Arduino 4 | branch = main 5 | [submodule "lib/ESPAsyncTCP"] 6 | path = lib/ESPAsyncTCP 7 | url = https://github.com/me-no-dev/ESPAsyncTCP 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | .pio 3 | .vscode 4 | commit* 5 | test.html 6 | gzipped 7 | webh 8 | *.code-workspace 9 | logs 10 | todo* 11 | !lib 12 | !platformio.ini 13 | **/__pycache__ 14 | tools/.no_web_update* 15 | managed_components 16 | old? 17 | data/* 18 | /tools/webfilesbuilder/.npm_install_marker* -------------------------------------------------------------------------------- /tools/export_compile_commands.py: -------------------------------------------------------------------------------- 1 | import os 2 | Import("env") 3 | 4 | # include toolchain paths 5 | env.Replace(COMPILATIONDB_INCLUDE_TOOLCHAIN=True) 6 | 7 | # override compilation DB path 8 | # use this to write compile commands into build dir, but 9 | # I personally just want it in the base dir 10 | #env.Replace(COMPILATIONDB_PATH=os.path.join("$SRC_DIR", "compile_commands.json")) 11 | -------------------------------------------------------------------------------- /.vscode/i18n-ally-custom-framework.yml: -------------------------------------------------------------------------------- 1 | # .vscode/i18n-ally-custom-framework.yml 2 | 3 | languageIds: 4 | - javascript 5 | - html 6 | 7 | usageMatchRegex: 8 | - "[^\\w\\d]t\\(['\"`]({key})['\"`]" 9 | - "['\";]({key})\\[placeholder\\]" 10 | - "['\";]({key})\\[title\\]" 11 | - "data-i18n=['\"]([^'\"]*?)['\"\\[]" 12 | 13 | #scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]" 14 | 15 | monopoly: true -------------------------------------------------------------------------------- /src/zb.h: -------------------------------------------------------------------------------- 1 | bool zbFwCheck(); 2 | void zbHwCheck(); 3 | bool zbLedToggle(); 4 | bool zigbeeErase(); 5 | void nvPrgs(const String &inputMsg); 6 | void zbEraseNV(void *pvParameters); 7 | void flashZbUrl(String url); 8 | bool eraseWriteZbUrl(const char *url, std::function progressShow, CCTools &CCTool); 9 | //bool eraseWriteZbFile(const char *filePath, std::function progressShow, CCTools &CCTool); 10 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | void mDNS_start(); 5 | 6 | void connectWifi(); 7 | // void handleLongBtn(); 8 | void handleTmrNetworkOverseer(); 9 | void setupCoordinatorMode(); 10 | void startAP(const bool start); 11 | 12 | // void toggleUsbMode(); 13 | 14 | 15 | void stopWifi(); 16 | void checkFileSys(); 17 | 18 | struct TaskParams 19 | { 20 | const char *url; 21 | }; 22 | 23 | 24 | #endif // MAIN_H -------------------------------------------------------------------------------- /src/log.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "log.h" 4 | #include "config.h" 5 | 6 | LogConsoleType logConsole; 7 | 8 | void logPush(char c) 9 | { 10 | logConsole.push(c); 11 | } 12 | 13 | String logPrint() 14 | { 15 | 16 | String buff = ""; 17 | 18 | if (logConsole.isEmpty()) { 19 | return ""; 20 | } else { 21 | for (decltype(logConsole)::index_t i = 0; i < logConsole.size() - 1; i++) { 22 | buff += logConsole[i]; 23 | } 24 | return buff; 25 | } 26 | } 27 | 28 | void logClear() 29 | { 30 | if (!logConsole.isEmpty()) { 31 | logConsole.clear(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /lib/CCTools/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CCTools", 3 | "frameworks": "Arduino", 4 | "keywords": "zigbee, flash, cc2538-bsl, bsl, cc1352, cc2652", 5 | "description": "Work with TI CC(1352/2652) series chips: erase and flash firmware, get chip ID, restart, enter BSL mode, etc.", 6 | "url": "https://github.com/xyzroe/CCTools", 7 | "authors": [ 8 | { 9 | "name": "xyzroe", 10 | "url": "https://xyzroe.cc" 11 | }, 12 | { 13 | "name": "ChatGPT 4", 14 | "url": "https://chat.openai.com" 15 | } 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/xyzroe/CCTools" 20 | }, 21 | "version": "0.0.6" 22 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: xyzroe 2 | buy_me_a_coffee: xyzroe 3 | 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /tools/webfilesbuilder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Combine, minify all css, html and js files into single and gzip them for the XZG project", 3 | "scripts": { 4 | "test": "echo \"Error: no test specified\" && exit 1", 5 | "start": "gulp" 6 | }, 7 | "author": "xyzroe", 8 | "license": "GNU GPL v3.0", 9 | "dependencies": { 10 | "gulp": "^4.0.2", 11 | "gulp-concat": "^2.6.1", 12 | "gulp-cssnano": "^2.1.3", 13 | "gulp-flatmap": "^1.0.2", 14 | "gulp-gzip": "^1.4.2", 15 | "gulp-htmlmin": "^4.0.0", 16 | "gulp-jsonminify": "^1.1.0", 17 | "gulp-purgecss": "^5.0.0", 18 | "gulp-rename": "^2.0.0", 19 | "gulp-uglify": "^3.0.1", 20 | "npm": "^6.14.15", 21 | "pump": "^3.0.0" 22 | }, 23 | "bin": "node_modules\\gulp\\bin\\gulp.js" 24 | } 25 | -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 0x5000, 3 | # Раздел для хранения данных NVS (Non-Volatile Storage), 20 KB 4 | otadata, data, ota, 0xe000, 0x2000, 5 | # Раздел для хранения данных OTA (Over-the-Air) обновлений, 8 KB 6 | app0, app, ota_0, 0x10000, 0x180000, 7 | # Первый раздел для приложения, используемый для OTA обновлений, 1.5 MB (1572864 байт) 8 | app1, app, ota_1, 0x190000,0x180000, 9 | # Второй раздел для приложения, используемый для OTA обновлений, 1.5 MB (1572864 байт) 10 | eeprom, data, 0x99, 0x310000,0x1000, 11 | # Раздел для хранения данных EEPROM, 4 KB 12 | spiffs, data, spiffs, 0x311000,0xAF000, 13 | # Раздел для файловой системы SPIFFS, 704 KB (720896 байт) -------------------------------------------------------------------------------- /partitions.default.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags ; Комментарии 2 | nvs, data, nvs, 0x9000, 0x5000, ; Раздел для хранения данных NVS (Non-Volatile Storage), 20 KB 3 | otadata, data, ota, 0xe000, 0x2000, ; Раздел для хранения данных OTA (Over-the-Air) обновлений, 8 KB 4 | app0, app, ota_0, 0x10000, 0x140000, ; Первый раздел для приложения, используемый для OTA обновлений, 1.25 MB (1310720 байт) 5 | app1, app, ota_1, 0x150000,0x140000, ; Второй раздел для приложения, используемый для OTA обновлений, 1.25 MB (1310720 байт) 6 | spiffs, data, spiffs, 0x290000,0x160000, ; Раздел для файловой системы SPIFFS, 1.5 MB (1441792 байт) 7 | coredump, data, coredump,0x3F0000,0x10000, ; Раздел для хранения дампов ядра, 64 KB (65536 байт) -------------------------------------------------------------------------------- /XZG.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "i18n-ally.keystyle": "nested", 9 | "i18n-ally.localesPaths": [ 10 | "src/websrc/json" 11 | ], 12 | "cSpell.useGitignore": false, 13 | "files.associations": { 14 | "*.tpp": "cpp", 15 | "typeinfo": "cpp", 16 | "*.ipp": "cpp" 17 | }, 18 | "i18n-ally.keysInUse": [ 19 | "p.lo.mlo", 20 | "p.lo.mwc", 21 | "p.lo.mnl", 22 | "p.mq.disc.ph", 23 | "p.mq.disc.n" 24 | ], 25 | }, 26 | "extensions": { 27 | "recommendations": [ 28 | "jock.svg", 29 | "DevEscalus.svg-sprites-viewer", 30 | "streetsidesoftware.code-spell-checker", 31 | "Perkovec.emoji", 32 | "lokalise.i18n-ally", 33 | "platformio.platformio-ide", 34 | "marcochan.get-svg-icons", 35 | "yzhang.markdown-all-in-one", 36 | "ms-vscode.cpptools" 37 | ] 38 | } 39 | } -------------------------------------------------------------------------------- /tools/build/func.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import time 4 | 5 | def print_colored(text, color): 6 | colors = { 7 | "red": "\033[31m", 8 | "green": "\033[32m", 9 | "yellow": "\033[33m", 10 | "blue": "\033[34m", 11 | "magenta": "\033[35m", 12 | "cyan": "\033[36m", 13 | "reset": "\033[0m" 14 | } 15 | print(colors[color] + text + colors["reset"]) 16 | 17 | def print_logo(): 18 | print("") 19 | print_colored("██╗░░██╗███████╗░██████╗░", "red") 20 | print_colored("╚██╗██╔╝╚════██║██╔════╝░", "red") 21 | print_colored("░╚███╔╝░░░███╔═╝██║░░██╗░", "yellow") 22 | print_colored("░██╔██╗░██╔══╝░░██║░░╚██╗", "yellow") 23 | print_colored("██╔╝╚██╗███████╗╚██████╔╝", "green") 24 | print_colored("╚═╝░░╚═╝╚══════╝░╚═════╝░", "green") 25 | print_colored("", "reset") 26 | time.sleep(1) 27 | 28 | 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Device information** 14 | - Screenshot of Zigbee and Device tab from root page of device 15 | - JSON object returned on [http://xzg.local/api?action=1¶m=all](http://xzg.local/api?action=1¶m=all) 16 | *You **should** remove your credentials first* 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/workflows/test_build_fw.yml: -------------------------------------------------------------------------------- 1 | name: Test build firmware 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - 'main' 7 | 8 | jobs: 9 | test_build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Clone repository 13 | uses: actions/checkout@v3 14 | with: 15 | ref: ${{ github.head_ref }} 16 | fetch-depth: 0 17 | submodules: 'recursive' 18 | 19 | - name: Install Node JS 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 18 23 | 24 | - uses: actions/cache@v3 25 | with: 26 | path: | 27 | ~/.cache/pip 28 | ~/.platformio/.cache 29 | key: ${{ runner.os }}-pio 30 | 31 | - name: Install Python 32 | uses: actions/setup-python@v4 33 | with: 34 | python-version: "3.9" 35 | 36 | - name: Install PlatformIO Core 37 | run: pip install --upgrade platformio==6.1.11 38 | 39 | - name: Build PlatformIO Project 40 | run: pio run 41 | -------------------------------------------------------------------------------- /tools/clean_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check for the presence of an argument (path to the Markdown file) 4 | if [ "$#" -ne 1 ]; then 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | # The path to the file, obtained from the argument 10 | FILE_PATH="$1" 11 | 12 | # Check if the file exists 13 | if [ ! -f "$FILE_PATH" ]; then 14 | echo "File not found: $FILE_PATH" 15 | exit 1 16 | fi 17 | 18 | # Temporary file for the processed content 19 | TEMP_FILE=$(mktemp) 20 | 21 | # Step 1: Remove lines that are list items (starting with '-', accounting for spaces/tabs before '-') 22 | # Step 2: Remove all empty lines 23 | # Step 3: Add an empty line after each remaining line 24 | awk '{ 25 | # Step 1: Skip list item lines 26 | if (/^[[:space:]]*-/) next; 27 | # Step 2 is handled implicitly by not printing empty lines here 28 | if (!/^[[:space:]]*$/) { 29 | # Step 3: Print the line and then a newline 30 | print $0 "\n"; 31 | } 32 | }' "$FILE_PATH" > "$TEMP_FILE" 33 | 34 | # Replace the original file with the temporary file 35 | mv "$TEMP_FILE" "$FILE_PATH" 36 | echo "File $FILE_PATH was cleaned" -------------------------------------------------------------------------------- /.github/workflows/build_wiki.yml: -------------------------------------------------------------------------------- 1 | name: Build Wiki 2 | on: 3 | workflow_dispatch: 4 | workflow_run: 5 | workflows: ["Update devices in wiki"] 6 | types: 7 | - completed 8 | push: 9 | branches: 10 | - mkdocs 11 | 12 | permissions: 13 | contents: write 14 | 15 | jobs: 16 | deploy: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | ref: mkdocs 22 | - name: Configure Git Credentials 23 | run: | 24 | git config user.name github-actions[bot] 25 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 26 | - uses: actions/setup-python@v5 27 | with: 28 | python-version: 3.x 29 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 30 | - uses: actions/cache@v4 31 | with: 32 | key: mkdocs-material-${{ env.cache_id }} 33 | path: .cache 34 | restore-keys: | 35 | mkdocs-material- 36 | - run: pip install mkdocs-material 37 | - run: pip install -r requirements.txt 38 | - run: mkdocs gh-deploy --force 39 | -------------------------------------------------------------------------------- /.github/workflows/merge_comment.yml: -------------------------------------------------------------------------------- 1 | name: Add merge comment 2 | 3 | on: 4 | pull_request_target: 5 | types: [closed] 6 | 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | issues: write 11 | 12 | jobs: 13 | add-merge-comment: 14 | runs-on: ubuntu-latest 15 | if: github.event.pull_request.merged == true 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | - name: Generate Comment 21 | id: generate_comment 22 | uses: actions/github-script@v6 23 | with: 24 | github-token: ${{ secrets.GITHUB_TOKEN }} 25 | script: | 26 | const pull_request = context.payload.pull_request; 27 | const author = pull_request.user.login; 28 | const issue_number = pull_request.number; 29 | const body = `Thanks @${author} 🏆`; 30 | console.log(`Generated comment: ${body}`); 31 | core.setOutput('body', body); 32 | 33 | - name: Comment PR 34 | uses: thollander/actions-comment-pull-request@v2 35 | with: 36 | message: ${{ steps.generate_comment.outputs.body }} 37 | reactions: rocket 38 | -------------------------------------------------------------------------------- /src/mqtt.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | void mqttConnectSetup(); 5 | void mqttDisconnectCleanup(); 6 | void connectToMqtt(); 7 | void onMqttConnect(bool sessionPresent); 8 | void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); 9 | void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total); 10 | void executeCommand(const char *command); 11 | void mqttPublishAvail(); 12 | void mqttPublishIo(const String &io, bool st); 13 | void mqttPublishUpdate(const String &chip, bool state = false); 14 | void mqttPublishState(); 15 | void mqttPublishDiscovery(); 16 | 17 | typedef String (*SensorValueFunction)(); 18 | 19 | struct mqttTopicsConfig { 20 | String name; 21 | String sensorType; 22 | String sensorId; 23 | String stateTopic; 24 | String commandTopic; 25 | String icon; 26 | String payloadPress; 27 | String valueTemplate; 28 | String deviceClass; 29 | String unitOfMeasurement; 30 | String entityCategory; 31 | String entityPicture; 32 | String payloadInstall; 33 | String releaseUrl; 34 | String jsonAttrTemplate; 35 | String jsonAttrTopic; 36 | SensorValueFunction getSensorValue; 37 | }; -------------------------------------------------------------------------------- /src/const/hw.h: -------------------------------------------------------------------------------- 1 | #ifndef HW_H 2 | #define HW_H 3 | 4 | #include 5 | 6 | // Ethernet settings structure 7 | struct EthConfig 8 | { 9 | int addr; 10 | int pwrPin; 11 | int mdcPin; 12 | int mdiPin; 13 | eth_phy_type_t phyType; 14 | eth_clock_mode_t clkMode; 15 | //int pwrAltPin; 16 | }; 17 | 18 | // ZigBee settings structure 19 | struct ZbConfig 20 | { 21 | int txPin; 22 | int rxPin; 23 | int rstPin; 24 | int bslPin; 25 | }; 26 | 27 | // Miscellaneous settings structure 28 | struct MistConfig 29 | { 30 | int btnPin; 31 | int btnPlr; 32 | int uartSelPin; 33 | int uartSelPlr; 34 | int ledModePin; 35 | int ledModePlr; 36 | int ledPwrPin; 37 | int ledPwrPlr; 38 | }; 39 | 40 | // Root configuration structure that includes only configuration indices 41 | struct BrdConfigStruct 42 | { 43 | char board[50]; 44 | int ethConfigIndex; 45 | int zbConfigIndex; 46 | int mistConfigIndex; 47 | }; 48 | 49 | #define ETH_CFG_CNT 3 50 | #define ZB_CFG_CNT 8 51 | #define MIST_CFG_CNT 5 52 | #define BOARD_CFG_CNT 14 53 | 54 | struct ThisConfigStruct 55 | { 56 | char board[50]; 57 | EthConfig eth; 58 | ZbConfig zb; 59 | MistConfig mist; 60 | }; 61 | 62 | #endif // HW_H -------------------------------------------------------------------------------- /.github/workflows/update_devices.yml: -------------------------------------------------------------------------------- 1 | name: Update devices in wiki 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: 10 | - main 11 | paths: 12 | - src/const/hw.cpp 13 | 14 | jobs: 15 | update-devices: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout main branch 20 | uses: actions/checkout@v2 21 | with: 22 | ref: main 23 | path: main_branch 24 | 25 | - name: Checkout mkdocs branch 26 | uses: actions/checkout@v2 27 | with: 28 | ref: mkdocs 29 | path: mkdocs_branch 30 | 31 | - name: Set up Python 32 | uses: actions/setup-python@v2 33 | with: 34 | python-version: '3.x' 35 | 36 | - name: Install dependencies 37 | run: | 38 | python -m pip install --upgrade pip 39 | 40 | - name: Run update_devices.py 41 | run: python main_branch/.github/scripts/update_devices.py 42 | 43 | - name: Commit and push changes 44 | run: | 45 | cd mkdocs_branch 46 | git config --local user.email "action@github.com" 47 | git config --local user.name "GitHub Action" 48 | if [[ -n $(git status --porcelain) ]]; then 49 | git add docs/features.md 50 | git commit -m 'Update features.md with new device data' 51 | git push origin mkdocs 52 | else 53 | echo "No changes to commit" 54 | fi 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /tools/build/pre_build.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import re 4 | 5 | Import("env") 6 | 7 | def extract_constant_declarations(cpp_content): 8 | pattern = r'const\s+char\s*\*\s*(\w+)\s*=\s*"[^"]*";' 9 | matches = re.finditer(pattern, cpp_content) 10 | declarations = [f"extern const char *{match.group(1)};" for match in matches] 11 | return declarations 12 | 13 | def create_header_file(cpp_filename, h_filename): 14 | with open(cpp_filename, 'r', encoding='utf-8') as file: 15 | cpp_content = file.read() 16 | 17 | declarations = extract_constant_declarations(cpp_content) 18 | 19 | if declarations: 20 | with open(h_filename, 'w', encoding='utf-8') as file: 21 | file.write('// keys.h\n') 22 | file.write('#ifndef XZG_KEYS_H\n') 23 | file.write('#define XZG_KEYS_H\n\n') 24 | file.writelines("\n".join(declarations)) 25 | file.write('\n\n#endif // XZG_KEYS_H\n') 26 | else: 27 | print("No const found !") 28 | 29 | 30 | KEYS_CPP = "const/keys.cpp" 31 | KEYS_H = "const/keys.h" 32 | 33 | if os.environ.get("PLATFORMIO_INCLUDE_DIR") is not None: 34 | KEYS_CPP = ( 35 | os.environ.get("PLATFORMIO_INCLUDE_DIR") + os.sep + KEYS_CPP 36 | ) 37 | KEYS_H = ( 38 | os.environ.get("PLATFORMIO_INCLUDE_DIR") + os.sep + KEYS_H 39 | ) 40 | elif os.path.exists("src"): 41 | KEYS_CPP = "src" + os.sep + KEYS_CPP 42 | KEYS_H = "src" + os.sep + KEYS_H 43 | else: 44 | PROJECT_DIR = env.subst("$PROJECT_DIR") 45 | os.mkdir(PROJECT_DIR + os.sep + "include") 46 | KEYS_CPP = "include" + os.sep + KEYS_CPP 47 | KEYS_H = "include" + os.sep + KEYS_H 48 | 49 | 50 | create_header_file(KEYS_CPP, KEYS_H) 51 | -------------------------------------------------------------------------------- /.github/workflows/cp_releases.yml: -------------------------------------------------------------------------------- 1 | name: Process Releases 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | process_releases: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout releases branch 14 | uses: actions/checkout@v3 15 | with: 16 | ref: releases 17 | 18 | - name: Download release assets and create manifest.json 19 | env: 20 | GITHUB_REPO: ${{ github.repository }} 21 | run: | 22 | base_url="https://raw.githubusercontent.com/${GITHUB_REPO}/releases" 23 | curl -s https://api.github.com/repos/xyzroe/XZG/releases | jq -r ' 24 | .[] | 25 | {tag_name, url: .assets[] | select(.name | test("XZG_\\d{8}.full.bin")).browser_download_url} | 26 | "\(.tag_name);\(.url)" 27 | ' | while IFS=';' read -r tag_name url; do 28 | file_name=$(basename "$url") 29 | mkdir -p "$tag_name" 30 | wget -O "$tag_name/$file_name" "$url" 31 | file_path="${base_url}/${tag_name}/${file_name}" 32 | echo -e "{\n \"name\": \"XZG Firmware\",\n \"version\": \"$tag_name\",\n \"builds\": [\n {\n \"chipFamily\": \"ESP32\",\n \"improv\": false,\n \"parts\": [\n {\n \"path\": \"$file_path\",\n \"offset\": 0\n }\n ]\n }\n ]\n}" > "$tag_name/manifest.json" 33 | done 34 | 35 | - name: Push changes 36 | run: | 37 | git config --global user.email "action@github.com" 38 | git config --global user.name "GitHub Action" 39 | git add . 40 | git commit -m "Update releases with new manifests and FW files" 41 | git push 42 | -------------------------------------------------------------------------------- /src/web.h: -------------------------------------------------------------------------------- 1 | #ifndef WEB_H 2 | #define WEB_H 3 | 4 | #include "etc.h" // Включение заголовочного файла etc.h 5 | 6 | #include 7 | void handleEvents(); 8 | void initWebServer(); 9 | void webServerHandleClient(); 10 | void handleLoginGet(); 11 | void handleLoginPost(); 12 | void handleLogout(); 13 | bool is_authenticated(); 14 | void handleGeneral(); 15 | void handleSecurity(); 16 | void handleRoot(); 17 | void handleNetwork(); 18 | void handleMqtt(); 19 | void handleVpn(); 20 | void handleZigbeeBSL(); 21 | void handleZigbeeRestart(); 22 | void handleSerial(); 23 | void handleSavefile(); 24 | void handleApi(); 25 | void handleUpdateRequest(); 26 | void handleEspUpdateUpload(); 27 | //void handleZbUpdateUpload(); 28 | void handleNotFound(); 29 | bool captivePortal(); 30 | 31 | //void sendGzip(const char* contentType, const uint8_t content[], uint16_t contentLen); 32 | void sendGzipFromFS(const char* path, const char* contentType); 33 | void handleTools(); 34 | void printLogTime(); 35 | void printLogMsg(String msg); 36 | void handleSaveParams(); 37 | 38 | String getRootData(bool update = false); 39 | 40 | void sendEvent(const char *event, const uint8_t evsz, const String data); 41 | void progressFunc(unsigned int progress, unsigned int total); 42 | 43 | void getEspUpdate(String esp_fw_url); 44 | void runEspUpdateFirmware(uint8_t *data, size_t len); 45 | 46 | 47 | 48 | FirmwareInfo fetchLatestEspFw(String type = "ota"); 49 | String fetchLatestZbFw(); 50 | String extractVersionFromURL(String url); 51 | 52 | void updateWebTask(void *parameter); 53 | 54 | enum API_PAGE_t : uint8_t 55 | { 56 | API_PAGE_ROOT, 57 | API_PAGE_GENERAL, 58 | // API_PAGE_ETHERNET, 59 | API_PAGE_NETWORK, 60 | API_PAGE_ZIGBEE, 61 | API_PAGE_SECURITY, 62 | API_PAGE_TOOLS, 63 | API_PAGE_ABOUT, 64 | API_PAGE_MQTT, 65 | API_PAGE_VPN, 66 | 67 | }; 68 | 69 | #endif // WEB_H -------------------------------------------------------------------------------- /src/etc.h: -------------------------------------------------------------------------------- 1 | #ifndef ETC_H 2 | #define ETC_H 3 | 4 | #include 5 | 6 | #include 7 | #include "const/hw.h" 8 | 9 | void getReadableTime(String &readableTime, unsigned long beginTime); 10 | 11 | String sha1(String payloadStr); 12 | 13 | int check1wire(); 14 | void setup1wire(int pin); 15 | float get1wire(); 16 | 17 | extern BrdConfigStruct brdConfigs[BOARD_CFG_CNT]; 18 | 19 | ThisConfigStruct *findBrdConfig(int searchId); 20 | 21 | float getCPUtemp(bool clear = false); 22 | 23 | void zigbeeRouterRejoin(); 24 | void zigbeeEnableBSL(); 25 | void zigbeeRestart(); 26 | 27 | void usbModeSet(usbMode mode); 28 | 29 | void writeDefaultDeviceId(char *arr, bool ethernet); 30 | //void writeDefaultConfig(const char *path, DynamicJsonDocument &doc); 31 | 32 | #define TIMEOUT_FACTORY_RESET 3 33 | 34 | void factoryReset(); 35 | 36 | void setLedsDisable(bool all = false); 37 | void cronTest(); 38 | void nmActivate(); 39 | //bool checkDNS(bool setup = false); 40 | void setupCron(); 41 | 42 | void setClock(void *pvParameters); 43 | void setTimezone(String timezone); 44 | const char *getGmtOffsetForZone(const char *zone); 45 | char *convertTimeToCron(const String &time); 46 | 47 | void wgBegin(); 48 | void wgLoop(); 49 | 50 | void hnBegin(); 51 | 52 | void ledTask(void *parameter); 53 | String getTime(); 54 | 55 | void checkUpdateAvail(); 56 | 57 | bool isIpInSubnet(IPAddress ip, IPAddress subnet, IPAddress subnetMask); 58 | bool isValidIp(IPAddress ip); 59 | String getHostFromUrl(const String& url); 60 | String getRadioRoleKey(); 61 | String removeLeadingZeros(const String& block); 62 | String getShortenedIPv6(const String& ipv6); 63 | void restartDevice(); 64 | void freeHeapPrint(); 65 | bool dnsLookup(const String &url); 66 | void firstUpdCheck(); 67 | 68 | struct FirmwareInfo { 69 | String url; 70 | String version; 71 | String sha; 72 | }; 73 | 74 | #endif // ETC_H 75 | -------------------------------------------------------------------------------- /tools/build/version_update.py: -------------------------------------------------------------------------------- 1 | """ Create version header and tracker file if missing """ 2 | import datetime 3 | import os 4 | import sys 5 | 6 | Import("env") 7 | 8 | VERSION_HEADER = "version.h" 9 | 10 | sys.path.append("./tools") 11 | from func import print_logo, print_colored 12 | 13 | print_logo() 14 | 15 | def get_formatted_date(dateTimeBuild): 16 | return dateTimeBuild.strftime("%Y%m%d") 17 | 18 | def read_version_from_file(file_path): 19 | if os.path.exists(file_path): 20 | with open(file_path, "r") as file: 21 | for line in file: 22 | if "#define VERSION" in line: 23 | version = line.split('"')[1] 24 | return version 25 | return None 26 | 27 | def write_version_to_file(file_path, version): 28 | header_content = f"""// AUTO GENERATED FILE 29 | #ifndef VERSION 30 | #define VERSION "{version}" 31 | #endif 32 | """ 33 | with open(file_path, "w") as file: 34 | file.write(header_content) 35 | 36 | dateTimeBuild = datetime.datetime.now() 37 | current_date = get_formatted_date(dateTimeBuild) 38 | 39 | if os.environ.get("PLATFORMIO_INCLUDE_DIR") is not None: 40 | VERSION_HEADER = os.environ.get("PLATFORMIO_INCLUDE_DIR") + os.sep + VERSION_HEADER 41 | elif os.path.exists("src"): 42 | VERSION_HEADER = "src" + os.sep + VERSION_HEADER 43 | else: 44 | PROJECT_DIR = env.subst("$PROJECT_DIR") 45 | if not os.path.exists(PROJECT_DIR + os.sep + "include"): 46 | os.mkdir(PROJECT_DIR + os.sep + "include") 47 | VERSION_HEADER = "include" + os.sep + VERSION_HEADER 48 | 49 | current_version = read_version_from_file(VERSION_HEADER) 50 | 51 | if current_version is None: 52 | new_version = current_date 53 | elif current_version.startswith(current_date) or current_version > current_date: 54 | new_version = current_version 55 | else: 56 | new_version = current_date 57 | 58 | write_version_to_file(VERSION_HEADER, new_version) 59 | 60 | print_colored(f"Build: {new_version}", "magenta") 61 | -------------------------------------------------------------------------------- /src/websrc/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/webfilesbuilder/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var concat = require('gulp-concat'); 3 | var gzip = require('gulp-gzip'); 4 | var htmlmin = require('gulp-htmlmin'); 5 | var uglify = require('gulp-uglify'); 6 | var cssnano = require('gulp-cssnano'); 7 | const jsonminify = require('gulp-jsonminify'); 8 | 9 | function stylesConcat() { 10 | return gulp.src(['../../src/websrc/css/custom.css', '../../src/websrc/css/bootstrap.min.css']) 11 | .pipe(concat({ 12 | path: 'style.css', 13 | stat: { 14 | mode: 0666 15 | } 16 | })) 17 | .pipe(cssnano({preset: 'advanced'})) 18 | .pipe(gulp.dest('../../src/websrc/min/css/')) 19 | .pipe(gzip({ 20 | append: true 21 | })) 22 | .pipe(gulp.dest('../../data/css/')); 23 | } 24 | 25 | function scriptsgz() { 26 | return gulp.src("../../src/websrc/js/*.js") 27 | .pipe(uglify()) 28 | .pipe(gulp.dest("../../src/websrc/min/js/")) 29 | .pipe(gzip({ append: true })) 30 | .pipe(gulp.dest('../../data/js/')); 31 | } 32 | 33 | function imggz() { 34 | return gulp.src("../../src/websrc/img/*.*") 35 | .pipe(gzip({ 36 | append: true 37 | })) 38 | .pipe(gulp.dest('../../data/img/')); 39 | } 40 | 41 | function htmlgz() { 42 | return gulp.src("../../src/websrc/html/*.html") 43 | .pipe(htmlmin({ collapseWhitespace: true, removeComments: true, removeRedundantAttributes: true })) 44 | .pipe(gulp.dest("../../src/websrc/min/html/")) 45 | .pipe(gzip({ append: true })) 46 | .pipe(gulp.dest('../../data/html/')); 47 | } 48 | 49 | function jsongz() { 50 | return gulp.src("../../src/websrc/json/*.json") 51 | .pipe(jsonminify()) 52 | .pipe(gulp.dest("../../src/websrc/min/json/")) 53 | .pipe(gzip({ append: true })) 54 | .pipe(gulp.dest('../../data/json/')); 55 | } 56 | 57 | const styleTasks = gulp.series(stylesConcat); 58 | const scriptTasks = gulp.series(scriptsgz); 59 | const imgTasks = gulp.series(imggz); 60 | const htmlTasks = gulp.series(htmlgz); 61 | const jsonTasks = gulp.series(jsongz); 62 | 63 | exports.xzg = gulp.parallel(styleTasks, scriptTasks, imgTasks, htmlTasks, jsonTasks); -------------------------------------------------------------------------------- /tools/build/build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | Import("env") 4 | 5 | from subprocess import call 6 | import shutil 7 | import os 8 | import time 9 | from glob import glob 10 | import sys 11 | 12 | sys.path.append("./tools") 13 | from func import print_logo 14 | from func import print_colored 15 | 16 | VERSION_HEADER = "version.h" 17 | 18 | def extract_version_from_file(file_path): 19 | try: 20 | with open(file_path, 'r') as file: 21 | for line in file: 22 | if '#define VERSION' in line: 23 | version = line.split('"')[1] 24 | return version 25 | except FileNotFoundError: 26 | print("File not found") 27 | return None 28 | 29 | def after_build(source, target, env): 30 | time.sleep(2) 31 | shutil.copy(firmware_source, "bin/firmware.bin") 32 | for f in glob("bin/XZG_*.bin"): 33 | os.unlink(f) 34 | 35 | exit_code = call( 36 | "python tools/build/merge_bin_esp.py --output_folder ./bin --output_name XZG.full.bin --bin_path bin/bootloader_dio_40m.bin bin/partitions.bin bin/firmware.bin bin/XZG.fs.bin --bin_address 0x1000 0x8000 0x10000 0x290000", 37 | shell=True, 38 | ) 39 | 40 | VERSION_FILE = "src/" + VERSION_HEADER 41 | 42 | VERSION_NUMBER = extract_version_from_file(VERSION_FILE) 43 | 44 | NEW_NAME_BASE = "bin/XZG_" + VERSION_NUMBER 45 | 46 | build_env = env['PIOENV'] 47 | if "debug" in build_env: 48 | NEW_NAME_BASE += "_" + build_env 49 | 50 | NEW_NAME_FULL = NEW_NAME_BASE + ".full.bin" 51 | NEW_NAME_OTA = NEW_NAME_BASE + ".ota.bin" 52 | NEW_NAME_FS = NEW_NAME_BASE + ".fs.bin" 53 | 54 | shutil.move("bin/XZG.full.bin", NEW_NAME_FULL) 55 | shutil.move("bin/firmware.bin", NEW_NAME_OTA) 56 | shutil.move("bin/XZG.fs.bin", NEW_NAME_FS) 57 | 58 | print("") 59 | print_colored("--------------------------------------", "yellow") 60 | print_colored("{} created !".format(str(NEW_NAME_FULL)), "blue") 61 | print_colored("{} created !".format(str(NEW_NAME_OTA)), "magenta") 62 | print_colored("{} created !".format(str(NEW_NAME_FS)), "green") 63 | print_colored("--------------------------------------", "yellow") 64 | print_logo() 65 | print_colored("Build " + VERSION_NUMBER, "cyan") 66 | print("") 67 | 68 | env.AddPostAction("buildprog", after_build) 69 | 70 | firmware_source = os.path.join(env.subst("$BUILD_DIR"), "firmware.bin") 71 | -------------------------------------------------------------------------------- /.github/workflows/close_issues.yml: -------------------------------------------------------------------------------- 1 | name: Close Inactive Unlabeled Issues 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' # Run daily at midnight UTC 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | steps: 12 | - name: Process Inactive Issues 13 | uses: actions/github-script@v6 14 | with: 15 | script: | 16 | // Get all open issues without labels 17 | const { data: issues } = await github.rest.issues.listForRepo({ 18 | owner: context.repo.owner, 19 | repo: context.repo.repo, 20 | state: 'open', 21 | labels: 'none', 22 | per_page: 100 23 | }); 24 | 25 | // Calculate cutoff date (3 weeks ago) 26 | const cutoffDate = new Date(); 27 | cutoffDate.setDate(cutoffDate.getDate() - 21); 28 | 29 | for (const issue of issues) { 30 | const lastUpdated = new Date(issue.updated_at); 31 | 32 | if (!issue.labels.length && lastUpdated <= cutoffDate) { 33 | // Format date for comment 34 | const formattedDate = lastUpdated.toLocaleDateString('en-US', { 35 | year: 'numeric', 36 | month: 'long', 37 | day: 'numeric' 38 | }); 39 | 40 | // Add closure comment 41 | await github.rest.issues.createComment({ 42 | owner: context.repo.owner, 43 | repo: context.repo.repo, 44 | issue_number: issue.number, 45 | body: `🚦 **Automatic Closure**\n\n` + 46 | `This issue is being closed because:\n` + 47 | `• No labels were assigned\n` + 48 | `• No activity for more than 3 weeks (last updated: ${formattedDate})\n\n` + 49 | `_If this is still relevant, please comment to reopen._` 50 | }); 51 | 52 | // Close the issue 53 | await github.rest.issues.update({ 54 | owner: context.repo.owner, 55 | repo: context.repo.repo, 56 | issue_number: issue.number, 57 | state: 'closed' 58 | }); 59 | 60 | console.log(`Closed issue #${issue.number} with comment`); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/websrc/html/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
Logo XZG 19 |

21 |
23 |
28 |
29 | 41 |

xyzroe © 2024

43 |
44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /src/const/hw.cpp: -------------------------------------------------------------------------------- 1 | #include "const/hw.h" 2 | 3 | // Ethernet configurations 4 | // Don't forget to edit ETH_CFG_CNT ! 5 | EthConfig ethConfigs[] = { 6 | {.addr = 0, .pwrPin = 12, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO17_OUT}, // .pwrAltPin = -1}, // 0 Olimex-ESP32-POE 7 | {.addr = 1, .pwrPin = 16, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO0_IN}, // .pwrAltPin = -1}, // 1 WT32-ETH01 / SLZB-06 8 | {.addr = 0, .pwrPin = 5, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO17_OUT}, // .pwrAltPin = -1}, // 2 T-Internet-POE / UZG-01 / HamGeek POE Plus / WGNETZG 9 | }; 10 | 11 | // ZigBee configurations 12 | // Don't forget to edit ZB_CFG_CNT ! 13 | ZbConfig zbConfigs[] = { 14 | {.txPin = 4, .rxPin = 36, .rstPin = 16, .bslPin = 32}, // 0 UZG-01 / LilyZig / Olizig 15 | {.txPin = 17, .rxPin = 5, .rstPin = 33, .bslPin = 32}, // 1 ZigStar LAN / SLZB-06 / TubesZB-eth 16 | {.txPin = 33, .rxPin = 32, .rstPin = 12, .bslPin = 14}, // 2 No name China-GW 17 | {.txPin = 5, .rxPin = 17, .rstPin = 33, .bslPin = 32}, // 3 TubesZB-eth_usb 18 | {.txPin = 16, .rxPin = 5, .rstPin = 33, .bslPin = 32}, // 4 TubesZB-poe 19 | {.txPin = 16, .rxPin = 5, .rstPin = 13, .bslPin = 4}, // 5 TubesZB-poe-2022 20 | {.txPin = 4, .rxPin = 36, .rstPin = 5, .bslPin = 16}, // 6 TubesZB-poe-2023 21 | {.txPin = 23, .rxPin = 22, .rstPin = 18, .bslPin = 19}, // 7 SLS-classic 22 | }; 23 | 24 | // Mist configurations 25 | // Don't forget to edit MIST_CFG_CNT ! 26 | MistConfig mistConfigs[] = { 27 | {.btnPin = -1, .btnPlr = 0, .uartSelPin = -1, .uartSelPlr = 0, .ledModePin = -1, .ledModePlr = 0, .ledPwrPin = -1, .ledPwrPlr = 0}, // 0 No mist cfg 28 | {.btnPin = 35, .btnPlr = 1, .uartSelPin = 33, .uartSelPlr = 1, .ledModePin = 12, .ledModePlr = 1, .ledPwrPin = 14, .ledPwrPlr = 1}, // 1 UZG-01 / CZC-1.0 29 | {.btnPin = 35, .btnPlr = 1, .uartSelPin = 4, .uartSelPlr = 1, .ledModePin = 12, .ledModePlr = 1, .ledPwrPin = 14, .ledPwrPlr = 1}, // 2 SLZB-06 30 | {.btnPin = 33, .btnPlr = 1, .uartSelPin = -1, .uartSelPlr = 0, .ledModePin = -1, .ledModePlr = 0, .ledPwrPin = -1, .ledPwrPlr = 0}, // 3 SLS-classic 31 | {.btnPin = 14, .btnPlr = 1, .uartSelPin = -1, .uartSelPlr = 0, .ledModePin = -1, .ledModePlr = 0, .ledPwrPin = -1, .ledPwrPlr = 0}, // 4 T-Internet-POE 32 | }; 33 | 34 | // Board configurations 35 | // Don't forget to edit BOARD_CFG_CNT ! 36 | BrdConfigStruct brdConfigs[] = { 37 | {"SLS-classic", -1, 7, 3}, // 0 38 | {"UZG-01", 2, 0, 1}, // 1 39 | {"SLZB-06", 1, 1, 2}, // 2 40 | {"ZigStar LAN", 1, 1, 0}, // 3 41 | {"LilyZig", 2, 0, 4}, // 4 42 | {"Olizig", 0, 0, 0}, // 5 43 | {"China-GW", 0, 2, 0}, // 6 44 | {"TubesZB-eth", 1, 1, 0}, // 7 45 | {"TubesZB-eth_usb", 1, 3, 0}, // 8 46 | {"TubesZB-poe", 0, 4, 0}, // 9 47 | {"TubesZB-poe-2022", 0, 5, 0}, // 10 48 | {"TubesZB-poe-2023", 0, 6, 0}, // 11 49 | {"CZC-1.0", 2, 0, 1}, // 12 50 | {"HG POE Plus", 2, 0, 1}, // 13 51 | {"WGNETZG", 2, 0, 1}, // 14 52 | }; 53 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /tools/build/merge_bin_esp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | 4 | class bin(): 5 | def __init__(self, file_path, addr): 6 | self.file_path = file_path 7 | self.file_name = os.path.basename(file_path) 8 | self.addr = addr 9 | self.size = self.get_size() 10 | 11 | def get_size(self): 12 | return int(os.path.getsize(self.file_path)) 13 | 14 | class multiple_bin(): 15 | def __init__(self, name, output_folder): 16 | self.name = name 17 | self.output_folder = output_folder 18 | try: 19 | os.makedirs(os.path.realpath(self.output_folder)) 20 | except: 21 | pass 22 | self.output_path = os.path.realpath(os.path.join(self.output_folder,self.name)) 23 | self.bin_array = [] 24 | 25 | def add_bin(self, file_path, addr): 26 | self.bin_array.append(bin(file_path, addr)) 27 | 28 | def sort_bin(self): 29 | swapped = True 30 | while swapped: 31 | swapped = False 32 | for i in range(len(self.bin_array) - 1): 33 | if self.bin_array[i].addr > self.bin_array[i + 1].addr: 34 | self.bin_array[i], self.bin_array[i + 1] = self.bin_array[i + 1], self.bin_array[i] 35 | swapped = True 36 | 37 | def add_bin_to_other_bin(self, previous, binary): 38 | with open(self.output_path, "ab") as output_file: 39 | output_file.write( b'\xff' * (binary.addr-previous)) 40 | print ("Add %s from 0x%x to 0x%x (0x%x)"%(binary.file_name, binary.addr, binary.addr+binary.size, binary.size)) 41 | with open(self.output_path, "ab") as output_file, open(binary.file_path, "rb") as bin_file: 42 | output_file.write(bin_file.read()) 43 | return binary.addr+binary.size 44 | 45 | def create_bin(self): 46 | new_start = 0 47 | open(self.output_path, "wb").close 48 | for b in self.bin_array: 49 | new_start = self.add_bin_to_other_bin(new_start, b) 50 | 51 | def check_if_possible(self): 52 | for i in range(1, len(self.bin_array)): 53 | if(self.bin_array[i].addr < (self.bin_array[i-1].addr+self.bin_array[i-1].size)): 54 | print (self.bin_array[i].addr, (self.bin_array[i-1].addr+self.bin_array[i-1].size)) 55 | raise Exception("Not possible to create this bin, overlapping between %s and %s"%(self.bin_array[i].file_name, self.bin_array[i-1].file_name)) 56 | 57 | def main(): 58 | parser = argparse.ArgumentParser(description='Script to merge *.bin file at different position') 59 | parser.add_argument( 60 | '--output_name', 61 | help = 'Output file name', default = "output.bin") 62 | parser.add_argument( 63 | '--output_folder', default = "output", 64 | help = 'Output folder path') 65 | parser.add_argument( 66 | '--input_folder', default = "", 67 | help = 'Input folder path') 68 | parser.add_argument( 69 | '--bin_path',nargs='+',required=True, 70 | help = 'List of bin path, same order as bin_address (space seperated)') 71 | parser.add_argument( 72 | '--bin_address',nargs='+',type=lambda x: int(x,0),required=True, 73 | help = 'List of addr, same order as bin_path (space seperated)') 74 | parser.add_argument 75 | args = parser.parse_args() 76 | mb = multiple_bin(args.output_name, args.output_folder) 77 | for path,address in zip(args.bin_path, args.bin_address): 78 | mb.add_bin(os.path.join(args.input_folder, path), address) 79 | mb.sort_bin() 80 | mb.check_if_possible() 81 | mb.create_bin() 82 | print ("%s generated with success ! (size %u)"%(args.output_name, int(os.path.getsize(mb.output_path)))) 83 | 84 | if __name__ == "__main__": 85 | exit(main()) -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = prod-dual 13 | 14 | [env] 15 | framework = arduino 16 | lib_deps = 17 | bblanchon/ArduinoJson@6.21.3 18 | rlogiacco/CircularBuffer@>=1.4.0 19 | Martin-Laclaustra/CronAlarms 20 | mathieucarbou/AsyncTCP@3.2.5 21 | marvinroger/AsyncMqttClient@^0.9.0 22 | robtillaart/DS18B20@^0.2.3 23 | 24 | ;husarnet/Husarnet ESP32 @ 1.2.0-5 ;husarnet example 25 | ;husarnet/esp_husarnet^0.0.11 26 | ;me-no-dev/AsyncTCP@1.1.1 27 | ;elims/PsychicMqttClient@0.2.0 28 | ;sstaub/Ticker@>=4.4.0 29 | ;plerup/EspSoftwareSerial@8.1.0 30 | ;marian-craciunescu/ESP32Ping@>=1.7 31 | ;me-no-dev/ESPAsyncWebServer@1.2.3 32 | ;milesburton/DallasTemperature @ ^3.11.0 33 | monitor_filters = direct, esp32_exception_decoder, log2file ; default ; ; ; 34 | monitor_speed = 115200 35 | upload_speed = 460800 36 | ;platform_packages = 37 | ; framework-arduinoespressif32 @ https://github.com/husarnet/arduino-esp32/releases/download/1.0.4-1/arduino-husarnet-esp32.zip ;husarnet example 38 | platform_packages = 39 | tool-mklittlefs 40 | ;framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/3.0.4.240826/framework-arduinoespressif32.zip 41 | ;framework-arduinoespressif32-solo1 @ https://github.com/tasmota/arduino-esp32/releases/download/3.0.4.240826/framework-arduinoespressif32-solo1.zip 42 | extra_scripts = 43 | ;pre:tools/build/build_fs.py 44 | pre:tools/build/pre_build.py 45 | pre:tools/build/version_update.py 46 | pre:tools/webfilesbuilder/build_html.py 47 | pre:tools/export_compile_commands.py 48 | post:tools/build/build.py 49 | 50 | build_flags = 51 | -DBUILD_ENV_NAME=$PIOENV 52 | -Os 53 | 54 | [solo] 55 | platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.09.10/platform-espressif32.zip ; Platform 2024.08.11 Tasmota Arduino Core 3.0.4 based on IDF 5.1.4+ 56 | build_flags = 57 | -DFRAMEWORK_ARDUINO_SOLO1 58 | -DTASMOTA_PLATFORM 59 | 60 | [dual] 61 | platform = espressif32 @ 6.8.1 62 | ;platform = espressif32@2.1.0 ;husarnet example 63 | 64 | [prod] 65 | build_flags = 66 | -DCORE_DEBUG_LEVEL=0 67 | 68 | [debug] 69 | build_flags = 70 | -DCORE_DEBUG_LEVEL=2 71 | -DDEBUG 72 | 73 | [env:prod-solo] 74 | platform = ${solo.platform} 75 | board = esp32-solo1 76 | board_build.filesystem = littlefs 77 | build_flags = 78 | ${env.build_flags} 79 | ${solo.build_flags} 80 | ${prod.build_flags} 81 | board_build.f_cpu = 160000000L 82 | extra_scripts = 83 | ${env.extra_scripts} 84 | 85 | [env:debug-solo] 86 | platform = ${solo.platform} 87 | board = esp32-solo1 88 | board_build.filesystem = littlefs 89 | build_flags = 90 | ${env.build_flags} 91 | ${solo.build_flags} 92 | ${debug.build_flags} 93 | board_build.f_cpu = 160000000L 94 | extra_scripts = 95 | ${env.extra_scripts} 96 | monitor_speed = 115200 97 | 98 | [env:prod-dual] 99 | platform = ${dual.platform} 100 | board = esp32dev 101 | board_build.filesystem = littlefs 102 | build_flags = 103 | ${env.build_flags} 104 | ${prod.build_flags} 105 | extra_scripts = 106 | ${env.extra_scripts} 107 | 108 | [env:debug-dual] 109 | platform = ${dual.platform} 110 | board = esp32dev 111 | board_build.partitions = partitions.csv 112 | board_build.filesystem = littlefs 113 | build_flags = 114 | ${env.build_flags} 115 | ${debug.build_flags} 116 | extra_scripts = 117 | ${env.extra_scripts} -------------------------------------------------------------------------------- /src/websrc/html/security.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 12 |
13 |
14 | 17 |
18 |
19 | 20 | 22 |
23 |
24 | 25 |
26 | 30 | 34 |
35 |
36 |
37 |
38 |
41 |
42 |
43 | 44 | 46 |
47 |
48 | 49 | 51 |
52 |
53 |
54 |
55 |
56 |
58 |
59 |
60 |
61 |
-------------------------------------------------------------------------------- /tools/commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define colors 4 | RED='\033[0;31m' 5 | GREEN='\033[0;32m' 6 | YELLOW='\033[1;33m' 7 | NC='\033[0m' # No Color 8 | 9 | # Ask for confirmation to pull changes 10 | echo -e "${YELLOW}Do you want to pull the latest changes from the repository? 🔄 (Y/n)${NC}" 11 | read -r response 12 | response=${response:-y} # default 'yes' if empty 13 | if [[ "$response" =~ ^[Yy]$ ]]; then 14 | git pull 15 | echo -e "${GREEN}Changes pulled successfully. ✔️${NC}" 16 | else 17 | echo -e "${YELLOW}Pull skipped. Continuing without pulling changes. ⚠️${NC}" 18 | fi 19 | 20 | # Adding all changes to staging 21 | git add -A 22 | echo -e "${GREEN}All changes added to staging. ✔️${NC}" 23 | 24 | # Path to the version and commit message files 25 | COMMIT_MESSAGE_FILE="commit.md" 26 | VERSION_HEADER="src/version.h" 27 | 28 | # Check if the version file exists 29 | if [ ! -f "$VERSION_HEADER" ]; then 30 | echo -e "${RED}Version header not found: $VERSION_HEADER ❌${NC}" 31 | exit 1 32 | fi 33 | 34 | # Read version from the version file 35 | version=$(grep '#define VERSION' "$VERSION_HEADER" | awk -F '"' '{print $2}') 36 | 37 | tag=$version 38 | 39 | regex='^(.+)\.([0-9]+)$' 40 | 41 | if [[ $tag =~ $regex ]]; then 42 | base=${BASH_REMATCH[1]} 43 | suffix=${BASH_REMATCH[2]} 44 | else 45 | base=$tag 46 | suffix=0 47 | fi 48 | 49 | while git rev-parse "$tag" >/dev/null 2>&1; do 50 | suffix=$((suffix + 1)) 51 | tag="${base}.${suffix}" 52 | done 53 | 54 | sed -i.bak "s/#define VERSION \"${version}\"/#define VERSION \"${tag}\"/" "$VERSION_HEADER" 55 | 56 | rm "${VERSION_HEADER}.bak" 57 | 58 | echo "Updated version to $tag in $VERSION_HEADER" 59 | 60 | # Checking for commit message file 61 | if [ -f "$COMMIT_MESSAGE_FILE" ]; then 62 | echo -e "${YELLOW}Commit message file found. ${RED}The first line should be empty!${NC}" 63 | echo -e "${YELLOW}Do you want to use the existing commit message? (y/N) 📝${NC}" 64 | read -r useExistingMessage 65 | useExistingMessage=${useExistingMessage:-n} # default 'no' if empty 66 | if [[ "$useExistingMessage" =~ ^[Yy]$ ]]; then 67 | commitMessage=$(cat "$COMMIT_MESSAGE_FILE") 68 | # Prepend version to the commit message with a newline for separation 69 | formattedCommitMessage="${tag} 70 | ${commitMessage}" 71 | # Cleaning up the commit message file, if used 72 | if [ -f "$COMMIT_MESSAGE_FILE" ]; then 73 | tools/clean_file.sh "$COMMIT_MESSAGE_FILE" 74 | fi 75 | else 76 | echo -e "${YELLOW}Please enter your commit message: 📝${NC}" 77 | read -r commitMessage 78 | formattedCommitMessage="${commitMessage}" 79 | fi 80 | else 81 | echo -e "${YELLOW}Commit message file not found. Please enter your commit message: 📝${NC}" 82 | read -r commitMessage 83 | formattedCommitMessage="${commitMessage}" 84 | fi 85 | 86 | # Committing changes 87 | git commit -m "$formattedCommitMessage" 88 | echo -e "${GREEN}Changes committed with version prepended to message: ✔️${NC}" 89 | 90 | # Tagging process 91 | echo -e "${YELLOW}Do you want to create a new release by publishing a tag? 🏷️ (y/N)${NC}" 92 | read -r tagCommit 93 | tagCommit=${tagCommit:-n} # default 'no' if empty 94 | if [[ "$tagCommit" =~ ^[Yy]$ ]]; then 95 | git tag "$tag" 96 | echo -e "${GREEN}Tag assigned: $tag 🏷️${NC}" 97 | 98 | # Pushing changes and tag 99 | echo -e "${YELLOW}Do you want to push the changes and the new tag to the remote repository? 🚀 (Y/n)${NC}" 100 | read -r pushChanges 101 | pushChanges=${pushChanges:-y} # default 'yes' if empty 102 | if [[ "$pushChanges" =~ ^[Yy]$ ]]; then 103 | git push 104 | git push origin "$tag" 105 | echo -e "${GREEN}Changes and new tag pushed successfully. ✔️${NC}" 106 | else 107 | echo -e "${RED}Push of changes and tag skipped. ❌${NC}" 108 | fi 109 | else 110 | echo -e "${YELLOW}No new release will be created. Do you still want to push the changes? (Y/n) 🚀${NC}" 111 | read -r pushChanges 112 | pushChanges=${pushChanges:-y} # default 'yes' if empty 113 | if [[ "$pushChanges" =~ ^[Yy]$ ]]; then 114 | git push 115 | echo -e "${GREEN}Changes pushed successfully without creating a new release. ✔️${NC}" 116 | else 117 | echo -e "${RED}Push skipped. ❌${NC}" 118 | fi 119 | fi 120 | 121 | -------------------------------------------------------------------------------- /src/websrc/html/mqtt.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 |
9 |
10 |
14 |
19 |
22 |
25 |
26 |
34 |
35 |
39 |
43 |
47 |

51 |
53 |
54 |
55 |
56 |
57 |
58 |
-------------------------------------------------------------------------------- /src/const/keys.h: -------------------------------------------------------------------------------- 1 | // keys.h 2 | #ifndef XZG_KEYS_H 3 | #define XZG_KEYS_H 4 | 5 | extern const char *networkConfigKey; 6 | extern const char *wifiEnblKey; 7 | extern const char *wifiSsidKey; 8 | extern const char *wifiPassKey; 9 | extern const char *wifiDhcpKey; 10 | extern const char *wifiIpKey; 11 | extern const char *wifiMaskKey; 12 | extern const char *wifiGateKey; 13 | extern const char *wifiDns1Key; 14 | extern const char *wifiDns2Key; 15 | extern const char *wifiPwrKey; 16 | extern const char *wifiModeKey; 17 | extern const char *wifiIPv6Key; 18 | extern const char *ethEnblKey; 19 | extern const char *ethDhcpKey; 20 | extern const char *ethIpKey; 21 | extern const char *ethMaskKey; 22 | extern const char *ethGateKey; 23 | extern const char *ethDns1Key; 24 | extern const char *ethDns2Key; 25 | extern const char *ethIPv6Key; 26 | extern const char *vpnConfigKey; 27 | extern const char *wgEnableKey; 28 | extern const char *wgLocalIPKey; 29 | extern const char *wgLocalSubnetKey; 30 | extern const char *wgLocalPortKey; 31 | extern const char *wgLocalGatewayKey; 32 | extern const char *wgLocalPrivKeyKey; 33 | extern const char *wgEndAddrKey; 34 | extern const char *wgEndPubKeyKey; 35 | extern const char *wgEndPortKey; 36 | extern const char *wgAllowedIPKey; 37 | extern const char *wgAllowedMaskKey; 38 | extern const char *wgMakeDefaultKey; 39 | extern const char *wgPreSharedKeyKey; 40 | extern const char *hnEnableKey; 41 | extern const char *hnJoinCodeKey; 42 | extern const char *hnHostNameKey; 43 | extern const char *hnDashUrlKey; 44 | extern const char *mqttConfigKey; 45 | extern const char *enableKey; 46 | extern const char *serverKey; 47 | extern const char *portKey; 48 | extern const char *userKey; 49 | extern const char *passKey; 50 | extern const char *topicKey; 51 | extern const char *intervalKey; 52 | extern const char *updateIntKey; 53 | extern const char *discoveryKey; 54 | extern const char *reconnectIntKey; 55 | extern const char *systemConfigKey; 56 | extern const char *keepWebKey; 57 | extern const char *disableWebKey; 58 | extern const char *webAuthKey; 59 | extern const char *webUserKey; 60 | extern const char *webPassKey; 61 | extern const char *fwEnabledKey; 62 | extern const char *fwIpKey; 63 | extern const char *fwMaskKey; 64 | extern const char *serialSpeedKey; 65 | extern const char *socketPortKey; 66 | extern const char *tempOffsetKey; 67 | extern const char *disableLedUSBKey; 68 | extern const char *disableLedPwrKey; 69 | extern const char *disableLedsKey; 70 | extern const char *refreshLogsKey; 71 | extern const char *hostnameKey; 72 | extern const char *timeZoneKey; 73 | extern const char *ntpServ1Key; 74 | extern const char *ntpServ2Key; 75 | extern const char *nmStartHourKey; 76 | extern const char *nmEndHourKey; 77 | extern const char *nmEnableKey; 78 | extern const char *updCheckTimeKey; 79 | extern const char *updCheckDayKey; 80 | extern const char *updAutoInstKey; 81 | extern const char *systemVarsKey; 82 | extern const char *hwBtnIsKey; 83 | extern const char *hwLedUsbIsKey; 84 | extern const char *hwLedPwrIsKey; 85 | extern const char *hwUartSelIsKey; 86 | extern const char *hwZigbeeIsKey; 87 | extern const char *workModeKey; 88 | extern const char *connectedSocketKey; 89 | extern const char *connectedClientsKey; 90 | extern const char *socketTimeKey; 91 | extern const char *connectedEtherKey; 92 | extern const char *apStartedKey; 93 | extern const char *wifiWebSetupInProgressKey; 94 | extern const char *vpnWgInitKey; 95 | extern const char *vpnWgConnectKey; 96 | extern const char *vpnWgPeerIpKey; 97 | extern const char *vpnWgCheckTimeKey; 98 | extern const char *vpnHnInitKey; 99 | extern const char *mqttConnKey; 100 | extern const char *mqttReconnectTimeKey; 101 | extern const char *mqttHeartbeatTimeKey; 102 | extern const char *zbLedStateKey; 103 | extern const char *zbFlashingKey; 104 | extern const char *deviceIdKey; 105 | extern const char *timeZoneNameKey; 106 | extern const char *zbRoleKey; 107 | extern const char *zbFwKey; 108 | extern const char *rcpUpdAvailKey; 109 | extern const char *espUpdAvailKey; 110 | extern const char *tagZB_FW_info; 111 | extern const char *tagZB_FW_file; 112 | extern const char *tagZB_FW_err; 113 | extern const char *tagZB_FW_prgs; 114 | extern const char *tagZB_NV_prgs; 115 | extern const char *tagESP_FW_prgs; 116 | extern const char *hwConfigKey; 117 | extern const char *boardKey; 118 | extern const char *addrKey; 119 | extern const char *pwrPinKey; 120 | extern const char *mdcPinKey; 121 | extern const char *mdiPinKey; 122 | extern const char *phyTypeKey; 123 | extern const char *clkModeKey; 124 | extern const char *pwrAltPinKey; 125 | extern const char *btnPinKey; 126 | extern const char *btnPlrKey; 127 | extern const char *uartSelPinKey; 128 | extern const char *uartSelPlrKey; 129 | extern const char *ledModePinKey; 130 | extern const char *ledModePlrKey; 131 | extern const char *ledPwrPinKey; 132 | extern const char *ledPwrPlrKey; 133 | extern const char *zbTxPinKey; 134 | extern const char *zbRxPinKey; 135 | extern const char *zbRstPinKey; 136 | extern const char *zbBslPinKey; 137 | extern const char *contTypeText; 138 | 139 | #endif // XZG_KEYS_H 140 | -------------------------------------------------------------------------------- /src/websrc/html/about.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

XZG logo

6 |


7 |

8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |

20 |

21 |

23 |
24 |
25 |
26 |
27 | 28 |
29 |
30 |

31 |

32 |

34 | 35 |
37 | 38 |

39 |
40 |
41 |
42 |
43 | 44 |
45 |
46 |

47 |

48 |

50 |
51 |
52 |
53 |
54 | 55 |
56 |
57 |

58 |

59 |

61 |
62 |
63 |
64 |
65 | 66 |
67 | 81 |
82 |
83 |
84 | 85 |
86 |
87 |

88 |

89 |

91 |
92 |
93 |
94 |
95 |
-------------------------------------------------------------------------------- /tools/webfilesbuilder/build_html.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | Import("env") 4 | 5 | import time 6 | import os 7 | import sys 8 | import shutil 9 | 10 | # Create this file to avoid rebuilding webh files 11 | NO_WEB_UPDATE = "tools/.no_web_update" 12 | 13 | sys.path.append("./tools") 14 | from func import print_logo 15 | 16 | import subprocess 17 | from SCons.Script import DefaultEnvironment 18 | 19 | env = DefaultEnvironment() 20 | 21 | def get_directory_size(directory, block_size=4096): 22 | total_size = 0 23 | for dirpath, dirnames, filenames in os.walk(directory): 24 | for f in filenames: 25 | fp = os.path.join(dirpath, f) 26 | if os.path.exists(fp): 27 | file_size = os.path.getsize(fp) 28 | rounded_size = (file_size + block_size - 1) // block_size * block_size 29 | total_size += rounded_size 30 | return total_size 31 | 32 | def calculate_filesystem_size(directory, metadata_overhead=0.12, block_size=4096): 33 | size = get_directory_size(directory, block_size) 34 | size = int(size * (1 + metadata_overhead)) 35 | size = (size + block_size - 1) // block_size * block_size 36 | return size 37 | 38 | def build_filesystem(env): 39 | try: 40 | filesystem_dir = os.path.join(env['PROJECT_DIR'], 'data') 41 | size_file_path = os.path.join(env['PROJECT_DIR'], 'src', 'websrc', 'size.fs') 42 | 43 | if os.path.exists(size_file_path): 44 | with open(size_file_path, 'r') as size_file: 45 | filesystem_size = int(size_file.read().strip()) 46 | print(f"Using existing filesystem size from {size_file_path}: {filesystem_size} bytes") 47 | else: 48 | filesystem_size = calculate_filesystem_size(filesystem_dir) 49 | with open(size_file_path, 'w') as size_file: 50 | size_file.write(str(filesystem_size)) 51 | print(f"Calculated and saved filesystem size to {size_file_path}: {filesystem_size} bytes") 52 | 53 | filesystem_image = os.path.join(env['BUILD_DIR'], 'littlefs.bin') 54 | mklittlefs_path = os.path.join(env['PROJECT_PACKAGES_DIR'], 'tool-mklittlefs', 'mklittlefs') 55 | 56 | if not os.path.exists(mklittlefs_path): 57 | print(f"Path to mklittlefs tool is not set or invalid: {mklittlefs_path}") 58 | return False 59 | 60 | print(f"Building filesystem image at {filesystem_image}") 61 | print(f"Using mklittlefs tool at {mklittlefs_path}") 62 | print(f"Filesystem directory: {filesystem_dir}") 63 | print(f"Filesystem size: {filesystem_size} bytes") 64 | 65 | cmd = [ 66 | mklittlefs_path, 67 | '-c', filesystem_dir, 68 | '-b', '4096', 69 | '-p', '256', 70 | '-s', str(filesystem_size), 71 | filesystem_image 72 | ] 73 | 74 | print(f"Executing: {' '.join(cmd)}") 75 | result = env.Execute(" ".join(cmd)) 76 | if result == 0: 77 | return True 78 | else: 79 | return False 80 | except Exception as e: 81 | print(f"An error occurred: {e}") 82 | return False 83 | 84 | def build_html(): 85 | if (not os.path.exists(NO_WEB_UPDATE)) or any(target in sys.argv for target in ["buildfs", "uploadfs"]): 86 | 87 | print("") 88 | print("Try to build WEB files") 89 | print("") 90 | # time.sleep(1) 91 | 92 | os.makedirs("./data", exist_ok=True) 93 | 94 | try: 95 | result = subprocess.run(["git", "log", "-n", "1", "--pretty=format:%h", "--", "./src/websrc"], capture_output=True, text=True, check=True) 96 | git_commit_sha = result.stdout.strip() 97 | os.makedirs("./data/x", exist_ok=True) 98 | with open("./data/x/commit", "w") as f: 99 | f.write(git_commit_sha) 100 | print(f"Git commit SHA saved to ./data/x/commit: {git_commit_sha}") 101 | except subprocess.CalledProcessError as e: 102 | print(f"Failed to get Git commit SHA: {e}") 103 | 104 | os.chdir("./tools/webfilesbuilder/") 105 | 106 | marker_file = ".npm_install_marker" 107 | 108 | if not os.path.exists(marker_file): 109 | 110 | os.system("npm install") 111 | 112 | with open(marker_file, 'w') as f: 113 | f.write("npm install executed") 114 | 115 | else: 116 | print("npm install already executed. Skipping.") 117 | 118 | env.Execute("npx gulp xzg") 119 | 120 | print("") 121 | print("Finish building WEB files") 122 | 123 | 124 | os.chdir("../../") 125 | 126 | current_env = env['PIOENV'] 127 | 128 | if not current_env: 129 | raise ValueError("Environment variable PIOENV is not set") 130 | 131 | 132 | if not build_filesystem(env): 133 | print("Failed to build filesystem. Exiting.") 134 | sys.exit(1) 135 | print("File system built successfully") 136 | 137 | src_fs_path = f"./.pio/build/{current_env}/littlefs.bin" 138 | dest_fs_path = "./bin/XZG.fs.bin" 139 | os.makedirs("./bin", exist_ok=True) 140 | 141 | shutil.move(src_fs_path, dest_fs_path) 142 | print(f"File system moved to {dest_fs_path}") 143 | 144 | if any(target in sys.argv for target in ["buildfs"]): 145 | sys.exit(0) 146 | 147 | 148 | if not any(target in sys.argv for target in ["--clean", "erase"]): 149 | #print(sys.argv) 150 | build_html() -------------------------------------------------------------------------------- /src/websrc/html/vpn.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
14 |
17 |
20 |
23 |
26 |
29 |
32 |
35 |
38 |
41 |
44 |
47 |
48 |
49 |
50 | 70 |
72 |
73 |
-------------------------------------------------------------------------------- /src/const/keys.cpp: -------------------------------------------------------------------------------- 1 | #include "const/keys.h" 2 | 3 | const char *networkConfigKey = "network-config"; 4 | const char *wifiEnblKey = "wifiEnbl"; 5 | const char *wifiSsidKey = "wifiSsid"; 6 | const char *wifiPassKey = "wifiPass"; 7 | const char *wifiDhcpKey = "wifiDhcp"; 8 | const char *wifiIpKey = "wifiIp"; 9 | const char *wifiMaskKey = "wifiMask"; 10 | const char *wifiGateKey = "wifiGate"; 11 | const char *wifiDns1Key = "wifiDns1"; 12 | const char *wifiDns2Key = "wifiDns2"; 13 | const char *wifiPwrKey = "wifiPwr"; 14 | const char *wifiModeKey = "wifiMode"; 15 | const char *wifiIPv6Key = "wifiIPv6"; 16 | const char *ethEnblKey = "ethEnbl"; 17 | const char *ethDhcpKey = "ethDhcp"; 18 | const char *ethIpKey = "ethIp"; 19 | const char *ethMaskKey = "ethMask"; 20 | const char *ethGateKey = "ethGate"; 21 | const char *ethDns1Key = "ethDns1"; 22 | const char *ethDns2Key = "ethDns2"; 23 | const char *ethIPv6Key = "ethIPv6"; 24 | 25 | const char *vpnConfigKey = "vpn-config"; 26 | const char *wgEnableKey = "wgEnable"; 27 | const char *wgLocalIPKey = "wgLocalIP"; 28 | const char *wgLocalSubnetKey = "wgLocalSubnet"; 29 | const char *wgLocalPortKey = "wgLocalPort"; 30 | const char *wgLocalGatewayKey = "wgLocalGateway"; 31 | const char *wgLocalPrivKeyKey = "wgLocalPrivKey"; 32 | const char *wgEndAddrKey = "wgEndAddr"; 33 | const char *wgEndPubKeyKey = "wgEndPubKey"; 34 | const char *wgEndPortKey = "wgEndPort"; 35 | const char *wgAllowedIPKey = "wgAllowedIP"; 36 | const char *wgAllowedMaskKey = "wgAllowedMask"; 37 | const char *wgMakeDefaultKey = "wgMakeDefault"; 38 | const char *wgPreSharedKeyKey = "wgPreSharedKey"; 39 | 40 | const char *hnEnableKey = "hnEnable"; 41 | const char *hnJoinCodeKey = "hnJoinCode"; 42 | const char *hnHostNameKey = "hnHostName"; 43 | const char *hnDashUrlKey = "hnDashUrl"; 44 | 45 | const char *mqttConfigKey = "mqtt-config"; 46 | const char *enableKey = "enable"; 47 | const char *serverKey = "server"; 48 | const char *portKey = "port"; 49 | const char *userKey = "user"; 50 | const char *passKey = "pass"; 51 | const char *topicKey = "topic"; 52 | const char *intervalKey = "interval"; 53 | const char *updateIntKey = "updateInt"; 54 | const char *discoveryKey = "discovery"; 55 | const char *reconnectIntKey = "reconnectInt"; 56 | 57 | const char *systemConfigKey = "system-config"; 58 | // const char *keepWebKey = "keepWeb"; 59 | const char *disableWebKey = "disableWeb"; 60 | const char *webAuthKey = "webAuth"; 61 | const char *webUserKey = "webUser"; 62 | const char *webPassKey = "webPass"; 63 | const char *fwEnabledKey = "fwEnabled"; 64 | const char *fwIpKey = "fwIp"; 65 | const char *fwMaskKey = "fwMask"; 66 | const char *serialSpeedKey = "serialSpeed"; 67 | const char *socketPortKey = "socketPort"; 68 | const char *tempOffsetKey = "tempOffset"; 69 | const char *disableLedUSBKey = "disableLedUSB"; 70 | const char *disableLedPwrKey = "disableLedPwr"; 71 | const char *disableLedsKey = "disableLeds"; 72 | const char *refreshLogsKey = "refreshLogs"; 73 | const char *hostnameKey = "hostname"; 74 | const char *timeZoneKey = "timeZone"; 75 | const char *ntpServ1Key = "ntpServ1"; 76 | const char *ntpServ2Key = "ntpServ2"; 77 | const char *nmStartHourKey = "startHour"; 78 | const char *nmEndHourKey = "endHour"; 79 | const char *nmEnableKey = "nightMode"; 80 | 81 | const char *updCheckTimeKey = "updHour"; 82 | const char *updCheckDayKey = "updDays"; 83 | const char *updAutoInstKey = "autoIns"; 84 | 85 | const char *systemVarsKey = "system-vars"; 86 | const char *hwBtnIsKey = "hwBtnIs"; 87 | const char *hwLedUsbIsKey = "hwLedUsbIs"; 88 | const char *hwLedPwrIsKey = "hwLedPwrIs"; 89 | // const char *hwUartSelIsKey = "hwUartSelIs"; 90 | const char *hwZigbeeIsKey = "hwZigbeeIs"; 91 | const char *workModeKey = "workMode"; 92 | const char *connectedSocketKey = "connectedSocket"; 93 | const char *connectedClientsKey = "connectedClients"; 94 | const char *socketTimeKey = "socketTime"; 95 | const char *connectedEtherKey = "connectedEther"; 96 | const char *apStartedKey = "apStarted"; 97 | const char *wifiWebSetupInProgressKey = "wifiWebSetupInProgress"; 98 | const char *vpnWgInitKey = "vpnWgInit"; 99 | const char *vpnWgConnectKey = "vpnWgConnect"; 100 | const char *vpnWgPeerIpKey = "vpnWgPeerIp"; 101 | const char *vpnWgCheckTimeKey = "vpnWgCheckTimeKey"; 102 | const char *vpnHnInitKey = "vpnHnInit"; 103 | const char *mqttConnKey = "mqttConn"; 104 | const char *mqttReconnectTimeKey = "mqttReconnectTime"; 105 | const char *mqttHeartbeatTimeKey = "mqttHeartbeatTime"; 106 | // const char *zbLedStateKey = "zbLedState"; 107 | const char *zbFlashingKey = "zbFlashing"; 108 | const char *deviceIdKey = "deviceId"; 109 | const char *timeZoneNameKey = "timeZoneName"; 110 | const char *zbRoleKey = "zbRole"; 111 | const char *zbFwKey = "zbFw"; 112 | const char *rcpUpdAvailKey = "rcpUpdAvail"; 113 | const char *espUpdAvailKey = "espUpdAvail"; 114 | 115 | const char *tagZB_FW_info = "zb.fi"; 116 | const char *tagZB_FW_file = "zb.ff"; 117 | const char *tagZB_FW_err = "zb.fe"; 118 | const char *tagZB_FW_prgs = "zb.fp"; 119 | const char *tagZB_NV_prgs = "zb.nv"; 120 | const char *tagESP_FW_prgs = "esp.fp"; 121 | 122 | const char *hwConfigKey = "hw-config"; 123 | const char *boardKey = "board"; 124 | const char *addrKey = "addr"; 125 | const char *pwrPinKey = "pwrPin"; 126 | const char *mdcPinKey = "mdcPin"; 127 | const char *mdiPinKey = "mdiPin"; 128 | const char *phyTypeKey = "phyType"; 129 | const char *clkModeKey = "clkMode"; 130 | const char *pwrAltPinKey = "pwrAltPin"; 131 | const char *btnPinKey = "btnPin"; 132 | const char *btnPlrKey = "btnPlr"; 133 | const char *uartSelPinKey = "uartSelPin"; 134 | const char *uartSelPlrKey = "uartSelPlr"; 135 | const char *ledModePinKey = "ledModePin"; 136 | const char *ledModePlrKey = "ledModePlr"; 137 | const char *ledPwrPinKey = "ledPwrPin"; 138 | const char *ledPwrPlrKey = "ledPwrPlr"; 139 | const char *zbTxPinKey = "zbTxPin"; 140 | const char *zbRxPinKey = "zbRxPin"; 141 | const char *zbRstPinKey = "zbRstPin"; 142 | const char *zbBslPinKey = "zbBslPin"; 143 | const char *contTypeText = "text/plain"; -------------------------------------------------------------------------------- /src/per.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | // #include //not available now 11 | 12 | #include "config.h" 13 | #include "web.h" 14 | #include "log.h" 15 | #include "etc.h" 16 | #include "const/zones.h" 17 | // #include "const/hw.h" 18 | #include "zb.h" 19 | #include "per.h" 20 | 21 | #include 22 | 23 | // extern struct ConfigSettingsStruct ConfigSettings; 24 | extern BrdConfigStruct brdConfigs[BOARD_CFG_CNT]; 25 | 26 | extern struct ThisConfigStruct hwConfig; 27 | // extern struct CurrentModesStruct modes; 28 | 29 | extern struct SystemConfigStruct systemCfg; 30 | extern struct NetworkConfigStruct networkCfg; 31 | extern struct VpnConfigStruct vpnCfg; 32 | extern struct MqttConfigStruct mqttCfg; 33 | 34 | extern struct SysVarsStruct vars; 35 | 36 | extern LEDControl ledControl; 37 | 38 | extern CCTools CCTool; 39 | 40 | // extern int btnFlag; 41 | int btnFlag = 0; 42 | 43 | #include 44 | 45 | //Ticker tmrBtnLongPress(handleLongBtn, 1000, 0, MILLIS); 46 | Ticker tmrBtnLongPress; // Объявление объекта Ticker без параметров 47 | 48 | void handleLongBtn() 49 | { 50 | if (digitalRead(hwConfig.mist.btnPin) != hwConfig.mist.btnPlr) 51 | { 52 | LOGD("press +, %d s", btnFlag); 53 | 54 | btnFlag++; 55 | if (btnFlag >= 3) 56 | { 57 | ledControl.modeLED.mode = LED_FLASH_1Hz; 58 | } 59 | } 60 | else 61 | { 62 | LOGD("press -, %d s", btnFlag); 63 | 64 | if (btnFlag >= 3) 65 | { 66 | 67 | printLogMsg("BTN - 3sec - toggleUsbMode"); 68 | toggleUsbMode(); 69 | } 70 | else 71 | { 72 | printLogMsg("BTN - click - setLedsDisable"); 73 | 74 | setLedsDisable(!vars.disableLeds); 75 | vars.disableLeds = !vars.disableLeds; 76 | } 77 | //tmrBtnLongPress.stop(); 78 | tmrBtnLongPress.detach(); // Использование метода detach() вместо stop() 79 | btnFlag = false; 80 | } 81 | if (btnFlag >= 5) 82 | { 83 | ledControl.modeLED.mode = LED_FLASH_3Hz; 84 | printLogMsg("BTN - 5sec - zigbeeEnableBSL"); 85 | zigbeeEnableBSL(); 86 | //tmrBtnLongPress.stop(); 87 | tmrBtnLongPress.detach(); // Использование метода detach() вместо stop() 88 | btnFlag = false; 89 | } 90 | } 91 | 92 | void toggleUsbMode() 93 | { 94 | if (systemCfg.workMode != WORK_MODE_USB) 95 | { 96 | systemCfg.workMode = WORK_MODE_USB; 97 | } 98 | else 99 | { 100 | systemCfg.workMode = WORK_MODE_NETWORK; 101 | } 102 | saveSystemConfig(systemCfg); 103 | LOGD("Change mode to %s", String(systemCfg.workMode).c_str()); 104 | 105 | if (vars.hwLedUsbIs) 106 | { 107 | ledControl.modeLED.mode = LED_ON; 108 | } 109 | restartDevice(); 110 | } 111 | 112 | void buttonInit() 113 | { 114 | pinMode(hwConfig.mist.btnPin, INPUT); 115 | vars.hwBtnIs = true; 116 | } 117 | 118 | void buttonSetup() 119 | { 120 | // hard reset BTN 121 | // #if BUILD_ENV_NAME != debug 122 | if (digitalRead(hwConfig.mist.btnPin) != hwConfig.mist.btnPlr) 123 | { 124 | LOGW("!!! Entering hard reset mode !!!"); 125 | uint8_t counter = 0; 126 | while (digitalRead(hwConfig.mist.btnPin) != hwConfig.mist.btnPlr) 127 | { 128 | if (counter >= 10) 129 | { 130 | factoryReset(); 131 | } 132 | else 133 | { 134 | counter++; 135 | LOGW("%d", counter); 136 | delay(200); 137 | } 138 | } 139 | LOGI("Btn released, so exit"); 140 | } 141 | // #endif 142 | if (hwConfig.mist.btnPlr) 143 | { 144 | attachInterrupt(digitalPinToInterrupt(hwConfig.mist.btnPin), btnInterrupt, FALLING); 145 | } 146 | else 147 | { 148 | attachInterrupt(digitalPinToInterrupt(hwConfig.mist.btnPin), btnInterrupt, RISING); 149 | } 150 | } 151 | 152 | void buttonLoop() 153 | { 154 | if (digitalRead(hwConfig.mist.btnPin) != hwConfig.mist.btnPlr) // pressed 155 | { 156 | //if (tmrBtnLongPress.state() == STOPPED) 157 | if (!tmrBtnLongPress.active()) // Проверка активности таймера с помощью метода active() 158 | { 159 | //tmrBtnLongPress.start(); 160 | tmrBtnLongPress.attach(1, handleLongBtn); // Запуск таймера с интервалом 1 секунда и функцией обратного вызова handleLongBtn 161 | } 162 | } 163 | //tmrBtnLongPress.update(); 164 | } 165 | 166 | IRAM_ATTR bool debounce() 167 | { 168 | volatile static unsigned long lastFire = 0; 169 | if (millis() - lastFire < DEBOUNCE_TIME) 170 | { // Debounce 171 | return 0; 172 | } 173 | lastFire = millis(); 174 | return 1; 175 | } 176 | 177 | IRAM_ATTR void btnInterrupt() 178 | { 179 | if (debounce()) 180 | { 181 | if (!btnFlag) 182 | { 183 | btnFlag = true; 184 | } 185 | } 186 | } 187 | 188 | void ledModeSetup() 189 | { 190 | pinMode(hwConfig.mist.ledModePin, OUTPUT); 191 | vars.hwLedUsbIs = true; 192 | 193 | ledControl.modeLED.name = "Mode"; 194 | ledControl.modeLED.pin = hwConfig.mist.ledModePin; 195 | ledControl.modeLED.active = true; 196 | ledControl.modeLED.mode = LED_OFF; 197 | 198 | LOGD("%d", ledControl.modeLED.mode); 199 | 200 | xTaskCreate(ledTask, "MODE LED Task", 2048, &ledControl.modeLED, 7, NULL); 201 | } 202 | 203 | void ledPwrSetup() 204 | { 205 | pinMode(hwConfig.mist.ledPwrPin, OUTPUT); 206 | vars.hwLedPwrIs = true; 207 | 208 | ledControl.powerLED.name = "Power"; 209 | ledControl.powerLED.pin = hwConfig.mist.ledPwrPin; 210 | ledControl.powerLED.active = true; 211 | ledControl.powerLED.mode = LED_OFF; 212 | 213 | LOGD("%d", ledControl.powerLED.mode); 214 | 215 | xTaskCreate(ledTask, "PWR LED Task", 2048, &ledControl.powerLED, 6, NULL); 216 | } -------------------------------------------------------------------------------- /.github/scripts/update_devices.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def read_hw_file(hw_file_path): 4 | with open(hw_file_path, 'r') as file: 5 | return file.read() 6 | 7 | def parse_brd_configs(hw_content): 8 | pattern = re.compile(r'BrdConfigStruct brdConfigs\[\] = \{(.*?)\};', re.DOTALL) 9 | match = pattern.search(hw_content) 10 | if match: 11 | print("Found brdConfigs:") 12 | print (match.group(1)) 13 | return match.group(1) 14 | print("brdConfigs not found") 15 | return "" 16 | 17 | def extract_devices(brd_configs, mist_configs): 18 | devices = [] 19 | # device_pattern = re.compile(r'\{\s*"([^"]+)",\s*\.ethConfigIndex = (-?\d+),\s*\.zbConfigIndex = -?\d+,\s*\.mistConfigIndex = (-?\d+)\s*\}', re.DOTALL) 20 | # new style {"SLS-classic", -1, 7, 3}, // 0 21 | device_pattern = re.compile(r'\{\s*"([^"]+)",\s*(-?\d+),\s*(-?\d+),\s*(-?\d+)\s*\}', re.DOTALL) 22 | for device_match in device_pattern.finditer(brd_configs): 23 | print(f"Found device: {device_match.group(1)}, ethConfigIndex: {device_match.group(2)}, zbConfigIndex: {device_match.group(3)}, mistConfigIndex: {device_match.group(4)}") 24 | 25 | device_name = device_match.group(1) 26 | eth_config_index = int(device_match.group(2)) 27 | mist_config_index = int(device_match.group(4)) 28 | 29 | eth_is = eth_config_index > -1 30 | btn_is = mist_configs[mist_config_index]['btnPin'] > -1 31 | led_is = mist_configs[mist_config_index]['ledModePin'] > -1 or mist_configs[mist_config_index]['ledPwrPin'] > -1 32 | 33 | device = { 34 | 'name': device_name, 35 | 'eth': ':white_check_mark:' if eth_is else ':x:', 36 | 'button': ':white_check_mark:' if btn_is else ':x:', 37 | 'led': ':white_check_mark:' if led_is else ':x:', 38 | 'network_usb': ':white_check_mark:' 39 | } 40 | devices.append(device) 41 | print(f"Extracted device: {device}") 42 | return devices 43 | 44 | def parse_mist_configs(hw_content): 45 | pattern = re.compile(r'MistConfig mistConfigs\[\] = \{(.*?)\};', re.DOTALL) 46 | match = pattern.search(hw_content) 47 | if match: 48 | print("Found mistConfigs") 49 | mist_configs = match.group(1) 50 | return extract_mist_configs(mist_configs) 51 | print("mistConfigs not found") 52 | return [] 53 | 54 | def extract_mist_configs(mist_configs): 55 | configs = [] 56 | config_pattern = re.compile(r'\{\s*\.btnPin = (-?\d+),\s*\.btnPlr = \d+,\s*\.uartSelPin = -?\d+,\s*\.uartSelPlr = \d+,\s*\.ledModePin = (-?\d+),\s*\.ledModePlr = \d+,\s*\.ledPwrPin = (-?\d+),\s*\.ledPwrPlr = \d+\s*\}', re.DOTALL) 57 | for config_match in config_pattern.finditer(mist_configs): 58 | config = { 59 | 'btnPin': int(config_match.group(1)), 60 | 'ledModePin': int(config_match.group(2)), 61 | 'ledPwrPin': int(config_match.group(3)), 62 | } 63 | configs.append(config) 64 | print(f"Extracted mistConfig: {config}") 65 | return configs 66 | 67 | def read_features_file(features_file_path): 68 | with open(features_file_path, 'r') as file: 69 | return file.read() 70 | 71 | def extract_existing_links(features_content): 72 | device_links = {} 73 | table_pattern = re.compile(r'\| \[(.*?)\]\((.*?)\)', re.DOTALL) 74 | for match in table_pattern.finditer(features_content): 75 | device_name = match.group(1) 76 | link = match.group(2) 77 | device_links[device_name] = link 78 | print(f"Found link: {device_name} -> {link}") 79 | return device_links 80 | 81 | def update_features_content(features_content, devices, device_links): 82 | # Define the regular expression to match the whole Supported devices section 83 | table_pattern = re.compile(r'(.*## 🎮 Supported devices)(\s+\| .+\|.+?)(\* Some devices do not support all features.*)', re.DOTALL) 84 | match = table_pattern.search(features_content) 85 | if match: 86 | 87 | header = match.group(1) 88 | footer = match.group(3) 89 | 90 | updated_devices_table = "| Device Name | Button | ESP32 LEDs | Remote Network / USB mode selection | Ethernet |\n" 91 | updated_devices_table += "| :---------------------------------------------------------- | :----------------: | :----------------: | :---------------------------------: | :----------------: |\n" 92 | 93 | for device in devices: 94 | device_name = device['name'] 95 | link = device_links.get(device_name, "") 96 | if link: 97 | device_name = f"[{device_name}]({link})" 98 | device_row = f"| {device_name} | {device['button']} | {device['led']} | {device['network_usb']} | {device['eth']} |\n" 99 | updated_devices_table += device_row 100 | 101 | updated_features_content = header + "\n\n" + updated_devices_table + "\n" + footer 102 | print("Updated features content") 103 | return updated_features_content 104 | print("Supported devices section not found") 105 | return features_content 106 | 107 | def write_features_file(features_file_path, updated_content): 108 | with open(features_file_path, 'w') as file: 109 | file.write(updated_content) 110 | print(f"Updated features file: {features_file_path}") 111 | 112 | def main(): 113 | hw_file_path = 'main_branch/src/const/hw.cpp' 114 | features_file_path = 'mkdocs_branch/docs/features.md' 115 | 116 | hw_content = read_hw_file(hw_file_path) 117 | print(f"Read hw.cpp content: {len(hw_content)} characters") 118 | 119 | brd_configs = parse_brd_configs(hw_content) 120 | mist_configs = parse_mist_configs(hw_content) 121 | 122 | devices = extract_devices(brd_configs, mist_configs) 123 | 124 | features_content = read_features_file(features_file_path) 125 | print(f"Read features.md content: {len(features_content)} characters") 126 | 127 | device_links = extract_existing_links(features_content) 128 | 129 | updated_content = update_features_content(features_content, devices, device_links) 130 | 131 | write_features_file(features_file_path, updated_content) 132 | 133 | if __name__ == "__main__": 134 | main() 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XZG Firmware 2 |
3 | GitHub version 4 | GitHub Actions Workflow Status 5 | GitHub download 6 | GitHub Issues or Pull Requests 7 | License 8 |
9 |
10 |

11 | XZG logo 12 |

13 | XZG Firmware unifies the best innovations from
14 | previous Zigbee gateway projects into a single, comprehensive solution.
15 |
16 | By focusing the community's efforts on enhancing one product, XZG aims to streamline development,
17 | thereby improving the capabilities and efficiency of your Zigbee Gateways. 🌍 18 |
19 |

20 | 21 | ## 🍓 Firmware features 22 | 23 | Visit [features page](https://xzg.xyzroe.cc/features/) to get information 24 | 25 | https://github.com/xyzroe/XZG/assets/6440415/ae312626-f1d9-41c4-b982-11a1f9ba9ed5 26 | 27 | ## 📔 Documentation 28 | 29 | Visit [Wiki page](https://xzg.xyzroe.cc/quick-start/) to get information 30 | 31 | ## 🚀 Installation 32 | 33 | For a quick setup, use [XZG Web Flasher](https://xzg.xyzroe.cc/install) for an easy plug-and-flash experience. 34 | 35 | Please follow the installation guide tailored to your hardware. 36 | 37 | ## 🛠️ Compiling from source 38 | 39 | ### VS Code 40 | - You need npm and Python installed 41 | - Install Visual Studio Code (VSC) 42 | - Install PlatformIO extension to VSC 43 | - Clone this repository 44 | `git clone --recurse-submodules https://github.com/xyzroe/XZG.git` 45 | - Open `XZG.code-workspace` in VSC 46 | - Press "PlatformIO: Build" and wait until XZG*.bin are generated 47 | 48 | ### Linux CLI 49 | - You need npm ad Python installed 50 | - Install PlatformIO Core (it's in many package managers) 51 | - Clone this repository 52 | `git clone --recurse-submodules https://github.com/xyzroe/XZG.git` 53 | - Use `pio run` to build default environment 54 | - Binaries output to .pio/build/name_of_env/ 55 | - Use `pio run -t upload` to build and upload 56 | firmware image 57 | - Use `-e` flag to select a specific build: 58 | `pio run -e env_name -t upload` 59 | 60 | ### Language Server Setup 61 | - LSP (e.g. clangd and Neovim) users need to run 62 | `pio run -t compiledb` to 63 | generate a "compile_commands.json" 64 | 65 | ### Github 66 | - Fork this repository; 67 | - Made your changes; 68 | - Push a new tag to run workflow; 69 | - Just wait and get new release; 70 | 71 | ### Gitpod 72 | 73 | [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/xyzroe/XZG) 74 | 75 | ## 🖥️ Contribute 76 | 77 | Contributions are welcome! If you'd like to help improve the XZG Firmware, you can: 78 | 79 | - Provide Pull Requests with enhancements or fixes. Please see our [contribution guidelines](CONTRIBUTING.md). 80 | - Test newly released features and report issues. 81 | - Help expand our documentation for better user support. 82 | 83 | ## 🎉 Credits 84 | 85 | 86 | 87 | 88 | 89 | Active Contributors of xyzroe/XZG - Last 28 days 90 | 91 | 92 | 93 | 94 | Thanks to all the developers and contributors who make this project possible, and special thanks to [@mercenaruss](https://github.com/mercenaruss/) for **Zig Star devices development**. 95 | 96 | #### All contributors: 97 | 98 | 99 | 100 | Special thanks to all third-party library authors. Their work has significantly contributed to this project: 101 | 102 | - [espressif / arduino-esp32](https://github.com/espressif/arduino-esp32), 103 | - [esprfid / esp-rfid](https://github.com/esprfid/esp-rfid), 104 | - [fairecasoimeme / zigate-ethernet](https://github.com/fairecasoimeme/ZiGate-Ethernet), 105 | - [bblanchon / arduinojson](https://github.com/bblanchon/ArduinoJson), 106 | - [rLOGDacco / circularbuffer](https://github.com/rLOGDacco/CircularBuffer), 107 | - [sstaub / ticker](https://github.com/sstaub/Ticker), 108 | - [vurtun / lib](https://github.com/vurtun/lib), 109 | - [Tinkerforge / WireGuard-ESP32-Arduino](https://github.com/Tinkerforge/WireGuard-ESP32-Arduino), 110 | - [sstaub / Ticker](https://github.com/sstaub/Ticker), 111 | - [Martin-Laclaustra / CronAlarms](https://github.com/Martin-Laclaustra/CronAlarms), 112 | - [xreef / WebServer-Esp8266-ESP32-Tutorial](https://github.com/xreef/WebServer-Esp8266-ESP32-Tutorial), 113 | - [marvinroger / async-mqtt-client](https://github.com/marvinroger/async-mqtt-client) 114 | 115 | 116 | ## 📄 License 117 | 118 | XZG Firmware is released under the **GNU General Public License v3.0**. See the [LICENSE](LICENSE) file for more details. 119 | 120 | Third-party libraries used in this project are under their respective licenses. Please refer to each for more information. 121 | 122 | --- 123 | 124 |
Created with ❤️ by xyzroe © 2024
125 | 126 | --- 127 | -------------------------------------------------------------------------------- /.github/workflows/build_fw.yml: -------------------------------------------------------------------------------- 1 | name: Build, release, push firmware 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | tags: 10 | - "*" 11 | 12 | jobs: 13 | build_release_push: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Clone repository 17 | uses: actions/checkout@v3 18 | with: 19 | submodules: "recursive" 20 | fetch-depth: 0 21 | 22 | - name: Get Release tag 23 | id: get_tag 24 | shell: bash 25 | run: | 26 | value=${GITHUB_REF#refs/tags/} 27 | echo "tag=$value" >> $GITHUB_OUTPUT 28 | 29 | - name: Display src/version.h before update 30 | run: cat src/version.h 31 | 32 | - name: Update version in source code 33 | run: | 34 | sed -i 's/#define VERSION.*/#define VERSION "${{ steps.get_tag.outputs.tag }}"/' src/version.h 35 | 36 | - name: Display src/version.h after update 37 | run: cat src/version.h 38 | 39 | - name: Install Node JS 40 | uses: actions/setup-node@v3 41 | with: 42 | node-version: 18 43 | 44 | - uses: actions/cache@v3 45 | with: 46 | path: | 47 | ~/.cache/pip 48 | ~/.platformio/.cache 49 | key: ${{ runner.os }}-pio 50 | 51 | - name: Install Python 52 | uses: actions/setup-python@v4 53 | with: 54 | python-version: "3.9" 55 | 56 | - name: Install PlatformIO Core 57 | run: pip install --upgrade platformio==6.1.15 58 | 59 | - name: Build PlatformIO Project 60 | run: pio run 61 | 62 | - name: Get last commit message 63 | id: get_commit_message 64 | run: | 65 | currentTag=${{ steps.get_tag.outputs.tag }} 66 | badgeText="![GitHub Downloads](https://img.shields.io/github/downloads/xyzroe/XZG/${currentTag}/total)" 67 | printf -v badgeTextWithNewlines "%s\n\n" "$badgeText" 68 | commitMessage=$(git log -1 --pretty=%B | tail -n +2) 69 | fullCommitMessage="${badgeTextWithNewlines}${commitMessage}" 70 | echo "releaseMessage<> $GITHUB_ENV 71 | printf "%s\n" "$fullCommitMessage" >> $GITHUB_ENV 72 | echo "EOF" >> $GITHUB_ENV 73 | echo "commitMessage<> $GITHUB_ENV 74 | printf "%s\n" "$commitMessage" >> $GITHUB_ENV 75 | echo "EOF" >> $GITHUB_ENV 76 | env: 77 | GITHUB_REF: ${{ github.ref }} 78 | 79 | - name: Create manifest.json for ESP Web Tools 80 | run: | 81 | cat << EOF > manifest.json 82 | { 83 | "name": "XZG Firmware", 84 | "version": "${{ steps.get_tag.outputs.tag }}", 85 | "builds": [ 86 | { 87 | "chipFamily": "ESP32", 88 | "improv": false, 89 | "parts": [ 90 | { 91 | "path": "https://raw.githubusercontent.com/${{ github.repository }}/releases/${{ steps.get_tag.outputs.tag }}/XZG_${{ steps.get_tag.outputs.tag }}.full.bin", 92 | "offset": 0 93 | } 94 | ] 95 | } 96 | ] 97 | } 98 | EOF 99 | echo "Manifest file created." 100 | 101 | - name: Get the latest commit SHA for the whole project 102 | id: get_fw_commit_sha 103 | run: echo "::set-output name=fw_commit_sha::$(git log -n 1 --pretty=format:%h)" 104 | 105 | - name: Get the latest commit SHA in src/websrc 106 | id: get_fs_commit_sha 107 | run: echo "::set-output name=fs_commit_sha::$(git log -n 1 --pretty=format:%h -- src/websrc)" 108 | 109 | - name: Create xzg.json for firmware update 110 | run: | 111 | cat << EOF > xzg.json 112 | { 113 | "name": "XZG Firmware", 114 | "version": "${{ steps.get_tag.outputs.tag }}", 115 | "fw_sha": "${{ steps.get_fw_commit_sha.outputs.fw_commit_sha }}", 116 | "fs_sha": "${{ steps.get_fs_commit_sha.outputs.fs_commit_sha }}" 117 | } 118 | EOF 119 | echo "xzg.json file created." 120 | 121 | - name: Display xzg.json 122 | run: cat xzg.json 123 | 124 | - name: Release 125 | uses: softprops/action-gh-release@v1 126 | with: 127 | generate_release_notes: false 128 | name: "${{ steps.get_tag.outputs.tag }}" 129 | body: ${{ env.releaseMessage }} 130 | files: | 131 | bin/XZG_${{ steps.get_tag.outputs.tag }}.ota.bin 132 | bin/XZG_${{ steps.get_tag.outputs.tag }}.full.bin 133 | bin/XZG_${{ steps.get_tag.outputs.tag }}.fs.bin 134 | 135 | - name: Checkout releases branch 136 | uses: actions/checkout@v3 137 | with: 138 | ref: releases 139 | path: releases 140 | 141 | - name: Copy files to releases directory 142 | run: | 143 | mkdir -p releases/${{ steps.get_tag.outputs.tag }} 144 | cp ./bin/XZG_${{ steps.get_tag.outputs.tag }}.full.bin releases/${{ steps.get_tag.outputs.tag }}/ 145 | cp manifest.json releases/${{ steps.get_tag.outputs.tag }}/ 146 | echo "Files copied to releases directory." 147 | 148 | - name: Prepare latest release directory 149 | run: | 150 | if [ -d releases/latest ]; then 151 | rm -rf releases/latest 152 | fi 153 | mkdir -p releases/latest 154 | 155 | - name: Copy file to latest release directory 156 | run: | 157 | cp xzg.json releases/latest/ 158 | # cp ./bin/XZG_${{ steps.get_tag.outputs.tag }}.ota.bin releases/latest/XZG.ota.bin 159 | # cp ./bin/XZG_${{ steps.get_tag.outputs.tag }}.fs.bin releases/latest/XZG.fs.bin 160 | echo "File copied to latest release directory." 161 | 162 | - name: Commit and push files to releases branch 163 | run: | 164 | cd releases 165 | git config --local user.email "action@github.com" 166 | git config --local user.name "GitHub Action" 167 | if [[ -n $(git status --porcelain) ]]; then 168 | git add . 169 | git commit -m "${{ steps.get_tag.outputs.tag }}" 170 | git push origin releases 171 | else 172 | echo "No changes to commit" 173 | fi 174 | 175 | - name: Send Telegram Notification about release 176 | uses: appleboy/telegram-action@master 177 | with: 178 | to: ${{ secrets.TELEGRAM_CHAT_ID }} 179 | token: ${{ secrets.TELEGRAM_BOT_TOKEN }} 180 | format: markdown 181 | message: | 182 | ${{ env.commitMessage }} 183 | 184 | [${{ steps.get_tag.outputs.tag }}](https://github.com/${{ github.repository }}/releases/tag/${{ steps.get_tag.outputs.tag }}) 185 | 186 | - name: Send Discord Notification about release 187 | run: | 188 | json_payload=$(jq -n --arg content "https://github.com/${{ github.repository }}/releases/tag/${{ steps.get_tag.outputs.tag }}" '{content: $content}') 189 | curl -H "Content-Type: application/json" \ 190 | -d "$json_payload" \ 191 | ${{ secrets.DISCORD_WEBHOOK }} -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | **Any contribution helps our team and makes XZG Firmware better for the entire community!** 4 | 5 | Everybody is welcome and invited to contribute to XZG Firmware project by: 6 | 7 | * Testing newly released features and reporting issues. 8 | * Providing Pull Requests (Features, Proof of Concepts, Language files or Fixes). 9 | * Contributing missing documentation for features. 10 | 11 | This document describes rules that are in effect for this repository, meant for handling issues by contributors in the issue tracker and PRs. 12 | 13 | ## Opening New Issues 14 | 15 | **Issue tracker is NOT a general discussion forum!** 16 | 1. Opening an issue means that a problem exists in the code and should be addressed by the project contributors. 17 | 2. When opening an issue, it is required to fill out the presented template. The requested information is important! If the template is ignored or insufficient info about the issue is provided, the issue may be closed. 18 | 3. Questions of type "How do I..." or "Can you please help me with..." or "Can ZigStar UZG do..." WILL NOT be handled here. Such questions should be directed at a discussion forum or to the ZigStar Support Chat. All issues of this type will be closed with a simple reference to this contributing policy. 19 | 4. Issues about topics already handled in the documentation will be closed in a similar manner. 20 | 5. Issues for unmerged PRs will be closed. If there is an issue with a PR, the explanation should be added to the PR itself. 21 | 6. Issues with accompanied investigation that shows the root of the problem should be given priority. 22 | 7. Duplicate issues will be closed. 23 | 24 | ## Triaging of Issues/PR's 25 | 26 | 1. Any contributor to the project can participate in the triaging process, if he/she chooses to do so. 27 | 2. An issue that needs to be closed, either due to not complying with this policy, or for other reasons, should be closed by a contributor. 28 | 3. Issues that are accepted should be marked with appropriate labels. 29 | 4. Issues that could impact functionality for many users should be considered severe. 30 | 5. Issues caused by the SDK or chip should not be marked severe, as there usually isn’t much to be done. Common sense should be applied when deciding. Such issues should be documented in the Wiki, for reference by users. 31 | 6. Issues with feature requests should be discussed for viability/desirability. 32 | 7. Feature requests or changes that are meant to address a very specific/limited use case, especially if at the expense of increased code complexity, may be denied, or may be required to be redesigned, generalized, or simplified. 33 | 8. Feature requests that are not accompanied by a PR: 34 | * could be closed immediately (denied). 35 | * could be closed after some predetermined period of time (left as candidate for somebody to pick up). 36 | 9. In some cases, feedback may be requested from the issue reporter, either as additional info for clarification, additional testing, or other. If no feedback is provided, the issue may be closed by a contributor or after 30 days by the STALE bot. 37 | 38 | ## Pull requests 39 | 40 | A Pull Request (PR) is the process where code modifications are managed in GitHub. 41 | 42 | The process is straight-forward. 43 | 44 | - Read [How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#best-practices-for-faster-reviews) by Kubernetes (but skip step 0) 45 | - Fork the [XZG repository](https://github.com/xyzroe/XZG). 46 | - Create new branch for your modifications. 47 | - Write/Change the code in your fork for a new feature, bug fix, new sensor, optimization, etc. 48 | - Ensure tests work. 49 | - Create a Pull Request against the your branch to [XZG repository](https://github.com/xyzroe/XZG). 50 | 51 | 1. All pull requests must be done against the development branch. 52 | 2. Only relevant files should be touched (Also beware if your editor has auto-formatting feature enabled). 53 | 3. Only one feature/fix should be added per PR. 54 | 4. If adding a new functionality (new hardware, new library support) not related to an existing component move it to it's own modules (.ino file). 55 | 5. PRs that don't compile (fail in CI Tests) or cause coding errors will not be merged. Please fix the issue. Same goes for PRs that are raised against older commit in development - you might need to rebase and resolve conflicts. 56 | 6. All pull requests should undergo peer review by at least one contributor other than the creator, excepts for the owner. 57 | 7. All pull requests should consider updates to the documentation. 58 | 8. Pull requests that address an outstanding issue, particularly an issue deemed to be severe, should be given priority. 59 | 9. If a PR is accepted, then it should undergo review and updated based on the feedback provided, then merged. 60 | 10. By submitting a PR, it is needed to use the provided PR template and check all boxes, performing the required tasks and accepting the CLA. 61 | 11. Pull requests that don't meet the above will be denied and closed. 62 | 63 | -------------------------------------- 64 | 65 | ## Contributor License Agreement (CLA) 66 | 67 | ``` 68 | By making a contribution to this project, I certify that: 69 | 70 | (a) The contribution was created in whole or in part by me and I 71 | have the right to submit it under the GPL-3.0 license; or 72 | 73 | (b) The contribution is based upon previous work that, to the best 74 | of my knowledge, is covered under an appropriate open source 75 | license and I have the right under that license to submit that 76 | work with modifications, whether created in whole or in part 77 | by me, under the GPL-3.0 license; or 78 | 79 | (c) The contribution was provided directly to me by some other 80 | person who certified (a), (b) or (c) and I have not modified 81 | it. 82 | 83 | (d) I understand and agree that this project and the contribution 84 | are public and that a record of the contribution (including all 85 | personal information I submit with it) is maintained indefinitely 86 | and may be redistributed consistent with this project or the open 87 | source license(s) involved. 88 | ``` 89 | 90 | This Contributor License Agreement (CLA) was adopted on April 1st, 2019. 91 | 92 | The text of this license is available under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/). It is based on the Linux [Developer Certificate Of Origin](http://elinux.org/Developer_Certificate_Of_Origin), but is modified to explicitly use the GPL-3.0 license and not mention sign-off (due to GitHub.com keeps an historial, with your user name, of PRs' commits and all editions on PR's comments). 93 | 94 | To accept the CLA it is required to put a x between [ ] on `[ ] I accept the CLA` in the PR template when submitting it. The [ ] is an opt-in box, so you have to manually accept it. 95 | 96 | **Why a CLA ?** 97 | 98 | _"A Contributor Licence Agreement (CLA) is strongly recommended when accepting third party contributions to an open development project, such as an open source software project. In order to redistribute contributions, it is necessary to ensure that the project has the necessary rights to do so. A Contributor Licence Agreement is a lightweight agreement, signed by the copyright holder, that grants the necessary rights for the contribution to be redistributed as part of the project."_ [OSS Watch](http://oss-watch.ac.uk/resources/cla) 99 | 100 | A CLA is a legal document in which you state _you are entitled to contribute the code/documentation/translation to the project_ you’re contributing to and that _you are willing to have it used in distributions and derivative works_. This means that should there be any kind of legal issue in the future as to the origins and ownership of any particular piece of code, then that project has the necessary forms on file from the contributor(s) saying they were permitted to make this contribution. 101 | 102 | CLA is a safety because it also ensures that once you have provided a contribution, you cannot try to withdraw permission for its use at a later date. People can therefore use that software, confident that they will not be asked to stop using pieces of the code at a later date. 103 | 104 | A __license__ grants "outbound" rights to the user of project. 105 | 106 | A __CLA__ enables a contributor to grant "inbound" rights to a project. 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/websrc/html/general.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
18 |
19 |
20 |
21 |
22 |
24 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
93 |
94 |
95 |
96 |
97 |
99 |
100 |
105 |
110 |
111 |
112 |
113 |
115 |
116 |
-------------------------------------------------------------------------------- /tools/webfilesbuilder/gulpfile_old.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var fs = require('fs'); 3 | var concat = require('gulp-concat'); 4 | var gzip = require('gulp-gzip'); 5 | var flatmap = require('gulp-flatmap'); 6 | var path = require('path'); 7 | var htmlmin = require('gulp-htmlmin'); 8 | var uglify = require('gulp-uglify'); 9 | var pump = require('pump'); 10 | var purgecss = require('gulp-purgecss'); 11 | var cssnano = require('gulp-cssnano'); 12 | const jsonminify = require('gulp-jsonminify'); 13 | const rename = require('gulp-rename'); 14 | 15 | function stylesConcat() { 16 | return gulp.src(['../../src/websrc/css/custom.css', '../../src/websrc/css/bootstrap.min.css']) 17 | .pipe(concat({ 18 | path: 'style.css', 19 | stat: { 20 | mode: 0666 21 | } 22 | })) 23 | //.pipe(purgecss({ 24 | // content: ['../../src/websrc/html/*.html', '../../src/websrc/js/*.js'] 25 | //})) 26 | .pipe(cssnano({preset: 'advanced'})) 27 | .pipe(gulp.dest('../../src/websrc/min/css/')) 28 | .pipe(gzip({ 29 | append: true 30 | })) 31 | .pipe(gulp.dest('../../src/websrc/gzipped/css/')); 32 | } 33 | 34 | 35 | function styles(cb) { 36 | var source = "../../src/websrc/gzipped/css/" + "style.css.gz"; 37 | var destination = "../../src/webh/css/" + "style.css.gz.h"; 38 | fs.mkdirSync(path.dirname(destination), { recursive: true }); 39 | 40 | var wstream = fs.createWriteStream(destination); 41 | wstream.on('error', function (err) { 42 | console.log(err); 43 | }); 44 | 45 | var data = fs.readFileSync(source); 46 | 47 | wstream.write('#define required_css_gz_len ' + data.length + '\n'); 48 | wstream.write('const uint8_t required_css_gz[] PROGMEM = {') 49 | 50 | for (i = 0; i < data.length; i++) { 51 | if (i % 1000 == 0) wstream.write("\n"); 52 | wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2)); 53 | if (i < data.length - 1) wstream.write(','); 54 | } 55 | wstream.write('\n};') 56 | wstream.end(); 57 | cb(); 58 | } 59 | 60 | function scriptsgz() { 61 | return gulp.src("../../src/websrc/js/*.js") 62 | .pipe(uglify()) 63 | .pipe(gulp.dest("../../src/websrc/min/js/")) 64 | .pipe(gzip({ append: true })) 65 | .pipe(gulp.dest('../../src/websrc/gzipped/js/')); 66 | } 67 | 68 | function scripts() { 69 | return gulp.src("../../src/websrc/gzipped/js/*.*") 70 | .pipe(flatmap(function (stream, file) { 71 | var filename = path.basename(file.path); 72 | var directory = "../../src/webh/js/"; 73 | fs.mkdirSync(directory, { recursive: true }); 74 | var wstream = fs.createWriteStream(path.join(directory, filename + ".h")); 75 | wstream.on("error", function (err) { 76 | console.log(err); 77 | }); 78 | var data = file.contents; 79 | wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n"); 80 | wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {") 81 | 82 | for (i = 0; i < data.length; i++) { 83 | if (i % 1000 == 0) wstream.write("\n"); 84 | wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2)); 85 | if (i < data.length - 1) wstream.write(','); 86 | } 87 | wstream.write("\n};") 88 | wstream.end(); 89 | return stream; 90 | })); 91 | } 92 | 93 | function fontgz() { 94 | return gulp.src("../../src/websrc/fonts/*.*") 95 | .pipe(gulp.dest("../../src/websrc/fonts/")) 96 | .pipe(gzip({ 97 | append: true 98 | })) 99 | .pipe(gulp.dest('../../src/websrc/gzipped/fonts/')); 100 | } 101 | 102 | function fonts() { 103 | return gulp.src("../../src/websrc/gzipped/fonts/*.*") 104 | .pipe(flatmap(function (stream, file) { 105 | var filename = path.basename(file.path); 106 | var directory = "../../src/webh/fonts/"; 107 | fs.mkdirSync(directory, { recursive: true }); 108 | var wstream = fs.createWriteStream(path.join(directory, filename + ".h")); 109 | wstream.on("error", function (err) { 110 | console.log(err); 111 | }); 112 | var data = file.contents; 113 | wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n"); 114 | wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {") 115 | 116 | for (i = 0; i < data.length; i++) { 117 | if (i % 1000 == 0) wstream.write("\n"); 118 | wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2)); 119 | if (i < data.length - 1) wstream.write(','); 120 | } 121 | wstream.write("\n};") 122 | wstream.end(); 123 | return stream; 124 | })); 125 | } 126 | 127 | function imggz() { 128 | return gulp.src("../../src/websrc/img/*.*") 129 | .pipe(gulp.dest("../../src/websrc/img/")) 130 | .pipe(gzip({ 131 | append: true 132 | })) 133 | .pipe(gulp.dest('../../src/websrc/gzipped/img/')); 134 | } 135 | 136 | function imgs() { 137 | return gulp.src("../../src/websrc/gzipped/img/*.*") 138 | .pipe(flatmap(function (stream, file) { 139 | var filename = path.basename(file.path); 140 | var directory = "../../src/webh/img/"; 141 | fs.mkdirSync(directory, { recursive: true }); 142 | var wstream = fs.createWriteStream(path.join(directory, filename + ".h")); 143 | wstream.on("error", function (err) { 144 | console.log(err); 145 | }); 146 | var data = file.contents; 147 | wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n"); 148 | wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {") 149 | 150 | for (i = 0; i < data.length; i++) { 151 | if (i % 1000 == 0) wstream.write("\n"); 152 | wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2)); 153 | if (i < data.length - 1) wstream.write(','); 154 | } 155 | wstream.write("\n};") 156 | wstream.end(); 157 | return stream; 158 | })); 159 | } 160 | 161 | function htmlgz() { 162 | return gulp.src("../../src/websrc/html/*.html") 163 | .pipe(htmlmin({ collapseWhitespace: true, removeComments: true, removeRedundantAttributes: true })) 164 | .pipe(gulp.dest("../../src/websrc/min/html/")) 165 | .pipe(gzip({ append: true })) 166 | .pipe(gulp.dest('../../src/websrc/gzipped/html/')); 167 | } 168 | 169 | function htmls() { 170 | return gulp.src("../../src/websrc/gzipped/html/*.*") 171 | .pipe(flatmap(function (stream, file) { 172 | var filename = path.basename(file.path); 173 | var directory = "../../src/webh/html/"; 174 | fs.mkdirSync(directory, { recursive: true }); 175 | var wstream = fs.createWriteStream(path.join(directory, filename + ".h")); 176 | wstream.on("error", function (err) { 177 | console.log(err); 178 | }); 179 | var data = file.contents; 180 | wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n"); 181 | wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {") 182 | 183 | for (i = 0; i < data.length; i++) { 184 | if (i % 1000 == 0) wstream.write("\n"); 185 | wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2)); 186 | if (i < data.length - 1) wstream.write(','); 187 | } 188 | wstream.write("\n};") 189 | wstream.end(); 190 | return stream; 191 | })); 192 | } 193 | 194 | function jsongz() { 195 | return gulp.src("../../src/websrc/json/*.json") 196 | .pipe(jsonminify()) 197 | .pipe(gulp.dest("../../src/websrc/min/json/")) 198 | .pipe(gzip({ append: true })) 199 | .pipe(gulp.dest('../../src/websrc/gzipped/json/')); 200 | } 201 | 202 | 203 | function jsons() { 204 | return gulp.src("../../src/websrc/gzipped/json/*.*") 205 | .pipe(flatmap(function (stream, file) { 206 | var filename = path.basename(file.path); 207 | var directory = "../../src/webh/json/"; 208 | fs.mkdirSync(directory, { recursive: true }); 209 | var wstream = fs.createWriteStream(path.join(directory, filename + ".h")); 210 | wstream.on("error", function (err) { 211 | console.log(err); 212 | }); 213 | var data = file.contents; 214 | wstream.write("#define " + filename.replace(/\.|-/g, "_") + "_len " + data.length + "\n"); 215 | wstream.write("const uint8_t " + filename.replace(/\.|-/g, "_") + "[] PROGMEM = {"); 216 | 217 | for (var i = 0; i < data.length; i++) { 218 | if (i % 1000 == 0) wstream.write("\n"); 219 | wstream.write('0x' + ('00' + data[i].toString(16)).slice(-2)); 220 | if (i < data.length - 1) wstream.write(','); 221 | } 222 | wstream.write("\n};") 223 | wstream.end(); 224 | return stream; 225 | })); 226 | } 227 | 228 | 229 | const styleTasks = gulp.series(stylesConcat, styles); 230 | const scriptTasks = gulp.series(scriptsgz, scripts); 231 | const fontTasks = gulp.series(fontgz, fonts); 232 | const imgTasks = gulp.series(imggz, imgs); 233 | const htmlTasks = gulp.series(htmlgz, htmls); 234 | const jsonTasks = gulp.series(jsongz, jsons); 235 | 236 | exports.xzg = gulp.parallel(styleTasks, scriptTasks, fontTasks, imgTasks, htmlTasks, jsonTasks); 237 | -------------------------------------------------------------------------------- /src/websrc/html/loader.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |
23 | 35 |
36 | 50 | 77 |
78 |
79 | 140 |
141 |
142 |
xyzroe © 2024
144 |
145 |
146 |
147 | 148 | 149 | -------------------------------------------------------------------------------- /src/websrc/html/zigbee.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |
7 |
8 |
9 |
10 |
11 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 |
42 |
43 |
44 |
45 |
46 | 50 |
51 |
52 |
53 |
54 |
55 |
65 |
66 |
67 |
70 |
71 |
72 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | 82 |
83 |
84 |
85 | 86 |
87 |
88 |
89 |
91 | 92 | Coordinator
93 |
94 |
95 |
96 |
97 |
99 | 100 | Router
101 |
102 |
103 |
104 |
105 |
107 | 108 | OpenThread
109 |
110 |
111 |
112 |
113 |
115 |
116 |
117 |
118 | 175 |
-------------------------------------------------------------------------------- /dependencies.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | chmorgan/esp-libhelix-mp3: 3 | component_hash: cbb76089dc2c5749f7b470e2e70aedc44c9da519e04eb9a67d4c7ec275229e53 4 | dependencies: 5 | - name: idf 6 | require: private 7 | version: '>=4.1.0' 8 | source: 9 | registry_url: https://components.espressif.com/ 10 | type: service 11 | version: 1.0.3 12 | espressif/cbor: 13 | component_hash: 440f4ee4504841cc9b4f3a8ef755776a612ac9dace355514c68b999868f990ff 14 | dependencies: 15 | - name: idf 16 | require: private 17 | version: '>=4.3' 18 | source: 19 | registry_url: https://components.espressif.com/ 20 | type: service 21 | version: 0.6.0~1 22 | espressif/esp-dsp: 23 | component_hash: fa7fe74305df6da25867437ebcd4213e047cbfc0556cf92067ab657fce537c6e 24 | dependencies: 25 | - name: idf 26 | require: private 27 | version: '>=4.2' 28 | source: 29 | registry_url: https://components.espressif.com/ 30 | type: service 31 | version: 1.5.2 32 | espressif/esp-modbus: 33 | component_hash: 2168e6b4cbda4d0281a2a2d1a40a3848e231473b2690d73217e3600fd2c98c12 34 | dependencies: 35 | - name: idf 36 | require: private 37 | version: '>=4.3' 38 | source: 39 | registry_url: https://components.espressif.com/ 40 | type: service 41 | version: 1.0.16 42 | espressif/esp-serial-flasher: 43 | component_hash: dcc42a16712a1a636509cf0bf90e14032d7f2141784b533613b267b6aa318d52 44 | dependencies: [] 45 | source: 46 | registry_url: https://components.espressif.com/ 47 | type: service 48 | version: 0.0.11 49 | espressif/esp-zboss-lib: 50 | component_hash: ceb89aaab088a3bc037c608bb7b7925e7116b0a7dda7c3476f44d9b06ff66c0a 51 | dependencies: 52 | - name: idf 53 | require: private 54 | version: '>=5.0' 55 | source: 56 | registry_url: https://components.espressif.com/ 57 | type: service 58 | version: 1.5.0 59 | espressif/esp-zigbee-lib: 60 | component_hash: 238c29955025d3e4f430fd7243ad7f9f038b97775746032478f34532ad5979aa 61 | dependencies: 62 | - name: idf 63 | require: private 64 | version: '>=5.0' 65 | source: 66 | registry_url: https://components.espressif.com/ 67 | type: service 68 | version: 1.5.0 69 | espressif/esp_diag_data_store: 70 | component_hash: 8849195251dbb8a2d7268335277cfa310cef36e4ac1e90cd59ad3be4269a30d7 71 | dependencies: 72 | - name: idf 73 | require: private 74 | version: '>=4.1' 75 | source: 76 | registry_url: https://components.espressif.com/ 77 | type: service 78 | version: 1.0.1 79 | espressif/esp_diagnostics: 80 | component_hash: 2ea46a907cad1842e41a74b9efbd533ddd4908d3ccd06370c1f8355d41dd0342 81 | dependencies: 82 | - name: espressif/rmaker_common 83 | registry_url: https://components.espressif.com/ 84 | require: private 85 | version: ~1.4.0 86 | - name: idf 87 | require: private 88 | version: '>=4.1' 89 | source: 90 | registry_url: https://components.espressif.com/ 91 | type: service 92 | version: 1.2.0 93 | espressif/esp_insights: 94 | component_hash: c2b5219b3343e79e6d62aa5589a204612416abe8bdb6ba11224dca1b9a3c62ba 95 | dependencies: 96 | - name: espressif/cbor 97 | registry_url: https://components.espressif.com/ 98 | require: private 99 | rules: 100 | - if: idf_version >=5.0 101 | version: ~0.6 102 | - name: espressif/esp_diag_data_store 103 | registry_url: https://components.espressif.com/ 104 | require: private 105 | version: ~1.0 106 | - name: espressif/esp_diagnostics 107 | registry_url: https://components.espressif.com/ 108 | require: private 109 | version: '>=1.2.0' 110 | - name: espressif/rmaker_common 111 | registry_url: https://components.espressif.com/ 112 | require: private 113 | version: ~1.4.0 114 | - name: idf 115 | require: private 116 | version: '>=4.1' 117 | source: 118 | registry_url: https://components.espressif.com/ 119 | type: service 120 | version: 1.2.0 121 | espressif/esp_modem: 122 | component_hash: e48da33fee082dd9d9a97a354a228057e07a14ac108766b40ad84e018205410a 123 | dependencies: 124 | - name: idf 125 | require: private 126 | version: '>=4.1' 127 | source: 128 | registry_url: https://components.espressif.com/ 129 | type: service 130 | version: 1.1.0 131 | espressif/esp_rainmaker: 132 | component_hash: f89a4759347f3909417fb33059452f36c86befae9d10bda78b5417b7a5d19d11 133 | dependencies: 134 | - name: espressif/esp_rcp_update 135 | registry_url: https://components.espressif.com/ 136 | require: private 137 | rules: 138 | - if: idf_version >= 5.1 139 | version: ~1.2.0 140 | - name: espressif/esp_schedule 141 | registry_url: https://components.espressif.com/ 142 | require: private 143 | version: ~1.2.0 144 | - name: espressif/esp_secure_cert_mgr 145 | registry_url: https://components.espressif.com/ 146 | require: private 147 | rules: 148 | - if: idf_version >=4.3 149 | version: ^2.2.1 150 | - name: espressif/json_generator 151 | registry_url: https://components.espressif.com/ 152 | require: private 153 | version: ~1.1.1 154 | - name: espressif/json_parser 155 | registry_url: https://components.espressif.com/ 156 | require: private 157 | version: ~1.0.3 158 | - name: espressif/mdns 159 | registry_url: https://components.espressif.com/ 160 | require: private 161 | rules: 162 | - if: idf_version >=5.0 163 | version: ^1.2.0 164 | - name: espressif/network_provisioning 165 | registry_url: https://components.espressif.com/ 166 | require: private 167 | rules: 168 | - if: idf_version >= 5.1 169 | version: ~1.0.0 170 | - name: espressif/rmaker_common 171 | registry_url: https://components.espressif.com/ 172 | require: private 173 | version: ~1.4.6 174 | source: 175 | registry_url: https://components.espressif.com/ 176 | type: service 177 | version: 1.5.0 178 | espressif/esp_rcp_update: 179 | component_hash: c10afbd54a17f27eed880e61262b161656e6d36ad63376c307f9273e99d0abcd 180 | dependencies: 181 | - name: espressif/esp-serial-flasher 182 | registry_url: https://components.espressif.com/ 183 | require: private 184 | version: ~0.0.0 185 | - name: idf 186 | require: private 187 | version: '>=5.0' 188 | source: 189 | registry_url: https://components.espressif.com/ 190 | type: service 191 | version: 1.2.0 192 | espressif/esp_schedule: 193 | component_hash: e202a9c688f7f1ab601efb91d682e4bcfaebc508dcceee1a1e0a0d2d1ca75a26 194 | dependencies: 195 | - name: espressif/rmaker_common 196 | registry_url: https://components.espressif.com/ 197 | require: private 198 | version: ~1.4.2 199 | source: 200 | registry_url: https://components.espressif.com/ 201 | type: service 202 | version: 1.2.0 203 | espressif/esp_secure_cert_mgr: 204 | component_hash: a20007d67e65a000670ab77e45d7554c943eb8dcb0abeada0a57dd9adac3a703 205 | dependencies: 206 | - name: idf 207 | require: private 208 | version: '>=4.3' 209 | source: 210 | registry_url: https://components.espressif.com/ 211 | type: service 212 | version: 2.4.1 213 | espressif/jsmn: 214 | component_hash: d80350c41bbaa827c98a25b6072df00884e72f54885996fab4a4f0aebce6b6c3 215 | dependencies: 216 | - name: idf 217 | require: private 218 | version: '>=4.3' 219 | source: 220 | registry_url: https://components.espressif.com/ 221 | type: service 222 | version: 1.1.0 223 | espressif/json_generator: 224 | component_hash: 45033e1c199b13f1c8c1b544fb7d4e2df6a8e3071ebdcb1b22582b61a7974ff2 225 | dependencies: [] 226 | source: 227 | registry_url: https://components.espressif.com/ 228 | type: service 229 | version: 1.1.2 230 | espressif/json_parser: 231 | component_hash: d74b81729ad06ec11ff5eb5b1b0d7df1d00e6027fc11471f4b139c70dcf1b1e4 232 | dependencies: 233 | - name: espressif/jsmn 234 | registry_url: https://components.espressif.com/ 235 | require: private 236 | rules: 237 | - if: idf_version >=5.0 238 | version: ~1.1 239 | source: 240 | registry_url: https://components.espressif.com/ 241 | type: service 242 | version: 1.0.3 243 | espressif/libsodium: 244 | component_hash: f6e982479a2389cb6868e8fb761cf23aba6c355a8090b3e906299807775f58a3 245 | dependencies: 246 | - name: idf 247 | require: private 248 | version: '>=4.2' 249 | source: 250 | registry_url: https://components.espressif.com/ 251 | type: service 252 | version: 1.0.20~1 253 | espressif/mdns: 254 | component_hash: af6306fe65d637a3683d1cf671508fcedd6b05f9ca029a8815abeab64001fb8d 255 | dependencies: 256 | - name: idf 257 | require: private 258 | version: '>=5.0' 259 | source: 260 | registry_url: https://components.espressif.com/ 261 | type: service 262 | version: 1.4.0 263 | espressif/network_provisioning: 264 | component_hash: ef2e10182fd1861e68b821491916327c25416ca7ae70e5a6e43313dbc71fe993 265 | dependencies: 266 | - name: idf 267 | require: private 268 | version: '>=5.1' 269 | source: 270 | registry_url: https://components.espressif.com/ 271 | type: service 272 | version: 1.0.2 273 | espressif/qrcode: 274 | component_hash: 3b493771bc5d6ad30cbf87c25bf784aada8a08c941504355b55d6b75518ed7bc 275 | dependencies: [] 276 | source: 277 | registry_url: https://components.espressif.com/ 278 | type: service 279 | version: 0.1.0~2 280 | espressif/rmaker_common: 281 | component_hash: a3a1df881278d0351fc850b77792fe8a196ddd6dcacbea203d606329cc6a0239 282 | dependencies: [] 283 | source: 284 | registry_url: https://components.espressif.com/ 285 | type: service 286 | version: 1.4.6 287 | idf: 288 | source: 289 | type: idf 290 | version: 5.1.4 291 | joltwallet/littlefs: 292 | component_hash: 362f1f5beb5087b0c60169aff82676d2d0ffc991ead975212b0cba95959181c5 293 | dependencies: 294 | - name: idf 295 | require: private 296 | version: '>=4.3' 297 | source: 298 | registry_url: https://components.espressif.com/ 299 | type: service 300 | version: 1.14.8 301 | direct_dependencies: 302 | - chmorgan/esp-libhelix-mp3 303 | - espressif/esp-dsp 304 | - espressif/esp-modbus 305 | - espressif/esp-zboss-lib 306 | - espressif/esp-zigbee-lib 307 | - espressif/esp_insights 308 | - espressif/esp_modem 309 | - espressif/esp_rainmaker 310 | - espressif/libsodium 311 | - espressif/mdns 312 | - espressif/network_provisioning 313 | - espressif/qrcode 314 | - espressif/rmaker_common 315 | - idf 316 | - joltwallet/littlefs 317 | manifest_hash: 6e90f5b15283407daa0c179aac46415cf48b3dd974c63431ebecfcee6f2c1510 318 | target: esp32 319 | version: 2.0.0 320 | -------------------------------------------------------------------------------- /src/websrc/js/i18nextHttpBackend.min.js: -------------------------------------------------------------------------------- 1 | !function(e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).i18nextHttpBackend=e()}(function(){return function o(r,i,s){function a(t,e){if(!i[t]){if(!r[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(u)return u(t,!0);throw(e=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",e}n=i[t]={exports:{}},r[t][0].call(n.exports,function(e){return a(r[t][1][e]||e)},n,n.exports,o,r,i,s)}return i[t].exports}for(var u="function"==typeof require&&require,e=0;e 2 |
3 |
4 |
5 |
6 | 7 |
8 |
9 |
10 |
14 |
18 |
19 |
21 |
23 |
25 |
27 |
28 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 |
43 |
47 |
51 |
52 | 61 |
75 |
77 |
80 |
83 |
86 |
89 |

90 |
91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 110 | 111 | 112 | 122 | 123 | 124 |
103 |
104 |
108 |
109 |
113 |
116 |
117 |
121 |
125 |
126 |
127 |
130 |
131 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
150 |
151 | -------------------------------------------------------------------------------- /src/websrc/json/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "c": { 3 | "cancel": "取消", 4 | "cc": "复制到剪贴板", 5 | "conn": "已连接", 6 | "csp": "连接速度", 7 | "cst": "连接状态", 8 | "drm": "不提醒", 9 | "enable": "启用", 10 | "ercn": "错误。\n检查您的网络", 11 | "erss": "保存设置时出错。\n检查您的网络", 12 | "host": "主机名", 13 | "init": "已初始化", 14 | "inst": "安装", 15 | "ltr": "之后", 16 | "mac": "MAC地址", 17 | "mode": "模式", 18 | "now": "现在安装", 19 | "pass": "密码", 20 | "proj": "由 ❤️ 创建", 21 | "save": "保存", 22 | "sure": "确定", 23 | "user": "用户名", 24 | "ok": "好的", 25 | "connecting": "正在连接", 26 | "dis": "已关闭", 27 | "disconn": "已断开连接", 28 | "en": "启用", 29 | "err": "错误", 30 | "nc": "无连接", 31 | "cl": "关闭", 32 | "t": { 33 | "e": "时间结束", 34 | "s": "开始时间" 35 | }, 36 | "cerp": "已达到事件重新连接最大重试次数。\n刷新页面以继续。", 37 | "more": "更多信息" 38 | }, 39 | "md": { 40 | "esp": { 41 | "fr": { 42 | "msg": "这将删除所有设置。", 43 | "tt": "恢复出厂设置警告" 44 | }, 45 | "fu": { 46 | "fri": "正在获取固件信息...", 47 | "glm": "更新到最新的 GitHub 版本。", 48 | "gvm": "更新至 {{ver}} GitHub 版本。", 49 | "gvt": "GitHub 发布", 50 | "lfm": "从本地文件更新。", 51 | "lgds": "正在下载最新的 GitHub 版本...", 52 | "prgs": "更新:{{per}}%", 53 | "tt": "XZG固件更新", 54 | "ucr": "更新完成!\n重启!", 55 | "vgds": "正在从 GitHub 下载版本 {{ver}}...", 56 | "wdm": "正在等待设备...", 57 | "wm": "它将断开所有接口连接" 58 | }, 59 | "rst": { 60 | "msg": "等待重启。\n会自动关闭。", 61 | "nrps": "没有反应;\n检查IP或USB模式是否改变。", 62 | "tt": "重启设备" 63 | }, 64 | "ws": { 65 | "btn": "登录", 66 | "err": "连接错误,请检查SSID、密码并重试", 67 | "msg": "正在连接网络...等待结果。", 68 | "nip": "新的 IP 地址是 {{ip}} 设备现在将重新启动以使新设置生效。", 69 | "tt": "Wi-Fi 网络连接" 70 | }, 71 | "mc": { 72 | "mi": "支持不止一台与您的配置完全相同的设备。\n您需要选择您的设备。", 73 | "tt": "选择您的设备型号" 74 | } 75 | }, 76 | "zb": { 77 | "fu": {}, 78 | "dtc": "协调器", 79 | "dtr": "路由器", 80 | "efr": "获取固件信息时出错
检查您的网络!", 81 | "ncf": "没有可用的协调器固件。", 82 | "nrf": "没有可用的路由器固件。", 83 | "ot": "Zigbee OTA 更新", 84 | "rfm": "正在获取固件信息...", 85 | "dtt": "Open Thread", 86 | "ntf": "Thread 固件丢失。", 87 | "cte": "点击展开信息" 88 | }, 89 | "kw": { 90 | "msg": "此功能允许您保持通信通道之一处于活动状态并有权访问 Web 界面。\n设备本身会根据优先级选择可用通道:WIFI、ETHERNET、WIFI AP" 91 | }, 92 | "ss": { 93 | "msg": "新参数已保存。", 94 | "rl": "稍后手动重启", 95 | "rn": "现在重启", 96 | "rr": "某些设置需要重新启动。", 97 | "tt": "设置已保存" 98 | }, 99 | "zgb": { 100 | "fu": {} 101 | }, 102 | "zg": { 103 | "fu": { 104 | "f": "即将刷入:{{file}}", 105 | "fn": "刷机成功!", 106 | "nv": "新版本:{{ver}}", 107 | "st": "CC2652固件开始刷入...", 108 | "er": "擦除完毕" 109 | } 110 | } 111 | }, 112 | "l": { 113 | "ab": "关于", 114 | "ge": "常规", 115 | "lo": "退出", 116 | "mq": "MQTT", 117 | "ne": "网络", 118 | "se": "安全", 119 | "st": "状态", 120 | "to": "工具", 121 | "vp": "VPN", 122 | "zi": "Zigbee" 123 | }, 124 | "p": { 125 | "ge": { 126 | "desc": "常规设置", 127 | "host": { 128 | "n": "主机名", 129 | "ph": "使用此 name.local 通过网络访问设备", 130 | "tt": "拉丁字母和数字,无空格" 131 | }, 132 | "kw": "保持网络", 133 | "led": { 134 | "desc": "LED 配置", 135 | "dis": "完全禁用", 136 | "mode": "工作模式指示灯", 137 | "night": "夜间模式 - 基于时间的 LED 控制", 138 | "pwr": "电源指示灯" 139 | }, 140 | "ntp": { 141 | "desc": "NTP配置", 142 | "server": { 143 | "n": "NTP服务器", 144 | "ph": "pool.ntp.org", 145 | "tt": "输入有效域名" 146 | }, 147 | "zone": "时区" 148 | }, 149 | "refresh": { 150 | "n": "网页刷新间隔", 151 | "ph": "很快", 152 | "tt": "?" 153 | }, 154 | "work": { 155 | "mode": { 156 | "desc": "选择设备工作模式", 157 | "net": "网络模式", 158 | "usb": "USB模式" 159 | } 160 | }, 161 | "u": { 162 | "ai": "自动安装更新(ESP32 和 Zigbee)", 163 | "d": { 164 | "ev": "每天", 165 | "fr": "星期五", 166 | "mn": "星期一", 167 | "sa": "星期六", 168 | "su": "星期日", 169 | "th": "星期四", 170 | "tt": "星期几", 171 | "tu": "星期二", 172 | "wd": "星期三", 173 | "du": "禁用" 174 | }, 175 | "h": "新固件检查时间" 176 | } 177 | }, 178 | "lo": { 179 | "btn": "登录", 180 | "lbl": "请登录", 181 | "pt": "登录-XZG", 182 | "mlo": "退出成功", 183 | "mnl": "需要登录", 184 | "mwc": "用户名或者密码错误" 185 | }, 186 | "mq": { 187 | "broker": { 188 | "n": "服务器", 189 | "ph": "域名或IP地址", 190 | "tt": "输入有效的域名或IP" 191 | }, 192 | "desc": "MQTT 配置", 193 | "disc": { 194 | "n": "Home Assistant - 自动发现", 195 | "ph": "启用或禁用", 196 | "tt": "💡 为 Home Assistant 的 MQTT 发现功能启用此功能" 197 | }, 198 | "enb": "启用此选项以激活 MQTT", 199 | "interval": { 200 | "n": "间隔", 201 | "ph": "更新间隔(秒)", 202 | "tt": "输入发送更新的频率(以秒为单位)" 203 | }, 204 | "pass": { 205 | "ph": "MQTT 密码", 206 | "tt": "输入密码" 207 | }, 208 | "port": { 209 | "n": "端口", 210 | "ph": "MQTT 端口号", 211 | "tt": "输入端口号(通常是1883)" 212 | }, 213 | "topic": { 214 | "n": "主题", 215 | "ph": "MQTT 主题", 216 | "tt": "输入设备 X/Y/Z 或 X/Y 或 X 的 MQTT 主题。无空格。" 217 | }, 218 | "user": { 219 | "ph": "MQTT 用户名", 220 | "tt": "输入您的用户名" 221 | }, 222 | "reconnect": { 223 | "n": "重新连接", 224 | "ph": "重新连接间隔(以秒为单位)", 225 | "tt": "输入快速尝试重新连接代理(以秒为单位)" 226 | } 227 | }, 228 | "ne": { 229 | "descEth": "以太网配置", 230 | "descWifi": "无线网络配置", 231 | "dhcp": "DHCP服务", 232 | "dns": "DNS服务器", 233 | "gw": "网关", 234 | "ip": "IP地址", 235 | "mask": "子网掩码", 236 | "wifi": { 237 | "btn": { 238 | "man": "手动输入", 239 | "scan": "扫描 Wi-Fi 网络" 240 | }, 241 | "ch": "信道", 242 | "rssi": "信号强度", 243 | "sc": "安全", 244 | "ssid": "SSID", 245 | "nnf": "未找到 WiFi 网络", 246 | "mode": "模式", 247 | "power": "发射功率" 248 | }, 249 | "ipv6": "IPv6" 250 | }, 251 | "st": { 252 | "dic": { 253 | "cs": "核心", 254 | "dm": "设备型号", 255 | "ef": "处理器", 256 | "efs": "内存", 257 | "ehs": "堆", 258 | "et": "温度", 259 | "kb": "Kb", 260 | "mb": "MB", 261 | "mhz": "兆赫兹", 262 | "tt": "设备信息", 263 | "efte": "外部的", 264 | "efti": "内部的", 265 | "efss": "文件系统", 266 | "ens": "NV存储", 267 | "ent": "条目", 268 | "du": "设备运行时间", 269 | "lt": "本地时间", 270 | "et1": "1W温度" 271 | }, 272 | "te": "以太网", 273 | "tm": "MQTT", 274 | "tvh": "Husarnet", 275 | "tw": "无线上网", 276 | "wgc": { 277 | "ed": "端点地址", 278 | "lip": "本地IP地址", 279 | "pip": "对端IP地址", 280 | "tt": "WireGuard" 281 | }, 282 | "wc": { 283 | "map": "AP", 284 | "mcl": "客户端" 285 | }, 286 | "zbc": { 287 | "op": "运作模式", 288 | "opn": "Zigbee-网络", 289 | "opu": "Zigbee-USB", 290 | "scc": "接口已连接", 291 | "sccn": "无连接", 292 | "su": "连接时间", 293 | "sccy": "已连接,{{count}} 个客户端", 294 | "zfs": "闪存大小", 295 | "zie": "IEEE" 296 | }, 297 | "c": { 298 | "fv": "固件版本", 299 | "hv": "硬件版本", 300 | "r": "角色" 301 | }, 302 | "zr": { 303 | "c": "协调器", 304 | "r": "路由器", 305 | "t": "OpenTread" 306 | } 307 | }, 308 | "se": { 309 | "desc": "安全配置", 310 | "fw": { 311 | "desc": "IP白名单", 312 | "ip": "允许的IP", 313 | "mask": "允许戴口罩" 314 | }, 315 | "web": { 316 | "auth": "启用 Web 服务器身份验证", 317 | "dis": "连接接口时禁用 Web 服务器" 318 | } 319 | }, 320 | "to": { 321 | "cc": "清除控制台", 322 | "cf": "选择文件...", 323 | "csc": "外设控制", 324 | "czc": "检查 Zigbee 连接", 325 | "czv": "检查 Zigbee 版本", 326 | "dc": "调试控制台", 327 | "dt": "调试工具", 328 | "efu": "ESP32 本地升级", 329 | "egu": "ESP32 在线升级", 330 | "esp": "ESP32", 331 | "fb": "文件浏览器", 332 | "fm": "刷写模式", 333 | "fn": "文件名", 334 | "fr": "恢复出厂设置", 335 | "fu": "固件升级", 336 | "ilfg": "安装最新版本", 337 | "lb": "闪烁……", 338 | "ls": "开始...", 339 | "lt": "LED 切换", 340 | "mc": "模块控制", 341 | "mode": "模式", 342 | "ms": "模式选择", 343 | "ntw": "网络", 344 | "pwr": "电源", 345 | "rd": "原始数据", 346 | "rr": "路由器重新连接", 347 | "rst": "重启模块", 348 | "s": "尺寸", 349 | "sc": "系统控制", 350 | "usb": "USB", 351 | "zb": "Zigbee", 352 | "zu": "Zigbee 更新(开发中)", 353 | "saf": "显示可用固件", 354 | "zgu": "更新CC2652固件", 355 | "eznr": "擦除NVRAM" 356 | }, 357 | "vp": { 358 | "hn": { 359 | "du": "仪表板 URL(保留“默认”)", 360 | "hn": "您设备的主机名", 361 | "jc": "为您的网络加入代码", 362 | "tt": "Husarnet 配置(在开发中)" 363 | }, 364 | "wg": { 365 | "ea": "对端IP/域名", 366 | "ep": "对端端口", 367 | "epk": "对端公钥", 368 | "lip": "本地IP地址", 369 | "lpk": "本地私钥", 370 | "tt": "WireGuard 配置", 371 | "aip": "允许目标IP", 372 | "am": "允许目标掩码", 373 | "lgw": "本地网关", 374 | "lsn": "本地掩码", 375 | "md": "使默认", 376 | "psk": "预共享密钥", 377 | "lp": "监听端口" 378 | } 379 | }, 380 | "zi": { 381 | "cfg": { 382 | "alarm": "XZG 似乎工作在接入点模式(IP 192.168.1.1)。\n连接到以太网或 Wi-Fi 后 IP 地址会发生变化,之后需要重新生成配置。", 383 | "dp": "这里填写设备路径", 384 | "dzl": "禁用 Zigbee led?", 385 | "ha": "对于家庭助理:转到“设置”→“系统”→“硬件”→选择右上角的三点菜单→“所有硬件”→滚动到 ttyUSB 并找到您的适配器→复制设备路径,例如“/dev/ \nttyUSB0\"", 386 | "lin": "列出 Linux 上的 USB 设备: ls /dev/ttyUSB*", 387 | "lxzg": "XZG位置", 388 | "sel": "生成配置", 389 | "sopm": "将输出功率设置为最大 20", 390 | "ss": "串口配置", 391 | "tt": "配置生成器" 392 | }, 393 | "so": "串口配置", 394 | "sp": "端口", 395 | "ss": "波特率", 396 | "fws": { 397 | "alarm": "在正在运行的网络上更改固件会导致网络崩溃!", 398 | "tt": "角色" 399 | } 400 | }, 401 | "ab": { 402 | "c": { 403 | "l": "项目库", 404 | "m": "与我们一起改进翻译、增强文档或编写新功能。 \n💻", 405 | "t": "贡献" 406 | }, 407 | "cm": { 408 | "l": "加入Telegram群", 409 | "m": "您的见解、反馈和协作对我们来说非常宝贵🤝", 410 | "t": "社区", 411 | "d": "加入Discord社区" 412 | }, 413 | "f": { 414 | "l": "联系作者", 415 | "m": "如果您有任何想法、建议或希望以其他方式提供帮助,请随时:📬", 416 | "t": "反馈" 417 | }, 418 | "l1": "XZG 固件结合了来自", 419 | "l2": "以前的 Zigbee 网关项目,整合为单一、全面的解决方案。", 420 | "l3": "通过将社区的努力集中在增强一款产品上,XZG 旨在简化开发,从而提高 Zigbee 网关的功能和效率。 \n🌍", 421 | "r": { 422 | "l": "打开一个问题", 423 | "m": "要报告错误或请求功能,请:🐞", 424 | "t": "报告" 425 | }, 426 | "s": { 427 | "m": "通过以下方式之一支持该项目:💖", 428 | "t": "支持" 429 | }, 430 | "w": { 431 | "l": "官方维基", 432 | "m": "要了解有关 XZG 固件项目的更多信息,请访问我们的:📘", 433 | "t": "维基百科" 434 | } 435 | } 436 | }, 437 | "ts": { 438 | "esp": { 439 | "ane": { 440 | "msg": "没有启用网络接口。\n请激活至少一个界面才能继续。", 441 | "tt": "网络配置错误" 442 | }, 443 | "beta": { 444 | "cnt": "联系方式🧧", 445 | "msg": "感谢您测试新版本!🎢\n别忘了提供反馈 💌", 446 | "tt": "固件测试版反馈" 447 | }, 448 | "upd": { 449 | "msg": "已找到较新版本的 XZG 固件", 450 | "tt": "ESP32 更新可用🚀" 451 | } 452 | }, 453 | "zb": { 454 | "nzfa": { 455 | "tt": "未找到固件" 456 | }, 457 | "upd": { 458 | "tt": "发现新的 Zigbee 固件 🌟", 459 | "msg": "已找到 RCP 模块的更新版本固件" 460 | } 461 | } 462 | } 463 | } 464 | --------------------------------------------------------------------------------