508 lines
13 KiB
Markdown
Executable File
508 lines
13 KiB
Markdown
Executable File
# 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 – Wi‑Fi 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}")
|
||
```
|