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:
# 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.
# 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
# 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: [%:[%:@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: 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.