a574f664249410ca789b671eefe37233a21e2779
[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     if not(type) then
123       type="name"
124     end  
125     details=name
126   else
127     if not type then 
128       type='unknown'
129     end  
130     details=mac
131   end
132
133   if not (type=="unknown") then
134     mqtt_path=string.gsub(mqtt_topic,"{(.-)}",
135       function (name) 
136         if name=="type" then
137           return mqtt_encode(type)
138         elseif name=="details" then
139           return mqtt_encode(details)
140         else
141           return '{'..name..'}'
142         end      
143       end)
144         
145     if not pcall(mqtt_pub,mqtt_path,dump(packet)) then
146       printLog('Reconnecting MQTT...')
147       mqtt_client:connect(mqtt_id)
148     end
149
150   end
151
152 end
153
154 function starts(String,Start)
155    return string.sub(String,1,string.len(Start))==Start
156 end
157
158 function split(inputstr, sep)
159         if sep == nil then
160                 sep = "%s"
161         end
162         local t={} ; i=1
163         for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
164                 t[i] = str
165                 i = i + 1
166         end
167         return t
168 end
169
170 function read_loop()
171
172   packet={}
173   inbound=false
174
175   while true do
176
177     str=inp:read("*l")
178
179     if str then 
180
181       str=trim(str)
182
183       if inbound then
184         t = split(str,':')
185         if #t>=2 then
186           name=t[1]
187           value=trim(table.concat(t,':',2))
188           if name=="Address" then
189             value=split(value)[1]
190           end
191           packet[name]=value
192         elseif #t==1 and name then
193           if not(packet[name..'.list']) then
194             packet[name..'.list']={}
195           end  
196           table.insert(packet[name..'.list'],(trim(t[1])))
197         end  
198       end  
199
200       if starts(str,'> HCI Event: LE Meta Event (0x3e)') then
201         inbound=true
202         name=nil
203       elseif starts(str,'RSSI:') then
204         inbound=false
205         process_packet(packet)
206         packet={}
207       end
208       
209     end  
210     
211   end
212
213 end
214
215 io.stdout:setvbuf('no')
216 io.stdin:setvbuf('no') 
217
218 getConfig(arg[1])
219
220 if mqtt_host then
221   MQTT = require "mosquitto"
222   mqtt_client = MQTT.new(mqtt_id)
223   if mqtt_user then
224     mqtt_client:login_set(mqtt_user, mqtt_passwd)
225   end
226   mqtt_client:connect(mqtt_host,mqtt_port)
227 end
228
229 inp = open_dump()
230
231 read_loop()