This commit is contained in:
Gilles Soulier
2026-01-05 16:08:01 +01:00
parent dcba044cd6
commit c67befc549
2215 changed files with 26743 additions and 329 deletions

View File

@@ -0,0 +1,507 @@
# Feature: Classification intelligente des périphériques
## Vue d'ensemble
Détection automatique du `type_principal` et `sous_type` des périphériques basée sur l'analyse du contenu CLI et des fichiers markdown.
Le système analyse intelligemment :
- Le contenu de la sortie `lsusb -v`
- Les fichiers markdown importés
- Les vendor/product IDs
- Les classes USB
- Les chaînes de caractères (manufacturer, product)
## Fonctionnement
### Stratégies de détection (par ordre de priorité)
1. **USB Device Class** - Basé sur `bDeviceClass`
- `08` → Clé USB (Mass Storage)
- `03` → HID (Clavier/Souris, affiné par mots-clés)
- `0e` → Webcam (Video)
- `09` → Hub
- `e0` → Bluetooth (Wireless Controller)
2. **Vendor/Product Info** - Analyse des IDs et chaînes
- Recherche de mots-clés dans manufacturer/product strings
- Matching sur vendor_id/product_id connus
3. **Analyse du contenu CLI** - Mots-clés dans `lsusb -v`
- WiFi : `wifi`, `wireless`, `802.11`, `wlan`, `rtl81xx`, `mt76xx`
- Bluetooth : `bluetooth`, `bcm20702`
- Storage : `mass storage`, `flash drive`, `sandisk`
- Hub : `usb hub`, `multi-port`
- Keyboard : `keyboard`, `clavier`, `hid.*keyboard`
- Mouse : `mouse`, `souris`
- Webcam : `webcam`, `camera`, `uvc`
- Ethernet : `ethernet`, `gigabit`, `rtl81xx.*ethernet`
4. **Analyse du markdown** - Mots-clés dans le fichier .md
- Même système de patterns que pour le CLI
- Utilisé lors de l'import de fichiers markdown
### Système de scoring
- Chaque mot-clé trouvé augmente le score d'un type
- Le type avec le meilleur score est sélectionné
- Si aucun match : fallback sur `("USB", "Autre")`
## Exemples de détection
### Exemple 1 : Adaptateur WiFi
**Input CLI :**
```
Bus 002 Device 005: ID 0bda:8176 Realtek RTL8188CUS
bDeviceClass 0
iManufacturer 1 Realtek
iProduct 2 802.11n WLAN Adapter
```
**Détection :**
- Mots-clés trouvés : `realtek`, `rtl8176`, `802.11n`, `wlan`
- **Résultat : `type_principal = "USB"`, `sous_type = "Adaptateur WiFi"`**
### Exemple 2 : Clé USB
**Input CLI :**
```
Bus 002 Device 003: ID 0781:55ab SanDisk Corp.
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
iManufacturer 1 USB
iProduct 2 SanDisk 3.2Gen1
Interface Class: 08 — Mass Storage
```
**Détection :**
- Device Class 08 trouvé → Mass Storage
- Mots-clés : `sandisk`, `mass storage`
- **Résultat : `type_principal = "USB"`, `sous_type = "Clé USB"`**
### Exemple 3 : Bluetooth
**Input CLI :**
```
Bus 001 Device 004: ID 0b05:17cb ASUSTek
bDeviceClass 224 Wireless
iManufacturer 1 Broadcom Corp
iProduct 2 BCM20702A0
```
**Détection :**
- Device Class e0 (Wireless Controller)
- Mots-clés : `bluetooth`, `bcm20702`
- **Résultat : `type_principal = "Bluetooth"`, `sous_type = "Autre"`**
### Exemple 4 : Import markdown
**Fichier : `ID_0bda_8176.md`**
```markdown
# USB Device ID 0bda_8176
## Description
Realtek RTL8188CUS WiFi USB
```
**Détection :**
- Analyse du contenu markdown
- Mots-clés : `wi-fi`, `usb`, `realtek`
- IDs extraits du nom de fichier : `vendor_id=0x0bda`, `product_id=0x8176`
- **Résultat : `type_principal = "USB"`, `sous_type = "Adaptateur WiFi"`**
## Fichiers modifiés
### Backend
#### 1. Nouveau classificateur
**`backend/app/utils/device_classifier.py`** (NOUVEAU)
Classe `DeviceClassifier` avec méthodes :
```python
@staticmethod
def classify_device(cli_content: Optional[str] = None,
synthese_content: Optional[str] = None,
device_info: Optional[Dict] = None) -> Tuple[str, str]:
"""
Classify a device using all available information
Returns: (type_principal, sous_type)
"""
```
Dictionnaire `TYPE_KEYWORDS` - Mapping (type, sous_type) → liste de patterns regex
Dictionnaire `USB_CLASS_MAPPING` - Mapping USB class code → (type, sous_type)
#### 2. Endpoints modifiés
**`backend/app/api/endpoints/peripherals.py`**
**Ligne 28** - Import du classificateur :
```python
from app.utils.device_classifier import DeviceClassifier
```
**Ligne 737-746** - USB CLI extraction avec détection intelligente :
```python
# Intelligent classification of device type
type_principal, sous_type = DeviceClassifier.classify_device(
cli_content=device_section,
synthese_content=None,
device_info=device_info
)
# Refine Bluetooth subtype if needed
if type_principal == "Bluetooth" and sous_type == "Autre":
sous_type = DeviceClassifier.refine_bluetooth_subtype(device_section)
```
**Ligne 589-614** - Import markdown avec détection intelligente :
```python
# Intelligent classification of device type from markdown content
type_principal = parsed_data.get("type_principal")
sous_type = parsed_data.get("sous_type")
if not type_principal or not sous_type:
device_info = {
"vendor_id": parsed_data.get("caracteristiques_specifiques", {}).get("vendor_id"),
"product_id": parsed_data.get("caracteristiques_specifiques", {}).get("product_id"),
"manufacturer": parsed_data.get("marque"),
"product": parsed_data.get("modele"),
"device_class": parsed_data.get("caracteristiques_specifiques", {}).get("device_class"),
}
detected_type_principal, detected_sous_type = DeviceClassifier.classify_device(
cli_content=None,
synthese_content=md_content,
device_info=device_info
)
if not type_principal:
type_principal = detected_type_principal
if not sous_type:
sous_type = detected_sous_type
```
**Ligne 625** - Stockage de la synthèse markdown :
```python
"synthese": md_content, # Store the full markdown content in synthese field
```
### Frontend
**`frontend/js/peripherals.js`** - Ligne 452-477
Amélioration du pré-remplissage avec logique robuste :
```javascript
// Set type_principal and wait for subtypes to load before setting sous_type
if (suggested.type_principal) {
document.getElementById('type_principal').value = suggested.type_principal;
// Trigger the change event to load subtypes
const typePrincipalSelect = document.getElementById('type_principal');
const changeEvent = new Event('change');
typePrincipalSelect.dispatchEvent(changeEvent);
// Wait for subtypes to load, then set sous_type
if (suggested.sous_type) {
// Use a Promise-based approach with retry logic
const setSousType = async () => {
for (let i = 0; i < 10; i++) {
await new Promise(resolve => setTimeout(resolve, 100));
const sousTypeSelect = document.getElementById('sous_type');
if (sousTypeSelect && sousTypeSelect.options.length > 1) {
// Options are loaded
sousTypeSelect.value = suggested.sous_type;
break;
}
}
};
setSousType();
}
}
```
**Amélioration** : Retry logic avec vérification du chargement des options au lieu d'un simple timeout
## Flows utilisateur
### Flow 1 : Import USB avec CLI
1. Utilisateur clique **"Importer USB"**
2. Popup : colle la sortie de `lsusb -v`
3. Click **"Importer"**
4. Backend détecte les périphériques
5. Popup 2 : liste avec radio buttons
6. Utilisateur sélectionne un périphérique
7. Click **"Finaliser"**
8. **Backend analyse le CLI et détecte automatiquement le type**
9. Formulaire s'ouvre avec :
- `type_principal` = **"USB"** (pré-sélectionné)
- `sous_type` = **"Adaptateur WiFi"** (pré-sélectionné automatiquement)
- `nom`, `marque`, `modele`, `numero_serie` pré-remplis
- `cli` contient le markdown formaté
### Flow 2 : Import fichier markdown
1. Utilisateur clique **"Importer Markdown"**
2. Sélectionne un fichier `.md`
3. **Backend lit et analyse le contenu**
4. **Détecte le type automatiquement depuis le markdown**
5. Formulaire s'ouvre avec :
- `type_principal` et `sous_type` pré-sélectionnés
- `synthese` contient le contenu markdown complet
- Autres champs extraits du markdown
## Patterns de détection supportés
### WiFi/Wireless
```regex
wi[-]?fi
wireless
802\.11[a-z]
rtl81\d+ # Realtek WiFi chips
mt76\d+ # MediaTek WiFi chips
atheros
qualcomm.*wireless
broadcom.*wireless
wlan
wireless\s+adapter
```
### Bluetooth
```regex
bluetooth
bcm20702 # Broadcom BT chips
bt\s+adapter
```
### Storage - Clé USB
```regex
flash\s+drive
usb\s+stick
cruzer # SanDisk Cruzer series
datatraveler # Kingston DataTraveler
usb.*flash
clé\s+usb
pendrive
```
### Storage - Disque dur externe
```regex
external\s+hdd
external\s+ssd
portable\s+ssd
portable\s+drive
disk\s+drive
disque\s+dur\s+externe
my\s+passport # WD My Passport
expansion # Seagate Expansion
backup\s+plus # Seagate Backup Plus
elements # WD Elements
touro # Hitachi Touro
adata.*hd\d+ # ADATA external drives
```
### Storage - Lecteur de carte
```regex
card\s+reader
lecteur.*carte
sd.*reader
microsd.*reader
multi.*card
cf.*reader
```
### Hub
```regex
usb\s+hub
hub\s+controller
multi[-]?port
```
### ZigBee
```regex
zigbee
conbee # Dresden Elektronik ConBee
cc2531 # Texas Instruments ZigBee chip
cc2652 # TI newer ZigBee chip
dresden\s+elektronik
zigbee.*gateway
zigbee.*coordinator
thread.*border
```
### Lecteur biométrique (Fingerprint)
```regex
fingerprint
fingprint # Common typo
empreinte
biometric
biométrique
validity.*sensor # Validity sensors
synaptics.*fingerprint
goodix.*fingerprint
elan.*fingerprint
```
### Clavier
```regex
keyboard
clavier
hid.*keyboard
```
### Souris
```regex
mouse
souris
hid.*mouse
optical\s+mouse
```
### Webcam
```regex
webcam
camera
video\s+capture
uvc # USB Video Class
```
### Ethernet
```regex
ethernet
gigabit
network\s+adapter
lan\s+adapter
rtl81\d+.*ethernet
```
## Extensibilité
Pour ajouter un nouveau type de périphérique :
1. **Ajouter le type dans `config/peripheral_types.yaml`**
```yaml
- id: usb_nouveau_type
nom: Nouveau Type USB
type_principal: USB
sous_type: Mon Nouveau Type
icone: icon-name
```
2. **Ajouter les patterns dans `device_classifier.py`**
```python
TYPE_KEYWORDS = {
# ...
("USB", "Mon Nouveau Type"): [
r"pattern1",
r"pattern2",
r"mot[-]?clé",
],
}
```
3. **Redémarrer le backend**
Le nouveau type sera automatiquement :
- Détectable lors des imports
- Disponible dans les dropdowns
- Pré-sélectionné si les patterns matchent
## Avantages
**Gain de temps** - Plus besoin de sélectionner manuellement le type
**Précision** - Détection basée sur plusieurs sources d'information
**Extensible** - Facile d'ajouter de nouveaux types et patterns
**Robuste** - Fallback sur "USB / Autre" si détection impossible
**Multilingue** - Supporte patterns français et anglais
**Flexible** - Fonctionne avec CLI et markdown
## Limitations actuelles
⚠️ **Périphériques hybrides** - Un Unifying Receiver est détecté comme "USB / Autre" car il peut être clavier OU souris
⚠️ **Périphériques rares** - Types exotiques non couverts par les patterns
⚠️ **Patterns manquants** - Certains fabricants utilisent des termes non standards
## Améliorations futures
1. **Machine Learning** - Entraîner un modèle sur les périphériques existants
2. **Base de données USB ID** - Intégration avec `usb.ids` pour reconnaissance par vendor/product
3. **Détection multi-fonction** - Support des périphériques combinés (ex: hub + ethernet)
4. **Historique** - Apprendre des corrections manuelles utilisateur
5. **API externe** - Interroger des APIs publiques (USB ID Repository)
## Tests
### Test 1 : WiFi Realtek
```bash
# Préparer un fichier test
cat > /tmp/test_wifi.txt << 'EOF'
Bus 002 Device 005: ID 0bda:8176 Realtek Semiconductor Corp.
bDeviceClass 0
iManufacturer 1 Realtek
iProduct 2 802.11n WLAN Adapter
EOF
# Dans l'interface :
# 1. Importer USB
# 2. Coller le contenu
# 3. Sélectionner le périphérique
# 4. Vérifier : type_principal = "USB", sous_type = "Adaptateur WiFi"
```
**Résultat attendu** : ✅ Détection automatique comme "Adaptateur WiFi"
### Test 2 : Clé USB SanDisk
```bash
cat > /tmp/test_storage.txt << 'EOF'
Bus 002 Device 003: ID 0781:55ab SanDisk Corp.
bDeviceClass 0
iProduct 2 SanDisk 3.2Gen1
bInterfaceClass 8 Mass Storage
EOF
# Même procédure
```
**Résultat attendu** : ✅ Détection comme "Clé USB"
### Test 3 : Import markdown WiFi
```bash
# Utiliser le fichier existant
# fichier_usb/ID_0bda_8176.md
```
**Résultat attendu** : ✅ Détection automatique depuis le markdown
## Logs de détection
Pour débugger la détection, ajouter des logs dans `DeviceClassifier.classify_device()` :
```python
logger.info(f"Classification attempt - CLI: {bool(cli_content)}, Synthese: {bool(synthese_content)}")
logger.info(f"Device info: {device_info}")
logger.info(f"Detected: type_principal={type_principal}, sous_type={sous_type}")
```