When I started the current iteration of this project, I already had a mostly-functional project, with the exception of how I kept track of the keg volumes. I was using strain-gauges of the same type used in bathroom scales for quite a while, with okay results outside of 2 main issues: The first is simply that when I originally built the Keezer, I didn’t plan for scales and thus had to make some modifications in order to accommodate the extra height added by the scales; my fix was a bit hacky but worked well enough. The bigger issue comes down to the simple fact of what a Keezer is; a freezer with a temperature controller to keep it a bit ABOVE freezing. Normally in a freezer, condensate is simply turned into ice that periodically has to be removed by hand, but in my case it just pools in the bottom of the freezer and has to be mopped out every few weeks (more in the summer, less in the winter). It can get a little gross, but the kegs don’t particularly mind (and most importantly, it doesn’t come into contact with the beer). Funny thing is, strain-gauges don’t really like sitting in water (at least when they’re not corrosion resistant). This meant that as time went on, the strain-gauges slowly succumbed to the elements and became less accurate (or more precisely, their values tended to drift over time).

I did a bit of research on how to address these shortcomings over the years, and ultimately, I decided to go with some inexpensive flowrate meters. Not laboratory accurate, but presumably good enough for my needs. After buying one and testing it out over a few weeks at different carbonation pressures (I was a little concerned the carbonation might skew the readings, which it mostly didn’t), I went ahead and bought enough for all of my kegs and began building out the new code. Since I had originally used a Particle Photon as the microcontroller, all the code was written in a variation of C++. Pretty simple, but nothing compared to the simplicity of ESPHome. The first thing I did was write all of the YAML (simple, but surprisingly it still added up). Over a few days, I slowly built up my ‘virtual’ Keezer until it seemed to function the way I wanted.

In addition to switching to flowmeters, I decided to add a pressure sensor (located after the regulator on my CO2 tank, but before the regulators I have for each keg). Normally this part of the lines stays at a steady 40psi (the max pressure I ever want to provide to any keg), and while I left a scale on the tank, this allows me to both tell when the tank is COMPLETELY empty as well as troubleshoot other issues without opening the Keezer (Did I shut the CO2 tank off when I swapped it out and forget to turn it back on? Or is there something else wrong?). Far from a necessity, but I previously had a pressure sensor which I found useful until it failed. I also decided to add a relay and directly regulate the temperature/control the freezer instead of using a standalone temperature controller. ESPHome just makes this so easy; when using C++ I had considered this but was always afraid I would miss an edge case and damage the compressor. With ESPHome and a few lines of YAML, I can define exactly how I want it to operate and feel confident that at least on the software side of things, it will operate the way I intend. The remaining upgrades I made included adding individually addressable LEDs (instead of just white) under the taps, as well as replacing the screen (an old laptop screen connected to a Raspberry Pi) with a tablet that shows a dashboard from Home Assistant (instead of an HTML file I was using previously).

While I previously had a temperature sensor just for data, it was a simple analog one designed to be placed on a circuit board that I just soldered to some wire. I decided to replace and upgrade it as well, using a digital sensor (dallas) actually designed for placement in a wet environment. Nothing fancy, but it’s little improvements like this that make the finished product feel, well, finished.

In my previous build I had solenoid valves on each tap; I left them in place and simply cleaned up the wiring before integrating them into my new board. I also left the weight sensor I had made for the CO2 tank, as it’s not in a wet environment and I never had any issues with it. All that was required was to salvage one of the HX711 amplifier boards from the old PCB for use in my new one. Additionally, I previously had some LEDs installed on the interior for when the Keezer is open which were simply wired to a doorswitch (same as is used for closet doors to make the lights come on automatically). While this worked just fine, I decided to connect the door sensor and the LEDs to my new PCB. Entirely unnecessary, but hey, this whole project is exactly that and you never know when one might want to automate something based on opening the inside of the Keezer.

The physical upgrade process was straightforward, but as these types of projects always go, took me a bit longer than I anticipated. Over the course of a few weeks, I slowly pulled all the old electronics out and began to add the new. Somewhat surprisingly, I didn’t run into anything particularly unexpected. The final piece was milling my own custom PCB for everything. It ended up being a little bit of a challenge fitting everything onto the size boards I had on-hand, but a little PCB Tetris solved those issues.

