Comparing version 0.0.1 to 0.0.2
'use strict'; | ||
const async = require('marcosc-async'); | ||
class LovesenseBase { | ||
constructor() { | ||
this._runningAccel = false; | ||
} | ||
_expectAccelerometerData(data) { | ||
if (data[0] !== 'G') { | ||
return false; | ||
} | ||
this.emitAccelerometerReading(data); | ||
return true; | ||
} | ||
_expectInt(data) { | ||
if (typeof(data) !== 'string') { | ||
throw new Error('Expected return data to be a string!'); | ||
} | ||
if (this._runningAccel && this._expectAccelerometerData(data)) { | ||
return undefined; | ||
} | ||
// Expect the last character to be a semicolon, truncate it while converting | ||
// to int. | ||
try { | ||
return parseInt(data, 10); | ||
} catch (err) { | ||
throw new Error('Could not parse int from return!'); | ||
} | ||
} | ||
_expectOK(data) { | ||
if (typeof(data) !== 'string') { | ||
throw new Error('Expected return data to be a string!'); | ||
} | ||
if (this._runningAccel && this._expectAccelerometerData(data)) { | ||
return undefined; | ||
} | ||
if (data === 'OK') { | ||
return true; | ||
} | ||
throw new Error('Error returned from command!'); | ||
} | ||
_expectStatus(data) { | ||
if (typeof(data) !== 'string') { | ||
throw new Error('Expected return data to be a string!'); | ||
} | ||
if (this._runningAccel && this._expectAccelerometerData(data)) { | ||
return undefined; | ||
} | ||
// TODO String parsing | ||
throw new Error("IMPLEMENT ME"); | ||
} | ||
emitAccelerometerReading(accelReading) { | ||
} | ||
write(cmd) { | ||
throw new Error('Must be implemented by subclass!'); | ||
} | ||
vibrate(level) { | ||
if (typeof(level) != 'number') { | ||
throw new Error('Command requires a number!'); | ||
} | ||
return this.writeAndExpectOK('Vibrate:' + level + ';'); | ||
} | ||
rotate(level) { | ||
if (typeof(level) != 'number') { | ||
throw new Error('Command requires a number!'); | ||
} | ||
return this.writeAndExpectOK('Rotate:' + level + ';'); | ||
} | ||
air(level) { | ||
if (typeof(level) != 'number') { | ||
throw new Error('Command requires a number!'); | ||
} | ||
return this.writeAndExpectOK('Air:Level:' + level + ';'); | ||
} | ||
inflate(level) { | ||
if (typeof(level) != 'number') { | ||
throw new Error('Command requires a number!'); | ||
} | ||
return this.writeAndExpectOK('Air:In:' + level + ';'); | ||
} | ||
deflate(level) { | ||
if (typeof(level) != 'number') { | ||
throw new Error('Command requires a number!'); | ||
} | ||
return this.writeAndExpectOK('Air:Out:' + level + ';'); | ||
} | ||
startAccelerometer() { | ||
return this.writeAndExpectOK('StartMove:1;'); | ||
} | ||
stopAccelerometer() { | ||
return this.writeAndExpectOK('StopMove:1;'); | ||
} | ||
changeRotationDirection() { | ||
return this.writeAndExpectOK('RotateChange;'); | ||
} | ||
powerOff() { | ||
return this.writeAndExpectOK('PowerOff;'); | ||
} | ||
batteryLevel() { | ||
return this.writeAndExpectInt('BatteryLevel;'); | ||
} | ||
deviceType() { | ||
return this.writeAndExpectInt('DeviceType;'); | ||
} | ||
deviceStatus() { | ||
return this.writeAndExpectStatus('DeviceStatus:1;'); | ||
} | ||
}; | ||
setAirLevel() { | ||
class LovesenseSerial extends LovesenseBase { | ||
constructor(portAddr) { | ||
super(); | ||
if (portAddr === undefined) { | ||
throw new Error('LovesenseSerial requires a serial port address!'); | ||
} | ||
if (typeof(portAddr) !== 'string') { | ||
throw new Error('LovesenseSerial requires a string as serial port address!'); | ||
} | ||
this._portAddr = portAddr; | ||
} | ||
_getSerialPort() { | ||
// Requiring the module here means it's not loaded until a Lovesense | ||
// object is created, so if we just want to use the emulator or websockets, | ||
// we don't require serial to come along with it. | ||
const SerialPort = require('serialport'); | ||
const opts = { | ||
// It's bluetooth, so port settings don't matter. | ||
autoOpen: false, | ||
// Everything is strings! Easy! | ||
parser: SerialPort.parsers.readline(';') | ||
}; | ||
return new SerialPort(this._portAddr, opts); | ||
} | ||
open() { | ||
return new Promise((resolve, reject) => { | ||
try { | ||
this._port = this._getSerialPort(); | ||
} | ||
catch (err) { | ||
reject(err); | ||
throw(err); | ||
} | ||
this._port.open((err) => { | ||
if (err) { | ||
reject(err); | ||
throw(err); | ||
} | ||
}); | ||
this._port.on('open', (err) => { | ||
if (err) { | ||
// We'll have already rejected in the port open() callback. | ||
return; | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
close() { | ||
if (!this._port) { | ||
return new Promise((resolve, reject) => { | ||
resolve(); | ||
}); | ||
} | ||
this._port.close(); | ||
return new Promise((resolve, reject) => { | ||
this._port.on('close', (err) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
this._port = undefined; | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
write(cmd) { | ||
return new Promise((resolve, reject) => { | ||
if (typeof(cmd) !== 'string') { | ||
reject(Error('Commands must be a string!')); | ||
} | ||
if (!this._port) { | ||
reject(Error('Commands must be a string!')); | ||
} | ||
return this._port.write(cmd, (err) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
_writeAndParse(cmd, parser) { | ||
return new Promise((resolve, reject) => { | ||
this.write(cmd).catch((err) => { | ||
reject(err); | ||
}); | ||
let handler = (data) => { | ||
try { | ||
let ret = parser(data); | ||
// Got an accelerometer reading. Just keep listening. | ||
if (ret === undefined) { | ||
return; | ||
} | ||
resolve(ret); | ||
} catch (err) { | ||
reject(err); | ||
} finally { | ||
this._port.removeListener('data', handler); | ||
} | ||
}; | ||
this._port.on('data', handler); | ||
}); | ||
} | ||
writeAndExpectInt(cmd) { | ||
return this._writeAndParse(cmd, this._expectInt.bind(this)); | ||
} | ||
writeAndExpectStatus(cmd, promise) { | ||
return this._writeAndParse(cmd, this._expectStatus.bind(this)); | ||
} | ||
writeAndExpectOK(cmd, promise) { | ||
return this._writeAndParse(cmd, this._expectOK.bind(this)); | ||
} | ||
}; | ||
class LovesenseSerial { | ||
class LovesenseFakeSerial extends LovesenseSerial { | ||
constructor(portAddr) { | ||
super(portAddr); | ||
this._failNextCommand = false; | ||
} | ||
failNext() { | ||
this._failNextCommand = true; | ||
} | ||
_getSerialPort() { | ||
try { | ||
const SerialPort = require('virtual-serialport'); | ||
return new SerialPort(this._portAddr); | ||
} catch (err) { | ||
throw err; | ||
} | ||
} | ||
open() { | ||
return new Promise((resolve, reject) => { | ||
super.open().then(() => { | ||
this._port.on('dataToDevice', (data) => { | ||
this._simulateResponse(data); | ||
}); | ||
resolve(); | ||
}, (err) => { | ||
reject(err); | ||
}); | ||
}); | ||
} | ||
close() { | ||
//this._port.removeListener('dataToDevice', this); | ||
return super.close(); | ||
} | ||
_simulateResponse(data) { | ||
if (this._failNextCommand) { | ||
this._port.writeToComputer('ERR;'); | ||
return; | ||
} | ||
this._port.writeToComputer('OK;'); | ||
} | ||
} | ||
module.exports = { | ||
LovesenseSerial: LovesenseSerial, | ||
LovesenseFakeSerial: LovesenseFakeSerial | ||
}; |
{ | ||
"name": "lovesense", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Javascript/Node.js drivers and protocol implementation for Lovense Products (Max, Nora, Lush, Hush, etc...)", | ||
@@ -8,3 +8,4 @@ "main": "index.js", | ||
"generate-docs": "node_modules/.bin/jsdoc --configure doc/conf.json --verbose", | ||
"test": "mocha tests/" | ||
"test": "mocha tests/", | ||
"test-travis": "istanbul cover _mocha -- tests/ && codeclimate-test-reporter < coverage/lcov.info" | ||
}, | ||
@@ -30,7 +31,11 @@ "repository": { | ||
"dependencies": { | ||
"commander": "^2.9.0", | ||
"marcosc-async": "^3.0.0", | ||
"serialport": "4.x" | ||
"serialport": "4.x", | ||
"virtual-serialport": "git+https://github.com/qdot/virtual-serialport.git" | ||
}, | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"codeclimate-test-reporter": "^0.3.3", | ||
"istanbul": "^0.4.4", | ||
"minami": "^1.1.1", | ||
@@ -37,0 +42,0 @@ "mocha": "^2.5.3" |
# lovesense | ||
[![Build Status](https://img.shields.io/travis/metafetish/lovesense-js.svg)](https://travis-ci.org/metafetish/lovesense-js) [![npm](https://img.shields.io/npm/v/lovesense.svg)](https://npmjs.com/package/lovesense) [![codeclimate coverage](https://codeclimate.com/github/metafetish/lovesense-js/badges/coverage.svg)](https://codeclimate.com/github/metafetish/lovesense-js) [![codeclimate health](https://codeclimate.com/github/metafetish/lovesense-js/badges/gpa.svg)](https://codeclimate.com/github/metafetish/lovesense-js) | ||
lovesense is a library for controlling Lovense sex toys, such as the | ||
@@ -11,6 +13,6 @@ Max, Nora, and Lush. The library allows users to control all aspects | ||
- Python 2 (http://github.com/metafetish/lovesense-py) | ||
- Max/MSP (http://github.com/metafetish/lovesense-max) | ||
- Rust (with C Headers) (http://github.com/metafetish/lovesense-rs) | ||
- Javascript (http://github.com/metafetish/lovesense-js) | ||
- [Python](http://github.com/metafetish/lovesense-py) | ||
- [Max/MSP](http://github.com/metafetish/lovesense-max) | ||
- [Rust (with C Headers)](http://github.com/metafetish/lovesense-rs) | ||
- [Javascript](http://github.com/metafetish/lovesense-js) | ||
@@ -17,0 +19,0 @@ If you need an implementation in a language not currently supported by |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Git dependency
Supply chain riskContains a dependency which resolves to a remote git URL. Dependencies fetched from git URLs are not immutable and can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
75648
21
761
124
4
5
1
1