Skip to content
This repository has been archived by the owner on Feb 3, 2021. It is now read-only.

Commit

Permalink
feat(device): add UI to control device state
Browse files Browse the repository at this point in the history
  • Loading branch information
coderbyheart committed Jul 18, 2019
1 parent ea3126a commit 7d89ebf
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 15 deletions.
69 changes: 69 additions & 0 deletions data/device-ui.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Device UI</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<style type="text/css">
main {
max-width: 960px;
margin: 0 auto;
}
</style>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">

const Main = ({ children }) => <main>
<h1>Control device</h1>
{children}
</main>

const TempSlider = () => {
const [temp, setTemp] = React.useState(0)
return <div className="form-group">
<label htmlFor="temperatureSlider">Temperature: {temp}</label>
<input
type="range"
className="form-control-range"
id="temperatureSlider"
min="-30"
max="30"
value={temp}
onChange={({ target: { value } }) => {
setTemp(parseInt(value, 10))
}}
onMouseUp={() => {
fetch('/update', {
method: 'POST',
body: JSON.stringify({
temp: {
v: temp,
ts: new Date().toISOString(),
},
}),
headers: {
'Content-Type': 'application/json',
},
})
}}
/>
</div>
}

ReactDOM.render(
<Main>
<form>
<TempSlider/>
</form>
</Main>,
document.getElementById('root'),
)

</script>
</body>
</html>
67 changes: 52 additions & 15 deletions scripts/connect.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import * as path from 'path'
import { Iot } from 'aws-sdk'
import { device } from 'aws-iot-device-sdk'
import { thingShadow } from 'aws-iot-device-sdk'
import { deviceFileLocations } from './jitp/deviceFileLocations'
import chalk from 'chalk'
import { uiServer } from './device/ui-server'

/**
* Connect to the AWS IoT broker using a generated device certificate
*/
const main = async (args: { deviceId: string }) => {
const clientId = args.deviceId
const { endpointAddress } = await new Iot({
region: process.env.AWS_DEFAULT_REGION,
})
Expand All @@ -21,10 +23,10 @@ const main = async (args: { deviceId: string }) => {
console.log(
chalk.blue(`IoT broker hostname: ${chalk.yellow(endpointAddress)}`),
)
console.log(chalk.blue(`Device ID: ${chalk.yellow(args.deviceId)}`))
console.log(chalk.blue(`Device ID: ${chalk.yellow(clientId)}`))

const certsDir = path.resolve(process.cwd(), 'certificates')
const deviceFiles = deviceFileLocations(certsDir, args.deviceId)
const deviceFiles = deviceFileLocations(certsDir, clientId)

console.time(chalk.green(chalk.inverse(' connected ')))

Expand All @@ -36,30 +38,65 @@ const main = async (args: { deviceId: string }) => {
console.timeLog(note)
}, 5000)

const connection = new device({
const connection = new thingShadow({
privateKey: deviceFiles.key,
clientCert: deviceFiles.certWithCA,
caCert: path.resolve(process.cwd(), 'data', 'AmazonRootCA1.pem'),
clientId: args.deviceId.trim(),
clientId,
host: endpointAddress,
region: endpointAddress.split('.')[2],
debug: true,
})

connection.on('connect', async () => {
console.timeEnd(chalk.green(chalk.inverse(' connected ')))
clearInterval(connectingNote)
})

connection.on('close', () => {
console.error(chalk.red(chalk.inverse(' disconnected! ')))
})
connection.register(clientId, {}, async () => {
await uiServer({
onUpdate: update => {
console.log({ clientId, state: { state: { reported: update } } })
connection.update(clientId, { state: { reported: update } })
},
})
})

connection.on('close', () => {
console.error(chalk.red(chalk.inverse(' disconnected! ')))
})

connection.on('reconnect', () => {
console.log(chalk.magenta('reconnecting...'))
})

connection.on('status', function(thingName, stat, _, stateObject) {
console.log(
'received ' +
stat +
' on ' +
thingName +
': ' +
JSON.stringify(stateObject),
)
})

connection.on('delta', function(thingName, stateObject) {
console.log(
'received delta on ' + thingName + ': ' + JSON.stringify(stateObject),
)
})

connection.on('reconnect', () => {
console.log(chalk.magenta('reconnecting...'))
connection.on('timeout', function(thingName, clientToken) {
console.log(
'received timeout on ' + thingName + ' with token: ' + clientToken,
)
})
})
}

main({ deviceId: process.argv[process.argv.length - 1] }).catch(error => {
console.error(chalk.red(error))
process.exit(1)
})
main({ deviceId: process.argv[process.argv.length - 1].trim() }).catch(
error => {
console.error(chalk.red(error))
process.exit(1)
},
)
62 changes: 62 additions & 0 deletions scripts/device/ui-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as path from 'path'
import * as http from 'http'
import { promises as fs } from 'fs'
import chalk from 'chalk'

export const uiServer = async (args: {
onUpdate: (update: object) => void
}) => {
const port = 1024 + Math.round(Math.random() * (65535 - 1024))
const uiPage = await fs.readFile(
path.resolve(process.cwd(), 'data', 'device-ui.html'),
'utf-8',
)

const requestHandler: http.RequestListener = async (request, response) => {
let body = ''
switch (request.url) {
case '/':
case '/index.html':
response.writeHead(200, {
'Content-Length': Buffer.byteLength(uiPage),
'Content-Type': 'text/html',
})
response.end(uiPage)
break
case '/update':
request.on('data', chunk => {
body += chunk.toString() // convert Buffer to string
})
request.on('end', () => {
try {
const update = JSON.parse(body)
args.onUpdate(update)
response.statusCode = 202
response.end()
} catch (err) {
console.log(err)
const errData = JSON.stringify(err)
response.writeHead(400, {
'Content-Length': Buffer.byteLength(errData),
'Content-Type': 'application/json',
})
response.end(errData)
}
})
break
default:
response.statusCode = 404
response.end()
}
}

const server = http.createServer(requestHandler)

server.listen(port, () => {
console.log(
chalk.green(
`To control this device open your browser on http://localhost:${port}`,
),
)
})
}

0 comments on commit 7d89ebf

Please sign in to comment.