Inside of the updated Keezer
Backside of the updated Keezer
Front of Keezer, Open for Tinkering

After connecting everything up and testing this out, it was simply a matter of making up a Home Assistant dashboard that I was happy with. This is still a work in-progress, but I’m reasonably happy with what I have so far. Because I have a tablet display now, I have it running all the time displaying a weather dashboard and automatically switch it to the Keezer dashboard when it’s turned on. One thing that I always ran into previously was guests struggling to identify which beer is in each tap. While I thought a simple left to right numbering system made sense, people would still get confused. Since I used to print my own caps when I bottled, I have continued designing a simple cap/logo for each beer and always pick a color for it as well to make identifying them easier. With this already in place, I decided to take advantage of my new individually addressable LEDs and color the lights under each tap to match the beer. Time will tell whether I keep this; it’s fun but can also look a little hectic (and be confusing when the color is for instance gray or black, not to mention when a tap is currently empty).

Keezer 'Off'
Keezer Open for Business!
HA Dashboard

One thing that has changed significantly since I originally built the Keezer, is I go through less and less beer over time. I’ve never been a huge ‘beer drinker’ and have really only ever imbibed when we have friends over, but between the pandemic, kids and just life, it seems that we have occasions like this less and less. In the past, I always kept all 5 kegs full and immediately started brewing when a keg got low or ran out. Going forward, my plan is to only keep ~2 beers on tap at a given time plus likely a small keg of either homemade Ginger Beer (super-easy to make, quite tasty and great for mixed drinks) or store-bought root beer for our kids. I’m sure this will change over time, but having each beer on hand for so long made the entire hobby less fun and brewing had started to become a chore. I’m looking forward to having fun brewing new beers now that I have an updated Keezer to enjoy them from!

ESPHome YAML:

esphome:
  name: keezer
  friendly_name: Keezer

esp32:
  board: esp32dev
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"

ota:
  password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Keezer Fallback Hotspot"
    password: "xxxxxxxxxxxxxxxxx"

#Removed for Bluetooth Proxy feature
#captive_portal:

bluetooth_proxy:
  active: true

esp32_ble_tracker:


status_led:
  pin:
    number: GPIO2

button:
  - platform: restart
    name: Restart
  - platform: safe_mode
    name: Restart (Safe Mode)


# Keg 1 Pulse Counter Reset Button
  - platform: template
    name: Keg 1 Reset Pulse Counter Total
    on_press:
      - pulse_meter.set_total_pulses:
          id: keg_1_pulse_meter
          value: 0

# Keg 2 Pulse Counter Reset Button
  - platform: template
    name: Keg 2 Reset Pulse Counter Total
    on_press:
      - pulse_meter.set_total_pulses:
          id: keg_2_pulse_meter
          value: 0

# Keg 3 Pulse Counter Reset Button
  - platform: template
    name: Keg 3 Reset Pulse Counter Total
    on_press:
      - pulse_meter.set_total_pulses:
          id: keg_3_pulse_meter
          value: 0

# Keg 4 Pulse Counter Reset Button
  - platform: template
    name: Keg 4 Reset Pulse Counter Total
    on_press:
      - pulse_meter.set_total_pulses:
          id: keg_4_pulse_meter
          value: 0

# Keg 5 Pulse Counter Reset Button
  - platform: template
    name: Keg 5 Reset Pulse Counter Total
    on_press:
      - pulse_meter.set_total_pulses:
          id: keg_5_pulse_meter
          value: 0

number:

#REMOVED RESTORE VALUES TO KEEP FROM WEARING OUT THE FLASH WHILE TESTING
# Keg 1 Full Volume
  - platform: template
    name: Keg 1 Full
    optimistic: true
    id: keg_1_full_value
    icon: mdi:gauge-full
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 5
    mode: box
#    restore_value: true

# Keg 2 Full Volume
  - platform: template
    name: Keg 2 Full
    optimistic: true
    id: keg_2_full_value
    icon: mdi:gauge-full
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 5
    mode: box
#    restore_value: true

# Keg 3 Full Volume
  - platform: template
    name: Keg 3 Full
    optimistic: true
    id: keg_3_full_value
    icon: mdi:gauge-full
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 5
    mode: box
#    restore_value: true

# Keg 4 Full Volume
  - platform: template
    name: Keg 4 Full
    optimistic: true
    id: keg_4_full_value
    icon: mdi:gauge-full
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 5
    mode: box
#    restore_value: true

# Keg 5 Full Volume
  - platform: template
    name: Keg 5 Full
    optimistic: true
    id: keg_5_full_value
    icon: mdi:gauge-full
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 1.5
    mode: box
#    restore_value: true


# Keg 1 Consumed (Only updated on Keezer Shutdown)
  - platform: template
    name: Keg 1 Consumed LTS
    optimistic: true
    id: keg_1_consumed_value_lts
    icon: mdi:gauge
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 0
    mode: box
#    restore_value: true

# Keg 2 Consumed (Only updated on Keezer Shutdown)
  - platform: template
    name: Keg 2 Consumed LTS
    optimistic: true
    id: keg_2_consumed_value_lts
    icon: mdi:gauge
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 0
    mode: box
#    restore_value: true

# Keg 3 Consumed (Only updated on Keezer Shutdown)
  - platform: template
    name: Keg 3 Consumed LTS
    optimistic: true
    id: keg_3_consumed_value_lts
    icon: mdi:gauge
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 0
    mode: box
#    restore_value: true

# Keg 4 Consumed (Only updated on Keezer Shutdown)
  - platform: template
    name: Keg 4 Consumed LTS
    optimistic: true
    id: keg_4_consumed_value_lts
    icon: mdi:gauge
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 0
    mode: box
#    restore_value: true

# Keg 5 Consumed (Only updated on Keezer Shutdown)
  - platform: template
    name: Keg 5 Consumed LTS
    optimistic: true
    id: keg_5_consumed_value_lts
    icon: mdi:gauge
    device_class: volume_storage
    unit_of_measurement: gal
    max_value: 6
    min_value: 0
    step: 0.01
    initial_value: 0
    mode: box
#    restore_value: true


# Keg 1 Pulse Conversion
  - platform: template
    name: Keg 1 Pulse Conversion
    optimistic: true
    id: keg_1_pulse_conversion
    icon: mdi:pulse
    unit_of_measurement: pulses/gal
    max_value: 5000
    min_value: 0
    step: 1
    initial_value: 2566
    mode: box
#    restore_value: true

# Keg 2 Pulse Conversion
  - platform: template
    name: Keg 2 Pulse Conversion
    optimistic: true
    id: keg_2_pulse_conversion
    icon: mdi:pulse
    unit_of_measurement: pulses/gal
    max_value: 5000
    min_value: 0
    step: 1
    initial_value: 2566
    mode: box
#    restore_value: true

# Keg 3 Pulse Conversion
  - platform: template
    name: Keg 3 Pulse Conversion
    optimistic: true
    id: keg_3_pulse_conversion
    icon: mdi:pulse
    unit_of_measurement: pulses/gal
    max_value: 5000
    min_value: 0
    step: 1
    initial_value: 2566
    mode: box
#    restore_value: true

# Keg 4 Pulse Conversion
  - platform: template
    name: Keg 4 Pulse Conversion
    optimistic: true
    id: keg_4_pulse_conversion
    icon: mdi:pulse
    unit_of_measurement: pulses/gal
    max_value: 5000
    min_value: 0
    step: 1
    initial_value: 2566
    mode: box
#    restore_value: true

# Keg 5 Pulse Conversion
  - platform: template
    name: Keg 5 Pulse Conversion
    optimistic: true
    id: keg_5_pulse_conversion
    icon: mdi:pulse
    unit_of_measurement: pulses/gal
    max_value: 5000
    min_value: 0
    step: 1
    initial_value: 2566
    mode: box
#    restore_value: true


switch:
# Keezer Switch  
  - platform: template
    name: Keezer
    id: keezer_switch
    icon: mdi:glass-mug-variant
    optimistic: true
    turn_on_action:


#      - if:
#          condition:
#            - light.is_off: keezer_lights
#          then:
#Tap 1 LEDs
#            - light.turn_on:
#                id: tap_1_leds
#                brightness: 100%
#                red: 95%
#                green: 37%
#                blue: 71%

