Tutoriel détecteur LD2450 esphome

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 »