Initial commit — Serveur Lucas SmartEye

API réception alertes chute (SmartEye/YOLO), analyse IA (Gemini 2.5 Flash),
gestion alertes avec escalade (watchdog), notifications Firebase,
dashboard web, documentation MkDocs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-03-14 21:26:06 +01:00
commit 24dbc7cd6a
64 changed files with 9677 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
# Endpoints API
Section en cours de rédaction. Cette page décrira les endpoints de l'API REST, leurs paramètres et leurs réponses.

View File

@@ -0,0 +1,3 @@
# Payload FCM
Section en cours de rédaction. Cette page décrira la structure des payloads Firebase Cloud Messaging utilisés pour les notifications push.

View File

@@ -0,0 +1,3 @@
# Flux de données
Section en cours de rédaction. Cette page décrira le flux de données entre les différents composants du système SmartEye (Jetson, serveur, application mobile).

View File

@@ -0,0 +1,84 @@
# Architecture du système
## Vue d'ensemble
```mermaid
graph TB
subgraph Domicile["🏠 Domicile du bénéficiaire"]
CAM1[Caméra 1 - Salon]
CAM2[Caméra 2 - Chambre]
CAM3[Caméra 3 - Cuisine]
JETSON[Jetson Orin Nano<br/>SmartEye v30]
CAM1 -->|RTSP local| JETSON
CAM2 -->|RTSP local| JETSON
CAM3 -->|RTSP local| JETSON
end
subgraph OVH["☁️ Serveur OVH (57.128.74.87)"]
API[api.php<br/>Réception images]
ANALYZE[analyze.py<br/>Gemini 2.5 Flash]
ALERT[alert_manager.py<br/>Cycle de vie alertes]
WATCHDOG[alert_watchdog.py<br/>Escalade cron]
DB[(database.json)]
API --> ANALYZE
ANALYZE --> ALERT
ANALYZE --> DB
WATCHDOG --> ALERT
end
subgraph Mobile["📱 Téléphones aidants"]
APP1[LucasApp - Fille]
APP2[LucasApp - Fils]
end
JETSON ==>|SSH Tunnel<br/>Image POST| API
ANALYZE ==>|Firebase Push| APP1
ANALYZE ==>|Firebase Push| APP2
JETSON -.->|RTSP Tunnel| APP1
JETSON -.->|Audio WebSocket| APP1
```
## Composants
### SmartEye (Jetson Orin Nano)
| Caractéristique | Détail |
|----------------|--------|
| **Modèle IA** | YOLOv8 (détection) + pose estimation |
| **Caméras max** | 6 par site |
| **Flux** | HD (/11) pour détection, SD (/12) pour streaming |
| **Communication** | SSH tunnels inversés vers OVH |
### Lucas (Serveur OVH)
| Caractéristique | Détail |
|----------------|--------|
| **Analyse IA** | Google Gemini 2.5 Flash (vision) |
| **Notifications** | Firebase Cloud Messaging |
| **SMS backup** | OVH SMS API |
| **Dashboard** | PHP + Tailwind CSS |
### LucasApp (Flutter)
| Caractéristique | Détail |
|----------------|--------|
| **Plateforme** | Android (Flutter) |
| **Flux vidéo** | RTSP via tunnels SSH |
| **Interphone** | WebSocket audio bidirectionnel (PCM A-law) |
| **Acquittement** | POST vers alert_ack.php |
## Convention de ports
Chaque site client utilise une plage de 10 ports :
```
Site N → base_port = 8550 + (N-1) × 10
Offset 0-5 : RTSP caméras (max 6)
Offset 6 : Audio WebSocket relay
Offset 7 : WebUI locale SmartEye
Offset 8-9 : Réservés
```
!!! note "Site pilote (Aléria)"
Le site pilote conserve ses ports historiques : 8554/8555/8556 (RTSP) + 8800 (audio).

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

View File