#Tap 3 LEDs
#            - light.turn_on:
#                id: tap_3_leds
#                brightness: 100%
#                red: 0%
#                green: 69%
#                blue: 31%

#Tap 4 LEDs
#            - light.turn_on:
#                id: tap_4_leds
#                brightness: 100%
#                red: 50%
#                green: 50%
#                blue: 50%
#          else:

#Tap 1 LEDs
#      - light.turn_on:
#          id: tap_1_leds
#          brightness: 100%
#          red: 95%
#          green: 37%
#          blue: 71%

#Tap 3 LEDs
#      - light.turn_on:
#          id: tap_3_leds
#          brightness: 100%
#          red: 0%
#          green: 69%
#          blue: 31%

#Tap 4 LEDs
#      - light.turn_on:
#          id: tap_4_leds
#          brightness: 100%
#          red: 50%
#          green: 50%
#          blue: 50%


      - light.turn_on:
          id: keezer_lights
          brightness: 100%
          red: 0%
          green: 5%
          blue: 100%
      - switch.turn_on: keg_valves
      - pulse_meter.set_total_pulses:
          id: keg_1_pulse_meter
          value: 0
      - pulse_meter.set_total_pulses:
          id: keg_2_pulse_meter
          value: 0
      - pulse_meter.set_total_pulses:
          id: keg_3_pulse_meter
          value: 0
      - pulse_meter.set_total_pulses:
          id: keg_4_pulse_meter
          value: 0
      - pulse_meter.set_total_pulses:
          id: keg_5_pulse_meter
          value: 0

    turn_off_action:


#      - light.turn_off:
#          id: tap_1_leds
#      - light.turn_off:
#          id: tap_2_leds
#      - light.turn_off:
#          id: tap_3_leds
#      - light.turn_off:
#          id: tap_4_leds
#      - light.turn_off:
#          id: tap_5_leds


      - light.turn_off:
          id: keezer_lights
      - switch.turn_off: keg_valves
      - number.set:
          id: keg_1_consumed_value_lts
          value: !lambda "return (id(keg_1_consumed_value_lts).state + (id(keg_1_poured_volume).state * 0.0078125));"
      - number.set:
          id: keg_2_consumed_value_lts
          value: !lambda "return (id(keg_2_consumed_value_lts).state + (id(keg_2_poured_volume).state * 0.0078125));"
      - number.set:
          id: keg_3_consumed_value_lts
          value: !lambda "return (id(keg_3_consumed_value_lts).state + (id(keg_3_poured_volume).state * 0.0078125));"
      - number.set:
          id: keg_4_consumed_value_lts
          value: !lambda "return (id(keg_4_consumed_value_lts).state + (id(keg_4_poured_volume).state * 0.0078125));"
      - number.set:
          id: keg_5_consumed_value_lts
          value: !lambda "return (id(keg_5_consumed_value_lts).state + (id(keg_5_poured_volume).state * 0.0078125));"
      - pulse_meter.set_total_pulses:
          id: keg_1_pulse_meter
          value: 0
      - pulse_meter.set_total_pulses:
          id: keg_2_pulse_meter
          value: 0
      - pulse_meter.set_total_pulses:
          id: keg_3_pulse_meter
          value: 0
      - pulse_meter.set_total_pulses:
          id: keg_4_pulse_meter
          value: 0
      - pulse_meter.set_total_pulses:
          id: keg_5_pulse_meter
          value: 0

# Keg Valves
  - platform: gpio
    pin: GPIO19
    name: Keg Valves
    id: keg_valves
    device_class: switch
    icon: mdi:pipe-valve

# Freezer Relay
  - platform: gpio
    pin: GPIO26
    name: Freezer Relay
    id: freezer_relay
    device_class: switch
    icon: mdi:snowflake-thermometer

text_sensor:
  - platform: wifi_info
    ip_address:
      name: IP Address

sensor:
# Send WiFi signal strength & uptime to HA
  - platform: wifi_signal
    name: WiFi Strength
    update_interval: 60s
    disabled_by_default: true
  - platform: uptime
    name: Uptime
    disabled_by_default: true


