Python脚本终端运行正常,crontab执行时触发BLE蓝牙相关错误
问题:macOS下crontab执行BLE控制脚本提示蓝牙已关闭
我编写了一段使用bleak和asyncio控制BLE蓝牙设备的Python脚本,在PyCharm或终端手动运行时完全正常,但通过crontab定时执行时抛出错误,提示蓝牙设备已关闭。运行环境为macOS。
脚本内容
#!/usr/bin/python from bleak import BleakClient import asyncio import time # 原脚本遗漏此导入,需补充 address = "DFB18A2C-6BAB-439E-AF05-485FB4259B75" MODEL_NBR_UUID = "99FA0002-338A-1024-8A49-009C0215F78A" async def main(address): async with BleakClient(address) as client: for i in range(3): await client.write_gatt_char(MODEL_NBR_UUID, bytes.fromhex("0800")) time.sleep(0.5) # 建议替换为await asyncio.sleep(0.5),避免阻塞事件循环 asyncio.run(main(address))
报错信息
Traceback (most recent call last): File "/Users/emileberhard/PycharmProjects/BedMove/main.py", line 19, in <module> asyncio.run(main(address)) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/runners.py", line 44, in run return loop.run_until_complete(main) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete return future.result() File "/Users/emileberhard/PycharmProjects/BedMove/main.py", line 13, in main async with BleakClient(address) as client: File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/bleak/backends/client.py", line 61, in __aenter__ await self.connect() File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/bleak/backends/corebluetooth/client.py", line 78, in connect device = await BleakScannerCoreBluetooth.find_device_by_address( File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/bleak/backends/scanner.py", line 220, in find_device_by_address return await cls.find_device_by_filter( File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/bleak/backends/scanner.py", line 249, in find_device_by_filter async with cls(detection_callback=apply_filter, **kwargs): File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/bleak/backends/corebluetooth/scanner.py", line 44, in __init__ self._manager = CentralManagerDelegate.alloc().init() File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/bleak/backends/corebluetooth/CentralManagerDelegate.py", line 88, in init raise BleakError("Bluetooth device is turned off") bleak.exc.BleakError: Bluetooth device is turned off Exception ignored in: <CentralManagerDelegate objective-c instance 0x0> Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/bleak/backends/corebluetooth/CentralManagerDelegate.py", line 102, in __del__ IndexError: NSRangeException - Cannot remove an observer <CentralManagerDelegate 0x7fd036fbe0b0> for the key path "isScanning" from <CBCentralManager 0x600000aab100> because it is not registered as an observer. Exception ignored in: <function CentralManagerDelegate.__del__ at 0x7fd01863cca0>
解决方案
核心原因
macOS的crontab运行在无用户会话的后台环境,无法访问CoreBluetooth服务——该服务需要绑定到活跃的用户GUI会话才能正常工作,因此crontab执行时会被判定为蓝牙未开启。
替代方案:使用launchd定时执行
launchd是macOS原生的任务管理工具,支持在用户会话上下文运行任务,能正常访问蓝牙服务。
- 创建launchd配置文件
在~/Library/LaunchAgents/目录下新建com.yourname.bedmove.plist文件,内容如下(根据实际路径修改):
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.yourname.bedmove</string> <key>ProgramArguments</key> <array> <string>/Library/Frameworks/Python.framework/Versions/3.9/bin/python3</string> <string>/Users/emileberhard/PycharmProjects/BedMove/main.py</string> </array> <key>StartInterval</key> <integer>300</integer> <!-- 执行间隔,单位秒,这里是5分钟 --> <key>RunAtLoad</key> <true/> <!-- 登录时立即执行一次 --> <key>StandardOutPath</key> <string>/Users/emileberhard/PycharmProjects/BedMove/logs/out.log</string> <key>StandardErrorPath</key> <string>/Users/emileberhard/PycharmProjects/BedMove/logs/error.log</string> </dict> </plist>
- 准备日志目录
创建日志目录,避免权限问题:
mkdir -p /Users/emileberhard/PycharmProjects/BedMove/logs
- 加载并启动任务
# 加载配置 launchctl load ~/Library/LaunchAgents/com.yourname.bedmove.plist # 启动任务 launchctl start com.yourname.bedmove # 查看任务状态 launchctl list | grep com.yourname.bedmove
脚本优化
原脚本中在async函数里使用time.sleep(0.5)会阻塞asyncio事件循环,建议替换为:
await asyncio.sleep(0.5)
同时补充遗漏的import time(如果保留原sleep的话),或者直接移除import time改用asyncio的sleep。
内容的提问来源于stack exchange,提问作者emileber
相关产品推荐
相关产品推荐

