2
0

feat(gateway): addset light and play sounds

This commit is contained in:
Pierre CLEMENT
2018-02-21 22:01:31 +01:00
parent 7bff50da16
commit b6e311965a
18 changed files with 280 additions and 667 deletions

View File

@@ -1,204 +1,3 @@
<!-- The Gateway light Node -->
<script type="text/javascript">
RED.nodes.registerType('mi-devices-actions gateway_light', {
category: 'xiaomi actions',
color: '#64C4CD',
defaults: {
name: {value: ""},
brightness: {value: 100},
hexRgbColor: {value: "#ffffff"},
color: {value:{red: 255, green: 255, blue: 255}}
},
inputs: 1,
outputs: 1,
paletteLabel: "set light",
icon: "mi-bulb.png",
label: function () {
return this.name || "set light";
},
oneditsave: function() {
var hexRgbColor = $("#node-input-hexRgbColor").val();
var split = hexRgbColor.slice(1).match(/.{1,2}/g).map(function(hexColor) {
return parseInt(hexColor, 16);
});
this.color = {
red: split[0],
green: split[1],
blue: split[2]
};
}
});
</script>
<script type="text/x-red" data-template-name="mi-devices-actions gateway_light">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-brightness"><i class="icon-tag"></i> Brightness</label>
<input type="range" id="node-input-brightness" min="0" max="100">
</div>
<div class="form-row">
<label for="node-input-hexRgbColor"><i class="icon-tag"></i> Color</label>
<input type="color" id="node-input-hexRgbColor">
</div>
</script>
<script type="text/x-red" data-help-name="mi-devices-actions gateway_light">
<p>Change the light of the gateway.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>brightness
<span class="property-type">number</span>
</dt>
<dd>The brightness value between <code>0</code> and <code>100</code>.</dd>
<dt>color
<span class="property-type">object</span>
</dt>
<dd>The color itself. This object must contain the followinf properties:
<ul>
<li>
<code>red</code> - amout of red, between <code>0</code> and <code>255</code>
</li>
<li>
<code>green</code> - amout of green, between <code>0</code> and <code>255</code>
</li>
<li>
<code>blue</code> - amout of blue, between <code>0</code> and <code>255</code>
</li>
</ul>
</dd>
</dl>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Message to connect to a gateway out node.</li>
</ol>
</script>
<!-- The Gateway sound Node -->
<script type="text/javascript">
RED.nodes.registerType('mi-devices-actions gateway_sound', {
category: 'xiaomi actions',
color: '#64C4CD',
defaults: {
name: {value: ""},
mid: {value: ""},
volume: {value: ""}
},
inputs: 1,
outputs: 1,
paletteLabel: "play sound",
icon: "mi-sound.png",
label: function () {
return this.name || "play sound";
}
});
</script>
<script type="text/x-red" data-template-name="mi-devices-actions gateway_sound">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-mid"><i class="icon-tag"></i> Sound</label>
<select id="node-input-mid">
<option value="0">Police car tone 1</option>
<option value="1">Police car tone 2</option>
<option value="2">Safety incident sound</option>
<option value="3">Missile countdown</option>
<option value="4">Ghost cry</option>
<option value="5">Sniper rifle</option>
<option value="6">Battle sound</option>
<option value="7">Air raid alarm</option>
<option value="8">Barking</option>
<option value="10">Doorbell tone</option>
<option value="11">Door knocking tone</option>
<option value="12">Funny tone</option>
<option value="13">Alarm clock tone</option>
<option value="20">MiMix</option>
<option value="21">Enthusisatic</option>
<option value="22">GuitarClassic</option>
<option value="23">IceWorldPiano</option>
<option value="24">LeisureTime</option>
<option value="25">ChildHood</option>
<option value="26">MorningStreamLet</option>
<option value="27">MusicBox</option>
<option value="28">Orange</option>
<option value="29">Thinker</option>
</select>
</div>
<div class="form-row">
<label for="node-input-volume"><i class="icon-tag"></i> Volume</label>
<input type="range" id="node-input-volume" min="0" max="100">
</div>
</script>
<script type="text/x-red" data-help-name="mi-devices-actions gateway_sound">
<p>Play a sound on the gateway.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>mid
<span class="property-type">number</span>
</dt>
<dd>Music ID (user define sounds start from <code>1001</code>, <code>1000</code> means stop).</dd>
<dt>volume
<span class="property-type">number</span>
</dt>
<dd>Playing volume, between <code>0</code> and <code>100</code>.</dd>
</dl>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Message to connect to a gateway out node.</li>
</ol>
</script>
<!-- The Gateway stop sound Node -->
<script type="text/javascript">
RED.nodes.registerType('mi-devices-actions gateway_stop_sound',{
category: 'xiaomi actions',
color: '#64C4CD',
defaults: {
name: {value:""}
},
inputs:1,
outputs:1,
paletteLabel: "stop sound",
icon: "mi-mute.png",
label: function() {
return this.name||"stop sound";
}
});
</script>
<script type="text/x-red" data-template-name="mi-devices-actions gateway_stop_sound">
<div class="form-row">
<label for="node-input-name"><i class="icon-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-actions gateway_stop_sound">
<p>
Stop current playing sound on the gateway.
</p>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Message to connect to a gateway out node.</li>
</ol>
</script>
<!-- The "on" Node -->
<script type="text/javascript">
RED.nodes.registerType('mi-devices-actions on',{

View File

@@ -1,133 +1,5 @@
const miDevicesUtils = require('../src/utils');
module.exports = (RED) => {
/*********************************************
Read data from Gateway
*********************************************/
function XiaomiActionRead(config) {
RED.nodes.createNode(this, config);
this.on('input', (msg) => {
if(msg.sid) {
msg.payload = { cmd: "read", sid: msg.sid };
this.send(msg);
}
});
}
RED.nodes.registerType("mi-devices-actions read", XiaomiActionRead);
/*********************************************
Get registred ids of devices on gateway
*********************************************/
function XiaomiActionGetIdList(config) {
RED.nodes.createNode(this, config);
this.on('input', (msg) => {
msg.payload = { cmd: "get_id_list" };
node.send(msg);
});
}
RED.nodes.registerType("mi-devices-actions get_id_list", XiaomiActionGetIdList);
/*********************************************
Virtual single click on a button
*********************************************/
function XiaomiActionSingleClick(config) {
RED.nodes.createNode(this, config);
this.on('input', (msg) => {
msg.payload = {
cmd: "write",
data: { status: "click", sid: msg.sid }
};
this.send(msg);
});
}
RED.nodes.registerType("mi-devices-actions click", XiaomiActionSingleClick);
/*********************************************
Virtual Double click on a button
*********************************************/
function XiaomiActionDoubleClick(config) {
RED.nodes.createNode(this, config);
this.on('input', (msg) => {
msg.payload = {
cmd: "write",
data: { status: "double_click", sid: msg.sid }
};
this.send(msg);
});
}
RED.nodes.registerType("mi-devices-actions double_click", XiaomiActionDoubleClick);
/*********************************************
Set the gateway light
*********************************************/
function XiaomiActionGatewayLight(config) {
RED.nodes.createNode(this, config);
this.color = config.color;
this.brightness = config.brightness;
this.on('input', (msg) => {
let color = msg.color || this.color;
let brightness = msg.brightness || this.brightness;
if(msg.sid) {
let rgb = miDevicesUtils.computeColorValue(color.red, color.green, color.blue, brightness);
msg.payload = {
cmd: "write",
data: { rgb: rgb, sid: msg.sid }
};
}
else {
msg.payload = {
color: miDevicesUtils.computeColorValue(color.red, color.green, color.blue),
brightness: brightness
};
}
this.send(msg);
});
}
RED.nodes.registerType("mi-devices-actions gateway_light", XiaomiActionGatewayLight);
/*********************************************
Play a sound on the gateway
*********************************************/
function XiaomiActionGatewaySound(config) {
RED.nodes.createNode(this, config);
this.mid = config.mid;
this.volume = config.volume;
this.on('input', (msg) => {
msg.payload = {
cmd: "write",
data: {
mid: parseInt(msg.mid || this.mid),
volume: parseInt(msg.volume || this.volume),
sid: msg.sid
}
};
this.send(msg);
});
}
RED.nodes.registerType("mi-devices-actions gateway_sound", XiaomiActionGatewaySound);
/*********************************************
Stop playing a sound on the gateway
*********************************************/
function XiaomiActionGatewayStopSound(config) {
RED.nodes.createNode(this, config);
this.on('input', (msg) => {
msg.payload = {
cmd: "write",
data: { mid: 1000, sid: msg.sid }
};
this.send(msg);
});
}
RED.nodes.registerType("mi-devices-actions gateway_stop_sound", XiaomiActionGatewayStopSound);
/*********************************************
Turn device on
*********************************************/

View File

@@ -1,124 +0,0 @@
<script type="text/javascript">
RED.nodes.registerType('xiaomi-all', {
category: 'xiaomi',
color: '#3FADB5',
defaults: {
gateway: {value:"", type:"xiaomi-configurator"},
name: {value: ""},
onlyModels: {value: []},
excludedSids: { value: []}
},
inputs: 1,
outputs: 1,
outputLabels: ["All devices"],
paletteLabel: "all",
icon: "mi-all.png",
label: function () {
return this.name || "xiaomi-all";
},
oneditprepare: function() {
var node = this;
function getOnlyModelsValue(input) {
var cleanOnlyModels = [];
input.forEach((value) => {
cleanOnlyModels = cleanOnlyModels.concat(value.split(','));
});
return cleanOnlyModels;
}
function changeGateway(gateway, onlyModels, excludedSids) {
var configNodeID = gateway || $('#node-input-gateway').val();
if (configNodeID) {
var configNode = RED.nodes.node(configNodeID);
if(configNode) {
onlyModels = getOnlyModelsValue(onlyModels || $('#node-input-onlyModels').val() || []);
excludedSids = excludedSids || $('#node-input-excludedSids').val() || [];
$('#node-input-excludedSids').empty();
for (key in configNode.deviceList) {
var device = configNode.deviceList[key];
if (onlyModels.length == 0 || onlyModels.indexOf(device.model) >= 0) {
var option = $('<option value="' + device.sid + '">' + device.desc + '</option>');
if(excludedSids && excludedSids.indexOf(device.sid) >= 0) {
option.prop('selected', true);
}
$('#node-input-excludedSids').append(option);
}
}
}
}
}
changeGateway(this.gateway, this.onlyModels, this.excludedSids);
$("#node-input-gateway, #node-input-onlyModels").change(function () {
changeGateway();
});
},
oneditsave: function() {
if(!$('#node-input-onlyModels').val()) {
this.onlyModels = [];
}
if(!$('#node-input-excludedSids').val()) {
this.excludedSids = [];
}
}
});
</script>
<script type="text/x-red" data-template-name="xiaomi-all">
<div class="form-row">
<label for="node-input-gateway"><i class="icon-tag"></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="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<hr />
<h5>Filters</h5>
<div class="form-row">
<label for="node-input-onlyModels"><i class="icon-tag"></i> Only</label>
<select multiple id="node-input-onlyModels">
<option value="sensor_ht,weather.v1">Temperature/humidty</option>
<option value="motion">Motion</option>
<option value="switch,sensor_switch.aq2">Switches</option>
<option value="magnet,sensor_magnet.aq2">Contacts</option>
<option value="plug">Plugs</option>
</select>
</div>
<div class="form-row">
<label for="node-input-excludedSids"><i class="icon-tag"></i> Exclude</label>
<select multiple id="node-input-excludedSids" size=10></select>
</div>
</script>
<script type="text/x-red" data-help-name="xiaomi-all">
<p>All devices registred in the gateway, except gateway itself.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">object</span>
</dt>
<dd>When use as an incoming filter node, <code>sid</code> and <code>model</code> are mandatory.</dd>
</dl>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Devices output
<dl class="message-properties">
<dt>payload <span class="property-type">array</span></dt>
<dd>Array of devices.</dd>
</dl>
</li>
</ol>
<h4>Details</h4>
<p>Sample payload:</p>
<p><pre>[
{sid: "128d0901db1fa8", desc: "Door sensor" model: "magnet"},
{sid: "151d0401ab2491", desc: "Heat sensor", model: "sensor_ht"},
{sid: "658d030171427c", desc: "Button", model: "switch"}
]</pre>
</p>
</script>

View File

@@ -1,50 +0,0 @@
module.exports = (RED) => {
function getOnlyModelsValue(input) {
var cleanOnlyModels = [];
input.forEach((value) => {
cleanOnlyModels = cleanOnlyModels.concat(value.split(','));
});
return cleanOnlyModels;
}
function XiaomiAllNode(config) {
RED.nodes.createNode(this, config);
this.gateway = RED.nodes.getNode(config.gateway);
this.onlyModels = getOnlyModelsValue(config.onlyModels || []);
this.excludedSids = config.excludedSids;
this.isDeviceValid = (device) => {
if((!this.onlyModels || this.onlyModels.length == 0) && (!this.excludedSids || this.excludedSids.length == 0)) {
return true;
}
// Is excluded
if((this.excludedSids && this.excludedSids.length != 0) && this.excludedSids.indexOf(device.sid) >= 0) {
return false;
}
if((this.onlyModels && this.onlyModels.length != 0) && this.onlyModels.indexOf(device.model) >= 0) {
return true;
}
return false;
}
if (this.gateway) {
this.on('input', (msg) => {
// Filter input
if(msg.payload && msg.payload.model && msg.payload.sid) {
if(!this.isDeviceValid(msg.payload)) {
msg = null;
}
}
// Prepare for request
else {
msg.payload = this.gateway.deviceList.filter((device) => this.isDeviceValid(device));
}
this.send(msg);
});
}
}
RED.nodes.registerType("xiaomi-all", XiaomiAllNode);
}

View File

@@ -39,7 +39,7 @@ export class Gateway extends events.EventEmitter {
if (msg.isGetIdListAck()) {
(<MessageData.GatewayMessageGetIdListData> msg.data).forEach((sid) => {
this.send({cmd: "read", sid: sid});
this.read(sid);
});
}
@@ -69,7 +69,17 @@ export class Gateway extends events.EventEmitter {
return !!this._subdevices[sid];
}
setLight(brightness, rgb) {
getIdList() {
this.send({
cmd: "get_id_list",
})
}
read(sid?: string) {
this.send({cmd: "read", sid: sid || this.sid});
}
setLight(brightness: number, rgb: { red: number, green: number, blue: number }) {
this.send({
cmd: "write",
data: {
@@ -79,8 +89,15 @@ export class Gateway extends events.EventEmitter {
});
}
playSound() {
playSound(musicId: number, volume: number) {
this.send({
cmd: "write",
data: {
mid: musicId,
volume: volume,
sid: this.sid
}
});
}
send(message: any) {

View File

@@ -67,7 +67,7 @@ export class GatewayServer extends events.EventEmitter {
if ((msg.isHeartbeat() || msg.isIam()) && msg.model === "gateway") {
if (!this._gateways[msg.sid]) {
this._gateways[msg.sid] = new Gateway(msg.sid, remote.address);
this.sendToGateway(msg.sid, {cmd: "get_id_list"});
this._gateways[msg.sid].getIdList();
this.emit("gateway-online", msg.sid);
}
else {

View File

@@ -1,34 +0,0 @@
import { Red, NodeProperties } from "node-red";
import { Constants } from "../constants";
export default (RED:Red) => {
class GatewayLight {
public color:string;
public brightness:number;
constructor(props:NodeProperties) {
RED.nodes.createNode(<any> this, props);
(<any> this).setListeners();
}
protected setListeners() {
(<any> this).on('input', (msg) => {
let color = msg.color || this.color;
let brightness = msg.brightness || this.brightness;
if(msg.sid) {
msg.payload = {
cmd: "write",
data: { rgb: 123, sid: msg.sid }
};
}
else {
msg.payload = {
brightness: brightness
};
}
(<any> this).send(msg);
});
}
}
RED.nodes.registerType(`${Constants.NODES_PREFIX}-actions gateway_light`, <any> GatewayLight);
};

View File

@@ -0,0 +1,81 @@
<!-- The Gateway sound Node -->
<script type="text/javascript">
RED.nodes.registerType('<%= NODES_PREFIX %>-actions gateway_play_sound', {
category: 'xiaomi actions',
color: '#64C4CD',
defaults: {
name: {value: ""},
mid: {value: ""},
volume: {value: ""}
},
inputs: 1,
outputs: 1,
paletteLabel: "play sound",
icon: "mi-sound.png",
label: function () {
return this.name || "play sound";
}
});
</script>
<script type="text/x-red" data-template-name="<%= NODES_PREFIX %>-actions gateway_play_sound">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-mid"><i class="icon-tag"></i> Sound</label>
<select id="node-input-mid">
<option value="0">Police car tone 1</option>
<option value="1">Police car tone 2</option>
<option value="2">Safety incident sound</option>
<option value="3">Missile countdown</option>
<option value="4">Ghost cry</option>
<option value="5">Sniper rifle</option>
<option value="6">Battle sound</option>
<option value="7">Air raid alarm</option>
<option value="8">Barking</option>
<option value="10">Doorbell tone</option>
<option value="11">Door knocking tone</option>
<option value="12">Funny tone</option>
<option value="13">Alarm clock tone</option>
<option value="20">MiMix</option>
<option value="21">Enthusisatic</option>
<option value="22">GuitarClassic</option>
<option value="23">IceWorldPiano</option>
<option value="24">LeisureTime</option>
<option value="25">ChildHood</option>
<option value="26">MorningStreamLet</option>
<option value="27">MusicBox</option>
<option value="28">Orange</option>
<option value="29">Thinker</option>
</select>
</div>
<div class="form-row">
<label for="node-input-volume"><i class="icon-tag"></i> Volume</label>
<input type="range" id="node-input-volume" min="0" max="100">
</div>
</script>
<script type="text/x-red" data-help-name="<%= NODES_PREFIX %>-actions gateway_play_sound">
<p>Play a sound on the gateway.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>mid
<span class="property-type">number</span>
</dt>
<dd>Music ID (user define sounds start from <code>1001</code>, <code>1000</code> means stop).</dd>
<dt>volume
<span class="property-type">number</span>
</dt>
<dd>Playing volume, between <code>0</code> and <code>100</code>.</dd>
</dl>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Message to connect to a gateway out node.</li>
</ol>
</script>

View File

@@ -0,0 +1,31 @@
import {Red, NodeProperties} from "node-red";
import {Constants} from "../constants";
export default (RED: Red) => {
class GatewayPlaySound {
public mid: number;
public volume: number;
constructor(props: NodeProperties) {
RED.nodes.createNode(<any> this, props);
this.mid = parseInt((<any>props).mid);
this.volume = parseInt((<any>props).volume);
(<any> this).setListeners();
}
protected setListeners() {
(<any> this).on('input', (msg) => {
if (msg.sid) {
msg.payload = {
action: "playSound",
mid: msg.mid || this.mid,
volume: msg.volume || this.volume
};
}
(<any> this).send(msg);
});
}
}
RED.nodes.registerType(`${Constants.NODES_PREFIX}-actions gateway_play_sound`, <any> GatewayPlaySound);
};

View File

@@ -0,0 +1,36 @@
<!-- The Gateway stop sound Node -->
<script type="text/javascript">
RED.nodes.registerType('<%= NODES_PREFIX %>-actions gateway_stop_sound', {
category: 'xiaomi actions',
color: '#64C4CD',
defaults: {
name: {value: ""}
},
inputs: 1,
outputs: 1,
paletteLabel: "stop sound",
icon: "mi-mute.png",
label: function () {
return this.name || "stop sound";
}
});
</script>
<script type="text/x-red" data-template-name="<%= NODES_PREFIX %>-actions gateway_stop_sound">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="<%= NODES_PREFIX %>-actions gateway_stop_sound">
<p>
Stop current playing sound on the gateway.
</p>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Message to connect to a gateway out node.</li>
</ol>
</script>

View File

@@ -0,0 +1,26 @@
import {Red, NodeProperties} from "node-red";
import {Constants} from "../constants";
export default (RED: Red) => {
class GatewayStopSound {
constructor(props: NodeProperties) {
RED.nodes.createNode(<any> this, props);
(<any> this).setListeners();
}
protected setListeners() {
(<any> this).on('input', (msg) => {
if (msg.sid) {
msg.payload = {
action: "playSound",
mid: 1000
};
}
(<any> this).send(msg);
});
}
}
RED.nodes.registerType(`${Constants.NODES_PREFIX}-actions gateway_stop_sound`, <any> GatewayStopSound);
};

View File

@@ -1,6 +1,6 @@
<!-- The Gateway light Node -->
<script type="text/javascript">
RED.nodes.registerType('<%= NODES_PREFIX %>-actions gateway_light', {
RED.nodes.registerType('<%= NODES_PREFIX %>-actions light', {
category: 'xiaomi actions',
color: '#64C4CD',
defaults: {
@@ -30,7 +30,7 @@
});
</script>
<script type="text/x-red" data-template-name="<%= NODES_PREFIX %>-actions gateway_light">
<script type="text/x-red" data-template-name="<%= NODES_PREFIX %>-actions light">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
@@ -45,7 +45,7 @@
</div>
</script>
<script type="text/x-red" data-help-name="<%= NODES_PREFIX %>-actions gateway_light">
<script type="text/x-red" data-help-name="<%= NODES_PREFIX %>-actions light">
<p>Change the light of the gateway.</p>
<h3>Inputs</h3>

View File

@@ -0,0 +1,31 @@
import {Red, NodeProperties} from "node-red";
import {Constants} from "../constants";
export default (RED: Red) => {
class Light {
public color: string;
public brightness: number;
constructor(props: NodeProperties) {
RED.nodes.createNode(<any> this, props);
this.color = (<any>props).color;
this.brightness = (<any>props).brightness;
(<any> this).setListeners();
}
protected setListeners() {
(<any> this).on('input', (msg) => {
if (msg.sid) {
msg.payload = {
action: "setLight",
color: msg.color || this.color,
brightness: msg.brightness || this.brightness
};
}
(<any> this).send(msg);
});
}
}
RED.nodes.registerType(`${Constants.NODES_PREFIX}-actions light`, <any> Light);
};

View File

@@ -30,4 +30,6 @@
docTitle: "Virtual double click for switch."
}) %>
<%- include('./GatewayLight', {}) %>
<%- include('./Light', {}) %>
<%- include('./GatewayPlaySound', {}) %>
<%- include('./GatewayStopSound', {}) %>

View File

@@ -1,17 +1,20 @@
import { Red, NodeProperties } from "node-red";
import * as LumiAqara from 'lumi-aqara';
import {Red} from "node-red";
import {default as ReadAction} from './ReadAction';
import {default as WriteAction} from './WriteAction';
import {default as GatewayLight} from './GatewayLight';
import {default as Light} from './Light';
import {default as GatewayPlaySound} from './GatewayPlaySound';
import {default as GatewayStopSound} from './GatewayStopSound';
export = (RED:Red) => {
GatewayLight(RED);
export = (RED: Red) => {
["read", "get_id_list"].forEach((action) => {
ReadAction(RED, action);
});
["click", "double_click"].forEach((action) => {
WriteAction(RED, action);
});
Light(RED);
GatewayPlaySound(RED);
GatewayStopSound(RED);
};

View File

@@ -4,32 +4,43 @@ import { Constants } from "../constants";
export interface IGatewayNode extends Node {
gatewayConf:any;
gateway: LumiAqara.Gateway;
setGateway(gateway:LumiAqara.Gateway);
}
export default (RED:Red) => {
class Gateway {
protected gatewayConf: any;
protected gateway: LumiAqara.Gateway;
constructor(props:NodeProperties){
RED.nodes.createNode(<any> this, props);
this.gatewayConf = RED.nodes.getNode((<any> props).gateway);
this.gateway = null;
(<any> this).status({fill:"red", shape:"ring", text: "offline"});
(<any>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();
}
protected gatewayOnline() {
(<any>this).status({fill: "blue", shape: "dot", text: "online"});
}
protected gatewayOffline() {
(<any>this).status({fill: "red", shape: "ring", text: "offline"});
}
protected setMessageListener() {
(<any> this).on('input', (msg) => {
if (this.gateway) {
if (this.gatewayConf.gateway) {
var payload = msg.payload;
// Input from gateway
if(payload.sid && payload.sid == this.gateway.sid) {
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;
@@ -39,23 +50,13 @@ export default (RED:Red) => {
}
// Prepare for request
else {
msg.sid = this.gateway.sid;
msg.sid = this.gatewayConf.gateway.sid;
msg.gateway = this.gatewayConf.gateway;
(<any> this).send(msg);
}
}
});
}
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"});
});
};
}
RED.nodes.registerType(`${Constants.NODES_PREFIX}-gateway`, <any> Gateway);

View File

@@ -1,5 +1,6 @@
import {Red, NodeProperties} from "node-red";
import {Constants} from "../constants";
import {GatewayServer} from "../../devices/GatewayServer";
export default (RED: Red) => {
class GatewayOut {
@@ -11,7 +12,22 @@ export default (RED: Red) => {
protected setMessageListener() {
(<any> this).on("input", (msg) => {
if (msg.hasOwnProperty("payload") && msg.hasOwnProperty("gateway")) {
msg.gateway.gateway.send(msg);
let gateway = 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;
}
}
}
}
});
}

View File

@@ -1,94 +0,0 @@
var crypto = require("crypto");
var iv = Buffer.from([0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, 0x56, 0x2e]);
module.exports = {
computeBatteryLevel: function(voltage) {
/*
When full, CR2032 batteries are between 3 and 3.4V
http://farnell.com/datasheets/1496885.pdf
*/
return Math.min(Math.round((voltage - 2200) / 10), 100);
},
setStatus: function(node, data) {
if (data.voltage) {
var batteryPercent = Math.min(Math.round((data.voltage - 2200) / 14), 100);
var status = {
fill: "green", shape: "dot",
text: "battery - " + batteryPercent + "%"
};
if (data.voltage < 2500) {
status.color = "red";
} else if (data.voltage < 2900) {
status.color = "yellow";
}
node.status(status);
}
},
prepareFullDataOutput: function(payload) {
if(payload.data.voltage) {
payload.data.batteryLevel = this.computeBatteryLevel(payload.data.voltage);
}
return payload;
},
getGatewayKey: function(password, token) {
var cipher = crypto.createCipheriv('aes-128-cbc', password, iv);
var gatewayToken = token;
var key = cipher.update(gatewayToken, "ascii", "hex");
cipher.final('hex');
return key;
},
prepareForGatewayRequest: function(node, msg) {
msg.sid = node.sid;
msg.gateway = node.gateway;
},
computeColorValue: function (red, green, blue, brightness) {
return (brightness !== undefined ? 256*256*256*brightness : 0) + (256*256*red) + (256*green) + blue;
},
computeColor: function (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 }
};
},
defaultNode: function(RED, config, node) {
RED.nodes.createNode(node, config);
node.gateway = RED.nodes.getNode(config.gateway);
node.sid = config.sid;
node.status({fill:"grey", shape:"ring", text:"battery - na"});
if (node.gateway) {
node.on('input', (msg) => {
let payload = msg.payload;
// Input from gateway
if (payload.sid) {
if (payload.sid == node.sid) {
this.setStatus(node, payload.data);
node.send([msg]);
}
}
// Prepare for request
else {
this.prepareForGatewayRequest(node, msg);
node.send(msg);
}
});
}
}
}