Automatic Action trigger

In certain cases, one can set the trigger of action execution to happen automatically using the modifier update="auto" for the associated attribute.

This is useful for devices which report new values either periodically (e.g. a temperature sensor) or on some external event (e.g. RFID card is detected and read).

For example:

Action Result
#
Integer(Name: "SensorValue", update="auto", KnownBy: "PhidgetSensorAction")

Normally, all thing updates are initiated by their correspondent [cached] model instance update. That is, in order to initiate the image update Action, you set the Image attribute of camera model to null (or other value). As a result the Action executes a request/response exchange with the camera. 

Some devices, however, do not follow this communication pattern. Instead, the Action (and its workflow) needs to be put in a waiting state for the values to come in and “spontaneously” generate an event.

For example, without update="auto", if you initiate an RFID reader by modifying the associated attribute value, the Action will activate and wait for ONE reader event, update the known value of its model instance and then stop. You’ll need to modify the attribute again to trigger the Action for the next event and so on. Update=”auto” allows to automate such workflow re-activation. Every time it processes an event, the workflow will automatically restart itself to be ready for the next device event. This way you can have a continuous stream of events from the device without explicit request/re-activation after each one.

Automatic Action Example
#
Action(name: "RFIDReaderAction", documentation: "Read RFID tag and update the instance"): 
  Workflow Limit: "1", Live: "1", Timeout: "-1"): 
    Task(name: "Main", while: "true"): 
      Event(name: "Argument", as: "ActionArgument")
      Invoke(name: "ReadId", waitFor: "Argument", get: "[%:Event.Argument.URL.Value:%]")
      Output(name: "Result", as: "ActionResult"): 
        Value:
          RFID:
            [%:[%:@Output:%]Invoke.ReadId.Message.Value:%]

Note that using this option without any device throttling may result in continuous infinite loop of device updates, so it is most suitable for the devices which normally only generate events in response to external actions (e.g. detecting a RFID tag in reader’s proximity). That is, RFID workflow DOES NOT solicit device events by itself, it only listens for them. In case of IP camera snapshot, the situation would be very different because camera workflow actually asks the camera to produce one, so if you automatically restart it then it will ask again and again and again. While this may be a desirable behavior, caution must be exercised to limit the rate of such requests (for example, you can put a 1 sec delay on the get snapshot invoke to ensure that it will always wait at least a second before asking for the next snapshot).

Note that streaming of events / message from devices normally happens via Protocol Handler that is invoked. Protocol Handlers may provide additional parameter to facilitate throttling.

Stopping and Automatic Action

Stopping of automatic action trigger can be done in any one of the following ways:

  • Change the Conditional Flag Value

Automatic Action Example with Conditional Flag
#
ThingFacet(name: "PERIFSensorTF"): 
  ThingProperty(name: "TempC"): 
    ThingPort(name: "ListenSensor"): 
      string Format: "$ObjectFormat(xml)", KnownBy: "ListenAction"(name: "PropertyValue", update: "auto")
      String Cardinality: "1", Default: "Off"(name: "Switch")
      
  PERIFConnectionParams(name: "ConnParams")
  Action Documentation: "Action to listen data from sensor"(name: "ListenAction"): 
    Workflow Limit: "1", Live: "1", TimeOut: "-1"): 
      Task(name: "ListenSensorValue", While: "true"): 
        Event(name: "Argument", as: "ActionArgument")
        Invoke(name: "ListenValue", waitFor: "Argument", OnlyIf: "[%:[%:@WFRT:%]Event.Argument/TempC/ListenSensor/Switch/Value='On':%]", Scope: "Process", get: "perif://?Peripheral=[:Event.Argument.ConnParams.Peripheral.value:]&Operation=[:Event.Argument.ConnParams.Operation.value:]&InterfacePort=[:Event.Argument.ConnParams.InterfacePort.value:]&BaudRate=[:Event.Argument.ConnParams.BaudRate.value:]")
        Output(as: "ActionResult", name: "Result"): 
          Value:
            TempC:
              ListenSensor:
                PropertyValue:
                  Include:
                    Invoke.ListenValue.Message.Value

In this example we have a condition 

OnlyIf Condition on Invoke
#
OnlyIf:
	[%:[%:@WFRT:%]Event.Argument/TempC/ListenSensor/Switch/Value='On':%]

In order to stop Invoke from being trigger, we can now simply issue an update query that will set the Switch Value to off as shown below.

Update Query to Set the Switch Off
#
Update:
	TempC:
 		ListenSensor:
       		Switch: Off


  • Brute Force Approach - In some cases if you have NOT included conditional argument in your invoke statement, you can stop the action by simply deleting the instance. Make sure you take a backup copy of the model instance before delete the instance.