Skip to content

Instantly share code, notes, and snippets.

@lin-ycv
Last active November 15, 2024 07:09
Show Gist options
  • Save lin-ycv/03b908746b08c9b9093d88822e743968 to your computer and use it in GitHub Desktop.
Save lin-ycv/03b908746b08c9b9093d88822e743968 to your computer and use it in GitHub Desktop.
Apps Script to communicate with Bambu Cloud API and get printer status
// based on https://github.com/t0nyz0/BambuBoard/blob/main/bambuConnection.js
// and https://github.com/Doridian/OpenBambuAPI/blob/main/cloud-http.md
// https://bambulab.com/api/sign-in/form give 403 forbidden
const sProp = PropertiesService.getScriptProperties();
const account = sProp.getProperty('account');
const password = sProp.getProperty('password');
const sheetid = sProp.getProperty('sheetid');
const api = (e) => 'https://api.bambulab.com/v1/' + e;
const login = 'user-service/user/login';
const refresh = 'user-service/user/refreshtoken';
const bind = 'iot-service/api/user/bind';
const print = 'iot-service/api/user/print';
const tasks = 'user-service/my/tasks';
let postReq = {
'method': 'POST',
'muteHttpExceptions': true,
'headers': {
'Content-Type': 'application/json'
},
'payload': ''
},
getReq = {
'method': 'GET',
'muteHttpExceptions': true,
'headers': {
"Authorization": ''
}
};
function loginPassword() {
postReq.payload = JSON.stringify({
account: account,
password: password,
});
let r = UrlFetchApp.fetch(api(login), postReq);
let http = r.getResponseCode();
Logger.log(http);
if (http != 200) { setError(); return; }
let body = JSON.parse(r.getContentText());
Logger.log(body);
if (body.loginType === 'verifyCode') {
setError();
Logger.log('Requires verification code');
return;
}
getToken(body);
}
function loginCode() {
const cell = SpreadsheetApp.openById(sheetid).getSheetByName('Sheet2').getRange('A1');
let code = cell.getValue();
console.log(code);
if (!code) { setError(); Logger.log("Terminated: No code provided"); return; }
cell.setValue("");
postReq.payload = JSON.stringify({
account: account,
code: String(code),
});
let r = UrlFetchApp.fetch(api(login), postReq);
let body = JSON.parse(r.getContentText());
Logger.log(body);
let http = r.getResponseCode();
Logger.log(http);
if (http != 200) { setError(); return; }
getToken(body);
}
function refreshToken() {
postReq.payload = JSON.stringify({
refreshToken: sProp.getProperty('rToken')
});
let r = UrlFetchApp.fetch(api(refresh), postReq);
let http = r.getResponseCode();
Logger.log(http);
if (http != 200) { setError(); return; }
let body = JSON.parse(r.getContentText());
Logger.log(body);
getToken(body);
}
function getToken(j) {
const token = j.accessToken || null;
const rToken = j.refreshToken || null;
const expires = new Date(Date.now() + j.expiresIn * 1000) || null;
if (!token || token.length === 0) {
setError();
Logger.log("Login Bambu Cloud failed because access token was not found in the response.");
return;
}
setError(0);
getReq.headers.Authorization = 'Bearer ' + token;
sProp.setProperties({ token: token, rToken: rToken, expires: expires.toISOString() });
Logger.log(`Login Bambu Cloud access token expires on ${expires} in ${j.expiresIn} seconds`);
}
function setAuthToken() {
getReq.headers.Authorization = 'Bearer ' + sProp.getProperty('token');
}
function getStatus() {
if (Number(sProp.getProperty('ERROR_Count')) > 2) { Logger.log("Too many errors occured previously, don't run"); return; }
if (Date.now() >= (new Date(sProp.getProperty('expires')) - 2629746000)) refreshToken(); // refresh if there's only 1 month left
setAuthToken();
let r, body, http;
// r = UrlFetchApp.fetch(api(bind), getReq);
// body = JSON.parse(r.getContentText());
// http = r.getResponseCode();
// if (http == 200) readBind(body);
// else Logger.log(http + ' Get bind failed');
// r = UrlFetchApp.fetch(api(print), getReq);
// body = JSON.parse(r.getContentText());
// http = r.getResponseCode();
// if (http == 200) readPrint(body);
// else Logger.log(http + ' Get tasks failed');
r = UrlFetchApp.fetch(api(tasks + '?limit=20'), getReq);
body = JSON.parse(r.getContentText());
http = r.getResponseCode();
if (http == 200) { setError(0); readTasks(body); }
else { setError(); Logger.log(http + ' Get tasks failed'); }
}
// function readBind(body) {
// Logger.log(body.devices);
// for (let j of body.devices) {
// Logger.log(j.name);
// Logger.log(j.print_status); // RUNNING
// }
// }
// function readPrint(body){
// Logger.log(body.devices);
// for (let j of body.devices) {
// Logger.log(j.task_name);
// Logger.log(j.task_status);
// Logger.log(j.start_time);
// }
// }
function readTasks(body) {
for (let j of body.hits) {
j.startTime = new Date(j.startTime);
if ((new Date(j.endTime) - j.startTime) > 60000) continue;
j.endTime = new Date(j.startTime.getTime() + j.costTime * 1000);
j.deviceName = j.deviceName.slice(-1);
Logger.log(j.deviceName);
Logger.log(j.title);
Logger.log(j.startTime);
Logger.log(j.endTime);
}
}
function setError(inc = true) {
let e = 'ERROR_Count';
let v = inc ? Number(sProp.getProperty(e)) + 1 : 0;
sProp.setProperty(e, v);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment