Tutoriel détecteur LD2450 esphome

Bonjour, je ne comprends pas j’ai fait un copié collé du code et j’ai une erreur quand je veux l’installer.
Img1
et le message d’erreur :
Img2
Je ne suis pas super calé en YAML.

Regarder en line 3 et 71 surement un soucis d’indentation à la 71 je pense

Salut,
problème d’indexion.

code original:

substitutions:
  devicename: hpsz-room

ton code:

substitutions:
 devicename: hpsz-room #un espace en moins au début de la ligne.

Ligne 71 , ca doit être pareil, mais je connais pas la ligne.

Merci pour votre aide, ça marche, reste plus qu’a tester.

1 « J'aime »

je teste le code depuis hier soir, a première vue je ne constate aucune latence supplémentaire mais a voir a l’usage.

cependant je sèche sur un point, mon capteur se trouve dans ma cuisine ( 5.5x5.5m) mais dans un angle, sur ma capture le capteur est a cette endroit la mais balaye en diagonal le problème est que je suis limité a 4000 sur l’axe des X

Comment puis-je faire

As-tu vérifié que ta position cible (target) était bien détecté à 5,5m ? Si c’est le cas je modifierait le max X pour les zones. J’ai déjà commencé à modifier les max Y (chez moi) pour allez jusqu’à 8m. (Je vais faire une MAJ du code dans quelques jours).

Mais si tu peux orienter ton capteur à 45° vers le coin opposé de la pièce le problème sera résolu

oui la position cible fonctionne et le capteur donne a 45°. en réalité c’est juste pour que la vue plotly corresponde à la pièce. Sinon rien qu’en se positionnant là où on veux avec les coordonnées on le fait sans problème mais dans ce cas la vue plotly ne ressemblera pas à la pièce

faut aussi modifier le Y il est limité a 6000

Voila, 'j’ai modifier toutes les valeurs et cela fonctionne

Tu cherches le texte ci-dessous et tu remplaces 6000 par 8000 et tu recommence pour chaque zone.

  - platform: template
    name: Zone1 Y-End
    id: zone1_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 6000

Je suis surpris que tu sois détecté à 5,5m. Excuse mon scepticisme mais la doc dis que tu n’es pas sensé être détecté dans la zone rouge (Voir le graphique 7 à la page 10 du datasheet)

Je suis pas contre une capture d’écran
dc302bc8f70247d9b5447bece7db06277d61b7f7

c’est modifier, tu va mieux comprendre, n’oublie pas que mon capteur est a 45°.
Sur ton dessin c’est comme si mon capteur était dans le coin supérieur gauche mais de face

ps: merci pour ton code :+1:

Ok je comprends !
Du coup, pour la prochaine MAJ je mets quelle maximum pour X ?

j’aurais tendance dire que pour satisfaire le plus grand nombre de personnes et de configuration met -8000 et 8000

bon rectification, cela ne va pas comme j’ai fait. ma piece correspond bien a la réalité mais pour lui le capteur est droit et non a 45°, du coup a gauche sur l’axe X0 je suis hors zone.

Alors comment on fait

Pour le moment laisse le capteur à 45°, on va le faire en deux temps :

  • tu modifies temporairement (c’est juste une précaution) le graph en +/- 8000 en X et Y
  xaxis:
    dtick: 1000
    gridcolor: RGBA(200,200,200,0.15)
    zerolinecolor: RGBA(200,200,200,0.15)
    type: number
    fixedrange: true
    range:
      - -8000
      - 8000
  yaxis:
    dtick: 1000
    gridcolor: RGBA(200,200,200,0.15)
    zerolinecolor: RGBA(200,200,200,0.15)
    scaleanchor: x
    scaleratio: 1
    fixedrange: true
    range:
      - -8000
      - 8000
  • Tu te positionnes aux quatre coins de la pièce et tu relèves les coordonnées de la cible (pas du graph)
- Fond gauche :
  - X =
  - Y =
- Fond droit:
  - X =
  - Y =
- Devant gauche :
  - X =
  - Y =
- Devant droit:
  - X =
  - Y =

Tu postes le résultat et on analyse

alors on dit que le fond gauche et a l’endroit ou la capteur se trouve. Les valeurs sont approximatives car elle fluctuent a la prise de mesure.

- Fond gauche :
  - X =0
  - Y =580
- Fond droit:
  - X =2800
  - Y =2700
- Devant gauche :
  - X =-2600
  - Y =2200
- Devant droit:
  - X =590
  - Y =5300

C’est très intéressant !
Concernant l’augmentation des maxi pour l’axe X, je constate que c’est inutile et que la doc avait raison.
Par contre, dès que le capteur n’est pas parallèle au mur, si l’on veut fonctionner par zone, il va falloir appliquer un angle à celles-ci.

Je ne suis pas sûr que je vais y arriver, mais je vais tenter quelque chose. Je t’enverrai le code en message privé et tu me diras si ça fonctionne, si ça te va bien sûr.

1 « J'aime »

Bon j’ai retourné le problème dans à peu près tous les sens et je suis arrivé à la conclusion que pour prendre en compte l’angle du capteur par rapport au mur, je ne peux plus continuer à prendre les coordonnées X et Y de fin de zone. Je dois prendre les coordonnées X et Y du début puis la longueur et la largeur de la zone. Avec ces quatre informations et celle de l’angle entre le mur et le capteur, je peux déterminer la zone.
Il me faudrait donc rajouter un nouveau champ Side wall angle pour l’angle et renommer tous Zone ? X-End en Zone ? Width et les Zone ? Y-End en Zone ? Height
Sans titre-1

newplot

Qui serait intéressé par cette modification

  • Oui, ça m’intéresse
  • Non merci
0 votant
1 « J'aime »

c’est a sacré boulot cette modif, forcement je suis intéressé :wink: :wink:

Je ne dis pas que je vais réussir. Les maths sont du niveaux du niveau collège mais le manque de pratique et aussi d’assiduité à l’époque :roll_eyes: se font sentir.
J’ai galéré avec les sinus et cosinus pour dessiner la forme, et c’était la partie facile. Je dois maintenant faire des calcules de vérification d’inclusions (le point est-il dans la zone) et là j’ai le sentiment d’avoir raté le cour…
La théorie est passionnante, mais j’ai un peu peur pour la pratique.

1 « J'aime »

Salut Selecus
Merci ton code que j’utilise depuis une semaine avec succès, comment fait tu pour avoir le graph ploty associé a un second LD2450?
Regard
Victor

Salut Victor,
Comme tu as pu le comprendre je ne suis pas à l’origine de ce code mais je l’ai modifié.
J’ai remarqué comme toi qu’il n’était pas franchement très pratique quand tu as plus d’un capteur à utiliser. Je travaille sur une nouvelle version qui facilitera le déploiement de plusieurs capteurs dans différentes pièces.

