Versions Compared

Key

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

...

  1. Parameters required to make a protocol specific invocation 

 

Attributes to store the information received from the light. 

There are two important patterns

...

to look for:

...

Mapping of Device Vendor Complex Structure: The response from GetLights is a complex hierarchical response.

...

Helper Tags: Tags that are helpful in simplifying long name as well as parameterizing the Protocol Parameters instead of attributes of a ThingFacet

 

Let's create response structure using Types (Def) to match to the response structure of Lights REST API

Code Block
languagexml
titlePhilipLightState Definition
linenumberstrue
<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>

Helper Tag - 1: Simply define the Philip Light Bridge API URL using PhilipLightBaseURI Tag

...

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.

Helper Tag - 2: Tag AA to designate Event.Argument

Code Block
languagexml
titlePhilipLightBaseURI Tag
linenumberstrue
 <ThingFacet Name="PhilipLightFacet">
  <String Name="LightNumber"/>
  <PhilipLightState Name="State"/>
  <String Name="Type"/>
  <String Name="modelid"/>
  <String Name="uniqueid"/>
  <String Name="swversion"/>
  <!-- Helper Tag -->
  <AA>[:#o#Event.Argument:]</AA>
 </ThingFacet>

Write a Action with Workflow

...

Code Block
languagexml
titlePhilipLightAction
linenumberstrue
<ThingFacet Name="TempFacetSerialPhilipLightFacet">
	...
	<Action Name="PhilipLightAction">
		...
	</Action>
</ThingFacet>

...

Code Block
languagexml
titleInvoke HTTP Handler
linenumberstrue
 <Invoke Name="SetState" waitFor="ActionArgument" Put="[:PhilipLightBaseURI:]/lights/[%:[:AA:].LightNumber.Value:%]/state">
  <Message  <Message typexmlns="PLight" Type="json">
    <on_true>
  <Value>{"on":     <on>true</on>
    </on_true>
    <on_false>
      <on>false</on>
    </on_false>
    <Value>
      <Include>[:@WFRT:]on_[%:[:AA:].state.On.Value}:%]</Value>Include>
    </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

...

TagResolution
[:PhilipLightBaseURI:]
http://10.0.2.16/api/newdeveloper
[:AA:]
Event.Argument
[%:[:AA:].LightNumber.Value:%]
Runtime value of structure: Event.Argument.LightNumber.Value
<on_true> and <on_false>contains <on>true</on> and <on>false</on>; this is stop gap to avoid string to boolean conversion in TP
[:@WFRT:]

Delay processing of Tags at Workflow execution time. Note the TQLEngine goes through number of

processing / pre-processing phases. Prefixing it with WFRT tells the Engine to delay resolving the Tags until

the execution of the workflow

[%:[:AA:].state.On.Value:%]
Runtime value of structure: Event.Argument.state.On.Value
<Include>[:@WFRT:]on_[%:[:AA:].state.On.Value:%]</Include>Includes the content of either <on_true> or <on_false>


4. Process the message received from the Philip Light Bridge

Code Block
languagexml
titleMessage parsing using TP and XPath
<Output name="Result" as="ActionResult">
   <Value>
     <State>
        <On>[%:[:AA:].state.Value:%]</On>
     </State>
  </Value>
</Output>

...

Code Block
languagexml
titleAttribute State tied to Action
linenumberstrue
<ThingFacet Name="PhilipLightActionPhilipLightFacet">
	...
    <PhilipLightState Name="State" KnownBy="PhilipLightActionUsingPutPhilipLightAction"/>
</ThingFacet>
Expand
titleActionable attributes
Info

Include Page
Actionable attributes
Actionable attributes

Go to page

...

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 providersNumber of device vendors follow the pattern of providing metadata loading APIs. This is a common approach. 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.

...

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>

...

Code Block
languagexml
titleToggleLight
linenumberstrue
<Query>
  <Find format="All">
    <PhilipLightModel>
      <LightNumber eq="1"/>
    </PhilipLightModel>
  </Find>
  <SetResponseData>
    <Key>
      Message.Value.Find.Result.PhilipLightModel.state.On.Value
    </Key>
    <Value>
      false
    </Value>
  </SetResponseData>
  <Update>
    <From>
      Result
    </From>
    <Include>
      $Response.Message.Value.Find
    </Include>
  </Update>
</Query>

<Namespace Name="Atomiton"> <Domain Name="Lights"> <PhilipLightBaseURI>http://10.0.2.16/api/newdeveloper</PhilipLightBaseURI> <Macro Name="ExecuteQuery"> <Argument> <QueryString> <Query/
Code Block
languagexml
titlePhilipLight Complete Model
linenumberstrue
collapsetrue
true
<Namespace Name="Atomiton">
  <Domain Name="Lights">
         </QueryString>

      </Argument><PhilipLightBaseURI>http://10.0.2.16/api/newdeveloper</PhilipLightBaseURI>
    <Def Name="PhilipLightState">
 <Result>     <Boolean Name="On"/>
   <OnRequest>   <Integer Name="bri"/>
       <Target>[:RuntimeParams.FacetIDName:]</Target>

     <Integer Name="hue"/>
    <Process>  <Integer Name="sat"/>
      <String Name="effect"/>
  <Message>    <Double Name="xy" cardinality="2"/>
      <Integer Name="ct"/>
 <Value>[:$Macro.Argument.QueryString:]</Value>     <String Name="alert"/>
      <String </Message>Name="colormode"/>
      <Boolean Name="reachable"/>
    </Process>Def>

    <Macro Name="ExecuteQuery">
  </OnRequest>    <Argument>
   </Result>     <QueryString>
</Macro>         <Macro Name="AddLight" <Query/>
       <Argument> </QueryString>
      </Argument>
 <LightNumber/>     <Result>
    <On/>    <OnRequest>
     <bri/>     <Target>[:RuntimeParams.FacetIDName:]</Target>
    <hue/>      <Process>
   <sat/>        </Argument> <Message>
      <Result>        <Value>[:$Macro.Argument.QueryString:]</Value>
 <ExecuteQuery>           </Message>
<QueryString>          </Process>
   <DeleteAll>     </OnRequest>
      </Result>
   <PhilipLightModel> </Macro>

    <Macro Name="AddLight">
      <Argument>
   <LightNumber eq="[:$Macro.Argument.LightNumber:]"     <LightNumber/>
        <On/>
      </PhilipLightModel>  <bri/>
        <hue/>
  </DeleteAll>      <sat/>
      </Argument>
<Create>      <Result>
        <ExecuteQuery>
<PhilipLightModel>          <QueryString>
       <LightNumber>[:$Macro.Argument.LightNumber:]</LightNumber>     <DeleteAll>
            <state>  <PhilipLightModel>
                <LightNumber <On>Eq="[:$Macro.Argument.OnLightNumber:]</On>"/>
              </PhilipLightModel>
            <bri>[:$Macro.Argument.bri:]</bri>DeleteAll>
            <Create>
         <hue>[:$Macro.Argument.hue:]</hue>     <PhilipLightModel>
                <sat><LightNumber>[:$Macro.Argument.satLightNumber:]</sat>LightNumber>
                <State>
  </state>                <On>[:$Macro.Argument.On:]</PhilipLightModel>On>
             </Create>            </QueryString><bri>[:$Macro.Argument.bri:]</bri>
         </ExecuteQuery>        </Result>

    </Macro> <hue>[:$Macro.Argument.hue:]</hue>
        <Def Name="PhilipLightState">

      <Boolean Name="On"/>        <Integer Name="bri"/>
 <sat>[:$Macro.Argument.sat:]</sat>
      <Integer Name="hue"/>        <Integer Name="sat"/></State>
       <String Name="effect"/>      </PhilipLightModel>
 <Double Name="xy" cardinality="2"/>        <Integer Name="ct"/></Create>
         <String Name="alert"/></QueryString>
       <String Name="colormode"/>

  </ExecuteQuery>
   <Boolean Name="reachable"/>   </Result>
     </Def>Macro>

    <Def<ThingFacet Name="PointSymbolPhilipLightFacet">
       <String nameName="1" default="noneLightNumber"/>
      <PhilipLightState <String nameName="2State" defaultKnownBy="nonePhilipLightAction"/>

      <String nameName="3" default="noneType"/>

      <String nameName="4" default="none"modelid"/>
       <String nameName="5" default="none"uniqueid"/>

      <String nameName="6" default="none"swversion"/>
      <!-- Helper Tags -->
  <String name="7" default="none"/>    <AA>[:#o#Event.Argument:]</AA>
      <Action <String nameName="8PhilipLightAction" defaultDocumentation="none"/>Set the state of the Light </Def>(true/false)">
        <ThingFacet<Workflow NameLimit="PhilipLightFacet1"> 
      <String Name="LightNumber"/>Live="1" Timeout="-1">
         <PhilipLightState <Task Namename="stateMain" KnownByWhile="PhilipLightActionUsingCurlTrue"/>
            <Event <String Namename="Argument" as="TypeActionArgument"/>
       <String     <Invoke Name="Name"/"SetState" waitFor="ActionArgument" PUT="[:PhilipLightBaseURI:]/lights/[%:[:AA:].LightNumber.Value:%]/state">
       <String Name="modelid"/>      <Message  <String Namexmlns="PLight" Type="uniqueidjson"/>
       <String Name="swversion"/>        <PointSymbol Name="Pointsymbol"/><on_true>
          <AA>[:#o#Event.Argument:]</AA>        <Ontrue>{"on":true}</Ontrue><on>true</on>
           <Onfalse>"on":false</Onfalse>     </on_true>
     <Action Name="PhilipLightActionUsingCurl">          <Workflow Limit="1" Live="1" Timeout="-1"><on_false>
              <Task name="Main" While="True">  <on>false</on>
           <Event name="Argument" as="ActionArgument"/>   </on_false>
          <Invoke name="SetState" waitFor="ActionArgument"     <Value>
            execute="curl -X PUT -d '{&quot;on&quot;:[%:[:AA:].state.On.Value:%]}' [:PhilipLightBaseURI:]/lights/      <Include>[:@WFRT:]on_[%:[:AA:].state.LightNumberOn.Value:%]/state"/></Include>
                </Value>
              </Message>
       <Log Message="[:Invoke.SetState:]"/>     </Invoke>
             <Output name="Result" as="ActionResult">
               <Value>
                <State>
<state>                    <On>[%:[:AA:].state.On.Value:%]
</On>
                </state>
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" asAs="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" asAs="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<Sid Name="ManagerId"/>

    </ThingModel>

  </Domain>

</Namespace>