Slack → Azure → My Keyboard

Sometimes I just need to pursue a meaningless, over the top project because it just won't leave my head. There have been a lot of fun changes in my professional life lately. I'm getting much more into Azure, I just got a bunch of new hardware to play around with, and I just started a new project that I have more control over.

Backstory

In this new project, I decided to incorporate Slack for communication and team building. So far, it has been working out really well. Slack has been fantastic and I'm impressed with how much of a polished service it really is.

With every new project, I like to give my workspace a reboot to clean my mind and my desk of old work and thoughts. I recently purchased a bunch of hardware. One of those items was the new Razer Chroma Blackwidow Ultimate. Yes, I'm a keyboard snob and I'm starting to realize that I'm a very loyal Razer customer. Anyway, this keyboard is far more amazing than I originally thought. Aside from definitely increase in typing satisfaction (that's a thing), the level of customization on the keyboard lighting is just incredible.

I even found out that there is an SDK from Razer for customizing your colors in C# code. I played around with it a bit. My first project on that was to make a visible heat map. It kept track of how many times you pressed each key and would glow the keys from dark to light based on how often you pressed it.

Crazy Idea

All this change got me thinking about crazy ideas that serve no strong purpose but they definitely have an awesome factor. My idea was to create a Slack bot that my team could communicate with to change the styles of my keyboard. This presents many challenges and has no real benefit other than to see if it works. This is my kind of project!

Architecture

This is a strange problem because I need something very public to speak to my private computer and tell my personal keyboard to light up the way the user wanted it to. I knew that I was not going to be able open up ports on my work machine just for this crazy idea. Therefore, I knew I had to involve Azure. This is great because you learn these new technologies the best when you have a real world project. That's right; I just said remotely configuring the colors on my keyboard was a real world project.

Let's talk about the pieces.

Slack Slash Command

Like I said earlier, Slack has been fantastic for me and it's mostly because their documentation is helpful. Nothing ever goes as planned and I appreciate that Slack takes the time to explain how to troubleshoot problems and help me understand how the system operates. They have great support for integrations. After reviewing their documentation, I determined that a Slash Command is the integration that I wanted. It is just a command like /chroma [text] that just sends an HTTP GET or POST to a URL. Below, is the data of the request that Slack sends to the configured URL:

{
  "token": "gIkuvaNzQIHg97ATvDxqgjtO",
  "team_id": "T0001",
  "team_domain": "example",
  "channel_id": "C2147483705",
  "channel_name": "test",
  "user_id": "U2147483697",
  "user_name": "Steve",
  "command": "/weather",
  "text: "94070",
  "response_url": "https://hooks.slack.com/commands/1234/5678"
}

This tells me that I need to create an endpoint that can accept this JSON and read the important bits of this like the text property.

On top of that, I can even respond back to the person asking to change the style and tell them information about their request if I write it in the format of:

{
    "response_type": "in_channel",
    "text": "It's 80 degrees right now.",
    "attachments": [
        {
            "text":"Partly cloudy today and tomorrow"
        }
    ]
}

Azure API App

I knew nothing about API Apps before starting this project. I looked at a few Azure Friday videos from Scott Hanselman and determined that an API App is probably the way to go to create a simple, public endpoint to handle the type of request that Slack will be making.

Creating an API App was simple. I started up Visual Studio 2015, created a new ASP.NET Website and selected the API App option that is currently in preview. From there it just acts as an ASP.NET Web API project. I'm pretty familiar with those, so I got started.

First, I created a new ApiController for my StyleController. This controller will just have one method in it. It will have a method named "Post". By convention, the Web API controller will take that as a post verb so that the route of \localhost\api\style and a POST verb will take me right to my code.

Here is the start of that method:

public SlackResponse Post(SlackMessage message)
{
    var style = _styleParser.Parse(message.text);

    ...
}

As you can see on the first line of the method, I was faced with my first real problem. How do I take a string and translate it into a bunch of commands to my keyboard? I determined that I should just create my own grammar for how the user will communicate with the keyboard. I know that the Razer SDK supports many different effects, so I converted those effects and their parameters to English and came up with this set.

  • WAVE right
  • CYCLE
  • PULSE #FF0000
  • REACTIVE #00FF00
  • STATIC #FF00FF

In addition to those, I wanted to add a few of my own that I could manage in code

  • CLEAR
  • WAIT 2000
  • SPELL word

Each of these is written in sentence form to send commands in Slack like this:

/chroma wave right THEN wait 2000 THEN static #FF0000

