Fancy Gap HTRN

My Personal Playground

  • Increase font size
  • Default font size
  • Decrease font size
Home Home Automation Insteon Insteon 2412n controller

Insteon 2412n controller

The following demonstrates the ability to use the Smarthome Smartlinc network controller, similar to a dedicated PLM.

I recently saw a post on the Smarthome forum that uncovered a previously undocument method to communicate directly with the PLM embedded with in the Smartlinc controller.

The Smartlinc listens on TCP port 9761 and allows bi-directional communication. 

Insteon.rb  Main program

#!/usr/bin/env ruby
require 'rubygems'
require 'thread'
require 'logger'
require '/usr/src/insteon/Sunrise.rb'
require 'pp'
require "mysql"
require 'net/smtp'
require '/usr/src/insteon/ControllerPLM.rb'
require '/usr/src/insteon/insteonDeviceTypes.rb'
require '/usr/src/insteon/insteonConfig.rb'

#$log.level = Logger::DEBUG

$log.info('Starting...')


$mysql = Mysql::new($dbhost, $dbusername, $dbpassword, $dbtable)

$recvqueue = Queue.new
$sendqueue = Queue.new

$controller = ControllerPLM.new($chost,$cport,$cusername,$cpassword,$shost,$sport,$susername,$spassword)
producer,webserver = $controller.start
producer.priority = 2
producer.abort_on_exception = true
webserver.abort_on_exception = true


def parse_buffstatus(e)

   logdata = "Message: #{e['Description']}(#{e['Event']}) "

   case e['Event'] 
      when '0250' # received standard message
         logdata += "From: #{e['From']} "
         logdata += "To: #{e['To']} "
         logdata += "Flags:[" + parse_flag(e['Flags']) + '] '
         logdata += "Command: #{e['Command']} "
         if e['Flags'].hex & 32 == 32
            query = "SELECT * from jos_insteondevices WHERE device = '#{e['From']}'"
            $log.debug(query)
            res = $mysql.query(query)
            if res.num_rows() > 0
               r = res.fetch_hash()
               case r['type'][0..1]
                  when '01', '02'
                     #if  e['Command'][0..1] == '00' or e['Command'][0..1] == '11' or e['Command'][0..1] == '13'
                        if  e['Command'][2..3].hex > 0x00
                           query = "UPDATE jos_insteondevices SET state='ON',brightness='#{hextopercent(e['Command'][2..3])}',lastupdate=NOW() WHERE device = '#{e['From']}'"
                           $log.debug(query)
                           res = $mysql.query(query)
                        else
                           query = "UPDATE jos_insteondevices SET state='OFF',brightness='#{hextopercent(e['Command'][2..3])}',lastupdate=NOW() WHERE device = '#{e['From']}'"
                           $log.debug(query)
                           res = $mysql.query(query)
                        end
                     #end
                     if  e['Command'][0..1] == '15' or e['Command'][0..1] == '16'
                         $controller.SendLightStatus(e['From'],0)
                     end
                  when '10'
                     if  e['Command'][0..1] == '11'
                        query = "UPDATE jos_insteondevices SET state='ON',brightness='N/A',lastupdate=NOW() WHERE device = '#{e['From']}'"
                        $log.debug(query)
                        res = $mysql.query(query)
                     else                                                                
                        query = "UPDATE jos_insteondevices SET state='OFF',brightness='N/A',lastupdate=NOW() WHERE device = '#{e['From']}'"
                        $log.debug(query)
                        res = $mysql.query(query)
                     end
               end
            end
         end   
         if e['Flags'].hex & 192 == 192
            query = "SELECT * from jos_insteondevices WHERE device = '#{e['From']}'"
            $log.debug(query)
            res = $mysql.query(query)
            if res.num_rows() > 0
               r = res.fetch_hash()
               case r['type'][0..3]
                  when '1001'
                     if  e['Command'][0..1] == '11'
                        query = "UPDATE jos_insteondevices SET state='ON',brightness='N/A',lastupdate=NOW() WHERE device = '#{e['From']}'"
                        $log.debug(query)
                        res = $mysql.query(query)
                     else                                                                
                        query = "UPDATE jos_insteondevices SET state='OFF',brightness='N/A',lastupdate=NOW() WHERE device = '#{e['From']}'"
                        $log.debug(query)
                        res = $mysql.query(query)
                     end
               end
            end
         end   
         if e['Flags'].hex & 192 == 192
            #query = "SELECT * from jos_insteondevices WHERE todeviceid = '#{e['From']}' and devicegroup = '#{e['To'][-2..-1]}'"
            #$log.debug(query)
            #res = $mysql.query(query)
            #if res.num_rows() > 0
            #   r = res.fetch_hash()
            #   case r['type'][0..3]
            #      when '0005'
            #         case e['Command'][0..1]
            #            when '11'
            #               query = "UPDATE jos_insteondevices SET state='ON' WHERE todeviceid = '#{e['From']}' and devicegroup = '#{e['To'][-2..-1]}'"
            #               $log.debug(query)
            #               #res = $mysql.query(query)
            #            when '13'
            #               query = "UPDATE jos_insteondevices SET state='OFF' WHERE todeviceid = '#{e['From']}' and devicegroup = '#{e['To'][-2..-1]}'"
            #               $log.debug(query)
            #               #res = $mysql.query(query)
            #      end
            #   end
            #end
         end
        when '0251' # received extended message
                logdata +=  "From: #{e['From']} "
                logdata += "To: #{e['To']} "
                logdata += "Flags:[" + parse_flag(e['Flags']) + '] '
                logdata += "Command: #{e['Command']} "
                logdata += "Extended: #{e['Extended']} "
        when '0253' # ALL-Linking Completed
                logdata += "From: #{e['From']} "
                logdata += "Group: #{e['To']} "
                logdata += "Link Code: "
                case e['Linkcode']
                  when '00'
                    logdata += "IM is a Responder"
                  when '01'
                    logdata += "IM is a Controller"
                  when 'FF'
                    logdata += "ALL-Link to the device was deleted"
                  else
                    logdata += "Unknown"
                end
                logdata += "Cat: #{e['Category']} "
                logdata += "Sub Cat: #{e['Subcategory']} "
                logdata += "Status: #{e['Status']}"
        when '0254' # Button Event Report
                logdata += "Status: "
                case e['Status']
                  when '02'
                    logdata += "IM SET Button tapped"
                  when '03'
                    logdata += "IM SET Button held"
                  when '04'
                    logdata += "IM SET Button released after hold"
                  when '12'
                    logdata += "IM Button 2 tapped"
                  when '13'
                    logdata += "IM Button 2 held"
                  when '14'
                    logdata += "IM Button 2 released after hold"
                  when '22'
                    logdata += "IM Button 3 tapped"
                  when '23'
                    logdata += "IM Button 3 held"
                  when '24'
                    logdata += "IM Button 3 released after hold"
                  else
                    logdata += "Unknown"
                end
        when '0256' # ALL-Link Cleanup Failure Report
                logdata += "Group: #{e['To']} "
                logdata += "Device: #{e['From']} "
        when '0257' # All-Link Report
                logdata += "Flags:[" + parse_flag(e['Flags']) + '] '
                logdata += "Group: #{e['To']} "
                logdata += "Device: #{e['From']} "
                logdata += "Data: #{e['Command']} "
        when '0258' # ALL-Link Cleanup Status Report
                logdata += "Status: #{e['Status']}"
        when '0260' # Get IM Info
                logdata += "Device: #{e['From']} "
                logdata += "Cat: #{e['Category']} "
                logdata += "Sub Cat: #{e['Subcategory']} "
                logdata += "Version: #{e['Version']} "
                logdata += "Status: #{e['Status']}"
        when '0261' # Scene Command
                logdata += "Scene: #{e['From']} "
                logdata += "Command: "
                case e['Command']
                  when '11'
                    logdata += "On"
                  when '12'
                    logdata += "Fast On"
                  when '13'
                    logdata += "Off"
                  when '14'
                    logdata += "Fast Off"
                  else
                    logdata += "Unknown"
                end
                logdata += " Result: #{e['Status']}"
        when '0262' # Send Message                                         
                logdata += "To: #{e['To']} "
                logdata += "Flags:[" + parse_flag(e['Flags']) + '] '
                logdata += "Command: #{e['Command']} "
                if e['Flags'].hex & 16 == 16
                  logdata += e['Extended'] + ' '
                end
                logdata += "Status: "
                case e['Status']
                  when '06'
                    logdata += "Ack"
                  when '15'
                    logdata += "Nack"
                  when 'FF'
                    logdata += "Fail"
                  else
                    logdata += "Unknown"
                end
        when '0265' # Cancel All-linking
                logdata += "Status: #{e['Status']} "
        when '0266' # Get IM Info
                logdata += "Cat: #{e['Category']} "
                logdata += "Sub Cat: #{e['Subcategory']} "
                logdata += "Version: #{e['Version']} "
                logdata += "Status: #{e['Status']}"
        when '0269' # Get First ALL-Link
                logdata += "Status: #{e['Status']} "
        when '026A' # Get Next ALL-Link
                logdata += "Status: #{e['Status']} "
        when '026B' # Set IM Configuration
                logdata += "Command: #{e['Command']} "
                logdata += "Status: #{e['Status']} "
        when '026F' # Manage ALL-Link Record
                logdata += "Command: #{e['Command']} "
                logdata += "Flags: #{e['Command']} "
                logdata += "Group: #{e['To']} "
                logdata += "Device: #{e['From']} "
                logdata += "Cat: #{e['Category']} "
                logdata += "Sub Cat: #{e['Subcategory']} "
                logdata += "Version: #{e['Version']} "
                logdata += "Status: #{e['Status']} "
        else
                logdata += "Command: #{e['Command']} "
  end
  $log.info(logdata)
  rescue 
      $log.error("Parse Buffer error #{$!}  #{$@[0]} Line: #{$.}")
