#!/usr/bin/lua

function getConfig(configname)

  local uci=require("uci")
  local cur=uci.cursor()
  local config
  if configname then
    config=configname
  else
    config="beacon"
  end

  logging = cur.get(config,"logging","enabled") 

  mqtt_host = cur.get(config,"mqtt","host")
  mqtt_port = cur.get(config,"mqtt","port")
  mqtt_id = cur.get(config,"mqtt","id")
  mqtt_topic = cur.get(config,"mqtt","topic")

  mqtt_user = cur.get(config,"mqtt","user")
  mqtt_passwd = cur.get(config,"mqtt","password")

  if mqtt_host and not mqtt_id then
    mqtt_id="beaconmon"
  end

  if mqtt_host and not mqtt_port then
    mqtt_port = 1883
  end

  if mqtt_host and not mqtt_topic then
    mqtt_topic = 'beaconmon/{type}/{details}'
  end

end

function capture(cmd, raw)
  local f = assert(io.popen(cmd, 'r'))
  local s = assert(f:read('*a'))
  f:close()
  if raw then return s end
  s = string.gsub(s, '^%s+', '')
  s = string.gsub(s, '%s+$', '')
  s = string.gsub(s, '[\n\r]+', ' ')
  return s
end

function mqtt_encode(str)
  if (str) then
    str = string.gsub (str, "\n", "")
    str = string.gsub (str, "/", "-")
  end
  return str	
end

function printLog(str)
  if logging=="on" then
    capture("logger -t beaconmon "..str)
  else 
    print(str)  
  end 
end

function run_command(cmd)

  local file = assert(io.popen(cmd, 'r'))
  local output = file:read('*all')
  file:close()

end

function open_dump()

  run_command("/bin/kill `/usr/bin/pgrep hcidump`")
  run_command("/bin/kill `/usr/bin/pgrep hcitool`")
  f = assert(io.popen ("/usr/bin/hcidump --raw"))
  run_command("/usr/bin/hciconfig hci0 down")
  run_command("/usr/bin/hciconfig hci0 up")
  f_null = assert(io.popen ("/usr/bin/hcitool lescan --duplicates"))

  return f

end

function dump(o)
   if type(o) == 'table' then
      local s = '{ '
      for k,v in pairs(o) do
         if type(k) ~= 'number' then k = '"'..k..'"' end
         s = s .. '['..k..'] = ' .. dump(v) .. ','
--         s = s .. dump(v) .. ','
      end
      return s .. '} '
   else
      return tostring(o)
   end
end

function trim(s)
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

function mqtt_pub(path,value)
  printLog("Pub "..path.." "..value)
  return mqtt_client:publish(path,value)
end

function process_packet(packet)

  local bytes={}
  local idx=1
  local len
  local mac
  local flags
  local power
  local tx
  local type
  local paysublen
  local uuid
  local major
  local minor
  local details

  if packet:len()>1 then

    while packet:len()>2 do
      bytes[idx]=trim(packet:sub(1,3))
      idx=idx+1
      packet=packet:sub(4)
    end
    len = idx-1

    if bytes[1]=='04' and bytes[2]=='3E' then
      -- BLE Beacon?
      type=""
      mac=bytes[13]..':'..bytes[12]..':'..bytes[11]..':'..bytes[10]..':'..bytes[9]..':'..bytes[8]
      flags=bytes[14]
      power=tonumber("0x"..bytes[len-1])-256
      tx=tonumber("0x"..bytes[len])-256
      local j = 15
      while j<len-2 do
        paysublen=tonumber('0x'..bytes[j])
	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
	  -- Standard UUID iBeacon
          type="ibeacon"
	  local uuid1=bytes[j+6]..bytes[j+7]..bytes[j+8]..bytes[j+9]
	  local uuid2=bytes[j+10]..bytes[j+11]
	  local uuid3=bytes[j+12]..bytes[j+13]
	  local uuid4=bytes[j+14]..bytes[j+15]
	  local uuid5=bytes[j+16]..bytes[j+17]..bytes[j+18]..bytes[j+19]..bytes[j+20]..bytes[j+21]
	  uuid=string.lower(uuid1..'-'..uuid2..'-'..uuid3..'-'..uuid4..'-'..uuid5)
          major=bytes[j+23]..bytes[j+22]
          minor=bytes[j+25]..bytes[j+24]
	end
        j=j+1+paysublen
      end
      if type=="ibeacon" then
        printLog(string.format("{type:'ibeacon',mac:'%s',uuid:'%s',major:'%s',minor:'%s',power:%d,tx:%d}",mac,uuid,major,minor,power,tx))
        details=uuid..'/'..major..'/'..minor
      else
        type='unknown'
        details=dump(bytes)
--        printLog(details)
      end
      if not (type=="unknown") then
        mqtt_path=string.gsub(mqtt_topic,"{(.-)}", 
          function (name) 
            if name=="type" then
              return mqtt_encode(type)
            elseif name=="details" then
              return mqtt_encode(details)
            else
              return '{'..name..'}'
            end      
          end)
        
        if not pcall(mqtt_pub,mqtt_path,tx) then
          printLog('Reconnecting MQTT...')
          mqtt_client:connect(mqtt_id)
        end

      end        
    end
  end

end

function read_loop()

  packet = ""

  for line in inp:lines() do

    lchr=line:sub(1,1)
    line=trim(line)

    if lchr=="<" or lchr==">" then
      line = trim(line:sub(2))
    end

    if not (lchr == " ") then
      process_packet(packet)
      packet = ""
    end

    llen=line:len()

    if llen<59 then
      packet = packet .. " " .. line
      process_packet(packet)
      packet=""
    else
      packet = packet .. " " .. line
    end    
    
  end

end

getConfig(arg[1])

if mqtt_host then
  MQTT = require "paho.mqtt"
  mqtt_client = MQTT.client.create(mqtt_host, mqtt_port)
  if mqtt_user then
    mqtt_client:auth(mqtt_user, mqtt_passwd)
  end
  mqtt_client:connect(mqtt_id)
end

inp = open_dump()

read_loop()