Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

About

Полностью типизированный PHP SDK для локального управления принтерами Bambu Lab.

Resources

Contributing

Stars

Watchers

Forks

Releases

Contributors

Languages