adds support for ant+ heart rate monitors

This commit is contained in:
Lars Berning 2021-04-22 21:03:59 +02:00
parent 15c1553e42
commit d385ad78d7
8 changed files with 165 additions and 85 deletions

View File

@ -24,7 +24,7 @@ Open Rowing Monitor implements a physics model to simulate the typical metrics o
* Strokes per Minute
* Calories used (kcal)
* Training Duration
* Heart Rate (requires a BLE Heart Rate Monitor)
* Heart Rate (supports BLE and ANT+ heart rate monitors, ANT+ requires an ANT+ USB stick)
### Web Interface

64
app/ant/AntManager.js Normal file
View File

@ -0,0 +1,64 @@
'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
This manager creates a module to listen to ANT+ devices.
This currently can be used to get the heart rate from ANT+ heart rate sensors.
Requires an ANT+ USB stick, the following models might work:
- Garmin USB or USB2 ANT+ or an off-brand clone of it (ID 0x1008)
- Garmin mini ANT+ (ID 0x1009)
*/
import log from 'loglevel'
import Ant from 'ant-plus'
import EventEmitter from 'node:events'
function createAntManager () {
const emitter = new EventEmitter()
const antStick = new Ant.GarminStick2()
const antStick3 = new Ant.GarminStick3()
const heartrateSensor = new Ant.HeartRateSensor(antStick)
heartrateSensor.on('hbData', (data) => {
emitter.emit('heartrateMeasurement', { heartrate: data.ComputedHeartRate, batteryLevel: data.BatteryLevel })
})
heartrateSensor.on('attached', () => {
log.info('heart rate sensor attached')
})
heartrateSensor.on('detached', () => {
log.info('heart rate sensor detached')
})
antStick.on('startup', () => {
log.info('classic ANT+ stick found')
heartrateSensor.attach(0, 0)
})
antStick3.on('startup', () => {
log.info('mini ANT+ stick found')
heartrateSensor.attach(0, 0)
})
antStick.on('shutdown', () => {
log.info('classic ANT+ stick lost')
})
antStick3.on('shutdown', () => {
log.info('mini ANT+ stick lost')
})
if (!antStick.open()) {
log.debug('classic ANT+ stick not found')
}
if (!antStick3.open()) {
log.debug('mini ANT+ stick not found')
}
return Object.assign(emitter, {
})
}
export { createAntManager }

View File

