2
0

fix(gateay): fix all nodes and output node

also start setColor of the gateway
This commit is contained in:
Pierre CLEMENT
2018-02-20 21:36:54 +01:00
parent 6536f0b583
commit c81fb2db33
8 changed files with 98 additions and 101 deletions

View File

@@ -4,6 +4,7 @@ import * as crypto from 'crypto';
import {GatewayServer} from "./GatewayServer"; import {GatewayServer} from "./GatewayServer";
import {GatewayMessage, GatewaySubdevice, Magnet, Motion, Switch, Weather} from "./"; import {GatewayMessage, GatewaySubdevice, Magnet, Motion, Switch, Weather} from "./";
import * as MessageData from "./GatewayMessageData"; import * as MessageData from "./GatewayMessageData";
import {Color} from "../utils/Color";
export class Gateway extends events.EventEmitter { export class Gateway extends events.EventEmitter {
static iv: Buffer = Buffer.from([0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, 0x56, 0x2e]); static iv: Buffer = Buffer.from([0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, 0x56, 0x2e]);
@@ -16,8 +17,10 @@ export class Gateway extends events.EventEmitter {
} }
get key(): string { get key(): string {
if (!this.lastToken || !this.password) return null;
var cipher = crypto.createCipheriv('aes-128-cbc', this.password, Gateway.iv); var cipher = crypto.createCipheriv('aes-128-cbc', this.password, Gateway.iv);
var key = cipher.update(this.lastToken, "ascii", "hex"); var key = cipher.update(Buffer.from(this.lastToken), "ascii", "hex");
cipher.final('hex'); cipher.final('hex');
return key; return key;
@@ -27,12 +30,13 @@ export class Gateway extends events.EventEmitter {
if (msg.data) { if (msg.data) {
if (msg.model === "gateway" && msg.sid === this.sid && msg.token) { if (msg.model === "gateway" && msg.sid === this.sid && msg.token) {
this.lastToken = msg.token; this.lastToken = msg.token;
this.setLight(100, {red: 255, green: 0, blue: 0});
} }
} }
if (msg.isGetIdListAck()) { if (msg.isGetIdListAck()) {
(<MessageData.GatewayMessageGetIdListData> msg.data).forEach((sid) => { (<MessageData.GatewayMessageGetIdListData> msg.data).forEach((sid) => {
this.sendRead({cmd: "read", sid: sid}); this.send({cmd: "read", sid: sid});
}); });
} }
@@ -62,17 +66,34 @@ export class Gateway extends events.EventEmitter {
return !!this._subdevices[sid]; return !!this._subdevices[sid];
} }
setLight() { setLight(brightness, rgb) {
this.send({
cmd: "write",
data: {
rgb: Color.toValue(rgb.red, rgb.green, rgb.blue, brightness),
sid: this.sid
}
});
} }
playSound() { playSound() {
} }
sendRead(message: any) { send(message: any) {
message.sid = message.sid || this.sid; let msg = Object.assign({}, message.payload || message);
GatewayServer.getInstance().sendToGateway(this.sid, 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.getInstance().sendToGateway(this.sid, msg);
}
} }
get subdevices(): { [sid: string]: GatewaySubdevice } { get subdevices(): { [sid: string]: GatewaySubdevice } {

View File

@@ -1,7 +1,7 @@
import { Red, NodeProperties } from "node-red"; import {Red, NodeProperties} from "node-red";
import { Constants } from "../constants"; import {Constants} from "../constants";
export default (RED:Red) => { export default (RED: Red) => {
class All { class All {
protected gateway: any; protected gateway: any;
protected onlyModels: string[]; protected onlyModels: string[];
@@ -15,40 +15,50 @@ export default (RED:Red) => {
return cleanOnlyModels; return cleanOnlyModels;
} }
constructor(props:NodeProperties) { constructor(props: NodeProperties) {
RED.nodes.createNode(<any> this, props); RED.nodes.createNode(<any> this, props);
this.gateway = RED.nodes.getNode((<any> props).gateway); this.gateway = RED.nodes.getNode((<any> props).gateway);
this.onlyModels = All.getOnlyModelsValue((<any> props).onlyModels || []); this.onlyModels = All.getOnlyModelsValue((<any> props).onlyModels || []);
this.excludedSids = (<any> props).excludedSids; this.excludedSids = (<any> props).excludedSids;
this.setMessageListener();
} }
protected setMessageListener() { protected setMessageListener() {
(<any> this).on('input', (msg) => { (<any> this).on('input', (msg) => {
if (this.gateway) { if (this.gateway) {
// Filter input // Filter input
if(msg.payload && msg.payload.model && msg.payload.sid) { if (msg.payload && msg.payload.model && msg.payload.sid) {
if(!this.isDeviceValid(msg.payload)) { if (!this.isDeviceValid(msg.payload)) {
msg = null; msg = null;
} }
(<any> this).send(msg);
} }
// Prepare for request // Prepare for request
else { else {
msg.payload = this.gateway.deviceList.filter((device) => this.isDeviceValid(device)); Object.keys(this.gateway.deviceList || {})
.filter((sid) => this.isDeviceValid(sid))
.forEach((sid) => {
let curMsg = Object.assign({}, msg);
curMsg.sid = sid;
curMsg.gateway = this.gateway;
(<any> this).send(curMsg);
});
} }
(<any> this).send(msg);
} }
}); });
} }
isDeviceValid(device) { isDeviceValid(sid) {
if((!this.onlyModels || this.onlyModels.length == 0) && (!this.excludedSids || this.excludedSids.length == 0)) { if ((!this.onlyModels || this.onlyModels.length == 0) && (!this.excludedSids || this.excludedSids.length == 0)) {
return true; return true;
} }
let device = this.gateway.deviceList[sid];
// Is excluded // Is excluded
if((this.excludedSids && this.excludedSids.length != 0) && this.excludedSids.indexOf(device.sid) >= 0) { if ((this.excludedSids && this.excludedSids.length != 0) && this.excludedSids.indexOf(sid) >= 0) {
return false; return false;
} }
if((this.onlyModels && this.onlyModels.length != 0) && this.onlyModels.indexOf(device.model) >= 0) { if ((this.onlyModels && this.onlyModels.length != 0) && this.onlyModels.indexOf(device.internalModel) >= 0) {
return true; return true;
} }

View File

@@ -12,11 +12,14 @@ export default (RED:Red, type:string) => {
this.sid = (<any> props).sid; this.sid = (<any> props).sid;
(<any> this).status({fill:"grey", shape:"ring", text:"battery - na"}); (<any> this).status({fill:"grey", shape:"ring", text:"battery - na"});
this.setMessageListener();
}
protected setMessageListener() {
if (this.gateway) { if (this.gateway) {
(<any> this).on('input', (msg) => { (<any> this).on('input', (msg) => {
let payload = msg.payload; let payload = msg.payload;
// Input from gateway // Input from gateway
if (payload.sid) { if (payload.sid) {
if (payload.sid == this.sid) { if (payload.sid == this.sid) {
@@ -25,7 +28,7 @@ export default (RED:Red, type:string) => {
fill: "green", shape: "dot", fill: "green", shape: "dot",
text: "battery - " + batteryLevel + "%" text: "battery - " + batteryLevel + "%"
}; };
if (batteryLevel < 10) { if (batteryLevel < 10) {
status.fill = "red"; status.fill = "red";
} else if (batteryLevel < 45) { } else if (batteryLevel < 45) {

View File

@@ -16,17 +16,24 @@ export interface IGatewayConfiguratorNode extends Node {
on(event: "subdevice-update", listener: (subdevice: GatewaySubdevice) => void): any; on(event: "subdevice-update", listener: (subdevice: GatewaySubdevice) => void): any;
} }
interface GatewayConfiguratorSubDevice {
name: string;
internalModel: string;
}
export default (RED: Red) => { export default (RED: Red) => {
class GatewayConfigurator { class GatewayConfigurator {
sid: string; sid: string;
key: string; key: string;
deviceList: { [sid: string]: GatewayConfiguratorSubDevice };
_gateway: Gateway; _gateway: Gateway;
constructor(props: NodeProperties) { constructor(props: NodeProperties) {
RED.nodes.createNode(<any> this, props); RED.nodes.createNode(<any> this, props);
let {sid, key} = <any> props; let {sid, key, deviceList} = <any> props;
this.sid = sid; this.sid = sid;
this.key = key; this.key = key;
this.deviceList = deviceList;
let server = GatewayServer.getInstance(); let server = GatewayServer.getInstance();
if (this.sid) { if (this.sid) {
this.setGateway(); this.setGateway();

View File

@@ -36,51 +36,6 @@ export default (RED: Red) => {
protected gatewayOffline() { protected gatewayOffline() {
(<any>this).status({fill: "red", shape: "ring", text: "offline"}); (<any>this).status({fill: "red", shape: "ring", text: "offline"});
} }
/*setGateway(gateway:LumiAqara.Gateway) {
this.gateway = gateway;
this.gateway.setPassword(this.gatewayConf.password);
(<any>this).status({fill:"blue", shape:"dot", text: "online"});
this.gateway.on('offline', () => {
this.gateway = null;
(<any>this).status({fill:"red", shape:"ring", text: "offline"});
});
this.gateway.on('subdevice', (device) => {
device.sid = device.getSid();
device.type = device.getType();
device.data = {
voltage: device.getBatteryVoltage(),
batteryLevel: device.getBatteryPercentage()
};
switch (device.type) {
case 'magnet':
device.data.status = device.isOpen() ? 'open' : 'close';
break;
case 'switch':
device.on('click', () => {
// Saaad
});
break;
case 'motion':
break;
case 'sensor':
device.data.temperature = device.getTemperature();
device.data.humidity = device.getHumidity();
device.data.pressure = device.getPressure();
break;
case 'leak':
break;
case 'cube':
break;
};
(<any>this).send({
payload: device
});
});
}*/
} }
RED.nodes.registerType(`${Constants.NODES_PREFIX}-gateway in`, <any> GatewayIn); RED.nodes.registerType(`${Constants.NODES_PREFIX}-gateway in`, <any> GatewayIn);

View File

@@ -1,44 +1,19 @@
import { Red, NodeProperties } from "node-red"; import {Red, NodeProperties} from "node-red";
import { Constants } from "../constants"; import {Constants} from "../constants";
import {Gateway} from "../../devices/Gateway";
export interface IGatewayOutNode extends Node { export default (RED: Red) => {
gatewayConf:any;
gateway: Gateway;
}
export default (RED:Red) => {
class GatewayOut { class GatewayOut {
protected gatewayConf: any;
constructor(props: NodeProperties) { constructor(props: NodeProperties) {
RED.nodes.createNode(<any> this, props); RED.nodes.createNode(<any> this, props);
this.gatewayConf = RED.nodes.getNode((<any> props).gateway); this.setMessageListener();
(<any>this).status({fill: "red", shape: "ring", text: "offline"});
if (this.gatewayConf.gateway) {
(<any>this).status({fill: "blue", shape: "dot", text: "online"});
}
this.gatewayConf.on('gateway-online', () => {
(<any>this).status({fill: "blue", shape: "dot", text: "online"});
});
this.gatewayConf.on('gateway-offline', () => {
(<any>this).status({fill: "red", shape: "ring", text: "offline"});
});
} }
protected setMessageListener() { protected setMessageListener() {
/*(<any> this).on("input", (msg) => { (<any> this).on("input", (msg) => {
if (msg.hasOwnProperty("payload") && this.gateway) { if (msg.hasOwnProperty("payload") && msg.hasOwnProperty("gateway")) {
if(msg.payload.cmd === "write" && !msg.payload.data.key && this.gateway && this.gateway.sid && this.gateway._key) { msg.gateway.gateway.send(msg);
msg.payload.data.key = this.gateway._key;
}
this.gateway._sendUnicast(JSON.stringify(msg.payload));
} }
});*/ });
} }
} }

25
src/utils/Color.ts Normal file
View File

@@ -0,0 +1,25 @@
export class Color {
static toValue(red, green, blue, brightness) {
return (brightness !== undefined ? 256 * 256 * 256 * brightness : 0) + (256 * 256 * red) + (256 * green) + blue;
}
static fromValue(rgb) {
var blue = rgb % 256;
rgb = Math.max(rgb - blue, 0);
var green = rgb % (256 * 256);
rgb = Math.max(rgb - green, 0);
green /= 256;
var red = rgb % (256 * 256 * 256);
rgb = Math.max(rgb - red, 0);
red /= 256 * 256;
var brightness = rgb / (256 * 256 * 256);
return {
brightness: brightness,
color: {red: red, green: green, blue: blue}
};
}
}

1
src/utils/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './Color';