summaryrefslogtreecommitdiff
path: root/wakeup.lua
blob: 9b5b6a40d891ece2788213d1ce382de0bbc7153f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
local wakeup = require("+wakeup")


--- Builds and returns a Wake-on-LAN magic packet for [[mac]] address.
local
function magic_packet (mac)
	local data = ""
	for octet in mac:gmatch("%x%x") do
		data = data .. string.char(tonumber(octet, 16))
	end
	if #data ~= 6 then
		error(string.format("Invalid MAC\t%q", mac), 2)
	end
	return string.char(0xff):rep(6) .. data:rep(16)
end


function wakeup:open ()
	self.udp = net.createUDPSocket()
end


function wakeup:close ()
	if self.udp then
		self.udp:close()
	end
end


--- Creates a new counter that has :up() method that returns true until it gets called N [[times]]. Then it returns only
--- false.
local
function new_counter (times)
	return {
		count = 0,
		times = times,
		up = function (self)
			self.count = self.count + 1
			return self.count <= self.times
		end,
	}
end


--- Begins to send Wake-on-LAN magic packets to the broadcast [[.addr]] for selected [[.mac]]. Packets are sent several
--- [[times]] each separated by [[interval]]. Callback is called [[after]] all packets are sent.
function wakeup:sendout (interval, times, after)
	self:open()
	local count = new_counter(times)
	return tmr.create():alarm(interval, tmr.ALARM_SEMI, function (timer)
		if count:up() then
			print("Waking up", self.mac, self.addr)
			self.udp:send(self.port, self.addr, self.data)
			timer:start()
		else
			timer:unregister()
			self:close()
			after()
		end
	end)
end


function wakeup:now ()
	local time = rtctime.epoch2cal(rtctime.get())
	time.year = nil
	time.sec = nil
	return time
end


local
function compare (time, pattern)
	for key, value in pairs(pattern) do
		if time[key] ~= value then
			return false
		end
	end
	return true
end


function wakeup:init ()
	gpio.write(4, gpio.HIGH)
	gpio.mode(4, gpio.OUTPUT)
	self.data = magic_packet(self.mac)

	wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function ()
		sntp.sync(
			self.sntp,
			function ()
				if compare(self:now(), self.when) then
					gpio.write(4, gpio.LOW)
					self:sendout(500, 5, function ()
						wifi.setmode(wifi.NULLMODE, false)
						gpio.write(4, gpio.HIGH)
					end)
				else
					print("Not yet time")
					wifi.setmode(wifi.NULLMODE, false)
					tmr.create():alarm(25000, tmr.ALARM_SINGLE, function ()
						self:run()
					end)
				end
			end,
			function (code, err)
				print("SNTP sync failed", code, err)
				node.restart()
			end)
	end)
end


function wakeup:run ()
	wifi.setmode(wifi.STATION, false)
	wifi.sta.connect()
end


return wakeup