Skip to content

Commit

Permalink
work
Browse files Browse the repository at this point in the history
  • Loading branch information
QuinnDamerell committed Nov 26, 2024
1 parent 55f5891 commit db0629d
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 52 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
]
},
{
"name": "Bambu Connect - P1S",
"name": "Bambu Connect - X1C",
"type": "debugpy",
"request": "launch",
"module": "bambu_octoeverywhere",
Expand All @@ -51,7 +51,7 @@
]
},
{
"name": "Bambu Connect - X1C",
"name": "Bambu Connect - P1S",
"type": "debugpy",
"request": "launch",
"module": "bambu_octoeverywhere",
Expand Down
175 changes: 125 additions & 50 deletions docker_octoeverywhere/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ def CreateDirIfNotExists(path: str) -> None:
config = Config(configPath)


#
#
# Step 1: Ensure all required vars are set.
#
#

# The serial number is always required, in both Bambu Cloud and LAN mode.
# So we always get that first.
printerSn = os.environ.get("SERIAL_NUMBER", None)
Expand All @@ -84,7 +90,7 @@ def CreateDirIfNotExists(path: str) -> None:
logger.error("")
logger.error("")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error(" You must pass the printer's Serial Number as an env var.")
logger.error(" You must provide your printer's Serial Number.")
logger.error("Use `docker run -e SERIAL_NUMBER=<token>` or add it to your docker-compose file.")
logger.error("")
logger.error(" To find your Serial Number -> https://octoeverywhere.com/s/bambu-sn")
Expand All @@ -95,39 +101,137 @@ def CreateDirIfNotExists(path: str) -> None:
time.sleep(5.0)
sys.exit(1)

# The access code is also always required, in both Bambu Cloud and LAN mode.
# In cloud mode the we can get the access code from the service, but it's often wrong...?
# If there is a arg passed, always update or set it.
# This allows users to update the values after the image has ran the first time.
accessCode = os.environ.get("ACCESS_CODE", None)
if accessCode is not None:
logger.info(f"Setting Access Code: {accessCode}")
config.SetStr(Config.SectionBambu, Config.BambuAccessToken, accessCode)
# Ensure something is set now.
if config.GetStr(Config.SectionBambu, Config.BambuAccessToken, None) is None:
logger.error("")
logger.error("")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error(" You must provide your printer's Access Code.")
logger.error(" Use `docker run -e ACCESS_CODE=<code>` or add it to your docker-compose file.")
logger.error("")
logger.error(" To find your Access Code -> https://octoeverywhere.com/s/access-code")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error("")
logger.error("")
# Sleep some, so we don't restart super fast and then exit.
time.sleep(5.0)
sys.exit(1)

# For now, we also need the user to supply the printer's IP address in both modes, since we can't auto scan the network in docker.
# We also need this for the Bambu Cloud mode, since we can't get it from the Bambu Cloud API and we can't scan for the printer.
printerId = os.environ.get("PRINTER_IP", None)
if printerId is not None:
logger.info(f"Setting Printer IP: {printerId}")
config.SetStr(Config.SectionCompanion, Config.CompanionKeyIpOrHostname, printerId)
# Ensure something is set now.
if config.GetStr(Config.SectionCompanion, Config.CompanionKeyIpOrHostname, None) is None:
logger.error("")
logger.error("")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error(" You must provide your printer's IP Address.")
logger.error(" Use `docker run -e PRINTER_IP=<ip address>` or add it to your docker-compose file.")
logger.error("")
logger.error(" To find your printer's IP Address -> https://octoeverywhere.com/s/bambu-ip")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error("")
logger.error("")
# Sleep some, so we don't restart super fast and then exit.
time.sleep(5.0)
sys.exit(1)

# The port is always the same, so we just set the known Bambu Lab printer port.
if config.GetStr(Config.SectionCompanion, Config.CompanionKeyPort, None) is None:
config.SetStr(Config.SectionCompanion, Config.CompanionKeyPort, "8883")

#
#
# Step 2: Determine the connection mode. Bambu Cloud or LAN.
#
#
# Bambu updated the printer and broke LAN access unless the printer is in LAN mode.
# The work around was to connect to the Bambu Cloud instead of directly to the printer.
# The biggest downside of this is that we need to get the user's email address and password for Bambu Cloud.
# BUT the user can also do the LAN only mode, if they want to.
isLanOnlyMode = bool(os.environ.get("LAN_ONLY_MODE", "").lower() in ("true", "1", "yes"))
isAccessCodeRequired = True
if isLanOnlyMode:
# In LAN only mode we only need the Serial number and access code.
logger.info("Connection Mode: LAN Only (Use the env var LAN_ONLY_MODE=FALSE to enable Bambu Cloud mode.)")
# This is LAN only mode, where we need the user to get us the Access Code. (In the cloud mode, we can get it from the Bambu Cloud API)
# If there is a arg passed, always update or set it.
# This allows users to update the values after the image has ran the first time.
accessCode = os.environ.get("ACCESS_CODE", None)
if accessCode is not None:
logger.info(f"Setting Access Code: {accessCode}")
config.SetStr(Config.SectionBambu, Config.BambuAccessToken, accessCode)
# Ensure something is set now.
if config.GetStr(Config.SectionBambu, Config.BambuAccessToken, None) is None:
#
# It seems that bambu may have walked back on the no LAN access, so for now we default to the LAN mode, which is preferred.
useBambuCloud = False
envVarBambuCloudEmailKey = "BAMBU_CLOUD_ACCOUNT_EMAIL"
envVarBambuCloudPasswordKey = "BAMBU_CLOUD_ACCOUNT_PASSWORD"
envVarConnectionModeKey = "CONNECTION_MODE"

# First, we will see if the user explicitly set the mode.
connectionModeVar = os.environ.get(envVarConnectionModeKey, None)
if connectionModeVar is not None:
# Ensure the passed value is what we expect.
connectionModeVar = connectionModeVar.lower().trim()
if connectionModeVar == "cloud" or connectionModeVar == "bambucloud":
useBambuCloud = True
elif connectionModeVar == "local":
useBambuCloud = False
else:
logger.error("")
logger.error("")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error(" You must pass the printer's Access Code as an env var.")
logger.error(" Use `docker run -e ACCESS_CODE=<code>` or add it to your docker-compose file.")
logger.error(" Invalid CONNECTION_MODE value passed.")
logger.error("")
logger.error(" To connect via the Bambu Cloud use the value of `cloud`")
logger.error(" To make a local connection via your local network use the value of `local`")
logger.error("")
logger.error(" Use `docker run -e CONNECTION_MODE=<mode>` or add it to your docker-compose file.")
logger.error("")
logger.error(" To find your Access Code -> https://octoeverywhere.com/s/access-code")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error("")
logger.error("")
# Sleep some, so we don't restart super fast and then exit.
time.sleep(5.0)
sys.exit(1)
logger.info(f"Connection mode specified as: {connectionModeVar}. Use Cloud Connection Mode: {useBambuCloud}")
else:
logger.info("Connection Mode: Bambu Cloud (Use the env var LAN_ONLY_MODE=TRUE to enable LAN Only mode.)")
# If the user didn't explicitly pick a mode, we will figure it out based on what they passed.
# This is a legacy flag, check if first.
lanOnlyMode = os.environ.get("LAN_ONLY_MODE", None)
if lanOnlyMode is not None:
useBambuCloud = not bool(lanOnlyMode.lower().trim() in ("true", "1", "yes"))
logger.info(f"LAN_ONLY_MODE specified as: {lanOnlyMode}. Use Cloud Connection Mode: {useBambuCloud}")
else:
# If there are no explicit flags, check if it's set in the config.
configMode = config.GetStr(Config.SectionBambu, Config.BambuConnectionMode, None)
if configMode is not None:
if configMode == "cloud":
useBambuCloud = True
elif configMode == "local":
useBambuCloud = False
else:
logger.error(f"Invalid Bambu Connection Mode in config: {configMode}, defaulting to local.")
useBambuCloud = False
logger.info(f"Connection mode found in the OctoEverywhere config as `{configMode}`.")
else:
# There's nothing passed and nothing in the config, figure it out based on if they user passed the email and password.
if os.environ.get(envVarBambuCloudEmailKey, None) is not None or os.environ.get(envVarBambuCloudPasswordKey, None) is not None:
useBambuCloud = True
logger.info("Bambu cloud email or password supplied, so we will use Bambu Cloud connection mode.")
else:
useBambuCloud = False
logger.info("No bambu cloud email or password passed, so we will use the preferred local connection mode.")
logger.info(f"If you want to change the connection mode, use `{envVarConnectionModeKey}` which can be set to 'cloud' or 'local'.")

# Explicitly set the mode in the config.
config.SetStr(Config.SectionBambu, Config.BambuConnectionMode, "cloud" if useBambuCloud else "local")

#
#
# Step 3: Get any required Bambu Cloud values.
#
#
if useBambuCloud:
# In Bambu Cloud mode, we need the user's email and password.
bambuCloud = BambuCloud(logger, config)
# Get any existing values.
Expand All @@ -140,7 +244,7 @@ def CreateDirIfNotExists(path: str) -> None:
logger.error("")
logger.error("")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error(" You must pass your Bambu Cloud account email address as an env var.")
logger.error(" You must provide your Bambu Cloud account email address.")
logger.error("Use `docker run -e BAMBU_CLOUD_ACCOUNT_EMAIL=<email>` or add it to your docker-compose file.")
logger.error("")
logger.error(" Your Bambu email address and password are KEPT LOCALLY, encrypted on disk")
Expand All @@ -157,7 +261,7 @@ def CreateDirIfNotExists(path: str) -> None:
logger.error("")
logger.error("")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error(" You must pass your Bambu Cloud account password as an env var.")
logger.error(" You must provide your Bambu Cloud account password.")
logger.error("Use `docker run -e BAMBU_CLOUD_ACCOUNT_PASSWORD=<password>` or add it to your docker-compose file.")
logger.error("")
logger.error(" Your Bambu email address and password are KEPT LOCALLY, encrypted on disk")
Expand Down Expand Up @@ -190,35 +294,6 @@ def CreateDirIfNotExists(path: str) -> None:
logger.info("Setting Bambu Cloud to the default value for world wide accounts.")
config.SetStr(Config.SectionBambu, Config.BambuCloudRegion, "worldwide")

# For now, we also need the user to supply the printer's IP address, since we can't auto scan the network in docker.
# We also need this for the Bambu Cloud mode, since we can't get it from the Bambu Cloud API and we can't scan for the printer.
printerId = os.environ.get("PRINTER_IP", None)
if printerId is not None:
logger.info(f"Setting Printer IP: {printerId}")
config.SetStr(Config.SectionCompanion, Config.CompanionKeyIpOrHostname, printerId)
# Ensure something is set now.
if config.GetStr(Config.SectionCompanion, Config.CompanionKeyIpOrHostname, None) is None:
logger.error("")
logger.error("")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error(" You must pass the printer's IP Address as an env var.")
logger.error(" Use `docker run -e PRINTER_IP=<ip address>` or add it to your docker-compose file.")
logger.error("")
logger.error(" To find your printer's IP Address -> https://octoeverywhere.com/s/bambu-ip")
logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
logger.error("")
logger.error("")
# Sleep some, so we don't restart super fast and then exit.
time.sleep(5.0)
sys.exit(1)

# The port is always the same, so we just set the known Bambu Lab printer port.
if config.GetStr(Config.SectionCompanion, Config.CompanionKeyPort, None) is None:
config.SetStr(Config.SectionCompanion, Config.CompanionKeyPort, "8883")

# We don't set the IP address of the printer. The Bambu Connect plugin will automatically find the printer
# on the local network using the Access Token and SN. By not setting the value, it will force it to search first.

# Create the rest of the required dirs based in the data dir, since it's persistent.
localStoragePath = os.path.join(dataPath, "octoeverywhere-store")
CreateDirIfNotExists(localStoragePath)
Expand Down
2 changes: 2 additions & 0 deletions linux_host/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class Config:
# Used only for Bambu Connect
#
SectionBambu = "bambu"
BambuConnectionMode = "connection_mode" # Explicitly defines what connection mode we are using. Can be "cloud" or "local"
BambuAccessToken = "access_token"
BambuPrinterSn = "printer_serial_number"
# Used if the user is logged into Bambu Cloud
Expand All @@ -76,6 +77,7 @@ class Config:
{ "Target": CompanionKeyPort, "Comment": "The port this companion plugin will use to connect to Moonraker. The OctoEverywhere plugin service needs to be restarted before changes will take effect."},
{ "Target": BambuAccessToken, "Comment": "The access token to the Bambu printer. It can be found using the LCD screen on the printer, in the settings. The OctoEverywhere plugin service needs to be restarted before changes will take effect."},
{ "Target": BambuPrinterSn, "Comment": "The serial number of your Bambu printer. It can be found using this guide: https://wiki.bambulab.com/en/general/find-sn The OctoEverywhere plugin service needs to be restarted before changes will take effect."},
{ "Target": BambuConnectionMode,"Comment": "The connection mode used for Bambu Connect. Can be 'cloud' or 'local'. 'cloud' will use the bambu cloud which requires the user's email and password to be set, `local` will connect via the LAN."},
{ "Target": WebcamNameToUseAsPrimary, "Comment": "This is the webcam name OctoEverywhere will use for Gadget AI, notifications, and such. This much match the camera 'Name' from your Mainsail of Fluidd webcam settings. The default value of 'Default' will pick whatever camera the system can find."},
{ "Target": WebcamAutoSettings, "Comment": "Enables or disables auto webcam setting detection. If enabled, OctoEverywhere will find the webcam settings configured via the frontend (Fluidd, Mainsail, etc) and use them. Disable to manually set the values and have them not be overwritten."},
{ "Target": WebcamStreamUrl, "Comment": "Webcam streaming URL. This can be a local relative path (ex: /webcam/?action=stream) or absolute http URL (ex: http://10.0.0.1:8080/webcam/?action=stream or http://webcam.local/webcam/?action=stream)"},
Expand Down

0 comments on commit db0629d

Please sign in to comment.