Skip to content

Commit ac25e30

Browse files
committed
Work to support the new Bambu Cloud.
1 parent b5778b1 commit ac25e30

File tree

7 files changed

+225
-70
lines changed

7 files changed

+225
-70
lines changed

.vscode/launch.json

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
]
3737
},
3838
{
39-
"name": "Bambu Connect - P1S",
39+
"name": "Bambu Connect - X1C",
4040
"type": "debugpy",
4141
"request": "launch",
4242
"module": "bambu_octoeverywhere",
@@ -51,7 +51,7 @@
5151
]
5252
},
5353
{
54-
"name": "Bambu Connect - X1C",
54+
"name": "Bambu Connect - P1S",
5555
"type": "debugpy",
5656
"request": "launch",
5757
"module": "bambu_octoeverywhere",
@@ -129,5 +129,23 @@
129129
"module": "octoprint_octoeverywhere",
130130
"justMyCode": true
131131
},
132+
{
133+
"name": "Docker Container Bootstrap & Run",
134+
"type": "debugpy",
135+
"request": "launch",
136+
"module": "docker_octoeverywhere",
137+
"justMyCode": false,
138+
"env": {
139+
"VENV_DIR": "/home/pi/octoeverywhere-env",
140+
"REPO_DIR": "/home/pi/octoeverywhere",
141+
"DATA_DIR": "/home/pi/.octoeverywhere-docker-bootstrap", // This path needs to be created.
142+
//"SERIAL_NUMBER": "serial",
143+
//"ACCESS_CODE": "test123"
144+
//"PRINTER_IP": "127.0.0.1",
145+
//"CONNECTION_MODE": "cloud",
146+
//"BAMBU_CLOUD_ACCOUNT_EMAIL":"[email protected]",
147+
//"BAMBU_CLOUD_ACCOUNT_PASSWORD": "test123",
148+
},
149+
},
132150
]
133151
}

Dockerfile

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Start with the lastest alpine, for a solid base,
1+
# Start with the latest alpine, for a solid base,
22
# since we need some advance binaries for things like pillow and ffmpeg.
33
FROM alpine:3.20.0
44

5-
# We will base ourselves in root, becuase why not.
5+
# We will base ourselves in root, because why not.
66
WORKDIR /root
77

88
# Define some user vars we will use for the image.
@@ -11,23 +11,23 @@ ENV USER=root
1111
ENV REPO_DIR=/root/octoeverywhere
1212
ENV VENV_DIR=/root/octoeverywhere-env
1313
# This is a special dir that the user MUST mount to the host, so that the data is persisted.
14-
# If this is not mounted, the printer will need to be re-linked everytime the container is remade.
14+
# If this is not mounted, the printer will need to be re-linked every time the container is remade.
1515
ENV DATA_DIR=/data/
1616

1717
# Install the required packages.
18-
# Any packages here should be mirrored in the install script - and any optaionl pillow packages done inline.
18+
# Any packages here should be mirrored in the install script - and any optional pillow packages done inline.
1919
# GCC, python3-dev, and musl-dev are required for pillow, and jpeg-dev and zlib-dev are required for jpeg support.
2020
RUN apk add --no-cache curl ffmpeg jq python3 python3-dev gcc musl-dev py3-pip py3-virtualenv jpeg-dev libjpeg-turbo-dev zlib-dev py3-pillow libffi-dev
2121

2222
#
23-
# We decided to not run the installer, since the point of the installer is to setup the env, build the launch args, and setup the serivce.
24-
# Instead, we will manually run the smaller subset of commands that are requred to get the env setup in docker.
23+
# We decided to not run the installer, since the point of the installer is to setup the env, build the launch args, and setup the service.
24+
# Instead, we will manually run the smaller subset of commands that are required to get the env setup in docker.
2525
# Note that if this ever becomes too much of a hassle, we might want to revert back to using the installer, and supporting a headless install.
2626
#
2727
RUN virtualenv -p /usr/bin/python3 ${VENV_DIR}
2828
RUN ${VENV_DIR}/bin/python -m pip install --upgrade pip
2929

30-
# Copy the entire repo into the image, do this as late as possible to avoid rebuilding the image everytime the repo changes.
30+
# Copy the entire repo into the image, do this as late as possible to avoid rebuilding the image every time the repo changes.
3131
COPY ./ ${REPO_DIR}/
3232
RUN ${VENV_DIR}/bin/pip3 install --require-virtualenv --no-cache-dir -q -r ${REPO_DIR}/requirements.txt
3333

