esphome:
  on_boot:
    - sensor.template.publish:
        id: template_template_sens
        state: 42.0

    # Templated
    - sensor.template.publish:
        id: template_template_sens
        state: !lambda "return 42.0;"

    - water_heater.template.publish:
        id: template_water_heater
        target_temperature: 50.0
        mode: ECO
        away: false
        is_on: true

    # Templated
    - water_heater.template.publish:
        id: template_water_heater
        current_temperature: !lambda "return 45.0;"
        target_temperature: !lambda "return 55.0;"
        mode: !lambda "return water_heater::WATER_HEATER_MODE_GAS;"
        away: !lambda "return true;"
        is_on: !lambda "return false;"

    # Test C++ API: set_template() with stateless lambda (no captures)
    # NOTE: set_template() is not intended to be a public API, but we test it to ensure it doesn't break.
    - lambda: |-
        id(template_template_sens).set_template([]() -> std::optional<float> {
          return 123.0f;
        });
    # Test that esphome::optional alias still works for backward compatibility
    - lambda: |-
        id(template_template_sens).set_template([]() -> esphome::optional<float> {
          return 42.0f;
        });

    - datetime.date.set:
        id: template_test_date
        date:
          year: 2021
          month: 1
          day: 1
    - datetime.date.set:
        id: template_test_date
        date: !lambda "return {.day_of_month = 1, .month = 1, .year = 2021};"
    - datetime.date.set:
        id: template_test_date
        date: "2021-01-01"

binary_sensor:
  - platform: template
    id: template_some_binary_sensor
    name: "Garage Door Open"
    lambda: |-
      if (id(template_template_sens).state > 30) {
        // Garage Door is open.
        return true;
      } else {
        // Garage Door is closed.
        return false;
      }
  - platform: template
    id: select_binary_sensor
    name: Select is one or two
    condition:
      any:
        - select.is:
            id: template_select
            options: [one, two]
        - select.is:
            id: template_select
            lambda: return current == id(template_text).state;
  - platform: template
    id: other_binary_sensor
    name: "Garage Door Closed"
    condition:
      sensor.in_range:
        id: template_template_sens
        below: 30.0
    filters:
      - invert:
      - delayed_on: 100ms
      - delayed_off: 100ms
      - delayed_on_off: !lambda "if (id(test_switch).state) return 1000; else return 0;"
      - delayed_on_off:
          time_on: 10s
          time_off: !lambda "if (id(test_switch).state) return 1000; else return 0;"
      - autorepeat:
          - delay: 1s
            time_off: 100ms
            time_on: 900ms
          - delay: 5s
            time_off: 100ms
            time_on: 400ms
      - lambda: |-
          if (id(other_binary_sensor).state) {
            return x;
          }
          return {};
      - settle: 500ms
      - timeout: 5s

sensor:
  - platform: template
    name: "Template Sensor"
    id: template_template_sens
    lambda: |-
      if (id(template_some_binary_sensor).state) {
        return 42.0;
      }
      return 0.0;
    update_interval: 60s
    filters:
      - calibrate_linear:
          - 0.0 -> 0.0
          - 40.0 -> 45.0
          - 100.0 -> 102.5
      - calibrate_polynomial:
          degree: 2
          datapoints:
            # Map 0.0 (from sensor) to 0.0 (true value)
            - 0.0 -> 0.0
            - 10.0 -> 12.1
            - 13.0 -> 14.0
      - clamp:
          # Infinity and NaN will be clamped (NaN -> min_value, +Infinity -> max_value, -Infinity -> min_value)
          max_value: 10.0
          min_value: -10.0
      - debounce: 0.1s
      - delta: 5.0
      - delta:
          max_value: 2%
      - exponential_moving_average:
          alpha: 0.1
          send_every: 15
      - filter_out:
          - 10
          - 20
          - !lambda return 10;
      - filter_out: 10
      - filter_out: !lambda return NAN;
      - heartbeat: 5s
      - heartbeat:
          period: 5s
          optimistic: true
      - lambda: return x * (9.0/5.0) + 32.0;
      - max:
          window_size: 10
          send_every: 2
          send_first_at: 1
      - median:
          window_size: 7
          send_every: 4
          send_first_at: 3
      - min:
          window_size: 10
          send_every: 2
          send_first_at: 1
      - multiply: 1
      - multiply: !lambda return 2;
      - offset: 10
      - offset: !lambda return 10;
      - or:
          - quantile:
              window_size: 7
              send_every: 4
              send_first_at: 3
              quantile: .9
          - round: 1
      - round_to_multiple_of: 0.25
      - round_to_significant_digits: 3
      - skip_initial: 3
      - sliding_window_moving_average:
          window_size: 15
          send_every: 15
      - throttle: 1s
      - throttle_average: 2s
      - throttle_with_priority: 5s
      - throttle_with_priority:
          timeout: 3s
          value: 42.0
      - throttle_with_priority:
          timeout: 3s
          value: !lambda return 1.0f / 2.0f;
      - throttle_with_priority:
          timeout: 3s
          value:
            - 42.0
            - !lambda return 2.0f / 2.0f;
            - nan
      - timeout:
          timeout: 10s
          value: !lambda return 10;
      - timeout:
          timeout: 1h
          value: 20.0
      - timeout:
          timeout: 1min
          value: last
      - timeout:
          timeout: 1d
      - to_ntc_temperature:
          calibration:
            b_constant: 3950
            reference_temperature: 25.0°C
            reference_resistance: 10kOhm
      - to_ntc_resistance:
          calibration:
            - 10.0kOhm -> 25°C
            - 27.219kOhm -> 0°C
            - 14.674kOhm -> 15°C
      - to_ntc_temperature:
          calibration:
            - 10.0kOhm -> 25°C
            - 27.219kOhm -> 0°C
            - 14.674kOhm -> 15°C

