Skip to content

Commit

Permalink
Merge branch 'master' into format5-invalid-data-handling
Browse files Browse the repository at this point in the history
  • Loading branch information
pbfulmar committed Feb 20, 2021
2 parents d9f7a44 + cd4201d commit e8c0eae
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 119 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Node.js module for reading data from a [Ruuvitag](http://tag.ruuvi.com)
weather station.

Tested on Raspberry Pi 3. Depends on [noble](https://github.com/sandeepmistry/noble). See [instructions](https://github.com/sandeepmistry/noble) on
Tested on Raspberry Pi 3. Depends on [noble](https://github.com/abandonware/noble). See [instructions](https://github.com/abandonware/noble) on
how to enable BLE on RasPi and how to run without root.

### Installation
Expand All @@ -13,7 +13,7 @@ npm install node-ruuvitag


### Usage example
```
```js
const ruuvi = require('node-ruuvitag');

ruuvi.on('found', tag => {
Expand All @@ -24,12 +24,18 @@ ruuvi.on('found', tag => {
});
});

ruuvi.on('warning', message => {
console.error(new Error(message));
});

```

### Events

#### found
Module ```ruuvi``` emits a ```found``` event, when a new RuuviTag
is discovered. Event's payload is a ```ruuviTag``` object (see below)
### warning
Module relays noble's [```warning``` events](https://github.com/noble/noble#warnings) (see below)

### API

Expand Down Expand Up @@ -72,6 +78,7 @@ following properties (depending on data format):
* ```txPower``` -- in data format 5
* ```movementCounter``` -- in data format 5
* ```measurementSequenceNumber``` -- in data format 5
* ```mac``` -- in data format 5

See [data formats](https://github.com/ruuvi/ruuvi-sensor-protocols) for
info about RuuviTag sensor values.
Expand Down
48 changes: 48 additions & 0 deletions adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const noble = require('@abandonware/noble');
const EventEmitter = require('events').EventEmitter;

class Adapter extends EventEmitter {
constructor () {
super();

noble.on('discover', (peripheral) => {
this.emit('discover', peripheral);
});

noble.on('warning', (warning) => {
this.emit('warning', warning);
});

// start scanning
if (noble.state === 'poweredOn') {
this.start();
} else {
noble.once('stateChange', (state) => {
if (state === 'poweredOn') {
this.start();
} else {
this.stop();
}
this.emit('stateChange', state);
});
}
}

start () {
if (this._scanning) {
return;
}
this._scanning = true;
noble.startScanning([], true);
}

stop () {
if (!this._scanning) {
return;
}
this._scanning = false;
noble.stopScanning();
}
}

module.exports = new Adapter();
19 changes: 16 additions & 3 deletions dataformats/5.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const parseRawRuuvi = function(data) {
let robject = {};
const parseRawRuuvi = function (data) {
const robject = {};

/***************************************************************************
* Modifications by pbfulmar:
Expand Down Expand Up @@ -142,9 +142,22 @@ const parseRawRuuvi = function(data) {
robject.measurementSequenceNumber = measurementSequenceNumber;
}

robject.mac = [
int2Hex(data[20]),
int2Hex(data[21]),
int2Hex(data[22]),
int2Hex(data[23]),
int2Hex(data[24]),
int2Hex(data[25])
].join(':');

return robject;
};

module.exports = {
parse: buffer => parseRawRuuvi(buffer),
parse: buffer => parseRawRuuvi(buffer)
};

function int2Hex (str) {
return ('0' + str.toString(16).toUpperCase()).slice(-2);
}
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const adapter = require('./adapter.js');
const Ruuvi = require('./ruuvi.js');

module.exports = new Ruuvi(adapter);
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "node-ruuvitag",
"version": "4.2.0",
"version": "4.6.1",
"engines": {
"node": ">6.0.0"
},
"repository": "https://github.com/pakastin/node-ruuvitag",
"description": "Read data from RuuviTag weather station",
"main": "ruuvi.js",
"main": "index.js",
"scripts": {
"test": "jasmine test/*Spec.js",
"lint": "eslint '**/*.js'",
Expand All @@ -15,14 +15,14 @@
"author": "",
"license": "BSD-3",
"devDependencies": {
"eslint": "^6.8.0",
"jasmine": "^3.6.3",
"eslint": "^7.4.0",
"jasmine": "^3.5.0",
"mockery": "^2.1.0",
"prettier": "^1.19.1",
"sinon": "^8.1.1"
"prettier": "^2.0.5",
"sinon": "^9.0.2"
},
"dependencies": {
"@abandonware/noble": "^1.9.2-10"
"@abandonware/noble": "^1.9.2-9"
},
"prettier": {
"printWidth": 120,
Expand Down
71 changes: 38 additions & 33 deletions ruuvi.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const noble = require("@abandonware/noble");
const EventEmitter = require("events").EventEmitter;
const parser = require("./lib/parse");
const parseEddystoneBeacon = require("./lib/eddystone");
const EventEmitter = require('events').EventEmitter;
const parser = require('./lib/parse');
const parseEddystoneBeacon = require('./lib/eddystone');

class RuuviTag extends EventEmitter {
constructor(data) {
constructor (data) {
super();
this.id = data.id;
this.address = data.address;
Expand All @@ -14,8 +13,9 @@ class RuuviTag extends EventEmitter {
}

class Ruuvi extends EventEmitter {
constructor() {
constructor (adapter) {
super();
this._adapter = adapter;
this._foundTags = []; // this array will contain registered RuuviTags
this._tagLookup = {};
this.scanning = false;
Expand All @@ -26,7 +26,11 @@ class Ruuvi extends EventEmitter {
this._tagLookup[tag.id] = tag;
};

const onDiscover = peripheral => {
this._adapter.on('warning', warning => {
console.error(new Error(warning));
});

this._adapter.on('discover', peripheral => {
let newRuuviTag;

// Scan for new RuuviTags, add them to the array of found tags
Expand All @@ -38,28 +42,28 @@ class Ruuvi extends EventEmitter {
id: peripheral.id,
address: peripheral.address,
addressType: peripheral.addressType,
connectable: peripheral.connectable,
connectable: peripheral.connectable
});
registerTag(newRuuviTag);
this.emit("found", newRuuviTag);
this.emit('found', newRuuviTag);
}
} else {
// is it a RuuviTag in Eddystone mode?

const serviceDataArray = peripheral.advertisement ? peripheral.advertisement.serviceData : undefined;
const serviceData = serviceDataArray && serviceDataArray.length ? serviceDataArray[0] : undefined;
if (serviceData && serviceData.uuid === "feaa") {
if (serviceData && serviceData.uuid === 'feaa') {
const url = parseEddystoneBeacon(serviceData.data);
if (url && url.match(/ruu\.vi/)) {
if (!this._tagLookup[peripheral.id]) {
newRuuviTag = new RuuviTag({
id: peripheral.id,
address: peripheral.address,
addressType: peripheral.addressType,
connectable: peripheral.connectable,
connectable: peripheral.connectable
});
registerTag(newRuuviTag);
this.emit("found", newRuuviTag);
this.emit('found', newRuuviTag);
}
}
}
Expand All @@ -70,13 +74,13 @@ class Ruuvi extends EventEmitter {

if (ruuviTag) {
if (peripheral.advertisement && peripheral.advertisement.manufacturerData) {
let dataFormat = peripheral.advertisement.manufacturerData[2];
const dataFormat = peripheral.advertisement.manufacturerData[2];
return ruuviTag.emit(
"updated",
'updated',
Object.assign(
{ dataFormat: dataFormat, rssi: peripheral.rssi },
parser.parseManufacturerData(peripheral.advertisement.manufacturerData),
),
parser.parseManufacturerData(peripheral.advertisement.manufacturerData)
)
);
}

Expand All @@ -87,41 +91,42 @@ class Ruuvi extends EventEmitter {
const url = serviceData ? parseEddystoneBeacon(serviceData.data) : undefined;
const parsed = url ? parser.parseUrl(url) : undefined;
if (parsed && !(parsed instanceof Error)) {
ruuviTag.emit("updated", {
ruuviTag.emit('updated', {
url: url,
dataFormat: parsed.dataFormat,
rssi: peripheral.rssi,
humidity: parsed.humidity,
temperature: parsed.temperature,
pressure: parsed.pressure,
eddystoneId: parsed.eddystoneId,
eddystoneId: parsed.eddystoneId
});
}
}
};

noble.on("discover", onDiscover);

// start scanning
if (noble.state === "poweredOn") {
noble.startScanning([], true);
} else {
noble.once("stateChange", () => {
noble.startScanning([], true);
});
}
});
}

findTags() {
findTags () {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (this._foundTags.length) {
return resolve(this._foundTags);
}
reject(new Error("No beacons found"));
reject(new Error('No beacons found'));
}, 5000);
});
}

start () {
if (!this.scanning) {
this.scanning = true;
}
}

stop () {
if (this.scanning) {
this.scanning = false;
}
}
}

module.exports = new Ruuvi();
module.exports = Ruuvi;
Loading

0 comments on commit e8c0eae

Please sign in to comment.