Exemple complet, bout-en-bout, sur un scénario “suspicion de chute mmWave”
Ci-dessous un exemple complet, bout-en-bout, sur un scénario “suspicion de chute mmWave” avec :
- Intent Couche 1 (instrumentation : normalisation / extraction)
- Skill Couche 2 (métier : écritures Odoo + notifications)
- Payloads JSON-RPC Odoo typiques (authenticate, search_read, create, write)
- Résultat notifications
- Audit log append-only (trace_id, policy, write_set, preuves)
Je garde volontairement des noms de modèles x_synergia_* (vos modèles Studio) et des champs explicites ; adaptez les champs à votre schéma exact.

0) Scénario
- Capteur mmWave détecte un pattern “chute suspectée” à 13:44:31Z.
- La couche 1 normalise l’événement (sensor_event_normalizer) → fall_suspect.
- La couche 2 propose une alerte mais, comme c’est critique, elle exige approval hard.
- Le coordinateur valide.
-
La couche 2 exécute synergia.odoo.trigger_alert.v2 :
- crée x_synergia_alert,
- crée x_synergia_notification (IDEL + famille),
- envoie RCS/SMS/WhatsApp selon consentement,
- journalise tout dans un audit append-only.
1) Couche 1 — IoT → signal structuré (sans décision)
1.1 Input (événement brut capteur)
{ "meta": { "trace_id": "trc_20251219_134431_MM1", "timestamp": "2025-12-19T13:44:31Z", "tenant_id": "smartco-ch", "channel": "iot", "actor": { "type": "device", "id": "mmwave_DS_0042", "role": "system" }, "context": { "senior_id": "rp_5561", "visit_id": null, "locale": "fr-FR" } }, "payload": { "source": "mmwave", "event_type": "fall_pattern", "ts": "2025-12-19T13:44:31Z", "raw": { "zone": "living_room", "confidence": 0.82, "duration_ms": 2100 } } }
1.2 Output Couche 1 (skill synergia.sensor_event_normalizer.v1)
{ "meta": { "trace_id": "trc_20251219_134431_MM1", "skill": "synergia.sensor_event_normalizer.v1", "version": "1.0.0", "status": "success", "duration_ms": 42, "warnings": [] }, "result": { "sensor_event": { "type": "fall_suspect", "timestamp": "2025-12-19T13:44:31Z", "attributes": { "zone": "living_room", "duration_ms": 2100, "source": "mmwave" }, "confidence": 0.82 } } }
À ce stade : aucune alerte déclenchée. On a uniquement un événement structuré + preuve.
2) Couche 2 — Décision opératoire contrôlée → écriture Odoo + notifications
La couche 2 commence par résoudre le senior, vérifier consentements et contacts, puis applique la policy (approval hard ici).
2.1 JSON-RPC Odoo — Authentification
POST https://odoo.example.com/jsonrpc
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "common", "method": "authenticate", "args": ["smartco_prod", "svc_synergia", "SVC_PASSWORD", {}] }, "id": 1 }
Réponse
{ "jsonrpc": "2.0", "id": 1, "result": 7 }
uid=7
2.2 JSON-RPC Odoo — Lecture senior & contacts (search_read)
a) Lire le senior (si vous stockez un identifiant externe)
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "res.partner", "search_read", [[["x_synergia_external_id", "=", "rp_5561"]]], { "fields": ["id", "name", "mobile", "email", "x_synergia_family_contact_ids"] } ] }, "id": 2 }
Réponse (exemple)
{ "jsonrpc": "2.0", "id": 2, "result": [ { "id": 5561, "name": "Mme Dupont", "mobile": "+33611111111", "email": "senior@example.com", "x_synergia_family_contact_ids": [880, 881] } ] }
b) Lire consentement “notifier famille”
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "x_synergia_consent", "search_read", [[["x_senior_partner_id", "=", 5561], ["x_scope", "=", "family_notifications"], ["x_state", "=", "granted"]]], { "fields": ["id", "x_scope", "x_state", "x_granted_at"] } ] }, "id": 3 }
Réponse
{ "jsonrpc": "2.0", "id": 3, "result": [ { "id": 1902, "x_scope": "family_notifications", "x_state": "granted", "x_granted_at": "2025-10-02T09:10:00Z" } ] }
2.3 Gate d’approbation (hard)
2.3.1 Pré-demande d’approbation (création d’un “approval_request” interne)
Ici, soit vous avez un modèle x_synergia_approval_request, soit vous utilisez mail.activity ou un objet dédié.
Exemple avec un modèle dédié :
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "x_synergia_approval_request", "create", [{ "x_trace_id": "trc_20251219_134431_MM1", "x_kind": "trigger_alert", "x_priority": "high", "x_senior_partner_id": 5561, "x_payload_json": "{\"trigger\":\"fall_suspect\",\"confidence\":0.82,\"zone\":\"living_room\"}", "x_state": "pending" }] ] }, "id": 4 }
Réponse
{ "jsonrpc": "2.0", "id": 4, "result": 3007 }
approval_request_id=3007 (pending)
2.3.2 Le coordinateur approuve (hors scope JSON-RPC ici : via UI ou WebApp)
On simule l’approbation par un write :
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "x_synergia_approval_request", "write", [[3007], { "x_state": "approved", "x_approver_user_id": 42, "x_approval_comment": "Validation : suspicion de chute (mmWave 0.82) → alerte IDEL + famille." }] ] }, "id": 5 }
Réponse
{ "jsonrpc": "2.0", "id": 5, "result": true }
3) Exécution du skill Couche 2 : synergia.odoo.trigger_alert.v2
3.1 Input Couche 2 (après approval)
{ "meta": { "trace_id": "trc_20251219_134431_MM1", "timestamp": "2025-12-19T13:44:45Z", "tenant_id": "smartco-ch", "channel": "iot", "actor": { "type": "service", "id": "synergia_orchestrator", "role": "system" }, "context": { "senior_id": "rp_5561", "visit_id": null, "locale": "fr-FR" } }, "payload": { "senior_id": "rp_5561", "trigger": "fall_suspect", "priority": "high", "message": "Suspicion de chute (mmWave, zone salon, confiance 0.82). Vérification requise.", "assignee_role": "idel", "sla_minutes": 15, "notify": [ { "audience": "idel", "channel": "whatsapp" }, { "audience": "family", "channel": "rcs" } ], "evidence_refs": [ "iot:mmwave_DS_0042@2025-12-19T13:44:31Z", "approval_request:3007" ], "approval": { "approver_user_id": "usr_coord_0042", "approval_comment": "Validation : suspicion de chute (mmWave 0.82) → alerte IDEL + famille." } } }
3.2 JSON-RPC Odoo — Création de l’alerte x_synergia_alert
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "x_synergia_alert", "create", [{ "x_trace_id": "trc_20251219_134431_MM1", "x_senior_partner_id": 5561, "x_trigger": "fall_suspect", "x_priority": "high", "x_message": "Suspicion de chute (mmWave, zone salon, confiance 0.82). Vérification requise.", "x_state": "open", "x_sla_deadline": "2025-12-19T13:59:45Z", "x_assignee_role": "idel", "x_evidence_refs": "iot:mmwave_DS_0042@2025-12-19T13:44:31Z;approval_request:3007" }] ] }, "id": 6 }
Réponse
{ "jsonrpc": "2.0", "id": 6, "result": 8821 }
alert_id=8821
3.3 JSON-RPC Odoo — Création des notifications x_synergia_notification
Notification IDEL WhatsApp
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "x_synergia_notification", "create", [{ "x_trace_id": "trc_20251219_134431_MM1", "x_alert_id": 8821, "x_audience": "idel", "x_channel": "whatsapp", "x_delivery_state": "queued", "x_payload_json": "{\"text\":\"ALERTE chute suspectée (0.82) – Mme Dupont – Salon. Merci de vérifier.\"}" }] ] }, "id": 7 }
Réponse
{ "jsonrpc": "2.0", "id": 7, "result": 9912 }
Notification Famille RCS
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "x_synergia_notification", "create", [{ "x_trace_id": "trc_20251219_134431_MM1", "x_alert_id": 8821, "x_audience": "family", "x_channel": "rcs", "x_delivery_state": "queued", "x_payload_json": "{\"text\":\"Nous avons détecté un événement à domicile (suspicion de chute). Un professionnel est en cours de vérification.\",\"suggestions\":[\"J'AI COMPRIS\",\"RAPPELER\"]}" }] ] }, "id": 8 }
Réponse
{ "jsonrpc": "2.0", "id": 8, "result": 9913 }
3.4 Envoi effectif via connecteur (hors Odoo, mais journalisé dans Odoo)
Exemple de résultat côté connecteur (WhatsApp/RCS), puis on met à jour Odoo :
Update notification IDEL (sent)
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "x_synergia_notification", "write", [[9912], { "x_delivery_state": "sent", "x_delivery_ref": "wa_msg_7F3A", "x_sent_at": "2025-12-19T13:44:48Z" }] ] }, "id": 9 }
Update notification famille (sent)
{ "jsonrpc": "2.0", "method": "call", "params": { "service": "object", "method": "execute_kw", "args": [ "smartco_prod", 7, "SVC_PASSWORD", "x_synergia_notification", "write", [[9913], { "x_delivery_state": "sent", "x_delivery_ref": "rcs_msg_91C2", "x_sent_at": "2025-12-19T13:44:49Z" }] ] }, "id": 10 }
4) Sortie du skill Couche 2 (avec write_set + notification_results)
{ "meta": { "trace_id": "trc_20251219_134431_MM1", "skill": "synergia.odoo.trigger_alert.v2", "version": "2.0.0", "status": "success", "duration_ms": 1240, "warnings": [] }, "result": { "approval_status": "approved", "odoo_ids": { "alert_id": 8821 }, "notification_results": [ { "audience": "idel", "channel": "whatsapp", "status": "sent", "reason": null }, { "audience": "family", "channel": "rcs", "status": "sent", "reason": null } ], "write_set": [ { "model": "x_synergia_approval_request", "id": 3007, "op": "write" }, { "model": "x_synergia_alert", "id": 8821, "op": "create" }, { "model": "x_synergia_notification", "id": 9912, "op": "create" }, { "model": "x_synergia_notification", "id": 9913, "op": "create" }, { "model": "x_synergia_notification", "id": 9912, "op": "write" }, { "model": "x_synergia_notification", "id": 9913, "op": "write" } ] } }
5) Audit Log append-only (exemple complet)
Vous pouvez stocker ceci dans :
- un store immuable externe (preferred), et/ou
- un modèle Odoo x_synergia_audit_log en append-only.
5.1 JSON d’audit (append-only)
{ "trace_id": "trc_20251219_134431_MM1", "timestamp_start": "2025-12-19T13:44:31Z", "timestamp_end": "2025-12-19T13:44:49Z", "tenant_id": "smartco-ch", "channel": "iot", "actor": { "type": "service", "id": "synergia_orchestrator", "role": "system" }, "policy": { "decision": "allow", "approval_mode": "hard", "approval_ref": "approval_request:3007", "checks": [ { "name": "consent.family_notifications", "result": "pass", "ref": "x_synergia_consent:1902" }, { "name": "contact.family_available", "result": "pass" }, { "name": "skill_permission_scope", "result": "pass" } ] }, "intent_chain": [ { "layer": 1, "skill": "synergia.sensor_event_normalizer.v1", "result": { "type": "fall_suspect", "confidence": 0.82, "zone": "living_room" } }, { "layer": 2, "skill": "synergia.odoo.trigger_alert.v2", "result": { "alert_id": 8821 } } ], "evidence_refs": [ "iot:mmwave_DS_0042@2025-12-19T13:44:31Z", "approval_request:3007" ], "odoo_write_set": [ { "model": "x_synergia_alert", "id": 8821, "op": "create" }, { "model": "x_synergia_notification", "id": 9912, "op": "create" }, { "model": "x_synergia_notification", "id": 9913, "op": "create" }, { "model": "x_synergia_notification", "id": 9912, "op": "write" }, { "model": "x_synergia_notification", "id": 9913, "op": "write" } ], "notifications": [ { "audience": "idel", "channel": "whatsapp", "status": "sent", "provider_ref": "wa_msg_7F3A" }, { "audience": "family", "channel": "rcs", "status": "sent", "provider_ref": "rcs_msg_91C2" } ], "security": { "pii_redaction": true, "secrets_exposed": false, "data_minimization": "on" } }
Variante rapide (si vous voulez compléter encore)
Je peux décliner le même bout-en-bout sur :
- “Marquer personne à risque” depuis WebApp Nurse (pop-up) → create_risk_flag (soft/hard)
- “Non-prise médicamenteuse pilulier” → trigger_alert (soft) + message famille avec consentement
Dites-moi simplement quel scénario vous voulez en second, et je conserve exactement le même niveau de détail JSON-RPC.