...
Expand | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
|
Only when the TempSensor ThingModel "combines" TempFacetSerial, can TempFacetSerial be instantiated within the TempSensor and accessible by TQL queries. TempSensor will PhilipLightFacet can only be instantiated (and write TQL Queries) when it is combined with PhilipLightModel. PhilipLightModel will inherit all the attributes from TempFacetSerialPhilipLightFacet, in addition to its own attributes. The ThingFacet TempFacetSerial PhilipLightFacet 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.
...
title | Deploy and instantiate |
---|
...
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.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<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
...
language | xml |
---|---|
title | Find TempSensor query |
linenumbers | true |
...
Initializing Lights
Typically ThingModels are instantiated by writing external Create or Save Queries. In this case the Lights data is itself coming from another Provider API. This type of loading initial set of devices and their metadata is common to number of device providers. In PhilipLight example, we can load PhilipLightModel data by invoking HTTP Get on Lights. Let's create another ThingFacet and ThingModel for the purpose of loading the data. We also have to call the Create Query within the ThingFacet.
The building blocks to Loading Lights are:
- Ability to run Create TQL Query from ThingFacet
- Ability to Load lights at any given time
TQLEngine allows us to run TQL Queries from the Model itself using <OnRequest> Tag. We need to provide the Target ID to make this request to. Since queries can be called any number of times, it is always a good idea to wrap calling of a TQL Query in a generic Macro Definition. Let's call this ExecuteQuery.
Generic Macro to execute a TQL Query
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<Macro Name="ExecuteQuery">
<Argument>
<QueryString>
<Query/>
</QueryString>
</Argument>
<Result>
<OnRequest>
<Target>[:RuntimeParams.FacetIDName:]</Target>
<Process>
<Message>
<Value>[:$Macro.Argument.QueryString:]</Value>
</Message>
</Process>
</OnRequest>
</Result>
</Macro>
|
Macro to Create Light
Since creating of light can be called multiple times, we can wrap the creating of Light itself in another Macro definition called AddLight. In AddLight we can in turn use ExecuteQuery Macro.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<Macro Name="AddLight">
<Argument>
<LightNumber/>
<On/>
<bri/>
<hue/>
<sat/>
</Argument>
<Result>
<ExecuteQuery>
<QueryString>
<DeleteAll>
<PhilipLightModel>
<LightNumber Eq="[:$Macro.Argument.LightNumber:]"/>
</PhilipLightModel>
</DeleteAll>
<Create>
<PhilipLightModel>
<LightNumber>[:$Macro.Argument.LightNumber:]</LightNumber>
<State>
<On>[:$Macro.Argument.On:]</On>
<bri>[:$Macro.Argument.bri:]</bri>
<hue>[:$Macro.Argument.hue:]</hue>
<sat>[:$Macro.Argument.sat:]</sat>
</State>
</PhilipLightModel>
</Create>
</QueryString>
</ExecuteQuery>
</Result>
</Macro> |
ThingFacet to Load Lights
We need a trigger mechanism to load the lights. For this purpose we can write another PhilipLightManagerFacet ThingFacet with an LoadLightsAction Action against a LoadLights Attribute. This ThingFacet is similar to any other ThingFacet except that the Invoke is to the AddLight Macro using FacetScript.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<ThingFacet Name="PhilipLightManagerFacet"> <String Name="LoadLights" KnownBy="LoadLightsAction"/> <Action Name="LoadLightsAction"> <Workflow Limit="1" Live="1" Timeout="-1"> <Task Name="Main" While="True"> <Event Name="Argument" As="ActionArgument"/> <Invoke Name="GetLights" waitFor="ActionArgument" Get="[:PhilipLightBaseURI:]/lights"/> <Log Message="Lights Loaded...[:Invoke.GetLights.Message.Value:]"/> <Invoke Name="ParseGetLightsResult"> <FacetScript> <AddLight> <LightNumber>1</LightNumber> <On>[:Invoke.GetLights.Message.Value.1.State.On:]</On> <bri>[:Invoke.GetLights.Message.Value.1.State.bri:]</bri> <hue>[:Invoke.GetLights.Message.Value.1.State.hue:]</hue> <sat>[:Invoke.GetLights.Message.Value.1.State.sat:]</sat> </AddLight> <AddLight> <LightNumber>2</LightNumber> <On>[:Invoke.GetLights.Message.Value.2.State.On:]</On> <bri>[:Invoke.GetLights.Message.Value.2.State.bri:]</bri> <hue>[:Invoke.GetLights.Message.Value.2.State.hue:]</hue> <sat>[:Invoke.GetLights.Message.Value.2.State.sat:]</sat> </AddLight> <AddLight> <sensorId ne="" /> </TempSensor> <<LightNumber>3</Find> </Query> |
You will see the result from the RESULTS window:
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
<Find Status="Success" Format="version,current">LightNumber> <Result> <TempSensor> <On>[:Invoke.GetLights.Message.Value.3.State.On:]</On> <sensorId>KLHEPDF7AAAAUAABB4JDTW4S</sensorId> <interfacePort Value="/dev/cu.usbserial-A7030IWB" Version="1"/><bri>[:Invoke.GetLights.Message.Value.3.State.bri:]</bri> <payload Value="" Version="1"/> <hue>[:Invoke.GetLights.Message.Value.3.State.hue:]</hue> <peripheral Value="serial" Version="1"/> <interface Value="serial" Version="1"/> <sat>[:Invoke.GetLights.Message.Value.3.State.sat:]</sat> <operation Value="receive" Version="1"/> </AddLight> <tempValue Value="" Version="1"/> </FacetScript> <baudrate Value="115200" Version="1"/></Invoke> <uniqueId<Output ValueName="76522Result" VersionAs="1ActionResult"/>> <Value> <format Value="ascii" Version="1"/> <<LoadLights>True</TempSensor>LoadLights> </Result> </Find> |
...
title | Queries |
---|
...
...
language | xml |
---|---|
title | Subscribe to temperature sensor |
linenumbers | true |
...
...
</Value> |
...
|
...
|
...
</Output> |
...
</Task> </ |
...
Workflow> </ |
...
Action> </ |
...
ThingFacet>
|
Let's combine PhilipLightManagerFacet with a PhilipLightManagerModel.
Code Block | |||
---|---|---|---|
|
...
<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.
...
| ||||
<ThingModel Name="PhilipLightManagerModel" Combines="PhilipLightManagerFacet">
<Sid Name="ManagerId"/>
</ThingModel> |
Query and subscription
Queries we need are:
- Trigger Loading of Lights using PhilipLightManagerModel
- Change the state of Light using PhilipLightModel
- Find the Light and Manager models.
Queries
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<Query> <Find format="all"> <PhilipLightModel> <LightNumber eq="1"/> </PhilipLightModel> </Find> </Query> |
...
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
<Namespace Name="Atomiton"> <Domain Name="Lights"> <PhilipLightBaseURI>http://10.0.2.16/api/newdeveloper</PhilipLightBaseURI> <Macro Name="ExecuteQuery"> <Argument> <QueryString> <Query/> </QueryString> </Argument> <Result> <OnRequest> <Target>[:RuntimeParams.FacetIDName:]</Target> <Process> <Message> <Value>[:$Macro.Argument.QueryString:]</Value> </Message> </Process> </OnRequest> </Result> </Macro> <Macro Name="AddLight"> <Argument> <LightNumber/> <On/> <bri/> <hue/> <sat/> </Argument> <Result> <ExecuteQuery> <QueryString> <DeleteAll> <PhilipLightModel> <LightNumber eq="[:$Macro.Argument.LightNumber:]"/> </PhilipLightModel> </DeleteAll> <Create> <PhilipLightModel> <LightNumber>[:$Macro.Argument.LightNumber:]</LightNumber> <state> <On>[:$Macro.Argument.On:]</On> <bri>[:$Macro.Argument.bri:]</bri> <hue>[:$Macro.Argument.hue:]</hue> <sat>[:$Macro.Argument.sat:]</sat> </state> </PhilipLightModel> </Create> </QueryString> </ExecuteQuery> </Result> </Macro> <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> <Def Name="PointSymbol"> <String name="1" default="none"/> <String name="2" default="none"/> <String name="3" default="none"/> <String name="4" default="none"/> <String name="5" default="none"/> <String name="6" default="none"/> <String name="7" default="none"/> <String name="8" default="none"/> </Def> <ThingFacet Name="PhilipLightFacet"> <String Name="LightNumber"/> <PhilipLightState Name="state" KnownBy="PhilipLightActionUsingCurl"/> <String Name="Type"/> <String Name="Name"/> <String Name="modelid"/> <String Name="uniqueid"/> <String Name="swversion"/> <PointSymbol Name="Pointsymbol"/> <AA>[:#o#Event.Argument:]</AA> <Ontrue>{"on":true}</Ontrue> <Onfalse>"on":false</Onfalse> <Action Name="PhilipLightActionUsingCurl"> <Workflow Limit="1" Live="1" Timeout="-1"> <Task name="Main" While="True"> <Event name="Argument" as="ActionArgument"/> <Invoke name="SetState" waitFor="ActionArgument" execute="curl -X PUT -d '{"on":[%:[:AA:].state.On.Value:%]}' [:PhilipLightBaseURI:]/lights/[%:[:AA:].LightNumber.Value:%]/state"/> <Log Message="[:Invoke.SetState:]"/> <Output name="Result" as="ActionResult"> <Value> <state> [%:[:AA:].state.On.Value:%] </state> </Value> </Output> </Task> </Workflow> </Action> </ThingFacet> <ThingFacet Name="PhilipLightManagerFacet"> <String Name="LoadLights" KnownBy="LoadLightsAction"/> <Action Name="LoadLightsAction"> <Workflow Limit="1" Live="1" Timeout="-1"> <Task Name="Main" While="True"> <Event Name="Argument" as="ActionArgument"/> <Invoke Name="GetLights" waitFor="ActionArgument" Get="[:PhilipLightBaseURI:]/lights"/> <Log Message="Lights Loaded...[:Invoke.GetLights.Message.Value:]"/> <Invoke Name="ParseGetLightsResult"> <FacetScript> <AddLight> <LightNumber>1</LightNumber> <On>[:Invoke.GetLights.Message.Value.1.State.On:]</On> <bri>[:Invoke.GetLights.Message.Value.1.State.bri:]</bri> <hue>[:Invoke.GetLights.Message.Value.1.State.hue:]</hue> <sat>[:Invoke.GetLights.Message.Value.1.State.sat:]</sat> </AddLight> <AddLight> <LightNumber>2</LightNumber> <On>[:Invoke.GetLights.Message.Value.2.State.On:]</On> <bri>[:Invoke.GetLights.Message.Value.2.State.bri:]</bri> <hue>[:Invoke.GetLights.Message.Value.2.State.hue:]</hue> <sat>[:Invoke.GetLights.Message.Value.2.State.sat:]</sat> </AddLight> <AddLight> <LightNumber>3</LightNumber> <On>[:Invoke.GetLights.Message.Value.3.State.On:]</On> <bri>[:Invoke.GetLights.Message.Value.3.State.bri:]</bri> <hue>[:Invoke.GetLights.Message.Value.3.State.hue:]</hue> <sat>[:Invoke.GetLights.Message.Value.3.State.sat:]</sat> </AddLight> </FacetScript> </Invoke> <Output Name="Result" as="ActionResult"> <Value> <LoadLights>True</LoadLights> </Value> </Output> </Task> </Workflow> </Action> </ThingFacet> <ThingModel Name="PhilipLightModel" Combines="PhilipLightFacet"> <Sid Name="LightId"/> </ThingModel> <ThingModel Name="PhilipLightManagerModel" Combines="PhilipLightManagerFacet"> <sid Name="ManagerId"/> </ThingModel> </Domain> </Namespace> |