* script for controlling garage that is aware of door states * Overhaul garage door example Hardware now calls for reed relay in order to be able to tell the state of the switch. This way, we can have distinct open/close operations that error if the door is already in that state. Allows for potential better integration with home automation systems. * Prepare for push to GitHub * Restore defaults * Make HTTP auth realm match zeroconf networking name
154 lines
5.2 KiB
Lua
154 lines
5.2 KiB
Lua
-- garage_door_open.lua
|
|
-- Part of nodemcu-httpserver, example.
|
|
-- Author: Marcos Kirsch
|
|
|
|
--[[
|
|
This example assumed you have a Wemos D1 Pro to control a two-door garage.
|
|
For each garage door, a Wemos relay shield is used to simulate a button (connect relay in
|
|
parallel with the actual physical button) and a reed switch is used in order to know
|
|
whether a door is currently open or closed (install switch so that it is in the closed
|
|
position when your garage door is closed).
|
|
|
|
You can configure which GPIO pins you use for each function by modifying variable
|
|
pinConfig below.
|
|
]]--
|
|
|
|
local function pushTheButton(connection, pinConfig)
|
|
-- push the button!
|
|
gpio.write(pinConfig["controlPin"], gpio.HIGH)
|
|
gpio.mode(pinConfig["controlPin"], gpio.OUTPUT, gpio.FLOAT)
|
|
tmr.delay(300000) -- in microseconds
|
|
gpio.mode(pinConfig["controlPin"], gpio.INPUT, gpio.FLOAT)
|
|
gpio.write(pinConfig["controlPin"], gpio.LOW)
|
|
end
|
|
|
|
|
|
local function readDoorStatus(pinConfig)
|
|
-- When the garage door is closed, the reed relay closes, grounding the pin and causing us to read low (0).
|
|
-- When the garage door is open, the reed relay is open, so due to pullup we read high (1).
|
|
gpio.write(pinConfig["statusPin"], gpio.HIGH)
|
|
gpio.mode(pinConfig["statusPin"], gpio.INPUT, gpio.PULLUP)
|
|
if gpio.read(pinConfig["statusPin"]) == 1 then return 'open' else return 'closed' end
|
|
end
|
|
|
|
|
|
local function sendResponse(connection, httpCode, errorCode, action, pinConfig, message)
|
|
|
|
-- Handle nil inputs
|
|
if action == nil then action = '' end
|
|
if pinConfig == nil then
|
|
pinConfig = {}
|
|
pinConfig["door"] = 0
|
|
pinConfig["controlPin"] = 0
|
|
pinConfig["statusPin"] = 0
|
|
end
|
|
if message == nil then message = '' end
|
|
|
|
connection:send("HTTP/1.0 "..httpCode.." OK\r\nContent-Type: application/json\r\nCache-Control: private, no-store\r\n\r\n")
|
|
connection:send('{"error":'..errorCode..', "door":'..pinConfig["door"]..', "controlPin":'..pinConfig["controlPin"]..', "statusPin":'..pinConfig["statusPin"]..', "action":"'..action..'", "message":"'..message..'"}')
|
|
end
|
|
|
|
|
|
local function sendStatus(connection, pinConfig)
|
|
connection:send("HTTP/1.0 200 OK\r\nContent-Type: application/json\r\nCache-Control: private, no-store\r\n\r\n")
|
|
connection:send('{"error":0, "door":'..pinConfig["door"]..', "controlPin":'..pinConfig["controlPin"]..', "statusPin":'..pinConfig["statusPin"]..', "action":"status"'..', "status":"'..readDoorStatus(pinConfig)..'"}')
|
|
end
|
|
|
|
|
|
local function openDoor(connection, pinConfig)
|
|
-- errors if door is already open.
|
|
local doorStatus = readDoorStatus(pinConfig)
|
|
if doorStatus == 'open' then
|
|
return false
|
|
else
|
|
pushTheButton(connection, pinConfig)
|
|
return true
|
|
end
|
|
end
|
|
|
|
|
|
local function closeDoor(connection, pinConfig)
|
|
-- errors if door is already closed.
|
|
local doorStatus = readDoorStatus(pinConfig)
|
|
if doorStatus == 'closed' then
|
|
return false
|
|
else
|
|
pushTheButton(connection, pinConfig)
|
|
return true
|
|
end
|
|
end
|
|
|
|
|
|
return function (connection, req, args)
|
|
|
|
-- The values for pinConfig depend on how your Wemo D1 mini Pro is wired.
|
|
-- Adjust as needed.
|
|
pinConfig = {}
|
|
pinConfig["1"] = {}
|
|
pinConfig["1"]["door"] = 1
|
|
pinConfig["1"]["controlPin"] = 2
|
|
pinConfig["1"]["statusPin"] = 5
|
|
pinConfig["2"] = {}
|
|
pinConfig["2"]["door"] = 2
|
|
pinConfig["2"]["controlPin"] = 1
|
|
pinConfig["2"]["statusPin"] = 6
|
|
|
|
-- Make this work with both GET and POST methods.
|
|
-- In the POST case, we need to extract the arguments.
|
|
print("method is " .. req.method)
|
|
if req.method == "POST" then
|
|
local rd = req.getRequestData()
|
|
for name, value in pairs(rd) do
|
|
args[name] = value
|
|
end
|
|
end
|
|
|
|
-- validate door input
|
|
|
|
if args.door == nil then
|
|
sendResponse(connection, 400, -1, args.action, pinConfig[args.door], "No door specified")
|
|
return
|
|
end
|
|
|
|
if pinConfig[args.door] == nil then
|
|
sendResponse(connection, 400, -2, args.action, pinConfig[args.door], "Bad door specified")
|
|
return
|
|
end
|
|
|
|
-- perform action
|
|
|
|
if args.action == "open" then
|
|
if(openDoor(connection, pinConfig[args.door])) then
|
|
sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Door opened")
|
|
else
|
|
sendResponse(connection, 400, -3, args.action, pinConfig[args.door], "Door is already open")
|
|
end
|
|
return
|
|
end
|
|
|
|
if args.action == "close" then
|
|
if(closeDoor(connection, pinConfig[args.door])) then
|
|
sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Door closed")
|
|
else
|
|
sendResponse(connection, 400, -4, args.action, pinConfig[args.door], "Door is already closed")
|
|
end
|
|
return
|
|
end
|
|
|
|
if args.action == "toggle" then
|
|
pushTheButton(connection, pinConfig[args.door])
|
|
sendResponse(connection, 200, 0, args.action, pinConfig[args.door], "Pushed the button")
|
|
return
|
|
end
|
|
|
|
if args.action == "status" then
|
|
sendStatus(connection, pinConfig[args.door])
|
|
return
|
|
end
|
|
|
|
-- everything else is error
|
|
|
|
sendResponse(connection, 400, -5, args.action, pinConfig[args.door], "Bad action")
|
|
|
|
end
|