779bc496cb3e64f37d77849d07cea77b062932b2
[weathermon.git] / bin / weather-lcd
1 #!/usr/bin/lua
2
3 socket = require "socket"
4 json = require "json"
5 require "uci"
6 cur = uci.cursor()
7 require "wm_util"
8
9 config_name = arg[1]
10 if not config_name then
11   config_name = "weathermon"
12 end  
13
14 weather_file = uci.get(config_name,"process","dump_file")
15 weather_db = uci.get(config_name,"process","logdb")
16
17 graph_duration = uci.get(config_name,"display","graph_duration")
18
19 if graph_duration and not(weather_db == "") then
20   pcall(function ()
21       local dbdriver = require "luasql.sqlite3"
22       local env = assert(dbdriver.sqlite3())
23       log_con = assert(env:connect(weather_db))
24     end)  
25 end
26
27 function round(num, numDecimalPlaces)
28   local mult = 10^(numDecimalPlaces or 0)
29   return math.floor(num * mult + 0.5) / mult
30 end
31
32 function trim(s)
33   return (s:gsub("^%s*(.-)%s*$", "%1"))
34 end
35
36 function lpad(s, l, c)
37   local res = string.rep(c or ' ', l - #s) .. s
38   return res, res ~= s
39 end
40
41 function process_file()
42   txt = get_file_content(weather_file)
43   data = json.decode(txt)
44   for k,v in pairs(data) do
45     return v
46   end
47 end
48
49 function get_display_config()
50
51   width  = tonumber(uci.get(config_name,"display","width"))
52   height = tonumber(uci.get(config_name,"display","height"))
53   charwidth  = tonumber(uci.get(config_name,"display","charwidth"))
54   charheight = tonumber(uci.get(config_name,"display","charheight"))
55   title  = uci.get(config_name,"display","title")
56   
57   pages = {}
58   pagetitles = {}
59   pagedurations = {}
60   pagecolumns = {}
61   pagecolwidth = {}
62   
63   local page, def, line, col, pos
64   
65   cur.foreach(config_name,"display", function(s) 
66       page = {}
67       local columns = s["columns"]
68       if not columns or columns == "" then
69         columns = 1
70       else 
71         columns = tonumber(columns)
72       end
73       local pagetitle = s["title"]
74       if not pagetitle then
75         pagetitle = title
76       end
77       local pageduration = s["duration"]
78       if not (pagetitle=="") then
79         line = 2
80       else
81         line = 1
82       end
83       col = 1
84       pos = 0
85       for k,v in pairs(s["parameter"]) do
86         def = {}
87         if not(v == "") then
88           v = split(v,":")
89           def["sensor"] = v[1]
90           def["param"] = v[2]
91           def["label"] = v[3]
92           def["unit"] = v[4]
93           def["scale"] = tonumber(v[5])
94           def["round"] = tonumber(v[6])
95           def["pos"] = tonumber(v[7])
96           def["line"] = line
97           def["col"] = col
98           page[#page+1] = def
99         end
100         col = col + 1
101         if col > columns then
102           col = 1
103           line = line + 1
104         end
105       end
106       pages[#pages+1] = page
107       pagetitles[#pages] = pagetitle
108       pagedurations[#pages] = pageduration
109       pagecolumns[#pages] = columns
110       pagecolwidth[#pages] = math.floor(width/columns)
111     end)
112
113 end
114
115 function connect_server()
116   local ip = uci.get(config_name,"display","server")
117   local port = uci.get(config_name,"display","port")
118   conn = socket.connect(ip,port)
119   return conn
120 end
121
122 function write_command(conn,command)
123   conn:send(command.."\n")
124   local str=conn:receive()
125   return str
126 end
127
128 function setup_pages(conn)
129   write_command(conn,"hello")
130   write_command(conn,"client_set -name weathermon")
131   for i,page in pairs(pages) do
132     local pageid = "page"..trim(tostring(i))
133     write_command(conn,"screen_add "..pageid)
134     local pagetitle = pagetitles[i]
135     write_command(conn,"screen_set "..pageid.." -cursor off")
136     if not(pagetitle == "") then
137       write_command(conn,"screen_set "..pageid.." -name "..pagetitle)
138       write_command(conn,"widget_add "..pageid.." "..pageid..".title title")
139       write_command(conn,"widget_set "..pageid.." "..pageid..".title \"".. pagetitle.."\"")
140     end
141     local pageduration = pagedurations[i]
142     if pageduration and not(pageduration == "") then
143       write_command(conn,"screen_set "..pageid.." -duration "..pageduration)
144     end
145     for j,def in pairs(page) do
146       defid = def["sensor"].."."..def["param"]
147       write_command(conn,"widget_add "..pageid.." "..defid.." string")
148     end
149   end
150   if log_con then
151     for i,page in pairs(pages) do
152       for j,def in pairs(page) do
153         pageid = def["sensor"].."."..def["param"]
154         pagetitle = trim(def["label"])..", "..def["unit"]
155         write_command(conn,"screen_add "..pageid)
156         write_command(conn,"screen_set "..pageid.." -cursor off -name "..pagetitle.." -duration "..graph_duration)
157         write_command(conn,"widget_add "..pageid.." "..pageid..".title title")
158         write_command(conn,"widget_add "..pageid.." "..pageid..".max string")
159         write_command(conn,"widget_add "..pageid.." "..pageid..".min string")
160         for k = 3,height-1 do
161           write_command(conn,"widget_add "..pageid.." "..pageid..".place"..trim(tostring(k)).." string")
162         end
163         for k=1,width do
164           write_command(conn,"widget_add "..pageid.." "..pageid..".bar"..trim(tostring(k)).." vbar")
165         end
166         write_command(conn,"widget_set "..pageid.." "..pageid..".title \"".. pagetitle.."\"")
167       end
168     end
169   end
170 end
171
172 function process_vals(vals)
173   for i,page in pairs(pages) do
174     col_width = pagecolwidth[i]
175     local pageid = "page"..trim(tostring(i))
176     for j,def in pairs(page) do
177       val = vals[def["sensor"]][def["param"]]
178       if val then
179         val = round(val * def["scale"], def["round"])
180       end
181       defid = def["sensor"].."."..def["param"]
182       val = lpad(tostring(val),def["pos"]).." "..def["unit"]
183       if not(def["label"] == "") then
184         val = def["label"]..": "..val
185       end
186       write_command(conn,"widget_set "..pageid.." "..defid.." "..trim(tostring((def["col"]-1)*col_width+1)).." "..def["line"].." \""..val.."\"")
187     end
188   end
189 end  
190
191 function process_graphs()
192   for i,page in pairs(pages) do
193     for j,def in pairs(page) do
194       local sensor = def["sensor"]
195       local param  = def["param"]
196       local pageid = sensor.."."..param
197       local cur = assert (log_con:execute(string.format("select hour,avg(value) val from (select strftime('%%Y-%%m-%%d %%H',time_stamp)||':00' hour,value from log where sensor='%s' and param='%s') group by hour order by hour desc limit 24",sensor,param)))
198       local row = cur:fetch ({}, "a")
199       local vals = {}
200       local maxval = -99999999
201       local minval =  99999999
202       for k=width,1,-1 do
203         if row then
204           val = row["val"]
205           row = cur:fetch ({}, "a")
206         else
207           val = nil  
208         end  
209         vals[k] = val
210         if val and (val > maxval) then maxval = val; end
211         if val and (val < minval) then minval = val; end
212       end          
213       minval = math.floor(minval)
214       local minvalstr = trim(tostring(minval))
215       maxval = math.ceil(maxval)
216       local maxvalstr = trim(tostring(maxval))
217       local len = math.max(string.len(minvalstr),string.len(maxvalstr))
218       write_command(conn,"widget_set "..pageid.." "..pageid..".max "..trim(tostring(width-string.len(maxvalstr)+1)).." 2 "..maxvalstr)
219       for k = 3,height-1 do
220         write_command(conn,"widget_set "..pageid.." "..pageid..".place"..trim(tostring(k)).." "..trim(tostring(width-len+1)).." "..k.." \""..string.rep("-",len).."\"")
221       end
222       write_command(conn,"widget_set "..pageid.." "..pageid..".min "..trim(tostring(width-string.len(minvalstr)+1)).." "..height.." "..minvalstr)
223       local m = width
224       for k = width-len,1,-1 do
225         val = vals[m]
226         if val then
227           h = math.floor(0.5+(val-minval)/(maxval-minval)*(height-1)*charheight)
228         else
229           h = 0
230         end  
231         write_command(conn,"widget_set "..pageid.." "..pageid..".bar"..trim(tostring(k)).." "..k.." "..height.." "..h)
232         m = m - 1
233       end
234       for k= m,1,-1 do
235         write_command(conn,"widget_set "..pageid.." "..pageid..".bar"..trim(tostring(k)).." 0 0 0")
236       end
237     end
238   end
239 end
240
241 get_display_config()
242
243 conn = connect_server()
244
245 setup_pages(conn)
246
247 while true do
248
249   vals = process_file()
250   process_vals(vals)
251
252   if log_con then
253     process_graphs()
254   end  
255       
256   os.execute("inotifywait -e MODIFY \""..weather_file.."\"")
257   socket.sleep(3)
258   
259 end