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