Files
Button/winDeployScripts/make-adb-bundle.ps1
T
GrigoandClaude Opus 5.5 d71fa812da Display/GUI watchdog for the "backlight on, no picture" failure
On PoE sites the MIPI-DSI panel can lose its init on a supply dip while the
RK3566 keeps running; the UI may also hang or exit. Three layers:

- hardware watchdog: systemd RuntimeWatchdogSec=20s (system.conf.d)
- GUI: SdWatchdog pings systemd per swapped frame (WATCHDOG_USEC=30s, enabled
  by the binary so older versions run under the same unit) and writes
  run/qt.frame; buttontask.service gets Restart=always, NotifyAccess=main,
  TimeoutAbortSec=10s
- bt-displaywatch (root, buttontask-displaywatch.service): kernel DRM/DSI
  timeouts, eth link down->up (PoE dip hint), stale UI frames, restart storms
  -> panel re-init (stop UI, fb0 blank 4->0, start UI), repeated -> reboot
  (max 3/day, never during OTA); incident log in /var/log/buttontask/

Manual recovery: hold one finger 10 s in the bottom-left corner (panels are
single-touch). Delivered by OTA via bt-update sync_system; the web service
runs `bt-update sync-system` at start for devices updated by an older
bt-update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 07:10:10 +00:00

