7a366b04f320e6a709b23ad2d8f7b26e1a5a1bf2
[weathermon.git] / weathermon.lua
1 #!/usr/bin/lua
2
3 require("json")
4 require("socket")
5
6 function startswith(String,Start)
7    if String then
8      return string.sub(String,1,string.len(Start))==Start
9    else
10      return False
11    end    
12 end
13
14 function url_encode(str)
15   if (str) then
16     str = string.gsub (str, "\n", "\r\n")
17     str = string.gsub (str, "([^%w %-%_%.%~])",
18         function (c) return string.format ("%%%02X", string.byte(c)) end)
19     str = string.gsub (str, " ", "+")
20   end
21   return str    
22 end
23
24 function mqtt_encode(str)
25   if (str) then
26     str = string.gsub (str, "\n", "")
27     str = string.gsub (str, "/", "-")
28   end
29   return str    
30 end
31
32 function getConfig(configname)
33
34   local uci=require("uci")
35   local cur=uci.cursor()
36   local config
37   if configname then
38     config=configname
39   else
40     config="weathermon"
41   end
42
43   web_url  = cur.get(config,"web","url")
44   web_user = cur.get(config,"web","user")
45   web_pass = cur.get(config,"web","password")
46   web_devid = cur.get(config,"web","devid")
47
48   web_iface = cur.get(config,"web","iface")
49
50   if web_iface then
51   
52     command = '/sbin/ifconfig '..web_iface..' | grep \'\\<inet\\>\' | sed -n \'1p\' | tr -s \' \' | cut -d \' \' -f3 | cut -d \':\' -f2'
53     f=io.popen(command,'r')
54     ip_addr=f:read()
55   
56   end
57
58   if not web_devid then
59   
60     if web_iface then
61       io.input("/sys/class/net/"..web_iface.."/address")
62     else
63       io.input("/sys/class/net/eth0/address")
64     end
65
66     mac = io.read("*line")
67     mac = mac:gsub(":","")
68     mac = mac:upper()
69
70     web_devid = mac
71
72   end
73
74   logging = cur.get(config,"logging","enabled") 
75
76   serial_port = cur.get(config,"serial","port")
77   serial_baud = cur.get(config,"serial","baud")
78
79   input_file = cur.get(config,"input","file")
80   input_exec = cur.get(config,"input","exec")
81   alarm_exec = cur.get(config,"alarm","exec")
82
83   if serial_port then
84
85     command = "stty -F  "..serial_port.." "..serial_baud
86     os.capture(command)
87
88   end
89
90   mqtt_host = cur.get(config,"mqtt","host")
91   mqtt_port = cur.get(config,"mqtt","port")
92   mqtt_id = cur.get(config,"mqtt","id")
93   mqtt_topic = cur.get(config,"mqtt","topic")
94   mqtt_alarm_topic = cur.get(config,"mqtt","alarm_topic")
95
96   mqtt_user = cur.get(config,"mqtt","user")
97   mqtt_passwd = cur.get(config,"mqtt","password")
98
99   if mqtt_host and not mqtt_id then
100     mqtt_id="weather-"..web_devid
101   end
102
103   if mqtt_host and not mqtt_port then
104     mqtt_port = 1883
105   end
106
107   if mqtt_host and not mqtt_topic then
108     mqtt_topic = 'weathermon/{dev}/{type}/{id}/{param}'
109   end
110
111   if mqtt_host and not mqtt_alarm_topic then
112     mqtt_alarm_topic = 'alarm/{dev}/{type}/{id}'
113   end
114
115 end
116
117 function sleep(sec)
118   socket.select(nil, nil, sec)
119 end
120
121 function splitStr(str,char)
122
123   local res = {}
124   local idx = 1
125
126   while str:len()>0 do
127     pos = str:find(char); 
128     if pos == nil then
129       res[idx]=str
130       str=""
131     else
132       res[idx]=str:sub(1,pos-1)
133       idx=idx+1
134       str=str:sub(pos+1)
135     end
136   end
137
138   return res
139
140 end
141
142 function printLog(str)
143   if logging=="on" then
144     os.execute("logger -t weathermon "..str)
145   else 
146     print(str)  
147   end 
148 end
149
150 function submitValue(type,id,param,val)
151
152   url = web_url.."?stype="..url_encode(type).."&sid="..url_encode(id).."&param="..url_encode(param).."&value="..url_encode(val)
153
154   command = "curl"
155
156   if web_iface then
157     command = command.." --interface "..ip_addr
158   end
159
160   if web_user then
161     command = command.." -u "..web_user..":"..web_pass
162   end
163
164   command = command.." \""..url.."\""
165
166   os.execute(command)
167
168 end
169
170 function processJson(str)
171
172   msg=json.decode(str)
173
174   sensor={}
175
176   for key,value in pairs(msg) do
177     if value then
178       if key=="model" then
179         sensor_type=value
180       elseif key=="id" then
181         sensor_id=value
182       elseif key=='time' then
183         sensor_time=value
184       else
185         sensor[key]=value
186       end
187     end
188   end
189
190   if not (sensor_type==nil or sensor_id==nil or sensor_type=='' or sensor_id=='') then
191     for k,v in pairs(sensor) do
192       printLog("Type = "..sensor_type..", ID = "..sensor_id..", Param = "..k..", Value = "..v)
193       submitValue(sensor_type,sensor_id,k,v)
194       if mqtt_client then
195         mqtt_path=string.gsub(mqtt_topic,"{(.-)}", 
196           function (name) 
197             if name=="dev" then
198               return mqtt_encode(web_devid)
199             elseif name=="type" then
200               return mqtt_encode(sensor_type)
201             elseif name=="id" then
202               return mqtt_encode(sensor_id)
203             elseif name=="param" then
204               return k
205             else
206               return '{'..name..'}'
207             end      
208           end)
209         mqtt_client:publish(mqtt_path,v)
210       end  
211     end
212   else
213     printLog("Cannot parse sensor input: "..msg_body)
214   end
215
216 end
217
218 function processLine(str)
219
220   msg=splitStr(line,':')
221   msg_type=msg[1] or ''
222   msg_body=msg[2] or ''
223   if msg_type=="STATUS" then
224     printLog("Status: "..msg_body)
225   elseif msg_type=="ERROR" then
226     printLog("Error: "..msg_body)  
227   elseif msg_type=="SENSOR" then
228     printLog("SENSOR: "..msg_body)  
229     sens = splitStr(msg_body,",")
230     sensor = {}
231     idx = 1
232     sensor_type = nil
233     sensor_id = web_devid
234     for i,rec in ipairs(sens) do
235       recrd=splitStr(rec,'=')
236       key=recrd[1] or ''
237       value=recrd[2] or ''
238       if value then
239         if key=="TYPE" then
240           sensor_type=value
241         elseif key=="ID" then
242           sensor_id=value
243         else
244           sensor[key]=value
245         end
246       end
247     end
248     if not (sensor_type==nil or sensor_id==nil or sensor_type=='' or sensor_id=='') then
249       for k,v in pairs(sensor) do
250         printLog("Type = "..sensor_type..", ID = "..sensor_id..", Param = "..k..", Value = "..v)
251         submitValue(sensor_type,sensor_id,k,v)
252         if mqtt_client then
253           mqtt_path=string.gsub(mqtt_topic,"{(.-)}", 
254             function (name) 
255               if name=="dev" then
256                 return web_devid
257               elseif name=="type" then
258                 return sensor_type
259               elseif name=="id" then
260                 return sensor_id
261               elseif name=="param" then
262                 return k
263               else
264                 return '{'..name..'}'
265               end      
266             end)
267           mqtt_client:publish(mqtt_path,v)
268         end  
269       end
270     else
271       printLog("Cannot parse sensor input: "..msg_body)
272     end
273   elseif msg_type=="ALARM" then
274     printLog("ALARM: "..msg_body)  
275     sens = splitStr(msg_body,",")
276     sensor = {}
277     idx = 1
278     sensor_type = nil
279     sensor_id = web_devid
280     mqtt_param = {}
281     for i,rec in ipairs(sens) do
282       recrd=splitStr(rec,'=')
283       key=recrd[1] or ''
284       value=recrd[2] or ''
285       if value then
286         if key=="TYPE" then
287           alarm_type=value
288         elseif key=="ID" then
289           alarm_id=value
290         end
291       end
292     end
293     if not (alarm_type==nil or alarm_id==nil or alarm_type=='' or alarm_id=='') then
294       if mqtt_client then
295         mqtt_path=string.gsub(mqtt_alarm_topic,"{(.-)}", 
296           function (name) 
297             if name=="dev" then
298               return web_devid
299             elseif name=="type" then
300               return sensor_type
301             elseif name=="id" then
302               return sensor_id
303             else
304               return '{'..name..'}'
305             end      
306           end)
307         mqtt_client:publish(mqtt_path,msg_body)
308       end
309       if alarm_exec then
310         command=alarm_exec..
311           " \""..string.gsub(alarm_type,"\"","\\\"")..
312           "\" \""..string.gsub(alarm_id,"\"","\\\"")..
313           "\" \""..string.gsub(msg_body,"\"","\\\"").."\""
314         os.execute(command)
315       end
316     else
317       printLog("Cannot parse alarm input: "..msg_body)
318     end
319   end
320
321 end
322
323 getConfig(arg[1])
324
325 if mqtt_host then
326   MQTT = require "paho.mqtt"
327   mqtt_client = MQTT.client.create(mqtt_host, mqtt_port)
328   if mqtt_user then
329     mqtt_client:auth(mqtt_user, mqtt_passwd)
330   end
331   mqtt_client:connect(mqtt_id)
332 end
333
334 if serial_port then
335   serialin=io.open(serial_port,"r")
336 elseif input_file == "-" then
337   serialin=io.stdin;
338 elseif input_file then
339   serialin=io.open(input_file,"r")
340 elseif input_exec then
341   serialin=io.popen(input_exec,"r")
342 else
343   printLog("No input selected")
344   return
345 end  
346 while 1 do
347   line=serialin:read()
348   if line == nil then
349     break
350   end
351   printLog("Received: "..line);
352   if startswith(line,'{') then
353     processJson(line)
354   else
355     processLine(line)
356   end
357 end