diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 925e5f7..a69268b 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/README.md b/README.md index 367de02..c7f5339 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ virtualenv venv venv/bin/pip3 install -r requirements.txt ``` -Update the config in `nfc2klipper-config.json5`. +Copy and update the `nfc2klipper.cfg` to `~/.config/nfc2klipper/nfc2klipper.cfg`. ## Preparing an NFC reader @@ -115,7 +115,7 @@ where `mainsailos.local` should be replaced with the computer's name (or IP addr The program uses a development web server with **no security** at all so it shouldn't be run if the computer is running on an untrusted network. -The program has a configuration file (nfc2klipper-config.json5) for +The program has a configuration file (`~/.config/nfc2klipper/nfc2klipper.cfg`) for enabling the web server, setting the port number, addresses to moonraker and mainsail, the webserver's address and NFC device to use. diff --git a/REUSE.toml b/REUSE.toml index fd95976..ae3eb81 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -8,7 +8,7 @@ SPDX-PackageSupplier = "Sebastian Andersson " SPDX-PackageDownloadLocation = "https://github.com/bofh69/nfc2klipper" [[annotations]] -path = [".github/**", "requirements.txt", ".gitignore", "nfc2klipper.service", "nfc2klipper-config.json5"] +path = [".github/**", "requirements.txt", ".gitignore", "nfc2klipper.service", "nfc2klipper.cfg"] precedence = "aggregate" SPDX-FileCopyrightText = "$YEAR $NAME <$CONTACT>" SPDX-License-Identifier = "CC0-1.0" diff --git a/nfc2klipper-config.json5 b/nfc2klipper-config.json5 deleted file mode 100755 index 97c73a9..0000000 --- a/nfc2klipper-config.json5 +++ /dev/null @@ -1,23 +0,0 @@ -{ - "disable_web_server": true, - - /// The address the web server listens to, - /// use 0.0.0.0 for all IPv4 - "web_address": "0.0.0.0", - - /// The port the web server listens to - "web_port": 5001, - - /// Clear the spool & filament info if no tag can be read - "clear-spool": false, - - /// Which NFC reader to use, see - /// https://nfcpy.readthedocs.io/en/latest/topics/get-started.html#open-a-local-device - "nfc-device": "ttyAMA0", - - /// URL for the moonraker installation - "moonraker-url": "http://mainsailos.local", - - /// URL for the spoolman installation - "spoolman-url": "http://mainsailos.local:7912", -} diff --git a/nfc2klipper.cfg b/nfc2klipper.cfg new file mode 100644 index 0000000..7d5bed4 --- /dev/null +++ b/nfc2klipper.cfg @@ -0,0 +1,27 @@ +[webserver] +# The web server is a experimental feature. +# If turned on, don't let it be accessed from internet or untrusted +# networks. +disable_web_server = true + +# The address the web server listens to, +# use 0.0.0.0 for all IPv4 +web_address = "0.0.0.0" +# The port the web server listens to +web_port = 5001 + +[nfc] +# Which NFC reader to use, see +# https://nfcpy.readthedocs.io/en/latest/topics/get-started.html#open-a-local-device +nfc-device = "ttyAMA0" + +[spoolman] +# URL for the spoolman installation +spoolman-url = "http://mainsailos.local:7912" + +[moonraker] +# URL for the moonraker installation +moonraker-url = "http://mainsailos.local" + +# If true, clears the spool & filament info if no tag can be read. +clear-spool = false diff --git a/nfc2klipper.py b/nfc2klipper.py index 4d7cbdd..b7fe3bb 100755 --- a/nfc2klipper.py +++ b/nfc2klipper.py @@ -6,11 +6,14 @@ """Program to set current filament & spool in klipper, and write to tags. """ import logging -import threading import os +import sys +import shutil +import threading +from pathlib import Path from flask import Flask, render_template -import json5 +import toml from lib.moonraker_web_client import MoonrakerWebClient from lib.nfc_handler import NfcHandler @@ -20,18 +23,39 @@ FILAMENT = "FILAMENT" NDEF_TEXT_TYPE = "urn:nfc:wkt:T" -script_dir = os.path.dirname(__file__) -cfg_filename = os.path.join(script_dir, "nfc2klipper-config.json5") -with open(cfg_filename, "r", encoding="utf-8") as fp: - args = json5.load(fp) +CFG_DIR = "~/.config/nfc2klipper" logging.basicConfig( level=logging.DEBUG, format="%(asctime)s %(levelname)s - %(name)s: %(message)s" ) -spoolman = SpoolmanClient(args["spoolman-url"]) -moonraker = MoonrakerWebClient(args["moonraker-url"]) -nfc_handler = NfcHandler(args["nfc-device"]) +args = None # pylint: disable=C0103 +for path in ["~/nfc2klipper.cfg", CFG_DIR + "/nfc2klipper.cfg"]: + cfg_filename = os.path.expanduser(path) + if os.path.exists(cfg_filename): + with open(cfg_filename, "r", encoding="utf-8") as fp: + args = toml.load(fp) + break + +if not args: + print( + "WARNING: The config file is missing, installing a default version.", + file=sys.stderr, + ) + if not os.path.exists(CFG_DIR): + cfg_dir = os.path.expanduser(CFG_DIR) + print(f"Creating dir {cfg_dir}", file=sys.stderr) + Path(cfg_dir).mkdir(parents=True, exist_ok=True) + script_dir = os.path.dirname(__file__) + from_filename = os.path.join(script_dir, "nfc2klipper.cfg") + to_filename = os.path.join(cfg_dir, "nfc2klipper.cfg") + shutil.copyfile(from_filename, to_filename) + print(f"Created {to_filename}, please update it", file=sys.stderr) + sys.exit(1) + +spoolman = SpoolmanClient(args["spoolman"]["spoolman-url"]) +moonraker = MoonrakerWebClient(args["moonraker"]["moonraker-url"]) +nfc_handler = NfcHandler(args["nfc"]["nfc-device"]) app = Flask(__name__) @@ -89,13 +113,20 @@ def index(): return render_template("index.html", spools=spools) +def should_clear_spool() -> bool: + """Returns True if the config says the spool should be cleared""" + if args["moonraker"].get("clear_spool"): + return True + return False + + def on_nfc_tag_present(spool, filament): """Handles a read tag""" - if not args.get("clear_spool"): + if not should_clear_spool(): if not (spool and filament): app.logger.info("Did not find spool and filament records in tag") - if args.get("clear_spool") or (spool and filament): + if should_clear_spool() or (spool and filament): if not spool: spool = 0 if not filament: @@ -105,20 +136,20 @@ def on_nfc_tag_present(spool, filament): def on_nfc_no_tag_present(): """Called when no tag is present (or tag without data)""" - if args.get("clear_spool"): + if should_clear_spool(): set_spool_and_filament(0, 0) if __name__ == "__main__": - if args.get("clear_spool"): + if should_clear_spool(): # Start by unsetting current spool & filament: set_spool_and_filament(0, 0) nfc_handler.set_no_tag_present_callback(on_nfc_no_tag_present) nfc_handler.set_tag_present_callback(on_nfc_tag_present) - if not args.get("disable_web_server"): + if not args["webserver"].get("disable_web_server"): app.logger.info("Starting nfc-handler") thread = threading.Thread(target=nfc_handler.run) thread.daemon = True @@ -126,7 +157,9 @@ def on_nfc_no_tag_present(): app.logger.info("Starting web server") try: - app.run(args["web_address"], port=args["web_port"]) + app.run( + args["webserver"]["web_address"], port=args["webserver"]["web_port"] + ) except Exception: nfc_handler.stop() thread.join() diff --git a/requirements.txt b/requirements.txt index 16444bf..d444559 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ flask==3.0.3 -json5==0.9.25 +toml==0.10.2 nfcpy==1.0.4 npyscreen==4.10.5 requests==2.32.3