node-red-contrib-miio-localdevices
Version:
Node for Node-Red to control Mi Devices locally via node-mihome (Humidifiers, Air Purifiers, Heaters, Fans, Vacuums, Lights - list of devices to be enlarged). See latest updates in documentation.
195 lines (176 loc) • 6.76 kB
JavaScript
const MIIOcommandsVocabulary = require('../lib/commandsLib.js');
const MIIOdevtypesVocabulary = require('../lib/devtypesLib.js');
const EventEmitter = require('events');
const mihome = require('node-mihome');
module.exports = function(RED) {
function MIIOdevicesNode(n) {
RED.nodes.createNode(this,n);
let node = this;
node.setMaxListeners(255);
node.name = n.name;
node.room = n.room;
node.MI_id = n.MI_id;
node.device_type = n.device_type;
node.model = n.model;
node.address = n.address;
node.token = n.token;
node.isMIOT = n.isMIOT;
node.username = n.username;
node.password = n.password
node.isPolling = n.isPolling;
node.pollinginterval = n.pollinginterval;
// 0) Transfering data from runtime to filter commands CONFIG-node and commands in SEND-node
var NODE_PATH = '/node-red-contrib-miio-localdevices/nodes/';
RED.httpAdmin.get(NODE_PATH + 'getHumidList/', function (req, res) {
var ImportedHumidList = MIIOdevtypesVocabulary.humid_list();
res.json(ImportedHumidList);
});
RED.httpAdmin.get(NODE_PATH + 'getPurifList/', function (req, res) {
var ImportedPurifList = MIIOdevtypesVocabulary.purif_list();
res.json(ImportedPurifList);
});
RED.httpAdmin.get(NODE_PATH + 'getHeatFanList/', function (req, res) {
var ImportedHeatFanList = MIIOdevtypesVocabulary.heatfan_list();
res.json(ImportedHeatFanList);
});
RED.httpAdmin.get(NODE_PATH + 'getVacuumList/', function (req, res) {
var ImportedVacuumList = MIIOdevtypesVocabulary.vacuum_list();
res.json(ImportedVacuumList);
});
RED.httpAdmin.get(NODE_PATH + 'getLightsList/', function (req, res) {
var ImportedLightsList = MIIOdevtypesVocabulary.light_list();
res.json(ImportedLightsList);
});
RED.httpAdmin.get(NODE_PATH + 'getCommands/' + node.id, function (req, res) {
var ModelForCommand = node.model;
var ImportedJSON = MIIOcommandsVocabulary.command_list(ModelForCommand);
res.json(ImportedJSON);
});
// 1) Initialization of MI Protocols
MiioConnect ();
MiotConnect ();
// 2) Setting up the device
const device = mihome.device({
id: node.MI_id,
model: node.model,
address: node.address,
token: node.token,
});
// 3) Defining auto-polling variables
Poll_or_Not = node.isPolling;
if (node.pollinginterval == null) {Polling_Interval = 30}
else {Polling_Interval = node.pollinginterval};
// 4) Tiding Up after device is destroyed
node.on('close', () => OnClose());
// 5) Main Function - Polling the device
OldData = {};
ConnDevice().then((data) => {
data = OldData;
node.emit('onInit', data);
});
// 6) Commands from send-node
ExecuteSingleCMD ();
ExecuteJsonCMD ();
// 7) Auto-polling cycle
setTimeout(function run() {
// 7.1) stop auto-polling cycle
if (Poll_or_Not == false) {return};
// 7.2) continue auto-polling cycle
if (Poll_or_Not == true && Polling_Interval > 0) {
// 7.2.1) re-define auto-polling interval
if (node.pollinginterval == null) {New_Interval = 30}
else {New_Interval = node.pollinginterval};
// 7.2.2) check for changing the Interval, if changed then stop previous cycle
if (New_Interval == Polling_Interval) {
ConnDevice ();
setTimeout(run, Polling_Interval * 1000);
};
};
}, Polling_Interval * 1000);
// functions in USE:
// A) Initializing MiLocal
function MiioConnect () {
mihome.miioProtocol.init();
};
// B) Logging into MiCloud if needed
async function MiotConnect () {
MIOT_login = node.isMIOT;
if (MIOT_login == true) {
await mihome.miCloudProtocol.login(node.username, node.password);
} else {return};
};
// C) OnClose Destroying
function OnClose () {
device.destroy();
};
// D) Main Function - Polling the device
async function ConnDevice () {
try {
// D.1) connect to device and poll for properties
await device.on('properties', (data) => {
NewData = data;
// D.1.1) check for any changes in properties
for (var key in NewData) {
var value = NewData[key];
if (key in OldData) {
if (OldData[key] !== value) {
node.emit('onChange', data);
OldData = data;
}
}
};
// D.1.2) case with no changes in properties
OldData = data;
});
await device.init();
device.destroy();
} catch (exception) {
// D.2) catching errors from MIHOME Protocol
PollError = `Mihome Exception. IP: ${node.address} -> ${exception.message}`;
node.emit('onError', PollError);
}
};
// E) Executing single command from send-node
function ExecuteSingleCMD () {
node.on('onSingleCommand', async function (SingleCMD, SinglePayload) {
try {
// E.1) Initializing device if MIOT
if (device._miotSpecType) {
await device.init();
};
// E.2) transfer command from input into device (in AWAIT mode)
await eval("device.set" + SingleCMD + "(" + SinglePayload + ")");
device.destroy();
} catch(exception) {
// E.3) catching errors from MIIO Protocol and sending back to send-node
SingleCMDErrorMsg = exception.message;
SingleCMDErrorCube = SingleCMD;
node.emit('onSingleCMDSentError', SingleCMDErrorMsg, SingleCMDErrorCube);
};
})
};
// F) Executing JSON command from send-node (for each Item in JSON asynchronously)
function ExecuteJsonCMD () {
node.on('onJsonCommand', async function (CustomJsonCMD) {
for (let key of Object.keys(CustomJsonCMD)) {
try {
// F.1) Initializing device if MIOT
if (device._miotSpecType) {
await device.init();
};
// F.2) transfer command from input into device (in AWAIT mode)
JsonItemX = [key] + "(" + CustomJsonCMD[key] + ")";
await eval("device.set" + JsonItemX);
device.destroy();
} catch(exception) {
// F.3) catching errors from MIIO Protocol and sending back to send-node
JsonCMDErrorMsg = exception.message;
JsonCMDErrorCube = CustomJsonCMD;
node.emit('onJsonCMDSentError', JsonCMDErrorMsg, JsonCMDErrorCube);
};
};
});
};
};
RED.nodes.registerType("MIIOdevices",MIIOdevicesNode);
}