Last active
February 29, 2024 21:27
-
-
Save aclud/8652819dab2337fbd9f4f9b947a5eaa0 to your computer and use it in GitHub Desktop.
Garage Monitor python script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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