doc(main): update documentation and flows
This commit is contained in:
113
dist/devices/gateway/Gateway.js
vendored
Normal file
113
dist/devices/gateway/Gateway.js
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const events = require("events");
|
||||
const crypto = require("crypto");
|
||||
const GatewayServer_1 = require("./GatewayServer");
|
||||
const _1 = require("./");
|
||||
const Color_1 = require("../../utils/Color");
|
||||
class Gateway extends events.EventEmitter {
|
||||
constructor(sid, ip) {
|
||||
super();
|
||||
this.sid = sid;
|
||||
this.ip = ip;
|
||||
this._subdevices = {};
|
||||
}
|
||||
set password(password) {
|
||||
this._password = password;
|
||||
}
|
||||
get key() {
|
||||
if (!this.lastToken || !this._password)
|
||||
return null;
|
||||
var cipher = crypto.createCipheriv('aes-128-cbc', this._password, Gateway.iv);
|
||||
var key = cipher.update(Buffer.from(this.lastToken), "ascii", "hex");
|
||||
cipher.final('hex');
|
||||
return key;
|
||||
}
|
||||
handleMessage(msg) {
|
||||
if (msg.data) {
|
||||
if (msg.model === "gateway" && msg.sid === this.sid && msg.token) {
|
||||
this.lastToken = msg.token;
|
||||
}
|
||||
}
|
||||
if (msg.isGetIdListAck()) {
|
||||
msg.data.forEach((sid) => {
|
||||
this.read(sid);
|
||||
});
|
||||
}
|
||||
if (msg.isReadAck() || msg.isReport()) {
|
||||
if (!this._subdevices[msg.sid]) {
|
||||
for (let SubDeviceClass of [_1.Magnet, _1.Motion, _1.Switch, _1.Weather]) {
|
||||
if (SubDeviceClass.acceptedModels.indexOf(msg.model) >= 0) {
|
||||
this._subdevices[msg.sid] = new SubDeviceClass(msg.sid, msg.model);
|
||||
this._subdevices[msg.sid].on('values-updated', (sidOrMessage) => {
|
||||
this.emit("subdevice-values-updated", sidOrMessage);
|
||||
});
|
||||
this.emit("subdevice-found", msg.sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this._subdevices[msg.sid]) {
|
||||
this._subdevices[msg.sid].handleMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
getSubdevice(sid) {
|
||||
return this._subdevices[sid] || null;
|
||||
}
|
||||
hasSubdevice(sid) {
|
||||
return !!this._subdevices[sid];
|
||||
}
|
||||
getIdList() {
|
||||
this.send({
|
||||
cmd: "get_id_list",
|
||||
});
|
||||
}
|
||||
read(sid) {
|
||||
this.send({ cmd: "read", sid: sid || this.sid });
|
||||
}
|
||||
setLight(brightness, rgb) {
|
||||
this.send({
|
||||
cmd: "write",
|
||||
data: {
|
||||
rgb: Color_1.Color.toValue(rgb.red, rgb.green, rgb.blue, brightness),
|
||||
sid: this.sid
|
||||
}
|
||||
});
|
||||
}
|
||||
playSound(musicId, volume) {
|
||||
this.send({
|
||||
cmd: "write",
|
||||
data: {
|
||||
mid: musicId,
|
||||
volume: volume,
|
||||
sid: this.sid
|
||||
}
|
||||
});
|
||||
}
|
||||
send(message) {
|
||||
let msg = Object.assign({}, message.payload || message);
|
||||
if (msg.cmd) {
|
||||
msg.sid = message.sid || this.sid;
|
||||
if (msg.gateway) {
|
||||
delete msg.gateway;
|
||||
}
|
||||
if (msg.cmd === "write") {
|
||||
msg.data.key = this.key;
|
||||
}
|
||||
GatewayServer_1.GatewayServer.getInstance().sendToGateway(this.sid, msg);
|
||||
}
|
||||
}
|
||||
get subdevices() {
|
||||
return this._subdevices;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
sid: this.sid,
|
||||
ip: this.ip,
|
||||
key: this.password,
|
||||
subdevices: this.subdevices
|
||||
};
|
||||
}
|
||||
}
|
||||
Gateway.iv = Buffer.from([0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, 0x56, 0x2e]);
|
||||
exports.Gateway = Gateway;
|
||||
30
dist/devices/gateway/GatewayMessage.js
vendored
Normal file
30
dist/devices/gateway/GatewayMessage.js
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class GatewayMessage {
|
||||
constructor(raw) {
|
||||
Object.assign(this, raw);
|
||||
if (raw.port) {
|
||||
this.port = parseInt(raw.port);
|
||||
}
|
||||
if (raw.data) {
|
||||
this.data = JSON.parse(raw.data) || raw.data;
|
||||
}
|
||||
this.timestamp = +new Date;
|
||||
}
|
||||
isHeartbeat() {
|
||||
return this.cmd === "heartbeat";
|
||||
}
|
||||
isIam() {
|
||||
return this.cmd === "iam";
|
||||
}
|
||||
isGetIdListAck() {
|
||||
return this.cmd === "get_id_list_ack";
|
||||
}
|
||||
isReadAck() {
|
||||
return this.cmd === "read_ack";
|
||||
}
|
||||
isReport() {
|
||||
return this.cmd === "report";
|
||||
}
|
||||
}
|
||||
exports.GatewayMessage = GatewayMessage;
|
||||
2
dist/devices/gateway/GatewayMessageData.js
vendored
Normal file
2
dist/devices/gateway/GatewayMessageData.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
121
dist/devices/gateway/GatewayServer.js
vendored
Normal file
121
dist/devices/gateway/GatewayServer.js
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const events = require("events");
|
||||
const dgram = require("dgram");
|
||||
const _1 = require("./");
|
||||
const GatewayMessage_1 = require("./GatewayMessage");
|
||||
class GatewayServer extends events.EventEmitter {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._gateways = {};
|
||||
this._gatewaysPing = {};
|
||||
}
|
||||
static getInstance() {
|
||||
if (!this.instance) {
|
||||
this.instance = new GatewayServer();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
discover(ipv = 4) {
|
||||
if (this.server) {
|
||||
return;
|
||||
}
|
||||
this.server = dgram.createSocket({
|
||||
type: `udp${ipv}`,
|
||||
reuseAddr: true
|
||||
});
|
||||
this.server.on('listening', () => {
|
||||
var address = this.server.address();
|
||||
//this.log(RED._("udp.status.listener-at",{host:address.address,port:address.port}));
|
||||
this.server.setBroadcast(true);
|
||||
try {
|
||||
this.server.setMulticastTTL(128);
|
||||
this.server.addMembership(GatewayServer.MULTICAST_ADDRESS, null);
|
||||
}
|
||||
catch (e) {
|
||||
/*if (e.errno == "EINVAL") {
|
||||
this.error(RED._("udp.errors.bad-mcaddress"));
|
||||
} else if (e.errno == "ENODEV") {
|
||||
this.error(RED._("udp.errors.interface"));
|
||||
} else {
|
||||
this.error(RED._("udp.errors.error",{error:e.errno}));
|
||||
}*/
|
||||
}
|
||||
});
|
||||
this.server.on("error", (err) => {
|
||||
/*if ((err.code == "EACCES") && (this.port < 1024)) {
|
||||
this.error(RED._("udp.errors.access-error"));
|
||||
} else {
|
||||
this.error(RED._("udp.errors.error",{error:err.code}));
|
||||
}*/
|
||||
this.server.close();
|
||||
delete this.server;
|
||||
});
|
||||
this.server.on('message', (message, remote) => {
|
||||
let msg = new GatewayMessage_1.GatewayMessage(JSON.parse(message.toString('utf8')));
|
||||
//console.log(msg);
|
||||
let gatewaySid = null;
|
||||
if ((msg.isHeartbeat() || msg.isIam()) && msg.model === "gateway") {
|
||||
if (!this._gateways[msg.sid]) {
|
||||
this._gateways[msg.sid] = new _1.Gateway(msg.sid, remote.address);
|
||||
this._gateways[msg.sid].getIdList();
|
||||
this.emit("gateway-online", msg.sid);
|
||||
}
|
||||
else {
|
||||
// Any IP update?
|
||||
this._gateways[msg.sid].ip = remote.address;
|
||||
}
|
||||
if (this._gatewaysPing[msg.sid]) {
|
||||
clearTimeout(this._gatewaysPing[msg.sid]);
|
||||
delete this._gatewaysPing[msg.sid];
|
||||
}
|
||||
// Consider the gateway as unreachable after 2 heartbeats missed (1 heartbeat every 10s)
|
||||
this._gatewaysPing[msg.sid] = setTimeout(() => {
|
||||
this.emit("gateway-offline", msg.sid);
|
||||
delete this._gateways[msg.sid];
|
||||
}, 25 * 1000);
|
||||
gatewaySid = msg.sid;
|
||||
}
|
||||
if (!gatewaySid) {
|
||||
gatewaySid = Object.keys(this._gateways).filter((gatewaySid) => this._gateways[gatewaySid].ip === remote.address)[0];
|
||||
}
|
||||
gatewaySid && this._gateways[gatewaySid] && this._gateways[gatewaySid].handleMessage(msg);
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this.server.bind(GatewayServer.SERVER_PORT, null);
|
||||
let msg = Buffer.from(JSON.stringify({ cmd: "whois" }));
|
||||
this.server.send(msg, 0, msg.length, GatewayServer.MULTICAST_PORT, GatewayServer.MULTICAST_ADDRESS);
|
||||
resolve(this.server);
|
||||
}
|
||||
catch (e) {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
stop() {
|
||||
if (this.server) {
|
||||
this.server.close();
|
||||
delete this.server;
|
||||
}
|
||||
}
|
||||
getGateway(sid) {
|
||||
return this._gateways[sid] || null;
|
||||
}
|
||||
hasGateway(sid) {
|
||||
return !!this._gateways[sid];
|
||||
}
|
||||
get gateways() {
|
||||
return this._gateways;
|
||||
}
|
||||
sendToGateway(sid, message) {
|
||||
if (this.server && this._gateways[sid]) {
|
||||
let msg = Buffer.from(JSON.stringify(message));
|
||||
this.server.send(msg, 0, msg.length, GatewayServer.SERVER_PORT, this._gateways[sid].ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
GatewayServer.MULTICAST_ADDRESS = '224.0.0.50';
|
||||
GatewayServer.MULTICAST_PORT = 4321;
|
||||
GatewayServer.SERVER_PORT = 9898;
|
||||
exports.GatewayServer = GatewayServer;
|
||||
40
dist/devices/gateway/GatewaySubdevice.js
vendored
Normal file
40
dist/devices/gateway/GatewaySubdevice.js
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const events = require("events");
|
||||
class GatewaySubdevice extends events.EventEmitter {
|
||||
constructor(sid, model) {
|
||||
super();
|
||||
this.sid = sid;
|
||||
this.model = model;
|
||||
}
|
||||
get batteryLevel() {
|
||||
/*
|
||||
When full, CR2032 batteries are between 3 and 3.4V
|
||||
http://farnell.com/datasheets/1496885.pdf
|
||||
*/
|
||||
return this.voltage ? Math.min(Math.round((this.voltage - 2200) / 10), 100) : -1;
|
||||
}
|
||||
handleMessage(msg) {
|
||||
if (msg.data.voltage) {
|
||||
this.voltage = msg.data.voltage;
|
||||
}
|
||||
this.message = msg;
|
||||
}
|
||||
static get acceptedModels() {
|
||||
return [];
|
||||
}
|
||||
;
|
||||
toJSON() {
|
||||
let json = {};
|
||||
for (let prop of Object.keys(this)) {
|
||||
json[prop] = this[prop];
|
||||
}
|
||||
delete json._events;
|
||||
delete json._eventsCount;
|
||||
delete json._maxListeners;
|
||||
json.batteryLevel = this.batteryLevel;
|
||||
json.internalModel = this.internalModel;
|
||||
return json;
|
||||
}
|
||||
}
|
||||
exports.GatewaySubdevice = GatewaySubdevice;
|
||||
32
dist/devices/gateway/Magnet.js
vendored
Normal file
32
dist/devices/gateway/Magnet.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const GatewaySubdevice_1 = require("./GatewaySubdevice");
|
||||
class Magnet extends GatewaySubdevice_1.GatewaySubdevice {
|
||||
static get acceptedModels() {
|
||||
return ['magnet', 'sensor_magnet.aq2'];
|
||||
}
|
||||
get internalModel() {
|
||||
return 'mi.magnet';
|
||||
}
|
||||
isClosed() {
|
||||
return this.status === "close";
|
||||
}
|
||||
isOpened() {
|
||||
return this.status === "open";
|
||||
}
|
||||
isUnkownState() {
|
||||
return this.status === "unkown";
|
||||
}
|
||||
handleMessage(msg) {
|
||||
super.handleMessage(msg);
|
||||
if (msg.isReadAck() || msg.isReport()) {
|
||||
let data = msg.data;
|
||||
// mintime
|
||||
if (this.status !== data.status) {
|
||||
this.status = data.status;
|
||||
this.emit('values-updated', this.sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Magnet = Magnet;
|
||||
29
dist/devices/gateway/Motion.js
vendored
Normal file
29
dist/devices/gateway/Motion.js
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const GatewaySubdevice_1 = require("./GatewaySubdevice");
|
||||
class Motion extends GatewaySubdevice_1.GatewaySubdevice {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.lux = 0;
|
||||
}
|
||||
static get acceptedModels() {
|
||||
return ['motion', 'sensor_motion.aq2'];
|
||||
}
|
||||
get internalModel() {
|
||||
return 'mi.motion';
|
||||
}
|
||||
handleMessage(msg) {
|
||||
super.handleMessage(msg);
|
||||
if (msg.isReadAck() || msg.isReport()) {
|
||||
let data = msg.data;
|
||||
if (data.lux) {
|
||||
this.lux = parseInt(data.lux);
|
||||
}
|
||||
if (data.status === "motion") {
|
||||
this.lastMotionTimestamp = data.timestamp;
|
||||
this.emit('values-updated', { sid: this.sid, data: { hasMotion: true } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Motion = Motion;
|
||||
15
dist/devices/gateway/Switch.js
vendored
Normal file
15
dist/devices/gateway/Switch.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const GatewaySubdevice_1 = require("./GatewaySubdevice");
|
||||
class Switch extends GatewaySubdevice_1.GatewaySubdevice {
|
||||
static get acceptedModels() {
|
||||
return ['switch', 'sensor_switch.aq2'];
|
||||
}
|
||||
get internalModel() {
|
||||
return 'mi.switch';
|
||||
}
|
||||
handleMessage(msg) {
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
exports.Switch = Switch;
|
||||
36
dist/devices/gateway/Weather.js
vendored
Normal file
36
dist/devices/gateway/Weather.js
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const GatewaySubdevice_1 = require("./GatewaySubdevice");
|
||||
class Weather extends GatewaySubdevice_1.GatewaySubdevice {
|
||||
static get acceptedModels() {
|
||||
return ['sensor_ht', 'weather.v1'];
|
||||
}
|
||||
get internalModel() {
|
||||
return 'mi.weather';
|
||||
}
|
||||
get temperatureInDegrees() {
|
||||
return this.temperature / 100;
|
||||
}
|
||||
get humidityInPercent() {
|
||||
return this.humidity / 100;
|
||||
}
|
||||
get pressureInBar() {
|
||||
return this.pressure / 100000;
|
||||
}
|
||||
get pressureInhPa() {
|
||||
return this.pressure / 100;
|
||||
}
|
||||
handleMessage(msg) {
|
||||
super.handleMessage(msg);
|
||||
if (msg.isReadAck() || msg.isReport()) {
|
||||
let data = msg.data;
|
||||
['temperature', 'humidity', 'pressure'].forEach((dataType) => {
|
||||
if (data[dataType]) {
|
||||
this[dataType] = parseInt(data[dataType]);
|
||||
}
|
||||
});
|
||||
this.emit('values-updated', this.sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Weather = Weather;
|
||||
14
dist/devices/gateway/index.js
vendored
Normal file
14
dist/devices/gateway/index.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
function __export(m) {
|
||||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
||||
}
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__export(require("./"));
|
||||
__export(require("./Gateway"));
|
||||
__export(require("./GatewayMessage"));
|
||||
__export(require("./GatewayServer"));
|
||||
__export(require("./GatewaySubdevice"));
|
||||
__export(require("./Magnet"));
|
||||
__export(require("./Motion"));
|
||||
__export(require("./Switch"));
|
||||
__export(require("./Weather"));
|
||||
45
dist/devices/yeelight/YeelightServer.js
vendored
Normal file
45
dist/devices/yeelight/YeelightServer.js
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const events = require("events");
|
||||
const YeelightSearch = require("yeelight-wifi");
|
||||
class YeelightServer extends events.EventEmitter {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._bulbs = {};
|
||||
this._bulbsJson = {};
|
||||
}
|
||||
static getInstance() {
|
||||
if (!this.instance) {
|
||||
this.instance = new YeelightServer();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
get bulbs() {
|
||||
return this._bulbsJson;
|
||||
}
|
||||
getBulb(sid) {
|
||||
return this._bulbs[sid];
|
||||
}
|
||||
discover() {
|
||||
new Promise(() => {
|
||||
(new YeelightSearch()).on('found', (bulb) => {
|
||||
bulb.sid = parseInt(bulb.id);
|
||||
if (!this._bulbs[bulb.sid]) {
|
||||
this._bulbs[bulb.sid] = bulb;
|
||||
this._bulbsJson[bulb.sid] = YeelightServer.bulbToJSON(bulb);
|
||||
this.emit("yeelight-online", bulb.sid);
|
||||
}
|
||||
});
|
||||
});
|
||||
// TODO: disconected ?
|
||||
}
|
||||
static bulbToJSON(bulb) {
|
||||
return {
|
||||
sid: bulb.sid,
|
||||
ip: bulb.hostname,
|
||||
name: bulb.name,
|
||||
model: bulb.model
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.YeelightServer = YeelightServer;
|
||||
0
dist/devices/yeelight/index.js
vendored
Normal file
0
dist/devices/yeelight/index.js
vendored
Normal file
Reference in New Issue
Block a user