# Keg 1 Pulse Meter
  - platform: pulse_meter
    pin: GPIO4
    name: Keg 1 Pulse Meter
    id: keg_1_pulse_meter
    internal_filter: 20us     #Default is 13us. Used 20, 10, 5, 2 with Pulse and no noise.
    internal_filter_mode: PULSE
    timeout: 1s
    total:
      name: Keg 1 Total Pulses
      id: keg_1_pulse_meter_total

# Keg 2 Pulse Meter
  - platform: pulse_meter
    pin: GPIO13
    name: Keg 2 Pulse Meter
    id: keg_2_pulse_meter
    internal_filter: 20us     #Default is 13us. Used 20, 10, 5, 2 with Pulse and no noise.
    internal_filter_mode: PULSE
    timeout: 1s
    total:
      name: Keg 2 Total Pulses
      id: keg_2_pulse_meter_total

# Keg 3 Pulse Meter
  - platform: pulse_meter
    pin: GPIO16
    name: Keg 3 Pulse Meter
    id: keg_3_pulse_meter
    internal_filter: 20us     #Default is 13us. Used 20, 10, 5, 2 with Pulse and no noise.
    internal_filter_mode: PULSE
    timeout: 1s
    total:
      name: Keg 3 Total Pulses
      id: keg_3_pulse_meter_total

# Keg 4 Pulse Meter
  - platform: pulse_meter
    pin: GPIO17
    name: Keg 4 Pulse Meter
    id: keg_4_pulse_meter
    internal_filter: 20us     #Default is 13us. Used 20, 10, 5, 2 with Pulse and no noise.
    internal_filter_mode: PULSE
    timeout: 1s
    total:
      name: Keg 4 Total Pulses
      id: keg_4_pulse_meter_total

# Keg 5 Pulse Meter
  - platform: pulse_meter
    pin: GPIO18
    name: Keg 5 Pulse Meter
    id: keg_5_pulse_meter
    internal_filter: 20us     #Default is 13us. Used 20, 10, 5, 2 with Pulse and no noise.
    internal_filter_mode: PULSE
    timeout: 1s
    total:
      name: Keg 5 Total Pulses
      id: keg_5_pulse_meter_total


# Keg 1 Percent Full
  - platform: template
    name: Keg 1 Percent Full
    id: keg_1_percent_full
    state_class: measurement
    unit_of_measurement: "%"
    icon: mdi:keg
    lambda: |-
      if (id(keezer_switch).state and (id(keg_1_pulse_meter_total).state > 10)) {
        return (((id(keg_1_full_value).state - (id(keg_1_consumed_value_lts).state + (id(keg_1_pulse_meter_total).state * (1 / id(keg_1_pulse_conversion).state)))) / (id(keg_1_full_value).state)) * 100);
      } else {
        return (((id(keg_1_full_value).state - id(keg_1_consumed_value_lts).state) / (id(keg_1_full_value).state)) * 100);
      }
    update_interval: 1s

# Keg 2 Percent Full
  - platform: template
    name: Keg 2 Percent Full
    id: keg_2_percent_full
    state_class: measurement
    unit_of_measurement: "%"
    icon: mdi:keg
    lambda: |-
      if (id(keezer_switch).state and (id(keg_2_pulse_meter_total).state > 10)) {
        return (((id(keg_2_full_value).state - (id(keg_2_consumed_value_lts).state + (id(keg_2_pulse_meter_total).state * (1 / id(keg_2_pulse_conversion).state)))) / (id(keg_2_full_value).state)) * 100);
      } else {
        return (((id(keg_2_full_value).state - id(keg_2_consumed_value_lts).state) / (id(keg_2_full_value).state)) * 100);
      }
    update_interval: 1s

# Keg 3 Percent Full
  - platform: template
    name: Keg 3 Percent Full
    id: keg_3_percent_full
    state_class: measurement
    unit_of_measurement: "%"
    icon: mdi:keg
    lambda: |-
      if (id(keezer_switch).state and (id(keg_3_pulse_meter_total).state > 10)) {
        return (((id(keg_3_full_value).state - (id(keg_3_consumed_value_lts).state + (id(keg_3_pulse_meter_total).state * (1 / id(keg_3_pulse_conversion).state)))) / (id(keg_3_full_value).state)) * 100);
      } else {
        return (((id(keg_3_full_value).state - id(keg_3_consumed_value_lts).state) / (id(keg_3_full_value).state)) * 100);
      }
    update_interval: 1s

