Skip to content

Instantly share code, notes, and snippets.

@schester44
Created May 8, 2020 00:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schester44/70fbcae7695972a27d3156f3391496e4 to your computer and use it in GitHub Desktop.
Save schester44/70fbcae7695972a27d3156f3391496e4 to your computer and use it in GitHub Desktop.
RaspberryPi + NodeJS Garage Door Opener
/**
* This file should be a repo but oh well..
* This code will check a magnetic sensor value and send a text message via twilio if the circuit remains open for an extended amount of time
* Used Koa for an http server so I could read the current state via clients.
**/
const Koa = require("koa")
const Router = require("koa-router")
const rpio = require("rpio")
const cors = require("@koa/cors")
const Twilio = require("twilio")
const { getHours, isWeekend, isBefore, subSeconds, differenceInSeconds } = require("date-fns")
const bodyParser = require("koa-bodyparser")
const app = new Koa()
const router = new Router()
const ALLOWED_LIMIT = 60
const twilioConfig = {
ACCOUNT_SID: "",
AUTH_TOKEN: "",
NUMBER: ""
}
const twilio = new Twilio(twilioConfig.ACCOUNT_SID, twilioConfig.AUTH_TOKEN)
const getDoorStatus = () => (rpio.read(12) ? "open" : "closed")
const OPENER_PIN = 7
// sensor
rpio.open(12, rpio.INPUT, rpio.PULL_UP)
// opener
rpio.open(OPENER_PIN, rpio.OUTPUT, rpio.LOW);
let settings = {
textEnabled: true,
overrideEnabled: false
}
let status = {
status: getDoorStatus(),
lastCheck: new Date(),
lastAlertSent: undefined,
lastOpen: undefined
}
const shouldSendText = () => {
// dont send if we manually disabled it
if (!settings.textEnabled) return false
const now = new Date()
const currentHour = getHours(now)
// send if weekday between midnight and 6pm
let isValidTime = currentHour < 18
let isWeek = !isWeekend(now)
return (isValidTime && isWeek) || settings.overrideEnabled
}
const getNewStatus = current => {
let newStatus = { ...current, status: getDoorStatus(), lastCheck: new Date() }
// if the door was closed but now its open then the last time it was opened was just now.
if (current.status === "closed" && newStatus.status == "open") {
newStatus.lastOpen = status.lastCheck
}
return newStatus
}
const doSendAlertCheck = async current => {
// the current time minus the allowed limit is our deadline. If the lastOpen time is before that time then we know its been too long.
const deadline = subSeconds(new Date(), ALLOWED_LIMIT)
const doorNeedsShut = isBefore(current.lastOpen, deadline)
if (!doorNeedsShut) {
return current.lastAlertSent
}
// Don't send a message if we've already sent one since the door has been opened.
// The only time lastAlertSent should exist is if the door is still open.
if (current.lastAlertSent) {
return current.lastAlertSent
}
if (!shouldSendText()) return
console.log(`Sending text message. The door has been open longer than ${ALLOWED_LIMIT} seconds.`)
await twilio.messages.create({
body: "The garage door is open.",
to: twilioConfig.NUMBER,
from: twilioConfig.NUMBER
})
return new Date()
}
setInterval(async () => {
let newStatus = getNewStatus(status)
const statusHasChanged = status.status !== newStatus.status
const isOpen = newStatus.status === "open"
if (!isOpen) {
// Door is closed, erase the last time we sent an alert
newStatus.lastAlertSent = undefined
}
if (isOpen) {
newStatus.lastAlertSent = await doSendAlertCheck(newStatus)
}
// TODO: Send to logger
if (statusHasChanged) {
const now = new Date()
console.log(`Garage door is now ${newStatus.status}`, now)
if (!isOpen) {
console.log(`Door was open for ${differenceInSeconds(now, status.lastOpen)} seconds.`)
}
}
status = newStatus
}, 500)
router.post('/opener', async ctx => {
rpio.write(OPENER_PIN, ctx.request.body.position === 'open' ? rpio.LOW : rpio.HIGH);
ctx.body = { status: 'success' }
})
router.get("/status", (ctx, next) => {
ctx.body = { status, settings, limit: ALLOWED_LIMIT }
})
router.post("/settings", (ctx, next) => {
settings = {
...settings,
...ctx.request.body
}
console.log('Settings updated...', settings, new Date())
})
app.use(bodyParser())
app.use(cors())
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000)
console.log("listening on 3000")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment