Python : Script minimal pour lister et lire les caractéristiques d'un périphérique BLE avec Python (Bleak)

Ce script liste les périphériques Bluetooth Low Energy (BLE) et lit leurs caractéristiques à l’aide de la bibliothèque Bleak en Python. Il s’agit d’un exemple minimal pour démontrer comment se connecter à un périphérique BLE et lire ses caractéristiques.

connect_ble_device.py
#!/usr/bin/env python3
"""
Connexion à un périphérique BLE et exploration des services

Ce script se connecte à un périphérique BLE spécifique par adresse MAC et liste
tous les services disponibles ainsi que leurs caractéristiques/attributs.

Prérequis :
    - bibliothèque bleak : pip install bleak

Utilisation :
    python connect_ble_device.py [ADRESSE_MAC]

Exemple :
    python connect_ble_device.py 24:EC:4A:76:00:32
"""

import asyncio
import sys
import argparse
from bleak import BleakClient
from bleak.exc import BleakError
from datetime import datetime

async def explore_device_services(client, show_descriptors=False):
    """
    Explorer tous les services et caractéristiques d'un périphérique BLE connecté.

    Args:
        client (BleakClient): Client BLE connecté
    """
    try:
        # Récupérer tous les services sous forme de liste
        services = list(client.services)
        if not services:
            print("Aucun service trouvé sur ce périphérique.")
            return

        print(f"{len(services)} service(s) trouvé(s) :")
        print("=" * 80)

        for service in services:
            print(f"\nService : {service.uuid}")
            print(f"Description : {service.description}")
            print(f"Handle : {service.handle}")

            # Récupérer les caractéristiques de ce service
            characteristics = service.characteristics

            if characteristics:
                print(f"  Caractéristiques ({len(characteristics)}) :")
                print("  " + "-" * 76)

                for char in characteristics:
                    print(f"    UUID : {char.uuid}")
                    print(f"    Description : {char.description}")
                    print(f"    Handle : {char.handle}")
                    print(f"    Propriétés : {', '.join(char.properties)}")

                    # Essayer de lire la caractéristique si elle est lisible
                    if "read" in char.properties:
                        try:
                            value = await client.read_gatt_char(char.uuid)
                            # Essayer de décoder en chaîne, sinon afficher en hexadécimal
                            try:
                                decoded_value = value.decode('utf-8')
                                print(f"    Valeur (chaîne) : {decoded_value}")
                            except UnicodeDecodeError:
                                hex_value = ' '.join(f'{b:02x}' for b in value)
                                print(f"    Valeur (hex) : {hex_value}")
                                print(f"    Valeur (octets bruts) : {value}")
                        except Exception as e:
                            print(f"    Valeur : <Lecture impossible - {e}>")

                    # Afficher les descripteurs uniquement si demandé
                    if show_descriptors:
                        descriptors = char.descriptors
                        if descriptors:
                            print(f"    Descripteurs ({len(descriptors)}) :")
                            for desc in descriptors:
                                print(f"      UUID : {desc.uuid}")
                                print(f"      Description : {desc.description}")
                                print(f"      Handle : {desc.handle}")
                                # Essayer de lire le descripteur si possible
                                try:
                                    desc_value = await client.read_gatt_descriptor(desc.handle)
                                    try:
                                        decoded_desc = desc_value.decode('utf-8')
                                        print(f"      Valeur (chaîne) : {decoded_desc}")
                                    except UnicodeDecodeError:
                                        hex_desc = ' '.join(f'{b:02x}' for b in desc_value)
                                        print(f"      Valeur (hex) : {hex_desc}")
                                except Exception as e:
                                    print(f"      Valeur : <Lecture impossible - {e}>")
                            print()  # Ligne vide entre les caractéristiques
                    else:
                        print()
            else:
                print("    Aucune caractéristique trouvée pour ce service.")

            print("-" * 80)

    except Exception as e:
        print(f"Erreur lors de l'exploration des services : {e}")


async def connect_and_explore(mac_address, show_descriptors=False):
    """
    Se connecter à un périphérique BLE et explorer ses services.

    Args:
        mac_address (str): Adresse MAC du périphérique auquel se connecter
        scan_time (int): Durée de recherche du périphérique
    """
    print(f"\nTentative de connexion à {mac_address} ...")
    try:
        async with BleakClient(mac_address) as client:
            if client.is_connected:
                print(f"Connexion réussie à {mac_address}")
                print(f"Connecté à : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
                print()
                # Explorer tous les services et caractéristiques
                await explore_device_services(client, show_descriptors=show_descriptors)
                print(f"\nDéconnecté de {mac_address}")
                return True
            else:
                print(f"Échec de la connexion à {mac_address}")
                return False
    except BleakError as e:
        print(f"Erreur Bluetooth : {e}")
        return False
    except Exception as e:
        print(f"Erreur inattendue : {e}")
        return False


async def main():
    """
    Fonction principale exécutant le connecteur et l'explorateur de périphérique BLE.
    """
    print("Connexion à un périphérique BLE et exploration des services")
    print(f"Démarré à : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print()

    parser = argparse.ArgumentParser(description="Se connecter à un périphérique BLE et lister tous les services et attributs.")
    parser.add_argument("mac_address", nargs="?", default="24:EC:4A:76:00:32", help="Adresse MAC du périphérique BLE (par défaut : 24:EC:4A:76:00:32)")
    parser.add_argument("-d", "--descriptors", action="store_true", help="Afficher les descripteurs individuels pour chaque caractéristique")
    args = parser.parse_args()

    mac_address = args.mac_address
    show_descriptors = args.descriptors

    print(f"Utilisation de l'adresse MAC : {mac_address}")
    if show_descriptors:
        print("Affichage des descripteurs activé.")

    # Valider le format de l'adresse MAC (vérification basique)
    if len(mac_address.replace(":", "").replace("-", "")) != 12:
        print(f"Format d'adresse MAC invalide : {mac_address}")
        print("Format attendu : XX:XX:XX:XX:XX:XX ou XX-XX-XX-XX-XX-XX")
        return

    print()

    # Se connecter et explorer le périphérique
    success = await connect_and_explore(mac_address, show_descriptors=show_descriptors)

    if success:
        print("\nExploration du périphérique terminée avec succès.")
    else:
        print("\nÉchec de l'exploration du périphérique.")


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("\nOpération interrompue par l'utilisateur.")
    except Exception as e:
        print(f"Une erreur s'est produite : {e}")
        sys.exit(1)

Check out similar posts by category: Bluetooth, Python