Je vais partir du principe que tu as déjà deux capteurs avec deux ESP32 (ou équivalent) en ta possession.
Lorsque tu es intègres à ESP Home le fichier YAML, home assistant ajoute un nouvel appareil. Ce dernier est constitué de plusieurs entités.
Chaque entité prend pour ID le titre de son champ. Par exemple Target1 X devient target1_x. Mais lorsque tu rajoutes un deuxième appareil, bien que les titres vont rester identiques l’ID, lui, ne peut pas être identique. Alors plutôt que de te poser la question comment tu veux le renommer, Home assistant ajoute un numéro à la fin de l’ID. Et donc, pour ton deuxième appareil Target1 X devient target1_x_2.

C’est ça que tu dois chercher dans le graphe et remplacer une fois que tu as trouvé l’ancien nom tu le remplaces par le nouveau
Donc

entities:
  - entity: ''
    name: Person1
    show_value: true
    unit_of_measurement: mm
    marker:
      size: 12
    line:
      shape: spline
      width: 5
    x:
      - $ex hass.states["sensor.target1_x"].state
    'y':
      - $ex hass.states["sensor.target1_y"].state

va devenir

entities:
  - entity: ''
    name: Person1
    show_value: true
    unit_of_measurement: mm
    marker:
      size: 12
    line:
      shape: spline
      width: 5
    x:
      - $ex hass.states["sensor.target1_x_2"].state
    'y':
      - $ex hass.states["sensor.target1_y_2].state

C’est en faisant ces remplacements que tu pourras utiliser plus d’un graphe

1 « J'aime »

Voici la nouvelle mise à jour !
Merci à @thebadboy et @jerome6994 de m’avoir aidé à tester le code avant de le diffuser

Plusieurs changements ont été faits :

  • On peu définir un angle si on n’a pas posé le capteur à plat contre un mur
  • La longueur maximum de détection est passé de 6 à 8 mètres et peut maintenant commencer à - 0,5 mètres
  • Le positionnement des zones ne fonctionnent plus avec le même système. Avant il y avait une zone X et Y minimum et une zone X et Y maximum. Maintenant il y a juste une position X et Y avec une longueur et une largeur
  • Chaque entité du capteur possède maintenant le nom de la pièce dans laquelle on l’intègre. Il faut choisir le nom de cette pièce manuellement. (entity_name: « Room »)
  • Le graphique est devenu plus interactif. Que la cible soit affichée soit ou non, quand quelqu’un est dans une zone, elle devient plus colorée.
  • Pour supporter tous ces changements, le code a été à écrire en profondeur avec des ajouts, des suppressions et des modifications. Il est maintenant nécessaire d’avoir un fichier supplémentaire à côté de notre fichier principal YAML. C’est une librairie de code.

Screenshot_20240420-162508_Zoom
Screenshot_20240420-155111_Zoom

Le graph

type: custom:plotly-graph
title: "Room:\_LD2450"
refresh_interval: 2
hours_to_show: current_day
ha_theme: true
layout:
  legend:
    'y': 8000
    orientation: h
  autosize: true
  margin:
    autoexpand: true
    l: 50
    r: 20
    t: 20
    b: 40
  showlegend: true
  xaxis:
    dtick: 1000
    gridcolor: RGBA(200,200,200,0.15)
    zerolinecolor: RGBA(200,200,200,0.15)
    type: number
    fixedrange: true
    range:
      - -4000
      - 4000
  yaxis:
    dtick: 1000
    gridcolor: RGBA(200,200,200,0.15)
    zerolinecolor: RGBA(200,200,200,0.15)
    scaleanchor: x
    scaleratio: 1
    fixedrange: true
    range:
      - 8000
      - 0
entities:
  - entity: ''
    name: Person1
    show_value: true
    unit_of_measurement: mm
    marker:
      size: 12
    line:
      shape: spline
      width: 5
    x:
      - $ex hass.states["sensor.room_target1_x"].state
    'y':
      - $ex hass.states["sensor.room_target1_y"].state
  - entity: ''
    name: Person2
    show_value: true
    unit_of_measurement: mm
    marker:
      size: 12
    line:
      shape: spline
      width: 5
    x:
      - $ex hass.states["sensor.room_target2_x"].state
    'y':
      - $ex hass.states["sensor.room_target2_y"].state
  - entity: ''
    name: Person3
    show_value: true
    unit_of_measurement: mm
    marker:
      size: 12
    line:
      shape: spline
      width: 5
    x:
      - $ex hass.states["sensor.room_target3_x"].state
    'y':
      - $ex hass.states["sensor.room_target3_y"].state
  - entity: ''
    name: Zone1
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["binary_sensor.room_zone1_presence"].state == "on" ?
      "RGBA(0,250,0,0.4)" : "RGBA(0,250,0,0.1)"
    line:
      color: RGBA(0,250,0,0.9)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.room_zone1_x"].state*1
      - >-
        $ex hass.states["number.room_zone1_x"].state*1 -
        hass.states["number.room_zone1_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone1_x"].state*1 -
        hass.states["number.room_zone1_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone1_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone1_x"].state*1 +
        hass.states["number.room_zone1_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zone1_x"].state*1
    'y':
      - $ex hass.states["number.room_zone1_y"].state*1
      - >-
        $ex hass.states["number.room_zone1_y"].state*1 +
        hass.states["number.room_zone1_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone1_y"].state*1 +
        hass.states["number.room_zone1_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone1_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone1_y"].state*1 +
        hass.states["number.room_zone1_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zone1_y"].state*1
  - entity: ''
    name: Zone2
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["binary_sensor.room_zone2_presence"].state == "on" ?
      "RGBA(250,110,0,0.4)" : "RGBA(250,110,0,0.1)"
    line:
      color: RGBA(250,110,0,0.9)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.room_zone2_x"].state*1
      - >-
        $ex hass.states["number.room_zone2_x"].state*1 -
        hass.states["number.room_zone2_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone2_x"].state*1 -
        hass.states["number.room_zone2_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone2_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone2_x"].state*1 +
        hass.states["number.room_zone2_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zone2_x"].state*1
    'y':
      - $ex hass.states["number.room_zone2_y"].state*1
      - >-
        $ex hass.states["number.room_zone2_y"].state*1 +
        hass.states["number.room_zone2_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone2_y"].state*1 +
        hass.states["number.room_zone2_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone2_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone2_y"].state*1 +
        hass.states["number.room_zone2_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zone2_y"].state*1
  - entity: ''
    name: Zone3
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["binary_sensor.room_zone3_presence"].state == "on" ?
      "RGBA(0,0,250,0.4)" : "RGBA(0,0,250,0.1)"
    line:
      color: RGBA(0,0,250,0.9)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.room_zone3_x"].state*1
      - >-
        $ex hass.states["number.room_zone3_x"].state*1 -
        hass.states["number.room_zone3_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone3_x"].state*1 -
        hass.states["number.room_zone3_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone3_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone3_x"].state*1 +
        hass.states["number.room_zone3_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zone3_x"].state*1
    'y':
      - $ex hass.states["number.room_zone3_y"].state*1
      - >-
        $ex hass.states["number.room_zone3_y"].state*1 +
        hass.states["number.room_zone3_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone3_y"].state*1 +
        hass.states["number.room_zone3_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone3_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone3_y"].state*1 +
        hass.states["number.room_zone3_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zone3_y"].state*1
  - entity: ''
    name: Zone4
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["binary_sensor.room_zone4_presence"].state == "on" ?
      "RGBA(250,210,0,0.4)" : "RGBA(250,210,0,0.1)"
    line:
      color: RGBA(250,210,0,0.9)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.room_zone4_x"].state*1
      - >-
        $ex hass.states["number.room_zone4_x"].state*1 -
        hass.states["number.room_zone4_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone4_x"].state*1 -
        hass.states["number.room_zone4_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone4_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone4_x"].state*1 +
        hass.states["number.room_zone4_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zone4_x"].state*1
    'y':
      - $ex hass.states["number.room_zone4_y"].state*1
      - >-
        $ex hass.states["number.room_zone4_y"].state*1 +
        hass.states["number.room_zone4_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone4_y"].state*1 +
        hass.states["number.room_zone4_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone4_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone4_y"].state*1 +
        hass.states["number.room_zone4_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zone4_y"].state*1
  - entity: ''
    name: Zone5
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["binary_sensor.room_zone5_presence"].state == "on" ?
      "RGBA(0,150,0,0.4)" : "RGBA(0,150,0,0.1)"
    line:
      color: RGBA(0,150,0,0.9)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.room_zone5_x"].state*1
      - >-
        $ex hass.states["number.room_zone5_x"].state*1 -
        hass.states["number.room_zone5_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone5_x"].state*1 -
        hass.states["number.room_zone5_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone5_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone5_x"].state*1 +
        hass.states["number.room_zone5_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zone5_x"].state*1
    'y':
      - $ex hass.states["number.room_zone5_y"].state*1
      - >-
        $ex hass.states["number.room_zone5_y"].state*1 +
        hass.states["number.room_zone5_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone5_y"].state*1 +
        hass.states["number.room_zone5_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone5_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone5_y"].state*1 +
        hass.states["number.room_zone5_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zone5_y"].state*1
  - entity: ''
    name: Zone6
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["binary_sensor.room_zone6_presence"].state == "on" ?
      "RGBA(250,50,130,0.4)" : "RGBA(250,50,130,0.1)"
    line:
      color: RGBA(250,50,130,0.9)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.room_zone6_x"].state*1
      - >-
        $ex hass.states["number.room_zone6_x"].state*1 -
        hass.states["number.room_zone6_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone6_x"].state*1 -
        hass.states["number.room_zone6_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone6_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone6_x"].state*1 +
        hass.states["number.room_zone6_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zone6_x"].state*1
    'y':
      - $ex hass.states["number.room_zone6_y"].state*1
      - >-
        $ex hass.states["number.room_zone6_y"].state*1 +
        hass.states["number.room_zone6_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone6_y"].state*1 +
        hass.states["number.room_zone6_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zone6_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zone6_y"].state*1 +
        hass.states["number.room_zone6_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zone6_y"].state*1
  - entity: ''
    name: Zout1
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["switch.room_zout1_enable"].state == "on" ?
      hass.states["binary_sensor.room_zout1_presence"].state == "on" ?
      "RGBA(250,0,0,0.4)" : "RGBA(250,0,0,0.2)" : "RGBA(0,0,0,0)"
    line:
      color: >-
        $ex hass.states["switch.room_zout1_enable"].state == "on" ?
        "RGBA(0,150,0,0.9)" : "RGBA(0,0,0,0)" 
      width: 3
      dash: dash
    x:
      - $ex hass.states["number.room_zout1_x"].state*1
      - >-
        $ex hass.states["number.room_zout1_x"].state*1 -
        hass.states["number.room_zout1_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout1_x"].state*1 -
        hass.states["number.room_zout1_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zout1_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout1_x"].state*1 +
        hass.states["number.room_zout1_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zout1_x"].state*1
    'y':
      - $ex hass.states["number.room_zout1_y"].state*1
      - >-
        $ex hass.states["number.room_zout1_y"].state*1 +
        hass.states["number.room_zout1_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout1_y"].state*1 +
        hass.states["number.room_zout1_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zout1_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout1_y"].state*1 +
        hass.states["number.room_zout1_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zout1_y"].state*1
  - entity: ''
    name: Zout2
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["switch.room_zout2_enable"].state == "on" ?
      hass.states["binary_sensor.room_zout2_presence"].state == "on" ?
      "RGBA(250,0,0,0.4)" : "RGBA(250,0,0,0.2)" : "RGBA(0,0,0,0)"
    line:
      color: >-
        $ex hass.states["switch.room_zout2_enable"].state == "on" ?
        "RGBA(250,110,0,0.9)" : "RGBA(0,0,0,0)" 
      width: 3
      dash: dash
    x:
      - $ex hass.states["number.room_zout2_x"].state*1
      - >-
        $ex hass.states["number.room_zout2_x"].state*1 -
        hass.states["number.room_zout2_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout2_x"].state*1 -
        hass.states["number.room_zout2_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zout2_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout2_x"].state*1 +
        hass.states["number.room_zout2_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zout2_x"].state*1
    'y':
      - $ex hass.states["number.room_zout2_y"].state*1
      - >-
        $ex hass.states["number.room_zout2_y"].state*1 +
        hass.states["number.room_zout2_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout2_y"].state*1 +
        hass.states["number.room_zout2_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zout2_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout2_y"].state*1 +
        hass.states["number.room_zout2_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zout2_y"].state*1
  - entity: ''
    name: Zout3
    mode: lines
    fill: toself
    fillcolor: >-
      $ex hass.states["switch.room_zout3_enable"].state == "on" ?
      hass.states["binary_sensor.room_zout3_presence"].state == "on" ?
      "RGBA(250,0,0,0.4)" : "RGBA(250,0,0,0.2)" : "RGBA(0,0,0,0)"
    line:
      color: >-
        $ex hass.states["switch.room_zout3_enable"].state == "on" ?
        "RGBA(0,0,250,0.9)" : "RGBA(0,0,0,0)" 
      width: 3
      dash: dash
    x:
      - $ex hass.states["number.room_zout3_x"].state*1
      - >-
        $ex hass.states["number.room_zout3_x"].state*1 -
        hass.states["number.room_zout3_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout3_x"].state*1 -
        hass.states["number.room_zout3_width"].state *
        Math.cos((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zout3_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout3_x"].state*1 +
        hass.states["number.room_zout3_height"].state *
        Math.cos((hass.states["number.room_angle"].state*1-90)*3.1415/180)
      - $ex hass.states["number.room_zout3_x"].state*1
    'y':
      - $ex hass.states["number.room_zout3_y"].state*1
      - >-
        $ex hass.states["number.room_zout3_y"].state*1 +
        hass.states["number.room_zout3_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout3_y"].state*1 +
        hass.states["number.room_zout3_width"].state *
        Math.sin((hass.states["number.room_angle"].state)*3.1415/180) +
        hass.states["number.room_zout3_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - >-
        $ex hass.states["number.room_zout3_y"].state*1 +
        hass.states["number.room_zout3_height"].state *
        Math.sin((hass.states["number.room_angle"].state*1+90)*3.1415/180)
      - $ex hass.states["number.room_zout3_y"].state*1
  - entity: ''
    name: Coverage
    mode: lines
    line:
      width: 1
      color: rgba(100, 100, 100, .6)
      dash: dot
    x:
      - 0
      - 1000
      - 3700
      - 3500
      - 2000
      - 0
      - -2000
      - -3500
      - -3700
      - -1000
      - 0
    'y':
      - 0
      - 500
      - 3700
      - 6000
      - 7400
      - 8000
      - 7400
      - 6000
      - 3700
      - 500
      - 0
raw_plotly_config: true

Le YAML

#https://github.com/53l3cu5/ESP32_LD2450
#https://docs.screek.io/2a
substitutions:
  devicename: hpsz-room
  entity_name: "Room"

  
#ESP32/LD2450 : Human Presence Sensor by Zone
esphome:
  name: $devicename
  comment: Human Presence Sensor by Zone (ESP32/LD2450)
  #name_add_mac_suffix: True
  platformio_options:
    board_build.flash_mode: dio
    # board_build.f_cpu: 80000000L
  project: 
    name: 53l3cu5.Human_Presence_Sensor_by_Zone
    version: 2.O
  on_boot:
    - priority: -200
      then:
        lambda: |-
          id(zone1_target_exist).publish_state(false);
          id(zone2_target_exist).publish_state(false);
          id(zone3_target_exist).publish_state(false);
          id(zone4_target_exist).publish_state(false);
          id(zone5_target_exist).publish_state(false);
          id(zone6_target_exist).publish_state(false);
          id(zone_ex1_target_exist).publish_state(false);
          id(zone_ex2_target_exist).publish_state(false);
          id(zone_ex3_target_exist).publish_state(false);
  includes:
    - zone.h
    
preferences:
    flash_write_interval: 5s
  

esp32:
  board: esp32dev

globals:
  - id: last_update_ld2450
    type: unsigned long
    restore_value: no
    initial_value: '0'
  - id: init_zone_publish
    type: bool
    restore_value: no
    initial_value: "false"

improv_serial:
  
logger:

debug:
  update_interval: 30s

api:
  encryption:
    # use your own encryption key plz.
    # https://esphome.io/components/api.html?#configuration-variables
    key: "Xd8KczJ8qKVYtAjyh3zVPar7zF8pkmZeOpnHSfjOqwA="
    
ota:
  # use your own ota password plz.
  password: !secret ota_password
  safe_mode: False

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: LIGHT
  reboot_timeout: 10min
  ap:
    ssid: "${devicename} Hotspot"
    password: !secret hotspot_password

captive_portal:

web_server:
  port: 80
  
text_sensor:
  - platform: debug
    reset_reason:
      name: "${entity_name} ESP Reset Reason"
      icon: mdi:anchor
      disabled_by_default: True
  - platform: wifi_info
    ip_address:
      name: ${entity_name} ESP IP Address
      entity_category: "diagnostic"
      disabled_by_default: True
      icon: mdi:ip-network
    mac_address:
      name: ${entity_name} ESP MAC
      entity_category: "diagnostic"
      icon: mdi:ip-network
      disabled_by_default: True
  - platform: template
    name: "${entity_name} Zone1 Info"
    id: tips_zone1_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Zone2 Info"
    id: tips_zone2_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Zone3 Info"
    id: tips_zone3_conf
    icon: mdi:information-outline
    entity_category: config
  - platform: template
    name: "${entity_name} Zone4 Info"
    id: tips_zone4_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Zone5 Info"
    id: tips_zone5_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Zone6 Info"
    id: tips_zone6_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Zout1 Info"
    id: tips_zone_ex1_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Zone Exclusion 1" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Zout2 Info"
    id: tips_zone_ex2_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Zone Exclusion 2" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Zout3 Info"
    id: tips_zone_ex3_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Zone Exclusion 3" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Any-Presence Info"
    id: tips_any_presence_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Any Presence Config" };
    update_interval: 1000s
  - platform: template
    name: "${entity_name} Target1 Direction"
    id: target1_direction
    icon: mdi:directions
  - platform: template
    name: "${entity_name} Target2 Direction"
    id: target2_direction
    icon: mdi:directions
  - platform: template
    name: "${entity_name} Target3 Direction"
    id: target3_direction
    icon: mdi:directions
  - platform: template
    name: "${entity_name} Target1 Position"
    id: target1_position
    icon: mdi:directions
  - platform: template
    name: "${entity_name} Target2 Position"
    id: target2_position
    icon: mdi:directions
  - platform: template
    name: "${entity_name} Target3 Position"
    id: target3_position
    icon: mdi:directions

number:
  - platform: template
    name: "${entity_name} Angle"
    id: wall_angle
    min_value: 0
    max_value: 90
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: °
    icon: mdi:angle-acute
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "${entity_name} Any Presence Timeout"
    id: any_presence_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "${entity_name} Zone1 Timeout"
    id: zone1_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "${entity_name} Zone2 Timeout"
    id: zone2_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "${entity_name} Zone3 Timeout"
    id: zone3_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "${entity_name} Zone4 Timeout"
    id: zone4_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "${entity_name} Zone5 Timeout"
    id: zone5_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "${entity_name} Zone6 Timeout"
    id: zone6_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
    
  # Zone 1
  - platform: template
    name: ${entity_name} Zone1 X
    id: zone1_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone1_valide
  - platform: template
    name: ${entity_name} Zone1 Y
    id: zone1_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone1_valide
  - platform: template
    name: ${entity_name} Zone1 Height
    id: zone1_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone1_valide
  - platform: template
    name: ${entity_name} Zone1 Width
    id: zone1_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone1_valide
    
  # Zone 2
  - platform: template
    name: ${entity_name} Zone2 X
    id: zone2_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone2_valide
  - platform: template
    name: ${entity_name} Zone2 Y
    id: zone2_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone2_valide
  - platform: template
    name: ${entity_name} Zone2 Height
    id: zone2_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone2_valide
  - platform: template
    name: ${entity_name} Zone2 Width
    id: zone2_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone2_valide
    
  # Zone 3
  - platform: template
    name: ${entity_name} Zone3 X
    id: zone3_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone3_valide
  - platform: template
    name: ${entity_name} Zone3 Y
    id: zone3_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone3_valide
  - platform: template
    name: ${entity_name} Zone3 Height
    id: zone3_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone3_valide
  - platform: template
    name: ${entity_name} Zone3 Width
    id: zone3_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone3_valide
    
  # Zone 4
  - platform: template
    name: ${entity_name} Zone4 X
    id: zone4_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone4_valide
  - platform: template
    name: ${entity_name} Zone4 Y
    id: zone4_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone4_valide
  - platform: template
    name: ${entity_name} Zone4 Height
    id: zone4_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone4_valide
  - platform: template
    name: ${entity_name} Zone4 Width
    id: zone4_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone4_valide
    
  # Zone 5
  - platform: template
    name: ${entity_name} Zone5 X
    id: zone5_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone5_valide
  - platform: template
    name: ${entity_name} Zone5 Y
    id: zone5_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone5_valide
  - platform: template
    name: ${entity_name} Zone5 Height
    id: zone5_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone5_valide
  - platform: template
    name: ${entity_name} Zone5 Width
    id: zone5_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone5_valide
    
  # Zone 6
  - platform: template
    name: ${entity_name} Zone6 X
    id: zone6_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone6_valide
  - platform: template
    name: ${entity_name} Zone6 Y
    id: zone6_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone6_valide
  - platform: template
    name: ${entity_name} Zone6 Height
    id: zone6_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone6_valide
  - platform: template
    name: ${entity_name} Zone6 Width
    id: zone6_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone6_valide
    
  # Zout1
  - platform: template
    name: ${entity_name} Zout1 X
    id: zone_ex1_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout1_valide
  - platform: template
    name: ${entity_name} Zout1 Y
    id: zone_ex1_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout1_valide
  - platform: template
    name: ${entity_name} Zout1 Height
    id: zone_ex1_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout1_valide
  - platform: template
    name: ${entity_name} Zout1 Width
    id: zone_ex1_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout1_valide
    
  # Zout2
  - platform: template
    name: ${entity_name} Zout2 X
    id: zone_ex2_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout2_valide
  - platform: template
    name: ${entity_name} Zout2 Y
    id: zone_ex2_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout2_valide
  - platform: template
    name: ${entity_name} Zout2 Height
    id: zone_ex2_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout2_valide
  - platform: template
    name: ${entity_name} Zout2 Width
    id: zone_ex2_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout2_valide
    
  # Zout3
  - platform: template
    name: ${entity_name} Zout3 X
    id: zone_ex3_x
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout3_valide
  - platform: template
    name: ${entity_name} Zout3 Y
    id: zone_ex3_y
    mode: box
    min_value: -500
    max_value: 8000
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout3_valide
  - platform: template
    name: ${entity_name} Zout3 Height
    id: zone_ex3_height
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 8000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout3_valide
  - platform: template
    name: ${entity_name} Zout3 Width
    id: zone_ex3_width
    mode: box
    min_value: 0
    max_value: 8000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout3_valide

binary_sensor:
  - platform: status
    name: ${entity_name} Online
    id: ink_ha_connected
  - platform: template
    name: "${entity_name} Any Presence"
    id: any_target_exist
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish) || !id(zone_fn_enable).state) {
            return 0;
          };
          return id(any_presence_timeout).state * 1000.0;
  - platform: template
    name: "${entity_name} Zone1 Presence"
    id: zone1_target_exist
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish) || !id(zone_fn_enable).state) {
            return 0;
          }
          return id(zone1_timeout).state * 1000.0;
  - platform: template
    name: "${entity_name} Zone2 Presence"
    id: zone2_target_exist
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish) || !id(zone_fn_enable).state) {
            return 0;
          }
          return id(zone2_timeout).state * 1000.0;
  - platform: template
    name: "${entity_name} Zone3 Presence"
    id: zone3_target_exist
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish) || !id(zone_fn_enable).state) {
            return 0;
          }
          return id(zone3_timeout).state * 1000.0;
  - platform: template
    name: "${entity_name} Zone4 Presence"
    id: zone4_target_exist
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish) || !id(zone_fn_enable).state) {
            return 0;
          }
          return id(zone4_timeout).state * 1000.0;
  - platform: template
    name: "${entity_name} Zone5 Presence"
    id: zone5_target_exist
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish) || !id(zone_fn_enable).state) {
            return 0;
          }
          return id(zone5_timeout).state * 1000.0;
  - platform: template
    name: "${entity_name} Zone6 Presence"
    id: zone6_target_exist
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish) || !id(zone_fn_enable).state) {
            return 0;
          }
          return id(zone6_timeout).state * 1000.0;
  - platform: template
    name: "${entity_name} Zout1 Presence"
    id: zone_ex1_target_exist
    icon: mdi:account-multiple-remove
    device_class: occupancy
  - platform: template
    name: "${entity_name} Zout2 Presence"
    id: zone_ex2_target_exist
    icon: mdi:account-multiple-remove
    device_class: occupancy
  - platform: template
    name: "${entity_name} Zout3 Presence"
    id: zone_ex3_target_exist
    icon: mdi:account-multiple-remove
    device_class: occupancy

