feat(yeelight): handle yeelights
This commit is contained in:
@@ -1,104 +0,0 @@
|
|||||||
<!-- The "on" Node -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
RED.nodes.registerType('mi-devices-actions on',{
|
|
||||||
category: 'xiaomi actions',
|
|
||||||
color: '#64C4CD',
|
|
||||||
defaults: {
|
|
||||||
name: {value:""}
|
|
||||||
},
|
|
||||||
inputs:1,
|
|
||||||
outputs:1,
|
|
||||||
paletteLabel: "on",
|
|
||||||
icon: "mi-on.png",
|
|
||||||
label: function() {
|
|
||||||
return this.name||"power on";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script type="text/x-red" data-template-name="mi-devices-actions on">
|
|
||||||
<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 on">
|
|
||||||
<p>
|
|
||||||
Turn input device to on.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Outputs</h3>
|
|
||||||
<ol class="node-ports">
|
|
||||||
<li>Message to connect to a gateway/yeelight out node.</li>
|
|
||||||
</ol>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- The "off" Node -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
RED.nodes.registerType('mi-devices-actions off',{
|
|
||||||
category: 'xiaomi actions',
|
|
||||||
color: '#64C4CD',
|
|
||||||
defaults: {
|
|
||||||
name: {value:""}
|
|
||||||
},
|
|
||||||
inputs:1,
|
|
||||||
outputs:1,
|
|
||||||
paletteLabel: "off",
|
|
||||||
icon: "mi-off.png",
|
|
||||||
label: function() {
|
|
||||||
return this.name||"power off";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script type="text/x-red" data-template-name="mi-devices-actions off">
|
|
||||||
<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 off">
|
|
||||||
<p>
|
|
||||||
Turn input device to off.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Outputs</h3>
|
|
||||||
<ol class="node-ports">
|
|
||||||
<li>Message to connect to a gateway/yeelight out node.</li>
|
|
||||||
</ol>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- The "toggle" Node -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
RED.nodes.registerType('mi-devices-actions toggle',{
|
|
||||||
category: 'xiaomi actions',
|
|
||||||
color: '#64C4CD',
|
|
||||||
defaults: {
|
|
||||||
name: {value:""}
|
|
||||||
},
|
|
||||||
inputs:1,
|
|
||||||
outputs:1,
|
|
||||||
paletteLabel: "toggle",
|
|
||||||
icon: "mi-toggle.png",
|
|
||||||
label: function() {
|
|
||||||
return this.name||"toggle power";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script type="text/x-red" data-template-name="mi-devices-actions toggle">
|
|
||||||
<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 toggle">
|
|
||||||
<p>Toggle device.</p>
|
|
||||||
|
|
||||||
<h3>Outputs</h3>
|
|
||||||
<ol class="node-ports">
|
|
||||||
<li>Message to connect to a gateway/yeelight out node.</li>
|
|
||||||
</ol>
|
|
||||||
</script>
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
const miDevicesUtils = require('../src/utils');
|
|
||||||
module.exports = (RED) => {
|
|
||||||
/*********************************************
|
|
||||||
Turn device on
|
|
||||||
*********************************************/
|
|
||||||
function XiaomiActionPowerOn(config) {
|
|
||||||
RED.nodes.createNode(this, config);
|
|
||||||
|
|
||||||
this.on('input', (msg) => {
|
|
||||||
if(msg.sid){
|
|
||||||
msg.payload = {
|
|
||||||
cmd: "write",
|
|
||||||
data: { status: "on", sid: msg.sid }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
msg.payload = "on";
|
|
||||||
}
|
|
||||||
this.send(msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
RED.nodes.registerType("mi-devices-actions on", XiaomiActionPowerOn);
|
|
||||||
|
|
||||||
/*********************************************
|
|
||||||
Turn device off
|
|
||||||
*********************************************/
|
|
||||||
function XiaomiActionPowerOff(config) {
|
|
||||||
RED.nodes.createNode(this, config);
|
|
||||||
|
|
||||||
this.on('input', (msg) => {
|
|
||||||
if(msg.sid){
|
|
||||||
msg.payload = {
|
|
||||||
cmd: "write",
|
|
||||||
data: { status: "off", sid: msg.sid }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
msg.payload = "off";
|
|
||||||
}
|
|
||||||
this.send(msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
RED.nodes.registerType("mi-devices-actions off", XiaomiActionPowerOff);
|
|
||||||
|
|
||||||
/*********************************************
|
|
||||||
Toggle device
|
|
||||||
*********************************************/
|
|
||||||
function XiaomiActionToggle(config) {
|
|
||||||
RED.nodes.createNode(this, config);
|
|
||||||
|
|
||||||
this.on('input', (msg) => {
|
|
||||||
msg.payload = "toggle";
|
|
||||||
this.send(msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
RED.nodes.registerType("mi-devices-actions toggle", XiaomiActionToggle);
|
|
||||||
}
|
|
||||||
@@ -46,8 +46,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cryptojs": "^2.5.3",
|
"cryptojs": "^2.5.3",
|
||||||
"lumi-aqara": "^1.4.0",
|
"miio": "^0.15.4",
|
||||||
"miio": "^0.15.2",
|
|
||||||
"yeelight-wifi": "^2.3.0"
|
"yeelight-wifi": "^2.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -4,7 +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";
|
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]);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as events from 'events';
|
import * as events from 'events';
|
||||||
import * as dgram from "dgram";
|
import * as dgram from "dgram";
|
||||||
import {Gateway} from "./Gateway";
|
import {Gateway} from "./";
|
||||||
import Timer = NodeJS.Timer;
|
import Timer = NodeJS.Timer;
|
||||||
import {GatewayMessage} from "./GatewayMessage";
|
import {GatewayMessage} from "./GatewayMessage";
|
||||||
|
|
||||||
@@ -92,36 +92,6 @@ export class GatewayServer extends events.EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gatewaySid && this._gateways[gatewaySid] && this._gateways[gatewaySid].handleMessage(msg);
|
gatewaySid && this._gateways[gatewaySid] && this._gateways[gatewaySid].handleMessage(msg);
|
||||||
|
|
||||||
/*if(remote.address == this.addr) {
|
|
||||||
var msg = message.toString('utf8');
|
|
||||||
var jsonMsg = JSON.parse(msg);
|
|
||||||
if(jsonMsg.data) {
|
|
||||||
jsonMsg.data = JSON.parse(jsonMsg.data) || jsonMsg.data;
|
|
||||||
if(jsonMsg.data.voltage) {
|
|
||||||
jsonMsg.data.batteryLevel = miDevicesUtils.computeBatteryLevel(jsonMsg.data.voltage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg = { payload: jsonMsg };
|
|
||||||
if(this.gateway && jsonMsg.data.ip && jsonMsg.data.ip === this.gateway.ip) {
|
|
||||||
if(jsonMsg.token) {
|
|
||||||
this.gateway.lastToken = jsonMsg.token;
|
|
||||||
if(!this.gateway.sid) {
|
|
||||||
this.gateway.sid = jsonMsg.sid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RED.nodes.eachNode((tmpNode) => {
|
|
||||||
if(tmpNode.type.indexOf("xiaomi-gateway") === 0 && tmpNode.gateway == this.gatewayNodeId) {
|
|
||||||
let tmpNodeInst = RED.nodes.getNode(tmpNode.id);
|
|
||||||
if(tmpNode.type === "xiaomi-gateway out" && !this.gateway.lastToken) {
|
|
||||||
tmpNodeInst.status({fill:"yellow", shape:"ring", text: "waiting input"});
|
|
||||||
}
|
|
||||||
tmpNodeInst.status({fill:"blue", shape:"dot", text: "online"});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.send(msg);
|
|
||||||
}*/
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from './';
|
||||||
export * from './Gateway';
|
export * from './Gateway';
|
||||||
export * from './GatewayMessage';
|
export * from './GatewayMessage';
|
||||||
export * from './GatewayServer';
|
export * from './GatewayServer';
|
||||||
47
src/devices/yeelight/YeelightServer.ts
Normal file
47
src/devices/yeelight/YeelightServer.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import * as events from 'events';
|
||||||
|
import * as YeelightSearch from 'yeelight-wifi';
|
||||||
|
|
||||||
|
export class YeelightServer extends events.EventEmitter {
|
||||||
|
private static instance: YeelightServer;
|
||||||
|
|
||||||
|
private _bulbs: { [sid: string]: any } = {};
|
||||||
|
private _bulbsJson: { [sid: string]: any } = {};
|
||||||
|
|
||||||
|
static getInstance() {
|
||||||
|
if (!this.instance) {
|
||||||
|
this.instance = new YeelightServer();
|
||||||
|
}
|
||||||
|
return this.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bulbs(): { [sid: string]: any } {
|
||||||
|
return this._bulbsJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBulb(sid) {
|
||||||
|
return this._bulbs[sid];
|
||||||
|
}
|
||||||
|
|
||||||
|
discover() {
|
||||||
|
new Promise(() => {
|
||||||
|
(new YeelightSearch()).on('found', (bulb: any) => {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
0
src/devices/yeelight/index.ts
Normal file
0
src/devices/yeelight/index.ts
Normal file
@@ -15,13 +15,11 @@ export default (RED: Red) => {
|
|||||||
|
|
||||||
protected setListeners() {
|
protected setListeners() {
|
||||||
(<any> this).on('input', (msg) => {
|
(<any> this).on('input', (msg) => {
|
||||||
if (msg.sid) {
|
msg.payload = {
|
||||||
msg.payload = {
|
action: "setLight",
|
||||||
action: "setLight",
|
color: msg.color || this.color,
|
||||||
color: msg.color || this.color,
|
brightness: msg.brightness || this.brightness
|
||||||
brightness: msg.brightness || this.brightness
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
(<any> this).send(msg);
|
(<any> this).send(msg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/nodes/actions/ToggleAction.ts
Normal file
20
src/nodes/actions/ToggleAction.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {Red, NodeProperties} from "node-red";
|
||||||
|
import {Constants} from "../constants";
|
||||||
|
|
||||||
|
export default (RED: Red, action: string) => {
|
||||||
|
class ToggleAction {
|
||||||
|
constructor(props: NodeProperties) {
|
||||||
|
RED.nodes.createNode(<any> this, props);
|
||||||
|
(<any> this).setListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setListeners() {
|
||||||
|
(<any> this).on('input', (msg) => {
|
||||||
|
msg.payload = { action };
|
||||||
|
(<any> this).send(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RED.nodes.registerType(`${Constants.NODES_PREFIX}-actions ${action}`, <any> ToggleAction);
|
||||||
|
};
|
||||||
@@ -32,4 +32,28 @@
|
|||||||
|
|
||||||
<%- include('./Light', {}) %>
|
<%- include('./Light', {}) %>
|
||||||
<%- include('./GatewayPlaySound', {}) %>
|
<%- include('./GatewayPlaySound', {}) %>
|
||||||
<%- include('./GatewayStopSound', {}) %>
|
<%- include('./GatewayStopSound', {}) %>
|
||||||
|
|
||||||
|
<%# ---------------------------------- turn_on ---------------------------------- %>
|
||||||
|
<%- include('./Action', {
|
||||||
|
type: "turn_on",
|
||||||
|
label: "turn on",
|
||||||
|
icon: "mi-on",
|
||||||
|
docTitle: "Turn device on."
|
||||||
|
}) %>
|
||||||
|
|
||||||
|
<%# ---------------------------------- double_click ---------------------------------- %>
|
||||||
|
<%- include('./Action', {
|
||||||
|
type: "turn_off",
|
||||||
|
label: "turn off",
|
||||||
|
icon: "mi-off",
|
||||||
|
docTitle: "Turn device off."
|
||||||
|
}) %>
|
||||||
|
|
||||||
|
<%# ---------------------------------- double_click ---------------------------------- %>
|
||||||
|
<%- include('./Action', {
|
||||||
|
type: "toggle",
|
||||||
|
label: "toggle",
|
||||||
|
icon: "mi-toggle",
|
||||||
|
docTitle: "Toggle device."
|
||||||
|
}) %>
|
||||||
@@ -5,6 +5,7 @@ import {default as WriteAction} from './WriteAction';
|
|||||||
import {default as Light} from './Light';
|
import {default as Light} from './Light';
|
||||||
import {default as GatewayPlaySound} from './GatewayPlaySound';
|
import {default as GatewayPlaySound} from './GatewayPlaySound';
|
||||||
import {default as GatewayStopSound} from './GatewayStopSound';
|
import {default as GatewayStopSound} from './GatewayStopSound';
|
||||||
|
import {default as ToggleAction} from './ToggleAction';
|
||||||
|
|
||||||
export = (RED: Red) => {
|
export = (RED: Red) => {
|
||||||
["read", "get_id_list"].forEach((action) => {
|
["read", "get_id_list"].forEach((action) => {
|
||||||
@@ -17,4 +18,7 @@ export = (RED: Red) => {
|
|||||||
Light(RED);
|
Light(RED);
|
||||||
GatewayPlaySound(RED);
|
GatewayPlaySound(RED);
|
||||||
GatewayStopSound(RED);
|
GatewayStopSound(RED);
|
||||||
|
["turn_on", "turn_off", "toggle"].forEach(action => {
|
||||||
|
ToggleAction(RED, action);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Red, NodeProperties } from "node-red";
|
import { Red } from "node-red";
|
||||||
import * as LumiAqara from 'lumi-aqara';
|
|
||||||
|
|
||||||
import {default as All} from "./All";
|
import {default as All} from "./All";
|
||||||
import {default as Plug} from "./Plug";
|
import {default as Plug} from "./Plug";
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Red, Node, NodeProperties } from "node-red";
|
import { Red, Node, NodeProperties } from "node-red";
|
||||||
import { LumiAqara } from "../../../typings/index";
|
|
||||||
import { Constants } from "../constants";
|
import { Constants } from "../constants";
|
||||||
|
|
||||||
export interface IGatewayNode extends Node {
|
export interface IGatewayNode extends Node {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {Red, Node, NodeProperties} from "node-red";
|
import {Red, Node, NodeProperties} from "node-red";
|
||||||
import {Constants} from "../constants";
|
import {Constants} from "../constants";
|
||||||
import {GatewayServer} from "../../devices/GatewayServer";
|
import {GatewayServer} from "../../devices/gateway/GatewayServer";
|
||||||
import {Gateway} from "../../devices/Gateway";
|
import {Gateway} from "../../devices/gateway/Gateway";
|
||||||
import {GatewaySubdevice} from "../../devices/GatewaySubdevice";
|
import {GatewaySubdevice} from "../../devices/gateway/GatewaySubdevice";
|
||||||
import {isString} from "util";
|
import {isString} from "util";
|
||||||
|
|
||||||
export interface IGatewayConfiguratorNode extends Node {
|
export interface IGatewayConfiguratorNode extends Node {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {Red, Node, NodeProperties} from 'node-red';
|
import {Red, Node, NodeProperties} from 'node-red';
|
||||||
import {Constants} from '../constants';
|
import {Constants} from '../constants';
|
||||||
import {Gateway} from "../../devices/Gateway";
|
import {Gateway} from "../../devices/gateway/Gateway";
|
||||||
|
|
||||||
export interface IGatewayInNode extends Node {
|
export interface IGatewayInNode extends Node {
|
||||||
gatewayConf: any;
|
gatewayConf: any;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {Red, NodeProperties} from "node-red";
|
import {Red, NodeProperties} from "node-red";
|
||||||
import {Constants} from "../constants";
|
import {Constants} from "../constants";
|
||||||
import {GatewayServer} from "../../devices/GatewayServer";
|
import {GatewayServer} from "../../devices/gateway/GatewayServer";
|
||||||
|
|
||||||
export default (RED: Red) => {
|
export default (RED: Red) => {
|
||||||
class GatewayOut {
|
class GatewayOut {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Red, NodeProperties } from "node-red";
|
import { Red } from "node-red";
|
||||||
import * as LumiAqara from 'lumi-aqara';
|
|
||||||
|
|
||||||
import { GatewayServer } from "../../devices/GatewayServer";
|
import { GatewayServer } from "../../devices/gateway/GatewayServer";
|
||||||
import {default as GatewayConfigurator} from "./GatewayConfigurator";
|
import {default as GatewayConfigurator} from "./GatewayConfigurator";
|
||||||
import {default as Gateway} from "./Gateway";
|
import {default as Gateway} from "./Gateway";
|
||||||
import {default as GatewayIn} from "./GatewayIn";
|
import {default as GatewayIn} from "./GatewayIn";
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import { Red } from "node-red";
|
|
||||||
|
|
||||||
import * as YeelightSearch from 'yeelight-wifi';
|
|
||||||
import { Constants } from "../constants";
|
|
||||||
import { IYeelightConfiguratorNode } from "./YeelightConfigurator";
|
|
||||||
|
|
||||||
export class Searcher {
|
|
||||||
static _bulbs:any[] = [];
|
|
||||||
|
|
||||||
static discover(RED:Red) {
|
|
||||||
new Promise(() => {
|
|
||||||
(new YeelightSearch()).on('found', (bulb:any) => {
|
|
||||||
this._bulbs.push({
|
|
||||||
name: bulb.name,
|
|
||||||
model: bulb.model,
|
|
||||||
sid: parseInt(bulb.id),
|
|
||||||
ip: bulb.hostname
|
|
||||||
});
|
|
||||||
RED.nodes.eachNode((tmpNode) => {
|
|
||||||
if(tmpNode.type.indexOf(`${Constants.NODES_PREFIX}-yeelight configurator`) === 0) {
|
|
||||||
let tmpNodeInst = <IYeelightConfiguratorNode> RED.nodes.getNode(tmpNode.id);
|
|
||||||
if(tmpNodeInst.ip == bulb.hostname || tmpNodeInst.sid == parseInt(bulb.id)) {
|
|
||||||
tmpNodeInst.bulb = bulb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static get bulbs() {
|
|
||||||
return this._bulbs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,23 +3,22 @@
|
|||||||
category: 'config',
|
category: 'config',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: {value: ""},
|
name: {value: ""},
|
||||||
ip: {value: ""},
|
|
||||||
sid: {value: ""}
|
sid: {value: ""}
|
||||||
},
|
},
|
||||||
label: function () {
|
label: function () {
|
||||||
return this.name || "yeelight conf";
|
return this.name || "yeelight conf";
|
||||||
},
|
},
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
RED.settings.miDevicesYeelightConfiguratorDiscoveredBulbs.forEach(function(bulb, index) {
|
var foundBulbs = RED.settings.miDevicesYeelightConfiguratorDiscoveredBulbs;
|
||||||
|
Object.keys(foundBulbs).forEach(function(sid) {
|
||||||
|
var bulb = foundBulbs[sid];
|
||||||
$('#discovered-bulbs').append('<option value="' + bulb.sid + '">' + (bulb.name || bulb.sid) + ' - ' + bulb.model + ' - ' + bulb.ip + '</option>');
|
$('#discovered-bulbs').append('<option value="' + bulb.sid + '">' + (bulb.name || bulb.sid) + ' - ' + bulb.model + ' - ' + bulb.ip + '</option>');
|
||||||
});
|
});
|
||||||
var node = this;
|
|
||||||
$('#discovered-bulbs').on('change', function() {
|
$('#discovered-bulbs').on('change', function() {
|
||||||
var sid = $('#discovered-bulbs').val();
|
var sid = $('#discovered-bulbs').val();
|
||||||
var bulb = sid && RED.settings.miDevicesYeelightConfiguratorDiscoveredBulbs.filter(function(e) { return e.sid == sid })[0];
|
var bulb = foundBulbs[sid];
|
||||||
$("#node-config-input-name").val(bulb && bulb.name);
|
$("#node-config-input-name").val(bulb && bulb.name);
|
||||||
$("#node-config-input-sid").val(bulb && bulb.sid);
|
$("#node-config-input-sid").val(bulb && bulb.sid);
|
||||||
$("#node-config-input-ip").val("");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -37,15 +36,10 @@
|
|||||||
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
|
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||||
<input type="text" id="node-config-input-name" placeholder="Name">
|
<input type="text" id="node-config-input-name" placeholder="Name">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
|
||||||
<label for="node-config-input-ip"><i class="fa fa-compass"></i> IP</label>
|
|
||||||
<input type="text" id="node-config-input-ip" placeholder="IP">
|
|
||||||
</div>
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-config-input-sid"><i class="fa fa-barcode"></i> SID</label>
|
<label for="node-config-input-sid"><i class="fa fa-barcode"></i> SID</label>
|
||||||
<input type="text" id="node-config-input-sid" placeholder="sid">
|
<input type="text" id="node-config-input-sid" placeholder="sid">
|
||||||
</div>
|
</div>
|
||||||
<p>Note: use <code>ip</code> or <code>sid</code> - <code>sid</code> is better.</p>
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/x-red" data-help-name="<%= NODES_PREFIX %>-yeelight configurator">
|
<script type="text/x-red" data-help-name="<%= NODES_PREFIX %>-yeelight configurator">
|
||||||
|
|||||||
@@ -1,31 +1,49 @@
|
|||||||
import { Red, Node, NodeProperties, NodeStatus, ClearNodeStatus } from "node-red";
|
import {Red, Node, NodeProperties, NodeStatus, ClearNodeStatus} from "node-red";
|
||||||
import { Constants } from "../constants";
|
import {Constants} from "../constants";
|
||||||
import { Searcher } from "./Searcher";
|
import {YeelightServer} from "../../devices/yeelight/YeelightServer";
|
||||||
|
|
||||||
export interface IYeelightConfiguratorNode extends Node {
|
export interface IYeelightConfiguratorNode extends Node {
|
||||||
ip:string;
|
ip: string;
|
||||||
sid:number;
|
sid: number;
|
||||||
bulb:any;
|
bulb: any;
|
||||||
|
|
||||||
on(event: "bulbFound", listener: () => void): any;
|
on(event: "bulb-online", listener: () => void): any;
|
||||||
|
on(event: "bulb-offline", listener: () => void): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (RED:Red) => {
|
export default (RED: Red) => {
|
||||||
class YeelightConfigurator {
|
class YeelightConfigurator {
|
||||||
ip:string;
|
ip: string;
|
||||||
sid:number;
|
sid: number;
|
||||||
_bulb:any;
|
_bulb: any;
|
||||||
|
|
||||||
constructor(props: NodeProperties) {
|
constructor(props: NodeProperties) {
|
||||||
RED.nodes.createNode(<any> this, props);
|
RED.nodes.createNode(<any> this, props);
|
||||||
let {ip, sid} = <any> props;
|
let {sid} = <any> props;
|
||||||
this.sid = sid;
|
this.sid = parseInt(sid);
|
||||||
this.ip = ip;
|
|
||||||
|
if (this.sid) {
|
||||||
|
this.setBulb();
|
||||||
|
}
|
||||||
|
let server = YeelightServer.getInstance();
|
||||||
|
|
||||||
|
server.on('yeelight-online', (sid) => {
|
||||||
|
if (sid === this.sid) {
|
||||||
|
this.setBulb();
|
||||||
|
(<any> this).emit('bulb-online');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('yeelight-offline', (sid) => {
|
||||||
|
if (sid === this.sid) {
|
||||||
|
this._bulb = null;
|
||||||
|
(<any> this).emit('bulb-offline');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
set bulb(bulb) {
|
protected setBulb() {
|
||||||
this._bulb = bulb;
|
this._bulb = YeelightServer.getInstance().getBulb(this.sid);
|
||||||
(<any> this).emit('bulbFound');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get bulb() {
|
get bulb() {
|
||||||
@@ -35,7 +53,10 @@ export default (RED:Red) => {
|
|||||||
|
|
||||||
RED.nodes.registerType(`${Constants.NODES_PREFIX}-yeelight configurator`, <any> YeelightConfigurator, {
|
RED.nodes.registerType(`${Constants.NODES_PREFIX}-yeelight configurator`, <any> YeelightConfigurator, {
|
||||||
settings: {
|
settings: {
|
||||||
miDevicesYeelightConfiguratorDiscoveredBulbs: { value: Searcher.bulbs, exportable: true }
|
miDevicesYeelightConfiguratorDiscoveredBulbs: {
|
||||||
|
value: YeelightServer.getInstance().bulbs,
|
||||||
|
exportable: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,50 +1,79 @@
|
|||||||
import { Red, Node, NodeProperties, NodeStatus, ClearNodeStatus } from "node-red";
|
import {Red, Node, NodeProperties, NodeStatus, ClearNodeStatus} from "node-red";
|
||||||
import { Constants } from "../constants";
|
import {Constants} from "../constants";
|
||||||
import { IYeelightConfiguratorNode } from "./YeelightConfigurator";
|
import {IYeelightConfiguratorNode} from "./YeelightConfigurator";
|
||||||
|
|
||||||
export interface IYeelightOutNode {
|
export interface IYeelightOutNode {
|
||||||
yeelightConfNode:IYeelightConfiguratorNode;
|
yeelightConf: IYeelightConfiguratorNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (RED:Red) => {
|
export default (RED: Red) => {
|
||||||
class YeelightOut implements IYeelightOutNode {
|
class YeelightOut implements IYeelightOutNode {
|
||||||
yeelightConfNode:IYeelightConfiguratorNode;
|
yeelightConf: IYeelightConfiguratorNode;
|
||||||
|
|
||||||
constructor(props: NodeProperties) {
|
constructor(props: NodeProperties) {
|
||||||
RED.nodes.createNode(<any> this, props);
|
RED.nodes.createNode(<any> this, props);
|
||||||
this.yeelightConfNode = <any> RED.nodes.getNode((<any> props).yeelight);
|
this.yeelightConf = <any> RED.nodes.getNode((<any> props).yeelight);
|
||||||
|
|
||||||
(<any> this).status({fill: "red", shape: "ring", text: "offline"});
|
|
||||||
this.yeelightConfNode && this.yeelightConfNode.on('bulbFound', () => {
|
(<any>this).status({fill: "red", shape: "ring", text: "offline"});
|
||||||
(<any>this).status({fill:"blue", shape:"dot", text: "online"});
|
|
||||||
});
|
if (this.yeelightConf.bulb) {
|
||||||
|
this.yeelightOnline();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.yeelightConf.on('bulb-online', () => this.yeelightOnline());
|
||||||
|
this.yeelightConf.on('bulb-offline', () => this.yeelightOffline());
|
||||||
|
|
||||||
this.setListener();
|
this.setListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected yeelightOnline() {
|
||||||
|
(<any>this).status({fill: "blue", shape: "dot", text: "online"});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected yeelightOffline() {
|
||||||
|
(<any>this).status({fill: "red", shape: "ring", text: "offline"});
|
||||||
|
}
|
||||||
|
|
||||||
protected setListener() {
|
protected setListener() {
|
||||||
(<any> this).on('input', (msg) => {
|
(<any> this).on("input", (msg) => {
|
||||||
if (this.yeelightConfNode.bulb) {
|
let bulb = this.yeelightConf.bulb;
|
||||||
if(msg.payload === "on") {
|
if (msg.hasOwnProperty("payload") && bulb) {
|
||||||
this.yeelightConfNode.bulb.turnOn();
|
switch (msg.payload.action) {
|
||||||
}
|
case 'turn_on':
|
||||||
else if(msg.payload === "off") {
|
bulb.turnOn();
|
||||||
this.yeelightConfNode.bulb.turnOff();
|
break;
|
||||||
}
|
case 'turn_off':
|
||||||
else if(msg.payload === "toggle") {
|
bulb.turnOff();
|
||||||
this.yeelightConfNode.bulb.toggle();
|
break;
|
||||||
}
|
case 'toggle':
|
||||||
|
bulb.toggle();
|
||||||
if(msg.payload.color !== undefined) {
|
break;
|
||||||
// TODO: revoir la couleur
|
case 'setLight':
|
||||||
this.yeelightConfNode.bulb.setRGB(msg.payload.color);
|
if (msg.payload.color !== undefined) {
|
||||||
}
|
let rgb = msg.payload.color.blue | (msg.payload.color.green << 8) | (msg.payload.color.red << 16);
|
||||||
if(msg.payload.brightness !== undefined) {
|
let hex = '#' + (0x1000000 + rgb).toString(16).slice(1);
|
||||||
this.yeelightConfNode.bulb.setBrightness(msg.payload.brightness);
|
bulb.setRGB(hex);
|
||||||
|
}
|
||||||
|
(msg.payload.brightness !== undefined) && bulb.setBrightness(Math.max(1, msg.payload.brightness));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
/*(<any> this).on('input', (msg) => {
|
||||||
|
if (this.yeelightConf.bulb) {
|
||||||
|
|
||||||
|
|
||||||
|
if(msg.payload.color !== undefined) {
|
||||||
|
// TODO: revoir la couleur
|
||||||
|
this.yeelightConf.bulb.setRGB(msg.payload.color);
|
||||||
|
}
|
||||||
|
if(msg.payload.brightness !== undefined) {
|
||||||
|
this.yeelightConf.bulb.setBrightness(msg.payload.brightness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { Red, NodeProperties } from "node-red";
|
import { Red } from "node-red";
|
||||||
import * as YeelightSearch from 'yeelight-wifi';
|
|
||||||
|
|
||||||
import { Searcher } from "./Searcher";
|
import { YeelightServer } from "../../devices/yeelight/YeelightServer";
|
||||||
import {default as YeelightConfigurator} from "./YeelightConfigurator";
|
import {default as YeelightConfigurator} from "./YeelightConfigurator";
|
||||||
import {default as YeelightOut} from "./YeelightOut";
|
import {default as YeelightOut} from "./YeelightOut";
|
||||||
|
|
||||||
export = (RED:Red) => {
|
export = (RED:Red) => {
|
||||||
Searcher.discover(RED);
|
YeelightServer.getInstance().discover();
|
||||||
|
|
||||||
YeelightConfigurator(RED);
|
YeelightConfigurator(RED);
|
||||||
YeelightOut(RED);
|
YeelightOut(RED);
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
"inlineSourceMap": false,
|
"inlineSourceMap": false,
|
||||||
"outDir": "dist/",
|
"outDir": "dist/",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"moduleResolution": "node"
|
"moduleResolution": "node",
|
||||||
|
"noImplicitAny": false
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user