npx create-react-app my-app --template typescript
npm install @iot-app-kit/[email protected] @iot-app-kit/[email protected] @iot-app-kit/[email protected] @awsui/components-react @awsui/global-styles @aws-sdk/types
REACT_APP_AWS_ACCESS_KEY_ID=<replace-with-aws-access-key-id>
REACT_APP_AWS_SECRET_ACCESS_KEY=<replace-with-aws-access-key>
REACT_APP_AWS_SESSION_TOKEN=<replace-with-aws-session-token>
import { CredentialProvider } from "@aws-sdk/types";
// REACT_APP prefix so that react-app picks them up by default
const ENV_KEY = "REACT_APP_AWS_ACCESS_KEY_ID";
const ENV_SECRET = "REACT_APP_AWS_SECRET_ACCESS_KEY";
const ENV_SESSION = "REACT_APP_AWS_SESSION_TOKEN";
const ENV_EXPIRATION = "REACT_APP_AWS_CREDENTIAL_EXPIRATION";
export const fromEnvReactApp = (): CredentialProvider => {
return () => {
const accessKeyId = process.env[ENV_KEY];
const secretAccessKey = process.env[ENV_SECRET];
const expiry = process.env[ENV_EXPIRATION];
if (accessKeyId && secretAccessKey) {
return Promise.resolve({
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey,
sessionToken: process.env[ENV_SESSION],
expiration: expiry ? new Date(expiry) : undefined,
});
};
return Promise.reject(new Error("Unable to find environment variable credentials. Expected REACT_APP_AWS_ACCESS_KEY_ID and REACT_APP_AWS_SECRET_ACCESS_KEY to be defined"));
}
}
import { AppLayout, BreadcrumbGroup, Container, Grid, Header, TopNavigation } from "@awsui/components-react";
/* --- BEGIN: AWS @iot-app-kit and related implementation*/
import { initialize } from "@iot-app-kit/source-iotsitewise";
import { fromEnvReactApp } from "./fromEnv";
import { BarChart, LineChart, StatusTimeline, ResourceExplorer, WebglContext, } from "@iot-app-kit/react-components";
import "./App.css";
const { defineCustomElements } = require("@iot-app-kit/components/loader");
const { query } = initialize({
awsCredentials: fromEnvReactApp(),
awsRegion: "us-east-1",
});
defineCustomElements();
/* --- END: AWS @iot-app-kit and related implementation*/
function Breadcrumbs() {
const breadcrumbItems = [
{
text: 'Bottling Line',
href: '#'
},
{
text: 'Machine Dashboard',
href: '#'
}
];
return <BreadcrumbGroup items={breadcrumbItems} expandAriaLabel="Show path" ariaLabel="Breadcrumbs"/>;
}
function PageHeader() {
return <Header variant="h1">Machine Dashboard</Header>;
}
function SitwiseResourceExplorer() {
const columnDefinitions = [{
sortingField: 'name',
id: 'name',
header: 'Asset Name',
cell: ({ name }: any) => name,
}];
return (
<Container
disableContentPaddings={true}
header={ <Header variant="h2" description="List of sitewise assets"> Sitewise Assets </Header> }
>
{/* --- BEGIN: `ResourceExplorer` implementation*/}
<ResourceExplorer
query={query.assetTree.fromRoot()}
onSelectionChange={(event) => console.log("changes asset", event)}
columnDefinitions={columnDefinitions}
/>
{/* --- END: `ResourceExplorer` implementation*/}
</Container>
);
}
function MachineState(props: any) {
return (
<Container
disableContentPaddings={true}
header={ <Header variant="h2" description="Operational state of the machine as timeline"> Machine State </Header> }
>
{/* --- BEGIN: `StatusTimeline` implementation*/}
<StatusTimeline
viewport={{ duration: '15m' }}
// annotations={{
// y: [
// { color: '#FF0000', comparisonOperator: 'EQ', value: 30.45 },
// { color: '#0000FF', comparisonOperator: 'EQ', value: 2.93 },
// { color: '#00FF00', comparisonOperator: 'EQ', value: 2.93 },
// ]
// }}
queries={[
query.timeSeriesData({
assets: [{
assetId: props.assetId,
properties: [{
propertyId: props.propertyId
}]
}]
})
]}
/>
<WebglContext/>
{/* --- END: `StatusTimeline` implementation*/}
</Container>
);
}
function ProductionCount(props: any) {
return (
<Container disableContentPaddings={true} header={ <Header variant="h2" description="Count of total and bad output from machine"> Production Count </Header> } >
{/* --- BEGIN: `LineChart` implementation*/}
<BarChart
viewport={{ duration: "15m" }}
queries={[
query.timeSeriesData({
assets: [
{
assetId: props.assetId,
properties: [
{
propertyId: props.badPartsCountPropertyId,
refId: 'bad-parts-count'
},
{
propertyId: props.totalPartsCountPropertyId,
refId: 'total-parts-count'
},
],
},
],
}),
]}
styleSettings={{
'bad-parts-count': { color: '#d13212', name: 'Bad Count' },
'total-parts-count': { color: '#1d8102', name: 'Total Count' }
}}
/>
<WebglContext />
{/* --- END: `LineChart` implementation*/}
</Container>
);
}
function Content() {
// Replace the asset ids and asset property ids before you run the application
const WASHING_MACHINE_ASSET_ID = '<replace-with-sitwise-asset-id>';
const BAD_PARTS_COUNT_PROPERTY = '<replace-with-corresponding-sitwise-asset-property-id>';
const TOTAL_PARTS_COUNT_PROPERTY = '<replace-with-corresponding-sitwise-asset-property-id>';
const MACHINE_STATE_PROPERTY = '<replace-with-corresponding-sitwise-asset-property-id>';
return (
<Grid gridDefinition={[
{ colspan: { l: 2, m: 2, default: 12 } },
{ colspan: { l: 5, m: 5, default: 12 } },
{ colspan: { l: 5, m: 5, default: 12 } }
]}>
<SitwiseResourceExplorer/>
<MachineState
assetId={WASHING_MACHINE_ASSET_ID}
propertyId={MACHINE_STATE_PROPERTY}
/>
<ProductionCount
assetId={WASHING_MACHINE_ASSET_ID}
badPartsCountPropertyId={BAD_PARTS_COUNT_PROPERTY}
totalPartsCountPropertyId={TOTAL_PARTS_COUNT_PROPERTY}
/>
</Grid>
);
}
function App() {
return (
<>
<TopNavigation
identity={{
href: "#",
title: "AWS IoT Application Kit Demo",
}}
i18nStrings={{ overflowMenuTitleText: "More", overflowMenuTriggerText: "More" }}
/>
<AppLayout
breadcrumbs={<Breadcrumbs/>}
contentHeader={<PageHeader/>}
content={<Content/>}
navigationHide={true}
toolsHide={true}
/>
</>
);
}
export default App;