# Curious? https://hackaday.io/project/176690-the-water-watcher
substitutions:
  node_name: waterbug
  log_level: DEBUG

  # For my own reasons, I use a static IP address for this node.
  # Things would work fine if you just used DHCP.
  wifi_ssid: !secret wifi_ssid
  wifi_password: !secret wifi_password
  static_ip: !secret waterbug_ip
  gateway: !secret wifi_gateway
  subnet:  !secret wifi_subnet
  dns1:    !secret wifi_dns1
  dns2:    !secret wifi_dns2

  ota_password: !secret ota_password
  
  # I use the node_name as the MQTT client ID and username
  mqtt_broker: !secret mqtt_broker
  mqtt_password: !secret waterbug_mqtt_password

  SCL: 'D1'  # GPIO 5
  SDA: 'D2'  # GPIO 4

  FULL_FLAME_DAZZLE_PERIOD:  '720'   # seconds
  FULL_FLAME_THRESHOLD:     '5000'   # full_spectrum reading
  PILOT_ON_THRESHOLD:          '5'   # full_spectrum reading
  
  # Need both the quoted and unquoted forms. Thanks, YAML.
  MESSAGE_FULL_FLAME: '"full_flame"'
  NAME_FULL_FLAME:     'full_flame'
  MESSAGE_DAZZLED:    '"dazzled"'
  NAME_DAZZLED:        'dazzled'
  MESSAGE_PILOT_ON:   '"pilot_on"'
  NAME_PILOT_ON:       'pilot_on'
  MESSAGE_PILOT_OFF:  '"pilot_off"'
  NAME_PILOT_OFF:      'pilot_off'
  MESSAGE_STARTUP:    '"startup"'
  NAME_STARTUP:        'startup'
  
  MQTT_TOPIC:          "waterwatcher/flame"

globals:
  # These two items are globals because we have to use them
  # across scopes in esphome, so local variables won't do it.
  
  # The message to be send to mqtt to indicate the current state
  - id: FLAME_MESSAGE
    type: std::string
  # The numeric value of the full_spectrum reading. Sent along as an MQTT
  # message for convenience so troublshooting can just watch
  # the action on the listener end.
  - id: CURRENT_FULL_SPECTRUM
    type: float

esphome:
  name: ${node_name}
  platform: ESP8266
  board: nodemcuv2

wifi:
  ssid:      ${wifi_ssid}
  password:  ${wifi_password}
  power_save_mode: high
  fast_connect: off
  manual_ip:
    static_ip: ${static_ip}
    gateway:   ${gateway}
    subnet:    ${subnet}
    dns1:      ${dns1}
    dns2:      ${dns2}

logger:
  level: ${log_level}
ota:
  password: ${ota_password}
switch:
  - platform: restart
    name: "${node_name} Reboot"

mqtt:
  id: i_mqtt
  broker: ${mqtt_broker}
  client_id: ${node_name}
  username: ${node_name}
  password: ${mqtt_password}
  reboot_timeout: 60min

i2c:
  sda: ${SDA}
  scl: ${SCL}
  scan: true
  id: i_i2c

sensor:
  - platform: bme280
    id: i_bme280
    temperature:
      name: "${node_name} Temp"
      oversampling: 16x
    pressure:
      name: "${node_name} AirPr"
    humidity:
      name: "${node_name} RHumidity"
    address: 0x76
    update_interval: 60s

    # We used uptime as an easy way to examine relative
    # times. We don't really care about the absolute values.
    # Update interval is "never" because we force update when we need it.
  - platform: uptime
    id: device_uptime
    name: "${node_name} Uptime"
    update_interval: never
    internal: true

  - platform: tsl2591
    gain: high
    integration_time: 600ms
    update_interval: 30s
    full_spectrum:
      name: "${node_name} full_spectrum"
      id: i_full_spectrum
      on_value:
        then:
          - script.execute: send_flame_status
    infrared:
      name: "${node_name} infrared"
    visible:
      name: "${node_name} visible"
    calculated_lux:
      name: "${node_name} calculated_lux"

script:
  - id: send_flame_status
    then:
      - globals.set:
          id: FLAME_MESSAGE
          # full flame is typically between 50,000 - 100,000
          # pilot is somehwere about 50-100
          #
          # After full flame, the sensor seems to be dazzled for a bit
          # and gives an artificially low 0 reading for a couple of minutes,
          # then gives a reading of 400-600 for a bit, then settles down
          # to normal pilot light values. If we get a low reading during
          # that dazzle period, we'll send the "dazzled" message. Listeners
          # can choose to treat it like one of the other states or as its
          # own distinct "dazzled" state (a presentation matter).
          value: !lambda |-
            static auto last_full_flame_time = 0;
            id(CURRENT_FULL_SPECTRUM) = id(i_full_spectrum).state;
            if (id(CURRENT_FULL_SPECTRUM) > $FULL_FLAME_THRESHOLD) {
              id(device_uptime).update();
              last_full_flame_time = id(device_uptime).state;
              return $MESSAGE_FULL_FLAME;
            }
            if (id(CURRENT_FULL_SPECTRUM) < $PILOT_ON_THRESHOLD) {
              id(device_uptime).update();
              auto now = id(device_uptime).state;
              if ((now - last_full_flame_time) < $FULL_FLAME_DAZZLE_PERIOD) {
                return $MESSAGE_DAZZLED;
              } else {
                return $MESSAGE_PILOT_OFF;
              }
            }
            return $MESSAGE_PILOT_ON;
      - logger.log: {level: INFO, format: "${node_name} '%s' full_spectrum: %.1f >>==>", args: [id(FLAME_MESSAGE).c_str(), id(CURRENT_FULL_SPECTRUM)]}
      - mqtt.publish:
          topic: $MQTT_TOPIC
          payload: !lambda "return id(FLAME_MESSAGE).c_str();"
      - mqtt.publish:
          topic: $MQTT_TOPIC
          payload: !lambda |-
            static char buff[100];
            snprintf(buff, sizeof(buff), "full_spectrum: %.1f ${node_name}", id(CURRENT_FULL_SPECTRUM));
            return buff;
