Hardware Setup
Philip Hue is a system of 4 components:
- Apps – These are ways to control the lights to make them do smart things.
- Bridge – This is used to enable your smart bulbs to communicate with each other and the Portal via the internet. The main set of APIs are those offered by the bridge. These allow you to control all settings of the lights in your system. These APIs require direct access to your bridge so you’ll only be able to access them when your app and bridge are on the same local network.
- Portal – This is a web based control panel which connects your home to the internet. It delivers control commands from outside and keeps your software in the bridge up-to-date. The portal presents a utility API to help you discover the address of your bridge.
- Lights – This is the output of the system. These smart bulbs contain 3 types of LED specifically chosen to produce a range of colors and intensities. Lights create a mesh network with each other which enables each light to pass on messages to the next extending the range and making everything more robust. They are connected to the bridge via an open standards protocol called ZigBee Light Link.
We will be controlling the lights using Philip Bridge HTTP APIs.
Philip Bridge HTTP Requests
http://10.0.2.16/api/newdeveloper/lights
Request Name: Get the list of lights
{ "1": { "state": { "on": true, "bri": 144, "hue": 13088, "sat": 212, "effect": "none", "xy": [ 0.5128, 0.4147 ], "ct": 467, "alert": "none", "colormode": "hs", "reachable": true }, "type": "Extended color light", "name": "Hue Lamp 1", "modelid": "LCT001", "uniqueid": "00:17:88:01:00:b6:c6:f0-0b", "swversion": "66009663", "pointsymbol": { "1": "none", "2": "none", "3": "none", "4": "none", "5": "none", "6": "none", "7": "none", "8": "none" } }, "2": { "state": { "on": true, "bri": 144, "hue": 13088, "sat": 212, "effect": "none", "xy": [ 0.5128, 0.4147 ], "ct": 467, "alert": "none", "colormode": "ct", "reachable": true }, "type": "Extended color light", "name": "Hue Lamp 2", "modelid": "LCT001", "uniqueid": "00:17:88:01:00:b9:38:7c-0b", "swversion": "66009663", "pointsymbol": { "1": "none", "2": "none", "3": "none", "4": "none", "5": "none", "6": "none", "7": "none", "8": "none" } }, "3": { "state": { "on": true, "bri": 144, "hue": 13088, "sat": 212, "effect": "none", "xy": [ 0.5128, 0.4147 ], "ct": 467, "alert": "none", "colormode": "hs", "reachable": true }, "type": "Extended color light", "name": "Hue Lamp 3", "modelid": "LCT001", "uniqueid": "00:17:88:01:00:b9:38:5c-0b", "swversion": "66009663", "pointsymbol": { "1": "none", "2": "none", "3": "none", "4": "none", "5": "none", "6": "none", "7": "none", "8": "none" } } }
Request Name: Change the state of Light
http://10.0.2.16/api/newdeveloper/lights/1/state
{"on":true}
Write a ThingFacet, Workflow and Device Logic (Message Transformation)
Write a ThingFacet
To abstract the interactions with the Philip Light Bulbs, we create a PhilipLightFacet ThingFacet.
We add two types of attributes in this ThingFacet
- Parameters required to make a protocol specific invocation
Attributes to store the information received from the light.
There are two important patterns for look for:
- The response from GetLights is a complex hierarchical response.
- Parameterizing the Protocol Parameters instead of attributes of a ThingFacet
Let's create response structure using Types (Def) to match to the response of Lights
<Def Name="PhilipLightState"> <Boolean Name="On"/> <Integer Name="bri"/> <Integer Name="hue"/> <Integer Name="sat"/> <String Name="effect"/> <Double Name="xy" cardinality="2"/> <Integer Name="ct"/> <String Name="alert"/> <String Name="colormode"/> <Boolean Name="reachable"/> </Def>
Simply define the Philip Light Bridge API URL using PhilipLightBaseURI Tag
<PhilipLightBaseURI>http://10.0.2.16/api/newdeveloper</PhilipLightBaseURI>
Lets now create a ThingFacet using the PhilipLightState type and rest of the parameters like - Type, modelId, etc as parameters of the PhilipLightFacet ThingFacet. Now the entire ThingFacet definition maps to the exact Response of the GetLight structure.
<ThingFacet Name="PhilipLightFacet"> <String Name="LightNumber"/> <PhilipLightState Name="State"/> <String Name="Type"/> <String Name="modelid"/> <String Name="uniqueid"/> <String Name="swversion"/> </ThingFacet>
Write a Action with Workflow
The next step in ThingFacet is writing a Action which contains a workflow responsible for making external protocol specific call and store the output in the appropriate ThingFacet attribute. An important decision while writing Action is correct protocol selection. See the /wiki/spaces/TQLDocs/pages/1179871 in the Working with Things section. For this Philip Light we need simple HTTP request, which is part of the TQLEngine.
1. Write an Action name as PhilipLightAction
<ThingFacet Name="TempFacetSerial"> ... <Action Name="PhilipLightAction"> ... </Action> </ThingFacet>
2. Create a Workflow within the Action, with one single continuous running Task and waiting for the Event with its ActionArgument.
<Action Name="PhilipLightAction"> <Workflow Limit="1" Live="1" Timeout="-1"> <Task name="Main" while="true"> <Event name="Argument" as="ActionArgument"/> ... </Task> </Workflow> </Action>
Here we used three modifiers for this workflow. Limit = "1" means there can be at most one instance of this workflow waiting. Live = "1" means there can be at most one instance of this workflow running. Timeout ="-1" means this workflow will never be timed out. We used a modifier while = "true" with the Task to make the workflow running in a continuous loop, because it needs to run repeatedly, not just once. For more details, refer to workflow modifiers and the lifecycle of a workflow.
The task will be activated by the event handler Event called ActionArgument. "ActionArgument" is the event generated whenever the attribute(s) associated with this Action is modified (See Associate Action with a ThingFacet Attribute). ActionArgument carries all the current values of the ThingFacet attributes, which can be used in the task if needed.
3. Invoke HTTP call: Method of HTTP is PUT. We use Invoke modifier Put for this purpose. The parameters required for HTTP PUT are passed as part of <Message> and <Value>.
<Invoke Name="SetState" waitFor="ActionArgument" Put="[:PhilipLightBaseURI:]/lights/[%:[:AA:].LightNumber.Value:%]/state"> <Message type="json"> <Value>{"on": [:AA:].state.On.Value}</Value> </Message> </Invoke>
Note that PhilipLightBaseURI is referenced using TP tag: [:PhilipLightBaseURI:] The notation [%: is part of the /wiki/spaces/TEACH/pages/21170773. More details on /wiki/spaces/TEACH/pages/21170773 can be found in the Developer's Guide.
4. Process the message received from the Philip Light Bridge
<Output name="Result" as="ActionResult"> <Value> <State> <On>[%:[:AA:].state.Value:%]</On> </State> </Value> </Output>
Associate Action with a ThingFacet Attribute
We will now have to associate a ThingFacet Attribute (State) with the Action using the KnownBy modifier. When State is "KnownBy" PhilipLightAction, any State value changes will activate the PhilipLightAction.
<ThingFacet Name="PhilipLightAction"> ... <PhilipLightState Name="State" KnownBy="PhilipLightActionUsingPut"/> </ThingFacet>
The attribute modifier update= "auto" makes sure that once the action associated with this attribute is triggered, its workflow continues to run and wait for subsequent sensor events (not just the first event). This modifier is only used with actionable attributes. For more details, refer to Automatic Action trigger.
Combining ThingFacet with a ThingModel
Finally, in order to use ThingFacet we have to combine it with a ThingModel. We define ThingModel to contain only a unique system identifier.
<ThingModel Name="PhilipLightModel" Combines="PhilipLightFacet"> <Sid Name="LightId"/> </ThingModel>
Only when the TempSensor ThingModel "combines" TempFacetSerial, can TempFacetSerial be instantiated within the TempSensor and accessible by TQL queries. TempSensor will inherit all the attributes from TempFacetSerial, in addition to its own attributes. The ThingFacet TempFacetSerial hence serves as a reusable component.
More details on the use of "combines" can be found here. Information on Sid can be found here.
Deploy and test via queries and subscriptions
Export, import and deploy
Once the models are built in a TQLStudio project, you can export the project. The URL of the Zip file containing the content of your project will be sent over to your email account. Once you have downloaded the engine – launch the TQLEngine User Interface in the browser and select Import Project. You can copy the URL from Export Project Email and hit import button. Go to ThingSpaces and Deploy the project.
Instantiation
Here we use a query to instantiate the sensor, creating 1 instance of the TempSensor ThingModel. In the query, we first use "DeleteAll" to delete all the current instances of the TempSensor, if there are any. The Save will save the values into the corresponding model attributes (inherited from the ThingFacet). These values provide the runtime parameters for the perif:// handler, as well as providing an update of the TempValue actionable attribute ($Null ( )), which will trigger the associated action SerialReadAction.
<Query> <DeleteAll format="version,current"> <TempSensor> <sensorId ne=""/> </TempSensor> </DeleteAll> <Save format="version,current"> <!-- This will read --> <TempSensor> <Peripheral>serial</Peripheral> <Baudrate>9600</Baudrate> <InterfacePort>/dev/cu.usbserial-AL01C1HO</InterfacePort> <Interface>serial</Interface> <Format>ascii</Format> <Operation>receive</Operation> <UniqueId>76522</UniqueId> <Payload>$Null()</Payload> <TempValue>$Null()</TempValue> </TempSensor> </Save> </Query>
Query and subscription
Now we can try a simple Find Query to get sensor values
<Query> <Find format="version,current"> <TempSensor> <sensorId ne="" /> </TempSensor> </Find> </Query>
You will see the result from the RESULTS window:
<Query Storage='TqlSubscription'> <Save> <TqlSubscription Label='TempSensor' sid='20'> <Topic>Atomiton.Sensor.TempSensor.tempValue</Topic> </TqlSubscription> </Save> </Query>
Or you can subscribe to the specific attribute of the model.
<Topic>Atomiton.Sensor.TempSensor.tempValue</Topic> <Topic>Atomiton.Sensor.TempSensor.*</Topic>
The Label is the string that will be attached to every message you receive from this subscription. Here we give an sid to this instance of the subscription.
Hardware Setup
Queries
<Query> <Find format="all"> <PhilipLightModel> <LightNumber eq="1"/> </PhilipLightModel> </Find> </Query>
<Query> <Find format="All"> <PhilipLightManagerModel> <ManagerId ne=""/> </PhilipLightManagerModel> </Find> </Query>
<Query> <DeleteAll> <PhilipLightManagerModel> <ManagerId ne=""/> </PhilipLightManagerModel> </DeleteAll> <Create> <PhilipLightManagerModel> <LoadLights> True </LoadLights> </PhilipLightManagerModel> </Create> </Query>
<Query> <Find format="All"> <PhilipLightModel> <LightNumber eq="1"/> </PhilipLightModel> </Find> <SetResponseData> <Key> Message.Value.Find.Result.PhilipLightModel.state.On </Key> <Value> false </Value> </SetResponseData> <Update> <From> Result </From> <Include> $Response.Message.Value.Find </Include> </Update> </Query>