0.1.8rc11

update pybambu/bambu_cloud.py from upstream HA project
This commit is contained in:
jneilliii 2024-11-14 23:35:03 -05:00
parent b54e372342
commit 98ab94b371
2 changed files with 102 additions and 30 deletions

View File

@ -3,7 +3,11 @@ from __future__ import annotations
import base64
import json
from curl_cffi import requests
curl_available = True
try:
from curl_cffi import requests as curl_requests
except ImportError:
curl_available = False
from dataclasses import dataclass
@ -33,6 +37,9 @@ class BambuCloud:
def _get_authentication_token(self) -> dict:
LOGGER.debug("Getting accessToken from Bambu Cloud")
if not curl_available:
LOGGER.debug(f"Curl library is unavailable.")
return 'curlUnavailable'
# First we need to find out how Bambu wants us to login.
data = {
@ -41,7 +48,14 @@ class BambuCloud:
"apiError": ""
}
response = requests.post(get_Url(BambuUrl.LOGIN, self._region), json=data, impersonate=IMPERSONATE_BROWSER)
response = curl_requests.post(get_Url(BambuUrl.LOGIN, self._region), json=data, impersonate=IMPERSONATE_BROWSER)
# Check specifically for cloudflare block
if response.status_code == 403:
if 'cloudflare' in response.text:
LOGGER.error('CloudFlare blocked connection attempt')
return 'cloudFlare'
if response.status_code >= 400:
LOGGER.error(f"Login attempt failed with error code: {response.status_code}")
LOGGER.debug(f"Response: '{response.text}'")
@ -80,7 +94,7 @@ class BambuCloud:
}
LOGGER.debug("Requesting verification code")
response = requests.post(get_Url(BambuUrl.EMAIL_CODE, self._region), json=data, impersonate=IMPERSONATE_BROWSER)
response = curl_requests.post(get_Url(BambuUrl.EMAIL_CODE, self._region), json=data, impersonate=IMPERSONATE_BROWSER)
if response.status_code == 200:
LOGGER.debug("Verification code requested successfully.")
@ -96,7 +110,7 @@ class BambuCloud:
"code": code
}
response = requests.post(get_Url(BambuUrl.LOGIN, self._region), json=data, impersonate=IMPERSONATE_BROWSER)
response = curl_requests.post(get_Url(BambuUrl.LOGIN, self._region), json=data, impersonate=IMPERSONATE_BROWSER)
LOGGER.debug(f"Response: {response.status_code}")
if response.status_code == 200:
@ -128,7 +142,7 @@ class BambuCloud:
"tfaCode": code
}
response = requests.post(get_Url(BambuUrl.TFA_LOGIN, self._region), json=data, impersonate=IMPERSONATE_BROWSER)
response = curl_requests.post(get_Url(BambuUrl.TFA_LOGIN, self._region), json=data, impersonate=IMPERSONATE_BROWSER)
LOGGER.debug(f"Response: {response.status_code}")
if response.status_code == 200:
@ -209,13 +223,11 @@ class BambuCloud:
self._password = password
result = self._get_authentication_token()
if result == 'verifyCode':
return result
elif result == 'tfa':
return result
elif result is None:
if result is None:
LOGGER.error("Unable to authenticate.")
return None
elif len(result) < 20:
return result
else:
self._auth_token = result
self._username = self._get_username_from_authentication_token()
@ -223,7 +235,7 @@ class BambuCloud:
def login_with_verification_code(self, code: str):
result = self._get_authentication_token_with_verification_code(code)
if result == 'codeExpired' or result == 'codeIncorrect':
if len(result) < 20:
return result
self._auth_token = result
self._username = self._get_username_from_authentication_token()
@ -231,16 +243,29 @@ class BambuCloud:
def login_with_2fa_code(self, code: str):
result = self._get_authentication_token_with_2fa_code(code)
if len(result) < 20:
return result
self._auth_token = result
self._username = self._get_username_from_authentication_token()
return 'success'
def get_device_list(self) -> dict:
LOGGER.debug("Getting device list from Bambu Cloud")
response = requests.get(get_Url(BambuUrl.BIND, self._region), headers=self._get_headers_with_auth_token(), timeout=10, impersonate=IMPERSONATE_BROWSER)
if not curl_available:
LOGGER.debug(f"Curl library is unavailable.")
raise None
response = curl_requests.get(get_Url(BambuUrl.BIND, self._region), headers=self._get_headers_with_auth_token(), timeout=10, impersonate=IMPERSONATE_BROWSER)
if response.status_code == 403:
if 'cloudflare' in response.text:
LOGGER.error('CloudFlare blocked connection attempt')
raise ValueError(response.status_code)
if response.status_code >= 400:
LOGGER.debug(f"Received error: {response.status_code}")
LOGGER.error(f"Received error: '{response.text}'")
raise ValueError(response.status_code)
return response.json()['devices']
# The slicer settings are of the following form:
@ -311,13 +336,23 @@ class BambuCloud:
# }
def get_slicer_settings(self) -> dict:
LOGGER.debug("Getting slicer settings from Bambu Cloud")
response = requests.get(get_Url(BambuUrl.SLICER_SETTINGS, self._region), headers=self._get_headers_with_auth_token(), timeout=10, impersonate=IMPERSONATE_BROWSER)
if response.status_code >= 400:
LOGGER.error(f"Slicer settings load failed: {response.status_code}")
LOGGER.error(f"Slicer settings load failed: '{response.text}'")
return None
return response.json()
LOGGER.debug("DISABLED: Getting slicer settings from Bambu Cloud")
# Disabled for now since it may be contributing to cloudflare detection speed.
#
# if curl_available:
# response = curl_requests.get(get_Url(BambuUrl.SLICER_SETTINGS, self._region), headers=self._get_headers_with_auth_token(), timeout=10, impersonate=IMPERSONATE_BROWSER)
# if response.status_code == 403:
# if 'cloudflare' in response.text:
# LOGGER.error(f"Cloudflare blocked slicer settings lookup.")
# return None
# if response.status_code >= 400:
# LOGGER.error(f"Slicer settings load failed: {response.status_code}")
# LOGGER.error(f"Slicer settings load failed: '{response.text}'")
# return None
# return response.json()
return None
# The task list is of the following form with a 'hits' array with typical 20 entries.
#
@ -362,24 +397,46 @@ class BambuCloud:
# },
def get_tasklist(self) -> dict:
LOGGER.debug("Getting full task list from Bambu Cloud")
if not curl_available:
LOGGER.debug(f"Curl library is unavailable.")
raise None
url = get_Url(BambuUrl.TASKS, self._region)
response = requests.get(url, headers=self._get_headers_with_auth_token(), timeout=10, impersonate=IMPERSONATE_BROWSER)
response = curl_requests.get(url, headers=self._get_headers_with_auth_token(), timeout=10, impersonate=IMPERSONATE_BROWSER)
if response.status_code == 403:
if 'cloudflare' in response.text:
LOGGER.error('CloudFlare blocked connection attempt')
return None
# Check specifically for cloudflare block
if response.status_code == 403:
if 'cloudflare' in response.text:
LOGGER.error('CloudFlare blocked connection attempt')
return None
if response.status_code >= 400:
LOGGER.debug(f"Received error: {response.status_code}")
LOGGER.debug(f"Received error: '{response.text}'")
raise ValueError(response.status_code)
raise None
return response.json()
def get_latest_task_for_printer(self, deviceId: str) -> dict:
LOGGER.debug(f"Getting latest task from Bambu Cloud")
data = self.get_tasklist_for_printer(deviceId)
if len(data) != 0:
return data[0]
LOGGER.debug("No tasks found for printer")
LOGGER.debug(f"Getting latest task for printer from Bambu Cloud")
try:
data = self.get_tasklist_for_printer(deviceId)
if len(data) != 0:
return data[0]
LOGGER.debug("No tasks found for printer")
except:
LOGGER.debug("Unable to make call")
return None
return None
def get_tasklist_for_printer(self, deviceId: str) -> dict:
LOGGER.debug(f"Getting task list from Bambu Cloud")
LOGGER.debug(f"Getting full task list for printer from Bambu Cloud")
tasks = []
data = self.get_tasklist()
for task in data['hits']:
@ -394,10 +451,21 @@ class BambuCloud:
def download(self, url: str) -> bytearray:
LOGGER.debug(f"Downloading cover image: {url}")
response = requests.get(url, timeout=10, impersonate=IMPERSONATE_BROWSER)
if not curl_available:
LOGGER.debug(f"Curl library is unavailable.")
return None
response = curl_requests.get(url, timeout=10, impersonate=IMPERSONATE_BROWSER)
if response.status_code == 403:
if 'cloudflare' in response.text:
LOGGER.error('CloudFlare blocked connection attempt')
raise ValueError(response.status_code)
if response.status_code >= 400:
LOGGER.debug(f"Received error: {response.status_code}")
LOGGER.debug(f"Received error: {response.text}")
raise ValueError(response.status_code)
return response.content
@property
@ -408,6 +476,10 @@ class BambuCloud:
def auth_token(self):
return self._auth_token
@property
def bambu_connected(self) -> bool:
return self._auth_token != "" and self._auth_token != None
@property
def cloud_mqtt_host(self):
return "cn.mqtt.bambulab.com" if self._region == "China" else "us.mqtt.bambulab.com"

View File

@ -14,7 +14,7 @@ plugin_package = "octoprint_bambu_printer"
plugin_name = "OctoPrint-BambuPrinter"
# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
plugin_version = "0.1.8rc10"
plugin_version = "0.1.8rc11"
# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
# module