script:
  - id: check_zone1_valide
    then:
      - lambda: |-
          if (id(zone1_x).state == 0 && id(zone1_width).state == 0 && id(zone1_y).state == 0 && id(zone1_height).state == 0){
            id(tips_zone1_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone1_width).state;
          int y_size = id(zone1_height).state;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone1_conf).publish_state(combined);
  - id: check_zone2_valide
    then:
      - lambda: |-
          if (id(zone2_x).state == 0 && id(zone2_width).state == 0 && id(zone2_y).state == 0 && id(zone2_height).state == 0){
            id(tips_zone2_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone2_width).state;
          int y_size = id(zone2_height).state;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone2_conf).publish_state(combined);
  - id: check_zone3_valide
    then:
      - lambda: |-
          if (id(zone3_x).state == 0 && id(zone3_width).state == 0 && id(zone3_y).state == 0 && id(zone3_height).state == 0){
            id(tips_zone3_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone3_width).state;
          int y_size = id(zone3_height).state ;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone3_conf).publish_state(combined);
  - id: check_zone4_valide
    then:
      - lambda: |-
          if (id(zone4_x).state == 0 && id(zone4_width).state == 0 && id(zone4_y).state == 0 && id(zone4_height).state == 0){
            id(tips_zone4_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone4_width).state;
          int y_size = id(zone4_height).state;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone4_conf).publish_state(combined);
  - id: check_zone5_valide
    then:
      - lambda: |-
          if (id(zone5_x).state == 0 && id(zone5_width).state == 0 && id(zone5_y).state == 0 && id(zone5_height).state == 0){
            id(tips_zone5_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone5_width).state;
          int y_size = id(zone5_height).state;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone5_conf).publish_state(combined);
  - id: check_zone6_valide
    then:
      - lambda: |-
          if (id(zone6_x).state == 0 && id(zone6_width).state == 0 && id(zone6_y).state == 0 && id(zone6_height).state == 0){
            id(tips_zone6_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone6_width).state;
          int y_size = id(zone6_height).state;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone6_conf).publish_state(combined);
  - id: check_zout1_valide
    then:
      - lambda: |-
          id(tips_zone_ex1_conf).publish_state("Zone Exclusion 1");

  - id: check_zout2_valide
    then:
      - lambda: |-
          id(tips_zone_ex2_conf).publish_state("Zone Exclusion 2");

  - id: check_zout3_valide
    then:
      - lambda: |-
          id(tips_zone_ex3_conf).publish_state("Zone Exclusion 3");

