Initial commit
This commit is contained in:
+183
@@ -0,0 +1,183 @@
|
||||
'use strict';
|
||||
|
||||
const dgram = require('dgram');
|
||||
const util = require('util');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const crypto = require('crypto');
|
||||
const Devices = require('./devices');
|
||||
|
||||
function Hub(options) {
|
||||
if (!(this instanceof Hub)) return new Hub(options);
|
||||
|
||||
options = options || {};
|
||||
options.port = parseInt(options.port, 10) || 9898;
|
||||
|
||||
this.sensors = {};
|
||||
|
||||
this.key = options.key;
|
||||
this.keys = {};
|
||||
this.token = {};
|
||||
this.iv = Buffer.from([0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, 0x56, 0x2e]);
|
||||
|
||||
if (options.keys) {
|
||||
for (let i = 0; i < options.keys.length; i++) {
|
||||
this.keys[options.keys[i].ip] = options.keys[i].key;
|
||||
}
|
||||
}
|
||||
|
||||
/* this.clickTypes = {
|
||||
click: 'click',
|
||||
double_click: 'double_click',
|
||||
long_click_press: 'long_click_press',
|
||||
long_click_release: 'long_click_release'
|
||||
}; */
|
||||
|
||||
this.listen = function () {
|
||||
this.socket = dgram.createSocket('udp4');
|
||||
this.socket.on('message', this.onMessage.bind(this));
|
||||
this.socket.on('error', this.onError.bind(this));
|
||||
this.socket.on('listening', this.onListening.bind(this));
|
||||
this.socket.bind(options.port);
|
||||
};
|
||||
|
||||
this.stop = function (cb) {
|
||||
if (this._state === 'CLOSED') return false;
|
||||
this._state = 'CLOSED';
|
||||
|
||||
if (this.socket) {
|
||||
try {
|
||||
this.socket.removeAllListeners();
|
||||
this.socket.close(cb);
|
||||
this.socket = null;
|
||||
} catch (e) {
|
||||
cb && cb();
|
||||
}
|
||||
} else {
|
||||
cb && cb();
|
||||
}
|
||||
};
|
||||
|
||||
this.onListening = function () {
|
||||
this._state = 'CONNECTED';
|
||||
this.socket.setBroadcast(true);
|
||||
this.socket.setMulticastTTL(128);
|
||||
if (options.bind && options.bind !== '0.0.0.0') {
|
||||
this.socket.addMembership('224.0.0.50', options.bind);
|
||||
} else {
|
||||
this.socket.addMembership('224.0.0.50');
|
||||
}
|
||||
const whoIsCommand = '{"cmd": "whois"}';
|
||||
this.socket.send(whoIsCommand, 0, whoIsCommand.length, 4321, '224.0.0.50');
|
||||
};
|
||||
|
||||
this.onError = function (err) {
|
||||
if (this._state === 'CLOSED') return false;
|
||||
this.emit('error', err);
|
||||
};
|
||||
|
||||
this.onMessage = function (msgBuffer, rinfo) {
|
||||
if (this._state === 'CLOSED') return false;
|
||||
let msg;
|
||||
try {
|
||||
msg = JSON.parse(msgBuffer.toString());
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sensor = this.getSensor(msg.sid);
|
||||
if (!sensor) {
|
||||
// {"model":"lumi.lock.v1","did":"lumi.1xxxxxxxxxxxxx8","name":"Front door lock"}
|
||||
if (!msg.model) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (options.browse) {
|
||||
if (msg.model === 'gateway') {
|
||||
this.emit('browse', {ip: rinfo.address});
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
sensor = this.sensorFactory(msg.sid, msg.model, rinfo.address, msg.name);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.emit('warning', 'Could not add new sensor: ' + e.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sensor) {
|
||||
if (msg.data && typeof msg.data === 'string') {
|
||||
try {
|
||||
msg.data = JSON.parse(msg.data);
|
||||
} catch (e) {
|
||||
this.emit('warning', 'Could not parse: ' + msg.data);
|
||||
msg.data = null;
|
||||
}
|
||||
}
|
||||
if (msg.token) {
|
||||
this.token[rinfo.address] = msg.token;
|
||||
}
|
||||
|
||||
if (msg.cmd === 'heartbeat') {
|
||||
sensor.heartBeat(msg.token, msg.data);
|
||||
} else {
|
||||
sensor.heartBeat();
|
||||
}
|
||||
|
||||
if (msg.data && (msg.cmd === 'report' || msg.cmd.indexOf('_ack') !== -1)) {
|
||||
sensor.onMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('message', msg);
|
||||
};
|
||||
|
||||
this.getKey = function (ip) {
|
||||
if (!this.token[ip]) return null;
|
||||
const key = this.keys[ip] || this.key;
|
||||
const cipher = crypto.createCipheriv('aes-128-cbc', key, this.iv);
|
||||
const crypted = cipher.update(this.token[ip], 'ascii', 'hex');
|
||||
cipher.final('hex'); // Useless data, don't know why yet.
|
||||
return crypted;
|
||||
};
|
||||
|
||||
this.sendMessage = function (message, ip) {
|
||||
if (this._state === 'CLOSED') return false;
|
||||
const json = JSON.stringify(message);
|
||||
this.socket.send(json, 0, json.length, options.port, ip || '224.0.0.50');
|
||||
};
|
||||
|
||||
this.sensorFactory = function (sid, model, ip, name) {
|
||||
if (this._state === 'CLOSED') return false;
|
||||
let sensor = null;
|
||||
const dev = Object.keys(Devices).find(id => Devices[id].type === model);
|
||||
|
||||
if (Devices[dev] && Devices[dev].ClassName) {
|
||||
sensor = new Devices[dev].ClassName(sid, ip, this, model, options);
|
||||
} else {
|
||||
throw new Error('Type "' + model + '" is not valid, use one of Hub::sensorTypes');
|
||||
}
|
||||
this.registerSensor(sensor, name);
|
||||
return sensor;
|
||||
};
|
||||
this.getSensor = function (sid) {
|
||||
return this.sensors[sid] || null;
|
||||
};
|
||||
this.registerSensor = function (sensor, name) {
|
||||
if (this._state === 'CLOSED') return false;
|
||||
this.emit('device', sensor, name);
|
||||
this.sensors[sensor.sid] = sensor;
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// extend the EventEmitter class using our Radio class
|
||||
util.inherits(Hub, EventEmitter);
|
||||
|
||||
module.exports = {
|
||||
Hub,
|
||||
Devices
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
|
||||
const texts = {
|
||||
0: 'Release alarm',
|
||||
1: 'Gas alarm',
|
||||
2: 'Analog alarm',
|
||||
64: 'Sensitivity fault alarm',
|
||||
32768: 'I2C communication failure'
|
||||
};
|
||||
|
||||
function Alarm(sid, ip, hub, model) {
|
||||
this.type = model;
|
||||
this.sid = sid;
|
||||
this.hub = hub;
|
||||
this.ip = ip;
|
||||
|
||||
this.state = null;
|
||||
this.description = null;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
}
|
||||
|
||||
Alarm.prototype.getData = function (data, isHeartbeat) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
|
||||
if (data.voltage !== undefined) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
if (data.alarm !== undefined && !isHeartbeat) {
|
||||
if (data.alarm !== true && data.alarm !== false) {
|
||||
data.alarm = parseInt(data.alarm, 10) || 0;
|
||||
if (data.alarm === 1) {
|
||||
if (this.type === 'smoke') {
|
||||
data.description = 'Smoke alarm';
|
||||
} else {
|
||||
data.description = 'Gas alarm';
|
||||
}
|
||||
} else {
|
||||
data.description = texts[data.alarm] || '';
|
||||
}
|
||||
data.alarm = (data.alarm === 1 || data.alarm === 2);
|
||||
}
|
||||
|
||||
this.state = data.alarm;
|
||||
obj.state = data.alarm;
|
||||
obj.description = data.description;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
Alarm.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data, true);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
Alarm.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
module.exports = Alarm;
|
||||
@@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
function Button(sid, ip, hub) {
|
||||
this.type = 'switch';
|
||||
this.sid = sid;
|
||||
this.hub = hub;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
this.ip = ip;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Button.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.voltage) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
if (data.status) {
|
||||
if (data.status === 'click') {
|
||||
obj.click = true;
|
||||
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {click: false}), 300);
|
||||
}
|
||||
if (data.status === 'double_click') {
|
||||
obj.double = true;
|
||||
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {double: false}), 300);
|
||||
}
|
||||
if (data.status === 'long_click_press') {
|
||||
obj.long = true;
|
||||
}
|
||||
if (data.status === 'long_click_release') {
|
||||
obj.long = false;
|
||||
}
|
||||
newData = true;
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
Button.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Button.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
module.exports = Button;
|
||||
@@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
|
||||
function Cube(sid, ip, hub, rotatePosition) {
|
||||
this.type = 'cube';
|
||||
this.sid = sid;
|
||||
this.ip = ip;
|
||||
this.hub = hub;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
this.rotate_position = parseFloat(rotatePosition || 0) || 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Cube.prototype.trigger = function (obj, name) {
|
||||
obj[name] = true;
|
||||
|
||||
setTimeout(() => {
|
||||
const _obj = {};
|
||||
_obj[name] = false;
|
||||
this.hub.emit('data', this.sid, this.type, _obj);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
Cube.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.voltage) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
if (data.status) {
|
||||
// flip90, flip180, move, tap_twice, shake_air, swing, alert, free_fall, rotate_left, rotate_right
|
||||
this.trigger(obj, data.status);
|
||||
newData = true;
|
||||
}
|
||||
if (data.rotate) {
|
||||
// rotate
|
||||
obj.rotate = parseFloat(data.rotate.replace(',', '.')) || 0;
|
||||
if (obj.rotate >= 0) {
|
||||
this.trigger(obj, 'rotate_right');
|
||||
} else if (obj.rotate < 0) {
|
||||
this.trigger(obj, 'rotate_left');
|
||||
}
|
||||
this.rotate_position += obj.rotate;
|
||||
if (this.rotate_position < 0) this.rotate_position = 0;
|
||||
if (this.rotate_position > 100) this.rotate_position = 100;
|
||||
obj.rotate_position = this.rotate_position;
|
||||
|
||||
newData = true;
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
Cube.prototype.Control = function (attr, val) {
|
||||
if (attr === 'rotate_position') {
|
||||
val = parseFloat(val);
|
||||
if (val < 0) val = 0;
|
||||
if (val > 100) val = 100;
|
||||
|
||||
if (this.rotate_position !== val) {
|
||||
this.hub.emit('data', this.sid, this.type, {rotate_position: this.rotate_position});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Cube.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Cube.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Cube;
|
||||
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
|
||||
function Curtain(sid, ip, hub) {
|
||||
this.type = 'curtain';
|
||||
this.sid = sid;
|
||||
this.hub = hub;
|
||||
this.ip = ip;
|
||||
this.curtain_level = null;
|
||||
}
|
||||
|
||||
Curtain.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.curtain_level) {
|
||||
this.curtain_level = parseFloat(data.curtain_level);
|
||||
obj.curtain_level = this.curtain_level;
|
||||
newData = true;
|
||||
}
|
||||
if (data.status) {
|
||||
if (this.status === 'open') {
|
||||
obj.open = true;
|
||||
newData = true;
|
||||
} else
|
||||
if (this.status === 'close') {
|
||||
obj.close = true;
|
||||
newData = true;
|
||||
} else
|
||||
if (this.status === 'stop') {
|
||||
obj.stop = true;
|
||||
newData = true;
|
||||
} else {
|
||||
this.hub.emit('warning', 'Unknown status "' + this.status + '"');
|
||||
}
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
Curtain.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Curtain.prototype.Control = function (attr, value) {
|
||||
let message;
|
||||
if (attr === 'stop' || attr === 'open' || attr === 'close') {
|
||||
message = {
|
||||
cmd: 'write',
|
||||
model: this.type,
|
||||
sid: this.sid,
|
||||
short_id: 0,
|
||||
data: {
|
||||
status: attr,
|
||||
key: this.hub.getKey(this.ip)
|
||||
}
|
||||
};
|
||||
} else if (attr === 'curtain_level') {
|
||||
message = {
|
||||
cmd: 'write',
|
||||
model: this.type,
|
||||
sid: this.sid,
|
||||
short_id: 0,
|
||||
data: {
|
||||
curtain_level: value.toString(), //Strange thing, working only if string.
|
||||
key: this.hub.getKey(this.ip)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
this.hub.emit('warning', 'Unknown control attribute "' + attr + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
this.hub.sendMessage(message, this.ip);
|
||||
};
|
||||
|
||||
Curtain.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
if (message.data.status) {
|
||||
this.on = message.data.status === 'on';
|
||||
this.hub.emit('data', this.sid, this.type, {
|
||||
state: this.on
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
module.exports = Curtain;
|
||||
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
function DoorSensor(sid, ip, hub) {
|
||||
this.type = 'magnet';
|
||||
this.sid = sid;
|
||||
this.hub = hub;
|
||||
this.ip = ip;
|
||||
this.opened = null;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
}
|
||||
|
||||
DoorSensor.prototype.getData = function (data, isHeartbeat) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.voltage !== undefined) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
if (data.status && data.status !== 'unknown' && !isHeartbeat) {
|
||||
this.opened = data.status !== 'close';
|
||||
obj.state = this.opened;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
DoorSensor.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data, true);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
DoorSensor.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
module.exports = DoorSensor;
|
||||
@@ -0,0 +1,242 @@
|
||||
'use strict';
|
||||
|
||||
function Gateway(sid, ip, hub) {
|
||||
this.type = 'gateway';
|
||||
this.sid = sid;
|
||||
this.ip = ip;
|
||||
this.hub = hub;
|
||||
this.token = null;
|
||||
this.illumination = null;
|
||||
this.connected = false;
|
||||
|
||||
this.lastValues = {
|
||||
rgb: '#FFFFFF',
|
||||
dimmer: 100
|
||||
};
|
||||
this.rgb = null;
|
||||
this.dimmer = null;
|
||||
this.on = null;
|
||||
|
||||
this.hub.sendMessage({cmd: 'get_id_list', sid: sid}, this.ip);
|
||||
this.timeout = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Gateway.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
|
||||
if (data.illumination !== undefined) {
|
||||
this.illumination = parseFloat(data.illumination);
|
||||
obj.illumination = this.illumination;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
if (!this.connected) {
|
||||
this.connected = true;
|
||||
obj.connected = this.connected;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
// Start timeout to detect dicsonnect
|
||||
this.timeout && clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() => {
|
||||
this.timeout = null;
|
||||
this.hub.emit('data', this.sid, this.type, {connected: false});
|
||||
}, 20000);
|
||||
|
||||
if (data.rgb !== undefined) {
|
||||
let rgb = parseInt(data.rgb, 10);
|
||||
if (!rgb) {
|
||||
this.rgb = '#000000';
|
||||
this.dimmer = 0;
|
||||
this.on = false;
|
||||
} else {
|
||||
rgb = rgb.toString(16);
|
||||
if (rgb.length === 7) {
|
||||
rgb = '0' + rgb;
|
||||
} else if (rgb.length === 6) {
|
||||
rgb = '00' + rgb;
|
||||
} else if (rgb.length === 5) {
|
||||
rgb = '000' + rgb;
|
||||
} else if (rgb.length === 4) {
|
||||
rgb = '0000' + rgb;
|
||||
} else if (rgb.length === 3) {
|
||||
rgb = '00000' + rgb;
|
||||
} else if (rgb.length === 2) {
|
||||
rgb = '000000' + rgb;
|
||||
} else if (rgb.length === 1) {
|
||||
rgb = '0000000' + rgb;
|
||||
}
|
||||
this.dimmer = parseInt(rgb.substring(0, 2), 16);
|
||||
this.rgb = '#' + rgb.substring(2).toUpperCase();
|
||||
this.on = true;
|
||||
}
|
||||
obj.on = this.on;
|
||||
obj.dimmer = this.dimmer;
|
||||
obj.rgb = this.rgb;
|
||||
|
||||
// remember last non null values
|
||||
if (obj.dimmer) {
|
||||
this.lastValues.dimmer = obj.dimmer;
|
||||
}
|
||||
if (parseInt(obj.rgb.replace('#', ''), 16)) {
|
||||
this.lastValues.rgb = obj.rgb;
|
||||
}
|
||||
|
||||
newData = true;
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
Gateway.prototype.heartBeat = function (token, data) {
|
||||
if (token) {
|
||||
this.token = token;
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Gateway.prototype.onMessage = function (message) {
|
||||
if (message.cmd === 'get_id_list_ack') {
|
||||
this.initSensors(message.data);
|
||||
} else if (message.cmd === 'write_ack') {
|
||||
if (message.data.error) {
|
||||
this.hub.emit('error', message.data.error);
|
||||
}
|
||||
const obj_ = this.getData(message.data);
|
||||
if (obj_) {
|
||||
this.hub.emit('data', this.sid, this.type, obj_);
|
||||
}
|
||||
} else if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Gateway.prototype.Control = function (attr, value) {
|
||||
if (attr === 'on' || attr === 'dimmer' || attr === 'rgb') {
|
||||
if (this.dimmer === null) {
|
||||
this.dimmer = this.lastValues.dimmer;
|
||||
}
|
||||
if (this.rgb === null) {
|
||||
this.rgb = this.lastValues.rgb;
|
||||
}
|
||||
if (this.on === null) {
|
||||
this.on = true;
|
||||
}
|
||||
|
||||
if (attr === 'on') {
|
||||
this.on = !!value;
|
||||
|
||||
if (this.on) {
|
||||
if (!parseInt(this.rgb.replace('#', ''), 16)) {
|
||||
this.rgb = this.lastValues.rgb;
|
||||
}
|
||||
if (!this.dimmer) {
|
||||
this.dimmer = this.lastValues.dimmer;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attr === 'dimmer') {
|
||||
this.dimmer = value;
|
||||
if (this.dimmer < 0) this.dimmer = 0;
|
||||
if (this.dimmer > 100) this.dimmer = 100;
|
||||
|
||||
if (this.dimmer) {
|
||||
this.on = true;
|
||||
|
||||
if (!parseInt(this.rgb.replace('#', ''), 16)) {
|
||||
this.rgb = this.lastValues.rgb;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (attr === 'rgb') {
|
||||
this.rgb = value;
|
||||
|
||||
if (parseInt(this.rgb.replace('#', ''), 16)) {
|
||||
this.on = true;
|
||||
|
||||
if (!this.dimmer) {
|
||||
this.dimmer = this.lastValues.dimmer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.timer) clearTimeout(this.timer);
|
||||
|
||||
// collect data before send
|
||||
this.timer = setTimeout(() => {
|
||||
this.timer = null;
|
||||
let value;
|
||||
if (!this.on || !this.dimmer || this.rgb === '000000' || this.rgb === '#000000') {
|
||||
value = 0;
|
||||
} else {
|
||||
value = (this.dimmer << 24) | parseInt(this.rgb.replace('#', ''), 16);
|
||||
}
|
||||
|
||||
const message = {
|
||||
cmd: 'write',
|
||||
model: this.type,
|
||||
sid: this.sid,
|
||||
short_id: 0,
|
||||
data: {
|
||||
rgb: value,
|
||||
key: this.hub.getKey(this.ip)
|
||||
}
|
||||
};
|
||||
|
||||
this.hub.sendMessage(message, this.ip);
|
||||
}, 200);
|
||||
} else if (attr === 'volume') {
|
||||
if (value < 0) value = 0;
|
||||
if (value > 100) value = 100;
|
||||
|
||||
const message = {
|
||||
cmd: 'write',
|
||||
model: this.type,
|
||||
sid: this.sid,
|
||||
short_id: 0,
|
||||
data: {
|
||||
mid: this.mid || 999,
|
||||
vol: value,
|
||||
key: this.hub.getKey(this.ip)
|
||||
}
|
||||
};
|
||||
|
||||
this.hub.sendMessage(message, this.ip);
|
||||
} else if (attr === 'mid') {
|
||||
this.mid = value;
|
||||
const message = {
|
||||
cmd: 'write',
|
||||
model: this.type,
|
||||
sid: this.sid,
|
||||
short_id: 0,
|
||||
data: {
|
||||
mid: value,
|
||||
key: this.hub.getKey(this.ip)
|
||||
}
|
||||
};
|
||||
|
||||
this.hub.sendMessage(message, this.ip);
|
||||
} else {
|
||||
this.hub.emit('warning', 'Unknown attribute ' + attr);
|
||||
}
|
||||
};
|
||||
|
||||
Gateway.prototype.initSensors = function (sids) {
|
||||
this.hub.sendMessage({cmd: 'read', sid: this.sid}, this.ip);
|
||||
for (let i = 0; i < sids.length; i++) {
|
||||
this.hub.sendMessage({cmd: 'read', sid: sids[i]}, this.ip);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Gateway;
|
||||
@@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
|
||||
function Lock(sid, ip, hub) {
|
||||
this.type = 'lock_aq1';
|
||||
this.sid = sid;
|
||||
this.ip = ip;
|
||||
this.hub = hub;
|
||||
this.fing_verified = null;
|
||||
this.psw_verified = null;
|
||||
this.card_verified = null;
|
||||
this.verified_wrong = null;
|
||||
}
|
||||
// {'cmd': 'read_ack', 'model': 'lock.aq1', 'sid': '158d000222****', 'short_id': 55***, 'data': '{"voltage":3387}'}
|
||||
// {'cmd': 'read_ack', 'model': 'lock.aq1', 'sid': '158d000222****', 'short_id': 55***, 'data': '{"fing_verified": 65536}'}
|
||||
// {'cmd': 'read_ack', 'model': 'lock.aq1', 'sid': '158d000222****', 'short_id': 55***, 'data': '{"fing_verified": 65537}'}
|
||||
// {'cmd': 'read_ack', 'model': 'lock.aq1', 'sid': '158d000222****', 'short_id': 55***, 'data': '{"psw_verified": 131074}'}
|
||||
// {'cmd': 'read_ack', 'model': 'lock.aq1', 'sid': '158d000222****', 'short_id': 55***, 'data': '{"card_verified": 196608}'}
|
||||
// {'cmd': 'report', 'model': 'lock.aq1', 'sid': '158d000222****', 'short_id': 55***, 'data': '{"verified_wrong":"3"}'}
|
||||
|
||||
Lock.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.voltage !== undefined) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
if (data.fing_verified) {
|
||||
this.fing_verified = parseInt(data.fing_verified);
|
||||
obj.fing_verified = this.fing_verified;
|
||||
newData = true;
|
||||
}
|
||||
if (data.psw_verified) {
|
||||
this.psw_verified = parseInt(data.psw_verified);
|
||||
obj.psw_verified = this.psw_verified;
|
||||
newData = true;
|
||||
}
|
||||
if (data.card_verified) {
|
||||
this.card_verified = parseInt(data.card_verified);
|
||||
obj.card_verified = this.card_verified;
|
||||
newData = true;
|
||||
}
|
||||
if (data.verified_wrong) {
|
||||
this.verified_wrong = parseInt(data.verified_wrong);
|
||||
obj.verified_wrong = this.verified_wrong;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
Lock.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Lock.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
module.exports = Lock;
|
||||
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
function MotionSensor(sid, ip, hub, model) {
|
||||
this.type = model;
|
||||
this.sid = sid;
|
||||
this.ip = ip;
|
||||
this.hub = hub;
|
||||
this.motion = null;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
}
|
||||
|
||||
MotionSensor.prototype.getData = function (data, isHeartbeat) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.voltage !== undefined) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
if (data.status && !isHeartbeat) {
|
||||
this.motion = data.status === 'motion';
|
||||
obj.state = this.motion;
|
||||
if (this.motion) {
|
||||
this.no_motion = 0;
|
||||
obj.no_motion = 0;
|
||||
}
|
||||
newData = true;
|
||||
}
|
||||
if (data.no_motion !== undefined && !isHeartbeat) {
|
||||
this.no_motion = parseInt(data.no_motion, 10);
|
||||
obj.no_motion = this.no_motion;
|
||||
obj.state = !this.no_motion;
|
||||
newData = true;
|
||||
}
|
||||
if (data.lux !== undefined) {
|
||||
this.lux = parseInt(data.lux, 10);
|
||||
obj.lux = this.lux;
|
||||
newData = true;
|
||||
}
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
MotionSensor.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data, true);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
MotionSensor.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = MotionSensor;
|
||||
@@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
function Plug (sid, ip, hub) {
|
||||
this.type = 'plug';
|
||||
this.sid = sid;
|
||||
this.ip = ip;
|
||||
this.hub = hub;
|
||||
this.on = null;
|
||||
this.load_power = null;
|
||||
this.power_consumed = null;
|
||||
this.state = null;
|
||||
this.inuse = null;
|
||||
}
|
||||
// {"cmd":"report","model":"plug","sid":"dfdfd","short_id":52239,"data":{"status":"on"}}
|
||||
// {"cmd":"report","model":"plug","sid":"dfdfdf","short_id":52239,"data":{"status":"off"}}
|
||||
// {"cmd":"heartbeat","model":"plug","sid":"fdfdfd","short_id":52239,"data":{"voltage":3600,"status":"on","inuse":"0","power_consumed":"26326","load_power":"0.00"}}
|
||||
Plug.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.load_power) {
|
||||
this.load_power = parseFloat(data.load_power);
|
||||
obj.load_power = this.load_power;
|
||||
newData = true;
|
||||
}
|
||||
if (data.power_consumed) {
|
||||
this.power_consumed = parseFloat(data.power_consumed);
|
||||
obj.power_consumed = this.power_consumed;
|
||||
newData = true;
|
||||
}
|
||||
if (data.inuse) {
|
||||
this.inuse = !!parseInt(data.inuse, 10);
|
||||
obj.inuse = this.inuse;
|
||||
newData = true;
|
||||
}
|
||||
if (data.status) {
|
||||
this.state = data.status === 'on';
|
||||
obj.state = this.state;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
Plug.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Plug.prototype.Control = function (attr, value) {
|
||||
if (attr !== 'channel_0' && attr !== 'state') {
|
||||
this.hub.emit('warning', 'Unknown attribute ' + attr);
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
cmd: 'write',
|
||||
model: this.type,
|
||||
sid: this.sid,
|
||||
short_id: 0,
|
||||
data: {
|
||||
channel_0: value ? 'on' : 'off',
|
||||
key: this.hub.getKey(this.ip)
|
||||
}
|
||||
};
|
||||
|
||||
this.hub.sendMessage(message, this.ip);
|
||||
};
|
||||
|
||||
Plug.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
if (message.data.status) {
|
||||
this.on = message.data.status === 'on';
|
||||
this.hub.emit('data', this.sid, this.type, {
|
||||
state: this.on
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
module.exports = Plug;
|
||||
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
function THSensor(sid, ip, hub, model, options) {
|
||||
this.type = model;
|
||||
this.sid = sid;
|
||||
this.hub = hub;
|
||||
this.ip = ip;
|
||||
|
||||
this.interval = parseInt((options && options.interval) || 5000, 10) || 0;
|
||||
this.temperature = null;
|
||||
this.humidity = null;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
this.lastData = null;
|
||||
}
|
||||
|
||||
THSensor.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
const ts = Date.now();
|
||||
if (this.interval && this.lastData) {
|
||||
const diff = ts - this.lastData;
|
||||
if (diff > 200 && diff < this.interval) {
|
||||
obj.doublePress = true;
|
||||
// deactivate after 300 ms
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {doublePress: false}), 300);
|
||||
this.lastData = null;
|
||||
} else {
|
||||
this.lastData = ts;
|
||||
}
|
||||
} else {
|
||||
this.lastData = ts;
|
||||
}
|
||||
|
||||
if (data.voltage !== undefined) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
if (data.temperature === '10000' || data.temperature === 10000) {
|
||||
// ignore all values if 10000 as temperature.
|
||||
return null;
|
||||
}
|
||||
if (data.temperature !== undefined) {
|
||||
this.temperature = parseInt(data.temperature) / 100;
|
||||
obj.temperature = this.temperature;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
if (data.humidity !== undefined) {
|
||||
this.humidity = parseInt(data.humidity) / 100;
|
||||
obj.humidity = this.humidity;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
if (data.pressure !== undefined) {
|
||||
this.pressure = parseInt(data.pressure) / 100;
|
||||
obj.pressure = this.pressure;
|
||||
newData = true;
|
||||
}
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
THSensor.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
THSensor.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
module.exports = THSensor;
|
||||
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
function VibrationSensor(sid, ip, hub, model) {
|
||||
this.type = model;
|
||||
this.sid = sid;
|
||||
this.ip = ip;
|
||||
this.hub = hub;
|
||||
this.vibration = null;
|
||||
this.orientationX = null;
|
||||
this.orientationY = null;
|
||||
this.orientationZ = null;
|
||||
this.bed_activity = null;
|
||||
this.tilt_angle = null;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
}
|
||||
|
||||
VibrationSensor.prototype.getData = function (data, isHeartbeat) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.voltage !== undefined) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
if (data.status && !isHeartbeat) {
|
||||
this.vibration = data.status === 'vibration' || data.status === 'vibrate';
|
||||
if (this.vibration) {
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {state: false}), 300);
|
||||
}
|
||||
|
||||
obj.state = this.vibration;
|
||||
newData = true;
|
||||
}
|
||||
if (data.final_tilt_angle !== undefined) {
|
||||
this.tilt_angle = parseInt(data.final_tilt_angle, 10);
|
||||
obj.tilt_angle = this.tilt_angle;
|
||||
newData = true;
|
||||
}
|
||||
if (data.coordination !== undefined) {
|
||||
const parts = data.coordination.split(',').map(num => parseInt(num.trim()));
|
||||
this.orientationX = parts[0];
|
||||
this.orientationY = parts[1];
|
||||
this.orientationZ = parts[2];
|
||||
obj.orientationX = this.orientationX;
|
||||
obj.orientationY = this.orientationY;
|
||||
obj.orientationZ = this.orientationZ;
|
||||
newData = true;
|
||||
}
|
||||
if (data.bed_activity !== undefined) {
|
||||
this.bed_activity = parseInt(data.bed_activity, 10);
|
||||
obj.bed_activity = this.bed_activity;
|
||||
newData = true;
|
||||
}
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
VibrationSensor.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data, true);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
VibrationSensor.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = VibrationSensor;
|
||||
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
function WallButtons(sid, ip, hub, model) {
|
||||
this.type = model;
|
||||
this.sid = sid;
|
||||
this.hub = hub;
|
||||
this.ip = ip;
|
||||
this.channel_0 = null;
|
||||
this.channel_1 = null;
|
||||
this.dual_channel = null;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
WallButtons.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
|
||||
if (data.voltage !== undefined) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
if (data.channel_0) {
|
||||
obj.channel_0_double = data.channel_0 === 'double_click';
|
||||
this.channel_0 = data.channel_0 === 'click';
|
||||
obj.channel_0 = this.channel_0;
|
||||
newData = true;
|
||||
if (obj.channel_0) {
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {channel_0: false}), 300);
|
||||
}
|
||||
if (obj.channel_0_double) {
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {channel_0_double: false}), 300);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.channel_1) {
|
||||
obj.channel_1_double = data.channel_1 === 'double_click';
|
||||
this.channel_1 = data.channel_1 === 'click';
|
||||
obj.channel_1 = this.channel_1;
|
||||
newData = true;
|
||||
if (obj.channel_1) {
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {channel_1: false}), 300);
|
||||
}
|
||||
if (obj.channel_1_double) {
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {channel_1_double: false}), 300);
|
||||
}
|
||||
}
|
||||
if (data.dual_channel) {
|
||||
this.dual_channel = data.dual_channel === 'both_click';
|
||||
obj.dual_channel = this.dual_channel;
|
||||
newData = true;
|
||||
if (obj.dual_channel) {
|
||||
setTimeout(() => this.hub.emit('data', this.sid, this.type, {dual_channel: false}), 300);
|
||||
}
|
||||
}
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
WallButtons.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WallButtons.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = WallButtons;
|
||||
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
function WallWiredSwitch(sid, ip, hub, model) {
|
||||
this.type = model;
|
||||
this.sid = sid;
|
||||
this.hub = hub;
|
||||
this.ip = ip;
|
||||
this.channel_0 = null;
|
||||
this.channel_1 = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
WallWiredSwitch.prototype.getData = function (data) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
|
||||
if (data.channel_0) {
|
||||
this.channel_0 = data.channel_0 === 'on';
|
||||
obj.channel_0 = this.channel_0;
|
||||
newData = true;
|
||||
}
|
||||
if (data.channel_1) {
|
||||
this.channel_1 = data.channel_1 === 'on';
|
||||
obj.channel_1 = this.channel_1;
|
||||
newData = true;
|
||||
}
|
||||
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
WallWiredSwitch.prototype.Control = function (attr, value) {
|
||||
if (attr !== 'channel_0' && attr !== 'channel_1') {
|
||||
this.hub.emit('warning', 'Unknown attribute ' + attr);
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
cmd: 'write',
|
||||
model: this.type,
|
||||
sid: this.sid,
|
||||
short_id: 0,
|
||||
data: {
|
||||
key: this.hub.getKey(this.ip)
|
||||
}
|
||||
};
|
||||
message.data[attr] = value ? 'on' : 'off';
|
||||
|
||||
this.hub.sendMessage(message, this.ip);
|
||||
};
|
||||
|
||||
WallWiredSwitch.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WallWiredSwitch.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = WallWiredSwitch;
|
||||
@@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
function WaterSensor(sid, ip, hub) {
|
||||
this.type = 'sensor_wleak.aq1';
|
||||
this.sid = sid;
|
||||
this.hub = hub;
|
||||
this.ip = ip;
|
||||
this.motion = null;
|
||||
this.voltage = null;
|
||||
this.percent = null;
|
||||
}
|
||||
|
||||
WaterSensor.prototype.getData = function (data, isHeartbeat) {
|
||||
let newData = false;
|
||||
let obj = {};
|
||||
if (data.voltage !== undefined) {
|
||||
data.voltage = parseInt(data.voltage, 10);
|
||||
this.voltage = data.voltage / 1000;
|
||||
this.percent = ((data.voltage - 2200) / 10);
|
||||
if (this.percent > 100) {
|
||||
this.percent = 100;
|
||||
}
|
||||
if (this.percent < 0) {
|
||||
this.percent = 0;
|
||||
}
|
||||
obj.voltage = this.voltage;
|
||||
obj.percent = this.percent;
|
||||
newData = true;
|
||||
}
|
||||
// {“cmd”:“report”,“model”:“sensor_wleak.aq1”,“sid”:“aaa000xxxxxxx”,“short_id”:12345,“data”:"{“status”:“leak”}"}
|
||||
// {“cmd”:“report”,“model”:“sensor_wleak.aq1”,“sid”:“aaa000xxxxxxx”,“short_id”:12345,“data”:"{“status”:“no_leak”}"}
|
||||
if (data.status && !isHeartbeat) {
|
||||
this.leak = data.status === 'leak';
|
||||
obj.state = this.leak;
|
||||
newData = true;
|
||||
}
|
||||
return newData ? obj : null;
|
||||
};
|
||||
|
||||
WaterSensor.prototype.heartBeat = function (token, data) {
|
||||
if (data) {
|
||||
const obj = this.getData(data, true);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
WaterSensor.prototype.onMessage = function (message) {
|
||||
if (message.data) {
|
||||
const obj = this.getData(message.data);
|
||||
if (obj) {
|
||||
this.hub.emit('data', this.sid, this.type, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = WaterSensor;
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
'use strict';
|
||||
|
||||
const Gateway = require('./Sensors/Gateway');
|
||||
const THSensor = require('./Sensors/THSensor');
|
||||
const DoorSensor = require('./Sensors/DoorSensor');
|
||||
const MotionSensor = require('./Sensors/MotionSensor');
|
||||
const VibrationSensor = require('./Sensors/VibrationSensor');
|
||||
const Plug = require('./Sensors/Plug');
|
||||
const Button = require('./Sensors/Button');
|
||||
const Cube = require('./Sensors/Cube');
|
||||
const WallButtons = require('./Sensors/WallButtons');
|
||||
const WallWiredSwitch = require('./Sensors/WallWiredSwitch');
|
||||
const Alarm = require('./Sensors/Alarm');
|
||||
const Curtain = require('./Sensors/Curtain');
|
||||
const WaterSensor = require('./Sensors/WaterSensor');
|
||||
const Lock = require('./Sensors/Lock');
|
||||
|
||||
const states = {
|
||||
voltage: {name: 'Battery voltage', role: 'battery.voltage', write: false, read: true, type: 'number', unit: 'V', icon: '/icons/battery_v.png', desc: 'Battery voltage'},
|
||||
percent: {name: 'Battery percent', role: 'battery.percent', write: false, read: true, type: 'number', unit: '%', icon: '/icons/battery_p.png', desc: 'Battery level in percent', min: 0, max: 100},
|
||||
|
||||
temperature: {name: 'Temperature', role: 'value.temperature', write: false, read: true, type: 'number', unit: '°C'},
|
||||
humidity: {name: 'Humidity', role: 'value.humidity', write: false, read: true, type: 'number', unit: '%', min: 0, max: 100},
|
||||
|
||||
doublePress: {name: 'Double press', role: 'state', write: false, read: true, type: 'boolean', desc: 'You can press connect button twice'},
|
||||
|
||||
opened: {name: 'Is opened', role: 'state', write: false, read: true, type: 'boolean'},
|
||||
description: {name: 'Alarm description', role: 'state', write: false, read: true, type: 'string'},
|
||||
|
||||
motion: {name: 'Is motion', role: 'indicator.motion', write: false, read: true, type: 'boolean'},
|
||||
no_motion: {name: 'Last motion', role: 'state', write: false, read: true, type: 'number', unit: 'seconds', desc: 'Last motion for at least X seconds'},
|
||||
fing_verified: {name: 'Finger verified', role: 'value', write: false, read: true, type: 'number'},
|
||||
|
||||
psw_verified: {name: 'Password verified', role: 'value', write: false, read: true, type: 'number'},
|
||||
card_verified: {name: 'Card verified', role: 'value', write: false, read: true, type: 'number'},
|
||||
verified_wrong: {name: 'Wrong verification', role: 'value', write: false, read: true, type: 'number'},
|
||||
|
||||
click: {name: 'Simple click', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
double: {name: 'Double click', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
long: {name: 'Long click', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
|
||||
channel_0: {name: 'First button pressed', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
channel_0_double: {name: 'First button pressed double', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
channel_1: {name: 'Second button pressed', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
channel_1_double: {name: 'Second button pressed double', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
dual_channel: {name: 'Both buttons pressed', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
|
||||
power: {name: 'Socket plug', role: 'switch', write: true, read: true, type: 'boolean'},
|
||||
load_power: {name: 'Load power', role: 'value.power', write: false, read: true, type: 'number', unit: 'W'},
|
||||
power_consumed: {name: 'Power consumed', role: 'value.consumption', write: false, read: true, type: 'number', unit: 'W'},
|
||||
inuse: {name: 'Is in use', role: 'state', write: false, read: true, type: 'number'},
|
||||
|
||||
wall_switch: {name: 'Wall switch', role: 'switch', write: true, read: true, type: 'boolean'},
|
||||
wall_switch0: {name: 'Wall switch 0', role: 'switch', write: true, read: true, type: 'boolean'},
|
||||
wall_switch1: {name: 'Wall switch 1', role: 'switch', write: true, read: true, type: 'boolean'},
|
||||
|
||||
rotate: {name: 'Rotation angle', role: 'state', write: false, read: true, type: 'number'},
|
||||
rotate_position: {name: 'Rotation angle', role: 'state', write: true, read: true, type: 'number', min: 0, max: 100, unit: '%'},
|
||||
flip90: {name: 'Flip on 90°', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
flip180: {name: 'Flip on 180°', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
move: {name: 'Move action', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
tap_twice: {name: 'Tapped twice', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
shake_air: {name: 'Shaken in air', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
swing: {name: 'Swing action', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
alert: {name: 'Alert action', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
free_fall: {name: 'Free fall action', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
rotate_left: {name: 'Rotate left', role: 'button', write: false, read: true, type: 'boolean'},
|
||||
rotate_right: {name: 'Rotate right', role: 'button', write: false, read: true, type: 'boolean'}
|
||||
};
|
||||
|
||||
// type - name as delivered by gateway
|
||||
// fullName - Name of the device
|
||||
// ClassName - handler class
|
||||
// states - list of states for this sensor
|
||||
|
||||
const devices = {
|
||||
gateway: {type: 'gateway', fullName: 'Xiaomi RGB Gateway', ClassName: Gateway, states: {
|
||||
illumination: {name: 'Illumination', role: 'value.lux', write: false, read: true, type: 'number', unit: 'lux'},
|
||||
rgb: {name: 'RGB', role: 'level.rgb', write: true, read: true, type: 'string'},
|
||||
on: {name: 'Light', role: 'switch', write: true, read: true, type: 'boolean'},
|
||||
dimmer: {name: 'Light', role: 'level.dimmer', write: true, read: true, type: 'number', unit: '%', min: 0, max: 100},
|
||||
volume: {name: 'Volume', role: 'level.volume', write: true, read: true, type: 'number', unit: '%', min: 0, max: 100},
|
||||
mid: {name: 'Music ID', role: 'state', write: true, read: false, type: 'number', desc: '10000 - stop, 10005 - custom ringtone'},
|
||||
connected: {name: 'Is gateway connected', role: 'indicator.reachable', write: true, read: false, type: 'boolean', desc: 'Will be set to false if no packets received in 20 seconds'}
|
||||
}
|
||||
},
|
||||
th: {type: 'sensor_ht', fullName: 'Xiaomi Temperature/Humidity', ClassName: THSensor, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
temperature: states.temperature,
|
||||
humidity: states.humidity,
|
||||
doublePress: states.doublePress
|
||||
}
|
||||
},
|
||||
weather: {type: 'weather.v1', fullName: 'Xiaomi Temperature/Humidity/Pressure', ClassName: THSensor, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
temperature: states.temperature,
|
||||
humidity: states.humidity,
|
||||
pressure: {name: 'Pressure', role: 'value.pressure', write: false, read: true, type: 'number', unit: 'Pa', min: 0, max: 100},
|
||||
doublePress: states.doublePress
|
||||
}
|
||||
},
|
||||
button: {type: 'switch', fullName: 'Xiaomi Wireless Switch', ClassName: Button, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
click: states.click,
|
||||
double: states.double,
|
||||
long: states.long
|
||||
}
|
||||
},
|
||||
button2: {type: 'sensor_switch.aq2', fullName: 'Xiaomi Wireless Switch Sensor', ClassName: Button, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
click: states.click,
|
||||
double: states.double,
|
||||
long: states.long
|
||||
}
|
||||
},
|
||||
button3: {type: 'sensor_switch.aq3', fullName: 'Xiaomi Wireless Switch Sensor', ClassName: Button, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
click: states.click,
|
||||
double: states.double,
|
||||
long: states.long
|
||||
}
|
||||
},
|
||||
button4: {type: 'remote.b1acn01', fullName: 'Xiaomi Aqara Smart Wireless Switch', ClassName: Button, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
click: states.click,
|
||||
double: states.double,
|
||||
long: states.long
|
||||
}
|
||||
},
|
||||
plug: {type: 'plug', fullName: 'Xiaomi Smart Plug', ClassName: Plug, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: states.power,
|
||||
load_power: states.load_power,
|
||||
power_consumed: states.power_consumed,
|
||||
inuse: states.inuse
|
||||
}
|
||||
},
|
||||
plug86: {type: '86plug', fullName: 'Xiaomi Smart Wall Plug', ClassName: Plug, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: states.power,
|
||||
load_power: states.load_power,
|
||||
power_consumed: states.power_consumed,
|
||||
inuse: states.inuse
|
||||
}
|
||||
},
|
||||
remote_b286acn01: {type: 'remote.b286acn01', fullName: 'Xiaomi Aqara Wireless Remote Switch (Double Rocker)', ClassName: WallButtons, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
channel_0: states.channel_0,
|
||||
channel_1: states.channel_1,
|
||||
dual_channel: states.dual_channel,
|
||||
channel_0_double: states.channel_0_double,
|
||||
channel_1_double: states.channel_1_double
|
||||
}
|
||||
},
|
||||
|
||||
remote_b186acn01: {type: 'remote.b186acn01', fullName: 'Xiaomi Aqara Wireless Remote Switch (Single Rocker)', ClassName: WallButtons, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
channel_0: states.channel_0,
|
||||
channel_0_double: states.channel_0_double
|
||||
}
|
||||
},
|
||||
|
||||
sw2_86: {type: '86sw2', fullName: 'Xiaomi Wireless Dual Wall Switch', ClassName: WallButtons, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
channel_0: states.channel_0,
|
||||
channel_1: states.channel_1,
|
||||
dual_channel: states.dual_channel,
|
||||
channel_0_double: states.channel_0_double,
|
||||
channel_1_double: states.channel_1_double
|
||||
}
|
||||
},
|
||||
sw1_86: {type: '86sw1', fullName: 'Xiaomi Wireless Single Wall Switch', ClassName: WallButtons, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
channel_0: states.click,
|
||||
channel_0_double: states.double
|
||||
}
|
||||
},
|
||||
natgas: {type: 'natgas', fullName: 'Xiaomi Mijia Honeywell Gas Alarm Detector', ClassName: Alarm, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: {name: 'Alarm state', role: 'indicator.alarm.CO2', write: false, read: true, type: 'boolean'},
|
||||
description: states.description
|
||||
}
|
||||
},
|
||||
smoke: {type: 'smoke', fullName: 'Xiaomi Mijia Honeywell Fire Alarm Detector', ClassName: Alarm, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: {name: 'Alarm state', role: 'indicator.alarm.fire', write: false, read: true, type: 'boolean'},
|
||||
description: states.description
|
||||
}
|
||||
},
|
||||
ctrl_ln1: {type: 'ctrl_ln1', fullName: 'Xiaomi Aqara 86 Fire Wall Switch One Button', ClassName: WallWiredSwitch, states: {
|
||||
channel_0: states.wall_switch
|
||||
}
|
||||
},
|
||||
ctrl_ln1_aq1: {type: 'ctrl_ln1.aq1', fullName: 'Xiaomi Aqara Wall Switch LN', ClassName: WallWiredSwitch, states: {
|
||||
channel_0: states.wall_switch
|
||||
}
|
||||
},
|
||||
ctrl_ln2: {type: 'ctrl_ln2', fullName: 'Xiaomi 86 zero fire wall switch double key', ClassName: WallWiredSwitch, states: {
|
||||
channel_0: states.wall_switch0,
|
||||
channel_1: states.wall_switch1
|
||||
}
|
||||
},
|
||||
ctrl_ln2_aq1: {type: 'ctrl_ln2.aq1', fullName: 'Xiaomi Aqara Wall Switch LN double key', ClassName: WallWiredSwitch, states: {
|
||||
channel_0: states.wall_switch0,
|
||||
channel_1: states.wall_switch1
|
||||
}
|
||||
},
|
||||
ctrl_86plug_aq1: {type: 'ctrl_86plug.aq1', fullName: 'Xiaomi Aqara Wall Socket', ClassName: Plug, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: states.power,
|
||||
load_power: states.load_power,
|
||||
power_consumed: states.power_consumed,
|
||||
inuse: states.inuse
|
||||
}
|
||||
},
|
||||
ctrl_neutral2: {type: 'ctrl_neutral2', fullName: 'Xiaomi Wired Dual Wall Switch', ClassName: WallWiredSwitch, states: {
|
||||
channel_0: states.wall_switch0,
|
||||
channel_1: states.wall_switch1
|
||||
}
|
||||
},
|
||||
ctrl_neutral1: {type: 'ctrl_neutral1', fullName: 'Xiaomi Wired Single Wall Switch', ClassName: WallWiredSwitch, states: {
|
||||
channel_0: states.wall_switch
|
||||
}
|
||||
},
|
||||
cube: {type: 'cube', fullName: 'Xiaomi Cube', ClassName: Cube, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
rotate: states.rotate,
|
||||
rotate_position: states.rotate_position,
|
||||
flip90: states.flip90,
|
||||
flip180: states.flip180,
|
||||
move: states.move,
|
||||
tap_twice: states.tap_twice,
|
||||
shake_air: states.shake_air,
|
||||
swing: states.swing,
|
||||
alert: states.alert,
|
||||
free_fall: states.free_fall,
|
||||
rotate_left: states.rotate_left,
|
||||
rotate_right: states.rotate_right,
|
||||
}
|
||||
},
|
||||
cube2: {type: 'sensor_cube.aqgl01', fullName: 'Xiaomi Cube 01', ClassName: Cube, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
rotate: states.rotate,
|
||||
rotate_position: states.rotate_position,
|
||||
flip90: states.flip90,
|
||||
flip180: states.flip180,
|
||||
move: states.move,
|
||||
tap_twice: states.tap_twice,
|
||||
shake_air: states.shake_air,
|
||||
swing: states.swing,
|
||||
alert: states.alert,
|
||||
free_fall: states.free_fall,
|
||||
rotate_left: states.rotate_left,
|
||||
rotate_right: states.rotate_right,
|
||||
}
|
||||
},
|
||||
magnet: {type: 'magnet', fullName: 'Xiaomi Door Sensor', ClassName: DoorSensor, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: states.opened
|
||||
}
|
||||
},
|
||||
magnet2: {type: 'sensor_magnet.aq2', fullName: 'Xiaomi Door Sensor', ClassName: DoorSensor, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: states.opened
|
||||
}
|
||||
},
|
||||
curtain: {type: 'curtain', fullName: 'Xiaomi Aqara Smart Curtain', ClassName: Curtain, states: {
|
||||
curtain_level: {name: 'Curtain level', role: 'level.blinds', write: true, read: true, type: 'number', min: 0, max: 100, unit: '%'},
|
||||
open: {name: 'Open', role: 'button.open', write: true, read: false, type: 'boolean'},
|
||||
close: {name: 'Close', role: 'button.close', write: true, read: false, type: 'boolean'},
|
||||
stop: {name: 'Stop', role: 'button.stop', write: true, read: false, type: 'boolean'}
|
||||
}
|
||||
},
|
||||
motion: {type: 'motion', fullName: 'Xiaomi Motion Sensor', ClassName: MotionSensor, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: states.motion,
|
||||
no_motion: states.no_motion
|
||||
}
|
||||
},
|
||||
lock_aq1: {type: 'lock.aq1', fullName: 'Xiaomi Lock', ClassName: Lock, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
fing_verified: states.fing_verified,
|
||||
psw_verified: states.psw_verified,
|
||||
card_verified: states.card_verified,
|
||||
verified_wrong: states.verified_wrong
|
||||
}
|
||||
},
|
||||
lock_v1: {type: 'lock.v1', fullName: 'Xiaomi Vima Smart Lock', ClassName: Lock, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
fing_verified: states.fing_verified,
|
||||
psw_verified: states.psw_verified,
|
||||
card_verified: states.card_verified,
|
||||
verified_wrong: states.verified_wrong
|
||||
}
|
||||
},
|
||||
motion2: {type: 'sensor_motion.aq2', fullName: 'Xiaomi Motion Sensor', ClassName: MotionSensor, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: states.motion,
|
||||
no_motion: states.no_motion,
|
||||
lux: {name: 'Brightness', role: 'indicator.brightness', write: false, read: true, type: 'number', unit: 'lux'}
|
||||
}
|
||||
},
|
||||
vibration: {type: 'vibration', fullName: 'Xiaomi Vibration Sensor', ClassName: VibrationSensor, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: {name: 'Is vibration', role: 'indicator.vibration', write: false, read: true, type: 'boolean', desc: 'Last tilt angle'},
|
||||
tilt_angle: {name: 'Tilt angle', role: 'value', write: false, read: true, type: 'number', unit: '°'},
|
||||
orientationX: {name: 'Orientation X', role: 'value', write: false, read: true, type: 'number', desc: 'Last X orientation'},
|
||||
orientationY: {name: 'Orientation Y', role: 'value', write: false, read: true, type: 'number', desc: 'Last Y orientation'},
|
||||
orientationZ: {name: 'Orientation Z', role: 'value', write: false, read: true, type: 'number', desc: 'Last Z orientation'},
|
||||
bed_activity: {name: 'Bed activity', role: 'value', write: false, read: true, type: 'number', desc: 'Last bed activity'}
|
||||
}
|
||||
},
|
||||
wleak1: {type: 'sensor_wleak.aq1', fullName: 'Xiaomi Aqara Water Sensor', ClassName: WaterSensor, states: {
|
||||
voltage: states.voltage,
|
||||
percent: states.percent,
|
||||
|
||||
state: {name: 'Is water detected', role: 'indicator.leakage', write: false, read: true, type: 'boolean'}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = devices;
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "homebridge-mi-home",
|
||||
"version": "0.0.1",
|
||||
"description": "Mi home plugin for Homebridge.",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"homebridge-plugin", "xiaomi", "mi", "mi home"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=0.12.0",
|
||||
"homebridge": ">=0.2.0",
|
||||
"miio": ">=0.12.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let controllerDir;
|
||||
let appName;
|
||||
|
||||
/**
|
||||
* returns application name
|
||||
*
|
||||
* The name of the application can be different and this function finds it out.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function getAppName() {
|
||||
const parts = __dirname.replace(/\\/g, '/').split('/');
|
||||
return parts[parts.length - 2].split('.')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* looks for js-controller home folder
|
||||
*
|
||||
* @param {boolean} isInstall
|
||||
* @returns {string}
|
||||
*/
|
||||
function getControllerDir(isInstall) {
|
||||
// Find the js-controller location
|
||||
const possibilities = [
|
||||
'yunkong2.js-controller',
|
||||
'yunkong2.js-controller',
|
||||
];
|
||||
/** @type {string} */
|
||||
let controllerPath;
|
||||
for (const pkg of possibilities) {
|
||||
try {
|
||||
const possiblePath = require.resolve(pkg);
|
||||
if (fs.existsSync(possiblePath)) {
|
||||
controllerPath = possiblePath;
|
||||
break;
|
||||
}
|
||||
} catch (e) { /* not found */ }
|
||||
}
|
||||
if (controllerPath == null) {
|
||||
if (!isInstall) {
|
||||
console.log('Cannot find js-controller');
|
||||
process.exit(10);
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
// we found the controller
|
||||
return path.dirname(controllerPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* reads controller base settings
|
||||
*
|
||||
* @alias getConfig
|
||||
* @returns {object}
|
||||
*/
|
||||
function getConfig() {
|
||||
let configPath;
|
||||
if (fs.existsSync(
|
||||
configPath = path.join(controllerDir, 'conf', appName + '.json')
|
||||
)) {
|
||||
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
} else if (fs.existsSync(
|
||||
configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json')
|
||||
)) {
|
||||
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
} else {
|
||||
throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
|
||||
}
|
||||
}
|
||||
appName = getAppName();
|
||||
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
|
||||
const adapter = require(path.join(controllerDir, 'lib/adapter.js'));
|
||||
|
||||
exports.controllerDir = controllerDir;
|
||||
exports.getConfig = getConfig;
|
||||
exports.Adapter = adapter;
|
||||
exports.appName = appName;
|
||||
Reference in New Issue
Block a user