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>
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user