-
Notifications
You must be signed in to change notification settings - Fork 77
how to build an iot dashboard using aws iot twinmaker and iot application kit
This tutorial explains how to build an IoT dashboard application in React using AWS IoT TwinMaker (TwinMaker) and IoT Application Kit (IoT AppKit).
TwinMaker is an AWS IoT service that you can use to build operational digital twins of physical and digital systems.
IoT AppKit is an open-source library that offers a variety of React components and a TwinMaker data source specifically developed to enable your IoT and digital twin applications.
To work with TwinMaker in this tutorial, you will need:
-
An AWS Account and the necessary permissions to use AWS IoT TwinMaker. If you already have AWS IoT TwinMaker set up, you can proceed to the next section. Otherwise, follow the instructions to set up an account and configure authentication.
-
You have created an AWS IoT TwinMaker workspace. For more information on workspace creation, read the workspace creation topic.
-
The Cookie Factory v2 demo deployed to your AWS account. Resources created for the demo will be used in this tutorial.
-
Minimum versions of Node.js, npm, and an IDE or simple code editor installed.
-
Node.js 16+. npm recommends using a Node version manager, e.g. nvm to install Node.js and npm.
-
npm 8+. npm is automatically installed when you install Node.js or a Node.js version manager.
-
An IDE or code editor of your choice, e.g. AWS Cloud9 or Visual Studio Code.
-
-
A React starter project. To ensure your project is properly configured for this tutorial, complete the Create a basic web application with React, Typescript, and webpack tutorial to create a basic React project with the required dependencies and configuration.
-
Experience building a React application with IoT AppKit. If you would like learn how to build applications with React and IoT AppKit, complete the How to use IoT Application Kit in a React web application tutorial.
Create an IoT dashboard to monitor the Cookie Factory Freezer Tunnel.
- Installing IoT AppKit packages
- Importing IoT AppKit modules
- Importing IoT AppKit styles
- Setting your AWS credentials
- Setting your TwinMaker workspace name
- Setting the time span for the dashboard
- Creating styles for the alarm visualization
- Creating styles for the data visualizations
- Initializing the TwinMaker data source
- Creating the TwinMaker queries
- Putting it all together
IoT AppKit is a collection of npm packages you install individually as needed based on your application requirements. For the dashboard application, the following packages are required.
npm install -S @iot-app-kit/react-components @iot-app-kit/source-iottwinmaker
import type { DurationViewport, StyleSettingsMap, Threshold } from '@iot-app-kit/core';
import { LineChart, StatusTimeline, TimeSync, WebglContext } from '@iot-app-kit/react-components';
import { initialize, type TwinMakerEntityHistoryQuery } from '@iot-app-kit/source-iottwinmaker';
So your charts look and behave as expected, you should import the IoT AppKit styles in one of your application components.
import '@iot-app-kit/components/styles.css';
The dashboard application will access resources in your AWS account. Update the __FILL_IN__
placeholder values specific to your AWS account credentials.
const AWS_CREDENTIALS_ACCESS_KEY_ID: string = '__FILL_IN__';
const AWS_CREDENTIALS_SECRET_ACCESS_KEY: string = '__FILL_IN__';
const AWS_CREDENTIALS_SESSION_TOKEN: string = '__FILL_IN__';
const AWS_CREDENTIALS_EXPIRATION_IN_MS: number = __FILL_IN__;
const AWS_REGION: string = '__FILL_IN__';
The dashboard application will use resources created for the Cookie Factory v2 demo. Update the __FILL_IN__
placeholder value with the name of the TwinMaker workspace you created when deploying the demo.
const AWS_IOT_TWINMAKER_WORKSPACE_NAME: string = '__FILL_IN__';
The dashboard application will query historical data for a 15-minute time range from live (now).
const VIEWPORT_TIME_SPAN_IN_MIN = 15;
IoT AppKit components accept a DurationViewport
for setting a time span, but in milliseconds. Convert VIEWPORT_TIME_SPAN_IN_MIN
to milliseconds and set the viewport duration
property.
To learn more about the IoT AppKit viewport and how to use it, visit the IoT AppKit documentation.
const VIEWPORT: DurationViewport = { duration: VIEWPORT_TIME_SPAN_IN_MIN * 60 * 1000 };
The IoT AppKit Threshold
allows you to optionally style the StatusTimeline
visualization based on a property's latest value. For the dashboard application, if the AlarmMessage
property value is "High," data block will be red. If the value is "Medium," the data block will be dark orange. And so forth as shown below.
To learn more about IoT AppKit thresholds and how to use them, visit the IoT AppKit documentation.
const THRESHOLDS: Threshold<'High' | 'Medium' | 'Low' | 'Normal'>[] = [
{
color: 'red',
value: 'High',
comparisonOperator: 'EQ',
},
{
color: 'darkorange',
value: 'Medium',
comparisonOperator: 'EQ',
},
{
color: 'gold',
value: 'Low',
comparisonOperator: 'EQ',
},
{
color: 'darkgreen',
value: 'Normal',
comparisonOperator: 'EQ',
},
];
The IoT AppKit StyleSettingsMap
allows you to optionally style visualization components. For the dashboard application, the Temperature
property line chart plot will be teal, and the Speed
property line chart plot will be purple.
To learn more about styling IoT AppKit components and how to use StyleSettingsMap
, visit the IoT AppKit documentation.
const LINE_CHART_STYLES: StyleSettingsMap = {
['Temperature']: { color: 'teal' },
['Speed']: { color: 'purple' },
};
Before you can query data from TwinMaker, you must initialize the IoT AppKit TwinMaker data source.
To learn more about the TwinMaker data source and how to use it, visit the IoT AppKit documentation.
import { initialize } from '@iot-app-kit/source-iottwinmaker';
const { query } = initialize(AWS_IOT_TWINMAKER_WORKSPACE_NAME, {
awsCredentials: {
accessKeyId: AWS_CREDENTIALS_ACCESS_KEY_ID,
expiration: new Date(AWS_CREDENTIALS_EXPIRATION_IN_MS),
secretAccessKey: AWS_CREDENTIALS_SECRET_ACCESS_KEY,
sessionToken: AWS_CREDENTIALS_SESSION_TOKEN,
},
awsRegion: AWS_REGION,
});
To query data from TwinMaker using IoT AppKit, you create TwinMaker entity history queries (TwinMakerEntityHistoryQuery
) for the entities and properties of interest. From those history queries, you generate time series queries (TimeSeriesDataQuery
) IoT AppKit components will actually use to fetch data.
For the dashboard application, create the entity history query for the Freezer Tunnel entity properties AlarmMessage
, Temperature
, and Speed
as defined on the CookieLineComponent
component.
const baseHistoryQuery: TwinMakerEntityHistoryQuery = {
componentName: 'CookieLineComponent',
entityId: 'FREEZER_TUNNEL_e12e0733-f5df-4604-8f10-417f49e6d298',
properties: [],
};
const alarmHistoryQuery: TwinMakerEntityHistoryQuery = {
...baseHistoryQuery,
properties: [
{
propertyName: 'AlarmMessage',
refId: 'AlarmMessage',
},
],
};
const dataHistoryQueries: TwinMakerEntityHistoryQuery[] = [
{
...baseHistoryQuery,
properties: [
{
propertyName: 'Temperature',
refId: 'Temperature',
},
],
},
{
...baseHistoryQuery,
properties: [
{
propertyName: 'Speed',
refId: 'Speed',
},
],
},
];
Next, generate the time series query for the alarm property AlarmMessage
. Pass alarmHistoryQuery
to the query
module's timeSeriesData
.
const alarmTimeSeriesQuery = query.timeSeriesData(alarmHistoryQuery);
Finally, generate the time series queries for the data properties Temperature
and Speed
. So you can visualize each property on its own chart, map over dataHistoryQueries
and generate a time series query for each.
const dataTimeSeriesQueries = dataHistoryQueries.map((dataHistoryQuery) => {
return query.timeSeriesData(dataHistoryQuery);
});
Let's build the Cookie Factory dashboard application! Using your React starter project:
npm install -S @iot-app-kit/react-components @iot-app-kit/source-iottwinmaker
CSS styles transform your HTML code into a layout that looks like a dashboard.
Create the file.
touch src/styles.css
Open src/styles.css
, paste the following CSS code, and save the file.
* {
box-sizing: border-box;
}
/* Element selectors */
body {
display: flex;
min-height: 100vh;
padding: 15px 30px 30px;
background-color: #e6e6e7;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
'Helvetica Neue', sans-serif;
color: #445;
}
h1 {
margin: 0;
font-weight: 300;
}
h2 {
margin: 0;
font-weight: 400;
}
h3 {
margin: 0;
font-weight: 500;
}
/* Id selectors */
#app {
flex: 1;
display: flex;
flex-direction: column;
row-gap: 15px;
}
/* Class selectors */
.card {
display: flex;
flex-direction: column;
padding: 17px 30px 30px;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 10px rgb(0, 0, 0, 0.2);
}
.header {
display: flex;
justify-content: space-between;
align-items: baseline;
}
.charts {
display: flex;
flex-direction: column;
width: 100%;
margin-top: 15px;
padding-top: 15px;
border-top: solid 1px #e6e6e6;
}
/* Set a size on the chart container so that the IoT AppKit component sizes itself accordingly */
.charts .chart {
width: 100%;
height: 260px;
}
.charts .alarm {
height: 200px;
}
Open src/index.tsx
, delete the existing code, paste the following React code, and save the file.
NOTE: You must update the __FILL_IN__
placeholder values with your AWS account credentials and TwinMaker workspace name.
import { createRoot } from 'react-dom/client';
// IoTAppkit required modules
import type { DurationViewport, StyleSettingsMap, Threshold } from '@iot-app-kit/core';
import { LineChart, StatusTimeline, TimeSync, WebglContext } from '@iot-app-kit/react-components';
import { initialize, type TwinMakerEntityHistoryQuery } from '@iot-app-kit/source-iottwinmaker';
// IoTAppkit styles
import '@iot-app-kit/components/styles.css';
// The dashbaord styles
import './styles.css';
// Your AWS account credentials
const AWS_CREDENTIALS_ACCESS_KEY_ID: string = '__FILL_IN__';
const AWS_CREDENTIALS_SECRET_ACCESS_KEY: string = '__FILL_IN__';
const AWS_CREDENTIALS_SESSION_TOKEN: string = '__FILL_IN__';
const AWS_CREDENTIALS_EXPIRATION_IN_MS: number = __FILL_IN__;
const AWS_REGION: string = '__FILL_IN__';
// Your TwinMaker workspace name
const AWS_IOT_TWINMAKER_WORKSPACE_NAME: string = '__FILL_IN__';
// The dashbaord time span from live
const VIEWPORT_TIME_SPAN_IN_MIN = 15;
const VIEWPORT: DurationViewport = { duration: VIEWPORT_TIME_SPAN_IN_MIN * 60 * 1000 };
// StatusTimeline styles
const THRESHOLDS: Threshold<'High' | 'Medium' | 'Low' | 'Normal'>[] = [
{
color: 'red',
value: 'High',
comparisonOperator: 'EQ',
},
{
color: 'darkorange',
value: 'Medium',
comparisonOperator: 'EQ',
},
{
color: 'gold',
value: 'Low',
comparisonOperator: 'EQ',
},
{
color: 'darkgreen',
value: 'Normal',
comparisonOperator: 'EQ',
},
];
// LineChart styles
const LINE_CHART_STYLES: StyleSettingsMap = {
['Temperature']: { color: 'teal' },
['Speed']: { color: 'purple' },
};
const { query } = initialize(AWS_IOT_TWINMAKER_WORKSPACE_NAME, {
awsCredentials: {
accessKeyId: AWS_CREDENTIALS_ACCESS_KEY_ID,
expiration: new Date(AWS_CREDENTIALS_EXPIRATION_IN_MS),
secretAccessKey: AWS_CREDENTIALS_SECRET_ACCESS_KEY,
sessionToken: AWS_CREDENTIALS_SESSION_TOKEN,
},
awsRegion: AWS_REGION,
});
const baseHistoryQuery: TwinMakerEntityHistoryQuery = {
componentName: 'CookieLineComponent',
entityId: 'FREEZER_TUNNEL_e12e0733-f5df-4604-8f10-417f49e6d298',
properties: [],
};
const alarmHistoryQuery: TwinMakerEntityHistoryQuery = {
...baseHistoryQuery,
properties: [
{
propertyName: 'AlarmMessage',
refId: 'AlarmMessage',
},
],
};
const dataHistoryQueries: TwinMakerEntityHistoryQuery[] = [
{
...baseHistoryQuery,
properties: [
{
propertyName: 'Temperature',
refId: 'Temperature',
},
],
},
{
...baseHistoryQuery,
properties: [
{
propertyName: 'Speed',
refId: 'Speed',
},
],
},
];
// Alarm times series query
const alarmTimeSeriesQuery = query.timeSeriesData(alarmHistoryQuery);
// Data times series queries
const dataTimeSeriesQueries = dataHistoryQueries.map((dataHistoryQuery) => {
return query.timeSeriesData(dataHistoryQuery);
});
const element = document.getElementById('app');
if (element) {
createRoot(element).render(<App />);
}
function App() {
// Map over the `dataTimeSeriesQueries` to create the line charts
const lineCharts = dataTimeSeriesQueries.map((timeSeriesQuery) => {
return (
// Make sure to set a unique key for each element, in this case using the unique query string
<div className="chart" key={timeSeriesQuery.toQueryString()}>
<LineChart queries={[timeSeriesQuery]} viewport={VIEWPORT} styles={LINE_CHART_STYLES} />
</div>
);
});
return (
<>
<h2>Cookie Factory Monitor</h2>
<section className="card">
<section className="header">
<h1>Freezer Tunnel Dashboard</h1>
<span>Last {VIEWPORT_TIME_SPAN_IN_MIN} minutes</span>
</section>
<section className="charts">
{/* Use the `TimeSync` provider to synchronize the current
viewport time range across all charts */}
<TimeSync initialViewport={VIEWPORT}>
<h3>Alarm status</h3>
<div className="chart alarm">
<StatusTimeline queries={[alarmTimeSeriesQuery]} thresholds={THRESHOLDS} />
</div>
<h3>Property data</h3>
{lineCharts}
</TimeSync>
</section>
</section>
<WebglContext />
</>
);
}
npm run dev
Navigate to https://localhost:8080. Your application should look similar to this view.