<p>{a1}为错误提供了一点解释:</p>
<pre class="lang-none prettyprint-override"><code>Methods RegisterAdvertisement(object advertisement, dict options)
Registers an advertisement object to be sent over the LE
Advertising channel. The service must be exported
under interface LEAdvertisement1.
...
If the maximum number of advertisement instances is
reached it will result in NotPermitted error.
</code></pre>
<p>因此,错误来自<code>RegisterAdvertisement</code>调用:</p>
<pre><code>ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
reply_handler=register_ad_cb,
error_handler=functools.partial(register_ad_error_cb, mainloop))
</code></pre>
<p>清理以前注册的广告的一种“强力”方法是,在运行BLE服务器之前,始终重新启动蓝牙服务</p>
<pre><code># Restart bluetooth
$ systemctl restart bluetooth
$ systemctl status bluetooth
● bluetooth.service - Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset
Active: active (running) since Thu 2020-08-20 18:07:55 JST; 4s ago
...
# Start BLE server
$ python3 gatt-server.py
</code></pre>
<p>一种更“优雅”的方法是尝试确保您的BLE服务器实现具有适当的关闭或清理处理。当服务器进程结束/退出时,尝试调用BlueZ广告管理器和GATT管理器API<code>Unregister*</code>方法:</p>
<ul>
<li><p><code>UnregisterAdvertisement</code></p>
<ul>
<li><a href="https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/advertising-api.txt#n178" rel="nofollow noreferrer">https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/advertising-api.txt#n178</a></li>
<li>
<blockquote>
<p>This unregisters an advertisement that has been previously registered. The object path parameter must match the same value that has been used on registration.</p>
</blockquote>
</li>
</ul>
</li>
<li><p><code>UnregisterApplication</code></p>
<ul>
<li><a href="https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt#n477" rel="nofollow noreferrer">https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt#n477</a></li>
<li>
<blockquote>
<p>This unregisters the services that has been previously registered. The object path parameter must match the same value that has been used on registration.</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<p>您可能还需要<a href="https://lazka.github.io/pgi-docs/GLib-2.0/classes/MainLoop.html#GLib.MainLoop.quit" rel="nofollow noreferrer">^{<cd5>}</a>创建的<code>GLib.MainLoop</code></p>
<p>在<a href="https://stackoverflow.com/a/55680037/2745495">accepted answer</a>中提到的<a href="https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/example-gatt-server" rel="nofollow noreferrer">example-gatt-server</a>不执行此操作,但是您也可以检查<a href="https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/example-advertisement" rel="nofollow noreferrer">example-advertisement</a>代码(也来自BlueZ),该代码<em>执行</em>操作:</p>
<pre><code>def shutdown(timeout):
print('Advertising for {} seconds...'.format(timeout))
time.sleep(timeout)
mainloop.quit()
def main(timeout=0):
...
mainloop = GObject.MainLoop()
ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
reply_handler=register_ad_cb,
error_handler=register_ad_error_cb)
if timeout > 0:
threading.Thread(target=shutdown, args=(timeout,)).start()
else:
print('Advertising forever...')
mainloop.run() # blocks until mainloop.quit() is called
ad_manager.UnregisterAdvertisement(test_advertisement)
print('Advertisement unregistered')
dbus.service.Object.remove_from_connection(test_advertisement)
</code></pre>
<p>注意<code>mainloop</code>退出时,<code>main</code>函数以cleanup调用结束</p>
<p><a href="https://github.com/Jumperr-labs/python-gatt-server" rel="nofollow noreferrer">https://github.com/Jumperr-labs/python-gatt-server</a>中的代码只是BlueZ代码的移植/重新组织版本。一个改进是修改<code>advertising</code>和<code>gatt-server</code>模块以添加适当的清理</p>
<p>一种方法是使用SIGTERM信号优雅地终止BLE服务器进程,然后<a href="https://docs.python.org/3/library/signal.html" rel="nofollow noreferrer">catching that SIGTERM within the app</a></p>
<pre><code>#
def terminate(signum, frame):
adv_manager.UnregisterAdvertisement(...)
gatt_manager.UnregisterApplication(...)
main_loop.quit()
return True
#
signal.signal(signal.SIGTERM, terminate)
</code></pre>