diff --git a/docs/cloud.md b/docs/cloud.md new file mode 100644 index 00000000..6d35e880 --- /dev/null +++ b/docs/cloud.md @@ -0,0 +1,52 @@ +--- +title: Cloud Remote Access (Experimental) +--- + +# Cloud Remote Access (Experimental) + +**Available in: Extended firmware only** + +Control your printer remotely using cloud-based remote access providers. + +> **Note**: Cloud providers are downloaded on-demand when enabled by the user. See [third-party integration design](design/third_party.md) for details on how external components are managed. + +> **Warning**: This feature is experimental. Cloud services consume additional CPU and memory resources which may affect print quality or reliability during active prints. It is recommended to monitor system performance closely. + +## Supported Providers + +- **none** - Cloud access disabled (default) +- **octoeverywhere** - Remote access via [OctoEverywhere.com](https://octoeverywhere.com) + +## OctoEverywhere + +- Access your printer remotely from anywhere +- AI print failure detection and notifications +- Webcam streaming and timelapse +- Requires no port forwarding or VPN configuration + +### Using firmware-config Web UI (preferred) + +Navigate to the [firmware-config](firmware_config.md) web interface, go to the Remote Access section, and select OctoEverywhere under Cloud Provider. This will automatically download and install the OctoEverywhere plugin and display the account linking instructions. + +### Manual Setup (advanced) + +**Step 1:** Download OctoEverywhere (requires internet connection): +```bash +ssh root@ +octoeverywhere-pkg download +``` + +**Step 2:** Edit `extended/extended2.cfg`, set the `cloud`: +```ini +[remote_access] +cloud: octoeverywhere +``` + +**Step 3:** Start the cloud service: +```bash +/etc/init.d/S99cloud restart +``` + +**Step 4:** Link your account by downloading `octoeverywhere.log` from Mainsail or Fluidd to find the account linking URL. Open the URL in your browser to link your printer to your OctoEverywhere.com account. + +**Need help?** Visit [OctoEverywhere Support for Snapmaker U1](https://octoeverywhere.com/s/snapmaker-u1) for assistance. diff --git a/docs/firmware_config.md b/docs/firmware_config.md index 834ecae7..923ed874 100644 --- a/docs/firmware_config.md +++ b/docs/firmware_config.md @@ -49,6 +49,7 @@ Toggle settings directly from the web interface: | Remote Screen | Enabled, Disabled | Enable remote screen access | | Klipper Metrics Exporter | Enabled, Disabled | Enable Prometheus metrics | | VPN Provider | None, Tailscale | Enable VPN remote access (Experimental) | +| Cloud | None, OctoEverywhere | Enable Cloud-based remote access (Experimental) | Changes are applied immediately and relevant services are restarted. @@ -160,6 +161,12 @@ Note: Remote screen requires additional Moonraker configuration. See [Remote Scr See [VPN Remote Access](vpn.md) for setup instructions. +**cloud** +- `none` (default) - No cloud providers enabled. +- `octoeverywhere` - [OctoEverywhere.com](https://octoeverywhere.com) remote access + +See the [3D Printing Clouds](cloud.md) for setup instructions. + #### [monitoring] **klipper_exporter** - Enable Prometheus metrics exporter for Klipper @@ -194,6 +201,10 @@ ssh: false # VPN provider for remote access: none, tailscale # Must SSH and run "tailscale up" to complete login flow vpn: none +# Cloud: none, octoeverywhere +# - none - No cloud services enabled. +# - octoeverywhere - OctoEverywhere.com remote access +cloud: none [monitoring] # Enable Klipper Prometheus exporter on specified address diff --git a/docs/index.md b/docs/index.md index f754ef63..fdabd4ec 100644 --- a/docs/index.md +++ b/docs/index.md @@ -61,6 +61,7 @@ Heavily expanded firmware with extensive features and customization. Includes al - Moonraker Adaptive Mesh Support - Object processing for adaptive mesh features - Moonraker Apprise Notifications - Send print notifications to Discord, Telegram, Slack, and 90+ services - [Timelapse Recovery Tool](https://github.com/horzadome/snapmaker-u1-timelapse-recovery) - Recover unplayable timelapse videos +- [OctoEverywhere](cloud.md) - Cloud-based remote access service for 3D printers ## Support diff --git a/overlays/firmware-extended/10-firmware-config/root/usr/local/share/firmware-config/extended/extended2.cfg b/overlays/firmware-extended/10-firmware-config/root/usr/local/share/firmware-config/extended/extended2.cfg index 618f5856..7422778e 100644 --- a/overlays/firmware-extended/10-firmware-config/root/usr/local/share/firmware-config/extended/extended2.cfg +++ b/overlays/firmware-extended/10-firmware-config/root/usr/local/share/firmware-config/extended/extended2.cfg @@ -22,6 +22,10 @@ ssh: false # VPN provider for remote access: none, tailscale # Must SSH and run "tailscale up" to complete login flow vpn: none +# Cloud: none, octoeverywhere +# - none - No cloud services enabled. +# - octoeverywhere - OctoEverywhere.com cloud-based remote access service +cloud: none [monitoring] # Enable Klipper Prometheus exporter on specified address diff --git a/overlays/firmware-extended/23-cloud/root/etc/init.d/S99cloud b/overlays/firmware-extended/23-cloud/root/etc/init.d/S99cloud new file mode 100755 index 00000000..e19bca40 --- /dev/null +++ b/overlays/firmware-extended/23-cloud/root/etc/init.d/S99cloud @@ -0,0 +1,74 @@ +#!/bin/sh + +OCTOEVERYWHERE_DAEMON="/usr/local/sbin/octoeverywhered" +OCTOEVERYWHERE_PIDFILE="/var/run/octoeverywhere.pid" +OCTOEVERYWHERE_LOGFILE="/home/lava/printer_data/logs/octoeverywhere.log" +OCTOEVERYWHERE_STATE_DIR="/home/lava/printer_data/octoeverywhere-store" + +# Load config from user +EXTENDED_CFG="/home/lava/printer_data/config/extended/extended2.cfg" +CLOUD_PROVIDER=$(/usr/local/bin/extended-config.py get "$EXTENDED_CFG" remote_access cloud none) + +start_octoeverywhere() { + if [ -f "$OCTOEVERYWHERE_PIDFILE" ] && kill -0 "$(cat "$OCTOEVERYWHERE_PIDFILE")" 2>/dev/null; then + echo "moonraker_octoeverywhere already running" + return 0 + fi + + printf "Starting moonraker_octoeverywhere: " + mkdir -p "$OCTOEVERYWHERE_STATE_DIR" + mv "$OCTOEVERYWHERE_LOGFILE" "$OCTOEVERYWHERE_LOGFILE.old" + + start-stop-daemon -S -b -m -u lava \ + -p "$OCTOEVERYWHERE_PIDFILE" \ + -x "$OCTOEVERYWHERE_DAEMON" + + echo "OK" +} + +start() { + case "$CLOUD_PROVIDER" in + octoeverywhere) + start_octoeverywhere + ;; + *) + echo "No cloud is enabled in the extended configuration, not starting any cloud service." + exit 0 + ;; + esac +} + +stop() { + # Stop all cloud services, if any are running. + printf "Stopping OctoEverywhere: " + if [ -f "$OCTOEVERYWHERE_PIDFILE" ]; then + start-stop-daemon -K -p "$OCTOEVERYWHERE_PIDFILE" -s TERM + rm -f "$OCTOEVERYWHERE_PIDFILE" + echo "OK" + else + echo "not running" + fi +} + +case "$1" in + start) + start + ;; + + stop) + stop + ;; + + restart|reload) + stop + sleep 1 + start + ;; + + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac + +exit $? diff --git a/overlays/firmware-extended/23-cloud/root/usr/local/bin/octoeverywhere-pkg b/overlays/firmware-extended/23-cloud/root/usr/local/bin/octoeverywhere-pkg new file mode 100755 index 00000000..6e065737 --- /dev/null +++ b/overlays/firmware-extended/23-cloud/root/usr/local/bin/octoeverywhere-pkg @@ -0,0 +1,106 @@ +#!/bin/bash + +# +# OctoEverywhere package manager +# Downloads and installs a pinned version of OctoEverywhere from GitHub +# + +VERSION="4.6.6" +URL="https://github.com/QuinnDamerell/OctoPrint-OctoEverywhere/archive/refs/tags/${VERSION}.zip" +SHA256="8fedea60d50adc5763269cec47a46d81e534ea0066b790176fc819abea1b8db5" + +# These paths are referenced in the init.d script +APP_DIR="/oem/apps/octoeverywhere" + +check() { + if [[ -d "$APP_DIR/latest" ]]; then + echo "OctoEverywhere is installed." + return 0 + else + echo "OctoEverywhere is not installed." + return 1 + fi +} + +download() { + rm -rf "$APP_DIR" + mkdir -p "$APP_DIR" + + echo "Downloading OctoEverywhere ${VERSION} From GitHub..." + if ! /usr/local/bin/curl -sfSL -o "$APP_DIR/octoeverywhere.zip" "$URL"; then + echo "Download failed" + return 1 + fi + + echo "Verifying SHA256 checksum..." + ACTUAL_SHA256=$(sha256sum "$APP_DIR/octoeverywhere.zip" | awk '{print $1}') + if [[ "$ACTUAL_SHA256" != "$SHA256" ]]; then + echo "SHA256 mismatch!" + echo " Expected: $SHA256" + echo " Actual: $ACTUAL_SHA256" + return 1 + fi + echo "Checksum verified." + + echo "Extracting..." + if ! unzip -q "$APP_DIR/octoeverywhere.zip" -d "$APP_DIR"; then + echo "Extraction failed" + return 1 + fi + echo "Extraction successful." + + rm -rf "$APP_DIR/octoeverywhere.zip" + + echo "Setting up Python virtual environment (this can take up to 60 seconds)..." + if ! python3 -m venv "$APP_DIR/venv"; then + echo "Failed to create virtual environment" + return 1 + fi + + echo "Installing dependencies (this can also take a bit)..." + if ! "$APP_DIR/venv/bin/pip" install --disable-pip-version-check -r "$APP_DIR/OctoPrint-OctoEverywhere-${VERSION}/requirements.txt"; then + echo "Failed to install dependencies" + return 1 + fi + + echo "Trying to install the optional zstdstandard package for better performance..." + if ! "$APP_DIR/venv/bin/pip" install --disable-pip-version-check zstandard; then + echo "Failed to install zstandard, continuing without it." + fi + + # GitHub archives extract to OctoPrint-OctoEverywhere- + ln -s "OctoPrint-OctoEverywhere-${VERSION}" "$APP_DIR/latest" + echo "OctoEverywhere installed successfully to $APP_DIR" +} + +case "$1" in + check) + check + ;; + download) + if check; then + echo "OctoEverywhere is already installed." + exit 0 + fi + if ! download; then + rm -rf "$APP_DIR" + exit 1 + fi + ;; + update) + echo "Updating OctoEverywhere..." + if ! download; then + rm -rf "$APP_DIR" + exit 1 + fi + ;; + clean) + echo "Removing OctoEverywhere installation..." + rm -rf "$APP_DIR" + echo "Removed." + ;; + *) + echo "Usage: $0 check|download|update|clean" + exit 1 + ;; +esac diff --git a/overlays/firmware-extended/23-cloud/root/usr/local/sbin/octoeverywhered b/overlays/firmware-extended/23-cloud/root/usr/local/sbin/octoeverywhered new file mode 100755 index 00000000..892b6702 --- /dev/null +++ b/overlays/firmware-extended/23-cloud/root/usr/local/sbin/octoeverywhered @@ -0,0 +1,45 @@ +#!/bin/bash + +DIR="/oem/apps/octoeverywhere/latest" +PIDFILE="/var/run/octoeverywhere.pid" +PYTHON="/oem/apps/octoeverywhere/venv/bin/python3" +STATE_DIR="/home/lava/printer_data/octoeverywhere-store" + +if [[ ! -e "$PYTHON" ]]; then + echo "Error: Python interpreter not found at $PYTHON" + exit 1 +fi + +if [[ ! -e "$DIR" ]]; then + echo "Error: OctoEverywhere daemon not found at $DAEMON" + exit 1 +fi + +if [[ ! -e "$STATE_DIR" ]]; then + echo "Error: State directory not found at $STATE_DIR" + exit 1 +fi + +config_json() { + cat <<"EOF" +{ + "ConfigFolder": "/home/lava/printer_data/config", + "LogFolder": "/home/lava/printer_data/logs", + "LocalFileStoragePath": "/home/lava/printer_data/octoeverywhere-store", + "ServiceName": "octoeverywhere", + "VirtualEnvPath": "/oem/apps/octoeverywhere/venv", + "RepoRootFolder": "/oem/apps/octoeverywhere/latest", + "IsCompanion": false, + "MoonrakerConfigFile": "/home/lava/printer_data/config/moonraker.conf" +} +EOF +} + +mkdir -p "$STATE_DIR" + +# Tune malloc for embedded systems, so PY doesn't each so much memory. +export MALLOC_TRIM_THRESHOLD_=65536 +export MALLOC_ARENA_MAX=2 +export PYTHONPATH="$DIR:$PYTHONPATH" + +exec "$PYTHON" -m moonraker_octoeverywhere "$(base64 <<< $(config_json))" diff --git a/overlays/firmware-extended/23-cloud/root/usr/local/share/firmware-config/functions/24_settings_cloud.yaml b/overlays/firmware-extended/23-cloud/root/usr/local/share/firmware-config/functions/24_settings_cloud.yaml new file mode 100644 index 00000000..e5423aba --- /dev/null +++ b/overlays/firmware-extended/23-cloud/root/usr/local/share/firmware-config/functions/24_settings_cloud.yaml @@ -0,0 +1,59 @@ +settings: + remote_access: + label: Remote Access + items: + cloud: + label: Cloud Access (Experimental) + get_cmd: + - /usr/local/bin/extended-config.py + - get + - /home/lava/printer_data/config/extended/extended2.cfg + - remote_access + - cloud + - none + options: + none: + label: None + cmd: + - bash + - -xc + - | + /usr/local/bin/extended-config.py add /home/lava/printer_data/config/extended/extended2.cfg remote_access cloud none && + /etc/init.d/S99cloud stop + /usr/local/bin/octoeverywhere-pkg clean + octoeverywhere: + label: OctoEverywhere + cmd: + - bash + - -ec + - | + echo "WARNING: OctoEverywhere support is experimental. Higher resource usage may affect print quality." + sleep 5 + + echo ">> Downloading OctoEverywhere package..." + /usr/local/bin/octoeverywhere-pkg download + + echo ">> Enabling OctoEverywhere..." + /usr/local/bin/extended-config.py add /home/lava/printer_data/config/extended/extended2.cfg remote_access cloud octoeverywhere + + echo ">> Restarting OctoEverywhere service..." + /etc/init.d/S99cloud restart + + echo + echo "OctoEverywhere enabled. Please wait a moment or check the logs for the account linking URL." + echo "If you need help, follow this guide:" + echo "https://octoeverywhere.com/s/snapmaker-u1" + echo "Contact Support: https://octoeverywhere.com/support" + echo + + for i in {1..15}; do + echo ">> Looking for authorization URL in logs... ($i)" + if MATCH=$(grep -C 20 "This Plugin Isn't Connected To OctoEverywhere!" /home/lava/printer_data/logs/octoeverywhere.log); then + sed -n 's/.*WARNING //p' <<< "$MATCH" + exit 0 + fi + sleep 1 + done + + echo "Authorization URL not found in logs. Please check the logs for more details." + default: none