#!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later # adapted from https://github.com/bluez/bluez/blob/master/test/example-advertisement #---------------------------------------------------------------- # a standalone python-3.6 or later AirPlay Service-Discovery Bluetooth LE beacon for UxPlay # (c) F. Duncanh, October 2025 import sys if not sys.version_info >= (3,6): print("uxplay-beacon.py requires Python 3.6 or higher") import gi try: from gi.repository import GLib except ImportError as e: print(f'ImportError: {e}, failed to import GLib from Python GObject Introspection Library ("gi")') print('Install PyGObject pip3 install PyGobject==3.50.0') print(f'You may need to use pip option "--break-system-packages" (disregard the warning)') raise SystemExit(1) import importlib import argparse import textwrap import os import struct import socket import time import platform try: import psutil except ImportError as e: print(f'ImportError {e}: failed to import psutil') print(f' install the python3 psutil package') raise SystemExit(1) # global variables beacon_is_running = False beacon_is_pending_on = False beacon_is_pending_off = False advertised_port = None port = None advmin = None advmax = None ipv4_str = None index = None windows = 'Windows' linux = 'Linux' os_name = platform.system() # external functions that must be supplied by loading a module: from typing import Optional def setup_beacon(ipv4_str: str, port: int, advmin: Optional[int], advmax: Optional[int], index: Optional[int]) -> bool: return False def beacon_on() ->Optional[int]: return None def beacon_off(): return def find_device(device: Optional[str]) -> Optional[str]: return None #internal functions def start_beacon(): global beacon_is_running global port global ipv4_str global advmin global advmax global index if beacon_is_running: print(f'code error, should not happen') raise SystemExit(1) setup_beacon(ipv4_str, port, advmin, advmax, index) advertised_port = beacon_on() beacon_is_running = advertised_port is not None count = 1 while not beacon_is_running: print(f'Failed attempt {count} to start beacon:') advertised_port = beacon_on() beacon_is_running = advertised_port is not None count += 1 if count > 5: print(f'Giving up, check Bluetooth adapter') raise SystemExit(1) def stop_beacon(): global beacon_is_running global advertised_port beacon_off() advertised_port = None beacon_is_running = False def pid_is_running(pid): return psutil.pid_exists(pid) def check_port(port): if advertised_port is None or port == advertised_port: return True else: return False def check_process_name(pid, pname): try: process = psutil.Process(pid) if process.name().find(pname,0) == 0: return True else: return False except psutil.NoSuchProcess: return False def check_pending(): global beacon_is_pending_on global beacon_is_pending_off if beacon_is_running: if beacon_is_pending_off: stop_beacon() beacon_is_pending_off = False else: if beacon_is_pending_on: start_beacon() beacon_is_pending_on = False return True def check_file_exists(file_path): global port global beacon_is_pending_on global beacon_is_pending_off pname = "process name unread" if os.path.isfile(file_path): test = True try: with open(file_path, 'rb') as file: data = file.read(2) port = struct.unpack('