By the end of this chapter, your solution will do the following:
In this chapter you will construct a serverless application without any code that analyzes the inputs from your smart thermostat and determines the behavior for the corresponding (fictitious) HVAC system. You will use IoT Events to deploy a resource called a detector model that processes the device shadow messages forwarded by your IoT Core rule, evaluate if any state changes should occur from heating to cooling to standby, then send a message back to your smart thermostat with the updated state change, if any.
Here is a preview of the detector model to create:
As you can see, there are three states for the HVAC application: heating, cooling, and standby. The application will always be in one of those states and initializes to the standby state. As new messages arrive from the smart thermostat, the detector model evaluates the inputs against conditional logic to determine if the model should transition to a new state.
For example, from the standby state, if a new temperature reading comes in over 80 (in degrees Fahrenheit), the model will evaluate a transition to the cooling state. Then upon entering the cooling state, the model will publish a new message back to IoT Core to update the device shadow of the smart thermostat with a message like: { "state": { "desired": { "hvacStatus": "COOLING" } } }
. Based on the work you initially did in the chapter Data sync, these new commands on the desired node will be acknowledged and the state on the device updated to reflect the command by updating the LED strips to be red, blue, or off.
It is beyond the scope of this learning module to fully explore how to create detector models in IoT Events. Instead, below are steps for getting your detector model up and running, with a few notes on key pieces so you know how this model works. For a primer on getting started with AWS IoT Events, please review the developer guide.
To create the detector model previewed in the image above, complete the following steps.
First, you will create a file on your local desktop with the following contents. Name the file something like model.json
. You must edit the file to find and replace the three instances of the term «CLIENT_ID» with the client Id/serial number of your device (it is printed on your device screen).
{
"detectorModelDefinition": {
"states": [
{
"stateName": "standby",
"onInput": {
"events": [],
"transitionEvents": [
{
"eventName": "startHeating",
"condition": "($input.thermostat.current.state.reported.temperature <= $variable.heatingThresholdUnoccupied) || ($input.thermostat.current.state.reported.roomOccupancy && $input.thermostat.current.state.reported.temperature <= $variable.heatingThresholdOccupied) ",
"actions": [],
"nextState": "heating"
},
{
"eventName": "startCooling",
"condition": "($input.thermostat.current.state.reported.temperature >= $variable.coolingThresholdUnoccupied) || ($input.thermostat.current.state.reported.roomOccupancy && $input.thermostat.current.state.reported.temperature >= $variable.coolingThresholdOccupied) ",
"actions": [],
"nextState": "cooling"
}
]
},
"onEnter": {
"events": [
{
"eventName": "setThresholds",
"condition": "true",
"actions": [
{
"setVariable": {
"variableName": "heatingThresholdUnoccupied",
"value": "60"
}
},
{
"setVariable": {
"variableName": "heatingThresholdOccupied",
"value": "68"
}
},
{
"setVariable": {
"variableName": "coolingThresholdOccupied",
"value": "72"
}
},
{
"setVariable": {
"variableName": "coolingThresholdUnoccupied",
"value": "80"
}
}
]
},
{
"eventName": "setStandbyInShadow",
"condition": "true",
"actions": [
{
"iotTopicPublish": {
"mqttTopic": "'$aws/things/<<CLIENT_ID>>/shadow/update'",
"payload": {
"contentExpression": "'{\"state\":{\"desired\":{\"hvacStatus\":\"STANDBY\"}}}'",
"type": "JSON"
}
}
}
]
}
]
},
"onExit": {
"events": []
}
},
{
"stateName": "cooling",
"onInput": {
"events": [],
"transitionEvents": [
{
"eventName": "stopCooling",
"condition": "(!$input.thermostat.current.state.reported.roomOccupancy && $input.thermostat.current.state.reported.temperature < $variable.coolingThresholdUnoccupied) || ($input.thermostat.current.state.reported.roomOccupancy && $input.thermostat.current.state.reported.temperature < $variable.coolingThresholdOccupied) ",
"actions": [],
"nextState": "standby"
}
]
},
"onEnter": {
"events": [
{
"eventName": "setCoolingInShadow",
"condition": "true",
"actions": [
{
"iotTopicPublish": {
"mqttTopic": "'$aws/things/<<CLIENT_ID>>/shadow/update'",
"payload": {
"contentExpression": "'{\"state\":{\"desired\":{\"hvacStatus\":\"COOLING\"}}}'",
"type": "JSON"
}
}
}
]
}
]
},
"onExit": {
"events": []
}
},
{
"stateName": "heating",
"onInput": {
"events": [],
"transitionEvents": [
{
"eventName": "stopHeating",
"condition": "(!$input.thermostat.current.state.reported.roomOccupancy && $input.thermostat.current.state.reported.temperature > $variable.heatingThresholdUnoccupied) || ($input.thermostat.current.state.reported.roomOccupancy && $input.thermostat.current.state.reported.temperature > $variable.heatingThresholdOccupied) ",
"actions": [],
"nextState": "standby"
}
]
},
"onEnter": {
"events": [
{
"eventName": "setHeatingInShadow",
"condition": "true",
"actions": [
{
"iotTopicPublish": {
"mqttTopic": "'$aws/things/<<CLIENT_ID>>/shadow/update'",
"payload": {
"contentExpression": "'{\"state\":{\"desired\":{\"hvacStatus\":\"HEATING\"}}}'",
"type": "JSON"
}
}
}
]
}
]
},
"onExit": {
"events": []
}
}
],
"initialStateName": "standby"
},
"detectorModelName": "hvacApplication",
"detectorModelDescription": "cloud application to manage HVAC state",
"evaluationMethod": "BATCH"
}
Next, you will use this file in the steps to import a new detector model in the AWS IoT Events console.
edukit-iotevents
. The console will create a new IAM role that gives IoT Events permission to operate your model.You have now deployed the detector model to IoT Events. As new messages are published from your smart thermostat, the IoT Core rule you created in the previous chapter forwards them to the IoT Events input resource, then IoT Events pushes copies of the messages from any inputs to any consuming detector models, like this one, for evaluation of any state changes.
Now for an explanation of a few key pieces of the model. Each state of the model (heating/cooling/standby) is nearly the same. The standby state has one extra action to set the numeric thresholds that every state uses to determine whether it should change states. These could be set once in a one-time initialization state before moving to standby for the first time, but they are included in standby for simplicity of the demonstration.
The rest of the states’ configuration is similar. There is an OnEnter event to publish a message to the smart thermostat’s device shadow, indicating which state the HVAC should now be in. There is a conditional transition that gets evaluated after each message is processed that checks if the state should change. For example, if the model is in the heating state and the conditional expression for stopHeating evalutes true, the model will move to the standby state. Note that the standby state has two transitions, one each for moving to heating or cooling. This model is simplified in that the system will always go through the standby state instead of directly from heating to cooling or vice versa. The conditional transition logic takes into consideration the two temperature boundaries; one when the room is occupied and another when the room is unoccupied.
Below is a sample transition from the heating state labeled stopHeating. There are two boolean expressions joined with OR ||
logic. In plainer English, this expression means “stop heating if the room is unoccupied and the room temperature is warm enough while unoccupied, OR if the room is occupied and the room is warm enough for people.”
(!$input.thermostat.current.state.reported.roomOccupancy && $input.thermostat.current.state.reported.temperature > $variable.heatingThresholdUnoccupied) || ($input.thermostat.current.state.reported.roomOccupancy && $input.thermostat.current.state.reported.temperature > $variable.heatingThresholdOccupied)
If everything has been configured per this module’s instructions, you should now be seeing status changes delivered to your smart thermostat in the form of updating LED strips, completing the end-to-end solution!
Before moving on to the next chapter, you can validate that the solution is configured as intended by…
Note: the temperature sensor is inside the housing of the kit and the code includes a hardcoded offset. You may see variance in the temperature reading up to 10 degrees from reading to reading.
If the solution is working as expected, you can move on to the Conclusion.