sensor:
  - platform: uptime
    name: ${entity_name} ESP Uptime
    id: sys_uptime
    update_interval: 60s
    disabled_by_default: True
  - platform: wifi_signal 
    name: ${entity_name} RSSI
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"
    
#-------------------------------------#
  # Advanced radar data
  - platform: template
    name: "${entity_name} All Target Counts"
    id: all_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zone1 Target Counts"
    id: zone1_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zone2 Target Counts"
    id: zone2_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zone3 Target Counts"
    id: zone3_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zone4 Target Counts"
    id: zone4_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zone5 Target Counts"
    id: zone5_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zone6 Target Counts"
    id: zone6_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zout1 Target Counts"
    id: zone_ex1_target_count
    accuracy_decimals: 0
    icon: mdi:account-multiple-minus-outline
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zout2 Target Counts"
    id: zone_ex2_target_count
    accuracy_decimals: 0
    icon: mdi:account-multiple-minus-outline
    unit_of_measurement: "targets"
  - platform: template
    name: "${entity_name} Zout3 Target Counts"
    id: zone_ex3_target_count
    accuracy_decimals: 0
    icon: mdi:account-multiple-minus-outline
    unit_of_measurement: "targets"

  # Target 1
  - platform: template
    name: "${entity_name} Target1 X"
    id: target1_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    icon: mdi:focus-field-horizontal
    device_class: distance
  - platform: template
    name: "${entity_name} Target1 Y"
    id: target1_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
    icon: mdi:focus-field-vertical
  - platform: template
    name: "${entity_name} Target1 Speed"
    id: target1_speed
    accuracy_decimals: 2
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
  - platform: template
    name: "${entity_name} Target1 Resolution"
    id: target1_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  
  # Target 2
  - platform: template
    name: "${entity_name} Target2 X"
    id: target2_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
    icon: mdi:focus-field-horizontal
  - platform: template
    name: "${entity_name} Target2 Y"
    id: target2_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
    icon: mdi:focus-field-vertical
  - platform: template
    name: "${entity_name} Target2 Speed"
    id: target2_speed
    accuracy_decimals: 0
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
  - platform: template
    name: "${entity_name} Target2 Resolution"
    id: target2_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance

  # Target 3
  - platform: template
    name: "${entity_name} Target3 X"
    id: target3_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
    icon: mdi:focus-field-horizontal
  - platform: template
    name: "${entity_name} Target3 Y"
    id: target3_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
    icon: mdi:focus-field-vertical
  - platform: template
    name: "${entity_name} Target3 Speed"
    id: target3_speed
    accuracy_decimals: 0
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
  - platform: template
    name: "${entity_name} Target3 Resolution"
    id: target3_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "${entity_name} Target1 Angle"
    id: target1_angle
    unit_of_measurement: 'º'
    accuracy_decimals: 1
    icon: mdi:angle-acute
  - platform: template
    name: "${entity_name} Target2 Angle"
    id: target2_angle
    accuracy_decimals: 1
    unit_of_measurement: 'º'
    icon: mdi:angle-acute
  - platform: template
    name: "${entity_name} Target3 Angle"
    id: target3_angle
    accuracy_decimals: 1
    unit_of_measurement: 'º'
    icon: mdi:angle-acute

