diff --git a/data/device-ui.html b/data/device-ui.html
new file mode 100644
index 00000000..1385876c
--- /dev/null
+++ b/data/device-ui.html
@@ -0,0 +1,69 @@
+
+
+
+
+ Device UI
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/connect.ts b/scripts/connect.ts
index 95107b1b..f53f0d20 100644
--- a/scripts/connect.ts
+++ b/scripts/connect.ts
@@ -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,
})
@@ -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 ')))
@@ -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)
+ },
+)
diff --git a/scripts/device/ui-server.ts b/scripts/device/ui-server.ts
new file mode 100644
index 00000000..5221d45d
--- /dev/null
+++ b/scripts/device/ui-server.ts
@@ -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}`,
+ ),
+ )
+ })
+}