mmWave Presence Detection - ESPHome style

@crlogic - I have made some tweaks to your code to better utilise substitutions so that if someone wants to set up more than one of these then all the buttons/sensors/switches are easier to differentiate in HA. Also some minor tweaks to units of measurement:


esphome:
  name: living-room-presence
  includes:
    - uart_read_line_sensor.h

esp8266:
  board: d1_mini #change as needed

# Enable logging
logger:
  logs:
    text_sensor: INFO
    sensor: INFO

substitutions:
  device_name: living_room_presence #change as needed
  device_friendly_name: Living Room Presence #change as needed

# Enable Home Assistant API
api:
  services:
      # Service to send a command directly to the display. Useful for testing
    - service: send_command
      variables:
        cmd: string
      then:
        - uart.write: !lambda
            std::string command = to_string(cmd) +"\r";
            return std::vector<uint8_t>(command.begin(), command.end());

ota:
  password: #snip

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Living Room Presence Hotspot"
    password: #snip

captive_portal:

uart:
  id: uart_bus
  tx_pin: D2
  rx_pin: D1
  baud_rate: 115200

web_server:
  port: 80
  version: 2
  include_internal: true
  ota: false

http_request:
  useragent: esphome/$device_name
  timeout: 2s

binary_sensor:
  - platform: gpio
    name: $device_friendly_name
    id: $device_name
    pin:
      number: D0 #change as needed
      mode: INPUT_PULLDOWN
    on_state:
     - if:
        condition:
          - binary_sensor.is_off: $device_name
        then:
          - sensor.template.publish:
              id: target_1_distance_m
              state: 0.0
          - sensor.template.publish:
              id: target_1_SNR
              state: 0.0
          - sensor.template.publish:
              id: ${device_name}_num_targets
              state: 0.0

sensor:
  - platform: template
    name: ${device_friendly_name} Target 1 Distance
    id: target_1_distance_m
    unit_of_measurement: m
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 2 Distance
    id: target_2_distance_m
    unit_of_measurement: m
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 3 Distance
    id: target_3_distance_m
    unit_of_measurement: m
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 4 Distance
    id: target_4_distance_m
    unit_of_measurement: m
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 5 Distance
    id: target_5_distance_m
    unit_of_measurement: m
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 6 Distance
    id: target_6_distance_m
    unit_of_measurement: m
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 7 Distance
    id: target_7_distance_m
    unit_of_measurement: m
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 8 Distance
    id: target_8_distance_m
    unit_of_measurement: m
    internal: true

  - platform: template
    name: ${device_friendly_name} Target 1 SNR
    id: target_1_SNR
    internal: true

  - platform: template
    name: ${device_friendly_name} Target 2 SNR
    id: target_2_SNR
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 3 SNR
    id: target_3_SNR
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 4 SNR
    id: target_4_SNR
    internal: true

  - platform: template
    name: ${device_friendly_name} Target 5 SNR
    id: target_5_SNR
    internal: true

  - platform: template
    name: ${device_friendly_name} Target 6 SNR
    id: target_6_SNR
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 7 SNR
    id: target_7_SNR
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Target 8 SNR
    id: target_8_SNR
    internal: true
    
  - platform: template
    name: ${device_friendly_name} Targets
    id: ${device_name}_num_targets

  - platform: bh1750 #ignore if not using a bh1750 lux sensor
    name: ${device_friendly_name} Lux
    address: 0x23
    update_interval: 60s

switch:
  - platform: safe_mode
    name: ${device_friendly_name} Safe Mode
    entity_category: diagnostic 

  - platform: template
    name: ${device_friendly_name} mmWave Sensor
    id: ${device_name}_mmwave_sensor
    entity_category: config
    optimistic: true
    restore_state: true
    assumed_state: true
    turn_on_action:
      - uart.write: "sensorStart"
    turn_off_action:
      - uart.write: "sensorStop"
    
  - platform: template
    name: ${device_friendly_name} LED
    id: ${device_name}_led
    entity_category: config
    optimistic: true
    restore_state: true
    assumed_state: true
    turn_on_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: "setLedMode 1 0"
      - delay: 1s 
      - uart.write: "saveConfig"
      - delay: 3s
      - switch.turn_on: ${device_name}_mmwave_sensor
      - delay: 1s
    turn_off_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: "setLedMode 1 1"
      - delay: 1s      
      - uart.write: "saveConfig"
      - delay: 3s
      - switch.turn_on: ${device_name}_mmwave_sensor
      - delay: 1s
      
  - platform: template
    name: ${device_friendly_name} UART Presence Output
    id: ${device_name}_uart_presence_output
    entity_category: diagnostic 
    optimistic: true
    restore_state: true
    assumed_state: true
    turn_on_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: "setUartOutput 1 1"
      - delay: 1s 
      - uart.write: "saveConfig"
      - delay: 3s
      - switch.turn_on: ${device_name}_mmwave_sensor
      - delay: 1s
    turn_off_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: "setUartOutput 1 0"
      - delay: 1s      
      - uart.write: "saveConfig"
      - delay: 3s
      - switch.turn_on: ${device_name}_mmwave_sensor
      - delay: 1s
      
  - platform: template
    name: ${device_friendly_name} UART Target Output
    id: ${device_name}_uart_target_output
    entity_category: diagnostic 
    optimistic: true
    restore_state: true
    assumed_state: false
    turn_on_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: "setUartOutput 2 1 1 1"
      - delay: 1s 
      - uart.write: "saveConfig"
      - delay: 3s
      - switch.turn_on: ${device_name}_mmwave_sensor
      - delay: 1s
    turn_off_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: "setUartOutput 2 0"
      - delay: 1s      
      - uart.write: "saveConfig"
      - delay: 3s
      - switch.turn_on: ${device_name}_mmwave_sensor
      - delay: 1s   