light:
  - platform: status_led
    name: ${entity_name} sys_status
    pin: GPIO13
    internal: True
    restore_mode: ALWAYS_OFF
  - platform: binary
    name: "${entity_name} Red Info Light"
    output: board_info_ed
    entity_category: diagnostic
    restore_mode: ALWAYS_OFF
    disabled_by_default: True
time:
  - platform: sntp
    id: time_now

output:
  - platform: gpio
    id: board_info_ed
    pin: GPIO12

switch:
#  - platform: factory_reset
#    name: ${entity_name} Factory Reset
#    disabled_by_default: True
#    icon: mdi:heart-broken
#    entity_category: diagnostic
  - platform: template
    name: ${entity_name} Zout1 Enable
    id: zone_ex1_enable
    optimistic: True
    icon: mdi:account-cancel
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
  - platform: template
    name: ${entity_name} Zout2 Enable
    id: zone_ex2_enable
    optimistic: True
    icon: mdi:account-cancel
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
  - platform: template
    name: ${entity_name} Zout3 Enable
    id: zone_ex3_enable
    optimistic: True
    icon: mdi:account-cancel
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
  - platform: template
    name: ${entity_name} Target Enable
    id: target_fn_enable
    optimistic: True
    icon: mdi:target-variant
    entity_category: config
    restore_mode: RESTORE_DEFAULT_ON
  - platform: template
    name: ${entity_name} Zone Enable
    id: zone_fn_enable
    optimistic: True
    icon: mdi:target-variant
    entity_category: config
    restore_mode: RESTORE_DEFAULT_ON
  - platform: template
    name: ${entity_name} Illuminance Fast-Update
    id: bh1750_fast_update
    optimistic: True
    entity_category: diagnostic
    restore_mode: RESTORE_DEFAULT_OFF
    icon: mdi:run-fast
    disabled_by_default: True

