How to add your own GSD device
From LinuxMCE wiki
Generic_Serial_Device (also known as GSD) is a LinuxMCE device that allows an end-user to do simple programming for RS232, serial USB or network connected devices. This enables them to quickly and easily write drivers for such devices.
Note, however, that a GSD device driver is more limited than a compiled driver. The GSD driver allows only a single communication channel with the device (RS232, serial USB or a TCP connection across a network). If your device has other communication channels (an UDP connection for example), you will have to write a driver in another language and compile it.
It is also possible to use a GSD device to send DCE commands to other devices. See for instance the Toshiba RAS series template, which acts as a sort of message translator, translating DCE thermostat command to IR commands sent through a IR transmitter.
How to add your own GSD device
For those of you who like to learn by example, there are some already in the LinuxMCE database. In the LinuxMCE web admin interface go to Advanced-->Configuration-->Device Templates and pick one of the following device templates:
- Panasonic IP camera: A simple driver that enables the retrieval of video frames from this network enabled camera
- Proliphix NT series thermostat: A more elaborate driver for a network enabled thermostat. This driver also sends events to the LMCE system in case, for example, the ambient temperature changes.
- Toshiba RAS series air condition: Another kind of GSD device, just receiving and sending DCE commands
Creating a new GSD device
The steps to add GSD device are following:
Install the necessary packages
Be sure that you have installed GSD package -
dpkg -l 'pluto-generic-serial-device'
(it should be there). This shouldn't normally be necessary as LMCE should automatically install the package when you have added such a device.
Create a device template
Created a device template for your system and specify that it should use GSD. The Edit_Device_Template page may be of help here.
Some things you should take into account:
- Add your template under the proper category; add it under category lighting interfaces if you want to control lighting devices only. To control all kind of devices (security, climate etc) it's better to use Interface::Specialize.
- Specify a communication type - RS232, Ethernet etc. and do not forget to specify the way to connect to the device, i.e.:
- For an Ethernet device, add the TCP Port (#69) to the device data.
- Specify that your device is controlled via category "Device Category:Core" ("Computers - Core"). Otherwise the driver will not be started on the core.
Furthermore you should add the commands implemented by your device, and the events it can generate. If possible, try to fill in the Plug & Play section to ensure that your device will be automatically detected. Also, fill in the comment fields provide others with information about your device template.
Adding the device
If you have added Plug & Play information to your device template, a quick reload of the router, followed by the connection of your device will result in the device being detected and added automatically.
If auto-detection is not possible, however, you will have to add your device manually. This is done on the GSD page: Wizard --> Devices --> Generic Serial Devices.
After installing the device, and a quick reload of your router you should see GSD process in the console:
ps -elf|grep Generic_Serial_Device
5 S root 10085 1 0 76 0 - 683 - Jul17 ? 00:00:00 SCREEN -d -m -S LinCon_8000-30 /usr/pluto/bin/Spawn_Device.sh 30 localhost Generic_Serial_Device 5 S root 10086 10085 0 75 0 - 646 wait Jul17 pts/8 00:00:00 /bin/bash /usr/pluto/bin/Spawn_Device.sh 30 localhost Generic_Serial_Device 0 S root 31800 10086 0 79 0 - 123146 295621 Jul17 pts/8 00:00:00 ./Generic_Serial_Device -d 30 -r localhost -l /var/log/pluto/30_Generic_Serial_Device.log
This tells you that a Generic_Serial_Device driver is running. The line containing "./Generic_Serial_Device" shows you the process is running and that this instance of the driver has been given device id 30, and that the logging will be written to the file "/var/log/pluto/30_Generic_Serial_Device.log". In this log you can find all Ruby errors, connection status and other useful information.
Adding the Ruby code
From the Device Template page and from the GSD page you can edit the Ruby codes for your device. There you can write the actual code for the methods implemented by your device.
You should implement the commands you selected for your device, and you should also implement the "Ruby Internal Commands". These commands consist of the following 6 functions:
- Private Method Listing
- This is not really a function in itself, but rather a section in your Ruby script where you may define common functions that are used in the rest of your driver. A good example would be a function to write a line to the device's log file:
def log(line) # This function logs a line to the log file of the device log = File.open("/var/log/pluto/" + device_.devid_.to_s + "_Generic_Serial_Device.log", "a") log.puts Time.now.to_s + " (Ruby script):" + line.to_s log.close end
- Process IDLE
- This function is executed each one - two seconds. It may be useful to check for a connection between your device and LinuxMCE, or to poll your device for updates.
- See the Proliphix NT series thermostat driver for an example of polling, where the time between polls is reduced to once every 5 minutes, instead of once every few seconds.
- Process Incoming Data
- This function is called when some data comes from your device:
recv = conn_.Recv(100,500);
- recv contains now the data received from the physical device (say some switch is OFF). You can parse it to update a status of that switch in LinuxMCE:
cmd = Command.new(device_.devid_, -1001, 1, 2, 48) cmd.params_ = 0 SendCommand(cmd)
- Process Receive Command For Child
- if your device is a parent device for some other devices, this function is called when a command is send to one of the child devices.
- This function should therefore forward the command to it's children. For example, a GSD device that controls dimmer or switches might receive a some command like: ON, OFF or SET LEVEL for one of it's children. It should then send these commands in the desired format to the physical device:
cmdId = cmd.id_ # Command ID: ON, OFF, SET LEVEL cmdTo = cmd.devidto_ # Device ID in LinuxMCE devPort = device_.childdevices_[cmdTo].devdata_ # 12 contains a port/channel childType = device_.childdevices_[cmdTo].devtemplid_ # Template ID to know type of device: switch or dimmer case cmdId when 192 #192 is ON command = '<your ON command format>' when 193 #193 is OFF command = '<your OFF command format>' when 184 #184 is Level of dimmer command = '<your SET LEVEL command format>' end conn_.Send(command)
- In this example the command includes following parameters: cmdTo - the LinuxMCE ID of the child device - to update the status in LinuxMCE, devPort - Port Number - to know where send actual command, cmdId - command type - ON, OFF, SET LEVEL, level value for SET LEVEL. It might be good idea to push unsuccessful commands into array in the GSD device to run them once again.
- NOTE: From trial and error, I have come to the assumption that ON/OFF and SETLEVEL, internally, have seperate values. Be careful as you can have a device that is OFF with a setlevel of 100%. This results in a pretty UGLY message when to attempt the following: Use cmd=100 to turn a device ON. (but don't actually send a cmd.. when you attempt to turn the device OFF, you'll get an ugly message in the GSD device saying that the 'CMD won't be processed because it is useless'. (I'm paraphrasing.. you get the idea...) What makes this worse, is when you use an orbiter to turn a device on, it will send you a cmd, not a cmd. You will need to watch for this and send a cmd as needed.
- NOTE FOR ABOVE: I have found a way to force the command through. a bit of history first:
The reason we get the infamous 'won't be processed because it is useless' is because DCE has to deal with NON Discrete codes. for instance: a device that has a power toggle. IF we turn it on, it turns on. Now, if we turn it ON again, DCE thinks it's on and because it's a toggle, it won't send the command. That is why we get this message. I dug pretty deep to find this next piece: params=1 this parameter, when set inside a command, will FORCE DCERouter to send the command regardless of what it thinks the state is. Hope this helps other GSD programmers. DDamron
- Process Initialize
- This function is called when GSD device is starting. It's good point to initialize common variables. It is also a good place get the actual status of your device and notify LinuxMCE of it, since the state might have changed while the GSD driver was down.
- Process Release
- This function is called when the GSD device is closing.
Note: LinuxMCE caches your Ruby code, so you should do a quick reload of your router each time when you modify your Ruby code!
Other Ruby code examples
Sending an event
To send an event to the LinuxMCE system you can use the following snippet:
cmd = Command.new(device_.devid_, -1001, 1, 2, 25) cmd.params_ = 20 SendCommand(cmd)
This will send the command from your device (device_.devid_) to the LinuxMCE event handler (-1001), identifying it as a priority 1 message, of the event type (2). Furthermore the event has id 25, which means it's a "Temperature Changed" event (the device template page shows the ids of the events you have added to the device).
Additional parameters of the event can be set by using the cmd.params_ mapping. The device template page has an edit button for each event that enables you to see the parameters associated with that event, including a description of its use. In our example it shows that the parameter with index 30 is the current ambient temperature in degrees Celcius.
After the cmd object has been initialized it is send to the LinuxMCE system using the SendCommand function.
Note that the same piece of code can be used to send commands instead of events. The only changes are that the message type will be 1 (command) instead of 2 (event), and that the destination will probably differ from the event handler device -1001.
Note that the GSD framework currently does not allow you to get any return(out) parameters from commands.
This section contains some tips on troubleshooting your new driver.
Viewing the logs
The best way to test the new GSD device is to edit the file /etc/pluto.conf and comment out the LogLevels filter, so the logging is verbose. You can do this by typing:
Then move to the start of the line that starts with LogLevels, press 'i' for Insert mode in vim, type a # character, then press [ESC]:wq The # at the start of a line means it's commented out, or ignored.
now do a reload router. You can do this from the console with: /usr/pluto/bin/MessageSend dcerouter 0 -1000 7 1
Then go to the log directory:
ls will list the contents of the directory: There will be a log file that starts with the device number of the gsd device. To follow it type:
tail -f [filename]
rather than typing in the full filename, you can just type the first letters (ie the device number) and press tab, which does an auto complete.
Now send commands to the device. You can do this through the web site or the console. For example, assuming your GSD device has the device id 57, you can send it an ON command (ON is command #192), you could type: /usr/pluto/bin/MessageSend dcerouter 0 57 1 192
The log will show a bunch of data. The lines that start with 40 and 41 are the serial data that is being sent to/from the device. To filter the tail so it only shows the serial data, type:
tail -f [filename] | grep '^40\|^41'
Getting the device to start
If your device will not start (anymore) as described in section How to add your own GSD device#Adding the device there can be several causes.
Check that your device is controlled by the core, as described in How to add your own GSD device#Create a device template. Otherwise the core assumes another device will start the driver.
If your Ruby code contains an error, LinuxMCE may decide to disable the device. So, check that your device is not disabled: on Wizard-->Devices-->Generic Serial Devices click on "Advanced" for your device and make sure the Disabled box is NOT ticked. If it is, untick it, save, and do a quick reload of the router.
Sometimes a quick reload of the router does not suffice. In that case you can run:
This should start your device. If not, the output of that script is a good place to look for further clues.
Sharing your driver
Once your driver is working properly, you can share it with the rest of the LinuxMCE community. The procedure for this is as follows:
- Do an sqlCVS update from the web-admin to ensure that your database is up-to-date (Advanced --> sqlCVS --> Update, select all sections)
- If it just returns without any reply, you're most likelly being tricked by your browser's pop-up blocker.
- Create a Trac ticked describing your device (http://svn.linuxmce.org)
- Do a check-in of your changes: Use the sqlCVS diff function (Advanced --> sqlCVS --> Diff) to determine the updates to the "dce" and "ir" sections of your database and check them in anonymously. The comment of your check-in should mention the Trac ticket number. You should only check in the items related to your device template.
- The developers will verify this and check the driver into the database