Changes to get working on tessel

This commit is contained in:
Dana Woodman 2018-07-05 22:08:47 -07:00
parent b3eda3a674
commit 8c83229696
3 changed files with 167 additions and 29 deletions

View File

@ -1,6 +1,6 @@
const path = require('path') const path = require('path')
require('dotenv').config({ path: path.join(__dirname, '..', '.env') }) require('dotenv').config({ path: path.join(__dirname, '.env') })
const ENV = process.env.NODE_ENV || 'development' const ENV = process.env.NODE_ENV || 'development'
const USB_MOUNT_PATH = process.env.USB_MOUNT_PATH || '/mnt/sda1' const USB_MOUNT_PATH = process.env.USB_MOUNT_PATH || '/mnt/sda1'

View File

@ -1,27 +1,170 @@
const chalk = require('chalk') 'use strict'
const Cobot = require('./cobot')
// const axios = require('axios')
const https = require('https')
const fs = require('fs') const fs = require('fs')
const SerialPort = require('serialport') const SerialPort = require('serialport')
const tessel = require('tessel') const tessel = require('tessel')
const { const {
CARD_UPDATE_INTERVAL, CARD_UPDATE_INTERVAL,
CARDS_PATH, CARDS_PATH,
COBOT_CARDS_API,
COBOT_CLIENT_ID,
COBOT_CLIENT_SECRET,
COBOT_SCOPE,
COBOT_USER_EMAIL,
COBOT_USER_PASSWORD,
DOOR_OPEN_DELAY, DOOR_OPEN_DELAY,
ENV,
RFID_READER_SERIAL_NUMBER, RFID_READER_SERIAL_NUMBER,
} = require('./constants') } = require('./constants')
const TEST = process.env.NODE_ENV === 'test' const TEST = ENV === 'test'
class Cobot {
constructor(token) {
this.token = token
}
cards() {
if (!COBOT_CARDS_API)
throw new Error('missing "COBOT_CARDS_API" env variable!')
return new Promise((resolve, reject) => {
const req = https.request(
{
headers: {
Authorization: `Bearer ${this.token}`,
},
hostname: 'chimera.cobot.me',
method: 'GET',
path: '/api/check_in_tokens',
},
res => {
const { statusCode, headers } = res
console.log('\n----------------------------------------------------')
console.log('COBOT CARDS RESPONSE:')
console.log(JSON.stringify({ statusCode, headers }, null, 2))
res.setEncoding('utf8')
let cards = ''
res.on('data', chunk => {
cards += chunk
})
res.on('end', () => {
cards = JSON.parse(cards)
console.log(JSON.stringify(cards, null, 2))
if (!cards || !cards.length) {
throw new Error('No cards received from API!')
}
resolve(
cards.map(card => ({
name: card.membership.name,
number: card.token,
}))
)
console.log(
'----------------------------------------------------\n'
)
})
}
)
req.on('data', console.log)
req.on('error', e => {
console.error(e)
reject(e)
})
req.end()
})
// return axios
// .get(COBOT_CARDS_API, {
// headers: {
// Authorization: `Bearer ${this.token}`,
// },
// })
// .then(resp =>
// resp.data.map(card => ({
// name: card.membership.name,
// number: card.token,
// }))
// )
}
static authorize() {
console.log('Authorizing Cobot application...')
if (!COBOT_SCOPE) throw new Error('missing "COBOT_SCOPE" env variable!')
if (!COBOT_USER_EMAIL)
throw new Error('missing "COBOT_USER_EMAIL" env variable!')
if (!COBOT_USER_PASSWORD)
throw new Error('missing "COBOT_USER_PASSWORD" env variable!')
if (!COBOT_CLIENT_ID)
throw new Error('missing "COBOT_CLIENT_ID" env variable!')
if (!COBOT_CLIENT_SECRET)
throw new Error('missing "COBOT_CLIENT_SECRET" env variable!')
const qs = [
`scope=${COBOT_SCOPE}`,
`grant_type=password`,
`username=${COBOT_USER_EMAIL}`,
`password=${encodeURI(COBOT_USER_PASSWORD)}`,
`client_id=${COBOT_CLIENT_ID}`,
`client_secret=${COBOT_CLIENT_SECRET}`,
].join('&')
return new Promise((resolve, reject) => {
const req = https.request(
{
hostname: 'www.cobot.me',
method: 'POST',
path: `/oauth/access_token?${qs}`,
},
res => {
const { statusCode, headers } = res
console.log('\n----------------------------------------------------')
console.log('COBOT AUTHORIZATION RESPONSE:')
console.log(JSON.stringify({ statusCode, headers }, null, 2))
res.setEncoding('utf8')
let cobot
res.on('data', chunk => {
const body = JSON.parse(chunk)
console.log(JSON.stringify({ body }, null, 2))
const token = body.access_token
if (!token) {
console.error(`Expected access token, got: ${body}`)
throw new Error('Error getting access token')
}
cobot = new Cobot(token)
})
res.on('end', () => {
resolve(cobot)
console.log(
'----------------------------------------------------\n'
)
})
}
)
req.on('error', e => reject(e))
req.end()
})
// return axios
// .post(`https://www.cobot.me/oauth/access_token?${qs}`)
// .then(resp => new Cobot(resp.data.access_token))
}
}
class DoorLock { class DoorLock {
constructor() { constructor() {
this.log(chalk.gray('Initializing doorlock')) this.log('Initializing doorlock')
if (!RFID_READER_SERIAL_NUMBER) { if (!RFID_READER_SERIAL_NUMBER) {
throw new Error('No serial number set!') throw new Error('No serial number set!')
} }
this.cards = [] this.cards = []
// this.fetchCardListFromCobot() this.fetchCardListFromCobot()
this.initializeRFIDReader() this.initializeRFIDReader()
} }
@ -37,8 +180,8 @@ class DoorLock {
getListOfSerialDevices() { getListOfSerialDevices() {
return (SerialPort.list() || Promise.resolve([])).then(devices => { return (SerialPort.list() || Promise.resolve([])).then(devices => {
this.log(chalk.green('Available serial devices:')) this.log('Available serial devices:')
this.log(chalk.gray(JSON.stringify(devices.map(d => d.comName), null, 2))) this.log(JSON.stringify(devices.map(d => d.comName), null, 2))
return devices return devices
}) })
} }
@ -56,7 +199,7 @@ class DoorLock {
} }
connectToRFIDReaderDevice(port) { connectToRFIDReaderDevice(port) {
this.log(chalk.green('Connected to device:'), chalk.gray(port)) this.log('Connected to device:', port)
const device = new SerialPort(port, { baudRate: 9600 }) const device = new SerialPort(port, { baudRate: 9600 })
const Readline = SerialPort.parsers.Readline const Readline = SerialPort.parsers.Readline
const parser = new Readline() const parser = new Readline()
@ -76,24 +219,24 @@ class DoorLock {
16 16
) )
this.log(chalk.green('Scanned card:'), chalk.gray(scanned)) this.log('Scanned card:', scanned)
return this.readCardsFromSDCard().then(cards => { return this.readCardsFromSDCard().then(cards => {
const card = cards.find(c => parseInt(c.number) === scanned) const card = cards.find(c => parseInt(c.number) === scanned)
if (card) { if (card) {
const name = card.name.split(' ')[0] const name = card.name.split(' ')[0]
this.log(chalk.green(`Welcome in ${name}!`), chalk.gray(scanned)) this.log(`Welcome in ${name}!`, scanned)
this.openDoor() this.openDoor()
} else { } else {
this.log(chalk.red('Card is invalid:'), chalk.gray(scanned)) this.log('Card is invalid:', scanned)
} }
}) })
} }
openDoor() { openDoor() {
return new Promise(resolve => { return new Promise(resolve => {
this.log(chalk.green('Opening door!')) this.log('Opening door!')
// TODO: trigger door opening... // TODO: trigger door opening...
if (tessel.led) tessel.led[3].on() if (tessel.led) tessel.led[3].on()
@ -107,27 +250,25 @@ class DoorLock {
} }
closeDoor() { closeDoor() {
this.log(chalk.green('Closing door!')) this.log('Closing door!')
if (tessel.led) tessel.led[3].off() if (tessel.led) tessel.led[3].off()
} }
fetchCardListFromCobot() { fetchCardListFromCobot() {
this.log(chalk.gray('Updating cards...')) this.log('Updating cards...')
Cobot.authorize() Cobot.authorize()
.then(cobot => cobot.cards()) .then(cobot => cobot.cards())
.then(cards => { .then(cards => {
this.log(chalk.green('UPDATED CARDS:', cards.length, 'cards')) this.log('UPDATED CARDS:', cards.length, 'cards')
this.writeCardsToSDCard(cards) this.writeCardsToSDCard(cards)
this.cards = cards this.cards = cards
}) })
.then(() => { .then(() => {
this.log( this.log(
chalk.gray(
'Updating card list in', 'Updating card list in',
CARD_UPDATE_INTERVAL / 1000, CARD_UPDATE_INTERVAL / 1000,
'seconds...' 'seconds...'
) )
)
setTimeout(this.fetchCardListFromCobot.bind(this), CARD_UPDATE_INTERVAL) setTimeout(this.fetchCardListFromCobot.bind(this), CARD_UPDATE_INTERVAL)
}) })
.catch(this.logErrorMessage) .catch(this.logErrorMessage)
@ -137,7 +278,6 @@ class DoorLock {
const sorted = cards.sort( const sorted = cards.sort(
(a, b) => (a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1) (a, b) => (a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1)
) )
this.log(chalk.green('CARDS:'), sorted)
return sorted return sorted
} }
@ -145,8 +285,6 @@ class DoorLock {
const json = JSON.stringify(this.sortCardsByName(cards)) const json = JSON.stringify(this.sortCardsByName(cards))
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.writeFile(CARDS_PATH, json, err => { fs.writeFile(CARDS_PATH, json, err => {
console.log(json)
console.log('JSON', json)
if (err) return reject(err) if (err) return reject(err)
resolve() resolve()
}) })
@ -164,8 +302,8 @@ class DoorLock {
logErrorMessage(error) { logErrorMessage(error) {
if (TEST) return if (TEST) return
console.error(chalk.red(error.message)) console.error(error.message)
console.error(chalk.gray(error.stack)) console.error(error.stack)
process.exit process.exit
} }

View File

@ -1,10 +1,10 @@
{ {
"name": "doorlock", "name": "doorlock",
"version": "0.1.0", "version": "0.1.0",
"main": "src/boot.js", "main": "doorlock.js",
"scripts": { "scripts": {
"deploy": "t2 run src/boot.js", "deploy": "t2 run doorlock.js",
"start": "node src/boot.js", "start": "node doorlock.js",
"test": "jest", "test": "jest",
"watch-test": "npm test -- --watch" "watch-test": "npm test -- --watch"
}, },