# Keg 4 Percent Full
  - platform: template
    name: Keg 4 Percent Full
    id: keg_4_percent_full
    state_class: measurement
    unit_of_measurement: "%"
    icon: mdi:keg
    lambda: |-
      if (id(keezer_switch).state and (id(keg_4_pulse_meter_total).state > 10)) {
        return (((id(keg_4_full_value).state - (id(keg_4_consumed_value_lts).state + (id(keg_4_pulse_meter_total).state * (1 / id(keg_4_pulse_conversion).state)))) / (id(keg_4_full_value).state)) * 100);
      } else {
        return (((id(keg_4_full_value).state - id(keg_4_consumed_value_lts).state) / (id(keg_4_full_value).state)) * 100);
      }
    update_interval: 1s

# Keg 5 Percent Full
  - platform: template
    name: Keg 5 Percent Full
    id: keg_5_percent_full
    state_class: measurement
    unit_of_measurement: "%"
    icon: mdi:keg
    lambda: |-
      if (id(keezer_switch).state and (id(keg_5_pulse_meter_total).state > 10)) {
        return (((id(keg_5_full_value).state - (id(keg_5_consumed_value_lts).state + (id(keg_5_pulse_meter_total).state * (1 / id(keg_5_pulse_conversion).state)))) / (id(keg_5_full_value).state)) * 100);
      } else {
        return (((id(keg_5_full_value).state - id(keg_5_consumed_value_lts).state) / (id(keg_5_full_value).state)) * 100);
      }
    update_interval: 1s


# Keg 1 Poured Volume
  - platform: template
    name: Keg 1 Poured Volume
    id: keg_1_poured_volume
    device_class: volume_storage
    state_class: measurement
    unit_of_measurement: fl. oz.
    accuracy_decimals: 1
    icon: mdi:beer
    lambda: |-
      if (id(keezer_switch).state and (id(keg_1_pulse_meter_total).state > 10)) {
        return ((id(keg_1_pulse_meter_total).state * (1 / id(keg_1_pulse_conversion).state)) * 128);
      } else {
        return 0;
      }
    update_interval: 1s

# Keg 2 Poured Volume
  - platform: template
    name: Keg 2 Poured Volume
    id: keg_2_poured_volume
    device_class: volume_storage
    state_class: measurement
    unit_of_measurement: fl. oz.
    accuracy_decimals: 1
    icon: mdi:beer
    lambda: |-
      if (id(keezer_switch).state and (id(keg_2_pulse_meter_total).state > 10)) {
        return ((id(keg_2_pulse_meter_total).state * (1 / id(keg_2_pulse_conversion).state)) * 128);
      } else {
        return 0;
      }
    update_interval: 1s

# Keg 3 Poured Volume
  - platform: template
    name: Keg 3 Poured Volume
    id: keg_3_poured_volume
    device_class: volume_storage
    state_class: measurement
    unit_of_measurement: fl. oz.
    accuracy_decimals: 1
    icon: mdi:beer
    lambda: |-
      if (id(keezer_switch).state and (id(keg_3_pulse_meter_total).state > 10)) {
        return ((id(keg_3_pulse_meter_total).state * (1 / id(keg_3_pulse_conversion).state)) * 128);
      } else {
        return 0;
      }
    update_interval: 1s

# Keg 4 Poured Volume
  - platform: template
    name: Keg 4 Poured Volume
    id: keg_4_poured_volume
    device_class: volume_storage
    state_class: measurement
    unit_of_measurement: fl. oz.
    accuracy_decimals: 1
    icon: mdi:beer
    lambda: |-
      if (id(keezer_switch).state and (id(keg_4_pulse_meter_total).state > 10)) {
        return ((id(keg_4_pulse_meter_total).state * (1 / id(keg_4_pulse_conversion).state)) * 128);
      } else {
        return 0;
      }
    update_interval: 1s

# Keg 5 Poured Volume
  - platform: template
    name: Keg 5 Poured Volume
    id: keg_5_poured_volume
    device_class: volume_storage
    state_class: measurement
    unit_of_measurement: fl. oz.
    accuracy_decimals: 1
    icon: mdi:beer
    lambda: |-
      if (id(keezer_switch).state and (id(keg_5_pulse_meter_total).state > 10)) {
        return ((id(keg_5_pulse_meter_total).state * (1 / id(keg_5_pulse_conversion).state)) * 128);
      } else {
        return 0;
      }
    update_interval: 1s


