diff --git a/devices/build.sh b/devices/build.sh index 9a76159..3fe255b 100755 --- a/devices/build.sh +++ b/devices/build.sh @@ -97,23 +97,88 @@ 4 # ───────────────────────────────────────────── -# Копируем релизные бинари в builds/ +# Helpers для релизных пакетов # ───────────────────────────────────────────── -copy_release() { + +extract_version() { + local ino_file="$1" + grep -oP 'const char\* FW_VERSION = "\K[^"]+' "${ino_file}" 2>/dev/null || echo "0.0.0" +} + +create_firmware_package() { local src_dir="$1" - local dst_dir="${DEVICES_DIR}/builds/$2" + local pkg_name="$2" local bin_name="$3" + local ino_file="$4" + local platform="$5" + local channels="$6" + + local dst_dir="${DEVICES_DIR}/builds/${pkg_name}" mkdir -p "${dst_dir}" + cp "${src_dir}/${bin_name}.bin" "${dst_dir}/${bin_name}.bin" 2>/dev/null || true cp "${src_dir}/${bin_name}.elf" "${dst_dir}/${bin_name}.elf" 2>/dev/null || true cp "${src_dir}/${bin_name}.map" "${dst_dir}/${bin_name}.map" 2>/dev/null || true + + local version + version="$(extract_version "${ino_file}")" + + local manifest="${dst_dir}/manifest.json" + cat > "${manifest}" </devdev/null) + ok "Package ${pkg_name} → ${zip_path}" } +# ───────────────────────────────────────────── +# Копируем релизные бинари и собираем ZIP +# ───────────────────────────────────────────── + echo "" -info "Copying release binaries..." -copy_release "${DEVICES_DIR}/relay/build/esp8266.esp8266.generic" "relay_esp8266_x1" "relay.ino" -copy_release "${DEVICES_DIR}/button/build/esp8266.esp8266.d1_mini" "button_esp8266_x2" "button.ino" -copy_release "${DEVICES_DIR}/button/build/esp8266.esp8266.d1_mini_x4" "button_esp8266_x4" "button.ino" +info "Creating release packages..." + +create_firmware_package \ + "${DEVICES_DIR}/relay/build/esp8266.esp8266.generic" \ + "relay-esp8266-x8" \ + "relay.ino" \ + "${DEVICES_DIR}/relay/relay.ino" \ + "esp8266" \ + 8 + +create_firmware_package \ + "${DEVICES_DIR}/relay/build/esp32.esp32.esp32c3" \ + "relay-esp32-x8" \ + "relay.ino" \ + "${DEVICES_DIR}/relay/relay.ino" \ + "esp32" \ + 8 + +create_firmware_package \ + "${DEVICES_DIR}/button/build/esp8266.esp8266.d1_mini" \ + "button-esp8266-x2" \ + "button.ino" \ + "${DEVICES_DIR}/button/button.ino" \ + "esp8266" \ + 2 + +create_firmware_package \ + "${DEVICES_DIR}/button/build/esp8266.esp8266.d1_mini_x4" \ + "button-esp8266-x4" \ + "button.ino" \ + "${DEVICES_DIR}/button/button.ino" \ + "esp8266" \ + 4 # ───────────────────────────────────────────── # Итог diff --git a/docs/server-api.md b/docs/server-api.md index 5343a22..b5c531a 100644 --- a/docs/server-api.md +++ b/docs/server-api.md @@ -504,6 +504,90 @@ --- +## Прошивки `/api/v1/firmwares` + +### `GET /api/v1/firmwares` +Список всех прошивок в каталоге (in-memory, сканирует ZIP-файлы в `firmwares_dir`). + +**Ответ:** +```json +{ + "status": true, + "data": { + "firmwares": [ + { + "id": "relay-1.3.0-esp8266-x8", + "device_type": "relay", + "platform": "esp8266", + "channels": 8, + "version": "1.3.0", + "core_version": "1.5.0", + "bin_filename": "relay.ino.bin", + "description": "...", + "changelog": "..." + } + ], + "total": 1 + } +} +``` + +--- + +### `GET /api/v1/firmwares/id/{id}` +Детали одной прошивки по ID манифеста. + +--- + +### `GET /api/v1/firmwares/id/{id}/download` +Скачать бинарный файл прошивки (`Content-Type: application/octet-stream`). + +--- + +### `POST /api/v1/firmwares/refresh` +Принудительно пересканировать директорию с прошивками и обновить in-memory кэш. + +**Ответ:** `{status: true}` + +--- + +### `GET /api/v1/devices/id/{id}/firmware-compatibility` +Проверить совместимость устройства с прошивками в каталоге. Сервер запрашивает устройство `GET /about` и фильтрует каталог по `device_type`, `platform`, `channels`, и версии (только более новые). + +**Ответ:** +```json +{ + "status": true, + "data": { + "compatible": [ + { "id": "relay-1.3.0-esp8266-x8", "version": "1.3.0", "description": "...", "changelog": "..." } + ], + "current_version": "1.22.0", + "current_platform": "esp8266", + "current_channels": 8 + } +} +``` + +--- + +### `POST /api/v1/devices/update-firmware` +Запустить OTA-обновление устройства. Сервер распаковывает `.bin` из ZIP, проверяет совместимость, отправляет файл на устройство через `POST /update` (multipart/form-data). + +**Тело:** +```json +{ + "device_id": 12, + "firmware_id": "relay-1.3.0-esp8266-x8" +} +``` + +**Ответ (успех):** `{status: true, data: {device_msg: "Update OK! Rebooting..."}}` + +**Ошибки:** `device_not_found` | `firmware_not_found` | `firmware_not_compatible` | `device_request_fail` | `ota_failed` + +--- + ## Запланировано, не реализовано Следующие разделы описаны в спецификациях (`docs/server-api-v1/`), но ещё не реализованы: diff --git a/docs/virtual-device-emulator.md b/docs/virtual-device-emulator.md index 265f293..f3c1923 100644 --- a/docs/virtual-device-emulator.md +++ b/docs/virtual-device-emulator.md @@ -210,7 +210,7 @@ | Method | Path | Auth | Request | Response | |--------|------|------|---------|----------| | GET | `/` | — | — | Web UI HTML | -| GET | `/about` | — | — | `{device_name, device_type, firmware_version, device_id, server, status, ip_address, mac_address, uptime}` | +| GET | `/about` | — | — | `{device_name, device_type, platform, firmware_version, core_version, device_id, server, status, ip_address, mac_address, uptime, channels}` | | GET | `/status` | Bearer | — | `{channels: [...]}` | | POST | `/action` | Bearer | `{action, params}` | `{status, message}` или `{status, error, message}` | | POST | `/set_token` | — (setup) / Bearer (normal) | `{token}` | `{status, message}` | @@ -221,6 +221,8 @@ | POST | `/set_channels_schema` | — (setup) / Bearer (normal) | `{schema: [int]}` | `{status, message}` | | GET | `/setup` | — | — | `{status, message}` (только в setup) | | POST | `/setup` | — | `{ssid, password}` | `{status, message}` (только в setup) | +| GET | `/update` | — | — | HTML форма загрузки прошивки | +| POST | `/update` | — | `multipart/form-data` с полем `firmware` | `Update OK! Rebooting...` | | POST | `/simulate-event` | — | `{event_name, channel}` | `{status, message}` | ## End-to-end пример @@ -308,6 +310,5 @@ - **Нет реальной перезагрузки** — `/reboot` возвращает `ok`, процесс не перезапускается - **Best-effort события** — `requests.post` с `timeout=3`, ошибки игнорируются - **Локальное хранилище** — JSON без шифрования, только для dev -- **Нет OTA** — невозможно обновить "прошивку" - **Нет сетевых задержек** — ping ≈ 0 ms - **Один процесс = одно устройство** — для множества устройств нужно несколько портов