Merge branch 'master' of rvb.name:openhab-process
[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   else 
69     print(str)  
70   end 
71 end
72
73 function run_command(cmd)
74
75   local file = assert(io.popen(cmd, 'r'))
76   local output = file:read('*all')
77   file:close()
78
79 end
80
81 function open_dump()
82
83   run_command("/bin/kill `/usr/bin/pgrep btmon`")
84   run_command("/bin/kill `/usr/bin/pgrep hcitool`")
85   f = assert(io.popen ("/usr/bin/stdbuf -o0 /usr/bin/btmon"))
86   run_command("/usr/bin/hciconfig hci0 down")
87   run_command("/usr/bin/hciconfig hci0 up")
88   f_null = assert(io.popen ("/usr/bin/hcitool lescan --duplicates --passive"))
89
90   return f
91
92 end
93
94 function dump(o)
95   return json.encode(o)
96 end
97
98 function trim(s)
99   return (s:gsub("^%s*(.-)%s*$", "%1"))
100 end
101
102 function mqtt_pub(path,value)
103   res=mqtt_client:publish(path,value)
104   printLog("Pub "..path.." "..value.." returned "..res);
105   return res
106 end
107
108 function process_packet(packet)
109
110   local mac
111   local uuid
112   local details
113   local type
114   local name
115
116   mac = packet['Address']
117   uuid = packet['uuid']
118   type = packet['Type']
119   name = packet['Name (complete)']
120
121   if type=='iBeacon' then
122     details=uuid
123   elseif name then
124     if not(type) then
125       type="name"
126     end  
127     details=name
128   else
129     if not type then 
130       type='unknown'
131     end  
132     details=mac
133   end
134
135   if not (type=="unknown") then
136     mqtt_path=string.gsub(mqtt_topic,"{(.-)}",
137       function (name) 
138         if name=="type" then
139           return mqtt_encode(type)
140         elseif name=="details" then
141           return mqtt_encode(details)
142         else
143           return '{'..name..'}'
144         end      
145       end)
146         
147     if not pcall(mqtt_pub,mqtt_path,dump(packet)) then
148       printLog('Reconnecting MQTT...')
149       mqtt_client:connect(mqtt_id)
150     end
151
152   end
153
154 end
155
156 function starts(String,Start)
157    return string.sub(String,1,string.len(Start))==Start
158 end
159
160 function split(inputstr, sep)
161         if sep == nil then
162                 sep = "%s"
163         end
164         local t={} ; i=1
165         for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
166                 t[i] = str
167                 i = i + 1
168         end
169         return t
170 end
171
172 function read_loop()
173
174   packet={}
175   inbound=false
176
177   while true do
178
179     str=inp:read("*l")
180
181     if str then 
182
183       str=trim(str)
184
185       if inbound then
186         t = split(str,':')
187         if #t>=2 then
188           name=t[1]
189           value=trim(table.concat(t,':',2))
190           if name=="Address" then
191             value=split(value)[1]
192           end
193           packet[name]=value
194         elseif #t==1 and name then
195           if not(packet[name..'.list']) then
196             packet[name..'.list']={}
197           end  
198           table.insert(packet[name..'.list'],(trim(t[1])))
199         end  
200       end  
201
202       if starts(str,'> HCI Event: LE Meta Event (0x3e)') then
203         inbound=true
204         name=nil
205       elseif starts(str,'RSSI:') then
206         inbound=false
207         process_packet(packet)
208         packet={}
209       end
210       
211     end  
212     
213   end
214
215 end
216
217 io.stdout:setvbuf('no')
218 io.stdin:setvbuf('no') 
219
220 getConfig(arg[1])
221
222 if mqtt_host then
223   MQTT = require "mosquitto"
224   mqtt_client = MQTT.new(mqtt_id)
225   if mqtt_user then
226     mqtt_client:login_set(mqtt_user, mqtt_passwd)
227   end
228   mqtt_client:connect(mqtt_host,mqtt_port)
229 end
230
231 inp = open_dump()
232
233 read_loop()