# Keg 1 Flowrate
  - platform: template
    name: Keg 1 Flowrate
    id: keg_1_flowrate
    device_class: volume_flow_rate
    state_class: measurement
    unit_of_measurement: gal/min
    accuracy_decimals: 2
    icon: mdi:water-pump
    lambda: |-
      if (id(keezer_switch).state) {
        return ((id(keg_1_pulse_meter).state * (1 / id(keg_1_pulse_conversion).state)));
      } else {
        return 0;
      }
    update_interval: 1s

# Keg 2 Flowrate
  - platform: template
    name: Keg 2 Flowrate
    id: keg_2_flowrate
    device_class: volume_flow_rate
    state_class: measurement
    unit_of_measurement: gal/min
    accuracy_decimals: 2
    icon: mdi:water-pump
    lambda: |-
      if (id(keezer_switch).state) {
        return ((id(keg_2_pulse_meter).state * (1 / id(keg_2_pulse_conversion).state)));
      } else {
        return 0;
      }
    update_interval: 1s

# Keg 3 Flowrate
  - platform: template
    name: Keg 3 Flowrate
    id: keg_3_flowrate
    device_class: volume_flow_rate
    state_class: measurement
    unit_of_measurement: gal/min
    accuracy_decimals: 2
    icon: mdi:water-pump
    lambda: |-
      if (id(keezer_switch).state) {
        return ((id(keg_3_pulse_meter).state * (1 / id(keg_3_pulse_conversion).state)));
      } else {
        return 0;
      }
    update_interval: 1s

# Keg 4 Flowrate
  - platform: template
    name: Keg 4 Flowrate
    id: keg_4_flowrate
    device_class: volume_flow_rate
    state_class: measurement
    unit_of_measurement: gal/min
    accuracy_decimals: 2
    icon: mdi:water-pump
    lambda: |-
      if (id(keezer_switch).state) {
        return ((id(keg_4_pulse_meter).state * (1 / id(keg_4_pulse_conversion).state)));
      } else {
        return 0;
      }
    update_interval: 1s

# Keg 5 Flowrate
  - platform: template
    name: Keg 5 Flowrate
    id: keg_5_flowrate
    device_class: volume_flow_rate
    state_class: measurement
    unit_of_measurement: gal/min
    accuracy_decimals: 2
    icon: mdi:water-pump
    lambda: |-
      if (id(keezer_switch).state) {
        return ((id(keg_5_pulse_meter).state * (1 / id(keg_5_pulse_conversion).state)));
      } else {
        return 0;
      }
    update_interval: 1s


# Gas Weight RAW Sensor
  - platform: hx711
#    name: Gas Value RAW
#    id: gas_value_raw
    name: Gas Percent Full
    id: gas_percent_full
    state_class: measurement
    unit_of_measurement: "%"
    accuracy_decimals: 1
    dout_pin: GPIO21
    clk_pin: GPIO22
    update_interval: 1s #Used 0.1s for Beehive

    filters:

# Used this avergae for the Beehive
      - sliding_window_moving_average:
          window_size: 600
          send_every: 15
          send_first_at: 15

# From Particle Code
# Gas_Empty_Calibration=7213399; 
# Gas_Full_Calibration=7335977;
# Actual readings are showing as negative
      - calibrate_linear:
         method: least_squares
         datapoints:
          - -7213399 -> 0
          - -7335977 -> 100


# Temperature Sensor
  - platform: dallas
    address: 0xff3190d4454bfa28
    name: Keezer Temperature
    id: keezer_temperature


# Pressure Transducer
  - platform: adc
    pin: GPIO39
    name: Gas Pressure
    icon: mdi:gas-cylinder
    unit_of_measurement: psi
    device_class: pressure
    state_class: measurement
    accuracy_decimals: 1
    update_interval: 1s
    attenuation: auto
    filters:
#      - median:
#          window_size: 11
#          send_every: 11
#          send_first_at: 11

      - sliding_window_moving_average:
          window_size: 15
          send_every: 15
          send_first_at: 15

      - calibrate_linear:
         method: least_squares
         datapoints:
