Growl
I was going to write myself a desktop notification user control. I was planning it to be a simple window popping up when an event occurred. Before investing time and effort into this, I decided to look around to find a similar project and build on it. Unfortunately I couldn’t find something to my liking but discovered Growl. It has all feature you might expect from desktop notification tool. One additional feature that pleasingly surprised me is that you can send notifications to another machine over the network. This sounds good to me as I’m working on running my applications on Raspberry Pi using Mono lately. So the idea is to run the program on my Pi and receive the notifications on my desktop where I spend most of my time. Another benefit of Growl is that it is open-source which can be found here: https://code.google.com/p/growl-for-windows/
I don’t like my programs to be dependent on some external software that needs to be installed on the client machine but I thought this could be optional because desktop notifications can be one channel for communications and others can be added if necessary. So it is not a dependency but rather it enhancement in functionality. Also a pitfall in software development is the anti-pattern described as Not Invented Here. One simply cannot develop every piece of software needed to build complex systems. It’s not feasible. Of course when I write code on my own, my main goal is to learn something new but still I like to get results and produce working software. So best practices for commercial software still apply. Having convinced myself to use Growl for messaging I started looking for ways integrate it with my application. It comes with .NET SDK which is quite easy to use.
There are two assemblies need to be referred to:
- Growl.CoreLibrary.dll
- Growl.Connector.dll
The interesting bits are in Growl.Connector library. First you need to create an instance of GrowlConnectorclass. You can specify the remote hostname and password to send the notifications over the network which is what I wanted to do.
this.growl = new GrowlConnector("password", "192.168.1.64", GrowlConnector.TCP_PORT);
Next, you have to register the application. If it is not registered, Growl will discard notifications coming from this source
this.application = new Growl.Connector.Application("Test notifier from ROHAN");
this.notificationType = new NotificationType(sampleNotificationType, "Sample Notification");
this.growl.Register(this.application, new NotificationType[] { notificationType });
Final step is to enable notifications over the network. By default it only accepts messages from the local machine.
After the setup is completed we can send a test notification by this piece of simple code:
string text = string.Format("DateTime: {0}", DateTime.Now.ToString("dd/MM/yyyy HH:mm"));
Notification notification = new Notification(this.application.Name, this.notificationType.Name, DateTime.Now.Ticks.ToString(), "Mmessage from ROHAN", text);
this.growl.Notify(notification);
And the result is:
So far so good. With only a few lines of code we managed to send a desktop notification over the network. We could specify a callback method to handle responses from the Growl host. We could also specify the encryption algorithm to enhance security. Now the last thing to test for me is to see it running on Raspberry Pi. To do that I created a sample console application that looks like this:
using System;
using Growl.Connector;
namespace MonoWorkout
{
class MainClass
{
public static void Main(string[] args)
{
GrowlConnector growl = new GrowlConnector("password", "192.168.1.64", GrowlConnector.TCP_PORT);
growl.EncryptionAlgorithm = Cryptography.SymmetricAlgorithmType.PlainText;
Growl.Connector.Application application = new Application("Test notifier from Raspberry Pi");
NotificationType notificationType = new NotificationType("SAMPLE_NOTIFICATION", "Sample Notification");
growl.Register(application, new NotificationType[] { notificationType });
Console.WriteLine("Type message to generate notification");
string message = string.Empty;
while ((message = Console.ReadLine()) != null)
{
if (message == "q")
{
Console.WriteLine("Quitting program");
break;
}
string text = string.Format("DateTime: {0} \t Message: {1}", DateTime.Now.ToString("dd/MM/yyyy HH:mm"), message);
Notification notification = new Notification(application.Name, notificationType.Name, DateTime.Now.Ticks.ToString(), "Message from Raspberry Pi", text);
growl.Notify(notification);
Console.WriteLine("Notification sent");
}
}
}
}
I ran the application but did not receive the results. I immediately ran WireShark and could see the packages coming to my desktop machine so it is not a network or firewall issue. After Googling a little bit I’ve found that there is Mono branch in the source code. I downloaded it and replaced the binaries with their Mono counterparts. Tested it again but to no avail.
When I sent the message I can clearly see it in wireshark but I don’t know why Growl is rejecting them. I ran the same application in both a Windows 7 instance and Raspberry Pi and captured the message packets. Outcome is interesting:
Here’s the message sent from Windows:
GNTP/1.0 NOTIFY NONE MD5:C5FB01D47A56832A17B3F941BC6F327F.3ECBA79D164DA5F8
Application-Name: Test notifier from Raspberry Pi
Notification-Name: SAMPLE_NOTIFICATION
Notification-ID: 634982701932496447
Notification-Title: Message from Raspberry Pi
Notification-Text: DateTime: 07/03/2013 16:23 Message: TEST_ROHAN
Notification-Sticky: No
Notification-Priority: 0
Notification-Coalescing-ID:
Origin-Machine-Name: ROHAN
Origin-Software-Name: GrowlConnector
Origin-Software-Version: 2.0.0.0
Origin-Platform-Name: Microsoft Windows NT 6.1.7601 Service Pack 1
Origin-Platform-Version: 6.1.7601.65536
</pre>
And this is the one coming from Raspberry Pi:
<pre name="code" class="c-sharp:nocontrols">
0'_`E{@P@ZTg<
DGNTP/1.0 NOTIFY NONE MD5:45DF50ED8E166AE3AF39F0FEFFC36F5D.6EC74DE6BCD67A71
Application-Name: Test notifier from Raspberry Pi
Notification-Name: SAMPLE_NOTIFICATION
Notification-ID: 634982703645630380
Notification-Title: Message from Raspberry Pi
Notification-Text: DateTime: 07/03/2013 16:26 Message: TEST_RASPBERRYPI
Notification-Sticky: No
Notification-Priority: 0
Notification-Coalescing-ID:
Origin-Machine-Name: raspberrypi
Origin-Software-Name: GrowlConnector
Origin-Software-Version: 2.0.0.0
Origin-Platform-Name: Unix 3.1.9.0
Origin-Platform-Version: 3.1.9.0
There is a 16 byte block at the beginning and I believe because of that Growl cannot parse the message therefore end up discarding it.
At this point, I’ll shelf this problem and look for alternative solutions. I hate leaving a problem unsolved like this but it is not a crucial feature so I’d rather not invest too much time into it. So for now my official opinion is, despite the Mono branch in the SVN, I don’t think Growl supports Mono.