end              

def parse_flag(flag)
    retval = flag.hex
    f = ''
    if retval & 128 == 128
        f = f +  'B'
    else
        f = f +  'b'
    end
    if retval & 64  == 64
        f = f +  'G'
    else
        f = f +  'g'
    end
    if retval & 32 == 32
        f = f +  'A'
    else
        f = f +  'a'
    end
    if retval & 16  == 16
         f = f +  'E'
   else
        f = f +  'e'
    end
    f = f + ' HL:' 
    if retval & 8 == 8
        f = f +  '1'
    else
        f = f +  '0'
    end
    if retval & 4 == 4
        f = f +  '1'
    else
        f = f +  '0'
    end
    f = f + ' MH:' 
    if retval & 2 == 2
        f = f +  '1'
    else
        f = f +  '0'
    end
    if retval & 1 == 1
        f = f +  '1'
    else
        f = f +  '0'
    end
    
    return f
end
def percenttohex(level)
  # convert from percent to byte
  return "%X" %(level.to_i * 2.55).to_i
end

def hextopercent(level)
  return (level.hex.to_i / 2.55).to_i
end

def checkSchedule(id,time=Time.now)
   date = Date.parse(time.to_s)
   calc = SolarEventCalculator.new(date, BigDecimal.new($long), BigDecimal.new($lat))  
   localSunrise = calc.compute_official_sunrise($timezone) 
   localSunset = calc.compute_official_sunset($timezone)  
   sr = Time.parse(localSunrise.to_s)
   ss = Time.parse(localSunset.to_s)
   
   logdata = ""
   retval = false
   query = "SELECT * from jos_insteonschedules as c WHERE c.id = '#{id}' and c.published = 1"
   $log.debug(query)
   sres = $mysql.query(query)
   if sres.num_rows() > 0
      s = sres.fetch_hash()
      logdata += "Schedule: #{s['description']}, "
      daymatch = false
      case time.wday()
      when 0
         daymatch = true if s['dsun'] == '1'
      when 1
         daymatch = true if s['dmon'] == '1' 
      when 2
         daymatch = true if s['dtue'] == '1'
      when 3
         daymatch = true if s['dwed'] == '1'
      when 4
         daymatch = true if s['dthu'] == '1'
      when 5
         daymatch = true if s['dfri'] == '1'
      when 6
         daymatch = true if s['dsat'] == '1'
      end
      if daymatch
         logdata += "Days matched, "
         case s['timecode'] 
         when '0' # timecode 0 = before time
            logdata += "Schedule before time "
            stime = Time.parse("#{time.strftime("%Y-%m-%d")} #{s['stime']}")
            #$log.info("Times ct #{time.strftime("%Y/%m/%d %I:%M%p")} at #{stime.strftime("%Y/%m/%d %I:%M%p")} ")
            if (time - stime) <= 0
               retval = true
               #$log.info("Time match #{time} #{stime}")
            end
         when '1' # timecode 1 = After time
            logdata += "Schedule After time "
            stime = Time.parse("#{time.strftime("%Y-%m-%d")} #{s['stime']}")
            #$log.info("Times ct #{time.strftime("%Y/%m/%d %I:%M%p")} at #{stime.strftime("%Y/%m/%d %I:%M%p")} ")
            if time >= stime
               retval = true
               #$log.info("Time match #{time} #{stime}")
            end
         when '2' # timecode 2 = at Time
            logdata += "Schedule AT time "
            stime = Time.parse("#{time.strftime("%Y-%m-%d")} #{s['stime']}")
            #$log.info("Times ct #{time.strftime("%Y/%m/%d %I:%M%p")} at #{stime.strftime("%Y/%m/%d %I:%M%p")} diff #{time - stime}")
            diff = time - stime
            if (diff >= 0 && diff <= 0.5)
               retval = true
               #$log.info("Time match #{time} #{stime}")
            end
         when '3' # timecode 3 = before sunrise
            logdata += "Schedule before sunrise "
            
            if time >= ss
               sr += 60*60*24  # add day for sunrise tomorrow
            end
            #$log.info("Times sr #{sr.strftime("%Y/%m/%d %I:%M%p")} ss #{ss.strftime("%Y/%m/%d %I:%M%p")} ")
            if time <= sr - (s['timeoffset'].to_i * 60)
               retval = true
               #$log.info("Time match #{time} #{stime}")
            end
         when '4' # timecode 4 = after sunrise
            logdata += "Schedule after sunrise "
            if time >= ss
               sr += 60*60*24  # add day for sunrise tomorrow
            end
            #$log.info("Times sr #{sr.strftime("%Y/%m/%d %I:%M%p")} ss #{ss.strftime("%Y/%m/%d %I:%M%p")} ")
            if time >= sr + (s['timeoffset'].to_i * 60)
               retval = true
               #$log.info("Time match #{time} #{stime}")
            end
         when '5' # timecode 5 = before sunset
            logdata += "Schedule before sunset "
            
            if time <= sr
               ss -= 60*60*24  # subtract day for sunrise yesterday
            end
            #$log.info("Times sr #{sr.strftime("%Y/%m/%d %I:%M%p")} ss #{ss.strftime("%Y/%m/%d %I:%M%p")} ")
            if time <= ss - (s['timeoffset'].to_i * 60)
               retval = true
               #$log.info("Time match #{time} #{stime}")
            end
         when '6' # timecode 6 = after sunset
            logdata += "Schedule after sunset "
            
            if time <= sr
               ss -= 60*60*24  # subtract day for sunrise yesterday
            end
            #$log.info("Times sr #{sr.strftime("%Y/%m/%d %I:%M%p")} ss #{ss.strftime("%Y/%m/%d %I:%M%p")} ")
            if time >= ss + (s['timeoffset'].to_i * 60)
               retval = true
               #$log.info("Time match #{time} #{stime}")
            end
        end
      end
      logdata += "Matched" 
      #pp s
   end
   return retval, logdata
