Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
bofh69 committed Mar 5, 2024
1 parent 713dcb0 commit 4cf4d7d
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 1 deletion.
52 changes: 52 additions & 0 deletions .github/workflows/bandit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

# Bandit is a security linter designed to find common security issues in Python code.
# This action will run Bandit on your codebase.
# The results of the scan will be found under the Security tab of your repository.

# https://github.com/marketplace/actions/bandit-scan is ISC licensed, by abirismyname
# https://pypi.org/project/bandit/ is Apache v2.0 licensed, by PyCQA

name: Bandit
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '33 13 * * 6'

jobs:
bandit:
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Bandit Scan
uses: shundor/python-bandit-scan@9cc5aa4a006482b8a7f91134412df6772dbda22c
with: # optional arguments
# exit with 0, even with results found
exit_zero: true # optional, default is DEFAULT
# Github token of the repository (automatically created by Github)
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information.
# File or directory to run bandit on
# path: # optional, default is .
# Report only issues of a given severity level or higher. Can be LOW, MEDIUM or HIGH. Default is UNDEFINED (everything)
# level: # optional, default is UNDEFINED
# Report only issues of a given confidence level or higher. Can be LOW, MEDIUM or HIGH. Default is UNDEFINED (everything)
# confidence: # optional, default is UNDEFINED
# comma-separated list of paths (glob patterns supported) to exclude from scan (note that these are in addition to the excluded paths provided in the config file) (default: .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg)
# excluded_paths: # optional, default is DEFAULT
# comma-separated list of test IDs to skip
# skips: # optional, default is DEFAULT
# path to a .bandit file that supplies command line arguments
# ini_path: # optional, default is DEFAULT

39 changes: 39 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Request,
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
# Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable
# packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
name: 'Dependency review'
on:
pull_request:
branches: [ "main" ]

# If using a dependency submission action in this workflow this permission will need to be set to:
#
# permissions:
# contents: write
#
# https://docs.github.com/en/enterprise-cloud@latest/code-security/supply-chain-security/understanding-your-software-supply-chain/using-the-dependency-submission-api
permissions:
contents: read
# Write permissions for pull-requests are required for using the `comment-summary-in-pr` option, comment out if you aren't using this option
pull-requests: write

jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout repository'
uses: actions/checkout@v4
- name: 'Dependency Review'
uses: actions/dependency-review-action@v4
# Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options.
with:
comment-summary-in-pr: always
# fail-on-severity: moderate
# deny-licenses: GPL-1.0-or-later, LGPL-2.0-or-later
# retry-on-snapshot-warnings: true
24 changes: 24 additions & 0 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Pylint

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
pip install -r requirements.txt
- name: Analysing the code with pylint
run: |
pylint $(git ls-files '*.py')
77 changes: 76 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,77 @@
# nfc2klipper
Set loaded spool & filament in klipper from NFC tags

Set loaded spool & filament in klipper from NFC tags.

WARNING: This is Work In Progress and not yet tested


## Preparing an NFC reader

I use a PN532 based reader connected via UART to the raspberry pi
where this program is running.

