11 Commits
Author SHA1 Message Date
kkallas 6a62beb6aa Merge branch 'feature/wifi-over-usb' into main
Wi-Fi over USB tab (firmware v13 usb_console), LF line endings for all text files, live v13 test capture.
2026-09-30 13:58:53 +02:00
kkallasandClaude Opus 5.5 5bd14e1d5b Test: live v13 box answers USB commands
Capture from box 14:C1:9F:57:72:48 after flashing merged-binary-v13.bin: ping and wifi get replies, logs intact through the USB-Serial-JTAG driver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:38:02 +02:00
kkallasandClaude Opus 5.5 ab420220e1 Wi-Fi over USB tab for firmware with the USB console (v13)
Writes / reads / removes the box's Wi-Fi over the serial port with the
usb_console commands (kota ping, wifi set|get|clear, health) and follows
the box through the restart until it joins the network. The password is
sent hex-encoded and never logged.

firmware/ now holds merged-binary-v13.bin (build 3ae273ec) instead of the
previous 1.0.16 build; error_ids.csv synced with the firmware.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:30:35 +02:00
kkallasandClaude Opus 5.5 0d21c5c205 Store all text files with LF line endings
Some files had been saved with CRLF on Windows, so a one-line change
showed as a whole-file diff. .gitattributes now stores text as LF
(.bat stays CRLF for cmd, .command/.sh LF for macOS).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:30:25 +02:00
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
28 changed files with 2110 additions and 25 deletions

No files matched your search

