Обработка спецсимволов для упрощения работы с OpenHAB.
[openhab-process.git] / mqtt-bt / scan-beacons
1 #!/usr/bin/lua
2
3 function getConfig(configname)
4
5   local uci=require("uci")
6   local cur=uci.cursor()
7   local config
8   if configname then
9     config=configname
10   else
11     config="beacon"
12   end
13
14   logging = cur.get(config,"logging","enabled") 
15
16   mqtt_host = cur.get(config,"mqtt","host")
17   mqtt_port = cur.get(config,"mqtt","port")
18   mqtt_id = cur.get(config,"mqtt","id")
19   mqtt_topic = cur.get(config,"mqtt","topic")
20
21   mqtt_user = cur.get(config,"mqtt","user")
22   mqtt_passwd = cur.get(config,"mqtt","password")
23
24   if mqtt_host and not mqtt_id then
25     mqtt_id="beaconmon"
26   end
27
28   if mqtt_host and not mqtt_port then
29     mqtt_port = 1883
30   end
31
32   if mqtt_host and not mqtt_topic then
33     mqtt_topic = 'beaconmon/{type}/{details}'
34   end
35
36 end
37
38 function capture(cmd, raw)
39   local f = assert(io.popen(cmd, 'r'))
40   local s = assert(f:read('*a'))
41   f:close()
42   if raw then return s end
43   s = string.gsub(s, '^%s+', '')
44   s = string.gsub(s, '%s+$', '')
45   s = string.gsub(s, '[\n\r]+', ' ')
46   return s
47 end
48
49 function mqtt_encode(str)
50   if (str) then
51     str = string.gsub (str, "\n", "")
52     str = string.gsub (str, ":", "-")
53     str = string.gsub (str, "/", "-")
54     str = string.gsub (str, " ", "_")
55   end
56   return str    
57 end
58
59 function printLog(str)
60   if logging=="on" then
61     capture("logger -t beaconmon "..str)
62   else 
63     print(str)  
64   end 
65 end
66
67 function run_command(cmd)
68
69   local file = assert(io.popen(cmd, 'r'))
70   local output = file:read('*all')
71   file:close()
72
73 end
74
75 function open_dump()
76
77   run_command("/bin/kill `/usr/bin/pgrep hcidump`")
78   run_command("/bin/kill `/usr/bin/pgrep hcitool`")
79   f = assert(io.popen ("/usr/bin/hcidump --raw"))
80   run_command("/usr/bin/hciconfig hci0 down")
81   run_command("/usr/bin/hciconfig hci0 up")
82   f_null = assert(io.popen ("/usr/bin/hcitool lescan --duplicates"))
83
84   return f
85
86 end
87
88 function dump(o)
89    if type(o) == 'table' then
90       local s = '{ '
91       for k,v in pairs(o) do
92          if type(k) ~= 'number' then k = '"'..k..'"' end
93          s = s .. '['..k..'] = ' .. dump(v) .. ','
94 --         s = s .. dump(v) .. ','
95       end
96       return s .. '} '
97    else
98       return tostring(o)
99    end
100 end
101
102 function trim(s)
103   return (s:gsub("^%s*(.-)%s*$", "%1"))
104 end
105
106 function mqtt_pub(path,value)
107   printLog("Pub "..path.." "..value)
108   return mqtt_client:publish(path,value)
109 end
110
111 function process_packet(packet)
112
113   local bytes={}
114   local idx=1
115   local len
116   local mac
117   local flags
118   local power
119   local tx
120   local type
121   local paysublen
122   local uuid
123   local major
124   local minor
125   local details
126
127   if packet:len()>1 then
128
129     while packet:len()>2 do
130       bytes[idx]=trim(packet:sub(1,3))
131       idx=idx+1
132       packet=packet:sub(4)
133     end
134     len = idx-1
135
136     if bytes[1]=='04' and bytes[2]=='3E' then
137       -- BLE Beacon?
138       type=""
139       mac=bytes[13]..':'..bytes[12]..':'..bytes[11]..':'..bytes[10]..':'..bytes[9]..':'..bytes[8]
140       flags=bytes[14]
141       power=tonumber("0x"..bytes[len-1])-256
142       tx=tonumber("0x"..bytes[len])-256
143       local j = 15
144       while j<len-2 do
145         paysublen=tonumber('0x'..bytes[j])
146         if bytes[j+1]=="FF" and bytes[j+2]=="4C" and bytes[j+3]=="00" and bytes[j+4]=="02" and bytes[j+5]=="15" then
147           -- Standard UUID iBeacon
148           type="ibeacon"
149           local uuid1=bytes[j+6]..bytes[j+7]..bytes[j+8]..bytes[j+9]
150           local uuid2=bytes[j+10]..bytes[j+11]
151           local uuid3=bytes[j+12]..bytes[j+13]
152           local uuid4=bytes[j+14]..bytes[j+15]
153           local uuid5=bytes[j+16]..bytes[j+17]..bytes[j+18]..bytes[j+19]..bytes[j+20]..bytes[j+21]
154           uuid=string.lower(uuid1..'-'..uuid2..'-'..uuid3..'-'..uuid4..'-'..uuid5)
155           major=bytes[j+23]..bytes[j+22]
156           minor=bytes[j+25]..bytes[j+24]
157         end
158         j=j+1+paysublen
159       end
160       if type=="ibeacon" then
161         printLog(string.format("{type:'ibeacon',mac:'%s',uuid:'%s',major:'%s',minor:'%s',power:%d,tx:%d}",mac,uuid,major,minor,power,tx))
162         details=uuid..'/'..major..'/'..minor
163       else
164         type='unknown'
165         details=dump(bytes)
166 --        printLog(details)
167       end
168       if not (type=="unknown") then
169         mqtt_path=string.gsub(mqtt_topic,"{(.-)}", 
170           function (name) 
171             if name=="type" then
172               return mqtt_encode(type)
173             elseif name=="details" then
174               return mqtt_encode(details)
175             else
176               return '{'..name..'}'
177             end      
178           end)
179         
180         if not pcall(mqtt_pub,mqtt_path,tx) then
181           printLog('Reconnecting MQTT...')
182           mqtt_client:connect(mqtt_id)
183         end
184
185       end        
186     end
187   end
188
189 end
190
191 function read_loop()
192
193   packet = ""
194
195   for line in inp:lines() do
196
197     lchr=line:sub(1,1)
198     line=trim(line)
199
200     if lchr=="<" or lchr==">" then
201       line = trim(line:sub(2))
202     end
203
204     if not (lchr == " ") then
205       process_packet(packet)
206       packet = ""
207     end
208
209     llen=line:len()
210
211     if llen<59 then
212       packet = packet .. " " .. line
213       process_packet(packet)
214       packet=""
215     else
216       packet = packet .. " " .. line
217     end    
218     
219   end
220
221 end
222
223 getConfig(arg[1])
224
225 if mqtt_host then
226   MQTT = require "paho.mqtt"
227   mqtt_client = MQTT.client.create(mqtt_host, mqtt_port)
228   if mqtt_user then
229     mqtt_client:auth(mqtt_user, mqtt_passwd)
230   end
231   mqtt_client:connect(mqtt_id)
232 end
233
234 inp = open_dump()
235
236 read_loop()