Klipper — приёмы работы с вендорским форком QIDI
Вендоры (QIDI и др.) поставляют свой Klipper с закрытыми модулями и урезанным Moonraker без [update_manager]. Приёмы отладки такого форка через Moonraker API и SSH.
Гоча: после FIRMWARE_RESTART состояние startup — это НЕ ошибка
FIRMWARE_RESTART перезапускает Klipper + все MCU. Сразу после printer/info отдаёт:
{"state":"startup","state_message":"Printer is not ready ... The klippy host software is attempting to connect."}Это штатная фаза переподключения MCU (на боксе с боксом-MCU ~12 с). Реальный промах: проверил состояние ОДИН раз через 16 с, увидел startup, счёл сбоем и откатил свои макросы. Перезапустил с поллингом — реальные тайминги: t=3s startup, t=6s startup, t=9s startup, t=12s ready. Правильно — опрашивать в цикле до ready, отличая startup (ждать) от error/shutdown (реальный сбой, читать state_message):
for i in $(seq 1 20); do sleep 3
S=$(curl -s http://IP:7125/printer/info | python3 -c "import sys,json;print(json.load(sys.stdin)['result']['state'])")
[ "$S" = ready ] && break
case "$S" in error|shutdown) echo FAIL; break;; esac
doneПроверка регистрации макроса после: curl .../printer/gcode/help | ... 'MY_MACRO' in help.
Что QIDI вырезала/сломала в Moonraker (и гоча 404 vs 405)
/machine/system_info→ 500 (падает на отсутствующем/dev_info.txt);/machine/update/status→ 404 (нет[update_manager]— Klipper/Moonraker штатно не обновить, только перепрошивкой);- НЕ вырезано, хоть и кажется:
/machine/services/restart,/machine/reboot,/machine/shutdown— POST-only.GETпо ним даёт 405 (не 404!), что легко прочитать как «функции нет». Различать 404 (нет маршрута) и 405 (не тот метод), пробовать целевым методом. Рестарт Moonraker:POST /machine/services/restartс{"service":"moonraker"}. - Гоча загрузки:
POST /server/files/uploadтрактует полеpathкак каталог, имя берётся из файла;path=foo.cfgсоздастfoo.cfg/foo.cfg.
Правка конфига и порядок include
Новые секции — отдельным файлом ([include extras.cfg]), строку include ставить до автосейв-блока #*# (SAVE_CONFIG в конце printer.cfg), иначе include попадёт внутрь автосейва и сломает его. Реальный пример вставки — [include klipperscreen-extras.cfg] после [include box.cfg] через sed -i '/^\[include box.cfg\]/a ...'. Владельца/права — под klipper-юзера (chown qidi:netdev, chmod 644). Применение — FIRMWARE_RESTART. Держать бэкап и авто-откат при error.
Бинарные (скомпилированные) модули
QIDI часть extras отгружает как .so (напр. multi_color_controller.so в klippy/extras/) — исходника нет. Что делать:
- список команд —
curl http://IP:7125/printer/gcode/help(в т.ч. вендорскиеMULTI_COLOR_*); - имена параметров —
strings module.so | grep -iE 'TEMP|SLOT|time|state|cmd_': выдаёт токены gcode-параметров (SLOT,TEMP,minutes,state) и имена cmd-функций (cmd_multi_color_dry); - безопасный пробинг — вызвать команду без аргументов и прочитать ответ: если параметр обязателен, Klipper вернёт ошибку с его именем, НЕ выполнив действие. НО если параметр опционален, команда сработает с дефолтом → держать сеть безопасности (для нагрева —
*_DISABLE_HEATER) и проверять эффект по объекту (printer/objects/query?<module>). Пример:SET_FILAMENT_DRY/MULTI_COLOR_DRYбез аргументов →{"result":"ok"}, нагрев НЕ запущен (drying.box1.dry_state:0).
Запуск shell из макроса
gcode_shell_command (сторонний extra) есть не во всех форках — проверить ls klippy/extras/gcode_shell_command.py. Если есть — из макроса можно дёргать shell (напр. переключать сервисы, если у klipper-юзера беспарольный sudo):
[gcode_shell_command my_cmd]
command: bash -c "..."
timeout: 30.
verbose: True
[gcode_macro MY_MACRO]
gcode:
RUN_SHELL_COMMAND CMD=my_cmdgcode_shell_command исполняется от klipper-юзера — для sudo внутри нужен его NOPASSWD.
Чтение консоли (что видит экран)
Вывод команд (respond_info, //-строки) идёт в консоль, не попапом. Забрать: curl -s "http://IP:7125/server/gcode_store?count=30" (JSON, result.gcode_store[].message). ⚠️ парсить питоном через heredoc, а не python3 -c "...['result']..." внутри ssh '...' — вложенные одинарные кавычки ломают ключ, получаешь NameError: name 'result' is not defined (это про кавычки, не про данные). Родственное — KlipperScreen на слабом SBC.
Переопределение вендорских макросов (rename_existing и слияние секций)
Чтобы обернуть штатный макрос (например T0, добавив своё действие), в Klipper есть
rename_existing. С ним две ловушки:
- Тип имени. Цель переименования обязана быть того же класса, что исходное имя.
T0—
«традиционный» g-code (буква+цифра), поэтому и цель — буква+цифра.rename_existing: BOX_T0
падает:G-Code macro rename of different types ('T0' vs 'BOX_T0'). Валидно только вродеT90. - Слияние секций. Klipper сливает одноимённые
[gcode_macro T0]из разных include
(опции более позднего файла побеждают). Если и вендорскийbox.cfg, и твой файл объявляют
[gcode_macro T0], отдельного «оригинала» для переименования не остаётся → на connect падает
Existing command 'T0' not found in gcode_macro rename.
Вывод: переопределять вендорский макрос из своего include надо полным переопределением, а
не rename_existing: в своём файле (подключённом ПОСЛЕ вендорского) объявить тот же
[gcode_macro T0], скопировав тело вендора и добавив своё — более поздняя опция gcode: побеждает
при слиянии. Чьё тело победило — видно по description в printer/gcode/help. Обе ошибки валят
Klipper в error, чинится следующим FIRMWARE_RESTART.
Урезанный Moonraker: чего ещё может не быть
Кроме [update_manager], на старом форке нередко отсутствует компонент [analysis] (нет
analysis.py, среди компонентов не грузится) — тогда серверная точная оценка времени недоступна,
остаётся только слайсерный post-process (см.
klipper_estimator — точная оценка времени печати).
Веб-морда Fluidd при этом может быть стоковой, но с вендорскими правками настроек и скрытыми
карточками — см. Fluidd — настройки в БД Moonraker.
Цифра в имени G-code команды = команда молча теряется
Парсер G-code Klipper режет имя команды по первой цифре: моя CS1237_FIND_SENSOR регистрировалась (видна в printer/gcode/help с моим description), но диспатчилась как несуществующая CS1237 — без ошибки, просто ничего не происходило. Поэтому вендорские команды называются CS_WEIGHT_*, а не CS1237_* (и между версиями прошивки их переименовывали — декомпилированные имена сверять с живым gcode/help). Свои команды и макросы — только [A-Z_] в имени.
Extras перечитываются ТОЛЬКО рестартом сервиса (и Moonraker после него глохнет)
RESTART/FIRMWARE_RESTART не перечитывают новые/изменённые файлы в klippy/extras/ — только systemctl restart klipper (удалённо: POST /machine/services/restart с {"service":"klipper"}). Две гочи следом:
- Moonraker после рестарта сервиса 30–60 с молча теряет G-code:
/printer/infoужеready, аPOST gcode/scriptвисит или возвращается пустым. Лечение — пинговатьM115в цикле до первого{"result": "ok"}, только потом слать команды. - Обходные каналы мимо Moonraker, когда он глохнет: pty
~/printer_data/comms/klippy.serial(printf "CMD\n" > <pty>, ответыcatоттуда же) и unix-сокетklippy.sock(JSON{"id":1,"method":"gcode/script","params":{"script":"CMD"}}+ терминатор 0x03). Работают мгновенно и сразу после старта klippy.
Жёсткая остановка движения = THR MCU в shutdown
Рестарт сервиса klipper посреди активного движения роняет MCU тулхеда: klippy при переподключении падает в error с Can not update MCU 'THR' config as it is shutdown. Это штатно чинится FIRMWARE_RESTART (~15 с) — не паниковать и не искать поломку железа. Хоуминг после этого потерян — G28 перед следующим движением. Runtime-вскрытие закрытых модулей — Runtime-интроспекция закрытого Klipper-модуля.
Источники
- Moonraker API:
printer/info,printer/gcode/help,printer/gcode/script,server/gcode_store,printer/objects/query,machine/services,server/files/upload github.com/qidi-community/q2-wiki