hopex/bambulab-sdk

Typed PHP SDK for local control and monitoring of Bambu Lab 3D printers.

Maintainers

Package info

github.com/Hopex-Development/bambulab-sdk

pkg:composer/hopex/bambulab-sdk

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-02 02:03 UTC

This package is auto-updated.

Last update: 2026-08-02 10:03:54 UTC


README

Bambu Lab

Bambu Lab API для PHP

Полностью типизированный 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:

  1. Подключите принтер и PHP-приложение к одной локальной сети.
  2. Включите LAN Mode в настройках принтера.
  3. Получите Access Code.
  4. Определите локальный IP-адрес принтера.
  5. Получите серийный номер принтера.

Не рекомендуется открывать 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, могут отсутствовать.

Состояния G-code

Состояние печати представлено 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();

G-code

Отправка одной команды:

$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;
}

Только модели 3MF

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

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;
    },
);

Температура, свет и HMS

$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

После успешного переподключения клиент:

  1. создаёт новое MQTT-соединение;
  2. повторно активирует подписку;
  3. запрашивает полное состояние;
  4. сохраняет зарегистрированные обработчики событий;
  5. продолжает цикл обработки сообщений.

Одна итерация с автоматическим восстановлением:

$client->tickWithReconnect(
    new ReconnectPolicy(
        attempts: 5,
        delayMilliseconds: 2_000,
    ),
);

Явное переподключение:

$client->reconnect();

Пользовательские реализации

Основные механизмы SDK построены на контрактах.

MQTT-транспорт

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 приветствуются.

Перед отправкой изменений:

  1. проверьте соответствие структуры feature-first архитектуре;
  2. сохраняйте строгую типизацию;
  3. добавляйте документацию на русском языке;
  4. не добавляйте зависимость от Laravel в основной пакет;
  5. не ломайте существующие публичные контракты без необходимости.

Лицензия

Проект распространяется по лицензии MIT.