LinuxMCE Forums

General => Developers => Topic started by: ddamron on November 25, 2007, 09:18:24 pm

Title: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on November 25, 2007, 09:18:24 pm

See END OF THREAD for most current code.


Here is my beta code for EZBridge.  This code works fairly well.  It's a far cry from my previous attempt(Gimme a break, I was learning Ruby at the same time, and still am).

I have implemented a cmdqueue to queue commands so I can receive the responses in a timely manner.

I still have ONE SMALL question, I hope someone here can answer..

I process the Receive command for Child: all Ok.
I want to tell linuxmce the command was successful.

What device do I send the 'Successful' command?  Or, do I have to send an event?!?

Here's a snippet from my DCERouter log showing the two commands:

Received Message from 41 (Windows XP PC/tablet (Horiz) / Office) to 40 (Office Light / Office), type 1 id 184 Command:Set Level, retry none,
parameters: <0x877ccb90>
08   12/04/07 19:08:53.346        Parameter 76(Level): 100 <0x877ccb90>
08   12/04/07 19:08:56.534      Received Message from 40 (Office Light / Office) to 1 (CORE / Office), type 1 id 184 Command:Set Level, retry none,
parameters: <0x82fc3b90>
08   12/04/07 19:08:56.534        Parameter 76(Level): 100 <0x82fc3b90>

I've tried sending the command to:
1 (CORE/Office),
2 (DCERouter), and
41 (Windows XP PC/tablet (Horiz) / Office)

The commands get sent, but status doesn't get updated...

I'm really fighting with this issue because:
when I click <Light ON> from an orbiter, all is well, (from my log:)

05   12/04/07 18:50:41.630      GSDMessageTranslator isCmdImplemented = false <0xb6010b90>
05   12/04/07 18:50:41.631      #### Pre-Process Queue = 1 <0xb6010b90>
05   12/04/07 18:50:41.651      _QueueProc Pre - 184 : 0 <0xb7813b90>
05   12/04/07 18:50:41.651      GSD-Sleep Pre 184 : 0 <0xb7813b90>
05   12/04/07 18:50:41.651      Process Queue = 1 <0xb7813b90>

when, I click <Light OFF> from said orbiter, I get (in my log):

05   12/04/07 18:50:59.099      GSDMessageTranslator isCmdImplemented = false <0xb6010b90>
05   12/04/07 18:50:59.100      #### Pre-Process Queue = 1 <0xb6010b90>
05   12/04/07 18:50:59.154      _QueueProc Pre - 193 : 0 <0xb7813b90>
05   12/04/07 18:50:59.155      GSD-DispatchMessage - ignoring 193 because is useless. <0xb7813b90>
05   12/04/07 18:50:59.155      _QueueProc Post - 193 : 0 <0xb7813b90>

Now, Here's the problem:  I don't want to send an 'ack' until I've verified the command has sent successfully.  This includes 'process incoming data' routines...

Am I going about this in the wrong way??  do I HAVE to return the 'ack' from within the Process Command for Child (or private methods as needed) ?

