Menu

I recently purchased a few Philips Hue lights. My goal is to create a small kind of home automation with the lights. Automation capabilities are good and easily configurable with Philips Hue mobile app. I added a few lights to the kids room and they can now change the colors of the lights easily with Philips Hue mobile app. Kids really likes this.

Now I started to think about what else could I do with the lights. I got an idea that I will create some automation to notify family when I’m in the meeting. I think it would be a good idea to notify family by the colors of the light (red) when I’m working at home and in the meeting.

This blog post shows how I implemented a Slack Event listener which changes to kids room light color based on my status in Slack. Later when MS Teams Presence API is available then I implement this to work also with MS Teams.

About Philips Hue and Slack APIs

Philips and Slack have good API documentation and resources for developers.

I didn't want to create a wheel again so I used already implemented API wrappers for .NET platform. I used Q42.HueApi library for Philips Hue API communication and SlackNet library for handling Slack API connection.

Implementation

I created two .NET Core console applications:

Slack Event Listener App listens user status change events from Slack. If status indicates that I’m in the meeting then App changes light colors to red by using Philips Hue APIs.

undefined

undefined

Hue App Registration registers your application to Hue Bridge. After registration you got an application key value which is used in the Slack Event Listener app. Q42.HueApi library allows you also to use Philips Remote APIs. In this example I will use local network communication to Philips Hue Bridge.

Before starting

I use in this sample Slack legacy tokens even though they are not the current best practice. Maybe later I refactor this solution to use OAuth and Slack Apps to get tokens. Slack highly recommends that legacy tokens are no longer used in the production systems.  

Go to https://api.slack.com/custom-integrations/legacy-tokens page to create a new token. From the legacy information section you can find a form which allows you to create a user and workspace specific token.

undefined

This token will be later configured to the Event Listener app.

Hue App Registration

The purpose of the Hue App Registration is to create application key for your event listener application.

 Application itself is very simple. It takes your application and device names as parameter and creates a new application to the Philips Hue Bridge. After creation application key will be returned to you. Application key will be used later in the Slack Event Listener App.

Main function

static async Task Main(string[] args)
        {
            var applicationName = args[0];
            var deviceName = args[1];
            await InitPhilipsBridgeClient();
            var appKey = await RegisterPhilipsHueApp(applicationName, deviceName);
            Console.WriteLine($"Your app key is {appKey}. Save this key to the EventListener App's appsettings file!");
          
        }

Philips Hue Bridge initialization

BridgeLocator returns all you bridges from your network. LocalHueClient is initialized with IP-address of the Hue Bridge.

 public static async Task InitPhilipsBridgeClient()
        {
            IBridgeLocator locator = new HttpBridgeLocator();
            IEnumerable<LocatedBridge> bridgeIPs = await locator.LocateBridgesAsync(TimeSpan.FromSeconds(5));

            if (bridgeIPs != null)
            {
                var firstBridge = bridgeIPs.FirstOrDefault();

                if (firstBridge != null)
                {
                    _client = new LocalHueClient(firstBridge.IpAddress);
                }
            }
        }

App registration

LocalHueClient has a Register method which registers your application. Note! Before calling RegisterAsync the button of the Philips Hue Bridge has to be pressed.

        /// <summary>
        /// Registers Philips Hue application to bridge
        /// </summary>
        /// <returns></returns>
        private static async Task<string> RegisterPhilipsHueApp(string applicationName, string deviceName)
        {
            if (string.IsNullOrEmpty(applicationName))
            {
                Console.WriteLine("Application name is empty!");
                return string.Empty;
            }
            if (string.IsNullOrEmpty(deviceName))
            {
                Console.WriteLine("Decice name is empty!");
                return string.Empty;
            }

            Console.WriteLine("Press the button on the bridge before pressing any key!");

            await WaitForKeyPress();

            //Make sure the user has pressed the button on the bridge before calling RegisterAsync
            //It will throw an LinkButtonNotPressedException if the user did not press the button
            var appKey = await _client.RegisterAsync(applicationName, deviceName);

            return appKey;
        }

How to use Registration App

From the windows command line execute the following command:

dotnet Slack.RegisterHueApp.dll "YouApplicationName" "YourDeviceName"

