esphome:
  name: pet-feeder-v3
  friendly_name: pet-feeder-v3

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "your-encryption-key"

ota:
  - platform: esphome
    password: "your-ota-password"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Pet-Feeder-V3 Fallback Hotspot"
    password: "your-ap-password"

captive_portal:
    
i2c:
  sda: 5
  scl: 6
  frequency: 300kHz
  scan: false

font:
  - file: "gfonts://Roboto"
    id: dfont
    size: 12

display:
  - platform: ssd1306_i2c
    id: oled_42
    model: "SSD1306 72x40"
    address: 0x3C
    lambda: |-
       it.printf(0, 0, id(dfont), "%.2fV %.0f%%", id(v_bat).state, id(bat_percent).state);
       if(id(time_synced).state) {
        it.strftime(0, 14, id(dfont), "Now: %H:%M", id(sntp_time).now());
        it.strftime(0, 28, id(dfont), "Next: %H:%M", id(feeding_date).state_as_esptime());
       } else {
        it.printf(0, 14, id(dfont), "Time");
        it.printf(0, 28, id(dfont), "sync...", id(feeding_date).state_as_esptime());
       }

time:
  - platform: sntp
    id: sntp_time
    timezone: Europe/Kyiv
    servers:
     - 0.pool.ntp.org
     - 1.pool.ntp.org
     - 2.pool.ntp.org
    on_time_sync: 
      then:
        - component.update: feeding_date
        - switch.turn_on: time_synced

deep_sleep:
  id: deep_sleep_obj
  wakeup_pin_mode: KEEP_AWAKE
  wakeup_pin:
    number: 3
    allow_other_uses: true 

globals:
  - id: next_feeding_timestamp
    type: long int
    restore_value: yes
    initial_value: '1681182000'

datetime:
  - platform: template
    id: feeding_date
    type: datetime
    name: "Next feeding date"
    lambda: |-
      char str[17];
      strftime(str, sizeof(str), "%Y-%m-%d %H:%M", localtime(&id(next_feeding_timestamp)));
      ESPTime esp_time;
      ESPTime::strptime(str, esp_time);
      return esp_time;
    set_action:
      lambda: |-
        x.recalc_timestamp_local();
        // ESP_LOGD("TIME", "%d",x.timestamp);
        id(next_feeding_timestamp) = x.timestamp;
        id(feeding_date).update();

  - platform: template
    id: time_diff
    type: time
    name: "Time between feedings"
    optimistic: yes
    initial_value: "12:00"
    restore_value: true

switch:
  - platform: template
    id: sleep_switch
    optimistic: true
    restore_mode: ALWAYS_OFF
    name: "Sleep"
    turn_on_action:
      if:
        condition:
          - binary_sensor.is_off: wake_binary_sensor
        then:
          - lambda: !lambda |-
              long int next = id(next_feeding_timestamp) - id(sntp_time).now().timestamp;
              if(next < 0){
                  auto diff = id(time_diff).state_as_esptime();
                  long int seconds = diff.hour * 3600 + diff.minute * 60 + diff.second;
                  id(next_feeding_timestamp) = id(next_feeding_timestamp) + seconds;
                  id(feeding_date).update();
                  next = id(next_feeding_timestamp) - id(sntp_time).now().timestamp; 
              }

              id(oled_42).turn_off();
              id(deep_sleep_obj).set_sleep_duration(next * 1000);
              id(deep_sleep_obj).begin_sleep(false);

  - platform: template
    id: step_switch
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF 
    name: "Half step"

  - platform: template
    id: double_switch
    optimistic: true
    restore_mode: ALWAYS_OFF 

  - platform: template
    id: time_synced
    optimistic: true
    restore_mode: ALWAYS_OFF
    on_turn_on: 
      then:
        if:
          condition:
            - binary_sensor.is_off: wake_binary_sensor
          then:
            if:
              condition:
                - lambda: "return id(sntp_time).now().timestamp >= id(next_feeding_timestamp);"
              then:
                - button.press: feed_button
              else:
                - switch.turn_on: sleep_switch

  - platform: gpio
    id: motor_switch
    name: "Motor switch"
    pin:
      number: 7
    on_turn_on :
      - delay: 3s
      - if:
          condition:
            - switch.is_on: motor_switch
          then:
            - switch.turn_off: motor_switch
            # TODO: show error

  - platform: gpio
    id: board_led
    name: "Board led"
    pin:
      inverted: true
      number: 8

