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