7 Commits
Author SHA1 Message Date
kkallas 3fe45fa4d5 Merge pull request 'Feature/updater' (#2) from feature/updater into main
Reviewed-on: #2
2026-09-28 19:47:15 +02:00
kkallasandClaude Opus 5.5 1ab8af8fe2 README: fix two Windows paths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:44:56 +02:00
kkallasandClaude Opus 5.5 bfbbd537d5 Updater messages without "!", README and changelog for v0.9
cmd drops "!" from messages when delayed expansion is on, so errors
read "ERROR:". README describes the two files per system and the
first install from a release zip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:43:50 +02:00
kkallasandClaude Opus 5.5 a33736742a Technician updater and one shared Python environment (v0.9.0)
update_windows.bat / update_macos.command: fetch release tags from Gitea,
move the folder to the newest vX.Y (git downloads only what changed),
connect a folder unpacked from a release zip on first run, install git/uv
when missing and sync the Python libraries. Developer copies on a branch
are left alone.

run_* and update_* share one environment (KotaTechnicianTool/venv)
instead of one per version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:42:25 +02:00
kkallas 724c2d62f9 Merge pull request 'README: install and update from Gitea instead of Google Drive folders' (#1) from docs/readme-gitea into main
Reviewed-on: #1
2026-09-28 19:36:35 +02:00
kkallasandClaude Opus 5.5 43f4c51aa4 README: install and update from Gitea instead of Google Drive folders
Describes the zip from Releases and git clone/pull, the macOS
executable-bit note for zips, and the branch/PR/tag workflow for the team.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:34:01 +02:00
kkallas bfa7edce6f Technician tool v0.8
Imported from the Google Drive folder 004-technician_tool/v0.8.
2026-09-28 19:19:55 +02:00
16 changed files with 362 additions and 25 deletions

No files matched your search

+77
View File
@@ -0,0 +1,77 @@
# KOTA Sensorbox Technician Tool
Diagnoza sensorboxa przez USB, wgrywanie firmware z folderu `firmware/` i przygotowanie karty SD.
Działa na Windows 10/11 i macOS 13 (Ventura) lub nowszym, na Macach z Intelem i Apple Silicon.
Repozytorium: https://gitea.kota-app.com/kkallas/kota-technician-tool (dostęp nadaje właściciel repozytorium).
## Dla technika: dwa pliki
| System | Aktualizacja | Uruchomienie |
|---|---|---|
| Windows | `update_windows.bat` | `run_windows.bat` |
| macOS | `update_macos.command` | `run_macos.command` |
- **Aktualizacja** sprawdza na Gitea najnowszą wydaną wersję i pobiera tylko to, co się zmieniło
(pliki programu, firmware i potrzebne biblioteki). Uruchom ją, gdy firmware team ogłosi nową wersję,
albo po prostu przed wyjazdem do klienta. Pokazuje, co się zmieniło.
- **Uruchomienie** startuje program. Nie łączy się z Gitea, działa też bez internetu (po pierwszej aktualizacji).
Nie trzymaj folderu narzędzia w Google Drive ani OneDrive.
## Pierwsza instalacja (raz na komputer)
1. Na Gitea otwórz **Releases** i przy najnowszej wersji pobierz **Source Code (ZIP)**.
2. Rozpakuj do zwykłego folderu, np. `Dokumenty\KOTA\technician-tool` (macOS: `~/Documents/KOTA/technician-tool`).
3. Uruchom plik aktualizacji dla swojego systemu. Za pierwszym razem:
- połączy folder z Gitea i zapyta o login do Gitea (hasło lub token); system je zapamięta,
- zainstaluje brakujące narzędzia za Twoją zgodą: Git (Windows: przez winget; macOS: Apple Command Line Tools
- po ich instalacji uruchom aktualizację jeszcze raz) oraz `uv` (menedżer Pythona, tylko dla bieżącego
użytkownika),
- pobierze biblioteki Pythona (1–2 minuty, potrzebny internet).
4. Uruchom program plikiem `run_...`.
**macOS:** jeśli Finder odpowie, że pliku nie można uruchomić (zdarza się po rozpakowaniu zip), otwórz Terminal,
wpisz `bash ` (ze spacją), przeciągnij `update_macos.command` do okna i naciśnij Enter. Po pierwszej aktualizacji
oba pliki `.command` dają się już otwierać dwuklikiem.
Zamiast zip można też zrobić `git clone https://gitea.kota-app.com/kkallas/kota-technician-tool.git` i dalej
używać tych samych dwóch plików.
Konkretna starsza wersja (np. do serwisu nietypowego boxa): w folderze narzędzia `git switch --detach v0.8`;
kolejna aktualizacja wraca do najnowszej.
Wszystkie wersje używają jednego środowiska Pythona poza folderem narzędzia:
`%LOCALAPPDATA%\KotaTechnicianTool\venv` (Windows), `~/Library/Caches/KotaTechnicianTool/venv` (macOS).
Stare środowiska `venv-0.x` z poprzednich wersji można usunąć.
Sesje, kopie zapasowe flash i ustawienia są w `~/KotaTechnicianTool` (Windows: `C:\Users\<użytkownik>\KotaTechnicianTool`).
## Port sensorboxa
- Windows: `COMx - Sensorbox (native USB) [MAC]`
- macOS: `/dev/cu.usbmodemXXXX - Sensorbox (native USB) [MAC]`
Kabel USB musi przenosić dane (nie tylko ładowanie). Przy restartach z komunikatem o brownout użyj innego portu
lub zasilacza.
## Firmware
Wgrać można tylko pliki `.bin` z folderu `firmware/`. Lista pokazuje datę kompilacji zapisaną w pliku,
najnowszy build jest na górze. Nowy build: wrzuć plik do `firmware/` i kliknij **Refresh list**.
## Dla zespołu
- Praca nad zmianą: gałąź (`git switch -c feature/<nazwa>`) → commity z przechodzącymi testami →
`git push -u origin feature/<nazwa>` → pull request na Gitea → merge do `main`.
- Wydanie wersji: numer w `pyproject.toml`, `kota_tech/__init__.py` i `uv.lock`, tag `vX.Y`
(`git tag -a vX.Y -m "Technician tool vX.Y"`, `git push origin vX.Y`), opis w **Releases** na Gitea.
Aktualizacja techników bierze **tylko tagi `v*`** – zmiany w `main` bez tagu do nich nie trafiają.
- Nie commitujemy `ENV.TXT` z prawdziwym tokenem ani haseł Wi-Fi.
- Testy:
```
uv run --frozen pytest -q
```
Zmiany w wersjach: `docs/CHANGES-v0.*.md`; historia v0.1–v0.8 jest w tagach gita.
+21
View File
@@ -0,0 +1,21 @@
# Technician tool v0.8 - changes
## Windows + macOS
- Dependencies verified to install on macOS arm64 and x86_64 (uv.lock: PySide6 6.11 universal2 wheels,
requires macOS 13+); Windows unchanged.
- SD card check: macOS `diskutil` reports FAT32 partitions as `DOS_FAT_32` / `Windows_FAT_32`; these are now
accepted (previously a correct card was reported as "not FAT32" on a Mac).
- Unit tests for the macOS serial-port filter (`/dev/cu.*` only) and FAT32 names.
- README.md with launch steps for both systems (incl. the Google Drive executable-bit workaround on macOS);
docs/mac_agent_command.txt updated for a v0.8 test run on a Mac.
## Diagnosis
- Harmless Wi-Fi start-up errors are ignored (rule `benign.wifi_start`): `wifi:sta is connecting, return error`,
`esp_wifi_connect() failed: ESP_ERR_WIFI_CONN`, `Failed to get configured network when updating network
status` (unpaired box).
- Brown-out resets are detected (`E BOD: Brownout detector was triggered`, ROM reports RTC_SW_SYS_RST).
- Service report note = the Diagnosis panel as text (LED, firmware, box self-report, start-up progress with
times, findings with "Seen Nx, first at ... s, last at ... s" and log line), shortened to fit the service
form's 5000 characters.
Not yet run on a real Mac: see docs/mac_agent_command.txt.
+9
View File
@@ -0,0 +1,9 @@
# Technician tool v0.9 - changes
- Two files per system for technicians: `update_windows.bat` / `update_macos.command` (update) and
`run_windows.bat` / `run_macos.command` (start).
- Update: checks Gitea for the newest release tag `vX.Y`, downloads only what changed (git), shows the list of
changes, protects files edited by hand (asks first), connects a folder unpacked from a release zip on first
run, installs git / uv when missing (asks first) and brings the Python libraries up to date.
- One shared Python environment for all versions: `KotaTechnicianTool/venv` (was `venv-0.x` per version).
- A developer copy on a branch is left alone by the update script (use `git pull`).
+21 -16
View File
@@ -1,9 +1,10 @@
You are testing the KOTA sensorbox technician tool v0.5 on macOS. The current folder is You are testing the KOTA sensorbox technician tool v0.8 on macOS. The current folder is
004-technician_tool/v0.5 (a Python/PySide6 desktop app; it has only been tested on Windows so far). 004-technician_tool/v0.8 (a Python/PySide6 desktop app; tested on Windows 11, macOS paths are covered by
unit tests but not yet run on a real Mac).
Rules: Rules:
- Python only via uv or conda/mamba. Never use bare/system python or pip. - Python only via uv or conda/mamba. Never use bare/system python or pip.
- Do not build, modify or flash firmware other than the bundled firmware/new_system.bin. - Do not build or modify firmware. Flash only files from the tool's firmware/ folder.
- Do not push anything to Git or GitLab. This is a local demo. - Do not push anything to Git or GitLab. This is a local demo.
- Ask me before any destructive action: formatting an SD card, Full-mode flashing, or deleting files. - Ask me before any destructive action: formatting an SD card, Full-mode flashing, or deleting files.
Before formatting, show me the exact disk (identifier, name, size, current files). Before formatting, show me the exact disk (identifier, name, size, current files).
@@ -11,20 +12,24 @@ Rules:
Tests, in order. Report pass/fail with evidence for each: Tests, in order. Report pass/fail with evidence for each:
1. Launch: `bash run_macos.command` (it offers to install uv). The app window opens; note any warnings. 1. Launch: `bash run_macos.command` (it offers to install uv). The app window opens; note any warnings.
2. Tests: `UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv-0.5" uv run --frozen pytest -q` Also check whether a double-click in Finder works (Google Drive may drop the executable bit).
2. Tests: `UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv-0.8" uv run --frozen pytest -q`
(expect 172 passed).
3. Serial: plug in the sensorbox (native USB). The port list shows a /dev/cu.usbmodem* "Sensorbox (native USB)" 3. Serial: plug in the sensorbox (native USB). The port list shows a /dev/cu.usbmodem* "Sensorbox (native USB)"
entry with a MAC. Connect, press "Reset & capture": the boot log starts at "ESP-ROM:esp32s3" and the entry with a MAC (no /dev/tty.* duplicate). Connect, press "Reset & capture": the log starts at
Diagnosis panel identifies firmware v1.0 (build 45fa9d5b9). Check whether the port disappears after the "ESP-ROM:esp32s3" and the Diagnosis panel shows firmware v1.0.16 (build 4f10989e), the "Box self-report"
reset and whether the tool reconnects. section and the LED with its pattern. Check whether the port disappears after the reset and whether the
4. Guided troubleshooting: run "Quick check" and answer truthfully with what I tell you the LED shows. tool reconnects. Wait 2 minutes: a "KOTA: health" line appears every 60 s.
5. Flash (App only, factory image; the box already runs it): the tool must enter flashing mode by itself, 4. Show filter "KOTA diagnostics + errors" shows only KOTA lines and errors.
write, restart the box and report "Success ... build 45fa9d5b9". Capture the esptool output. 5. Guided troubleshooting: run "Quick check" and answer truthfully with what I tell you the LED shows.
6. SD card tab: the settings load from ../../002-SD_card_format/ENV.txt. The card reader and card are listed 6. Flash (App only, the newest file in firmware/, chosen by its compile date): the tool must enter flashing mode
(internal disks must NOT be listed). Run "Check card", then, after my OK, "Format card and write ENV.TXT". by itself, write, restart the box and report "Success ... build 4f10989e". Capture the esptool output.
It must use `diskutil eraseDisk FAT32 KOTA MBRFormat` on the correct disk, write ENV.TXT byte-identical 7. SD card tab: the card reader and card are listed (internal disks must NOT be listed). "Check card" must NOT
to the template, and eject the card. report a wrong file system for a FAT32 card. After my OK, "Format card and write ENV.TXT" must use
7. "Service report..." opens the browser at https://service.kota-app.com/<MAC>#kota-report=... (it is fine `diskutil eraseDisk FAT32 KOTA MBRFormat` on the correct disk, write ENV.TXT and eject the card.
if the page itself cannot prefill yet). 8. "Service report..." opens the browser at https://service.kota-app.com/<MAC>#kota-report=...; the note in
the form must match the Diagnosis panel (self-report, start-up progress with times, findings with
"Seen Nx, first at ... s").
At the end, write a short report: macOS version, Mac model/architecture, each test with result, errors, and At the end, write a short report: macOS version, Mac model/architecture, each test with result, errors, and
suggested fixes (file:line). suggested fixes (file:line).
+1 -1
View File
@@ -1,3 +1,3 @@
"""KOTA sensorbox technician tool.""" """KOTA sensorbox technician tool."""
__version__ = "0.7.0" __version__ = "0.9.0"
+8
View File
@@ -137,6 +137,14 @@ id = "benign.zcl"
pattern = 'chip\[ZCL\]: WRITE ERR|data_model: Cluster cannot be NULL' pattern = 'chip\[ZCL\]: WRITE ERR|data_model: Cluster cannot be NULL'
severity = "ignore" severity = "ignore"
[[rule]]
id = "benign.wifi_start"
# Harmless Wi-Fi noise seen on every 1.0.16 boot: Matter asks for a connect
# while the station is already connecting, and an unpaired box polls a
# network it does not have yet (matter.not_commissioned covers that case).
pattern = 'wifi:sta is connecting, return error|esp_wifi_connect\(\) failed: ESP_ERR_WIFI_CONN|Failed to get configured network when updating network status'
severity = "ignore"
[[rule]] [[rule]]
id = "benign.connect_chain" id = "benign.connect_chain"
# Transport-level echo of a failure that a more specific rule reports. # Transport-level echo of a failure that a more specific rule reports.
+3 -2
View File
@@ -243,8 +243,9 @@ def check_card(mount: str, filesystem: str = "") -> CardReport:
"""What the box would find on this card (values masked in the UI).""" """What the box would find on this card (values masked in the UI)."""
root = Path(mount) root = Path(mount)
problems: list[str] = [] problems: list[str] = []
fs = filesystem.upper() # Windows says "FAT32"; macOS diskutil says "DOS_FAT_32" / "Windows_FAT_32" / "MS-DOS FAT32".
if fs and "FAT32" not in fs and fs not in ("FAT", "MS-DOS FAT32", "MSDOS"): fs = filesystem.upper().replace("_", "").replace(" ", "")
if fs and "FAT32" not in fs and fs not in ("FAT", "MSDOS"):
problems.append(f"File system is {filesystem}; the box needs FAT32 - re-format the card.") problems.append(f"File system is {filesystem}; the box needs FAT32 - re-format the card.")
env_file = find_case_insensitive(root, ENV_NAME) env_file = find_case_insensitive(root, ENV_NAME)
env = parse_env(env_file.read_bytes()) if env_file else None env = parse_env(env_file.read_bytes()) if env_file else None
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "kota-technician-tool" name = "kota-technician-tool"
version = "0.7.0" version = "0.9.0"
description = "KOTA sensorbox technician diagnostic tool (demo)" description = "KOTA sensorbox technician diagnostic tool (demo)"
requires-python = ">=3.11,<3.14" requires-python = ">=3.11,<3.14"
dependencies = [ dependencies = [
+4 -2
View File
@@ -2,8 +2,10 @@
# Double-click in Finder, or run: bash run_macos.command # Double-click in Finder, or run: bash run_macos.command
cd "$(dirname "$0")" || exit 1 cd "$(dirname "$0")" || exit 1
# Keep the virtual environment out of the (Google Drive) tool folder. # If Finder refuses to open this file (Google Drive drops the executable bit), run it once as:
export UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv-0.7" # bash run_macos.command (or: chmod +x run_macos.command)
# One shared Python environment for every version (update_macos.command keeps it current).
export UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv"
export PATH="$HOME/.local/bin:$PATH" export PATH="$HOME/.local/bin:$PATH"
if ! command -v uv >/dev/null 2>&1; then if ! command -v uv >/dev/null 2>&1; then
+2 -2
View File
@@ -2,8 +2,8 @@
setlocal setlocal
cd /d "%~dp0" cd /d "%~dp0"
rem Keep the virtual environment out of the (Google Drive) tool folder. rem One shared Python environment for every version (update_windows.bat keeps it current).
set "UV_PROJECT_ENVIRONMENT=%LOCALAPPDATA%\KotaTechnicianTool\venv-0.7" set "UV_PROJECT_ENVIRONMENT=%LOCALAPPDATA%\KotaTechnicianTool\venv"
set "PATH=%USERPROFILE%\.local\bin;%PATH%" set "PATH=%USERPROFILE%\.local\bin;%PATH%"
where uv >nul 2>nul where uv >nul 2>nul
+10
View File
@@ -247,3 +247,13 @@ def test_live_brownout_loop_is_a_power_problem():
assert top.finding.rule_id == "reboot.brownout" and top.finding.count >= 5 assert top.finding.rule_id == "reboot.brownout" and top.finding.count >= 5
assert "reboot.loop" in {c.rule_id for c in top.consequences} assert "reboot.loop" in {c.rule_id for c in top.consequences}
assert r.verdict.startswith("Problem: Power supply") assert r.verdict.startswith("Problem: Power supply")
@pytest.mark.parametrize("line", [
"E (2893) wifi:sta is connecting, return error",
"E (2892) chip[DL]: esp_wifi_connect() failed: ESP_ERR_WIFI_CONN",
"E (5120) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F",
])
def test_harmless_wifi_start_errors_are_not_reported(line):
_, r = run(BOOT + APP + [line])
assert r.roots == [] and "Unrecognised" not in r.to_text()
+12
View File
@@ -12,3 +12,15 @@ def test_other_ports_not_sensorbox():
bt = PortInfo("COM3", "Standard Serial over Bluetooth link", None, None, None) bt = PortInfo("COM3", "Standard Serial over Bluetooth link", None, None, None)
assert not bt.is_sensorbox and bt.mac is None assert not bt.is_sensorbox and bt.mac is None
assert bt.label == "COM3 - Standard Serial over Bluetooth link" assert bt.label == "COM3 - Standard Serial over Bluetooth link"
def test_macos_lists_only_callout_devices(monkeypatch):
from types import SimpleNamespace
from kota_tech.serialio import ports
fake = [SimpleNamespace(device=d, description="USB JTAG/serial debug unit", vid=0x303A, pid=0x1001,
serial_number="A4:CB:8F:C9:46:F8")
for d in ("/dev/tty.usbmodem1101", "/dev/cu.usbmodem1101")]
monkeypatch.setattr(ports.sys, "platform", "darwin")
monkeypatch.setattr(ports.list_ports, "comports", lambda: fake)
assert [p.device for p in ports.list_serial_ports()] == ["/dev/cu.usbmodem1101"]
+11
View File
@@ -170,3 +170,14 @@ def test_elevated_result_folder_is_not_owner_only(tmp_path, monkeypatch):
assert work.parent == tmp_path / "tmp" and work.is_dir() assert work.parent == tmp_path / "tmp" and work.is_dir()
(work / "result.json").write_text('{"ok": true}', encoding="utf-8") (work / "result.json").write_text('{"ok": true}', encoding="utf-8")
assert (work / "result.json").read_text(encoding="utf-8") assert (work / "result.json").read_text(encoding="utf-8")
@pytest.mark.parametrize("fs", ["FAT32", "DOS_FAT_32", "Windows_FAT_32", "MS-DOS FAT32"])
def test_fat32_names_from_windows_and_macos_are_accepted(tmp_path, fs):
(tmp_path / "ENV.TXT").write_bytes(b"INFLUX_URL=https://x\nINFLUX_TOKEN=abc\n")
assert not any("File system" in p for p in check_card(str(tmp_path), fs).problems)
def test_exfat_is_flagged(tmp_path):
(tmp_path / "ENV.TXT").write_bytes(b"INFLUX_URL=https://x\nINFLUX_TOKEN=abc\n")
assert any("File system" in p for p in check_card(str(tmp_path), "ExFAT").problems)
+74
View File
@@ -0,0 +1,74 @@
#!/bin/bash
# KOTA Sensorbox Technician Tool - update to the newest released version.
# Checks Gitea for the newest release tag (vX.Y) and downloads only what changed (git).
# Works in a git clone and in a folder unpacked from a release zip (it is connected on first run).
# If Finder refuses to open this file, run it once as: bash update_macos.command
cd "$(dirname "$0")" || exit 1
REPO_URL="${KOTA_REPO_URL:-https://gitea.kota-app.com/kkallas/kota-technician-tool.git}"
export UV_PROJECT_ENVIRONMENT="${UV_PROJECT_ENVIRONMENT:-$HOME/Library/Caches/KotaTechnicianTool/venv}"
export PATH="$HOME/.local/bin:$PATH"
finish() {
[ -z "$KOTA_NO_PAUSE" ] && [ -t 0 ] && read -r -p "Press Enter to close..." _
exit "$1"
}
fail() { echo; echo "[!] $*"; echo "[!] Update did not finish."; finish 1; }
# ---- git (macOS: the Command Line Tools installer offers it) --------------
if ! git --version >/dev/null 2>&1; then
if [ "$(uname)" = Darwin ]; then
xcode-select --install >/dev/null 2>&1
fail "Git is being installed (Apple Command Line Tools). Run this again when the installer has finished."
fi
fail "Git is not installed."
fi
# ---- connect a folder unpacked from a zip ----------------------------------
if [ ! -d .git ]; then
echo "Connecting this folder to $REPO_URL ..."
git init -q && git remote add origin "$REPO_URL" || fail "Cannot set up git in this folder."
fi
# A developer copy follows a branch; it is updated with "git pull", not here.
if git rev-parse -q --verify HEAD >/dev/null 2>&1 && branch=$(git symbolic-ref -q --short HEAD); then
echo "This is a developer copy on branch $branch. Update it with: git pull"
finish 0
fi
echo "Checking for a new version..."
git fetch --quiet --force --tags origin ||
fail "Cannot reach $REPO_URL. Check the Internet connection and your Gitea login, then try again."
latest=$(git tag -l 'v*' --sort=-v:refname | head -n 1)
[ -n "$latest" ] || fail "No released version found on the server."
current=$(git describe --tags --exact-match HEAD 2>/dev/null)
if [ "$current" = "$latest" ]; then
echo "You already have the newest version $latest."
else
if [ -n "$current" ]; then
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
echo "Some tool files in this folder were changed by hand. They will be replaced by $latest."
read -r -p "Continue? [y/N] " answer
[[ "$answer" =~ ^[Yy]$ ]] || fail "Cancelled."
fi
echo "Updating $current -> $latest. Changes:"
git --no-pager log --oneline --no-decorate "$current..$latest"
else
echo "Installing $latest..."
fi
git -c advice.detachedHead=false checkout -q -f "$latest" || fail "Checkout of $latest failed."
echo "Now on $latest."
fi
# ---- Python libraries (only what changed) ----------------------------------
if ! command -v uv >/dev/null 2>&1; then
echo "Installing uv (Python package runner, current user only)..."
curl -LsSf https://astral.sh/uv/install.sh | sh || fail "uv installation failed. Ask the firmware team for help."
fi
echo "Checking Python libraries..."
uv sync --frozen --no-dev --python 3.12 --quiet || fail "Installing Python libraries failed."
echo
echo "Done. Start the tool with run_macos.command."
finish 0
+107
View File
@@ -0,0 +1,107 @@
@echo off
rem KOTA Sensorbox Technician Tool - update to the newest released version.
rem Checks Gitea for the newest release tag (vX.Y) and downloads only what changed (git).
rem Works in a git clone and in a folder unpacked from a release zip (it is connected on first run).
setlocal EnableDelayedExpansion
cd /d "%~dp0"
title KOTA Technician Tool - update
if not defined KOTA_REPO_URL set "KOTA_REPO_URL=https://gitea.kota-app.com/kkallas/kota-technician-tool.git"
set "UV_PROJECT_ENVIRONMENT=%LOCALAPPDATA%\KotaTechnicianTool\venv"
set "PATH=%USERPROFILE%\.local\bin;%ProgramFiles%\Git\cmd;%PATH%"
rem ---- git ----------------------------------------------------------------
where git >nul 2>nul
if errorlevel 1 (
echo Git is not installed. It is needed to download updates.
choice /m "Install Git for Windows now"
if errorlevel 2 goto :fail
winget install --id Git.Git -e --source winget --accept-package-agreements
where git >nul 2>nul
if errorlevel 1 (
echo ERROR: Git installation failed. Install it from https://git-scm.com and run this again.
goto :fail
)
)
rem ---- connect a folder unpacked from a zip ---------------------------------
if not exist ".git" (
echo Connecting this folder to %KOTA_REPO_URL% ...
git init -q || goto :fail
git remote add origin "%KOTA_REPO_URL%" || goto :fail
)
rem A developer copy follows a branch; it is updated with "git pull", not here.
git rev-parse -q --verify HEAD >nul 2>nul
if not errorlevel 1 (
for /f "delims=" %%b in ('git symbolic-ref -q --short HEAD 2^>nul') do (
echo This is a developer copy on branch %%b. Update it with: git pull
goto :done
)
)
echo Checking for a new version...
git fetch --quiet --force --tags origin
if errorlevel 1 (
echo ERROR: Cannot reach %KOTA_REPO_URL%.
echo Check the Internet connection and your Gitea login, then try again.
goto :fail
)
set "LATEST="
for /f "delims=" %%t in ('git tag -l "v*" --sort^=-v:refname') do if not defined LATEST set "LATEST=%%t"
if not defined LATEST (
echo ERROR: No released version found on the server.
goto :fail
)
set "CURRENT="
for /f "delims=" %%t in ('git describe --tags --exact-match HEAD 2^>nul') do set "CURRENT=%%t"
if "%CURRENT%"=="%LATEST%" (
echo You already have the newest version %LATEST%.
goto :deps
)
if defined CURRENT (
for /f "delims=" %%s in ('git status --porcelain --untracked-files^=no') do set "DIRTY=1"
if defined DIRTY (
echo Some tool files in this folder were changed by hand. They will be replaced by %LATEST%.
choice /m "Continue"
if errorlevel 2 goto :fail
)
echo Updating %CURRENT% -^> %LATEST%. Changes:
git --no-pager log --oneline --no-decorate "%CURRENT%..%LATEST%"
) else (
echo Installing %LATEST%...
)
git -c advice.detachedHead=false checkout -q -f "%LATEST%"
if errorlevel 1 goto :fail
echo Now on %LATEST%.
rem ---- Python libraries (only what changed) ----------------------------------
:deps
where uv >nul 2>nul
if errorlevel 1 (
echo Installing uv ^(Python package runner, current user only^)...
powershell -NoProfile -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
)
where uv >nul 2>nul
if errorlevel 1 (
echo ERROR: uv installation failed. Ask the firmware team for help.
goto :fail
)
echo Checking Python libraries...
uv sync --frozen --no-dev --python 3.12 --quiet
if errorlevel 1 goto :fail
echo.
echo Done. Start the tool with run_windows.bat.
:done
if not defined KOTA_NO_PAUSE pause
exit /b 0
:fail
echo.
echo ERROR: Update did not finish.
if not defined KOTA_NO_PAUSE pause
exit /b 1
Generated
+1 -1
View File
@@ -250,7 +250,7 @@ wheels = [
[[package]] [[package]]
name = "kota-technician-tool" name = "kota-technician-tool"
version = "0.7.0" version = "0.9.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "esptool" }, { name = "esptool" },