9ee87ba529f4a7007dab6997818cf61889698e3f
[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   width  = tonumber(uci.get(config_name,"display","width"))
51   height = tonumber(uci.get(config_name,"display","height"))
52   charwidth  = tonumber(uci.get(config_name,"display","charwidth"))
53   charheight = tonumber(uci.get(config_name,"display","charheight"))
54   cols   = tonumber(uci.get(config_name,"display","columns"))
55   title  = uci.get(config_name,"display","title")
56   col_width = math.floor(width/cols)
57   
58   pages = {}
59   pagetitles = {}
60   pagedurations = {}
61   
62   local page, def, line, col, pos
63   
64   cur.foreach(config_name,"display", function(s) 
65       page = {}
66       local pagetitle = s["title"]
67       if not pagetitle then
68         pagetitle = title
69       end
70       local pageduration = s["duration"]
71       if not (pagetitle=="") then
72         line = 2
73       else
74         line = 1
75       end
76       col = 1
77       pos = 0
78       for k,v in pairs(s["parameter"]) do
79         def = {}
80         if not(v == "") then
81           v = split(v,":")
82           def["sensor"] = v[1]
83           def["param"] = v[2]
84           def["label"] = v[3]
85           def["unit"] = v[4]
86           def["scale"] = tonumber(v[5])
87           def["round"] = tonumber(v[6])
88           def["pos"] = tonumber(v[7])
89           def["line"] = line
90           def["col"] = col
91           page[#page+1] = def
92         end
93         col = col + 1
94         if col > cols then
95           col = 1
96           line = line + 1
97         end
98       end
99       pages[#pages+1] = page
100       pagetitles[#pages] = pagetitle
101       pagedurations[#pages] = pageduration
102     end)
103
104 end
105
106 function connect_server()
107   local ip = uci.get(config_name,"display","server")
108   local port = uci.get(config_name,"display","port")
109   conn = socket.connect(ip,port)
110   return conn
111 end
112
113 function write_command(conn,command)
114   conn:send(command.."\n")
115   local str=conn:receive()
116   return str
117 end
118
119 function setup_pages(conn)
120   write_command(conn,"hello")
121   write_command(conn,"client_set -name weathermon")
122   for i,page in pairs(pages) do
123     local pageid = "page"..trim(tostring(i))
124     write_command(conn,"screen_add "..pageid)
125     local pagetitle = pagetitles[i]
126     write_command(conn,"screen_set "..pageid.." -cursor off")
127     if not(pagetitle == "") then
128       write_command(conn,"screen_set "..pageid.." -name "..pagetitle)
129       write_command(conn,"widget_add "..pageid.." "..pageid..".title title")
130       write_command(conn,"widget_set "..pageid.." "..pageid..".title \"".. pagetitle.."\"")
131     end
132     local pageduration = pagedurations[i]
133     if pageduration and not(pageduration == "") then
134       write_command(conn,"screen_set "..pageid.." -duration "..pageduration)
135     end
136     for j,def in pairs(page) do
137       defid = def["sensor"].."."..def["param"]
138       write_command(conn,"widget_add "..pageid.." "..defid.." string")
139     end
140   end
141   if log_con then
142     for i,page in pairs(pages) do
143       for j,def in pairs(page) do
144         pageid = def["sensor"].."."..def["param"]
145         pagetitle = trim(def["label"])..", "..def["unit"]
146         write_command(conn,"screen_add "..pageid)
147         write_command(conn,"screen_set "..pageid.." -cursor off")
148         write_command(conn,"screen_set "..pageid.." -name "..pagetitle.." -duration "..graph_duration)
149         write_command(conn,"widget_add "..pageid.." "..pageid..".title title")
150         write_command(conn,"widget_set "..pageid.." "..pageid..".title \"".. pagetitle.."\"")
151         write_command(conn,"widget_add "..pageid.." "..pageid..".max string")
152         write_command(conn,"widget_add "..pageid.." "..pageid..".min string")
153         for k=1,width-def["pos"] do
154           write_command(conn,"widget_add "..pageid.." "..pageid..".bar"..trim(tostring(k)).." vbar")
155         end
156       end
157     end
158   end
159 end
160
161 function process_vals(vals)
162   for i,page in pairs(pages) do
163     local pageid = "page"..trim(tostring(i))
164     for j,def in pairs(page) do
165       val = vals[def["sensor"]][def["param"]]
166       if val then
167         val = round(val * def["scale"], def["round"])
168       end
169       defid = def["sensor"].."."..def["param"]
170       val = lpad(tostring(val),def["pos"]).." "..def["unit"]
171       if not(def["label"] == "") then
172         val = def["label"]..": "..val
173       end
174       write_command(conn,"widget_set "..pageid.." "..defid.." "..trim(tostring((def["col"]-1)*col_width+1)).." "..def["line"].." \""..val.."\"")
175     end
176   end
177 end  
178
179 function process_graphs()
180   for i,page in pairs(pages) do
181     for j,def in pairs(page) do
182       local sensor = def["sensor"]
183       local param  = def["param"]
184       local pageid = sensor.."."..param
185       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)))
186       local row = cur:fetch ({}, "a")
187       local vals = {}
188       local maxval = -99999999
189       local minval =  99999999
190       for k=width-def["pos"],1,-1 do
191         if row then
192           val = row["val"]
193           row = cur:fetch ({}, "a")
194         else
195           val = nil  
196         end  
197         vals[k] = val
198         if val and (val > maxval) then maxval = val; end
199         if val and (val < minval) then minval = val; end
200       end          
201       minval = math.floor(minval)
202       minvalstr = trim(tostring(minval))
203       maxval = math.ceil(maxval)
204       maxvalstr = trim(tostring(maxval))
205       write_command(conn,"widget_set "..pageid.." "..pageid..".max "..trim(tostring(width-string.len(maxvalstr)+1)).." 2 "..maxvalstr)
206       write_command(conn,"widget_set "..pageid.." "..pageid..".min "..trim(tostring(width-string.len(minvalstr)+1)).." "..height.." "..minvalstr)
207       for k = 1,width-def["pos"] do
208         val = vals[k]
209         if val then
210           h = math.floor(0.5+(val-minval)/(maxval-minval)*(height-1)*charheight)
211         else
212           h = 0
213         end  
214         write_command(conn,"widget_set "..pageid.." "..pageid..".bar"..trim(tostring(k)).." "..k.." "..height.." "..h)
215       end
216     end
217   end
218 end
219
220 get_display_config()
221
222 conn = connect_server()
223
224 setup_pages(conn)
225
226 while true do
227
228   vals = process_file()
229   process_vals(vals)
230
231   if log_con then
232     process_graphs()
233   end  
234       
235   os.execute("inotifywait -e MODIFY \""..weather_file.."\"")
236   socket.sleep(3)
237   
238 end