button:
  - platform: restart
    icon: mdi:power-cycle
    name: "${entity_name} ESP Reboot"
    entity_category: diagnostic

uart:
  id: uart_bus
  tx_pin: 
    number: GPIO17
    mode:
      input: true
      pullup: true
  rx_pin: 
    number: GPIO16
    mode:
      input: true
      pullup: true
  baud_rate: 256000
  parity: NONE
  stop_bits: 1
  data_bits: 8
  debug:
    direction: BOTH
    dummy_receiver: True
    after:
     delimiter: [0X55, 0XCC]
    sequence:
      - lambda: |-
      
          // Tableaux pour stocker les noms des champs
          static template_::TemplateNumber* zone_x[] = {id(zone1_x), id(zone2_x), id(zone3_x), id(zone4_x), id(zone5_x), id(zone6_x)};
          static template_::TemplateNumber* zone_y[] = {id(zone1_y), id(zone2_y), id(zone3_y), id(zone4_y), id(zone5_y), id(zone6_y)};
          static template_::TemplateNumber* zone_height[] = {id(zone1_height), id(zone2_height), id(zone3_height), id(zone4_height), id(zone5_height), id(zone6_height)};
          static template_::TemplateNumber* zone_width[] = {id(zone1_width), id(zone2_width), id(zone3_width), id(zone4_width), id(zone5_width), id(zone6_width)};
          static template_::TemplateSensor* zone_target_count[] = {id(zone1_target_count), id(zone2_target_count), id(zone3_target_count), id(zone4_target_count), id(zone5_target_count), id(zone6_target_count)};
          static template_::TemplateBinarySensor* zone_target_exist[] = {id(zone1_target_exist), id(zone2_target_exist), id(zone3_target_exist), id(zone4_target_exist), id(zone5_target_exist), id(zone6_target_exist)};
          // static template_::TemplateTextSensor* tips_zone_conf[] = {id(tips_zone1_conf), id(tips_zone2_conf), id(tips_zone3_conf), id(tips_zone4_conf), id(tips_zone5_conf), id(tips_zone6_conf)};
          const int NUM_ZONES = 6;
          static template_::TemplateSwitch* zone_ex_enable[] = {id(zone_ex1_enable), id(zone_ex2_enable), id(zone_ex3_enable)};
          static template_::TemplateNumber* zone_ex_x[] = {id(zone_ex1_x), id(zone_ex2_x), id(zone_ex3_x)};
          static template_::TemplateNumber* zone_ex_y[] = {id(zone_ex1_y), id(zone_ex2_y), id(zone_ex3_y)};
          static template_::TemplateNumber* zone_ex_height[] = {id(zone_ex1_height), id(zone_ex2_height), id(zone_ex3_height)};
          static template_::TemplateNumber* zone_ex_width[] = {id(zone_ex1_width), id(zone_ex2_width), id(zone_ex3_width)};
          static template_::TemplateSensor* zone_ex_target_count[] = {id(zone_ex1_target_count), id(zone_ex2_target_count), id(zone_ex3_target_count)};
          static template_::TemplateBinarySensor* zone_ex_target_exist[] = {id(zone_ex1_target_exist), id(zone_ex2_target_exist), id(zone_ex3_target_exist)};
          // static template_::TemplateTextSensor* tips_zone_ex_conf[] = {id(tips_zone_ex1_conf), id(tips_zone_ex2_conf), id(tips_zone_ex3_conf)};
          const int NUM_ZONES_EX = 3;
          static template_::TemplateSensor* target_angle[] = {id(target1_angle), id(target2_angle), id(target3_angle)};
          static template_::TemplateTextSensor* target_position[] = {id(target1_position), id(target2_position), id(target3_position)};
          static template_::TemplateTextSensor* target_direction[] = {id(target1_direction), id(target2_direction), id(target3_direction)};
          static template_::TemplateSensor* target_x[] = {id(target1_x), id(target2_x), id(target3_x)};
          static template_::TemplateSensor* target_y[] = {id(target1_y), id(target2_y), id(target3_y)};
          static template_::TemplateSensor* target_speed[] = {id(target1_speed), id(target2_speed), id(target3_speed)};
          static template_::TemplateSensor* target_resolution[] = {id(target1_resolution), id(target2_resolution), id(target3_resolution)};
          const int NUM_TARGETS = 3;
          float angle = id(wall_angle).state*1;
          
          struct Position  p[NUM_TARGETS];
          struct Zone zone_ex[NUM_ZONES_EX], zone[NUM_ZONES];
          
          if ((millis() - id(last_update_ld2450)) <= 500) { 
            return;
          };
          id(last_update_ld2450) = millis();

          //Targets' data
          int b = 0;
          for (int i = 0; i < NUM_TARGETS; i++) {
            p[i].x = (uint16_t((bytes[b+5] << 8) | bytes[b+4] ));
            if ((bytes[b+5] & 0x80) >> 7){
              p[i].x -= pow(2, 15); 
            }else{
              p[i].x = 0 - p[i].x;
            }
            p[i].x = p[i].x * -1;

            p[i].y = (uint16_t((bytes[b+7] << 8) | bytes[b+6] ));
            if ((bytes[b+7] & 0x80) >> 7){
              p[i].y -= pow(2, 15);
            }else{
              p[i].y = 0 - p[i].y;
            }

            p[i].speed = (bytes[b+9] << 8 | bytes[b+8] );
            if ((bytes[b+9] & 0x80) >> 7){
              p[i].speed -= pow(2, 15);
            }else{
              p[i].speed = 0 - p[i].speed;
            }
            p[i].distance_resolution = (uint16_t((bytes[b+11] << 8) | bytes[b+10] )); 
            p[i].valide = (p[i].x != 0 || p[i].y > 0);
            b += 8;
          }
          
          // Excluded zones 
          for (int i = 0; i < NUM_ZONES_EX; i++) {
            zone_ex[i].x = zone_ex_x[i]->state;
            zone_ex[i].y = zone_ex_y[i]->state;
            zone_ex[i].width = zone_ex_width[i]->state;
            zone_ex[i].height = zone_ex_height[i]->state;
            //zone_ex[i].tips_conf = tips_zone_ex_conf[i];
            //zone_ex[i].name += "_ex" + std::to_string(i+1);

            if (zone_ex_enable[i]->state){
              for (int j = 0; j < NUM_TARGETS; j++) {
                if (p[j].valide){
                  if (check_targets_in_zone(zone_ex[i], p[j], angle)){
                    zone_ex[i].target_count++;
                    p[j].zone_ex_enter = true;
                  }
                }
              }
            }
            zone_ex[i].has_target = (zone_ex[i].target_count > 0);
          }
          
          // all targets
          int16_t all_target_counts = 0;
          for (int i = 0; i < NUM_TARGETS; i++) {
            if (p[i].valide && !p[i].zone_ex_enter){
              all_target_counts ++;
            }
          }

          bool has_target_in_zone_all = (all_target_counts > 0);

          // zones check
          if (id(zone_fn_enable).state){
            for (int i = 0; i < NUM_ZONES; i++) {
              zone[i].x = zone_x[i]->state;
              zone[i].y = zone_y[i]->state;
              zone[i].width = zone_width[i]->state;
              zone[i].height = zone_height[i]->state;
              //zone[i].tips_conf = tips_zone_conf[i];
              //zone[i].name += std::to_string(i+1);
              
              for (int j = 0; j < NUM_TARGETS; j++) {
                if (p[j].valide && !p[j].zone_ex_enter){
                  if(check_targets_in_zone(zone[i], p[j], angle)) {
                    zone[i].target_count++;
                  }
                }
              }
              zone[i].has_target = (zone[i].target_count > 0);
            }
          }

          // Angle, Position and Direction, idea from walberjunior.
          for (int i = 0; i < NUM_TARGETS; i++) {
            if (p[i].valide){
              p[i].angle = ((float)p[i].x / (float)p[i].y) * 180 / M_PI;;
            }
            if (target_angle[i]->state != p[i].angle && id(target_fn_enable).state){
              target_angle[i]->publish_state(p[i].angle);
            }

            if (p[i].speed > 0) {
              p[i].position = "Moving away";
            } else if (p[i].speed < 0) {
              p[i].position = "Approaching";
            } 
            if (p[i].position != target_position[i]->state && id(target_fn_enable).state){
              target_position[i]->publish_state(p[i].position);
            }

            if (p[i].x > 0) {
              p[i].direction = "Right";
            } else if (p[i].x < 0) {
              p[i].direction = "Left";
            } else if (p[i].y > 0){
              p[i].direction = "Middle";
            }
            if (p[i].direction != target_direction[i]->state && id(target_fn_enable).state){
              target_direction[i]->publish_state(p[i].direction);
            }
          }

          // public all info
          for (int i = 0; i < NUM_TARGETS; i++) {
            if (target_x[i]->state != p[i].x && id(target_fn_enable).state){
              target_x[i]->publish_state(p[i].x);
            }
            if (target_y[i]->state != p[i].y && id(target_fn_enable).state){
              target_y[i]->publish_state(p[i].y);
            }

            p[i].speed = float(p[i].speed) / 100.0;
            if (target_speed[i]->state != p[i].speed && id(target_fn_enable).state){
              target_speed[i]->publish_state(p[i].speed);
            }
            if (target_resolution[i]->state != p[i].distance_resolution && id(target_fn_enable).state){
              target_resolution[i]->publish_state(p[i].distance_resolution);
            }
          }

          // publish target and zone info
          if (id(all_target_count).state != all_target_counts){
            id(all_target_count).publish_state(all_target_counts);
            id(any_target_exist).publish_state(has_target_in_zone_all);
          }else if (id(any_target_exist).state != has_target_in_zone_all){
            id(any_target_exist).publish_state(has_target_in_zone_all);
          }

          for (int i = 0; i < NUM_ZONES; i++) {
            if (zone_target_count[i]->state != zone[i].target_count){
              zone_target_count[i]->publish_state(zone[i].target_count);
              zone_target_exist[i]->publish_state(zone[i].has_target);
            }else if (zone_target_exist[i]->state != zone[i].has_target){
              zone_target_exist[i]->publish_state(zone[i].has_target);
            }
          }
          for (int i = 0; i < NUM_ZONES_EX; i++) {
            if (zone_ex_target_count[i]->state != zone_ex[i].target_count){
              zone_ex_target_count[i]->publish_state(zone_ex[i].target_count);
            }
            if (zone_ex_target_exist[i]->state != zone_ex[i].has_target){
              zone_ex_target_exist[i]->publish_state(zone_ex[i].has_target);
            }
          }

          if (!id(init_zone_publish)){
            id(init_zone_publish) = true;
          }

