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.
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).
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