Managing Philips Hue Lights from a C++Builder FireMonkey Desktop Application

By: David Intersimone

Abstract: Using the REST Client Library components, Indy, TColorPanel and TSpinBoxes

This is a repost of a blog post by David I. Click the blog title below to see the original post or to comment.

  Managing my Philips Hue Lights from a C++Builder FireMonkey Desktop

As part of our recent "Making the Connection: Programming Devices and Gadgets with RAD Studio" (https://www.embarcadero.com/rad-in-action/programming-devices-and-gadgets) I showed developers how to use the REST Client Library components with Delphi XE5. In this blog post I’ll show you a C++Builder XE5 FireMonkey application that I built to find and control the same lights. The application uses the REST Client Library components (the Philips Hue lights SDK supports REST/JSON), Project Indy TIdHTTP component (for the PUT commands), TColorPanel to allow selection of colors for the lights (using the CIE xy color space), and TSpinBoxes to let you also set the Hue, Brightness and Saturation.

Before I get into the code, to know a little more about the Philips Hue LED light system, you should look at the Hue web site. I bought a starter kit from Amazon which gave me a Hue Bridge Box (connects to your network via ethernet cable) and 3 wireless LED lights (the bridge box uses thezigbee alliance wireless technology to communicate with the LEDs). The Hue Developer site has documentation about how Hue workshow to get started, explains some core concepts and presents the REST/JSON based APIs for Lights, Groups, Schedules, Configuration and Portal.

One of the key core concepts is the discussion about setting the color for the HUE LED lights. The section is titled "Color gets more complicated" and talks about the "CIE Color Space". Since I wanted to be able to control the color,brightness and saturation for the LEDs, I needed to learn that there is more to color than just setting the Hue.

After some reading and some searching I found information that would help me use the FireMonkey TColorPanel component to select a TAlphaColor and then take the RGB values and calculate the CIE x,y values.

// helper to make working with TAlphaColorRec easy for C++
struct TCppAlphaColorRec : public TAlphaColorRec {
	TCppAlphaColorRec(TAlphaColor c) { this->Color = c; }
};

C++ code to calculate CIE x,y values from the TColorPanel’s Color:

	// Convert  RGB to CIE x,y
	int R = TCppAlphaColorRec(ColorPanel1->Color).R;
	int G = TCppAlphaColorRec(ColorPanel1->Color).G;
	int B = TCppAlphaColorRec(ColorPanel1->Color).B;
	// calculate X,Y,Z using the RGB values
	double X1 = 0.4124*R + 0.3576*G + 0.1805*B;
	double Y1 = 0.2126*R + 0.7152*G + 0.0722*B;
	double Z1 = 0.0193*R + 0.1192*G + 0.9505*B;
	//  normalize the CIE x,y values using X,Y,Z
	double x = X1 / (X1 + Y1 + Z1);
	double y = Y1 / (X1 + Y1 + Z1);

Once I knew how to control the LED Hue, Brightness, Saturation and CIE xy I was ready to build my C++Builder FireMonkey desktop application (from there, I can easily also build an iOS app). Start your project with File | New | FireMonkey Desktop Application - C++Builder. I added a TEdit box so that I could put in the developer ID that I stored in the HUE Bridge. Next I added a TButton and REST Client Library components (in a separate data module) so that I could use the Hue broker server discover process at "www.meethue.com/api/nupnp" to return all of the Hue Bridge boxes on my network (I only have one for now).

To explore and learn more about the Hue REST support, I also used XE5’s REST Debugger to try out some of the APIs. You can learn more about using the REST Debugger on the Embarcadero DocWiki and read my recent blog post, "Using the REST Debugger will help you prototype your Delphi and C++ REST apps" at http://blogs.embarcadero.com/davidi/2014/01/17/43254.

The FindHue RESTResponseDataSetAdpater feeds all of the HUE Bridges found into a ClientDataSet. I have code to read through the ClientDataSets and populate a TComboBox with the IP addresses for all that are found. Here is the code that will populate the HueBridgeComboBox with the found bridge boxes.

void __fastcall TForm6::FindHueButtonClick(TObject *Sender)
{
	// find HUE Bridge - if there is one and there may be more
	HueBridgeComboBox->Clear();
	// Execute the REST Get to receive all Hue Bridge boxes
	// the RESTRequest uses the Hue broker server
	//   discover process at www.meethue.com/api/nupnp
	FindHueDM->FindHueRESTRequest->Execute();
	// display the JSON result
	Memo1->Lines->Add(FindHueDM->FindHueRESTResponse->Content);

	// populate combobox with all Hue Bridge Boxes found using the CDS
	if (FindHueDM->FindHueClientDataSet->RecordCount > 0) {
		for (int HueBridgeIndex = 0; HueBridgeIndex < FindHueDM->FindHueClientDataSet->RecordCount; HueBridgeIndex++) {
			HueBridgeComboBox->Items->Add(
				FindHueDM->FindHueClientDataSet->FieldByName("internalipaddress")->AsString
			);
			Memo1->Lines->Add("Hue Bridge Found: "+HueBridgeComboBox->Items[HueBridgeIndex].Text);
		}
		HueBridgeComboBox->ItemIndex = 0;
		// Bridge found - enable the other buttons
		AllLightsButton->Enabled = true;
		SetLightButton->Enabled = true;

		//TODO: Populate Lights ComboBox using JSON return from lights get
		//  for now I will assume that I have the 3 starter kit lights

	}
	else {
		Memo1->Lines->Add("Hue Bridge not Found!");
	}
}

After the program is connected to the Hue Bridge I’ve added a TButton that will use the TIdHTTP component to "put" JSON to turn on and off the all of the lights in Group 0 (I only have defined one group for all 3 lights). The OnClick event for this button uses the On/Off ComboBox to turn the lights on and off.

void __fastcall TForm6::AllLightsButtonClick(TObject *Sender)
{
	// http:///api/newdeveloper/groups/0/action
	// Body	{"on":false}
	// Method	PUT
	TStringStream *jsonToSend = new TStringStream;
	String jsonBodyString;

	// build the json body string
	if (AllLightsButton->Text == "All Lights On") {
		AllLightsButton->Text = "All Lights Off";
		jsonBodyString = "{\"on\":true}";
	}
	else {
		AllLightsButton->Text = "All Lights On";
		jsonBodyString = "{\"on\":false}";
	}
	jsonToSend->WriteString(jsonBodyString);

	// build the http put string
	String putString =
		"http://"
		+ HueBridgeComboBox->ListItems[HueBridgeComboBox->ItemIndex]->Text
		+ "/api/"
		+ DeveloperIDEdit->Text
		+ "/groups/0/action";  // assume that for now I only have one group

	// HTTP Put to turn on/off all lights and put return result in the Memo
	Memo1->Lines->Add(
		HueSetLightsIdHttp->Put(
			putString,
			jsonToSend)
	);
	delete jsonToSend;
}

So far, so good - I can connect and control the lights. The last step for this application is to add another TButton and create its OnClick event handler that will use the TColorPicker, TColorQuad, two comboboxes (one to select a light, one to choose On and Off for the light) and the TIdHTTP component to send the JSON base commands to the selected light.

void __fastcall TForm6::SetLightButtonClick(TObject *Sender)
{
	String lightStateString;
	TStringStream *jsonToSend = new TStringStream;

	if (OnOffComboBox->Items[OnOffComboBox->ItemIndex].Text == "Off")
		lightStateString = "false";
	else
		lightStateString = "true";

	// Convert ColorPanel RGB to CIE x,y
	int R = TCppAlphaColorRec(ColorPanel1->Color).R;
	int G = TCppAlphaColorRec(ColorPanel1->Color).G;
	int B = TCppAlphaColorRec(ColorPanel1->Color).B;
	// calculate X,Y,Z using the RGB values
	double X1 = 0.4124*R + 0.3576*G + 0.1805*B;
	double Y1 = 0.2126*R + 0.7152*G + 0.0722*B;
	double Z1 = 0.0193*R + 0.1192*G + 0.9505*B;
	//  normalize the CIE x,y values using X,Y,Z
	double x = X1 / (X1 + Y1 + Z1);
	double y = Y1 / (X1 + Y1 + Z1);

	// display the CIE x,y values
	XLabel->Text = FloatToStrF(x,ffFixed,4,3);
	YLabel->Text = FloatToStrF(y,ffFixed,4,3);

	// build the json body string
	String jsonBodyString =
		"{\"on\":" + lightStateString
		+ ",\"bri\":" + FloatToStr(BrightnessSpinBox->Value)
		+ ",\"hue\":" + FloatToStr(HueSpinBox->Value)
		+ ",\"sat\":" + FloatToStr(SaturationSpinBox->Value)
		+ ",\"xy\":" + "["+XLabel->Text+","+YLabel->Text+"]"
		+ "}";
	jsonToSend->WriteString(jsonBodyString);

	// build the http put string
	String putString =
		"http://"
		+ HueBridgeComboBox->ListItems[HueBridgeComboBox->ItemIndex]->Text
		+ "/api/"
		+ DeveloperIDEdit->Text
		+ "/lights/"
		+ LightsComboBox->ListItems[LightsComboBox->ItemIndex]->Text
		+ "/state";

	// HTTP Put to set a light's color and put return result in the Memo
	Memo1->Lines->Add(
		HueSetLightsIdHttp->Put(
			putString,
			jsonToSend)
	);

	delete jsonToSend;
}

The coding is done. The UI is done. Time to compile the C++ application for Win32, Win64 and OSX. The project and target platforms appear in the Project Manager window in the IDE.

One note if you build the project for OS X deployment, you’ll need to use the Project | Deployment menu item and use the "Add Feature Files" and check the Midas checkbox. We need to deploy the LibMidas.dylib along with the Mac application (required since I use the ClientDataSet).

Here is the running application on Windows and Mac OS X and a picture with the LEDs colored and on.

 

You can download the current version of the source code project from Code Central at http://cc.embarcadero.com/item/29711.

My next to do item is to create an iOS mobile UI and reuse all of the same components and code using C++Builder XE5 update 2 support for iOS. Also on my Philips Hue development plan is the creation of components to cut down the amount of coding required to "almost nil" :)

Server Response from: ETNASC01