esphome:
  name: uart-mock-ld2412-test

host:
api:
  batch_delay: 0ms  # Disable batching to receive all state updates
logger:
  level: VERBOSE

external_components:
  - source:
      type: local
      path: EXTERNAL_COMPONENT_PATH

# Dummy uart entry to satisfy ld2412's DEPENDENCIES = ["uart"]
# The actual UART bus used is the uart_mock component below
uart:
  baud_rate: 115200
  port: /dev/null

uart_mock:
  id: mock_uart
  baud_rate: 256000
  auto_start: false
  injections:
    # Phase 1 (t=100ms): Valid LD2412 normal mode data frame - happy path
    # The buffer is clean at this point, so this frame should parse correctly.
    # Moving target: 100cm, energy 50
    # Still target: 120cm, energy 25
    # Target state: 0x03 (moving + still)
    # detection_distance = 100 (LD2412 computes from moving target when MOVE_BITMASK set)
    #
    # Frame layout (24 bytes):
    #   [0-3]   F4 F3 F2 F1 = data frame header
    #   [4-5]   0D 00       = length 13
    #   [6]     02          = data type (normal)
    #   [7]     AA          = data header marker
    #   [8]     03          = target states (moving+still)
    #   [9-10]  64 00       = moving distance 100 (0x0064)
    #   [11]    32          = moving energy 50
    #   [12-13] 78 00       = still distance 120 (0x0078)
    #   [14]    19          = still energy 25
    #   [15-16] 64 00       = detect distance bytes (ignored by LD2412 code)
    #   [17]    00          = padding
    #   [18]    55          = data footer marker
    #   [19]    00          = CRC/check
    #   [20-23] F8 F7 F6 F5 = data frame footer
    - delay: 100ms
      inject_rx:
        [
          0xF4, 0xF3, 0xF2, 0xF1,
          0x0D, 0x00,
          0x02, 0xAA,
          0x03,
          0x64, 0x00,
          0x32,
          0x78, 0x00,
          0x19,
          0x64, 0x00,
          0x00,
          0x55, 0x00,
          0xF8, 0xF7, 0xF6, 0xF5,
        ]

    # Phase 2 (t=300ms): Garbage bytes
    # LD2412's parser rejects bytes that don't match the frame header at
    # position 0 (must start with F4 or FD), so buffer stays empty.
    - delay: 200ms
      inject_rx: [0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x11, 0x22]

    # Phase 3 (t=400ms): Truncated frame (header + partial data, no footer)
    # Starts with valid data frame header so parser accepts it.
    # After this, buffer_pos_ = 8.
    - delay: 100ms
      inject_rx: [0xF4, 0xF3, 0xF2, 0xF1, 0x0D, 0x00, 0x02, 0xAA]

    # Phase 4 (t=600ms): Overflow - inject 60 bytes of 0xFF (MAX_LINE_LENGTH=54)
    # Buffer has 8 bytes from phase 3 (garbage in phase 2 was rejected).
    # Overflow math: buffer_pos_ starts at 8, overflow triggers when
    # buffer_pos_ reaches 53 (MAX_LINE_LENGTH - 1). Need 45 more bytes to
    # fill positions 8-52, then byte 46 triggers overflow. After overflow,
    # buffer_pos_ = 0 and remaining 0xFF bytes are rejected (don't match header).
    - delay: 200ms
      inject_rx:
        [
          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        ]

    # Phase 5 (t=700ms): Valid frame after overflow - recovery test
    # Buffer was reset by overflow. This valid frame should parse correctly.
    # Moving target: 50cm, energy 100
    # Still target: 75cm, energy 80
    # detection_distance = 50 (moving target distance, since MOVE_BITMASK set)
    - delay: 100ms
      inject_rx:
        [
          0xF4, 0xF3, 0xF2, 0xF1,
          0x0D, 0x00,
          0x02, 0xAA,
          0x03,
          0x32, 0x00,
          0x64,
          0x4B, 0x00,
          0x50,
          0x32, 0x00,
          0x00,
          0x55, 0x00,
          0xF8, 0xF7, 0xF6, 0xF5,
        ]

ld2412:
  id: ld2412_dev
  uart_id: mock_uart

sensor:
  - platform: ld2412
    ld2412_id: ld2412_dev
    moving_distance:
      name: "Moving Distance"
      filters:
        - timeout:
            timeout: 50ms
            value: last
        - throttle_with_priority: 50ms
    still_distance:
      name: "Still Distance"
      filters:
        - timeout:
            timeout: 50ms
            value: last
        - throttle_with_priority: 50ms
    moving_energy:
      name: "Moving Energy"
      filters:
        - timeout:
            timeout: 50ms
            value: last
        - throttle_with_priority: 50ms
    still_energy:
      name: "Still Energy"
      filters:
        - timeout:
            timeout: 50ms
            value: last
        - throttle_with_priority: 50ms
    detection_distance:
      name: "Detection Distance"
      filters:
        - timeout:
            timeout: 50ms
            value: last
        - throttle_with_priority: 50ms

binary_sensor:
  - platform: ld2412
    ld2412_id: ld2412_dev
    has_target:
      name: "Has Target"
      filters:
        - settle: 50ms
    has_moving_target:
      name: "Has Moving Target"
      filters:
        - settle: 50ms
    has_still_target:
      name: "Has Still Target"
      filters:
        - settle: 50ms

button:
  - platform: template
    name: "Start Scenario"
    id: start_scenario_btn
    on_press:
      - lambda: 'id(mock_uart).start_scenario();'
