2
0

doc(main): update documentation and flows

This commit is contained in:
Pierre CLEMENT
2018-03-21 23:29:36 +01:00
parent c850ca9536
commit d04f5c1196
65 changed files with 2990 additions and 4 deletions

46
dist/nodes/gateway/Gateway.js vendored Normal file
View File

@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("../constants");
exports.default = (RED) => {
class Gateway {
constructor(props) {
RED.nodes.createNode(this, props);
this.gatewayConf = RED.nodes.getNode(props.gateway);
this.status({ fill: "red", shape: "ring", text: "offline" });
if (this.gatewayConf.gateway) {
this.gatewayOnline();
}
this.gatewayConf.on('gateway-online', () => this.gatewayOnline());
this.gatewayConf.on('gateway-offline', () => this.gatewayOffline());
this.setMessageListener();
}
gatewayOnline() {
this.status({ fill: "blue", shape: "dot", text: "online" });
}
gatewayOffline() {
this.status({ fill: "red", shape: "ring", text: "offline" });
}
setMessageListener() {
this.on('input', (msg) => {
if (this.gatewayConf.gateway) {
var payload = msg.payload;
// Input from gateway
if (payload.sid && payload.sid == this.gatewayConf.gateway.sid) {
if (payload.data.rgb) {
/*var decomposed = miDevicesUtils.computeColor(payload.data.rgb);
payload.data.brightness = decomposed.brightness;
payload.data.color = decomposed.color;*/
}
this.send(msg);
}
else {
msg.sid = this.gatewayConf.gateway.sid;
msg.gateway = this.gatewayConf.gateway;
this.send(msg);
}
}
});
}
}
RED.nodes.registerType(`${constants_1.Constants.NODES_PREFIX}-gateway`, Gateway);
};

View File

@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("../constants");
const GatewayServer_1 = require("../../devices/gateway/GatewayServer");
exports.default = (RED) => {
class GatewayConfigurator {
constructor(props) {
RED.nodes.createNode(this, props);
let { sid, key, deviceList } = props;
this.sid = sid;
this.key = key;
this.deviceList = deviceList;
let server = GatewayServer_1.GatewayServer.getInstance();
if (this.sid) {
this.setGateway();
}
server.on('gateway-online', (sid) => {
if (sid === this.sid) {
this.setGateway();
this.emit('gateway-online');
}
});
server.on('gateway-offline', (sid) => {
if (sid === this.sid) {
this._gateway = null;
this.emit('gateway-offline');
}
});
}
setGateway() {
this._gateway = GatewayServer_1.GatewayServer.getInstance().getGateway(this.sid);
if (this._gateway) {
this._gateway.password = this.key;
this._gateway.on("subdevice-values-updated", (sidOrMessage) => {
let sid = sidOrMessage.sid || sidOrMessage;
let subdevice = this._gateway.getSubdevice(sid);
if (subdevice) {
(sidOrMessage.data ? Object.keys(sidOrMessage.data) : []).forEach((key) => {
subdevice[key] = sidOrMessage.data[key];
});
this.emit('subdevice-update', subdevice);
}
});
}
}
get gateway() {
return this._gateway;
}
}
RED.nodes.registerType(`${constants_1.Constants.NODES_PREFIX}-gateway configurator`, GatewayConfigurator, {
settings: {
miDevicesGatewayConfiguratorDiscoveredGateways: {
value: GatewayServer_1.GatewayServer.getInstance().gateways,
exportable: true
}
}
});
};

27
dist/nodes/gateway/GatewayIn.js vendored Normal file
View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("../constants");
exports.default = (RED) => {
class GatewayIn {
constructor(props) {
RED.nodes.createNode(this, props);
this.gatewayConf = RED.nodes.getNode(props.gateway);
this.status({ fill: "red", shape: "ring", text: "offline" });
if (this.gatewayConf.gateway) {
this.gatewayOnline();
}
this.gatewayConf.on('gateway-online', () => this.gatewayOnline());
this.gatewayConf.on('gateway-offline', () => this.gatewayOffline());
}
gatewayOnline() {
this.status({ fill: "blue", shape: "dot", text: "online" });
this.gatewayConf.on('subdevice-update', (subdevice) => {
this.send({ payload: subdevice });
});
}
gatewayOffline() {
this.status({ fill: "red", shape: "ring", text: "offline" });
}
}
RED.nodes.registerType(`${constants_1.Constants.NODES_PREFIX}-gateway in`, GatewayIn);
};

