From db0629da81a9c5035f3492088a8f52c98bcfec11 Mon Sep 17 00:00:00 2001 From: Quinn Damerell Date: Tue, 26 Nov 2024 11:40:25 -0800 Subject: [PATCH] work --- .vscode/launch.json | 4 +- docker_octoeverywhere/__main__.py | 175 +++++++++++++++++++++--------- linux_host/config.py | 2 + 3 files changed, 129 insertions(+), 52 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c54e0bb..e21ac5e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -36,7 +36,7 @@ ] }, { - "name": "Bambu Connect - P1S", + "name": "Bambu Connect - X1C", "type": "debugpy", "request": "launch", "module": "bambu_octoeverywhere", @@ -51,7 +51,7 @@ ] }, { - "name": "Bambu Connect - X1C", + "name": "Bambu Connect - P1S", "type": "debugpy", "request": "launch", "module": "bambu_octoeverywhere", diff --git a/docker_octoeverywhere/__main__.py b/docker_octoeverywhere/__main__.py index 7b7b7e0..3890766 100644 --- a/docker_octoeverywhere/__main__.py +++ b/docker_octoeverywhere/__main__.py @@ -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) @@ -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=` or add it to your docker-compose file.") logger.error("") logger.error(" To find your Serial Number -> https://octoeverywhere.com/s/bambu-sn") @@ -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=` 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=` 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=` 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=` 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. @@ -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=` or add it to your docker-compose file.") logger.error("") logger.error(" Your Bambu email address and password are KEPT LOCALLY, encrypted on disk") @@ -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=` or add it to your docker-compose file.") logger.error("") logger.error(" Your Bambu email address and password are KEPT LOCALLY, encrypted on disk") @@ -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=` 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) diff --git a/linux_host/config.py b/linux_host/config.py index 2a9df2a..cb5ac33 100644 --- a/linux_host/config.py +++ b/linux_host/config.py @@ -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 @@ -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)"},