From a33960f6f9f73690798831da55f331138655cb90 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2020 03:44:55 +0000 Subject: [PATCH 01/28] Bump sinon from 8.1.1 to 9.0.1 Bumps [sinon](https://github.com/sinonjs/sinon) from 8.1.1 to 9.0.1. - [Release notes](https://github.com/sinonjs/sinon/releases) - [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md) - [Commits](https://github.com/sinonjs/sinon/compare/v8.1.1...v9.0.1) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ecbf53..cced1ce 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "jasmine": "^3.5.0", "mockery": "^2.1.0", "prettier": "^1.19.1", - "sinon": "^8.1.1" + "sinon": "^9.0.1" }, "dependencies": { "@abandonware/noble": "^1.9.2-5" From c367def22eab6571b0b7afbda46f16c454ae1078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilari=20M=C3=A4kel=C3=A4?= Date: Sun, 3 May 2020 15:57:53 +0300 Subject: [PATCH 02/28] Point to correct noble module Point to correct noble NPM module at https://github.com/abandonware/noble --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc66d40..59d0fc0 100644 --- a/README.md +++ b/README.md @@ -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 From b6d8210ae4dcaa25f75ccdbc8fbc7df9228ba485 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Mon, 4 May 2020 11:29:54 +0300 Subject: [PATCH 03/28] Update dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e156da1..1385d4f 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "gulp-plumber": "^1.2.1", "jshint": "^2.11.0", "mockery": "^2.1.0", - "sinon": "^8.1.1" + "sinon": "^9.0.2" }, "dependencies": { - "@abandonware/noble": "^1.9.2-5" + "@abandonware/noble": "^1.9.2-8" } } From e51e54274cdb4b2bdb418a8c50ecb180e73b1f85 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Mon, 4 May 2020 11:31:51 +0300 Subject: [PATCH 04/28] whoops --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 9822d80..093bc74 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,11 @@ "eslint": "^6.8.0", "jasmine": "^3.5.0", "mockery": "^2.1.0", + "prettier": "^1.19.1", "sinon": "^9.0.2" }, "dependencies": { - "@abandonware/noble": "^1.9.2-8", - "prettier": "^1.19.1", - "sinon": "^9.0.1" + "@abandonware/noble": "^1.9.2-8" }, "prettier": { "printWidth": 120, From ba03ea7c2fb39806e808996adca505afab8bc93b Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Mon, 4 May 2020 11:32:14 +0300 Subject: [PATCH 05/28] 4.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 093bc74..3b5d5b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.2.0", + "version": "4.2.1", "engines": { "node": ">6.0.0" }, From e62b4ca4099cc8e354028f7bf7708b47967b9be4 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Sun, 24 May 2020 20:43:40 +0300 Subject: [PATCH 06/28] Update dependencies --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3b5d5b0..5cc472c 100644 --- a/package.json +++ b/package.json @@ -15,14 +15,14 @@ "author": "", "license": "BSD-3", "devDependencies": { - "eslint": "^6.8.0", + "eslint": "^7.1.0", "jasmine": "^3.5.0", "mockery": "^2.1.0", - "prettier": "^1.19.1", + "prettier": "^2.0.5", "sinon": "^9.0.2" }, "dependencies": { - "@abandonware/noble": "^1.9.2-8" + "@abandonware/noble": "^1.9.2-9" }, "prettier": { "printWidth": 120, From 46dd290477c9cb37edc5ef49b06266fc77d29cb7 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Sun, 24 May 2020 20:44:59 +0300 Subject: [PATCH 07/28] 4.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5cc472c..cba5319 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.2.1", + "version": "4.2.2", "engines": { "node": ">6.0.0" }, From e06940048c593bb3fd9102d62a82ea5dea54224e Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Sun, 24 May 2020 20:48:03 +0300 Subject: [PATCH 08/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 59d0fc0..a298235 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ npm install node-ruuvitag ### Usage example -``` +```js const ruuvi = require('node-ruuvitag'); ruuvi.on('found', tag => { From f65127f6bb344a6a4359b8f86166fb828204ccd2 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 11:12:16 +0300 Subject: [PATCH 09/28] Reformat with semistandard, add warning listener --- ruuvi.js | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/ruuvi.js b/ruuvi.js index 179c53b..b957803 100644 --- a/ruuvi.js +++ b/ruuvi.js @@ -1,10 +1,10 @@ -const noble = require("@abandonware/noble"); -const EventEmitter = require("events").EventEmitter; -const parser = require("./lib/parse"); -const parseEddystoneBeacon = require("./lib/eddystone"); +const noble = require('@abandonware/noble'); +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; @@ -14,7 +14,7 @@ class RuuviTag extends EventEmitter { } class Ruuvi extends EventEmitter { - constructor() { + constructor () { super(); this._foundTags = []; // this array will contain registered RuuviTags this._tagLookup = {}; @@ -26,6 +26,10 @@ class Ruuvi extends EventEmitter { this._tagLookup[tag.id] = tag; }; + const onWarning = message => { + this.emit('warning', message); + }; + const onDiscover = peripheral => { let newRuuviTag; @@ -38,17 +42,17 @@ 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]) { @@ -56,10 +60,10 @@ 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); } } } @@ -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) + ) ); } @@ -87,38 +91,39 @@ 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); + noble.on('discover', onDiscover); + noble.on('warning', onWarning); // start scanning - if (noble.state === "poweredOn") { + if (noble.state === 'poweredOn') { noble.startScanning([], true); } else { - noble.once("stateChange", () => { + 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); }); } From 4c299b0dc15b1e85778bdb33b816981b899e546e Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 11:12:39 +0300 Subject: [PATCH 10/28] 4.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cba5319..9151429 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.2.2", + "version": "4.3.0", "engines": { "node": ">6.0.0" }, From 39df9ab8dd7d6197ff19f4f97ffa50ee52b4925e Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 11:17:12 +0300 Subject: [PATCH 11/28] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a298235..d8fee46 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ ruuvi.on('found', tag => { }); }); +ruuvi.on('warning', message => { + console.error(new Error(message)); +}); + ``` ### Events From 856bc7aa004d54efdda2bc524709d37c398d239c Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 11:19:35 +0300 Subject: [PATCH 12/28] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d8fee46..96fdfbf 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,11 @@ ruuvi.on('warning', 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 From 1540450a7d2ad6aa10722e4fc409d278f8f75984 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 11:31:05 +0300 Subject: [PATCH 13/28] Add better state handling --- ruuvi.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/ruuvi.js b/ruuvi.js index b957803..0545292 100644 --- a/ruuvi.js +++ b/ruuvi.js @@ -20,6 +20,7 @@ class Ruuvi extends EventEmitter { this._tagLookup = {}; this.scanning = false; this.listenerAttached = false; + this.scanning = false; const registerTag = tag => { this._foundTags.push(tag); @@ -106,14 +107,17 @@ class Ruuvi extends EventEmitter { noble.on('discover', onDiscover); noble.on('warning', onWarning); + noble.on('stateChange', (state) => { + if (state === 'poweredOn') { + this.start(); + } else { + this.stop(); + } + }); // start scanning if (noble.state === 'poweredOn') { - noble.startScanning([], true); - } else { - noble.once('stateChange', () => { - noble.startScanning([], true); - }); + this.start(); } } @@ -127,6 +131,20 @@ class Ruuvi extends EventEmitter { }, 5000); }); } + + start () { + if (!this.scanning) { + this.scanning = true; + noble.startScanning([], true); + } + } + + stop () { + if (this.scanning) { + this.scanning = false; + noble.stopScanning(); + } + } } module.exports = new Ruuvi(); From 197c8f6f005805728a733c6b9c013e9078b973a9 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 11:31:25 +0300 Subject: [PATCH 14/28] 4.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9151429..ce54a20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.3.0", + "version": "4.4.0", "engines": { "node": ">6.0.0" }, From cabd0cd527a6a97b807d4420c940ba936f7ed6e0 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 11:35:34 +0300 Subject: [PATCH 15/28] Emit stateChange --- ruuvi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruuvi.js b/ruuvi.js index 0545292..58fc3fd 100644 --- a/ruuvi.js +++ b/ruuvi.js @@ -20,7 +20,6 @@ class Ruuvi extends EventEmitter { this._tagLookup = {}; this.scanning = false; this.listenerAttached = false; - this.scanning = false; const registerTag = tag => { this._foundTags.push(tag); @@ -113,6 +112,7 @@ class Ruuvi extends EventEmitter { } else { this.stop(); } + this.emit('stateChange', state); }); // start scanning From 4855dda3f2f2ad9cd7606d8d5de4172227f62253 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 11:35:52 +0300 Subject: [PATCH 16/28] 4.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce54a20..9504a0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.4.0", + "version": "4.4.1", "engines": { "node": ">6.0.0" }, From b7ad571f218a9559faf6744cb0b8a4361a936878 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 15:15:09 +0300 Subject: [PATCH 17/28] Add Mac address parsing, fixes #17 --- dataformats/5.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dataformats/5.js b/dataformats/5.js index d38c16f..a735a18 100644 --- a/dataformats/5.js +++ b/dataformats/5.js @@ -27,7 +27,14 @@ const parseRawRuuvi = function(data) { robject.txPower = (powerInfo & 0b11111) * 2 - 40; robject.movementCounter = data[17] & 0xff; robject.measurementSequenceNumber = ((data[18] & 0xff) << 8) | (data[19] & 0xff); - + robject.mac = [ + data[20].toString(16).toUpperCase(), + data[21].toString(16).toUpperCase(), + data[22].toString(16).toUpperCase(), + data[23].toString(16).toUpperCase(), + data[24].toString(16).toUpperCase(), + data[25].toString(16).toUpperCase() + ].join(':'); return robject; }; From 4aa230716c71e67e4b5b16370177970f68626d72 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 15:15:17 +0300 Subject: [PATCH 18/28] 4.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9504a0b..b96f4bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.4.1", + "version": "4.5.0", "engines": { "node": ">6.0.0" }, From edb9bba16d97ad3bada4da37c2aa7fef1bf702ef Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 15:39:46 +0300 Subject: [PATCH 19/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 96fdfbf..42badeb 100644 --- a/README.md +++ b/README.md @@ -78,7 +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. From 6391d6332dab46bb5f910107c5b54df84624a4d5 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Fri, 29 May 2020 15:40:05 +0300 Subject: [PATCH 20/28] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 42badeb..5a908f5 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ following properties (depending on data format): * ```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. From a3db3559dff571b45720b394503469a26530e154 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Tue, 2 Jun 2020 20:51:10 +0300 Subject: [PATCH 21/28] Add test for version 5, with MAC address including value starting with zero --- test/parseSpec.js | 119 +++++++++++++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 33 deletions(-) diff --git a/test/parseSpec.js b/test/parseSpec.js index d6bf208..b93d1eb 100644 --- a/test/parseSpec.js +++ b/test/parseSpec.js @@ -1,6 +1,6 @@ -const parser = require("../lib/parse"); +const parser = require('../lib/parse'); -const createManufacturerData = function() { +const createManufacturerData = function () { const values = { humidity: 58.5, temperature: 21.58, @@ -8,78 +8,79 @@ const createManufacturerData = function() { accelerationX: 14850, accelerationY: -9235, accelerationZ: 580, - battery: 2958, + battery: 2958 }; const manufacturerId = [0x99, 0x04]; const dataFormat = [0x03]; const valuesArray = [0x75, 21, 58, 0xc8, 0x64, 0x3a, 0x02, 0xdb, 0xed, 0x02, 0x44, 0x0b, 0x8e]; return { values: values, - buffer: Buffer.from(manufacturerId.concat(dataFormat).concat(valuesArray)), + buffer: Buffer.from(manufacturerId.concat(dataFormat).concat(valuesArray)) }; }; -describe("parse.js", () => { +describe('parse.js', () => { const data = [0x98, 0x15, 0x00, 0xc0, 0x30]; - const dataBuffer_format_2 = Buffer.from([0x02].concat(data)); - const dataBuffer_format_4 = Buffer.from([0x04].concat(data).concat([0x3e])); - const testUrl_dataFormat_2 = "ruu.vi/#" + dataBuffer_format_2.toString("base64"); - const testUrl_dataFormat_4 = ("ruu.vi/#" + dataBuffer_format_4.toString("base64")).slice(0, 17); + const dataBufferFormat2 = Buffer.from([0x02].concat(data)); + const dataBufferFormat4 = Buffer.from([0x04].concat(data).concat([0x3e])); + const testUrlDataFormat2 = 'ruu.vi/#' + dataBufferFormat2.toString('base64'); + const testUrlDataFormat4 = ('ruu.vi/#' + dataBufferFormat4.toString('base64')).slice(0, 17); + const dataFormat5 = [0x05, 0x12, 0xFC, 0x53, 0x94, 0xC3, 0x7C, 0x00, 0x04, 0xFF, 0xFC, 0x04, 0x0C, 0xAC, 0x36, 0x42, 0x00, 0xCD, 0xCB, 0xB8, 0x33, 0x4C, 0x88, 0x01]; - it("should return error if not a ruuviTag url", done => { - const result = parser.parseUrl("https://bad.url.com/#foo"); + it('should return error if not a ruuviTag url', done => { + const result = parser.parseUrl('https://bad.url.com/#foo'); if (!(result instanceof Error)) { - return done.fail("Should have got an error"); + return done.fail('Should have got an error'); } expect(result.message).toMatch(/not a ruuvitag url/i); done(); }); it("should return error if url doesn't contain data", done => { - const result = parser.parseUrl("https://ruu.vi/foo"); + const result = parser.parseUrl('https://ruu.vi/foo'); if (!(result instanceof Error)) { - return done.fail("Should have got an error"); + return done.fail('Should have got an error'); } expect(result.message).toMatch(/invalid url/i); done(); }); - it("should return error if url contains invalid data", done => { - const result = parser.parseUrl("https://ruu.vi/#foo"); + it('should return error if url contains invalid data', done => { + const result = parser.parseUrl('https://ruu.vi/#foo'); if (!(result instanceof Error)) { - return done.fail("Should have got an error"); + return done.fail('Should have got an error'); } expect(result.message).toMatch(/invalid data/i); done(); }); - it("should return error if data format is unsupported", done => { - const result = parser.parseUrl("https://ruu.vi/#" + Buffer.from([5, 6, 7, 8, 9, 10]).toString("base64")); + it('should return error if data format is unsupported', done => { + const result = parser.parseUrl('https://ruu.vi/#' + Buffer.from([5, 6, 7, 8, 9, 10]).toString('base64')); if (!(result instanceof Error)) { - return done.fail("Should have got an error"); + return done.fail('Should have got an error'); } expect(result.message).toMatch(/unsupported data format: 5/i); done(); }); - describe("parsing data format 2", () => { - const result = parser.parseUrl(testUrl_dataFormat_2); - it("should parse humidity value", () => { + describe('parsing data format 2', () => { + const result = parser.parseUrl(testUrlDataFormat2); + it('should parse humidity value', () => { expect(result.humidity).toBe(76); }); - it("should parse temperature value", () => { + it('should parse temperature value', () => { expect(result.temperature).toBe(21); }); - it("should parse pressure value", () => { + it('should parse pressure value', () => { expect(result.pressure).toBe(992); }); }); - describe("parsing data format 3", () => { + describe('parsing data format 3', () => { const data = createManufacturerData(); const testValues = Object.keys(data.values).map(key => key); - it("should parse all values correctly", () => { + it('should parse all values correctly', () => { const result = parser.parseManufacturerData(data.buffer); testValues.forEach(key => { expect(result[key]).toBe(data.values[key]); @@ -87,24 +88,76 @@ describe("parse.js", () => { }); }); - describe("parsing data format 4", () => { - const result = parser.parseUrl(testUrl_dataFormat_4); + describe('parsing data format 4', () => { + const result = parser.parseUrl(testUrlDataFormat4); it("shouldn't return error", () => { expect(result instanceof Error).toBeFalsy(); }); - it("should parse humidity value", () => { + it('should parse humidity value', () => { expect(result.humidity).toBe(76); }); - it("should parse temperature value", () => { + it('should parse temperature value', () => { expect(result.temperature).toBe(21); }); - it("should parse pressure value", () => { + it('should parse pressure value', () => { expect(result.pressure).toBe(992); }); - it("should parse eddystoneId", () => { + it('should parse eddystoneId', () => { expect(result.eddystoneId).toBeTruthy(); }); }); + + describe('parsing data format 5', () => { + const result = parser.parseManufacturerData(Buffer.from([0x99, 0x04].concat(dataFormat5))); + + it("shouldn't return error", () => { + expect(result instanceof Error).toBeFalsy(); + }); + + it('should parse temperature value', () => { + expect(result.temperature).toBe(24.3); + }); + + it('should parse pressure value', () => { + expect(result.pressure).toBe(100044); + }); + + it('should parse humidity value', () => { + expect(result.humidity).toBe(53.49); + }); + + it('should parse accelerationX', () => { + expect(result.accelerationX).toBe(4); + }); + + it('should parse accelerationY', () => { + expect(result.accelerationY).toBe(-4); + }); + + it('should parse accelerationZ', () => { + expect(result.accelerationZ).toBe(1036); + }); + + it('should parse txPower', () => { + expect(result.txPower).toBe(4); + }); + + it('should parse battery', () => { + expect(result.battery).toBe(2977); + }); + + it('should parse movementCounter', () => { + expect(result.movementCounter).toBe(66) + }); + + it('should parse measurementSequenceNumber', () => { + expect(result.measurementSequenceNumber).toBe(205); + }); + + it('should parse MAC address', () => { + expect(result.mac).toBe('CB:B8:33:4C:88:01'); + }); + }); }); From b4a7608c95662c949b1e763da672bbea386f5274 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Tue, 2 Jun 2020 20:51:40 +0300 Subject: [PATCH 22/28] Further fix #17 --- dataformats/5.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/dataformats/5.js b/dataformats/5.js index a735a18..98caa46 100644 --- a/dataformats/5.js +++ b/dataformats/5.js @@ -1,5 +1,5 @@ -const parseRawRuuvi = function(data) { - let robject = {}; +const parseRawRuuvi = function (data) { + const robject = {}; let temperature = (data[3] << 8) | (data[4] & 0xff); if (temperature > 32767) { @@ -11,33 +11,37 @@ const parseRawRuuvi = function(data) { robject.pressure = (((data[7] & 0xff) << 8) | (data[8] & 0xff)) + 50000; let accelerationX = (data[9] << 8) | (data[10] & 0xff); - if (accelerationX > 32767) accelerationX -= 65536; //two's complement + if (accelerationX > 32767) accelerationX -= 65536; // two's complement robject.accelerationX = accelerationX; let accelerationY = (data[11] << 8) | (data[12] & 0xff); - if (accelerationY > 32767) accelerationY -= 65536; //two's complement + if (accelerationY > 32767) accelerationY -= 65536; // two's complement robject.accelerationY = accelerationY; let accelerationZ = (data[13] << 8) | (data[14] & 0xff); - if (accelerationZ > 32767) accelerationZ -= 65536; //two's complement + if (accelerationZ > 32767) accelerationZ -= 65536; // two's complement robject.accelerationZ = accelerationZ; - let powerInfo = ((data[15] & 0xff) << 8) | (data[16] & 0xff); + const powerInfo = ((data[15] & 0xff) << 8) | (data[16] & 0xff); robject.battery = (powerInfo >>> 5) + 1600; robject.txPower = (powerInfo & 0b11111) * 2 - 40; robject.movementCounter = data[17] & 0xff; robject.measurementSequenceNumber = ((data[18] & 0xff) << 8) | (data[19] & 0xff); robject.mac = [ - data[20].toString(16).toUpperCase(), - data[21].toString(16).toUpperCase(), - data[22].toString(16).toUpperCase(), - data[23].toString(16).toUpperCase(), - data[24].toString(16).toUpperCase(), - data[25].toString(16).toUpperCase() + 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); +} From 64c12a065d80793b777a04e8898d5cf9c5d4fb2e Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Tue, 2 Jun 2020 20:51:55 +0300 Subject: [PATCH 23/28] 4.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b96f4bc..0e4768f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.5.0", + "version": "4.5.1", "engines": { "node": ">6.0.0" }, From 376427291f30e221216ffb9c44f11b105fcc3937 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Wed, 15 Jul 2020 09:46:44 +0300 Subject: [PATCH 24/28] Refactor --- adapter.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ index.js | 4 ++++ ruuvi.js | 32 +++++++------------------------- 3 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 adapter.js create mode 100644 index.js diff --git a/adapter.js b/adapter.js new file mode 100644 index 0000000..1fe7ae9 --- /dev/null +++ b/adapter.js @@ -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(); diff --git a/index.js b/index.js new file mode 100644 index 0000000..d6ffc4b --- /dev/null +++ b/index.js @@ -0,0 +1,4 @@ +const adapter = require('./adapter.js'); +const Ruuvi = require('./ruuvi.js'); + +module.exports = new Ruuvi(adapter); \ No newline at end of file diff --git a/ruuvi.js b/ruuvi.js index 58fc3fd..0f0428d 100644 --- a/ruuvi.js +++ b/ruuvi.js @@ -1,4 +1,3 @@ -const noble = require('@abandonware/noble'); const EventEmitter = require('events').EventEmitter; const parser = require('./lib/parse'); const parseEddystoneBeacon = require('./lib/eddystone'); @@ -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; @@ -26,11 +26,11 @@ class Ruuvi extends EventEmitter { this._tagLookup[tag.id] = tag; }; - const onWarning = message => { - this.emit('warning', message); - }; + this._adapter.on('warning', warning => { + console.error(new Error(warning)); + }); - const onDiscover = peripheral => { + this._adapter.on('discover', peripheral => { let newRuuviTag; // Scan for new RuuviTags, add them to the array of found tags @@ -102,23 +102,7 @@ class Ruuvi extends EventEmitter { }); } } - }; - - noble.on('discover', onDiscover); - noble.on('warning', onWarning); - noble.on('stateChange', (state) => { - if (state === 'poweredOn') { - this.start(); - } else { - this.stop(); - } - this.emit('stateChange', state); }); - - // start scanning - if (noble.state === 'poweredOn') { - this.start(); - } } findTags () { @@ -135,16 +119,14 @@ class Ruuvi extends EventEmitter { start () { if (!this.scanning) { this.scanning = true; - noble.startScanning([], true); } } stop () { if (this.scanning) { this.scanning = false; - noble.stopScanning(); } } } -module.exports = new Ruuvi(); +module.exports = Ruuvi; From fa7756ffd8c364dd90ae549a7788cdb6def88c7e Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Wed, 15 Jul 2020 09:46:53 +0300 Subject: [PATCH 25/28] Update dependencies, change main file --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0e4768f..bc5cedc 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ }, "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'", @@ -15,7 +15,7 @@ "author": "", "license": "BSD-3", "devDependencies": { - "eslint": "^7.1.0", + "eslint": "^7.4.0", "jasmine": "^3.5.0", "mockery": "^2.1.0", "prettier": "^2.0.5", From 46291b721fe982c62350135e614641bcb71ca3b4 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Wed, 15 Jul 2020 09:46:58 +0300 Subject: [PATCH 26/28] 4.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bc5cedc..40b5fec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.5.1", + "version": "4.6.0", "engines": { "node": ">6.0.0" }, From 78b4b07150e1b77cbceb82775e8220ab9617b7db Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Wed, 15 Jul 2020 09:51:50 +0300 Subject: [PATCH 27/28] Fix test --- test/ruuviSpec.js | 83 ++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/test/ruuviSpec.js b/test/ruuviSpec.js index d6f8aaa..1831dce 100644 --- a/test/ruuviSpec.js +++ b/test/ruuviSpec.js @@ -1,14 +1,14 @@ -const mockery = require("mockery"); -const sinon = require("sinon"); -const mocks = require("./mocks"); +const mockery = require('mockery'); +const sinon = require('sinon'); +const mocks = require('./mocks'); const nobleMock = mocks.nobleMock; -const EventEmitter = require("events").EventEmitter; +const EventEmitter = require('events').EventEmitter; const catchFail = done => { return err => done.fail(err); }; -describe("module ruuvi", () => { +describe('module ruuvi', () => { const findTagsScanTime = 5000; const numberOfRuuviTags = 2; @@ -16,30 +16,33 @@ describe("module ruuvi", () => { beforeEach(() => { mockery.enable(); - mockery.registerMock("@abandonware/noble", nobleMock.mock); - mockery.registerMock("noble", nobleMock.mock); - mockery.registerAllowable("../ruuvi"); - mockery.registerAllowable("./lib/parse"); - mockery.registerAllowable("./lib/eddystone"); - mockery.registerAllowable("events"); + mockery.registerMock('@abandonware/noble', nobleMock.mock); + mockery.registerMock('noble', nobleMock.mock); + mockery.registerAllowable('../adapter'); + mockery.registerAllowable('../ruuvi'); + mockery.registerAllowable('./lib/parse'); + mockery.registerAllowable('./lib/eddystone'); + mockery.registerAllowable('events'); nobleMock.mock.enableTagFinding(); - ruuvi = require("../ruuvi"); + adapter = require('../adapter'); + Ruuvi = require('../ruuvi'); + ruuvi = new Ruuvi(adapter); jasmine.clock().install(); nobleMock.mock.startScanning(); }); - it("should be eventEmitter", () => { - const EventEmitter = require("events").EventEmitter; + it('should be eventEmitter', () => { + const EventEmitter = require('events').EventEmitter; expect(ruuvi instanceof EventEmitter).toBeTruthy(); }); - describe("method findTags", () => { + describe('method findTags', () => { beforeEach(() => { ruuvi._foundTags = []; ruuvi._tagLookup = {}; }); - it("should return a promise which is resolved with an array of ruuviTag objects", done => { + it('should return a promise which is resolved with an array of ruuviTag objects', done => { ruuvi .findTags() .then(tags => { @@ -55,35 +58,35 @@ describe("module ruuvi", () => { jasmine.clock().tick(findTagsScanTime); }); - it("should return a promise which is rejected if no tags were found", done => { + it('should return a promise which is rejected if no tags were found', done => { nobleMock.mock.disableTagFinding(); ruuvi .findTags() - .then(data => done.fail("Should have returned an error")) + .then(data => done.fail('Should have returned an error')) .catch(err => { - expect(err.message).toBe("No beacons found"); + expect(err.message).toBe('No beacons found'); done(); }); jasmine.clock().tick(findTagsScanTime); }); }); - describe("events: ", () => { + describe('events: ', () => { it('should emit "found" when a new RuuviTag is found', done => { ruuvi._foundTags = []; ruuvi._tagLookup = {}; let count = 0; - ruuvi.on("found", data => { + ruuvi.on('found', data => { count++; - expect("id" in data).toBeTruthy(); - expect("address" in data).toBeTruthy(); - expect("addressType" in data).toBeTruthy(); - expect("connectable" in data).toBeTruthy(); + expect('id' in data).toBeTruthy(); + expect('address' in data).toBeTruthy(); + expect('addressType' in data).toBeTruthy(); + expect('connectable' in data).toBeTruthy(); expect(data instanceof EventEmitter).toBeTruthy(); }); - setTimeout(function() { + setTimeout(function () { expect(count).toBe(numberOfRuuviTags); done(); }, 5000); @@ -92,7 +95,7 @@ describe("module ruuvi", () => { }); }); - describe("class RuuviTag", () => { + describe('class RuuviTag', () => { let tags; beforeEach(done => { @@ -106,16 +109,16 @@ describe("module ruuvi", () => { jasmine.clock().tick(findTagsScanTime); }); - describe("instantiated object", () => { + describe('instantiated object', () => { it('should have properties "id", "address", "addressType", "connectable"', () => { - expect("id" in tags[0]).toBeTruthy(); - expect("address" in tags[0]).toBeTruthy(); - expect("addressType" in tags[0]).toBeTruthy(); - expect("connectable" in tags[0]).toBeTruthy(); + expect('id' in tags[0]).toBeTruthy(); + expect('address' in tags[0]).toBeTruthy(); + expect('addressType' in tags[0]).toBeTruthy(); + expect('connectable' in tags[0]).toBeTruthy(); }); it('should emit "updated" when ruuvitag signal is received', done => { - tags.forEach(tag => tag.on("updated", data => (tag.hasEmitted = true))); + tags.forEach(tag => tag.on('updated', data => (tag.hasEmitted = true))); setTimeout(() => { expect(tags.filter(tag => tag.hasEmitted).length).toBe(2); done(); @@ -123,22 +126,22 @@ describe("module ruuvi", () => { jasmine.clock().tick(nobleMock.mock.advertiseInterval); }); - describe("emitted data", () => { + describe('emitted data', () => { beforeEach(done => { const waitTime = nobleMock.mock.advertiseInterval; - tags.forEach(tag => tag.on("updated", data => (tag.receivedData = data))); + tags.forEach(tag => tag.on('updated', data => (tag.receivedData = data))); setTimeout(() => { done(); }, waitTime); jasmine.clock().tick(waitTime + 1); }); - it("should have sensor data", () => { - const expectedDataKeys = (function() { - const tag_1_keys = ["humidity", "temperature", "pressure", "rssi"]; + it('should have sensor data', () => { + const expectedDataKeys = (function () { + const tag_1_keys = ['humidity', 'temperature', 'pressure', 'rssi']; return { tag_1: tag_1_keys, - tag_0: tag_1_keys.concat(["accelerationX", "accelerationY", "accelerationZ", "battery"]), + tag_0: tag_1_keys.concat(['accelerationX', 'accelerationY', 'accelerationZ', 'battery']) }; })(); @@ -149,7 +152,7 @@ describe("module ruuvi", () => { }); }); - afterEach(function() { + afterEach(function () { jasmine.clock().uninstall(); mockery.deregisterAll(); mockery.disable(); From cd4201d792e5b3d5c5dd06ff0c4eda00f31be834 Mon Sep 17 00:00:00 2001 From: Juha Lindstedt Date: Wed, 15 Jul 2020 09:51:52 +0300 Subject: [PATCH 28/28] 4.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 40b5fec..12112c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-ruuvitag", - "version": "4.6.0", + "version": "4.6.1", "engines": { "node": ">6.0.0" },