+2
View File
@@ -1,4 +1,6 @@
* text=auto eol=lf
*.command text eol=lf *.command text eol=lf
*.sh text eol=lf *.sh text eol=lf
*.bat text eol=crlf *.bat text eol=crlf
*.bin binary *.bin binary
*.png binary
+86
View File
@@ -0,0 +1,86 @@
# 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.
## Wi-Fi przez USB (bez otwierania obudowy)
Zakładka **Wi-Fi (USB)**: podłącz box kablem USB, kliknij **Connect to the box**, wpisz nazwę sieci i hasło,
kliknij **Write to the box and connect**. Box zapisuje je na karcie SD, restartuje się i łączy z siecią
(zamiast Matter); zakładka pokazuje każdy krok aż do „Connected ... (signal -58 dBm)”.
**Read current Wi-Fi settings** pokazuje zapisaną sieć (bez hasła), **Remove Wi-Fi** wraca do Matter.
Wymaga firmware z komendami USB (plik `firmware/merged-binary-v13.bin` lub nowszy); starszy firmware
wgraj w zakładce **Flash firmware** (tryb App only zachowuje parowanie i ustawienia).
## 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.
+14
View File
@@ -0,0 +1,14 @@
# Technician tool v0.10 - changes (draft)
- New tab **Wi-Fi (USB)**: write the Wi-Fi name and password to the box's SD card over the USB cable, without
opening the case (firmware usb_console, `kota wifi set`). Shows each step: command support check, stored,
restart in Wi-Fi mode, connected with signal strength (polls `kota health`). Also reads the stored network
(`kota wifi get`, password never shown) and removes it (`kota wifi clear`, back to Matter).
- The password is sent hex-encoded, never written to the session log or the service report.
- Diagnosis: restarts `usb_wifi_set` / `usb_wifi_clear` are intentional (no "unexpected restart"); box
self-report shows the stored Wi-Fi network; `cmd health` replies update the health line.
- firmware/: `merged-binary-v13.bin` (usb_console, build 3ae273ec) replaces `merged-binary.bin` (build 4f10989e,
still in git tag v0.9). Note: v13 reports the same version 1.0.16 and compile date as the previous build;
the firmware team should bump the version for the next build.
- error_ids.csv updated from the firmware (E-CON-01).
- Test that builds the main window (catches start-up errors).
+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"
+22
View File
@@ -335,6 +335,8 @@ class DiagnosticsEngine:
self.matter_qr: str | None = None # "KOTA: matter ... qr=MT:..." self.matter_qr: str | None = None # "KOTA: matter ... qr=MT:..."
self.matter_manual: str | None = None self.matter_manual: str | None = None
self.kota_seen = False # firmware prints KOTA: lines self.kota_seen = False # firmware prints KOTA: lines
self.console_api: int | None = None # "cmd ping ... api=N": box accepts USB commands
self.wifi_config: dict[str, str] | None = None # last "cmd wifi_get" reply (never a password)
self._reset_led_state() self._reset_led_state()
def _reset_led_state(self) -> None: def _reset_led_state(self) -> None:
@@ -714,6 +716,20 @@ class DiagnosticsEngine:
fd = self._custom(key, Severity[sev.upper()], title, meaning, actions, expl) fd = self._custom(key, Severity[sev.upper()], title, meaning, actions, expl)
self._touch(fd, line) self._touch(fd, line)
def _kota_cmd(self, line: LogLine, f: dict[str, str]) -> None:
"""Replies to commands sent by this tool (firmware usb_console)."""
name = line.words[0] if line.words else ""
if f.get("ok") != "1":
return # the Wi-Fi tab reports failures itself
if name == "ping":
api = f.get("api", "")
self.console_api = int(api) if api.isdigit() else None
elif name == "health":
self.boot.kota["health"] = line # same fields as the periodic health line
self._kota_health(line, f)
elif name == "wifi_get":
self.wifi_config = {k: f.get(k, "?") for k in ("ssid", "password_set", "mode")}
def _kota_led(self, line: LogLine, f: dict[str, str]) -> None: def _kota_led(self, line: LogLine, f: dict[str, str]) -> None:
if "previous_boot" in line.words: if "previous_boot" in line.words:
code = f.get("code", "0") code = f.get("code", "0")
@@ -845,6 +861,12 @@ class DiagnosticsEngine:
if ln := k.get("mic"): if ln := k.get("mic"):
on = ln.fields.get("enabled") == "1" on = ln.fields.get("enabled") == "1"
out.append(("Microphone", "on" if on else "switched OFF (switch on the back)", "ok" if on else "info")) out.append(("Microphone", "on" if on else "switched OFF (switch on the back)", "ok" if on else "info"))
if self.wifi_config:
from ..serialio.commands import unhex_utf8
w = self.wifi_config
ssid = unhex_utf8(w["ssid"])
out.append(("Wi-Fi config", (f"network '{ssid}', password {'set' if w['password_set'] == '1' else 'NOT set'}"
if ssid else "no network on the SD card") + f", mode {w['mode']}", "info"))
if ln := k.get("health"): if ln := k.get("health"):
f = ln.fields f = ln.fields
wifi = f.get("wifi", "?") wifi = f.get("wifi", "?")
+1
View File
@@ -191,3 +191,4 @@ E-GATH-01,GATH,error,Failed to take line protocol mutex,Internal error - send th
E-GATH-02,GATH,error,Failed to get line protocol from sensor,Device ran out of memory; restart it and send the log to the firmware team E-GATH-02,GATH,error,Failed to get line protocol from sensor,Device ran out of memory; restart it and send the log to the firmware team
E-GATH-03,GATH,error,Failed to create task for sensor,Internal error - send the log to the firmware team E-GATH-03,GATH,error,Failed to create task for sensor,Internal error - send the log to the firmware team
E-GATH-04,GATH,error,Failed to create main gather task,Internal error - send the log to the firmware team E-GATH-04,GATH,error,Failed to create main gather task,Internal error - send the log to the firmware team
E-CON-01,CON,error,Failed to install USB-Serial-JTAG driver: %s,Internal error - send the log to the firmware team
1 id module severity message action
191 E-GATH-02 GATH error Failed to get line protocol from sensor Device ran out of memory; restart it and send the log to the firmware team
192 E-GATH-03 GATH error Failed to create task for sensor Internal error - send the log to the firmware team
193 E-GATH-04 GATH error Failed to create main gather task Internal error - send the log to the firmware team
194 E-CON-01 CON error Failed to install USB-Serial-JTAG driver: %s Internal error - send the log to the firmware team
+1
View File
@@ -39,6 +39,7 @@ I2C_DEVICES = {"0x39": "AS7341", "0x68": "DS1307", "0x6B": "SEN66", "0x76": "BME
INTENTIONAL_RESTARTS = { INTENTIONAL_RESTARTS = {
"ota_update", "button_provisioning_abort", "button_wifi_cleared", "ota_update", "button_provisioning_abort", "button_wifi_cleared",
"button_provisioning_toggle", "provisioning_saved", "button_provisioning_toggle", "provisioning_saved",
"usb_wifi_set", "usb_wifi_clear", # Wi-Fi changed from the technician tool (usb_console)
} }
MODULES = { MODULES = {
+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
+283
View File
@@ -0,0 +1,283 @@
"""Commands to the box over the USB serial port (firmware with usb_console, api=1).
Protocol (kota-sensorboard main/usb_console.hpp): ASCII lines "kota ...",
one reply line each, logged as "KOTA: cmd <name> ok=1|0 [key=value ...]":
kota ping -> cmd ping ok=1 fw=<version> api=1
kota health -> cmd health ok=1 <health fields> | ok=0 err=not_ready
kota wifi get -> cmd wifi_get ok=1 ssid=<hex|-> password_set=0|1 mode=...
kota wifi set <ssid_hex> <pass_hex> -> cmd wifi_set ok=1 restart=1, then restart (usb_wifi_set)
kota wifi clear -> cmd wifi_clear ok=1 restart=1, then restart (usb_wifi_clear)
Firmware without the console never answers. The password is only ever sent
hex-encoded and is never written to the log by the box or by this tool.
WifiFlow runs a whole "write / clear Wi-Fi" job as a small state machine
driven by `feed(line)` and `tick(now)`, so it is testable without Qt or a box.
"""
from __future__ import annotations
import enum
import time
from collections.abc import Callable
from dataclasses import dataclass, field
from ..core.logline import LineKind, LogLine
API_VERSION = 1
# Technician-facing text for the firmware's err= codes.
ERRORS = {
"bad_args": "The box did not understand the command (tool/firmware mismatch).",
"bad_hex": "The box could not decode the settings (tool/firmware mismatch).",
"bad_char": "The name or password contains a line break.",
"ssid_length": "The network name must be 1-32 bytes.",
"password_length": "The password must be 8-63 characters.",
"sd_not_mounted": "The box cannot read its SD card, so it cannot store the settings.",
"write_failed": "Writing ENV.TXT on the SD card failed.",
"verify_failed": "ENV.TXT did not read back correctly after writing.",
"unknown_command": "This firmware does not know the command.",
"not_ready": "The box is still starting; try again in a few seconds.",
}
def hex_utf8(text: str) -> str:
return text.encode("utf-8").hex()
def unhex_utf8(value: str) -> str | None:
if not value or value == "-":
return None
try:
return bytes.fromhex(value).decode("utf-8", errors="replace")
except ValueError:
return None
def validate_wifi(ssid: str, password: str) -> list[str]:
"""Same limits as the firmware (usb_console.cpp); empty list = OK."""
problems = []
if not ssid:
problems.append("Enter the Wi-Fi network name (SSID).")
elif len(ssid.encode("utf-8")) > 32:
problems.append("The network name is longer than 32 bytes.")
if not 8 <= len(password.encode("utf-8")) <= 63:
problems.append("The password must be 8-63 characters (WPA2). Open networks are not supported.")
if any(c in ssid + password for c in "\r\n\0"):
problems.append("The name or password contains a line break.")
return problems
def wifi_set_command(ssid: str, password: str) -> str:
problems = validate_wifi(ssid, password)
if problems:
raise ValueError(" ".join(problems))
return f"kota wifi set {hex_utf8(ssid)} {hex_utf8(password)}"
def loggable(command: str) -> str:
"""The command as it may appear in the session log: never the password."""
parts = command.split()
if parts[:3] == ["kota", "wifi", "set"] and len(parts) == 5:
return f"kota wifi set (network '{unhex_utf8(parts[3])}', password hidden)"
return command
@dataclass(frozen=True)
class Reply:
name: str # ping | health | wifi_get | wifi_set | wifi_clear | unknown
ok: bool
fields: dict[str, str]
@property
def error(self) -> str:
code = self.fields.get("err", "")
return ERRORS.get(code, f"The box reported an error ({code or 'unknown'}).")
def parse_reply(line: LogLine) -> Reply | None:
"""'I (5) KOTA: cmd wifi_set ok=1 restart=1' -> Reply('wifi_set', True, {...})."""
if line.kind != LineKind.KOTA or line.record != "cmd" or not line.words:
return None
return Reply(line.words[0], line.fields.get("ok") == "1", dict(line.fields))
@dataclass(frozen=True)
class WifiInfo:
"""What `kota wifi get` reported."""
ssid: str | None
password_set: bool
mode: str
@classmethod
def from_reply(cls, r: Reply) -> WifiInfo:
return cls(unhex_utf8(r.fields.get("ssid", "-")), r.fields.get("password_set") == "1",
r.fields.get("mode", "?"))
def describe(self) -> str:
mode = {"wifi": "local Wi-Fi from the SD card", "matter": "Matter (Wi-Fi given by the Matter app)",
"provisioning": "Wi-Fi setup portal"}.get(self.mode, self.mode)
net = f"network '{self.ssid}'" + (", password set" if self.password_set else ", NO password")
return f"Mode: {mode}. " + (net if self.ssid else "No network stored on the SD card.")
# ------------------------------------------------------------------ flows
class Step(str, enum.Enum):
PING = "Checking the box accepts USB commands"
READ = "Reading the Wi-Fi settings"
SEND = "Storing the settings on the box's SD card"
RESTART = "Waiting for the box to restart"
CONNECT = "Waiting for the box to join the Wi-Fi network"
DONE = "Done"
FAILED = "Failed"
@dataclass
class FlowEvent:
step: Step
text: str
ok: bool | None = None # None = in progress, True = finished OK, False = failed
PING_TIMEOUT_S = 3.0
REPLY_TIMEOUT_S = 8.0
RESTART_TIMEOUT_S = 40.0
CONNECT_TIMEOUT_S = 90.0
HEALTH_POLL_S = 5.0
@dataclass
class WifiFlow:
"""One technician job: "read", "set" (ssid+password) or "clear".
`send(text)` writes one command line to the box (returns False when the
port is not connected); `on_event` reports progress for the UI.
"""
action: str
send: Callable[[str], bool]
on_event: Callable[[FlowEvent], None]
ssid: str = ""
password: str = ""
now: Callable[[], float] = time.monotonic
step: Step = Step.PING
info: WifiInfo | None = None
_deadline: float = 0.0
_next_poll: float = 0.0
_events: list[FlowEvent] = field(default_factory=list)
@property
def finished(self) -> bool:
return self.step in (Step.DONE, Step.FAILED)
def start(self) -> None:
if self.action == "set":
problems = validate_wifi(self.ssid, self.password)
if problems:
return self._fail(" ".join(problems))
self._go(Step.PING, "kota ping", PING_TIMEOUT_S)
def cancel(self) -> None:
if not self.finished:
self._fail("Cancelled.")
# -------------------------------------------------------- driving
def feed(self, line: LogLine) -> None:
if self.finished:
return
if self.step == Step.RESTART and line.kind == LineKind.KOTA and line.record == "boot":
return self._on_boot(line.fields)
r = parse_reply(line)
if r is None:
return
if self.step == Step.PING and r.name == "ping":
if not r.ok or int(r.fields.get("api", "0") or 0) < API_VERSION:
return self._fail("The box answered, but its command version is not supported.")
self._emit(f"Firmware {r.fields.get('fw', '?')} accepts USB commands.", True)
if self.action == "read":
return self._go(Step.READ, "kota wifi get", REPLY_TIMEOUT_S)
if self.action == "set":
return self._go(Step.SEND, wifi_set_command(self.ssid, self.password), REPLY_TIMEOUT_S)
return self._go(Step.SEND, "kota wifi clear", REPLY_TIMEOUT_S)
if self.step == Step.READ and r.name == "wifi_get":
if not r.ok:
return self._fail(r.error)
self.info = WifiInfo.from_reply(r)
return self._finish(self.info.describe())
if self.step == Step.SEND and r.name in ("wifi_set", "wifi_clear"):
if not r.ok:
return self._fail(r.error)
self._emit("The box stored the settings and is restarting.", True)
self.step, self._deadline = Step.RESTART, self.now() + RESTART_TIMEOUT_S
return self._emit(Step.RESTART.value + "...")
if self.step == Step.CONNECT and r.name == "health" and r.ok:
return self._on_health(r.fields)
def tick(self) -> None:
"""Call periodically (e.g. every 0.5 s): timeouts and health polling."""
if self.finished:
return
t = self.now()
if self.step == Step.CONNECT and t >= self._next_poll:
self._next_poll = t + HEALTH_POLL_S
self.send("kota health")
if t < self._deadline:
return
if self.step == Step.PING:
self._fail("The box does not answer USB commands. It needs firmware with the USB console "
"(Flash firmware tab), or it is still starting - try again in 10 s.")
elif self.step == Step.RESTART:
self._fail("The box stored the settings but no restart was seen. Press Reset & capture in the "
"Serial monitor and check the Diagnosis panel.")
elif self.step == Step.CONNECT:
self._fail(f"The box did not join '{self.ssid}' within {int(CONNECT_TIMEOUT_S)} s. Check the network "
"name and password, that it is a 2.4 GHz network and that it is in range. The settings stay "
"on the box; it keeps retrying.")
else:
self._fail("The box did not answer. Check the USB connection and try again.")
# -------------------------------------------------------- internals
def _on_boot(self, f: dict[str, str]) -> None:
mode, last = f.get("mode", "?"), f.get("last_restart", "?")
want = "usb_wifi_set" if self.action == "set" else "usb_wifi_clear"
if last != want:
return self._fail(f"The box restarted for another reason ({last}); the settings may not be applied.")
self._emit(f"Restarted in {mode} mode.", True)
if self.action == "clear":
ok = mode == "matter"
return self._finish("Wi-Fi removed; the box uses Matter again (pair it in the Matter app if needed).") \
if ok else self._fail(f"The box restarted in {mode} mode instead of Matter.")
if mode != "wifi":
return self._fail(f"The box restarted in {mode} mode instead of local Wi-Fi.")
self.step = Step.CONNECT
self._deadline = self.now() + CONNECT_TIMEOUT_S
self._next_poll = self.now() + HEALTH_POLL_S
self._emit(Step.CONNECT.value + f" '{self.ssid}'...")
def _on_health(self, f: dict[str, str]) -> None:
if f.get("wifi") != "connected":
return
rssi = f.get("rssi", "?")
note = " - weak signal, consider moving the box or the router" if rssi.lstrip("-").isdigit() \
and int(rssi) < -75 else ""
self._finish(f"Connected to '{self.ssid}' (signal {rssi} dBm{note}). Data upload starts within a minute.")
def _go(self, step: Step, command: str, timeout: float) -> None:
self.step, self._deadline = step, self.now() + timeout
self._emit(step.value + "...")
if not self.send(command):
self._fail("The box is not connected. Connect it first.")
def _emit(self, text: str, ok: bool | None = None) -> None:
ev = FlowEvent(self.step, text, ok)
self._events.append(ev)
self.on_event(ev)
def _finish(self, text: str) -> None:
self.step = Step.DONE
self._emit(text, True)
def _fail(self, text: str) -> None:
self.step = Step.FAILED
self._emit(text, False)
+40
View File
@@ -28,6 +28,7 @@ from ..core.logline import LineKind, LogLine, host_event
from ..core.session import SessionLog from ..core.session import SessionLog
from ..diag.engine import DiagnosticsEngine from ..diag.engine import DiagnosticsEngine
from ..diag.guides import GuideRunner from ..diag.guides import GuideRunner
from ..serialio.commands import loggable
from ..serialio.manager import SerialManager, State from ..serialio.manager import SerialManager, State
from ..serialio.ports import PortInfo, list_serial_ports from ..serialio.ports import PortInfo, list_serial_ports
from ..matter.payload import SetupPayload, payload_from_log from ..matter.payload import SetupPayload, payload_from_log
@@ -38,6 +39,7 @@ from .flash_tab import FlashTab
from .matter_qr import MatterQrDialog from .matter_qr import MatterQrDialog
from .guide_panel import GuidePanel from .guide_panel import GuidePanel
from .sd_tab import SdTab from .sd_tab import SdTab
from .wifi_tab import WifiTab
MAX_LINES = 20000 MAX_LINES = 20000
# "KOTA" = the firmware's structured diagnostic lines (1.0.16+) plus errors. # "KOTA" = the firmware's structured diagnostic lines (1.0.16+) plus errors.
@@ -76,6 +78,7 @@ class SerialTab(QWidget):
self._engine = DiagnosticsEngine() self._engine = DiagnosticsEngine()
self._diag_dirty = True self._diag_dirty = True
self._runner = GuideRunner(self._engine, on_action=self._guide_action, on_log=self._guide_log) self._runner = GuideRunner(self._engine, on_action=self._guide_action, on_log=self._guide_log)
self._line_listeners: list = [] # other tabs following the live log (Wi-Fi over USB)
# --- controls # --- controls
self.port_combo = QComboBox(minimumWidth=320) self.port_combo = QComboBox(minimumWidth=320)
@@ -237,6 +240,39 @@ class SerialTab(QWidget):
def engine(self) -> DiagnosticsEngine: def engine(self) -> DiagnosticsEngine:
return self._engine return self._engine
# ---- commands to the box (firmware usb_console) -----------------------
@property
def is_connected(self) -> bool:
return self._manager.state == State.CONNECTED
@property
def active_port(self) -> PortInfo | None:
return self._active_port if self._manager.is_running else None
def add_line_listener(self, callback) -> None:
self._line_listeners.append(callback)
def send_command(self, command: str) -> bool:
"""Write one "kota ..." line to the box; False when not connected.
The session log gets the command without the Wi-Fi password."""
if not self.is_connected:
return False
self._manager.write((command + "\n").encode("ascii"))
if not command.startswith(("kota health", "kota ping")): # polled; would flood the log
self._guide_log(f"sent to the box: {loggable(command)}")
return True
def connect_first_box(self) -> bool:
"""Connect to the first sensorbox port found (for tabs that need the box)."""
if self._manager.is_running:
return True
self.refresh_ports()
port = next((p for p in self._ports if p.is_sensorbox), None)
if port is None:
return False
self.connect_port(port, capture=False)
return True
def release_port(self) -> None: def release_port(self) -> None:
"""Stop monitoring so esptool can open the port exclusively.""" """Stop monitoring so esptool can open the port exclusively."""
if self._manager.is_running: if self._manager.is_running:
@@ -293,6 +329,8 @@ class SerialTab(QWidget):
self._session.write(line) self._session.write(line)
self._engine.feed(line) self._engine.feed(line)
self._runner.feed(line) self._runner.feed(line)
for listener in self._line_listeners:
listener(line)
self._diag_dirty = True self._diag_dirty = True
if line.kind == LineKind.BOOT: if line.kind == LineKind.BOOT:
self._counts["boots"] += 1 self._counts["boots"] += 1
@@ -402,6 +440,8 @@ class MainWindow(QMainWindow):
tabs.addTab(guide, "Quick start") tabs.addTab(guide, "Quick start")
tabs.addTab(self.serial_tab, "Serial monitor") tabs.addTab(self.serial_tab, "Serial monitor")
tabs.addTab(self.flash_tab, "Flash firmware") tabs.addTab(self.flash_tab, "Flash firmware")
self.wifi_tab = WifiTab(self.serial_tab)
tabs.addTab(self.wifi_tab, "Wi-Fi (USB)")
self.sd_tab = SdTab(self.serial_tab) self.sd_tab = SdTab(self.serial_tab)
tabs.addTab(self.sd_tab, "SD card") tabs.addTab(self.sd_tab, "SD card")
self.setCentralWidget(tabs) self.setCentralWidget(tabs)
+207
View File
@@ -0,0 +1,207 @@
"""Wi-Fi over USB: read / write / remove the box's Wi-Fi without opening the case.
Needs firmware with the USB console (usb_console, api=1). The job itself is
serialio.commands.WifiFlow; this tab only collects the input and shows the
steps. Lines arrive from the Serial monitor's connection (one port owner).
"""
from __future__ import annotations
from PySide6.QtCore import Qt, QTimer
from PySide6.QtWidgets import (
QCheckBox,
QFormLayout,
QGroupBox,
QHBoxLayout,
QLabel,
QLineEdit,
QListWidget,
QListWidgetItem,
QMessageBox,
QPushButton,
QVBoxLayout,
QWidget,
)
from PySide6.QtGui import QBrush, QColor
from ..core.logline import LogLine
from ..serialio.commands import FlowEvent, WifiFlow, validate_wifi
INTRO = (
"Stores the Wi-Fi name and password on the box's SD card over the USB cable - <b>no need to open the "
"case</b>. The box then restarts and uses this network instead of Matter. Needs firmware with USB commands "
"(<i>Flash firmware</i> tab, newest file). Only 2.4 GHz networks with a password (8-63 characters)."
)
MARK = {True: ("✔", "#1b7a33"), False: ("✖", "#b03a2e"), None: ("…", "#2e6da4")}
class WifiTab(QWidget):
def __init__(self, serial_tab, parent: QWidget | None = None):
super().__init__(parent)
self.serial_tab = serial_tab
self._flow: WifiFlow | None = None
intro = QLabel(INTRO, wordWrap=True)
# 1. box
box = QGroupBox("1. Sensorbox")
self.box_state = QLabel(wordWrap=True)
self.connect_btn = QPushButton("Connect to the box")
self.connect_btn.clicked.connect(self._connect)
self.read_btn = QPushButton("Read current Wi-Fi settings")
self.read_btn.clicked.connect(lambda: self._run("read"))
bl = QVBoxLayout(box)
bl.addWidget(self.box_state)
brow = QHBoxLayout()
brow.addWidget(self.connect_btn)
brow.addWidget(self.read_btn)
brow.addStretch(1)
bl.addLayout(brow)
# 2. network
net = QGroupBox("2. Wi-Fi network for this box")
self.ssid = QLineEdit(placeholderText="network name (SSID), exactly as on the router")
self.password = QLineEdit(placeholderText="8-63 characters")
self.password.setEchoMode(QLineEdit.EchoMode.Password)
show = QCheckBox("Show password")
show.toggled.connect(lambda on: self.password.setEchoMode(
QLineEdit.EchoMode.Normal if on else QLineEdit.EchoMode.Password))
form = QFormLayout()
form.addRow("Network (SSID)", self.ssid)
prow = QHBoxLayout()
prow.addWidget(self.password, 1)
prow.addWidget(show)
form.addRow("Password", prow)
self.input_problem = QLabel(wordWrap=True)
self.input_problem.setStyleSheet("color:#b03a2e")
self.write_btn = QPushButton("Write to the box and connect")
self.write_btn.setMinimumHeight(34)
self.write_btn.clicked.connect(lambda: self._run("set"))
self.clear_btn = QPushButton("Remove Wi-Fi (back to Matter)")
self.clear_btn.clicked.connect(self._clear)
nl = QVBoxLayout(net)
nl.addLayout(form)
nl.addWidget(self.input_problem)
arow = QHBoxLayout()
arow.addWidget(self.write_btn)
arow.addWidget(self.clear_btn)
arow.addStretch(1)
nl.addLayout(arow)
# 3. progress
prog = QGroupBox("3. Progress")
self.steps = QListWidget()
self.steps.setSelectionMode(QListWidget.SelectionMode.NoSelection)
self.result = QLabel(wordWrap=True)
self.result.setMargin(8)
self.result.setVisible(False)
self.cancel_btn = QPushButton("Cancel")
self.cancel_btn.clicked.connect(self._cancel)
pl = QVBoxLayout(prog)
pl.addWidget(self.steps)
pl.addWidget(self.result)
pl.addWidget(self.cancel_btn, 0, Qt.AlignmentFlag.AlignLeft)
lay = QVBoxLayout(self)
lay.addWidget(intro)
lay.addWidget(box)
lay.addWidget(net)
lay.addWidget(prog, 1)
serial_tab.add_line_listener(self._on_line)
for w in (self.ssid, self.password):
w.textChanged.connect(self._update)
self._timer = QTimer(self, interval=500)
self._timer.timeout.connect(self._tick)
self._timer.start()
self._update()
# ------------------------------------------------------------ state
def showEvent(self, event): # noqa: N802 - refresh when the tab is opened
self._update()
super().showEvent(event)
def _update(self) -> None:
connected = self.serial_tab.is_connected
busy = self._flow is not None and not self._flow.finished
port = self.serial_tab.active_port
eng = self.serial_tab.engine
if connected and port:
fw = eng.firmware.describe() if eng.firmware.elf_sha else "firmware not identified yet"
api = {None: "", 1: " - USB commands supported"}.get(eng.console_api, f" - USB commands api {eng.console_api}")
self.box_state.setText(f"Connected: <b>{port.label}</b><br><small>{fw}{api}</small>")
elif self.serial_tab.active_port:
self.box_state.setText("Waiting for the box... (it may be restarting)")
else:
self.box_state.setText("Not connected. Plug in the sensorbox (native USB) and press "
"<b>Connect to the box</b>.")
problems = validate_wifi(self.ssid.text(), self.password.text())
typed = bool(self.ssid.text() or self.password.text())
self.input_problem.setText(" ".join(problems) if typed else "")
self.connect_btn.setVisible(not self.serial_tab.active_port)
for w in (self.read_btn, self.clear_btn):
w.setEnabled(connected and not busy)
self.write_btn.setEnabled(connected and not busy and not problems)
self.ssid.setEnabled(not busy)
self.password.setEnabled(not busy)
self.cancel_btn.setVisible(busy)
def _tick(self) -> None:
if self._flow is not None:
self._flow.tick()
self._update()
# ------------------------------------------------------------ actions
def _connect(self) -> None:
if not self.serial_tab.connect_first_box():
QMessageBox.information(self, "Wi-Fi over USB", "No sensorbox found. Check the USB cable (data-capable) "
"and that the box is powered, then try again.")
self._update()
def _clear(self) -> None:
if QMessageBox.question(
self, "Remove Wi-Fi",
"Remove the Wi-Fi network from the box?\n\nThe box restarts and uses Matter again. If it was never "
"paired in Matter, it has no network until you pair it in the Matter app.",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) == QMessageBox.StandardButton.Yes:
self._run("clear")
def _run(self, action: str) -> None:
if action == "set" and QMessageBox.question(
self, "Write Wi-Fi",
f"Store network '{self.ssid.text()}' on the box?\n\nThe box restarts and connects to this Wi-Fi "
"instead of Matter (about 30 s).",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) != QMessageBox.StandardButton.Yes:
return
self.steps.clear()
self.result.setVisible(False)
self._flow = WifiFlow(action, self.serial_tab.send_command, self._on_event,
ssid=self.ssid.text(), password=self.password.text())
self._flow.start()
self._update()
def _cancel(self) -> None:
if self._flow is not None:
self._flow.cancel()
self._update()
# ------------------------------------------------------------ events
def _on_line(self, line: LogLine) -> None:
if self._flow is not None:
self._flow.feed(line)
def _on_event(self, ev: FlowEvent) -> None:
mark, colour = MARK[ev.ok]
item = QListWidgetItem(f"{mark} {ev.text}")
item.setForeground(QBrush(QColor(colour)))
self.steps.addItem(item)
self.steps.scrollToBottom()
if self._flow is not None and self._flow.finished or ev.ok is False:
ok = ev.ok is True
border, bg = ("#2e9e44", "#e3f4e7") if ok else ("#d9534f", "#f9e0df")
self.result.setStyleSheet(f"QLabel {{ border:2px solid {border}; background:{bg}; border-radius:6px; }}")
self.result.setText(("<b>Done.</b> " if ok else "<b>Not done.</b> ") + ev.text)
self.result.setVisible(True)
if ok and self._flow and self._flow.info and self._flow.info.ssid and not self.ssid.text():
self.ssid.setText(self._flow.info.ssid) # read: prefill the stored network name
self._update()
+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
@@ -0,0 +1,973 @@
--- connected to COM14 ---
W (9426) wifi:Haven't to connect to a suitable AP now!
--- reset issued by tool ---
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x15 (USB_UART_CHIP_RESET),boot:0xb (SPI_FAST_FLASH_BOOT)
Saved PC:0x4037f366
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce2820,len:0x14f0
load:0x403c8700,len:0xd24
load:0x403cb700,len:0x2f48
entry 0x403c8924
I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader
I (24) boot: compile time Sep 10 2026 13:24:39
I (25) boot: Multicore bootloader
I (25) boot: chip revision: v0.2
I (28) boot: efuse block revision: v1.4
I (32) boot.esp32s3: Boot SPI Speed : 80MHz
I (35) boot.esp32s3: SPI Mode : DIO
I (39) boot.esp32s3: SPI Flash Size : 8MB
I (43) boot: Enabling RNG early entropy source...
I (48) boot: Partition Table:
I (50) boot: ## Label Usage Type ST Offset Length
I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000
I (63) boot: 1 nvs WiFi data 01 02 00010000 00020000
I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000
I (76) boot: 3 otadata OTA data 01 00 00031000 00002000
I (82) boot: 4 phy_init RF data 01 01 00033000 00001000
I (89) boot: 5 ota_0 OTA app 00 10 00040000 00300000
I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000
I (102) boot: 7 fctry WiFi data 01 02 00640000 00006000
I (109) boot: End of partition table
I (112) esp_image: segment 0: paddr=00040020 vaddr=3c190020 size=67420h (422944) map
I (194) esp_image: segment 1: paddr=000a7448 vaddr=3fc9c700 size=05d00h ( 23808) load
I (200) esp_image: segment 2: paddr=000ad150 vaddr=40374000 size=02ec8h ( 11976) load
I (203) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=187370h (1602416) map
I (488) esp_image: segment 4: paddr=00237398 vaddr=40376ec8 size=1580ch ( 88076) load
I (507) esp_image: segment 5: paddr=0024cbac vaddr=5000002c size=00020h ( 32) load
I (517) boot: Loaded app from partition at offset 0x40000
I (518) boot: Disabling RNG early entropy source...
I (528) octal_psram: vendor id : 0x0d (AP)
I (528) octal_psram: dev id : 0x02 (generation 3)
I (529) octal_psram: density : 0x03 (64 Mbit)
I (530) octal_psram: good-die : 0x01 (Pass)
I (535) octal_psram: Latency : 0x01 (Fixed)
I (539) octal_psram: VCC : 0x01 (3V)
I (543) octal_psram: SRF : 0x01 (Fast Refresh)
I (548) octal_psram: BurstType : 0x01 (Hybrid Wrap)
I (553) octal_psram: BurstLen : 0x01 (32 Byte)
I (557) octal_psram: Readlatency : 0x02 (10 cycles@Fixed)
I (562) octal_psram: DriveStrength: 0x00 (1/1)
I (567) esp_psram: Found 8MB PSRAM device
I (570) esp_psram: Speed: 40MHz
I (573) cpu_start: Multicore app
I (1307) esp_psram: SPI SRAM memory test OK
I (1315) cpu_start: GPIO 44 and 43 are used as console UART I/O pins
I (1316) cpu_start: Pro cpu start user code
I (1316) cpu_start: cpu freq: 160000000 Hz
I (1318) app_init: Application information:
I (1322) app_init: Project name: kota-sensorboard
I (1327) app_init: App version: 1.0.16
I (1331) app_init: Compile time: Sep 28 2026 15:07:57
I (1336) app_init: ELF file SHA256: 3ae273ec1...
I (1340) app_init: ESP-IDF: v5.5.3-dirty
I (1345) efuse_init: Min chip rev: v0.0
I (1348) efuse_init: Max chip rev: v0.99
I (1353) efuse_init: Chip rev: v0.2
I (1357) heap_init: Initializing. RAM available for dynamic allocation:
I (1363) heap_init: At 3FCB4AD0 len 00034C40 (211 KiB): RAM
I (1368) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (1373) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (1379) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM
I (1384) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator
I (1392) spi_flash: detected chip: generic
I (1394) spi_flash: flash io: dio
I (1400) sleep_gpio: Configure to isolate all GPIO pins in sleep state
I (1404) sleep_gpio: Enable automatic switching of GPIO sleep configuration
I (1419) main_task: Started on CPU0
I (1422) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (1430) main_task: Calling app_main()
E (1433) ID: [E-MAIN-04] Device MAC address: 14:C1:9F:57:72:48
I (1475) KOTA: led color=green pattern=solid
I (1676) I2C: Creating I2C mutex
I (1694) KOTA: i2c SDA=1 SCL=1 0x39=AS7341 0x68=DS1307 0x6B=SEN66 0x76=BME688
I (1696) OTA_CLIENT: OTA token loaded from NVS: <REDACTED>
I (1698) OTA_CLIENT: Device is not in selfcheck mode
I (1702) OTA_CLIENT: Update metadata loaded from NVS: version=15, status=2
I (1707) OTA_CLIENT: Device is in factory state
I (1711) SPI: Creating SPI mutex
I (1755) SD_CARD: SD card mounted successfully
I (1756) SD_CARD: /sdcard exists, SD card initialized successfully
I (1758) INFLUX_BUFFER: Buffer file does not exist, returning count of 0
I (1763) KOTA: sd type=SDHC size=29.2GB fs=FAT32 free=29.1GB env_keys=INFLUX_TOKEN,INFLUX_URL buffer_lines=0
I (1774) WIFI_CREDENTIALS: SSID/PASSWORD not set in .env; Matter will be used for WiFi provisioning
I (1785) MAIN: Matter node created successfully
W (1787) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again.
W (1793) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again.
W (1802) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again.
W (1811) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1819) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again.
W (1828) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1837) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again.
W (1846) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again.
W (1854) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again.
W (1863) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1872) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again.
W (1881) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1890) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again.
W (1898) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again.
W (1907) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again.
W (1916) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1925) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again.
W (1933) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again.
I (1944) pp: pp rom version: e7ae62f
I (1945) net80211: net80211 rom version: e7ae62f
I (1951) wifi:wifi driver task: 3fcc9610, prio:23, stack:6656, core=0
I (1969) wifi:wifi firmware version: 4df78f2
I (1970) wifi:wifi certification version: v7.0
I (1971) wifi:config NVS flash: enabled
I (1971) wifi:config nano formatting: disabled
I (1975) wifi:Init data frame dynamic rx buffer num: 32
I (1980) wifi:Init static rx mgmt buffer num: 5
I (1984) wifi:Init management short buffer num: 32
I (1988) wifi:Init dynamic tx buffer num: 32
I (1992) wifi:Init static tx FG buffer num: 2
I (1997) wifi:Init static rx buffer size: 1600
I (2001) wifi:Init static rx buffer num: 10
I (2004) wifi:Init dynamic rx buffer num: 32
I (2009) wifi_init: rx ba win: 6
I (2011) wifi_init: accept mbox: 6
I (2014) wifi_init: tcpip mbox: 32
I (2018) wifi_init: udp mbox: 6
I (2020) wifi_init: tcp mbox: 6
I (2023) wifi_init: tcp tx win: 5760
I (2027) wifi_init: tcp rx win: 5760
I (2030) wifi_init: tcp mss: 1440
I (2045) chip[DL]: NVS set: chip-counters/reboot-count = 13 (0xD)
I (2048) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC)
I (2050) BLE_INIT: BT controller compile version [1bb2f50]
I (2053) BLE_INIT: Using main XTAL as clock source
I (2058) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1
I (2067) BLE_INIT: Bluetooth MAC: 14:c1:9f:57:72:4a
I (2072) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10
I (2119) NimBLE: GAP procedure initiated: stop advertising.
I (2122) CHIP[DL]: BLE host-controller synced
I (2623) chip[DL]: Starting ESP WiFi layer
I (2626) wifi:mode : sta (14:c1:9f:57:72:48)
I (2627) wifi:enable tsf
W (2628) wifi:Haven't to connect to a suitable AP now!
I (2629) chip[DL]:
I (2629) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43Attempting to connect WiFi station interface
I (2641) chip[DL]: WiFi station state change: NotConnected -> Connecting
I (2642) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2
I (2647) chip[DL]:
Done driving station state, nothing else to do...
I (2659) chip[SVR]: Initializing subscription resumption storage...
I (2666) chip[SVR]: Server initializing...
I (2669) chip[TS]: Last Known Good Time: 2026-09-29T14:59:23
I (2674) chip[DMG]: AccessControl: initializing
I (2678) chip[DMG]: Examples::AccessControlDelegate::Init
I (2683) chip[DMG]: AccessControl: setting
I (2687) chip[DMG]: DefaultAclStorage: initializing
I (2691) chip[DMG]: DefaultAclStorage: 0 entries loaded
I (2696) chip[SVR]: WARNING: mTestEventTriggerDelegate is null
I (2715) chip[ZCL]: Using ZAP configuration...
I (2716) esp_matter_cluster: Cluster plugin init common callback
I (2718) chip[DMG]: AccessControlCluster: initializing
I (2720) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported
I (2730) chip[DIS]: Updating services using commissioning mode 1
I (2733) chip[DIS]: CHIP minimal mDNS started advertising.
I (2739) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0
I (2749) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 995B80B34BC8AAD9.
I (2758) chip[DIS]: mDNS service published: _matterc._udp
I (2763) chip[IN]: CASE Server enabling CASE session setups
I (2769) chip[SVR]: Joining Multicast groups
I (2772) chip[SVR]: Server Listening...
I (2778) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 **********
I (2788) data_model: Dynamic endpoint 0 added
E (2790) data_model: Cluster cannot be NULL.
I (2795) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 **********
I (2806) data_model: Dynamic endpoint 1 added
E (2809) data_model: Cluster cannot be NULL.
I (2815) chip[DL]:
I (2815) MAIN: ESP-Matter started successfully
WIFI_EVENT_STA_START
W (2821) wifi:Haven't to connect to a suitable AP now!
I (2826) chip[DL]: Attempting to connect WiFi station interface
E (2832) wifi:sta is connecting, return error
E (2836) chip[DL]: esp_wifi_connect() failed: ESP_ERR_WIFI_CONN
I (2842) KOTA: boot fw=1.0.16 build=3ae273ec hw=2 mac=14C19F577248 reset=USB_UART_CHIP_RESET mode=matter commissioned=0 last_restart=none
I (2843) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable)
I (2861) NimBLE: GAP procedure initiated: advertise;
I (2865) NimBLE: disc_mode=2
I (2867) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40
I (2877) NimBLE:
I (2880) chip[DL]: CHIPoBLE advertising started
I (2883) MAIN: Commissioning window opened
I (2888) KOTA: matter fabrics=0 window=open qr=MT:Y.K9042C00KA0648G00 manual=34970112332
I (2896) KOTA: led color=cyan pattern=breathe period=3000ms
I (2901) KOTA: matter fabrics=0 window=open qr=MT:Y.K9042C00KA0648G00 manual=34970112332
W (2909) wifi:Haven't to connect to a suitable AP now!
I (2908) KOTA: health up=1s status=OK wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=0 heap=117491 psram=7.9M last_upload=never ota=registered
W (3401) wifi:Haven't to connect to a suitable AP now!
I (3910) KOTA: rtc running=0 time=2000-01-01T00:00:00
W (4402) wifi:Haven't to connect to a suitable AP now!
W (5403) wifi:Haven't to connect to a suitable AP now!
W (6404) wifi:Haven't to connect to a suitable AP now!
I (7219) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (7222) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (7223) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (7226) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (7233) wifi:Haven't to connect to a suitable AP now!
E (7238) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (7248) wifi:Haven't to connect to a suitable AP now!
I (7252) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (7259) wifi:Haven't to connect to a suitable AP now!
E (7264) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (7273) chip[DL]: Next WiFi station reconnect in 100 ms
I (7278) chip[DL]: Done driving station state, nothing else to do...
W (7379) wifi:Haven't to connect to a suitable AP now!
I (7380) chip[DL]: Attempting to connect WiFi station interface
I (7383) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (7386) wifi:Haven't to connect to a suitable AP now!
E (7391) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (7400) chip[DL]: Done driving station state, nothing else to do...
W (7405) wifi:Haven't to connect to a suitable AP now!
W (8411) wifi:Haven't to connect to a suitable AP now!
W (9415) wifi:Haven't to connect to a suitable AP now!
W (10417) wifi:Haven't to connect to a suitable AP now!
W (11419) wifi:Haven't to connect to a suitable AP now!
I (12201) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (12204) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (12205) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (12208) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (12215) wifi:Haven't to connect to a suitable AP now!
E (12220) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (12230) wifi:Haven't to connect to a suitable AP now!
I (12234) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (12242) wifi:Haven't to connect to a suitable AP now!
E (12247) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (12256) chip[DL]: Next WiFi station reconnect in 100 ms
I (12261) chip[DL]: Done driving station state, nothing else to do...
W (12361) wifi:Haven't to connect to a suitable AP now!
I (12362) chip[DL]: Attempting to connect WiFi station interface
I (12366) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (12368) wifi:Haven't to connect to a suitable AP now!
E (12373) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (12383) chip[DL]: Done driving station state, nothing else to do...
W (12427) wifi:Haven't to connect to a suitable AP now!
W (13428) wifi:Haven't to connect to a suitable AP now!
I (13912) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota
I (13917) INFLUX_BUFFER: Buffer file does not exist, returning count of 0
W (13920) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota
I (13943) BME688: Delaying for 10000 microseconds
W (14430) wifi:Haven't to connect to a suitable AP now!
I (14563) KOTA: mic enabled=1 (GPIO3=0, expected 0)
I (14566) Flicker: Flicker sensor initialized on pin 14
I (14567) temperature_sensor: Range [20°C ~ 100°C], error < 2°C
I (14569) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data
W (14573) wifi:
I (14569) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data
Haven't to connect to a suitable AP now!
I (14570) SEN66: Creating sensor mutex
I (14572) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms
I (14600) Flicker: Starting to collect samples in gather task
I (14585) DeviceStatus: Active tasks: 27
I (14588) SEN66: Waking up sensor by setting power GPIO to enabled level...
I (14570) INFLUX_BUFFER: Buffer file does not exist, returning empty buffer
I (14622) INFLUX_BUFFER: Buffer is empty, nothing to flush
I (14613) DeviceStatus: Task list:
gather_sensor_t R 1 1920 25
gather_sensor_t X 1 1368 23
IDLE1 R 0 700 6
IDLE0 R 0 692 5
gather_sensor_t B 1 2556 22
reset_task B 0 3356 10
ds1307_update B 1 776 18
tiT B 18 2420 12
gather_task B 1 1916 20
gather_task B 1 14288 21
ota_task B 1 7516 19
CHIP B 1 3700 14
health B 1 728 17
Tmr Svc B 1 1364 7
led_task B 5 868 8
nimble_host B 21 1348 16
ipc0 S 24 372 1
ipc1 S 24 524 2
sys_evt B 20 2676 11
gather_sensor_t B 1 3180 26
esp_timer S 22 3044 3
gather_sensor_t B 1 2324 24
main B 1 892 4
gather_sensor_t S 1 1480 27
wifi B 23 3796 13
btController B 23 1864 15
flicker_gather_ S 24 1988 28
usb_console B 1 2840 9
W (14702) DeviceStatus: RTC is uninitialized, using current time as timestamp
W (15433) wifi:Haven't to connect to a suitable AP now!
I (15630) Flicker: Finished collecting samples, notifying main task
I (15632) Flicker: Collected 1024 samples for dominant frequency analysis
I (15639) SENSOR_GATHERER: Sensor should now be asleep
W (15650) AS7341: Reading spectral data from AS7341
I (15659) BME688: Delaying for 199517 microseconds
W (15710) DeviceStatus: RTC is uninitialized, using current time as timestamp
W (15958) AS7341: Reading basic counts data from AS7341
W (15974) AS7341: Reading flicker detection state from AS7341
W (16194) as7341: FD Status Register: 0x20 (0b00100000)
I (16201) AS7341: Updating Matter attributes with AS7341 data: 10474
I (16202) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 **********
I (16208) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 10474 **********
E (16219) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0
E (16229) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0
W (16238) AS7341: RTC is uninitialized, using current time as timestamp
I (16241) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null **********
I (16256) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1021 **********
E (16267) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0
E (16276) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0
W (16286) BME688: RTC is uninitialized, using current time as timestamp
W (16434) wifi:Haven't to connect to a suitable AP now!
W (16711) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (17183) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (17186) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (17187) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (17190) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (17198) wifi:Haven't to connect to a suitable AP now!
E (17203) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (17212) wifi:Haven't to connect to a suitable AP now!
I (17217) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (17224) wifi:Haven't to connect to a suitable AP now!
E (17229) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (17239) chip[DL]: Next WiFi station reconnect in 100 ms
I (17243) chip[DL]: Done driving station state, nothing else to do...
W (17244) AS7341: RTC is uninitialized, using current time as timestamp
W (17291) BME688: RTC is uninitialized, using current time as timestamp
W (17344) wifi:Haven't to connect to a suitable AP now!
I (17346) chip[DL]: Attempting to connect WiFi station interface
I (17349) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (17351) wifi:Haven't to connect to a suitable AP now!
E (17356) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (17366) chip[DL]: Done driving station state, nothing else to do...
W (17437) wifi:Haven't to connect to a suitable AP now!
W (17712) DeviceStatus: RTC is uninitialized, using current time as timestamp
W (18256) AS7341: RTC is uninitialized, using current time as timestamp
W (18292) BME688: RTC is uninitialized, using current time as timestamp
W (18439) wifi:Haven't to connect to a suitable AP now!
W (18713) DeviceStatus: RTC is uninitialized, using current time as timestamp
W (19257) AS7341: RTC is uninitialized, using current time as timestamp
W (19293) BME688: RTC is uninitialized, using current time as timestamp
W (19440) wifi:Haven't to connect to a suitable AP now!
I (19615) SEN66: Sent start measurement command, waiting for sensor to be ready...
W (19714) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (19928) KOTA: cmd ping ok=1 fw=1.0.16 api=1
I (19933) KOTA: cmd wifi_get ok=1 ssid=- password_set=0 mode=matter
W (20258) AS7341: RTC is uninitialized, using current time as timestamp
W (20294) BME688: RTC is uninitialized, using current time as timestamp
W (20441) wifi:Haven't to connect to a suitable AP now!
W (20715) DeviceStatus: RTC is uninitialized, using current time as timestamp
W (21259) AS7341: RTC is uninitialized, using current time as timestamp
W (21295) BME688: RTC is uninitialized, using current time as timestamp
W (21442) wifi:Haven't to connect to a suitable AP now!
W (21716) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (22166) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (22169) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (22170) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (22173) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (22181) wifi:Haven't to connect to a suitable AP now!
E (22186) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (22196) wifi:Haven't to connect to a suitable AP now!
I (22200) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (22207) wifi:Haven't to connect to a suitable AP now!
E (22212) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (22222) chip[DL]: Next WiFi station reconnect in 100 ms
I (22226) chip[DL]: Done driving station state, nothing else to do...
W (22260) AS7341: RTC is uninitialized, using current time as timestamp
W (22296) BME688: RTC is uninitialized, using current time as timestamp
W (22327) wifi:Haven't to connect to a suitable AP now!
I (22328) chip[DL]: Attempting to connect WiFi station interface
I (22332) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (22334) wifi:Haven't to connect to a suitable AP now!
E (22340) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (22349) chip[DL]: Done driving station state, nothing else to do...
W (22446) wifi:Haven't to connect to a suitable AP now!
I (22574) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (22717) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (23075) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (23261) AS7341: RTC is uninitialized, using current time as timestamp
W (23297) BME688: RTC is uninitialized, using current time as timestamp
W (23447) wifi:Haven't to connect to a suitable AP now!
I (23576) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (23718) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (24077) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (24262) AS7341: RTC is uninitialized, using current time as timestamp
W (24299) BME688: RTC is uninitialized, using current time as timestamp
W (24448) wifi:Haven't to connect to a suitable AP now!
I (24570) OTA_CLIENT: HTTP client handle: 0x3fcf02e4
I (24578) SENSOR_GATHERER: 2/2 tasks still running, waiting...
I (24710) MICROPHONE: Read 600000 bytes in 9940 ms
I (24712) MICROPHONE: Read 600000 bytes of audio data
W (24719) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (25079) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (25264) AS7341: RTC is uninitialized, using current time as timestamp
W (25300) BME688: RTC is uninitialized, using current time as timestamp
W (25449) wifi:Haven't to connect to a suitable AP now!
I (25580) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (25720) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (26081) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (26265) AS7341: RTC is uninitialized, using current time as timestamp
W (26301) BME688: RTC is uninitialized, using current time as timestamp
W (26450) wifi:Haven't to connect to a suitable AP now!
I (26582) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (26721) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (27083) SENSOR_GATHERER: 2/2 tasks still running, waiting...
I (27150) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (27152) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (27154) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (27157) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (27164) wifi:Haven't to connect to a suitable AP now!
E (27169) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (27179) wifi:Haven't to connect to a suitable AP now!
I (27183) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (27190) wifi:Haven't to connect to a suitable AP now!
E (27196) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (27205) chip[DL]: Next WiFi station reconnect in 100 ms
I (27209) chip[DL]: Done driving station state, nothing else to do...
W (27266) AS7341: RTC is uninitialized, using current time as timestamp
W (27302) BME688: RTC is uninitialized, using current time as timestamp
W (27310) wifi:Haven't to connect to a suitable AP now!
I (27311) chip[DL]: Attempting to connect WiFi station interface
I (27315) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (27318) wifi:Haven't to connect to a suitable AP now!
E (27323) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (27332) chip[DL]: Done driving station state, nothing else to do...
W (27456) wifi:Haven't to connect to a suitable AP now!
I (27584) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (27722) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (28085) SENSOR_GATHERER: 2/2 tasks still running, waiting...
I (28160) MICROPHONE: Raw block stats: mean=-17.8 min=-146018 max=134018
I (28161) MICROPHONE: Sum SQ: 0.578925, RMS pressure: 0.001701, Normalized: 0.001701, dB peak: -55.38
W (28166) SEN66: RTC is uninitialized, using current time as timestamp
W (28267) AS7341: RTC is uninitialized, using current time as timestamp
W (28303) BME688: RTC is uninitialized, using current time as timestamp
W (28457) wifi:Haven't to connect to a suitable AP now!
I (28586) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (28723) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (29087) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (29172) SEN66: RTC is uninitialized, using current time as timestamp
W (29268) AS7341: RTC is uninitialized, using current time as timestamp
W (29304) BME688: RTC is uninitialized, using current time as timestamp
W (29458) wifi:Haven't to connect to a suitable AP now!
I (29588) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (29724) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (30089) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (30173) SEN66: RTC is uninitialized, using current time as timestamp
W (30269) AS7341: RTC is uninitialized, using current time as timestamp
W (30305) BME688: RTC is uninitialized, using current time as timestamp
W (30459) wifi:Haven't to connect to a suitable AP now!
I (30590) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (30725) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (31091) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (31174) SEN66: RTC is uninitialized, using current time as timestamp
W (31270) AS7341: RTC is uninitialized, using current time as timestamp
W (31306) BME688: RTC is uninitialized, using current time as timestamp
W (31460) wifi:Haven't to connect to a suitable AP now!
E (31572) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0
E (31573) esp-tls: Failed to open new connection
E (31575) transport_base: Failed to open a new connection
E (31580) HTTP_CLIENT: Connection failed, sock < 0
E (31585) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:355
I (31592) SENSOR_GATHERER: 2/2 tasks still running, waiting...
E (31593) OTA_CLIENT: [E-OTA-13] Failed to check for updates: ESP_ERR_HTTP_CONNECT
I (31606) KOTA: status OK -> CONNECTION_ERROR reason=ota_check_failed
W (31726) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (32098) SENSOR_GATHERER: 2/2 tasks still running, waiting...
I (32133) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (32135) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (32137) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (32140) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (32147) wifi:Haven't to connect to a suitable AP now!
E (32152) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (32162) wifi:Haven't to connect to a suitable AP now!
I (32166) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (32173) wifi:Haven't to connect to a suitable AP now!
W (32175) SEN66: RTC is uninitialized, using current time as timestamp
E (32179) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (32194) chip[DL]: Next WiFi station reconnect in 100 ms
I (32199) chip[DL]: Done driving station state, nothing else to do...
W (32271) AS7341: RTC is uninitialized, using current time as timestamp
W (32300) wifi:Haven't to connect to a suitable AP now!
I (32301) chip[DL]: Attempting to connect WiFi station interface
I (32305) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (32307) BME688: RTC is uninitialized, using current time as timestamp
W (32307) wifi:Haven't to connect to a suitable AP now!
E (32319) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (32328) chip[DL]: Done driving station state, nothing else to do...
W (32462) wifi:Haven't to connect to a suitable AP now!
I (32599) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (32727) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (32730) chip[DL]: bleAdv Timeout : Start slow advertisement
I (32732) chip[DL]: Configuring CHIPoBLE advertising (interval 500 ms, connectable)
I (32737) chip[DL]: Device already advertising, stop active advertisement and restart
I (32744) NimBLE: GAP procedure initiated: stop advertising.
I (32750) NimBLE: GAP procedure initiated: advertise;
I (32754) NimBLE: disc_mode=2
I (32756) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=800 adv_itvl_max=800
I (32767) NimBLE:
I (33100) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (33184) SEN66: RTC is uninitialized, using current time as timestamp
W (33272) AS7341: RTC is uninitialized, using current time as timestamp
W (33320) BME688: RTC is uninitialized, using current time as timestamp
W (33463) wifi:Haven't to connect to a suitable AP now!
I (33601) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (33728) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (34102) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (34185) SEN66: RTC is uninitialized, using current time as timestamp
W (34274) AS7341: RTC is uninitialized, using current time as timestamp
W (34321) BME688: RTC is uninitialized, using current time as timestamp
W (34464) wifi:Haven't to connect to a suitable AP now!
I (34603) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (34729) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (35104) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (35186) SEN66: RTC is uninitialized, using current time as timestamp
W (35275) AS7341: RTC is uninitialized, using current time as timestamp
W (35322) BME688: RTC is uninitialized, using current time as timestamp
W (35465) wifi:Haven't to connect to a suitable AP now!
I (35605) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (35730) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (36106) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (36187) SEN66: RTC is uninitialized, using current time as timestamp
W (36276) AS7341: RTC is uninitialized, using current time as timestamp
W (36323) BME688: RTC is uninitialized, using current time as timestamp
W (36466) wifi:Haven't to connect to a suitable AP now!
I (36607) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (36731) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (37107) SENSOR_GATHERER: 2/2 tasks still running, waiting...
I (37122) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (37125) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (37126) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (37129) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (37137) wifi:Haven't to connect to a suitable AP now!
E (37142) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (37151) wifi:Haven't to connect to a suitable AP now!
I (37156) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (37163) wifi:Haven't to connect to a suitable AP now!
E (37168) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (37178) chip[DL]: Next WiFi station reconnect in 100 ms
I (37182) chip[DL]: Done driving station state, nothing else to do...
W (37188) SEN66: RTC is uninitialized, using current time as timestamp
W (37277) AS7341: RTC is uninitialized, using current time as timestamp
W (37283) wifi:Haven't to connect to a suitable AP now!
I (37284) chip[DL]: Attempting to connect WiFi station interface
I (37287) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (37290) wifi:Haven't to connect to a suitable AP now!
E (37295) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (37305) chip[DL]: Done driving station state, nothing else to do...
W (37324) BME688: RTC is uninitialized, using current time as timestamp
W (37468) wifi:Haven't to connect to a suitable AP now!
I (37608) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (37732) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (38109) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (38195) SEN66: RTC is uninitialized, using current time as timestamp
W (38278) AS7341: RTC is uninitialized, using current time as timestamp
W (38325) BME688: RTC is uninitialized, using current time as timestamp
W (38470) wifi:Haven't to connect to a suitable AP now!
I (38610) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (38733) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (39111) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (39196) SEN66: RTC is uninitialized, using current time as timestamp
W (39279) AS7341: RTC is uninitialized, using current time as timestamp
W (39326) BME688: RTC is uninitialized, using current time as timestamp
W (39471) wifi:Haven't to connect to a suitable AP now!
I (39612) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (39736) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (40113) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (40197) SEN66: RTC is uninitialized, using current time as timestamp
W (40280) AS7341: RTC is uninitialized, using current time as timestamp
W (40326) BME688: RTC is uninitialized, using current time as timestamp
W (40473) wifi:Haven't to connect to a suitable AP now!
I (40614) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (40737) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (41115) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (41198) SEN66: RTC is uninitialized, using current time as timestamp
W (41281) AS7341: RTC is uninitialized, using current time as timestamp
W (41327) BME688: RTC is uninitialized, using current time as timestamp
W (41474) wifi:Haven't to connect to a suitable AP now!
I (41616) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (41738) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (42105) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (42108) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (42109) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (42112) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
I (42116) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (42120) wifi:Haven't to connect to a suitable AP now!
E (42130) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (42140) wifi:Haven't to connect to a suitable AP now!
I (42144) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (42151) wifi:Haven't to connect to a suitable AP now!
E (42156) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (42166) chip[DL]: Next WiFi station reconnect in 100 ms
I (42170) chip[DL]: Done driving station state, nothing else to do...
W (42199) SEN66: RTC is uninitialized, using current time as timestamp
W (42271) wifi:Haven't to connect to a suitable AP now!
I (42272) chip[DL]: Attempting to connect WiFi station interface
I (42275) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (42278) wifi:Haven't to connect to a suitable AP now!
W (42283) AS7341: RTC is uninitialized, using current time as timestamp
E (42283) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (42299) chip[DL]: Done driving station state, nothing else to do...
W (42328) BME688: RTC is uninitialized, using current time as timestamp
W (42476) wifi:Haven't to connect to a suitable AP now!
I (42625) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (42739) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (43126) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (43200) SEN66: RTC is uninitialized, using current time as timestamp
W (43290) AS7341: RTC is uninitialized, using current time as timestamp
W (43329) BME688: RTC is uninitialized, using current time as timestamp
W (43482) wifi:Haven't to connect to a suitable AP now!
I (43627) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (43740) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (44128) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (44201) SEN66: RTC is uninitialized, using current time as timestamp
W (44291) AS7341: RTC is uninitialized, using current time as timestamp
W (44330) BME688: RTC is uninitialized, using current time as timestamp
W (44486) wifi:Haven't to connect to a suitable AP now!
I (44629) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (44741) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (45130) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (45202) SEN66: RTC is uninitialized, using current time as timestamp
W (45292) AS7341: RTC is uninitialized, using current time as timestamp
W (45331) BME688: RTC is uninitialized, using current time as timestamp
W (45487) wifi:Haven't to connect to a suitable AP now!
I (45631) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (45742) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (46132) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (46203) SEN66: RTC is uninitialized, using current time as timestamp
W (46293) AS7341: RTC is uninitialized, using current time as timestamp
W (46332) BME688: RTC is uninitialized, using current time as timestamp
W (46488) wifi:Haven't to connect to a suitable AP now!
I (46633) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (46743) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (47093) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (47095) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (47097) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (47100) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (47107) wifi:Haven't to connect to a suitable AP now!
E (47112) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (47122) wifi:Haven't to connect to a suitable AP now!
I (47126) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
I (47134) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (47134) wifi:Haven't to connect to a suitable AP now!
E (47144) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (47154) chip[DL]: Next WiFi station reconnect in 100 ms
I (47158) chip[DL]: Done driving station state, nothing else to do...
W (47204) SEN66: RTC is uninitialized, using current time as timestamp
W (47259) wifi:Haven't to connect to a suitable AP now!
I (47260) chip[DL]: Attempting to connect WiFi station interface
I (47263) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (47266) wifi:Haven't to connect to a suitable AP now!
E (47271) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (47281) chip[DL]: Done driving station state, nothing else to do...
W (47294) AS7341: RTC is uninitialized, using current time as timestamp
W (47333) BME688: RTC is uninitialized, using current time as timestamp
W (47493) wifi:Haven't to connect to a suitable AP now!
I (47639) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (47744) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (48140) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (48205) SEN66: RTC is uninitialized, using current time as timestamp
W (48295) AS7341: RTC is uninitialized, using current time as timestamp
W (48334) BME688: RTC is uninitialized, using current time as timestamp
W (48495) wifi:Haven't to connect to a suitable AP now!
I (48641) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (48747) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (49142) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (49206) SEN66: RTC is uninitialized, using current time as timestamp
W (49296) AS7341: RTC is uninitialized, using current time as timestamp
W (49335) BME688: RTC is uninitialized, using current time as timestamp
W (49496) wifi:Haven't to connect to a suitable AP now!
I (49643) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (49748) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (50144) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (50207) SEN66: RTC is uninitialized, using current time as timestamp
W (50297) AS7341: RTC is uninitialized, using current time as timestamp
W (50336) BME688: RTC is uninitialized, using current time as timestamp
W (50498) wifi:Haven't to connect to a suitable AP now!
I (50645) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (50749) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (51146) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (51208) SEN66: RTC is uninitialized, using current time as timestamp
W (51298) AS7341: RTC is uninitialized, using current time as timestamp
W (51337) BME688: RTC is uninitialized, using current time as timestamp
W (51500) wifi:Haven't to connect to a suitable AP now!
I (51647) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (51750) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (52081) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (52084) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (52085) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (52088) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (52096) wifi:Haven't to connect to a suitable AP now!
E (52100) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (52110) wifi:Haven't to connect to a suitable AP now!
I (52115) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (52122) wifi:Haven't to connect to a suitable AP now!
E (52127) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (52136) chip[DL]: Next WiFi station reconnect in 100 ms
I (52141) chip[DL]: Done driving station state, nothing else to do...
I (52148) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (52209) SEN66: RTC is uninitialized, using current time as timestamp
W (52242) wifi:Haven't to connect to a suitable AP now!
I (52243) chip[DL]: Attempting to connect WiFi station interface
I (52246) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (52249) wifi:Haven't to connect to a suitable AP now!
E (52254) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (52264) chip[DL]: Done driving station state, nothing else to do...
W (52299) AS7341: RTC is uninitialized, using current time as timestamp
W (52338) BME688: RTC is uninitialized, using current time as timestamp
W (52508) wifi:Haven't to connect to a suitable AP now!
I (52653) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (52751) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (53154) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (53210) SEN66: RTC is uninitialized, using current time as timestamp
W (53300) AS7341: RTC is uninitialized, using current time as timestamp
W (53339) BME688: RTC is uninitialized, using current time as timestamp
W (53509) wifi:Haven't to connect to a suitable AP now!
I (53655) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (53752) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (54156) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (54212) SEN66: RTC is uninitialized, using current time as timestamp
W (54301) AS7341: RTC is uninitialized, using current time as timestamp
W (54340) BME688: RTC is uninitialized, using current time as timestamp
W (54510) wifi:Haven't to connect to a suitable AP now!
I (54657) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (54753) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (55158) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (55213) SEN66: RTC is uninitialized, using current time as timestamp
W (55302) AS7341: RTC is uninitialized, using current time as timestamp
W (55341) BME688: RTC is uninitialized, using current time as timestamp
W (55511) wifi:Haven't to connect to a suitable AP now!
I (55659) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (55754) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (56160) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (56214) SEN66: RTC is uninitialized, using current time as timestamp
W (56303) AS7341: RTC is uninitialized, using current time as timestamp
W (56342) BME688: RTC is uninitialized, using current time as timestamp
W (56512) wifi:Haven't to connect to a suitable AP now!
I (56661) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (56755) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (57064) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (57067) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (57068) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (57071) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (57079) wifi:Haven't to connect to a suitable AP now!
E (57083) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (57093) wifi:Haven't to connect to a suitable AP now!
I (57097) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (57105) wifi:Haven't to connect to a suitable AP now!
E (57110) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (57119) chip[DL]: Next WiFi station reconnect in 100 ms
I (57124) chip[DL]: Done driving station state, nothing else to do...
I (57162) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (57215) SEN66: RTC is uninitialized, using current time as timestamp
W (57225) wifi:Haven't to connect to a suitable AP now!
I (57226) chip[DL]: Attempting to connect WiFi station interface
I (57230) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (57232) wifi:Haven't to connect to a suitable AP now!
E (57237) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (57247) chip[DL]: Done driving station state, nothing else to do...
W (57304) AS7341: RTC is uninitialized, using current time as timestamp
W (57344) BME688: RTC is uninitialized, using current time as timestamp
W (57517) wifi:Haven't to connect to a suitable AP now!
I (57663) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (57756) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (58164) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (58216) SEN66: RTC is uninitialized, using current time as timestamp
W (58305) AS7341: RTC is uninitialized, using current time as timestamp
W (58345) BME688: RTC is uninitialized, using current time as timestamp
W (58518) wifi:Haven't to connect to a suitable AP now!
I (58665) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (58757) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (59166) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (59217) SEN66: RTC is uninitialized, using current time as timestamp
W (59306) AS7341: RTC is uninitialized, using current time as timestamp
W (59346) BME688: RTC is uninitialized, using current time as timestamp
W (59519) wifi:Haven't to connect to a suitable AP now!
I (59667) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (59758) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (60168) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (60218) SEN66: RTC is uninitialized, using current time as timestamp
W (60307) AS7341: RTC is uninitialized, using current time as timestamp
W (60346) BME688: RTC is uninitialized, using current time as timestamp
W (60520) wifi:Haven't to connect to a suitable AP now!
I (60669) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (60759) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (61170) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (61219) SEN66: RTC is uninitialized, using current time as timestamp
W (61308) AS7341: RTC is uninitialized, using current time as timestamp
W (61347) BME688: RTC is uninitialized, using current time as timestamp
W (61521) wifi:Haven't to connect to a suitable AP now!
I (61671) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (61760) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (62047) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (62050) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (62051) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (62054) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (62062) wifi:Haven't to connect to a suitable AP now!
E (62067) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (62076) wifi:Haven't to connect to a suitable AP now!
I (62081) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (62088) wifi:Haven't to connect to a suitable AP now!
E (62093) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (62102) chip[DL]: Next WiFi station reconnect in 100 ms
I (62107) chip[DL]: Done driving station state, nothing else to do...
I (62172) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (62208) wifi:Haven't to connect to a suitable AP now!
I (62209) chip[DL]: Attempting to connect WiFi station interface
I (62212) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (62215) wifi:Haven't to connect to a suitable AP now!
W (62220) SEN66: RTC is uninitialized, using current time as timestamp
E (62220) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (62236) chip[DL]: Done driving station state, nothing else to do...
W (62309) AS7341: RTC is uninitialized, using current time as timestamp
W (62348) BME688: RTC is uninitialized, using current time as timestamp
W (62527) wifi:Haven't to connect to a suitable AP now!
I (62673) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (62761) DeviceStatus: RTC is uninitialized, using current time as timestamp
W (62925) wifi:Haven't to connect to a suitable AP now!
I (62925) KOTA: health up=61s status=CONNECTION_ERROR wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=0 heap=47259 psram=7.3M last_upload=never ota=registered
I (63174) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (63227) SEN66: RTC is uninitialized, using current time as timestamp
W (63310) AS7341: RTC is uninitialized, using current time as timestamp
W (63349) BME688: RTC is uninitialized, using current time as timestamp
W (63528) wifi:Haven't to connect to a suitable AP now!
I (63675) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (63762) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (64176) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (64228) SEN66: RTC is uninitialized, using current time as timestamp
W (64311) AS7341: RTC is uninitialized, using current time as timestamp
W (64350) BME688: RTC is uninitialized, using current time as timestamp
W (64529) wifi:Haven't to connect to a suitable AP now!
I (64617) SEN66: First measurement, waiting additional 120 seconds for sensor to stabilize...
I (64677) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (64763) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (65178) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (65229) SEN66: RTC is uninitialized, using current time as timestamp
W (65312) AS7341: RTC is uninitialized, using current time as timestamp
W (65351) BME688: RTC is uninitialized, using current time as timestamp
W (65530) wifi:Haven't to connect to a suitable AP now!
I (65679) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (65764) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (66180) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (66230) SEN66: RTC is uninitialized, using current time as timestamp
W (66313) AS7341: RTC is uninitialized, using current time as timestamp
W (66352) BME688: RTC is uninitialized, using current time as timestamp
W (66531) wifi:Haven't to connect to a suitable AP now!
I (66681) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (66765) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (67030) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (67033) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (67034) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (67037) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (67045) wifi:Haven't to connect to a suitable AP now!
E (67049) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (67059) wifi:Haven't to connect to a suitable AP now!
I (67063) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (67071) wifi:Haven't to connect to a suitable AP now!
E (67076) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (67085) chip[DL]: Next WiFi station reconnect in 100 ms
I (67090) chip[DL]: Done driving station state, nothing else to do...
I (67182) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (67191) wifi:Haven't to connect to a suitable AP now!
I (67192) chip[DL]: Attempting to connect WiFi station interface
I (67195) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (67198) wifi:Haven't to connect to a suitable AP now!
E (67203) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (67213) chip[DL]: Done driving station state, nothing else to do...
W (67231) SEN66: RTC is uninitialized, using current time as timestamp
W (67314) AS7341: RTC is uninitialized, using current time as timestamp
W (67353) BME688: RTC is uninitialized, using current time as timestamp
W (67537) wifi:Haven't to connect to a suitable AP now!
I (67683) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (67766) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (68184) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (68232) SEN66: RTC is uninitialized, using current time as timestamp
W (68315) AS7341: RTC is uninitialized, using current time as timestamp
W (68354) BME688: RTC is uninitialized, using current time as timestamp
W (68542) wifi:Haven't to connect to a suitable AP now!
I (68685) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (68767) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (69186) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (69233) SEN66: RTC is uninitialized, using current time as timestamp
W (69316) AS7341: RTC is uninitialized, using current time as timestamp
W (69355) BME688: RTC is uninitialized, using current time as timestamp
W (69546) wifi:Haven't to connect to a suitable AP now!
I (69687) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (69768) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (70188) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (70234) SEN66: RTC is uninitialized, using current time as timestamp
W (70317) AS7341: RTC is uninitialized, using current time as timestamp
W (70356) BME688: RTC is uninitialized, using current time as timestamp
W (70547) wifi:Haven't to connect to a suitable AP now!
I (70689) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (70769) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (71190) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (71235) SEN66: RTC is uninitialized, using current time as timestamp
W (71318) AS7341: RTC is uninitialized, using current time as timestamp
W (71357) BME688: RTC is uninitialized, using current time as timestamp
W (71548) wifi:Haven't to connect to a suitable AP now!
I (71691) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (71770) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (72013) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy
I (72015) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5
I (72017) chip[DL]: WIFI_EVENT_STA_DISCONNECTED
I (72020) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed
W (72027) wifi:Haven't to connect to a suitable AP now!
E (72032) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
W (72042) wifi:Haven't to connect to a suitable AP now!
I (72046) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected
W (72054) wifi:Haven't to connect to a suitable AP now!
E (72059) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (72068) chip[DL]: Next WiFi station reconnect in 100 ms
I (72073) chip[DL]: Done driving station state, nothing else to do...
W (72173) wifi:Haven't to connect to a suitable AP now!
I (72175) chip[DL]: Attempting to connect WiFi station interface
I (72178) chip[DL]: WiFi station state change: NotConnected -> Connecting
W (72181) wifi:Haven't to connect to a suitable AP now!
E (72185) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F
I (72191) SENSOR_GATHERER: 2/2 tasks still running, waiting...
I (72195) chip[DL]: Done driving station state, nothing else to do...
W (72236) SEN66: RTC is uninitialized, using current time as timestamp
W (72319) AS7341: RTC is uninitialized, using current time as timestamp
W (72358) BME688: RTC is uninitialized, using current time as timestamp
W (72552) wifi:Haven't to connect to a suitable AP now!
I (72700) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (72771) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (73201) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (73237) SEN66: RTC is uninitialized, using current time as timestamp
W (73320) AS7341: RTC is uninitialized, using current time as timestamp
W (73359) BME688: RTC is uninitialized, using current time as timestamp
W (73553) wifi:Haven't to connect to a suitable AP now!
I (73702) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (73772) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (74203) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (74238) SEN66: RTC is uninitialized, using current time as timestamp
W (74321) AS7341: RTC is uninitialized, using current time as timestamp
W (74360) BME688: RTC is uninitialized, using current time as timestamp
W (74555) wifi:Haven't to connect to a suitable AP now!
I (74581) SENSOR_GATHERER: 3/4 tasks still running, waiting...
I (74627) INFLUX_BUFFER: Buffer file does not exist, returning empty buffer
I (74628) INFLUX_BUFFER: Buffer is empty, nothing to flush
I (74704) SENSOR_GATHERER: 2/2 tasks still running, waiting...
W (74773) DeviceStatus: RTC is uninitialized, using current time as timestamp
--- disconnected ---
+177
View File
@@ -0,0 +1,177 @@
"""Wi-Fi over USB (firmware usb_console api=1): encoding, replies and the whole
write/clear/read job, driven with reply lines in the firmware's exact format."""
import pytest
from kota_tech.core.logline import parse_line
from kota_tech.diag.engine import DiagnosticsEngine
from kota_tech.serialio.commands import (
CONNECT_TIMEOUT_S, PING_TIMEOUT_S, Step, WifiFlow, loggable, parse_reply, validate_wifi, wifi_set_command)
PING = "I (5000) KOTA: cmd ping ok=1 fw=1.0.16 api=1"
BOOT = ("I (2843) KOTA: boot fw=1.0.16 build=3ae273ec hw=2 mac=A4CB8FC946F8 reset=RTC_SW_SYS_RST "
"mode={mode} commissioned=- last_restart={last}")
HEALTH = ("I (9000) KOTA: cmd health ok=1 up=9s status=OK wifi={wifi} rssi={rssi} time_valid=1 sd=ok buffer=0 "
"heap=90000 psram=7.6M last_upload=never ota=registered")
class Clock:
def __init__(self):
self.t = 0.0
def __call__(self):
return self.t
def make(action, **kw):
sent, events, clock = [], [], Clock()
flow = WifiFlow(action, lambda c: sent.append(c) or True, events.append, now=clock, **kw)
return flow, sent, events, clock
def feed(flow, text):
flow.feed(parse_line(text))
# ------------------------------------------------------------- encoding
def test_wifi_set_command_is_hex_utf8():
assert wifi_set_command("Biuro 2.4", "tajne=haslo1") == \
"kota wifi set 426975726f20322e34 74616a6e653d6861736c6f31"
assert wifi_set_command("Łąka", "zażółć1234").split()[3] == "c581c4856b61"
@pytest.mark.parametrize("ssid,password,problem", [
("", "12345678", "network name"),
("x" * 33, "12345678", "32 bytes"),
("net", "1234567", "8-63"),
("net", "x" * 64, "8-63"),
("net\n", "12345678", "line break"),
])
def test_validation_matches_firmware_limits(ssid, password, problem):
assert any(problem in p for p in validate_wifi(ssid, password))
with pytest.raises(ValueError):
wifi_set_command(ssid, password)
def test_password_never_logged():
cmd = wifi_set_command("Biuro", "sekretne-haslo")
assert "sekretne" not in loggable(cmd) and "7365" not in loggable(cmd)
assert "Biuro" in loggable(cmd)
def test_reply_parsing():
r = parse_reply(parse_line("I (1) KOTA: cmd wifi_set ok=0 err=password_length"))
assert r.name == "wifi_set" and not r.ok and "8-63" in r.error
assert parse_reply(parse_line("I (1) KOTA: health up=1s")) is None
# ----------------------------------------------------------------- flows
def test_write_wifi_happy_path():
flow, sent, events, clock = make("set", ssid="Biuro", password="haslo1234")
flow.start()
assert sent == ["kota ping"]
feed(flow, PING)
assert sent[-1] == wifi_set_command("Biuro", "haslo1234") and flow.step == Step.SEND
feed(flow, "I (5100) KOTA: cmd wifi_set ok=1 restart=1")
assert flow.step == Step.RESTART
feed(flow, "W (5101) KOTA: restart reason=usb_wifi_set")
feed(flow, BOOT.format(mode="wifi", last="usb_wifi_set"))
assert flow.step == Step.CONNECT
clock.t += 6
flow.tick()
assert sent[-1] == "kota health"
feed(flow, HEALTH.format(wifi="disconnected", rssi=0))
assert flow.step == Step.CONNECT
feed(flow, HEALTH.format(wifi="connected", rssi=-58))
assert flow.step == Step.DONE and "Connected to 'Biuro'" in events[-1].text and "-58" in events[-1].text
def test_old_firmware_without_console_times_out_on_ping():
flow, sent, events, clock = make("read")
flow.start()
clock.t += PING_TIMEOUT_S + 0.1
flow.tick()
assert flow.step == Step.FAILED and "USB console" in events[-1].text
def test_box_rejects_settings():
flow, sent, events, _ = make("set", ssid="Biuro", password="haslo1234")
flow.start()
feed(flow, PING)
feed(flow, "I (5100) KOTA: cmd wifi_set ok=0 err=sd_not_mounted")
assert flow.step == Step.FAILED and "SD card" in events[-1].text
def test_wrong_password_does_not_connect():
flow, _, events, clock = make("set", ssid="Biuro", password="zlehaslo1")
flow.start()
feed(flow, PING)
feed(flow, "I (5100) KOTA: cmd wifi_set ok=1 restart=1")
feed(flow, BOOT.format(mode="wifi", last="usb_wifi_set"))
clock.t += CONNECT_TIMEOUT_S + 1
flow.tick()
assert flow.step == Step.FAILED and "2.4 GHz" in events[-1].text
def test_clear_returns_to_matter():
flow, sent, events, _ = make("clear")
flow.start()
feed(flow, PING)
assert sent[-1] == "kota wifi clear"
feed(flow, "I (5100) KOTA: cmd wifi_clear ok=1 restart=1")
feed(flow, BOOT.format(mode="matter", last="usb_wifi_clear"))
assert flow.step == Step.DONE and "Matter" in events[-1].text
def test_read_current_settings():
flow, sent, events, _ = make("read")
flow.start()
feed(flow, PING)
assert sent[-1] == "kota wifi get"
feed(flow, "I (5100) KOTA: cmd wifi_get ok=1 ssid=426975726f password_set=1 mode=wifi")
assert flow.step == Step.DONE and flow.info.ssid == "Biuro" and flow.info.password_set
assert "local Wi-Fi" in events[-1].text
def test_not_connected():
events = []
flow = WifiFlow("read", lambda c: False, events.append)
flow.start()
assert flow.step == Step.FAILED and "not connected" in events[-1].text
# ---------------------------------------------------------------- engine
def test_engine_treats_usb_wifi_restart_as_intentional_and_reads_replies():
e = DiagnosticsEngine()
for t in ["ESP-ROM:esp32s3-20210327", "I (1790) KOTA: i2c SDA=1 SCL=1 0x39=AS7341",
BOOT.format(mode="matter", last="none"), PING,
"I (5100) KOTA: cmd wifi_set ok=1 restart=1", "W (5101) KOTA: restart reason=usb_wifi_set",
"ESP-ROM:esp32s3-20210327", "rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT)",
BOOT.format(mode="wifi", last="usb_wifi_set"),
"I (6000) KOTA: cmd wifi_get ok=1 ssid=426975726f password_set=1 mode=wifi",
HEALTH.format(wifi="connected", rssi=-60)]:
e.feed(parse_line(t))
r = e.report()
assert not any(f.rule_id.startswith("reboot.") for rc in r.roots for f in [rc.finding, *rc.consequences])
assert e.console_api == 1
box = dict((t, x) for t, x, _ in r.box)
assert "network 'Biuro'" in box["Wi-Fi config"] and "Wi-Fi connected (-60 dBm)" in box["Health"]
assert any(m == "wifi_connected" and t is not None for m, _, t in r.milestones)
def test_live_v13_box_answers_usb_commands():
# Real capture (2026-09-29): box 14:C1:9F:57:72:48 after flashing merged-binary-v13.bin App-only;
# the Wi-Fi tab's read flow sent "kota ping" and "kota wifi get".
from pathlib import Path
from kota_tech.core.logfile import read_log_file
e = DiagnosticsEngine()
for ln in read_log_file(Path(__file__).parent / "fixtures" / "live_boot_14C19F577248_v13_usb_console.txt"):
e.feed(ln)
r = e.report()
assert e.firmware.elf_sha.startswith("3ae273ec") and e.console_api == 1
assert dict((t, x) for t, x, _ in r.box)["Wi-Fi config"] == "no network on the SD card, mode matter"
assert not any(f.rule_id.startswith("other.") for f in e.findings.values()) # logs intact via the driver
+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)
+19
View File
@@ -0,0 +1,19 @@
"""The whole window builds (catches errors that only show when the app starts)."""
import os
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
def test_main_window_builds_with_all_tabs():
from PySide6.QtWidgets import QApplication
from kota_tech.ui.main_window import MainWindow
app = QApplication.instance() or QApplication([])
w = MainWindow()
tabs = w.centralWidget()
assert [tabs.tabText(i) for i in range(tabs.count())] == \
["Quick start", "Serial monitor", "Flash firmware", "Wi-Fi (USB)", "SD card"]
assert not w.wifi_tab.write_btn.isEnabled() # nothing connected, nothing typed
w.serial_tab.shutdown()
app.processEvents()
+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" },