nodemcu-httpserver/http/garage_door.lua
Marcos adde150009 Overhaul garage door example (#93)
* 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
2017-07-01 15:18:11 -05:00

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