lumnik. / scénarios Tous les scénarios · English

La sonde du congélateur voit 6,8 °C en pleine nuit

Objectif Être alerté dès qu'une sonde HACCP indépendante voit un congélateur au-dessus de -18 °C, même quand le contrôleur frigorifique ne dit rien.

À l'automne 2026, des contrôleurs frigorifiques de supermarchés ont été modifiés à distance. En France, le 11 octobre, aucune alarme ne s'est déclenchée. Une sonde HACCP indépendante mesure la température, et ChirpStack publie ses relevés sur Kafka.

probes.csv
device,store,cabinet,aisle,products,max_temperature_c,on_call
PROBE-07,Store 17 (Brussels),FROZEN-07,Frozen food,Ice cream and frozen vegetables,-18,Sofia Martin
PROBE-08,Store 17 (Brussels),FROZEN-08,Frozen food,Frozen fish,-18,Sofia Martin
PROBE-09,Store 17 (Brussels),FROZEN-09,Frozen food,Frozen meals,-18,Sofia Martin
evening.jsonl
{"deduplicationId": "00000000-0000-4000-8000-000000000001", "time": "2026-11-01T22:00:00Z", "deviceInfo": {"tenantName": "Store 17", "applicationName": "cold-room-probes", "deviceProfileName": "haccp-probe", "deviceName": "PROBE-07", "devEui": "a84041000181c007", "tags": {}}, "devAddr": "01f2a307", "adr": true, "dr": 5, "fCnt": 101, "fPort": 2, "confirmed": false, "data": "AH0=", "object": {"temperature": -21.4}, "rxInfo": [{"gatewayId": "0016c001ff10a235", "rssi": -71, "snr": 9.5}], "txInfo": {"frequency": 868100000}}
{"deduplicationId": "00000000-0000-4000-8000-000000000002", "time": "2026-11-01T22:00:00Z", "deviceInfo": {"tenantName": "Store 17", "applicationName": "cold-room-probes", "deviceProfileName": "haccp-probe", "deviceName": "PROBE-08", "devEui": "a84041000181c008", "tags": {}}, "devAddr": "01f2a308", "adr": true, "dr": 5, "fCnt": 102, "fPort": 2, "confirmed": false, "data": "AH0=", "object": {"temperature": -22.1}, "rxInfo": [{"gatewayId": "0016c001ff10a235", "rssi": -71, "snr": 9.5}], "txInfo": {"frequency": 868100000}}
{"deduplicationId": "00000000-0000-4000-8000-000000000003", "time": "2026-11-01T22:00:00Z", "deviceInfo": {"tenantName": "Store 17", "applicationName": "cold-room-probes", "deviceProfileName": "haccp-probe", "deviceName": "PROBE-09", "devEui": "a84041000181c009", "tags": {}}, "devAddr": "01f2a309", "adr": true, "dr": 5, "fCnt": 103, "fPort": 2, "confirmed": false, "data": "AH0=", "object": {"temperature": -21.8}, "rxInfo": [{"gatewayId": "0016c001ff10a235", "rssi": -71, "snr": 9.5}], "txInfo": {"frequency": 868100000}}
{"deduplicationId": "00000000-0000-4000-8000-000000000004", "time": "2026-11-01T22:05:00Z", "deviceInfo": {"tenantName": "Store 17", "applicationName": "cold-room-probes", "deviceProfileName": "haccp-probe", "deviceName": "PROBE-09", "devEui": "a84041000181c009", "tags": {}}, "margin": 9, "externalPowerSource": false, "batteryLevel": 86.0}
manifests/30-probe-reading.yaml
kind: Entityname: ScenarioProbeReadingAdescription: "A cold-room probe reading with the cabinet it measures."match: [deduplicationId]scopes: [cold-chain]columns:  deduplicationId: { label: "Uplink" }  device: { label: "Probe" }  measured_at: { label: "Measured at" }  temperature_c: { label: "Temperature (°C)" }  store: { label: "Store" }  cabinet: { label: "Cabinet" }  aisle: { label: "Aisle" }  products: { label: "Products" }  max_temperature_c: { label: "Maximum (°C)" }  on_call: { label: "On call" }sources:  - table: t_scenario_probe_events_a    map:      deduplicationId: deduplicationId      device: device      measured_at: measured_at      temperature_c: temperature_cjoins:  - table: t_scenario_probe_registry_a    from: device    to: device    map:      store: store      cabinet: cabinet      aisle: aisle      products: products      max_temperature_c: max_temperature_c      on_call: on_call
manifests/40-frozen-alert.yaml
apiVersion: apps.lumnik.io/v1kind: Processmetadata:  name: scenario-probe-frozen-aspec:  scope: cold-chain  on:    event: data.row.changed    table: connector.t_scenario_probe_events_a  when:    - temperature_c>-18  emit:    type: decision.frozen-too-warm-a    message: "{device} reads {temperature_c} °C at {measured_at}: a frozen cabinet must stay at -18 °C or below"
night.jsonl
{"deduplicationId": "00000000-0000-4000-8000-000000000005", "time": "2026-11-02T02:30:00Z", "deviceInfo": {"tenantName": "Store 17", "applicationName": "cold-room-probes", "deviceProfileName": "haccp-probe", "deviceName": "PROBE-08", "devEui": "a84041000181c008", "tags": {}}, "devAddr": "01f2a308", "adr": true, "dr": 5, "fCnt": 105, "fPort": 2, "confirmed": false, "data": "AH0=", "object": {"temperature": -21.9}, "rxInfo": [{"gatewayId": "0016c001ff10a235", "rssi": -71, "snr": 9.5}], "txInfo": {"frequency": 868100000}}
{"deduplicationId": "00000000-0000-4000-8000-000000000006", "time": "2026-11-02T02:31:00Z", "deviceInfo": {"tenantName": "Store 17", "applicationName": "cold-room-probes", "deviceProfileName": "haccp-probe", "deviceName": "PROBE-07", "devEui": "a84041000181c007", "tags": {}}, "margin": 9, "externalPowerSource": false, "batteryLevel": 86.0}
{"deduplicationId": "00000000-0000-4000-8000-000000000007", "time": "2026-11-02T02:30:00Z", "deviceInfo": {"tenantName": "Store 17", "applicationName": "cold-room-probes", "deviceProfileName": "haccp-probe", "deviceName": "PROBE-07", "devEui": "a84041000181c007", "tags": {}}, "devAddr": "01f2a307", "adr": true, "dr": 5, "fCnt": 107, "fPort": 2, "confirmed": false, "data": "AH0=", "object": {"temperature": 6.8}, "rxInfo": [{"gatewayId": "0016c001ff10a235", "rssi": -71, "snr": 9.5}], "txInfo": {"frequency": 868100000}}
{"deduplicationId": "00000000-0000-4000-8000-000000000008", "time": "2026-11-02T02:30:00Z", "deviceInfo": {"tenantName": "Store 17", "applicationName": "cold-room-probes", "deviceProfileName": "haccp-probe", "deviceName": "PROBE-09", "devEui": "a84041000181c009", "tags": {}}, "devAddr": "01f2a309", "adr": true, "dr": 5, "fCnt": 108, "fPort": 2, "confirmed": false, "data": "AH0=", "object": {"temperature": -21.6}, "rxInfo": [{"gatewayId": "0016c001ff10a235", "rssi": -71, "snr": 9.5}], "txInfo": {"frequency": 868100000}}
$ lm inbox --process scenario-probe-frozen-c
ID   WHEN      PROCESS                  MESSAGE                                                                                       ENTITY_KEY
276  just now  scenario-probe-frozen-c  PROBE-07 reads 6.8 °C at 2026-11-02T02:30:00Z: a frozen cabinet must stay at -18 °C or below  d05be1cf9429dda18cfe3e63a18c0878c3809e42b022bba79279d5bfa6da0bcc

$ lm entity get ScenarioProbeReadingC --filter device=PROBE-07 -o json
[
  {
    "deduplicationId": "00000000-0000-4000-8000-000000000001",
    "device": "PROBE-07",
    "measured_at": "2026-11-01T22:00:00Z",
    "temperature_c": "-21.4",
    "store": "Store 17 (Brussels)",
    "cabinet": "FROZEN-07",
    "aisle": "Frozen food",
    "products": "Ice cream and frozen vegetables",
    "max_temperature_c": "-18",
    "on_call": "Sofia Martin",
    "_conflicts": "{}"
  },
  {
    "deduplicationId": "00000000-0000-4000-8000-000000000006",
    "device": "PROBE-07",
    "measured_at": "2026-11-02T02:31:00Z",
    "temperature_c": null,
    "store": "Store 17 (Brussels)",
    "cabinet": "FROZEN-07",
    "aisle": "Frozen food",
    "products": "Ice cream and frozen vegetables",
    "max_temperature_c": "-18",
    "on_call": "Sofia Martin",
    "_conflicts": "{}"
  },
  {
    "deduplicationId": "00000000-0000-4000-8000-000000000007",
    "device": "PROBE-07",
    "measured_at": "2026-11-02T02:30:00Z",
    "temperature_c": "6.8",
    "store": "Store 17 (Brussels)",
    "cabinet": "FROZEN-07",
    "aisle": "Frozen food",
    "products": "Ice cream and frozen vegetables",
    "max_temperature_c": "-18",
    "on_call": "Sofia Martin",
    "_conflicts": "{}"
  }
]

