14 KiB
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.
// bridge.interface.ts
interface IAutomationBridge {
/** Nom du bridge (ex: "hue", "homeassistant", "mqtt") */
readonly name: string;
/** Initialise la connexion avec le hub/controller */
connect(): Promise<void>;
/** Déconnexion propre */
disconnect(): Promise<void>;
/** Vérifie si le bridge est connecté et fonctionnel */
isConnected(): boolean;
/** Liste tous les appareils découverts */
listDevices(): Promise<AutomationDevice[]>;
/** Récupère l'état d'un appareil */
getDeviceState(deviceId: string): Promise<DeviceState>;
/** Exécute une commande sur un appareil */
executeCommand(deviceId: string, command: DeviceCommand): Promise<void>;
/** 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<string, unknown> };
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.
class AutomationManager {
private bridges: Map<string, IAutomationBridge>;
private devices: Map<string, AutomationDevice>; // Cache unifié
/** Enregistre un nouveau bridge */
registerBridge(bridge: IAutomationBridge): void;
/** Connecte tous les bridges enregistrés */
connectAll(): Promise<void>;
/** 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<void>;
/** Crée des scènes (groupes de commandes) */
executeScene(scene: AutomationScene): Promise<void>;
}
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 :
- Le robot-client scanne le réseau local pour trouver le Hue Bridge (mDNS ou SSDP).
- L'utilisateur est invité à appuyer sur le bouton du bridge (pour autoriser l'accès).
- Le robot-client enregistre une clé API et la stocke localement (chiffrée).
- 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.
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)