Technician tool v0.8

Imported from the Google Drive folder 004-technician_tool/v0.8.
This commit is contained in:
kkallas committed 2026-09-28 19:19:55 +02:00
1 parent dd915b5a74
commit 8e03986af8
13 files changed
+134 -23

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
# KOTA Sensorbox Technician Tool v0.8
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.
## Uruchomienie
**Windows:** dwuklik `run_windows.bat`.
**macOS:** dwuklik `run_macos.command`.
Jeśli Finder odpowie, że pliku nie można uruchomić (Google Drive gubi uprawnienie „wykonywalny”),
otwórz Terminal i jednorazowo wpisz `bash ` (ze spacją), przeciągnij plik `run_macos.command` do okna i naciśnij Enter.
Na stałe naprawia to: `chmod +x run_macos.command`.
Przy pierwszym uruchomieniu skrypt proponuje instalację `uv` (menedżer Pythona, instaluje się tylko dla bieżącego
użytkownika) i pobiera biblioteki. Potrzebny jest internet, trwa to 1–2 minuty. Kolejne starty są natychmiastowe.
Środowisko jest poza folderem Google Drive:
`%LOCALAPPDATA%\KotaTechnicianTool\venv-0.8` (Windows), `~/Library/Caches/KotaTechnicianTool/venv-0.8` (macOS).
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**.
## Testy (dla zespołu)
```
uv run --frozen pytest -q
```
Zmiany: `docs/CHANGES-v0.7.md`, `docs/CHANGES-v0.8.md`.
+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.
+21 -16
View File
@@ -1,9 +1,10 @@
You are testing the KOTA sensorbox technician tool v0.5 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).
You are testing the KOTA sensorbox technician tool v0.8 on macOS. The current folder is
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:
- 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.
- 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).
@@ -11,20 +12,24 @@ Rules:
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.
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)"
entry with a MAC. Connect, press "Reset & capture": the boot log starts at "ESP-ROM:esp32s3" and the
Diagnosis panel identifies firmware v1.0 (build 45fa9d5b9). Check whether the port disappears after the
reset and whether the tool reconnects.
4. Guided troubleshooting: run "Quick check" and answer truthfully with what I tell you the LED shows.
5. Flash (App only, factory image; the box already runs it): the tool must enter flashing mode by itself,
write, restart the box and report "Success ... build 45fa9d5b9". Capture the esptool output.
6. SD card tab: the settings load from ../../002-SD_card_format/ENV.txt. The card reader and card are listed
(internal disks must NOT be listed). Run "Check card", then, after my OK, "Format card and write ENV.TXT".
It must use `diskutil eraseDisk FAT32 KOTA MBRFormat` on the correct disk, write ENV.TXT byte-identical
to the template, and eject the card.
7. "Service report..." opens the browser at https://service.kota-app.com/<MAC>#kota-report=... (it is fine
if the page itself cannot prefill yet).
entry with a MAC (no /dev/tty.* duplicate). Connect, press "Reset & capture": the log starts at
"ESP-ROM:esp32s3" and the Diagnosis panel shows firmware v1.0.16 (build 4f10989e), the "Box self-report"
section and the LED with its pattern. Check whether the port disappears after the reset and whether the
tool reconnects. Wait 2 minutes: a "KOTA: health" line appears every 60 s.
4. Show filter "KOTA diagnostics + errors" shows only KOTA lines and errors.
5. Guided troubleshooting: run "Quick check" and answer truthfully with what I tell you the LED shows.
6. Flash (App only, the newest file in firmware/, chosen by its compile date): the tool must enter flashing mode
by itself, write, restart the box and report "Success ... build 4f10989e". Capture the esptool output.
7. SD card tab: the card reader and card are listed (internal disks must NOT be listed). "Check card" must NOT
report a wrong file system for a FAT32 card. After my OK, "Format card and write ENV.TXT" must use
`diskutil eraseDisk FAT32 KOTA MBRFormat` on the correct disk, write ENV.TXT and eject the card.
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
suggested fixes (file:line).
+1 -1
View File
@@ -1,3 +1,3 @@
"""KOTA sensorbox technician tool."""
__version__ = "0.7.0"
__version__ = "0.8.0"
+8
View File
@@ -137,6 +137,14 @@ id = "benign.zcl"
pattern = 'chip\[ZCL\]: WRITE ERR|data_model: Cluster cannot be NULL'
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]]
id = "benign.connect_chain"
# 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)."""
root = Path(mount)
problems: list[str] = []
fs = filesystem.upper()
if fs and "FAT32" not in fs and fs not in ("FAT", "MS-DOS FAT32", "MSDOS"):
# Windows says "FAT32"; macOS diskutil says "DOS_FAT_32" / "Windows_FAT_32" / "MS-DOS FAT32".
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.")
env_file = find_case_insensitive(root, ENV_NAME)
env = parse_env(env_file.read_bytes()) if env_file else None
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "kota-technician-tool"
version = "0.7.0"
version = "0.8.0"
description = "KOTA sensorbox technician diagnostic tool (demo)"
requires-python = ">=3.11,<3.14"
dependencies = [
+3 -1
View File
@@ -2,8 +2,10 @@
# Double-click in Finder, or run: bash run_macos.command
cd "$(dirname "$0")" || exit 1
# If Finder refuses to open this file (Google Drive drops the executable bit), run it once as:
# bash run_macos.command (or: chmod +x run_macos.command)
# Keep the virtual environment out of the (Google Drive) tool folder.
export UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv-0.7"
export UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv-0.8"
export PATH="$HOME/.local/bin:$PATH"
if ! command -v uv >/dev/null 2>&1; then
+1 -1
View File
@@ -3,7 +3,7 @@ setlocal
cd /d "%~dp0"
rem Keep the virtual environment out of the (Google Drive) tool folder.
set "UV_PROJECT_ENVIRONMENT=%LOCALAPPDATA%\KotaTechnicianTool\venv-0.7"
set "UV_PROJECT_ENVIRONMENT=%LOCALAPPDATA%\KotaTechnicianTool\venv-0.8"
set "PATH=%USERPROFILE%\.local\bin;%PATH%"
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 "reboot.loop" in {c.rule_id for c in top.consequences}
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)
assert not bt.is_sensorbox and bt.mac is None
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()
(work / "result.json").write_text('{"ok": true}', 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)
Generated
+1 -1
View File
@@ -250,7 +250,7 @@ wheels = [
[[package]]
name = "kota-technician-tool"
version = "0.7.0"
version = "0.8.0"
source = { virtual = "." }
dependencies = [
{ name = "esptool" },