Then my code could interpret each command by the THEN keyword and parse each command into something usable by my keyboard. To do this I just made a parser that takes the string and converts it into a style structure that I can use. Here are the structures I parsed the string into:

public class ChromaStyle
{
    public StyleCommand[] Commands { get; set; }
}

public class StyleCommand
{
    public ChromaAction Action { get; set; }
    public string Parameter { get; set; }
    public Color Color { get; set; }
}

public enum ChromaAction
{
    Pulse = 0,
    Clear,
    Spell,
    Wait,
    Reactive,
    Static,
    Wave,
    Cycle
}

Once I had a usable format, I needed to send it to my local computer that has the Razer Chroma keyboard. To do this I had to add another layer to my project so that my local computer could request the style rather than be told the style. Doing it this way addressed my security concerns of having to open my local machine up to the public web.

Azure Service Bus Queue

I decided to use an Azure Service Bus Queue because it seemed to make the most sense to me. I believe I also could have used an event hub but it seemed like the event hub was for more throughput than I really need. The queue serves a simple purpose for me. I just needed a mailbox. Something that the API App could drop a style in the mailbox and an application running on my computer could pull that off of the mailbox and process the style.

Setting up the queue was even simpler than the API App. I just created it from the Azure Portal with a Sender and a Reciever role and saved off those connection strings so that my sender and receiver applications could use them.

To get going with an Azure Service Bus Queue, just go download the latest Azure Service Bus NuGet package. From there you can write some simple code to create a queue client and send that message to the queue. You have the option to send it Async as well.

public SlackResponse Post(SlackMessage message)
{
    var style = _styleParser.Parse(message.text);

    var queue = QueueClient.CreateFromConnectionString(
        QueueConnectionString,
        "ChromaCommandQueue");
    queue.Send(new BrokeredMessage(style));

    return GetSlackResponse(style);
}

Chroma Client

Now I could create a simple client to get those messages of the queue and change the style of my keyboard. To interface with my keyboard, I'm using the Colore library which is a C# wrapper around a C++ Razer Chroma SDK.

I created a console application that simply waits for messages on the queue and processes them. Here's some of that code.

static void Main(string[] args)
{
    _keyboard = Corale.Colore.Core.Keyboard.Instance;
    var queue = QueueClient.CreateFromConnectionString(
        connectionString,
        "ChromaCommandQueue",
        ReceiveMode.ReceiveAndDelete);

    while (true)
    {
        var message = queue.Receive();
        if (message == null)
            continue;

        _keyboard.Clear();
        var style = message.GetBody<ChromaStyle>();
        foreach (var command in style.Commands)
        {
            StyleKeyboard(command);
        }
    }
}

I'm using the Receiver role connection string that I set up in Azure when I create the queue client. When I do that, it will sit and wait for a bit until it can find a message and eventually give up returning null. In my case, I just want to ask again until it finds one.

Once it does find a message, it's time to start using the Razer Chroma SDK and update my keyboard. Here is some of that Razer Chroma code that interprets my ChromaStyle class into actual keyboard effects.

private static void StyleKeyboard(StyleCommand command)
{
    switch (command.Action)
    {
        case ChromaAction.Pulse:
            StylePulse(command.Color);
            break;
        case ChromaAction.Wave:
            StyleWave(command.Parameter);
            break;
        ...
    }
}

private static void StylePulse(Color color)
{
    var pulseColor = new Corale.Colore.Core.Color(
        color.R,
        color.G,
        color.B,
        color.A);

    _keyboard.SetBreathing(pulseColor, pulseColor);
}

private static void StyleWave(string direction)
{
    if (direction.ToLower() == "left")
        _keyboard.SetWave(Direction.RightToLeft);
    else
        _keyboard.SetWave(Direction.LeftToRight);
}

This implementation is certainly not complete. I'd love to have it support more effects such as only pulsing/rippling/reacting to specific keys. Things like that and many more are now pretty simple to implement now that I have the architecture nailed down.

Conclusion

Looking back in the Slack channel, here is what it showed to the user:

This was a crazy idea and it really serves no purpose other than to show that it can be done. It'll be funny when my team members spell out things on my keyboard from their desks or even their phones. Sometimes you just need to play out crazy ideas and you definitely grow as a developer when you pull them off. Now I'm comfortable in several areas of Azure, and even creating custom commands in Slack all because of this crazy idea.

All the source code is on on my chroma-commands GitHub repository.

Do you see any improvements I can make on the implementation?