-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1d78b5a
Showing
8 changed files
with
959 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# prusa-cam | ||
|
||
![Snapshot of `prusa-cam`](snapshot.png) | ||
|
||
I set up a Logitech C920 webcam [inside my Prusa enclosure](https://www.printables.com/model/433908-logitech-c920-original-prusa-enclosure-mount) to be able to monitor my prints using Prusa Connect. `prusa-cam` is a Bash script that POSTs snapshots from your webcam to the Prusa Connect API in 10 second intervals, which I believe is the minimum time generally allowed by Prusa. It only sends snapshots when your printer is online and actively printing. It pings your printer locally and runs as a systemd service, and logs if things are successful or not, resetting states if failures occur. | ||
|
||
![Camera inside printer enclosure](camera.jpg) | ||
|
||
![View from camera](cheese.jpg) | ||
|
||
## Dependencies | ||
|
||
- curl | ||
- jq | ||
- v4l2-ctl | ||
- ffmpeg | ||
|
||
## Setup | ||
|
||
- Copy `prusa-cam` to somewhere like `"$HOME/.local/bin/"` | ||
- Copy the systemd service to `"$HOME/.config/systemd/user/"` | ||
- Edit the systemd service with the full path to the script | ||
- Copy `env.example` to `"$HOME/.config/prusa-cam/env"` | ||
- Edit the file with your settings | ||
- Get your printer API key from your printer (PrusaLink API key) | ||
- Get your camera name from `v4l2-ctl --list-devices` | ||
- Generate a fingerprint with something like `pwgen 32 1 | base64` | ||
- Get your token from Prusa Connect > Camera > Other cameras | ||
- Run `prusa-cam`, ensure you're getting successes | ||
- Note: `prusa-cam` will only start POSTing snapshots if your printer is actively printing | ||
- `prusa-cam` will let you know if one of the critical dependencies is missing, or if your config is missing required fields | ||
- Run `systemctl --user enable --now prusa-cam` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#!/usr/bin/env bash | ||
|
||
: "${PRINTER_NAME:=mk4}" | ||
: "${PRINTER_IP:=192.168.1.10}" | ||
# Fetch printer API key from your printer (PrusaLink API key) | ||
: "${PRINTER_API_KEY:=some_api_key}" | ||
# Fetch from `v4l2-ctl --list-devices` | ||
: "${CAMERA_NAME:=HD Pro Webcam C920}" | ||
# Generate with something like `pwgen 32 1 | base64` | ||
: "${FINGERPRINT:=some_fingerprint}" | ||
# Fetch from Prusa Connect > Camera > Other cameras | ||
: "${TOKEN:=some_token}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
#!/usr/bin/env bash | ||
|
||
: "${CONFIG:=$HOME/.config/prusa-cam/env}" | ||
|
||
if ! [[ -f $CONFIG ]]; then | ||
echo "$CONFIG doesn't exist, quitting" | ||
exit 1 | ||
fi | ||
|
||
# shellcheck disable=SC1090 | ||
source "$CONFIG" | ||
|
||
: "${PRINTER_STATUS_URL:=api/v1/status}" | ||
: "${SNAPSHOT_DIR:=$(mktemp -d --suffix=-prusa-cam)}" | ||
: "${SNAPSHOT_FILENAME:=output.jpg}" | ||
: "${SNAPSHOT_URL:=https://webcam.connect.prusa3d.com/c/snapshot}" | ||
: "${DELAY_SECONDS:=10}" | ||
|
||
config_check() { | ||
if [[ -z $PRINTER_NAME ]]; then | ||
echo Config missing PRINTER_NAME | ||
exit 1 | ||
elif [[ -z $PRINTER_IP ]]; then | ||
echo Config missing PRINTER_IP | ||
exit 1 | ||
elif [[ -z $PRINTER_API_KEY ]]; then | ||
echo Config missing PRINTER_API_KEY | ||
exit 1 | ||
elif [[ -z $CAMERA_NAME ]]; then | ||
echo Config missing CAMERA_NAME | ||
exit 1 | ||
elif [[ -z $FINGERPRINT ]]; then | ||
echo Config missing FINGERPRINT | ||
exit 1 | ||
elif [[ -z $TOKEN ]]; then | ||
echo Config missing TOKEN | ||
exit 1 | ||
fi | ||
} | ||
|
||
dependency_check() { | ||
if ! command -v curl >/dev/null; then | ||
echo OS missing curl... wtf | ||
exit 1 | ||
elif ! command -v jq >/dev/null; then | ||
echo OS missing jq | ||
exit 1 | ||
elif ! command -v v4l2-ctl >/dev/null; then | ||
echo OS missing v4l2-ctl | ||
exit 1 | ||
elif ! command -v ffmpeg >/dev/null; then | ||
echo OS missing ffmpeg | ||
exit 1 | ||
fi | ||
} | ||
|
||
get_camera_device() { | ||
CAMERA_DEVICE=$(v4l2-ctl --list-devices | | ||
grep -A 1 "$CAMERA_NAME" | | ||
tail -n 1 | | ||
tr -d \\t) | ||
|
||
if [[ -z $CAMERA_DEVICE ]]; then | ||
echo "Could not get camera device" | ||
exit 1 | ||
fi | ||
} | ||
|
||
quit() { | ||
rm -r "$SNAPSHOT_DIR" | ||
exit 1 | ||
} | ||
|
||
trap quit SIGINT SIGTERM | ||
|
||
is_printer_online() { | ||
if ping \ | ||
-c 1 \ | ||
"$PRINTER_IP" \ | ||
>/dev/null 2>&1; then | ||
return 0 | ||
else | ||
return 1 | ||
fi | ||
} | ||
|
||
is_printer_active() { | ||
PRINTER_STATE=$(curl \ | ||
-H "X-Api-Key: $PRINTER_API_KEY" \ | ||
-s \ | ||
"$PRINTER_IP/$PRINTER_STATUS_URL" | | ||
jq -r .printer.state) | ||
|
||
if [[ $PRINTER_STATE = 'PRINTING' ]]; then | ||
return 0 | ||
else | ||
return 1 | ||
fi | ||
} | ||
|
||
get_snapshot() { | ||
if ffmpeg \ | ||
-loglevel quiet \ | ||
-stats \ | ||
-y \ | ||
-f v4l2 \ | ||
-i "$CAMERA_DEVICE" \ | ||
-f image2 \ | ||
-vframes 1 \ | ||
-pix_fmt yuyv422 \ | ||
"$SNAPSHOT_DIR/$SNAPSHOT_FILENAME" \ | ||
2>/dev/null; then | ||
return 0 | ||
else | ||
return 1 | ||
fi | ||
} | ||
|
||
post_snapshot() { | ||
if curl \ | ||
-X PUT \ | ||
-H "Accept: */*" \ | ||
-H "Content-Type: image/jpg" \ | ||
-H "Fingerprint: $FINGERPRINT" \ | ||
-H "Token: $TOKEN" \ | ||
--data-binary "@$SNAPSHOT_DIR/$SNAPSHOT_FILENAME" \ | ||
--no-progress-meter \ | ||
--compressed \ | ||
"$SNAPSHOT_URL"; then | ||
return 0 | ||
else | ||
return 1 | ||
fi | ||
} | ||
|
||
main() { | ||
while true; do | ||
if is_printer_online; then | ||
unset PRINTER_NOT_ONLINE_SHOWN | ||
|
||
if [[ -z $PRINTER_ONLINE_SHOWN ]]; then | ||
echo "$PRINTER_NAME online" | ||
PRINTER_ONLINE_SHOWN=1 | ||
fi | ||
|
||
if is_printer_active; then | ||
unset PRINTER_NOT_ACTIVE_SHOWN | ||
|
||
if [[ -z $PRINTER_ACTIVE_SHOWN ]]; then | ||
echo "$PRINTER_NAME active" | ||
PRINTER_ACTIVE_SHOWN=1 | ||
fi | ||
|
||
if get_snapshot; then | ||
unset FFMPEG_FAILURE_SHOWN | ||
|
||
if [[ -z $FFMPEG_SUCCESS_SHOWN ]]; then | ||
echo "Successfully capturing snapshots" | ||
FFMPEG_SUCCESS_SHOWN=1 | ||
fi | ||
|
||
if post_snapshot; then | ||
unset CLOUD_CONNECT_FAILURE_SHOWN | ||
|
||
if [[ -z $CLOUD_CONNECT_SUCCESS_SHOWN ]]; then | ||
echo "Successfully POSTing to Prusa Connect" | ||
CLOUD_CONNECT_SUCCESS_SHOWN=1 | ||
fi | ||
else | ||
unset CLOUD_CONNECT_SUCCESS_SHOWN | ||
|
||
if [[ -z $CLOUD_CONNECT_FAILURE_SHOWN ]]; then | ||
echo "POSTing to Prusa Connect returned an error" | ||
CLOUD_CONNECT_FAILURE_SHOWN=1 | ||
fi | ||
fi | ||
else | ||
unset FFMPEG_SUCCESS_SHOWN | ||
|
||
if [[ -z $FFMPEG_FAILURE_SHOWN ]]; then | ||
echo "FFmpeg returned an error" | ||
FFMPEG_FAILURE_SHOWN=1 | ||
fi | ||
|
||
unset CLOUD_CONNECT_SUCCESS_SHOWN | ||
unset CLOUD_CONNECT_FAILURE_SHOWN | ||
fi | ||
else | ||
unset PRINTER_ACTIVE_SHOWN | ||
|
||
if [[ -z $PRINTER_NOT_ACTIVE_SHOWN ]]; then | ||
echo "$PRINTER_NAME not active" | ||
PRINTER_NOT_ACTIVE_SHOWN=1 | ||
fi | ||
|
||
unset FFMPEG_SUCCESS_SHOWN | ||
unset FFMPEG_FAILURE_SHOWN | ||
|
||
unset CLOUD_CONNECT_SUCCESS_SHOWN | ||
unset CLOUD_CONNECT_FAILURE_SHOWN | ||
fi | ||
else | ||
unset PRINTER_ONLINE_SHOWN | ||
|
||
if [[ -z $PRINTER_NOT_ONLINE_SHOWN ]]; then | ||
echo "$PRINTER_NAME offline" | ||
PRINTER_NOT_ONLINE_SHOWN=1 | ||
fi | ||
|
||
unset PRINTER_ACTIVE_SHOWN | ||
unset PRINTER_NOT_ACTIVE_SHOWN | ||
|
||
unset FFMPEG_SUCCESS_SHOWN | ||
unset FFMPEG_FAILURE_SHOWN | ||
|
||
unset CLOUD_CONNECT_SUCCESS_SHOWN | ||
unset CLOUD_CONNECT_FAILURE_SHOWN | ||
fi | ||
|
||
sleep "$DELAY_SECONDS" | ||
done | ||
} | ||
|
||
config_check | ||
|
||
dependency_check | ||
|
||
get_camera_device | ||
|
||
main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[Unit] | ||
Description=Prusa Cam | ||
After=network-online.target | ||
|
||
[Service] | ||
ExecStart=/home/user/.local/bin/prusa-cam | ||
Restart=always | ||
RestartSec=5 | ||
|
||
[Install] | ||
WantedBy=default.target |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.