fan:
  - platform: template
    id: test_fan
    name: "Test Fan"
    preset_modes:
      - Eco
      - Sleep
      - Turbo
    has_oscillating: true
    has_direction: true
    speed_count: 3
    # Exercise fan.turn_on inside a trigger whose Ts pack is non-empty
    # (StringRef from on_preset_set) so the apply-lambda + inner-lambda
    # codegen runs through the cvref-normalized path.
    on_preset_set:
      then:
        - fan.turn_on:
            id: test_fan
            speed: !lambda "return x.empty() ? 1 : 3;"

# Test lambdas using get_preset_mode() which returns StringRef
# These examples match the migration guide in the PR description
binary_sensor:
  - platform: template
    id: fan_has_preset
    name: "Fan Has Preset"
    lambda: |-
      // Migration guide: Checking if preset mode is set
      // Use empty() or has_preset_mode()
      if (!id(test_fan).get_preset_mode().empty()) {
        // preset is set
      }
      if (id(test_fan).has_preset_mode()) {
        // preset is set
      }

      // Migration guide: Comparing preset mode
      // Use == operator directly (safe, works even when empty)
      if (id(test_fan).get_preset_mode() == "Eco") {
        return true;
      }

      // Migration guide: Checking for no preset
      if (id(test_fan).get_preset_mode().empty()) {
        // no preset
      }
      if (!id(test_fan).has_preset_mode()) {
        // no preset
      }

      // Migration guide: Getting as std::string
      std::string preset = std::string(id(test_fan).get_preset_mode());

      // Migration guide: Logging option 1
      // Use .c_str() - works because StringRef points to null-terminated string in traits
      ESP_LOGD("test", "Preset: %s", id(test_fan).get_preset_mode().c_str());

      // Migration guide: Logging option 2
      // Use %.*s format (safer, no null-termination assumption)
      auto preset_ref = id(test_fan).get_preset_mode();
      ESP_LOGD("test", "Preset: %.*s", (int)preset_ref.size(), preset_ref.c_str());

      // Test != comparison
      if (id(test_fan).get_preset_mode() != "Sleep") {
        return true;
      }
      return false;

# Exercise fan.turn_on with various field combinations so the
# TurnOnAction codegen paths get build coverage.
button:
  - platform: template
    name: "Fan Speed Only"
    on_press:
      - fan.turn_on:
          id: test_fan
          speed: 2
  - platform: template
    name: "Fan Oscillating + Direction"
    on_press:
      - fan.turn_on:
          id: test_fan
          oscillating: true
          direction: REVERSE
  - platform: template
    name: "Fan All Fields"
    on_press:
      - fan.turn_on:
          id: test_fan
          oscillating: false
          speed: 3
          direction: FORWARD
  - platform: template
    name: "Fan Lambda Speed"
    on_press:
      - fan.turn_on:
          id: test_fan
          speed: !lambda 'return 1;'

# Exercise fan.turn_on inside triggers with non-empty Ts:
#  - number.on_value: Ts = float (Python value type; previously raised
#    AttributeError on .operator("const"))
#  - fan.on_preset_set: Ts = StringRef (already a value-type wrapper around
#    a const char * + size; tests the cvref-normalized inner-lambda path)
number:
  - platform: template
    id: fan_speed_number
    optimistic: true
    min_value: 1
    max_value: 3
    step: 1
    on_value:
      then:
        - fan.turn_on:
            id: test_fan
            speed: !lambda "return (int) x;"
