Status notifier app powered by Philips Hue lights
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 the 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 the Philips Hue mobile app. Kids really like this.
Now I started to think about what else could I do with the lights. I got the idea that I would create some automation to notify the family when I’m in the meeting. I think it would be a good idea to notify the 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 the .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 to user status change events from Slack. If the status indicates that I’m in the meeting then the App changes light colors to red by using Philips Hue APIs.
Hue App Registration registers your application to Hue Bridge. After registration, you get 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 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 that allows you to create a user and workspace-specific token.
This token will be later configured to the Event Listener app.
Hue App Registration
The purpose of the Hue App Registration is to create an application key for your event listener application.
The application itself is very simple. It takes your application and device names as parameters and creates a new application for the Philips Hue Bridge. After creation application key will be returned to you. The 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 your 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 that 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 to Slack Events in the background.
Main method
The 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 to all user change types 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);
}
}
}
The handle event method sets the right color of the light based on the status and sends a 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 = The Application key that was generated by the 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 for the availability of the MS Teams Presence API. The source code is available on GitHub https://github.com/kalleantero/Slack-Event-Listener
Comments