From e5a33a1c347149ce36fa35a758c5273e90eea6b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kallas Date: Sat, 26 Sep 2026 16:11:07 +0200 Subject: [PATCH] Technician tool v0.1 Imported from the Google Drive folder 004-technician_tool/v0.1. --- .gitattributes | 4 + .gitignore | 6 + environment.yml | 10 + kota_tech/__init__.py | 3 + kota_tech/__main__.py | 17 + kota_tech/core/__init__.py | 0 kota_tech/core/logline.py | 140 ++++ kota_tech/core/redact.py | 37 + kota_tech/core/session.py | 39 ++ kota_tech/serialio/__init__.py | 0 kota_tech/serialio/manager.py | 192 ++++++ kota_tech/serialio/ports.py | 76 +++ kota_tech/ui/__init__.py | 0 kota_tech/ui/cheatsheet.py | 83 +++ kota_tech/ui/main_window.py | 234 +++++++ pyproject.toml | 19 + run_macos.command | 18 + run_windows.bat | 27 + .../kota_serial_log_20260917_163439.txt | 368 ++++++++++ .../fixtures/live_boot_A4CB8FC946F8_fw1.0.txt | 641 ++++++++++++++++++ tests/fixtures/serial_log_20260917_162548.txt | 514 ++++++++++++++ tests/fixtures/serial_log_20260917_162714.txt | 139 ++++ tests/fixtures/serial_log_20260917_162922.txt | 69 ++ tests/test_logline.py | 84 +++ tests/test_manager.py | 98 +++ tests/test_ports.py | 14 + tests/test_redact.py | 33 + uv.lock | 123 ++++ 28 files changed, 2988 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 environment.yml create mode 100644 kota_tech/__init__.py create mode 100644 kota_tech/__main__.py create mode 100644 kota_tech/core/__init__.py create mode 100644 kota_tech/core/logline.py create mode 100644 kota_tech/core/redact.py create mode 100644 kota_tech/core/session.py create mode 100644 kota_tech/serialio/__init__.py create mode 100644 kota_tech/serialio/manager.py create mode 100644 kota_tech/serialio/ports.py create mode 100644 kota_tech/ui/__init__.py create mode 100644 kota_tech/ui/cheatsheet.py create mode 100644 kota_tech/ui/main_window.py create mode 100644 pyproject.toml create mode 100755 run_macos.command create mode 100644 run_windows.bat create mode 100644 tests/fixtures/kota_serial_log_20260917_163439.txt create mode 100644 tests/fixtures/live_boot_A4CB8FC946F8_fw1.0.txt create mode 100644 tests/fixtures/serial_log_20260917_162548.txt create mode 100644 tests/fixtures/serial_log_20260917_162714.txt create mode 100644 tests/fixtures/serial_log_20260917_162922.txt create mode 100644 tests/test_logline.py create mode 100644 tests/test_manager.py create mode 100644 tests/test_ports.py create mode 100644 tests/test_redact.py create mode 100644 uv.lock diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..adfdc3d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.command text eol=lf +*.sh text eol=lf +*.bat text eol=crlf +*.bin binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..899c477 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +__pycache__/ +*.pyc +.pytest_cache/ +.venv/ +desktop.ini +.DS_Store diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..08969ee --- /dev/null +++ b/environment.yml @@ -0,0 +1,10 @@ +# Alternative to the uv launchers for developers using conda/mamba: +# mamba env create -f environment.yml && mamba run -n kota-technician-tool python -m kota_tech +name: kota-technician-tool +channels: + - conda-forge +dependencies: + - python=3.12 + - pyserial>=3.5 + - pyside6>=6.7 + - pytest>=8 diff --git a/kota_tech/__init__.py b/kota_tech/__init__.py new file mode 100644 index 0000000..a5b92aa --- /dev/null +++ b/kota_tech/__init__.py @@ -0,0 +1,3 @@ +"""KOTA sensorbox technician tool.""" + +__version__ = "0.1.0" diff --git a/kota_tech/__main__.py b/kota_tech/__main__.py new file mode 100644 index 0000000..6b93d27 --- /dev/null +++ b/kota_tech/__main__.py @@ -0,0 +1,17 @@ +import sys + +from PySide6.QtWidgets import QApplication + +from .ui.main_window import MainWindow + + +def main() -> int: + app = QApplication(sys.argv) + app.setStyle("Fusion") + win = MainWindow() + win.show() + return app.exec() + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/kota_tech/core/__init__.py b/kota_tech/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kota_tech/core/logline.py b/kota_tech/core/logline.py new file mode 100644 index 0000000..881b3cf --- /dev/null +++ b/kota_tech/core/logline.py @@ -0,0 +1,140 @@ +"""Serial byte stream -> framed, cleaned, parsed log lines. + +ESP-IDF log format: " () : ", optionally +wrapped in ANSI colour codes. ROM/bootloader output (boot banner, reset +reason) has no level prefix and is classified separately, since a boot +banner is the only reliable reboot marker. +""" + +from __future__ import annotations + +import re +import time +from dataclasses import dataclass, field +from enum import Enum + +from .redact import redact + +ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]") +ESP_LOG_RE = re.compile(r"^([EWIDV]) \((\d+)\) ([^:]{1,40}?): ?(.*)$") +# ESP32-S3 ROM prints e.g. "ESP-ROM:esp32s3-20210327" then +# "rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)". +BOOT_BANNER_RE = re.compile(r"^ESP-ROM:") +RESET_REASON_RE = re.compile(r"^rst:0x([0-9a-fA-F]+) \(([A-Z0-9_]+)\)") +PANIC_RE = re.compile( + r"Guru Meditation Error|abort\(\) was called|ESP_ERROR_CHECK failed|" + r"^Backtrace:|Rebooting\.\.\.$" +) + + +class LineKind(str, Enum): + LOG = "log" # ESP_LOGx line + BOOT = "boot" # ROM boot banner (start of a new boot) + RESET = "reset" # ROM reset-reason line + PANIC = "panic" # crash / abort output + RAW = "raw" # anything else (task list rows, line protocol, ...) + HOST = "host" # event generated by this tool (connect, reset, ...) + + +def host_event(text: str) -> LogLine: + return LogLine(f"--- {text} ---", LineKind.HOST) + + +@dataclass(frozen=True) +class LogLine: + text: str # cleaned + redacted + kind: LineKind + host_time: float = field(default_factory=time.time) + level: str | None = None # E/W/I/D/V for LOG lines + uptime_ms: int | None = None + tag: str | None = None + message: str | None = None + reset_reason: str | None = None + + +def clean(raw: str) -> str: + return ANSI_RE.sub("", raw).replace("\r", "").rstrip() + + +def parse_line(raw: str, host_time: float | None = None) -> LogLine: + text = redact(clean(raw)) + t = time.time() if host_time is None else host_time + if m := ESP_LOG_RE.match(text): + level, ms, tag, msg = m.groups() + return LogLine(text, LineKind.LOG, t, level, int(ms), tag.strip(), msg) + if BOOT_BANNER_RE.match(text): + return LogLine(text, LineKind.BOOT, t) + if m := RESET_REASON_RE.match(text): + return LogLine(text, LineKind.RESET, t, reset_reason=m.group(2)) + if PANIC_RE.search(text): + return LogLine(text, LineKind.PANIC, t) + return LogLine(text, LineKind.RAW, t) + + +class LineFramer: + """Accumulates bytes and yields complete lines. + + A partial line is released after `idle_flush_s` without new bytes so + prompts / unterminated output are not held back forever. + """ + + # Stale FIFO bytes arrive immediately after open; anything later is live. + STALE_WINDOW_S = 0.25 + + def __init__(self, idle_flush_s: float = 0.3, max_line: int = 4096, + drop_first_partial: bool = False, now: float | None = None): + self._buf = bytearray() + self._last_rx = 0.0 + self.idle_flush_s = idle_flush_s + self.max_line = max_line + # The S3 USB-Serial-JTAG FIFO keeps the tail of an old line while no + # host is reading and flushes it on open; drop bytes up to the first + # newline after a (re)connect so it is not glued to a fresh line. + # Only bytes arriving within STALE_WINDOW_S of open are considered + # stale, otherwise an idle board's next boot banner would be lost. + self._dropping = drop_first_partial + self._drop_deadline = (time.monotonic() if now is None else now) + self.STALE_WINDOW_S + + def cancel_drop(self) -> None: + self._dropping = False + + def feed(self, data: bytes, now: float | None = None) -> list[str]: + now = time.monotonic() if now is None else now + if self._dropping and now > self._drop_deadline: + self._dropping = False + if data: + self._buf.extend(data) + self._last_rx = now + if self._dropping: + i = self._buf.find(b"\n") + if i < 0: + return [] + del self._buf[: i + 1] + self._dropping = False + out: list[str] = [] + while (i := self._buf.find(b"\n")) >= 0: + out.append(self._decode(self._buf[:i])) + del self._buf[: i + 1] + if len(self._buf) >= self.max_line: + out.append(self._decode(self._buf)) + self._buf.clear() + return out + + def poll_idle(self, now: float | None = None) -> list[str]: + now = time.monotonic() if now is None else now + if self._buf and now - self._last_rx >= self.idle_flush_s: + line = self._decode(self._buf) + self._buf.clear() + return [line] + return [] + + def flush(self) -> list[str]: + if not self._buf: + return [] + line = self._decode(self._buf) + self._buf.clear() + return [line] + + @staticmethod + def _decode(b: bytes | bytearray) -> str: + return bytes(b).decode("utf-8", errors="replace") diff --git a/kota_tech/core/redact.py b/kota_tech/core/redact.py new file mode 100644 index 0000000..609fbc6 --- /dev/null +++ b/kota_tech/core/redact.py @@ -0,0 +1,37 @@ +"""Secret redaction for anything shown on screen or written to disk. + +Firmware prints the OTA token in clear text (ota_client.cpp: +"OTA token loaded from NVS: %s", "received token: %s") and env.txt +values may be echoed. Influx tokens are ~88-char base64 strings. +Redaction is applied before a line leaves the serial layer. +""" + +from __future__ import annotations + +import re + +MASK = "" + +_RULES: list[re.Pattern[str]] = [ + # "... token loaded from NVS: X", "... received token: X", "token: X" + re.compile(r"(?i)(\btoken\b[^:=\n]{0,40}[:=]\s*)(\S+)"), + # HTTP auth header echo: "Authorization: Token X" / "Token X" + re.compile(r"(\bToken\s+)([A-Za-z0-9_\-+/=]{16,})"), + # env-style secrets: INFLUX_TOKEN=..., PASSWORD=... + re.compile(r"(?i)(\b(?:INFLUX_TOKEN|PASSWORD|PASS|SECRET|API_KEY)\s*=\s*)(\S+)"), +] +# Bare long base64/url-safe blobs (Influx tokens end in "=="). +_BLOB = re.compile(r"(? str: + for rule in _RULES: + text = rule.sub(lambda m: m.group(1) + MASK, text) + + def _blob(m: re.Match[str]) -> str: + s = m.group(0) + return s if _BLOB_ALLOW.search(s) else MASK + + return _BLOB.sub(_blob, text) diff --git a/kota_tech/core/session.py b/kota_tech/core/session.py new file mode 100644 index 0000000..f47837e --- /dev/null +++ b/kota_tech/core/session.py @@ -0,0 +1,39 @@ +"""Per-connection session log on disk (already redacted lines only).""" + +from __future__ import annotations + +import datetime as dt +import re +from pathlib import Path + +from .logline import LogLine + +SESSIONS_DIR = Path.home() / "KotaTechnicianTool" / "sessions" + + +class SessionLog: + def __init__(self, root: Path = SESSIONS_DIR): + self.root = root + self.path: Path | None = None + self._fh = None + + def start(self, device_hint: str) -> Path: + self.stop() + self.root.mkdir(parents=True, exist_ok=True) + stamp = dt.datetime.now().strftime("%Y%m%d_%H%M%S") + safe = re.sub(r"[^A-Za-z0-9]+", "", device_hint)[:20] or "board" + self.path = self.root / f"session_{stamp}_{safe}.log" + self._fh = self.path.open("a", encoding="utf-8", newline="\n") + return self.path + + def write(self, line: LogLine) -> None: + if self._fh is None: + return + ts = dt.datetime.fromtimestamp(line.host_time).strftime("%H:%M:%S.%f")[:-3] + self._fh.write(f"[{ts}] {line.text}\n") + self._fh.flush() + + def stop(self) -> None: + if self._fh is not None: + self._fh.close() + self._fh = None diff --git a/kota_tech/serialio/__init__.py b/kota_tech/serialio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kota_tech/serialio/manager.py b/kota_tech/serialio/manager.py new file mode 100644 index 0000000..93095fd --- /dev/null +++ b/kota_tech/serialio/manager.py @@ -0,0 +1,192 @@ +"""Serial connection manager (GUI-independent). + +One background thread owns the port: it opens, reads, frames and parses +lines, executes queued commands (reset, write) and re-finds the board if +the port vanishes (power switch, USB re-enumeration). Callbacks are +invoked from that thread; the Qt layer marshals them to the UI thread. +""" + +from __future__ import annotations + +import enum +import queue +import threading +import time +from collections.abc import Callable +from dataclasses import dataclass + +import serial + +from ..core.logline import LineFramer, LogLine, host_event, parse_line +from .ports import PortInfo, find_port + +READ_TIMEOUT_S = 0.1 +REOPEN_POLL_S = 0.5 + + +class State(str, enum.Enum): + DISCONNECTED = "Disconnected" + CONNECTED = "Connected" + WAITING = "Waiting for board" # port lost; polling for it to return + + +@dataclass +class _Target: + device: str + serial_number: str | None + + +class SerialManager: + def __init__( + self, + on_line: Callable[[LogLine], None], + on_state: Callable[[State, str], None], + baudrate: int = 115200, # ignored by native USB CDC, used for UART + ): + self._on_line = on_line + self._on_state = on_state + self._baud = baudrate + self._cmds: queue.Queue[tuple[str, object]] = queue.Queue() + self._thread: threading.Thread | None = None + self._stop = threading.Event() + self._target: _Target | None = None + self.state = State.DISCONNECTED + + # ---- public API (any thread) ------------------------------------- + def connect(self, port: PortInfo) -> None: + self.disconnect() + self._target = _Target(port.device, port.serial_number) + self._stop.clear() + self._thread = threading.Thread(target=self._run, name="serial-reader", daemon=True) + self._thread.start() + + def disconnect(self) -> None: + if self._thread and self._thread.is_alive(): + self._stop.set() + self._thread.join(timeout=2.0) + self._thread = None + + def reset_board(self) -> None: + """Hard-reset the chip into the application (not download mode).""" + self._cmds.put(("reset", None)) + + def write(self, data: bytes) -> None: + self._cmds.put(("write", data)) + + @property + def is_running(self) -> bool: + return bool(self._thread and self._thread.is_alive()) + + # ---- reader thread ----------------------------------------------- + def _set_state(self, state: State, detail: str = "") -> None: + if state != self.state: + self.state = state + self._on_state(state, detail) + + def _open(self) -> serial.Serial | None: + assert self._target + info = find_port(self._target.serial_number, self._target.device) + if info is None: + return None + # serial_for_url also accepts test URLs such as "loop://". + ser = serial.serial_for_url( + info.device, baudrate=self._baud, timeout=READ_TIMEOUT_S, do_not_open=True + ) + # Deassert both lines before open. On USB-Serial-JTAG, RTS=1/DTR=0 + # holds the chip in reset and DTR=1 during reset selects download + # mode; pyserial would otherwise assert both on open (Windows). + ser.dtr = False + ser.rts = False + try: + ser.open() + except (serial.SerialException, OSError): + return None + self._target.device = info.device # COM number / node may change + return ser + + @staticmethod + def _set_rts(ser: serial.Serial, state: bool) -> None: + ser.rts = state + # Windows usbser.sys only sends SET_CONTROL_LINE_STATE when DTR is + # written, so an RTS-only change never reaches the chip. Re-write + # DTR with its current value to push the new RTS (esptool does the + # same). Harmless on macOS. + ser.dtr = ser.dtr + + @classmethod + def _hard_reset(cls, ser: serial.Serial) -> None: + # Same sequence as esptool HardReset for USB-Serial-JTAG: pulse + # EN via RTS with DTR (GPIO0) released, long enough for the USB + # peripheral to settle. + ser.dtr = False + cls._set_rts(ser, True) + time.sleep(0.2) + cls._set_rts(ser, False) + time.sleep(0.2) + + def _run(self) -> None: + framer = LineFramer() + ser: serial.Serial | None = None + announced_wait = False + try: + while not self._stop.is_set(): + if ser is None: + ser = self._open() + if ser is None: + if not announced_wait: + self._set_state(State.WAITING, "Plug in / power on the sensorbox") + announced_wait = True + self._drain_cmds(None) + time.sleep(REOPEN_POLL_S) + continue + announced_wait = False + framer = LineFramer(drop_first_partial=True) + self._set_state(State.CONNECTED, ser.port) + self._on_line(host_event(f"connected to {ser.port}")) + + try: + if self._drain_cmds(ser): + framer.cancel_drop() # output after a reset is always live + data = ser.read(ser.in_waiting or 1) + except (serial.SerialException, OSError) as exc: + for raw in framer.flush(): + self._on_line(parse_line(raw)) + self._on_line(host_event(f"port lost ({exc.__class__.__name__}); waiting for board")) + self._close(ser) + ser = None + continue + + for raw in framer.feed(data) + framer.poll_idle(): + self._on_line(parse_line(raw)) + finally: + for raw in framer.flush(): + self._on_line(parse_line(raw)) + if ser is not None: + self._close(ser) + self._on_line(host_event("disconnected")) + self._set_state(State.DISCONNECTED) + + def _drain_cmds(self, ser: serial.Serial | None) -> bool: + """Execute queued commands; returns True if the board was reset.""" + did_reset = False + while True: + try: + cmd, arg = self._cmds.get_nowait() + except queue.Empty: + return did_reset + if ser is None: + self._on_line(host_event(f"'{cmd}' ignored: board not connected")) + continue + if cmd == "reset": + self._on_line(host_event("reset issued by tool")) + self._hard_reset(ser) + did_reset = True + elif cmd == "write": + ser.write(arg) # type: ignore[arg-type] + + @staticmethod + def _close(ser: serial.Serial) -> None: + try: + ser.close() + except (serial.SerialException, OSError): + pass diff --git a/kota_tech/serialio/ports.py b/kota_tech/serialio/ports.py new file mode 100644 index 0000000..76c2260 --- /dev/null +++ b/kota_tech/serialio/ports.py @@ -0,0 +1,76 @@ +"""Serial port discovery. + +Technicians connect through the ESP32-S3 native USB (USB-Serial-JTAG, +VID 0x303A / PID 0x1001). Its USB serial number is the chip MAC, which +is stable across resets and power cycles, so it is used to re-find the +same box after the port disappears (power switch toggled). +""" + +from __future__ import annotations + +import sys +from dataclasses import dataclass + +from serial.tools import list_ports + +ESPRESSIF_VID = 0x303A +USB_SERIAL_JTAG_PID = 0x1001 + + +@dataclass(frozen=True) +class PortInfo: + device: str + description: str + vid: int | None + pid: int | None + serial_number: str | None + + @property + def is_sensorbox(self) -> bool: + return self.vid == ESPRESSIF_VID + + @property + def mac(self) -> str | None: + """USB-Serial-JTAG reports the MAC as 'AA:BB:CC:DD:EE:FF'.""" + sn = self.serial_number or "" + parts = sn.split(":") + if len(parts) == 6 and all(len(p) == 2 for p in parts): + return sn.upper() + return None + + @property + def label(self) -> str: + if self.is_sensorbox: + kind = "Sensorbox (native USB)" if self.pid == USB_SERIAL_JTAG_PID else "Espressif USB" + return f"{self.device} - {kind}" + (f" [{self.mac}]" if self.mac else "") + return f"{self.device} - {self.description}" + + +def _from_pyserial(p) -> PortInfo: + return PortInfo(p.device, p.description or "", p.vid, p.pid, p.serial_number) + + +def list_serial_ports(include_all: bool = False) -> list[PortInfo]: + ports = [_from_pyserial(p) for p in list_ports.comports()] + if sys.platform == "darwin": + # Each macOS device appears twice; /dev/tty.* blocks on open waiting + # for carrier detect, /dev/cu.* is the right one for us. + ports = [p for p in ports if not p.device.startswith("/dev/tty.")] + if not include_all: + ports = [p for p in ports if p.is_sensorbox] + # Sensorboxes first, then by name. + return sorted(ports, key=lambda p: (not p.is_sensorbox, p.device)) + + +def find_port(serial_number: str | None, device: str | None) -> PortInfo | None: + """Re-locate a board: prefer the USB serial number (MAC), fall back to name.""" + ports = list_serial_ports(include_all=True) + if serial_number: + for p in ports: + if p.serial_number == serial_number: + return p + if device: + for p in ports: + if p.device == device: + return p + return None diff --git a/kota_tech/ui/__init__.py b/kota_tech/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kota_tech/ui/cheatsheet.py b/kota_tech/ui/cheatsheet.py new file mode 100644 index 0000000..a713587 --- /dev/null +++ b/kota_tech/ui/cheatsheet.py @@ -0,0 +1,83 @@ +"""Quick-start cheat sheet shown on launch. + +Targets the DEPLOYED firmware: new_system.bin, app "kota-sensorboard" +1.0, built Jun 29 2026 11:03:32, ELF SHA256 45fa9d5b9... (verified on a +live board). That build has no Wi-Fi setup portal / SSID-on-SD support; +those exist only in the newer, unbuilt source. The status LED is always +SOLID; the separate small LED toggles every second while alive. +""" + +# (swatch colour, LED name, meaning, first action) +LED_TABLE = [ + ("#2e9e44", "Green", "Everything OK.", "Nothing to do."), + ("#8e44ad", "Purple", + "No Internet / InfluxDB or OTA server unreachable.", + "Check the box is commissioned in the Matter app (sticker QR code) and the " + "site Wi-Fi has Internet. Board reboots itself after ~2.5 min of purple."), + ("#d64541", "Red", + "Sensor or hardware fault (BME688, SEN66, AS7341, microphone, flicker) " + "or failed self-check after update.", + "Connect serial - the log names the failing sensor. Check sensor cables."), + ("#2f6fd6", "Blue", + "Clock (RTC DS1307) failed to start. The RTC is the first I2C device, " + "so a broken I2C cable also shows blue.", + "Check the I2C harness first, then the RTC module / coin cell."), + ("#e6c229", "Yellow", "Firmware update downloading.", "Do not switch off. Wait."), + ("#f08c1a", "Orange", "Self-check after an update (~2.5 min).", + "Wait; should turn green (or red on failure)."), + ("#f4f4f4", "White", + "SD card missing/unreadable or ENV.TXT incomplete - or a factory reset is " + "in progress (Matter button held 5 s).", + "If nobody pressed the Matter button: the SD card is inside the case - " + "unscrew, re-seat or re-provision it."), + ("#444444", "Off", "Firmware did not start (or crashed very early).", + "Check power switch & USB cable. If the small LED is also dark: re-flash."), +] + +HINTS = [ + "Colour keeps changing / LED goes dark every few seconds = reboot loop. " + "Connect serial and press Reset & capture: the last lines before the " + "reboot name the cause.", + "Small LED next to the RGB LED blinks once per second while the " + "firmware runs. Not blinking = firmware hung or not running.", + "Outside the case: power switch and the Matter button. Holding the " + "Matter button 5 s factory-resets the box (it forgets its Matter " + "network and must be commissioned again with the sticker QR code).", + "Inside the case (unscrew): RESET and BOOT buttons and the SD card. " + "Normally not needed - the tool resets the board over USB.", + "Gathering messages for the first ~2 minutes are normal " + "(the air-quality sensor warms up for 120 s).", +] + +STEPS = [ + "Switch the sensorbox power switch ON.", + "Connect the laptop to the box's native USB port with a data-capable cable.", + "Open the Serial monitor tab, choose the sensorbox port, press " + "Connect.", + "Press Reset & capture to record a full boot log from the first line.", + "Note the RGB LED colour and compare with the table below.", +] + + +def cheatsheet_html() -> str: + rows = "".join( + f"      {name}" + f"{meaning}{action}" + for c, name, meaning, action in LED_TABLE + ) + steps = "".join(f"
  • {s}
  • " for s in STEPS) + hints = "".join(f"
  • {h}
  • " for h in HINTS) + return f""" +

    Sensorbox quick start

    +
      {steps}
    +

    RGB status LED (always solid colour)

    + + + + + {rows} +
    LEDMost likely meaningFirst action
    +

    Good to know

    +
      {hints}
    + """ diff --git a/kota_tech/ui/main_window.py b/kota_tech/ui/main_window.py new file mode 100644 index 0000000..11f9b5a --- /dev/null +++ b/kota_tech/ui/main_window.py @@ -0,0 +1,234 @@ +from __future__ import annotations + +from PySide6.QtCore import QObject, Qt, QTimer, QUrl, Signal +from PySide6.QtGui import QColor, QDesktopServices, QFont, QTextCharFormat, QTextCursor +from PySide6.QtWidgets import ( + QCheckBox, + QComboBox, + QFileDialog, + QHBoxLayout, + QLabel, + QMainWindow, + QPlainTextEdit, + QPushButton, + QTabWidget, + QTextBrowser, + QVBoxLayout, + QWidget, +) + +from .. import __version__ +from ..core.logline import LineKind, LogLine +from ..core.session import SessionLog +from ..serialio.manager import SerialManager, State +from ..serialio.ports import PortInfo, list_serial_ports +from .cheatsheet import cheatsheet_html + +MAX_LINES = 20000 +LEVEL_FILTERS = {"All lines": None, "Warnings + errors": {"W", "E"}, "Errors only": {"E"}} +COLORS = { + "E": "#ff6b6b", + "W": "#f5c451", + "I": "#d4d4d4", + LineKind.BOOT: "#6fc3ff", + LineKind.RESET: "#6fc3ff", + LineKind.PANIC: "#ff4dd2", + LineKind.HOST: "#7ee07e", + LineKind.RAW: "#a8a8a8", +} + + +class _Bridge(QObject): + """Marshals SerialManager callbacks (reader thread) onto the UI thread.""" + + line = Signal(object) + state = Signal(object, str) + + +class SerialTab(QWidget): + def __init__(self, parent: QWidget | None = None): + super().__init__(parent) + self._bridge = _Bridge() + self._bridge.line.connect(self._on_line) + self._bridge.state.connect(self._on_state) + self._manager = SerialManager(self._bridge.line.emit, self._bridge.state.emit) + self._session = SessionLog() + self._ports: list[PortInfo] = [] + self._counts = {"E": 0, "W": 0, "boots": 0} + + # --- controls + self.port_combo = QComboBox(minimumWidth=320) + self.all_ports = QCheckBox("Show all ports") + self.refresh_btn = QPushButton("Refresh") + self.connect_btn = QPushButton("Connect") + self.reset_btn = QPushButton("Reset && capture") + self.reset_btn.setToolTip("Reboot the sensorbox and record the log from the first boot line") + self.filter_combo = QComboBox() + self.filter_combo.addItems(LEVEL_FILTERS) + self.clear_btn = QPushButton("Clear view") + self.save_btn = QPushButton("Save view as...") + self.folder_btn = QPushButton("Open logs folder") + + row1 = QHBoxLayout() + for w in (QLabel("Port:"), self.port_combo, self.all_ports, self.refresh_btn, + self.connect_btn, self.reset_btn): + row1.addWidget(w) + row1.addStretch(1) + row2 = QHBoxLayout() + for w in (QLabel("Show:"), self.filter_combo, self.clear_btn, self.save_btn, self.folder_btn): + row2.addWidget(w) + row2.addStretch(1) + + self.view = QPlainTextEdit(readOnly=True) + self.view.setMaximumBlockCount(MAX_LINES) + self.view.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) + mono = QFont("Menlo" if QFont("Menlo").exactMatch() else "Consolas") + mono.setStyleHint(QFont.StyleHint.Monospace) + mono.setPointSize(10) + self.view.setFont(mono) + self.view.setStyleSheet("QPlainTextEdit { background:#1e1e1e; color:#d4d4d4; }") + + self.status = QLabel() + layout = QVBoxLayout(self) + layout.addLayout(row1) + layout.addLayout(row2) + layout.addWidget(self.view, 1) + layout.addWidget(self.status) + + # --- wiring + self.refresh_btn.clicked.connect(self.refresh_ports) + self.all_ports.toggled.connect(self.refresh_ports) + self.connect_btn.clicked.connect(self._toggle_connect) + self.reset_btn.clicked.connect(self._manager.reset_board) + self.clear_btn.clicked.connect(self.view.clear) + self.save_btn.clicked.connect(self._save_view) + self.folder_btn.clicked.connect(self._open_folder) + + # Auto-detect a box being plugged in while idle. + self._poll = QTimer(self, interval=2000) + self._poll.timeout.connect(self._auto_refresh) + self._poll.start() + + self.refresh_ports() + self._on_state(State.DISCONNECTED, "") + + # ---- ports ------------------------------------------------------- + def refresh_ports(self) -> None: + current = self.port_combo.currentData() + self._ports = list_serial_ports(include_all=self.all_ports.isChecked()) + self.port_combo.clear() + for p in self._ports: + self.port_combo.addItem(p.label, p.device) + if not self._ports: + self.port_combo.addItem("No sensorbox found - check USB cable and power switch", None) + idx = self.port_combo.findData(current) + self.port_combo.setCurrentIndex(max(idx, 0)) + + def _auto_refresh(self) -> None: + if self._manager.is_running: + return + devices = [p.device for p in list_serial_ports(self.all_ports.isChecked())] + if devices != [p.device for p in self._ports]: + self.refresh_ports() + + def _selected_port(self) -> PortInfo | None: + dev = self.port_combo.currentData() + return next((p for p in self._ports if p.device == dev), None) + + # ---- connection -------------------------------------------------- + def _toggle_connect(self) -> None: + if self._manager.is_running: + # Session file is closed in _on_state(DISCONNECTED), after the + # reader thread's final queued lines have been written. + self._manager.disconnect() + return + port = self._selected_port() + if port is None: + return + self._counts = {"E": 0, "W": 0, "boots": 0} + self._session.start(port.mac or port.device) + self._manager.connect(port) + + def _on_state(self, state: State, detail: str) -> None: + running = state != State.DISCONNECTED + if not running: + self._session.stop() + self.connect_btn.setText("Disconnect" if running else "Connect") + self.reset_btn.setEnabled(state == State.CONNECTED) + self.port_combo.setEnabled(not running) + self.all_ports.setEnabled(not running) + self.refresh_btn.setEnabled(not running) + self._state_text = f"{state.value}" + (f" ({detail})" if detail else "") + self._update_status() + + def _update_status(self) -> None: + sess = f" | Session log: {self._session.path}" if self._session.path else "" + c = self._counts + self.status.setText( + f"{getattr(self, '_state_text', '')} | boots seen: {c['boots']} " + f"errors: {c['E']} warnings: {c['W']}{sess}" + ) + + # ---- lines ------------------------------------------------------- + def _on_line(self, line: LogLine) -> None: + self._session.write(line) + if line.kind == LineKind.BOOT: + self._counts["boots"] += 1 + if line.level in ("E", "W"): + self._counts[line.level] += 1 + self._update_status() + + allowed = LEVEL_FILTERS[self.filter_combo.currentText()] + if allowed is not None and line.kind != LineKind.HOST and not ( + line.level in allowed or line.kind in (LineKind.PANIC, LineKind.BOOT, LineKind.RESET) + ): + return + self._append(line) + + def _append(self, line: LogLine) -> None: + sb = self.view.verticalScrollBar() + at_bottom = sb.value() >= sb.maximum() - 4 + fmt = QTextCharFormat() + fmt.setForeground(QColor(COLORS.get(line.level) or COLORS.get(line.kind, "#d4d4d4"))) + if line.kind in (LineKind.PANIC, LineKind.BOOT, LineKind.HOST): + fmt.setFontWeight(QFont.Weight.Bold) + cur = QTextCursor(self.view.document()) + cur.movePosition(QTextCursor.MoveOperation.End) + if not self.view.document().isEmpty(): + cur.insertBlock() + cur.insertText(line.text, fmt) + if at_bottom: + sb.setValue(sb.maximum()) + + # ---- misc -------------------------------------------------------- + def _save_view(self) -> None: + path, _ = QFileDialog.getSaveFileName(self, "Save log view", "sensorbox_log.txt", "Text (*.txt)") + if path: + with open(path, "w", encoding="utf-8") as fh: + fh.write(self.view.toPlainText()) + + def _open_folder(self) -> None: + self._session.root.mkdir(parents=True, exist_ok=True) + QDesktopServices.openUrl(QUrl.fromLocalFile(str(self._session.root))) + + def shutdown(self) -> None: + self._manager.disconnect() + self._session.stop() + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle(f"KOTA Sensorbox Technician Tool v{__version__}") + self.resize(1200, 800) + tabs = QTabWidget() + guide = QTextBrowser(openExternalLinks=True) + guide.setHtml(cheatsheet_html()) + self.serial_tab = SerialTab() + tabs.addTab(guide, "Quick start") + tabs.addTab(self.serial_tab, "Serial monitor") + self.setCentralWidget(tabs) + + def closeEvent(self, event): # noqa: N802 (Qt naming) + self.serial_tab.shutdown() + super().closeEvent(event) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0da02d3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "kota-technician-tool" +version = "0.1.0" +description = "KOTA sensorbox technician diagnostic tool (demo)" +requires-python = ">=3.11,<3.14" +dependencies = [ + "pyserial>=3.5", + "PySide6-Essentials>=6.7", +] + +[dependency-groups] +dev = ["pytest>=8"] + +[tool.uv] +package = false + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["."] diff --git a/run_macos.command b/run_macos.command new file mode 100755 index 0000000..608ca65 --- /dev/null +++ b/run_macos.command @@ -0,0 +1,18 @@ +#!/bin/bash +# 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.1" +export PATH="$HOME/.local/bin:$PATH" + +if ! command -v uv >/dev/null 2>&1; then + echo "uv (Python package runner) is not installed." + echo "It will be installed for the current user from https://astral.sh/uv" + read -r -p "Install uv now? [y/N] " answer + [[ "$answer" =~ ^[Yy]$ ]] || exit 1 + curl -LsSf https://astral.sh/uv/install.sh | sh || { echo "uv installation failed."; exit 1; } +fi + +echo "Starting KOTA Sensorbox Technician Tool (first start downloads dependencies)..." +uv run --frozen --no-dev --python 3.12 python -m kota_tech || read -r -p "Press Enter to close..." diff --git a/run_windows.bat b/run_windows.bat new file mode 100644 index 0000000..e32fef6 --- /dev/null +++ b/run_windows.bat @@ -0,0 +1,27 @@ +@echo off +setlocal +cd /d "%~dp0" + +rem Keep the virtual environment out of the (Google Drive) tool folder. +set "UV_PROJECT_ENVIRONMENT=%LOCALAPPDATA%\KotaTechnicianTool\venv-0.1" +set "PATH=%USERPROFILE%\.local\bin;%PATH%" + +where uv >nul 2>nul +if errorlevel 1 ( + echo uv ^(Python package runner^) is not installed. + echo It will be installed for the current user from https://astral.sh/uv + choice /m "Install uv now" + if errorlevel 2 exit /b 1 + powershell -NoProfile -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" +) + +where uv >nul 2>nul +if errorlevel 1 ( + echo [!] uv installation failed. Ask the firmware team for help. + pause + exit /b 1 +) + +echo Starting KOTA Sensorbox Technician Tool (first start downloads dependencies)... +uv run --frozen --no-dev --python 3.12 python -m kota_tech +if errorlevel 1 pause diff --git a/tests/fixtures/kota_serial_log_20260917_163439.txt b/tests/fixtures/kota_serial_log_20260917_163439.txt new file mode 100644 index 0000000..00738ec --- /dev/null +++ b/tests/fixtures/kota_serial_log_20260917_163439.txt @@ -0,0 +1,368 @@ +I (14060) SPI: Creating SPI mutex +I (14113) INFLUX_BUFFER: SD card mounted successfully +I (14114) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully +I (14117) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (14131) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (14155) BME688: Delaying for 10000 microseconds +I (14773) Flicker: Flicker sensor initialized on pin 14 +I (14775) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14777) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +I (14778) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +W (14781) wifi:I (14779) SEN66: Creating sensor mutex +Haven't to connect to a suitable AP now! +W (14791) AS7341: Reading spectral data from AS7341 +I (14794) SEN66: Waking up sensor by setting power GPIO to enabled level... +I (14784) INFLUX_BUFFER: Flushing chunk starting at line 3: +I (14796) DeviceStatus: Active tasks: 26 +I (14819) INFLUX: Writing to InfluxDB +I (14824) DeviceStatus: Task list: +main R 1 876 4 +gather_sensor_t X 1 1352 21 +IDLE0 R 0 692 5 +IDLE1 R 0 700 6 +gather_sensor_t B 1 2544 20 +ds1307_update B 1 812 17 +gather_task B 1 1752 19 +gather_task B 1 14044 22 +alive_diode_tas B 5 352 8 +reset_task B 0 3428 15 +tiT B 18 2420 10 +gather_sensor_t B 1 1816 24 +ota_task B 1 7512 18 +CHIP B 1 3700 12 +Tmr Svc B 1 1364 7 +gather_sensor_t B 1 1092 25 +sys_evt B 20 2684 9 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +console B 5 644 16 +gather_sensor_t B 1 2544 26 +esp_timer S 22 3332 3 +wifi B 23 4244 11 +gather_sensor_t B 1 2332 23 +btController B 23 1960 13 +nimble_host B 21 1476 14 + +W (14888) DeviceStatus: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (15110) AS7341: Reading basic counts data from AS7341 +W (15126) AS7341: Reading flicker detection state from AS7341 +W (15344) as7341: FD Status Register: 0x20 (0b00100000) +I (15350) AS7341: Updating Matter attributes with AS7341 data: 20808 +I (15350) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (15364) Flicker: Starting to collect samples in gather task +I (15352) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (15378) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 20808 ********** +E (15391) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (15393) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (15405) AS7341: Formatting AS7341 data in line protocol +AS7341 line protocol: +as7341_adc,device=A4CB8FC9473C f1=2,f2=11,f3=15,f4=16,f5=9,f6=13,f7=22,f8=13,clear=54,nir=7 946684801000000000 +as7341_basic_counts,device=A4CB8FC9473C f1=4.46,f2=24.54,f3=33.46,f4=35.70,f5=20.08,f6=29.00,f7=49.08,f8=29.00,clear=120.47,nir=15.62 946684801000000000 +as7341_flicker,device=A4CB8FC9473C state="unknown" 946684801000000000 +W (15900) DeviceStatus: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (16389) Flicker: Finished collecting samples, notifying main task +I (16391) Flicker: Collected 1024 samples for dominant frequency analysis +I (16398) SENSOR_GATHERER: Sensor should now be asleep +I (16408) BME688: Delaying for 199517 microseconds +I (16410) SENSOR_GATHERER: Sensor should now be asleep +I (16847) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16850) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1004 ********** +E (16862) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16875) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16877) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (16903) SENSOR_GATHERER: Sensor should now be asleep +W (17888) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (18889) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (19808) SEN66: Sent start measurement command, waiting for sensor to be ready... +W (19891) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (19914) MICROPHONE: Read 300000 bytes in 4936 ms +I (19915) MICROPHONE: Read 300000 bytes of audio data +I (20023) MICROPHONE: Sum SQ: 0.000000, RMS pressure: 0.000000, Normalized: 0.000000, dB peak: -inf +W (20025) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (20892) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (21037) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +E (21825) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 + +[FAIL] NETWORK ERROR: DNS Resolution Failed! Board cannot reach the internet. Did it lose WiFi? +E (21827) esp-tls: Failed to open new connection +E (21828) transport_base: Failed to open a new connection +E (21840) HTTP_CLIENT: Connection failed, sock < 0 +I (21841) INFLUX: HTTP request performed, status code: 0 +E (21852) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (21854) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL + +[FAIL] CLOUD ERROR: Failed to upload sensor data to InfluxDB. +W (21893) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (22038) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (22780) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (22894) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (23039) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (23281) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (23782) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (23865) INFLUX: Writing to InfluxDB +W (23895) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (24040) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (24283) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (24777) OTA_CLIENT: Device is not registered, attempting to register... +I (24780) OTA_CLIENT: HTTP client handle: 0x3fcf04c0 +I (24784) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (24896) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (25041) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (25285) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (25786) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (25897) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (26042) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (26287) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (26788) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (26898) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (27043) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (27289) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (27790) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (27899) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (28044) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (28291) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (28792) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (28900) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (29045) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (29293) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (29794) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (29901) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (30046) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (30295) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (30796) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (30866) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 + +[FAIL] NETWORK ERROR: DNS Resolution Failed! Board cannot reach the internet. Did it lose WiFi? +E (30866) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 + +[FAIL] NETWORK ERROR: DNS Resolution Failed! Board cannot reach the internet. Did it lose WiFi? +E (30868) esp-tls: Failed to open new connection +E (30880) esp-tls: Failed to open new connection +E (30881) transport_base: Failed to open a new connection +E (30892) transport_base: Failed to open a new connection +E (30893) HTTP_CLIENT: Connection failed, sock < 0 +E (30905) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223 +E (30907) OTA_CLIENT: Device registration failed: ESP_ERR_HTTP_CONNECT + +[FAIL] CLOUD ERROR: Failed to register with the OTA server. +E (30894) HTTP_CLIENT: Connection failed, sock < 0 +W (30902) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (30921) INFLUX: HTTP request performed, status code: 0 +E (30933) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (30934) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL + +[FAIL] CLOUD ERROR: Failed to upload sensor data to InfluxDB. +W (31047) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (31297) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (31798) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (31922) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (32048) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (32299) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (32404) chip[DL]: bleAdv Timeout : Start slow advertisement +I (32406) chip[DL]: Configuring CHIPoBLE advertising (interval 500 ms, connectable) +I (32408) chip[DL]: Device already advertising, stop active advertisement and restart +I (32420) NimBLE: GAP procedure initiated: stop advertising. + +I (32422) NimBLE: GAP procedure initiated: advertise; + +[INFO] BLE Provisioning Started: Board has fallen back to Bluetooth. It needs WiFi credentials. +I (32434) NimBLE: disc_mode=2 +I (32434) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=800 adv_itvl_max=800 +I (32436) NimBLE: + +I (32800) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (32923) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (32946) INFLUX: Writing to InfluxDB +W (33049) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (33301) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (33802) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (33924) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (34050) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (34303) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (34804) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (34925) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (35051) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (35305) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (35806) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (35926) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (36052) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (36307) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (36808) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (36927) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (37053) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (37309) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (37810) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (37928) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (38054) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (38311) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (38812) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (38929) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (39055) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (39313) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (39814) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (39930) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +E (39947) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 + +[FAIL] NETWORK ERROR: DNS Resolution Failed! Board cannot reach the internet. Did it lose WiFi? +E (39949) esp-tls: Failed to open new connection +E (39950) transport_base: Failed to open a new connection +E (39961) HTTP_CLIENT: Connection failed, sock < 0 +I (39962) INFLUX: HTTP request performed, status code: 0 +E (39974) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (39975) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL + +[FAIL] CLOUD ERROR: Failed to upload sensor data to InfluxDB. +W (40056) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (40315) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (40816) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (40919) OTA_CLIENT: Device is not registered, attempting to register... +I (40921) OTA_CLIENT: HTTP client handle: 0x3fcf0d68 +W (40931) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (41057) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (41317) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (41818) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (41932) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (41986) INFLUX: Writing to InfluxDB +W (42058) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (42319) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (42820) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (42933) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (43059) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (43321) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (43822) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (43934) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (44060) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (44323) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (44824) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (44935) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (45061) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (45325) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (45826) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (45936) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (46062) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +I (46327) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (46828) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (46937) BME688: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... +W (47063) SEN66: RTC is uninitialized, using current time as timestamp + +[WARN] Time not synced yet. Board might be offline or still connecting... diff --git a/tests/fixtures/live_boot_A4CB8FC946F8_fw1.0.txt b/tests/fixtures/live_boot_A4CB8FC946F8_fw1.0.txt new file mode 100644 index 0000000..23a9537 --- /dev/null +++ b/tests/fixtures/live_boot_A4CB8FC946F8_fw1.0.txt @@ -0,0 +1,641 @@ +W (15899) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (16391) Flicker: Finished collecting samples, notifying main task +I (16393) Flicker: Collected 1024 samples for dominant frequency analysis +I (16400) SENSOR_GATHERER: Sensor should now be asleep +I (16412) SENSOR_GATHERER: Sensor should now be asleep +I (16452) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16454) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1013 ********** +E (16467) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16479) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16481) BME688: RTC is uninitialized, using current time as timestamp +I (16900) SENSOR_GATHERER: Sensor should now be asleep +I (17264) SENSOR_GATHERER: Gathered line protocol: +device_status,device=A4CB8FC946F8 battery=4.04,last_restart=13397,status="OK",temp=30.40,rssi=0,wifi_status="disconnected",free_heap=55171,psram_free=8306056,heap_blocks=253532,psram_blocks=79868,active_tasks=26 946684801000000000 + +W (17492) BME688: RTC is uninitialized, using current time as timestamp +W (18493) BME688: RTC is uninitialized, using current time as timestamp +W (19494) BME688: RTC is uninitialized, using current time as timestamp +I (19816) SEN66: Sent start measurement command, waiting for sensor to be ready... +W (20495) BME688: RTC is uninitialized, using current time as timestamp +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 (505) 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 (1295) 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 (1350) 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 (1461) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1465) MAIN: Matter node created successfully +W (1466) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1470) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1496) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1505) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1523) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1531) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1540) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1549) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1566) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1575) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1584) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1593) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1601) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1610) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1621) pp: pp rom version: e7ae62f +I (1622) net80211: net80211 rom version: e7ae62f +I (1628) wifi:wifi driver task: 3fcc524c, prio:23, stack:6656, core=0 +I (1636) wifi:wifi firmware version: 4df78f2 +I (1637) wifi:wifi certification version: v7.0 +I (1641) wifi:config NVS flash: enabled +I (1644) wifi:config nano formatting: disabled +I (1649) wifi:Init data frame dynamic rx buffer num: 32 +I (1654) wifi:Init static rx mgmt buffer num: 5 +I (1658) wifi:Init management short buffer num: 32 +I (1662) wifi:Init dynamic tx buffer num: 32 +I (1666) wifi:Init static tx FG buffer num: 2 +I (1670) wifi:Init static rx buffer size: 1600 +I (1674) wifi:Init static rx buffer num: 10 +I (1678) wifi:Init dynamic rx buffer num: 32 +I (1683) wifi_init: rx ba win: 6 +I (1685) wifi_init: accept mbox: 6 +I (1688) wifi_init: tcpip mbox: 32 +I (1691) wifi_init: udp mbox: 6 +I (1694) wifi_init: tcp mbox: 6 +I (1697) wifi_init: tcp tx win: 5760 +I (1700) wifi_init: tcp rx win: 5760 +I (1704) wifi_init: tcp mss: 1440 +I (1715) chip[DL]: NVS set: chip-counters/reboot-count = 5 (0x5) +I (1718) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1720) BLE_INIT: BT controller compile version [1bb2f50] +I (1725) BLE_INIT: Using main XTAL as clock source +I (1730) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1738) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1743) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +I (1786) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) +I (1803) NimBLE: GAP procedure initiated: stop advertising. + +I (1805) CHIP[DL]: BLE host-controller synced +I (2306) chip[DL]: Starting ESP WiFi layer +I (2308) wifi:mode : sta (a4:cb:8f:c9:46:f8) +I (2309) wifi:enable tsf +W (2309) wifi:Haven't to connect to a suitable AP now! +I (2310) chip[DL]: I (2310) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 +Done driving station state, nothing else to do... +I (2319) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 +W (2323) wifi:Haven't to connect to a suitable AP now! +I (2334) chip[DL]: Done driving station state, nothing else to do... +I (2340) chip[SVR]: Initializing subscription resumption storage... +I (2347) chip[SVR]: Server initializing... +I (2350) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 +I (2355) chip[DMG]: AccessControl: initializing +I (2359) chip[DMG]: Examples::AccessControlDelegate::Init +I (2364) chip[DMG]: AccessControl: setting +I (2368) chip[DMG]: DefaultAclStorage: initializing +I (2373) chip[DMG]: DefaultAclStorage: 0 entries loaded +I (2378) chip[SVR]: WARNING: mTestEventTriggerDelegate is null +I (2394) chip[ZCL]: Using ZAP configuration... +I (2395) esp_matter_cluster: Cluster plugin init common callback +I (2395) chip[DMG]: AccessControlCluster: initializing +I (2399) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported +I (2408) chip[DIS]: Updating services using commissioning mode 1 +I (2413) chip[DIS]: CHIP minimal mDNS started advertising. +I (2418) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +I (2428) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: DC7A32ADCF87492D. +I (2437) chip[DIS]: mDNS service published: _matterc._udp +I (2442) chip[IN]: CASE Server enabling CASE session setups +I (2448) chip[SVR]: Joining Multicast groups +I (2451) chip[SVR]: Server Listening... +I (2457) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** +I (2466) data_model: Dynamic endpoint 0 added +E (2470) data_model: Cluster cannot be NULL. +I (2474) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** +I (2485) data_model: Dynamic endpoint 1 added +E (2488) data_model: Cluster cannot be NULL. +I (2493) chip[DL]: I (2494) MAIN: ESP-Matter started successfully +WIFI_EVENT_STA_START +W (2501) wifi:Haven't to connect to a suitable AP now! +I (2512) chip[DL]: Done driving station state, nothing else to do... +I (2514) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) +I (2516) NimBLE: GAP procedure initiated: advertise; +I (2517) NimBLE: disc_mode=2 +I (2528) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 +I (2529) NimBLE: + +I (2531) chip[DL]: CHIPoBLE advertising started +I (2542) MAIN: Commissioning window opened +> +I (4063) I2C: Creating I2C mutex +I (14064) SPI: Creating SPI mutex +I (14106) INFLUX_BUFFER: SD card mounted successfully +I (14107) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully +I (14110) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (14124) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (14148) BME688: Delaying for 10000 microseconds +I (14766) Flicker: Flicker sensor initialized on pin 14 +I (14768) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14770) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +I (14770) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +W (14775) wifi:I (14771) SEN66: Creating sensor mutex +Haven't to connect to a suitable AP now!I (14786) SEN66: Waking up sensor by setting power GPIO to enabled level... + +I (14777) INFLUX_BUFFER: Flushing chunk starting at line 10: +I (14784) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +W (14772) MICROPHONE: Microphone is in shutdown state, skipping sample collection +I (14813) Flicker: Starting to collect samples in gather task +I (14800) DeviceStatus: Active tasks: 26 +I (14800) INFLUX: Writing to InfluxDB +E (14824) SENSOR_GATHERER: Failed to get line protocol from sensor +I (14843) SENSOR_GATHERER: Sensor should now be asleep +I (14830) DeviceStatus: Task list: +gather_sensor_t X 1 1228 22 +main R 1 876 4 +IDLE1 R 0 700 6 +IDLE0 R 0 804 5 +gather_task B 1 1756 19 +gather_task B 1 14032 20 +alive_diode_tas B 5 356 8 +reset_task B 0 3432 15 +tiT B 18 2424 10 +gather_sensor_t B 23 1720 24 +ota_task B 1 7516 18 +CHIP B 1 3708 12 +Tmr Svc B 1 1364 7 +ds1307_update B 1 800 17 +sys_evt B 20 2672 9 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +console B 5 648 16 +gather_sensor_t B 1 2284 23 +gather_sensor_t B 1 2324 25 +esp_timer S 22 3332 3 +wifi B 23 4248 11 +gather_sensor_t S 1 1344 26 +btController B 23 1952 13 +gather_sensor_t B 1 1496 21 +flicker_gather_ S 24 1948 27 +nimble_host B 21 1480 14 + +W (14919) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (15850) Flicker: Finished collecting samples, notifying main task +I (15852) Flicker: Collected 1024 samples for dominant frequency analysis +W (15859) AS7341: Reading spectral data from AS7341 +I (15860) SENSOR_GATHERER: Sensor should now be asleep +I (15869) BME688: Delaying for 199517 microseconds +W (15920) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (16167) AS7341: Reading basic counts data from AS7341 +W (16183) AS7341: Reading flicker detection state from AS7341 +W (16401) as7341: FD Status Register: 0x20 (0b00100000) +I (16407) AS7341: Updating Matter attributes with AS7341 data: 16495 +I (16408) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (16421) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16495 ********** +E (16434) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (16436) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (16448) AS7341: RTC is uninitialized, using current time as timestamp +I (16449) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16462) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1013 ********** +E (16475) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16487) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16488) BME688: RTC is uninitialized, using current time as timestamp +I (16921) SENSOR_GATHERER: Sensor should now be asleep +I (17272) SENSOR_GATHERER: Gathered line protocol: +device_status,device=A4CB8FC946F8 battery=4.04,last_restart=13410,status="OK",temp=31.40,rssi=0,wifi_status="disconnected",free_heap=54943,psram_free=8305944,heap_blocks=253752,psram_blocks=79976,active_tasks=26 946684801000000000 + +W (17450) AS7341: RTC is uninitialized, using current time as timestamp +W (17500) BME688: RTC is uninitialized, using current time as timestamp +W (18451) AS7341: RTC is uninitialized, using current time as timestamp +W (18501) BME688: RTC is uninitialized, using current time as timestamp +W (19452) AS7341: RTC is uninitialized, using current time as timestamp +W (19502) BME688: RTC is uninitialized, using current time as timestamp +I (19798) SEN66: Sent start measurement command, waiting for sensor to be ready... +W (20453) AS7341: RTC is uninitialized, using current time as timestamp +W (20503) BME688: RTC is uninitialized, using current time as timestamp +W (21454) AS7341: RTC is uninitialized, using current time as timestamp +W (21504) BME688: RTC is uninitialized, using current time as timestamp +E (21832) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (21834) esp-tls: Failed to open new connection +E (21835) transport_base: Failed to open a new connection +E (21846) HTTP_CLIENT: Connection failed, sock < 0 +I (21847) INFLUX: HTTP request performed, status code: 0 +E (21858) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (21860) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (22455) AS7341: RTC is uninitialized, using current time as timestamp +W (22505) BME688: RTC is uninitialized, using current time as timestamp +W (23456) AS7341: RTC is uninitialized, using current time as timestamp +W (23506) BME688: RTC is uninitialized, using current time as timestamp +I (23871) INFLUX: Writing to InfluxDB +W (24457) AS7341: RTC is uninitialized, using current time as timestamp +W (24507) BME688: RTC is uninitialized, using current time as timestamp +I (24770) OTA_CLIENT: Device is not registered, attempting to register... +I (24770) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +W (24774) wifi:W (24770) MICROPHONE: Microphone is in shutdown state, skipping sample collection +Haven't to connect to a suitable AP now! +E (24787) SENSOR_GATHERER: Failed to get line protocol from sensor +I (24773) OTA_CLIENT: HTTP client handle: 0x3fcf1b34 +I (24788) DeviceStatus: Active tasks: 26 +I (24799) SENSOR_GATHERER: Sensor should now be asleep +I (24804) DeviceStatus: Task list: +gather_sensor_t R 1 1496 21 +gather_sensor_t X 1 1148 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 +gather_sensor_t B 1 532 25 +alive_diode_tas B 5 356 8 +gather_sensor_t B 1 748 23 +reset_task B 0 3432 15 +CHIP B 1 3708 12 +gather_sensor_t B 23 1720 24 +Tmr Svc B 1 1364 7 +tiT B 18 2424 10 +console B 5 648 16 +ipc0 S 1 372 1 +gather_sensor_t S 1 1344 26 +sys_evt B 20 2672 9 +main B 1 876 4 +wifi B 23 4248 11 +esp_timer S 22 3332 3 +btController B 23 1952 13 +ota_task B 1 5356 18 +nimble_host B 21 1480 14 +ipc1 S 24 524 2 + +W (24879) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (25458) AS7341: RTC is uninitialized, using current time as timestamp +W (25508) BME688: RTC is uninitialized, using current time as timestamp +W (25880) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (26459) AS7341: RTC is uninitialized, using current time as timestamp +W (26509) BME688: RTC is uninitialized, using current time as timestamp +W (26881) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (27460) AS7341: RTC is uninitialized, using current time as timestamp +W (27510) BME688: RTC is uninitialized, using current time as timestamp +W (27882) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (28461) AS7341: RTC is uninitialized, using current time as timestamp +W (28511) BME688: RTC is uninitialized, using current time as timestamp +W (28883) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (29462) AS7341: RTC is uninitialized, using current time as timestamp +W (29512) BME688: RTC is uninitialized, using current time as timestamp +W (29884) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (30463) AS7341: RTC is uninitialized, using current time as timestamp +W (30513) BME688: RTC is uninitialized, using current time as timestamp +E (30872) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (30872) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (30874) esp-tls: Failed to open new connection +W (30885) DeviceStatus: RTC is uninitialized, using current time as timestamp +E (30886) esp-tls: Failed to open new connection +E (30887) transport_base: Failed to open a new connection +E (30910) HTTP_CLIENT: Connection failed, sock < 0 +E (30899) transport_base: Failed to open a new connection +E (30912) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223 +E (30924) OTA_CLIENT: Device registration failed: ESP_ERR_HTTP_CONNECT +E (30912) HTTP_CLIENT: Connection failed, sock < 0 +I (30937) INFLUX: HTTP request performed, status code: 0 +E (30938) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (30939) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (31464) AS7341: RTC is uninitialized, using current time as timestamp +W (31514) BME688: RTC is uninitialized, using current time as timestamp +W (31898) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (32409) chip[DL]: bleAdv Timeout : Start slow advertisement +I (32411) chip[DL]: Configuring CHIPoBLE advertising (interval 500 ms, connectable) +I (32413) chip[DL]: Device already advertising, stop active advertisement and restart +I (32425) NimBLE: GAP procedure initiated: stop advertising. + +I (32427) NimBLE: GAP procedure initiated: advertise; +I (32438) NimBLE: disc_mode=2 +I (32439) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=800 adv_itvl_max=800 +I (32451) NimBLE: + +W (32465) AS7341: RTC is uninitialized, using current time as timestamp +W (32515) BME688: RTC is uninitialized, using current time as timestamp +I (32775) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (32899) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (32951) INFLUX: Writing to InfluxDB +I (33276) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (33466) AS7341: RTC is uninitialized, using current time as timestamp +W (33516) BME688: RTC is uninitialized, using current time as timestamp +I (33777) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (33900) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (34278) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (34467) AS7341: RTC is uninitialized, using current time as timestamp +W (34517) BME688: RTC is uninitialized, using current time as timestamp +I (34779) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (34901) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (35280) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (35468) AS7341: RTC is uninitialized, using current time as timestamp +W (35518) BME688: RTC is uninitialized, using current time as timestamp +I (35781) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (35902) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (36282) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (36469) AS7341: RTC is uninitialized, using current time as timestamp +W (36519) BME688: RTC is uninitialized, using current time as timestamp +I (36783) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (36903) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (37284) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (37470) AS7341: RTC is uninitialized, using current time as timestamp +W (37520) BME688: RTC is uninitialized, using current time as timestamp +I (37785) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (37904) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (38286) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (38471) AS7341: RTC is uninitialized, using current time as timestamp +W (38521) BME688: RTC is uninitialized, using current time as timestamp +I (38787) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (38905) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (39288) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (39472) AS7341: RTC is uninitialized, using current time as timestamp +W (39522) BME688: RTC is uninitialized, using current time as timestamp +I (39789) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (39906) DeviceStatus: RTC is uninitialized, using current time as timestamp +E (39952) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (39954) esp-tls: Failed to open new connection +E (39955) transport_base: Failed to open a new connection +E (39966) HTTP_CLIENT: Connection failed, sock < 0 +I (39967) INFLUX: HTTP request performed, status code: 0 +E (39978) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (39980) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +I (40290) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (40473) AS7341: RTC is uninitialized, using current time as timestamp +W (40523) BME688: RTC is uninitialized, using current time as timestamp +I (40791) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (40907) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (40926) OTA_CLIENT: Device is not registered, attempting to register... +I (40928) OTA_CLIENT: HTTP client handle: 0x3fcf0434 +I (41292) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (41474) AS7341: RTC is uninitialized, using current time as timestamp +W (41524) BME688: RTC is uninitialized, using current time as timestamp +I (41793) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (41908) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (41991) INFLUX: Writing to InfluxDB +I (42294) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (42475) AS7341: RTC is uninitialized, using current time as timestamp +W (42525) BME688: RTC is uninitialized, using current time as timestamp +I (42795) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (42909) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (43296) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (43476) AS7341: RTC is uninitialized, using current time as timestamp +W (43526) BME688: RTC is uninitialized, using current time as timestamp +I (43797) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (43910) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (44298) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (44477) AS7341: RTC is uninitialized, using current time as timestamp +W (44527) BME688: RTC is uninitialized, using current time as timestamp +I (44799) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (44911) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (45300) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (45478) AS7341: RTC is uninitialized, using current time as timestamp +W (45528) BME688: RTC is uninitialized, using current time as timestamp +I (45801) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (45912) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (46302) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (46479) AS7341: RTC is uninitialized, using current time as timestamp +W (46529) BME688: RTC is uninitialized, using current time as timestamp +I (46803) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (46913) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (47304) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (47480) AS7341: RTC is uninitialized, using current time as timestamp +W (47530) BME688: RTC is uninitialized, using current time as timestamp +I (47805) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (47914) DeviceStatus: RTC is uninitialized, using current time as timestamp +E (47930) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (47932) esp-tls: Failed to open new connection +E (47932) transport_base: Failed to open a new connection +E (47944) HTTP_CLIENT: Connection failed, sock < 0 +E (47945) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223 +E (47957) OTA_CLIENT: Device registration failed: ESP_ERR_HTTP_CONNECT +I (48306) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (48481) AS7341: RTC is uninitialized, using current time as timestamp +W (48531) BME688: RTC is uninitialized, using current time as timestamp +I (48807) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (48915) DeviceStatus: RTC is uninitialized, using current time as timestamp +E (48930) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (48932) esp-tls: Failed to open new connection +E (48943) transport_base: Failed to open a new connection +E (48944) HTTP_CLIENT: Connection failed, sock < 0 +I (48945) INFLUX: HTTP request performed, status code: 0 +E (48956) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (48957) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +I (49308) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (49482) AS7341: RTC is uninitialized, using current time as timestamp +W (49532) BME688: RTC is uninitialized, using current time as timestamp +I (49809) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (49916) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (50310) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (50483) AS7341: RTC is uninitialized, using current time as timestamp +W (50533) BME688: RTC is uninitialized, using current time as timestamp +I (50811) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (50917) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (50969) INFLUX: Writing to InfluxDB +I (51312) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (51484) AS7341: RTC is uninitialized, using current time as timestamp +W (51534) BME688: RTC is uninitialized, using current time as timestamp +I (51813) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (51918) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (52314) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (52485) AS7341: RTC is uninitialized, using current time as timestamp +W (52535) BME688: RTC is uninitialized, using current time as timestamp +I (52815) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (52919) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (53316) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (53486) AS7341: RTC is uninitialized, using current time as timestamp +W (53536) BME688: RTC is uninitialized, using current time as timestamp +I (53817) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (53920) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (54318) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (54487) AS7341: RTC is uninitialized, using current time as timestamp +W (54537) BME688: RTC is uninitialized, using current time as timestamp +I (54819) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (54921) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (55320) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (55488) AS7341: RTC is uninitialized, using current time as timestamp +W (55538) BME688: RTC is uninitialized, using current time as timestamp +I (55821) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (55922) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (56322) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (56489) AS7341: RTC is uninitialized, using current time as timestamp +W (56539) BME688: RTC is uninitialized, using current time as timestamp +I (56823) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (56923) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (57324) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (57490) AS7341: RTC is uninitialized, using current time as timestamp +W (57540) BME688: RTC is uninitialized, using current time as timestamp +I (57825) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (57924) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (57958) OTA_CLIENT: Device is not registered, attempting to register... +I (57960) OTA_CLIENT: HTTP client handle: 0x3fcf1b34 +E (57970) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (57972) esp-tls: Failed to open new connection +E (57973) transport_base: Failed to open a new connection +E (57984) HTTP_CLIENT: Connection failed, sock < 0 +I (57985) INFLUX: HTTP request performed, status code: 0 +E (57996) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (57998) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +I (58326) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (58491) AS7341: RTC is uninitialized, using current time as timestamp +W (58541) BME688: RTC is uninitialized, using current time as timestamp +I (58827) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (58925) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (59328) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (59492) AS7341: RTC is uninitialized, using current time as timestamp +W (59542) BME688: RTC is uninitialized, using current time as timestamp +I (59829) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (59926) DeviceStatus: RTC is uninitialized, using current time as timestamp +E (60009) INFLUX_BUFFER: Failed to write chunk with 5 retries to Influx: ESP_FAIL +E (60011) MAIN: Failed to flush Influx buffer: ESP_FAIL +I (60330) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (60493) AS7341: RTC is uninitialized, using current time as timestamp +W (60543) BME688: RTC is uninitialized, using current time as timestamp +I (60831) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (60927) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (61332) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (61494) AS7341: RTC is uninitialized, using current time as timestamp +W (61544) BME688: RTC is uninitialized, using current time as timestamp +I (61833) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (61928) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (62334) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (62495) AS7341: RTC is uninitialized, using current time as timestamp +W (62545) BME688: RTC is uninitialized, using current time as timestamp +I (62835) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (62929) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (63336) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (63496) AS7341: RTC is uninitialized, using current time as timestamp +W (63546) BME688: RTC is uninitialized, using current time as timestamp +I (63837) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (63930) DeviceStatus: RTC is uninitialized, using current time as timestamp +E (63970) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (63972) esp-tls: Failed to open new connection +E (63972) transport_base: Failed to open a new connection +E (63984) HTTP_CLIENT: Connection failed, sock < 0 +E (63985) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223 +E (63997) OTA_CLIENT: Device registration failed: ESP_ERR_HTTP_CONNECT +I (64338) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (64497) AS7341: RTC is uninitialized, using current time as timestamp +W (64547) BME688: RTC is uninitialized, using current time as timestamp +I (64800) SEN66: First measurement, waiting additional 120 seconds for sensor to stabilize... +I (64839) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (64931) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (65340) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (65498) AS7341: RTC is uninitialized, using current time as timestamp +W (65548) BME688: RTC is uninitialized, using current time as timestamp +I (65841) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (65932) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (66342) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (66499) AS7341: RTC is uninitialized, using current time as timestamp +W (66549) BME688: RTC is uninitialized, using current time as timestamp +I (66843) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (66933) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (67344) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (67500) AS7341: RTC is uninitialized, using current time as timestamp +W (67550) BME688: RTC is uninitialized, using current time as timestamp +I (67845) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (67934) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (68346) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (68501) AS7341: RTC is uninitialized, using current time as timestamp +W (68551) BME688: RTC is uninitialized, using current time as timestamp +I (68847) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (68935) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (69348) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (69502) AS7341: RTC is uninitialized, using current time as timestamp +W (69552) BME688: RTC is uninitialized, using current time as timestamp +I (69849) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (69936) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (70350) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (70503) AS7341: RTC is uninitialized, using current time as timestamp +W (70553) BME688: RTC is uninitialized, using current time as timestamp +I (70851) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (70937) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (71352) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (71504) AS7341: RTC is uninitialized, using current time as timestamp +W (71554) BME688: RTC is uninitialized, using current time as timestamp +I (71853) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (71938) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (72354) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (72505) AS7341: RTC is uninitialized, using current time as timestamp +W (72555) BME688: RTC is uninitialized, using current time as timestamp +I (72855) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (72939) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (73356) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (73506) AS7341: RTC is uninitialized, using current time as timestamp +W (73556) BME688: RTC is uninitialized, using current time as timestamp +I (73857) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (73940) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (73998) OTA_CLIENT: Device is not registered, attempting to register... +I (74000) OTA_CLIENT: HTTP client handle: 0x3fcf0634 +I (74358) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (74507) AS7341: RTC is uninitialized, using current time as timestamp +W (74557) BME688: RTC is uninitialized, using current time as timestamp +I diff --git a/tests/fixtures/serial_log_20260917_162548.txt b/tests/fixtures/serial_log_20260917_162548.txt new file mode 100644 index 0000000..8c44e4b --- /dev/null +++ b/tests/fixtures/serial_log_20260917_162548.txt @@ -0,0 +1,514 @@ +I (469) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 sizeI (2298) chip[DL]: Starting ESP WiFi layer +I (2300) wifi:mode : sta (a4:cb:8f:c9:47:3c) +I (2301) wifi:enable tsf +W (2301) wifi:Haven't to connect to a suitable AP now! +I (2302) chip[DL]: I (2302) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 +Done driving station state, nothing else to do... +I (2311) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 +W (2315) wifi:Haven't to connect to a suitable AP now! +I (2326) chip[DL]: Done driving station state, nothing else to do... +I (2332) chip[SVR]: Initializing subscription resumption storage... +I (2339) chip[SVR]: Server initializing... +I (2342) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 +I (2347) chip[DMG]: AccessControl: initializing +I (2351) chip[DMG]: Examples::AccessControlDelegate::Init +I (2356) chip[DMG]: AccessControl: setting +I (2360) chip[DMG]: DefaultAclStorage: initializing +I (2365) chip[DMG]: DefaultAclStorage: 0 entries loaded +I (2370) chip[SVR]: WARNING: mTestEventTriggerDelegate is null +I (2386) chip[ZCL]: Using ZAP configuration... +I (2387) esp_matter_cluster: Cluster plugin init common callback +I (2388) chip[DMG]: AccessControlCluster: initializing +I (2391) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported +I (2401) chip[DIS]: Updating services using commissioning mode 1 +I (2405) chip[DIS]: CHIP minimal mDNS started advertising. +I (2410) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +I (2420) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 45D6D10CEB7D6CC4. +I (2430) chip[DIS]: mDNS service published: _matterc._udp +I (2434) chip[IN]: CASE Server enabling CASE session setups +I (2440) chip[SVR]: Joining Multicast groups +I (2443) chip[SVR]: Server Listening... +I (2449) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** +I (2458) data_model: Dynamic endpoint 0 added +E (2462) data_model: Cluster cannot be NULL. +I (2466) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** +I (2477) data_model: Dynamic endpoint 1 added +E (2480) data_model: Cluster cannot be NULL. +I (2485) chip[DL]: I (2486) MAIN: ESP-Matter started successfully +WIFI_EVENT_STA_START +W (2493) wifi:Haven't to connect to a suitable AP now! +I (2504) chip[DL]: Done driving station state, nothing else to do... +I (2506) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) +I (2508) NimBLE: GAP procedure initiated: advertise; +I (2509) NimBLE: disc_mode=2 +I (2520) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 +I (2522) NimBLE: + +I (2523) chip[DL]: CHIPoBLE advertising started +I (2535) MAIN: Commissioning window opened +> I (4056) I2C: Creating I2C mutex +I (14057) SPI: Creating SPI mutex +I (14110) INFLUX_BUFFER: SD card mounted successfully +I (14111) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully +I (14114) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (14128) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (14152) BME688: Delaying for 10000 microseconds +I (14770) Flicker: Flicker sensor initialized on pin 14 +I (14772) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14774) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +W (14778) wifi:I (14776) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +Haven't to connect to a suitable AP now!I (14777) SEN66: Creating sensor mutex + +I (14800) SEN66: Waking up sensor by setting power GPIO to enabled level... +I (14780) INFLUX_BUFFER: Flushing chunk starting at line 3: +I (14778) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (14802) DeviceStatus: Active tasks: 26 +I (14815) Flicker: Starting to collect samples in gather task +I (14832) DeviceStatus: Task list: +gather_sensor_t X 1 1136 21 +main R 1 876 4 +IDLE0 R 0 692 5 +IDLE1 R 0 700 6 +ds1307_update B 1 804 17 +gather_task B 1 1744 19 +gather_task B 1 14228 22 +alive_diode_tas B 5 356 8 +reset_task B 0 3436 15 +tiT B 18 2424 10 +gather_sensor_t B 1 1688 24 +ota_task B 1 7504 18 +CHIP B 1 3708 12 +Tmr Svc B 1 1364 7 +gather_sensor_t B 1 2552 20 +sys_evt B 20 2672 9 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +console B 5 652 16 +gather_sensor_t B 1 3172 25 +gather_sensor_t B 1 2332 23 +esp_timer S 22 3332 3 +wifi B 23 4248 11 +gather_sensor_t S 1 1344 26 +btController B 23 1952 13 +flicker_gather_ S 24 1948 27 +nimble_host B 21 1484 14 + +W (14909) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (15853) Flicker: Finished collecting samples, notifying main task +I (15855) Flicker: Collected 1024 samples for dominant frequency analysis +I (15862) SENSOR_GATHERER: Sensor should now be asleep +W (15873) AS7341: Reading spectral data from AS7341 +I (15882) BME688: Delaying for 199517 microseconds +W (15911) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (16175) AS7341: Reading basic counts data from AS7341 +W (16191) AS7341: Reading flicker detection state from AS7341 +W (16409) as7341: FD Status Register: 0x20 (0b00100000) +I (16415) AS7341: Updating Matter attributes with AS7341 data: 22779 +I (16417) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (16429) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 22779 ********** +E (16432) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (16444) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +I (16457) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +W (16458) AS7341: RTC is uninitialized, using current time as timestamp +I (16469) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1004 ********** +E (16484) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16486) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16498) BME688: RTC is uninitialized, using current time as timestamp +W (16913) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (17471) AS7341: RTC is uninitialized, using current time as timestamp +W (17510) BME688: RTC is uninitialized, using current time as timestamp +W (17915) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (18472) AS7341: RTC is uninitialized, using current time as timestamp +W (18511) BME688: RTC is uninitialized, using current time as timestamp +W (18917) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (19473) AS7341: RTC is uninitialized, using current time as timestamp +W (19512) BME688: RTC is uninitialized, using current time as timestamp +I (19802) SEN66: Sent start measurement command, waiting for sensor to be ready... +I (19911) MICROPHONE: Read 300000 bytes in 4936 ms +I (19912) MICROPHONE: Read 300000 bytes of audio data +W (19919) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (20020) MICROPHONE: Sum SQ: 0.000000, RMS pressure: 0.000000, Normalized: 0.000000, dB peak: -inf +W (20022) SEN66: RTC is uninitialized, using current time as timestamp +W (20474) AS7341: RTC is uninitialized, using current time as timestamp +W (20513) BME688: RTC is uninitialized, using current time as timestamp +W (20920) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (21034) SEN66: RTC is uninitialized, using current time as timestamp +W (21475) AS7341: RTC is uninitialized, using current time as timestamp +W (21514) BME688: RTC is uninitialized, using current time as timestamp +E (21832) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (21834) esp-tls: Failed to open new connection +E (21835) transport_base: Failed to open a new connection +E (21846) HTTP_CLIENT: Connection failed, sock < 0 +I (21847) INFLUX: HTTP request performed, status code: 0 +E (21859) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (21861) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (21922) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (22035) SEN66: RTC is uninitialized, using current time as timestamp +W (22476) AS7341: RTC is uninitialized, using current time as timestamp +W (22515) BME688: RTC is uninitialized, using current time as timestamp +I (22786) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (22924) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (23036) SEN66: RTC is uninitialized, using current time as timestamp +I (23287) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (23477) AS7341: RTC is uninitialized, using current time as timestamp +W (23516) BME688: RTC is uninitialized, using current time as timestamp +I (23788) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (23872) INFLUX: Writing to InfluxDB +W (23926) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (24037) SEN66: RTC is uninitialized, using current time as timestamp +I (24289) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (24478) AS7341: RTC is uninitialized, using current time as timestamp +W (24517) BME688: RTC is uninitialized, using current time as timestamp +I (24774) OTA_CLIENT: Device is not registered, attempting to register... +I (24777) OTA_CLIENT: HTTP client handle: 0x3fcf0bc0 +I (24790) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (24928) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (25038) SEN66: RTC is uninitialized, using current time as timestamp +I (25291) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (25479) AS7341: RTC is uninitialized, using current time as timestamp +W (25518) BME688: RTC is uninitialized, using current time as timestamp +I (25792) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (25930) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (26039) SEN66: RTC is uninitialized, using current time as timestamp +I (26293) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (26480) AS7341: RTC is uninitialized, using current time as timestamp +W (26519) BME688: RTC is uninitialized, using current time as timestamp +I (26794) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (26932) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (27040) SEN66: RTC is uninitialized, using current time as timestamp +I (27295) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (27481) AS7341: RTC is uninitialized, using current time as timestamp +W (27520) BME688: RTC is uninitialized, using current time as timestamp +I (27796) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (27934) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (28041) SEN66: RTC is uninitialized, using current time as timestamp +I (28297) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (28482) AS7341: RTC is uninitialized, using current time as timestamp +W (28521) BME688: RTC is uninitialized, using current time as timestamp +I (28798) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (28936) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (29042) SEN66: RTC is uninitialized, using current time as timestamp +I (29299) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (29483) AS7341: RTC is uninitialized, using current time as timestamp +W (29522) BME688: RTC is uninitialized, using current time as timestamp +I (29800) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (29938) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (30043) SEN66: RTC is uninitialized, using current time as timestamp +I (30301) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (30484) AS7341: RTC is uninitialized, using current time as timestamp +W (30523) BME688: RTC is uninitialized, using current time as timestamp +I (30802) SENSOR_GATHERER: 2/2 tasks still running, waiting... +E (30873) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (30873) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (30887) esp-tls: Failed to open new connection +E (30875) esp-tls: Failed to open new connection +E (30888) transport_base: Failed to open a new connection +E (30889) transport_base: Failed to open a new connection +E (30900) HTTP_CLIENT: Connection failed, sock < 0 +E (30901) HTTP_CLIENT: Connection failed, sock < 0 +I (30913) INFLUX: HTTP request performed, status code: 0 +E (30913) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223 +E (30914) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (30926) OTA_CLIENT: Device registration failed: ESP_ERR_HTTP_CONNECT +E (30927) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (30940) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (31044) SEN66: RTC is uninitialized, using current time as timestamp +I (31303) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (31485) AS7341: RTC is uninitialized, using current time as timestamp +W (31524) BME688: RTC is uninitialized, using current time as timestamp +I (31804) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (31952) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (32045) SEN66: RTC is uninitialized, using current time as timestamp +I (32305) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (32401) chip[DL]: bleAdv Timeout : Start slow advertisement +I (32403) chip[DL]: Configuring CHIPoBLE advertising (interval 500 ms, connectable) +I (32406) chip[DL]: Device already advertising, stop active advertisement and restart +I (32418) NimBLE: GAP procedure initiated: stop advertising. + +I (32419) NimBLE: GAP procedure initiated: advertise; +I (32431) NimBLE: disc_mode=2 +I (32431) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=800 adv_itvl_max=800 +I (32434) NimBLE: + +W (32486) AS7341: RTC is uninitialized, using current time as timestamp +W (32525) BME688: RTC is uninitialized, using current time as timestamp +I (32806) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (32940) INFLUX: Writing to InfluxDB +W (32954) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (33046) SEN66: RTC is uninitialized, using current time as timestamp +I (33307) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (33487) AS7341: RTC is uninitialized, using current time as timestamp +W (33526) BME688: RTC is uninitialized, using current time as timestamp +I (33808) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (33956) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (34047) SEN66: RTC is uninitialized, using current time as timestamp +I (34309) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (34488) AS7341: RTC is uninitialized, using current time as timestamp +W (34527) BME688: RTC is uninitialized, using current time as timestamp +I (34810) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (34958) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (35048) SEN66: RTC is uninitialized, using current time as timestamp +I (35311) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (35489) AS7341: RTC is uninitialized, using current time as timestamp +W (35528) BME688: RTC is uninitialized, using current time as timestamp +I (35812) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (35960) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (36049) SEN66: RTC is uninitialized, using current time as timestamp +I (36313) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (36490) AS7341: RTC is uninitialized, using current time as timestamp +W (36529) BME688: RTC is uninitialized, using current time as timestamp +I (36814) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (36962) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (37050) SEN66: RTC is uninitialized, using current time as timestamp +I (37315) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (37491) AS7341: RTC is uninitialized, using current time as timestamp +W (37530) BME688: RTC is uninitialized, using current time as timestamp +I (37816) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (37964) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (38051) SEN66: RTC is uninitialized, using current time as timestamp +I (38317) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (38492) AS7341: RTC is uninitialized, using current time as timestamp +W (38531) BME688: RTC is uninitialized, using current time as timestamp +I (38818) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (38966) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (39052) SEN66: RTC is uninitialized, using current time as timestamp +I (39319) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (39493) AS7341: RTC is uninitialized, using current time as timestamp +W (39532) BME688: RTC is uninitialized, using current time as timestamp +I (39820) SENSOR_GATHERER: 2/2 tasks still running, waiting... +E (39941) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (39943) esp-tls: Failed to open new connection +E (39944) transport_base: Failed to open a new connection +E (39956) HTTP_CLIENT: Connection failed, sock < 0 +I (39957) INFLUX: HTTP request performed, status code: 0 +E (39968) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +W (39968) DeviceStatus: RTC is uninitialized, using current time as timestamp +E (39969) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (40053) SEN66: RTC is uninitialized, using current time as timestamp +I (40321) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (40494) AS7341: RTC is uninitialized, using current time as timestamp +W (40533) BME688: RTC is uninitialized, using current time as timestamp +I (40822) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (40939) OTA_CLIENT: Device is not registered, attempting to register... +I (40942) OTA_CLIENT: HTTP client handle: 0x3fcf03a8 +W (40981) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (41054) SEN66: RTC is uninitialized, using current time as timestamp +I (41323) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (41495) AS7341: RTC is uninitialized, using current time as timestamp +W (41534) BME688: RTC is uninitialized, using current time as timestamp +I (41824) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (41982) INFLUX: Writing to InfluxDB +W (41983) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (42055) SEN66: RTC is uninitialized, using current time as timestamp +I (42325) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (42496) AS7341: RTC is uninitialized, using current time as timestamp +W (42535) BME688: RTC is uninitialized, using current time as timestamp +I (42826) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (42985) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (43056) SEN66: RTC is uninitialized, using current time as timestamp +I (43327) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (43497) AS7341: RTC is uninitialized, using current time as timestamp +W (43536) BME688: RTC is uninitialized, using current time as timestamp +I (43828) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (43987) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (44057) SEN66: RTC is uninitialized, using current time as timestamp +I (44329) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (44498) AS7341: RTC is uninitialized, using current time as timestamp +W (44537) BME688: RTC is uninitialized, using current time as timestamp +I (44830) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (44989) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (45058) SEN66: RTC is uninitialized, using current time as timestamp +I (45331) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (45499) AS7341: RTC is uninitialized, using current time as timestamp +W (45538) BME688: RTC is uninitialized, using current time as timestamp +I (45832) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (45991) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (46059) SEN66: RTC is uninitialized, using current time as timestamp +I (46333) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (46500) AS7341: RTC is uninitialized, using current time as timestamp +W (46539) BME688: RTC is uninitialized, using current time as timestamp +I (46834) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (46993) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (47060) SEN66: RTC is uninitialized, using current time as timestamp +I (47335) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (47501) AS7341: RTC is uninitialized, using current time as timestamp +W (47540) BME688: RTC is uninitialized, using current time as timestamp +I (47836) SENSOR_GATHERER: 2/2 tasks still running, waiting... +E (47944) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (47946) esp-tls: Failed to open new connection +E (47947) transport_base: Failed to open a new connection +E (47958) HTTP_CLIENT: Connection failed, sock < 0 +E (47959) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223 +E (47972) OTA_CLIENT: Device registration failed: ESP_ERR_HTTP_CONNECT +W (47995) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (48061) SEN66: RTC is uninitialized, using current time as timestamp +I (48337) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (48502) AS7341: RTC is uninitialized, using current time as timestamp +W (48541) BME688: RTC is uninitialized, using current time as timestamp +I (48838) SENSOR_GATHERER: 2/2 tasks still running, waiting... +E (48944) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (48946) esp-tls: Failed to open new connection +E (48947) transport_base: Failed to open a new connection +E (48958) HTTP_CLIENT: Connection failed, sock < 0 +I (48960) INFLUX: HTTP request performed, status code: 0 +E (48971) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (48972) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (48997) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (49062) SEN66: RTC is uninitialized, using current time as timestamp +I (49339) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (49503) AS7341: RTC is uninitialized, using current time as timestamp +W (49542) BME688: RTC is uninitialized, using current time as timestamp +I (49840) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (49999) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (50063) SEN66: RTC is uninitialized, using current time as timestamp +I (50341) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (50504) AS7341: RTC is uninitialized, using current time as timestamp +W (50543) BME688: RTC is uninitialized, using current time as timestamp +I (50842) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (50984) INFLUX: Writing to InfluxDB +W (51001) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (51064) SEN66: RTC is uninitialized, using current time as timestamp +I (51343) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (51505) AS7341: RTC is uninitialized, using current time as timestamp +W (51544) BME688: RTC is uninitialized, using current time as timestamp +I (51844) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (52003) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (52065) SEN66: RTC is uninitialized, using current time as timestamp +I (52345) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (52506) AS7341: RTC is uninitialized, using current time as timestamp +W (52545) BME688: RTC is uninitialized, using current time as timestamp +I (52846) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (53005) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (53066) SEN66: RTC is uninitialized, using current time as timestamp +I (53347) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (53507) AS7341: RTC is uninitialized, using current time as timestamp +W (53546) BME688: RTC is uninitialized, using current time as timestamp +I (53848) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (54007) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (54067) SEN66: RTC is uninitialized, using current time as timestamp +I (54349) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (54508) AS7341: RTC is uninitialized, using current time as timestamp +W (54547) BME688: RTC is uninitialized, using current time as timestamp +I (54850) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (55009) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (55068) SEN66: RTC is uninitialized, using current time as timestamp +I (55351) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (55509) AS7341: RTC is uninitialized, using current time as timestamp +W (55548) BME688: RTC is uninitialized, using current time as timestamp +I (55852) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (56011) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (56069) SEN66: RTC is uninitialized, using current time as timestamp +I (56353) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (56510) AS7341: RTC is uninitialized, using current time as timestamp +W (56549) BME688: RTC is uninitialized, using current time as timestamp +I (56854) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (57013) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (57070) SEN66: RTC is uninitialized, using current time as timestamp +I (57355) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (57511) AS7341: RTC is uninitialized, using current time as timestamp +W (57550) BME688: RTC is uninitialized, using current time as timestamp +I (57856) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (57974) OTA_CLIENT: Device is not registered, attempting to register... +I (57977) OTA_CLIENT: HTTP client handle: 0x3fcf0b98 +E (57985) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (57987) esp-tls: Failed to open new connection +E (57988) transport_base: Failed to open a new connection +E (57999) HTTP_CLIENT: Connection failed, sock < 0 +I (58000) INFLUX: HTTP request performed, status code: 0 +E (58012) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (58013) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (58015) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (58071) SEN66: RTC is uninitialized, using current time as timestamp +I (58357) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (58512) AS7341: RTC is uninitialized, using current time as timestamp +W (58551) BME688: RTC is uninitialized, using current time as timestamp +I (58858) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (59026) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (59072) SEN66: RTC is uninitialized, using current time as timestamp +I (59359) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (59513) AS7341: RTC is uninitialized, using current time as timestamp +W (59552) BME688: RTC is uninitialized, using current time as timestamp +I (59860) SENSOR_GATHERER: 2/2 tasks still running, waiting... +E (60024) INFLUX_BUFFER: Failed to write chunk with 5 retries to Influx: ESP_FAIL +E (60026) MAIN: Failed to flush Influx buffer: ESP_FAIL +W (60028) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (60073) SEN66: RTC is uninitialized, using current time as timestamp +I (60361) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (60514) AS7341: RTC is uninitialized, using current time as timestamp +W (60553) BME688: RTC is uninitialized, using current time as timestamp +I (60862) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (61040) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (61074) SEN66: RTC is uninitialized, using current time as timestamp +I (61363) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (61515) AS7341: RTC is uninitialized, using current time as timestamp +W (61554) BME688: RTC is uninitialized, using current time as timestamp +I (61864) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (62042) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (62075) SEN66: RTC is uninitialized, using current time as timestamp +I (62365) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (62516) AS7341: RTC is uninitialized, using current time as timestamp +W (62555) BME688: RTC is uninitialized, using current time as timestamp +I (62866) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (63044) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (63076) SEN66: RTC is uninitialized, using current time as timestamp +I (63367) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (63517) AS7341: RTC is uninitialized, using current time as timestamp +W (63556) BME688: RTC is uninitialized, using current time as timestamp +I (63868) SENSOR_GATHERER: 2/2 tasks still running, waiting... +E (63985) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (63987) esp-tls: Failed to open new connection +E (63988) transport_base: Failed to open a new connection +E (63999) HTTP_CLIENT: Connection failed, sock < 0 +E (64000) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223 +E (64012) OTA_CLIENT: Device registration failed: ESP_ERR_HTTP_CONNECT +W (64046) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (64077) SEN66: RTC is uninitialized, using current time as timestamp +I (64369) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (64518) AS7341: RTC is uninitialized, using current time as timestamp +W (64557) BME688: RTC is uninitialized, using current time as timestamp +I (64804) SEN66: First measurement, waiting additional 120 seconds for sensor to stabilize... +I (64870) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (65048) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (65078) SEN66: RTC is uninitialized, using current time as timestamp +I (65371) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (65519) AS7341: RTC is uninitialized, using current time as timestamp +W (65558) BME688: RTC is uninitialized, using current time as timestamp +I (65872) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (66050) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (66079) SEN66: RTC is uninitialized, using current time as timestamp +I (66373) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (66520) AS7341: RTC is uninitialized, using current time as timestamp +W (66559) BME688: RTC is uninitialized, using current time as timestamp +I (66874) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (67052) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (67080) SEN66: RTC is uninitialized, using current time as timestamp +I (67375) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (67521) AS7341: RTC is uninitialized, using current time as timestamp +W (67560) BME688: RTC is uninitialized, using current time as timestamp +I (67876) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (68054) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (68081) SEN66: RTC is uninitialized, using current time as timestamp +I (68377) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (68522) AS7341: RTC is uninitialized, using current time as timestamp +W (68561) BME688: RTC is uninitialized, using current time as timestamp +I (68878) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (69056) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (69082) SEN66: RTC is uninitialized, using current time as timestamp +I (69379) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (69523) AS7341: RTC is uninitialized, using current time as timestamp +W (69562) BME688: RTC is uninitialized, using current time as timestamp +I (69880) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (70058) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (70083) SEN66: RTC is uninitialized, using current time as timestamp +I (70381) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (70524) AS7341: RTC is uninitialized, using current time as timestamp +W (70563) BME688: RTC is uninitialized, using current time as timestamp +I (70882) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (71060) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (71084) SEN66: RTC is uninitialized, using current time as timestamp +I (71383) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (71525) AS7341: RTC is uninitialized, using current time as timestamp +W (71564) BME688: RTC is uninitialized, using current time as timestamp +I (71884) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (72062) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (72085) SEN66: RTC is uninitialized, using current time as timestamp diff --git a/tests/fixtures/serial_log_20260917_162714.txt b/tests/fixtures/serial_log_20260917_162714.txt new file mode 100644 index 0000000..7c53e71 --- /dev/null +++ b/tests/fixtures/serial_log_20260917_162714.txt @@ -0,0 +1,139 @@ +I (16394) Flicker: Finished collecting samples, notifying main task +I (16396) Flicker: Collected 1024 samples for dominant frequency analysis +I (16404) SENSOR_GATHERER: Sensor should now be asleep +I (16415) SENSOR_GATHERER: Sensor should now be asleep +I (16455) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16458) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1004 ********** +E (16470) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16482) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16484) BME688: RTC is uninitialized, using current time as timestamp +I (16915) SENSOR_GATHERER: Sensor should now be asleep +W (17496) BME688: RTC is uninitialized, using current time as timestamp +W (18497) BME688: RTC is uninitialized, using current time as timestamp +W (19498) BME688: RTC is uninitialized, using current time as timestamp +I (19830) SEN66: Sent start measurement command, waiting for sensor to be ready... +I (19927) MICROPHONE: Read 300000 bytes in 4936 ms +I (19928) MICROPHONE: Read 300000 bytes of audio data +I (20036) MICROPHONE: Sum SQ: 0.000000, RMS pressure: 0.000000, Normalized: 0.000000, dB peak: -inf +W (20038) SEN66: RTC is uninitialized, using current time as timestamp +W (20499) BME688: RTC is uninitialized, using current time as timestamp +W (21050) SEN66: RTC is uninitialized, using current time as timestamp +W (21500) BME688: RTC is uninitialized, using current time as timestamp +E (21837) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (21839) esp-tls: Failed to open new connection +E (21840) transport_base: Failed to open a new connection +E (21851) HTTP_CLIENT: Connection failed, sock < 0 +I (21852) INFLUX: HTTP request performed, status code: 0 +E (21863) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (21865) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (22051) SEN66: RTC is uninitialized, using current time as timestamp +W (22501) BME688: RTC is uninitialized, using current time as timestamp +I (22792) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (23052) SEN66: RTC is uninitialized, using current time as timestamp +I (23293) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (23502) BME688: RTC is uninitialized, using current time as timestamp +I (23794) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (23876) INFLUX: Writing to InfluxDB +W (24053) SEN66: RTC is uninitialized, using current time as timestamp +I (24295) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (24503) BME688: RTC is uninitialized, using current time as timestamp +I (24789) OTA_CLIENT: Device is not registered, attempting to register... +I (24792) OTA_CLIENT: HTTP client handle: 0x3fcf1560 +I (24796) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (25054) SEN66: RTC is uninitialized, using current time as timestamp +I (25297) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (25504) BME688: RTC is uninitialized, using current time as timestamp +I (25798) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (26055) SEN66: RTC is uninitialized, using current time as timestamp +I (26299) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (26505) BME688: RTC is uninitialized, using current time as timestamp +I (26800) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (27056) SEN66: RTC is uninitialized, using current time as timestamp +I (27301) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (27506) BME688: RTC is uninitialized, using current time as timestamp +I (27802) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (28057) SEN66: RTC is uninitialized, using current time as timestamp +I (28303) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (28507) BME688: RTC is uninitialized, using current time as timestamp +I (28804) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (29058) SEN66: RTC is uninitialized, using current time as timestamp +I (29305) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (29508) BME688: RTC is uninitialized, using current time as timestamp +I (29806) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (30059) SEN66: RTC is uninitialized, using current time as timestamp +I (30307) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (30509) BME688: RTC is uninitialized, using current time as timestamp +I (30808) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (30877) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (30877) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (30879) esp-tls: Failed to open new connection +E (30891) esp-tls: Failed to open new connection +E (30892) transport_base: Failed to open a new connection +E (30903) transport_base: Failed to open a new connection +E (30904) HTTP_CLIENT: Connection failed, sock < 0 +E (30905) HTTP_CLIENT: Connection failed, sock < 0 +E (30917) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223 +E (30929) OTA_CLIENT: Device registration failed: ESP_ERR_HTTP_CONNECT +I (30917) INFLUX: HTTP request performed, status code: 0 +E (30932) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (30944) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (31060) SEN66: RTC is uninitialized, using current time as timestamp +I (31309) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (31510) BME688: RTC is uninitialized, using current time as timestamp +I (31810) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (32061) SEN66: RTC is uninitialized, using current time as timestamp +I (32311) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (32404) chip[DL]: bleAdv Timeout : Start slow advertisement +I (32406) chip[DL]: Configuring CHIPoBLE advertising (interval 500 ms, connectable) +I (32408) chip[DL]: Device already advertising, stop active advertisement and restart +I (32420) NimBLE: GAP procedure initiated: stop advertising. + +I (32422) NimBLE: GAP procedure initiated: advertise; +I (32434) NimBLE: disc_mode=2 +I (32434) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=800 adv_itvl_max=800 +I (32436) NimBLE: + +W (32511) BME688: RTC is uninitialized, using current time as timestamp +I (32812) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (32945) INFLUX: Writing to InfluxDB +W (33062) SEN66: RTC is uninitialized, using current time as timestamp +I (33313) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (33512) BME688: RTC is uninitialized, using current time as timestamp +I (33814) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (34063) SEN66: RTC is uninitialized, using current time as timestamp +I (34315) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (34513) BME688: RTC is uninitialized, using current time as timestamp +I (34816) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (35064) SEN66: RTC is uninitialized, using current time as timestamp +I (35317) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (35514) BME688: RTC is uninitialized, using current time as timestamp +I (35818) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (36065) SEN66: RTC is uninitialized, using current time as timestamp +I (36319) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (36515) BME688: RTC is uninitialized, using current time as timestamp +I (36820) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (37066) SEN66: RTC is uninitialized, using current time as timestamp +I (37321) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (37516) BME688: RTC is uninitialized, using current time as timestamp +I (37822) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (38067) SEN66: RTC is uninitialized, using current time as timestamp +I (38323) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (38517) BME688: RTC is uninitialized, using current time as timestamp +I (38824) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (39068) SEN66: RTC is uninitialized, using current time as timestamp +I (39325) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (39518) BME688: RTC is uninitialized, using current time as timestamp +I (39826) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (39946) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (39948) esp-tls: Failed to open new connection +E (39949) transport_base: Failed to open a new connection +E (39960) HTTP_CLIENT: Connection failed, sock < 0 +I (39961) INFLUX: HTTP request performed, status code: 0 +E (39972) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (39974) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (40069) SEN66: RTC is uninitialized, using current time as timestamp +I (40327) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (40519) BME688: RTC is uninitialized, using current time as timestamp +I (40828) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (40931) OTA_CLIENT: Device is not registered, attempting to register... +I (40934) OTA_CLIENT: HTTP client handle: 0x3fcf05b0 diff --git a/tests/fixtures/serial_log_20260917_162922.txt b/tests/fixtures/serial_log_20260917_162922.txt new file mode 100644 index 0000000..68898d9 --- /dev/null +++ b/tests/fixtures/serial_log_20260917_162922.txt @@ -0,0 +1,69 @@ +I (483) esp_image: segment 4: paddr=00533a74 vaddr=40378f08 sizeI (10222) SNTP: Time synchronized: Thu Sep 17 14:29:22 2026 + +I (10223) SNTP: RTC time set to: Thu Sep 17 14:29:22 2026 + +I (13664) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (13667) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (13687) BME688: Delaying for 10000 microseconds +I (14305) Flicker: Flicker sensor initialized on pin 14 +I (14306) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14307) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +I (14307) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +I (14308) SEN66: Creating sensor mutex +I (14320) SEN66: Waking up sensor by setting power GPIO to enabled level... +I (14314) DeviceStatus: Active tasks: 23 +I (14311) INFLUX_BUFFER: Buffer is empty, nothing to flush +W (14324) AS7341: Reading spectral data from AS7341 +I (14334) DeviceStatus: Task list: +gather_sensor_t X 1 1404 23 +main R 1 1124 4 +IDLE1 R 0 692 6 +IDLE0 R 0 700 5 +gather_sensor_t B 1 2552 22 +reset_task B 0 3348 9 +ds1307_update B 1 812 18 +tiT B 18 1276 11 +gather_task B 1 1992 20 +gather_task B 1 14280 21 +gather_sensor_t B 1 1920 25 +ota_task B 1 7508 19 +Tmr Svc B 1 1372 7 +alive_diode_tas B 5 364 8 +ipc1 S 24 516 2 +sys_evt B 20 2620 10 +wifi B 23 3388 12 +esp_timer S 22 3132 3 +gather_sensor_t B 1 2324 24 +gather_sensor_t B 1 1468 26 +gather_sensor_t B 1 2552 27 +CHIP B 1 4508 13 +ipc0 S 1 380 1 + +I (14404) SENSOR_GATHERER: Sensor should now be asleep +W (14682) AS7341: Reading basic counts data from AS7341 +W (14698) AS7341: Reading flicker detection state from AS7341 +W (14916) as7341: FD Status Register: 0x20 (0b00100000) +I (14922) AS7341: Updating Matter attributes with AS7341 data: 22293 +I (14922) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (14923) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (14929) Flicker: Starting to collect samples in gather task +I (14939) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 22293 ********** +I (14932) BME688: Delaying for 199517 microseconds +E (14956) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (14969) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (14977) AS7341: Formatting AS7341 data in line protocol +AS7341 line protocol: +as7341_adc,device=14C19F57720C f1=3,f2=8,f3=13,f4=17,f5=19,f6=22,f7=31,f8=34,clear=76,nir=17 1789655366000000000 +as7341_basic_counts,device=14C19F57720C f1=6.69,f2=17.85,f3=29.00,f4=37.93,f5=42.39,f6=49.08,f7=69.16,f8=75.85,clear=169.55,nir=37.93 1789655366000000000 +as7341_flicker,device=14C19F57720C state="unknown" 1789655366000000000 +I (15967) Flicker: Finished collecting samples, notifying main task +I (15968) Flicker: Collected 1024 samples for dominant frequency analysis +I (15976) SENSOR_GATHERER: Sensor should now be asleep +I (15987) SENSOR_GATHERER: Sensor should now be asleep +I (16027) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16028) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1005 ********** +E (16039) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16047) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +I (16056) BME688: Formatted line protocol: bme688,device=14C19F57720C temperature=24.18,pressure=100450.20,humidity=48.42 1789655367000000000 +I (16068) SENSOR_GATHERER: Sensor should now be asleep +I (19327) SEN66: Sent start measurement command, waiting for sensor to be ready... diff --git a/tests/test_logline.py b/tests/test_logline.py new file mode 100644 index 0000000..7836da0 --- /dev/null +++ b/tests/test_logline.py @@ -0,0 +1,84 @@ +from pathlib import Path + +import pytest + +from kota_tech.core.logline import LineFramer, LineKind, parse_line + +FIXTURES = sorted((Path(__file__).parent / "fixtures").glob("*.txt")) + + +def test_parse_esp_log_line(): + ln = parse_line("E (21863) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT") + assert ln.kind == LineKind.LOG + assert (ln.level, ln.uptime_ms, ln.tag) == ("E", 21863, "INFLUX") + assert ln.message.startswith("Failed to perform HTTP request") + + +def test_ansi_colour_stripped(): + ln = parse_line("\x1b[0;33mW (14131) INFLUX: Initializing Influx client\x1b[0m\r") + assert ln.text == "W (14131) INFLUX: Initializing Influx client" + assert ln.level == "W" + + +def test_boot_and_reset_markers(): + assert parse_line("ESP-ROM:esp32s3-20210327").kind == LineKind.BOOT + r = parse_line("rst:0xc (RTC_SW_CPU_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)") + assert r.kind == LineKind.RESET and r.reset_reason == "RTC_SW_CPU_RST" + assert parse_line("abort() was called at PC 0x42008e1b on core 0").kind == LineKind.PANIC + + +def test_non_log_lines_are_raw(): + assert parse_line("gather_task \tB\t1\t1992\t20").kind == LineKind.RAW + + +def test_framer_splits_and_idle_flushes(): + f = LineFramer(idle_flush_s=0.3) + assert f.feed(b"I (1) A: one\r\nI (2) B: tw", now=0.0) == ["I (1) A: one\r"] + assert f.poll_idle(now=0.1) == [] + assert f.poll_idle(now=0.5) == ["I (2) B: tw"] + + +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"] + + +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"] + + +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_tolerates_invalid_utf8(): + f = LineFramer() + assert f.feed(b"\xff\xfeI (1) X: y\n") == ["��I (1) X: y"] + + +@pytest.mark.parametrize("path", FIXTURES, ids=lambda p: p.name) +def test_replay_sample_log_chunked_equals_whole(path): + data = path.read_bytes() + whole = LineFramer().feed(data) + f = LineFramer() + chunked = [] + for i in range(0, len(data), 37): # odd chunk size splits lines and UTF-8 + chunked += f.feed(data[i : i + 37]) + chunked += f.flush() + tail = LineFramer() + tail.feed(data) + assert chunked == whole + tail.flush() + + +@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 (")) + assert sum(1 for ln in lines if ln.level == "E") == raw_errors + assert raw_errors > 0 diff --git a/tests/test_manager.py b/tests/test_manager.py new file mode 100644 index 0000000..af8d605 --- /dev/null +++ b/tests/test_manager.py @@ -0,0 +1,98 @@ +"""SerialManager end-to-end over pyserial's loop:// (TX is looped back to RX).""" + +import threading +import time + +from kota_tech.core.logline import LineKind +from kota_tech.serialio import manager as mgr_mod +from kota_tech.serialio.manager import SerialManager, State +from kota_tech.serialio.ports import PortInfo + +LOOP = PortInfo("loop://", "loopback", None, None, None) + + +def _wait(pred, timeout=3.0): + end = time.monotonic() + timeout + while time.monotonic() < end: + if pred(): + return True + time.sleep(0.02) + return False + + +def test_loopback_lines_reset_and_disconnect(monkeypatch): + monkeypatch.setattr(mgr_mod, "find_port", lambda sn, dev: LOOP) + lines, states = [], [] + lock = threading.Lock() + + def on_line(ln): + with lock: + lines.append(ln) + + m = SerialManager(on_line, lambda s, d: states.append(s)) + 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") + 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() + assert _wait(lambda: any("reset issued" in ln.text for ln in lines)) + m.disconnect() + + kinds = [ln.kind for ln in lines] + assert LineKind.BOOT in kinds + err = next(ln for ln in lines if ln.level == "E") + assert "s3cr3t" not in err.text + assert states[0] == State.CONNECTED and states[-1] == State.DISCONNECTED + assert lines[-1].text == "--- disconnected ---" + + +class _RecordingSerial: + """Records control-line writes in order, like usbser.sys would see them.""" + + def __init__(self): + self.calls = [] + self._dtr = self._rts = False + + @property + def dtr(self): + return self._dtr + + @dtr.setter + def dtr(self, v): + self._dtr = v + self.calls.append(("dtr", v, self._rts)) # DTR write carries both lines + + @property + def rts(self): + return self._rts + + @rts.setter + def rts(self, v): + self._rts = v + self.calls.append(("rts", v)) + + +def test_hard_reset_pushes_rts_via_dtr_write(monkeypatch): + monkeypatch.setattr(mgr_mod.time, "sleep", lambda s: None) + ser = _RecordingSerial() + SerialManager._hard_reset(ser) + # Every RTS change must be followed by a DTR write carrying the new RTS, + # with DTR (GPIO0) low so the chip boots the app, not download mode. + pushed = [(c[1], c[2]) for c in ser.calls if c[0] == "dtr"] + assert (False, True) in pushed # reset asserted (RTS=1, DTR=0) + assert pushed[-1] == (False, False) # released + assert all(c[1] is False for c in ser.calls if c[0] == "dtr") + + +def test_waits_when_port_missing(monkeypatch): + monkeypatch.setattr(mgr_mod, "find_port", lambda sn, dev: None) + states, lines = [], [] + m = SerialManager(lines.append, lambda s, d: states.append(s)) + m.connect(LOOP) + assert _wait(lambda: m.state == State.WAITING) + m.reset_board() + assert _wait(lambda: any("ignored" in ln.text for ln in lines)) + m.disconnect() + assert states == [State.WAITING, State.DISCONNECTED] diff --git a/tests/test_ports.py b/tests/test_ports.py new file mode 100644 index 0000000..d810c42 --- /dev/null +++ b/tests/test_ports.py @@ -0,0 +1,14 @@ +from kota_tech.serialio.ports import PortInfo + + +def test_native_usb_port_identified_with_mac(): + p = PortInfo("COM7", "USB Serial Device", 0x303A, 0x1001, "14:c1:9f:57:72:0c") + assert p.is_sensorbox + assert p.mac == "14:C1:9F:57:72:0C" + assert "Sensorbox (native USB)" in p.label and "14:C1:9F:57:72:0C" in p.label + + +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" diff --git a/tests/test_redact.py b/tests/test_redact.py new file mode 100644 index 0000000..2710997 --- /dev/null +++ b/tests/test_redact.py @@ -0,0 +1,33 @@ +from kota_tech.core.redact import MASK, redact + +INFLUX_TOKEN = "Zq3v9nX2kLmP0aBcD4eFgH7iJ8kLmN1oPqRsT5uVwXyZ0aB-cD_eFgHiJkLmNoPqRsTuVwXyZ0123456789abcd==" + + +def test_ota_token_lines(): + assert redact("I (1) OTA_CLIENT: OTA token loaded from NVS: abc123def") == ( + f"I (1) OTA_CLIENT: OTA token loaded from NVS: {MASK}" + ) + out = redact("I (2) OTA_CLIENT: Device registered successfully, received token: tok_9f8e7d") + assert "tok_9f8e7d" not in out and out.endswith(MASK) + + +def test_env_and_header_secrets(): + assert INFLUX_TOKEN not in redact(f"INFLUX_TOKEN={INFLUX_TOKEN}") + assert "hunter2" not in redact("PASSWORD=hunter2") + assert INFLUX_TOKEN not in redact(f"Authorization: Token {INFLUX_TOKEN}") + + +def test_bare_long_blob(): + assert redact(f"x {INFLUX_TOKEN} y") == f"x {MASK} y" + + +def test_non_secrets_untouched(): + for s in [ + "I (13664) INFLUX_BUFFER: Read INFLUX_URL from .env file: " + "https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota", + "as7341_adc,device=14C19F57720C f1=3,f2=8 1789655366000000000", + "E (30917) OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:223", + "I (24792) OTA_CLIENT: HTTP client handle: 0x3fcf1560", + "I (2300) wifi:mode : sta (a4:cb:8f:c9:47:3c)", + ]: + assert redact(s) == s diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..85c01a9 --- /dev/null +++ b/uv.lock @@ -0,0 +1,123 @@ +version = 1 +revision = 3 +requires-python = ">=3.11, <3.14" + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "kota-technician-tool" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "pyserial" }, + { name = "pyside6-essentials" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, +] + +[package.metadata] +requires-dist = [ + { name = "pyserial", specifier = ">=3.5" }, + { name = "pyside6-essentials", specifier = ">=6.7" }, +] + +[package.metadata.requires-dev] +dev = [{ name = "pytest", specifier = ">=8" }] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyserial" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" }, +] + +[[package]] +name = "pyside6-essentials" +version = "6.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/16/0b7ecf89ebada82ed0430be33809ff325761ece83104f8635b1bd101fcd0/pyside6_essentials-6.11.2-cp310-abi3-macosx_13_0_universal2.whl", hash = "sha256:77795c145202e65a78d88f7cd409d186e3ba23d159bdb3ba2dcd159ae5e5f0d9", size = 110800753, upload-time = "2026-08-18T06:36:33.674Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1a/84a84bbc75835ee88abdcb66eb875b8e1a21834af9b833bc1893f2c1bfaf/pyside6_essentials-6.11.2-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:aaf9f25f0f324874085fa5b26a610318db8a8e243cf85bb3e5400595191c7778", size = 80108947, upload-time = "2026-08-18T06:36:40.637Z" }, + { url = "https://files.pythonhosted.org/packages/60/4c/d92b08a2151d095b9a3725fa0e71b215ec686d40bbd5d8b702d21a9b5eaa/pyside6_essentials-6.11.2-cp310-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:d3ec6e1885c46e57f16364f38ea8040ffc5dc0b18341058f608fede3ba930567", size = 79575497, upload-time = "2026-08-18T06:36:46.97Z" }, + { url = "https://files.pythonhosted.org/packages/dc/09/d2662684aee4d3f027ff9b1e965f22301bc28ffc5b7f167658508e4475dc/pyside6_essentials-6.11.2-cp310-abi3-win_amd64.whl", hash = "sha256:c8a29def77032773a30879f7f24415b5395ad08592d147c170824ef4c735dfc1", size = 76913043, upload-time = "2026-08-18T06:36:54.038Z" }, + { url = "https://files.pythonhosted.org/packages/cd/87/4dd2a6468cea600da1fcf9c7f02bc248eeaa98cdc75658e48e669d95326a/pyside6_essentials-6.11.2-cp310-abi3-win_arm64.whl", hash = "sha256:fadd75c5c20800d64dd0a586ea8cb337c5e630aa2246df0e5105738afa46e02c", size = 58125520, upload-time = "2026-08-18T06:36:59.073Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "shiboken6" +version = "6.11.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/44/11bf71c36e71936ab4e923e5dd23599dab527c4488b01860b0f689ce9947/shiboken6-6.11.2-cp310-abi3-macosx_13_0_universal2.whl", hash = "sha256:53659683b1f7a08e9f87eff9b1065f1ceb7110cd7a4bc09fdf5efe43d286604d", size = 476674, upload-time = "2026-08-18T06:37:11.526Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9a/f41bea4316d0c1bec4800842dcb4bd8fd3e29d6a79c496bc32e1aec42583/shiboken6-6.11.2-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:7a7a0a72a9ed26c9bf77d42246b1c736486befb8f31aa2fb29957ea4cdd1c1c2", size = 272435, upload-time = "2026-08-18T06:37:13.397Z" }, + { url = "https://files.pythonhosted.org/packages/56/05/79ed0697515d4fc7435459e4ed4c4a3b5621a55b7c91d0f3f2cc3d617aa0/shiboken6-6.11.2-cp310-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:4bbbd6fa4d7cff5ec5e12bc4c10e1d845fa30c89457ecb13cc64f7bddb77f6f9", size = 270668, upload-time = "2026-08-18T06:37:15.025Z" }, + { url = "https://files.pythonhosted.org/packages/ce/6b/fee1b4814317e4664e318644c9c007676c65a71e5b2b9d3ac4121a94dc0a/shiboken6-6.11.2-cp310-abi3-win_amd64.whl", hash = "sha256:6ab0eba1c904455df621f9a6df3ca2bb896bab8670572d2bc4e37804ae91f19a", size = 1226578, upload-time = "2026-08-18T06:37:18.002Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b5/80c4e5aafeb8ac777cf5a1becfe87ac7197c35051df00b5f7819787e09d5/shiboken6-6.11.2-cp310-abi3-win_arm64.whl", hash = "sha256:97c49432488df958f0308736f3d15b6915d8826fc380af17bb4b9ec5c4a5e81b", size = 1784669, upload-time = "2026-08-18T06:37:21.046Z" }, +]