end

def sendcommands(id)
   query  = "SELECT * from jos_insteoncommands as c WHERE c.eventid = #{id} and c.published = 1 ORDER BY eventid, ordering,id"
   $log.debug(query)
   cres = $mysql.query(query)
   if cres.num_rows() > 0
      clearqueue = true
      cres.each_hash() {|sd|
         if sd['commandtype'] == '0'  # insteon command
            command = "#{sd['commandprefix']}#{sd['commanddevice']}#{sd['commandflag']}#{sd['command']}"
            if clearqueue
               $controller.ClearSendQueue(sd['commanddevice'])
               clearqueue = false
            end
            $log.info("Sending insteon command #{sd['description']} #{sd['command']} delay #{sd['delay']}")
            $controller.SendPLM(command,desc='Schedule command',sd['delay'].to_i)
         end   
         if sd['commandtype'] == '1'  # email command
            $log.info("Sending email #{sd['description']} to #{sd['email']}")
            time = Time.new
            msg  = "From: #{$emailfromname} <#{$emailfrom}>\n"
            msg += "To: <#{sd['email']}>\n"
            msg += "Subject: #{sd['description']}\n"
            msg += "\n#{sd['description']} at #{time.strftime("%Y/%m/%d %I:%M%p")}\n"
    
            Net::SMTP.start($emailserver) do |smtp|
               smtp.send_message msg, $emailfrom, sd['email']
               smtp.finish
              end
         end   
      }
   end
end

def execute_message(m)
   time = Time.now
   if m == nil
      query  = "SELECT * from jos_insteonevents as c "
      query += "WHERE c.eventtype = 1 "
      query += "and c.published = 1"
   else
      query  = "SELECT * from jos_insteonevents as c "
      query += "WHERE c.from = '#{m['From']}' and c.to = '#{m['To']}' and '#{m['Command']}' LIKE c.command and c.eventtype = 0 "
      query += "and c.published = 1"
   end
   $log.debug(query)
   eres = $mysql.query(query)
   if eres.num_rows() > 0
      eres.each_hash() {|e|
         logdata = "Event:#{e['description']}\n"
         query = "SELECT * from jos_insteonscheduledetail as c WHERE c.eventid = #{e['id']} and c.published = 1"
         $log.debug(query)
         sdres = $mysql.query(query)
         if sdres.num_rows() > 0
            match = true
            sdres.each_hash() {|sd|
               if match
                  match,slog = checkSchedule(sd['schedulesid'])
                  logdata += "   Schedule Detail:#{sd['description']}\n      #{slog}\n" if match
               end
            }
         else
            match = true
         end
         if match
            logdata.split("\n").each { |l|
               $log.info(l)
            }
            sendcommands(e['id'])
         end   
      }
   end
end