La librairie

Le fichier dois se nommer zone.h et être enregistré dans le même répertoire que le YAML

#include <cmath>
#include <iostream>

struct Position {
  int16_t x; 
  int16_t y;
  int speed;
  int16_t distance_resolution;
  bool valide;
  bool zone_ex_enter = false;
  float angle = 0;
  std::basic_string<char> position = "Static";
  std::basic_string<char> direction = "None";
};
struct Zone {
  int16_t x;
  int16_t y;
  int16_t height;
  int16_t width;
  int16_t target_count = 0;
  bool has_target = false;
  
  // template_::TemplateTextSensor* tips_conf;
  // std::string name = "Zone";
};

struct Pxy {
  int16_t x; 
  int16_t y;
};

// Définition de la fonction pour vérifier les cibles dans une zone donnée
bool check_targets_in_zone(struct Zone &z, struct Position &t, float angle) {
  struct Pxy p1, p2, p3, p4;
  float d12, d14, d15, d23, d25, d34, d35, d45; 
  float a152, a154, a253, a354, a_sum;
  float TAU = 6.283185; // 2*PI
  bool isInside = false;
  
  p1.x = z.x;
  p1.y = z.y;
  p2.x = z.x - z.width * cos(angle*PI/180);
  p2.y = z.y + z.width * sin(angle*PI/180);
  p3.x = z.x - z.width * cos(angle*PI/180) + z.height * cos((angle-90)*PI/180);
  p3.y = z.y + z.width * sin(angle*PI/180) + z.height * sin((angle+90)*PI/180);
  p4.x = z.x + z.height * cos((angle-90)*PI/180);
  p4.y = z.y + z.height * sin((angle+90)*PI/180);
  
  d15 = sqrt(pow(p1.x-t.x,2)+pow(p1.y-t.y,2));
  d25 = sqrt(pow(p2.x-t.x,2)+pow(p2.y-t.y,2));
  d35 = sqrt(pow(p3.x-t.x,2)+pow(p3.y-t.y,2));
  d45 = sqrt(pow(p4.x-t.x,2)+pow(p4.y-t.y,2));
  d12 = sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2));
  d14 = sqrt(pow(p1.x-p4.x,2)+pow(p1.y-p4.y,2));
  d23 = sqrt(pow(p2.x-p3.x,2)+pow(p2.y-p3.y,2));
  d34 = sqrt(pow(p3.x-p4.x,2)+pow(p3.y-p4.y,2));
  
  a152 = acos((pow(d15,2)+pow(d25,2)-pow(d12,2))/(2*d15*d25));
  a154 = acos((pow(d15,2)+pow(d45,2)-pow(d14,2))/(2*d15*d45));
  a253 = acos((pow(d25,2)+pow(d35,2)-pow(d23,2))/(2*d25*d35));
  a354 = acos((pow(d35,2)+pow(d45,2)-pow(d34,2))/(2*d35*d45));
  a_sum = a152+a154+a253+a354;
  
  if (a_sum>=TAU) {
    isInside = true;
  }
  return isInside;
}
7 « J'aime »