output:
  - platform: template
    id: outputsplit
    type: float
    write_action:
      - logger.log: "write_action"

switch:
  - platform: template
    id: test_switch
    name: "Template Switch"
    lambda: |-
      if (id(template_some_binary_sensor).state) {
        return true;
      }
      return false;
    turn_on_action:
      - logger.log: "turn_on_action"
    turn_off_action:
      - logger.log: "turn_off_action"

button:
  - platform: template
    name: "Template Button"
    on_press:
      - logger.log: Button Pressed

cover:
  - platform: template
    name: "Template Cover"
    lambda: |-
      if (id(template_some_binary_sensor).state) {
        return COVER_OPEN;
      }
      return COVER_CLOSED;
    open_action:
      - logger.log: open_action
    close_action:
      - logger.log: close_action
    stop_action:
      - logger.log: stop_action
    optimistic: true
  - platform: template
    name: "Template Cover with Triggers"
    id: template_cover_with_triggers
    lambda: |-
      if (id(template_some_binary_sensor).state) {
        return COVER_OPEN;
      }
      return COVER_CLOSED;
    open_action:
      - logger.log: open_action
    close_action:
      - logger.log: close_action
    stop_action:
      - logger.log: stop_action
    optimistic: true
    on_opened:
      - logger.log: "Cover fully opened"
    on_closed:
      - logger.log: "Cover fully closed"
    on_opening:
      - logger.log: "Cover started opening"
    on_closing:
      - logger.log: "Cover started closing"
    on_idle:
      - logger.log: "Cover stopped moving"
      - logger.log: "Cover stopped moving"
      - if:
          condition:
            cover.is_open: template_cover_with_triggers
          then:
            logger.log: Cover is open
      - if:
          condition:
            cover.is_closed: template_cover_with_triggers
          then:
            logger.log: Cover is closed
  # Exercise cover.control / cover.template.publish action variants so they
  # get build coverage in CI (and so memory-impact analysis on PRs that
  # touch ControlAction / CoverPublishAction sees real instances).
  - platform: template
    name: "Template Cover Actions"
    id: template_cover_actions
    has_position: true
    optimistic: true
    open_action:
      # CONF_STATE alias for the position bit
      - cover.template.publish:
          id: template_cover_actions
          state: OPEN
      - cover.template.publish:
          id: template_cover_actions
          position: 1.0
      - cover.template.publish:
          id: template_cover_actions
          current_operation: IDLE
    close_action:
      - cover.template.publish:
          id: template_cover_actions
          position: 0.0
          tilt: 0.0
    stop_action:
      - cover.template.publish:
          id: template_cover_actions
          current_operation: IDLE
    tilt_action:
      - lambda: |-
          id(template_cover_actions).tilt = tilt;
          id(template_cover_actions).publish_state();
    on_idle:
      # position only
      - cover.control:
          id: template_cover_actions
          position: 50%
      # tilt only
      - cover.control:
          id: template_cover_actions
          tilt: 75%
      # position + tilt
      - cover.control:
          id: template_cover_actions
          position: 25%
          tilt: 30%
      # stop
      - cover.control:
          id: template_cover_actions
          stop: true
      # CONF_STATE alias for position
      - cover.control:
          id: template_cover_actions
          state: OPEN

number:
  - platform: template
    id: template_number
    name: "Template number"
    optimistic: true
    min_value: 0
    max_value: 100
    step: 1
  # Exercise valve.control inside a trigger with non-empty Ts (number on_value
  # passes float).
  - platform: template
    id: template_valve_position_number
    optimistic: true
    min_value: 0
    max_value: 100
    step: 1
    on_value:
      then:
        - valve.control:
            id: template_valve
            position: !lambda "return x / 100.0f;"
  # Same regression test for cover.control: forces the apply-lambda
  # codegen to handle a non-empty trigger Ts (float).
  - platform: template
    id: template_cover_position_number
    optimistic: true
    min_value: 0
    max_value: 100
    step: 1
    on_value:
      then:
        - cover.control:
            id: template_cover_with_triggers
            position: !lambda "return x / 100.0f;"

