diff --git a/spec/init_spec.lua b/spec/init_spec.lua new file mode 100644 index 0000000..26f8f95 --- /dev/null +++ b/spec/init_spec.lua @@ -0,0 +1,161 @@ +describe("init", function() + local mock_timer + + before_each(function() + mock(_G.enduser_setup) + mock_timer = mock({ + register = function() end, + alarm = function() end, + unregister = function() end, + start = function() end + }) + end) + + describe("when wifi is not configured yet", function() + before_each(function() + require("spec/nodemcu_stubs") + nodemcu.wifi.sta.config = "" + mock(_G.enduser_setup) + + dofile("src/init.lua") + end) + + after_each(function() + nodemcu.wifi.sta.config = { ssid = 'test' } + end) + + it("starts enduser_setup", function() + assert.spy(_G.enduser_setup.manual).was.called_with(false) + assert.spy(_G.enduser_setup.start).was.called() + end) + end) + + describe("tmr: poll for IP address", function() + + before_each(function() + require("spec/nodemcu_stubs") + stub(_G.tmr, 'create').returns(mock_timer) + dofile("src/init.lua") + + spy.on(_G.gpio, 'write') + spy.on(wifi.eventmon, 'unregister') + end) + + describe("when there's no IP address", function() + before_each(function() + nodemcu.run_tmr(900, tmr.ALARM_AUTO) + end) + + it("does not stop enduser_setup", function() + assert.spy(_G.enduser_setup.stop).was_not.called() + end) + end) + + describe("when there's an IP address", function() + before_each(function() + nodemcu.wifi.sta.ip = '192.168.1.100' + nodemcu.run_tmr(900, tmr.ALARM_AUTO) + end) + + it("stops enduser_setup", function() + assert.spy(_G.enduser_setup.stop).was.called() + end) + + it("turns off the led", function() + assert.spy(_G.gpio.write).was.called_with(4, _G.gpio.HIGH) + end) + + it("unregisters the failsafeTimer and wifiFailTimer", function() + assert.stub(mock_timer.unregister).was.called() + assert.are.equal(#mock_timer.unregister.calls, 2) -- wifiFailTimer and failSafeTimer + end) + + it("unregisters the wifi disconnect eventmon", function() + assert.spy(wifi.eventmon.unregister).was.called_with(wifi.eventmon.STA_DISCONNECTED) + end) + end) + + end) + + describe("when receiving a disconnect signal", function() + before_each(function() + require("spec/nodemcu_stubs") + stub(_G.tmr, 'create').returns(mock_timer) + dofile("src/init.lua") + spy.on(wifi.eventmon, 'unregister') + + nodemcu.wifi.eventmon['STA_DISCONNECTED']({SSID = "test", BSSID = "test", reason = "201"}) + end) + + it("starts the wifiFailTimer", function() + assert.stub(mock_timer.start).was.called() + end) + + describe("after failing the wifiFailTimer", function() + before_each(function() + nodemcu.run_tmr(30000, tmr.ALARM_SINGLE) + end) + + it("starts wifi setup", function() + assert.spy(_G.enduser_setup.manual).was.called_with(false) + assert.spy(_G.enduser_setup.start).was.called() + end) + + it("starts the failsafeTimer", function() + assert.stub(mock_timer.start).was.called() + end) + + it("unregisters the wifi disconnect eventmon", function() + assert.spy(wifi.eventmon.unregister).was.called_with(wifi.eventmon.STA_DISCONNECTED) + end) + + it("unregisters the wifiFailTimer", function() + assert.stub(mock_timer.unregister).was.called() + assert.are.equal(#mock_timer.unregister.calls, 1) + end) + + describe("after failing the failsafeTimer", function() + before_each(function() + spy.on(node, 'restart') + nodemcu.run_tmr(300000, tmr.ALARM_SINGLE) + end) + + it("restarts", function() + assert.spy(node.restart).was.called() + end) + end) + + describe("after connecting", function() + before_each(function() + nodemcu.wifi.sta.ip = '192.168.1.100' + nodemcu.run_tmr(900, tmr.ALARM_AUTO) + end) + + it("it stops the wifiFailTimer and failSafeTimer", function() + assert.stub(mock_timer.unregister).was.called() + assert.are.equal(#mock_timer.unregister.calls, 2) -- wifiFailTimer and failSafeTimer + end) + + it("unregisters the wifi disconnect eventmon", function() + assert.spy(wifi.eventmon.unregister).was.called_with(wifi.eventmon.STA_DISCONNECTED) + end) + end) + end) + + describe("after connecting", function() + before_each(function() + nodemcu.wifi.sta.ip = '192.168.1.100' + nodemcu.run_tmr(900, tmr.ALARM_AUTO) + end) + + it("it stops the wifiFailTimer and failSafeTimer", function() + assert.stub(mock_timer.unregister).was.called() + assert.are.equal(#mock_timer.unregister.calls, 2) -- wifiFailTimer and failSafeTimer + end) + + it("unregisters the wifi disconnect eventmon", function() + assert.spy(wifi.eventmon.unregister).was.called_with(wifi.eventmon.STA_DISCONNECTED) + end) + end) + end) +end) \ No newline at end of file diff --git a/spec/nodemcu_stubs.lua b/spec/nodemcu_stubs.lua index 3b1f06d..6e288c8 100644 --- a/spec/nodemcu_stubs.lua +++ b/spec/nodemcu_stubs.lua @@ -1,7 +1,116 @@ +nodemcu = { + tmrs = {}, + run_tmr = function(interval, type) + for _,v in pairs(nodemcu.tmrs) do + if v.interval == interval and v.type == type then + print('Running tmr ' .. type .. ', interval:', interval ) + return v.fn({unregister = function() end}) + end + end + end, + wifi = { + eventmon = {}, + sta = { + config = { ssid = 'test' }, + ip = nil + } + } +} + _G.node = { - heap = function() return(0) end + heap = function() return 0 end, + chipid = function() return 0 end, + restart = function() end +} + +_G.tmr = { + ALARM_SINGLE = 'ALARM_SINGLE', + ALARM_AUTO = 'ALARM_AUTO', + ALARM_SEMI= 'ALARM_SEMI', + create = function() + return { + alarm = function(_, interval, type, fn) + table.insert(nodemcu.tmrs, { + interval = interval, + type = type, + fn = fn + }) + end, + register = function(_, interval, type, fn) + table.insert(nodemcu.tmrs, { + interval = interval, + type = type, + fn = fn + }) + end, + unregister = function() end, + start = function() end + } + end } _G.file = { + list = function() return {} end, + exists = function() end +} + +_G.gpio = { + HIGH = 'HIGH', + LOW = 'LOW', + mode = function() end, + read = function() end, + write = function() end +} + +_G.wifi = { + eventmon = { + STA_DISCONNECTED = 'STA_DISCONNECTED', + register = function(key, fn) + print(key) + nodemcu.wifi.eventmon[key] = fn + end, + unregister = function(key) + nodemcu.wifi.eventmon[key] = nil + end, + reason = { + AUTH_EXPIRE = 2 + } + }, + + sta = { + getip = function() return nodemcu.wifi.sta.ip end, + getconfig = function() return nodemcu.wifi.sta.config end, + getmac = function() return 'aa:bb:cc:dd:ee:ff' end + } +} + +_G.enduser_setup = { + start = function() end, + stop = function() end, + manual = function() end +} + +_G.net = { + multicastJoin = function() end, + createServer = function() + return { + listen = function() end, + on = function() end + } + end +} -} \ No newline at end of file +-- Print contents of `tbl`, with indentation. +-- `indent` sets the initial level of indentation. +function tprint (tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(" ", indent) .. k .. ": " + if type(v) == "table" then + print(formatting) + tprint(v, indent+1) + else + print(formatting .. tostring(v)) + end + end +end \ No newline at end of file diff --git a/src/application.lua b/src/application.lua index 19861db..efc200a 100644 --- a/src/application.lua +++ b/src/application.lua @@ -39,7 +39,7 @@ sendTimer:alarm(200, tmr.ALARM_AUTO, function(t) local sensor = sensors[sensorSend[1]] timeout:start() http.put( - table.concat({ smartthings.apiUrl, "\/device\/", dni, "\/", sensor.pin, "\/", gpio.read(sensor.pin) }), + table.concat({ smartthings.apiUrl, "/device/", dni, "/", sensor.pin, "/", gpio.read(sensor.pin) }), table.concat({ "Authorization: Bearer ", smartthings.token, "\r\n" }), "", function(code) diff --git a/src/device.lua b/src/device.lua index e64c4ea..692bb7d 100644 --- a/src/device.lua +++ b/src/device.lua @@ -1,6 +1,6 @@ local me = { name = "Security", hwVersion = "2.0.0", - swVersion = "2.0.2" + swVersion = "2.0.3" } return me diff --git a/src/init.lua b/src/init.lua index 885845a..0b1ff19 100644 --- a/src/init.lua +++ b/src/init.lua @@ -2,45 +2,65 @@ print("Heap: ", node.heap(), "Initializing device") require("start") print("Heap: ", node.heap(), "Loaded: ", "Startup (compiler & blinker)") print("Heap: ", node.heap(), "Connecting to Wifi..") -local startCountDown = 0 -wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T) - print("Heap: ", node.heap(), "Cannot connect to WiFi:", T.SSID, T.BSSID, T.reason) +local startWifiSetup = function() + print("Heap: ", node.heap(), "Entering Wifi setup mode") + wifi.eventmon.unregister(wifi.eventmon.STA_DISCONNECTED) + wifiFailTimer:unregister() + wifiFailTimer = nil enduser_setup.manual(false) enduser_setup.start() - wifi.eventmon.unregister(wifi.eventmon.STA_DISCONNECTED) - print("Heap: ", node.heap(), "WiFi Setup started") + failsafeTimer:start() +end + +-- wait 30 seconds before entering wifi setup mode in case of a momentary outage +wifiFailTimer = tmr.create() +wifiFailTimer:register(30000, tmr.ALARM_SINGLE, function() startWifiSetup() end) + +-- failsafe: reboot after 5 minutes in case of extended wifi outage +failsafeTimer = tmr.create() +failsafeTimer:register(300000, tmr.ALARM_SINGLE, function() node.restart() end) + +wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T) + print("Heap: ", node.heap(), "Cannot connect to WiFi ", T.SSID, 'Reason Code:', T.reason) + + if T.reason == wifi.eventmon.reason.AUTH_EXPIRE then + -- wifi password is incorrect, immediatly enter setup mode + print("Heap: ", node.heap(), "Wifi password is incorrect") + startWifiSetup() + else + wifiFailTimer:start() + end end) if wifi.sta.getconfig() == "" then print("Heap: ", node.heap(), "WiFi not configured") - startCountDown = 5 - enduser_setup.manual(false) - enduser_setup.start() + startWifiSetup() print("Heap: ", node.heap(), "WiFi Setup started") end local _ = tmr.create():alarm(900, tmr.ALARM_AUTO, function(t) require("led_flip").flip() if wifi.sta.getip() then + wifi.eventmon.unregister(wifi.eventmon.STA_DISCONNECTED) t:unregister() t = nil + if wifiFailTimer then + wifiFailTimer:unregister() + wifiFailTimer = nil + end + failsafeTimer:unregister() + failsafeTimer = nil print("Heap: ", node.heap(), "Wifi connected with IP: ", wifi.sta.getip()) if file.exists("update_init.lc")then require("update") else - if startCountDown > 1 then - startCountDown = startCountDown - 1 - else - startCountDown = nil - gpio.write(4, gpio.HIGH) - enduser_setup.stop() - wifi.eventmon.unregister(wifi.eventmon.STA_DISCONNECTED) - require("server") - print("Heap: ", node.heap(), "Loaded: ", "server") - require("application") - print("Heap: ", node.heap(), "Loaded: ", "application") - end + gpio.write(4, gpio.HIGH) + enduser_setup.stop() + require("server") + print("Heap: ", node.heap(), "Loaded: ", "server") + require("application") + print("Heap: ", node.heap(), "Loaded: ", "application") end end end) diff --git a/src/manifest.json b/src/manifest.json index cf20108..bc29d25 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,15 +1,15 @@ { "src/actuators.lua":"47a1a50c0c8cfb547554ded22a8134eaed76c0a3", - "src/application.lua":"19861db05c155e56fb5ae5676f5ba807847304d2", + "src/application.lua":"efc200a36de373147b950dcaaaeb9013a0e3c2dc", "src/device.lua":"e64c4ea6dd1a98042a579069f381f06c70482cad", "src/enduser_setup.html.gz":"fbf730b33c309ceab15e88f162f43c232593f7da", "src/http_favicon.ico.gz":"483f371c590b0407c380d019bcc6938cf1d62f0a", "src/http_index.html.gz":"e1149f19e459500c32273dbdacbcfa978db16364", "src/httpd_req.lua":"80053674f20f087e58e1cfd490fc71e6cdd92acf", "src/httpd_res.lua":"7226f19699168e171970cda73b953a49450b7aa8", - "src/init.lua":"885845a1021082ac1c2421e7221696e46b45a2cf", + "src/init.lua":"0b1ff195c16e723517de7a97f771566ff30f0ad0", "src/led_flip.lua":"590e7be1afe686f4213d097d0e9dba9e7a3910a0", - "src/manifest.json":"2dfbfeb917d06f146035c6269b78f945f2c05f31", + "src/manifest.json":"0c859af04bcf717d7fd601fef2753dcb7e3b4ced", "src/sensors.lua":"d0d94fa73f713dcf2c517da9262f59f306b81d2b", "src/server.lua":"636843d3464b5aaba720b6eabe265b53580ed780", "src/server_device.lua":"dca5e6501b4836d120cadc20538999387c9c54d1", @@ -23,5 +23,5 @@ "src/update_process.lua":"ed66480f96c6b7631c93279b7a8a7dd0429d5a56", "src/variables_build.lua":"0029cb028698822ee981c35bcf84710c19e21486", "src/variables_set.lua":"2c2d9c13c8acb577d80f40e4b513c7cd23ffdd1f", - "updated_at":"2017-08-09:06:50:14" + "updated_at":"2017-08-15:07:09:42" }