esphome:
  name: sauna
  friendly_name: Sauna
  on_boot:
    priority: 650
    then:
      - logger.log: "=== ESPSauna booting ==="
      - lambda: |-
          gpio_set_direction(GPIO_NUM_25, GPIO_MODE_INPUT);
          gpio_set_pull_mode(GPIO_NUM_25, GPIO_FLOATING);
          gpio_hold_dis(GPIO_NUM_25);
          ESP_LOGI("boot", "GPIO25 configured as input (floating)");
      - light.turn_on:
          id: onboard_light
          effect: "Fast Pulse"
      - logger.log: "ESPSauna - initialization complete"
  on_shutdown:
    then:
      - logger.log: "=== ESPSauna shutting down ==="

esp32:
  board: esp32doit-devkit-v1
  framework:
    type: esp-idf
    advanced:
      minimum_chip_revision: "3.1"
      sram1_as_iram: true

wifi:
  id: wifi_id
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  on_connect:
    - switch.turn_on: onboard_switch
    - light.turn_on:
        id: onboard_light
        effect: none
  on_disconnect:
    - switch.turn_off: onboard_switch
    - light.turn_on:
        id: onboard_light
        effect: "Fast Pulse"

api:
  encryption:
    key: "f4jrA8qlOorf2sUh6Ko0uc1pi9ap5LIm0Zg/p/OlnOA="

ota:
  - platform: esphome

logger:
  level: DEBUG

# ====================== CONFIGURATION ======================
globals:
  - id: relay_duty
    type: float
    initial_value: '0.5'
    restore_value: true

# ====================== I2C for SHT45 ======================
i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  frequency: 400kHz

# ====================== OUTPUTS ======================
output:
  - platform: ledc
    pin:
      number: GPIO2
      mode: OUTPUT
    id: onboard_led
    inverted: false

  - platform: ledc
    pin: GPIO13
    id: remote_relay
    frequency: 25000Hz   # Above audible range, low heat in MOSFET
    inverted: false

  - platform: ledc
    pin: GPIO14
    id: safety_relay
    frequency: 25000Hz   # Above audible range, low heat in MOSFET
    inverted: false

# ====================== LIGHTS ======================
light:
  - platform: monochromatic
    name: "WiFi LED"
    id: onboard_light
    output: onboard_led
    effects:
      - pulse:
          name: "Fast Pulse"
          transition_length: 250ms
          update_interval: 500ms

# ====================== SWITCHES (Relays) ======================
switch:
  - platform: template
    name: "Remote Control"
    id: remote_switch
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF
    turn_on_action:
      - logger.log: "Remote relay ON → Heater enabled"
      - output.set_level:
          id: remote_relay
          level: 100%                    # Full power pulse
      - delay: 300ms                     # Reliable pull-in time
      - output.set_level:
          id: remote_relay
          level: !lambda 'return id(relay_duty);' # Hold duty
    turn_off_action:
      - logger.log: "Remote relay OFF"
      - output.set_level:
          id: remote_relay
          level: 0%
  
  - platform: template
    name: "Safety Interlock"
    id: safety_switch
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF
    turn_on_action:
      - logger.log: "⚠️ SAFETY ON"
      - output.set_level:
          id: safety_relay
          level: 100%                    # Full power pulse
      - delay: 300ms                     # Reliable pull-in time
      - output.set_level:
          id: safety_relay
          level: !lambda 'return id(relay_duty);' # Hold duty
    turn_off_action:
      - logger.log: "⚠️ SAFETY OFF"
      - output.set_level:
          id: safety_relay
          level: 0%

  - platform: template
    name: "Built In LED (Blue)"
    id: onboard_switch
    icon: mdi:wifi
    turn_on_action:
      - output.turn_on: onboard_led
      - switch.template.publish:
          id: onboard_switch
          state: true
    turn_off_action:
      - output.turn_off: onboard_led
      - switch.template.publish:
          id: onboard_switch
          state: false

# ====================== BINARY SENSORS ======================
binary_sensor:
  - platform: template
    name: "WiFi Connected"
    id: wifi_connected
    icon: "mdi:wifi"
    internal: true
    lambda: |-
      return id(wifi_id).is_connected();

  - platform: template
    name: "Sauna Stove On"
    id: stove_on
    device_class: heat
    icon: mdi:stove
    lambda: |-
      return id(heater_current).state > 0.5;   // Adjust threshold if needed
    filters:
      - delayed_on_off:
          time_on: 3s
          time_off: 5s
    on_state:
      then:
        - lambda: |-
            if (x) {
              ESP_LOGD("stove", "Sauna Stove is now ON");
            } else {
              ESP_LOGD("stove", "Sauna Stove is now OFF");
            }
          