194 lines
8.4 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# make-adb-bundle.ps1 — собирает offline-бандл (Windows-хост, без WSL).
#
# powershell -ExecutionPolicy Bypass -File make-adb-bundle.ps1 -Version 2.0
#
# По умолчанию берёт софт из .\software (бинарник ButtonTask + webconfig\),
# пакеты из .\debs. Все shell-скрипты/юниты нормализуются в LF (CRLF-фикс).
param(
[string]$SoftwareDir = '.\software',
[Parameter(Mandatory = $true)][string]$Version,
[string]$BinPath,
[string]$DebsDir,
[string]$MasterPassword
)
$ErrorActionPreference = 'Stop'
$Here = Split-Path -Parent $MyInvocation.MyCommand.Path
function Log($m) { Write-Host "[bundle] $m" -ForegroundColor Green }
function Warn($m) { Write-Host "[warn] $m" -ForegroundColor Yellow }
function Die($m) { Write-Host "[error] $m" -ForegroundColor Red; exit 1 }
function Write-Bytes($path, [string]$text) {
[System.IO.File]::WriteAllText($path, $text, (New-Object System.Text.UTF8Encoding($false)))
}
function ConvertTo-Lf($file) {
$t = [System.IO.File]::ReadAllText($file)
$t = ($t -replace "`r`n", "`n") -replace "`r", "`n"
Write-Bytes $file $t
}
if (-not (Test-Path $SoftwareDir)) { Die "нет каталога: $SoftwareDir" }
$SoftwareDir = (Resolve-Path $SoftwareDir).Path
if (-not $BinPath) { $BinPath = Join-Path $SoftwareDir 'ButtonTask' }
if (-not $DebsDir) { $DebsDir = Join-Path $Here 'debs' }
$web = Join-Path $SoftwareDir 'webconfig'
if (-not (Test-Path $BinPath)) { Die "нет бинарника: $BinPath" }
if (-not (Test-Path $web)) { Die "нет webconfig: $web" }
if (-not (Get-Command tar.exe -ErrorAction SilentlyContinue)) {
Die "не найден tar.exe (есть в Windows 10 1803+)"
}
# юниты systemd — все *.service из webconfig
$svc = @(Get-ChildItem $web -Filter '*.service' -File)
if ($svc.Count -eq 0) { Die "в webconfig нет ни одного *.service" }
# хелперы (должны совпадать с device-install.sh)
$scriptsSrc = Join-Path $web 'scripts'
$needScripts = @(
'bt-netconfig', 'bt-netconfig-boot', 'bt-update', 'bt-service',
'bt-settime', 'bt-reboot', 'bt-access', 'bt-brightness', 'bt-set-master',
'bt-displaywatch',
'buttontask.sudoers'
)
foreach ($s in $needScripts) {
if (-not (Test-Path (Join-Path $scriptsSrc $s))) { Die "нет webconfig\scripts\$s" }
}
$stage = Join-Path $Here "stage-$Version"
if (Test-Path $stage) { Remove-Item -Recurse -Force $stage }
foreach ($d in 'app', 'debs', 'scripts', 'config', 'icons', 'systemd') {
New-Item -ItemType Directory -Force -Path (Join-Path $stage $d) | Out-Null
}
Log "копирую бинарник"
Copy-Item $BinPath (Join-Path $stage 'app\ButtonTask')
Log "копирую webconfig"
Copy-Item $web (Join-Path $stage 'webconfig') -Recurse
# BLE collector (repo root or software/collector)
$collectorCandidates = @(
(Join-Path $SoftwareDir 'collector'),
(Join-Path (Split-Path -Parent $Here) 'collector')
)
$collectorSrc = $collectorCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($collectorSrc) {
Log "копирую collector: $collectorSrc"
Copy-Item $collectorSrc (Join-Path $stage 'collector') -Recurse
# Drop caches / tests optional keep tests
Get-ChildItem (Join-Path $stage 'collector') -Recurse -Directory -Filter '__pycache__' -ErrorAction SilentlyContinue |
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
} else {
Warn "collector/ не найден — BLE-сервис не попадёт в бандл"
}
Log "копирую хелперы и sudoers"
foreach ($s in $needScripts) { Copy-Item (Join-Path $scriptsSrc $s) (Join-Path $stage "scripts\$s") }
Log "копирую юниты: $($svc.Name -join ', ')"
foreach ($u in $svc) { Copy-Item $u.FullName (Join-Path $stage "systemd\$($u.Name)") }
$ovr = Join-Path $web 'buttontask.service.d\override.conf.example'
if (Test-Path $ovr) { Copy-Item $ovr (Join-Path $stage 'systemd\override.conf.example') }
else { Warn "override.conf.example не найден — авто-настройка тача будет пропущена" }
# config.json — ищем; если нигде нет, кладём дефолтный
$cfg = @(
(Join-Path $SoftwareDir 'config\config.json'),
(Join-Path $web 'config\config.json'),
(Join-Path $Here 'config\config.json')
) | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($cfg) {
Copy-Item $cfg (Join-Path $stage 'config\config.json')
Log "config.json: $cfg"
}
else {
Warn "config.json нигде не найден — кладу дефолтный"
$default = @'
{
"version": 2,
"layout": { "mode": "grid", "columns": 2, "spacing": 10, "showLabels": true },
"background": { "type": "gradient", "color1": "#00007b", "color2": "#7b007b", "animated": true, "imagePath": "" },
"feedback": { "okColor": "#00cc44", "errorColor": "#cc2200", "okWidth": 6, "errorWidth": 6, "glowRadius": 20 },
"settings": { "darkMode": true, "password": "admin_26", "kioskMode": false, "iconsDir": "/opt/buttontask/icons", "webPort": 8080, "brightness": 60 },
"buttons": [
{
"id": "cleaning-call",
"label": "Вызов клининга",
"iconPath": "cleaner1Crop.png",
"action": { "type": "http_get", "url": "http://192.168.30.147:80/api/click/btn?bid=01*04*01", "headers": {}, "body": "" },
"latchReset": { "enabled": true, "resetUrl": "http://192.168.30.147:80/api/click/btn?bid=01*04*00", "successMatch": "OK" },
"feedback": { "successText": "Вызов отправлен", "errorText": "Ошибка", "pendingText": "...", "fadeMs": 5000 },
"trigger": { "mode": "hold", "holdMs": 800 },
"color": "#7b007b"
}
]
}
'@
Write-Bytes (Join-Path $stage 'config\config.json') ($default -replace "`r`n", "`n")
}
$netCfg = Join-Path $SoftwareDir 'config\network.json'
if (Test-Path $netCfg) {
Copy-Item $netCfg (Join-Path $stage 'config\network.json')
Log "network.json: $netCfg"
}
else {
Warn 'network.json не найден в .\software\config\ — сеть останется на DHCP'
}
$iconExts = '.png', '.jpg', '.jpeg', '.bmp', '.svg', '.gif'
$iconSources = @()
$iconSources += Get-ChildItem $SoftwareDir -File -ErrorAction SilentlyContinue |
Where-Object { $iconExts -contains $_.Extension.ToLowerInvariant() }
$iconsDir = Join-Path $SoftwareDir 'icons'
if (Test-Path $iconsDir) {
$iconSources += Get-ChildItem $iconsDir -File -ErrorAction SilentlyContinue |
Where-Object { $iconExts -contains $_.Extension.ToLowerInvariant() }
}
$iconSources = @($iconSources | Sort-Object FullName -Unique)
foreach ($ic in $iconSources) {
Copy-Item $ic.FullName (Join-Path $stage "icons\$($ic.Name)")
}
if ($iconSources.Count -gt 0) {
Log "иконок в бандле: $($iconSources.Count)"
}
else {
Warn "иконки в .\software или .\software\icons не найдены"
}
if ($MasterPassword) {
Write-Bytes (Join-Path $stage 'config\.master') $MasterPassword
Log 'мастер-пароль записан в бандл (config/.master)'
}
else {
Warn 'мастер-пароль не задан (-MasterPassword) — на устройстве задайте вручную: bt-set-master'
}
# .deb
if ((Test-Path $DebsDir) -and (Get-ChildItem $DebsDir -Filter *.deb -ErrorAction SilentlyContinue)) {
Copy-Item (Join-Path $DebsDir '*.deb') (Join-Path $stage 'debs')
Log "пакетов в бандле: $((Get-ChildItem (Join-Path $stage 'debs') -Filter *.deb).Count)"
}
else { Warn "каталог debs пуст ($DebsDir) — сначала прогоните collect-debs.ps1" }
Log "кладу device-install.sh и VERSION"
Copy-Item (Join-Path $Here 'device-install.sh') (Join-Path $stage 'device-install.sh')
Write-Bytes (Join-Path $stage 'VERSION') ($Version + "`n")
Log "нормализую переводы строк в LF (CRLF-фикс)"
$fix = @()
$fix += Get-ChildItem (Join-Path $stage 'scripts') -File
$fix += Get-ChildItem (Join-Path $stage 'systemd') -File
$fix += Get-Item (Join-Path $stage 'device-install.sh')
$fix += @(Get-ChildItem (Join-Path $stage 'webconfig') -Recurse -File -Include 'bt-*', '*.sh', '*.service', '*.sudoers', 'override.conf*', '*.conf')
$fix | Sort-Object FullName -Unique | ForEach-Object { ConvertTo-Lf $_.FullName }
Log "пакую архив"
$tgz = Join-Path $Here "buttontask-deploy-$Version.tgz"
if (Test-Path $tgz) { Remove-Item $tgz }
& tar.exe -C $stage -czf $tgz .
if ($LASTEXITCODE -ne 0) { Die "tar завершился с ошибкой" }
Remove-Item -Recurse -Force $stage
Log "готово: $tgz"