很多年以前就想把家里的老式打印机改成无线,前前后后摸索了这些方案:
- 用小白盒连接路由器:其实这个思路了网络打印机很类似,就是打印机 over IP,企业里几乎也都是这样的做法。缺点就是需要客户端安装驱动,所以相比之下就牺牲了移动端。
- windows/MacOS 共享:由于缺少 airprint,所以 Apple 设备无法使用隔空打印。其实 Windows 的兼容性是最好的。
- 在 OpenWrt 上安装 Cups 驱动,然后打印机接路由器当做无线使用。
感谢这篇文章,给了我很大的帮助:https://www.bilibili.com/opus/720655857020305463
我的方案是在打印机接群晖,然后使用 Docker 运行 Cups,来支持 Airprint。
虽然群晖自己支持了 cups,但是驱动不全,联想的打印机基本没有驱动,换几个其他的打印机型号也无法正确驱动起来,反而因为指令集冲突打印机一直在出空白页。
于是,打上了 docker 的主意。。。。
因为懒,也觉得没必要做数据映射:
1
| sudo docker run -d --name=airprint --net=host --privileged=true -e TZ="Asia/Shanghai" -e CUPSADMIN="admin" -e CUPSPASSWORD="pass" -e HOST_OS="Synology" -e TCP_PORT_631="631" chuckcharlie/cups-avahi-airprint:latest
|
从 631 端口进去 web 页,

选择识别的打印机:

填写信息,选择共享这个打印机。

没有打印机的驱动,所以我选了兄弟的。

打印机信息一览:

一个小插曲:
Mac 升级之后把高级选项弄丢了,需要在这里邮件,选择自定义工具栏

要把 logo 拖放到 2 处而不是 1 处,这个设计很反人类。

主要原因是一开始使用其他的 docker 镜像无法识别打印机,所以在这里使用 http 和 ipp 添加
http 的这么添加:

ipp 的把这串输入到浏览器,MacOS 可以,手机和 Ipad 不行:
1
| ipp://192.168.5.171:631/printers/Lenovo_M7400_Pro
|
这俩 docker 怎么都搜不到打印机(iPhone 不行,Window 可以,Mac 可以用上述办法添加),踩了几个小时的坑:

换了最上边那个容器之后全平台都可以了:
第二个就是我的打印机,第一个是群晖自己 cups 映射出来的,有 bug systemctl stop cupsd 也关不掉,不过也没啥影响。

MacOS 结果:

Iphone 默认无法选择打印机,只能点击分享,然后下拉菜单选择打印:

Iphone 默认只支持隔空打印,但是使用 Cups 之后我们的打印机不在列表中,但是也能正常的使用了。

整了这么多测试页,主打一个折腾开心:

最后还是有一个小问题,就是打印机由于关机或者拔掉 USB 的再重启的话,这个 docker 服务没有轮训机制,所以如果不常用的话,就需要每次打开打印机之后再手动运行重启下容器。
虽然现在的打印机都支持了 Airpint,cups 虽然已经成为了历史了,这么做算是圆了一个以前折腾的梦吧。
写了一个重启 CUPS docker 的 web:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Restart Container</title> <style> body { font-family: Arial, sans-serif; background-color: #f4f4f9; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; } .container { text-align: center; background: white; padding: 2rem; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } h1 { color: #333; margin-bottom: 1.5rem; } #restart-btn { background-color: #007bff; color: white; border: none; padding: 0.75rem 1.5rem; font-size: 1rem; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease; } #restart-btn:hover { background-color: #0056b3; } #status { margin-top: 1.5rem; font-size: 1.1rem; color: #555; } </style> </head> <body> <div class="container"> <h1>Restart Docker Container</h1> <button id="restart-btn">重启打印机服务</button> <p id="status"></p> </div>
<script> document .getElementById("restart-btn") .addEventListener("click", async () => { const statusElement = document.getElementById("status"); statusElement.textContent = "Restarting...";
try { const response = await fetch("/restart", { method: "POST", }); const result = await response.json(); statusElement.textContent = result.message; } catch (error) { statusElement.textContent = "Failed to restart container."; } }); </script> </body> </html>
|
后端使用 Flask:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from flask import Flask, render_template, request, jsonify import docker
app = Flask(__name__) client = docker.from_env()
@app.route('/') def index(): return render_template('index.html')
@app.route('/restart', methods=['POST']) def restart_container(): container_name = 'airprint' try: container = client.containers.get(container_name) container.restart() return jsonify({'status': 'success', 'message': f'Container {container_name} restarted successfully!'}) except Exception as e: return jsonify({'status': 'error', 'message': str(e)}), 500
if __name__ == '__main__': app.run(host='0.0.0.0', port=8000)
|
做好容器放在群晖上,完美~
1 2 3
| docker buildx build --platform linux/amd64 -t printer:latest --load . docker save printer:latest > 1.tar docker run -d --name printer_container -p 8888:8000 -v /var/run/docker.sock:/var/run/docker.sock --restart unless-stopped printer:latest
|
