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
*.sh text eol=lf
*.bat text eol=crlf
*.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
004-technician_tool/v0.5 (a Python/PySide6 desktop app; it has only been tested on Windows so far).
You are testing the KOTA sensorbox technician tool v0.8 on macOS. The current folder is
004-technician_tool/v0.8 (a Python/PySide6 desktop app; tested on Windows 11, macOS paths are covered by
unit tests but not yet run on a real Mac).
Rules:
- Python only via uv or conda/mamba. Never use bare/system python or pip.
- Do not build, modify or flash firmware other than the bundled firmware/new_system.bin.
- Do not build or modify firmware. Flash only files from the tool's firmware/ folder.
- Do not push anything to Git or GitLab. This is a local demo.
- Ask me before any destructive action: formatting an SD card, Full-mode flashing, or deleting files.
Before formatting, show me the exact disk (identifier, name, size, current files).
@@ -11,20 +12,24 @@ Rules:
Tests, in order. Report pass/fail with evidence for each:
1. Launch: `bash run_macos.command` (it offers to install uv). The app window opens; note any warnings.
2. Tests: `UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv-0.5" uv run --frozen pytest -q`
Also check whether a double-click in Finder works (Google Drive may drop the executable bit).
2. Tests: `UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv-0.8" uv run --frozen pytest -q`
(expect 172 passed).
3. Serial: plug in the sensorbox (native USB). The port list shows a /dev/cu.usbmodem* "Sensorbox (native USB)"
entry with a MAC. Connect, press "Reset & capture": the boot log starts at "ESP-ROM:esp32s3" and the
Diagnosis panel identifies firmware v1.0 (build 45fa9d5b9). Check whether the port disappears after the
reset and whether the tool reconnects.
4. Guided troubleshooting: run "Quick check" and answer truthfully with what I tell you the LED shows.
5. Flash (App only, factory image; the box already runs it): the tool must enter flashing mode by itself,
write, restart the box and report "Success ... build 45fa9d5b9". Capture the esptool output.
6. SD card tab: the settings load from ../../002-SD_card_format/ENV.txt. The card reader and card are listed
(internal disks must NOT be listed). Run "Check card", then, after my OK, "Format card and write ENV.TXT".
It must use `diskutil eraseDisk FAT32 KOTA MBRFormat` on the correct disk, write ENV.TXT byte-identical
to the template, and eject the card.
7. "Service report..." opens the browser at https://service.kota-app.com/<MAC>#kota-report=... (it is fine
if the page itself cannot prefill yet).
entry with a MAC (no /dev/tty.* duplicate). Connect, press "Reset & capture": the log starts at
"ESP-ROM:esp32s3" and the Diagnosis panel shows firmware v1.0.16 (build 4f10989e), the "Box self-report"
section and the LED with its pattern. Check whether the port disappears after the reset and whether the
tool reconnects. Wait 2 minutes: a "KOTA: health" line appears every 60 s.
4. Show filter "KOTA diagnostics + errors" shows only KOTA lines and errors.
5. Guided troubleshooting: run "Quick check" and answer truthfully with what I tell you the LED shows.
6. Flash (App only, the newest file in firmware/, chosen by its compile date): the tool must enter flashing mode
by itself, write, restart the box and report "Success ... build 4f10989e". Capture the esptool output.
7. SD card tab: the card reader and card are listed (internal disks must NOT be listed). "Check card" must NOT
report a wrong file system for a FAT32 card. After my OK, "Format card and write ENV.TXT" must use
`diskutil eraseDisk FAT32 KOTA MBRFormat` on the correct disk, write ENV.TXT and eject the card.
8. "Service report..." opens the browser at https://service.kota-app.com/<MAC>#kota-report=...; the note in
the form must match the Diagnosis panel (self-report, start-up progress with times, findings with
"Seen Nx, first at ... s").
At the end, write a short report: macOS version, Mac model/architecture, each test with result, errors, and
suggested fixes (file:line).
+1 -1
View File
@@ -1,3 +1,3 @@
"""KOTA sensorbox technician tool."""
__version__ = "0.7.0"
__version__ = "0.9.0"
+22
View File
@@ -335,6 +335,8 @@ class DiagnosticsEngine:
self.matter_qr: str | None = None # "KOTA: matter ... qr=MT:..."
self.matter_manual: str | None = None
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()
def _reset_led_state(self) -> None:
@@ -714,6 +716,20 @@ class DiagnosticsEngine:
fd = self._custom(key, Severity[sev.upper()], title, meaning, actions, expl)
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:
if "previous_boot" in line.words:
code = f.get("code", "0")
@@ -845,6 +861,12 @@ class DiagnosticsEngine:
if ln := k.get("mic"):
on = ln.fields.get("enabled") == "1"
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"):
f = ln.fields
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-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-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 = {
"ota_update", "button_provisioning_abort", "button_wifi_cleared",
"button_provisioning_toggle", "provisioning_saved",
"usb_wifi_set", "usb_wifi_clear", # Wi-Fi changed from the technician tool (usb_console)
}
MODULES = {
+8
View File
@@ -137,6 +137,14 @@ id = "benign.zcl"
pattern = 'chip\[ZCL\]: WRITE ERR|data_model: Cluster cannot be NULL'
severity = "ignore"
[[rule]]
id = "benign.wifi_start"
# Harmless Wi-Fi noise seen on every 1.0.16 boot: Matter asks for a connect
# while the station is already connecting, and an unpaired box polls a
# network it does not have yet (matter.not_commissioned covers that case).
pattern = 'wifi:sta is connecting, return error|esp_wifi_connect\(\) failed: ESP_ERR_WIFI_CONN|Failed to get configured network when updating network status'
severity = "ignore"
[[rule]]
id = "benign.connect_chain"
# Transport-level echo of a failure that a more specific rule reports.
+3 -2
View File
@@ -243,8 +243,9 @@ def check_card(mount: str, filesystem: str = "") -> CardReport:
"""What the box would find on this card (values masked in the UI)."""
root = Path(mount)
problems: list[str] = []
fs = filesystem.upper()
if fs and "FAT32" not in fs and fs not in ("FAT", "MS-DOS FAT32", "MSDOS"):
# Windows says "FAT32"; macOS diskutil says "DOS_FAT_32" / "Windows_FAT_32" / "MS-DOS FAT32".
fs = filesystem.upper().replace("_", "").replace(" ", "")
if fs and "FAT32" not in fs and fs not in ("FAT", "MSDOS"):
problems.append(f"File system is {filesystem}; the box needs FAT32 - re-format the card.")
env_file = find_case_insensitive(root, ENV_NAME)
env = parse_env(env_file.read_bytes()) if env_file else None
+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 ..diag.engine import DiagnosticsEngine
from ..diag.guides import GuideRunner
from ..serialio.commands import loggable
from ..serialio.manager import SerialManager, State
from ..serialio.ports import PortInfo, list_serial_ports
from ..matter.payload import SetupPayload, payload_from_log
@@ -38,6 +39,7 @@ from .flash_tab import FlashTab
from .matter_qr import MatterQrDialog
from .guide_panel import GuidePanel
from .sd_tab import SdTab
from .wifi_tab import WifiTab
MAX_LINES = 20000
# "KOTA" = the firmware's structured diagnostic lines (1.0.16+) plus errors.
@@ -76,6 +78,7 @@ class SerialTab(QWidget):
self._engine = DiagnosticsEngine()
self._diag_dirty = True
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
self.port_combo = QComboBox(minimumWidth=320)
@@ -237,6 +240,39 @@ class SerialTab(QWidget):
def engine(self) -> DiagnosticsEngine:
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:
"""Stop monitoring so esptool can open the port exclusively."""
if self._manager.is_running:
@@ -293,6 +329,8 @@ class SerialTab(QWidget):
self._session.write(line)
self._engine.feed(line)
self._runner.feed(line)
for listener in self._line_listeners:
listener(line)
self._diag_dirty = True
if line.kind == LineKind.BOOT:
self._counts["boots"] += 1
@@ -402,6 +440,8 @@ class MainWindow(QMainWindow):
tabs.addTab(guide, "Quick start")
tabs.addTab(self.serial_tab, "Serial monitor")
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)
tabs.addTab(self.sd_tab, "SD card")
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]
name = "kota-technician-tool"
version = "0.7.0"
version = "0.9.0"
description = "KOTA sensorbox technician diagnostic tool (demo)"
requires-python = ">=3.11,<3.14"
dependencies = [
+4 -2
View File
@@ -2,8 +2,10 @@
# Double-click in Finder, or run: bash run_macos.command
cd "$(dirname "$0")" || exit 1
# Keep the virtual environment out of the (Google Drive) tool folder.
export UV_PROJECT_ENVIRONMENT="$HOME/Library/Caches/KotaTechnicianTool/venv-0.7"
# If Finder refuses to open this file (Google Drive drops the executable bit), run it once as:
# bash run_macos.command (or: chmod +x run_macos.command)
# 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"
if ! command -v uv >/dev/null 2>&1; then
+2 -2
View File
@@ -2,8 +2,8 @@
setlocal
cd /d "%~dp0"
rem Keep the virtual environment out of the (Google Drive) tool folder.
set "UV_PROJECT_ENVIRONMENT=%LOCALAPPDATA%\KotaTechnicianTool\venv-0.7"
rem One shared Python environment for every version (update_windows.bat keeps it current).
set "UV_PROJECT_ENVIRONMENT=%LOCALAPPDATA%\KotaTechnicianTool\venv"
set "PATH=%USERPROFILE%\.local\bin;%PATH%"
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 "reboot.loop" in {c.rule_id for c in top.consequences}
assert r.verdict.startswith("Problem: Power supply")
@pytest.mark.parametrize("line", [
"E (2893) wifi:sta is connecting, return error",
"E (2892) chip[DL]: esp_wifi_connect() failed: ESP_ERR_WIFI_CONN",
"E (5120) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F",
])
def test_harmless_wifi_start_errors_are_not_reported(line):
_, r = run(BOOT + APP + [line])
assert r.roots == [] and "Unrecognised" not in r.to_text()
+12
View File
@@ -12,3 +12,15 @@ def test_other_ports_not_sensorbox():
bt = PortInfo("COM3", "Standard Serial over Bluetooth link", None, None, None)
assert not bt.is_sensorbox and bt.mac is None
assert bt.label == "COM3 - Standard Serial over Bluetooth link"
def test_macos_lists_only_callout_devices(monkeypatch):
from types import SimpleNamespace
from kota_tech.serialio import ports
fake = [SimpleNamespace(device=d, description="USB JTAG/serial debug unit", vid=0x303A, pid=0x1001,
serial_number="A4:CB:8F:C9:46:F8")
for d in ("/dev/tty.usbmodem1101", "/dev/cu.usbmodem1101")]
monkeypatch.setattr(ports.sys, "platform", "darwin")
monkeypatch.setattr(ports.list_ports, "comports", lambda: fake)
assert [p.device for p in ports.list_serial_ports()] == ["/dev/cu.usbmodem1101"]
+11
View File
@@ -170,3 +170,14 @@ def test_elevated_result_folder_is_not_owner_only(tmp_path, monkeypatch):
assert work.parent == tmp_path / "tmp" and work.is_dir()
(work / "result.json").write_text('{"ok": true}', encoding="utf-8")
assert (work / "result.json").read_text(encoding="utf-8")
@pytest.mark.parametrize("fs", ["FAT32", "DOS_FAT_32", "Windows_FAT_32", "MS-DOS FAT32"])
def test_fat32_names_from_windows_and_macos_are_accepted(tmp_path, fs):
(tmp_path / "ENV.TXT").write_bytes(b"INFLUX_URL=https://x\nINFLUX_TOKEN=abc\n")
assert not any("File system" in p for p in check_card(str(tmp_path), fs).problems)
def test_exfat_is_flagged(tmp_path):
(tmp_path / "ENV.TXT").write_bytes(b"INFLUX_URL=https://x\nINFLUX_TOKEN=abc\n")
assert any("File system" in p for p in check_card(str(tmp_path), "ExFAT").problems)
+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]]
name = "kota-technician-tool"
version = "0.7.0"
version = "0.9.0"
source = { virtual = "." }
dependencies = [
{ name = "esptool" },