Anywho, here's the code snippets..
(There is useless code here, that's beta for you!)

Private Method Listing:
Code: [Select]
#### Written by Dan Damron
#### #373 Private Method Listing ####
#
# Commands send back to DCE
# 184 - SetLevel value 0-100 (% on) or +-1 for steps
# 193 - Turn Light OFF
# 192 - Turn Light ON
#
#
#
#
require "rexml/document"
require "rexml/streamlistener"

include REXML


def EZToDCE(param)
#param contains a hash of EZBridge command structure
log('--|---------EZtoDCE')
checkWait(param)
log('-----:)-----EZtoDCE ' + param['Response'] + ' Received.')
case param['Response']
# Standard Commands (responses to command)
when 'GetRevision' # Special Response
log('-----|---------GetRevision:' + param['Parameter1'])

when 'GetLatLong'  # Special Response
log('-----|---------GetLatLong: Lat=' + param['Lat'] + ', Long=' + param['Long'])

when 'SetLatLong'
if param['Parameter1'] = 'True' then
log('-----|---------SetLatLong: Ok')
else
log('-----X---------SetLatLong: FAILED!')
end
when 'SetPasswd'

when 'SetTimeZone'

when 'GetClock' # Special Response

when 'SetClock'

when 'SetNTPServer'

when 'Upgrade'

when 'NetCfg'

when 'Reset'
sleep 0.5
log('----X-------' + param['Parameter1']) # RestartEZBridge

when 'LstTimers'  # Special Response


when 'ClrTimers'

when 'AddTimer'

when 'GetTimer'    # Special Response

when 'SetTimer'

when 'DelTimer'

when 'GetVersion' # Special Response

when 'SndGrp'

when 'SndIns'

when 'SndX10'

when 'StLnk'

when 'CancelLnk'

when 'SetDev'

when 'RstPLM'

when 'GetLnk'

when 'GetNext'

when 'SetCfg'

when 'GetLnkData'

when 'LEDON'

when 'LEDOFF'

when 'MngLnk'

when 'GetCfg' # Special Response

when 'LstMacros' # Special Response

when 'ClrMacros'

when 'AddMacro'

when 'GetMacro' # Special Response

when 'SetMacro'

when 'DelMacro'

when 'LstDevices' # Special Response

when 'ClrDevices'

when 'AddDevice'

when 'GetDevice' # Special Response

when 'SetDevice'

when 'DelDevice'

when 'LstZones'

when 'ClrZones'

when 'AddZone'

when 'GetZone'

when 'SetZone'

when 'DelZone'

when 'AddDevZone'

when 'DelDevZone'

# Response Messages
when 'InsStdMsg'

when 'InsExtMsg'

when 'X10Msg'

when 'InsLnkSts'

when 'BtnRpt'

when 'UsrRst'

when 'GrpEvntRpt'

when 'LnkData'

# Other messages
when 'PLMEchoError' # I know theres a PLM comm error...
# error sending command to PLM, reset command and try again
log('-----X------' + param['Response'])
resetcmd = {'Command' => 'Reset'}
$cmdqueue.insert(0, resetcmd)
sleep 0.1
$wAIT = false
SndIns()
when 'LongAck'
log('-----X------' + param['Response'])
# Error - seems to show up after PLMEchoError
resetcmd = {'Command' => 'Reset'}
$cmdqueue.insert(0, resetcmd)
sleep 0.1
$wAIT = false
SndIns()
else
log('-----XXXXXXX UNKNOWN Response Received:' + param['Response'])
end

end
def checkWait(param)
#Checks for response from current command.
if $cmdqueue[0] != nil
log('---|--------CheckWait:')
log('---|--------Current Command:' + $cmdqueue[0]['Command'])
log('---|--------param:' + param['Response'])
log('---|--------Flag:'+ $wAIT.to_s)
if $cmdqueue[0]['Command'] == param['Response']
$wAIT = false
log('---|--------Current Command Queue Length Before Delete:' + $cmdqueue.nitems.to_s)
$cmdqueue.delete_at(0)
log('---|--------Flag & currentCmd cleared')
log('---|--------Current Command Queue Length:' + $cmdqueue.nitems.to_s)
if $cmdqueue.nitems > 0 then
SndIns()
end
end
else
log('------:)----checkWait: command queue is emtpy')
if $wAIT == true
log('------:(----CheckWait: TRUE')
else
log('------:)----CheckWait: TRUE')
end
end
end

def parseMessage(param)
param.keys.each{|p| log("PM Param:" + p.to_s + "=" + param[p].to_s)}
case param['Response']
when 'SndIns' # response message
$wAIT = false
insHb = param['Parameter3'] # From
insMb = param['Parameter4']
insLb = param['Parameter5']
insID = insHb + "." + insMb + "." + insLb
myDevFrom = $children[insID]
myDevTo = 2 #DCE Router
myPriority = 1
myType = 1 # Command

#####log('Parent:' + parent_) -- this crashes the code

case param['Parameter7']
when '11' # dim/Brighten
myID = 184
log('From:' + myDevFrom.to_s)
log('To:' + myDevTo.to_s)
log('Pri:' + myPriority.to_s)
log('Type:' + myType.to_s)
log('ID:' + myID.to_s)
cmd = Command.new(myDevFrom, myDevTo, myPriority, myType, 184)
cmd.params_[184] = hextopercent(param['Parameter8']).to_s
SendCommand(cmd)
log('SENT!!!..............')
when '13' # off
cmd = Command.new(myDevFrom, myDevTo, myPriority, myType, 193)
SendCommand(cmd)
when '12' # on
cmd = Command.new(myDevFrom, myDevTo, myPriority, myType, 192)
SendCommand(cmd)
end
when 'InsStdMsg'
insHb = param['Parameter3']
insMb = param['Parameter4']
insLb = param['Parameter5']
insID = insHb + "." + insMb + "." + insLb
myDevFrom = $children[insID]
myDevTo = 2 #DCE Router
myPriority = 1
myType = 1 # Command
myID = InsCmdtoID(param['Parameter10'])


$wAIT = false
when 'InsExtMsg'
$wAIT = false
when 'X10Msg'
$wAIT = false

when 'InsLnkSts'
$wAIT = false
when 'BtnRpt'
$wAIT = false
when 'UsrRst'
$wAIT = false
when 'GrpEvntRpt'
$wAIT = false
when 'LnkData'
$wAIT = false
when 'GetVersion'
$wAIT = false
when 'GetCfg'
$wAIT = false
when 'LstTimers'
$wAIT = false
when 'GetTimer'
$wAIT = false
when 'GetRevision'
$wAIT = false
when 'GetLatLong'
$wAIT = false
when 'GetClock'
$wAIT = false
when 'LstMacros'
$wAIT = false
when 'GetMacro'
$wAIT = false
when 'LstDevices'
$wAIT = false
when 'GetDevice'
$wAIT = false
else
log('*#*#*#*#*#*#*#UNKNOWN COMMAND*#*#*#*#*#*#')
log(param['Response'])
$wAIT = false
end
end



def InsCmdtoID(value)
case value
when '01' # Assign to (Insteon) Group
return 0
when '02' # Delete from (Insteon) Group
return 0
when '10' # Ping
return 0
when '11' # ON
return 192
when '13' # OFF
return 193
when '15' # Brighten 1 step
return 0
when '16' # Dim 1 step
return 0
when '17' # Start Manual Change
return 0
when '18' # Stop Manual Change
return 0
when '19' # Status Request
return 0
when '24' # Do EE READ
return 0
when '28' # Set Address MSB
return 0
when '29' # POKE
return 0
when '2A' # POKE Extended
return 0
when '2B' # PEEK
return 0
when '2C' # PEEK Internal
return 0
when '2D' # POKE internal
return 0
else
return 0
end

end

def rawX10(value)

x10MSN = $X10HouseCodes.index[value[0..0]]
x10LSN = $X10UnitCodes.index[value[1..1]]
return x10MSN + x10LSN
end
def X10Flag(value)
  return + $X10CommandCodes.index[Value] + '0'
end

def SndIns
# create a new XML command for SndIns
log('|-----------SndIns:WAIT FLAG = ' + $wAIT.to_s)
if $wAIT == false
param = $cmdqueue[0]
log('|-----------SndIns: Items in queue:' + $cmdqueue.nitems.to_s)
doc = Document.new
doc << XMLDecl.new
el = doc.add_element 'command'
el.text = param['Command']
param.keys.each {|k|
#log('|-----------SndIns:param[' + k.to_s + ']=' + param[k].to_s)
if k != 'Command' then
el1 = el.add_element k
el1.text = param[k]
end
}
conn_.Reconnect()
conn_.Send(doc.to_s)
log('Out---------SndIns:CmdSent:' + doc.to_s)

$wAIT = TRUE
else
log('X-----------SndIns: Waiting for response to:' +  $cmdqueue[0]['Command'].to_s)
log('X-----------SndIns: Current Queue Length:' + $cmdqueue.nitems.to_s)
end
end

def padhex(hex)
if hex.length==1
hex = "0" + hex
end
return hex
end

def percenttohex(level)
# convert from percent to byte
return "%X" %((level.to_i * 2.56) -1).to_i
end

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

def log(line)
$log = File.open("/var/log/pluto/39_Generic_Serial_Device.log", "a")
$log.puts "(***):" + line.to_s
$log.close
end

Process Incoming Data:
Code: [Select]
#### Written by Dan Damron
#### #350 Process Incoming Data ####


#$recvBuff = ''
#while (true)
# buff = conn_.RecvDelimited($responseString, 100)
# if (buff.length() == 0)
# break
# end
# $recvbuff = $recvbuff + buff
#end

log('In----------Processing Incoming Data')
while(true)
    buff=conn_.Recv(128,100)
    if(buff.length() == 0)
        break
    end
    $recvbuff = $recvbuff + buff
end

$recvbuff = parsestring($recvbuff)

Process Initialize:

Code: [Select]
#### Written by Dan Damron
#### #355 Process Initialize ####
$log = ''
$cmdTo = ''
$cmdFrom = ''
$cmdPriority = ''
$cmdType = ''
$cmdID = ''
$cmdParams = {}
$children = {}
$recvbuff = ''
$wAIT = false
$currentCmd = {}
$flagsBroadcastMessage = 128
$flagsDirectMessage = 0
$flagsAckDirectMessage = 32
$flagsNackDirectMessage = 160
$flagsGroupBroadcaseMessage = 192
$flagsGroupCleanupDirectMessage = 64
$flagsGroupAckDirectMessage =96
$flagsGroupNackDirectMessage = 224
$cmdqueue = []
$dcequeue = []
$X10HouseCodes = {
'6', 'A',
'E', 'B',
'2', 'C',
'A', 'D',
'1', 'E',
'9', 'F',
'5', 'G',
'D', 'H',
'7', 'I',
'F', 'J',
'3', 'K',
'B', 'L',
'0', 'M',
'8', 'N',
'4', 'O',
'C', 'P'}
$X10UnitCodes = {'6', '1', 'E', '2', '2', '3', 'A', '4',
'1', '5', '9', '6', '5', '7', 'D', '8',
'7', '9', 'F', '10', '3', '11', 'B', '12',
'0', '13', '8', '14', '4', '15', 'C', '16'}
$X10CommandCodes = {'6', 'All Lights Off', 'E', 'Status = off',
'2', 'On', 'A', 'Pre-Set Dim',
'1', 'All Lights On', '9', 'Hail Ack',
'5', 'Bright', 'D', 'Status=on',
'7', 'Extended Code','F', 'Status Request',
'3', 'Off', 'B', 'Pre-set Dim',
'0', 'All Units Off', '8', 'Hail Request',
'4', 'Dim', 'C', 'Extended Data(analog)'}
$responseString = '<' + '/' + 'Response>'
log('Finding Children..')
device_.childdevices_.each{|c|
log(c.to_s.to_i)
log(device_.childdevices_[c.to_s.to_i].devdata_[12])
$children[device_.childdevices_[c.to_s.to_i].devdata_[12].chomp.lstrip.rstrip] = c.to_s.to_i
}
$children.keys.each{|c| log(c + ' = ' + $children[c].to_s)}


Process Command for Child:
Code: [Select]
#### Written by Dan Damron
#### #384 Process Receive Command for Child ####


log('------dce--- PRCFC:$wAIT=' + $wAIT.to_s)
#add DCE command to DCE queue




insteonID = device_.childdevices_[cmd.devidto_].devdata_[12].chomp.split('.')
$cmdID = cmd.id_
$cmdTo = cmd.devidto_
childType = device_.childdevices_[cmd.devidto_].devtemplid_
$cmdFrom = cmd.devidfrom_
$cmdPriority = cmd.priority_
$cmdType = cmd.type_
$cmdParams = cmd.params_
log('------dce--- cmdID:' + $cmdID.to_s + ', cmdFrom:' + $cmdFrom.to_s + ', cmdTo:' + $cmdTo.to_s + ', cmdType:' + $cmdType.to_s + ', Priority:' + $cmdPriority.to_s)
$cmdParams.keys.each {|p| log('------dce--- Param_[' + p.to_s + ']=' + $cmdParams[p])}
case cmd.id_
when 192 # ON
if insteonID.length > 2 then

param = {'Command' => 'SndIns',
'Parameter1' => insteonID[0],
'Parameter2' => insteonID[1],
'Parameter3' => insteonID[2],
'Parameter4' => '0F',
'Parameter5' => '11',
'Parameter6' => 'FF'}
$cmdqueue << param
SndIns()
else
log "------dce--- X10 DEVICE!!!"


param = {'Command' => 'SndX10',
'Parameter1' => rawX10(device_.childdevices_[cmd.devidto_].devdata_[12]),
'Parameter2' => X10Flag('On')}
$cmdqueue << param
SndIns()
end
resp = Command.new($cmdTo, 1, $cmdPriority, $cmdType, $cmdID)
resp.params_ = $cmdParams
SendCommand(resp)
when 193 #OFF
if insteonID.length > 2 then

param = {'Command' => 'SndIns',
'Parameter1' => insteonID[0],
'Parameter2' => insteonID[1],
'Parameter3' => insteonID[2],
'Parameter4' => '0F',
'Parameter5' => '13',
'Parameter6' => '00'}
$cmdqueue << param
SndIns()
else
log "------dce--- X10 DEVICE!!!"
param = {'Command' => 'SndX10',
'Parameter1' => rawX10(device_.childdevices_[cmd.devidto_].devdata_[12]),
'Parameter2' => X10Flag('Off')}
$cmdqueue << param
SndIns()

end
resp = Command.new($cmdTo, 1, $cmdPriority, $cmdType, $cmdID)
resp.params_ = $cmdParams
SendCommand(resp)

when 184 #SetLevel
# convert from percent to hex
dim_level = percenttohex(cmd.params_[76])
log("------dce--- Dim Level:" + dim_level.to_s)
if insteonID.length > 2 then

param = {'Command' => 'SndIns',
'Parameter1' => insteonID[0],
'Parameter2' => insteonID[1],
'Parameter3' => insteonID[2],
'Parameter4' => '0F',
'Parameter5' => '11',
'Parameter6' => dim_level}
$cmdqueue << param
SndIns()
else
log "X10 DEVICE!!! relative Dimming command"
# have to send 2 commands
#param = {'Command' => 'SndX10',
#'Parameter1' => rawX10(device_.childdevices_[cmd.devidto_].devdata_[12]),
#'Parameter2' => $X10CommandCodes.index['Bright'] + $X10UnitCodes.index[dim_level]
#$cmdqueue << param
#param = {'Command' => 'SndX10',
#dim_level = ((cmd.params_[76] * 16 + 1)/100).to_i.to_s
#'Parameter1' => rawX10(device_.Childdevices_[cmd.devidto_].devdata_[12]),
#'Parameter2' => $X10CommandCodes.index['Dim'] + $X10UnitCodes.index[dim_level]}

SndIns()

end
resp = Command.new($cmdTo, 1, $cmdPriority, $cmdType, $cmdID)
resp.params_ = $cmdParams
SendCommand(resp)
end
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 05, 2007, 04:25:52 am
Bump (modified original message)
Dan
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: tschak909 on December 05, 2007, 02:03:12 pm
dude, that's awesome.. i don't have any insteon devices yet, or i'd be trying it right now!

-Thom
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 05, 2007, 04:59:27 pm
Thanks Thom!

Still waiting to see if anyone knows the answer to my question...

I'm starting work on the PLM (serial) now.

with the code I've already done, it should go pretty quick.. a few days..
will post it too!
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: bulek on December 05, 2007, 11:14:42 pm
Hi,

I've done similar code in Perl for my home automation system from Cybrotech. And basically events do change state of device.

For instance :
- you send on command to light and then return event with light being on and then state will change - although currently majority of home automation systems aren't two way, so light switch at the moment is designed to make transition by itself - but if you send event also it will change it's state accordingly.

For sensors like temperature, brightness, thermostat setpoint you just send proper events about their current state ("temperature changed" for instance)...

See this :
http://wiki.linuxmce.org/index.php/Sensors (http://wiki.linuxmce.org/index.php/Sensors)
http://wiki.linuxmce.org/index.php/MessageSend (http://wiki.linuxmce.org/index.php/MessageSend)
http://wiki.linuxmce.org/index.php/DCE_from_commandline (http://wiki.linuxmce.org/index.php/DCE_from_commandline)

and make few tests with sending events/commands - you easily see what works and what doesn't...

HTH,

regards,

Bulek.
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 06, 2007, 03:07:56 am
Bulek,

THANK YOU!

I could not find that info anywhere... now I just need to track down which events to fire for each command...

and who do I send the event to?  DCERouter I'm guessing...

Can I send 2 seperate events.. 1 for 'Command Sent Successfully' and the other for 'Command Executed Successfully'?  I just noticed the links you said to see.. I'll investigate those now...

again,

THANK YOU!

Dan
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 06, 2007, 04:11:57 am
Ok, I think I understand the Events... sort of...
I now know to fire the event to device ID -1000 (hardcoded to DCERouter)

I understand how to fire an event.
looking at the zwave device template, I found the device on/off event.
I understand (if I'm not mistaken) I have to create a new event:
1.  'Report Light On-Level' (#75)
with parameters:
2.  'value' # 30
with text description of 'value of On-Level percent 0 to 100%'

Now, do I create that event in :
A:  the actual child device (light switch, dimmable),
B:  the EZBridge device (GSD)
C:  or both?

Once the event is created, what must I do to allow DCERouter to respond to (a new event)?

Thanks so much for your help in pointing me in the right direction...

Dan
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 06, 2007, 08:28:23 pm
Trial and error provided me with:

1.  The event must be fired from the CHILD DEVICE, not the parent.
2.  Sending the event to -1000 reports (in the log) as UNKNOWN, but dcerouter seems to get it (I can change light status ON/OFF)
3.  My newly created event fires correctly, however I don't understand how to tell dcerouter:

 "When you hear THIS event, Set the senders INTERNAL Light Level value to this events parameter value!'

ie:  device:40 (Office Light) when Event:75 (Report Light On-Level) fires from  with parameter:30 (value)s value set to 50

07   12/06/07 11:57:16.958      Event #75 has no handlers <0x6699bb90>
07   12/06/07 11:57:16.958      Received Message from 40 (Office Light / Office) to -1000 (unknown / ), type 2 id 75 Event:Report Light On-Level, retry none, parameters: <0x6699bb90>
07   12/06/07 11:57:16.958        Parameter 30(Value): 50 <0x6699bb90>

Thanks ahead of time! 
BTW, PLM integration is well on it's way...
I've learned more ruby, and code is again (!) much much more object oriented.
I've created self validating command objects to handle all the PLM communication, and all the fields IN those command objects are also self-validating... with all kinds of methods to insert and extract data.

I've currently created class files for:
InsteonID
InsteonFlags
InsteonCmdByte
InsteonExtendedData
X10Translations
InsStdCmd (Insteon Standard Command)

etc.etc.etc..





Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: bulek on December 06, 2007, 11:18:23 pm
Hi,

usually DCERouter only reroutes events to coresponding plugins to act upon (I guess lighting plugin should be addressed in this case, but also it should understand it )...

I'm not sure, but it seems like event #75 ix not yet supported (or better said - it's not routed further to some plugin that would understand it...). And if it were, current state for lights is not shown on Orbiter anyway - so other changes in code are also needed... Maybe for now you can try to put current level into device state directly, not via plugin...

Currently support for events is quite limited and one can get a lot of frustrations when trying to add support for something new (it's almost a year since I began my efforts in adding "brightness changed" event, but it's still not supported properly - I must say that any new event has to be added to main database and code base so one can implement support for it - so things are sometimes slower from our expectations)...

Anyway, to speed up such thing, I've written several feature requests and forum threads and did a lot of discussion sessions with Pluto guys, but things are going slowly- although we came to somekind of agreement that this problem need to be addressed as soon as possible, cause only few devices are currently properly shown on Floorplan and can be controlled from there... My proposition is described here :
http://mantis.linuxmce.org/view.php?id=3456 (http://mantis.linuxmce.org/view.php?id=3456)

Please put as much info as possible about your work - it will be tremendously useful for other potential contributors...

HTH,

regards,

Bulek.
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 07, 2007, 02:41:25 am
Thanks Bulek, Great reply..

Digging around I found the event system..under Advanced/Configuration/Events...

Yes, Event # 75 is new - I created it and added it to my device template #38 Light Switch (dimmable), which, SHOULD filter down to all my lighting devices...

looking closer, I see that the piece of data I want to change is the STATE (string)...

I've coded almost 1200 lines of NEW code for Insteon PLM interface.. and still not done...line #1131 is where my class objects STOP (so far)

I've got about 8 more objects to create <Phew> before I can start testing to make sure the 'glue' is right from object to object.

I'll keep digging around the advanced event system to see if I can find a command to change a devices state..

Thanks again for all your help...

really apreciate the input.

Dan
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 07, 2007, 03:02:02 am
Bulek,

There's got to be a way to do it... Orbiters do it..
I can see the state of the light in the Devices/Lights/advanced.. under Device Info.. at the bottom... state (mine currently says ON/50)

The orbiters DO use this if you use a fiire chief remote, and momentarily press VOL&LIGHT and wave up/down..
you can see the state change on the left side of the screen..

There's got to be something I'm (we're) missing.. lemme find out exactly what an orbiter does when you change the 'state'

unless it's a one-way value... hmmm.. that could be it.. but should I not be able to fire a command based on an event?

Yes, I can do that, I saw it in the advanced events.. but the trick here is.. can I fire a command based on an event's parameters.... hmmm....

Brainstorming here.... anyone else have any suggestions??

BTW, I think I figured out how to add an event handler for a custom event, so that part shouldn't be too hard...

HTH,
Dan
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 07, 2007, 05:49:11 am
K, I'm tired, just another 200 lines of code to total 1400.
Started testing the 'glue', all seems ok so far.. only tested 5 command objects.

For those of you who are following this...  here's my code so far...
WTF!@! Grrr....

The code is too big to fit in the message :(
anyone know how I can do this??  I get an error message saying that the message max is 20000 bytes..

Ahh well, I'll just attach it then... hehe

I'll be awake for about another hour... if you get a chance to reply, please do!  I LOVE to hear comments / suggestions...

TTYL

Dan
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: bulek on December 07, 2007, 09:13:40 am
Hi,

I'm not sure if any changes will appear in 710, but we agreed with developers, that maybe Climate and Lighting plugin will go in direction described in mantis feature request - so they will draw device's state on Floorplan whatever it is... we will see what happens...

Till then you can change device state directly in sql database with sql statement...


...

There's got to be something I'm (we're) missing.. lemme find out exactly what an orbiter does when you change the 'state'

unless it's a one-way value... hmmm.. that could be it.. but should I not be able to fire a command based on an event?

Yes, I can do that, I saw it in the advanced events.. but the trick here is.. can I fire a command based on an event's parameters.... hmmm....

Brainstorming here.... anyone else have any suggestions??

BTW, I think I figured out how to add an event handler for a custom event, so that part shouldn't be too hard...

HTH,
Dan

Not sure why do you need that (I guess you want to add event handler over web-admin? ). Otherwise you can send any command you want from your gsd device, but that's probably not what you're looking for....

Hmm, I still need to take Ruby lessons to be able to do something usefull with GSD...

Regards,

Bulek.
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 07, 2007, 06:15:42 pm
Bulek,

Thanks again for all your info..  and Yes, I agree about the Lighting and Climate plugins.  I will make my views known in mantis, but I want to finish this code first.

I think I have the rest of it down... we'll see once I start implementing this code..

Dan
Title: RC1 Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 12, 2007, 11:26:50 pm
here they are...

Code: [Select]
#### Written by Dan Damron
#### #373 Private Method Listing ####
#
# Commands send back to DCE
# 184 - SetLevel value 0-100 (% on) or +-1 for steps
# 193 - Turn Light OFF
# 192 - Turn Light ON
#
#
#
#
require "rexml/document"
require "rexml/streamlistener"

include REXML


def EZToDCE(param)
#param contains a hash of EZBridge command structure
log('--|---------EZtoDCE')
log('Queue:' + $cmdqueue[0].inspect)
log('Param:' + param.inspect)
checkWait(param)
case param['Response']
# Standard Commands (responses to command)
when 'GetRevision' # Special Response
log('-----|---------GetRevision:' + param['Parameter1'])

when 'GetLatLong'  # Special Response
log('-----|---------GetLatLong: Lat=' + param['Lat'] + ', Long=' + param['Long'])

when 'SetLatLong'
if param['Parameter1'] = 'True' then
log('-----|---------SetLatLong: Ok')
else
log('-----X---------SetLatLong: FAILED!')
end
when 'SetPasswd'

when 'SetTimeZone'

when 'GetClock' # Special Response

when 'SetClock'

when 'SetNTPServer'

when 'Upgrade'

when 'NetCfg'

when 'Reset'
#Reset Response
cmdComplete
when 'LstTimers'  # Special Response


when 'ClrTimers'

when 'AddTimer'

when 'GetTimer'    # Special Response

when 'SetTimer'

when 'DelTimer'

when 'GetVersion' # Special Response

when 'SndGrp'

when 'SndIns' # response from SndIns
#First, check the response to make sure the command made it ok.
if param['Parameter9'].to_i == 6 #Ack received
case param['Parameter7'].hex #Received an ack to THIS command
when '1'.hex #Assign to Group
when '2'.hex #Delete from Group
when '10'.hex # PING
when '11'.hex #ON
$currentcmd = 11
$cmdqueue[0]['Command'] = 'InsStdMsg'
when '13'.hex #OFF
$currentcmd = 13
$cmdqueue[0]['Command'] = 'InsStdMsg'
when '15'.hex #Bright
$currentcmd = 15
$cmdqueue[0]['Command'] = 'InsStdMsg'
when '16'.hex #DIM
$currentcmd = 16
$cmdqueue[0]['Command'] = 'InsStdMsg'
when '17'.hex #Start Manual Change
when '18'.hex #Stop Manual Change
when '19'.hex #Status Request
# Status Request Acked - get ready for InsStdMsg
$currentcmd = 19
# change cmd to reflect InsStdMsg
$cmdqueue[0]['Command'] = 'InsStdMsg'
when '24'.hex
log('Caught ACK for Insteon DO EE READ')
when '28'.hex
log('Caught ACK for Insteon SET ADDRESS MSB')
when '29'.hex
log('Caught ACK for Insteon POKE')
when '2A'.hex
log('Caught ACK for Insteon POKE EXTENDED')
when '2B'.hex
log('Caught ACK for Insteon PEEK')
when '2C'.hex
log('Caught ACK for Insteon PEEK INTERNAL')
when '2D'.hex
log('Caught ACK for Insteon POKE INTERNAL')
else
log('Unknown Insteon Cmd1')
end

else # NACK received - command failed.
end

when 'SndX10'

when 'StLnk'

when 'CancelLnk'

when 'SetDev'

when 'RstPLM'

when 'GetLnk'

when 'GetNext'

when 'SetCfg' # response from Setcfg command
if param['Parameter4'].to_i == 6 #ack
log('SetCfg Completed')
cmdComplete
else
log('SetCfg NACKED')
end
when 'GetLnkData'

when 'LEDON'

when 'LEDOFF'

when 'MngLnk'

when 'GetCfg' # Special Response

when 'LstMacros' # Special Response

when 'ClrMacros'

when 'AddMacro'

when 'GetMacro' # Special Response

when 'SetMacro'

when 'DelMacro'

when 'LstDevices' # Special Response

when 'ClrDevices'

when 'AddDevice'

when 'GetDevice' # Special Response

when 'SetDevice'

when 'DelDevice'

when 'LstZones'

when 'ClrZones'

when 'AddZone'

when 'GetZone'

when 'SetZone'

when 'DelZone'

when 'AddDevZone'

when 'DelDevZone'

# Response Messages
when 'InsExtMsg'

when 'InsStdMsg'
case $currentcmd # if this msg is part of a response, the Insteon Cmd1 will be saved here.
when 0
#there is no current command
# THIS IS COMING FROM AN OUTSIDE SOURCE
#EG: Manually turning Light Switch On/Off
# process this command.
# Note, this is a standard message
# there is no ack here.  Simply Send EVENTS back to DCE
# and Complete the command.
case param['Parameter10'].hex #this is the command
when '1'.hex #Assign to Group
log('Caught EXTERNAL Assign to Group')
when '2'.hex #Delete from Group
log('Caught EXTERNAL Delete from Group')
when '10'.hex # PING
log('Caught EXTERNAL Insteon PING result')
when '11'.hex #ON
log('Caught EXTERNAL Insteon ON command result')
#on level is in cmd2
insHb = param['Parameter3'] # From
insMb = param['Parameter4']
insLb = param['Parameter5']
insID = insHb + "." + insMb + "." + insLb
myDevFrom = $children[insID]
myDevTo = -1000 #DCE Router
myPriority = 1
myType = 2 #Event
#Send an EVENT
#Event #48, pipe 10, value 1=on, 2=off
cmd = Command.new(myDevFrom, myDevTo, myPriority, myType, 48) #Event
cmd.params_[10] = '1' # EVENT ON
SendCommand(cmd)
$currentcmd = 0
cmdComplete
log('EVENT SENT!!!.48:[10]=1')


when '13'.hex #OFF
log('Caught EXTERNAL Insteon OFF result')
#on level is in cmd2
insHb = param['Parameter3'] # From
insMb = param['Parameter4']
insLb = param['Parameter5']
insID = insHb + "." + insMb + "." + insLb
myDevFrom = $children[insID]
myDevTo = -1000 #DCE Router
myPriority = 1
myType = 2 #Event
#Send an EVENT
#Event #48, pipe 10, value 1=on, 2=off
cmd = Command.new(myDevFrom, myDevTo, myPriority, myType, 48) #Event
cmd.params_[10] = '0' # EVENT OFF
SendCommand(cmd)
$currentcmd = 0
cmdComplete
log('EVENT SENT!!!.48:[10]=0')


when '15'.hex #Bright
log('Caught EXTERNAL Insteon Brighten Result')
when '16'.hex #DIM
log('Caught EXTERNAL Insteon DIM Result')
when '24'.hex
log('Caught EXTERNAL Insteon DO EE READ')
when '28'.hex
log('Caught EXTERNAL Insteon SET ADDRESS MSB')
when '29'.hex
log('Caught EXTERNAL Insteon POKE')
when '2A'.hex
log('Caught EXTERNAL Insteon POKE EXTENDED')
when '2B'.hex
log('Caught EXTERNAL Insteon PEEK')
when '2C'.hex
log('Caught EXTERNAL Insteon PEEK INTERNAL')
when '2D'.hex
log('Caught EXTERNAL Insteon POKE INTERNAL')
else
log('Unknown EXTERNAL Insteon Command.')
end
when 1 #Assign to Group
when 2 #Delete from Group
when 11 # ON
log('Caught Insteon ON from EXTERNAL source')
insHb = param['Parameter3'] # From
insMb = param['Parameter4']
insLb = param['Parameter5']
insID = insHb + "." + insMb + "." + insLb
myDevFrom = $children[insID]
myDevTo = -1000 #DCE Router
myPriority = 1
myType = 2 #Event
#Send an EVENT
#Event #48, pipe 10, value 1=on, 2=off
cmd = Command.new(myDevFrom, myDevTo, myPriority, myType, 48) #Event
cmd.params_[10] = '1' # EVENT ON
SendCommand(cmd)
$currentcmd = 0
cmdComplete
log('EVENT SENT!!!.48:[10]=1')
when 13 # OFF
insHb = param['Parameter3'] # From
insMb = param['Parameter4']
insLb = param['Parameter5']
insID = insHb + "." + insMb + "." + insLb
myDevFrom = $children[insID]
myDevTo = -1000 #DCE Router
myPriority = 1
myType = 2 #Event
#Send an EVENT
#Event #48, pipe 10, value 1=on, 2=off
cmd = Command.new(myDevFrom, myDevTo, myPriority, myType, 48) #Event
cmd.params_[10] = '0' # EVENT OFF
SendCommand(cmd)
$currentcmd = 0
cmdComplete
log('EVENT SENT!!!.48:[10]=0')
when 19 # Status Report
insHb = param['Parameter3'] # From
insMb = param['Parameter4']
insLb = param['Parameter5']
insID = insHb + "." + insMb + "." + insLb
myDevFrom = $children[insID]
myDevTo = -1000 #DCE Router
myPriority = 1
myType = 2 #Event
#Send an EVENT
#Event #48, pipe 10, value 1=on, 2=off
cmd = Command.new(myDevFrom, myDevTo, myPriority, myType, 48) #Event
#cant send dim value, only on or off
#cmd.params_[184] = hextopercent(param['Parameter8']).to_s
if param['Parameter11'].hex.to_i == 0 #if off
cmd.params_[10] = '0' # Send Event OFF
else
cmd.params_[10] = '1' # otherwise, send EVENT ON
end
log('Parameter11 = ' + param['Parameter11'].to_s)
log('Params_[10] = ' + cmd.params_[10].to_s)
SendCommand(cmd)
log('EVENT SENT!!!..............')
# now to clear the currentcmd and remove command from the queue
$currentcmd = 0
cmdComplete
else
end

when 'X10Msg'
log('X10 Message Received')

when 'InsLnkSts'

when 'BtnRpt'

when 'UsrRst'

when 'GrpEvntRpt'
log('Group Event Report Received')

when 'LnkData'
#Caught a LnkData Response
log('LnkData Message Received')

# Other messages
when 'PLMTimeout'
log('PLM Timeout detected.. resetting EZBridge')
resetcmd = {'Command' => 'Reset'}
$cmdqueue.insert(0, resetcmd)
sleep 15
$wAIT = false
SndIns()
when 'PLMEchoError' # Have not seen this since a1.23
# error sending command to PLM, reset command and try again
log('-----XXXXXXX' + param['Response'])
resetcmd = {'Command' => 'Reset'}
$cmdqueue.insert(0, resetcmd)
sleep 15
$wAIT = false
SndIns()
when 'LongAck'
log('-----XXXXXXX' + param['Response'])
# Error - seems to show up after PLMEchoError
resetcmd = {'Command' => 'Reset'}
$cmdqueue.insert(0, resetcmd)
sleep 15
$wAIT = false
SndIns()
else
log('-----XXXXXXX UNKNOWN Response Received:' + param['Response'])
end

end
def cmdComplete
# Clears the wait flag, and removes the command from the queue.
$wAIT = false
$cmdqueue.delete_at(0)
# now that THAT command is completed, check to see if there is
# another command ready to transmit.
log ("Command Completed.  Checking queue")
if $cmdqueue.nitems > 0 then
log("Queue:" + $cmdqueue.nitems.to_s + " : executing next command")
SndIns()
else
log ('Queue Empty.')
end
end
def checkWait(param)
#Checks for response from current command.
if $cmdqueue[0] != nil
else
log('------:)----checkWait: command queue is emtpy')
if $wAIT == true #this should never happen
log('------:(XXX-CheckWait: TRUE')
else
log('------:)----CheckWait: FALSE')
end
end
end
def parsestring(value)
## This routine simply extracts complete XML messages
### Could not use RecvDelimited in PID as duplicate messages came through

  param = {}
 ar = value.split($responseString, -1)

  if ar.nitems < 2 then
      value = ar[0]
  else
      value = ar.delete_at(ar.nitems - 1)
      ar.each{|p| p = p + $responseString
      doc = Document.new p
      command = doc.root.get_text.value.to_s
      param['Response'] = command.chomp.lstrip.rstrip

      if doc.root.has_elements? then
#has elements
el = doc.root.elements[1]
while el != nil
          elname = el.name.to_s
          eltext = padhex("%X" %doc.root.elements[elname].text.to_s.hex)
          param[elname] = eltext
          el = doc.root.elements[elname].next_element
end
      end
#parseMessage(param)
EZToDCE(param)
}
    end
return value
end




def InsCmdtoID(value)
case value
when '01' # Assign to (Insteon) Group
return 0
when '02' # Delete from (Insteon) Group
return 0
when '10' # Ping
return 0
when '11' # ON
return 192
when '13' # OFF
return 193
when '15' # Brighten 1 step
return 0
when '16' # Dim 1 step
return 0
when '17' # Start Manual Change
return 0
when '18' # Stop Manual Change
return 0
when '19' # Status Request
return 0
when '24' # Do EE READ
return 0
when '28' # Set Address MSB
return 0
when '29' # POKE
return 0
when '2A' # POKE Extended
return 0
when '2B' # PEEK
return 0
when '2C' # PEEK Internal
return 0
when '2D' # POKE internal
return 0
else
return 0
end

end

def rawX10(value)

x10MSN = $X10HouseCodes.index[value[0..0]]
x10LSN = $X10UnitCodes.index[value[1..1]]
return x10MSN + x10LSN
end
def X10Flag(value)
  return + $X10CommandCodes.index[Value] + '0'
end

def SndIns
# create a new XML command for SndIns
if $wAIT == false
param = $cmdqueue[0]
doc = Document.new
doc << XMLDecl.new
el = doc.add_element 'command'
el.text = param['Command']
param.keys.each {|k|
if k != 'Command' then
el1 = el.add_element k
el1.text = param[k]
end
}
conn_.Reconnect()
conn_.Send(doc.to_s)
log('Out---------SndIns:CmdSent:' + doc.to_s)
sleep 1
$wAIT = TRUE
else
log('X-----------SndIns: Waiting for response to:' +  $cmdqueue[0]['Command'].to_s)
log('X-----------SndIns: Current Queue Length:' + $cmdqueue.nitems.to_s)
end
end

def padhex(hex)
if hex.length==1
hex = "0" + hex
end
return hex
end

def percenttohex(level)
# convert from percent to byte
return "%X" %((level.to_i * 2.56) -1).to_i
end

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

def log(line)
$log = File.open("/var/log/pluto/39_Generic_Serial_Device.log", "a")
$log.puts "(***):" + line.to_s
$log.close
end

Code: [Select]
#### Written by Dan Damron
#### #351 Process IDLE ####
############################ RECEIVE Error Trap ###########################
if  $cmdqueue.length != 0
if $cmdqueue == $savedstate
if $resetNext == 5
#Command has stalled. Reset wait flag and resend command.
log('Command Stalled!  resetting and retrying')
if $cmdqueue[0]['Command'] == 'InsStdMsg'
$cmdqueue[0]['Command'] = 'SndIns'
end
$wAIT = false
$resetNext = 0
SndIns()
else
$resetNext +=1
end
else
$resetNext = 0
$savedstate = $cmdqueue
end
end
###########################################################################
Continued in next message
Title: Re: Beta Ruby code for EZ-Bridge (Insteon)
Post by: ddamron on December 12, 2007, 11:28:33 pm
Code: [Select]
#### Written by Dan Damron
#### #350 Process Incoming Data ####


#$recvBuff = ''
#while (true)
# buff = conn_.RecvDelimited($responseString, 100)
# if (buff.length() == 0)
# break
# end
# $recvbuff = $recvbuff + buff
#end

log('In----------Processing Incoming Data')
while(true)
    buff=conn_.Recv(128,100)
    if(buff.length() == 0)
        break
    end
    $recvbuff = $recvbuff + buff
end

$recvbuff = parsestring($recvbuff)
Code: [Select]
#### Written by Dan Damron
#### #355 Process Initialize ####
$log = ''
$cmdTo = ''
$cmdFrom = ''
$cmdPriority = ''
$cmdType = ''
$cmdID = ''
$cmdParams = {}
$children = {}
$recvbuff = ''
$wAIT = false
$currentCmd = {}
$flagsBroadcastMessage = 128
$flagsDirectMessage = 0
$flagsAckDirectMessage = 32
$flagsNackDirectMessage = 160
$flagsGroupBroadcaseMessage = 192
$flagsGroupCleanupDirectMessage = 64
$flagsGroupAckDirectMessage =96
$flagsGroupNackDirectMessage = 224
$cmdqueue = []
$currentcmd = 0 # to save state of command when receiving more data.
$resetNext = 0 # used in Process IDLE to trap stalled responses.
$savedstate = nil
$dcequeue = []
$X10HouseCodes = {
'6', 'A',
'E', 'B',
'2', 'C',
'A', 'D',
'1', 'E',
'9', 'F',
'5', 'G',
'D', 'H',
'7', 'I',
'F', 'J',
'3', 'K',
'B', 'L',
'0', 'M',
'8', 'N',
'4', 'O',
'C', 'P'}
$X10UnitCodes = {'6', '1', 'E', '2', '2', '3', 'A', '4',
'1', '5', '9', '6', '5', '7', 'D', '8',
'7', '9', 'F', '10', '3', '11', 'B', '12',
'0', '13', '8', '14', '4', '15', 'C', '16'}
$X10CommandCodes = {'6', 'All Lights Off', 'E', 'Status = off',
'2', 'On', 'A', 'Pre-Set Dim',
'1', 'All Lights On', '9', 'Hail Ack',
'5', 'Bright', 'D', 'Status=on',
'7', 'Extended Code','F', 'Status Request',
'3', 'Off', 'B', 'Pre-set Dim',
'0', 'All Units Off', '8', 'Hail Request',
'4', 'Dim', 'C', 'Extended Data(analog)'}
$responseString = '<' + '/' + 'Response>'
log('Finding Children..')


device_.childdevices_.each{|c| $children[device_.childdevices_[c.to_s.to_i].devdata_[12].chomp.lstrip.rstrip] = c.to_s.to_i}
$children.keys.each{|c| log(c + ' = ' + $children[c].to_s)}
#children found... get the status of each child...
$children.keys.each{|c|
if c.length > 2 #filter out X10 for now
  #pack insteon message here, and send it out!
# receive message in parsemessage and relay commands back in it.
insteonID = c.chomp.split('.')
param = {'Command' => 'SndIns',
'Parameter1' => insteonID[0],
'Parameter2' => insteonID[1],
'Parameter3' => insteonID[2],
'Parameter4' => '0F', #flags
'Parameter5' => '19', #Status Request
'Parameter6' => '00'} #not used.
$cmdqueue << param

end
}
#lastly, put the PLM in monitor mode.
param = {'Command' => 'SetCfg', 'Parameter1' => '40'}
$cmdqueue << param
SndIns() #Lastly, start executing commands in order.
Code: [Select]
#### Written by Dan Damron
#### #384 Process Receive Command for Child ####


log('------dce--- PRCFC:$wAIT=' + $wAIT.to_s)
#add DCE command to DCE queue




insteonID = device_.childdevices_[cmd.devidto_].devdata_[12].chomp.split('.')
###
#log('INSPECT:'+ device_.childdevices_[cmd.devidto_].devdata_[])
###
$cmdID = cmd.id_
$cmdTo = cmd.devidto_
childType = device_.childdevices_[cmd.devidto_].devtemplid_
$cmdFrom = cmd.devidfrom_
$cmdPriority = cmd.priority_
$cmdType = cmd.type_
$cmdParams = cmd.params_
log('------dce--- cmdID:' + $cmdID.to_s + ', cmdFrom:' + $cmdFrom.to_s + ', cmdTo:' + $cmdTo.to_s + ', cmdType:' + $cmdType.to_s + ', Priority:' + $cmdPriority.to_s)
$cmdParams.keys.each {|p| log('------dce--- Param_[' + p.to_s + ']=' + $cmdParams[p])}
case cmd.id_
when 192 # ON
if insteonID.length > 2 then

param = {'Command' => 'SndIns',
'Parameter1' => insteonID[0],
'Parameter2' => insteonID[1],
'Parameter3' => insteonID[2],
'Parameter4' => '0F',
'Parameter5' => '11',
'Parameter6' => 'FF'}
$cmdqueue << param
SndIns()
else
log "------dce--- X10 DEVICE!!!"


param = {'Command' => 'SndX10',
'Parameter1' => rawX10(device_.childdevices_[cmd.devidto_].devdata_[12]),
'Parameter2' => X10Flag('On')}
$cmdqueue << param
SndIns()
end
# resp = Command.new($cmdTo, 1, $cmdPriority, $cmdType, $cmdID)
# resp.params_ = $cmdParams
# SendCommand(resp)
when 193 #OFF
if insteonID.length > 2 then

param = {'Command' => 'SndIns',
'Parameter1' => insteonID[0],
'Parameter2' => insteonID[1],
'Parameter3' => insteonID[2],
'Parameter4' => '0F',
'Parameter5' => '13',
'Parameter6' => '00'}
$cmdqueue << param
SndIns()
else
log "------dce--- X10 DEVICE!!!"
param = {'Command' => 'SndX10',
'Parameter1' => rawX10(device_.childdevices_[cmd.devidto_].devdata_[12]),
'Parameter2' => X10Flag('Off')}
$cmdqueue << param
SndIns()

end
# resp = Command.new($cmdTo, 1, $cmdPriority, $cmdType, $cmdID)
# resp.params_ = $cmdParams
# SendCommand(resp)

when 184 #SetLevel
# convert from percent to hex
dim_level = percenttohex(cmd.params_[76])
log("------dce--- Dim Level:" + dim_level.to_s)
if insteonID.length > 2 then

param = {'Command' => 'SndIns',
'Parameter1' => insteonID[0],
'Parameter2' => insteonID[1],
'Parameter3' => insteonID[2],
'Parameter4' => '0F',
'Parameter5' => '11',
'Parameter6' => dim_level}
$cmdqueue << param
SndIns()
else
log "X10 DEVICE!!! relative Dimming command"
# have to send 2 commands
#param = {'Command' => 'SndX10',
#'Parameter1' => rawX10(device_.childdevices_[cmd.devidto_].devdata_[12]),
#'Parameter2' => $X10CommandCodes.index['Bright'] + $X10UnitCodes.index[dim_level]
#$cmdqueue << param
#param = {'Command' => 'SndX10',
#dim_level = ((cmd.params_[76] * 16 + 1)/100).to_i.to_s
#'Parameter1' => rawX10(device_.Childdevices_[cmd.devidto_].devdata_[12]),
#'Parameter2' => $X10CommandCodes.index['Dim'] + $X10UnitCodes.index[dim_level]}

SndIns()

end
# resp = Command.new($cmdTo, 1, $cmdPriority, $cmdType, $cmdID)
# resp.params_ = $cmdParams
# SendCommand(resp)
end

Key features:
1.  Entirely in Ruby
2.  Distinguishes EXTERNAL insteon commands and sends events to LMCE based on them.
  (eg.  if you turn on a light switch manually, lmce will automatically be updated.)
3.  Full command queueing with failure recovery.
4.  following the log, you can actually 'peer' in on insteon activity.
5.  Structure in place for future expansion / enhancements.
6.  Requests initial status of each device (in initialize), then puts the PLM into monitor mode.

If you want to try this code out, post a reply and I'll put together template instructions.

Dan
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: tschak909 on December 13, 2007, 12:40:15 am
I think you've just created the single most complete interface to a home automation control standard in LinuxMCE.

-Thom
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 13, 2007, 02:03:46 am
Thanks Thom!

I'm using it quite reliably now...

It works with Group commands and scenarios quite well, although I'd like to implement the groups differently

It is also designed to be a Interface-Specialized.. because it can talk to a variety of devices as it stands now.

I'm using it for:
Light Switches (on/off and dimmable)
ApplianceLinc V2 (insteon version of the X10 appliance module)
EZIO8SA IOboard (8 relay outputs, 8 inputs, 1-wire, 2 analog inputs, etc..) connected to a DS18S20 1-wire thermometer
EZRain1 Sprinkler controller
EZX10RF (really, an X10 receiver)

and the list goes on...

The SwitchLinc V2's have an LED Status indicator on the left side... I think I'm going to add support to use those for displaying various types of information..  If I can't figure out groups, that will be next.

I also forgot to add another feature it has... upon initialize, it goes out and gets a status request from each insteon device and fires events to the DCE to set initial status.  Then it puts the PLM into monitor mode to sense when someone manually uses a device, and reports that to DCE.

Dan
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: PeteK on December 13, 2007, 03:40:50 am
Dan--

Here are the functions I wrote for adding and deleting remote records.  I've tested these successfully on a couple of lamplinc modules.

Code: [Select]
/*
 *
 * insteon_set_remnote_link
 *
 */
int insteon_set_remote_link(ilib_t *iplc, unsigned char *target_id, unsigned char *plc_id, unsigned char group,
unsigned char on_level, unsigned char ramp)
{
unsigned char new_record[8];
unsigned char record[8];
int i = 0;
unsigned short record_address = 0xFFF8;
unsigned short write_target;
int last_record = 0;

/*insteon_write_byte(iplc,target_id,0xFFF8,0x80);*/
/*insteon_read_bytes(iplc,target_id,0x0FF8,8,record);*/

/*Build Record to write to ALDB/L  */
new_record[0] = 0x22 | ALDB_L_VALID;   //For some reason, bit 5 needs to be set...Saw this when SDM creates record
new_record[1] = group;
new_record[2] = plc_id[0];
new_record[3] = plc_id[1];
new_record[4] = plc_id[2];
new_record[5] = on_level;
new_record[6] = ramp;
new_record[7] = 0x0;

/*Find next Open Address */

do { 
insteon_read_bytes(iplc,target_id,record_address,1,record);

if ((record[0] & ALDB_L_VALID) == 0)
{
/*We found a record that has been erased.  Write it here. */
write_target = record_address;
last_record = ((record[0] & ALDB_L_HIGH_WATER) == 0);
break;
}

insteon_read_bytes(iplc,target_id,record_address+1 ,7,(record + 1));

if ((record[1] == group) && (record[2] == plc_id[0]) && (record[3] == plc_id[1])
&& (record[4] == plc_id[2]))
{
//We have found an existing record for this PLC and this group.  Go ahead and overwrite this record
write_target = record_address;
break;
}
record_address-= 8;
}
while (1);

if (last_record == 1)  //Make sure we mark the next record with a high water mark if this is the last record
{
insteon_write_byte(iplc,target_id,write_target-8,0x00);
}

  for (i = 0; i < 8; i++)
{
insteon_write_byte(iplc,target_id,write_target,new_record[i]);
++write_target;
}
return last_record;

}

int insteon_delete_remote_link(ilib_t *iplc, unsigned char *target_id, unsigned char *plc_id, unsigned char group)
{
unsigned char record[8];
unsigned short record_address = 0xFFF8;
unsigned char next_record_flags;

do
{
insteon_read_bytes(iplc,target_id,record_address,1,record);

if ((record[0] & ALDB_L_VALID) != 0)
{
/*This is a valid record
* First get the rest of the record
*/
insteon_read_bytes(iplc,target_id,record_address+1,7,record + 1);

if ((record[1] == group) && (record[2] == plc_id[0]) && (record[3] == plc_id[1])
&& (record[4] == plc_id[2]))
{
/* We found the record in the ALDB */

/* Check to see if the next record has the high water mark set
* If so, we need to set the high water mark for this record after we delete it
*/
insteon_read_bytes(iplc,target_id,record_address-8,1,&next_record_flags);
if ((next_record_flags & ALDB_L_HIGH_WATER) == 0)
{
record[0] &= ~ALDB_L_HIGH_WATER;
}
/* Now clear the valid bit */
record[0] &= ~ALDB_L_VALID;
/* Write the new flags byte */
insteon_write_byte(iplc,target_id,record_address,record[0]);
return 0;
}
}
//Go to the next record
record_address -= 8;
}
while((record[0] & ALDB_L_HIGH_WATER) != 0);
return 1;
}

Let me know if you have any questions on it.
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 13, 2007, 06:40:56 am
Thanks Petek, That will come in handy..
I'll have to rewrite it in Ruby, but it saves me the 6 hours of testing...

As always, your comments are more than welcome.
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: PeteK on December 13, 2007, 05:05:50 pm
Sure.  This should work for the PLM, but I have no idea about the EZ-bridge.  When you say you're working with the EZ Rain valves, how do you differentiate the commands for the valve versus the commands for the generic lights?  I've created EZRain Valve ON and EZRain OFF commands that are going into the 0710 release, but the generic sprinkler command set supports Generic On and Generic OFF.  I'd like to be able to use those rather than new commands, but I have to differentiate whether the target is a generic light or the sprinkler.
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 13, 2007, 06:17:03 pm
The EZBridge, for all intents and purposes, is simply a 'pipe' to the plm.  I can issue any command the PLM can receive.
The difference is, i format an XML message, that includes all the bytes needed to send to the PLM.
so, I shouldn't have any problems at all with that.

I've created a subroutine to generate the XML, for me.  I pass it a hash param[] with the commands/values.
eg:
param['Command'] = 'SndIns'
param['Parameter1'] = '00'

sending to SndIns(param) generates

<Command>SndIns<Parameter1>00</Parameter1></Command>
and sends it out.

take a look at my initialize routine... I interrogate the Device Status of each device in a loop.
Code: [Select]
insteonID = c.chomp.split('.')
param = {'Command' => 'SndIns',
'Parameter1' => insteonID[0],
'Parameter2' => insteonID[1],
'Parameter3' => insteonID[2],
'Parameter4' => '0F', #flags
'Parameter5' => '19', #Status Request
'Parameter6' => '00'} #not used.
$cmdqueue << param
$cmdqueue is a FIFO array of commands.
There are two ways to diffrentiate, depending on what you want to do.
1.  in LMCE, look up the templateID for what type of device it should be.
and/or

2.  Interrogate the insteon devices for their Category, SubCat, and (insteon)DeviceID
Once you know that, all is well.
I use the standard ON/OFF 192/193 for valves too.
Take a look at the Insteon Command Tables Reference. DevCat 0x01 is lighting, 0x04 (I believe) is irrigation.

HTH

Dan
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: PeteK on December 13, 2007, 07:34:23 pm
There are two ways to diffrentiate, depending on what you want to do.
1.  in LMCE, look up the templateID for what type of device it should be.
and/or

2.  Interrogate the insteon devices for their Category, SubCat, and (insteon)DeviceID
Once you know that, all is well.
I use the standard ON/OFF 192/193 for valves too.
Take a look at the Insteon Command Tables Reference. DevCat 0x01 is lighting, 0x04 (I believe) is irrigation.

I like method 2. Is that what you're doing? If so, do you build a database each time you start the Insteon interface or do you store that data somewhere (like in the sql database)?  The Z-wave implementation grabs it each time at startup.  That may be the best way to do it.

Thanks for the info.  Two people working on a problem sure is a lot better than one (at least in this case :) )

Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 13, 2007, 09:07:25 pm
Just pulled out my Insteon Dev guide..  Chapter 7, Page 83

Device Categories:

0x00 Generalized controllers
0x01 Dimmable Lighting Control
0x02 Switched Lighting Control
0x03 Network Bridges
0x04 Irrigation Control
0x05 Climate Control
...
You can query the Device's category with a Product Data Request message
cmd1=0x03, cmd2=0x00
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 13, 2007, 09:30:31 pm
Just pulled out my Insteon Dev guide..  Chapter 7, Page 83

Device Categories:

0x00 Generalized controllers
0x01 Dimmable Lighting Control
0x02 Switched Lighting Control
0x03 Network Bridges
0x04 Irrigation Control
0x05 Climate Control
...
You can query the Device's category with a Product Data Request message
cmd1=0x03, cmd2=0x00


I (will) interrogate the devices... and store them in a global hash (array of objects).
Currently, I create a 'reverse' hash for all child devices.

$children = {} # define the hash

then populate the hash like so:
device_.childdevices_.each{|c| $children[device_.childdevices_[c.to_s.to_i].devdata_[12].chomp.lstrip.rstrip] = c.to_s.to_i}

which effectively gives me this: InsteonID as the key, and the object (currently a string) has the child's LMCE device ID.

$children['11.11.11'] => '49'

Keep in mind, most commands (eg 192 ON, 193 OFF) correspond pretty much directly with Insteon Commands (11 ON, 13 OFF)
and the device Category only comes in to play when you have 'extended' properties..
Again, these extended properties correspond pretty much directly with Insteon commands...
all we are is an overglorified command massager to say "This command in LMCE is THIS command in INSTEON".

HTH

Dan
and YES, two people working on the same (or similar) project IS MUCH MUCH easier!

I still have some problems reporting back events and such...  Maybe you can tell me how you (in general terms) handle each command..

ie.  DCE sends you a '192 ON' command for a child..
How do you respond back?
I've tried sending a COMMAND (both from the child, and from my device).
I've tried sending an EVENT (from Child to DCERouter dev -1000)

Here's my problem:  When I click Office Light ON, DCE sends me the command, and it's handled fine.
Then I click Office Light OFF, I can see the command in the DCE log go out... but when it reaches my device, I get:
05   12/13/07 13:03:25.778      GSDMessageTranslator isCmdImplemented = false <0xb6016b90>
05   12/13/07 13:03:25.781      #### Pre-Process Queue = 1 <0xb6016b90>
05   12/13/07 13:03:25.781      _QueueProc Pre - 193 : 0 <0xb7819b90>
05   12/13/07 13:03:25.781      GSD-DispatchMessage - ignoring 193 because is useless. <0xb7819b90>
05   12/13/07 13:03:25.781      _QueueProc Post - 193 : 0 <0xb7819b90>

GSD-DispatchMessage - ignoring 193 because is useless..

This tells me I have to report to GSD that I have successfully turned on the child.

however, if I go into FLOORPLAN, and select the device, DIM it, then click ON (from there), I can then click OFF and the command is received.

I know most of that has to do with GSD, which your not using..

Problem is, I don't know C++ very well at all :(

Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: PeteK on December 13, 2007, 09:44:18 pm
Just pulled out my Insteon Dev guide..  Chapter 7, Page 83

Keep in mind, most commands (eg 192 ON, 193 OFF) correspond pretty much directly with Insteon Commands (11 ON, 13 OFF)
and the device Category only comes in to play when you have 'extended' properties..
Again, these extended properties correspond pretty much directly with Insteon commands...
all we are is an overglorified command massager to say "This command in LMCE is THIS command in INSTEON".


Cool.  I wasn't certain that there was a standard set of commands for, say all irrigation devices.  I know the EZRain Valve Open Command is not 0x11.  I didn't know if the actual commands were all generated by SimpleHomeNet or if Insteon defined them.  The implementation could be more generic if it's the latter.
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 13, 2007, 11:50:09 pm
I have only speedread the irrigation details...
Insteon Dev guide shows:
0x40 Sprinkler Valve ON
0x41 Sprinkler Valve OFF
0x42 Sprinkler Program ON
0x43 Sprinkler Program OFF
0x44 Sprinkler Control with subcommands in cmd2:
(among others)
0x44 0x02 Get Valve Status
 etc..

LinuxMCE's command Structure:
Environment-Irrigation Devices - Irrigation Command Group #75
Delay # 257
Off # 193
ON # 192

I thought there was more..

ahh well, The EZRain1 does report its device info when requested to do so..
Keep in mind, LEGACY devices (Keypadlinc, Lamplinc, etc) do NOT report device category...
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 14, 2007, 12:28:34 am
Just did some quick testing on Product Data Request cmd 0x03

here's the results:
message sent:
02 62 - Standard Insteon Message
11 11 11 - InsteonID to:
0F - flags
03 00 (cmd1&2)

Legacy devices respond with:
02 62 11 11 11 0F 03 00 06 <-- response from the PLM saying Message Sent OK.

02 50 (Standard Message Received)
11 11 11 (from ID) Lamplinc
AA AA AA (to ID) PLM
2B (flags) - Ack of Direct Message, hopsleft=2, maxhops=3
03 00 (cmd1&cmd2) echoed back as is.

end of transmission.

The EZRain1 does the EXACT SAME THING, however, it also sends (after the above)
02 51 08 9B 62 AA AA AA 36 03 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00

then: {
02 51 08 9B 62 AA AA AA 3B 03 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
} * 4

flags are different in the first Extended Message...
Don't know why it sends  the second extended message 4 times over...

disecting the first message as a Product Data Response ED:
02 51 08 9B 62 AA AA AA 36 03 00 blah blah standard stuff

00 D1 - Always set to 0x00
00 D2 - Insteon Product Key MSB
01 D3 - Insteon Product Key 2MSB
00 D4 Insteon Procude Key LSB
00 D5 Device Category
00 D6 - Device Subcategory
00 D7 - supposed to be FF
00 D8 - supposed to be FF
00  D9-D14 - User defined

Something doesn't add up here..
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 14, 2007, 12:43:02 am
I've posted a problem report on simplehomenet forums... let's see what they come back with.
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: PeteK on December 14, 2007, 01:00:31 am
Are you going through the EZbridge to the PLM for these results?  If so, I wouldn't rule out a problem with that device.  I'll try this weekend with the PLC and let you know if I see the same thing. 
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 14, 2007, 01:59:58 am
I've tried BOTH the EZBridge, and direct to the PLM.
(I have my developer PLM attached to my windows box)
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 14, 2007, 02:13:22 am
I have double checked, BOTH the PLM and the EZBridge report back exactly the same..

Looks like the problem is with the device being interrogated (the EZRain1)

I've run the same tests on other devices, as a result of the problems with the EZRain..
The only device I found that reports CORRECTLY is the EZX10RF.

The EZBridge (when being interrogated by an external PLM) responds the same way as the EZRain... the 2MSB is different though..

When it comes down to it, even though the devices don't follow spec, they are still distinguishable, unlike the legacy devices.

I'm trying to find a way to determine if I'm talking to a Switchlinc, or a Keypadlinc...

I'll let you know if I find a way...

Dan
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 14, 2007, 03:00:21 am
On another note, it looks like the PLM support Higher level All-Link commands.
I've verified the EZBridge also supports them.
so, it looks as though I don't have to poke the devices after all...

Good news for me... :)
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: PeteK on December 14, 2007, 03:13:17 am
Ah, lucky you. ;)
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: PeteK on December 14, 2007, 03:44:21 am
So have you tried linking yet?
Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 14, 2007, 04:05:33 am
not linking yet.. I've been trying to figure out how to tell the devices apart...

I FINALLY FOUND IT!!!

Legacy devices INCLUDED!!!

Here's how:
send a SD 0x1000 (ping)
watch for a broadcast message back (message flags 8B)
in the To Address fields of that message will be:
High Byte = DEV CAT
Middle Byte = SUB CAT
Low Byte = Firmware Version

This seems to be the way of determining at least the device types..

I've been painstakingly going through my devices and recording the results in a spreadsheet..  All seems OK so far..

Here's my results in a nutshell:
LampLinc V2 = 01 00 32
EZRain1 = 04 00 FF
KeypadLincs = 01 09 29
Access Points: return an Extended length message with 00 00 22
SwitchLincs = 01 01 27
ApplianceLincs = 02 09 32
SwitchLinc Relays = 02 0A 2C
SwitchLinc 1000W Dimmer = 01 04 28

Keep in mind, the last value is a firmware version, may not always be the same...

That should make you happy!

Title: Re: Ruby code for EZ-Bridge (Insteon) RC1
Post by: ddamron on December 14, 2007, 07:42:39 am
Aaawwkgghhhrrrraaaa

Been at that stupid problem ALL DAY.  Finally got it figured out, and can't think straight enough to implement it..

I've got it implemented into my code, just not debugged yet.. 12 hours at the same problem (Solid, I might add)  It's bed time.  I can't program like I used to in the days of structured.. 16 Hours no problem, sometimes 24...

Ahh, getting older, and not liking it..

G'night all