Skip to content

Instantly share code, notes, and snippets.

@aclud
Last active February 29, 2024 21:27
Show Gist options
  • Save aclud/8652819dab2337fbd9f4f9b947a5eaa0 to your computer and use it in GitHub Desktop.
Save aclud/8652819dab2337fbd9f4f9b947a5eaa0 to your computer and use it in GitHub Desktop.
Garage Monitor python script
#!/usr/bin/python
# uses python, raspberry pi, disassembled garage remote, and sensors
# to control garage door w/ alexa script or IFTTT to open/close/provide temp and humidity
# amount of light, time, and motion sensors used
import RPi.GPIO as io
import time
# from datetime
import datetime
import logging
from logging.handlers import RotatingFileHandler
import sys
import urllib2
EVENT = "sensor1"
BASE_URL = "https://maker.ifttt.com/trigger/"
KEY = "***REMOVED***"
def send_event():
response = urllib2.urlopen(
BASE_URL + EVENT + "/with/key/" + KEY)
print(response.read())
# Use BCM io references instead of physical pin numbers
io.setmode(io.BCM)
# Define io to use on Pi
io_PIR = 17
io_LED = 21
io_DOORSWITCH = 22
io_LIGHTSENSOR = 6
io_GARAGEREMOTE1 = 19
# Set pin in/out
io.setup(io_PIR, io.IN)
io.setup(io_LED, io.OUT, initial=0)
io.setup(io_DOORSWITCH, io.IN, pull_up_down=io.PUD_UP)
io.setup(io_GARAGEREMOTE1, io.OUT, initial=0)
# Declare variables during startup
enableConsoleDebug = True # set to true to write output to console
logFileName = 'GarageMonitor.log'
fmtDateTime = '%Y-%m-%d %H:%M:%S'
intPirState = 0 # 0=no motion, 1=motion
intPirStatePrev = 0
intDoorState = io.input(io_DOORSWITCH)
intDoorState_prev = io.input(io_DOORSWITCH)
door_open_dttm = datetime.datetime.now()
door_close_dttm = datetime.datetime.now()
pir_last_motion_detect_dttm = datetime.datetime.now()
intDelay = 0.1 # delay between checks
intLimitDoorOpenDay = 0.1 # number of minutes until the door auto-closes during the day
intLimitDoorOpenNight = 0.13 # reserved for when light sensor comes
intLimitMotionTimer = 0.16 # number of minutes for motion sensor inactivity required to allow the door to close
intLightSenseAtNight = 33333 # if function for light sensor returns > this number we know it's night time
intSleeptimeMins = 0.060 # number of minutes to pause the script if there is a problem
boolQuitbyUser = False # used to only print log line when user exits in finally: block
# Create logging object
log = logging.getLogger('GarageMonitor')
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s")
log.setLevel(logging.DEBUG)
# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(logFileName, maxBytes=1024000, backupCount=5)
handler.setFormatter(logFormatter)
log.addHandler(handler)
# Write all the messages out to console for debugging purposes
if enableConsoleDebug:
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
log.addHandler(consoleHandler)
# Function to return formatted date time
def fnDateTimeNow():
global dttmnow
dttmnow = datetime.datetime.now().strftime(fmtDateTime)
return dttmnow
# Function to wait for PIR to settle on startup
def fnReadyPIR():
global intPirState
while io.input(io_PIR) == 1:
intPirState = 1
log.info("Motion sensor ready")
# Function to return light sensor reading, input is pin #
def LightSensor(LightSensorPin):
reading = 0
io.setup(LightSensorPin, io.OUT)
io.output(LightSensorPin, io.LOW)
time.sleep(0.1)
io.setup(LightSensorPin, io.IN)
# This takes about 1 millisecond per loop cycle
while io.input(LightSensorPin) == io.LOW:
reading += 1
return reading
# Function to control garage door. action = open/close, pin = pin # door connected to, source used for debugging
def fnGarageControl(action, pin, source):
global intDoorState
intDoorState = io.input(io_DOORSWITCH)
if action == "open":
if intDoorState:
log.info("door is already open...")
elif not intDoorState:
log.info("opening door..."+source)
io.output(pin, io.HIGH)
time.sleep(0.5)
io.output(pin, io.LOW)
time.sleep(3)
if not intDoorState:
log.warning("something went wrong opening the door, pausing script for " +
str(intSleeptimeMins) + " minutes")
time.sleep(intSleeptimeMins * 60)
if action == "close":
if not intDoorState:
log.info("door is already closed...")
elif intDoorState:
log.info("closing door..."+source)
io.output(pin, io.HIGH)
time.sleep(0.5)
io.output(pin, io.LOW)
time.sleep(3) # check the door again in 30 seconds
intDoorState = io.input(io_DOORSWITCH)
if intDoorState:
log.warning("something went wrong closing the door, pausing script for " +
str(intSleeptimeMins) + " minutes.")
time.sleep(intSleeptimeMins * 60)
# Function to tell how long the door was in prior state
def fnTimer(d1, d2, source): # source exists for debugging purposes
if d1 < d2:
da = d1
db = d2
elif d2 < d1:
da = d2
db = d1
else:
return 0
# figure out difference and return it
delta = db - da
# return 0 if total seconds is less than 1
if delta.total_seconds() < 1:
return 0
else:
return delta.total_seconds() / 60
try:
# loop until PIR ready
fnReadyPIR()
# main loop
while True:
# Check door state
intDoorState = io.input(io_DOORSWITCH)
# if door is open
if intDoorState:
if intDoorState_prev != intDoorState: # door state changed
intDoorState_prev = intDoorState # set state for next loop
door_open_dttm = datetime.datetime.now()
log.info("door is open at "+fnDateTimeNow())
# daytime/nighttime difference in time to close
if LightSensor(io_LIGHTSENSOR) < intLightSenseAtNight:
# if the door's open for longer than it should be during the day
if fnTimer(door_open_dttm, datetime.datetime.now(), "door") > intLimitDoorOpenDay:
# only close when no movement for x time
if fnTimer(pir_last_motion_detect_dttm, datetime.datetime.now(), "motion") > intLimitMotionTimer:
fnGarageControl("close", io_GARAGEREMOTE1, "LightSensorDaytime")
else:
# if the door's open for longer than it should be during the night
if fnTimer(door_open_dttm, datetime.datetime.now(), "door") > intLimitDoorOpenNight:
# only close when no movement for x time
if fnTimer(pir_last_motion_detect_dttm, datetime.datetime.now(), "motion") > intLimitMotionTimer:
fnGarageControl("close", io_GARAGEREMOTE1, "LightSensorNighttime")
# if door is closed
elif not intDoorState:
if intDoorState_prev != intDoorState: # door state changed
intDoorState_prev = intDoorState # set state for next loop
door_close_dttm = datetime.datetime.now()
log.info("door is closed at "+fnDateTimeNow())
intPirState = io.input(io_PIR)
# print "pir state is currently ",intPirState
if intPirState:
# set motion detect date/time for auto-close timer
pir_last_motion_detect_dttm = datetime.datetime.now()
if intPirState == 1 and intPirStatePrev == 0:
# strMsg = "motion detect at ", fnDateTimeNow()
log.info("motion detected at "+fnDateTimeNow())
io.output(io_LED, io.HIGH)
# time.sleep(intDelay)
intPirStatePrev = 1
elif intPirState == 0 and intPirStatePrev == 1:
log.info("back to sleep at "+fnDateTimeNow())
io.output(io_LED, io.LOW)
# time.sleep(intDelay)
intPirStatePrev = 0
time.sleep(intDelay)
except KeyboardInterrupt:
log.info("Quit by ctrl-c")
boolQuitbyUser = True
except Exception:
exc_type, exc_obj, exc_tb = sys.exc_info()
log.error("Caught Exception at line " + str(exc_tb.tb_lineno) + ": " + str(sys.exc_info()))
# todo: send alert or email w/ detail before dying
finally:
if not boolQuitbyUser:
log.info("Quit by script exit")
io.cleanup()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment