From 04b9e6c0aa75118e098b6870ebdb06196efbb42c Mon Sep 17 00:00:00 2001 From: maziggy Date: Sun, 22 Feb 2026 15:33:41 +0100 Subject: [PATCH] Add systemd script for Spoolbuddy daemon --- spoolbuddy/daemon/config.py | 49 +++++++++----------- spoolbuddy/daemon/main.py | 8 ++-- spoolbuddy/daemon/systemd/spoolbuddy.service | 6 ++- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/spoolbuddy/daemon/config.py b/spoolbuddy/daemon/config.py index 134f95848..7e0b9fcce 100644 --- a/spoolbuddy/daemon/config.py +++ b/spoolbuddy/daemon/config.py @@ -1,17 +1,26 @@ -"""Configuration loader for SpoolBuddy daemon.""" +"""Configuration loader for SpoolBuddy daemon. + +All configuration is via environment variables. The systemd service file +or a shell wrapper sets these before launching the daemon. + +Required: + SPOOLBUDDY_BACKEND_URL — Bambuddy server URL (e.g. http://192.168.1.100:5000) + SPOOLBUDDY_API_KEY — API key created in Bambuddy Settings → API Keys + +Optional: + SPOOLBUDDY_DEVICE_ID — Unique device identifier (default: derived from MAC) + SPOOLBUDDY_HOSTNAME — Display name (default: system hostname) +""" import os +import socket from dataclasses import dataclass from pathlib import Path -import yaml - -CONFIG_PATH = Path(os.environ.get("SPOOLBUDDY_CONFIG", "/etc/spoolbuddy/config.yaml")) - @dataclass class Config: - backend_url: str = "http://localhost:5000" + backend_url: str = "" api_key: str = "" device_id: str = "" hostname: str = "" @@ -30,25 +39,15 @@ class Config: def load(cls) -> "Config": cfg = cls() - # Load from YAML if exists - if CONFIG_PATH.exists(): - with open(CONFIG_PATH) as f: - data = yaml.safe_load(f) or {} - for key, val in data.items(): - if hasattr(cfg, key): - setattr(cfg, key, val) + cfg.backend_url = os.environ.get("SPOOLBUDDY_BACKEND_URL", "") + cfg.api_key = os.environ.get("SPOOLBUDDY_API_KEY", "") + cfg.device_id = os.environ.get("SPOOLBUDDY_DEVICE_ID", "") + cfg.hostname = os.environ.get("SPOOLBUDDY_HOSTNAME", "") - # Environment overrides - env_map = { - "SPOOLBUDDY_BACKEND_URL": "backend_url", - "SPOOLBUDDY_API_KEY": "api_key", - "SPOOLBUDDY_DEVICE_ID": "device_id", - "SPOOLBUDDY_HOSTNAME": "hostname", - } - for env_key, attr in env_map.items(): - val = os.environ.get(env_key) - if val: - setattr(cfg, attr, val) + if not cfg.backend_url: + raise RuntimeError("SPOOLBUDDY_BACKEND_URL is required (e.g. http://192.168.1.100:5000)") + if not cfg.api_key: + raise RuntimeError("SPOOLBUDDY_API_KEY is required (create one in Bambuddy Settings → API Keys)") # Default device_id from MAC address if not cfg.device_id: @@ -56,8 +55,6 @@ class Config: # Default hostname from system if not cfg.hostname: - import socket - cfg.hostname = socket.gethostname() return cfg diff --git a/spoolbuddy/daemon/main.py b/spoolbuddy/daemon/main.py index 84bb83d53..016ce57f9 100644 --- a/spoolbuddy/daemon/main.py +++ b/spoolbuddy/daemon/main.py @@ -6,8 +6,8 @@ import logging import socket import time -from spoolbuddy.daemon.api_client import APIClient -from spoolbuddy.daemon.config import Config +from .api_client import APIClient +from .config import Config logging.basicConfig( level=logging.INFO, @@ -30,7 +30,7 @@ def _get_ip() -> str: async def nfc_poll_loop(config: Config, api: APIClient): """Continuous NFC polling loop — runs in asyncio with blocking reads offloaded.""" - from spoolbuddy.daemon.nfc_reader import NFCReader + from .nfc_reader import NFCReader nfc = NFCReader() if not nfc.ok: @@ -62,7 +62,7 @@ async def nfc_poll_loop(config: Config, api: APIClient): async def scale_poll_loop(config: Config, api: APIClient): """Continuous scale reading loop — reads at 100ms, reports at 1s intervals.""" - from spoolbuddy.daemon.scale_reader import ScaleReader + from .scale_reader import ScaleReader scale = ScaleReader( tare_offset=config.tare_offset, diff --git a/spoolbuddy/daemon/systemd/spoolbuddy.service b/spoolbuddy/daemon/systemd/spoolbuddy.service index d10f0467e..2163bbc1e 100644 --- a/spoolbuddy/daemon/systemd/spoolbuddy.service +++ b/spoolbuddy/daemon/systemd/spoolbuddy.service @@ -6,8 +6,10 @@ Wants=network-online.target [Service] Type=simple User=pi -Environment=SPOOLBUDDY_CONFIG=/etc/spoolbuddy/config.yaml -ExecStart=/usr/bin/python3 -m spoolbuddy.daemon.main +WorkingDirectory=/opt/bambuddy/spoolbuddy +Environment=SPOOLBUDDY_BACKEND_URL=http://bambuddy:5000 +Environment=SPOOLBUDDY_API_KEY=bb_change_me +ExecStart=/usr/bin/python3 -m daemon.main Restart=always RestartSec=5 StandardOutput=journal