intval3/services/bluetooth/index.js

161 lines
4.0 KiB
JavaScript

'use strict'
const ipc = require('node-ipc')
const os = require('os')
const bleno = require('bleno')
const util = require('util')
const wifi = require('../../lib/wifi')
const BLENO_DEVICE_NAME = process.env.BLENO_DEVICE_NAME || 'my_project'
const DEVICE_ID = process.env.DEVICE_ID || 'my_project_id'
const SERVICE_ID = process.env.SERVICE_ID || 'blootstrap'
const CHAR_ID = process.env.CHAR_ID || 'blootstrapchar'
const WIFI_ID = process.env.WIFI_ID || 'blootstrapwifi'
const NETWORK = os.networkInterfaces()
const MAC = getMac() || spoofMac()
let currentWifi = 'disconnected'
const chars = []
ipc.config.id = 'blootstrap_ble'
ipc.config.retry = 1500
ipc.config.rawBuffer = true
ipc.config.encoding = 'hex'
function createChar(name, uuid, prop, write, read) {
function characteristic () {
bleno.Characteristic.call(this, {
uuid : uuid,
properties: prop
})
}
util.inherits(characteristic, bleno.Characteristic)
if (prop.indexOf('read') !== -1) {
//data, offset, withoutResponse, callback
characteristic.prototype.onReadRequest = read
}
if (prop.indexOf('write') !== -1) {
characteristic.prototype.onWriteRequest = write
}
chars.push(new characteristic())
}
function createChars () {
createChar('wifi', WIFI_ID, ['read', 'write'], onWifiWrite, onWifiRead)
}
function onWifiWrite (data, offset, withoutResponse, callback) {
let result
let utf8
let obj
let ssid
let pwd
if (offset) {
console.warn(`Offset scenario`)
result = bleno.Characteristic.RESULT_ATTR_NOT_LONG
return callback(result)
}
utf8 = data.toString('utf8')
obj = JSON.parse(utf8)
ssid = obj.ssid
pwd = obj.pwd
console.log(`Connecting to AP: ${ssid}...`)
return wifi.setNetwork(ssid, pwd, (err, data) => {
if (err) {
console.error('Error configuring wifi', err)
result = bleno.Characteristic.RESULT_UNLIKELY_ERROR
return callback(result)
}
currentWifi = ssid
console.log(`Connected to ${ssid}`)
result = bleno.Characteristic.RESULT_SUCCESS
return callback(result)
})
}
function onWifiRead (offset, callback) {
const result = bleno.Characteristic.RESULT_SUCCESS
const data = new Buffer(JSON.stringify(currentWifi))
callback(result, data.slice(offset, data.length))
}
function getMac () {
const colonRe = new RegExp(':', 'g')
if (NETWORK && NETWORK.wlan0 && NETWORK.wlan0[0] && NETWORK.wlan0[0].mac) {
return NETWORK.wlan0[0].mac.replace(colonRe, '')
}
return undefined
}
function spoofMac () {
const fs = require('fs')
const FSPATH = require.resolve('uuid')
const IDFILE = os.homedir() + '/.intval3id'
let uuid
let UUIDPATH
let TMP
let MACTMP
let dashRe
delete require.cache[FSPATH]
if (fs.existsSync(IDFILE)) {
return fs.readFileSync(IDFILE, 'utf8')
}
uuid = require('uuid').v4
UUIDPATH = require.resolve('uuid')
delete require.cache[UUIDPATH]
TMP = uuid()
MACTMP = TMP.replace(dashRe, '').substring(0, 12)
dashRe = new RegExp('-', 'g')
fs.writeFileSync(IDFILE, MACTMP, 'utf8')
return MACTMP
}
console.log('Starting bluetooth service')
bleno.on('stateChange', state => {
const BLE_ID = `${DEVICE_ID}_${MAC}`
console.log(`on -> stateChange: ${state}`)
if (state === 'poweredOn') {
console.log(`Started advertising BLE serveses as ${BLE_ID}`)
bleno.startAdvertising(BLENO_DEVICE_NAME, [BLE_ID])
} else {
bleno.stopAdvertising()
}
})
bleno.on('advertisingStart', err => {
console.log('on -> advertisingStart: ' + (err ? 'error ' + err : 'success'))
createChars()
if (!err) {
bleno.setServices([
new bleno.PrimaryService({
uuid : SERVICE_ID, //hardcoded across panels
characteristics : chars
})
])
}
})
bleno.on('accept', clientAddress => {
console.log(`${clientAddress} accepted`)
})
bleno.on('disconnect', clientAddress => {
console.log(`${clientAddress} disconnected`)
})
ipc.serve(() => {
ipc.server.on('connect', socket => {
ipc.log('Client connected to socket')
})
ipc.server.on('disconnect', () => {
ipc.log('Client disconnected from socket')
})
ipc.server.on('data', (data, socket) => {
ipc.server.emit(socket, JSON.stringify({}))
})
})
ipc.server.start()