#:########################################################################################:#
# ESP-NOW Test Receiver
#:########################################################################################:#
#
# DEVICE:
#   ESP32 DevKit V1 / ESP-WROOM-32
#
# VERSION:
#   1.0 - 2026-07-11
#     - Initial ESP-NOW receiver test build.
#     - Receives broadcast packets from ESP32-S3-Zero transmitter.
#     - Publishes last sender MAC, payload, RSSI, received count and duplicate count.
#     - Creates a short Home Assistant event pulse for each unique press_id.
#
# NOTES:
#   - This device is the powered receiver.
#   - The transmitter should send the same packet 2-3 times for reliability.
#   - This receiver de-duplicates repeated packets using the full payload string.
#   - Expected test payload format from transmitter:
#       REMOTE_A:BUTTON_1:123
#
#:########################################################################################:#

substitutions:
  device_name: esp-espnow_test_receiver
  friendly_name: "ESPNow Test Receiver"
  area: "Test Bench"

  # Passwords & Secrets (Unfortunately, you can't use substitutions inside secret names)
  # Re-using your existing bulb flasher secrets (adjust if you prefer per-device secrets)
  api_key: !secret esp-api_key
  ota_pass: !secret esp-ota_pass
  #static_ip_address: !secret esp-bedrm1arlecbulb_ip

#:########################################################################################:#
# PACKAGES: Included Common Packages
# https://esphome.io/components/packages.html
#:########################################################################################:#
packages:
  #### WIFI, Network (DHCP/IPV6 etc), Fallback AP, Safemode ####
  common_wifi: !include
    file: common/network_common_dhcp.yaml
    vars:
      local_device_name: "${device_name}"
      local_ota_pass: "${ota_pass}"

  #### HOME ASSISTANT API (choose encryption or no encryption options) ####
  common_api: !include
    file: common/api_common.yaml
    #file: common/api_common_noencryption.yaml
    vars:
      local_api_key: "${api_key}"

  #### MQTT ####
  #common_mqtt: !include
  #  file: common/mqtt_common.yaml
  #  vars:
  #    local_device_name: "${device_name}"

  #### WEB PORTAL ####
  #common_webportal: !include common/webportal_common.yaml
  common_webportal: !include common/webportal_common_nopasswd.yaml

  #### SNTP (Only use if you want/need accurate timeclocks) ####
  #common_sntp: !include common/sntp_common.yaml

  #### DIAGNOSTICS Sensors ####
  diag_basic: !include common/include_basic_diag_sensors.yaml
  diag_more: !include common/include_more_diag_sensors_nomqtt.yaml
  #diag_debug: !include common/include_debug_diag_sensors.yaml
  #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml

esphome:
  name: ${device_name}
  friendly_name: ${friendly_name}
  area: ${area}

esp32:
  board: esp32dev
  framework:
    type: arduino

#:########################################################################################:#
# ESP-NOW Receiver
#:########################################################################################:#
#
# Do not set channel here while using Wi-Fi.
# The receiver's ESP-NOW channel follows the connected Wi-Fi channel.
#