def datetest
$times = []
$times << Time.parse("2010-11-10 01:00")
$times << Time.parse("2010-11-10 02:00")
$times << Time.parse("2010-11-10 03:00")
$times << Time.parse("2010-11-10 04:00")
$times << Time.parse("2010-11-10 05:00")
$times << Time.parse("2010-11-10 06:00")
$times << Time.parse("2010-11-10 07:00")
$times << Time.parse("2010-11-10 08:00")
$times << Time.parse("2010-11-10 09:00")
$times << Time.parse("2010-11-10 10:00")
$times << Time.parse("2010-11-10 11:00")
$times << Time.parse("2010-11-10 12:00")
$times << Time.parse("2010-11-10 13:00")
$times << Time.parse("2010-11-10 14:00")
$times << Time.parse("2010-11-10 15:00")
$times << Time.parse("2010-11-10 16:00")
$times << Time.parse("2010-11-10 17:00")
$times << Time.parse("2010-11-10 18:00")
$times << Time.parse("2010-11-10 19:00")
$times << Time.parse("2010-11-10 20:00")
$times << Time.parse("2010-11-10 21:00")
$times << Time.parse("2010-11-10 22:00")
$times << Time.parse("2010-11-10 23:00")
$times << Time.parse("2010-11-10 00:00")

query = "SELECT * from jos_insteonschedules as c WHERE c.published = 1"
$log.debug(query)
sdres = $mysql.query(query)
if sdres.num_rows() > 0
   sdres.each_hash() {|sd|
      $times.each {|t|
         $log.info("Schedule #{sd['description']} Time #{t.strftime("%Y/%m/%d %I:%M%p")}")
         match=checkSchedule(sd['id'],t)
         if match
            $log.info("Matched #{sd['description']} \n")
         else
            $log.info("Missed  #{sd['description']} \n")
         end
      }
   }
end

exit
end

sleep(2)  #wait for the controller thread to get going
$controller.clearRecvQueue()

$log.info("Sending IM Monitor Mode")
$controller.SendMonitor()

#get status of the lights
query = "SELECT * from jos_insteondevices as c WHERE c.published = 1"
$log.debug(query)
res = $mysql.query(query)
if res.num_rows() > 0
    res.each_hash() {|d|
        case d['type'][0..1]
        when '01', '02'
            command = "0262#{d['device']}0F1900"
            $log.info("Sending insteon status #{command} delay 0")
            $controller.SendPLM(command,desc='Initialize',0)
        end
    }
end

scheduletime = Time.now
while true
  $log.debug("Producer #{producer.status} Webserver #{webserver.status} Main #{Thread.main.status}")
  #puts $recvqueue.length()
  if producer.status == 'run'
  end
  if $recvqueue.length() > 0
    #$log.info("event")
    message = $recvqueue.pop()
    parse_buffstatus(message)
    execute_message(message)
  else
   if Time.now > scheduletime + 15
        #$log.info("periodic")
        execute_message(nil)
        scheduletime = Time.now
    end    
  end
  sleep(0.1)
end
$log.close
ControllerPLM.rb
require 'timeout'
require 'thread'
require 'logger'
require 'socket'  

