

End of support notice: On May 20, 2026, AWS will end support for AWS IoT Events. After May 20, 2026, you will no longer be able to access the AWS IoT Events console or AWS IoT Events resources. For more information, see [AWS IoT Events end of support](https://docs.aws.amazon.com/iotevents/latest/developerguide/iotevents-end-of-support.html).

# Tutorials for AWS IoT Events uses cases
<a name="iotevents-tutorials"></a>

AWS IoT Events tutorials provide a collection of procedures covering various aspects of AWS IoT Events, from basic setup to more specific use cases. Each tutorial shows examples of practical scenarios, helping you build real-world skills in creating detector models, configuring inputs, setting up actions, and integrating with other AWS services to create powerful IoT solutions.

This chapter shows you how to:
+ Get help to decide which states to include in your detector model, and determine whether you need one detector instance or several.
+ Follow an example that uses the AWS CLI. 
+ Create an input to receive telemetry data from a device and a detector model to monitor and report on the state of the device that sends that data.
+ Review restrictions and limitations on inputs, detector models, and the AWS IoT Events service.
+ See a more complex example of a detector model, with comments included.

**Topics**
+ [Using AWS IoT Events to monitor your IoT devices](iotevents-how-to-use.md)
+ [Create an AWS IoT Events detector for two states using CLI](iotevents-simple-example.md)
+ [AWS IoT Events detector model restrictions and limitations](iotevents-restrictions-detector-model.md)
+ [A commented example: HVAC temperature control with AWS IoT Events](iotevents-commented-example.md)

# Using AWS IoT Events to monitor your IoT devices
<a name="iotevents-how-to-use"></a>

You can use AWS IoT Events to monitor your devices or processes, and take action based on significant events. To do so, follow these basic steps:

**Create inputs**  
You must have a way for your devices and processes to get telemetry data into AWS IoT Events. You do this by sending messages as *inputs* to AWS IoT Events. You can send messages as inputs in several ways:  
+ Use the [ BatchPutMessage](https://docs.aws.amazon.com/iotevents/latest/apireference/API_iotevents-data_BatchPutMessage.html) operation.
+ Define an [`iotEvents` rule‐action](https://docs.aws.amazon.com/iot/latest/developerguide/iotevents-rule-action.html) for the [AWS IoT Core rules engine](https://docs.aws.amazon.com/iot/latest/developerguide/iot-rule-actions.html). The rule‐action forwards message data from your input into AWS IoT Events. 
+ In AWS IoT Analytics, use the [CreateDataset](https://docs.aws.amazon.com/iotanalytics/latest/userguide/automate.html#aws-iot-analytics-automate-create-dataset) operation to create a data set with `contentDeliveryRules`. These rules specify the AWS IoT Events input where data set contents are sent automatically.
+ Define an [iotEvents action](https://docs.aws.amazon.com/iotevents/latest/apireference/API_IotEventsAction.html) in an AWS IoT Events detector model's `onInput`, `onExit` or `transitionEvents` event. Information about the detector model instance and the event that initiated the action are fed back into the system as an input with the name that you specify.
Before your devices start sending data in this way, you must define one or more inputs. To do so, give each input a name and specify which fields in the incoming message data the input monitors. AWS IoT Events receives its input, in the form of JSON payload, from many sources. Each input can be acted on by itself, or combined with other inputs to detect more complex events. 

**Create a detector model**  
Define a *detector model* (a model of your equipment or process) using *states*. For each state, you define conditional (Boolean) logic that evaluates the incoming inputs to detect significant events. When an event is detected, it can change the state or initiate custom-built or predefined actions using other AWS services. You can define additional events that initiate actions when entering or exiting a state and, optionally, when a condition is met.   
In this tutorial, you send an Amazon SNS message as the action when the model enters or exits a certain state.

**Monitor a device or process**  
If you're monitoring several devices or processes, you specify a field in each input that identifies the particular device or process the input comes from. (See the `key` field in `CreateDetectorModel`.) When a new device is identified (a new value is seen in the input field identified by the `key`), a detector is created. (Each detector is an instance of the detector model.) Then the new detector continues responding to inputs coming from that device until its detector model is updated or deleted.  
If you're monitoring a single process (even if several devices or subprocesses are sending inputs), you don't specify a unique identifying `key` field. In this case, a single detector (instance) is created when the first input arrives.

**Send messages as inputs to your detector model**  
There are several ways to send a message from a device or process as an input into an AWS IoT Events detector that don't require you to perform additional formatting on the message. In this tutorial, you use the AWS IoT console to write an [AWS IoT Events action](https://docs.aws.amazon.com/iot/latest/developerguide/iot-rule-actions.html#iotevents-rule) rule for the AWS IoT Core rules engine that forwards your message data into AWS IoT Events. To do this, you identify the input by name. Then you continue to use the AWS IoT console to generate some messages that are forwarded as inputs to AWS IoT Events.

## How do you know which states you need in a detector model?
<a name="how-to-use-iotevents-what-states"></a>

To determine what states your detector model should have, first decide what actions you can take. For example, if your automobile runs on gasoline, you look at the fuel gauge when you start a trip to see if you need to refuel. Here you have one action: tell the driver to "go get gas". Your detector model needs two states: "car doesn't need fuel", and "car does need fuel". In general, you want to define one state for each possible action, plus one more for when no action is required. This works even if the action itself is more complicated. For example, you might want to look up and include information on where to find the closest gas station, or the cheapest price, but you do this when you send the message to "go get gas".

To decide which state to enter next, you look at inputs. Inputs contain the information that you need to decide what state you should be in. To create an input, you select one or more fields in a message sent by your device or process that help you decide. In this example, you need one input that tells you the current fuel level ("percent full"). Maybe your car is sending you several different messages, each with several different fields. To create this input, you must select the message and the field that reports the current gas gauge level. The length of the trip you are about to take ("distance to destination") can be hardcoded to keep things simple; you can use your average trip length. You'll do some calculations based on the input (how many gallons does that percent full translate to? is the average trip length greater than the miles you can travel, given the gallons you have and your average "miles per gallon"). You perform these calculations and send messages in *events*.

So far you have two states and one input. You need an event in the first state that performs the calculations based on the input and decides whether to go to the second state. That is a transition event. (`transitionEvents` are in a state's `onInput` event list. *On* receiving an *input* in this first state, the *event* performs a *transition* to the second state, if the event's `condition` is met.) When you reach the second state, you send the message as soon as you enter the state. (You use an `onEnter` event. On entering the second state, this event sends the message. No need to wait for another input to arrive.) There are other types of events, but that's all you need for a simple example.

The other types of events are `onExit` and `onInput`. As soon as an input is received, and the condition is met, an `onInput` event performs the specified actions. When an operation exits its current state, and the condition is met, the `onExit` event performs the specified actions.

Are you missing anything? Yes, how do you get back to the first "car doesn't need fuel" state? After you fill your gas tank, the input shows a full tank. In your second state you need a transition event back to the first state that happens when the input is received (in the second state's `onInput:` events). It should transition back to the first state if its calculations show you now have enough gas to get you where you want to go.

That's the basics. Some detector models get more complex by adding states that reflect important inputs, not just possible actions. For example, you might have three states in a detector model that keeps track of the temperature: a "normal" state, a "too hot" state, and a "potential problem" state. You transition to the potential problem state when the temperature rises above a certain level, but hasn't become too hot yet. You don't want to send an alarm unless it stays at this temperature for more than 15 minutes. If the temperature returns to normal before then, the detector transitions back to the normal state. If the timer expires, the detector transitions to the too hot state and sends an alarm, just to be cautious. You could do the same thing using variables and a more complex set of event conditions. But often it is easier to use another state to, in effect, store the results of your calculations.

## How do you know if you need one instance of a detector or several?
<a name="how-to-use-iotevents-instances"></a>

To decide how many instances you need, ask yourself "What are you interested in knowing?" Let's say you want to know what the weather is like today. Is it raining (state)? Do you need to take an umbrella (action)? You can have a sensor that reports the temperature, another that reports the humidity, and others that report the barometric pressure, wind speed and direction, and precipitation. But you must monitor all these sensors together to determine the state of the weather (rain, snow, overcast, sunny) and the appropriate action to take (grab an umbrella or apply sunscreen). In spite of the number of sensors, you want one detector instance to monitor the state of the weather and inform you which action to take.

But if you're the weather forecaster for your region, you might have multiple instances of such sensor arrays, situated at different locations throughout the region. People at each location need to know what the weather is like in that location. In this case, you need multiple instances of your detector. The data reported by each sensor in each location must include a field that you have designated as the `key` field. This field enables AWS IoT Events to create a detector instance for the area, and then to continue to route this information to that detector instance as it continues to arrive. No more ruined hair or sunburned noses\$1

Essentially, you need one detector instance if you have one situation (one process or one location) to monitor. If you have many situations (locations, processes) to monitor, you need multiple detector instances.

# Create an AWS IoT Events detector for two states using CLI
<a name="iotevents-simple-example"></a>

In this example, we call the AWS IoT Events APIs using AWS CLI commands to create a detector that models two states of an engine: a normal state and an over-pressure condition.

When the measured pressure in the engine exceeds a certain threshold, the model transitions to the over-pressure state and sends an Amazon Simple Notification Service (Amazon SNS) message to alert a technician to the condition. When the pressure drops below the threshold for three consecutive pressure readings, the model returns to the normal state and sends another Amazon SNS message as a confirmation that the condition has cleared. We require three consecutive readings below the pressure threshold to eliminate possible stuttering of over-pressure/normal messages in case of a nonlinear recovery phase or a one-off anomalous recovery reading.

The following is an overview of the steps to create the detector.

**Create *inputs*.**  
To monitor your devices and processes, they must have a way to get telemetry data into AWS IoT Events. This is done by sending messages as *inputs* to AWS IoT Events. You can do this in several ways:  
+ Use the [ BatchPutMessage](https://docs.aws.amazon.com/iotevents/latest/apireference/API_iotevents-data_BatchPutMessage.html) operation. This method is easy but requires that your devices or processes are able to access the AWS IoT Events API through an SDK or the AWS CLI.
+ In AWS IoT Core, write an [AWS IoT Events action](https://docs.aws.amazon.com/iot/latest/developerguide/iot-rule-actions.html#iotevents-rule) rule for the AWS IoT Core rules engine that forwards your message data into AWS IoT Events. This identifies the input by name. Use this method if your devices or processes can, or already are, sending messages through AWS IoT Core. This method generally requires less computing power from a device.
+ In AWS IoT Analytics, use the [ CreateDataset](https://docs.aws.amazon.com/iotanalytics/latest/userguide/automate.html#aws-iot-analytics-automate-create-dataset) operation to create a data set with `contentDeliveryRules` that specify the AWS IoT Events input, where data set contents are sent automatically. Use this method if you want to control your devices or processes based on data aggregated or analyzed in AWS IoT Analytics.
Before your devices can send data in this way, you must define one or more inputs. To do so, give each input a name and specify which fields in the incoming message data that the input monitors.

**Create a detector model**  
Create a *detector model* (a model of your equipment or process) using *states*. For each state, define conditional (Boolean) logic that evaluates the incoming inputs to detect significant events. When an event is detected, it can change the state or initiate custom-built or predefined actions using other AWS services. You can define additional events that initiate actions when entering or exiting a state and, optionally, when a condition is met.

**Monitor several devices or processes**  
If you're monitoring several devices or processes and you want to keep track of each of them separately, specify a field in each input that identifies the particular device or process the input comes from. See the `key` field in `CreateDetectorModel`. When a new device is identified (a new value is seen in the input field identified by the `key`), a detector instance is created. The new detector instance continues to respond to inputs coming from that particular device until its detector model is updated or deleted. You have as many unique detectors (instances) as there are unique values in input `key` fields.

**Monitor a single device or process**  
If you're monitoring a single process (even if several devices or subprocesses are sending inputs), you don't specify a unique identifying `key` field. In this case, a single detector (instance) is created when the first input arrives. For example, you might have temperature sensors in each room of a house, but only one HVAC unit to heat or cool the entire house. So you can only control this as a single process, even if each room occupant wants their vote (input) to prevail.

**Send messages from your devices or processes as inputs to your detector model**  
We described the several ways to send a message from a device or process as an input into an AWS IoT Events detector in *inputs*. After you created the inputs and build the detector model, you're ready to start sending data.  
When you create a detector model, or update an existing one, it takes several minutes before the new or updated detector model begins receiving messages and creating detectors (instances). If the detector model is updated, during this time you might continue to see behavior based on the previous version.

**Topics**
+ [Create an AWS IoT Events input to capture device data](iotevents-create-input.md)
+ [Create a detector model to represent device states in AWS IoT Events](iotevents-create-detector.md)
+ [Send messages as inputs to a detector in AWS IoT Events](iotevents-batch-put-messages.md)

# Create an AWS IoT Events input to capture device data
<a name="iotevents-create-input"></a>

When setting up inputs for AWS IoT Events, you can leverage the AWS CLI to define how your devices communicate sensor data. For example, if your devices send JSON-formatted messages with motor identifiers and sensor readings, you can capture this data by creating an input that maps specific attributes from the messages, such as the pressure and the motor ID. The process starts by defining an input in a JSON file, specifying the relevant data points, and using the AWS CLI to register the input for AWS IoT Events. This enables AWS IoT to monitor and respond to critical conditions based on real-time sensor data.

As an example, suppose your devices send messages with the following format.

```
{
  "motorid": "Fulton-A32",
  "sensorData": {
    "pressure": 23,
    "temperature": 47
  }
}
```

You can create an input to capture the `pressure` data and the `motorid` (that identifies the specific device that sent the message) using the following AWS CLI command.

```
aws iotevents create-input  --cli-input-json file://pressureInput.json 
```

The file `pressureInput.json` contains the following.

```
{
  "inputName": "PressureInput",
  "inputDescription": "Pressure readings from a motor",
  "inputDefinition": {
    "attributes": [
      { "jsonPath": "sensorData.pressure" },
      { "jsonPath": "motorid" }
    ]
  }
}
```

When you create your own inputs, remember to first collect example messages as JSON files from your devices or processes. You can use them to create an input from the console or the CLI.

# Create a detector model to represent device states in AWS IoT Events
<a name="iotevents-create-detector"></a>

In [Create an AWS IoT Events input to capture device data](iotevents-create-input.md), you created an `input` based on a message that reports pressure data from a motor. To continue with the example, here is a detector model that responds to an over-pressure event in a motor.

You create two states: "`Normal`", and "`Dangerous`". Each detector (instance) enters the "`Normal`" state when it's created. The instance is created when an input with a unique value for the `key` "`motorid`" arrives.

If the detector instance receives a pressure reading of 70 or greater, it enters the "`Dangerous`" state and sends an Amazon SNS message as a warning. If the pressure readings return to normal (less than 70) for three consecutive inputs, the detector returns to the "`Normal`" state and sends another Amazon SNS message as an all clear.

This example detector model assumes you have created two Amazon SNS topics whose Amazon Resource Names (ARNs) are shown in the definition as `"targetArn": "arn:aws:sns:us-east-1:123456789012:underPressureAction"` and `"targetArn": "arn:aws:sns:us-east-1:123456789012:pressureClearedAction"`. 

For more information, see the [Amazon Simple Notification Service Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/) and, more specifically, the documentation of the [CreateTopic](https://docs.aws.amazon.com/sns/latest/api/API_CreateTopic.html) operation in the *Amazon Simple Notification Service API Reference*.

This example also assumes you have created an AWS Identity and Access Management (IAM) role with appropriate permissions. The ARN of this role is shown in the detector model definition as `"roleArn": "arn:aws:iam::123456789012:role/IoTEventsRole"`. Follow the steps in [Setting up permissions for AWS IoT Events](iotevents-permissions.md) to create this role and copy the ARN of the role in the appropriate place in the detector model definition.

You can create the detector model using the following AWS CLI command.

```
aws iotevents create-detector-model  --cli-input-json file://motorDetectorModel.json
```

The file `"motorDetectorModel.json"` contains the following.

```
{
  "detectorModelName": "motorDetectorModel",
  "detectorModelDefinition": {
    "states": [
      {
        "stateName": "Normal",
        "onEnter": {
          "events": [
            {
              "eventName": "init",
              "condition": "true",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "pressureThresholdBreached",
                    "value": "0"
                  }
                }
              ]
            }
          ]
        },
        "onInput": {
          "transitionEvents": [
            {
              "eventName": "Overpressurized",
              "condition": "$input.PressureInput.sensorData.pressure > 70",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "pressureThresholdBreached",
                    "value": "$variable.pressureThresholdBreached + 3"
                  }
                }
              ],
              "nextState": "Dangerous"
            }
          ]
        }
      }, 
      {
        "stateName": "Dangerous",
        "onEnter": {
          "events": [
            {
              "eventName": "Pressure Threshold Breached",
              "condition": "$variable.pressureThresholdBreached > 1",
              "actions": [
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-east-1:123456789012:underPressureAction"
                  }
                }
              ]
            }
          ]
        },
        "onInput": {
          "events": [
            {
              "eventName": "Overpressurized",
              "condition": "$input.PressureInput.sensorData.pressure > 70",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "pressureThresholdBreached",
                    "value": "3"
                  }
                }
              ]
            },
            {
              "eventName": "Pressure Okay",
              "condition": "$input.PressureInput.sensorData.pressure <= 70",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "pressureThresholdBreached",
                    "value": "$variable.pressureThresholdBreached - 1"
                  }
                }
              ]
            }
          ],
          "transitionEvents": [
            {
              "eventName": "BackToNormal",
              "condition": "$input.PressureInput.sensorData.pressure <= 70 && $variable.pressureThresholdBreached <= 1",
              "nextState": "Normal"
            }
          ]
        },
        "onExit": {
          "events": [
            {
              "eventName": "Normal Pressure Restored",
              "condition": "true",
              "actions": [
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-east-1:123456789012:pressureClearedAction"
                  }
                }
              ]
            }
          ]
        }
      }
    ],
    "initialStateName": "Normal"
  },
  "key" : "motorid",
  "roleArn": "arn:aws:iam::123456789012:role/IoTEventsRole"
}
```

# Send messages as inputs to a detector in AWS IoT Events
<a name="iotevents-batch-put-messages"></a>

You have now defined an input that identifies the important fields in messages sent from a device (see [Create an AWS IoT Events input to capture device data](iotevents-create-input.md)). In the previous section, you created a `detector model` that responds to an over-pressure event in a motor (see [Create a detector model to represent device states in AWS IoT Events](iotevents-create-detector.md)).

To complete the example, send messages from a device (in this case a computer with the AWS CLI installed) as inputs to the detector. 

**Note**  
When you create a detector model or update an existing one, it takes several minutes before the new or updated detector model begins to receive messages and create detectors (instances). If you update the detector model, during this time you might continue to see behavior based on the previous version.

Use the following AWS CLI command to send a message with data that breaches the threshold.

```
aws iotevents-data batch-put-message --cli-input-json file://highPressureMessage.json --cli-binary-format raw-in-base64-out
```

The file "`highPressureMessage.json`" contains the following.

```
{
  "messages": [
    {
      "messageId": "00001",
      "inputName": "PressureInput",
      "payload": "{\"motorid\": \"Fulton-A32\", \"sensorData\": {\"pressure\": 80, \"temperature\": 39} }"
    }
  ]
}
```

You must change the `messageId` in each message sent. If you don't change it, the AWS IoT Events system deduplicates the messages. AWS IoT Events ignores a message if it has the same `messageID` as another message that was sent within the last five minutes.

At this point, a detector (instance) is created to monitor events for the motor `"Fulton-A32"`. This detector enters the `"Normal"` state when it's created. But because we sent a pressure value above the threshold, it immediately transitions to the `"Dangerous"` state. As it does so, the detector sends a message to the Amazon SNS endpoint whose ARN is `arn:aws:sns:us-east-1:123456789012:underPressureAction`.

Run the following AWS CLI command to send a message with data that is beneath the pressure threshold.

```
aws iotevents-data batch-put-message --cli-input-json file://normalPressureMessage.json --cli-binary-format raw-in-base64-out
```

The file `normalPressureMessage.json` contains the following.

```
{
  "messages": [
    {
      "messageId": "00002",
      "inputName": "PressureInput",
      "payload": "{\"motorid\": \"Fulton-A32\", \"sensorData\": {\"pressure\": 60, \"temperature\": 29} }"
    }
  ]
}
```

You must change the `messageId` in the file each time you invoke the `BatchPutMessage` command within a five minute period. Send the message two more times. After the message is sent three times, the detector (instance) for the motor "`Fulton-A32`" sends a message to the Amazon SNS endpoint `"arn:aws:sns:us-east-1:123456789012:pressureClearedAction"` and reenters the `"Normal"` state.

**Note**  
You can send multiple messages at one time with `BatchPutMessage`. However, the order in which these messages are processed isn't guaranteed. To guarantee messages (inputs) are processed in order, send them one at a time and wait for a successful response each time the API is called.

The following are example SNS message payloads created by the detector model example described in this section.

**on event "Pressure Threshold Breached"**

```
IoT> {
  "eventTime":1558129816420,
  "payload":{
    "actionExecutionId":"5d7444df-a655-3587-a609-dbd7a0f55267",
    "detector":{
      "detectorModelName":"motorDetectorModel",
      "keyValue":"Fulton-A32",
      "detectorModelVersion":"1"
    },
    "eventTriggerDetails":{
      "inputName":"PressureInput",
      "messageId":"00001",
      "triggerType":"Message"
    },
    "state":{
      "stateName":"Dangerous",
      "variables":{
        "pressureThresholdBreached":3
      },
      "timers":{}
    }
  },
  "eventName":"Pressure Threshold Breached"
}
```

**on event "Normal Pressure Restored"**

```
IoT> {
  "eventTime":1558129925568,
  "payload":{
    "actionExecutionId":"7e25fd38-2533-303d-899f-c979792a12cb",
    "detector":{
      "detectorModelName":"motorDetectorModel",
      "keyValue":"Fulton-A32",
      "detectorModelVersion":"1"
    },
    "eventTriggerDetails":{
      "inputName":"PressureInput",
      "messageId":"00004",
      "triggerType":"Message"
    },
    "state":{
      "stateName":"Dangerous",
      "variables":{
        "pressureThresholdBreached":0
      },
      "timers":{}
    }
  },
  "eventName":"Normal Pressure Restored"
}
```

If you have defined any timers, their current state is also shown in the SNS message payloads.

The message payloads contain information about the state of the detector (instance) at the time the message was sent (that is, at the time the SNS action was run). You can use the [https://docs.aws.amazon.com/iotevents/latest/apireference/API_iotevents-data_DescribeDetector.html](https://docs.aws.amazon.com/iotevents/latest/apireference/API_iotevents-data_DescribeDetector.html) operation to get similar information about the state of the detector.

# AWS IoT Events detector model restrictions and limitations
<a name="iotevents-restrictions-detector-model"></a>

The following things are important to consider when creating a detector model.

**How to use the `actions` field**  
The `actions` field is a list of objects. You can have more than one object, but only one action is allowed in each object.  

**Example**  

```
              "actions": [
                {
                  "setVariable": {
                    "variableName": "pressureThresholdBreached",
                    "value": "$variable.pressureThresholdBreached - 1"
                  }
                }
                {
                  "setVariable": {
                    "variableName": "temperatureIsTooHigh",
                    "value": "$variable.temperatureIsTooHigh - 1"
                  }
                }
              ]
```

**How to use the `condition` field**  
The `condition` is required for `transitionEvents` and is optional in other cases.  
If the `condition` field isn't present, it's equivalent to `"condition": true`.  
The result of the evaluation of a condition expression should be a Boolean value. If the result isn't a Boolean value, it's equivalent to `false` and won't initiate the `actions` or transition to the `nextState` specified in the event.

**Availability of variable values**  
By default, if the value of a variable is set in an event, its new value isn't available or used to evaluate conditions in other events in the same group. The new value isn't available or used in an event condition in the same `onInput`, `onEnter` or `onExit` field.  
Set the `evaluationMethod` parameter in the detector model definition to change this behavior. When the `evaluationMethod` is set to `SERIAL`, variables are updated and event conditions are evaluated in the order that the events are defined. Otherwise, when the `evaluationMethod` is set to `BATCH` or defaults to it, variables within a state are updated and events within a state are performed only after all event conditions are evaluated.  
In the `"Dangerous"` state, in the `onInput` field, `"$variable.pressureThresholdBreached"` is decremented by one in the `"Pressure Okay"` event when the condition is met (when the current input has pressure less than or equal to 70).  

```
            {
              "eventName": "Pressure Okay",
              "condition": "$input.PressureInput.sensorData.pressure <= 70",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "pressureThresholdBreached",
                    "value": "$variable.pressureThresholdBreached - 1"
                  }
                }
              ]
            }
```
The detector should transition back to the `"Normal"` state when `"$variable.pressureThresholdBreached"` reaches 0 (that is, when the detector has received three contiguous pressure readings less than or equal to 70). The `"BackToNormal"` event in `transitionEvents` must test that `"$variable.pressureThresholdBreached"` is less than or equal to 1 (not 0), and also verify again that the current value given by `"$input.PressureInput.sensorData.pressure"` is less than or equal to 70.  

```
          "transitionEvents": [
            {
              "eventName": "BackToNormal",
              "condition": "$input.PressureInput.sensorData.pressure <= 70 && $variable.pressureThresholdBreached <= 1",
              "nextState": "Normal"
            }
          ]
```
Otherwise, if the condition tests for only the value of the variable, two normal readings followed by an over-pressure reading would fulfill the condition and transition back to the `"Normal"` state. The condition is looking at the value that `"$variable.pressureThresholdBreached"` was given during the previous time an input was processed. The value of the variable is reset to 3 in the `"Overpressurized"` event, but remember that this new value is not yet available to any `condition`.  
By default, every time a control enters the `onInput` field, a `condition` can only see the value of a variable as it was at the start of processing the input, before it's changed by any actions specified in `onInput`. The same is true for `onEnter` and `onExit`. Any change made to a variable when we enter or exit the state isn't available to other conditions specified in the same `onEnter` or `onExit` fields.

**Latency when updating a detector model**  
If you update, delete, and recreate a detector model (see [UpdateDetectorModel](https://docs.aws.amazon.com/iotevents/latest/apireference/API_UpdateDetectorModel.html)), there is some delay before all spawned detectors (instances) are deleted and the new model is used to recreate the detectors. They are recreated after the new detector model takes effect and new inputs arrive. During this time inputs might continue to be processed by the detectors spawned by the previous version of the detector model. During this period, you might continue to receive alerts defined by the previous detector model.

**Spaces in input keys**  
Spaces are allowed in input keys, but references to the key must be enclosed in backticks, both in the definition of the input attribute and when the value of the key is referenced in an expression. For example, given a message payload like the following:  

```
{
  "motor id": "A32",
  "sensorData" {
    "motor pressure": 56,
    "motor temperature": 39
  }
}
```
Use the following to define the input.  

```
{
  "inputName": "PressureInput",
  "inputDescription": "Pressure readings from a motor",
  "inputDefinition": {
    "attributes": [
      { "jsonPath": "sensorData.`motor pressure`" },
      { "jsonPath": "`motor id`" }
    ]
  }
}
```
In a conditional expression, you must refer to the value of any such key using backticks also.  

```
$input.PressureInput.sensorData.`motor pressure`
```

# A commented example: HVAC temperature control with AWS IoT Events
<a name="iotevents-commented-example"></a>

Some of the following example JSON files have comments inline, which makes them invalid JSON. Complete versions of these examples, without comments, are available at [Example: Using HVAC temperature control with AWS IoT Events](iotevents-examples-hvac.md).

This example implements a thermostat control model that gives you the ability to do the following.
+ Define just one detector model that can be used to monitor and control multiple areas. A detector instance is created for each area.
+ Ingest temperature data from multiple sensors in each control area.
+ Change the temperature set point for an area.
+ Set operational parameters for each area and reset these parameters while the instance is in use.
+ Dynamically add or delete sensors from an area.
+ Specify a minimum runtime to protect heating and cooling units.
+ Reject anomalous sensor readings.
+ Define emergency set points that immediately engage heating or cooling if any one sensor reports a temperature above or below a given threshold.
+ Report anomalous readings and temperature spikes.

**Topics**
+ [Input definitions for detector models in AWS IoT Events](iotevents-commented-example-inputs.md)
+ [Create an AWS IoT Events detector model definition](iotevents-commented-example-detector-model.md)
+ [Use BatchUpdateDetector to update an AWS IoT Events detector model](iotevents-commented-example-batch-update-detector.md)
+ [Use BatchPutMessage for inputs in AWS IoT Events](iotevents-commented-example-input-usage-examples.md)
+ [Ingest MQTT messages in AWS IoT Events](iotevents-commented-example-ingest-mqtt.md)
+ [Generate Amazon SNS messages in AWS IoT Events](iotevents-commented-example-generated-sns.md)
+ [Configure the DescribeDetector API in AWS IoT Events](iotevents-commented-example-describe-detector.md)
+ [Use the AWS IoT Core rules engine for AWS IoT Events](iotevents-commented-examples-iot-rules-examples.md)

# Input definitions for detector models in AWS IoT Events
<a name="iotevents-commented-example-inputs"></a>

We want to create one detector model that we can use to monitor and control the temperature in several different areas. Each area can have several sensors that report the temperature. We assume each area is served by one heating unit and one cooling unit that can be turned on or off to control the temperature in the area. Each area is controlled by one detector instance.

Because the different areas we monitor and control might have different characteristics that demand different control parameters, we define the `'seedTemperatureInput'` to provide those parameters for each area. When we send one of these input messages to AWS IoT Events, a new detector model instance is created that has the parameters we want to use in that area. Here's the definition of that input.

CLI command:

```
aws iotevents create-input --cli-input-json file://seedInput.json
```

File: `seedInput.json`

```
{
  "inputName": "seedTemperatureInput",
  "inputDescription": "Temperature seed values.",
  "inputDefinition": {
    "attributes": [
      { "jsonPath": "areaId" },
      { "jsonPath": "desiredTemperature" },
      { "jsonPath": "allowedError" },
      { "jsonPath": "rangeHigh" },
      { "jsonPath": "rangeLow" },
      { "jsonPath": "anomalousHigh" },
      { "jsonPath": "anomalousLow" },
      { "jsonPath": "sensorCount" },
      { "jsonPath": "noDelay" }
    ]
  }
}
```

Response:

```
{
    "inputConfiguration": {
        "status": "ACTIVE", 
        "inputArn": "arn:aws:iotevents:us-west-2:123456789012:input/seedTemperatureInput", 
        "lastUpdateTime": 1557519620.736, 
        "creationTime": 1557519620.736, 
        "inputName": "seedTemperatureInput", 
        "inputDescription": "Temperature seed values."
    }
}
```

**Notes**
+ A new detector instance is created for each unique `'areaId'` received in any message. See the `'key'` field in the `'areaDetectorModel'` definition.
+ The average temperature can vary from the `'desiredTemperature'` by the `'allowedError'` before the heating or cooling units are activated for the area.
+ If any sensor reports a temperature above the `'rangeHigh'`, the detector reports a spike and immediately starts the cooling unit.
+ If any sensor reports a temperature below the `'rangeLow'`, the detector reports a spike and immediately starts the heating unit.
+ If any sensor reports a temperature above the `'anomalousHigh'` or below the `'anomalousLow'`, the detector reports an anomalous sensor reading, but ignores the reported temperature reading.
+ The `'sensorCount'` tells the detector how many sensors are reporting for the area. The detector calculates the average temperature in the area by giving the appropriate weight factor to each temperature reading it receives. Because of this, the detector won't have to keep track of what each sensor reports, and the number of sensors can be changed dynamically, as needed. However, if an individual sensor goes offline, the detector won't know this or make allowances for it. We recommend that you create another detector model specifically for monitoring the connection status of each sensor. Having two complementary detector models simplifies the design of both.
+ The `'noDelay'` value can be `true` or `false`. After a heating or cooling unit is turned on, it should remain on for a certain minimum time to protect the integrity of the unit and lengthen its operating life. If `'noDelay'` is set to `false`, the detector instance enforces a delay before it turns off the cooling and heating units, to ensure that they are run for the minimum time. The number of seconds of delay has been hardcoded in the detector model definition because we are unable to use a variable value to set a timer.

The `'temperatureInput'` is used to transmit sensor data to a detector instance.

CLI command:

```
aws iotevents create-input --cli-input-json file://temperatureInput.json
```

File: `temperatureInput.json`

```
{
  "inputName": "temperatureInput",
  "inputDescription": "Temperature sensor unit data.",
  "inputDefinition": {
    "attributes": [
      { "jsonPath": "sensorId" },
      { "jsonPath": "areaId" },
      { "jsonPath": "sensorData.temperature" }
    ]
  }
}
```

Response:

```
{
    "inputConfiguration": {
        "status": "ACTIVE", 
        "inputArn": "arn:aws:iotevents:us-west-2:123456789012:input/temperatureInput", 
        "lastUpdateTime": 1557519707.399, 
        "creationTime": 1557519707.399, 
        "inputName": "temperatureInput", 
        "inputDescription": "Temperature sensor unit data."
    }
}
```

**Notes**
+ The `'sensorId'` isn't used by an example detector instance to control or monitor a sensor directly. It's automatically passed into notifications sent by the detector instance. From there, it can be used to identify the sensors that are failing (for example, a sensor that regularly sends anomalous readings might be about to fail), or that have gone offline (when it's used as an input to an additional detector model that monitors the device's heartbeat). The `'sensorId'` can also help identify warm or cold zones in an area if its readings regularly differ from the average.
+ The `'areaId'` is used to route the sensor's data to the appropriate detector instance. A detector instance is created for each unique `'areaId'` received in any message. See the `'key'` field in the `'areaDetectorModel'` definition.

# Create an AWS IoT Events detector model definition
<a name="iotevents-commented-example-detector-model"></a>

The `'areaDetectorModel'` example has comments inline.

CLI command:

```
aws iotevents create-detector-model --cli-input-json file://areaDetectorModel.json
```

File: `areaDetectorModel.json`

```
{
  "detectorModelName": "areaDetectorModel",
  "detectorModelDefinition": {
    "states": [
      {
        "stateName": "start",
        // In the 'start' state we set up the operation parameters of the new detector instance. 
        //   We get here when the first input message arrives. If that is a 'seedTemperatureInput' 
        //   message, we save the operation parameters, then transition to the 'idle' state. If 
        //   the first message is a 'temperatureInput', we wait here until we get a 
        //   'seedTemperatureInput' input to ensure our operation parameters are set. We can 
        //   also reenter this state using the 'BatchUpdateDetector' API. This enables us to
        //   reset the operation parameters without needing to delete the detector instance.
        "onEnter": {
          "events": [
            {
              "eventName": "prepare",
              "condition": "true",
              "actions": [
                {
                  "setVariable": {
                    // initialize 'sensorId' to an invalid value (0) until an actual sensor reading 
                    //   arrives
                    "variableName": "sensorId",
                    "value": "0"
                  }
                },
                {
                  "setVariable": {
                    // initialize 'reportedTemperature' to an invalid value (0.1) until an actual 
                    //   sensor reading arrives
                    "variableName": "reportedTemperature",
                    "value": "0.1"
                  }
                },
                {
                  "setVariable": {
                    // When using 'BatchUpdateDetector' to re-enter this state, this variable should 
                    //   be set to true.
                    "variableName": "resetMe",
                    "value": "false"
                  }
                }
              ]
            }
          ]
        },
        "onInput": {
          "transitionEvents": [
            {
              "eventName": "initialize",
              "condition": "$input.seedTemperatureInput.sensorCount > 0",
              // When a 'seedTemperatureInput' message with a valid 'sensorCount' is received,
              //   we use it to set the operational parameters for the area to be monitored.
              "actions": [
                { 
                  "setVariable": {
                    "variableName": "rangeHigh",
                    "value": "$input.seedTemperatureInput.rangeHigh"
                  }
                },
                { 
                  "setVariable": {
                    "variableName": "rangeLow",
                    "value": "$input.seedTemperatureInput.rangeLow"
                  }
                },
                { 
                  "setVariable": {
                    "variableName": "desiredTemperature",
                    "value": "$input.seedTemperatureInput.desiredTemperature"
                  }
                },
                { 
                  "setVariable": {
                    // Assume we're at the desired temperature when we start.
                    "variableName": "averageTemperature",
                    "value": "$input.seedTemperatureInput.desiredTemperature"
                  }
                },
                { 
                  "setVariable": {
                    "variableName": "allowedError",
                    "value": "$input.seedTemperatureInput.allowedError"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "anomalousHigh",
                    "value": "$input.seedTemperatureInput.anomalousHigh"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "anomalousLow",
                    "value": "$input.seedTemperatureInput.anomalousLow"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "sensorCount",
                    "value": "$input.seedTemperatureInput.sensorCount"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "noDelay",
                    "value": "$input.seedTemperatureInput.noDelay == true"
                  }
                }
              ],
              "nextState": "idle"
            },
            {
              "eventName": "reset",
              "condition": "($variable.resetMe == true) && ($input.temperatureInput.sensorData.temperature < $variable.anomalousHigh && $input.temperatureInput.sensorData.temperature > $variable.anomalousLow)",
              // This event is triggered if we have reentered the 'start' state using the 
              //   'BatchUpdateDetector' API with 'resetMe' set to true. When we reenter using
              //   'BatchUpdateDetector' we do not automatically continue to the 'idle' state, but
              //   wait in 'start' until the next input message arrives. This event enables us to 
              //   transition to 'idle' on the next valid 'temperatureInput' message that arrives.
              "actions": [
                {
                  "setVariable": {
                    "variableName": "averageTemperature",
                    "value": "((($variable.averageTemperature * ($variable.sensorCount - 1)) + $input.temperatureInput.sensorData.temperature) / $variable.sensorCount)"
                  }
                }
              ],
              "nextState": "idle"
            }
          ]
        },
        "onExit": {
          "events": [
            {
              "eventName": "resetHeatCool",
              "condition": "true",
              // Make sure the heating and cooling units are off before entering 'idle'.
              "actions": [
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:heatOff"
                  }
                },
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:coolOff"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Heating/Off"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Cooling/Off"
                  }
                }
              ]
            }
          ]
        }
      },


      {
        "stateName": "idle",
        "onInput": {
          "events": [
            {
              "eventName": "whatWasInput",
              "condition": "true",
              // By storing the 'sensorId' and the 'temperature' in variables, we make them 
              //   available in any messages we send out to report anomalies, spikes, or just
              //   if needed for debugging.
              "actions": [
                {
                  "setVariable": {
                    "variableName": "sensorId",
                    "value": "$input.temperatureInput.sensorId"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "reportedTemperature",
                    "value": "$input.temperatureInput.sensorData.temperature"
                  }
                }
              ]
            },
            {
              "eventName": "changeDesired",
              "condition": "$input.seedTemperatureInput.desiredTemperature != $variable.desiredTemperature",
              // This event enables us to change the desired temperature at any time by sending a
              //   'seedTemperatureInput' message. But note that other operational parameters are not
              //   read or changed.
              "actions": [
                { 
                  "setVariable": {
                    "variableName": "desiredTemperature",
                    "value": "$input.seedTemperatureInput.desiredTemperature"
                  }
                }
              ]
            },
            {
              "eventName": "calculateAverage",
              "condition": "$input.temperatureInput.sensorData.temperature < $variable.anomalousHigh && $input.temperatureInput.sensorData.temperature > $variable.anomalousLow",
              // If a valid temperature reading arrives, we use it to update the average temperature.
              //   For simplicity, we assume our sensors will be sending updates at about the same rate,
              //   so we can calculate an approximate average by giving equal weight to each reading we receive.
              "actions": [
                {
                  "setVariable": {
                    "variableName": "averageTemperature",
                    "value": "((($variable.averageTemperature * ($variable.sensorCount - 1)) + $input.temperatureInput.sensorData.temperature) / $variable.sensorCount)"
                  }
                }
              ]
            }
          ],
          "transitionEvents": [
            {
              "eventName": "anomalousInputArrived",
              "condition": "$input.temperatureInput.sensorData.temperature >= $variable.anomalousHigh || $input.temperatureInput.sensorData.temperature <= $variable.anomalousLow",
              // When an anomalous reading arrives, send an MQTT message, but stay in the current state.
              "actions": [
                { 
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/anomaly"
                  }
                }
              ],
              "nextState": "idle"
            },

            {
              "eventName": "highTemperatureSpike",
              "condition": "$input.temperatureInput.sensorData.temperature > $variable.rangeHigh",
              // When even a single temperature reading arrives that is above the 'rangeHigh', take
              //   emergency action to begin cooling, and report a high temperature spike.
              "actions": [
                {
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/spike"
                  }
                },
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:coolOn"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Cooling/On"
                  }
                },
                {
                  "setVariable": {
                    // This is necessary because we want to set a timer to delay the shutoff
                    //   of a cooling/heating unit, but we only want to set the timer when we 
                    //   enter that new state initially.
                    "variableName": "enteringNewState",
                    "value": "true"
                  }
                }
              ],
              "nextState": "cooling"
            },

            {
              "eventName": "lowTemperatureSpike",
              "condition": "$input.temperatureInput.sensorData.temperature < $variable.rangeLow",
              // When even a single temperature reading arrives that is below the 'rangeLow', take
              //   emergency action to begin heating, and report a low-temperature spike.
              "actions": [
                {
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/spike"
                  }
                },
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:heatOn"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Heating/On"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "enteringNewState",
                    "value": "true"
                  }
                }
              ],
              "nextState": "heating"
            },

            {
              "eventName": "highTemperatureThreshold",
              "condition": "(((($variable.averageTemperature * ($variable.sensorCount - 1)) + $input.temperatureInput.sensorData.temperature) / $variable.sensorCount) > ($variable.desiredTemperature + $variable.allowedError))",
              // When the average temperature is above the desired temperature plus the allowed error factor,
              //   it is time to start cooling. Note that we calculate the average temperature here again
              //   because the value stored in the 'averageTemperature' variable is not yet available for use
              //   in our condition.
              "actions": [
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:coolOn"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Cooling/On"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "enteringNewState",
                    "value": "true"
                  }
                }
              ],
              "nextState": "cooling"
            },

            {
              "eventName": "lowTemperatureThreshold",
              "condition": "(((($variable.averageTemperature * ($variable.sensorCount - 1)) + $input.temperatureInput.sensorData.temperature) / $variable.sensorCount) < ($variable.desiredTemperature - $variable.allowedError))",
              // When the average temperature is below the desired temperature minus the allowed error factor,
              //   it is time to start heating. Note that we calculate the average temperature here again
              //   because the value stored in the 'averageTemperature' variable is not yet available for use
              //   in our condition.
              "actions": [
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:heatOn"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Heating/On"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "enteringNewState",
                    "value": "true"
                  }
                }
              ],
              "nextState": "heating"
            }
          ]
        }
      },


      {
        "stateName": "cooling",
        "onEnter": {
          "events": [
            {
              "eventName": "delay",
              "condition": "!$variable.noDelay && $variable.enteringNewState",
              // If the operational parameters specify that there should be a minimum time that the 
              //   heating and cooling units should be run before being shut off again, we set
              //   a timer to ensure the proper operation here.
              "actions": [
                {
                  "setTimer": {
                    "timerName": "coolingTimer",
                    "seconds": 180
                  }
                },
                {
                  "setVariable": {
                    // We use this 'goodToGo' variable to store the status of the timer expiration 
                    //   for use in conditions that also use input variable values. If 
                    //   'timeout()' is used in such mixed conditionals, its value is lost.
                    "variableName": "goodToGo",
                    "value": "false"
                  }
                }
              ]
            },
            {
              "eventName": "dontDelay",
              "condition": "$variable.noDelay == true",
              // If the heating/cooling unit shutoff delay is not used, no need to wait.
              "actions": [
                {
                  "setVariable": {
                    "variableName": "goodToGo",
                    "value": "true"
                  }
                }
              ]
            },
            {
              "eventName": "beenHere",
              "condition": "true",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "enteringNewState",
                    "value": "false"
                  }
                }
              ]
            }
          ]
        },

        "onInput": {
          "events": [
            // These are events that occur when an input is received (if the condition is
            //   satisfied), but don't cause a transition to another state.
            {
              "eventName": "whatWasInput",
              "condition": "true",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "sensorId",
                    "value": "$input.temperatureInput.sensorId"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "reportedTemperature",
                    "value": "$input.temperatureInput.sensorData.temperature"
                  }
                }
              ]
            },
            {
              "eventName": "changeDesired",
              "condition": "$input.seedTemperatureInput.desiredTemperature != $variable.desiredTemperature",
              "actions": [
                { 
                  "setVariable": {
                    "variableName": "desiredTemperature",
                    "value": "$input.seedTemperatureInput.desiredTemperature"
                  }
                }
              ]
            },
            {
              "eventName": "calculateAverage",
              "condition": "$input.temperatureInput.sensorData.temperature < $variable.anomalousHigh && $input.temperatureInput.sensorData.temperature > $variable.anomalousLow",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "averageTemperature",
                    "value": "((($variable.averageTemperature * ($variable.sensorCount - 1)) + $input.temperatureInput.sensorData.temperature) / $variable.sensorCount)"
                  }
                }
              ]
            },
            {
              "eventName": "areWeThereYet",
              "condition": "(timeout(\"coolingTimer\"))",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "goodToGo",
                    "value": "true"
                  }
                }
              ]
            }
          ],
          "transitionEvents": [
            // Note that some tests of temperature values (for example, the test for an anomalous value) 
            //   must be placed here in the 'transitionEvents' because they work together with the tests 
            //   in the other conditions to ensure that we implement the proper "if..elseif..else" logic. 
            //   But each transition event must have a destination state ('nextState'), and even if that 
            //   is actually the current state, the "onEnter" events for this state will be executed again.
            //   This is the reason for the 'enteringNewState' variable and related.
            {
              "eventName": "anomalousInputArrived",
              "condition": "$input.temperatureInput.sensorData.temperature >= $variable.anomalousHigh || $input.temperatureInput.sensorData.temperature <= $variable.anomalousLow",
              "actions": [
                { 
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/anomaly"
                  }
                }
              ],
              "nextState": "cooling"
            },

            {
              "eventName": "highTemperatureSpike",
              "condition": "$input.temperatureInput.sensorData.temperature > $variable.rangeHigh",
              "actions": [
                {
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/spike"
                  }
                }
              ],
              "nextState": "cooling"
            },

            {
              "eventName": "lowTemperatureSpike",
              "condition": "$input.temperatureInput.sensorData.temperature < $variable.rangeLow",
              "actions": [
                {
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/spike"
                  }
                },
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:coolOff"
                  }
                },
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:heatOn"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Cooling/Off"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Heating/On"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "enteringNewState",
                    "value": "true"
                  }
                }
              ],
              "nextState": "heating"
            },

            {
              "eventName": "desiredTemperature",
              "condition": "(((($variable.averageTemperature * ($variable.sensorCount - 1)) + $input.temperatureInput.sensorData.temperature) / $variable.sensorCount) <= ($variable.desiredTemperature - $variable.allowedError)) && $variable.goodToGo == true",
              "actions": [
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:coolOff"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Cooling/Off"
                  }
                }
              ],
              "nextState": "idle"
            }
          ]
        }
      },


      {
        "stateName": "heating",
        "onEnter": {
          "events": [
            {
              "eventName": "delay",
              "condition": "!$variable.noDelay && $variable.enteringNewState",
              "actions": [
                {
                  "setTimer": {
                    "timerName": "heatingTimer",
                    "seconds": 120
                  }
                },
                {
                  "setVariable": {
                    "variableName": "goodToGo",
                    "value": "false"
                  }
                }
              ]
            },
            {
              "eventName": "dontDelay",
              "condition": "$variable.noDelay == true",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "goodToGo",
                    "value": "true"
                  }
                }
              ]
            },
            {
              "eventName": "beenHere",
              "condition": "true",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "enteringNewState",
                    "value": "false"
                  }
                }
              ]
            }
          ]
        },

        "onInput": {
          "events": [
            {
              "eventName": "whatWasInput",
              "condition": "true",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "sensorId",
                    "value": "$input.temperatureInput.sensorId"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "reportedTemperature",
                    "value": "$input.temperatureInput.sensorData.temperature"
                  }
                }
              ]
            },
            {
              "eventName": "changeDesired",
              "condition": "$input.seedTemperatureInput.desiredTemperature != $variable.desiredTemperature",
              "actions": [
                { 
                  "setVariable": {
                    "variableName": "desiredTemperature",
                    "value": "$input.seedTemperatureInput.desiredTemperature"
                  }
                }
              ]
            },
            {
              "eventName": "calculateAverage",
              "condition": "$input.temperatureInput.sensorData.temperature < $variable.anomalousHigh && $input.temperatureInput.sensorData.temperature > $variable.anomalousLow",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "averageTemperature",
                    "value": "((($variable.averageTemperature * ($variable.sensorCount - 1)) + $input.temperatureInput.sensorData.temperature) / $variable.sensorCount)"
                  }
                }
              ]
            },
            {
              "eventName": "areWeThereYet",
              "condition": "(timeout(\"heatingTimer\"))",
              "actions": [
                {
                  "setVariable": {
                    "variableName": "goodToGo",
                    "value": "true"
                  }
                }
              ]
            }
          ],
          "transitionEvents": [
            {
              "eventName": "anomalousInputArrived",
              "condition": "$input.temperatureInput.sensorData.temperature >= $variable.anomalousHigh || $input.temperatureInput.sensorData.temperature <= $variable.anomalousLow",
              "actions": [
                { 
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/anomaly"
                  }
                }
              ],
              "nextState": "heating"
            },

            {
              "eventName": "highTemperatureSpike",
              "condition": "$input.temperatureInput.sensorData.temperature > $variable.rangeHigh",
              "actions": [
                {
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/spike"
                  }
                },
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:heatOff"
                  }
                },
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:coolOn"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Heating/Off"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Cooling/On"
                  }
                },
                {
                  "setVariable": {
                    "variableName": "enteringNewState",
                    "value": "true"
                  }
                }
              ],
              "nextState": "cooling"
            },

            {
              "eventName": "lowTemperatureSpike",
              "condition": "$input.temperatureInput.sensorData.temperature < $variable.rangeLow",
              "actions": [
                {
                  "iotTopicPublish": {
                    "mqttTopic": "temperatureSensor/spike"
                  }
                }
              ],
              "nextState": "heating"
            },

            {
              "eventName": "desiredTemperature",
              "condition": "(((($variable.averageTemperature * ($variable.sensorCount - 1)) + $input.temperatureInput.sensorData.temperature) / $variable.sensorCount) >= ($variable.desiredTemperature + $variable.allowedError)) && $variable.goodToGo == true",
              "actions": [
                {
                  "sns": {
                    "targetArn": "arn:aws:sns:us-west-2:123456789012:heatOff"
                  }
                },
                {
                  "iotTopicPublish": {
                    "mqttTopic": "hvac/Heating/Off"
                  }
                }
              ],
              "nextState": "idle"
            }
          ]
        }
      } 

    ],

    "initialStateName": "start"
  },
  "key": "areaId",
  "roleArn": "arn:aws:iam::123456789012:role/IoTEventsRole" 
}
```

Response:

```
{
    "detectorModelConfiguration": {
        "status": "ACTIVATING", 
        "lastUpdateTime": 1557523491.168, 
        "roleArn": "arn:aws:iam::123456789012:role/IoTEventsRole", 
        "creationTime": 1557523491.168, 
        "detectorModelArn": "arn:aws:iotevents:us-west-2:123456789012:detectorModel/areaDetectorModel", 
        "key": "areaId", 
        "detectorModelName": "areaDetectorModel", 
        "detectorModelVersion": "1"
    }
}
```

# Use BatchUpdateDetector to update an AWS IoT Events detector model
<a name="iotevents-commented-example-batch-update-detector"></a>

You can use the `BatchUpdateDetector` operation to put a detector instance into a known state, including timer and variable values. In the following example, the `BatchUpdateDetector` operation resets operational parameters for an area that is under temperature monitoring and control. This operation enables you to do this without having to delete, and recreate, or update the detector model.

CLI command:

```
aws iotevents-data batch-update-detector --cli-input-json file://areaDM.BUD.json
```

File: `areaDM.BUD.json`

```
{
  "detectors": [
    {
      "messageId": "0001",
      "detectorModelName": "areaDetectorModel",
      "keyValue": "Area51",
      "state": {
        "stateName": "start",
        "variables": [
          {
            "name": "desiredTemperature",
            "value": "22"
          },
          {
            "name": "averageTemperature",
            "value": "22"
          },
          {
            "name": "allowedError",
            "value": "1.0"
          },
          {
            "name": "rangeHigh",
            "value": "30.0"
          },
          {
            "name": "rangeLow",
            "value": "15.0"
          },
          {
            "name": "anomalousHigh",
            "value": "60.0"
          },
          {
            "name": "anomalousLow",
            "value": "0.0"
          },
          {
            "name": "sensorCount",
            "value": "12"
          },
          {
            "name": "noDelay",
            "value": "true"
          },
          {
            "name": "goodToGo",
            "value": "true"
          },
          {
            "name": "sensorId",
            "value": "0"
          },
          {
            "name": "reportedTemperature",
            "value": "0.1"
          },
          {
            "name": "resetMe",
            // When 'resetMe' is true, our detector model knows that we have reentered the 'start' state
            //   to reset operational parameters, and will allow the next valid temperature sensor
            //   reading to cause the transition to the 'idle' state.
            "value": "true"
          }
        ],
        "timers": [
        ]
      }
    }
  ]
}
```

Response:

```
{
    "batchUpdateDetectorErrorEntries": []
}
```

# Use BatchPutMessage for inputs in AWS IoT Events
<a name="iotevents-commented-example-input-usage-examples"></a>

**Example 1**  
Use the `BatchPutMessage` operation to send a `"seedTemperatureInput"` message that sets the operational parameters for a given area under temperature control and monitoring. Any message received by AWS IoT Events that has a new `"areaId"` causes a new detector instance to be created. But the new detector instance won't change state to `"idle"` and begin monitoring the temperature and controlling heating or cooling units until a `"seedTemperatureInput"` message is received for the new area.  
CLI command:  

```
aws iotevents-data batch-put-message --cli-input-json file://seedExample.json --cli-binary-format raw-in-base64-out
```
File: `seedExample.json`  

```
{
  "messages": [
    {
      "messageId": "00001",
      "inputName": "seedTemperatureInput",
      "payload": "{\"areaId\": \"Area51\", \"desiredTemperature\": 20.0, \"allowedError\": 0.7, \"rangeHigh\": 30.0, \"rangeLow\": 15.0, \"anomalousHigh\": 60.0, \"anomalousLow\": 0.0, \"sensorCount\": 10, \"noDelay\": false}"
    }
  ]
}
```
Response:  

```
{
    "BatchPutMessageErrorEntries": []
}
```

**Example**  
2  
Use the `BatchPutMessage` operation to send a `"temperatureInput"` message to report temperature sensor data for a sensor in a given control and monitoring area.  
CLI command:  

```
aws iotevents-data batch-put-message --cli-input-json file://temperatureExample.json --cli-binary-format raw-in-base64-out
```
File: `temperatureExample.json`  

```
{
  "messages": [
    {
      "messageId": "00005",
      "inputName": "temperatureInput",
      "payload": "{\"sensorId\": \"05\", \"areaId\": \"Area51\", \"sensorData\": {\"temperature\": 23.12} }"
    }
  ]
}
```
Response:  

```
{
    "BatchPutMessageErrorEntries": []
}
```

**Example 3**  
Use the `BatchPutMessage` operation to send a `"seedTemperatureInput"` message to change the value of the desired temperature for a given area.  
CLI command:  

```
aws iotevents-data batch-put-message --cli-input-json file://seedSetDesiredTemp.json --cli-binary-format raw-in-base64-out
```
File: `seedSetDesiredTemp.json`  

```
{
  "messages": [
    {
      "messageId": "00001",
      "inputName": "seedTemperatureInput",
      "payload": "{\"areaId\": \"Area51\", \"desiredTemperature\": 23.0}"
    }
  ]
}
```
Response:  

```
{
    "BatchPutMessageErrorEntries": []
}
```

# Ingest MQTT messages in AWS IoT Events
<a name="iotevents-commented-example-ingest-mqtt"></a>

If your sensor computing resources can't use the `"BatchPutMessage"` API, but can send their data to the AWS IoT Core message broker using a lightweight MQTT client, you can create an AWS IoT Core topic rule to redirect message data to an AWS IoT Events input. The following is a definition of an AWS IoT Events topic rule that takes the `"areaId"` and `"sensorId"` input fields from the MQTT topic, and the `"sensorData.temperature"` field from the message payload `"temp"` field, and ingests this data into our AWS IoT Events `"temperatureInput"`.

CLI command:

```
aws iot create-topic-rule --cli-input-json file://temperatureTopicRule.json
```

File: `seedSetDesiredTemp.json`

```
{
  "ruleName": "temperatureTopicRule",
  "topicRulePayload": {
    "sql": "SELECT topic(3) as areaId, topic(4) as sensorId, temp as sensorData.temperature FROM 'update/temperature/#'",
    "description": "Ingest temperature sensor messages into IoT Events",
    "actions": [
      {
        "iotEvents": {
          "inputName": "temperatureInput",
          "roleArn": "arn:aws:iam::123456789012:role/service-role/anotheRole" 
        }
      }
    ],
    "ruleDisabled": false,
    "awsIotSqlVersion": "2016-03-23"
  }
}
```

Response: [none]

If the sensor sends a message on the topic `"update/temperature/Area51/03"` with the following payload.

```
{ "temp": 24.5 }
```

This results in data being ingested into AWS IoT Events as if the following `"BatchPutMessage"` API call had been made.

```
aws iotevents-data batch-put-message --cli-input-json file://spoofExample.json --cli-binary-format raw-in-base64-out
```

File: `spoofExample.json`

```
{
  "messages": [
    {
      "messageId": "54321",
      "inputName": "temperatureInput",
      "payload": "{\"sensorId\": \"03\", \"areaId\": \"Area51\", \"sensorData\": {\"temperature\": 24.5} }"
    }
  ]
}
```

# Generate Amazon SNS messages in AWS IoT Events
<a name="iotevents-commented-example-generated-sns"></a>

The following are examples of SNS messages generated by the `"Area51"` detector instance.

AWS IoT Events can integrate with Amazon SNS to generate and publish notifications based on detected events. This section demonstrates how an AWS IoT Events detector instance, specifically the "Area51" detector, generates Amazon SNS messages. These examples showcase the structure and content of Amazon SNS notifications triggered by various states and events within the AWS IoT Events detector, illustrating the power of combining AWS IoT Events with Amazon SNS for real-time alerting and communication.

```
Heating system off command> {
  "eventTime":1557520274729,
  "payload":{
    "actionExecutionId":"f3159081-bac3-38a4-96f7-74af0940d0a4",
    "detector":{
      "detectorModelName":"areaDetectorModel","keyValue":"Area51","detectorModelVersion":"1"},"eventTriggerDetails":{"inputName":"seedTemperatureInput","messageId":"00001","triggerType":"Message"},"state":{"stateName":"start","variables":{"sensorCount":10,"rangeHigh":30.0,"resetMe":false,"enteringNewState":true,"averageTemperature":20.0,"rangeLow":15.0,"noDelay":false,"allowedError":0.7,"desiredTemperature":20.0,"anomalousHigh":60.0,"reportedTemperature":0.1,"anomalousLow":0.0,"sensorId":0},"timers":{}}},"eventName":"resetHeatCool"}
```

```
Cooling system off command> {"eventTime":1557520274729,"payload":{"actionExecutionId":"98f6a1b5-8f40-3cdb-9256-93afd4d66192","detector":{"detectorModelName":"areaDetectorModel","keyValue":"Area51","detectorModelVersion":"1"},"eventTriggerDetails":{"inputName":"seedTemperatureInput","messageId":"00001","triggerType":"Message"},"state":{"stateName":"start","variables":{"sensorCount":10,"rangeHigh":30.0,"resetMe":false,"enteringNewState":true,"averageTemperature":20.0,"rangeLow":15.0,"noDelay":false,"allowedError":0.7,"desiredTemperature":20.0,"anomalousHigh":60.0,"reportedTemperature":0.1,"anomalousLow":0.0,"sensorId":0},"timers":{}}},"eventName":"resetHeatCool"}
```

# Configure the DescribeDetector API in AWS IoT Events
<a name="iotevents-commented-example-describe-detector"></a>

The `DescribeDetector` API in AWS IoT Events lets you to retrieve detailed information about a specific detector instance. This operation provides insights into the current state, variable values, and active timers of a detector. By using this API, you can monitor the real-time status of your AWS IoT Events detectors, facilitating debugging, analysis, and management of your IoT event processing workflows.

CLI command:

```
aws iotevents-data describe-detector --detector-model-name areaDetectorModel --key-value Area51
```

Response:

```
{
    "detector": {
        "lastUpdateTime": 1557521572.216, 
        "creationTime": 1557520274.405, 
        "state": {
            "variables": [
                {
                    "name": "resetMe", 
                    "value": "false"
                }, 
                {
                    "name": "rangeLow", 
                    "value": "15.0"
                }, 
                {
                    "name": "noDelay", 
                    "value": "false"
                }, 
                {
                    "name": "desiredTemperature", 
                    "value": "20.0"
                }, 
                {
                    "name": "anomalousLow", 
                    "value": "0.0"
                }, 
                {
                    "name": "sensorId", 
                    "value": "\"01\""
                }, 
                {
                    "name": "sensorCount", 
                    "value": "10"
                }, 
                {
                    "name": "rangeHigh", 
                    "value": "30.0"
                }, 
                {
                    "name": "enteringNewState", 
                    "value": "false"
                }, 
                {
                    "name": "averageTemperature", 
                    "value": "19.572"
                }, 
                {
                    "name": "allowedError", 
                    "value": "0.7"
                }, 
                {
                    "name": "anomalousHigh", 
                    "value": "60.0"
                }, 
                {
                    "name": "reportedTemperature", 
                    "value": "15.72"
                }, 
                {
                    "name": "goodToGo", 
                    "value": "false"
                }
            ], 
            "stateName": "idle", 
            "timers": [
                {
                    "timestamp": 1557520454.0, 
                    "name": "idleTimer"
                }
            ]
        }, 
        "keyValue": "Area51", 
        "detectorModelName": "areaDetectorModel", 
        "detectorModelVersion": "1"
    }
}
```

# Use the AWS IoT Core rules engine for AWS IoT Events
<a name="iotevents-commented-examples-iot-rules-examples"></a>

The following rules republish AWS IoT Core MQTT messages as shadow update request messages. We assume that AWS IoT Core things are defined for a heating unit and a cooling unit for each area that is controlled by the detector model. In this example, we have defined things named `"Area51HeatingUnit"` and `"Area51CoolingUnit"`.

CLI command:

```
aws iot create-topic-rule --cli-input-json file://ADMShadowCoolOffRule.json
```

File: `ADMShadowCoolOffRule.json`

```
{
  "ruleName": "ADMShadowCoolOff",
  "topicRulePayload": {
    "sql": "SELECT topic(3) as state.desired.command FROM 'hvac/Cooling/Off'",
    "description": "areaDetectorModel mqtt topic publish to cooling unit shadow request",
    "ruleDisabled": false,
    "awsIotSqlVersion": "2016-03-23",
    "actions": [
      {
        "republish": {
          "topic": "$$aws/things/${payload.detector.keyValue}CoolingUnit/shadow/update",
          "roleArn": "arn:aws:iam::123456789012:role/service-role/ADMShadowRole" 
        }
      }
    ]
  }
}
```

Response: [empty]

CLI command:

```
aws iot create-topic-rule --cli-input-json file://ADMShadowCoolOnRule.json
```

File: `ADMShadowCoolOnRule.json`

```
{
  "ruleName": "ADMShadowCoolOn",
  "topicRulePayload": {
    "sql": "SELECT topic(3) as state.desired.command FROM 'hvac/Cooling/On'",
    "description": "areaDetectorModel mqtt topic publish to cooling unit shadow request",
    "ruleDisabled": false,
    "awsIotSqlVersion": "2016-03-23",
    "actions": [
      {
        "republish": {
          "topic": "$$aws/things/${payload.detector.keyValue}CoolingUnit/shadow/update",
          "roleArn": "arn:aws:iam::123456789012:role/service-role/ADMShadowRole" 
        }
      }
    ]
  }
}
```

Response: [empty]

CLI command:

```
aws iot create-topic-rule --cli-input-json file://ADMShadowHeatOffRule.json
```

File: `ADMShadowHeatOffRule.json`

```
{
  "ruleName": "ADMShadowHeatOff",
  "topicRulePayload": {
    "sql": "SELECT topic(3) as state.desired.command FROM 'hvac/Heating/Off'",
    "description": "areaDetectorModel mqtt topic publish to heating unit shadow request",
    "ruleDisabled": false,
    "awsIotSqlVersion": "2016-03-23",
    "actions": [
      {
        "republish": {
          "topic": "$$aws/things/${payload.detector.keyValue}HeatingUnit/shadow/update",
          "roleArn": "arn:aws:iam::123456789012:role/service-role/ADMShadowRole" 
        }
      }
    ]
  }
}
```

Response: [empty]

CLI command:

```
aws iot create-topic-rule --cli-input-json file://ADMShadowHeatOnRule.json
```

File: `ADMShadowHeatOnRule.json`

```
{
  "ruleName": "ADMShadowHeatOn",
  "topicRulePayload": {
    "sql": "SELECT topic(3) as state.desired.command FROM 'hvac/Heating/On'",
    "description": "areaDetectorModel mqtt topic publish to heating unit shadow request",
    "ruleDisabled": false,
    "awsIotSqlVersion": "2016-03-23",
    "actions": [
      {
        "republish": {
          "topic": "$$aws/things/${payload.detector.keyValue}HeatingUnit/shadow/update",
          "roleArn": "arn:aws:iam::123456789012:role/service-role/ADMShadowRole" 
        }
      }
    ]
  }
}
```

Response: [empty]