number:
  - platform: template
    name: ${device_friendly_name} Range
    id: ${device_name}_range
    entity_category: config
    min_value: 0.15
    max_value: 9.45
    initial_value: 3.15
    optimistic: true
    step: 0.15
    restore_value: true
    unit_of_measurement: m
    mode: box
    set_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: !lambda
                      std::string range = "setRange 0 " + str_sprintf("%.2f", x);
                      return std::vector<unsigned char>(range.begin(), range.end());
      - delay: 1s
      - uart.write: "saveConfig"
      - delay: 1s
      - switch.turn_on: ${device_name}_mmwave_sensor
      - delay: 1s
      
  - platform: template
    name: ${device_friendly_name} Latency
    id: ${device_name}_latency
    entity_category: config
    min_value: 1
    max_value: 600
    initial_value: 90
    optimistic: true
    step: 1
    restore_value: true
    unit_of_measurement: s
    mode: box
    set_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: !lambda
                      std::string setL = "setLatency 0.1 " + str_sprintf("%.0f", x);
                      return std::vector<unsigned char>(setL.begin(), setL.end());
      - delay: 1s
      - uart.write: "saveConfig"
      - delay: 1s
      - switch.turn_on: ${device_name}_mmwave_sensor

  - platform: template
    name: ${device_friendly_name} Sensitivity
    id: ${device_name}_sensitivity
    entity_category: config
    min_value: 0
    max_value: 9
    initial_value: 9
    optimistic: true
    step: 1
    restore_value: true
    set_action:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: !lambda
                      std::string mss = "setSensitivity " + to_string((int)x);
                      return std::vector<unsigned char>(mss.begin(), mss.end());
      - delay: 1s
      - uart.write: "saveConfig"
      - delay: 1s
      - switch.turn_on: ${device_name}_mmwave_sensor

button:
  - platform: restart
    name: ${device_friendly_name} Restart
    entity_category: diagnostic
  
  - platform: template
    name: ${device_friendly_name} Update Sensitivity
    entity_category: diagnostic
    internal: true
    on_press:
    - uart.write: !lambda
                      std::string getS = "getSensitivity";
                      return std::vector<unsigned char>(getS.begin(), getS.end());

  - platform: template
    name: ${device_friendly_name} Update Latency
    entity_category: diagnostic
    internal: true
    on_press:
    - uart.write: !lambda
                      std::string getL = "getLatency";
                      return std::vector<unsigned char>(getL.begin(), getL.end());

  - platform: template
    name: ${device_friendly_name} Update Range
    entity_category: diagnostic
    internal: true
    on_press:
    - uart.write: !lambda
                      std::string getR = "getRange";
                      return std::vector<unsigned char>(getR.begin(), getR.end());

  - platform: template
    name: ${device_friendly_name} Factory Reset
    id: ${device_name}_factory_reset
    entity_category: config
    on_press:
      - switch.turn_off: ${device_name}_mmwave_sensor
      - delay: 1s
      - uart.write: "resetCfg"
      - delay: 3s
      - switch.turn_on: ${device_name}_mmwave_sensor

text_sensor:
  - platform: custom
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    text_sensors:
      name: ${device_friendly_name} UART Output
      id: uart_readline
      internal: true
      on_value:
        lambda: |-
          std::string line = id(uart_readline).state;
          if (id(${device_name}_uart_target_output).state && line.substr(0, 6) == "$JYRPO")
          {    
              
              auto publishTarget = [](std::string idx, float dist, float snr) {
                  auto sens = App.get_sensors();
                  for(int i = 0; i < sens.size(); i++) {
                    auto name = sens[i]->get_name();
                    auto target = "target_" + to_string(idx);
                    if(name.size() > 10 && name.substr(0, 8) == target) {
                      if(name.substr(9, 3) == "dis") {
                        sens[i]->publish_state(dist);
                      } else if(name.substr(9, 3) == "SNR") {
                        sens[i]->publish_state(snr);
                      }
                    }
                  }
              };
              
              line = line.substr(6);
              std::vector<std::string> v;    
              for(int i = 0; i < line.length(); i++) {
                  if(line[i] == ',') {
                      v.push_back("");
                  } else {
                      v.back() += line[i];
                  }
              }
          
              id(${device_name}_num_targets).publish_state(parse_number<float>(v[0]).value());
              
              publishTarget(v[1], parse_number<float>(v[2]).value(), parse_number<float>(v[4]).value());
              for(int i = parse_number<int>(v[0]).value() +1 ; i < 9; i++) publishTarget(to_string(i), 0, 0);
          }

i2c: #ignore if not using an i2c bus (in my case for the bh1750)
  sda: D6
  scl: D7
  scan: true
  id: bus_a
3 Likes