button:
  - platform: template
    id: feed_button
    on_press:
      then:
        - if:
            condition:
              - switch.is_on: time_synced
            then:
              - lambda: |-
                  auto diff = id(time_diff).state_as_esptime();
                  int seconds = diff.hour * 3600 + diff.minute * 60 + diff.second;
                  id(next_feeding_timestamp) = id(next_feeding_timestamp) + seconds;
                  id(feeding_date).update();
              - switch.turn_on: motor_switch

binary_sensor:
  - platform: gpio
    pin:
      number: 3
      mode:
        input: true
        pulldown: True
      allow_other_uses: true 
    id: wake_binary_sensor
    name: "Wake button"
    filters:
      - delayed_on: 10ms

  - platform: gpio
    pin:
      number: 4
      mode:
        input: true
        pulldown: True
    id: feed_binary_sensor
    name: "Feed button"
    filters:
      - delayed_on: 10ms
    on_click:
      then:
        - button.press: feed_button

  - platform: gpio
    pin:
      mode:
        input: true
        pulldown: True
      number: 10
    id: endstop
    name: "Endstop"
    on_press:
      - if:
          condition:
            switch.is_on: step_switch
          then:
            - switch.turn_off: motor_switch
            - switch.turn_on: sleep_switch
          else:
            if:
              condition:
                - switch.is_off: double_switch
              then:
                - switch.turn_on: double_switch
              else:
                - switch.turn_off: motor_switch
                - switch.turn_off: double_switch
                - switch.turn_on: sleep_switch

sensor:
  - platform: adc
    pin: 0
    name: "VBat"
    id: v_bat
    update_interval: 5s
    attenuation: 11db
    filters:
      - multiply: 2

  - platform: template
    name: "Battery %"
    id: bat_percent
    lambda: return id(v_bat).state;
    update_interval: 5s
    accuracy_decimals: 0
    unit_of_measurement: "%"
    icon: mdi:battery-medium
    filters:
      - calibrate_linear:
         method: exact
         datapoints:
          - 0.00 -> 0.0
          - 3.30 -> 1.0
          - 3.39 -> 10.0
          - 3.75 -> 50.0
          - 4.11 -> 90.0
          - 4.20 -> 100.0
      - lambda: |-
          if (x <= 100) {
            return x;
          } else {
            return 100;
          }
          if (x < 0) {
            return 0;
          }


  - platform: template
    name: "Wakeup Cause"
    id: wakeup_cause
    accuracy_decimals: 0
    lambda: return esp_sleep_get_wakeup_cause();

    # 0 - ESP_SLEEP_WAKEUP_UNDEFINED: In case of deep sleep, reset was not caused by exit from deep sleep
    # 1 - ESP_SLEEP_WAKEUP_ALL: Not a wakeup cause, used to disable all wakeup sources with esp_sleep_disable_wakeup_source
    # 2 - ESP_SLEEP_WAKEUP_EXT0: Wakeup caused by external signal using RTC_IO
    # 3 - ESP_SLEEP_WAKEUP_EXT1: Wakeup caused by external signal using RTC_CNTL
    # 4 - ESP_SLEEP_WAKEUP_TIMER: Wakeup caused by timer
    # 5 - ESP_SLEEP_WAKEUP_TOUCHPAD: Wakeup caused by touchpad
    # 6 - ESP_SLEEP_WAKEUP_ULP: Wakeup caused by ULP program
    # 7 - ESP_SLEEP_WAKEUP_GPIO: Wakeup caused by GPIO (light sleep only on ESP32, S2 and S3)
    # 8 - ESP_SLEEP_WAKEUP_UART: Wakeup caused by UART (light sleep only)
    # 9 - ESP_SLEEP_WAKEUP_WIFI: Wakeup caused by WIFI (light sleep only)
    # 10 - ESP_SLEEP_WAKEUP_COCPU: Wakeup caused by COCPU int
    # 11 - ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG: Wakeup caused by COCPU crash
    # 12 - ESP_SLEEP_WAKEUP_BT: Wakeup caused by BT (light sleep only)

