-
Notifications
You must be signed in to change notification settings - Fork 3
/
device.js
135 lines (116 loc) · 3.21 KB
/
device.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
let noble = require('noble');
let Rx = require('rx');
const ACCEL_OFF = Buffer.from([0x00]);
const ACCEL_ON = Buffer.from([0x01]);
// MAGIC CONSTANTS FOLLOW
// these might be different for other devices, I wouldn't know
const IDENTIFIER = 'CHSLEEV_00';
const DEVICE_INFO_UUID = '180a';
const ACCEL_UUID = 'ffa0';
const RATIO = 14;
// MAGIC CONSTANTS END
noble.on('stateChange', function(state) {
if(state === 'poweredOn') {
noble.startScanning();
}
else {
noble.stopScanning();
}
});
let button$ = new Rx.Subject();
let discover$ = Rx.Observable.fromEvent(noble, 'discover');
let peripheral$ = discover$
.filter(p => p.advertisement.localName === IDENTIFIER)
.do(_ => noble.stopScanning())
.take(1)
.shareReplay(1)
let connection$ = peripheral$
.flatMapFirst(peripheral => {
let disconnect$ = Rx.Observable.fromEvent(peripheral, 'disconnect')
.map(_ => { return {state: 'disconnected'}})
.take(1);
let connect$ = Rx.Observable.fromEvent(peripheral, 'connect')
.map(_ => { return {state: 'connected', peripheral}})
.take(1);
peripheral.connect();
return disconnect$.merge(connect$);
})
.repeat()
.share()
let services$ = connection$
.filter(s => s && s.state === 'connected')
.flatMap(s => {
let peripheral = s.peripheral;
let discoverServices = Rx.Observable.fromNodeCallback(
peripheral.discoverSomeServicesAndCharacteristics,
peripheral
);
return discoverServices([DEVICE_INFO_UUID, ACCEL_UUID], []);
})
.filter(s => s.length == 2 && s[0].length == 2)
.map(s => s[0])
.share()
let serial$ = services$
.flatMap(s => {
let info = s[0].characteristics[2];
return Rx.Observable.fromNodeCallback(info.read, info)();
})
let connStatus$ = connection$.combineLatest(serial$, (conn, serial) => {
if(conn.state == 'connected') {
return Object.assign({serial}, conn);
}
else {
return conn;
}
}).share()
function makeAccelStream(characteristic) {
return Rx.Observable.fromEvent(characteristic, 'data')
.map(d => d[3] * 256 * 256 * 256 + d[2] * 256 * 256 + d[1] * 256 + d[0])
.startWith(-1)
}
let accel$ = services$
.do(s => {
s[1].characteristics[0].write(ACCEL_ON);
s[1].characteristics[2].subscribe();
s[1].characteristics[3].subscribe();
s[1].characteristics[4].subscribe();
})
.flatMap(s => {
let characteristics = s[1].characteristics;
return Rx.Observable.combineLatest(
makeAccelStream(characteristics[2]),
makeAccelStream(characteristics[3]),
makeAccelStream(characteristics[4])
)
})
.share()
let xAccel$ = accel$
.map(r => r[0])
.filter(v => v >= 0)
function item(type, value) {
return { type, value }
}
let tareFromButton$ = button$
.withLatestFrom(xAccel$, (_, accel) => item('reset', accel))
let tareFromXAccel$ = xAccel$
.map(accel => item('value', accel))
let tare$ = tareFromButton$.merge(tareFromXAccel$)
.scan((acc, item) => {
if(item.type === 'reset') {
return item.value;
}
else {
return Math.max(acc, item.value);
}
}, 0)
let weight$ = xAccel$
.combineLatest(tare$, (accel, tare) => {
return (tare - accel) / RATIO;
})
.map(Math.floor)
module.exports = {
button$,
connStatus$,
weight$,
accel$
}