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'
| < Prev | Next > |
|---|


