I used to run a Raspberry Pi to control some relays for a heater for the watering can as well as some lights to extend the “daylight” for the hens to promote laying. I’m in the process of switching to Home Assistant and ESP Home to do the same + run some heating bulbs in the coop. I’ve got several smart plugs (Kauf EP12’s) and an ESP board with some Dallas DS18b20’s. The heater bulbs seem straightforward enough to use a simple climate control. After some learning, I converted my python implementation for supplemental daylight to Home Assistant (described below). However, I could use some pointers on progressing with my water heater implementation.
I use a very typical galvanized water can for the chickens. And I purchased an inexpensive resistive heating mat (automotive engine oil pan heater) to sit under the watering can. A DS18b20 monitors air temperature near the watering can and I empirically determined how long to cycle the heater on to keep the water can from freezing. I like this approach because I don’t have to put anything but water in the container, which makes for easy cleaning. The python script is run in cron every 15 minutes, and a relay was cycled on for several minutes, inversely proportional to air temperature, and then off for the remainder of the 15 minute block of time:
#!/usr/bin/python2
#This code is intended to run with cron, I believe it should execute every 15 minutes
import os
import glob
import time
import datetime
import RPi.GPIO as GPIO
GPIO_PIN = 11
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN, GPIO.OUT)
#Enable the linux modules to read the one-wire temp devices
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
#uncomment for autodetect
#base_dir = '/sys/bus/w1/devices/'
#device_folder = [glob.glob(base_dir + '28*')[0]]#,glob.glob(base_dir + '28*')[1],glob.glob(base_dir + '28*')[2]]
#device_file = [device_folder[0] + '/w1_slave']#, device_folder[1] + '/w1_slave', device_folder[2] + '/w1_slave']
device_coop = '/sys/bus/w1/devices/######/w1_slave'
def read_temp_raw(device):
"""Pass this function either 0 or 1 to get the raw data from the
Temperature Sensor"""
f = open(device, 'r')
lines = f.readlines()
f.close()
return lines
def read_temp(device):
"""Pass 0 or 1 to get the temperature in celcius of either sensor"""
lines = read_temp_raw(device) #missing "device"
while lines[0].strip()[-3:] != 'YES':
time.sleep(0.2)
lines = read_temp_raw(device)
equals_pos = lines[1].find('t=')
if equals_pos != -1:
temp_string = lines[1][equals_pos+2:]
temp_c = float(temp_string) / 1000.0
return temp_c
while True:
try:
if (read_temp(device_coop) <= -18):
GPIO.output(GPIO_PIN,1)
print "It is sub zero!"
time.sleep(420) #7 minutes out of 15
GPIO.output(GPIO_PIN, 0)
break
elif (read_temp(device_coop) <= -4):
GPIO.output(GPIO_PIN, 1)
print "It is really cold"
time.sleep(300) #5 minutes out of 15
GPIO.output(GPIO_PIN, 0)
break
elif (read_temp(device_coop) <= 0):
GPIO.output(GPIO_PIN, 1)
print "It is a little cold"
time.sleep(120) #2 min out of 15
GPIO.output(GPIO_PIN, 0)
break
else:
GPIO.output(GPIO_PIN, 0)
print "It is warm enough"
break
#Stop on Ctrl-C and clean up GPIO pins
except KeyboardInterrupt:
GPIO.cleanup()#!/usr/bin/python
So my question is, what is a sensible way to structure similar control in Home Assistant / ESP Home? Thanks for any suggestions.
And for my supplemental lighting...
My python script from the Raspberry Pi that was checked by cron at 3 AM every day:
#!/usr/bin/python2
import time
import datetime
import RPi.GPIO as GPIO
import ephem
GPIO.setmode(GPIO.BCM)
GPIO.setup(10, GPIO.OUT, initial=GPIO.LOW)
#GPIO.output(10, 0)
light_hours = 13
sun = ephem.Sun()
home = ephem.Observer()
home.lat, home.lon = ######################
sun.compute(home)
sunrise, sunset = (ephem.localtime(home.next_rising(sun)),ephem.localtime(home.next_setting(sun)))
daylight_t = sunset - sunrise
daylight_f = (ephem.Date(sunset) - ephem.Date(sunrise))
timer_f = (ephem.Date(0 + light_hours*ephem.hour) - daylight_f)
time_on_t = ephem.Date(ephem.Date(sunrise) - timer_f)
while True:
try:
if (ephem.Date(datetime.datetime.now())) < time_on_t:
time.sleep(300)
elif (ephem.Date(datetime.datetime.now())) > ephem.Date(ephem.Date(sunrise)):
GPIO.cleanup()#!/usr/bin/python
print "The sun should have risen by now"
break
elif (ephem.Date(datetime.datetime.now())) >= time_on_t:
GPIO.output(10, 1)
print "Lights are being turned on now"
time.sleep(300)
#Stop on Ctrl-C and clean up GPIO pins
except KeyboardInterrupt:
GPIO.cleanup()#!/usr/bin/python
Based on the Sun2 add-on, I created a helper, templates, and an automation:
#input_numbers.yaml
t_chicken_daylight_hours:
name: "Minimum hours of daylight hens"
initial: 13
min: 10
max: 18
step: 0.05
mode: box
#templates.yaml
sensor:
- name: "Chicken daylight light turn ON time"
unique_id: "chicken_daylight_light_turn_on_time"
device_class: timestamp
state: >
{% set sunrise = states('sensor.sun_rising') | as_datetime | as_local %}
{% set daylight_needed = timedelta(hours=(states.input_number.t_chicken_daylight_hours.state | float)) %}
{% set daylight_actual = timedelta(hours=(states('sensor.sun_daylight') | float)) %}
{% set turn_on_time = sunrise - daylight_needed + daylight_actual %}
{{ turn_on_time }}
- name: "Chicken daylight duration deficit"
unique_id: "chicken_daylight_deficit"
state: >
{% set daylight_needed = states.input_number.t_chicken_daylight_hours.state | float %}
{% set daylight_actual = states('sensor.sun_daylight') | float %}
{% set defecit = daylight_needed - daylight_actual %}
{{ defecit | round(4) }}
- name: "Chicken daylight light turn OFF time"
unique_id: "chicken_daylight_light_turn_off_time"
device_class: timestamp
state: >
{% set sunrise = states('sensor.sun_rising') | as_datetime | as_local %}
{% set wait_time = timedelta(hours=0.5) %}
{% set turn_off_time = sunrise + wait_time %}
{{ turn_off_time}}
#automations.yaml
- id: ####
alias: Chicken Light Turn ON
description: ''
triggers:
- trigger: time
at: sensor.chicken_daylight_light_turn_on_time
conditions:
- condition: numeric_state
entity_id: sensor.chicken_daylight_duration_deficit
above: 0
- condition: time
before: sensor.chicken_daylight_light_turn_off_time
actions:
- action: switch.turn_on
metadata: {}
data: {}
target:
device_id: ####
mode: single
- id: ####
alias: Chicken Light Turn OFF
description: ''
triggers:
- trigger: time
at: sensor.chicken_daylight_light_turn_off_time
conditions:
- condition: numeric_state
entity_id: sensor.chicken_daylight_duration_deficit
above: 0
actions:
- action: switch.turn_off
metadata: {}
data: {}
target:
device_id: ####
mode: single