297 lines
7.6 KiB
JavaScript
297 lines
7.6 KiB
JavaScript
'use strict'
|
|
/*
|
|
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
|
|
|
|
This manager creates the different Bluetooth Low Energy (BLE) Peripherals and allows
|
|
switching between them
|
|
*/
|
|
import config from '../tools/ConfigManager.js'
|
|
import { createFtmsPeripheral } from './ble/FtmsPeripheral.js'
|
|
import { createPm5Peripheral } from './ble/Pm5Peripheral.js'
|
|
import log from 'loglevel'
|
|
import EventEmitter from 'node:events'
|
|
import { createCpsPeripheral } from './ble/CpsPeripheral.js'
|
|
import { createCscPeripheral } from './ble/CscPeripheral.js'
|
|
import AntManager from './ant/AntManager.js'
|
|
import { createAntHrmPeripheral } from './ant/HrmPeripheral.js'
|
|
import { createBleHrmPeripheral } from './ble/HrmPeripheral.js'
|
|
import { createFEPeripheral } from './ant/FEPeripheral.js'
|
|
|
|
const bleModes = ['FTMS', 'FTMSBIKE', 'PM5', 'CSC', 'CPS', 'OFF']
|
|
const antModes = ['FE', 'OFF']
|
|
const hrmModes = ['ANT', 'BLE', 'OFF']
|
|
function createPeripheralManager () {
|
|
const emitter = new EventEmitter()
|
|
let _antManager
|
|
let blePeripheral
|
|
let bleMode
|
|
|
|
let antPeripheral
|
|
let antMode
|
|
|
|
let hrmPeripheral
|
|
let hrmMode
|
|
|
|
let isPeripheralChangeInProgress = false
|
|
|
|
setupPeripherals()
|
|
|
|
async function setupPeripherals () {
|
|
await createBlePeripheral(config.bluetoothMode)
|
|
await createHrmPeripheral(config.heartRateMode)
|
|
await createAntPeripheral(config.antplusMode)
|
|
}
|
|
|
|
function getBlePeripheral () {
|
|
return blePeripheral
|
|
}
|
|
|
|
function getBlePeripheralMode () {
|
|
return bleMode
|
|
}
|
|
|
|
function getAntPeripheral () {
|
|
return antPeripheral
|
|
}
|
|
|
|
function getAntPeripheralMode () {
|
|
return antMode
|
|
}
|
|
|
|
function getHrmPeripheral () {
|
|
return hrmPeripheral
|
|
}
|
|
|
|
function getHrmPeripheralMode () {
|
|
return hrmMode
|
|
}
|
|
|
|
function switchBlePeripheralMode (newMode) {
|
|
if (isPeripheralChangeInProgress) return
|
|
isPeripheralChangeInProgress = true
|
|
// if now mode was passed, select the next one from the list
|
|
if (newMode === undefined) {
|
|
newMode = bleModes[(bleModes.indexOf(bleMode) + 1) % bleModes.length]
|
|
}
|
|
createBlePeripheral(newMode)
|
|
isPeripheralChangeInProgress = false
|
|
}
|
|
|
|
function notifyMetrics (type, metrics) {
|
|
if (bleMode !== 'OFF') { blePeripheral?.notifyData(type, metrics) }
|
|
if (antMode !== 'OFF') { antPeripheral?.notifyData(type, metrics) }
|
|
}
|
|
|
|
function notifyStatus (status) {
|
|
if (bleMode !== 'OFF') { blePeripheral?.notifyStatus(status) }
|
|
if (antMode !== 'OFF') { antPeripheral?.notifyStatus(status) }
|
|
}
|
|
|
|
async function createBlePeripheral (newMode) {
|
|
if (blePeripheral) {
|
|
await blePeripheral?.destroy()
|
|
blePeripheral = undefined
|
|
}
|
|
|
|
switch (newMode) {
|
|
case 'PM5':
|
|
log.info('bluetooth profile: Concept2 PM5')
|
|
blePeripheral = createPm5Peripheral(controlCallback)
|
|
bleMode = 'PM5'
|
|
break
|
|
|
|
case 'FTMSBIKE':
|
|
log.info('bluetooth profile: FTMS Indoor Bike')
|
|
blePeripheral = createFtmsPeripheral(controlCallback, {
|
|
simulateIndoorBike: true
|
|
})
|
|
bleMode = 'FTMSBIKE'
|
|
break
|
|
|
|
case 'CSC':
|
|
log.info('bluetooth profile: Cycling Speed and Cadence')
|
|
blePeripheral = createCscPeripheral()
|
|
bleMode = 'CSC'
|
|
break
|
|
|
|
case 'CPS':
|
|
log.info('bluetooth profile: Cycling Power Meter')
|
|
blePeripheral = createCpsPeripheral()
|
|
bleMode = 'CPS'
|
|
break
|
|
|
|
case 'FTMS':
|
|
log.info('bluetooth profile: FTMS Rower')
|
|
blePeripheral = createFtmsPeripheral(controlCallback, {
|
|
simulateIndoorBike: false
|
|
})
|
|
bleMode = 'FTMS'
|
|
break
|
|
|
|
default:
|
|
log.info('bluetooth profile: Off')
|
|
bleMode = 'OFF'
|
|
}
|
|
if (bleMode.toLocaleLowerCase() !== 'OFF'.toLocaleLowerCase()) { blePeripheral.triggerAdvertising() }
|
|
|
|
emitter.emit('control', {
|
|
req: {
|
|
name: 'blePeripheralMode',
|
|
peripheralMode: bleMode
|
|
}
|
|
})
|
|
}
|
|
|
|
function switchAntPeripheralMode (newMode) {
|
|
if (isPeripheralChangeInProgress) return
|
|
isPeripheralChangeInProgress = true
|
|
if (newMode === undefined) {
|
|
newMode = antModes[(antModes.indexOf(antMode) + 1) % antModes.length]
|
|
}
|
|
createAntPeripheral(newMode)
|
|
isPeripheralChangeInProgress = false
|
|
}
|
|
|
|
async function createAntPeripheral (newMode) {
|
|
if (antPeripheral) {
|
|
await antPeripheral?.destroy()
|
|
antPeripheral = undefined
|
|
|
|
try {
|
|
if (_antManager && hrmMode !== 'ANT' && newMode === 'OFF') { await _antManager.closeAntStick() }
|
|
} catch (error) {
|
|
log.error(error)
|
|
return
|
|
}
|
|
}
|
|
|
|
switch (newMode) {
|
|
case 'FE':
|
|
log.info('ant plus profile: FE')
|
|
if (!_antManager) {
|
|
_antManager = new AntManager()
|
|
}
|
|
|
|
try {
|
|
antPeripheral = createFEPeripheral(_antManager)
|
|
antMode = 'FE'
|
|
await antPeripheral.attach()
|
|
} catch (error) {
|
|
log.error(error)
|
|
return
|
|
}
|
|
break
|
|
|
|
default:
|
|
log.info('ant plus profile: Off')
|
|
antMode = 'OFF'
|
|
}
|
|
|
|
emitter.emit('control', {
|
|
req: {
|
|
name: 'antPeripheralMode',
|
|
peripheralMode: antMode
|
|
}
|
|
})
|
|
}
|
|
|
|
function switchHrmMode (newMode) {
|
|
if (isPeripheralChangeInProgress) return
|
|
isPeripheralChangeInProgress = true
|
|
if (newMode === undefined) {
|
|
newMode = hrmModes[(hrmModes.indexOf(hrmMode) + 1) % hrmModes.length]
|
|
}
|
|
createHrmPeripheral(newMode)
|
|
isPeripheralChangeInProgress = false
|
|
}
|
|
|
|
async function createHrmPeripheral (newMode) {
|
|
if (hrmPeripheral) {
|
|
await hrmPeripheral?.destroy()
|
|
hrmPeripheral?.removeAllListeners()
|
|
hrmPeripheral = undefined
|
|
try {
|
|
if (_antManager && newMode !== 'ANT' && antMode === 'OFF') { await _antManager.closeAntStick() }
|
|
} catch (error) {
|
|
log.error(error)
|
|
return
|
|
}
|
|
}
|
|
|
|
switch (newMode) {
|
|
case 'ANT':
|
|
log.info('heart rate profile: ANT')
|
|
if (!_antManager) {
|
|
_antManager = new AntManager()
|
|
}
|
|
|
|
try {
|
|
hrmPeripheral = createAntHrmPeripheral(_antManager)
|
|
hrmMode = 'ANT'
|
|
await hrmPeripheral.attach()
|
|
} catch (error) {
|
|
log.error(error)
|
|
return
|
|
}
|
|
break
|
|
|
|
case 'BLE':
|
|
log.info('heart rate profile: BLE')
|
|
hrmPeripheral = createBleHrmPeripheral()
|
|
hrmMode = 'BLE'
|
|
break
|
|
|
|
default:
|
|
log.info('heart rate profile: Off')
|
|
hrmMode = 'OFF'
|
|
}
|
|
|
|
if (hrmMode.toLocaleLowerCase() !== 'OFF'.toLocaleLowerCase()) {
|
|
hrmPeripheral.on('heartRateMeasurement', (heartRateMeasurement) => {
|
|
emitter.emit('heartRateMeasurement', heartRateMeasurement)
|
|
})
|
|
}
|
|
|
|
emitter.emit('control', {
|
|
req: {
|
|
name: 'hrmPeripheralMode',
|
|
peripheralMode: hrmMode
|
|
}
|
|
})
|
|
}
|
|
|
|
function controlCallback (event) {
|
|
emitter.emit('control', event)
|
|
}
|
|
|
|
async function shutdownAllPeripherals () {
|
|
log.debug('shutting down all peripherals')
|
|
|
|
try {
|
|
await blePeripheral?.destroy()
|
|
await antPeripheral?.destroy()
|
|
await hrmPeripheral?.destroy()
|
|
await _antManager?.closeAntStick()
|
|
} catch (error) {
|
|
log.error('peripheral shutdown was unsuccessful, restart of Pi may required', error)
|
|
}
|
|
}
|
|
|
|
return Object.assign(emitter, {
|
|
shutdownAllPeripherals,
|
|
getBlePeripheral,
|
|
getBlePeripheralMode,
|
|
getAntPeripheral,
|
|
getAntPeripheralMode,
|
|
getHrmPeripheral,
|
|
getHrmPeripheralMode,
|
|
switchHrmMode,
|
|
switchBlePeripheralMode,
|
|
switchAntPeripheralMode,
|
|
notifyMetrics,
|
|
notifyStatus
|
|
})
|
|
}
|
|
|
|
export { createPeripheralManager }
|