# Sensor advertises 0.5V=0psi & 4.5V=100psi. These values are from testing (using the analog gauge on the gas tank):
          - 0.28 -> 0
          - 1.98 -> 40

      - clamp:
          min_value: 0
          max_value: 100
          ignore_out_of_range: false #This determines whether it just ignores out of range values or treats them as a max/min value


binary_sensor:

# Lid Sensor
  - platform: gpio
    pin:
      number: GPIO23
      inverted: true
      mode:
        input: true
        pullup: true
    name: Keezer Lid
    id: keezer_lid
    device_class: door
    on_press:
      then:
        - light.turn_on: interior_lights
    on_release:
      then:
        - light.turn_off: interior_lights

light:
# Interior Lights
  - platform: binary
    name: Interior Lights
    id: interior_lights
    icon: mdi:led-strip
    output: interior_lights_output


# Keezer Lights

#  - platform: neopixelbus
#    type: GRB
#    variant: WS2812
#    pin: GPIO27
#    num_leds: 37
#    name: Keezer Lights
#    id: keezer_lights
#    icon: mdi:wall-sconce-flat


  - platform: partition
    name: "Tap #1 LEDs"
    id: tap_1_leds
    segments:
      - id: keezer_lights
        from: 0
        to: 7

  - platform: partition
    name: "Tap #2 LEDs"
    id: tap_2_leds
    segments:
      - id: keezer_lights
        from: 8
        to: 14

  - platform: partition
    name: "Tap #3 LEDs"
    id: tap_3_leds
    segments:
      - id: keezer_lights
        from: 15
        to: 21

  - platform: partition
    name: "Tap #4 LEDs"
    id: tap_4_leds
    segments:
      - id: keezer_lights
        from: 22
        to: 28

  - platform: partition
    name: "Tap #5 LEDs"
    id: tap_5_leds
    segments:
      - id: keezer_lights
        from: 29
        to: 36


  - platform: esp32_rmt_led_strip
    name: Keezer Lights
    id: keezer_lights
    icon: mdi:wall-sconce-flat
    rgb_order: GRB
    is_rgbw: False
    pin: GPIO27
    num_leds: 37
    rmt_channel: 0
    chipset: WS2812
    max_refresh_rate: 200ms #16ms ~= 60Hz # 100ms seemed to help quite a bit, but still got some flicker at times
    effects:
      - addressable_color_wipe:
          name: Christmas Wipe
          colors:
            - red: 100%
              green: 0%
              blue: 0%
              white: 0%
              num_leds: 15
            - red: 0%
              green: 100%
              blue: 0%
              white: 0%
              num_leds: 15
          add_led_interval: 200ms
          reverse: false

      - addressable_color_wipe:
          name: Chiefs Wipe
          colors:
            - red: 100%
              green: 0%
              blue: 0%
              white: 0%
              num_leds: 15
            - red: 100%
              green: 87%
              blue: 0%
              white: 0%
              num_leds: 15
          add_led_interval: 200ms
          reverse: false

      - random:
          name: Random
          transition_length: 5s
          update_interval: 7s

      - addressable_rainbow:
          name: Rainbow
          speed: 10
          width: 50

      - addressable_color_wipe:
          name: Random Color Wipe
          colors:
            - random: true
              num_leds: 15
          add_led_interval: 200ms
          reverse: false


output:
# Interior Lights
  - id: interior_lights_output
    platform: gpio
    pin: GPIO25


# Freezer Temperature Controller
climate:
  - platform: thermostat
    name: Thermostat
    sensor: keezer_temperature
    visual:
      min_temperature: 32 °F
      max_temperature: 60 °F
      temperature_step: 0.5 °F
    min_cooling_off_time: 300s
    min_cooling_run_time: 120s
    min_idle_time: 300s
    cool_action:
      - switch.turn_on: freezer_relay
    idle_action:
      - switch.turn_off: freezer_relay
    default_preset: Beer
    on_boot_restore_from: memory
    preset:
      - name: Beer
        mode: COOL
        default_target_temperature_high: 40 °F


dallas:
  - pin: GPIO32
    update_interval: 15s