@@ -36,7 +36,7 @@ RUN ${VENV_DIR}/bin/pip3 install --require-virtualenv --no-cache-dir -q -r ${REP
3636
RUN apk add zstd
3737
RUN ${VENV_DIR}/bin/pip3 install --require-virtualenv --no-cache-dir -q "zstandard>=0.21.0,<0.23.0"
3838

39-
# For docker, we use our docker_octoeverywhere host to handle the runtime setup and launch of the serivce.
39+
# For docker, we use our docker_octoeverywhere host to handle the runtime setup and launch of the service.
4040
WORKDIR ${REPO_DIR}
41-
# Use the full path to the venv, we msut use this [] notation for our ctlc handler to work in the contianer
41+
# Use the full path to the venv, we must use this [] notation for our ctlc handler to work in the container.
4242
ENTRYPOINT ["/root/octoeverywhere-env/bin/python", "-m", "docker_octoeverywhere"]

bambu_octoeverywhere/bambuclient.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,9 @@ def _TryToGetCloudConnectContext(self) -> ConnectionContext:
436436
if accessTokenResult.Status == LoginStatus.BadUserNameOrPassword:
437437
self.Logger.error("The email address or password is wrong. Re-run the Bambu Connect installer or use the docker files to update your email address and password.")
438438
elif accessTokenResult.Status == LoginStatus.TwoFactorAuthEnabled:
439-
self.Logger.error("To factor auth is enabled on this account. Bambu Lab doesn't allow us to support two factor auth, so it must be disabled on your account or LAN Only mode must be used on the printer.")
439+
self.Logger.error("Two factor auth is enabled on this account. Bambu Lab doesn't allow us to support two factor auth, so it must be disabled on your account or the local connection mode.")
440+
elif accessTokenResult.Status == LoginStatus.EmailCodeRequired:
441+
self.Logger.error("This account requires an email code to login. Bambu Lab doesn't allow us to support this, so you must use the local connection mode.")
440442
else:
441443
self.Logger.error("Unknown error, we will try again later.")
442444
self.Logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

bambu_octoeverywhere/bambucloud.py

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ class LoginStatus(Enum):
1717
Success = 0 # This is the only successful value
1818
TwoFactorAuthEnabled = 1
1919
BadUserNameOrPassword = 2
20-
UnknownError = 3
20+
EmailCodeRequired = 3
21+
UnknownError = 4
2122

2223

2324
# The result of a get access token request.
@@ -61,17 +62,19 @@ def Login(self) -> LoginStatus:
6162
# We also don't gain anything by storing the access token, since we need to hit an API anyways to make sure it's still valid and working.
6263
self.Logger.info("Logging into Bambu Cloud...")
6364

64-
# Get the correct URL.
65-
url = self._GetBambuCloudApi("/v1/user-service/user/login")
66-
6765
# Get the context.
6866
email, password = self.GetContext()
6967
if email is None or password is None:
7068
self.Logger.error("Login Bambu Cloud failed to get context from the config.")
7169
return LoginStatus.BadUserNameOrPassword
7270

71+
# Mocks the login request form bambu/orca slicer.
72+
data = {'account': email, 'password': password, "apiError": ""}
73+
7374
# Make the request.
74-
response = requests.post(url, json={'account': email, 'password': password}, timeout=30)
75+
response = self._DoBambuCloudApiRequest("POST", "/v1/user-service/user/login", data)
76+
77+
raise Exception(" TODO - We never finished this because we can't login with the email code. We also need to convert GetDeviceList to use _DoBambuCloudApiRequest ")
7578

7679
# Check the response.
7780
if response.status_code != 200:
@@ -88,10 +91,18 @@ def Login(self) -> LoginStatus:
8891

8992
# If the user has two factor auth enabled, this will still return 200, but there will be a tfaKey field with a string.
9093
j = response.json()
91-
tfaKey = j.get('tfaKey', None)
92-
if tfaKey is not None and len(tfaKey) > 0:
94+
loginType = j.get("loginType", None)
95+
if loginType is None:
96+
# This is unexpected, but if we get an access token we don't care.
97+
self.Logger.warn("Bambu cloud loginType not found")
98+
elif loginType == 'tfa':
9399
self.Logger.error("Login Bambu Cloud failed because two factor auth is enabled. Bambu Lab's APIs don't allow us to support two factor at this time.")
94100
return LoginStatus.TwoFactorAuthEnabled
101+
elif loginType == 'verifyCode':
102+
self.Logger.error("Login Bambu Cloud failed because an email two factor code is required. Bambu Lab's APIs don't allow us to support two factor at this time.")
103+
return LoginStatus.EmailCodeRequired
104+
else:
105+
self.Logger.warn(f"Bambu cloud loginType unknown. {loginType}")
95106

96107
# Try to get the access token
97108
accessToken = j.get('accessToken', None)
@@ -242,6 +253,35 @@ def _GetBambuCloudApi(self, urlPathAndParams:str):
242253
return "https://api.bambulab.com" + urlPathAndParams
243254

244255

256+
def _DoBambuCloudApiRequest(self, method:str, urlPathAndParams:str, jsonPayload:dict = None) -> requests.Response:
257+
try:
258+
# Get the correct URL.
259+
url = self._GetBambuCloudApi(urlPathAndParams)
260+
261+
# We need to add some headers to allow the request to go through.
262+
# These headers are emulated from Bambu/Orca slicer.
263+
sendHeaders = {
264+
'User-Agent': 'bambu_network_agent/01.09.05.01',
265+
'X-BBL-Client-Name': 'OrcaSlicer',
266+
'X-BBL-Client-Type': 'slicer',
267+
'X-BBL-Client-Version': '01.09.05.51',
268+
'X-BBL-Language': 'en-US',
269+
'X-BBL-OS-Type': 'linux',
270+
'X-BBL-OS-Version': '6.2.0',
271+
'X-BBL-Agent-Version': '01.09.05.01',
272+
'X-BBL-Executable-info': '{}',
273+
'X-BBL-Agent-OS-Type': 'linux',
274+
'accept': 'application/json',
275+
'Content-Type': 'application/json'
276+
}
277+
278+
# Make the request.
279+
return requests.request(method, url, json=jsonPayload, headers=sendHeaders, timeout=30)
280+
except Exception as e:
281+
Sentry.Exception("_DoBambuCloudApiRequest exception", e)
282+
return None
283+
284+
245285
# Sets the user's context into the config file.
246286
def SetContext(self, email:str, p:str) -> bool:
247287
try:

docker-compose.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@ services:
99
- SERIAL_NUMBER=XXXXXXXXXXXXXXX
1010
# Find using the printer's display or use https://octoeverywhere.com/s/bambu-ip
1111
- PRINTER_IP=XXX.XXX.XXX.XXX
12+
13+
# Optionally: If you want to connect via the Bambu Cloud, you can specify the following environment variables.
14+
# By default the plugin will use the local connection mode, which is preferred.
15+
#
16+
# If you use Bambu Cloud, you MUST disable 2 factor authentication, because Bambu does not allow us to support it.
17+
# Your Bambu email address and password are KEPT LOCALLY, securely on disk, and are NEVER SENT to the OctoEverywhere service
18+
19+
# - BAMBU_CLOUD_ACCOUNT_PASSWORD=supersecretpassword
20+
1221
volumes:
13-
# This can also be an absolue path, e.g. /var/octoeverywhere/plugin/data or /c/users/name/plugin/data
22+
# This can also be an absolute path, e.g. /var/octoeverywhere/plugin/data or /c/users/name/plugin/data
1423
- ./data:/data
1524

1625
# Add as many printers as you want!
@@ -23,6 +32,15 @@ services:
2332
# - SERIAL_NUMBER=XXXXXXXXXXXXXXX
2433
# # Find using the printer's display or use https://octoeverywhere.com/s/bambu-ip
2534
# - PRINTER_IP=XXX.XXX.XXX.XXX
35+
#
36+
# # Optionally: If you want to connect via the Bambu Cloud, you can specify the following environment variables.
37+
# # By default the plugin will use the local connection mode, which is preferred.
38+
# #
39+
# # If you use Bambu Cloud, you MUST disable 2 factor authentication, because Bambu does not allow us to support it.
40+
# # Your Bambu email address and password are KEPT LOCALLY, securely on disk, and are NEVER SENT to the OctoEverywhere service
41+
42+
# # - BAMBU_CLOUD_ACCOUNT_PASSWORD=supersecretpassword
43+
#
2644
# volumes:
2745
# # Specify a path mapping for the required persistent storage
2846
# - ./data:/data

0 commit comments

Comments
 (0)