Files
lx-d02/README.md
T
Alex CubeandClaude Opus 5.5 9e06f3ebd0 LX-D02 BLE thermal printer: PC, Windows printer, Home Assistant
- printer.py: protocol with flow control (pause / resend / done)
- lxd02.py: print text or pictures from the command line
- windows/: POS-58 driver + local ESC/POS server = a regular Windows printer,
  Explorer menu for pictures
- custom_components/lxd02: Home Assistant integration with a persistent queue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:56:45 +03:00

187 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# lx-d02
Печать на Bluetooth-термопринтере **LX-D02** (он же Funny Print, Xiqi, DOLEWA и другие
бренды) без приложения на смартфоне:
- **как на обычном принтере Windows**: из Блокнота, браузера, Word, из меню «Печать»;
- **картинки правым кликом** в проводнике, растянутые на всю ширину ленты;
- **из командной строки**: `python lxd02.py "Привет"`;
- **из Home Assistant**: действие `lxd02.print_text` с очередью, которая переживает
выключенный принтер и перезапуск HA.
Принтер работает только по BLE (Bluetooth Low Energy): USB-порт у него лишь для зарядки,
и Windows без этого проекта его как принтер не видит.
Протокол разобран в [ValdikSS/printer-driver-funnyprint](https://github.com/ValdikSS/printer-driver-funnyprint),
управление потоком сделано по образцу [klimromanyuk/funnyprint](https://github.com/klimromanyuk/funnyprint).
Подробный рассказ, как это сделано: [блог «Доктор Лексиум»](https://www.lexium.ru/).
## Что нужно
- Python 3.9+ и Bluetooth на компьютере.
- `pip install -r requirements.txt`
- Принтер включён и **не подключён к телефону**: LX-D02 держит только одно соединение,
и пока открыто Funny Print, компьютер его не найдёт.
Проверка: `python lxd02.py "Привет"` должен напечатать строку.
На Python 3.9 bleak 1.x падает при импорте, поэтому в `requirements.txt` стоит `bleak<1.0`.
На более новом Python ограничение можно снять.
## Печать из командной строки
```
python lxd02.py "Первая строка" "Вторая строка"
python lxd02.py фото.jpg скриншот.png
```
Текст печатается шрифтом DejaVu Sans (кириллица есть). Картинки растягиваются на ширину
ленты (48 мм, 384 точки), альбомные поворачиваются вдоль ленты, чтобы выйти крупнее,
полутона передаются растром.
Принтер ищется по имени `LX-D02`. Чтобы не тратить на поиск несколько секунд, задайте MAC:
`set LXD02_MAC=AA:BB:CC:DD:EE:FF` (MAC покажет любой BLE-сканер, например nRF Connect
на телефоне).
## Принтер в Windows
Как это устроено: Windows печатает на принтер с драйвером **POS-58** (стандартный драйвер
58-мм чековых принтеров), драйвер раскладывает страницу на ленту 58 мм и отдаёт её
командами ESC/POS в порт `127.0.0.1:9100`. Там их принимает `windows/print_server.py`
и печатает по Bluetooth.
Почему не встроенные драйверы Windows: «Microsoft Print to PDF» знает только офисные
форматы, и лист A4, ужатый до 48 мм, нечитаем. IPP Class Driver принимает только
стандартные названия бумаги, самое узкое из которых A6.
### Драйвер
Драйвер POS-58 обычно идёт с любым дешёвым 58-мм чековым принтером, ищите
«POS-58 driver» на сайте Xprinter или продавца. Проверено с `POS-58 11.2.0.0`
(Zjiang, `pos58.inf`). Сам принтер POS-58 не нужен, нужен только драйвер.
### Установка
В PowerShell **от имени администратора**:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
.\windows\install.ps1
```
Скрипт:
- добавляет принтер **LX-D02** на драйвере POS-58 и порту `127.0.0.1:9100`;
- ставит в Планировщик задание «LX-D02 print server» (сервер стартует при входе в систему);
- добавляет в меню картинок пункт «Print on LX-D02» и ярлык в «Отправить».
Удаление: `.\windows\install.ps1 -Uninstall`. Журнал: `windows\print_server.log`.
### Советы
- **Бумага.** У драйвера по умолчанию лента 3276 мм. Пусть вас не пугает превью: сервер
срезает белое сверху и снизу и сжимает пустые промежутки до 6 мм. Для удобного превью
выберите в настройках печати размер 58×210.
- **Поля.** Ставьте поля «Минимальные» (в браузере) или 0 (в редакторах). Левое поле
сервер убирает сам, но правое срежет драйвер: печатная полоса 48 мм начинается
от левого края листа, а браузер с обычными полями сдвигает текст вправо.
- **Картинки** лучше печатать правым кликом, а не из браузера или «Фотографий»:
те раскладывают картинку по листу, и она выходит мелкой или обрезанной.
- Странный результат? Последнее задание сохраняется в `windows\last_job.bin`,
`python windows\print_server.py --test windows\last_job.bin` покажет его картинкой.
## Home Assistant
Нужен Bluetooth на хосте HA (встроенный адаптер или прокси ESPHome).
Скопируйте `custom_components/lxd02` в `/config/custom_components/`, добавьте
в `configuration.yaml` и перезапустите HA:
```yaml
lxd02:
mac: AA:BB:CC:DD:EE:FF
```
Действие:
```yaml
action: lxd02.print_text
data:
text: "Привет из Home Assistant"
size: 28 # размер шрифта, 10..120
density: 3 # плотность, 0..7
feed: 40 # прогон ленты после текста
separator: true # линия под текстом
```
Задания попадают в очередь на диске (`/config/.storage/lxd02`). Если принтер выключен,
вне зоны или занят телефоном, задание ждёт, и очередь печатается, как только HA снова
увидит принтер. Пока очередь не пуста, HA пробует раз в минуту. Действие не падает,
если принтера нет: ищите `LX-D02` в журнале HA.
### Пример: чат MeshCore на ленте
Печатает входящие сообщения канала `my_channel` из интеграции MeshCore: отправитель
и время, ниже текст, под ним черта. Свои исходящие пропускаются: интеграция присылает
их заново на каждое обновление статуса доставки.
```yaml
triggers:
- trigger: event
event_type: meshcore_message
event_data:
message_type: channel
conditions:
- condition: template
value_template: "{{ trigger.event.data.channel | lower == 'my_channel' and not trigger.event.data.outgoing | default(false) }}"
actions:
- action: lxd02.print_text
continue_on_error: true
data:
size: 24
separator: true
text: "{{ trigger.event.data.sender_name }} · {{ now().strftime('%d.%m %H:%M') }}\n{{ trigger.event.data.message }}"
mode: queued
max: 20
```
### Если Bluetooth на хосте зависает
На ТВ-приставках встроенный чип раз в несколько недель может зависнуть
(`hci0: Opcode 0x0c03 failed: -110` в `dmesg`, HA пишет `NotReady`). Помогает
перепривязать чип к драйверу. Пример сторожа на systemd для чипа на UART (`hci_uart_bcm`):
```sh
#!/bin/sh
# /usr/local/sbin/bt-watchdog.sh: run every minute by bt-watchdog.timer
D=/sys/bus/serial/drivers/hci_uart_bcm
up() { hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; }
up && exit 0
sleep 20 # HA/BlueZ may be resetting the adapter itself
up && exit 0
logger -t bt-watchdog "hci0 down, rebinding serial0-0"
echo serial0-0 > $D/unbind 2>/dev/null
sleep 2
echo serial0-0 > $D/bind
sleep 8
timeout 5 bluetoothctl power on >/dev/null 2>&1
```
Имена драйвера и устройства у вас могут отличаться: смотрите `/sys/bus/serial/drivers/`.
## Протокол коротко
| | |
|---|---|
| Имя в BLE-рекламе | `LX-D02` |
| Сервис / запись / уведомления | `FFE6` / `FFE1` / `FFE2` |
| Ширина печати | 384 точки, 48 байт на строку, по две строки в пакете из 96 байт |
| Рукопожатие | принтер шлёт вызов `5a0a`, ответ `5a0b` + байт CRC16 от MAC × 10 |
| Управление потоком | `5a08` пауза (буфер полон), `5a05` + номер строки (дослать с неё), `5a06` готово |
Весь протокол: `custom_components/lxd02/printer.py`, самопроверка `python printer.py`.
## Лицензия
MIT, см. `LICENSE`.