esphome:
  name: uart-mock-ld2420-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 ld2420'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: 115200
  auto_start: false
  responses:
    # Version-specific response: match the complete firmware version command TX.
    # CMD_READ_VERSION (0x0000) TX = FD FC FB FA 02 00 00 00 04 03 02 01
    # Returns "v2.0.0" → get_firmware_int = 200 >= 154 → energy mode
    #
    # Response layout:
    #   [0-3]   FD FC FB FA = header
    #   [4-5]   0C 00       = length 12
    #   [6]     00          = cmd (CMD_READ_VERSION)
    #   [7]     01          = status (ACK)
    #   [8-9]   00 00       = error = 0
    #   [10]    06          = ver_len = 6
    #   [11]    00          = padding
    #   [12-17] "v2.0.0"   = version string
    #   [18-21] 04 03 02 01 = footer
    - expect_tx:
        [0xFD, 0xFC, 0xFB, 0xFA, 0x02, 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01]
      inject_rx:
        [
          0xFD, 0xFC, 0xFB, 0xFA,
          0x0C, 0x00,
          0x00, 0x01,
          0x00, 0x00,
          0x06, 0x00,
          0x76, 0x32, 0x2E, 0x30, 0x2E, 0x30,
          0x04, 0x03, 0x02, 0x01,
        ]

    # Catch-all response: match any command footer (04 03 02 01).
    # Returns a generic ACK with cmd=0xFF (CMD_ENABLE_CONF case in switch).
    # All commands get unblocked via cmd_reply_.ack = true.
    # Data fields stay zeroed (min_gate=0, max_gate=0, timeout=0, thresholds=0).
    #
    # Response layout:
    #   [0-3]  FD FC FB FA = header
    #   [4-5]  04 00       = length 4
    #   [6]    FF          = cmd (handled as CMD_ENABLE_CONF)
    #   [7]    01          = status (ACK)
    #   [8-9]  00 00       = error = 0
    #   [10-13] 04 03 02 01 = footer
    - expect_tx: [0x04, 0x03, 0x02, 0x01]
      inject_rx:
        [
          0xFD, 0xFC, 0xFB, 0xFA,
          0x04, 0x00,
          0xFF, 0x01,
          0x00, 0x00,
          0x04, 0x03, 0x02, 0x01,
        ]

  injections:
    # Phase 1 (t=100ms): Valid LD2420 energy mode data frame - happy path
    # Buffer is clean (buffer_pos_=0). This frame should parse correctly.
    # Presence: 1 (target detected), Distance: 100cm, Gate energies: all 0
    #
    # Energy frame layout (45 bytes):
    #   [0-3]   F4 F3 F2 F1 = energy frame header
    #   [4-5]   23 00       = length 35 (1+2+32)
    #   [6]     01          = presence (1 = target)
    #   [7-8]   64 00       = distance 100 (uint16_t LE)
    #   [9-40]  00 00 x16   = 16 gate energies (uint16_t LE each)
    #   [41-44] F8 F7 F6 F5 = energy frame footer
    - delay: 100ms
      inject_rx:
        [
          0xF4, 0xF3, 0xF2, 0xF1,
          0x23, 0x00,
          0x01,
          0x64, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0xF8, 0xF7, 0xF6, 0xF5,
        ]

    # Phase 2 (t=300ms): Garbage bytes
    # LD2420's readline_ does NOT check frame headers at position 0 (unlike LD2412),
    # so these bytes accumulate in the buffer. buffer_pos_ goes from 0 to 7.
    - delay: 200ms
      inject_rx: [0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x11, 0x22]

    # Phase 3 (t=400ms): Truncated energy frame WITH footer (13 bytes)
    # This tests PR #14458 bug #3: missing length validation in handle_energy_mode_.
    # The 7 garbage bytes from Phase 2 are still in the buffer (buffer_pos_=7).
    # These 13 bytes are appended at positions 7-19 (buffer_pos_=20).
    # Energy footer at positions 16-19 triggers handle_energy_mode_(buffer, 20).
    #
    # Pre-fix: handle_energy_mode_ reads 32 bytes of gate energy from buffer[9:40],
    #          which is past the 20 actual bytes. Reads uninitialized data.
    #          No "Energy frame too short" warning exists.
    # Post-fix: len=20 < 41 → logs "Energy frame too short: 20 bytes", returns early.
    #
    # Frame: header + length + presence + distance + footer (no gate data)
    - delay: 100ms
      inject_rx:
        [
          0xF4, 0xF3, 0xF2, 0xF1,
          0x23, 0x00,
          0x01,
          0x64, 0x00,
          0xF8, 0xF7, 0xF6, 0xF5,
        ]

    # Phase 4 (t=600ms): Overflow - inject 50 bytes of 0xFF (MAX_LINE_LENGTH=50)
    # After Phase 3, buffer_pos_=0 (reset after energy footer detection).
    # 49 bytes fill positions 0-48 (buffer_pos_=49), 50th byte triggers overflow.
    # Logs "Max command length exceeded; ignoring", buffer_pos_=0.
    - 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,
        ]

    # Phase 5 (t=1500ms): Valid frame after overflow - recovery test
    # Buffer was reset by overflow. This valid frame should parse correctly.
    # Presence: 1 (target), Distance: 50cm
    # Delay=900ms ensures >1000ms gap from Phase 1 for REFRESH_RATE_MS throttle.
    - delay: 900ms
      inject_rx:
        [
          0xF4, 0xF3, 0xF2, 0xF1,
          0x23, 0x00,
          0x01,
          0x32, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0xF8, 0xF7, 0xF6, 0xF5,
        ]

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

ld2420:
  id: ld2420_dev
  uart_id: mock_uart

sensor:
  - platform: ld2420
    ld2420_id: ld2420_dev
    moving_distance:
      name: "Moving Distance"
      filters:
        - timeout:
            timeout: 50ms
            value: last
        - throttle_with_priority: 50ms

binary_sensor:
  - platform: ld2420
    ld2420_id: ld2420_dev
    has_target:
      name: "Has Target"
      filters:
        - settle: 50ms