select:
  - platform: template
    id: template_select
    name: "Template select"
    optimistic: true
    options:
      - one
      - two
      - three
    initial_option: two
    # Test current_option() returning std::string_view - migration guide examples
    on_value:
      - lambda: |-
          // Migration guide: Check if select has a state
          // OLD: if (id(template_select).current_option() != nullptr)
          // NEW: Check with .empty()
          if (!id(template_select).current_option().empty()) {
            ESP_LOGI("test", "Select has state");
          }

          // Migration guide: Compare option values
          // OLD: if (strcmp(id(template_select).current_option(), "one") == 0)
          // NEW: Direct comparison works safely even when empty
          if (id(template_select).current_option() == "one") {
            ESP_LOGI("test", "Option is 'one'");
          }
          if (id(template_select).current_option() != "two") {
            ESP_LOGI("test", "Option is not 'two'");
          }

          // Migration guide: Logging options
          // Option 1: Using .c_str() - StringRef guarantees null-termination
          ESP_LOGI("test", "Current option: %s", id(template_select).current_option().c_str());

          // Option 2: Using %.*s format with size
          auto option = id(template_select).current_option();
          ESP_LOGI("test", "Current option (safe): %.*s", (int) option.size(), option.c_str());

          // Migration guide: Store in std::string
          std::string stored_option(id(template_select).current_option());
          ESP_LOGI("test", "Stored: %s", stored_option.c_str());
  - platform: template
    id: template_select_with_action
    name: "Template select with action"
    options:
      - option_a
      - option_b
    set_action:
      - logger.log:
          format: "Selected: %s"
          args: ["x.c_str()"]

lock:
  - platform: template
    name: "Template Lock"
    lambda: |-
      if (id(template_some_binary_sensor).state) {
        return LOCK_STATE_LOCKED;
      }
      return LOCK_STATE_UNLOCKED;
    lock_action:
      - logger.log: lock_action
    unlock_action:
      - logger.log: unlock_action
    open_action:
      - logger.log: open_action

valve:
  - platform: template
    id: template_valve
    name: "Template Valve"
    lambda: |-
      if (id(template_some_binary_sensor).state) {
        return VALVE_OPEN;
      }
      return VALVE_CLOSED;
    open_action:
      - logger.log: open_action
    close_action:
      - logger.log: close_action
      - valve.template.publish:
          id: template_valve
          state: CLOSED
    stop_action:
      - logger.log: stop_action
      # Exercise valve.control with various field combinations so the
      # ControlAction codegen paths get build coverage.
      - valve.control:
          id: template_valve
          stop: true
      - valve.control:
          id: template_valve
          position: 50%
      - valve.control:
          id: template_valve
          state: OPEN
      - valve.control:
          id: template_valve
          position: !lambda 'return 0.25f;'
    optimistic: true

text:
  - platform: template
    id: template_text
    name: "Template text"
    optimistic: true
    min_length: 0
    max_length: 100
    mode: text
  - platform: template
    name: "Template text lambda"
    mode: text
    update_interval: 1s
    lambda: |
      return std::string{"Hello!"};
    set_action:
      then:
        - logger.log:
            format: Template Text set to %s
            args: ["x.c_str()"]

alarm_control_panel:
  - platform: template
    name: Alarm Panel
    codes:
      - "1234"

water_heater:
  - platform: template
    id: template_water_heater
    name: "Template Water Heater"
    optimistic: true
    current_temperature: !lambda "return 42.0f;"
    target_temperature: !lambda "return 60.0f;"
    mode: !lambda "return water_heater::WATER_HEATER_MODE_ECO;"
    away: !lambda "return false;"
    is_on: !lambda "return true;"
    supported_modes:
      - "OFF"
      - ECO
      - GAS
      - ELECTRIC
      - HEAT_PUMP
      - HIGH_DEMAND
      - PERFORMANCE
    set_action:
      - logger.log: "set_action"

datetime:
  - platform: template
    name: Date
    id: template_test_date
    type: date
    initial_value: "2000-1-2"
    set_action:
      - logger.log: "set_value"
    on_value:
      - logger.log:
          format: "Date: %04d-%02d-%02d"
          args:
            - x.year
            - x.month
            - x.day_of_month
  - platform: template
    name: Time
    id: template_test_time
    type: time
    initial_value: "12:34:56am"
    set_action:
      - logger.log: "set_value"
    on_value:
      - logger.log:
          format: "Time: %02d:%02d:%02d"
          args:
            - x.hour
            - x.minute
            - x.second
  - platform: template
    name: DateTime
    id: template_test_datetime
    type: datetime
    initial_value: "2000-1-2 12:34:56"
    set_action:
      - logger.log: "set_value"
    on_value:
      - logger.log:
          format: "DateTime: %04d-%02d-%02d %02d:%02d:%02d"
          args:
            - x.year
            - x.month
            - x.day_of_month
            - x.hour
            - x.minute
            - x.second