# ====================== SENSORS ======================
sensor:
  # System
  - platform: wifi_signal
    name: "WiFi Signal Strength"
    update_interval: 60s
    icon: "mdi:wifi"

  # DHT22 / AM2302 (GPIO4)
  - platform: dht
    pin: GPIO4
    model: AM2302
    temperature:
      name: "Sauna Temperature (DHT22)"
      id: dht22_temp
      unit_of_measurement: "°C"
      accuracy_decimals: 1
      icon: mdi:thermometer
      internal: true
    humidity:
      name: "Sauna Humidity (DHT22)"
      accuracy_decimals: 1
      icon: mdi:water-percent
    update_interval: 30s
  
  - platform: template
    name: "Changing Room Temperature (DHT22)"
    unit_of_measurement: "°F"
    accuracy_decimals: 1
    icon: mdi:thermometer
    update_interval: 30s
    lambda: |-
      if (id(dht22_temp).state == 0) {
        return {};  // Not ready yet
      }
      return id(dht22_temp).state * 9.0 / 5.0 + 32.0;
    filters:
      - sliding_window_moving_average:
          window_size: 6
          send_every: 3

  # SHT45 (I²C — preferred for accuracy in hot/humid sauna)
  - platform: sht4x
    temperature:
      name: "Sauna Temperature (SHT45)"
      id: sht45_temp
      unit_of_measurement: "°C"
      accuracy_decimals: 2
      icon: mdi:thermometer
      internal: true   # Hide raw °C from UI
    humidity:
      name: "Changing Room Humidity (SHT45)"
      accuracy_decimals: 1
      icon: mdi:water-percent
    update_interval: 30s

  - platform: template
    name: "Changing Room Temperature (SHT45)"
    unit_of_measurement: "°F"
    accuracy_decimals: 1
    icon: mdi:thermometer
    update_interval: 30s
    lambda: |-
      if (id(sht45_temp).state == 0) {
        return {};  // Not ready yet
      }
      return id(sht45_temp).state * 9.0 / 5.0 + 32.0;
    filters:
      - sliding_window_moving_average:
          window_size: 6
          send_every: 3

  # Current Sense (SCT-013 60A via voltage divider + protection → GPIO35)
  - platform: adc
    pin: GPIO35
    name: "Heater ADC (Raw)"
    id: heater_raw_adc
    internal: true
    update_interval: 1s          # or faster if needed
    accuracy_decimals: 4
    attenuation: auto
    icon: mdi:voltage

  # High Peak (for burden resistor tuning)
  - platform: copy
    source_id: heater_raw_adc
    name: "Heater ADC High Peak"
    id: heater_adc_high
    internal: true
    unit_of_measurement: "V"
    accuracy_decimals: 4
    filters:
      - max:
          window_size: 30
          send_every: 3
    icon: mdi:arrow-up-bold

  # Low Peak
  - platform: copy
    source_id: heater_raw_adc
    name: "Heater ADC Low Peak"
    id: heater_adc_low
    internal: true
    unit_of_measurement: "V"
    accuracy_decimals: 4
    filters:
      - min:
          window_size: 30
          send_every: 3
    icon: mdi:arrow-down-bold

  # Peak-to-Peak (very useful for verifying burden)
  - platform: template
    name: "Heater ADC Peak-to-Peak"
    id: heater_adc_p2p
    internal: true
    unit_of_measurement: "V"
    accuracy_decimals: 4
    update_interval: 5s
    lambda: |-
      return id(heater_adc_high).state - id(heater_adc_low).state;
    icon: mdi:waveform

  # Uncalibrated Current Clamp Output (used  for calibration)
  - platform: ct_clamp
    sensor: heater_raw_adc
    name: "Heater Current (Raw)"
    id: heater_current_raw
    internal: false
    unit_of_measurement: "A"
    accuracy_decimals: 4
    update_interval: 10s
    filters:
      - lambda: |-
          if (x < 0.01) return 0.0;   // Force small/negative noise to zero
          return x;
    icon: mdi:current-ac

  - platform: copy
    source_id: heater_current_raw
    name: "Heater Current"
    id: heater_current
    unit_of_measurement: "A"
    accuracy_decimals: 2
    icon: mdi:current-ac
    filters:
      - calibrate_linear:
          - 0.00 -> 0.0          # Zero current point
          - 0.603 -> 42.7       # ← Tune this! (or more points)
      - multiply: 1.0            # Fine scaling
      - lambda: |-
          if (x < 0.0) return 0.0;
          return x;

  # Instantaneous Power — use template sensor
  - platform: template
    name: "Heater Power"
    id: heater_power
    unit_of_measurement: "W"
    icon: mdi:flash
    accuracy_decimals: 0
    update_interval: 10s
    lambda: |-
      if (id(heater_current).state < 0) return 0;  // Clamp negatives
      return id(heater_current).state * 235.0;     // ← Measured value (under load)
    filters:
      #- sliding_window_moving_average:
      #    window_size: 8
      #    send_every: 4

  # Energy (kWh) — integrates the power sensor
  - platform: integration
    name: "Heater Energy"
    id: heater_energy
    sensor: heater_power
    unit_of_measurement: "kWh"
    icon: mdi:flash
    time_unit: h
    accuracy_decimals: 3
    filters:
      - multiply: 0.001

# ====================== TEXT SENSORS ======================
text_sensor:
  - platform: uptime
    name: "Uptime"
    id: sauna_uptime
    update_interval: 60s
    format:
      separator: ", "      # Space between units (or ", " etc.)
      days: "d"
      hours: "h"
      minutes: "m"
      seconds: "s"      # omit if you don't want seconds when large
    icon: "mdi:clock-start"
    entity_category: diagnostic

  - platform: version
    name: "Version"
    hide_timestamp: true

  - platform: wifi_info
    id: wifi_ip
    ip_address:
      name: "IP Address"
      icon: mdi:ip-network

# ====================== BUTTONS ======================
button:
  - platform: restart
    name: "Restart"
