Monday, October 27, 2008

skype + dbus + mono

Once again, in effort to create yet another plugin for nGhost I've tread where no man has trodden before, skype + dbus + mono. Once again, because ndesk.dbus has excellent support for dbus, c# was chosen.

The skype API is a set of string commands that you send to skype to do various functions. You can do a lot through the API including sending SMS messages.

At the time of writing this, the dbus documentation for skype was definitely lacking. I was particularly confused on whether the method "Notify" was a dbus-signal, or a service that I had to implement myself. It ended up being the latter. Here are the interfaces:


[Interface("com.Skype.API")]
public interface SkypeSend: Introspectable
{
string Invoke(string message);
}

[Interface("com.Skype.API.Client")]
public interface SkypeResponse: Introspectable
{
void Notify(string message);
}


The "Invoke" method is for Client to Skype communication. Answers to queries are returned back from "Invoke"

Notify, as discussed earlier, is for Skype to client messages. Messages such as Call ... Ringing, or Chat messages are received here. This is a dbus service you have to register yourself using the same dbus connection as you created for "Invoke".

Here is the classes that implement the interfaces:


public class nSkypeDbus
{
public nSkypeDbus()
{
init();
}

public void init()
{
dbus = Bus.Session;
skype = dbus.GetObject("com.Skype.API", new ObjectPath("/com/Skype"));
send("NAME nskype");
send("PROTOCOL 7");
}

public string send(string message)
{
return skype.Invoke(message);
}

private SkypeSend skype;
public Connection dbus;
}


And the implementation of the Client interface:

public class SkypeClient: SkypeResponse
{
private Connection dbus;

public SkypeClient(Connection d)
{
dbus = d;

dbus.Register("com.Skype.API",new ObjectPath("/com/Skype/Client"), this);
NotifyEvent += new NotifyEventHandler(InterpretNotify);
}

public void Notify(string message)
{
System.Console.WriteLine(message);
if(NotifyEvent!=null)
NotifyEvent(message);
}

public void loop()
{
dbus.Iterate();
}


public string Introspect()
{
return "\n \n \n \n \n\n";
}

public void InterpretNotify(string message)
{
if(message.Contains("STATUS RINGING"))
///sample message: CALL 1412 STATUS RINGING
{
if(IncomingCall!=null)
{
string [] p = message.Split(' ');
string caller = p[1];
IncomingCall(this, caller);
}
}
}
public event NotifyEventHandler NotifyEvent;
public event IncomingCallHandler IncomingCall;
}

In the Client implementation, I am trying to catch incoming call notifications.

There's plenty of stuff you can do with skype. Here's the link to the API: http://share.skype.com/sites/devzone/2006/01/api_reference_for_skype_20_bet.html

Hope to see more skype goodies for Linux....

Friday, October 24, 2008

GPS (gpsd) Mono C#

While working on the thing that keeps me most busy (see http://openice.org), I often find myself doing things, going places, and accomplishing things that few other people (if any) have. I'm no expert at any of it, I've just got ideas that need testing...

I decided to make a plugin for nGhost that reports speed, altitude, and bearing. To do this, I decided to see if I could tap into gpsd's dbus interface.

Gpsd emits a dbus-signal every time its data changes (afaik). All one would have to do is register to receive that siganl. Since mono (http://mono-project.org) has fantastic dbus bindings for c#, I went with ndesk.dbus (dbus-sharp) to see if I could do it. Here is the code:


public class GPS
{
private Connection DbusConnection;
public Gpsd gps;
public GPS()
{
DbusConnection = Bus.System;
gps = DbusConnection.GetObject("org.gpsd", new ObjectPath("/org/gpsd"));
}

public void Loop()
{
DbusConnection.Iterate();
}

}

public struct GPSFix
{
public double time;
public int mode;
public double ept;
public double latitude;
public double longitude;
public double eph;
public double altitude;
public double epv;
public double track;
public double epd;
public double speed;
public double eps;
public double climb;
public double epc;
}

public delegate void GPSPositionChangedHandler(GPSFix fix);

[Interface("org.gpsd")]
public interface Gpsd
{
event GPSPositionChangedHandler fix;
}


This is the final interface after quite some time debugging. The main thing to note is that the interface's methods have to match the name of the signal being fired. In our case, "fix".

To tie it in, all you have to do is:


public void OnNewFix(GPSFix fix)
{
///do something with new fix.
}
GPS gps = new GPS();

gps.fix += new GPSPositionChangedHandler(OnNewFix);

///enter main loop...


That's it!