After execution the application key will be written to the console.

Slack Event Listener App

This is the main application which is listening Slack Events in the background.

Main method

Method initializes the Philips Hue Bridge client with the application key which was retrieved from the Registration App. After initialization App starts the Slack event listener.

 static async Task Main(string[] args)
        {
            var currentDirectory = Directory.GetCurrentDirectory();

            InitSettings(currentDirectory);

            if (string.IsNullOrEmpty(_philipsHueSettings.ApplicationKey))
            {
                Console.WriteLine("Philips Hue Application key is missing from the appsettings!");
                return;
            }

            var applicationkey = _philipsHueSettings.ApplicationKey;

            await InitPhilipsBridgeClientWithKey(applicationkey);
            await StartListeningSlackEvents();           
        } 

Event Listener

Event listener subscribes all user change type of events.

  private static async Task StartListeningSlackEvents()
        {
            if (string.IsNullOrEmpty(_slackSettings.Token))
            {
                Console.WriteLine("Slack token is missing from the appsettings!");
            }
            else
            {
                using (var rtmClient = new SlackRtmClient(_slackSettings.Token))
                {
                    await rtmClient.Connect().ConfigureAwait(false);
                    Console.WriteLine("Slack connected");
                    // subscribe user change events
                    var subscription = rtmClient.Events.Where(x => x.Type == "user_change").Subscribe(async args => await HandleSlackEvent(args));
                    await WaitForKeyPress().ConfigureAwait(false);
                }
            }
        }

Handle event method sets the right color of the light based on the status and sends light command to the Bridge.

  private static async Task<object> HandleSlackEvent(Event slackEvent)
        {
            var userChangeEvent = (UserChange)slackEvent;

            var command = new LightCommand();
            var busyColor = _philipsHueSettings.BusyColorHex ?? "ea0d0d";
            var availableColor = _philipsHueSettings.AvailableColorHex ?? "24d024";

            if (userChangeEvent?.User?.Profile?.StatusText == _philipsHueSettings.BusyStatusTextIndicators)
            {                
                command.TurnOn().SetColor(new RGBColor(busyColor));
            }
            else
            {
                command.TurnOn().SetColor(new RGBColor(availableColor));
            }

            await SendCommandToPhilipsHueBridge(command);

            return userChangeEvent;
        } 
 private static async Task SendCommandToPhilipsHueBridge(LightCommand command)
        {
            if (string.IsNullOrEmpty(_philipsHueSettings.PresenceLightName))
            {
                Console.WriteLine("Presence Light Name is missing from the appsettings!");
                return;
            }

            var lights = await _client.GetLightsAsync();

            if(lights == null)
            {
                Console.WriteLine("Lights not found!");
                return;
            }

            var presenceLight = lights.Where(x => x.Name == _philipsHueSettings.PresenceLightName).FirstOrDefault();

            if (presenceLight == null)
            {
                Console.WriteLine("Presence light not found. Check the light name from the appsettings!");
                return;
            }

            await _client.SendCommandAsync(command, new List<string> { presenceLight.Id });

            Console.WriteLine("Command sent to Philips Hue Bridge.");

        }

Event Listener app settings

Slack Settings:

Token = Slack legacy token will be copied to this section

Philips Hue Settings:

ApplicationKey = Application key which was generated by Registration app should be copied here.

PresenceLightName = Name of the light which is controlled by the App

BusyColorHex = Hex color value which indicates that I’m in the meeting

AvailableColorHex = Hex color value which indicates that I’m free

BusyStatusTextIndicators = Slack status text content which indicates that I’m in the meeting. Currently supports one value.

{
  "SlackSettings": {
    "Token": "[CHANGE]"
  },
  "PhilipsHueSettings": {
    "ApplicationKey": "[CHANGE]",
    "PresenceLightName": "[CHANGE]",
    "BusyColorHex": "ea0d0d",
    "AvailableColorHex": "24d024",
    "BusyStatusTextIndicators": "In a meeting"
  }
}

Summary

Both API libraries were very easy to use and the implementation was pretty straightforward. Next I wait the availability of the MS Teams Presence API. Source code is available in GitHub https://github.com/kalleantero/Slack-Event-Listener