35
dist/nodes/gateway/GatewayOut.js vendored Normal file
View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("../constants");
const GatewayServer_1 = require("../../devices/gateway/GatewayServer");
exports.default = (RED) => {
class GatewayOut {
constructor(props) {
RED.nodes.createNode(this, props);
this.setMessageListener();
}
setMessageListener() {
this.on("input", (msg) => {
if (msg.hasOwnProperty("payload") && msg.hasOwnProperty("gateway")) {
let gateway = GatewayServer_1.GatewayServer.getInstance().getGateway(msg.gateway.sid);
if (gateway) {
if (msg.payload.cmd) {
gateway.send(msg);
}
else if (msg.payload.action) {
switch (msg.payload.action) {
case 'setLight':
gateway.setLight(msg.payload.brightness, msg.payload.color);
break;
case 'playSound':
gateway.playSound(msg.payload.mid, msg.payload.volume);
break;
}
}
}
}
});
}
}
RED.nodes.registerType(`${constants_1.Constants.NODES_PREFIX}-gateway out`, GatewayOut);
};

BIN
dist/nodes/gateway/icons/mijia-io.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
dist/nodes/gateway/icons/mijia.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

295
dist/nodes/gateway/index.html vendored Normal file
View File

@@ -0,0 +1,295 @@
<script type="text/javascript">
RED.nodes.registerType('mi-devices-gateway configurator', {
category: 'config',
defaults: {
name: {value: ""},
sid: {value: ""},
key: { value: "" },
deviceList: {value:{}}
},
paletteLabel: "gateway configurator",
label: function () {
return this.name || "gateway configurator";
},
oneditprepare: function() {
var foundGateways = RED.settings.miDevicesGatewayConfiguratorDiscoveredGateways;
Object.keys(foundGateways).forEach(function(sid) {
var gateway = foundGateways[sid];
$('#discovered-gateways').append('<option value="' + gateway.sid + '">' + gateway.sid + ' - ' + gateway.ip + '</option>');
});
var node = this;
var devicesConfig = {
"mi.weather": {label:"weather", icon:"icons/node-red-contrib-mi-devices/thermometer-icon.png"},
"mi.magnet": {label:"magnet", icon:"icons/node-red-contrib-mi-devices/door-icon.png"},
"mi.motion": {label:"motion", icon:"icons/node-red-contrib-mi-devices/motion-icon.png"},
"mi.switch": {label:"switch", icon:"icons/node-red-contrib-mi-devices/mi-switch.png"},
"mi.plug": {label:"plug zigbee", icon:"icons/node-red-contrib-mi-devices/outlet-icon.png"}
};
$("#node-config-input-subdevices").css('min-height','250px').css('min-width','450px').editableList({
addItem: function(container, i, device) {
var row = container;
$('<label/>',{for:"node-config-input-sid-"+i, style:"margin-left: 3px; width: 15px;vertical-align:middle"}).appendTo(row);
var sid = $('<input/>',{id:"node-config-input-sid-"+i,type:"text", placeholder:"SID", style:"width:auto;vertical-align:top"}).appendTo(row);
sid.typedInput({
default: 'mi.weather',
types: Object.keys(devicesConfig).map(function(type) {
var cleanType = devicesConfig[type];
cleanType.value = type;
return cleanType;
})
});
$('<label/>',{for:"node-config-input-desc-"+i, style:"margin-left: 7px; width: 20px;vertical-align:middle"}).html('<i class="fa fa-pencil-square-o"></i>').appendTo(row);
var desc = $('<input/>',{id:"node-config-input-desc-"+i, type:"text", placeholder:"name", style:"width:auto;vertical-align:top"}).appendTo(row);
sid.typedInput('value', device.sid);
sid.typedInput('type', device.internalModel);
desc.val(device.name);
container.parent().attr("data-sid", device.sid);
},
removeItem: function(device) {
$("#node-config-input-subdevices").find("[data-sid=" + device.sid + "]").remove();
},
sortable: false,
removable: true
});
$('#discovered-gateways').on('change', function() {
var sid = $('#discovered-gateways').val();
$('#input-subdevices > *').remove();
var gateway = sid && RED.settings.miDevicesGatewayConfiguratorDiscoveredGateways[sid];
$("#node-config-input-sid").val(gateway && gateway.sid);
$("#node-config-input-key").val(gateway && gateway.key);
$("#node-config-input-subdevices").editableList('items').each(function(i, elt) {
$("#node-config-input-subdevices").editableList('removeItem', {sid: $(elt).find("#node-config-input-sid-"+i).val()});
});
var subdevices = gateway && Object.keys(gateway.subdevices).map(function(sid) { return gateway.subdevices[sid]; });
subdevices && subdevices.sort(function(a, b) { return a.internalModel > b.internalModel; }).forEach(function(device) {
if(!devicesConfig[device.internalModel] || !device.sid) {
return;
}
if(node.deviceList[device.sid]) {
device.name = node.deviceList[device.sid].name;
}
$("#node-config-input-subdevices").editableList('addItem', {
sid: device.sid,
internalModel: device.internalModel,
name: device.name
});
});
var listHeight = $("#node-config-input-subdevices").editableList('items').size() * 51 + 50;
$("#node-config-input-subdevices").editableList('height', listHeight);
});
Object.keys(this.deviceList || {}).forEach(function(sid) {
var device = node.deviceList[sid];
$("#node-config-input-subdevices").editableList('addItem', {
sid: sid,
internalModel: device.internalModel,
name: device.name
});
});
var listHeight = $("#node-config-input-subdevices").editableList('items').size() * 51 + 50;
$("#node-config-input-subdevices").editableList('height', listHeight);
},
oneditsave: function() {
var node = this;
var devices = $("#node-config-input-subdevices").editableList('items');
devices.each(function(i, elt) {
var deviceElement = $(elt);
var sid = deviceElement.find("#node-config-input-sid-"+i).val();
var desc = deviceElement.find("#node-config-input-desc-"+i).val();
var internalModel = deviceElement.find("#node-config-input-sid-"+i).typedInput('type');
node.deviceList[sid] = {internalModel: internalModel, name: desc};
});
}
});
</script>
<script type="text/x-red" data-template-name="mi-devices-gateway configurator">
<div class="form-row">
<label for="discovered-gateways"><i class="fa fa-search"></i> Found gateways</label>
<select id="discovered-gateways">
<option>- Select -</option>
</select>
</div>
<hr>
<h4>Gateway</h4>
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-config-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-config-input-sid"><i class="fa fa-barcode"></i> SID</label>
<input type="text" id="node-config-input-sid" placeholder="sid">
</div>
<div class="form-row">
<label for="node-input-gatewayKey"><i class="fa fa-key"></i> Key</label>
<input type="text" id="node-config-input-key" placeholder="Key">
</div>
<h4>Devices</h4>
<div class="form-row node-config-input-subdevices">
<ol id="node-config-input-subdevices"></ol>
</div>
</script>
<script type="text/x-red" data-help-name="mi-devices-gateway configurator">
<p>Gateway configuration for Xiaomi nodes.</p>
<h3>Details</h3>
<p>This configuration node is used by the Xiaomi device nodes. Here you can add
devices with their device-id (SID), type and a description.</p>
<p>At the moment the following devices are supported:
<lu>
<li>Humidity & Temperature sensor [sensor ht/]</li>
<li>Body motion sensor [motion]</li>
<li>Magnet contact sensor [contact]</li>
<li>Wall socket plug (zigbee) [plug]</li>
<li>Push button [switch]</li>
</lu>
</p>
<p>To be able to receive messages from the Xiaomi gateway, you need to set the gateway
in developer mode. Once in developer mode, the gateway sends JSON messages over the network as
UDP packages. On the internet their are a lot of guides on how to put the gateway in developer mode.</p>
<p>If you want to use the wall sockets, you need to set the key from the gateway. The key can be
retrieved via the Xiaomi Home App when in developer mode. Enter the key here and it is used
together with the token from the gateway's heartbeat message to recalculate the key to switch
the plug. If you do not specify a key, the plug-node can not be used.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mi-devices-gateway', {
category: 'xiaomi',
color: '#3FADB5',
defaults: {
gateway: {value:"", type:"mi-devices-gateway configurator"},
name: {value: ""}
},
inputs: 1,
outputs: 1,
outputLabels: ["Gateway"],
paletteLabel: "gateway",
icon: "mijia.png",
label: function () {
return this.name || "gateway";
}
});
</script>
<script type="text/x-red" data-template-name="mi-devices-gateway">
<div class="form-row">
<label for="node-input-gateway"><i class="fa fa-wrench"></i> Gateway</label>
<input type="text" id="node-input-gateway" placeholder="xiaomi gateway">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mi-devices-gateway">
<p>The gateway itself.</p>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Devices output
<dl class="message-properties">
<dt>gateway <span class="property-type">mi-devices-gateway configurator</span></dt>
<dd>The gateway.</dd>
</dl>
<dl class="message-properties">
<dt>payload <span class="property-type">json</span></dt>
<dd>Data from gateway, with computed data.</dd>
</dl>
</li>
</ol>
</script>
<script type="text/x-red" data-template-name="mi-devices-gateway in">
<div class="form-row">
<label for="node-input-gateway"><i class="fa fa-wrench"></i> Gateway</label>
<input type="text" id="node-input-gateway" placeholder="xiaomi gateway">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mi-devices-gateway in">
<p>A Xiaomi Gateway input node, that produces a <code>msg.payload</code> containing a
string with the gateway message content.
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mi-devices-gateway in',{
category: 'xiaomi in out',
color: '#087F8A',
defaults: {
name: {value:""},
gateway: {value:"", type:"mi-devices-gateway configurator"}
},
inputs:0,
outputs:1,
paletteLabel: "gateway in",
icon: "mijia-io.png",
label: function() {
return this.name||"gateway in";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
function changeGateway() {
var configNodeID = $('#node-input-gateway').val();
if (configNodeID) {
var configNode = RED.nodes.node(configNodeID);
if(configNode) {
if(!this.name) {
$("#node-input-name").val(configNode.name);
}
$('#node-input-ip').val(configNode.ip);
}
}
}
$("#node-input-gateway").change(function () {
changeGateway();
});
}
});
</script>
<script type="text/x-red" data-template-name="mi-devices-gateway out">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mi-devices-gateway out">
<p>This node sends <code>msg.payload</code> to <code>msg.gateway</code> Xiaomi Gateway.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mi-devices-gateway out',{
category: 'xiaomi in out',
color: '#087F8A',
defaults: {
name: {value:""}
},
inputs:1,
outputs:0,
paletteLabel: "gateway out",
icon: "mijia-io.png",
align: "right",
label: function() {
return this.name||"gateway out";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

13
dist/nodes/gateway/index.js vendored Normal file
View File

@@ -0,0 +1,13 @@
"use strict";
const GatewayServer_1 = require("../../devices/gateway/GatewayServer");
const GatewayConfigurator_1 = require("./GatewayConfigurator");
const Gateway_1 = require("./Gateway");
const GatewayIn_1 = require("./GatewayIn");
const GatewayOut_1 = require("./GatewayOut");
module.exports = (RED) => {
GatewayServer_1.GatewayServer.getInstance().discover();
GatewayConfigurator_1.default(RED);
Gateway_1.default(RED);
GatewayIn_1.default(RED);
GatewayOut_1.default(RED);
};