Полностью типизированный PHP 8.4 SDK для локального управления принтерами Bambu Lab.
Пакет предоставляет типизированный API для работы с состоянием принтера, заданиями печати, файлами, камерой, температурой, вентиляторами, подсветкой, прошивкой, HMS-сообщениями и событиями в реальном времени.
Проект является неофициальным SDK сообщества и не связан с Bambu Lab.
- MQTT
- FTPS
- Camera
- Printer State
- Events
- Firmware
- HMS
- Files
- Temperature
- Fans
- Light
- Automatic reconnect
- PHP 8.4 или новее;
- расширение
ext-curl; - расширение
ext-json; - расширение
ext-openssl; - доступ к принтеру по локальной сети;
- включённый LAN Mode;
- действующий Access Code принтера.
SDK использует несколько локальных сервисов принтера:
- MQTT для команд и состояния;
- FTPS для работы с файлами;
- отдельное TLS-соединение для камеры.
Поддержка конкретных возможностей зависит от модели принтера и версии прошивки.
composer require hopex/bambulab-sdkПеред использованием SDK:
- Подключите принтер и PHP-приложение к одной локальной сети.
- Включите LAN Mode в настройках принтера.
- Получите Access Code.
- Определите локальный IP-адрес принтера.
- Получите серийный номер принтера.
Не рекомендуется открывать MQTT, FTPS и порт камеры напрямую в интернет.
<?php
declare(strict_types=1);
use Hopex\BambuLab\BambuClient;
use Hopex\BambuLab\Printer\DTO\PrinterCredentials;
use Hopex\BambuLab\Transport\MqttTransport;
require __DIR__ . '/vendor/autoload.php';
$credentials = new PrinterCredentials(
host: '192.168.1.100',
accessCode: 'ACCESS_CODE',
serialNumber: 'SERIAL_NUMBER',
);
$client = new BambuClient(
credentials: $credentials,
transport: new MqttTransport(),
);
try {
$client->connect();
if (!$client->waitUntilReady(timeoutSeconds: 15)) {
throw new RuntimeException(
'Не удалось синхронизировать состояние принтера.',
);
}
$status = $client->state()->status();
echo sprintf(
"Состояние: %s\n",
$status->state?->value ?? 'UNKNOWN',
);
echo sprintf(
"Сопло: %.1f °C\n",
$status->nozzleTemperature ?? 0,
);
echo sprintf(
"Стол: %.1f °C\n",
$status->bedTemperature ?? 0,
);
} finally {
$client->disconnect();
}$client->connect();По умолчанию клиент активирует MQTT-подписку и запрашивает полное состояние принтера.
Подключение без автоматического запроса состояния:
$client->connect(synchronize: false);Одна итерация MQTT-цикла:
$client->tick();Блокирующий цикл:
$client->run();Остановка активного цикла:
$client->stop();$client->disconnect();PrinterState хранит накопленное состояние принтера.
Принтер часто отправляет только изменившиеся поля. SDK рекурсивно объединяет частичные сообщения с ранее полученным состоянием.
$state = $client->state();
$status = $state->status();
echo $status->progress();
echo $status->remainingMinutes();
echo $status->currentLayer();
echo $status->totalLayers();
echo $status->bedTemperature();
echo $status->nozzleTemperature();Получение полного накопленного payload:
$raw = $client->state()->raw();Текущее задание представлено объектом CurrentPrintJob.
$job = $client->state()->currentJob();
if ($job !== null) {
echo $job->fileName;
echo $job->name;
echo $job->printType;
echo $job->progress;
echo $job->remainingMinutes;
echo $job->currentLayer;
echo $job->totalLayers;
}Доступные вспомогательные методы:
$job?->isActive();
$job?->isRunning();
$job?->isPaused();
$job?->isPreparing();
$job?->isFinished();
$job?->isFailed();При локальном запуске облачные идентификаторы, например taskId и projectId, могут отсутствовать.
Состояние печати представлено enum GcodeState.
use Hopex\BambuLab\Gcode\Enum\GcodeState;
$state = $client->state()->gcodeState();
if ($state === GcodeState::RUNNING) {
echo 'Печать выполняется';
}Вспомогательные методы:
$state?->isPrinting();
$state?->isPaused();
$state?->isFinished();
$state?->isFailed();
$state?->isIdle();
$state?->isPreparing();Состояние FAILED может означать как реальную ошибку, так и ручную отмену задания.
Ожидание запуска печати:
use Hopex\BambuLab\Gcode\Enum\GcodeState;
$state = $client->waitForState(
GcodeState::RUNNING,
timeoutSeconds: 300,
);
if ($state === null) {
throw new RuntimeException(
'Принтер не начал печать.',
);
}Ожидание одного из нескольких состояний:
$state = $client->waitForState(
states: [
GcodeState::PAUSE,
GcodeState::FAILED,
],
timeoutSeconds: 60,
);Ожидание пользовательского условия:
use Hopex\BambuLab\Printer\PrinterState;
$reached = $client->waitUntil(
condition: static fn(PrinterState $state): bool =>
($state->progress() ?? 0) >= 50,
timeoutSeconds: 3600,
);use Hopex\BambuLab\Printer\DTO\PrintJobOptions;
$client->printer()->start(
new PrintJobOptions(
filename: 'model.3mf',
plate: 1,
useAms: false,
amsMapping: [],
bedLeveling: true,
flowCalibration: false,
vibrationCalibration: false,
layerInspection: false,
timelapse: false,
),
);Файл должен быть предварительно загружен на принтер.
$client->printer()->pause();$client->printer()->resume();$client->printer()->stop();После ручной отмены принтер обычно сообщает состояние FAILED.
$client->printer()->requestFullState();Отправка одной команды:
$client->gcode()->send('G28');Отправка нескольких команд:
$client->gcode()->send(
"G28\n"
. "G1 Z10 F600\n",
);Произвольный G-code может запустить движение механизмов, нагрев и другие опасные операции. Проверяйте команды перед отправкой.
Включение:
$client->light()->turnOn();Выключение:
$client->light()->turnOff();Получение текущего состояния:
$light = $client->state()->lightState();
echo $light?->value ?? 'UNKNOWN';Температура сопла:
$client->temperature()->setNozzle(220);Температура стола:
$client->temperature()->setBed(60);Получение текущих значений:
$status = $client->state()->status();
echo $status->nozzleTemperature;
echo $status->nozzleTargetTemperature;
echo $status->bedTemperature;
echo $status->bedTargetTemperature;
echo $status->chamberTemperature;Настроенные ограничения SDK:
echo $client
->temperature()
->maximumNozzleTemperature();
echo $client
->temperature()
->maximumBedTemperature();Корректная безопасная температура зависит от модели принтера, сопла, стола и используемого материала.
Управление вентиляторами выполняется через FanApi.
$client->fans()->setPart(128);
$client->fans()->setAuxiliary(80);
$client->fans()->setChamber(100);Альтернативно можно использовать проценты.
$client->fans()->setPartPercent(50);
$client->fans()->setAuxiliaryPercent(30);
$client->fans()->setChamberPercent(40);Остановка вентилятора:
$client->fans()->setPart(0);Перед использованием дополнительного вентилятора рекомендуется проверить возможности принтера:
$capabilities = $client
->device()
->capabilities();
if ($capabilities->hasAuxiliaryFan() === true) {
$client->fans()->setAuxiliaryPercent(50);
}При использовании заведомо отсутствующей возможности SDK может выбросить UnsupportedCapabilityException.
Работа с файловой системой принтера выполняется через FTPS.
foreach ($client->files()->entries() as $entry) {
echo $entry->path;
echo $entry->size;
echo $entry->modifiedAt?->format('Y-m-d H:i:s');
}foreach ($client->files()->files() as $file) {
echo $file->name;
}foreach ($client->files()->directories() as $directory) {
echo $directory->name;
}foreach ($client->files()->models() as $model) {
echo $model->name;
}$gcodeFiles = $client
->files()
->byExtension('gcode');Можно передавать расширение с точкой или без неё:
$models = $client
->files()
->byExtension('.3mf');$file = $client
->files()
->find('model.3mf');
if ($file !== null) {
echo $file->size;
}$exists = $client
->files()
->exists('model.3mf');use Hopex\BambuLab\Files\DTO\FileEntry;
$largeModels = $client->files()->filter(
static fn(FileEntry $entry): bool =>
$entry->isFile()
&& $entry->hasExtension('3mf')
&& ($entry->size ?? 0) >= 500_000,
);$client->files()->upload(
localPath: '/path/to/model.3mf',
remotePath: 'model.3mf',
);$client->files()->download(
remotePath: 'model.3mf',
localPath: '/path/to/downloaded-model.3mf',
);$client
->files()
->delete('model.3mf');Файловые операции используют собственное FTPS-соединение и не зависят от активного MQTT-цикла.
Получение одного JPEG-кадра:
$frame = $client
->camera()
->snapshot();Сохранение кадра:
$frame->saveAs(
'/path/to/snapshot.jpg',
);Дополнительные представления:
echo $frame->mimeType();
echo $frame->base64();
echo $frame->dataUri();
echo $frame->bytes();
echo $frame->size();
echo $frame->isEmpty();Поддержка камеры зависит от модели принтера и используемого локального протокола.
Текущая реализация TcpCameraTransport предназначена для совместимых локальных TLS-потоков камеры.
$device = $client->device();
echo $device->host();
echo $device->serialNumber();
echo $device->wifiSignal();
echo $device->wifiSignalDbm();
echo $device->nozzleType();
echo $device->nozzleDiameter();
echo $device->lifecycle();Состояние периферии:
$device->hasSdCard();
$device->hasAms();
$device->cameraAvailable();
$device->cameraRecordingEnabled();
$device->timelapseEnabled();$capabilities = $client
->device()
->capabilities();
var_dump([
'resolved' => $capabilities->isResolved(),
'camera' => $capabilities->hasCamera(),
'ams' => $capabilities->hasAms(),
'sd_card' => $capabilities->hasSdCard(),
'auxiliary_fan' => $capabilities->hasAuxiliaryFan(),
'chamber_fan' => $capabilities->hasChamberFan(),
'maximum_bed_temperature' =>
$capabilities->maximumBedTemperature(),
'maximum_nozzle_temperature' =>
$capabilities->maximumNozzleTemperature(),
]);Возможности определяются по фактическому состоянию, полученному от принтера.
До полной синхронизации некоторые значения могут быть равны null.
Запрос актуальной информации:
$client->firmware()->refresh();Получение основной версии:
echo $client
->firmware()
->current();Получение модулей:
foreach ($client->firmware()->modules() as $module) {
echo $module->name;
echo $module->softwareVersion;
echo $module->hardwareVersion;
echo $module->serialNumber;
echo $module->isOta();
}Получение состояния обновления:
$upgrade = $client
->firmware()
->upgradeState();
if ($upgrade !== null) {
echo $upgrade->status;
echo $upgrade->progress;
echo $upgrade->errorCode;
}Первая версия API прошивки предназначена только для чтения данных. Установка и откат прошивки не поддерживаются.
HMS содержит сообщения диагностики, предупреждения и аппаратные ошибки принтера.
foreach ($client->hms()->messages() as $message) {
echo $message->identifier();
echo $message->level->value;
echo $message->description;
}Предупреждения:
$warnings = $client
->hms()
->warnings();Ошибки:
$errors = $client
->hms()
->errors();Проверки:
$client->hms()->hasMessages();
$client->hms()->hasWarnings();
$client->hms()->hasErrors();
$client->hms()->hasUnknown();Не все прошивки передают человекочитаемое описание и уровень сообщения. В таком случае SDK сохраняет исходные данные и использует уровень UNKNOWN.
Обработчики событий вызываются синхронно во время tick(), run() или runWithReconnect().
use Hopex\BambuLab\Events\PrinterStateSnapshot;
$printStartedSubscription = $client->events()->onPrintStarted(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo 'Печать запущена';
},
);
$progressChangedSubscription = $client->events()->onProgressChanged(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo printf(
"%d%%\n",
$current->currentJob?->progress ?? 0,
);
},
);$client->events()->onPrintPaused(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo 'Печать приостановлена';
},
);
$client->events()->onPrintResumed(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo 'Печать продолжена';
},
);$client->events()->onPrintFinished(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo 'Печать завершена';
},
);
$client->events()->onPrintCancelled(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo 'Печать отменена';
},
);Определение ручной отмены является эвристическим, поскольку принтер обычно использует состояние FAILED.
$client->events()->onJobChanged(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo sprintf(
'%s -> %s',
$previous->currentJob?->fileName ?? 'NONE',
$current->currentJob?->fileName ?? 'NONE',
);
},
);$client->events()->onProgressChanged(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo $current->currentJob?->progress;
},
);
$client->events()->onLayerChanged(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo sprintf(
'%d/%d',
$current->currentJob?->currentLayer ?? 0,
$current->currentJob?->totalLayers ?? 0,
);
},
);$client->events()->onRemainingTimeChanged(
static function (
PrinterStateSnapshot $current,
PrinterStateSnapshot $previous,
): void {
echo $current->currentJob?->remainingMinutes;
},
);$client->events()->onTemperatureChanged(...);
$client->events()->onLightChanged(...);
$client->events()->onHmsChanged(...);$subscription->cancel();Удаление всех подписок:
$client->events()->clear();Для длительно работающих процессов можно использовать runWithReconnect().
use Hopex\BambuLab\Transport\ReconnectPolicy;
$client->connect();
$client->runWithReconnect(
new ReconnectPolicy(
attempts: 10,
delayMilliseconds: 3_000,
subscriptionWarmupMilliseconds: 1_000,
synchronizeAfterReconnect: true,
clearStateBeforeReconnect: true,
),
);| Параметр | Значение |
|---|---|
| attempts | количество попыток |
| delayMilliseconds | задержка между попытками |
| subscriptionWarmupMilliseconds | ожидание после подписки |
| synchronizeAfterReconnect | запрос полного состояния |
| clearStateBeforeReconnect | очистить накопленный PrinterState |
После успешного переподключения клиент:
- создаёт новое MQTT-соединение;
- повторно активирует подписку;
- запрашивает полное состояние;
- сохраняет зарегистрированные обработчики событий;
- продолжает цикл обработки сообщений.
Одна итерация с автоматическим восстановлением:
$client->tickWithReconnect(
new ReconnectPolicy(
attempts: 5,
delayMilliseconds: 2_000,
),
);Явное переподключение:
$client->reconnect();Основные механизмы SDK построены на контрактах.
use Hopex\BambuLab\Transport\Contracts\TransportContract;use Hopex\BambuLab\Files\Contracts\FileTransferContract;use Hopex\BambuLab\Camera\Contracts\CameraTransportContract;Это позволяет заменить стандартную реализацию, например при интеграции с другим MQTT-клиентом, файловым шлюзом или собственным сервисом камеры.
Все исключения SDK наследуются от:
Hopex\BambuLab\Exceptions\BambuExceptionОбщая обработка:
use Hopex\BambuLab\Exceptions\BambuException;
try {
$client->connect();
} catch (BambuException $exception) {
echo $exception->getMessage();
}Основные группы исключений:
- ошибки подключения и транспорта;
- ошибки переподключения;
- ошибки сериализации MQTT;
- ошибки файловых операций;
- ошибки камеры;
- ошибки G-code;
- попытка использовать неподдерживаемую возможность.
Исключения разнесены по соответствующим feature-модулям.
- SDK ориентирован на локальное управление принтером.
- Поведение протокола может отличаться между моделями и прошивками.
- Некоторые поля состояния могут отсутствовать.
- Не все функции протестированы на всех сериях Bambu Lab.
- AMS API пока не является приоритетным модулем.
- Потоковое видео камеры пока не поддерживается.
- Обновление и откат прошивки не поддерживаются.
- HMS может возвращать только код без текстового описания.
FAILEDне всегда означает аппаратную неисправность.- SDK не предоставляет облачную авторизацию Bambu Lab.
Полная документация по каждому модулю будет доступна отдельно:
Планируемые разделы:
- начало работы;
- клиент и подключение;
- состояние принтера;
- управление печатью;
- G-code;
- файлы;
- камера;
- температура;
- вентиляторы;
- подсветка;
- устройство;
- прошивка;
- HMS;
- события;
- переподключение;
- исключения;
- расширение SDK;
- архитектура.
Не публикуйте в репозитории:
- Access Code;
- серийный номер принтера;
- внутренний IP-адрес, если это нежелательно;
- файлы конфигурации с учётными данными.
Используйте переменные окружения:
$credentials = new PrinterCredentials(
host: getenv('BAMBU_HOST'),
accessCode: getenv('BAMBU_ACCESS_CODE'),
serialNumber: getenv('BAMBU_SERIAL_NUMBER'),
);Не открывайте локальные сервисы принтера напрямую в интернет.
SDK находится в активной разработке.
Перед выпуском стабильной версии рекомендуется зафиксировать:
- поддерживаемые модели;
- поддерживаемые версии PHP;
- список протестированных прошивок;
- правила обратной совместимости;
- формат версионирования.
Сообщения об ошибках и Pull Request приветствуются.
Перед отправкой изменений:
- проверьте соответствие структуры feature-first архитектуре;
- сохраняйте строгую типизацию;
- добавляйте документацию на русском языке;
- не добавляйте зависимость от Laravel в основной пакет;
- не ломайте существующие публичные контракты без необходимости.
Проект распространяется по лицензии MIT.