From 4ac7bd16d27a491287e74c44a46fe21ae2767cfe Mon Sep 17 00:00:00 2001 From: ordinarthur Date: Wed, 1 Apr 2026 17:10:18 +0200 Subject: [PATCH] add doc --- docs/architecture.md | 48 +++++- docs/features.md | 59 +++++++- docs/home-automation.md | 313 ++++++++++++++++++++++++++++++++++++++++ docs/roadmap.md | 57 +++++++- docs/robot-client.md | 284 ++++++++++++++++++++++++++++++++++++ docs/simulator.md | 42 +++++- 6 files changed, 794 insertions(+), 9 deletions(-) create mode 100644 docs/home-automation.md create mode 100644 docs/robot-client.md diff --git a/docs/architecture.md b/docs/architecture.md index 8574741..e4349f6 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -10,6 +10,36 @@ Le projet intègre de nombreux services externes (STT, TTS, LLM, Google Calendar ## Vue d'ensemble +### Architecture globale (robot + cloud) + +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ ROBOT PHYSIQUE │ +│ │ +│ ┌──────────────┐ UART ┌──────────────────────────┐ │ +│ │ ESP32-S3 │ ◄────────────►│ robot-client (Pi Zero) │ │ +│ │ • Audio I2S │ │ • WebSocket cloud │ │ +│ │ • Servos │ │ • Wake word │ │ +│ │ • LEDs │ │ • Domotique (local) │ │ +│ │ • Capteurs │ │ • WiFi/BLE setup │ │ +│ └──────────────┘ └────────────┬─────────────┘ │ +│ │ │ +└──────────────────────────────────────────────┼───────────────────────────────┘ + │ WebSocket (WSS) + ▼ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ BACKEND CLOUD (NestJS) │ +│ │ +│ Adaptateurs entrants → Core (services) → Adaptateurs sortants │ +│ (WebSocket, REST) (Conversation, (STT, TTS, LLM, │ +│ Calendar, etc.) Storage, etc.) │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` + +> **Note :** Le robot-client gère la domotique en local (Hue, Home Assistant, MQTT) pour la latence et la résilience. Les commandes domotiques sont décidées par le LLM côté cloud, puis transmises au robot-client qui les exécute sur le réseau local. Voir [home-automation.md](home-automation.md) et [robot-client.md](robot-client.md) pour le détail. + +### Architecture du backend cloud + ``` ┌─────────────────────────────────────────────┐ │ ADAPTATEURS ENTRANTS │ @@ -297,4 +327,20 @@ Utilisée par l'app web/mobile pour toute la configuration : - **Circuit breaker** sur les appels aux services externes (STT, TTS, LLM) — si un provider tombe, le système peut fallback sur un autre (ex: Deepgram → Whisper local). - **Retry avec backoff exponentiel** sur les appels API. - **Queue de messages** pour les function calls non critiques (ex: envoi d'email) — si ça échoue, on retry sans bloquer la conversation. -- **Mode dégradé offline** — le robot peut toujours gérer les timers, alarmes, et commandes basiques en local si le core est injoignable. +- **Mode dégradé offline** — le robot peut toujours gérer les timers, alarmes, commandes domotiques basiques, et commandes locales si le core est injoignable. + +## Robot Client + +Le robot-client (`apps/robot-client`) est l'application Node.js/TypeScript qui tourne sur le Raspberry Pi Zero 2 W. Il orchestre toute la logique locale du robot : communication UART avec l'ESP32-S3, connexion WebSocket vers le backend cloud, détection du wake word, provisioning WiFi (BLE + AP mode), et exécution des commandes domotiques sur le réseau local. + +En développement, le robot-client tourne sur le PC du développeur en mode simulateur (`ROBOT_MODE=simulator`), les modules hardware étant remplacés par des mocks. Le simulateur web (`apps/simulator`) sert alors de frontend de debug. + +Voir [robot-client.md](robot-client.md) pour l'architecture détaillée. + +## Couche Domotique + +L'intégration domotique est gérée **localement par le robot-client**, pas par le backend cloud. Chaque écosystème (Philips Hue, Home Assistant, MQTT…) est encapsulé dans un bridge qui implémente une interface `IAutomationBridge`. L'`AutomationManager` orchestre les bridges et expose une API unifiée. + +Le flux : le LLM côté cloud décide d'appeler un tool domotique → le backend transmet la commande au robot-client via WebSocket → le robot-client exécute la commande localement sur le réseau. + +Voir [home-automation.md](home-automation.md) pour le détail complet. diff --git a/docs/features.md b/docs/features.md index 8b8838d..f49ee24 100644 --- a/docs/features.md +++ b/docs/features.md @@ -258,7 +258,63 @@ Une application web (et mobile à terme) pour configurer Ti-Pote et gérer son c --- -## 12. Mode offline dégradé +## 12. Domotique (contrôle de la maison) + +### Description +Ti-Pote peut contrôler les appareils domotiques de la maison par la voix : lumières, thermostats, volets, media players, et tout appareil connecté via les écosystèmes supportés. Le contrôle est exécuté localement depuis le robot (pas via le cloud) pour la latence et la résilience. + +### Cas d'usage +- "Allume la lumière du salon" +- "Mets le salon en ambiance cinéma" +- "Quelle température il fait dans la chambre ?" +- "Monte le chauffage à 22 degrés" +- "Ferme les volets" +- "Active la scène bonne nuit" + +### Écosystèmes supportés +- **Philips Hue** — contrôle direct via l'API REST locale du Hue Bridge +- **Home Assistant** — accès à tous les appareils gérés par HA via son WebSocket API +- **MQTT** — support Zigbee2MQTT, Tasmota, et tout appareil publiant sur MQTT +- **Futurs** : Matter/Thread, Apple HomeKit, Tuya, KNX + +### Architecture +Chaque écosystème est encapsulé dans un bridge avec une interface commune (`IAutomationBridge`). Un `AutomationManager` orchestre les bridges et expose une API unifiée. La découverte des hubs se fait par mDNS. + +Voir [home-automation.md](home-automation.md) pour le détail complet. + +### Functions exposées au LLM +- `list_smart_devices` — Lister les appareils disponibles (par pièce, par type) +- `control_device` — Contrôler un appareil (on/off, brightness, color, temperature…) +- `get_device_state` — Lire l'état d'un appareil +- `execute_scene` — Activer une scène prédéfinie +- `get_sensor_value` — Lire un capteur (température, humidité, mouvement…) + +--- + +## 13. Provisioning WiFi et setup initial + +### Description +Le robot doit pouvoir être configuré sur n'importe quel réseau WiFi par un utilisateur non-technique. Deux mécanismes complémentaires sont prévus. + +### Cas d'usage +- Premier démarrage du robot : l'utilisateur le connecte à son WiFi via l'app mobile (BLE) ou via le captive portal (AP mode) +- Changement de réseau WiFi (déménagement, nouveau routeur) +- Reset réseau (bouton physique sur le robot) + +### Méthode principale : BLE Provisioning +L'app Ti-Pote (web/mobile) scanne les robots en BLE, envoie les credentials WiFi. Le robot se connecte et confirme. L'utilisateur n'a pas besoin de changer de réseau sur son téléphone. + +### Méthode fallback : AP Mode + Captive Portal +Le robot crée un hotspot WiFi (`Ti-Pote-XXXX`). L'utilisateur s'y connecte, un portail web s'ouvre pour choisir le réseau et entrer le mot de passe. Le robot se connecte et désactive le hotspot. + +### Implémentation +- BLE : librairie `noble` (npm) sur le Pi Zero 2 W +- AP mode : `hostapd` + `dnsmasq` + serveur HTTP local (Express/Fastify) +- Voir [robot-client.md](robot-client.md) pour le détail du flux + +--- + +## 14. Mode offline dégradé ### Description Si le robot perd la connexion internet ou que le core est injoignable, certaines fonctionnalités de base restent disponibles localement. @@ -266,6 +322,7 @@ Si le robot perd la connexion internet ou que le core est injoignable, certaines ### Fonctionnalités offline - Timers et alarmes (gérés localement) - Heure et date +- Commandes domotiques basiques (allumer/éteindre — via les bridges locaux, pas besoin du cloud) - Commandes de base pré-enregistrées - Feedback audio (sons, bips) pour confirmer les actions diff --git a/docs/home-automation.md b/docs/home-automation.md new file mode 100644 index 0000000..7a655e1 --- /dev/null +++ b/docs/home-automation.md @@ -0,0 +1,313 @@ +# Intégration Domotique + +## Vue d'ensemble + +Ti-Pote peut interagir avec les équipements domotiques de la maison. L'utilisateur contrôle ses lampes, thermostats, volets, et autres appareils connectés par la voix. L'architecture est conçue pour être extensible : chaque écosystème domotique est encapsulé dans un **bridge** (adaptateur) avec une interface commune. + +### Philosophie + +Le contrôle domotique est exécuté **localement depuis le robot-client** (sur le Pi Zero 2 W), pas depuis le backend cloud. C'est un choix délibéré : + +- **Latence** : les commandes domotiques doivent être quasi-instantanées. Passer par le cloud ajouterait 100-300ms inutiles. +- **Résilience** : si la connexion internet tombe, le robot peut encore contrôler les lumières et les appareils locaux. +- **Privacy** : les commandes domotiques ne transitent pas par des serveurs distants. + +Le flux est le suivant : l'utilisateur parle → le backend cloud traite la requête (STT → LLM) → le LLM décide d'appeler une function domotique → le backend envoie la commande au robot-client via WebSocket → le robot-client exécute la commande sur le réseau local. + +``` +Utilisateur : "Allume la lumière du salon" + + ┌─────────────┐ audio ┌──────────────┐ function call ┌──────────────────┐ + │ Robot │ ──────────►│ Backend │ ──────────────►│ Robot-client │ + │ (micro) │ │ Cloud │ "set_light" │ (Pi Zero) │ + └─────────────┘ │ │ │ │ + │ STT → LLM │ │ ┌──────────────┐ │ + │ │ │ │ Hue Bridge │ │ + │ │ │ │ (REST local) │ │ + └──────────────┘ │ └──────┬───────┘ │ + └────────┼─────────┘ + │ + ┌──────▼───────┐ + │ Lampe Hue │ + │ (salon) │ + └──────────────┘ +``` + +## Architecture des bridges + +Chaque écosystème domotique est implémenté comme un **bridge** qui respecte une interface TypeScript commune. Cela permet d'ajouter de nouveaux écosystèmes sans modifier le reste du code. + +```typescript +// bridge.interface.ts +interface IAutomationBridge { + /** Nom du bridge (ex: "hue", "homeassistant", "mqtt") */ + readonly name: string; + + /** Initialise la connexion avec le hub/controller */ + connect(): Promise; + + /** Déconnexion propre */ + disconnect(): Promise; + + /** Vérifie si le bridge est connecté et fonctionnel */ + isConnected(): boolean; + + /** Liste tous les appareils découverts */ + listDevices(): Promise; + + /** Récupère l'état d'un appareil */ + getDeviceState(deviceId: string): Promise; + + /** Exécute une commande sur un appareil */ + executeCommand(deviceId: string, command: DeviceCommand): Promise; + + /** S'abonne aux changements d'état (optionnel) */ + onStateChange?(callback: (deviceId: string, state: DeviceState) => void): void; +} + +interface AutomationDevice { + id: string; + name: string; // Nom lisible ("Lampe salon", "Thermostat chambre") + type: DeviceType; // "light" | "switch" | "thermostat" | "cover" | "sensor" | "media" | ... + room?: string; // Pièce (si disponible) + capabilities: string[]; // ["on_off", "brightness", "color", "temperature", ...] + bridge: string; // Nom du bridge source +} + +interface DeviceState { + on?: boolean; + brightness?: number; // 0-100 + color?: { r: number; g: number; b: number }; + colorTemp?: number; // en Kelvin + temperature?: number; // en °C (thermostat/capteur) + targetTemperature?: number; // consigne (thermostat) + position?: number; // 0-100 (volets) + [key: string]: unknown; // Propriétés spécifiques au bridge +} + +type DeviceCommand = + | { action: 'turn_on'; brightness?: number; color?: string; colorTemp?: number } + | { action: 'turn_off' } + | { action: 'set_brightness'; value: number } + | { action: 'set_color'; r: number; g: number; b: number } + | { action: 'set_color_temp'; kelvin: number } + | { action: 'set_temperature'; value: number } + | { action: 'set_position'; value: number } // volets + | { action: 'toggle' } + | { action: 'custom'; payload: Record }; +``` + +### AutomationManager + +Le `AutomationManager` orchestre tous les bridges actifs. Il maintient un registre unifié des appareils, quelle que soit leur source, et expose une API unique au reste du robot-client. + +```typescript +class AutomationManager { + private bridges: Map; + private devices: Map; // Cache unifié + + /** Enregistre un nouveau bridge */ + registerBridge(bridge: IAutomationBridge): void; + + /** Connecte tous les bridges enregistrés */ + connectAll(): Promise; + + /** Liste tous les appareils de tous les bridges */ + listAllDevices(): AutomationDevice[]; + + /** Cherche des appareils par nom ou pièce (fuzzy matching) */ + findDevices(query: string): AutomationDevice[]; + + /** Exécute une commande (résout le bridge automatiquement) */ + executeCommand(deviceId: string, command: DeviceCommand): Promise; + + /** Crée des scènes (groupes de commandes) */ + executeScene(scene: AutomationScene): Promise; +} +``` + +## Bridges supportés + +### 1. Philips Hue + +Communication directe avec le Hue Bridge via son API REST locale. Pas besoin du cloud Philips — tout se passe sur le réseau local. + +| Paramètre | Détail | +|-----------|--------| +| Protocole | HTTP REST (API v2 CLIP) | +| Découverte | mDNS (`_hue._tcp.local`) ou IP manuelle | +| Auth | Appui physique sur le bridge + échange de clé API | +| Appareils | Lampes, prises, capteurs de mouvement, boutons | +| Capacités | on/off, brightness, color (RGB + CT), groupes, scènes | + +**Découverte et appairage :** + +1. Le robot-client scanne le réseau local pour trouver le Hue Bridge (mDNS ou SSDP). +2. L'utilisateur est invité à appuyer sur le bouton du bridge (pour autoriser l'accès). +3. Le robot-client enregistre une clé API et la stocke localement (chiffrée). +4. Les lampes et groupes sont découverts automatiquement. + +**Exemples de commandes vocales :** +- "Allume la lumière du salon" +- "Mets le salon en rouge à 50%" +- "Éteins toutes les lumières" +- "Active la scène cinéma" + +### 2. Home Assistant + +Communication avec une instance Home Assistant via son WebSocket API. Home Assistant est un hub domotique open source qui supporte des centaines de protocoles (Zigbee, Z-Wave, WiFi, Bluetooth, Matter...). C'est le bridge le plus polyvalent : en connectant Ti-Pote à Home Assistant, on accède automatiquement à tous les appareils gérés par HA. + +| Paramètre | Détail | +|-----------|--------| +| Protocole | WebSocket (+ REST API pour certaines opérations) | +| Découverte | mDNS (`_home-assistant._tcp.local`) ou URL manuelle | +| Auth | Long-lived access token (généré dans l'interface HA) | +| Appareils | Tout ce que Home Assistant gère (lumières, thermostats, volets, media players, capteurs, aspirateurs, caméras...) | +| Capacités | Dépend des appareils — HA expose les entités avec leurs services | + +**Intégration :** + +Le bridge Home Assistant est particulièrement puissant car il permet aussi : + +- D'exécuter des **automatisations HA** via la voix ("Lance le scénario bonne nuit"). +- De lire les **capteurs** ("Quelle est la température dans la chambre ?"). +- D'interagir avec des **media players** ("Mets la musique dans la cuisine"). +- D'accéder à l'**historique** des états ("À quelle heure la porte du garage a été ouverte ?"). + +### 3. MQTT (Zigbee2MQTT, Tasmota...) + +Pour les utilisateurs qui utilisent un broker MQTT directement (sans Home Assistant). Permet de contrôler des appareils Zigbee (via Zigbee2MQTT), des prises Tasmota, ou tout appareil publiant sur MQTT. + +| Paramètre | Détail | +|-----------|--------| +| Protocole | MQTT (via broker Mosquitto ou autre) | +| Découverte | Topics de discovery (`homeassistant/+/+/config` ou custom) | +| Auth | Credentials MQTT (user/password) | +| Appareils | Dépend des topics publiés | + +### 4. Bridges futurs (Phase 4+) + +L'interface `IAutomationBridge` permet d'ajouter facilement : + +- **Matter/Thread** — le nouveau standard IoT (quand l'écosystème sera plus mature) +- **Apple HomeKit** — via le protocole HAP +- **Google Home** — via l'API locale (si disponible) +- **Tuya/Smart Life** — protocole local Tuya +- **KNX** — domotique filaire haut de gamme + +## Découverte automatique + +Au démarrage (et périodiquement), le robot-client scanne le réseau local pour découvrir les hubs domotiques disponibles : + +``` +1. Scan mDNS + ├── _hue._tcp.local → Hue Bridge trouvé à 192.168.1.42 + ├── _home-assistant._tcp.local → HA trouvé à 192.168.1.100 + └── (autres services...) + +2. Notification à l'utilisateur + "J'ai détecté un pont Philips Hue et une instance Home Assistant + sur ton réseau. Tu veux que je m'y connecte ?" + +3. Configuration via l'app frontend + └── L'utilisateur entre les credentials nécessaires (clé API Hue, token HA...) +``` + +## Functions LLM (tools pour le backend) + +Le backend cloud expose ces tools au LLM. Quand le LLM les appelle, le backend transmet la commande au robot-client qui l'exécute localement. + +```typescript +const automationTools = [ + { + name: 'list_smart_devices', + description: 'Liste les appareils domotiques disponibles dans la maison', + parameters: { + type: 'object', + properties: { + room: { type: 'string', description: 'Filtrer par pièce (optionnel)' }, + type: { type: 'string', description: 'Filtrer par type: light, switch, thermostat, cover, sensor, media' }, + }, + }, + }, + { + name: 'control_device', + description: 'Contrôle un appareil domotique (allumer, éteindre, régler luminosité, couleur, température...)', + parameters: { + type: 'object', + properties: { + device_name: { type: 'string', description: 'Nom de l\'appareil (ex: "lampe salon")' }, + action: { type: 'string', enum: ['turn_on', 'turn_off', 'toggle', 'set_brightness', 'set_color', 'set_color_temp', 'set_temperature', 'set_position'] }, + value: { type: 'number', description: 'Valeur numérique selon l\'action (brightness 0-100, temperature en °C, etc.)' }, + color: { type: 'string', description: 'Couleur (nom ou hex, ex: "rouge", "#FF0000")' }, + }, + required: ['device_name', 'action'], + }, + }, + { + name: 'get_device_state', + description: 'Récupère l\'état actuel d\'un appareil (allumé/éteint, température, luminosité...)', + parameters: { + type: 'object', + properties: { + device_name: { type: 'string', description: 'Nom de l\'appareil' }, + }, + required: ['device_name'], + }, + }, + { + name: 'execute_scene', + description: 'Active une scène ou un scénario domotique prédéfini', + parameters: { + type: 'object', + properties: { + scene_name: { type: 'string', description: 'Nom de la scène (ex: "cinéma", "bonne nuit", "matin")' }, + }, + required: ['scene_name'], + }, + }, + { + name: 'get_sensor_value', + description: 'Lit la valeur d\'un capteur domotique (température, humidité, mouvement, ouverture...)', + parameters: { + type: 'object', + properties: { + sensor_name: { type: 'string', description: 'Nom du capteur (ex: "température chambre", "capteur porte garage")' }, + }, + required: ['sensor_name'], + }, + }, +]; +``` + +## Exemples de conversations + +**Contrôle basique :** +> "Hey Ti-Pote, allume la lumière du salon" +> → LLM appelle `control_device("lampe salon", "turn_on")` → robot-client → Hue Bridge → lampe s'allume +> "C'est fait, la lampe du salon est allumée." + +**Contrôle avancé :** +> "Mets le salon en ambiance cinéma" +> → LLM appelle `execute_scene("cinéma")` → robot-client → Hue Bridge → scène activée +> "Scène cinéma activée. Bon film !" + +**Lecture de capteur :** +> "Quelle température il fait dans la chambre ?" +> → LLM appelle `get_sensor_value("température chambre")` → robot-client → Home Assistant → 21.5°C +> "Il fait 21 degrés et demi dans la chambre." + +**Combinaison avec d'autres services :** +> "Il fait froid, monte le chauffage et rappelle-moi de baisser dans 2 heures" +> → LLM appelle `control_device("thermostat salon", "set_temperature", 22)` + `set_timer("Baisser le chauffage", 7200)` +> "J'ai monté le thermostat à 22 degrés. Je te rappellerai de baisser dans 2 heures." + +## Configuration dans le frontend + +L'interface web (Next.js) propose une section "Domotique" dans les paramètres : + +- **Bridges détectés** : liste des hubs trouvés sur le réseau, avec statut de connexion +- **Appairage** : bouton pour initier l'appairage (Hue : appui sur le bridge, HA : saisie du token) +- **Appareils** : liste des devices découverts avec possibilité de renommer, assigner à une pièce, masquer +- **Scènes** : création de scènes personnalisées (groupes de commandes) +- **Test** : bouton pour tester une commande sur un appareil (allumer/éteindre) diff --git a/docs/roadmap.md b/docs/roadmap.md index 3f9b215..14e4d76 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -5,11 +5,11 @@ Le développement de Ti-Pote est découpé en phases progressives. Chaque phase produit un livrable fonctionnel et testable. L'objectif est de valider chaque brique avant d'empiler la suivante. ``` -Phase 0 Phase 1 Phase 2 Phase 3 Phase 4 -Setup & Conversation Services Intelligence Expansion -Infra vocale métier avancée -─────────────────────────────────────────────────────────────────────────────────► -[2-3 sem] [3-4 sem] [4-6 sem] [4-6 sem] [Continu] +Phase 0 Phase 1 Phase 1.5 Phase 2 Phase 3 Phase 4 +Setup & Conversation Robot Client Services Intelligence Expansion +Infra vocale & Domotique métier avancée +──────────────────────────────────────────────────────────────────────────────────────────────────► +[2-3 sem] [3-4 sem] [3-4 sem] [4-6 sem] [4-6 sem] [Continu] ``` --- @@ -82,6 +82,48 @@ L'utilisateur dit "Hey Ti-Pote, raconte-moi une blague" → le robot répond voc --- +## Phase 1.5 — Robot Client & Domotique (3-4 semaines) + +### Objectif +Remplacer le simulateur comme client direct du backend par un vrai **robot-client** TypeScript (`apps/robot-client`) qui reflète l'architecture cible du robot physique. Ajouter le provisioning WiFi et les premières intégrations domotiques. + +### Livrables + +**Robot Client (`apps/robot-client`)** +- Application Node.js TypeScript qui tourne sur le Pi (ou en mode simulateur sur PC de dev) +- Communication WebSocket bidirectionnelle avec le backend cloud (même protocole que le simulateur) +- Bridge UART avec l'ESP32-S3 via `serialport` (mode physical) ou mock (mode simulator) +- Flag `ROBOT_MODE=simulator|physical` pour switcher entre les deux modes +- Intégration du wake word (OpenWakeWord via subprocess Python) +- Système de health check et heartbeat avec le backend + +**WiFi Provisioning** +- Setup WiFi par BLE (méthode principale) : le robot advertise en BLE, l'app envoie les credentials +- Setup WiFi par AP mode + captive portal (fallback) : hotspot `Ti-Pote-XXXX`, portail web local +- Flux de premier démarrage (onboarding) complet : power on → WiFi → cloud → prêt + +**Domotique — première intégration** +- Architecture de bridges domotiques avec interface `IAutomationBridge` +- `AutomationManager` pour orchestrer plusieurs bridges +- Bridge **Philips Hue** : découverte mDNS, appairage, contrôle des lampes (on/off, brightness, color) +- Bridge **Home Assistant** : connexion WebSocket API, listing des entités, exécution de commandes +- Bridge **MQTT** : connexion broker, support Zigbee2MQTT / Tasmota +- Découverte automatique des hubs sur le réseau local (mDNS) +- Tools LLM : `list_smart_devices`, `control_device`, `get_device_state`, `execute_scene`, `get_sensor_value` + +**Simulateur (évolution)** +- Le simulateur web (`apps/simulator`) devient un frontend de debug pour le robot-client en mode simulator +- Nouvelle topologie : Browser ↔ robot-client (PC) ↔ Backend cloud + +**Frontend** +- Page de configuration WiFi (pour le setup initial) +- Section domotique : bridges détectés, appairage, liste des appareils, test de commande + +### Critère de validation +Le robot-client tourne sur un PC de dev en mode simulator. L'utilisateur peut parler via le simulateur web, la requête transite par le robot-client avant d'atteindre le backend. Depuis la voix, l'utilisateur peut allumer/éteindre une lampe Hue ou un appareil Home Assistant. Le setup WiFi fonctionne en AP mode sur un Pi physique. + +--- + ## Phase 2 — Services métier & Function calling (4-6 semaines) ### Objectif @@ -175,7 +217,7 @@ Améliorer, étendre et polish le produit. Cette phase est continue et n'a pas d - WhatsApp Business API - Telegram Bot API - Spotify / services de musique -- Domotique (Home Assistant, Philips Hue…) +- Nouveaux bridges domotiques (Matter/Thread, Apple HomeKit, Tuya, KNX…) **Mobilité** - Firmware pour le module base mobile @@ -212,6 +254,9 @@ Améliorer, étendre et polish le produit. Cette phase est continue et n'a pas d | Jalon | Phase | Description | |-------|-------|-------------| | "Hello World" vocal | Phase 1 | Premier aller-retour audio complet | +| Robot-client opérationnel | Phase 1.5 | Le robot-client fait le pont simulateur ↔ backend | +| "Allume la lumière" | Phase 1.5 | Ti-Pote contrôle une lampe Hue ou un device HA par la voix | +| Setup WiFi autonome | Phase 1.5 | Le robot se configure sur un réseau WiFi via AP mode ou BLE | | Premier function call | Phase 2 | Ti-Pote crée un événement dans Google Calendar | | "Tu te souviens ?" | Phase 3 | Ti-Pote retrouve un souvenir d'une conversation passée | | Notification proactive | Phase 3 | Ti-Pote rappelle un RDV sans qu'on lui demande | diff --git a/docs/robot-client.md b/docs/robot-client.md new file mode 100644 index 0000000..d92ae1b --- /dev/null +++ b/docs/robot-client.md @@ -0,0 +1,284 @@ +# Robot Client (`apps/robot-client`) + +## Vue d'ensemble + +Le **robot-client** est l'application TypeScript qui tourne directement sur le Raspberry Pi Zero 2 W du robot. C'est le "cerveau logiciel local" de Ti-Pote : il fait le pont entre le hardware (ESP32-S3 via UART) et le backend cloud (via WebSocket). En phase de développement, cette même app est simulée sur un PC de dev, permettant de travailler sans le robot physique. + +### Positionnement dans l'architecture + +``` +┌──────────────┐ UART ┌──────────────────────┐ WebSocket ┌──────────────┐ +│ ESP32-S3 │ ◄────────────►│ robot-client │ ◄─────────────►│ Backend │ +│ (firmware) │ │ (Node.js / Pi) │ │ Cloud │ +│ │ │ │ │ (NestJS) │ +│ • Audio I2S │ │ • Orchestration │ │ │ +│ • Servos │ │ • Wake word │ │ • STT/TTS │ +│ • LEDs │ │ • WiFi management │ │ • LLM │ +│ • Capteurs │ │ • Config locale │ │ • Services │ +└──────────────┘ │ • Domotique bridge │ └──────────────┘ + │ • OTA updates │ + └──────────────────────┘ +``` + +## Runtime et contraintes + +| Paramètre | Choix | +|-----------|-------| +| Runtime | **Node.js** (LTS) | +| Langage | TypeScript strict | +| OS | Raspberry Pi OS Lite (headless) | +| RAM disponible | ~300 Mo (sur 512 Mo, après OS) | +| Stockage | microSD 32 Go | +| Réseau | WiFi 802.11 b/g/n + BLE 4.2 | + +### Pourquoi Node.js ? + +Node.js est le choix le plus cohérent avec le reste du stack (backend NestJS, simulateur). L'écosystème npm offre des librairies matures pour tout ce dont on a besoin : WebSocket (socket.io-client), serialport (UART), mDNS, BLE, HTTP serveur local. La consommation mémoire est gérable sur le Pi Zero 2 W si on fait attention aux dépendances. + +## Architecture interne + +Le robot-client suit lui aussi une architecture modulaire avec des services découplés. Chaque module peut être activé/désactivé selon la configuration du robot. + +``` +apps/robot-client/ +├── src/ +│ ├── main.ts # Point d'entrée, bootstrap +│ ├── config/ +│ │ ├── robot.config.ts # Config locale (device ID, tokens, WiFi...) +│ │ └── hardware.config.ts # Config hardware (UART port, baudrate...) +│ │ +│ ├── transport/ +│ │ ├── cloud-socket.ts # Client WebSocket vers le backend cloud +│ │ ├── uart-bridge.ts # Communication UART avec l'ESP32 +│ │ └── local-server.ts # Serveur HTTP/WS local pour le setup +│ │ +│ ├── services/ +│ │ ├── wake-word.service.ts # Détection wake word (OpenWakeWord via subprocess) +│ │ ├── wifi.service.ts # Gestion WiFi (scan, connect, AP mode) +│ │ ├── bluetooth.service.ts # BLE pour le provisioning +│ │ ├── camera.service.ts # Capture image (CSI camera) +│ │ ├── ota.service.ts # Mises à jour OTA (self + ESP32) +│ │ ├── health.service.ts # Monitoring, heartbeat, diagnostics +│ │ └── automation/ +│ │ ├── automation.manager.ts # Gestionnaire de bridges domotiques +│ │ ├── bridges/ +│ │ │ ├── bridge.interface.ts # Interface commune pour tous les bridges +│ │ │ ├── hue.bridge.ts # Philips Hue (API REST locale) +│ │ │ ├── homeassistant.bridge.ts # Home Assistant (WebSocket API) +│ │ │ └── mqtt.bridge.ts # MQTT générique (Zigbee2MQTT, etc.) +│ │ └── discovery.service.ts # Découverte mDNS des hubs domotiques +│ │ +│ ├── setup/ +│ │ ├── captive-portal.ts # AP mode + portail captif pour config WiFi +│ │ ├── ble-provisioning.ts # Provisioning BLE (méthode principale) +│ │ └── setup-flow.ts # Orchestration du flux de premier setup +│ │ +│ └── utils/ +│ ├── logger.ts # Logging local + remote +│ ├── network.ts # Utilitaires réseau (IP, connectivity check) +│ └── system.ts # Infos système (CPU, RAM, température) +│ +├── scripts/ +│ ├── install.sh # Installation des dépendances système (Node, OpenWakeWord...) +│ └── setup-ap.sh # Configuration du point d'accès WiFi +│ +├── package.json +└── tsconfig.json +``` + +## Fonctionnalités clés + +### 1. Communication cloud (WebSocket) + +Le robot-client maintient une connexion WebSocket persistante avec le backend cloud. C'est le même protocole que le simulateur web — le backend ne fait pas la différence. En cas de déconnexion, le client tente automatiquement de se reconnecter avec un backoff exponentiel. + +Le client gère aussi la bufferisation : si la connexion tombe pendant un échange audio, il peut informer l'utilisateur via les LEDs (passage en orange) et basculer en mode dégradé local. + +### 2. Communication hardware (UART) + +Le robot-client communique avec l'ESP32-S3 via le port série UART à 921600 baud. Il implémente le protocole de frames binaire décrit dans [hardware.md](hardware.md) : réception des chunks audio du micro (AUDIO_UP), envoi des chunks TTS (AUDIO_DOWN), commandes servo/LED, et heartbeat. + +La librairie `serialport` npm est utilisée pour l'accès au port série. + +### 3. Configuration WiFi + +Le robot doit pouvoir être configuré sur n'importe quel réseau WiFi, même par un utilisateur non-technique. Deux mécanismes complémentaires sont prévus. + +#### Méthode principale : BLE Provisioning + +Le mécanisme de setup préféré. L'utilisateur ouvre l'app web/mobile Ti-Pote sur son téléphone et scanne les robots disponibles en BLE. L'app envoie les credentials WiFi (SSID + mot de passe) au robot via BLE. Le robot se connecte au WiFi et confirme la connexion. + +Avantage : l'utilisateur n'a pas besoin de changer de réseau WiFi sur son téléphone pendant le setup. + +``` +Téléphone (App Ti-Pote) Robot (Pi Zero 2 W) + │ │ + │ ─── BLE scan ──────────────────► │ + │ ◄── BLE advertise (Ti-Pote-XXXX) │ + │ │ + │ ─── Connect BLE ──────────────► │ + │ ─── Send WiFi credentials ────► │ + │ │ → connecte au WiFi + │ ◄── WiFi connected (IP, status) │ → contacte le backend cloud + │ │ + │ ─── Send device token ─────────► │ + │ ◄── Setup complete ──────────── │ +``` + +#### Méthode fallback : AP Mode + Captive Portal + +Si le BLE n'est pas disponible (navigateur desktop, BLE désactivé), le robot peut créer son propre point d'accès WiFi. + +1. Au premier démarrage (ou après un reset réseau), le Pi crée un hotspot WiFi : `Ti-Pote-XXXX` (où XXXX est un identifiant unique). +2. L'utilisateur se connecte à ce hotspot depuis son téléphone/PC. +3. Un captive portal s'ouvre automatiquement (ou manuellement à `http://192.168.4.1`). +4. Le portail affiche les réseaux WiFi disponibles. L'utilisateur choisit son réseau et entre le mot de passe. +5. Le robot se connecte au réseau, désactive le hotspot, et confirme via une page de succès. + +``` +┌────────────────────┐ +│ Captive Portal │ +│ (page web locale) │ +│ │ +│ ┌──────────────┐ │ +│ │ WiFi: Maison │ │ +│ │ MDP: ******* │ │ +│ │ [Connecter] │ │ +│ └──────────────┘ │ +│ │ +│ ► Réseau 1 │ +│ ► Réseau 2 │ +│ ► Réseau 3 │ +└────────────────────┘ +``` + +Technologies utilisées : `hostapd` (AP mode), `dnsmasq` (DHCP + DNS redirect), serveur HTTP local Express/Fastify pour le portail. + +### 4. Mode simulation (développement) + +En phase de développement, le robot-client tourne sur un PC de dev au lieu du Pi physique. Les modules hardware (UART, WiFi, BLE, caméra) sont remplacés par des mocks : + +- **UART** → mockée par le simulateur web (qui joue le rôle de l'ESP32 : capture micro via WebRTC, playback via Web Audio API) +- **WiFi** → pas nécessaire en mode dev (le PC est déjà sur le réseau) +- **BLE** → désactivé en mode dev +- **Caméra** → remplacée par la webcam du PC ou des images statiques + +Un flag d'environnement `ROBOT_MODE=simulator|physical` détermine quels modules sont chargés. + +```bash +# Mode développement (sur PC, avec le simulateur web comme frontend) +ROBOT_MODE=simulator pnpm dev:robot + +# Mode production (sur le Pi physique) +ROBOT_MODE=physical pnpm start:robot +``` + +### 5. Premier setup (onboarding) + +Le flux de premier démarrage du robot : + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ PREMIER DÉMARRAGE │ +│ │ +│ 1. Power on │ +│ └─► LEDs en respiration violette (mode setup) │ +│ │ +│ 2. WiFi pas configuré ? │ +│ ├─► Active BLE advertising ("Ti-Pote-XXXX") │ +│ └─► Active AP mode en fallback (30s sans connexion BLE) │ +│ │ +│ 3. Credentials WiFi reçus (BLE ou captive portal) │ +│ └─► Tente la connexion WiFi │ +│ ├─► Succès → LEDs en vert │ +│ └─► Échec → LEDs en rouge, retour étape 2 │ +│ │ +│ 4. Connexion au backend cloud │ +│ └─► Envoie device_id + firmware version │ +│ ├─► Nouveau device → le backend génère un device token │ +│ └─► Device connu → reprise de session │ +│ │ +│ 5. Setup terminé │ +│ └─► LEDs en bleu, robot prêt │ +│ └─► "Bonjour, je suis Ti-Pote ! Je suis prêt." │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +### 6. OTA Updates + +Le robot-client supporte les mises à jour over-the-air : + +- **Self-update** : le backend notifie le client qu'une nouvelle version est disponible. Le client télécharge le nouveau package, le vérifie (checksum), et se redéploie (process manager type PM2 ou systemd). +- **ESP32 update** : le Pi peut flasher l'ESP32 via UART (bootloader ESP32). Le firmware est téléchargé depuis le cloud et poussé vers l'ESP32. +- **Rollback** : si le nouveau firmware ne démarre pas correctement (pas de heartbeat dans les 60 secondes), le système revient à la version précédente. + +## Transition depuis le simulateur + +Le `apps/simulator` actuel (React + Vite) reste en place comme outil de développement. Il joue désormais le rôle d'interface visuelle pour le robot-client en mode simulateur : + +``` +Mode développement actuel : + Browser (simulator) ──WebSocket──► Backend cloud + +Mode développement futur : + Browser (simulator) ──WebSocket──► robot-client (PC) ──WebSocket──► Backend cloud + │ + (simule le Pi) + +Mode production : + ESP32 (hardware) ───UART──► robot-client (Pi) ──WebSocket──► Backend cloud +``` + +Le simulateur web évolue pour devenir un "frontend de debug" du robot-client, plutôt qu'un client direct du backend. Cela permet de tester la logique complète du robot-client (wake word, bufferisation, mode dégradé, domotique) sans hardware. + +## Dépendances système (Pi Zero 2 W) + +```bash +# Runtime +node >= 20 LTS +npm / pnpm + +# Wake word +python3 +pip: openwakeword + +# WiFi AP mode +hostapd +dnsmasq + +# Bluetooth +bluez +noble (npm, bindings BLE) + +# Port série +librairie native serialport (npm) + +# Caméra +libcamera-apps (rpicam-still, rpicam-vid) +``` + +## Variables d'environnement + +```bash +# Mode de fonctionnement +ROBOT_MODE=simulator|physical + +# Backend cloud +CLOUD_URL=wss://api.tipote.dev/ws/robot +DEVICE_TOKEN=eyJ... + +# Hardware (mode physical uniquement) +UART_PORT=/dev/ttyS0 +UART_BAUDRATE=921600 + +# WiFi AP (mode physical uniquement) +AP_SSID=Ti-Pote-XXXX +AP_CHANNEL=6 + +# Domotique +HUE_BRIDGE_IP=192.168.1.x +HUE_API_KEY=xxxxx +HOMEASSISTANT_URL=http://homeassistant.local:8123 +HOMEASSISTANT_TOKEN=xxxxx +MQTT_BROKER_URL=mqtt://192.168.1.x:1883 +``` diff --git a/docs/simulator.md b/docs/simulator.md index 23cd4d1..5f05abf 100644 --- a/docs/simulator.md +++ b/docs/simulator.md @@ -259,11 +259,51 @@ Le simulateur web affiche les timings de chaque étape : Ces timings permettent d'identifier les goulots d'étranglement et d'optimiser la latence. +## Transition vers le robot-client + +> **Important :** À partir de la Phase 1.5, le simulateur web n'est plus un client direct du backend cloud. Il devient un **frontend de debug** pour le robot-client (`apps/robot-client`). + +### Avant (Phase 0-1) + +``` +Browser (simulator) ──WebSocket──► Backend cloud (NestJS) +``` + +Le simulateur se connecte directement au backend, exactement comme le ferait un robot. C'est simple et suffisant pour développer les premières features. + +### Après (Phase 1.5+) + +``` +Browser (simulator) ──WebSocket──► robot-client (PC, mode simulator) ──WebSocket──► Backend cloud +``` + +Le robot-client tourne sur le PC du développeur en mode `ROBOT_MODE=simulator`. Le simulateur web lui envoie l'audio (comme l'ESP32 enverrait des frames UART). Le robot-client fait transiter vers le backend. Cela permet de tester la logique complète du robot-client (wake word, bufferisation, mode dégradé, domotique) sans hardware. + +### En production + +``` +ESP32-S3 ──UART──► robot-client (Pi Zero) ──WebSocket──► Backend cloud +``` + +Le simulateur n'est plus utilisé. Le robot physique parle directement au robot-client via UART. + +### Ce qui change pour le simulateur + +Le simulateur web reste en place et conserve son interface actuelle (statut, bouton wake word, transcript, logs). Ce qui change : + +- Il se connecte au **robot-client local** (ex: `ws://localhost:3001`) au lieu du backend cloud directement. +- Il peut afficher des informations supplémentaires : état des bridges domotiques, statut WiFi, devices connectés. +- Il sert aussi de frontend pour tester le **captive portal** en mode dev. + +Voir [robot-client.md](robot-client.md) pour l'architecture du robot-client. + ## Évolution progressive Le simulateur évolue avec le projet : -**Phase 1 (MVP)** — Simulateur web avec micro + speaker uniquement. Bouton wake word. Transcript et logs. CLI avec mode texte interactif. +**Phase 1 (MVP)** — Simulateur web avec micro + speaker uniquement. Bouton wake word. Transcript et logs. CLI avec mode texte interactif. Connexion directe au backend. + +**Phase 1.5** — Le simulateur se connecte au robot-client (mode simulator) au lieu du backend. Ajout d'un panneau domotique (état des devices). Test du flux complet robot-client ↔ backend. **Phase 2** — Ajout de la webcam pour simuler le module caméra. Interface pour afficher ce que le "robot voit". CLI avec mode audio (envoi de fichiers .wav).