libs/core: rework network model
[project/luci.git] / libs / core / luasrc / model / wireless.lua
1 --[[
2 LuCI - Wireless model
3
4 Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10         http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17
18 ]]--
19
20 local pairs, type, i18n, uci, math = pairs, type, luci.i18n, luci.model.uci, math
21
22 local iwi = require "iwinfo"
23 local utl = require "luci.util"
24 local uct = require "luci.model.uci.bind"
25
26 module "luci.model.wireless"
27
28 local ub = uct.bind("wireless")
29 local st, ifs
30
31 function init(cursor)
32         cursor:unload("wireless")
33         cursor:load("wireless")
34         ub:init(cursor)
35
36         st = uci.cursor_state()
37         ifs = { }
38
39         local count = { }
40
41         ub.uci:foreach("wireless", "wifi-iface",
42                 function(s)
43                         if s.device then
44                                 count[s.device] = count[s.device] and count[s.device] + 1 or 1
45
46                                 local id = "%s.network%d" %{ s.device, count[s.device] }
47
48                                 ifs[id] = {
49                                         id    = id,
50                                         sid   = s['.name'],
51                                         count = count
52                                 }
53
54                                 local dev = st:get("wireless", s['.name'], "ifname")
55                                         or st:get("wireless", s['.name'], "device")
56
57                                 local wtype = dev and iwi.type(dev)
58                                 if dev and wtype then
59                                         ifs[id].winfo = iwi[wtype]
60                                         ifs[id].wdev  = dev
61                                 end
62                         end
63                 end)
64 end
65
66 function get_device(self, dev)
67         return device(dev)
68 end
69
70 function get_devices(self)
71         local devs = { }
72         ub.uci:foreach("wireless", "wifi-device",
73                 function(s) devs[#devs+1] = device(s['.name']) end)
74         return devs
75 end
76
77 function get_network(self, id)
78         if ifs[id] then
79                 return network(ifs[id].sid)
80         else
81                 local n
82                 for n, _ in pairs(ifs) do
83                         if ifs[n].sid == id then
84                                 return network(id)
85                         end
86                 end
87         end
88 end
89
90 function add_network(self, options)
91         if type(options) == "table" and options.device and
92                 ub.uci:get("wireless", options.device) == "wifi-device"
93         then
94                 local s = ub.uci:section("wireless", "wifi-iface", nil, options)
95                 local c = 1
96                 ub.uci:foreach("wireless", "wifi-iface", function(s) c = c + 1 end)
97
98                 local id = "%s.network%d" %{ options.device, c }
99                 ifs[id] = {
100                         id    = id,
101                         sid   = s,
102                         count = c
103                 }
104
105                 local wtype = iwi.type(options.device)
106                 if wtype then
107                         ifs[id].winfo = iwi[wtype]
108                         ifs[id].wdev  = options.device
109                 end
110
111                 return network(s)
112         end
113 end
114
115 function del_network(self, id)
116         if ifs[id] then
117                 ub.uci:delete("wireless", ifs[id].sid)
118                 ifs[id] = nil
119         else
120                 local n
121                 for n, _ in pairs(ifs) do
122                         if ifs[n].sid == id then
123                                 ub.uci:delete("wireless", id)
124                                 ifs[n] = nil
125                         end
126                 end
127         end
128 end
129
130 function shortname(self, iface)
131         if iface.wdev and iface.winfo then
132                 return "%s %q" %{
133                         i18n.translate(iface:active_mode()),
134                         iface:active_ssid() or i18n.translate("(hidden)")
135                 }
136         else
137                 return iface:name()
138         end
139 end
140
141 function get_i18n(self, iface)
142         if iface.wdev and iface.winfo then
143                 return "%s: %s %q (%s)" %{
144                         i18n.translate("Wireless Network"),
145                         i18n.translate(iface:active_mode()),
146                         iface:active_ssid() or i18n.translate("(hidden)"), iface.wdev
147                 }
148         else
149                 return "%s: %q" %{ i18n.translate("Wireless Network"), iface:name() }
150         end
151 end
152
153 function find_interfaces(self, iflist, brlist)
154         local iface
155         for iface, _ in pairs(ifs) do
156                 iflist[iface] = ifs[iface]
157         end
158 end
159
160 function ignore_interface(self, iface)
161         if ifs and ifs[iface] then
162                 return false
163         else
164                 return iwi.type(iface) and true or false
165         end
166 end
167
168 function add_interface(self, net, iface)
169         if ifs and ifs[iface] and ifs[iface].sid then
170                 ub.uci:set("wireless", ifs[iface].sid, "network", net:name())
171                 ifs[iface].network = net:name()
172                 return true
173         end
174
175         return false
176 end
177
178 function del_interface(self, net, iface)
179         if ifs and ifs[iface] and ifs[iface].sid then
180                 ub.uci:delete("wireless", ifs[iface].sid, "network")
181                 --return true
182         end
183
184         return false
185 end
186
187
188 device = ub:section("wifi-device")
189 device:property("type")
190 device:property("channel")
191 device:property_bool("disabled")
192
193 function device.name(self)
194         return self.sid
195 end
196
197 function device.is_up(self)
198         local rv = false
199
200         if not self:disabled() then
201                 st:foreach("wireless", "wifi-iface",
202                         function(s)
203                                 if s.device == self:name() and s.up == "1" then
204                                         rv = true
205                                         return false
206                                 end
207                         end)
208         end
209
210         return rv
211 end
212
213 function device.get_networks(self)
214         local nets = { }
215
216         ub.uci:foreach("wireless", "wifi-iface",
217                 function(s)
218                         if s.device == self:name() then
219                                 nets[#nets+1] = network(s['.name'])
220                         end
221                 end)
222
223         return nets
224 end
225
226
227 network = ub:section("wifi-iface")
228 network:property("mode")
229 network:property("ssid")
230 network:property("bssid")
231 network:property("network")
232
233 function network._init(self, sid)
234         local count =  { }
235
236         local parent_dev = st:get("wireless", sid, "device")
237                 or ub.uci:get("wireless", sid, "device")
238
239         local dev = st:get("wireless", sid, "ifname")
240                 or parent_dev
241
242         if dev then
243                 ub.uci:foreach("wireless", "wifi-iface",
244                         function(s)
245                                 if s.device then
246                                         count[s.device] = count[s.device]
247                                                 and count[s.device] + 1 or 1
248                                         if s['.name'] == sid then
249                                                 self.id = "%s.network%d" %{ parent_dev, count[s.device] }
250
251                                                 local wtype = iwi.type(dev)
252                                                 if dev and wtype then
253                                                         self.winfo = iwi[wtype]
254                                                         self.wdev  = dev
255                                                 end
256                                         end
257                                 end
258                         end)
259         end
260 end
261
262 function network.name(self)
263         return self.id
264 end
265
266 function network.ifname(self)
267         return self.wdev
268 end
269
270 function network.get_device(self)
271         if self.device then
272                 return device(self.device)
273         end
274 end
275
276 function network.is_up(self)
277         return (st:get("wireless", self.sid, "up") == "1")
278 end
279
280 function network.active_mode(self)
281         local m = self.winfo and self.winfo.mode(self.wdev)
282         if not m then
283                 m = self:mode()
284                 if     m == "ap"      then m = "AP"
285                 elseif m == "sta"     then m = "Client"
286                 elseif m == "adhoc"   then m = "Ad-Hoc"
287                 elseif m == "mesh"    then m = "Mesh"
288                 elseif m == "monitor" then m = "Monitor"
289                 end
290         end
291         return m or "Client"
292 end
293
294 function network.active_mode_i18n(self)
295         return i18n.translate(self:active_mode())
296 end
297
298 function network.active_ssid(self)
299         return self.winfo and self.winfo.ssid(self.wdev) or
300                 self:ssid()
301 end
302
303 function network.active_bssid(self)
304         return self.winfo and self.winfo.bssid(self.wdev) or
305                 self:bssid() or "00:00:00:00:00:00"
306 end
307
308 function network.active_encryption(self)
309         local enc = self.winfo and self.winfo.encryption(self.wdev)
310         return enc and enc.description or "-"
311 end
312
313 function network.assoclist(self)
314         return self.winfo and self.winfo.assoclist(self.wdev) or { }
315 end
316
317 function network.frequency(self)
318         local freq = self.winfo and self.winfo.frequency(self.wdev)
319         return freq and freq > 0 and "%.03f" % (freq / 1000)
320 end
321
322 function network.bitrate(self)
323         local rate = self.winfo and self.winfo.bitrate(self.wdev)
324         return rate and rate > 0 and (rate / 1000)
325 end
326
327 function network.channel(self)
328         return self.winfo and self.winfo.channel(self.wdev)
329 end
330
331 function network.signal(self)
332         return self.winfo and self.winfo.signal(self.wdev) or 0
333 end
334
335 function network.noise(self)
336         return self.winfo and self.winfo.noise(self.wdev) or 0
337 end
338
339 function network.signal_level(self, s, n)
340         if self:active_bssid() ~= "00:00:00:00:00:00" then
341                 local signal = s or self:signal()
342                 local noise  = n or self:noise()
343
344                 if signal < 0 and noise < 0 then
345                         local snr = -1 * (noise - signal)
346                         return math.floor(snr / 5)
347                 else
348                         return 0
349                 end
350         else
351                 return -1
352         end
353 end
354
355 function network.signal_percent(self)
356         local qc = self.winfo and
357                 self.winfo.quality(self.wdev) or 0
358
359         local qm = self.winfo and
360                 self.winfo.quality_max(self.wdev) or 0
361
362         if qc > 0 and qm > 0 then
363                 return math.floor((100 / qm) * qc)
364         else
365                 return 0
366         end
367 end