I'm starting the framework for the PLCBUS interface.
I'm attempting this one slightly different than the Insteon.. I learned a few gotchas with that code..
This code will be more OOP.
So far, I have a Command class, which will create a command object. In that object is where I'll store all the specific data related to that command. (eg, which command it is, if it's an event, or command, all the devdata associated with it)
Then, I have a Commands class - a hash of command objects.
Device class will hold all device specific information (child device info, INCLUDING current State {!})
and, you guessed it, a Devices class to hold all the device objects.
That's it so far...
It's 5:30 AM right now here, I'm getting a bit tired.
TTYL
Dan
Super! I really look forward to get my hands on it!
Great!
Have no plcbus equipment yet.
Still waiting for a pricelist from the chinese manufacturer.
I beleive it will be a lot cheaper to do a bulk order instead of buying from another company in europe since there is no reseller for plcbus stuff in sweden yet.
I also asked if they have solved the local feedback issue when a user change a lightswitch status.
If they modify firmware to do so, it will enable us to get rid of the need to poll switch status.
/niz23
Quote from: niz23 on January 04, 2008, 05:31:15 PM
Great!
Have no plcbus equipment yet.
Still waiting for a pricelist from the chinese manufacturer.
I beleive it will be a lot cheaper to do a bulk order instead of buying from another company in europe since there is no reseller for plcbus stuff in sweden yet.
I also asked if they have solved the local feedback issue when a user change a lightswitch status.
If they modify firmware to do so, it will enable us to get rid of the need to poll switch status.
/niz23
i saw some quote on the homeseer forums: that won't happen soon as there is no easy way to distinguish between one and three phases. Another workaround ist to use scene controllers for every switch, so you get every message as no switches are controlled locally.
best regards,
Hari
hari,
Quote from: hari on January 04, 2008, 06:25:29 PM
i saw some quote on the homeseer forums: that won't happen soon as there is no easy way to distinguish between one and three phases. Another workaround ist to use scene controllers for every switch, so you get every message as no switches are controlled locally.
best regards,
Hari
I don´t understand. What´s the difference between one or three phases (use of a 3-phase module) What I want is that when I puch the button on a light switch the switch should send status message back to usb<->plcbus controller. I don´t see what that have to do with one ore three phases.
Using a scene controller is another workaround but it introduce the need for a micro module to build in somewhere since the scene controll is unable to direclty control a lod (like a lamp for example).
See http://www.x10-hk.com/store/manual/plcbus/plcbus-4206.pdf
I have to admit that I need to read up some more on plcbus. Maybe the answer to this question is obvious.
/niz23
Quote from: niz23 on January 04, 2008, 07:12:20 PM
I don´t understand. What´s the difference between one or three phases (use of a 3-phase module) What I want is that when I puch the button on a light switch the switch should send status message back to usb<->plcbus controller. I don´t see what that have to do with one ore three phases.
there is a phase coupler for three phase systems. To make it sending out your commands on all phases you set a special bit in the command. You have to do some different ack handling (first ack from the coupler, then ack from the device)
Quote
Using a scene controller is another workaround but it introduce the need for a micro module to build in somewhere since the scene controll is unable to direclty control a lod (like a lamp for example).
See http://www.x10-hk.com/store/manual/plcbus/plcbus-4206.pdf
the micro-module will fit behind the crystal scene controller. There is also a micro module scene controller to fit behind existing switches. The additional price will give you direct status feedback from "local" changes, as the scene controller sends a command to the micro module, and the usb interface sees that command on the bus.
There is also a command to report all switched on devices. That would help polling for local changes with some delay.
Quote
I have to admit that I need to read up some more on plcbus. Maybe the answer to this question is obvious.
/niz23
feel free to ask..
br hari
cool:
Quote
De order die u bij ons heeft geplaats is verzonden op: 4-1-2008 en heeft als verzendcode gekregen...
dan, looks like you get access to the hardware on monday or tuesday ;)
best regards,
Hari
Sweet!
Initial Sample Code for *ANY* HA interface.. (OOP)
The idea here, is to encapsulate everything in objects.
The Command Object will
1. get all the command info from DCE
2. figure out what it needs to send out to the GSD
3. Send the command out,
4. wait for a result (if required)
5. Process the result
6. send commands / events back to DCE
7. Repeat this procedure IN REVERSE if needed (eg. a command from the HA bus)
It also contains flags so at any time, we can query the status of the command.
The Device Object is all data associated with a particular device, including the current STATE information
(or at least what the code 'thinks' it should be)
The Commands Object is a simple collection of Command Objects
The Devices Object is a simple collection of Device Objects (go figure)
It's still pretty vague, but the structure is starting to show.
Regards,
Dan
class Command
def initialize(command)
#initialize here
@command = command
@event = false
@processing = false
@cmdsent = false
@cmdcomplete = false
@devdata = {}
end
def cmdSent
return @cmdsent
end
def cmdComplete
return @cmdcomplete
end
def dcecommand=(value)
@command = value
end
def dcecommand
return @command
end
def processing
return @processing
end
def gsdCommandOut
return @gsdcommand
end
def devdata(pkparam)
return @devdata[pkparam]
end
def setdevdata(pkparam,value)
@devdata[pkparam]= value
end
def event
return @event
end
def event=(event, default=false)
@event = event
end
end
class Commands
def initialize
@commands = []
end
def add(command)
#@cmd = Command.new(command)
@commands << command
end
def remove
@commands.delete_at(0)
end
def execute
end
end
class Device
def initialize(devid)
@devdata = {}
@devid = devid
@template = 0
@parent = 0
end
def template
return @template
end
def template=(templateid)
@template = templateid.to_i
end
def parent
return @parentid
end
def parent=(parentid)
@parentid=parentid.to_i
end
def setdevdata(pkparam, value)
@devdata[pkparam] = value.to_s
end
def devdata(pkparam)
return @devdata[pkparam]
end
def state
return @state
end
def configuration
return @configuration
end
def configuration=(value)
@configuration = value
end
def devid
return @devid
end
end
class Devices
def initialize
@devices = {}
device_.childdevices_.each_key{|c|
dev = Device.new(c)
dev.devdata[12] =device_.childdevices_[c].devdata_[12].chomp.lstrip.rstrip
dev.template = device_.childdevices_[c].devtemplid_
dev.parent = device_.childdevices_[c].parent_
add(c,dev)
}
end
def add(device)
@devices[device.devid] = device
end
def remove(devid)
@devices.remove[devid]
end
end
#Test Code Below:
cmd = Command.new(184)
cmd2 = Command.new(192)
cmd.setdevdata(74, '100')
cmd2.setdevdata(74, '200')
cmd2.event = true
puts cmd.devdata(74)
puts cmd.devdata(100)
cmd.setdevdata(50,'hello')
puts cmd.inspect
puts cmd.dcecommand
puts "Hello"
cmds = Commands.new()
cmds.add(cmd)
puts cmds.inspect
cmds.add(cmd2)
puts cmds.inspect
cmds.remove
puts cmds.inspect
more code
Started implementing the PLCBUS class
with definitions, getters and setters for the packet..
The structure is now starting to become apparent
Let me know what you think.. (I'm still learning Ruby, so if you can optimize it, let me know how!)
Regards,
Dan
class PLCBUS
attr_reader :HomeCodes, :UnitCodes, :CommandFunctions
STX=0x02.chr
ETX=0x03.chr
HomeCodes = {
'A'=>0b0000,
'B'=>0b0001,
'C'=>0b0010,
'D'=>0b0011,
'E'=>0b0100,
'F'=>0b0101,
'G'=>0b0110,
'H'=>0b0111,
'I'=>0b1000,
'J'=>0b1001,
'K'=>0b1010,
'L'=>0b1011,
'M'=>0b1100,
'N'=>0b1101,
'O'=>0b1110,
'P'=>0b1111}
UnitCodes = {
'1'=>0b0000,
'2'=>0b0001,
'3'=>0b0010,
'4'=>0b0011,
'5'=>0b0100,
'6'=>0b0101,
'7'=>0b0110,
'8'=>0b0111,
'9'=>0b1000,
'10'=>0b1001,
'11'=>0b1010,
'12'=>0b1011,
'13'=>0b1100,
'14'=>0b1101,
'15'=>0b1110,
'16'=>0b1111}
CommandFunctions = {
0x00=>'ALL UNIT OFF',
0x01=>'ALL LTS ON',
0x02=>'ON #',
0x03=>'OFF #',
0x04=>'DIM *#',
0x05=>'BRIGHT *#',
0x06=>'ALL LIGHT OFF',
0x07=>'ALL USER LTS ON',
0x08=>'ALL USER UNIT OFF',
0x09=>'ALL USER LIGHT OFF',
0x0A=>'BLINK *#',
0x0B=>'FADE STOP #',
0x0C=>'PRESETDIM *#',
0x0D=>'STATUS ON *',
0x0E=>'STATUS OFF',
0x0F=>'STATUS REQ',
0x10=>'(R)MASTER ADDRS SETUP *#',
0x11=>'(T)MASTER ADDRS SETUP *#',
0x12=>'SCENES ADDRS SETUP *',
0x13=>'SCENES ADDRS ERASE',
0x14=>'ALL SCENES ADDRS ERASE *#',
0x15=>'FOR FUTURE*',
0x16=>'FOR FUTURE*',
0x17=>'FOR FUTURE*',
0x18=>'GET SIGNAL STRENGTH #',
0x19=>'GET NOISE STRENGTH #',
0x1A=>'REPORT SIGNAL STRENGTH *',
0x1B=>'REPORT NOISE STRENGTH *',
0x1C=>'GET ALL ID PULSE',
0x1D=>'GET ONLYON ID PULSE',
0x1E=>'REPORT ALL ID PULSE',
0x1F=>'REPORT ONLY ON PULSE'}
def initialize()
@usercode = 0
@homeunit = 0
@command = 0
@data1 = 0
@data2 = 0
end
def command=(value)
@command = value
end
def command
return HomeCodes[@command]
end
def usercode=(value)
@usercode = value
end
def usercode
return @usercode
end
def homeunit=(value)
@homeunit = HomeCode[value[0].chr]
@homeunit += UnitCode[value[1].chr]
end
def homeunit
return @homeunit
end
def data1=(value)
@data1 = value
end
def data1
return @data1
end
def data2=(value)
@data2 = value
end
def data2
return @data2
end
def data
return (@usercode.chr @homeunit.chr + @command.chr + @data1.chr + @data2.chr)
end
def length
return data.length
end
def packet
return STX + length.chr + data + ETX
end
end
#Command Class
class RubyCommand
# Constants:
CommandType=['Command','Event']
Status=['Initializing', 'Sending Command', 'Waiting for Command ACK', 'Waiting for Response',
'Receiving Response', 'Processing Response', 'Sending Response', 'Complete', 'Error']
CommandDirection=['DCEtoGSD', 'GSDtoDCE'] #Direction of Command
DCEtoGSD = 0
GSDtoDCE = 1
def initialize(from, to, priority, cmdtype, command)
#initialize here
@from = from
@to = to
@priority = priority
@direction = CommandDirection[DCEtoGSD]
@command = command
@commandtype = CommandType[0]
@processing = false
@cmdsent = false
@cmdcomplete = false
@devdata = {}
@device = Device.new(to)
@status = Status[0]
end
def device=(deviceid)
@device = Devices(deviceid)
end
def cmdSent
return @cmdsent
end
def cmdComplete
#Boolean: Flag Set when Command has completed.
return @cmdcomplete
end
def dcecommand=(value)
@command = value
end
def dcecommand
return @command
end
def processing
return @processing
end
def gsdCommandOut
return @gsdcommand
end
def devdata(pkparam)
return @devdata[pkparam]
end
def setdevdata(pkparam,value)
@devdata[pkparam]= value
end
def commandtype
return @commandtype
end
def commandtype=(commandtype, default = 'Command')
if commandtype == 'Command'
@commandtype = CommandType[0]
end
if commandtype == 'Event'
@commandtype = CommandType[1]
end
end
end
class RubyCommands
def initialize
@commands = []
end
def add(command)
#@cmd = Command.new(command)
@commands << command
end
def remove
@commands.delete_at(0)
end
def execute
end
def nitems
return @commands.nitems
end
end
class Device
def initialize(devid)
@devdata = {}
@devid = devid
@template = 0
@parent = 0
end
def template
return @template
end
def template=(templateid)
@template = templateid.to_i
end
def parent
return @parentid
end
def parent=(parentid)
@parentid=parentid.to_i
end
def setdevdata(pkparam, value)
@devdata[pkparam] = value.to_s
end
def devdata(pkparam)
return @devdata[pkparam]
end
def state
return @state
end
def configuration
return @configuration
end
def configuration=(value)
@configuration = value
end
def devid
return @devid
end
end
class Devices
def initialize
@devices = {}
device_.childdevices_.each_key{|c|
dev = Device.new(c)
dev.devdata[12] =device_.childdevices_[c].devdata_[12].chomp.lstrip.rstrip
dev.template = device_.childdevices_[c].devtemplid_
dev.parent = device_.childdevices_[c].parent_
add(c,dev)
}
end
def add(device)
@devices[device.devid] = device
end
def remove(devid)
@devices.remove[devid]
end
end
#Test Code Below:
cmd = RubyCommand.new(-1000, 1, 1, 1, 184)
cmd2 = RubyCommand.new(-1000, 1, 1, 1, 192)
cmd.setdevdata(74, '100')
cmd2.setdevdata(74, '200')
cmd2.commandtype = 'Event'
puts cmd.devdata(74)
puts cmd.devdata(100)
cmd.setdevdata(50,'hello')
puts cmd.inspect
puts cmd.dcecommand
puts "Hello"
cmds = RubyCommands.new()
cmds.add(cmd)
puts cmds.inspect
cmds.add(cmd2)
puts cmds.inspect
#Show Queue Length
puts cmds.nitems
#Remove a command from the queue
cmds.remove
puts cmds.inspect
plc = PLCBUS.new
puts plc.HomeCodes.inspect
the "#" and "*" are not part of the commands.
best regards,
Hari
Heh, haven't got that far yet... but yeah, I know...
just trying to get the basics down.. Still have a few parts of the spec to add..
Do you see what I'm trying to do here? versus the 'Structured' method of:
1. receiving a command from DCE
2. processing the command
3. sending it out via GSD
4. waiting for an ACK
5. waiting for a Response
6. making sure the response is from the command sent
7. Processing the Response
8. sending the appropriate response back to DCE
At any one of those steps, exceptions happen.. and, each command could potentially have a different 'logical flow'
Add to that, (at least with the Insteon) in some circumstances, I needed to receive 2 response messages for a said command.. 1 from the PLM, and 1 from the Device..
If this method works better, I may rewrite the Insteon to use this method.
Regards,
Dan
Status update:
Haven't done much more.. added routines for Process Command for Child and Process Incoming Data..
Both of those routines will directly SET or call methods in a command object in the commands hash.
If the current command[0] (the first command in the hash) has a flag saying it's waiting for a response, AND the direction is proper, then the data is considered a response. Otherwise, it's considered a command, and a new command object is created.
This procedure works BOTH ways.. the only difference is the DIRECTION flag, which is either DCEtoGSD or GSDtoDCE
Once the command object has been populated with the data (either Command data or response data) it will automatically fire and complete the process, communicating, if needed, to the other side, and setting it's flags as required.
Neat eh?
This allows me to build all the 'Standard logic' into the Command class, while keeping all the 'Custom PLCBUS logic' inside the PLCBUS class. (which, by the way, is also included in the command object)
This technique SHOULD allow me to 'pull' out the PLCBUS class, and add any other HA standard..
As always, I'm open to suggestions...
Regards,
Dan