See [here](https://learn.adafruit.com/adafruit-nfc-rfid-on-raspberry-pi/pi-serial-port)
for how to configure a raspberry pi for it.


## Preparing klipper

Klipper should have two gcode macros:

* SET_ACTIVE_FILAMENT ID=n
* SET_ACTIVE_SPOOL ID=n


I use this configuration:
```ini
[gcode_macro SET_ACTIVE_SPOOL]
gcode:
{% if params.ID %}
{% set id = params.ID|int %}
{action_call_remote_method(
"spoolman_set_active_spool",
spool_id=id
)}
{% else %}
{action_respond_info("Parameter 'ID' is required")}
{% endif %}

[gcode_macro SET_ACTIVE_FILAMENT]
variable_active_filament: 0
gcode:
{% if params.ID %}
{% set id = params.ID|int %}
SET_GCODE_VARIABLE MACRO=SET_ACTIVE_FILAMENT VARIABLE=active_filament VALUE={id}
{% else %}
{action_respond_info("Parameter 'ID' is required")}
{% endif %}

[gcode_macro ASSERT_ACTIVE_FILAMENT]
gcode:
{% if params.ID %}
{% set id = params.ID|int %}
{% current_id = printer["gcode_macro set_active_filament"].active_filament %}
{% if id != current_id %}
{# TODO: Change to PAUSE & M117 message #}
{action_raise_error("Wrong filament is loaded, should be " + id)}
{% endif %}
{% else %}
{action_respond_info("Parameter 'ID' is required")}
{% endif %}
```

## Preparing tags

The tags should contain an NDEF record with a text block like this:
```
SPOOL: 3
FILAMENT: 2
```

The numbers are the id numbers that will be sent to the macros in
klipper via the [Moonraker](https://github.com/Arksine/moonraker) API.


I've written to my tags with an Android phone and NXP's TagWriter.

TODO: Write it with the NFC interface. A TUI selecting the spool to write?
112 changes: 112 additions & 0 deletions nfc2klipper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python3

"""
Program to set current filament & spool in klipper.
"""

import argparse

import nfc
import requests

SPOOL = "spool"
FILAMENT = "FILAMENT"
NDEF_TEXT_TYPE = "urn:nfc:wkt:T"

parser = argparse.ArgumentParser()
# description="Fetches filaments from Spoolman and creates SuperSlicer filament configs.",

parser.add_argument("--version", action="version", version="%(prog)s 0.0.1")

parser.add_argument(
"-c",
"--clear",
action="store_true",
help="Clears the spool & filamnet when no tag is present",
)

parser.add_argument(
"-d",
"--nfc-device",
metavar="device",
default="ttyS0",
help="Which NFC reader to use, see https://nfcpy.readthedocs.io/en/latest/topics/get-started.html#open-a-local-device for format",
)

parser.add_argument(
"-u",
"--url",
metavar="URL",
default="http://mainsailos.local",
help="URL for the moonraker installation",
)


args = parser.parse_args()


def set_spool_and_filament(url: str, spool: int, filament: int):
"""Calls moonraker with the current spool & filament"""

commands = {
"commands": [
f"SET_ACTIVE_SPOOL ID={spool}",
f"SET_ACTIVE_FILAMENT ID={filament}",
]
}

response = requests.post(
url + "/api/printer/command", timeout=10, json=commands
)
if response.status_code != 200:
raise ValueError(f"Request to moonraker failed: {response}")


def on_nfc_connect(tag):
"""Handles a read tag"""

if tag.ndef is None:
print("The tag doesn't have NDEF records")
return True

spool = None
filament = None

for record in tag.ndef.records:
if record.type == NDEF_TEXT_TYPE:
for line in record.text.split("\n"):
line = line.split(",")
if len(line) == 2:
if line[0] == SPOOL:
spool = line[1]
if line[0] == FILAMENT:
filament = line[1]
else:
print(f"Read other record: {record}")

if not args.clear:
if not (spool and filament):
print("Did not find spool and filament records in tag")
if args.clear or (spool and filament):
if not spool:
spool = 0
if not filament:
filament = 0
set_spool_and_filament(args.url, spool, filament)

# Don't let connect return until the tag is removed:
return True


# Open NFC reader. Will throw an exception if it fails.
clf = nfc.ContactlessFrontend(args.nfc_device)

if args.clear:
# Start by unsetting current spool & filament:
set_spool_and_filament(args.url, 0, 0)

while True:
clf.connect(rdwr={"on-connect": on_nfc_connect})
# No tag connected anymore.
if args.clear:
set_spool_and_filament(args.url, 0, 0)
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
nfcpy==1.0.4
requests==2.31.0

0 comments on commit 4cf4d7d

Please sign in to comment.