Limites

  • lumnik ne voit que ce que la sonde envoie: une sonde, une passerelle ou un ChirpStack muets ne déclenchent aucune décision ici.
  • Le message de décision ne cite que les colonnes de l'événement: le meuble et l'astreinte sont sur la fiche, pas dans le message.
  • Un seul seuil, -18 °C, pour tous les meubles de ce Process.
  • lumnik ne bloque pas une attaque et ne pilote pas le froid.
  • ChirpStack publie tous ses types d'événements sur le même topic: un status arrive comme une ligne sans température.
  • Magasin fictif, données synthétiques.

Chaîne du froid · magasin

À 02 h 30, un congélateur est à 6,8 °C.

Le contrôleur frigorifique ne dit rien.

En France, le 11 octobre 2026, des contrôleurs de supermarchés modifiés à distance n'ont déclenché aucune alarme.

Magasin fictif · données synthétiques

Le problème

L'alarme vit dans le boîtier attaqué.

  • Le contrôleur pilote le froid et ses propres alarmes.
  • Une sonde HACCP mesure à part, par sa propre radio.
  • Sa lecture arrive imbriquée dans un événement ChirpStack.

Rien n'est modifié

La sonde, ChirpStack et Kafka restent en place.

  • Le topic reste celui de ChirpStack.
  • Le registre des meubles reste un fichier CSV.
  • Aucune application à développer.

Ce qu'il a fallu écrire

promote:
  - name: temperature_c
    path: $.object.temperature

Une ligne lit la température dans l'événement.

Le scénario complet tient en 4 fichiers de déclaration, 76 lignes.

Ce que reçoit l'équipe

Une décision, pour la seule sonde au-dessus de -18 °C.

Boîte de décisions
PROBE-07 reads 6.8 °C at 2026-11-02T02:30:00Z: a frozen cabinet must stay at -18 °C or below

Message réel, rejeu du 3 novembre 2026.

La fiche dit le reste

Le meuble, le rayon et l'astreinte.

  • FROZEN-07, rayon surgelés.
  • Glaces et légumes surgelés.
  • Astreinte: Sofia Martin.

La limite

lumnik ne voit que ce que la sonde envoie.

  • Une sonde muette ne déclenche rien ici.
  • lumnik ne bloque pas l'attaque.
  • Il ne pilote pas le froid.

Essayer

Le scénario se rejoue de bout en bout.

ChirpStack, Kafka, CSV, entité, Process: chaque étape est publiée avec sa sortie.

Ce scénario demande lumnik Pro.

Voir l'offre →