@@ -0,0 +1,330 @@
# Preparation de la carte Gold (image maitre)
<div style="background: linear-gradient(135deg, #f57f17 0%, #e65100 100%); color: white; padding: 24px; border-radius: 12px; margin-bottom: 24px;">
<h2 style="margin:0; color: white;">Document reserve au responsable technique</h2>
<p style="margin: 8px 0 0 0; opacity: 0.9;">Cette procedure ne se fait qu'une seule fois. Elle produit l'image maitre qui sera clonee pour chaque nouveau client.</p>
</div>
---
## Qu'est-ce que la carte Gold ?
La carte Gold est une carte SD **de reference** contenant :
- Le systeme d'exploitation (JetPack / Linux)
- Les drivers GPU (CUDA, cuDNN, TensorRT)
- SmartEye complet (YOLO, modeles IA, services)
- **Aucune configuration client** (pas de cameras, pas de compte, pas de logs)
C'est l'equivalent de l'image usine d'une box Free ou Orange. Le Jetson qui demarre avec cette carte arrive directement en **mode installation**, pret a etre configure depuis un smartphone.
---
## Principe : on ne touche JAMAIS a la carte de dev
```
Carte actuelle (DEV/Aleria) Carte neuve n°1 Carte neuve n°2, 3, 4...
┌─────────────────────┐ ┌─────────────┐ ┌─────────────┐
│ SmartEye + Demo_01 │──── clone ──→│ Copie brute │ │ │
│ Cameras configurees │ │ (tout dedans)│ │ │
│ Logs, images, etc. │ └──────┬──────┘ │ Client X │
│ │ │ │ │
│ ON N'Y TOUCHE PAS │ nettoyage config │ │
│ │ suppression logs └─────────────┘
└─────────────────────┘ suppression images ▲
│ │
▼ │
┌──────────────┐ clone a l'infini
│ CARTE GOLD │────────────────┘
│ (mode usine)│
└──────────────┘
```
!!! warning "Regle d'or"
La carte du Jetson de developpement (Aleria/Demo_01) **reste en place, intacte**. On la clone d'abord, puis on nettoie le clone. Comme ca, si quelque chose se passe mal, le Jetson de dev est toujours operationnel.
---
## Pre-requis
| Element | Detail |
|---------|--------|
| **Jetson de dev** | Le Jetson actuel (Aleria) qui fonctionne |
| **Carte SD neuve** | 64 Go minimum, classe A2/U3 |
| **PC Linux ou Mac** | Pour cloner et nettoyer |
| **Lecteur de carte SD** | USB ou integre |
| **Espace disque** | ~64 Go libres sur le PC |
| **Balena Etcher** | [balena.io/etcher](https://www.balena.io/etcher/) (gratuit) |
---
!!! danger "Carte SD de reference"
La carte originale du Jetson de dev est une **Samsung EVO 128 Go**.
Les cartes clones doivent etre de capacite **egale ou superieure** et idealement de la meme marque.
Deux cartes "128 Go" de marques differentes n'ont pas exactement la meme taille en octets.
Si la carte clone est plus petite (meme de quelques Mo), le clone sera corrompu et ne bootera pas.
## Etape 1 — Cloner la carte actuelle (telle quelle)
Eteindre le Jetson de dev proprement :
```bash
# Sur le Jetson
sudo shutdown -h now
# Attendre extinction complete (LED eteintes)
```
Retirer la carte SD et l'inserer dans le lecteur du PC.
=== "Linux"
```bash
# Identifier la carte SD
lsblk
# Cloner la carte entiere (NE RIEN MODIFIER)
sudo dd if=/dev/sdb of=$HOME/smarteye-dev-backup.img bs=4M status=progress
# Compresser le backup
gzip $HOME/smarteye-dev-backup.img
```
=== "Mac"
```bash
# Identifier la carte SD
diskutil list
# Demonter sans ejecter
diskutil unmountDisk /dev/disk4
# Cloner (~ 1h pour une carte 128 Go)
sudo dd if=/dev/rdisk4 of=/Users/$(whoami)/smarteye-dev-backup.img bs=4m status=progress
# Compresser
gzip /Users/$(whoami)/smarteye-dev-backup.img
```
!!! tip "Pourquoi `/Users/$(whoami)/` et pas `~/` ?"
Avec `sudo`, le `~` pointe vers `/var/root/` au lieu de votre dossier utilisateur. `$(whoami)` resout ce probleme en inserant automatiquement votre nom d'utilisateur.
!!! tip "Conserver ce backup"
Le fichier `smarteye-dev-backup.img.gz` est votre **sauvegarde de securite** du Jetson de dev. Gardez-le precieusement. En cas de probleme, vous pouvez toujours reflasher la carte d'origine.
**Remettre immediatement la carte dans le Jetson de dev** et le redemarrer. Il reprend son fonctionnement normal.
---
## Etape 2 — Flasher le clone sur une carte neuve
Inserer une **carte SD neuve** (128 Go) dans le lecteur.
=== "Balena Etcher (recommande)"
1. Ouvrir Balena Etcher
2. **Flash from file** → selectionner `smarteye-dev-backup.img.gz`
3. **Select target** → selectionner la carte SD neuve
4. **Flash!** → attendre ~10 minutes
=== "Linux"
```bash
# 1. Verifier que la carte neuve est detectee
lsblk
# Reperer la carte neuve par sa taille (ex: /dev/sdb, 128 Go)
# 2. Flasher
gunzip -c $HOME/smarteye-dev-backup.img.gz | sudo dd of=/dev/sdb bs=4M status=progress
sync
```
=== "Mac"
```bash
# 1. Verifier que la carte neuve est detectee
diskutil list
# Reperer la carte par sa taille (ex: /dev/disk4, 134.2 GB = 128 Go)
# 2. Demonter la carte sans l'ejecter
diskutil unmountDisk /dev/disk4
# 3. Flasher (~ 1h pour 128 Go, adapter /dev/rdisk4 si necessaire)
gunzip -c /Users/$(whoami)/smarteye-dev-backup.img.gz | sudo dd of=/dev/rdisk4 bs=4m status=progress
```
!!! info "Pourquoi 3 commandes separees ?"
- `diskutil list` → **verifier** qu'on cible la bonne carte (pas le disque du Mac !)
- `diskutil unmountDisk` → **liberer** la carte pour que `dd` puisse y ecrire
- `gunzip -c ... | dd` → **decompresser et flasher** en une seule passe
A ce stade, la carte neuve est une **copie exacte** de la carte de dev (avec Demo_01, les cameras, les logs — tout).
---
## Etape 3 — Nettoyer le clone (le transformer en Gold)
Inserer la carte neuve (le clone) dans le Jetson **ou** la monter sur le PC pour nettoyer.
### Option A : Nettoyer depuis le Jetson
Inserer la carte clone dans un Jetson, le demarrer, puis :
```bash
# 1. Arreter SmartEye
sudo systemctl stop smarteye
# 2. Supprimer la configuration client (Demo_01)
rm -f /w/smarteye_config.yaml
# 3. Supprimer toutes les images capturees
rm -rf /w/runs/*
# 4. Supprimer les logs
rm -rf /w/logs/*
rm -f /w/*.log
# 5. Nettoyer les known_hosts SSH (specifiques au site)
rm -f ~/.ssh/known_hosts
# 6. Vider l'historique bash
history -c
> ~/.bash_history
```
### Option B : Nettoyer depuis le PC (sans Jetson)
Si vous n'avez qu'un seul Jetson, monter la carte sur le PC :
```bash
# Monter la partition principale de la carte SD
sudo mount /dev/sdb1 /mnt
# Nettoyer (adapter les chemins selon la structure)
sudo rm -f /mnt/home/*/w/smarteye_config.yaml
sudo rm -rf /mnt/home/*/w/runs/*
sudo rm -rf /mnt/home/*/w/logs/*
sudo rm -f /mnt/home/*/.ssh/known_hosts
sudo rm -f /mnt/home/*/.bash_history
# Demonter
sudo umount /mnt
```
### Verification du nettoyage
```
=== Verification ===
Config client : ABSENT ✓
Images : 0 fichiers ✓
Logs : 0 fichiers ✓
known_hosts : ABSENT ✓
```
---
## Etape 4 — Verifier le mode setup
Si vous avez nettoye depuis le Jetson (Option A), verifier que SmartEye demarre en mode installation :
```bash
sudo systemctl start smarteye
curl http://localhost:8080/api/identity
```
Reponse attendue :
```json
{
"type": "smarteye",
"version": "v30",
"status": "setup",
"message": "En attente de configuration"
}
```
Puis eteindre :
```bash
sudo shutdown -h now
```
---
## Etape 5 — Creer l'image Gold definitive
Retirer la carte nettoyee du Jetson et la recloner en tant que **Gold** :
=== "Linux"
```bash
# La carte nettoyee est dans le lecteur
lsblk
sudo dd if=/dev/sdb of=$HOME/smarteye-gold-v30.img bs=4M status=progress
gzip $HOME/smarteye-gold-v30.img
# Resultat final
ls -lh $HOME/smarteye-gold-v30.img.gz
```
=== "Mac"
```bash
# La carte nettoyee est dans le lecteur
diskutil list
diskutil unmountDisk /dev/disk4
sudo dd if=/dev/rdisk4 of=/Users/$(whoami)/smarteye-gold-v30.img bs=4m status=progress
gzip /Users/$(whoami)/smarteye-gold-v30.img
# Resultat final
ls -lh /Users/$(whoami)/smarteye-gold-v30.img.gz
```
!!! success "Votre image Gold est prete"
Le fichier `smarteye-gold-v30.img.gz` est votre image maitre. C'est a partir de ce fichier que vous [clonerez chaque nouveau Jetson](clonage.md).
---
## Resume des cartes
| Carte | Contenu | Usage |
|-------|---------|-------|
| **Carte de dev** | SmartEye + Demo_01 + cameras | Reste dans le Jetson de dev, **jamais modifiee** |
| **Backup dev** | `smarteye-dev-backup.img.gz` | Sauvegarde de securite, au cas ou |
| **Image Gold** | `smarteye-gold-v30.img.gz` | Image vierge, **source de tous les clones** |
| **Carte client** | Flashee depuis la Gold | Une par client, configuree sur site |
---
## Checklist finale
- [ ] Carte de dev clonee → `smarteye-dev-backup.img.gz`
- [ ] Carte de dev remise dans le Jetson, fonctionnement verifie
- [ ] Clone flashe sur carte neuve
- [ ] Clone nettoye (pas de config, pas d'images, pas de logs)
- [ ] Mode setup verifie (JSON "status: setup")
- [ ] Clone nettoye reclone → `smarteye-gold-v30.img.gz`
- [ ] Image Gold stockee en lieu sur
---
## Ou stocker les images ?
| Fichier | Taille estimee | Ou le garder |
|---------|:--------------:|--------------|
| `smarteye-dev-backup.img.gz` | ~8-15 Go | PC + disque externe |
| `smarteye-gold-v30.img.gz` | ~8-15 Go | PC + disque externe + serveur OVH |
!!! info "Versionnement"
A chaque mise a jour de SmartEye, creer une nouvelle image Gold :
```
smarteye-gold-v30-2026-02-21.img.gz ← actuelle
smarteye-gold-v31-2026-03-15.img.gz ← apres mise a jour
```
---
## Etape suivante
→ [Cloner et deployer un nouveau Jetson](clonage.md)

View File

@@ -0,0 +1,239 @@
# Clonage des cartes SD (au bureau)
<div style="background: linear-gradient(135deg, #00695c 0%, #004d40 100%); color: white; padding: 24px; border-radius: 12px; margin-bottom: 24px;">
<h2 style="margin:0; color: white;">Operations internes — production en serie</h2>
<p style="margin: 8px 0 0 0; opacity: 0.9;">Cloner, flasher, verifier et reparer les cartes SD avant expedition. Tout en ligne de commande depuis un Mac.</p>
</div>
---
## Image de reference
| Fichier | Taille | Statut |
|---------|:------:|--------|
| `smarteye-dev-backup.img` | ~54 Go | **Fonctionnelle** — validee le 22/02/2026 |
Localisation Mac : `/Users/rachid/jetson_backup/smarteye-dev-backup.img`
!!! warning "Sauvegarde obligatoire"
Cette image doit exister en **au moins 2 exemplaires** :
- Sur le Mac (disque local ou externe)
- Sur le serveur OVH : `scp smarteye-dev-backup.img debian@lucas.unigest.fr:/home/debian/backups/`
---
## 1 — Flasher une carte SD (production)
C'est l'operation principale. Pour chaque nouveau client, on flashe une carte SD neuve depuis l'image de reference.
### Materiel
- Carte SD neuve **SanDisk Extreme Pro** 64 Go minimum
- Lecteur de carte SD (integre ou USB)
### Procedure
```bash
# Identifier la carte SD
diskutil list
# Reperer la carte par sa taille et ses partitions
# Le lecteur integre du Mac peut apparaitre comme "internal, physical", c'est normal
# Demonter sans ejecter
diskutil unmountDisk /dev/diskN
# Flasher (remplacer N par le bon numero)
sudo dd if=/Users/rachid/jetson_backup/smarteye-dev-backup.img of=/dev/rdiskN bs=4m status=progress
# Ejecter proprement
diskutil eject /dev/diskN
```
!!! danger "Verifier le numero de disque"
Toujours verifier **deux fois** le `/dev/diskN` avant de lancer `dd`. Se tromper de disque = ecraser le disque du Mac.
Le `r` devant `diskN` (→ `rdiskN`) utilise le mode raw, **5x plus rapide**.
Duree : ~20-25 min pour 54 Go a ~39 MB/s.
### Verification rapide
Apres le flash, avant d'ejecter :
```bash
diskutil list /dev/diskN
```
Resultat attendu : une **GUID_partition_scheme** avec ~14 partitions Linux + 1 partition EFI. C'est la structure standard du Jetson Nano.
### Production en serie
Pour flasher plusieurs cartes a la suite :
1. Flasher la carte → ejecter
2. Etiqueter la carte (numero de serie / nom client)
3. Inserer la carte dans un Jetson
4. Inserer la carte suivante dans le lecteur → recommencer
La box part chez le client **carte SD deja inseree, prete a brancher**.
---
## 2 — Creer une nouvelle image de reference (backup)
Quand SmartEye evolue (nouvelle version YOLO, corrections, etc.), il faut creer une nouvelle image de reference depuis le Jetson de dev.
### Procedure
Eteindre le Jetson proprement, retirer la carte SD, l'inserer dans le lecteur du Mac.
```bash
# Identifier la carte SD
diskutil list
# Demonter
diskutil unmountDisk /dev/diskN
# Cloner bit-a-bit
sudo dd if=/dev/rdiskN of=/Users/rachid/jetson_backup/smarteye-dev-backup.img bs=4m status=progress
# Ejecter
diskutil eject /dev/diskN
```
Duree : ~20-25 min pour une carte 64 Go.
### Nommage et archivage
```
smarteye-dev-backup-2026-02-22.img ← actuelle (validee)
smarteye-dev-backup-2026-03-XX.img ← future
```
Conserver les anciennes images. Copier sur OVH :
```bash
scp smarteye-dev-backup.img debian@lucas.unigest.fr:/home/debian/backups/
```
---
## 3 — Verifier une image (sans carte SD)
Pour inspecter une image `.img` sans la flasher :
```bash
hdiutil attach -nomount -readonly /Users/rachid/jetson_backup/smarteye-dev-backup.img
```
Resultat attendu :
```
/dev/disk6 GUID_partition_scheme
/dev/disk6s1 EFI
/dev/disk6s2 - s15 Linux Filesystem (multiple)
```
Detacher apres verification :
```bash
hdiutil detach /dev/disk6
```
---
## 4 — Reparer une image corrompue
Si une carte SD ne boot plus (erreur `mmcblk0p1 not found` sur le Jetson), la table de partitions est corrompue. Les donnees sont generalement encore intactes.
### Diagnostic sur le Jetson
Si le Jetson tombe dans un shell d'urgence (`bash-5.1#`) :
```bash
ls /dev/mmcblk0*
```
- `mmcblk0` present mais pas `mmcblk0p1` → table de partitions corrompue → reparable
- `mmcblk0` absent → probleme hardware (carte morte ou mauvais contact)
### Etape 1 : testdisk (retrouver les partitions)
```bash
brew install testdisk
testdisk /Users/rachid/jetson_backup/smarteye-dev-backup.img
```
1. **Create** → nouveau log
2. Selectionner l'image → **Proceed**
3. Type de table → **Intel**
4. **Analyse****Quick Search**
5. testdisk retrouve les partitions (FAT32 + Linux)
6. **Entree** pour continuer → **Write****Y**
!!! warning "testdisk ne suffit pas"
testdisk ecrit une table MBR. Le Jetson Nano utilise une table **GPT** avec ~14 partitions. Il faut enchainer avec gdisk.
### Etape 2 : gdisk (restaurer la GPT)
```bash
brew install gptfdisk
gdisk /Users/rachid/jetson_backup/smarteye-dev-backup.img
```
gdisk detecte le conflit :
```
Found valid MBR and corrupt GPT. Which do you want to use?
1 - MBR
2 - GPT
3 - Create blank GPT
```
1. Taper **`2`** (GPT — le header principal est souvent intact)
2. Taper **`x`** (menu expert)
3. Taper **`e`** (relocate backup structures to end of disk)
4. Taper **`m`** (retour menu principal)
5. Taper **`w`** → **Y**
Si gdisk refuse avec "Secondary partition table overlaps the last partition by N blocks" :
```bash
# Dans un AUTRE terminal, agrandir l'image
dd if=/dev/zero bs=512 count=200 >> /Users/rachid/jetson_backup/smarteye-dev-backup.img
```
Puis **quitter** gdisk (`q`) et le **relancer** (il doit recharger la nouvelle taille). Recommencer : 2 → x → e → m → w → Y.
### Verification finale
```bash
hdiutil attach -nomount -readonly /Users/rachid/jetson_backup/smarteye-dev-backup.img
```
GUID_partition_scheme avec ~14 partitions = image reparee. Flasher sur une carte SD et tester le boot.
---
## Fiabilite en production
!!! danger "Cartes SD : prototypage seulement"
Les cartes SD ont une duree de vie limitee en ecriture. Docker + YOLO = beaucoup d'ecritures. Pour une production a plusieurs centaines de clients :
- **SSD SATA via USB 3.0** pour le systeme (fiabilite industrielle)
- **Carte SD uniquement pour le boot initial** (lecture seule)
- **Backup automatique** des configs vers le serveur OVH
| Composant | Cout unitaire |
|-----------|:------------:|
| SD 32 Go (boot) | ~10 EUR |
| SSD 120 Go SATA | ~15 EUR |
| Adaptateur USB 3.0 → SATA | ~8 EUR |
| **Total par box** | **~33 EUR** |
---
## Etape suivante
→ [Installation chez le client](installation-client.md)

View File

@@ -0,0 +1,110 @@
# Configuration video des cameras
<div style="background: linear-gradient(135deg, #b71c1c 0%, #1565c0 100%); color: white; padding: 24px; border-radius: 12px; margin-bottom: 24px;">
<h2 style="margin:0; color: #ffcdd2;">Les 2 flux RTSP — Detection & Preuve HD</h2>
<p style="margin: 8px 0 0 0; opacity: 0.9;">Chaque camera diffuse 2 flux simultanes utilises pour des besoins differents par SmartEye.</p>
</div>
---
## Comprendre les 2 flux
Chaque camera IP diffuse **2 flux video RTSP** en parallele :
| | Flux 2 (SD) | Flux 1 (HD) |
|---|---|---|
| **Subtype** | `subtype=1` | `subtype=0` |
| **Resolution** | 800 x 448 | 2880 x 1620 |
| **Usage** | Detection YOLO 24/7 | Capture preuve en cas de chute |
| **Debit** | 300 kbps | 6144 kbps |
| **Quand** | En permanence | Uniquement lors d'une detection |
```mermaid
graph LR
CAM[Camera IP] -->|Flux 2 - SD| YOLO[Jetson YOLO]
YOLO -->|Chute detectee| CAPTURE[Capture HD]
CAM -->|Flux 1 - HD| CAPTURE
CAPTURE -->|POST image| LUCAS[Lucas api.php]
LUCAS -->|Gemini analyse| AVA[Push AVA]
```
!!! question "Pourquoi 2 flux ?"
**YOLO n'a pas besoin de HD** pour detecter une personne. Un flux leger (800x448) permet d'analyser plus de FPS avec moins de charge GPU. Par contre, **l'image de preuve doit etre nette** pour que Gemini puisse analyser finement et pour que la famille voie clairement la situation.
---
## Parametres recommandes
### Parametres generaux
| Parametre | Valeur |
|-----------|--------|
| Format video | **50Hz** |
| Codage video | **H.264 Main Profile** |
!!! warning "H.264 obligatoire"
Le H.265 n'est pas supporte par tous les lecteurs RTSP. Toujours utiliser H.264 Main Profile.
### Flux 1 — Preuve HD (subtype=0)
| Parametre | Valeur | Explication |
|-----------|--------|-------------|
| Resolution | **2880 x 1620** | Resolution max. Image nette pour Gemini et la famille |
| Debit binaire | **6144 kbps** | Debit max. Qualite optimale pour la capture |
| Frequence | **20 fps** | Suffisant pour un screenshot net |
| Intervalle image cle | **20** | 1 keyframe/seconde. Capture plus rapide |
| Controle debit | **CBR** | Debit constant. Qualite stable en mouvement |
| Qualite image | **1** (meilleure) | Valeur basse = qualite max |
### Flux 2 — Detection YOLO (subtype=1)
| Parametre | Valeur | Explication |
|-----------|--------|-------------|
| Resolution | **800 x 448** | YOLO detecte bien en SD. Economise le GPU |
| Debit binaire | **300 kbps** | 20x moins que le flux HD |
| Frequence | **15 fps** | Suffisant pour detecter un mouvement |
| Intervalle image cle | **30** | 1 keyframe toutes les 2 secondes |
| Controle debit | **VBR** | Debit variable. Economise la bande passante au repos |
| Qualite image | **1** (meilleure) | Meilleure qualite meme en SD |
---
## URLs RTSP
Format pour cameras Dahua :
=== "Flux 1 — HD (preuve)"
```
rtsp://admin:password@192.168.1.196/cam/realmonitor?channel=1&subtype=0
```
=== "Flux 2 — SD (detection)"
```
rtsp://admin:password@192.168.1.196/cam/realmonitor?channel=1&subtype=1
```
!!! info "Parametres URL"
- `channel` : numero de canal (1 pour camera mono-objectif)
- `subtype` : **0** = flux principal (HD), **1** = flux secondaire (SD)
---
## Procedure de configuration
1. Acceder a l'interface web de la camera (`http://IP_CAMERA`)
2. Aller dans **Parametres > Camera > Video**
3. Configurer le **Premier flux** (HD) selon le tableau ci-dessus
4. Configurer le **Deuxieme flux** (SD) selon le tableau ci-dessus
5. Cliquer **Appliquer**
6. Tester les 2 flux avec VLC : `vlc rtsp://admin:password@IP/cam/realmonitor?channel=1&subtype=1`
7. Verifier que SmartEye recoit le flux 2 et detecte correctement
---
## Voir aussi
- [Installation camera IP](installation-camera.md) — Ajouter une camera au reseau
- [Checklist installation](https://lucas.unigest.fr/checklist.php) — Suivi complet d'une installation
- [Configuration video interactive](https://lucas.unigest.fr/doc_camera.php) — Version web avec tableaux detailles

View File

@@ -0,0 +1,154 @@
# Installation d'une camera IP
<div style="background: linear-gradient(135deg, #1a237e 0%, #283593 100%); color: white; padding: 24px; border-radius: 12px; margin-bottom: 24px;">
<h2 style="margin:0; color: #82b1ff;">Guide technicien — Ajout d'une camera</h2>
<p style="margin: 8px 0 0 0; opacity: 0.9;">Configuration d'une camera IP via l'application mobile. Temps estime : 5 minutes par camera.</p>
</div>
---
## Pre-requis
- [ ] Smartphone avec l'application **CamHi** (ou equivalent) installee
- [ ] Camera IP allumee et en mode appairage (LED clignotante)
- [ ] Acces au reseau WiFi du client (SSID + mot de passe)
- [ ] Bluetooth active sur le smartphone
---
## Etape 1 — Trouver la camera
Ouvrir l'application et lancer la recherche. La camera est detectee automatiquement via **Bluetooth**.
![Recherche de la camera via Bluetooth](../assets/images/deploiement/trouver-camera.png){ width="280" }
1. Verifier que la camera est **allumee** et en mode appairage
2. La camera apparait avec son identifiant (ex: `SSAT-326256-ECCFB`)
3. Appuyer sur **Ajouter a**
!!! tip "La camera n'apparait pas ?"
- Verifier que le Bluetooth est active sur le smartphone
- Rapprocher le telephone de la camera (< 3 metres)
- Redemarrer la camera (debrancher/rebrancher)
---
## Etape 2 — Configurer le WiFi
La camera doit se connecter au **reseau WiFi du client** pour etre accessible par le Jetson.
![Configuration du reseau sans fil](../assets/images/deploiement/configuration-sans-fil.png){ width="280" }
1. Le **nom du WiFi** (SSID) est pre-rempli avec le reseau actuel du smartphone
2. Saisir le **mot de passe WiFi** du client
3. Appuyer sur **Configurer le sans fil et ajouter**
!!! warning "Important"
La camera et le Jetson doivent etre sur le **meme reseau**. Utiliser le WiFi principal du client, pas un reseau invite.
---
## Etape 3 — Nommer la camera
Donner un nom explicite a la camera pour l'identifier facilement.
![Attribution du nom a la camera](../assets/images/deploiement/mise-en-place-nom.png){ width="280" }
1. Saisir le **nom de la piece** ou la camera est installee
2. Utiliser les suggestions rapides si disponibles : `Salon`, `Chambre`, `Corridor`, `Hall`, etc.
3. Valider avec **OK**
!!! info "Convention de nommage"
Utiliser des noms simples et coherents : **Salon cuisine**, **Chambre**, **Terrasse**, **Camera fenetre**. Ces noms apparaitront dans LucasApp.
---
## Etape 4 — Changer le mot de passe
A la premiere connexion, l'application demande de **changer le mot de passe par defaut** de la camera.
![Prompt de changement de mot de passe](../assets/images/deploiement/changement-password.png){ width="280" }
1. Appuyer sur **Ok** pour accepter
2. Definir un **mot de passe securise** (minimum 8 caracteres)
3. **Noter le mot de passe** — il sera necessaire pour configurer le flux RTSP sur le Jetson
!!! danger "Ne pas ignorer cette etape"
Une camera avec le mot de passe par defaut est un risque de securite. Toujours changer le mot de passe.
---
## Etape 5 — Verifier l'installation
La camera apparait maintenant dans la liste des appareils.
![Liste des cameras installees](../assets/images/deploiement/mes-cameras.png){ width="280" }
Verifier que :
- [ ] La camera est **en ligne** (apercu video visible)
- [ ] Le **nom** est correct
- [ ] L'**icone cloud** indique la connectivite
---
## Etape 6 — Parametrer la camera
Acceder aux reglages de la camera pour ajuster les parametres.
![Parametres de la camera](../assets/images/deploiement/settings-chambre.png){ width="280" }
Parametres recommandes :
| Parametre | Reglage |
|-----------|---------|
| **Parametres video** | Resolution maximale, 15-20 FPS |
| **Parametres audio** | Activer le micro et le haut-parleur |
| **Gestion des alarmes** | Desactiver (c'est SmartEye qui gere la detection) |
| **Enregistrement carte TF** | Optionnel — activer si une carte SD est inseree |
| **Reglage de l'heure** | Verifier le fuseau horaire (Europe/Paris) |
---
## Etape 7 — Recuperer l'adresse IP
Aller dans **Information sur l'appareil** pour noter l'adresse IP de la camera.
![Informations reseau de la camera](../assets/images/deploiement/informations-appareil.jpeg){ width="280" }
| Information | Usage |
|-------------|-------|
| **Adresse IP** | Necessaire pour configurer le Jetson (ex: `192.168.1.196`) |
| **Type de reseau** | Verifier que c'est bien `WIFI` (ou Ethernet) |
| **Version du logiciel** | Utile pour le support technique |
!!! success "Camera prete"
L'adresse IP est la derniere information necessaire. Elle sera utilisee pour configurer le flux RTSP dans SmartEye.
---
## Recapitulatif
| Etape | Action | Duree |
|:-----:|--------|:-----:|
| 1 | Trouver la camera (Bluetooth) | 30s |
| 2 | Configurer le WiFi | 30s |
| 3 | Nommer la camera | 15s |
| 4 | Changer le mot de passe | 30s |
| 5 | Verifier la connexion | 15s |
| 6 | Parametrer (video, audio) | 1 min |
| 7 | Recuperer l'adresse IP | 15s |
---
## Prochaine etape
Une fois toutes les cameras installees et leurs IPs notees, passer a l'[installation du Jetson SmartEye](jetson.md) pour configurer la detection IA.
!!! info "Flux RTSP"
Le Jetson utilise les flux RTSP des cameras. L'URL standard est :
```
rtsp://<login>:<password>@<IP>:554/11 (flux HD)
rtsp://<login>:<password>@<IP>:554/12 (flux SD)
```
SmartEye utilise le flux SD (`/12`) pour la detection afin d'optimiser les performances.

View File

@@ -0,0 +1,262 @@
# Installation chez le client
<div style="background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); color: white; padding: 32px; border-radius: 12px; margin-bottom: 24px; border: 1px solid rgba(0, 188, 212, 0.3); box-shadow: 0 0 30px rgba(0, 188, 212, 0.1);">
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 8px;">
<span style="font-size: 1.8em;">:material-shield-home:</span>
<h2 style="margin:0; color: #00e5ff; font-weight: 300; letter-spacing: 2px;">GUIDE TECHNICIEN TERRAIN</h2>
</div>
<p style="margin: 4px 0 0 0; opacity: 0.85; font-size: 0.95em;">Deploiement du systeme de surveillance intelligent Lucas. Duree estimee sur site : ~30 minutes.</p>
<div style="margin-top: 16px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,0.15); display: flex; gap: 24px; font-size: 0.85em; opacity: 0.7;">
<span>:material-cctv: Surveillance</span>
<span>:material-brain: IA embarquee</span>
<span>:material-shield-check: Protection 24/7</span>
<span>:material-phone-alert: Alerte instantanee</span>
</div>
</div>
---
## Pre-requis
!!! info "Le compte client est deja cree"
Le compte est cree automatiquement lors de la souscription. Le technicien recoit par email :
- **Identifiant client** (`client_id`)
- **Mot de passe** (token d'authentification)
- **Numero de site** attribue
**Chez le client :**
- [ ] Acces Internet (fibre ou ADSL stable)
- [ ] Reseau WiFi pour les cameras (SSID + mot de passe)
- [ ] Prise Ethernet pour le Jetson (cable recommande)
- [ ] Prises electriques a proximite des emplacements cameras
---
## Le kit materiel
Voici ce que le technicien deballera chez le client :
### Jetson Orin Nano — Le cerveau SmartEye
![Jetson Orin Nano Developer Kit](../assets/images/jetson-orin-nano.jpg){ width="500" }
*NVIDIA Jetson Orin Nano — Le boitier d'intelligence artificielle embarquee.*
| Caracteristique | Valeur |
|----------------|--------|
| **Processeur** | 6-core Arm Cortex-A78AE |
| **GPU** | 1024-core NVIDIA Ampere |
| **Performance IA** | Jusqu'a 67 TOPS |
| **RAM** | 8 Go LPDDR5 |
| **Stockage** | Carte SD pre-flashee (image Gold) |
| **Alimentation** | USB-C (adaptateur fourni) |
### Cameras Ctronics PTZ — Les yeux du systeme
<!-- Ajouter les images dans docs/assets/images/ :
ctronics-camera-1.jpg (vue de face)
ctronics-camera-2.jpg (vue d'angle / exterieur)
Source : https://ctronics.com/collections/all ou Amazon -->
*Cameras Ctronics PTZ avec vision nocturne et audio bidirectionnel.*
| Caracteristique | Valeur |
|----------------|--------|
| **Resolution** | 2K / 4MP minimum |
| **PTZ** | 355 pan, 90 tilt |
| **Vision nocturne** | 30m couleur |
| **Audio** | Bidirectionnel (micro + haut-parleur) |
| **Protocole** | ONVIF, RTSP |
| **Connexion** | WiFi 2.4/5GHz ou Ethernet |
| **Flux RTSP** | HD = `/11`, SD = `/12` |
### Checklist depart
- [ ] Boitier Jetson SmartEye (carte SD deja inseree, clonee depuis la Gold)
- [ ] Alimentation USB-C
- [ ] Cable Ethernet (RJ45) — 2 metres minimum
- [ ] Cameras IP (Ctronics) — autant que prevu pour ce client
- [ ] Alimentations cameras (12V)
- [ ] **Fiche credentials** (ID + Token + N site — recus par email)
- [ ] Smartphone avec **LucasApp** installe
!!! tip "Carte SD"
La carte SD doit etre flashee depuis l'image Gold **avant** le deplacement.
Voir [Clonage des cartes SD](clonage.md) pour la procedure.
---
## Sur site (chez le client)
### Etape 1 — Installer les cameras
**Avant de toucher au Jetson**, positionner et brancher toutes les cameras.
**Regles de positionnement** :
| Regle | Detail |
|-------|--------|
| Hauteur | 2 a 2,5 metres du sol (au-dessus des portes) |
| Angle | Plongee a ~30, couvrir le maximum de sol |
| Zones cibles | Salon, chambre, couloir — la ou le senior passe le plus de temps |
| Eviter | Contre-jour (fenetre dans le champ), reflets de miroirs |
| Branchement | Chaque camera a son alimentation + connectee au **reseau du client** (WiFi ou Ethernet) |
!!! info "Combien de cameras ?"
Le systeme supporte jusqu'a **6 cameras par site**. Minimum recommande : 2 (salon + chambre).
**Verifier** que chaque camera est allumee et connectee au reseau du client avant de continuer.
---
### Etape 2 — Brancher et configurer le Jetson
#### Branchement physique
```
Box Internet du client
[ ::: ]
|
Cable Ethernet
|
[ Jetson SmartEye ] ← USB-C (alimentation)
```
1. Brancher le **cable Ethernet** entre le Jetson et la box internet
2. Brancher le **cable USB-C** (alimentation)
3. Attendre **1 minute** — la LED passe au vert fixe
#### Configuration depuis le smartphone
Se connecter au **meme WiFi** que le boitier, puis ouvrir dans le navigateur :
<div style="background: #263238; color: #80cbc4; padding: 16px; border-radius: 8px; font-size: 1.3em; text-align: center; font-family: monospace;">
http://smarteye.local:8080
</div>
<br/>
!!! tip "Ca ne marche pas ?"
**Solution 1** — Ouvrir LucasApp > Parametres > **Installer un SmartEye**
**Solution 2** — Scanner le **QR code** colle sous le boitier
#### Ecran 1/4 : Identification
Saisir les informations recues par email :
| Champ | Quoi mettre |
|-------|-------------|
| **ID Client** | L'identifiant recu par email |
| **Token** | Le mot de passe recu par email |
| **Nom du site** | Nom libre (ex: `Mme Dupont - Aleria`) |
#### Ecran 2/4 : Cameras
Le Jetson detecte les cameras automatiquement sur le reseau.
Pour chaque camera trouvee :
1. **Nommer la piece** (Salon, Chambre, Cuisine...)
2. Saisir les **identifiants** si demande (login/mot de passe de la camera)
3. Appuyer sur **Tester** — verifier que l'image s'affiche
!!! warning "Aucune camera detectee ?"
- Les cameras sont-elles allumees ?
- Sont-elles sur le **meme reseau** que le Jetson ?
- Essayer de redemarrer la camera
#### Ecran 3/4 : Connexion serveur
Les valeurs sont **pre-remplies**. Ne rien modifier sauf indication contraire.
| Parametre | Valeur par defaut |
|-----------|-------------------|
| Serveur | `57.128.74.87` |
| Utilisateur | `debian` |
| N de site | Automatique |
Appuyer sur **Tester la connexion** — doit afficher un resultat vert.
#### Ecran 4/4 : Test final
Le systeme verifie toute la chaine automatiquement :
| Etape | Verification | Resultat |
|:-----:|-------------|:--------:|
| 1 | Connexion aux cameras | :material-check: |
| 2 | Detection IA (YOLO) | :material-check: |
| 3 | Tunnel vers le serveur | :material-check: |
| 4 | Envoi d'une image test | :material-check: |
| 5 | Notification recue sur le telephone | :material-check: |
Si tout est vert : appuyer sur **Terminer**.
Le Jetson passe en mode surveillance active.
---
## Avant de partir
### Etape 3 — Test de bout en bout
**Test obligatoire** — ne pas quitter le domicile sans l'avoir fait.
1. **Simuler une chute** : se coucher au sol devant une camera, rester immobile 30 secondes
2. **Verifier** la notification sur le telephone du technicien (LucasApp)
3. **Verifier** que l'image recue est nette et bien cadree
4. **Verifier** le flux video en direct dans LucasApp (toutes les cameras)
5. **Tester l'interphone** : parler dans l'app, verifier que le son sort de la camera
---
### Etape 4 — Configurer les telephones des aidants
Sur le telephone de **chaque aidant** (fils, fille, aide-soignant...) :
1. **Installer LucasApp** (Play Store / APK)
2. **Ouvrir l'app** — elle s'enregistre automatiquement aupres du serveur
3. **Verifier les notifications** : elles doivent etre autorisees dans les reglages du telephone
4. **Montrer le fonctionnement** :
- Comment repondre a une alerte
- Comment voir les cameras en direct
- Comment utiliser l'interphone
- Expliquer que le numero d'urgence (15 par defaut) est accessible depuis l'alerte
!!! success "Installation terminee"
Le systeme est operationnel. Le Jetson fonctionne 24h/24, redemarre seul en cas de coupure de courant.
Aucune intervention de l'aidant n'est necessaire au quotidien.
---
## Checklist de depart
Avant de quitter le domicile, cocher chaque point :
- [ ] Toutes les cameras sont fixees et alimentees
- [ ] Le Jetson est branche (Ethernet + USB-C)
- [ ] La LED du Jetson clignote rapidement (surveillance active)
- [ ] Le test de chute simulee a fonctionne
- [ ] Le flux video est visible dans LucasApp
- [ ] L'interphone fonctionne dans les deux sens
- [ ] **Chaque aidant** a LucasApp installe et les notifications activees
- [ ] Le boitier est place dans un endroit discret et ventile
---
## Depannage rapide
| Symptome | Cause probable | Solution |
|----------|---------------|----------|
| LED eteinte | Pas d'alimentation | Verifier le cable USB-C et l'adaptateur |
| `smarteye.local` ne repond pas | mDNS non supporte | Utiliser le scan LucasApp ou le QR code |
| Camera non detectee | Reseau different | Verifier que la camera est sur le meme reseau |
| Test serveur echoue | Pas d'internet | Verifier la connexion internet de la box client |
| Pas de notification | Notifications desactivees | Verifier les reglages du telephone de l'aidant |
| Image floue ou sombre | Mauvais positionnement | Repositionner la camera (hauteur, angle, eclairage) |
| Intercom muet | Camera sans audio | Verifier que la camera supporte l'audio bidirectionnel |
!!! info "Probleme non resolu ?"
Consulter le [Guide expert](jetson-expert.md) pour un diagnostic technique approfondi.

View File

@@ -0,0 +1,380 @@
# Installation du Jetson SmartEye
## Philosophie : le Jetson est une Box
Le Jetson SmartEye se deploie comme une box internet (Free, Orange). L'installateur arrive chez le beneficiaire avec le boitier pre-configure, le branche sur le reseau local, et **tout se passe depuis son smartphone**. Aucun ecran, aucun clavier, aucun cable HDMI.
```mermaid
graph LR
subgraph Domicile["Domicile du beneficiaire"]
BOX["Jetson SmartEye"]
CAM1["Camera 1"]
CAM2["Camera 2"]
CAM3["Camera 3"]
ROUTER["Box Internet"]
BOX ---|Ethernet| ROUTER
CAM1 ---|WiFi/Ethernet| ROUTER
CAM2 ---|WiFi/Ethernet| ROUTER
CAM3 ---|WiFi/Ethernet| ROUTER
end
subgraph Install["Smartphone installateur"]
APP["LucasApp / Navigateur"]
end
ROUTER ---|WiFi| APP
APP -.->|"http://smarteye.local:8080"| BOX
```
## Materiel necessaire
| Element | Detail |
|---------|--------|
| **Jetson Orin Nano** | Avec SmartEye pre-installe sur carte SD |
| **Alimentation** | USB-C 5V/3A (fournie) |
| **Cable Ethernet** | RJ45, branchement sur la box internet du client |
| **Cameras IP** | CTronic ou compatible ONVIF, deja installees |
| **Smartphone** | Android ou iOS, connecte au meme reseau WiFi |
---
## Etape 1 : Branchement physique
1. **Brancher le cable Ethernet** du Jetson a la box internet du client
2. **Brancher l'alimentation USB-C** du Jetson
3. **Attendre 60 secondes** — le Jetson demarre et se connecte au reseau local
!!! info "LED de statut"
Le Jetson n'a pas d'ecran mais dispose d'une LED verte :
- **Clignotement lent** : demarrage en cours
- **Fixe** : connecte au reseau, pret pour la configuration
- **Clignotement rapide** : surveillance active, tout fonctionne
---
## Etape 2 : Decouverte sur le reseau local
Le Jetson annonce automatiquement sa presence sur le reseau local via **mDNS (Avahi/Bonjour)**. Depuis le smartphone connecte au meme WiFi :
### Option A : Acces direct (recommande)
Ouvrir un navigateur et aller a :
```
http://smarteye.local:8080
```
!!! tip "Si smarteye.local ne repond pas"
Certaines box internet bloquent le mDNS. Dans ce cas, utiliser l'option B.
### Option B : Scan reseau depuis LucasApp
1. Ouvrir **LucasApp** sur le smartphone
2. Aller dans **Parametres > Installer un SmartEye**
3. L'application scanne le reseau local (plage 192.168.x.x)
4. Le Jetson apparait dans la liste des appareils detectes
5. Appuyer dessus pour ouvrir l'interface de configuration
```mermaid
sequenceDiagram
participant Phone as Smartphone
participant Jetson as Jetson SmartEye
participant Router as Box Internet
Phone->>Router: Scan reseau local (UDP broadcast)
Router-->>Phone: Liste des appareils
Phone->>Jetson: GET /api/identity (port 8080)
Jetson-->>Phone: {"type": "smarteye", "version": "v30", "status": "setup"}
Phone->>Phone: Affiche "SmartEye detecte"
```
### Option C : QR Code sur le boitier
Un QR code colle sur le Jetson contient son adresse MAC. LucasApp peut scanner ce QR code pour identifier le bon appareil sur le reseau.
---
## Etape 3 : Interface de configuration (WebUI)
L'interface web du Jetson s'affiche sur le smartphone. L'installation se fait en **4 sous-etapes** guidees.
### 3.1 — Identification du site
| Champ | Description | Exemple |
|-------|-------------|---------|
| **ID Client** | Identifiant du beneficiaire (fourni par Lucas) | `dupont_marie` |
| **Token** | Jeton d'authentification (fourni par Lucas) | `b5ce5015` |
| **Nom du site** | Nom lisible | `Residence Dupont` |
!!! warning "Ou trouver l'ID et le Token ?"
L'ID client et le token sont generes lors de la creation du dossier dans l'interface d'administration Lucas ([admin.php](https://lucas.unigest.fr/admin.php)). Voir le [Guide nouveau client](../guide/nouveau-client.md).
### 3.2 — Detection automatique des cameras (ONVIF)
Le Jetson scanne le reseau local a la recherche de cameras compatibles ONVIF.
```
Scan en cours...
Camera detectee : 192.168.1.143 (CTronic - Salon)
Camera detectee : 192.168.1.148 (CTronic - Chambre)
Camera detectee : 192.168.1.152 (CTronic - Cuisine)
3 cameras trouvees.
```
Pour chaque camera detectee :
| Champ | Auto-detecte | Modifiable |
|-------|:------------:|:----------:|
| **Adresse IP** | Oui | Oui |
| **Nom** (piece) | Non | Oui |
| **Login RTSP** | Non | Oui |
| **Mot de passe RTSP** | Non | Oui |
| **Flux HD** (/11) | Oui | Oui |
| **Flux SD** (/12) | Oui | Oui |
| **Active** | Oui | Oui |
!!! tip "Verifier le flux en direct"
Appuyer sur **Tester** a cote de chaque camera pour afficher un apercu en direct sur le smartphone. Cela confirme que les identifiants RTSP sont corrects.
### 3.3 — Connexion au serveur Lucas
Le Jetson doit etablir une connexion securisee (tunnel SSH) vers le serveur OVH.
| Parametre | Valeur par defaut | Modifiable |
|-----------|-------------------|:----------:|
| **Serveur** | `57.128.74.87` | Oui |
| **Utilisateur SSH** | `debian` | Oui |
| **Domaine** | `lucas.unigest.fr` | Oui |
| **Numero de site** | Auto (prochain disponible) | Oui |
```mermaid
sequenceDiagram
participant Jetson
participant OVH as Serveur OVH
Jetson->>OVH: Test connexion SSH
OVH-->>Jetson: OK - Cle autorisee
Jetson->>OVH: Ouverture tunnels RTSP (ports 8560-8565)
Jetson->>OVH: Ouverture tunnel Audio (port 8566)
Jetson->>OVH: POST api.php (test image)
OVH-->>Jetson: 200 OK - Client reconnu
Jetson-->>Jetson: Connexion validee
```
!!! note "Attribution automatique des ports"
Le Jetson calcule automatiquement sa plage de ports selon la convention :
```
Site N → base_port = 8550 + (N-1) x 10
Ports attribues :
base+0 a base+5 : RTSP cameras (max 6)
base+6 : Audio WebSocket
base+7 : WebUI locale
base+8 a base+9 : Reserves
```
Exemple pour le site n°2 : ports 8560 a 8569.
### 3.4 — Test de bout en bout
Le Jetson effectue un test complet de la chaine :
```
[1/5] Connexion cameras RTSP............ OK
[2/5] Detection IA (YOLO)............... OK (personne detectee)
[3/5] Tunnel SSH vers OVH............... OK
[4/5] Envoi image test a api.php........ OK (Gemini: "RAS")
[5/5] Notification push Firebase........ OK (recue sur LucasApp)
=== INSTALLATION TERMINEE ===
SmartEye est operationnel.
La surveillance demarre automatiquement.
```
!!! success "Installation terminee"
Une fois le test valide, le Jetson passe en mode **surveillance active**. Il demarre automatiquement a chaque mise sous tension, sans aucune intervention.
---
## Etape 4 : Verification depuis LucasApp
Sur le smartphone de chaque aidant :
1. Ouvrir **LucasApp**
2. Verifier que le flux video en direct est accessible
3. Verifier que l'interphone fonctionne (audio bidirectionnel)
4. Effectuer un **test de chute simule** :
- Se coucher au sol dans le champ d'une camera
- Attendre ~30 secondes
- Verifier la reception de la notification push
- Verifier l'image et le message Gemini dans l'alerte
---
## Architecture reseau complete
```mermaid
graph TB
subgraph Domicile["Domicile du beneficiaire"]
CAM1["Camera 1<br/>192.168.1.143"]
CAM2["Camera 2<br/>192.168.1.148"]
CAM3["Camera 3<br/>192.168.1.152"]
JETSON["Jetson SmartEye<br/>192.168.1.xxx"]
ROUTER["Box Internet<br/>192.168.1.1"]
CAM1 -->|RTSP local| JETSON
CAM2 -->|RTSP local| JETSON
CAM3 -->|RTSP local| JETSON
JETSON ---|Ethernet| ROUTER
end
subgraph OVH["Serveur OVH (57.128.74.87)"]
SSH["SSH Gateway"]
RTSP_RELAY["Relay RTSP<br/>ports 8560-8565"]
AUDIO["Audio WebSocket<br/>port 8566"]
API["api.php"]
GEMINI["Gemini 2.5 Flash"]
FCM["Firebase Cloud Messaging"]
end
JETSON ==>|"SSH Tunnel (autossh)<br/>Persistant, auto-reconnect"| SSH
SSH --> RTSP_RELAY
SSH --> AUDIO
JETSON ==>|"POST image<br/>si chute detectee"| API
API --> GEMINI
GEMINI --> FCM
subgraph Aidants["Smartphones aidants"]
APP1["LucasApp - Fille"]
APP2["LucasApp - Fils"]
end
FCM ==>|Push notification| APP1
FCM ==>|Push notification| APP2
RTSP_RELAY -.->|"Flux video live"| APP1
AUDIO -.->|"Interphone"| APP1
```
---
## Fichier de configuration genere
A la fin de l'installation, le Jetson cree automatiquement le fichier `smarteye_config.yaml` :
```yaml
# Genere automatiquement par l'assistant d'installation
# Ne pas modifier manuellement sauf si necessaire
site:
id: "dupont_marie"
name: "Residence Dupont"
client_token: "b5ce5015"
server:
api_url: "https://lucas.unigest.fr/api.php"
ovh_ip: "57.128.74.87"
ssh_user: "debian"
ports:
site_number: 2
base_port: 8560
cameras:
- id: "Cam1"
name: "Salon"
ip: "192.168.1.143"
username: "admin"
password: "********"
stream_hd: "/11"
stream_sd: "/12"
tunnel_port: 8560
enabled: true
- id: "Cam2"
name: "Chambre"
ip: "192.168.1.148"
username: "admin"
password: "********"
stream_hd: "/11"
stream_sd: "/12"
tunnel_port: 8561
enabled: true
- id: "Cam3"
name: "Cuisine"
ip: "192.168.1.152"
username: "admin"
password: "********"
stream_hd: "/11"
stream_sd: "/12"
tunnel_port: 8562
enabled: true
audio:
local_port: 8800
tunnel_port: 8566
detection:
yolo:
std_confidence: 0.15
pose_confidence: 0.25
alert_interval: 120
```
---
## Maintenance et acces distant
Une fois installe, le Jetson est accessible a distance depuis le serveur OVH via le tunnel SSH inverse :
```bash
# Depuis le serveur OVH, acceder au WebUI du Jetson
# (tunnel inverse sur le port base+7)
curl http://localhost:8567/api/status
```
### Commandes utiles (pour le support technique)
| Action | Commande |
|--------|----------|
| **Statut du Jetson** | `curl http://localhost:<base+7>/api/status` |
| **Redemarrer SmartEye** | `ssh -p <tunnel> jetson "sudo systemctl restart smarteye"` |
| **Voir les logs** | `ssh -p <tunnel> jetson "journalctl -u smarteye -f"` |
| **Mettre a jour** | `ssh -p <tunnel> jetson "cd /w && git pull && sudo systemctl restart smarteye"` |
!!! warning "Resilience reseau"
Le service `autossh` maintient les tunnels SSH en permanence. En cas de coupure internet :
- Les tunnels se retablissent automatiquement a la reconnexion
- SmartEye continue la detection localement meme sans internet
- Les alertes en attente sont envoyees des que la connexion revient
---
## Resume du flux d'installation
```mermaid
graph TD
A["Branchement Ethernet + USB-C"] --> B["Demarrage automatique (60s)"]
B --> C["Annonce mDNS sur le reseau local"]
C --> D{"Smartphone detecte le Jetson"}
D -->|"smarteye.local:8080"| E["WebUI de configuration"]
D -->|"LucasApp scan"| E
D -->|"QR Code"| E
E --> F["Saisie ID client + Token"]
F --> G["Scan ONVIF des cameras"]
G --> H["Configuration des cameras"]
H --> I["Connexion SSH vers OVH"]
I --> J["Test de bout en bout"]
J -->|"Tout OK"| K["Surveillance active"]
J -->|"Echec"| L["Diagnostic et correction"]
L --> J
style A fill:#455a64,color:#fff
style K fill:#00695c,color:#fff
style L fill:#bf360c,color:#fff
```

View File

@@ -0,0 +1,165 @@
# Installation SmartEye - Guide simplifie
<div style="background: linear-gradient(135deg, #00695c 0%, #004d40 100%); color: white; padding: 24px; border-radius: 12px; margin-bottom: 24px;">
<h2 style="margin:0; color: white;">Guide d'installation rapide</h2>
<p style="margin: 8px 0 0 0; opacity: 0.9;">Tout se fait depuis votre smartphone. Temps estime : 10 minutes.</p>
</div>
---
## Ce dont vous avez besoin
| | Element | Fourni |
|:-:|---------|:------:|
| 1 | **Boitier Jetson SmartEye** (avec carte SD) | Oui |
| 2 | **Alimentation USB-C** | Oui |
| 3 | **Cable Ethernet (RJ45)** | Oui |
| 4 | **Cameras** deja installees chez le client | - |
| 5 | **Votre smartphone** connecte au WiFi du client | - |
| 6 | **Fiche client** (ID + Token, fournis par l'admin) | Oui |
---
## Etape 1 — Brancher le boitier
```
Box Internet du client
[ ::: ]
|
Cable Ethernet
|
[ Jetson SmartEye ] ← USB-C (alimentation)
```
1. Brancher le **cable Ethernet** entre le Jetson et la box internet
2. Brancher le **cable USB-C** (alimentation)
3. Attendre **1 minute** que la LED passe au vert fixe
| LED | Signification |
|:---:|---------------|
| Clignotement lent | Demarrage en cours — patientez |
| **Vert fixe** | Pret — passez a l'etape 2 |
| Clignotement rapide | Surveillance active (tout roule) |
---
## Etape 2 — Se connecter au Jetson
Assurez-vous que votre smartphone est connecte **au meme WiFi** que le boitier.
Ouvrez votre navigateur et tapez :
<div style="background: #263238; color: #80cbc4; padding: 16px; border-radius: 8px; font-size: 1.3em; text-align: center; font-family: monospace;">
http://smarteye.local:8080
</div>
<br/>
!!! tip "Ca ne marche pas ?"
**Solution 1** — Ouvrir LucasApp > Parametres > **Installer un SmartEye** (scan automatique)
**Solution 2** — Scanner le **QR code** colle sous le boitier avec LucasApp
---
## Etape 3 — Configurer (4 ecrans)
### Ecran 1/4 : Identification
Saisir les informations de la **fiche client** :
| Champ | Quoi mettre | Exemple |
|-------|-------------|---------|
| **ID Client** | Sur la fiche | `dupont_marie` |
| **Token** | Sur la fiche | `b5ce5015` |
| **Nom du site** | Nom libre | `Mme Dupont - Aleria` |
Appuyer sur **Suivant →**
---
### Ecran 2/4 : Cameras
Le Jetson scanne et trouve les cameras automatiquement.
```
✓ Camera 1 — 192.168.1.143
✓ Camera 2 — 192.168.1.148
✓ Camera 3 — 192.168.1.152
```
Pour chaque camera :
1. **Nommer la piece** (Salon, Chambre, Cuisine...)
2. Saisir les **identifiants RTSP** si demande (login/mot de passe camera)
3. Appuyer sur **Tester** pour verifier l'image en direct
!!! warning "Aucune camera detectee ?"
Verifier que les cameras sont allumees et connectees au meme reseau (WiFi ou Ethernet).
Appuyer sur **Suivant →**
---
### Ecran 3/4 : Connexion serveur
Les valeurs par defaut sont pre-remplies. **Ne rien modifier** sauf indication contraire.
| Parametre | Valeur par defaut |
|-----------|-------------------|
| Serveur | `57.128.74.87` |
| Utilisateur | `debian` |
| Domaine | `lucas.unigest.fr` |
| N° de site | Automatique |
Appuyer sur **Tester la connexion**, puis **Suivant →**
---
### Ecran 4/4 : Test final
Le Jetson verifie toute la chaine automatiquement :
| Etape | Quoi | Resultat attendu |
|:-----:|------|:-----------------:|
| 1 | Connexion cameras | ✓ |
| 2 | Detection IA | ✓ |
| 3 | Tunnel vers serveur | ✓ |
| 4 | Envoi image test | ✓ |
| 5 | Notification sur telephone | ✓ |
Si tout est vert : appuyer sur **Terminer**.
!!! success "C'est fini"
Le Jetson passe en surveillance. Il redemarre seul en cas de coupure de courant. Rien d'autre a faire.
---
## Etape 4 — Verifier sur LucasApp
Sur le telephone de **chaque aidant** :
- [ ] Ouvrir LucasApp → verifier le flux video en direct
- [ ] Tester l'interphone (parler / ecouter)
- [ ] **Simuler une chute** : se coucher au sol devant une camera, attendre 30 secondes, verifier que la notification arrive
---
## En cas de probleme
| Symptome | Solution |
|----------|----------|
| LED ne s'allume pas | Verifier l'alimentation USB-C |
| `smarteye.local` ne repond pas | Utiliser le scan LucasApp ou le QR code |
| Camera non detectee | Verifier qu'elle est sur le meme reseau |
| Test serveur echoue | Verifier la connexion internet du client |
| Pas de notification | Verifier que LucasApp est installe et les notifications activees |
!!! info "Besoin d'aide ?"
Contacter le support technique ou consulter le [Guide expert](jetson-expert.md) pour un diagnostic approfondi.
---
<div style="background: #e8f5e9; padding: 16px; border-radius: 8px; border-left: 4px solid #2e7d32; color: #1b5e20;">
<strong>Recapitulatif</strong> : Brancher → Se connecter → 4 ecrans de config → Verifier sur LucasApp. C'est tout.
</div>

View File

@@ -0,0 +1,3 @@
# Gestion des alertes
Section en cours de rédaction. Cette page décrira le cycle de vie des alertes, de leur déclenchement à leur résolution.

View File

@@ -0,0 +1,3 @@
# Dashboard de surveillance
Section en cours de rédaction. Cette page décrira le tableau de bord de surveillance en temps réel et ses différentes fonctionnalités.

View File

@@ -0,0 +1,66 @@
# Ajouter un nouveau client
## 1. Accéder à l'administration
Rendez-vous sur **[lucas.unigest.fr/admin.php](https://lucas.unigest.fr/admin.php)** et entrez le code d'accès.
![Login SmartEye](../assets/admin-login.png){ width="400" }
## 2. Créer le dossier
Cliquez sur **+ Nouveau Dossier** en haut à droite.
### Fiche bénéficiaire
Remplissez les informations de la personne surveillée :
| Champ | Description | Obligatoire | Exemple |
|-------|-------------|:-----------:|---------|
| **ID Système** | Identifiant unique (minuscules, sans espaces) | :material-check: | `dupont_marie` |
| **Nom & Prénom** | Nom complet du bénéficiaire | :material-check: | `Marie Dupont` |
| **Age** | Age du bénéficiaire | | `87` |
| **Sexe** | F ou H | | `F` |
| **Adresse** | Adresse d'installation des caméras | | `12 rue des Roses` |
| **Ville** | Ville / Code postal | | `20270 Aléria` |
| **Mobile** | Numéro principal (format international) | :material-check: | `+33612345678` |
| **Fixe** | Numéro fixe du domicile | | `+33495123456` |
!!! tip "Convention de nommage"
L'ID système sera utilisé partout : dossier images, configuration SmartEye, base de données.
Choisissez un ID court et descriptif : `nom_prenom` ou `nom_ville`.
## 3. Ajouter les contacts d'urgence
Cliquez sur **+ Ajouter Contact** pour chaque personne à prévenir en cas de chute.
| Champ | Description | Exemple |
|-------|-------------|---------|
| **Nom** | Prénom du contact | Jean |
| **Rôle** | Lien avec le bénéficiaire | Fils |
| **Téléphone** | Numéro mobile (format international) | +33698765432 |
| **Email** | Email (optionnel) | jean@email.fr |
!!! warning "Ordre des contacts"
Les contacts sont alertés **dans l'ordre de la liste**. Placez le contact principal en premier.
### Exemple de réseau de contacts typique
```
1. Fille (proche géographiquement) → Premier appel
2. Fils (disponible en journée) → Deuxième appel
3. Voisin (à côté, peut intervenir) → Troisième appel
4. Médecin traitant → Notification email
```
## 4. Enregistrer
Cliquez sur **:material-content-save: Enregistrer la Fiche**. Le client apparaît dans le tableau principal avec le statut **Surveillance Active**.
## 5. Étapes suivantes
Après la création du client dans Lucas, il faut :
- [ ] **Installer les caméras** chez le bénéficiaire → [Guide déploiement](../deploiement/jetson.md)
- [ ] **Configurer SmartEye** avec l'ID client → [Installation client](../deploiement/installation-client.md)
- [ ] **Installer LucasApp** sur le téléphone des contacts → [Guide LucasApp](dashboard.md)
- [ ] **Tester une chute simulée** pour valider la chaîne complète

View File

@@ -0,0 +1,60 @@
# Lucas — Système Intelligent de Protection à Domicile
**Surveillance IA · Détection de chutes · Alerte instantanée · Protection 24/7**
---
## Comment ça marche ?
```mermaid
graph LR
A[Caméras IP] -->|RTSP| B[Jetson Orin Nano]
B -->|Détection YOLO| C{Chute ?}
C -->|Image| D[Serveur Lucas]
D -->|Gemini AI| E{Confirmée ?}
E -->|Oui| F[Alerte Firebase]
F -->|Push| G[LucasApp Mobile]
E -->|Non| H[Fausse alerte filtrée]
```
## Les 3 composants
| Composant | Rôle | Technologie |
|-----------|------|-------------|
| :material-cctv: **SmartEye** | Détection de chutes en temps réel | YOLO + Jetson Orin Nano |
| :material-server: **Lucas** | Backend IA, alertes, gestion clients | PHP + Python + Gemini |
| :material-cellphone: **LucasApp** | Réception alertes, flux live, interphone | Flutter (Android) |
## Démarrage rapide
<div class="grid cards" markdown>
- :material-account-plus:{ .lg .middle } **Ajouter un client**
---
Créer une fiche bénéficiaire et ses contacts d'urgence
[:octicons-arrow-right-24: Guide nouveau client](guide/nouveau-client.md)
- :material-server-network:{ .lg .middle } **Déployer un site**
---
Installer les caméras, le Jetson et configurer le réseau
[:octicons-arrow-right-24: Guide déploiement](deploiement/jetson.md)
- :material-api:{ .lg .middle } **API & Intégration**
---
Endpoints, payloads FCM et contrats entre composants
[:octicons-arrow-right-24: Documentation API](api/endpoints.md)
</div>
!!! warning "Philosophie de sécurité"
**Mieux vaut 10 fausses alertes filtrées par Gemini qu'une seule chute ignorée.**
YOLO ratisse large, Gemini confirme. En cas de doute, l'alerte part toujours.

View File

@@ -0,0 +1,57 @@
# Checklist Installation
<div style="background: linear-gradient(135deg, #1b5e20 0%, #2e7d32 100%); color: white; padding: 24px; border-radius: 12px; margin-bottom: 24px;">
<h2 style="margin:0; color: #a5d6a7;">Suivi d'installation client</h2>
<p style="margin: 8px 0 0 0; opacity: 0.9;">Outil interactif pour suivre chaque etape du deploiement chez un client — du bureau jusqu'a la mise en production.</p>
</div>
---
## Acceder a la checklist
:material-open-in-new: **[Ouvrir la checklist interactive](https://lucas.unigest.fr/checklist.php)**
---
## Les 6 phases d'installation
La checklist couvre le parcours complet du technicien :
| Phase | Description | Responsable principal |
|:-----:|-------------|:---------------------:|
| 1 | **Au bureau** — Creer le dossier client, preparer le Jetson et les cameras | :material-server: Lucas + :material-eye: SmartEye |
| 2 | **Sur place : Materiel** — Brancher, connecter, positionner | :material-eye: SmartEye |
| 3 | **Configuration AVA** — Enregistrer le compte, scanner les cameras | :material-cellphone: AVA |
| 4 | **Tunnels SSH** — Cle SSH, autossh, verification ports | :material-eye: SmartEye + :material-server: Lucas |
| 5 | **Tests complets** — Camera live, simulation chute, notifications | :material-check-all: Tous |
| 6 | **Mise en production** — Activation, formation famille, PV recette | :material-check-all: Tous |
---
## Fonctionnalites
- **2 cases par tache** : Fait + Teste
- **Selecteur client** : gerer plusieurs installations en parallele
- **Barre de progression** : vue d'ensemble de l'avancement
- **Notes libres** : documenter les particularites de chaque installation
- **Sauvegarde locale** : les donnees persistent dans le navigateur (localStorage)
- **Lien vers les guides** : chaque etape technique renvoie vers la documentation detaillee
---
## 3 systemes, 3 couleurs
| Couleur | Systeme | Role |
|---------|---------|------|
| :material-circle:{ style="color: #f59e0b" } Orange | **SmartEye** | Jetson + cameras + detection YOLO |
| :material-circle:{ style="color: #10b981" } Vert | **Lucas** | Serveur OVH + admin + API + Gemini |
| :material-circle:{ style="color: #6366f1" } Violet | **AVA** | Application mobile famille |
---
## Voir aussi
- [Configuration video camera](../deploiement/config-video-camera.md)
- [Installation camera IP](../deploiement/installation-camera.md)
- [Installation Jetson](../deploiement/jetson.md)
- [Guide nouveau client](../guide/nouveau-client.md)

View File

@@ -0,0 +1,72 @@
site_name: Lucas Documentation
site_description: Système intelligent de protection à domicile — Détection de chutes par IA
site_url: https://lucas.unigest.fr/docs
theme:
name: material
language: fr
palette:
- scheme: slate
primary: deep purple
accent: cyan
toggle:
icon: material/brightness-4
name: Mode clair
- scheme: default
primary: deep purple
accent: cyan
toggle:
icon: material/brightness-7
name: Mode sombre
font:
text: Inter
code: JetBrains Mono
icon:
logo: material/shield-home
features:
- navigation.tabs
- navigation.sections
- navigation.expand
- navigation.top
- search.highlight
- content.code.copy
- content.tabs.link
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- tables
- attr_list
nav:
- Accueil: index.md
- Architecture:
- Vue d'ensemble: architecture/overview.md
- Flux de données: architecture/data-flow.md
- Guide Utilisateur:
- Nouveau client: guide/nouveau-client.md
- Dashboard: guide/dashboard.md
- Gestion des alertes: guide/alertes.md
- Déploiement:
- Installation caméra IP: deploiement/installation-camera.md
- Configuration vidéo caméra: deploiement/config-video-camera.md
- Installation simplifiée: deploiement/jetson.md
- Installation expert: deploiement/jetson-expert.md
- Préparer la carte Gold: deploiement/carte-gold.md
- Clonage des cartes SD: deploiement/clonage.md
- Installation client: deploiement/installation-client.md
- Outils:
- Checklist installation: outils/checklist.md
- API:
- Endpoints: api/endpoints.md
- Payload FCM: api/fcm-payload.md

415
mkdocs-smarteye/upload.php Normal file
View File

@@ -0,0 +1,415 @@
<?php
/**
* Upload d'images pour la documentation MkDocs SmartEye
* Supporte : drag & drop, sélection de fichier, collage (Cmd+V / Ctrl+V)
*
* Accès : https://lucas.unigest.fr/docs-upload/upload.php
*/
// Sécurité basique — à adapter si besoin
$UPLOAD_DIR = __DIR__ . '/docs/assets/images/';
$ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'];
$MAX_SIZE = 10 * 1024 * 1024; // 10 MB
// Sous-dossiers disponibles
$SUBDIRS = ['general', 'architecture', 'guide', 'deploiement', 'api', 'app'];
// Traitement de l'upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
header('Content-Type: application/json');
$file = $_FILES['image'];
$subdir = isset($_POST['subdir']) && in_array($_POST['subdir'], $SUBDIRS) ? $_POST['subdir'] : 'general';
$targetDir = $UPLOAD_DIR . $subdir . '/';
// Vérifications
if ($file['error'] !== UPLOAD_ERR_OK) {
echo json_encode(['success' => false, 'error' => 'Erreur upload: ' . $file['error']]);
exit;
}
if ($file['size'] > $MAX_SIZE) {
echo json_encode(['success' => false, 'error' => 'Fichier trop volumineux (max 10 MB)']);
exit;
}
// Déterminer l'extension
$originalName = $file['name'];
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
// Si c'est un collage (blob), détecter le type MIME
if (empty($ext) || $ext === 'blob') {
$mime = mime_content_type($file['tmp_name']);
$mimeMap = [
'image/png' => 'png',
'image/jpeg' => 'jpg',
'image/gif' => 'gif',
'image/webp' => 'webp',
'image/svg+xml' => 'svg',
];
$ext = $mimeMap[$mime] ?? '';
}
if (!in_array($ext, $ALLOWED_EXTENSIONS)) {
echo json_encode(['success' => false, 'error' => "Extension non autorisée: $ext"]);
exit;
}
// Nom du fichier : soit le nom custom, soit le nom original, soit un timestamp
$customName = isset($_POST['filename']) ? trim($_POST['filename']) : '';
if ($customName) {
// Nettoyer le nom
$customName = preg_replace('/[^a-zA-Z0-9_\-]/', '-', $customName);
$filename = $customName . '.' . $ext;
} elseif ($originalName && $originalName !== 'blob' && $originalName !== 'image.png') {
$filename = preg_replace('/[^a-zA-Z0-9_\-\.]/', '-', $originalName);
} else {
$filename = date('Y-m-d_H-i-s') . '.' . $ext;
}
$targetPath = $targetDir . $filename;
// Éviter l'écrasement
if (file_exists($targetPath)) {
$base = pathinfo($filename, PATHINFO_FILENAME);
$filename = $base . '_' . time() . '.' . $ext;
$targetPath = $targetDir . $filename;
}
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
$mdPath = "assets/images/$subdir/$filename";
echo json_encode([
'success' => true,
'filename' => $filename,
'subdir' => $subdir,
'markdown' => "![Description]($mdPath)",
'path' => $mdPath,
]);
} else {
echo json_encode(['success' => false, 'error' => 'Impossible de déplacer le fichier']);
}
exit;
}
// Lister les images existantes
$existingImages = [];
foreach ($SUBDIRS as $sd) {
$dir = $UPLOAD_DIR . $sd . '/';
if (is_dir($dir)) {
$files = scandir($dir);
foreach ($files as $f) {
if ($f === '.' || $f === '..') continue;
$ext = strtolower(pathinfo($f, PATHINFO_EXTENSION));
if (in_array($ext, $ALLOWED_EXTENSIONS)) {
$existingImages[] = [
'name' => $f,
'subdir' => $sd,
'path' => "assets/images/$sd/$f",
'url' => "docs/assets/images/$sd/$f",
'size' => filesize($dir . $f),
];
}
}
}
}
// Image à la racine de images/
$rootDir = $UPLOAD_DIR;
if (is_dir($rootDir)) {
$files = scandir($rootDir);
foreach ($files as $f) {
if ($f === '.' || $f === '..' || is_dir($rootDir . $f)) continue;
$ext = strtolower(pathinfo($f, PATHINFO_EXTENSION));
if (in_array($ext, $ALLOWED_EXTENSIONS)) {
$existingImages[] = [
'name' => $f,
'subdir' => '(racine)',
'path' => "assets/images/$f",
'url' => "docs/assets/images/$f",
'size' => filesize($rootDir . $f),
];
}
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Images — Documentation SmartEye</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', -apple-system, sans-serif;
background: #1a1a2e;
color: #e0e0e0;
min-height: 100vh;
padding: 2rem;
}
h1 { color: #bb86fc; margin-bottom: 0.5rem; }
.subtitle { color: #888; margin-bottom: 2rem; }
.upload-zone {
border: 3px dashed #bb86fc44;
border-radius: 16px;
padding: 3rem 2rem;
text-align: center;
transition: all 0.3s;
cursor: pointer;
margin-bottom: 2rem;
background: #16213e;
}
.upload-zone:hover, .upload-zone.dragover {
border-color: #bb86fc;
background: #1a1a3e;
transform: scale(1.01);
}
.upload-zone .icon { font-size: 3rem; margin-bottom: 1rem; }
.upload-zone p { color: #aaa; }
.upload-zone .hint { font-size: 0.85rem; color: #666; margin-top: 0.5rem; }
.controls {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
align-items: center;
}
select, input[type="text"] {
padding: 0.6rem 1rem;
border-radius: 8px;
border: 1px solid #333;
background: #16213e;
color: #e0e0e0;
font-size: 0.95rem;
}
select:focus, input:focus { outline: none; border-color: #bb86fc; }
input[type="text"] { flex: 1; min-width: 200px; }
.result {
display: none;
background: #0f3460;
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.result.show { display: block; }
.result.error { background: #3d0000; }
.result .md-code {
background: #1a1a2e;
padding: 0.8rem 1rem;
border-radius: 8px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.9rem;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 0.5rem;
}
.result .md-code:hover { background: #222244; }
.result .md-code .copy-btn {
background: #bb86fc;
color: #000;
border: none;
padding: 0.3rem 0.8rem;
border-radius: 6px;
cursor: pointer;
font-size: 0.8rem;
}
.preview-img {
max-width: 300px;
max-height: 200px;
border-radius: 8px;
margin-top: 1rem;
}
h2 { color: #bb86fc; margin: 2rem 0 1rem; }
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.gallery-item {
background: #16213e;
border-radius: 12px;
padding: 0.8rem;
text-align: center;
}
.gallery-item img {
max-width: 100%;
max-height: 150px;
border-radius: 8px;
object-fit: contain;
}
.gallery-item .name {
font-size: 0.8rem;
color: #aaa;
margin-top: 0.5rem;
word-break: break-all;
}
.gallery-item .badge {
display: inline-block;
background: #bb86fc22;
color: #bb86fc;
padding: 0.15rem 0.5rem;
border-radius: 4px;
font-size: 0.7rem;
margin-top: 0.3rem;
}
.gallery-item .md-snippet {
font-size: 0.7rem;
color: #666;
cursor: pointer;
margin-top: 0.3rem;
}
.gallery-item .md-snippet:hover { color: #bb86fc; }
.empty { color: #555; text-align: center; padding: 2rem; }
.toast {
position: fixed;
bottom: 2rem;
right: 2rem;
background: #bb86fc;
color: #000;
padding: 0.8rem 1.5rem;
border-radius: 8px;
font-weight: 600;
transform: translateY(100px);
opacity: 0;
transition: all 0.3s;
}
.toast.show { transform: translateY(0); opacity: 1; }
</style>
</head>
<body>
<h1>Upload Images</h1>
<p class="subtitle">Documentation SmartEye — Glissez, cliquez ou collez (Cmd+V) une image</p>
<div class="controls">
<select id="subdir">
<?php foreach ($SUBDIRS as $sd): ?>
<option value="<?= $sd ?>"><?= ucfirst($sd) ?></option>
<?php endforeach; ?>
</select>
<input type="text" id="filename" placeholder="Nom du fichier (optionnel, sans extension)">
</div>
<div class="upload-zone" id="dropzone">
<div class="icon">📸</div>
<p><strong>Glissez une image ici</strong> ou cliquez pour sélectionner</p>
<p class="hint">Cmd+V pour coller depuis le presse-papiers &bull; JPG, PNG, GIF, WebP, SVG &bull; Max 10 MB</p>
<input type="file" id="fileInput" accept="image/*" style="display:none">
</div>
<div class="result" id="result"></div>
<h2>Images existantes (<?= count($existingImages) ?>)</h2>
<?php if (empty($existingImages)): ?>
<p class="empty">Aucune image pour l'instant.</p>
<?php else: ?>
<div class="gallery">
<?php foreach ($existingImages as $img): ?>
<div class="gallery-item">
<?php if (in_array(pathinfo($img['name'], PATHINFO_EXTENSION), ['svg'])): ?>
<img src="<?= htmlspecialchars($img['url']) ?>" alt="<?= htmlspecialchars($img['name']) ?>">
<?php else: ?>
<img src="<?= htmlspecialchars($img['url']) ?>" alt="<?= htmlspecialchars($img['name']) ?>" loading="lazy">
<?php endif; ?>
<div class="name"><?= htmlspecialchars($img['name']) ?></div>
<span class="badge"><?= htmlspecialchars($img['subdir']) ?></span>
<div class="md-snippet" onclick="copyText('![](<?= htmlspecialchars($img['path']) ?>)')">
Copier le markdown
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="toast" id="toast">Copié !</div>
<script>
const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('fileInput');
const resultDiv = document.getElementById('result');
// Click to select
dropzone.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', () => {
if (fileInput.files[0]) uploadFile(fileInput.files[0]);
});
// Drag & drop
dropzone.addEventListener('dragover', (e) => {
e.preventDefault();
dropzone.classList.add('dragover');
});
dropzone.addEventListener('dragleave', () => dropzone.classList.remove('dragover'));
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('dragover');
const file = e.dataTransfer.files[0];
if (file) uploadFile(file);
});
// Paste (Cmd+V)
document.addEventListener('paste', (e) => {
const items = e.clipboardData.items;
for (let item of items) {
if (item.type.startsWith('image/')) {
const file = item.getAsFile();
if (file) uploadFile(file);
return;
}
}
});
function uploadFile(file) {
const formData = new FormData();
formData.append('image', file);
formData.append('subdir', document.getElementById('subdir').value);
const customName = document.getElementById('filename').value.trim();
if (customName) formData.append('filename', customName);
resultDiv.className = 'result show';
resultDiv.innerHTML = '<p>Upload en cours...</p>';
fetch('upload.php', { method: 'POST', body: formData })
.then(r => r.json())
.then(data => {
if (data.success) {
resultDiv.className = 'result show';
resultDiv.innerHTML = `
<p><strong>Upload réussi !</strong> — ${data.filename} dans <em>${data.subdir}/</em></p>
<div class="md-code">
<code>${data.markdown}</code>
<button class="copy-btn" onclick="copyText('${data.markdown}')">Copier</button>
</div>
<img src="docs/assets/images/${data.subdir}/${data.filename}" class="preview-img" alt="Preview">
`;
// Reset le nom custom
document.getElementById('filename').value = '';
} else {
resultDiv.className = 'result show error';
resultDiv.innerHTML = `<p><strong>Erreur :</strong> ${data.error}</p>`;
}
})
.catch(err => {
resultDiv.className = 'result show error';
resultDiv.innerHTML = `<p><strong>Erreur réseau :</strong> ${err.message}</p>`;
});
}
function copyText(text) {
navigator.clipboard.writeText(text).then(() => {
const toast = document.getElementById('toast');
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 1500);
});
}
</script>
</body>
</html>