@ -15,6 +15,7 @@ import { createRowingEngine } from './engine/RowingEngine.js'
import { createRowingStatistics } from './engine/RowingStatistics.js'
import { createWebServer } from './WebServer.js'
import { createPeripheralManager } from './ble/PeripheralManager.js'
import { createAntManager } from './ant/AntManager.js'
// eslint-disable-next-line no-unused-vars
import { replayRowingSession } from './tools/RowingRecorder.js'
@ -87,10 +88,19 @@ rowingStatistics.on('metricsUpdate', (metrics) => {
peripheralManager.notifyMetrics('metricsUpdate', metrics)
})
const bleCentralService = fork('./app/ble/CentralService.js')
bleCentralService.on('message', (heartrateMeasurement) => {
rowingStatistics.handleHeartrateMeasurement(heartrateMeasurement)
})
if (config.heartrateMonitorBLE) {
const bleCentralService = fork('./app/ble/CentralService.js')
bleCentralService.on('message', (heartrateMeasurement) => {
rowingStatistics.handleHeartrateMeasurement(heartrateMeasurement)
})
}
if (config.heartrateMonitorANT) {
const antManager = createAntManager()
antManager.on('heartrateMeasurement', (heartrateMeasurement) => {
rowingStatistics.handleHeartrateMeasurement(heartrateMeasurement)
})
}
const webServer = createWebServer()
webServer.on('messageReceived', (message) => {

View File

@ -26,6 +26,16 @@ export default {
// supported modes: FTMS, FTMSBIKE, PM5
bluetoothMode: 'FTMS',
// turn this on if you want support for Bluetooth Low Energy heart rate monitors
// will currenty the hear rate and battery level of the first device found
heartrateMonitorBLE: true,
// turn this on if you want support for ANT+ heart rate monitors
// you will need an ANT+ USB stick for this to work, the following models might work:
// - Garmin USB or USB2 ANT+ or an off-brand clone of it (ID 0x1008)
// - Garmin mini ANT+ (ID 0x1009)
heartrateMonitorANT: false,
// defines the name that is used to announce the FTMS Rower via Bluetooth Low Energy (BLE)
// some rowing training applications expect that the rowing device is announced with a certain name
ftmsRowerPeripheralName: 'OpenRowingMonitor',

View File

@ -4,7 +4,6 @@ This is the very minimalistic Backlog for further development of this project.
## Soon
* add support for ANT+ heart rate monitors with USB dongles
* add an option to the installation script to directly attach a touchscreen to the Raspberry Pi and automatically show WebUI on this in kiosk mode
* validate FTMS with more training applications and harden implementation (i.e. Holofit and Coxswain)
* record a longer rowing session and analyze two encountered problems: 1) rarely the stroke rate doubles for a short duration (might be a problem with stroke detection when measurements are imprecise), 2) in one occasion the measured power jumped to a very high value after a break (40000 watts)

View File

@ -35,12 +35,27 @@ Open Rowing Monitor does not provide proper releases (yet), but you can update t
sudo /opt/openrowingmonitor/install/update.sh
```
### If you want to run Bluetooth Low Energy and the Web-Server on port 80 without root
### Running Open Rowing Monitor without root permissions
The default installation will run Open Rowing Monitor with root permissions. If you want you can also run it as user by modifying the following system services:
#### To use BLE and open the Web-Server on port 80
Issue the following command:
```zsh
sudo setcap cap_net_bind_service,cap_net_raw=+eip $(eval readlink -f `which node`)
```
#### To access ANT+ USB sticks
Create a file `/etc/udev/rules.d/51-garmin-usb.rules` with the following content:
```zsh
ATTRS{idVendor}=="0fcf", ATTRS{idProduct}=="1008", MODE="0666"
ATTRS{idVendor}=="0fcf", ATTRS{idProduct}=="1009", MODE="0666"
```
## Hardware Installation
Basically all that's left to do is hook up your sensor to the GPIO pins of the Raspberry Pi and configure the rowing machine specific parameters of the software.
@ -68,4 +83,4 @@ If your machine does not have something like this or if the sensor is not access
* PAS sensor (i.e. from an E-bike)
* Optical chopper wheel
You should now adjust the rower specific parameters in `config/config.js` to suit your rowing machine.
You should now adjust the rower specific parameters in `config/config.js` to suit your rowing machine. Have a look at [config/default.config.js](../config/default.config.js) to see what config parameters are available.

131
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "openrowingmonitor",
"version": "0.7.0",
"version": "0.7.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -130,9 +130,9 @@
"dev": true
},
"@npmcli/git": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.7.tgz",
"integrity": "sha512-HUSqNDWYsTpboc7yV1C4yPd/jbaGXfWVmGoTyB+h3QQSKMpYPzTXLrqUMpz+LEA6Dt9usUeRtjytwcrfoBMJpg==",
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.8.tgz",
"integrity": "sha512-LPnzyBZ+1p7+JzHVwwKycMF8M3lr1ze3wxGRnxn/QxJtk++Y3prSJQrdBDGCxJyRpFsup6J3lrRBVYBhJVrM8Q==",
"dev": true,
"requires": {
"@npmcli/promise-spawn": "^1.3.2",
@ -215,9 +215,9 @@
}
},
"@npmcli/run-script": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.4.tgz",
"integrity": "sha512-Yd9HXTtF1JGDXZw0+SOn+mWLYS0e7bHBHVC/2C8yqs4wUrs/k8rwBSinD7rfk+3WG/MFGRZKxjyoD34Pch2E/A==",
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.5.tgz",
"integrity": "sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A==",
"dev": true,
"requires": {
"@npmcli/node-gyp": "^1.0.2",
@ -317,6 +317,14 @@
"color-convert": "^1.9.0"
}
},
"ant-plus": {
"version": "0.1.24",
"resolved": "https://registry.npmjs.org/ant-plus/-/ant-plus-0.1.24.tgz",
"integrity": "sha512-otEIAN+9jtu/1mwHL81au0sO7muJT1QrWOm9j29NEg3x1Y+ZsSIYX+LSdY+BBr2mLA4hT/vpqv5pWX9KDWceVg==",
"requires": {
"usb": "^1.6.0"
}
},
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@ -406,8 +414,7 @@
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"optional": true
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
@ -434,7 +441,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"optional": true,
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
@ -445,7 +451,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"optional": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@ -476,7 +481,6 @@
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"optional": true,
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
@ -606,9 +610,9 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@ -659,8 +663,7 @@
"chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"optional": true
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"clean-stack": {
"version": "2.2.0",
@ -760,7 +763,6 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"optional": true,
"requires": {
"mimic-response": "^2.0.0"
}
@ -836,8 +838,7 @@
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"optional": true
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"diff": {
"version": "5.0.0",
@ -905,7 +906,6 @@
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"optional": true,
"requires": {
"once": "^1.4.0"
}
@ -1348,8 +1348,7 @@
"expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"optional": true
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
},
"extend": {
"version": "3.0.2",
@ -1491,8 +1490,7 @@
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"optional": true
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"fs-minipass": {
"version": "1.2.7",
@ -1570,8 +1568,7 @@
"github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=",
"optional": true
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
},
"glob": {
"version": "7.1.6",
@ -1750,8 +1747,7 @@
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"optional": true
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"ignore": {
"version": "4.0.6",
@ -2259,9 +2255,9 @@
"dev": true
},
"js-yaml": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz",
"integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"requires": {
"argparse": "^2.0.1"
@ -2308,8 +2304,7 @@
"mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"optional": true
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
},
"mini-debounce": {
"version": "1.0.8",
@ -2531,8 +2526,7 @@
"mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"optional": true
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"mri": {
"version": "1.1.6",
@ -2553,8 +2547,7 @@
"napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
"optional": true
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
},
"napi-thread-safe-callback": {
"version": "0.0.6",
@ -2596,10 +2589,9 @@
"dev": true
},
"node-abi": {
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.21.0.tgz",
"integrity": "sha512-smhrivuPqEM3H5LmnY3KU6HfYv0u4QklgAxfFyRNujKUzbUcYZ+Jc2EhukB9SRcD2VpqhxM7n/MIcp1Ua1/JMg==",
"optional": true,
"version": "2.26.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.26.0.tgz",
"integrity": "sha512-ag/Vos/mXXpWLLAYWsAoQdgS+gW7IwvgMLOgqopm/DbzAjazLltzgzpVMsFlgmo9TzG5hGXeaBZx2AI731RIsQ==",
"requires": {
"semver": "^5.4.1"
}
@ -2737,8 +2729,7 @@
"noop-logger": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=",
"optional": true
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
},
"nopt": {
"version": "4.0.3",
@ -2768,9 +2759,9 @@
"integrity": "sha512-9d1HbpKLh3sdWlhXMhU6MMH+wQzKkrgfRkYV0EBdvt99YJfj0ilCJrWRDYG2130Tm4GXbEoTCx5b34JSaP+HhA=="
},
"npm-bundled": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
"integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
@ -3056,9 +3047,9 @@
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-inspect": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
"integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==",
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz",
"integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==",
"dev": true
},
"object-keys": {
@ -3395,7 +3386,6 @@
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz",
"integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==",
"optional": true,
"requires": {
"detect-libc": "^1.0.3",
"expand-template": "^2.0.3",
@ -3456,7 +3446,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"optional": true,
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
@ -3743,14 +3732,12 @@
"simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"optional": true
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"optional": true,
"requires": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
@ -3807,9 +3794,9 @@
"dev": true
},
"snowpack": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/snowpack/-/snowpack-3.3.0.tgz",
"integrity": "sha512-LXoWtVQ9SGD7XhkFS9ixOoGO4eofJW/yj8gUYBopYhYIcOSeuokzJWkW0gF5M6pM7Vw9TMO8gK9KSfc2wAwTDw==",
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/snowpack/-/snowpack-3.3.5.tgz",
"integrity": "sha512-QI6/PcSud39ZAmrs7IiBSCr3PWXxc4/w2SNJp32+n7XsXxSB4waJ+jpG5rtJAeif5pqRNkX4887PKwWsMzFRCw==",
"dev": true,
"requires": {
"cli-spinners": "^2.5.0",
@ -3825,9 +3812,9 @@
}
},
"socks": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.0.tgz",
"integrity": "sha512-mNmr9owlinMplev0Wd7UHFlqI4ofnBnNzFuzrm63PPaHgbkqCFe4T5LzwKmtQ/f2tX0NTpcdVLyD/FHxFBstYw==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
"integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
"dev": true,
"requires": {
"ip": "^1.1.5",
@ -4008,9 +3995,9 @@
}
},
"table": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/table/-/table-6.1.0.tgz",
"integrity": "sha512-T4G5KMmqIk6X87gLKWyU5exPpTjLjY5KyrFWaIjv3SvgaIUGXV7UEzGEnZJdTA38/yUS6f9PlKezQ0bYXG3iIQ==",
"version": "6.3.2",
"resolved": "https://registry.npmjs.org/table/-/table-6.3.2.tgz",
"integrity": "sha512-I9/Ca6Huf2oxFag7crD0DhA+arIdfLtWunSn0NIXSzjtUlDgIBGVZY7SsMkNPNT3Psd/z4gza0nuEpmra9eRbg==",
"dev": true,
"requires": {
"ajv": "^8.0.1",
@ -4095,7 +4082,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"optional": true,
"requires": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
@ -4107,7 +4093,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"optional": true,
"requires": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
@ -4120,7 +4105,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"optional": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@ -4263,7 +4247,6 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/usb/-/usb-1.7.0.tgz",
"integrity": "sha512-LHm9d389NCzZSMd0DnilxT5Lord4P2E3ETwP1LeuJcEBmI5uLJv8Sd18z/9bairUMbDnnNqX+Hi5Xkl93Kvdmw==",
"optional": true,
"requires": {
"bindings": "^1.4.0",
"node-addon-api": "3.0.2",
@ -4273,8 +4256,7 @@
"node-addon-api": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.2.tgz",
"integrity": "sha512-+D4s2HCnxPd5PjjI0STKwncjXTUKKqm74MDMz9OPXavjsGmjkvwgLtA5yoxJUdmpj52+2u+RrXgPipahKczMKg==",
"optional": true
"integrity": "sha512-+D4s2HCnxPd5PjjI0STKwncjXTUKKqm74MDMz9OPXavjsGmjkvwgLtA5yoxJUdmpj52+2u+RrXgPipahKczMKg=="
}
}
},
@ -4374,8 +4356,7 @@
"which-pm-runs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=",
"optional": true
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
},
"wide-align": {
"version": "1.1.3",
@ -4397,9 +4378,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz",
"integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw=="
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
},
"xpc-connect": {
"version": "2.0.0",

View File

@ -26,13 +26,14 @@
"dependencies": {
"@abandonware/bleno": "^0.5.1-4",
"@abandonware/noble": "^1.9.2-13",
"ant-plus": "^0.1.24",
"finalhandler": "^1.1.2",
"http": "0.0.1-security",
"loglevel": "^1.7.1",
"nosleep.js": "^0.12.0",
"onoff": "^6.0.2",
"serve-static": "^1.14.1",
"ws": "^7.4.4"
"ws": "^7.4.5"
},
"devDependencies": {
"eslint": "^7.24.0",
@ -43,7 +44,7 @@
"husky": "^6.0.0",
"markdownlint-cli": "^0.27.1",
"npm-run-all": "^4.1.5",
"snowpack": "^3.3.0",
"snowpack": "^3.3.5",
"uvu": "^0.5.1",
"uvu-watch": "^1.0.11"
}