فهرست منبع

Fix SpoolBuddy daemon failing to import hardware drivers

The daemon imports read_tag and scale_diag as bare modules, but they
live in spoolbuddy/scripts/ which isn't on sys.path when systemd runs
the daemon. Added scripts/ to sys.path at startup, resolved relative
to the module file. Also moved the read_tag import inside NFCReader's
try/except (was crashing the daemon instead of skipping gracefully)
and demoted hardware-not-available messages from ERROR to INFO.
maziggy 3 ماه پیش
والد
کامیت
541f8926b4
4فایلهای تغییر یافته به همراه8 افزوده شده و 3 حذف شده
  1. 1 1
      CHANGELOG.md
  2. 5 0
      spoolbuddy/daemon/main.py
  3. 1 1
      spoolbuddy/daemon/nfc_reader.py
  4. 1 1
      spoolbuddy/daemon/scale_reader.py

+ 1 - 1
CHANGELOG.md

@@ -9,7 +9,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **SpoolBuddy Dashboard Redesign** — Redesigned the SpoolBuddy dashboard with a two-column layout: left column shows device connection status (scale and NFC with state-colored icons — green when device is online, gray when offline) and a compact printers list with live status indicators; right column shows the current spool card. Cards use a dashed border style for a cleaner look. The large weight display card was removed in favor of the inline scale reading in the device card.
 
 ### Fixed
-- **SpoolBuddy Daemon Crashes When NFC Module Not Installed** — The `NFCReader` constructor imported `read_tag` and instantiated `PN5180()` outside the try/except block, so a missing `read_tag` module raised an uncaught `ModuleNotFoundError` that crashed the entire daemon via `asyncio.gather`. The scale reader already handled its missing module gracefully. Moved the import and instantiation inside the existing try/except so a missing NFC driver sets `ok=False` and the daemon logs "NFC reader not available" and continues running the scale and heartbeat loops.
+- **SpoolBuddy Daemon Can't Find Hardware Drivers** — The daemon's `nfc_reader.py` and `scale_reader.py` import `read_tag` and `scale_diag` as bare modules, but these files live in `spoolbuddy/scripts/` which isn't on Python's module search path. The systemd service sets `WorkingDirectory` to `spoolbuddy/` and runs `python -m daemon.main`, so only the `spoolbuddy/` and `daemon/` directories are on `sys.path`. Added `scripts/` to `sys.path` at daemon startup, resolved relative to the module file so it works regardless of install path. Also moved the `read_tag` import inside `NFCReader.__init__`'s try/except block — it was previously outside, so a missing module crashed the entire daemon instead of gracefully skipping NFC polling. Demoted hardware-not-available log messages from ERROR to INFO since missing modules are expected when hardware isn't connected.
 
 ### Improved
 - **SpoolBuddy Scale Value Stabilization** — The SpoolBuddy daemon now suppresses redundant scale weight reports: only sends updates when the weight changes by ≥2g or the stability state flips (stable ↔ unstable). Previously every 1-second report interval sent a reading regardless of change, causing the dashboard weight display to bounce continuously. The frontend also applies a 3g display threshold as defense-in-depth.

+ 5 - 0
spoolbuddy/daemon/main.py

@@ -4,7 +4,12 @@
 import asyncio
 import logging
 import socket
+import sys
 import time
+from pathlib import Path
+
+# Add scripts/ to sys.path so hardware drivers (read_tag, scale_diag) are importable
+sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts"))
 
 from .api_client import APIClient
 from .config import Config

+ 1 - 1
spoolbuddy/daemon/nfc_reader.py

@@ -36,7 +36,7 @@ class NFCReader:
             self._ok = True
             logger.info("NFC reader initialized")
         except Exception as e:
-            logger.error("NFC init failed: %s", e)
+            logger.info("NFC not available: %s", e)
 
     @property
     def ok(self) -> bool:

+ 1 - 1
spoolbuddy/daemon/scale_reader.py

@@ -27,7 +27,7 @@ class ScaleReader:
             self._ok = True
             logger.info("Scale initialized (tare=%d, cal=%.6f)", tare_offset, calibration_factor)
         except Exception as e:
-            logger.error("Scale init failed: %s", e)
+            logger.info("Scale not available: %s", e)
 
     @property
     def ok(self) -> bool: