@hiv3d/bambu-node
Version:
A node.js library for connecting to and receiving data from Bambu Lab printers through their MQTT servers.
303 lines (217 loc) • 9.12 kB
Markdown
# Bambu Node
> [!CAUTION]
> 🚧 This library is still in the making. PRs and other feature requests are welcome.
A node.js library for connecting to and receiving data from Bambu Lab printers through
their MQTT servers.
- Every command & response field is documented & typed.
- Easily (and safely\*) construct commands & manage responses.
- Full async support! `client#executeCommand` waits until the command completion is
verified by the printer.
## Getting Started
### Prerequisites
Make sure you have the following installed:
- Node.js
- NPM
- TypeScript
> [!CAUTION]
> TypeScript is highly recommended for this package due to the type safety it provides.
> This is especially important in use cases like this project where the library
> communicates with external hardware which can very well come with property damage. And
> even with TypeScript, I am not liable for any such damages as stated in the
> [license](LICENSE).
### Installation
```bash
npm install bambu-node
```
### Example Usage
### Understanding the Protocol
#### Sequence ID
The sequence_id is a critical component of the Bambu Lab printer communication protocol. It ensures reliable, ordered, and secure command processing between the client and printer.
##### How It Works
- Each command sent to the printer includes a unique sequence_id
- Valid sequence_ids must be between 20000 and 30000
- The sequence_id must be incremented for each new command
- The printer returns responses with the same sequence_id as the original command
##### Why It's Critical
1. **Command Validation**
- The sequence_id range acts as a security measure
- Only commands with valid sequence_ids are processed
- Helps prevent unauthorized command injection
2. **Command-Response Matching**
- Responses include the original command's sequence_id
- Allows matching responses to their originating commands
- Essential for asynchronous command processing
3. **Error Handling**
- Error responses are matched to commands via sequence_id
- Enables proper error attribution and handling
- Critical for temperature and safety-related commands
4. **State Management**
- Maintains proper command ordering
- Prevents command duplication
- Ensures reliable command acknowledgment
##### Implementation Requirements
- Always increment sequence_id for each new command
- Never reuse sequence_ids within a session
- Validate sequence_ids in responses match sent commands
- Handle sequence_id wraparound when reaching the upper limit
##### Consequences of Improper Implementation
Failing to properly implement sequence_id management can result in:
- Commands being ignored or rejected
- Incorrect error attribution
- Mixed up command responses
- Failed command acknowledgments
- Compromised safety features
##### Library Implementation
The library provides a `sequenceIdManager` utility that handles sequence IDs according to the protocol specification:
```typescript
import { getSequenceId, isValidSequenceId, START_SEQ_ID, END_SEQ_ID } from './utils/sequenceIdManager';
// Get next sequence ID (automatically handles wraparound)
const nextId = getSequenceId(); // Returns number between 20000-30000
// Validate incoming sequence IDs
const isValid = isValidSequenceId(nextId); // Returns true if within valid range
// Example usage in commands
async function sendCommand(command: any) {
// Automatically assign next valid sequence ID
command.sequence_id = getSequenceId();
// Send command and validate response sequence_id matches
const response = await sendToDevice(command);
if (!isValidSequenceId(response.sequence_id)) {
throw new Error('Invalid sequence ID in response');
}
if (response.sequence_id !== command.sequence_id) {
throw new Error('Response sequence ID mismatch');
}
return response;
}
```
The sequence ID management is handled automatically by the library's internal command processing, so you typically don't need to manage it directly when using the public API.
```typescript
import { BambuClient, Fan, UpdateFanCommand } from "bambu-node"
// define a printer connection
const client = new BambuClient({
host: "your_printers_ip",
accessToken: "your_printers_access_token",
serialNumber: "your_printers_sn",
})
// more about the available events below
client.on("message", (topic, key, data) => {
console.log(`New ${key} message!`, data)
})
client.on("printer:statusUpdate", (oldStatus, newStatus) => {
console.log(`The printer's status has changed from ${oldStatus} to ${newStatus}!`)
})
// connect to the printer
await client.connect()
// update the speed of the auxiliary fan to 100%
await client.executeCommand(new UpdateFanCommand({ fan: Fan.AUXILIARY_FAN, speed: 100 }))
// we don't want to do anything else => we close the connection
// (can be kept open indefinitely if needed)
await client.disconnect()
```
## API
#### Legend
<u>Unnamed things inside classes</u>: Other classes that extend that class.
Every method, command and response is documented in JSDoc, so only events & utility
classes are documented here.
- Bambu Node
- [Class: `BambuClient`](#class-bambuclient)
- Method: `BambuClient.connect()`
- Method: `BambuClient.disconnect()`
- Method: `BambuClient.subscribe(topic)`
- Method: `BambuClient.executeCommand(command)`
- [Event: `message`](#message)
- [Event: `rawMessage`](#rawmessage)
- [Event: `client:connect`](#clientconnect)
- [Event: `client:disconnect`](#clientdisconnect)
- [Event: `printer:dataUpdate`](#printerdataupdate)
- [Event: `printer:statusUpdate`](#printerstatusupdate)
- [Event: `job:update`](#jobupdate)
- [Event: `job:start`](#jobstart)
- [Event: `job:pause`](#jobpause)
- [Event: `job:offlineRecovery`](#jobofflineRecovery)
- [Event: `job:unpause`](#jobunpause)
- [Event: `job:finish`](#jobfinish)
- [Class: `Job`](#class-job)
- Method: `Job.update(data)`
- Getter: `Job.data`
- Class: `AbstractCommand`
- Class: `GCodeCommand`
- `GCodeFileCommand`
- `GCodeLineCommand`
- `GetVersionCommand`
- `PushAllCommand`
- `UpdateFanCommand`
- `UpdateLightCommand`
- `UpdateSpeedCommand`
- `UpdateStateCommand`
- `UpdateTempCommand`
- _Command Responses_
- info
- Class: `InfoMessageCommand`
- `GetVersionResponse`
- mcPrint
- Class: `McPrintMessageCommand`
- `PushInfoResponse`
- print
- Class: `PrintMessageCommand`
- `GCodeFileResponse`
- `GCodeLineResponse`
- `ProjectFileResponse`
- `PushAllResponse`
- `PushStatusResponse`
- `UpdateFanResponse`
- `UpdateLightResponse`
- `UpdateSpeedResponse`
- `UpdateStateResponse`
- `UpdateTempResponse`
### Class: BambuClient
Responsible for managing the connection and messages to/from the printer.
#### Events
##### `rawMessage`
Triggered whenever a new message is received from the MQTT broker.
#### `message`
Triggered whenever a new <u>known</u> message is received from the MQTT broker. It's
already parsed and sent using its type.
#### `client:connect`
Triggered whenever the client connects to the printer. This will also trigger on a
reconnect.
#### `client:disconnect`
Triggered whenever the client disconnects from the printer. This can be on purpose using
the `client#disconnect` method or when the printer itself goes offline.
#### `client:error`
Triggered whenever the internal MQTT client encounters an error.
Examples include:
- Unresolvable host provided
- Incorrect credentials provided
- Unexpected responses
#### `printer:dataUpdate`
Triggered whenever new data is received from the printer and is merged into the data class
field.
#### `printer:statusUpdate`
Triggered whenever the printer's status changes to a new status.
#### `job:update`
Triggered whenever the current Job's data gets updated.
#### `job:start`
Triggered whenever a new printing job starts.
#### `job:pause`
Triggered whenever the current print job is paused.
#### `job:offlineRecovery`
Triggered whenever the current print job was recovered after the printer came back online
from an offline state.
#### `job:unpause`
Triggered whenever the current print job is resumed.
#### `job:finish`
Triggered whenever the current print job finishes.
Possible reasons:
- `SUCCESS`: Triggered whenever the current print job finishes without errors.
- `FAILED`: Triggered whenever the current print job finishes without errors.
- `UNEXPECTED`: Triggered whenever the current print job finishes unexpectedly. This is
only included as a proof of concept and is 99% bound to never happen.
### Class: Job
Responsible for managing the data about the current print job. It collects historical
data, error codes, etc. It is included in every event starting with `job:`.
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what
you would like to change.
## License
[MIT © Márk Böszörményi, Aaron Scherer](LICENSE)