Skip to Content



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 :

  1. Intent Couche 1 (instrumentation : normalisation / extraction)
  2. Skill Couche 2 (métier : écritures Odoo + notifications)
  3. Payloads JSON-RPC Odoo typiques (authenticate, search_read, create, write)
  4. Résultat notifications
  5. 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.