mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-15 00:34:05 +09:00
uxplay-beacon.py: code cleanups
This commit is contained in:
@@ -5,30 +5,21 @@
|
|||||||
# a standalone python-3.6 or later DBus-based AirPlay Service-Discovery Bluetooth LE beacon for UxPlay
|
# a standalone python-3.6 or later DBus-based AirPlay Service-Discovery Bluetooth LE beacon for UxPlay
|
||||||
# (c) F. Duncanh, October 2025
|
# (c) F. Duncanh, October 2025
|
||||||
|
|
||||||
import argparse
|
|
||||||
import gi
|
import gi
|
||||||
import os
|
try:
|
||||||
import sys
|
from gi.repository import GLib
|
||||||
import psutil
|
except ImportError:
|
||||||
import struct
|
print(f"ImportError: failed to import GLib")
|
||||||
import socket
|
|
||||||
|
|
||||||
from gi.repository import GLib
|
|
||||||
|
|
||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
import dbus.exceptions
|
import dbus.exceptions
|
||||||
import dbus.mainloop.glib
|
import dbus.mainloop.glib
|
||||||
import dbus.service
|
import dbus.service
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
|
|
||||||
ad_manager = None
|
ad_manager = None
|
||||||
airplay_advertisement = None
|
airplay_advertisement = None
|
||||||
port = int(0)
|
server_address = None
|
||||||
advmin = int(100)
|
|
||||||
advmax = int(100)
|
|
||||||
ipv4_str = "ipv4_address"
|
|
||||||
index = int(0)
|
|
||||||
|
|
||||||
BLUEZ_SERVICE_NAME = 'org.bluez'
|
BLUEZ_SERVICE_NAME = 'org.bluez'
|
||||||
LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
|
LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
|
||||||
@@ -112,7 +103,7 @@ class AirPlay_Service_Discovery_Advertisement(dbus.service.Object):
|
|||||||
in_signature='',
|
in_signature='',
|
||||||
out_signature='')
|
out_signature='')
|
||||||
def Release(self):
|
def Release(self):
|
||||||
print('%s: Released!' % self.path)
|
print(f'{self.path}: Released!')
|
||||||
|
|
||||||
|
|
||||||
class AirPlayAdvertisement(AirPlay_Service_Discovery_Advertisement):
|
class AirPlayAdvertisement(AirPlay_Service_Discovery_Advertisement):
|
||||||
@@ -134,14 +125,14 @@ class AirPlayAdvertisement(AirPlay_Service_Discovery_Advertisement):
|
|||||||
|
|
||||||
|
|
||||||
def register_ad_cb():
|
def register_ad_cb():
|
||||||
global ipv4_str
|
global server_address
|
||||||
global port
|
print(f'AirPlay Service_Discovery Advertisement ({server_address}) registered')
|
||||||
print(f'AirPlay Service_Discovery Advertisement ({ipv4_str}:{port}) registered')
|
|
||||||
|
|
||||||
|
|
||||||
def register_ad_error_cb(error):
|
def register_ad_error_cb(error):
|
||||||
print('Failed to register advertisement: ' + str(error))
|
print(f'Failed to register advertisement: {error}')
|
||||||
mainloop.quit()
|
global ad_manager
|
||||||
|
ad_manager = None
|
||||||
|
|
||||||
|
|
||||||
def find_adapter(bus):
|
def find_adapter(bus):
|
||||||
@@ -159,11 +150,13 @@ def find_adapter(bus):
|
|||||||
def setup_beacon(ipv4_str, port, advmin, advmax, index):
|
def setup_beacon(ipv4_str, port, advmin, advmax, index):
|
||||||
global ad_manager
|
global ad_manager
|
||||||
global airplay_advertisement
|
global airplay_advertisement
|
||||||
|
global server_address
|
||||||
|
server_address = f"{ipv4_str}:{port}"
|
||||||
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
bus = dbus.SystemBus()
|
bus = dbus.SystemBus()
|
||||||
adapter = find_adapter(bus)
|
adapter = find_adapter(bus)
|
||||||
if not adapter:
|
if not adapter:
|
||||||
print('LEAdvertisingManager1 interface not found')
|
print(f'LEAdvertisingManager1 interface not found')
|
||||||
return
|
return
|
||||||
adapter_props = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
|
adapter_props = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
|
||||||
"org.freedesktop.DBus.Properties")
|
"org.freedesktop.DBus.Properties")
|
||||||
@@ -176,9 +169,16 @@ def setup_beacon(ipv4_str, port, advmin, advmax, index):
|
|||||||
|
|
||||||
def beacon_on():
|
def beacon_on():
|
||||||
global ad_manager
|
global ad_manager
|
||||||
|
global airplay_advertisement
|
||||||
ad_manager.RegisterAdvertisement(airplay_advertisement.get_path(), {},
|
ad_manager.RegisterAdvertisement(airplay_advertisement.get_path(), {},
|
||||||
reply_handler=register_ad_cb,
|
reply_handler=register_ad_cb,
|
||||||
error_handler=register_ad_error_cb)
|
error_handler=register_ad_error_cb)
|
||||||
|
if ad_manager is None:
|
||||||
|
airplay_advertisement = None
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
def beacon_off():
|
def beacon_off():
|
||||||
global ad_manager
|
global ad_manager
|
||||||
global airplay_advertisement
|
global airplay_advertisement
|
||||||
@@ -190,12 +190,24 @@ def beacon_off():
|
|||||||
|
|
||||||
#==generic code (non-dbus) below here =============
|
#==generic code (non-dbus) below here =============
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import psutil
|
||||||
|
import struct
|
||||||
|
import socket
|
||||||
|
|
||||||
# global variables
|
# global variables
|
||||||
beacon_is_running = False
|
beacon_is_running = False
|
||||||
beacon_is_pending_on = False
|
beacon_is_pending_on = False
|
||||||
beacon_is_pending_off = False
|
beacon_is_pending_off = False
|
||||||
|
|
||||||
|
port = int(0)
|
||||||
|
advmin = int(100)
|
||||||
|
advmax = int(100)
|
||||||
|
ipv4_str = "ipv4_address"
|
||||||
|
index = int(0)
|
||||||
|
|
||||||
def start_beacon():
|
def start_beacon():
|
||||||
global beacon_is_running
|
global beacon_is_running
|
||||||
global port
|
global port
|
||||||
@@ -204,8 +216,7 @@ def start_beacon():
|
|||||||
global advmax
|
global advmax
|
||||||
global index
|
global index
|
||||||
setup_beacon(ipv4_str, port, advmin, advmax, index)
|
setup_beacon(ipv4_str, port, advmin, advmax, index)
|
||||||
beacon_on()
|
beacon_is_running = beacon_on()
|
||||||
beacon_is_running = True
|
|
||||||
|
|
||||||
def stop_beacon():
|
def stop_beacon():
|
||||||
global beacon_is_running
|
global beacon_is_running
|
||||||
@@ -227,12 +238,10 @@ def check_pending():
|
|||||||
global beacon_is_pending_on
|
global beacon_is_pending_on
|
||||||
global beacon_is_pending_off
|
global beacon_is_pending_off
|
||||||
if beacon_is_running:
|
if beacon_is_running:
|
||||||
#print(f"beacon running")
|
|
||||||
if beacon_is_pending_off:
|
if beacon_is_pending_off:
|
||||||
stop_beacon()
|
stop_beacon()
|
||||||
beacon_is_pending_off = False
|
beacon_is_pending_off = False
|
||||||
else:
|
else:
|
||||||
#print(f"beacon not running")
|
|
||||||
if beacon_is_pending_on:
|
if beacon_is_pending_on:
|
||||||
start_beacon()
|
start_beacon()
|
||||||
beacon_is_pending_on = False
|
beacon_is_pending_on = False
|
||||||
@@ -259,12 +268,12 @@ def check_file_exists(file_path):
|
|||||||
if not beacon_is_running:
|
if not beacon_is_running:
|
||||||
beacon_is_pending_on = True
|
beacon_is_pending_on = True
|
||||||
else:
|
else:
|
||||||
print(f"orphan beacon file {file_path} exists, but process {pname} (pid {pid}) is no longer active")
|
print(f'orphan beacon file {file_path} exists, but process {pname} (pid {pid}) is no longer active')
|
||||||
try:
|
try:
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
print(f"File '{file_path}' deleted successfully.")
|
print(f'File "{file_path}" deleted successfully.')
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"File '{file_path}' not found.")
|
print(f'File "{file_path}" not found.')
|
||||||
if beacon_is_running:
|
if beacon_is_running:
|
||||||
beacon_is_pending_off = True
|
beacon_is_pending_off = True
|
||||||
else:
|
else:
|
||||||
@@ -281,7 +290,7 @@ def process_input(value):
|
|||||||
my_integer = int(value)
|
my_integer = int(value)
|
||||||
return my_integer
|
return my_integer
|
||||||
except ValueError:
|
except ValueError:
|
||||||
printf(f"Error: could not convert '{value}' to integer: {my_integer}")
|
print(f'Error: could not convert "{value}" to integer: {my_integer}')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -289,11 +298,11 @@ def process_input(value):
|
|||||||
#check AdvInterval
|
#check AdvInterval
|
||||||
def check_adv_intrvl(min, max):
|
def check_adv_intrvl(min, max):
|
||||||
if not (100 <= min):
|
if not (100 <= min):
|
||||||
raise ValueError("AdvMin was smaller than 100 msecs")
|
raise ValueError('AdvMin was smaller than 100 msecs')
|
||||||
if not (max >= min):
|
if not (max >= min):
|
||||||
raise ValueError("AdvMax was smaller than AdvMin")
|
raise ValueError('AdvMax was smaller than AdvMin')
|
||||||
if not (max <= 10240):
|
if not (max <= 10240):
|
||||||
raise ValueError("AdvMax was larger than 10240 msecs")
|
raise ValueError('AdvMax was larger than 10240 msecs')
|
||||||
|
|
||||||
|
|
||||||
def main(file_path, ipv4_str_in, advmin_in, advmax_in, index_in):
|
def main(file_path, ipv4_str_in, advmin_in, advmax_in, index_in):
|
||||||
@@ -311,7 +320,7 @@ def main(file_path, ipv4_str_in, advmin_in, advmax_in, index_in):
|
|||||||
try:
|
try:
|
||||||
check_adv_intrvl(advmin, advmax)
|
check_adv_intrvl(advmin, advmax)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print(f"Error: {e}")
|
print(f'Error: {e}')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
GLib.timeout_add_seconds(5, on_timeout, file_path)
|
GLib.timeout_add_seconds(5, on_timeout, file_path)
|
||||||
@@ -319,12 +328,16 @@ def main(file_path, ipv4_str_in, advmin_in, advmax_in, index_in):
|
|||||||
mainloop = GLib.MainLoop()
|
mainloop = GLib.MainLoop()
|
||||||
mainloop.run()
|
mainloop.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print(f"\nExiting ...")
|
print(f'\nExiting ...')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
|
||||||
|
if not sys.version_info >= (3,6):
|
||||||
|
print("uxplay-beacon.py requires Python 3.6 or higher")
|
||||||
|
|
||||||
# Create an ArgumentParser object
|
# Create an ArgumentParser object
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@@ -383,7 +396,7 @@ if __name__ == '__main__':
|
|||||||
index = int(0)
|
index = int(0)
|
||||||
|
|
||||||
if args.file:
|
if args.file:
|
||||||
print(f"Using config file: {args.file}")
|
print(f'Using config file: {args.file}')
|
||||||
if os.path.exists(args.file):
|
if os.path.exists(args.file):
|
||||||
with open(args.file, 'r') as file:
|
with open(args.file, 'r') as file:
|
||||||
for line in file:
|
for line in file:
|
||||||
@@ -403,22 +416,22 @@ if __name__ == '__main__':
|
|||||||
if value.isdigit():
|
if value.isdigit():
|
||||||
advmin = int(value)
|
advmin = int(value)
|
||||||
else:
|
else:
|
||||||
print(f"Invalid config file input (--AdvMin) {value} in {args.file}")
|
print(f'Invalid config file input (--AdvMin) {value} in {args.file}')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
elif key == "--AdvMax":
|
elif key == "--AdvMax":
|
||||||
if value.isdigit():
|
if value.isdigit():
|
||||||
advmax = int(value)
|
advmax = int(value)
|
||||||
else:
|
else:
|
||||||
print(f"Invalid config file input (--AdvMax) {value} in {args.file}")
|
print(f'Invalid config file input (--AdvMax) {value} in {args.file}')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
elif key == "--index":
|
elif key == "--index":
|
||||||
if value.isdigit():
|
if value.isdigit():
|
||||||
index = int(value)
|
index = int(value)
|
||||||
else:
|
else:
|
||||||
print(f"Invalid config file input (--index) {value} in {args.file}")
|
print(f'Invalid config file input (--index) {value} in {args.file}')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
else:
|
else:
|
||||||
print(f"Unknown key \"{key}\" in config file {args.file}")
|
print(f'Unknown key "{key}" in config file {args.file}')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
if args.ipv4 == "use gethostbyname":
|
if args.ipv4 == "use gethostbyname":
|
||||||
@@ -431,26 +444,26 @@ if __name__ == '__main__':
|
|||||||
if args.AdvMin.isdigit():
|
if args.AdvMin.isdigit():
|
||||||
advmin = int(args.AdvMin)
|
advmin = int(args.AdvMin)
|
||||||
else:
|
else:
|
||||||
print("Invalid input (AdvMin) {args.AdvMin}")
|
print(f'Invalid input (AdvMin) {args.AdvMin}')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
if args.AdvMax != "0":
|
if args.AdvMax != "0":
|
||||||
if args.AdvMax.isdigit():
|
if args.AdvMax.isdigit():
|
||||||
advmax = int(args.AdvMax)
|
advmax = int(args.AdvMax)
|
||||||
else:
|
else:
|
||||||
print("Invalid input (AdvMin) {args.AdvMin}")
|
print(f'Invalid input (AdvMin) {args.AdvMin}')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
if args.index != "0":
|
if args.index != "0":
|
||||||
if args.index.isdigit():
|
if args.index.isdigit():
|
||||||
index = int(args.index)
|
index = int(args.index)
|
||||||
else:
|
else:
|
||||||
print("Invalid input (AdvMin) {args.AdvMin}")
|
print(f'Invalid input (AdvMin) {args.AdvMin}')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
if index < 0:
|
if index < 0:
|
||||||
raise ValueError("index was negative (forbidden)")
|
raise ValueError('index was negative (forbidden)')
|
||||||
|
|
||||||
print(f"AirPlay Service-Discovery Bluetooth LE beacon: using BLE file {args.path}, advmin:advmax {advmin}:{advmax} index:{index}")
|
print(f'AirPlay Service-Discovery Bluetooth LE beacon: using BLE file {args.path}, advmin:advmax {advmin}:{advmax} index:{index}')
|
||||||
print(f"(Press Ctrl+C to exit)")
|
print(f'(Press Ctrl+C to exit)')
|
||||||
main(args.path, ipv4_str, advmin, advmax, index)
|
main(args.path, ipv4_str, advmin, advmax, index)
|
||||||
|
|
||||||
|
|||||||
12
README.html
12
README.html
@@ -18,7 +18,7 @@ informing nearby iOS/macOS devices of the local IPv4 network address of
|
|||||||
the UxPlay server, and which TCP port to contact UxPlay on. A python
|
the UxPlay server, and which TCP port to contact UxPlay on. A python
|
||||||
script (Python >=3.6) “uxplay-beacon.py”, to broadcast the
|
script (Python >=3.6) “uxplay-beacon.py”, to broadcast the
|
||||||
Service-Discovery advertisement will be installed on systems with DBus
|
Service-Discovery advertisement will be installed on systems with DBus
|
||||||
support (Linux and *BSD, using Bluez for Bluetooth control): this does
|
support (Linux and *BSD, using BlueZ for Bluetooth control): this does
|
||||||
<strong>not</strong> require enhanced “root permissions” to run. A
|
<strong>not</strong> require enhanced “root permissions” to run. A
|
||||||
windows version of this script is also planned for the future.
|
windows version of this script is also planned for the future.
|
||||||
Instructions are <a href="#bluetooth-le-beacon-setup">given
|
Instructions are <a href="#bluetooth-le-beacon-setup">given
|
||||||
@@ -1457,7 +1457,15 @@ this to see even more of the GStreamer inner workings.</p>
|
|||||||
<p>The python>=3.6 script for running a Bluetooth-LE Service
|
<p>The python>=3.6 script for running a Bluetooth-LE Service
|
||||||
Discovery beacon is uxplay-beacon.py. Currently only a DBus version (for
|
Discovery beacon is uxplay-beacon.py. Currently only a DBus version (for
|
||||||
Linux and *BSD) is available, and it is only installed on systems which
|
Linux and *BSD) is available, and it is only installed on systems which
|
||||||
support DBus.</p>
|
support DBus. Bluetooth >= 4.0 hardware on the host computer is
|
||||||
|
required: a cheap USB bluetooth dongle can be used. Bluetooth support
|
||||||
|
(BlueZ) must be installed (on Debian-based systems:
|
||||||
|
<code>sudo apt install bluez bluez-tools</code>; recent Ubuntu releases
|
||||||
|
provide bluez as a snap package).</p>
|
||||||
|
<p>In addition to standard Python3 libraries, you may need to install
|
||||||
|
the gi, dbus, and psutil Python libraries used by uxplay-beacon.py. On
|
||||||
|
Debian-based systems:</p>
|
||||||
|
<pre><code>sudo apt install python3-gi python3-dbus python3-psutil</code></pre>
|
||||||
<p>If uxplay will be run with option “<code>uxplay -ble</code>” (so it
|
<p>If uxplay will be run with option “<code>uxplay -ble</code>” (so it
|
||||||
writes data for the Bluetooth beacon in the default BLE data file
|
writes data for the Bluetooth beacon in the default BLE data file
|
||||||
<code>~/.uxplay.ble</code>), just run <code>uxplay-beacon.py</code> in a
|
<code>~/.uxplay.ble</code>), just run <code>uxplay-beacon.py</code> in a
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -6,7 +6,7 @@
|
|||||||
service discovery). The user must set up a Bluetooth LE "beacon", (a USB 4.0 or later "dongle" can be used). See instructions
|
service discovery). The user must set up a Bluetooth LE "beacon", (a USB 4.0 or later "dongle" can be used). See instructions
|
||||||
below. The beacon runs independently of UxPlay and regularly broadcasts a Bluetooth LE ("Low Energy") 46 byte packet informing nearby iOS/macOS devices of
|
below. The beacon runs independently of UxPlay and regularly broadcasts a Bluetooth LE ("Low Energy") 46 byte packet informing nearby iOS/macOS devices of
|
||||||
the local IPv4 network address of the UxPlay server, and which TCP port to contact UxPlay on. A python script (Python >=3.6) "uxplay-beacon.py",
|
the local IPv4 network address of the UxPlay server, and which TCP port to contact UxPlay on. A python script (Python >=3.6) "uxplay-beacon.py",
|
||||||
to broadcast the Service-Discovery advertisement will be installed on systems with DBus support (Linux and *BSD, using Bluez for Bluetooth control):
|
to broadcast the Service-Discovery advertisement will be installed on systems with DBus support (Linux and *BSD, using BlueZ for Bluetooth control):
|
||||||
this does **not** require enhanced "root permissions" to run.
|
this does **not** require enhanced "root permissions" to run.
|
||||||
A windows version of this script is also planned for the future.
|
A windows version of this script is also planned for the future.
|
||||||
Instructions are [given below](#bluetooth-le-beacon-setup).
|
Instructions are [given below](#bluetooth-le-beacon-setup).
|
||||||
@@ -1475,7 +1475,17 @@ GStreamer inner workings.
|
|||||||
|
|
||||||
The python>=3.6 script for running a Bluetooth-LE Service Discovery beacon is uxplay-beacon.py.
|
The python>=3.6 script for running a Bluetooth-LE Service Discovery beacon is uxplay-beacon.py.
|
||||||
Currently only a DBus version (for Linux and *BSD) is available, and it is only installed on systems which
|
Currently only a DBus version (for Linux and *BSD) is available, and it is only installed on systems which
|
||||||
support DBus.
|
support DBus. Bluetooth >= 4.0 hardware on the host computer is required: a cheap USB bluetooth dongle
|
||||||
|
can be used. Bluetooth support (BlueZ) must be installed (on Debian-based systems: `sudo apt install bluez bluez-tools`;
|
||||||
|
recent Ubuntu releases provide bluez as a snap package).
|
||||||
|
|
||||||
|
In addition to standard Python3 libraries, you may need to install the gi, dbus, and psutil Python libraries used by
|
||||||
|
uxplay-beacon.py. On Debian-based systems:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install python3-gi python3-dbus python3-psutil
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
If uxplay will be run with option "`uxplay -ble`" (so it writes data for the Bluetooth beacon in the default BLE data file
|
If uxplay will be run with option "`uxplay -ble`" (so it writes data for the Bluetooth beacon in the default BLE data file
|
||||||
`~/.uxplay.ble`), just run ``uxplay-beacon.py`` in a separate terminal. The python script will start
|
`~/.uxplay.ble`), just run ``uxplay-beacon.py`` in a separate terminal. The python script will start
|
||||||
|
|||||||
14
README.txt
14
README.txt
@@ -12,7 +12,7 @@
|
|||||||
server, and which TCP port to contact UxPlay on. A python script
|
server, and which TCP port to contact UxPlay on. A python script
|
||||||
(Python \>=3.6) "uxplay-beacon.py", to broadcast the
|
(Python \>=3.6) "uxplay-beacon.py", to broadcast the
|
||||||
Service-Discovery advertisement will be installed on systems with
|
Service-Discovery advertisement will be installed on systems with
|
||||||
DBus support (Linux and \*BSD, using Bluez for Bluetooth control):
|
DBus support (Linux and \*BSD, using BlueZ for Bluetooth control):
|
||||||
this does **not** require enhanced "root permissions" to run. A
|
this does **not** require enhanced "root permissions" to run. A
|
||||||
windows version of this script is also planned for the future.
|
windows version of this script is also planned for the future.
|
||||||
Instructions are [given below](#bluetooth-le-beacon-setup).
|
Instructions are [given below](#bluetooth-le-beacon-setup).
|
||||||
@@ -1499,7 +1499,17 @@ this to see even more of the GStreamer inner workings.
|
|||||||
The python\>=3.6 script for running a Bluetooth-LE Service Discovery
|
The python\>=3.6 script for running a Bluetooth-LE Service Discovery
|
||||||
beacon is uxplay-beacon.py. Currently only a DBus version (for Linux and
|
beacon is uxplay-beacon.py. Currently only a DBus version (for Linux and
|
||||||
\*BSD) is available, and it is only installed on systems which support
|
\*BSD) is available, and it is only installed on systems which support
|
||||||
DBus.
|
DBus. Bluetooth \>= 4.0 hardware on the host computer is required: a
|
||||||
|
cheap USB bluetooth dongle can be used. Bluetooth support (BlueZ) must
|
||||||
|
be installed (on Debian-based systems:
|
||||||
|
`sudo apt install bluez bluez-tools`; recent Ubuntu releases provide
|
||||||
|
bluez as a snap package).
|
||||||
|
|
||||||
|
In addition to standard Python3 libraries, you may need to install the
|
||||||
|
gi, dbus, and psutil Python libraries used by uxplay-beacon.py. On
|
||||||
|
Debian-based systems:
|
||||||
|
|
||||||
|
sudo apt install python3-gi python3-dbus python3-psutil
|
||||||
|
|
||||||
If uxplay will be run with option "`uxplay -ble`" (so it writes data for
|
If uxplay will be run with option "`uxplay -ble`" (so it writes data for
|
||||||
the Bluetooth beacon in the default BLE data file `~/.uxplay.ble`), just
|
the Bluetooth beacon in the default BLE data file `~/.uxplay.ble`), just
|
||||||
|
|||||||
Reference in New Issue
Block a user