Корректный под всеми системами отстрел всех лишних процессов при перезапуске. Требует...
[openhab-process.git] / mqtt-bt / scan-beacons
1 #!/usr/bin/lua
2
3 json = require("json")
4 socket = require("socket")
5
6 function getConfig(configname)
7
8   local status,uci = pcall(require,"uci")
9
10   local config
11
12   if status then
13
14     if configname then
15       config=configname
16     else
17       config="beacon"
18     end
19
20     local cur=uci.cursor()
21
22     logging = cur.get(config,"logging","enabled") 
23
24     mqtt_host = cur.get(config,"mqtt","host")
25     mqtt_port = cur.get(config,"mqtt","port")
26     mqtt_id = cur.get(config,"mqtt","id")
27     mqtt_topic = cur.get(config,"mqtt","topic")
28   
29     mqtt_user = cur.get(config,"mqtt","user")
30     mqtt_passwd = cur.get(config,"mqtt","password")
31
32   else
33   
34     local status,ini = pcall(require,"ini")
35     if not status then
36       os.exit(1)
37     end 
38
39     if configname then
40       config=configname
41     else
42       config="/etc/beacon.ini"
43     end
44
45     local cur=ini.parse_file(config)
46     
47     logging = cur["logging"]["enabled"] 
48
49     mqtt_host = cur["mqtt"]["host"]
50     mqtt_port = cur["mqtt"]["port"]
51     mqtt_id = cur["mqtt"]["id"]
52     mqtt_topic = cur["mqtt"]["topic"]
53   
54     mqtt_user = cur["mqtt"]["user"]
55     mqtt_passwd = cur["mqtt"]["password"]
56   
57   end
58
59   hostname = socket.dns.gethostname()
60   if mqtt_host and not mqtt_id then
61     socket = require("socket")
62     posix = require("posix")
63     pid = posix.getpid()
64     mqtt_id="beaconmon-"..hostname.."-"..pid
65   end
66
67   if mqtt_host and not mqtt_port then
68     mqtt_port = 1883
69   end
70
71   if mqtt_host and not mqtt_topic then
72     mqtt_topic = 'beaconmon/{type}/{details}'
73   end
74
75 end
76
77 function capture(cmd, raw)
78   local f = assert(io.popen(cmd, 'r'))
79   local s = assert(f:read('*a'))
80   f:close()
81   if raw then return s end
82   s = string.gsub(s, '^%s+', '')
83   s = string.gsub(s, '%s+$', '')
84   s = string.gsub(s, '[\n\r]+', ' ')
85   return s
86 end
87
88 function mqtt_encode(str)
89   if (str) then
90     str = string.gsub (str, "\n", "")
91     str = string.gsub (str, ":", "-")
92     str = string.gsub (str, "/", "-")
93     str = string.gsub (str, " ", "_")
94   end
95   return str    
96 end
97
98 function printLog(str)
99   if logging=="yes" then
100     capture("logger -t beaconmon \""..str.."\"")
101   else 
102     print(str)  
103   end 
104 end
105
106 function run_command(cmd)
107
108   local file = assert(io.popen(cmd, 'r'))
109   local output = file:read('*all')
110   file:close()
111
112 end
113
114 function open_dump()
115
116   run_command("/usr/bin/pkill btmon")
117   run_command("/usr/bin/pkill hcitool")
118   f = assert(io.popen ("/usr/bin/stdbuf -o0 /usr/bin/btmon"))
119   run_command("hciconfig hci0 down")
120   run_command("hciconfig hci0 up")
121   f_null = assert(io.popen ("hcitool lescan --duplicates --passive"))
122
123   return f
124
125 end
126
127 function dump(o)
128   return json.encode(o)
129 end
130
131 function trim(s)
132   return (s:gsub("^%s*(.-)%s*$", "%1"))
133 end
134
135 local function starts_with(str, start)
136    return str:sub(1, #start) == start
137 end
138
139 function mqtt_pub(path,value)
140   res=mqtt_client:publish(path,value)
141   printLog("Pub "..path.." returned "..res);
142   return res
143 end
144
145 function process_packet(packet)
146
147   local mac
148   local uuid
149   local details
150   local type
151   local name
152
153   packet['origin'] = hostname
154
155   mac = packet['Address']
156   uuid = packet['UUID']
157   type = packet['Type']
158   name = packet['Name (complete)']
159
160   if type and starts_with(type,'iBeacon') then
161     details=uuid
162   elseif name then
163     if not(type) then
164       type="name"
165     end  
166     details=name
167   else
168     if not type then 
169       type='unknown'
170     end  
171     details=mac
172   end
173
174   if not (type=="unknown") then
175     mqtt_path=string.gsub(mqtt_topic,"{(.-)}",
176       function (name) 
177         if name=="type" then
178           return mqtt_encode(type)
179         elseif name=="details" then
180           return mqtt_encode(details)
181         else
182           return '{'..name..'}'
183         end      
184       end)
185         
186     if not pcall(mqtt_pub,mqtt_path,dump(packet)) then
187       printLog('Reconnecting MQTT...')
188       mqtt_client:connect(mqtt_id)
189     end
190
191   end
192
193 end
194
195 function starts(String,Start)
196    return string.sub(String,1,string.len(Start))==Start
197 end
198
199 function split(inputstr, sep)
200         if sep == nil then
201                 sep = "%s"
202         end
203         local t={} ; i=1
204         for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
205                 t[i] = str
206                 i = i + 1
207         end
208         return t
209 end
210
211 function read_loop()
212
213   packet={}
214   inbound=false
215
216   while true do
217
218     str=inp:read("*l")
219
220     if str then 
221
222       str=trim(str)
223
224       if inbound then
225         t = split(str,':')
226         if #t>=2 then
227           name=t[1]
228           value=trim(table.concat(t,':',2))
229           if name=="Address" then
230             value=split(value)[1]
231           end
232           packet[name]=value
233         elseif #t==1 and name then
234           if not(packet[name..'.list']) then
235             packet[name..'.list']={}
236           end  
237           table.insert(packet[name..'.list'],(trim(t[1])))
238         end  
239       end  
240
241       if starts(str,'> HCI Event: LE Meta Event (0x3e)') then
242         inbound=true
243         name=nil
244       elseif starts(str,'RSSI:') then
245         inbound=false
246         process_packet(packet)
247         packet={}
248       end
249       
250     end  
251     
252   end
253
254 end
255
256 io.stdout:setvbuf('no')
257 io.stdin:setvbuf('no') 
258
259 getConfig(arg[1])
260
261 if mqtt_host then
262   MQTT = require "mosquitto"
263   mqtt_client = MQTT.new(mqtt_id)
264   if mqtt_user then
265     mqtt_client:login_set(mqtt_user, mqtt_passwd)
266   end
267   mqtt_client:connect(mqtt_host,mqtt_port)
268 end
269
270 inp = open_dump()
271
272 read_loop()