Technician tool v0.2

Imported from the Google Drive folder 004-technician_tool/v0.2.
This commit is contained in:
kkallas committed 2026-09-26 16:53:59 +02:00
1 parent e5a33a1c34
commit e8b72b743d
17 files changed
+2410 -283

No files matched your search

@@ -0,0 +1,333 @@
W (19497) BME688: RTC is uninitialized, using current time as timestamp
I (19820) SEN66: Sent start measurement command, waiting for sensor to be ready...
W (20498) BME688: RTC is uninitialized, using current time as timestamp
W (21499) BME688: RTC is uninitialized, using current time as timestamp
E (21821) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0
E (21823) esp-tls: Failed to open new connection
E (21824) transport_base: Failed to open a new connection
E (21835) HTTP_CLIENT: Connection failed, sock < 0
I (21836) INFLUX: HTTP request performed, status code: 0
E (21847) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT
E (21849) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL
W (22500) BME688: RTC is uninitialized, using current time as timestamp
W (23501) BME688: RTC is uninitialized, using current time as timestamp
I (23860) INFLUX: Writing to InfluxDB
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x15 (USB_UART_CHIP_RESET),boot:0x2b (SPI_FAST_FLASH_BOOT)
Saved PC:0x4037f34a
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 Jun 29 2026 11:03:24
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 (36) 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 (70) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000
I (76) boot: 3 otadata OTA data 01 00 00031000 00002000
I (83) boot: 4 phy_init RF data 01 01 00033000 00001000
I (89) boot: 5 ota_0 OTA app 00 10 00040000 00300000
I (96) 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=3c170020 size=63194h (405908) map
I (191) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load
I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load
I (203) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map
I (469) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load
I (485) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load
I (495) boot: Loaded app from partition at offset 0x40000
I (495) boot: Disabling RNG early entropy source...
I (506) octal_psram: vendor id : 0x0d (AP)
I (506) octal_psram: dev id : 0x02 (generation 3)
I (506) octal_psram: density : 0x03 (64 Mbit)
I (508) octal_psram: good-die : 0x01 (Pass)
I (512) octal_psram: Latency : 0x01 (Fixed)
I (516) octal_psram: VCC : 0x01 (3V)
I (520) octal_psram: SRF : 0x01 (Fast Refresh)
I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap)
I (530) octal_psram: BurstLen : 0x01 (32 Byte)
I (535) octal_psram: Readlatency : 0x02 (10 cycles@Fixed)
I (540) octal_psram: DriveStrength: 0x00 (1/1)
I (544) esp_psram: Found 8MB PSRAM device
I (548) esp_psram: Speed: 40MHz
I (551) cpu_start: Multicore app
I (1285) esp_psram: SPI SRAM memory test OK
I (1293) cpu_start: GPIO 44 and 43 are used as console UART I/O pins
I (1294) cpu_start: Pro cpu start user code
I (1294) cpu_start: cpu freq: 160000000 Hz
I (1296) app_init: Application information:
I (1299) app_init: Project name: kota-sensorboard
I (1304) app_init: App version: 1.0
I (1308) app_init: Compile time: Jun 29 2026 11:03:32
I (1313) app_init: ELF file SHA256: 45fa9d5b9...
I (1317) app_init: ESP-IDF: v5.5.3-dirty
I (1322) efuse_init: Min chip rev: v0.0
I (1326) efuse_init: Max chip rev: v0.99
I (1330) efuse_init: Chip rev: v0.2
I (1334) heap_init: Initializing. RAM available for dynamic allocation:
I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM
I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (1351) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (1356) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM
I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator
I (1369) spi_flash: detected chip: generic
I (1372) spi_flash: flash io: dio
I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state
I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration
I (1397) main_task: Started on CPU0
I (1400) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (1408) main_task: Calling app_main()
E (1411) ID: Device MAC address: A4:CB:8F:C9:46:F8
I (1459) OTA_CLIENT: No OTA token found in NVS, need to register device
I (1463) MAIN: Matter node created successfully
W (1464) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again.
W (1468) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again.
W (1476) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again.
W (1485) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1494) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again.
W (1503) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1512) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again.
W (1520) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again.
W (1529) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again.
W (1538) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1547) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again.
W (1555) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1564) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again.
W (1573) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again.
W (1582) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again.
W (1590) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again.
W (1599) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again.
W (1608) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again.
I (1619) pp: pp rom version: e7ae62f
I (1620) net80211: net80211 rom version: e7ae62f
I (1626) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0
I (1635) wifi:wifi firmware version: 4df78f2
I (1635) wifi:wifi certification version: v7.0
I (1638) wifi:config NVS flash: enabled
I (1642) wifi:config nano formatting: disabled
I (1646) wifi:Init data frame dynamic rx buffer num: 32
I (1651) wifi:Init static rx mgmt buffer num: 5
I (1655) wifi:Init management short buffer num: 32
I (1660) wifi:Init dynamic tx buffer num: 32
I (1664) wifi:Init static tx FG buffer num: 2
I (1668) wifi:Init static rx buffer size: 1600
I (1672) wifi:Init static rx buffer num: 10
I (1676) wifi:Init dynamic rx buffer num: 32
I (1681) wifi_init: rx ba win: 6
I (1683) wifi_init: accept mbox: 6
I (1686) wifi_init: tcpip mbox: 32
I (1689) wifi_init: udp mbox: 6
I (1692) wifi_init: tcp mbox: 6
I (1695) wifi_init: tcp tx win: 5760
I (1698) wifi_init: tcp rx win: 5760
I (1701) wifi_init: tcp mss: 1440
I (1712) chip[DL]: NVS set: chip-counters/reboot-count = 11 (0xB)
I (1715) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC)
I (1718) BLE_INIT: BT controller compile version [1bb2f50]
I (1722) BLE_INIT: Using main XTAL as clock source
I (1727) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1
I (1736) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa
I (1741) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10
I (1784) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0)
I (1801) NimBLE: GAP procedure initiated: stop advertising.
I (1802) CHIP[DL]: BLE host-controller synced
I (2303) chip[DL]: Starting ESP WiFi layer
I (2305) wifi:mode : sta (a4:cb:8f:c9:46:f8)
I (2306) wifi:enable tsf
W (2306) wifi:Haven't to connect to a suitable AP now!
I (2307) chip[DL]: I (2307) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43
Done driving station state, nothing else to do...
I (2316) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2
W (2320) wifi:Haven't to connect to a suitable AP now!
I (2331) chip[DL]: Done driving station state, nothing else to do...
I (2337) chip[SVR]: Initializing subscription resumption storage...
I (2344) chip[SVR]: Server initializing...
I (2347) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06
I (2352) chip[DMG]: AccessControl: initializing
I (2356) chip[DMG]: Examples::AccessControlDelegate::Init
I (2361) chip[DMG]: AccessControl: setting
I (2365) chip[DMG]: DefaultAclStorage: initializing
I (2370) chip[DMG]: DefaultAclStorage: 0 entries loaded
I (2375) chip[SVR]: WARNING: mTestEventTriggerDelegate is null
I (2392) chip[ZCL]: Using ZAP configuration...
I (2393) esp_matter_cluster: Cluster plugin init common callback
I (2393) chip[DMG]: AccessControlCluster: initializing
I (2396) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported
I (2406) chip[DIS]: Updating services using commissioning mode 1
I (2410) chip[DIS]: CHIP minimal mDNS started advertising.
I (2415) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0
I (2425) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: F90440ED10E7BA33.
I (2435) chip[DIS]: mDNS service published: _matterc._udp
I (2440) chip[IN]: CASE Server enabling CASE session setups
I (2446) chip[SVR]: Joining Multicast groups
I (2449) chip[SVR]: Server Listening...
I (2454) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 **********
I (2464) data_model: Dynamic endpoint 0 added
E (2467) data_model: Cluster cannot be NULL.
I (2472) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 **********
I (2482) data_model: Dynamic endpoint 1 added
E (2486) data_model: Cluster cannot be NULL.
I (2491) chip[DL]: WIFI_EVENT_STA_START
I (2492) MAIN: ESP-Matter started successfully
W (2494) wifi:Haven't to connect to a suitable AP now!
I (2503) chip[DL]: Done driving station state, nothing else to do...
I (2510) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable)
I (2520) NimBLE: GAP procedure initiated: advertise;
I (2521) NimBLE: disc_mode=2
I (2521) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40
I (2526) NimBLE:
I (2527) chip[DL]: CHIPoBLE advertising started
I (2528) MAIN: Commissioning window opened
>
I (4074) I2C: Creating I2C mutex
I (14075) SPI: Creating SPI mutex
I (14117) INFLUX_BUFFER: SD card mounted successfully
I (14118) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully
I (14121) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota
W (14135) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota
I (14159) BME688: Delaying for 10000 microseconds
I (14777) Flicker: Flicker sensor initialized on pin 14
I (14779) temperature_sensor: Range [20°C ~ 100°C], error < 2°C
I (14781) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data
I (14781) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data
W (14786) wifi:I (14782) SEN66: Creating sensor mutex
Haven't to connect to a suitable AP now!I (14797) SEN66: Waking up sensor by setting power GPIO to enabled level...
I (14788) INFLUX_BUFFER: Flushing chunk starting at line 10:
I (14795) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms
W (14783) MICROPHONE: Microphone is in shutdown state, skipping sample collection
I (14823) Flicker: Starting to collect samples in gather task
I (14811) DeviceStatus: Active tasks: 26
I (14811) INFLUX: Writing to InfluxDB
E (14835) SENSOR_GATHERER: Failed to get line protocol from sensor
I (14841) DeviceStatus: Task list:
gather_sensor_t X 1 1216 22
main R 1 876 4
IDLE1 R 0 700 6
IDLE0 R 0 692 5
gather_task B 1 1756 19
gather_task B 1 14032 20
alive_diode_tas B 5 352 8
reset_task B 0 3432 15
tiT B 18 2420 10
gather_sensor_t B 23 1720 24
ota_task B 1 7516 18
CHIP B 1 3700 12
Tmr Svc B 1 1364 7
ds1307_update B 1 800 17
sys_evt B 20 2684 9
ipc0 S 24 372 1
ipc1 S 24 524 2
console B 5 648 16
gather_sensor_t B 1 2332 23
esp_timer S 22 3188 3
gather_sensor_t B 1 2324 25
wifi B 23 4324 11
gather_sensor_t S 1 1344 26
btController B 23 1960 13
gather_sensor_t B 1 1496 21
flicker_gather_ S 24 1948 27
nimble_host B 21 1480 14
I (14853) SENSOR_GATHERER: Sensor should now be asleep
W (14929) DeviceStatus: RTC is uninitialized, using current time as timestamp
I (15861) Flicker: Finished collecting samples, notifying main task
I (15863) Flicker: Collected 1024 samples for dominant frequency analysis
W (15870) AS7341: Reading spectral data from AS7341
I (15870) SENSOR_GATHERER: Sensor should now be asleep
I (15880) BME688: Delaying for 199517 microseconds
W (15941) DeviceStatus: RTC is uninitialized, using current time as timestamp
W (16178) AS7341: Reading basic counts data from AS7341
W (16194) AS7341: Reading flicker detection state from AS7341
W (16412) as7341: FD Status Register: 0x20 (0b00100000)
I (16418) AS7341: Updating Matter attributes with AS7341 data: 18108
I (16419) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 **********
I (16432) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 18108 **********
E (16445) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0
E (16447) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0
W (16459) AS7341: RTC is uninitialized, using current time as timestamp
I (16460) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null **********
I (16473) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1013 **********
E (16486) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0
E (16498) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0
W (16499) BME688: RTC is uninitialized, using current time as timestamp
I (16942) SENSOR_GATHERER: Sensor should now be asleep
I (17283) SENSOR_GATHERER: Gathered line protocol:
device_status,device=A4CB8FC946F8 battery=4.04,last_restart=13421,status="OK",temp=35.40,rssi=0,wifi_status="disconnected",free_heap=54927,psram_free=8305944,heap_blocks=253768,psram_blocks=79976,active_tasks=26 946684801000000000
W (17461) AS7341: RTC is uninitialized, using current time as timestamp
W (17511) BME688: RTC is uninitialized, using current time as timestamp
W (18462) AS7341: RTC is uninitialized, using current time as timestamp
W (18512) BME688: RTC is uninitialized, using current time as timestamp
W (19463) AS7341: RTC is uninitialized, using current time as timestamp
W (19513) BME688: RTC is uninitialized, using current time as timestamp
I (19809) SEN66: Sent start measurement command, waiting for sensor to be ready...
W (20464) AS7341: RTC is uninitialized, using current time as timestamp
W (20514) BME688: RTC is uninitialized, using current time as timestamp
W (21465) AS7341: RTC is uninitialized, using current time as timestamp
W (21515) BME688: RTC is uninitialized, using current time as timestamp
E (21843) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0
E (21845) esp-tls: Failed to open new connection
E (21846) transport_base: Failed to open a new connection
E (21857) HTTP_CLIENT: Connection failed, sock < 0
I (21858) INFLUX: HTTP request performed, status code: 0
E (21869) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT
E (21871) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL
W (22466) AS7341: RTC is uninitialized, using current time as timestamp
W (22516) BME688: RTC is uninitialized, using current time as timestamp
W (23467) AS7341: RTC is uninitialized, using current time as timestamp
W (23517) BME688: RTC is uninitialized, using current time as timestamp
I (23882) INFLUX: Writing to InfluxDB
W (24468) AS7341: RTC is uninitialized, using current time as timestamp
W (24518) BME688: RTC is uninitialized, using current time as timestamp
I (24781) OTA_CLIENT: Device is not registered, attempting to register...
I (24781) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data
W (24785) wifi:W (24781) MICROPHONE: Microphone is in shutdown state, skipping sample collection
Haven't to connect to a suitable AP now!
E (24798) SENSOR_GATHERER: Failed to get line protocol from sensor
I (24784) OTA_CLIENT: HTTP client handle: 0x3fcf1b34
I (24799) DeviceStatus: Active tasks: 26
I (24815) DeviceStatus: Task list:
gather_sensor_t R 1 1496 21
gather_sensor_t X 1 1136 22
IDLE1 R 0 700 6
IDLE0 R 0 692 5
gather_task B 1 1756 19
gather_task B 1 14032 20
ds1307_update B 1 800 17
alive_diode_tas B 5 352 8
gather_sensor_t B 1 532 25
gather_sensor_t B 1 748 23
reset_task B 0 3432 15
CHIP B 1 3700 12
gather_sensor_t B 23 1720 24
Tmr Svc B 1 1364 7
tiT B 18 2420 10
console B 5 648 16
ipc0 S 1 372 1
gather_sensor_t S 1 1344 26
sys_evt B 20 2684 9
main B 1 876 4
esp_timer S 22 3188 3
btController B 23 1960 13
wifi B 23 4324 11
ota_task B 1 5356 18
nimble_host B 21 1480 14
ipc1 S 24 524 2
W (24890) DeviceStatus: RTC is uninitialized, using current time as timestamp
+235
View File
@@ -0,0 +1,235 @@
"""Diagnostics engine: real captures (fixtures) + synthetic scenarios built
from the firmware's exact log strings for failures not captured yet."""
from pathlib import Path
import pytest
from kota_tech.core.logfile import read_log_file
from kota_tech.core.logline import LineKind, LogLine, parse_line
from kota_tech.diag.engine import DiagnosticsEngine, Severity, load_rules
FIX = Path(__file__).parent / "fixtures"
def run(lines, engine=None):
e = engine or DiagnosticsEngine()
t = 0.0
for item in lines:
t += 0.01
if isinstance(item, LogLine):
e.feed(item)
else:
e.feed(parse_line(item, host_time=t))
return e, e.report()
def replay(name):
e = DiagnosticsEngine()
for ln in read_log_file(FIX / name):
e.feed(ln)
return e.report()
def roots(report):
return {rc.finding.rule_id: rc for rc in report.roots}
BOOT = ["ESP-ROM:esp32s3-20210327"]
APP = ["I (1299) app_init: Project name: kota-sensorboard",
"I (1317) app_init: ELF file SHA256: 45fa9d5b9..."]
# ------------------------------------------------------------------ rules
def test_rules_load_and_have_no_cycles():
rules = load_rules()
by_id = {r.id: r for r in rules}
for r in rules:
seen, todo = set(), list(r.explained_by)
while todo:
rid = todo.pop()
assert rid != r.id, f"explained_by cycle through {r.id}"
if rid not in seen:
seen.add(rid)
todo += by_id[rid].explained_by
# --------------------------------------------------------- real captures
def test_live_bench_box_uncommissioned():
r = replay("live_boot_A4CB8FC946F8_fw1.0.txt")
rs = roots(r)
assert r.firmware.known and "new_system.bin" in r.firmware.describe()
assert r.firmware.supports_sd_wifi is False
assert r.led == "PURPLE" # confirmed on the bench
assert "Wi-Fi" in r.verdict and r.verdict.startswith("Problem")
grouped = {c.rule_id for c in rs["matter.not_commissioned"].consequences}
assert {"net.dns_fail", "influx.buffer_fail", "ota.registration_failed"} <= grouped
assert "rtc.clock_not_running" in rs
assert "mic.disabled" in rs
reached = {m for m, _, t in r.milestones if t is not None}
assert {"rom_boot", "app_started", "sd_mounted", "env_loaded", "sensors_ready"} <= reached
assert "wifi_connected" not in reached
def test_live_bench_box_led_still_green_at_30s():
# Purple only starts when the first Influx flush fails (~60 s uptime).
assert replay("live_reset_A4CB8FC946F8_fw1.0_restored.txt").led == "GREEN"
def test_connected_box_is_healthy():
r = replay("serial_log_20260917_162922.txt")
assert r.verdict_severity is None
assert "time_synced" in {m for m, _, t in r.milestones if t is not None}
assert r.mid_run
@pytest.mark.parametrize("name", ["serial_log_20260917_162714.txt", "kota_serial_log_20260917_163439.txt"])
def test_offline_sample_boxes(name):
rs = roots(replay(name))
assert "net.dns_fail" in rs and "rtc.clock_not_running" in rs
# ---------------------------------------------------- synthetic scenarios
def _rtc_fail_boot(reset="RTC_SW_CPU_RST"):
return BOOT + [f"rst:0xc ({reset}),boot:0x8 (SPI_FAST_FLASH_BOOT)"] + APP + [
"I (2493) MAIN: ESP-Matter started successfully",
"E (3510) DS1307: Failed to initialize DS1307 control register: ESP_ERR_TIMEOUT",
"E (3511) MAIN: Failed to initialize RTC: ESP_ERR_TIMEOUT",
"ESP_ERROR_CHECK failed: esp_err_t 0x107 (ESP_ERR_TIMEOUT) at 0x4200f7b2",
'file: "./main/main.cpp" line 473',
"abort() was called at PC 0x40379b1f on core 0",
]
def test_rtc_i2c_reboot_loop():
_, r = run(_rtc_fail_boot("POWERON") + _rtc_fail_boot() + _rtc_fail_boot() + _rtc_fail_boot())
rs = roots(r)
root = rs["rtc.init_failed"]
assert root.finding.severity == Severity.CRITICAL
assert "I2C" in root.finding.meaning
grouped = {c.rule_id for c in root.consequences}
assert {"sys.crash", "reboot.loop"} <= grouped
assert r.led == "BLUE"
assert r.verdict.startswith("Problem")
def test_sd_card_missing():
_, r = run(BOOT + APP + ["E (1519) SD_CARD: Failed to initialize the card (ESP_ERR_TIMEOUT). "
"Make sure SD card lines have pull-up resistors in place."])
f = roots(r)["sd.card_fail"].finding
assert "ESP_ERR_TIMEOUT" in f.title and f.severity == Severity.CRITICAL
assert r.led == "WHITE"
def test_influx_token_rejected():
_, r = run(BOOT + APP + ["E (30000) INFLUX: Failed to perform HTTP request: 401, ESP_OK"])
f = roots(r)["influx.auth"].finding
assert "401" in f.title and f.severity == Severity.CRITICAL
def test_recovery_resolves_network_findings():
lines = BOOT + APP + [
"E (21837) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0",
"I (30222) SNTP: Time synchronized: Thu Sep 17 14:29:22 2026",
"I (31000) INFLUX: HTTP request performed, status code: 204",
]
_, r = run(lines)
assert "net.dns_fail" not in roots(r)
assert any(f.rule_id == "net.dns_fail" for f in r.resolved)
assert r.verdict.startswith("Healthy")
def test_led_conditional_green_after_purple():
e, r = run(BOOT + APP + ["E (60009) INFLUX_BUFFER: Failed to write chunk with 5 retries to Influx: ESP_FAIL"])
assert r.led == "PURPLE"
_, r = run(["I (61000) OTA_CLIENT: No update available"], e)
assert r.led == "GREEN"
_, r = run(["I (62000) OTA_CLIENT: New update available, starting OTA update..."], e)
assert r.led == "YELLOW"
def test_status_watchdog_restart_grouped_under_network_cause():
lines = BOOT + APP + [
"I (2542) MAIN: Commissioning window opened",
"E (60009) INFLUX_BUFFER: Failed to write chunk with 5 retries to Influx: ESP_FAIL",
"W (200000) BME688: x",
] + BOOT + ["rst:0x3 (RTC_SW_SYS_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)"] + APP + [
"I (2542) MAIN: Commissioning window opened",
"E (60009) INFLUX_BUFFER: Failed to write chunk with 5 retries to Influx: ESP_FAIL",
]
_, r = run(lines)
grouped = {c.rule_id for c in roots(r)["matter.not_commissioned"].consequences}
assert "reboot.status_watchdog" in grouped
assert "reboot.loop" not in grouped # 200 s boots are not a loop
def test_tool_reset_is_not_a_finding():
lines = BOOT + APP + [LogLine("--- reset issued by tool ---", LineKind.HOST, 0.5)]
lines += BOOT + ["rst:0x15 (USB_UART_CHIP_RESET),boot:0x2b (SPI_FAST_FLASH_BOOT)"] + APP
_, r = run(lines)
assert not [rc for rc in r.roots if rc.finding.rule_id.startswith("reboot.")]
assert len(r.boots) == 3 # mid-run record + 2 boots
def test_first_seen_is_per_boot_and_own_severity_breaks_ties():
rtc = "W ({}) BME688: RTC is uninitialized, using current time as timestamp"
lines = [rtc.format(200_000 + i) for i in range(5)] # before the reset
lines += BOOT + APP + [rtc.format(15_000 + i) for i in range(5)]
lines += ["W (24756) MICROPHONE: Microphone is in shutdown state, skipping sample collection",
"E (24773) SENSOR_GATHERER: Failed to get line protocol from sensor"] * 2
_, r = run(lines)
order = [rc.finding.rule_id for rc in r.roots]
assert order.index("rtc.clock_not_running") < order.index("mic.disabled")
assert roots(r)["rtc.clock_not_running"].finding.first_uptime_ms == 15_000
def test_reboot_detected_without_banner():
e, r = run(["I (90000) A: running", "I (95000) A: running", "I (1200) B: fresh boot"])
assert len(r.boots) == 2 and not r.boots[1].explicit
def test_split_by_sensor():
_, r = run(BOOT + APP + [
"E (14000) MAIN: Failed to initialize BME688: ESP_FAIL",
"E (14001) MAIN: Failed to initialize SEN66: ESP_ERR_TIMEOUT",
])
titles = {rc.finding.title for rc in r.roots if rc.finding.rule_id == "sensor.init_failed"}
assert titles == {"BME688 failed to start", "SEN66 failed to start"}
assert r.led == "RED"
def test_rtc_loses_time_after_sync():
_, r = run(BOOT + APP + ["I (10222) SNTP: Time synchronized: Thu Sep 17 14:29:22 2026"]
+ [f"W ({30000 + i}) BME688: RTC is uninitialized, using current time as timestamp" for i in range(3)])
assert "rtc.lost_after_sync" in roots(r)
def test_unknown_firmware_reports_sd_wifi_from_evidence():
_, r = run(BOOT + ["I (1317) app_init: ELF file SHA256: 0123456789..."] + [
"I (1523) WIFI_CREDENTIALS: SSID/PASSWORD not set in .env; Matter will be used"])
assert r.firmware.known is None and r.firmware.supports_sd_wifi is True
assert "OTA" in r.firmware.describe()
def test_report_text_renders():
text = replay("live_boot_A4CB8FC946F8_fw1.0.txt").to_text()
assert text.startswith("VERDICT: Problem") and "consequence:" in text
# ------------------------------------------------------------- log files
def test_session_log_format(tmp_path):
p = tmp_path / "session.log"
p.write_text("[14:00:00.000] --- reset issued by tool ---\n"
"[14:00:00.400] ESP-ROM:esp32s3-20210327\n"
"[14:00:00.401] rst:0x15 (USB_UART_CHIP_RESET),boot:0x2b (SPI_FAST_FLASH_BOOT)\n"
"[14:00:01.500] I (1299) app_init: Project name: kota-sensorboard\n", encoding="utf-8")
lines = read_log_file(p)
assert [ln.kind for ln in lines] == [LineKind.HOST, LineKind.BOOT, LineKind.RESET, LineKind.LOG]
e = DiagnosticsEngine()
for ln in lines:
e.feed(ln)
assert e.boot.tool_reset
+23 -14
View File
@@ -2,7 +2,7 @@ from pathlib import Path
import pytest
from kota_tech.core.logline import LineFramer, LineKind, parse_line
from kota_tech.core.logline import LineFramer, LineKind, parse_line, split_glued
FIXTURES = sorted((Path(__file__).parent / "fixtures").glob("*.txt"))
@@ -39,27 +39,35 @@ def test_framer_splits_and_idle_flushes():
def test_framer_drops_stale_fragment_after_open():
f = LineFramer(drop_first_partial=True, now=0.0)
assert f.feed(b"current time as t", now=0.01) == []
assert f.feed(b"imestamp\r\nW (15899) DeviceStatus: RTC\r\n", now=0.02) == ["W (15899) DeviceStatus: RTC\r"]
# Real capture: stale FIFO tail released together with the next live line.
f = LineFramer(drop_first_partial=True)
got = f.feed(b"I (96) boot: 6 ota_1 OTA app 00 11 00340000"
b"W (19497) BME688: RTC is uninitialized\r\nI (19820) SEN66: x\r\n")
assert got == ["W (19497) BME688: RTC is uninitialized\r", "I (19820) SEN66: x\r"]
def test_framer_keeps_first_line_from_idle_board():
# Board was silent at open; the boot banner arriving later must survive.
f = LineFramer(drop_first_partial=True, now=0.0)
assert f.feed(b"", now=0.1) == []
assert f.feed(b"ESP-ROM:esp32s3-20210327\r\n", now=5.0) == ["ESP-ROM:esp32s3-20210327\r"]
# Board silent at open; the boot banner arriving later must survive.
f = LineFramer(drop_first_partial=True)
assert f.feed(b"ESP-ROM:esp32s3-20210327\r\n") == ["ESP-ROM:esp32s3-20210327\r"]
def test_framer_cancel_drop():
f = LineFramer(drop_first_partial=True, now=0.0)
f.cancel_drop()
assert f.feed(b"ESP-ROM:esp32s3\n", now=0.01) == ["ESP-ROM:esp32s3"]
def test_framer_keeps_complete_first_line():
f = LineFramer(drop_first_partial=True)
assert f.feed(b"W (1) A: complete\r\n") == ["W (1) A: complete\r"]
def test_framer_splits_interleaved_lines():
f = LineFramer()
assert f.feed(b"I (2302) chip[DL]: I (2302) chip[DL]: Posting event\n") == [
"I (2302) chip[DL]: ", "I (2302) chip[DL]: Posting event"]
assert f.feed(b"wifi:\x1b[0;32mI (5) X: y\n") == ["wifi:", "\x1b[0;32mI (5) X: y"]
def test_framer_tolerates_invalid_utf8():
f = LineFramer()
assert f.feed(b"\xff\xfeI (1) X: y\n") == ["��I (1) X: y"]
# Garbage in front of a log line is split off rather than corrupting it.
assert f.feed(b"\xff\xfeI (1) X: y\n") == ["��", "I (1) X: y"]
@pytest.mark.parametrize("path", FIXTURES, ids=lambda p: p.name)
@@ -79,6 +87,7 @@ def test_replay_sample_log_chunked_equals_whole(path):
@pytest.mark.parametrize("path", FIXTURES, ids=lambda p: p.name)
def test_replay_sample_log_parses(path):
lines = [parse_line(r) for r in LineFramer().feed(path.read_bytes())]
raw_errors = sum(1 for r in path.read_text("utf-8", "replace").splitlines() if r.startswith("E ("))
raw_parts = [p for r in path.read_text("utf-8", "replace").splitlines() for p in split_glued(r)]
raw_errors = sum(1 for p in raw_parts if p.startswith("E ("))
assert sum(1 for ln in lines if ln.level == "E") == raw_errors
assert raw_errors > 0
+1 -1
View File
@@ -33,7 +33,7 @@ def test_loopback_lines_reset_and_disconnect(monkeypatch):
m.connect(LOOP)
assert _wait(lambda: m.state == State.CONNECTED)
m.write(b"stale tail\r\nESP-ROM:esp32s3-20210327\r\nE (5) INFLUX: token: s3cr3t\r\npartial")
m.write(b"stale tailESP-ROM:esp32s3-20210327\r\nE (5) INFLUX: token: s3cr3t\r\npartial")
assert _wait(lambda: any(ln.text == "partial" for ln in lines)) # idle flush
assert not any("stale" in ln.text for ln in lines) # first fragment after open dropped
m.reset_board()