"""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