class HttpServer
   def initialize(session, request)
      @session = session
      @request = request
   end

   def serve()
      if @request =~ /GET .* HTTP.*/
         fileName = @request.gsub(/GET \//, '').gsub(/ HTTP.*/, '')
      end
      fileName = fileName.strip
      begin
         @session.print "HTTP/1.1 200/OK\r\nServer: Insteon\r\nContent-type: text/plain\r\n\r\n"
         @session.write(fileName)
      ensure
         @session.close
      end
      return fileName
   end
end

class ControllerPLM
   def initialize(host, port, username, password,shost, sport, susername, spassword)
      @buffer = ''
      @host = host
      @port = port
      @username = username
      @password = password
      @shost = shost
      @sport = sport
      @susername = susername
      @spassword = spassword
      @socket = nil
   end

   def start
      producer = Thread.new {
        @socket = TCPSocket.new( @host, @port )  
         while true
           if $sendqueue.length() > 0
               message = $sendqueue.pop()
               if message['Time'] <= Time.now
                  WritePLM(message['Path'],message['ErrorDesc'])
               else
                  $sendqueue.push(message)
               end                                              
            end
            check_controller()
            sleep(0.2) # don't go to fast or you will over run the PLM
         end     
      }
      webserver = Thread.new {
         server = TCPServer.new(@shost, @sport)

         loop do
            session = server.accept
            request = session.gets
            logStr =  "Web request: #{session.peeraddr[2]} (#{session.peeraddr[3]}) #{request.strip}"
            $log.info(logStr)

            Thread.start(session, request) do |session, request|
               command = HttpServer.new(session, request).serve()
               if command[0..1] == '02'
                  $log.info("Sending Web Server command #{command} ")
                  SendPLM(command,'Web Command')
               end
               sleep(0.1)
            end
         end
      }
      return producer, webserver
   end

   def recv_controller()
      retval = ''
      begin
         Timeout.timeout 1 do
            recv = @socket.recv( 1024 )
            retval = recv.unpack('H*').join.upcase
         end
      rescue Timeout::Error
         #$log.error("recv_controller timeout")
         retval = ''
      end
      return retval
   end

   def check_controller()
      @buffer += recv_controller()
      if !@buffer.empty?
        @buffer = $controller.queue_messages(@buffer)
      end
   end

   def queue_messages(buff)
      bs = ''
      buff = buff.gsub("0250", ",0250")
      buff = buff.gsub("0251", ",0251")
      buff = buff.gsub("0252", ",0252")
      buff = buff.gsub("0253", ",0253")
      buff = buff.gsub("0254", ",0254")
      buff = buff.gsub("0255", ",0255")
      buff = buff.gsub("0256", ",0256")
      buff = buff.gsub("0257", ",0257")
      buff = buff.gsub("0258", ",0258")

      buff = buff.gsub("0260", ",0260")
      buff = buff.gsub("0261", ",0261")
      buff = buff.gsub("0262", ",0262")
      buff = buff.gsub("0263", ",0263")
      buff = buff.gsub("0264", ",0264")
      buff = buff.gsub("0265", ",0265")
      buff = buff.gsub("0266", ",0266")
      buff = buff.gsub("0267", ",0267")
      buff = buff.gsub("0268", ",0268")
      buff = buff.gsub("0269", ",0269")
      buff = buff.gsub("026A", ",026A")
      buff = buff.gsub("026B", ",026B")
      buff = buff.gsub("026C", ",026C")
      buff = buff.gsub("026D", ",026D")
      buff = buff.gsub("026E", ",026E")
      buff = buff.gsub("026F", ",026F")

      buff = buff.gsub("0270", ",0270")
      buff = buff.gsub("0271", ",0271")
      buff = buff.gsub("0272", ",0272")
      buff = buff.gsub("0273", ",0273")
      
      time = Time.new

      e = buff.split(',')
      e.each_index {|i|
      if !e[i].empty?
         message = Hash.new
         message['Time'] = time
         message['Event'] = ''
         message['Description'] = ''
         message['From'] = ''
         message['To'] = ''
         message['Flags'] = ''
         message['Command'] = ''
         message['Extended'] = ''
         message['Category'] = ''
         message['Subcategory'] = ''
         message['Version'] = ''
         message['Status'] = ''
         message['Linkcode'] = ''

         event = e[i][0..3]
         case event 
         when '0250' # received standard message
            arr = e[i].scan(/(....)(......)(......)(..)(....)/)
            if arr.length == 1 and arr[0].length == 5
               message['Event'] = arr[0][0]
               message['Description'] = 'Std Message'
               message['From'] = arr[0][1]
               message['To'] = arr[0][2]
               message['Flags'] = arr[0][3]
               message['Command'] = arr[0][4]
            else
               bs = e[i]
            end
         when '0251' # received extended message
            arr = e[i].scan(/(....)(......)(......)(..)(....)(............................)/)
            if arr.length == 1 and arr[0].length == 6
               message['Event'] = arr[0][0]
               message['Description'] = 'Ext Message'
               message['From'] = arr[0][1]
               message['To'] = arr[0][2]
               message['Flags'] = arr[0][3]
               message['Command'] = arr[0][4]
               message['Extended'] = arr[0][5]
            else
               bs = e[i]
            end
         when '0253' # All Linking Completed
            arr = e[i].scan(/(....)(..)(..)(......)(..)(..)(..)/)
            if arr.length == 1 and arr[0].length == 7
               message['Event'] = arr[0][0]
               message['Description'] = 'All Linking Completed'
               message['Linkcode'] = arr[0][1]
               message['To'] = arr[0][2]
               message['From'] = arr[0][3]
               message['Category'] = arr[0][4]
               message['Subcateory'] = arr[0][5]
               message['Status'] = arr[0][6]
            else
               bs = e[i]
            end
         when '0254' # Button Event Report
            arr = e[i].scan(/(....)(..)/)
            if arr.length == 1 and arr[0].length == 2
               message['Event'] = arr[0][0]
               message['Description'] = 'Button Event Report'
               message['Status'] = arr[0][1]
            else
               bs = e[i]
            end
         when '0256' # ALL-Link Cleanup Failure Report
            arr = e[i].scan(/(....)(..)(......)/)
            if arr.length == 1 and arr[0].length == 3
               message['Event'] = arr[0][0]
               message['To'] = arr[0][1]
               message['From'] = arr[0][2]
               message['Description'] = 'ALL-Link Cleanup Failure Report'
            else
               bs = e[i]
            end
         when '0257' # All-Link Report
            arr = e[i].scan(/(....)(..)(..)(......)(......)/)
            if arr.length == 1 and arr[0].length == 5
               message['Event'] = arr[0][0]
               message['Description'] = 'All-Link Report'
               message['From'] = arr[0][3]
               message['To'] = arr[0][2]
               message['Flags'] = arr[0][1]
               message['Command'] = arr[0][4]
            else
               bs = e[i]
            end
         when '0258' # ALL-Link Cleanup Status Report
            arr = e[i].scan(/(....)(.*)/)
            if arr.length == 1 and arr[0].length == 2
               message['Event'] = arr[0][0]
               message['Description'] = 'All-Link Cleanup'
               message['Status'] = arr[0][1]
            else
               bs = e[i]
            end
         when '0260' # Get IM Info
            arr = e[i].scan(/(....)(......)(..)(..)(..)(..)/)
            if arr.length == 1 and arr[0].length == 6
               message['Event'] = arr[0][0]
               message['Description'] = 'Get IM Info'
               message['From'] = arr[0][1]
               message['Category'] = arr[0][2]
               message['Subcategory'] = arr[0][3]
               message['Version'] = arr[0][4]
               message['Status'] = arr[0][5]
            else
               bs = e[i]
            end
         when '0261' # Scene Command
            arr = e[i].scan(/(....)(..)(..)(.*)/)
            if arr.length == 1 and arr[0].length == 4
               message['Event'] = arr[0][0]
               message['Description'] = 'Scene Message'
               message['From'] = arr[0][1]
               message['Command'] = arr[0][2]
               message['Status'] = arr[0][3]
            else
               bs = e[i]
            end
         when '0262' # Send Message                                         
            case e[i].length
            when 46  # extended message
               arr = e[i].scan(/(....)(......)(..)(....)(............................)(..)/) 
               if arr.length == 1 and arr[0].length == 6
                  message['Event'] = arr[0][0]
                  message['Description'] = 'Send Message'
                  message['To'] = arr[0][1]
                  message['Flags'] = arr[0][2]
                  message['Command'] = arr[0][3]
                  message['Extended'] = arr[0][4]
                  message['Status'] = arr[0][5]
               end
            when 18  # standard message
               arr = e[i].scan(/(....)(......)(..)(....)(..)/) 
               if arr.length == 1 and arr[0].length == 5
                  message['Event'] = arr[0][0]
                  message['Description'] = 'Send Message'
                  message['To'] = arr[0][1]
                  message['Flags'] = arr[0][2]
                  message['Command'] = arr[0][3]
                  message['Status'] = arr[0][4]
                end
             else
                bs = e[i]
             end
         when '0265' # Cancel All-linking
            arr = e[i].scan(/(....)(..)/)
            if arr.length == 1 and arr[0].length == 2
               message['Event'] = arr[0][0]
               message['Description'] = 'Cancel All-linking'
               message['Status'] = arr[0][1]
            else
               bs = e[i]
            end
         when '0266' # Set Host Device Category
            arr = e[i].scan(/(....)(..)(..)(..)(..)/)
            if arr.length == 1 and arr[0].length == 5
               message['Event'] = arr[0][0]
               message['Description'] = 'Set Host Device Category'
               message['Category'] = arr[0][1]
               message['Subcategory'] = arr[0][2]
               message['Version'] = arr[0][3]
               message['Status'] = arr[0][4]
            else
               bs = e[i]
            end
         when '0269' # Get First ALL-Link
            arr = e[i].scan(/(....)(..)/)
            if arr.length == 1 and arr[0].length == 2
               message['Event'] = arr[0][0]
               message['Description'] = 'Get First ALL-Link'
               message['Status'] = arr[0][1]
            else
               bs = e[i]
            end
         when '026A' # Get Next ALL-Link
            arr = e[i].scan(/(....)(..)/)
            if arr.length == 1 and arr[0].length == 2
               message['Event'] = arr[0][0]
               message['Description'] = 'Get Next ALL-Link'
               message['Status'] = arr[0][1]
            else
               bs = e[i]
            end
         when '026B' # Set IM Configuration
            arr = e[i].scan(/(....)(..)(..)/)
            if arr.length == 1 and arr[0].length == 3
               message['Event'] = arr[0][0]
               message['Command'] = arr[0][1]
               message['Description'] = 'Set IM Configuration'
               message['Status'] = arr[0][2]
            else
               bs = e[i]
            end
         when '026F' # Manage ALL-Link Record
            #                 026F  20  A2  01  13DED1  00  05  38  06
            arr = e[i].scan(/(....)(..)(..)(..)(......)(..)(..)(..)(..)/)
            if arr.length == 1 and arr[0].length == 9
               message['Event'] = arr[0][0]
               message['Command'] = arr[0][1]
               message['Flags'] = arr[0][2]
               message['To'] = arr[0][3]
               message['From'] = arr[0][4]
               message['Category'] = arr[0][5]
               message['Subcategory'] = arr[0][6]
               message['Version'] = arr[0][7]
               message['Description'] = 'Manage ALL-Link Record'
               message['Status'] = arr[0][8]
            else
               bs = e[i]
            end
         else
            bs = e[i]
         end
         if !message['Event'].empty?
            $recvqueue.push(message)
         end
      end
   }
   return bs
   end

   def ClearSendQueue(device)
       tqueue = []
       while $sendqueue.length() > 0
          message = $sendqueue.pop()
          if message['Path'][0..9] == "0262#{device}"
            $log.info("Removing Queued command #{message['Path']} #{message['ErrorDesc']} #{message['Time']}")
          else
            tqueue << message
          end
       end
       tqueue.each {|m|
          $sendqueue.push(m)
       }

   end
   
   def clearRecvQueue()
       while $recvqueue.length() > 0
          message = $recvqueue.pop()
       end
   end
   
   def setclock()
   end

   def sendcommand(device,cmd,delay=0)
      path = "0262#{device}0F#{cmd}"  
      SendPLM(path,'Send Command',delay)
   end

   def sendExtCommand(device,cmd,delay=0)
      path = "0262#{device}1F#{cmd}"  
      SendPLM(path,'Send Extended Command',delay)
   end
   
   def SendGetFirst()
      path = "0269"  
      SendPLM(path,'Send Get First')
   end

   def SendGetNext()
      path = "026A"  
      SendPLM(path,'Send Get Next')
   end

   def SendRemoteSetMSB(device,msb)
      path = "0262#{device}0F28#{msb}"  
      SendPLM(path,'Send Remote Set MSB')
   end

   def SendRemotePeek(device,offset)
      path = "0262#{device}0F2B#{offset}"  
      SendPLM(path,'Send Remote Peek')
   end

   def SendGetFirst()
      path = "0269"  
      SendPLM(path,'Send Get First')
   end

   def SendMonitor()
      path = "026B40"  
      SendPLM(path,'Send Monitor')
   end

   def SendLightStatus(device,delay=10)
      path = "0262#{device}0F1900"  
      SendPLM(path,'Send Light status',delay)
   end

   def SendIdRequest(device)
      path = "0262#{device}0F1000"  
      SendPLM(path,'Send ID Request')
   end

   def SendEngineVersion(device)
      path = "0262#{device}0F0D00"  
      SendPLM(path,'Send Engine Version')
   end

   def SendGetIMinfo()
      path = "0260"  
      SendPLM(path,'Get IM Info')
   end

   def SendManageLink(record)
      path = "026F#{record}"  
      SendPLM(path,'Send Manage Link')
   end

   def SendPing(device)
      path = "0262#{device}0F1F00"  
      SendPLM(path,'Send Ping')
   end

   def ReadAllLinkDatabase(device,address='0000')
      d1  = '00' # unused
      d2  = '00' # read
      d3  = address
      #d5  = '01' # read one record
      d5  = '00' # read all records
      d6  = '00' # unused
      d7  = '00' # unused
      d8  = '00' # unused
      d9  = '00' # unused
      d10 = '00' # unused
      d11 = '00' # unused
      d12 = '00' # unused
      d13 = '00' # unused
      d14 = '00' # unused
      
      path = "0262#{device}1F2F00#{d1}#{d2}#{d3}#{d5}#{d6}#{d7}#{d8}#{d9}#{d10}#{d11}#{d12}#{d13}#{d14}"  
      SendPLM(path,'Read All Link Database')
   end

   def WritePLM(path,desc='Send error')
      # This is the HTTP request we send to fetch a file
      $log.debug("WritePLM #{path}")
      
      line=''
      while !path.empty?
        line += path[0..1].hex.chr
        path = path[2..path.length]
      end

      @socket.write line
      @socket.flush
      #line = ''
      #while !path.empty?
      #   line = path[0..1].hex.chr
      #   retval = $sp.write(line)
      #   path = path[2..path.length]
      #   #print "line #{line.inspect} retval #{retval} path #{path}\n"
      #end
      rescue 
         $log.error("#{desc} error #{$!} #{$@[0]} Line: #{$.}")
         retval = false
         return retval
   end

   def SendPLM(path,desc='Send error',delay=0)
      message = Hash.new
      message['Path'] = path
      message['ErrorDesc'] = desc
      message['Time'] = Time.now + delay
      $log.debug("SendPLM  Path:#{path} Time now:#{Time.now} Time delay:#{Time.now + delay}")
      $sendqueue.push(message)
   end

   def getIMinfo()
      begin
         retval = {}
         clearRecvQueue()
         Timeout.timeout 30 do
            SendGetIMinfo()
            ackfound = ''
            while ackfound != '06' and ackfound != '15'
               if $recvqueue.length() > 0
                  message = $recvqueue.pop()
                  if message['Event'] == '0260'
                     ackfound = message['Status']
                  end
               end
               Thread.pass
            end
            if ackfound == '06'
               retval['Address'] = message['From']
               retval['Type'] = "#{message['Category']}#{message['Subcategory']}#{message['Version']}"
            end
            return retval
         end
      rescue Timeout::Error
         $log.error("getIMinfo timeout...retrying")
         retry
      end
   end

   def getRequestID(device)
      begin
         retval = false
         clearRecvQueue()
         Timeout.timeout 30 do
            SendIdRequest(device)
            ackfound = ''
            while ackfound != '06' and ackfound != '15'
               if $recvqueue.length() > 0
                  message = $recvqueue.pop()
                  if message['Event'] == '0262' and message['To'] == device
                     ackfound = message['Status']
                  end
               end
               Thread.pass
            end
            if ackfound == '06'
               recfound = false
               while recfound == false
                  if $recvqueue.length() > 0
                     message = $recvqueue.pop()
                     if message['Event'] == '0250' and message['From'] == device and message['Flags'].hex & 32 == 0
                        retval = message['To']
                        recfound = true
                     end
                  end
                  Thread.pass
               end
            end
            return retval
         end
      rescue Timeout::Error
         $log.error("getRequestID timeout...retrying")
         retry
      end
   end

   def getEngineVersion(device)
      begin
         retval = false
         clearRecvQueue()
         Timeout.timeout 30 do
            SendEngineVersion(device)
            ackfound = ''
            while ackfound != '06' and ackfound != '15'
               if $recvqueue.length() > 0
                  message = $recvqueue.pop()
                  if message['Event'] == '0262' and message['To'] == device
                     ackfound = message['Status']
                  end
               end
               Thread.pass
            end
            if ackfound == '06'
               recfound = false
               while recfound == false
                  if $recvqueue.length() > 0
                     message = $recvqueue.pop()
                     if message['Event'] == '0250' and message['From'] == device
                        retval = message['Command'][2..3]
                        recfound = true
                     end
                  end
                  Thread.pass
               end
            end
            return retval
         end
      rescue Timeout::Error
         $log.error("getEngineVersion timeout...retrying")
         retry
      end
   end

   def remoteSpiderV1(device)
      begin
         Timeout.timeout 180 do
            linkrecords = []
            $remoteDBOffsetMSB = 0x0F # initial offset
            $remoteDBOffsetLSB = 0xF8 # initial LSB offset

            clearRecvQueue()
                  
            SendRemoteSetMSB(device,"%02X"%$remoteDBOffsetMSB)

            ackfound = ''
            while ackfound != '06' and ackfound != '15'
               if $recvqueue.length() > 0
                  message = $recvqueue.pop()
                  if message['Event'] == '0262' and message['To'] == device
                     if message['Status'] != '06'
                        more = false
                     end
                     ackfound = message['Status']
                  end
               end
            end
            if ackfound == '06'
               recfound = false
               while recfound == false
                  if $recvqueue.length() > 0
                     message = $recvqueue.pop()
                     if message['Event'] == '0250'
                        morepeek = true
                        while morepeek == true
                           peekrecord = ''
      
                           for x in 0..7
                              SendRemotePeek(device,"%02X"%($remoteDBOffsetLSB + x))
                              ackfound = ''
                              while ackfound != '06' and ackfound != '15'
                                 if $recvqueue.length() > 0
                                    message = $recvqueue.pop()
                                    if message['Event'] == '0262' and message['To'] == device
                                       if message['Status'] != '06'
                                          more = false
                                       end
                                       ackfound = message['Status']
                                    end
                                 end
                                 Thread.pass
                              end
                              if ackfound == '06'
                                 peekfound = false
                                 while !peekfound
                                    if $recvqueue.length() > 0
                                       message = $recvqueue.pop()
                                       if message['Event'] == '0250' and message['From'] == device
                                          peekfound = true
                                          peekrecord = peekrecord + message['Command'][2..3]
                                       end
                                    end
                                    Thread.pass
                                 end
                              end
                           end
                           peekflag = peekrecord[0..1]
                           peekgroup = peekrecord[2..3]
                           peekdevice = peekrecord[4..9]
                           peektype = peekrecord[10..15]
                           address = "#{"%02X"%$remoteDBOffsetMSB}#{"%02X"%$remoteDBOffsetLSB}"
                           $log.info("SpiderRemote device #{device} ALDB Flag:#{peekflag} Group:#{peekgroup} Device:#{peekdevice} Type:#{peektype} Address:#{address}")
                           if (peekflag.hex & 0x02) == 0x00 # High water mark
                              morepeek = false
                           end
                           if (peekflag.hex & 0x80) == 0x80 # Inuse
                              linkrecords << {'To'=>peekdevice,'From'=>device, 'Group'=>peekgroup, 'Flags' => peekflag,'Linkdata'  => peektype,'Address'  => address} 
                           end
                           if $remoteDBOffsetLSB == 0x00
                              #OffsetMSB needs to be decreased, and LSB needs reset.
                              $remoteDBOffsetMSB = $remoteDBOffsetMSB - 1
                              $remoteDBOffsetLSB = 0xFF # the next line will decrease this by 8
                           end
                           # decrease DBOffsetLSB by 0x08
                           $remoteDBOffsetLSB = $remoteDBOffsetLSB - 0x08
                        end
                        recfound = true
                     end
                  end
               end
            end
            return linkrecords
         end
      rescue Timeout::Error
         $log.error("remoteSpider timeout...retrying")
         retry
      end
   end

   def remoteSpider(device)
      begin
         Timeout.timeout 30 do
            linkrecords = []
            clearRecvQueue()

            nextaddress = '0000'            
            more = true                  
            while more
               ReadAllLinkDatabase(device,nextaddress)
               ackfound = ''
               while ackfound != '06' and ackfound != '15'
                  if $recvqueue.length() > 0
                     message = $recvqueue.pop()
                     if message['Event'] == '0262' and message['To'] == device
                        if message['Status'] != '06'
                           more = false
                        end
                        ackfound = message['Status']
                     end
                  end
                  Thread.pass
               end
               if ackfound == '06'
                  recfound = false
                  while recfound == false
                     if $recvqueue.length() > 0
                        message = $recvqueue.pop()
                        if message['Event'] == '0251' and message['From'] == device
                           peekaddress    = message['Extended'][4..7]
                           nextaddress    = "%04X"%(message['Extended'][4..7].hex - 8)
                           peekflag   = message['Extended'][10..11]
                           peekgroup  = message['Extended'][12..13]
                           peekdevice = message['Extended'][14..19]
                           peektype   = message['Extended'][20..25]
                           $log.info("SpiderRemote device #{device} ALDB Flag:#{peekflag} Group:#{peekgroup} Device:#{peekdevice} Type:#{peektype} Address:#{peekaddress}")
                           if (peekflag.hex & 0x02) == 0x00 # High water mark
                              more = false
                              recfound = true
                           end
                           if (peekflag.hex & 0x80) == 0x80 # Inuse
                              linkrecords << {'To'=>peekdevice,'From'=>device, 'Group'=>peekgroup, 'Flags' => peekflag,'Linkdata'  => peektype,'Address'  => peekaddress} 
                           end
                        end
                        Thread.pass
                     end
                  end
               end
            end
            return linkrecords
         end
      rescue Timeout::Error
         $log.error("remoteSpider timeout...retrying")
         retry
      end
   end

   def scanController()
      begin
         Timeout.timeout 30 do
            linkrecords = []
            clearRecvQueue()
            SendGetFirst()
            more = true
            while more
               ackfound = ''
               while ackfound != '06' and ackfound != '15'
                  if $recvqueue.length() > 0
                     message = $recvqueue.pop()
                     if message['Event'] == '0269' or message['Event'] == '026A'
                        if message['Status'] != '06'
                           more = false
                        end
                        ackfound = message['Status']
                     end
                  end
                  Thread.pass
               end
               if ackfound == '06'
                  recfound = false
                  while recfound == false
                     if $recvqueue.length() > 0
                        message = $recvqueue.pop()
                        if message['Event'] == '0257'
                           linkrecords << {'To'=>message['From'],'From'=>$controllerAddress, 'Group'=>message['To'], 'Flags' => message['Flags'],'Linkdata'  => message['Command'],'Type'  => message['Command'],'Engine'  => '00'} 
                           recfound = true
                        end
                     end
                     Thread.pass
                  end
               end
               if more 
                  $controller.SendGetNext()
               end
            end
            return linkrecords
         end
      rescue Timeout::Error
         $log.error("remoteSpider timeout...retrying")
         retry
      end
   end

   def ExistsInController(flags,devicegroup,device,linkdata)
      begin
         retval = false
         clearRecvQueue()
         Timeout.timeout 30 do
            SendManageLink("00#{flags}#{devicegroup}#{device}#{linkdata}")
            ackfound = ''
            while ackfound != '06' and ackfound != '15'
               if $recvqueue.length() > 0
                  message = $recvqueue.pop()
                  if message['Event'] == '026F' 
                     ackfound = message['Status']
                  end
               end
               Thread.pass
            end
            if ackfound == '06'
               retval = true
            else
               retval = false
            end
            return retval
         end
      rescue Timeout::Error
         $log.error("Exists In Controller timeout...retrying")
         retry
      end
   end

   def DeleteFromController(flags,devicegroup,device,linkdata)
      begin
         retval = false
         clearRecvQueue()
         Timeout.timeout 30 do
            SendManageLink("80#{flags}#{devicegroup}#{device}#{linkdata}")
            ackfound = ''
            while ackfound != '06' and ackfound != '15'
               if $recvqueue.length() > 0
                  message = $recvqueue.pop()
                  if message['Event'] == '026F' 
                     ackfound = message['Status']
                  end
               end
               Thread.pass
            end
            if ackfound == '06'
               retval = true
            else
               retval = false
            end
            return retval
         end
      rescue Timeout::Error
         $log.error("Delete From Controller timeout...retrying")
         retry
      end
   end

   def AddToController(flags,devicegroup,device,linkdata)
      begin
         retval = false
         clearRecvQueue()
         Timeout.timeout 30 do
            SendManageLink("20#{flags}#{devicegroup}#{device}#{linkdata}")
            ackfound = ''
            while ackfound != '06' and ackfound != '15'
               if $recvqueue.length() > 0
                  message = $recvqueue.pop()
                  if message['Event'] == '026F' 
                     ackfound = message['Status']
                  end
               end
               Thread.pass
            end
            if ackfound == '06'
               retval = true
            else
               retval = false
            end
            return retval
         end
      rescue Timeout::Error
         $log.error("Add To Controller Controller timeout...retrying")
         retry
      end
   end

   def Ping(device)
      retries = 0
      begin
         retval = false
         clearRecvQueue()
         Timeout.timeout 5 do
            SendPing(device)
            ackfound = ''
            while ackfound != '06' and ackfound != '15'
               if $recvqueue.length() > 0
                  message = $recvqueue.pop()
                  if message['Event'] == '0262' and message['To'] == device  and message['Command'] == '1F00'
                     ackfound = message['Status']
                  end
               end
               Thread.pass
            end
            if ackfound == '06'
               recfound = false
               while recfound == false
                  if $recvqueue.length() > 0
                     message = $recvqueue.pop()
                     if message['Event'] == '0250' and message['From'] == device
                        retval = true
                        recfound = true
                     end
                  end
                  Thread.pass
               end
            end
            return retval
         end
      rescue Timeout::Error
         $log.error("Ping timeout...retrying")
         retries += 1
         if retries == 2
            return false
         else
            retry
         end
      end
   end

end
insteonConfig.rb
$log = Logger.new(STDOUT)
$log.level = Logger::INFO
#$log.level = Logger::DEBUG
$log.datetime_format = "%Y-%m-%d %H:%M:%S"

#Create the location
$long = "36.76"
$lat = "-80.73"
$timezone = 'America/New_York'

#$chost = "127.0.0.1"
$chost = "192.168.5.25" #address of the Smartlinc
#$cport = 20000
$cport = 9761
$cusername = "" 
$cpassword = "" 

$shost = "127.0.0.1"
$sport = 9091
$susername = "" 
$spassword = "" 

$tty = '/dev/insteon'

$dbhost = 'localhost'
$dbusername = 'mysqluser'
$dbpassword = 'mysqlpassword'
$dbtable = 'joomla'

$emailserver = ''
$emailfrom = ''
$emailfromname = 'Home'