Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Expand
titleThingModels
Info

Include Page
ThingModels
ThingModels

Go to page

 

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.

...

titleDeploy and instantiate

...

Go to page

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
languagexml
titleSensor instantiation
linenumberstrue
<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

...

languagexml
titleFind TempSensor query
linenumberstrue

...

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
languagexml
titleMacro Definition to run any Query
linenumberstrue
<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
languagexml
titleAddLight Macro
linenumberstrue
<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
languagexml
titlePhilipLightManagerFacet
linenumberstrue
<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
languagexml
titleFind TempSensor query result
linenumberstrue
collapsetrue
<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>

 

 

...

titleQueries

...

Go to page

 

To subscribe to changes, use subscription is registered by through a query (query, create)

 

...

languagexml
titleSubscribe to temperature sensor
linenumberstrue

...

          

...

</Value>
        

...

 

...

 

...

  </Output>
          

...

</Task>
        </

...

Workflow>
      </

...

Action>
    </

...

ThingFacet>

Let's combine PhilipLightManagerFacet with a PhilipLightManagerModel.

Code Block
languagexml
title

...

<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.

...

PhilipLightManagerModel
linenumberstrue
<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
languagexml
titleFind Lights
linenumberstrue
 <Query>
  <Find format="all">
    <PhilipLightModel>
      <LightNumber eq="1"/>
    </PhilipLightModel>
  </Find>
</Query>

...

Code Block
languagexml
titlePhidgetMotor PhilipLight Complete Model
linenumberstrue
collapsetrue
<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 '{&quot;on&quot;:[%:[: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>