espnow:
  auto_add_peer: true

  on_broadcast:
    - then:
        - lambda: |-
            char src_mac[18];
            snprintf(src_mac, sizeof(src_mac), "%02X:%02X:%02X:%02X:%02X:%02X",
                     info.src_addr[0], info.src_addr[1], info.src_addr[2],
                     info.src_addr[3], info.src_addr[4], info.src_addr[5]);

            std::string payload(reinterpret_cast<const char *>(data), size);
            int rssi = info.rx_ctrl ? info.rx_ctrl->rssi : 0;

            id(last_sender_mac).publish_state(src_mac);
            id(last_payload).publish_state(payload);
            id(last_rssi).publish_state(rssi);

            id(received_count) += 1;
            id(received_count_sensor).publish_state(id(received_count));

            ESP_LOGI("espnow_rx", "ESP-NOW broadcast from %s, RSSI %d dBm, payload: %s",
                     src_mac, rssi, payload.c_str());

            // Test payload format:
            //   REMOTE_A:BUTTON_1:123
            //
            // For now, use the whole payload as the duplicate key.
            // This means the 2nd and 3rd repeated sends from one button press are ignored.
            if (payload == id(last_unique_payload)) {
              id(duplicate_count) += 1;
              id(duplicate_count_sensor).publish_state(id(duplicate_count));
              id(packet_is_unique) = false;

              ESP_LOGD("espnow_rx", "Duplicate ESP-NOW payload ignored: %s", payload.c_str());
              return;
            }

            id(last_unique_payload) = payload;
            id(packet_is_unique) = true;

            // Split simple colon-delimited payload for HA visibility.
            size_t first_colon = payload.find(':');
            size_t second_colon = payload.find(':', first_colon + 1);

            if (first_colon != std::string::npos && second_colon != std::string::npos) {
              std::string remote_id = payload.substr(0, first_colon);
              std::string button_id = payload.substr(first_colon + 1, second_colon - first_colon - 1);
              std::string press_id = payload.substr(second_colon + 1);

              id(last_remote_id).publish_state(remote_id);
              id(last_button_id).publish_state(button_id);
              id(last_press_id).publish_state(press_id);

              ESP_LOGI("espnow_rx", "Unique press: remote=%s button=%s press_id=%s",
                       remote_id.c_str(), button_id.c_str(), press_id.c_str());
            } else {
              id(last_remote_id).publish_state("unknown");
              id(last_button_id).publish_state("unknown");
              id(last_press_id).publish_state("unknown");

              ESP_LOGW("espnow_rx", "Payload did not match expected format: %s", payload.c_str());
            }

        - if:
            condition:
              lambda: |-
                return id(packet_is_unique);
            then:
              - binary_sensor.template.publish:
                  id: espnow_unique_press_event
                  state: ON
              - script.execute: clear_unique_press_event

#:########################################################################################:#
# Globals
#:########################################################################################:#

globals:
  - id: received_count
    type: int
    restore_value: true
    initial_value: '0'

  - id: duplicate_count
    type: int
    restore_value: true
    initial_value: '0'

  - id: last_unique_payload
    type: std::string
    restore_value: true
    initial_value: '""'

  - id: packet_is_unique
    type: bool
    restore_value: false
    initial_value: 'false'

#:########################################################################################:#
# Scripts
#:########################################################################################:#

script:
  - id: clear_unique_press_event
    mode: restart
    then:
      - delay: 500ms
      - binary_sensor.template.publish:
          id: espnow_unique_press_event
          state: OFF

#:########################################################################################:#
# Home Assistant Entities
#:########################################################################################:#

text_sensor:
  - platform: template
    name: "Last Sender MAC"
    id: last_sender_mac
    icon: mdi:access-point-network

  - platform: template
    name: "Last Payload"
    id: last_payload
    icon: mdi:message-text

  - platform: template
    name: "Last Remote ID"
    id: last_remote_id
    icon: mdi:remote

  - platform: template
    name: "Last Button ID"
    id: last_button_id
    icon: mdi:gesture-tap-button

  - platform: template
    name: "Last Press ID"
    id: last_press_id
    icon: mdi:identifier

sensor:
  - platform: template
    name: "Last RSSI"
    id: last_rssi
    unit_of_measurement: "dBm"
    accuracy_decimals: 0
    icon: mdi:wifi-strength-2

  - platform: template
    name: "Received Count"
    id: received_count_sensor
    accuracy_decimals: 0
    icon: mdi:counter

  - platform: template
    name: "Duplicate Count"
    id: duplicate_count_sensor
    accuracy_decimals: 0
    icon: mdi:counter

binary_sensor:
  - platform: template
    name: "Unique Press Event"
    id: espnow_unique_press_event
    icon: mdi:gesture-tap-button

button:
  - platform: template
    name: "Reset ESP-NOW Test Counts"
    icon: mdi:counter
    on_press:
      then:
        - lambda: |-
            id(received_count) = 0;
            id(duplicate_count) = 0;
            id(last_unique_payload) = "";
            id(received_count_sensor).publish_state(0);
            id(duplicate_count_sensor).publish_state(0);