UNPKG

node-red-contrib-xiaomi-miio

Version:
294 lines (227 loc) 8.95 kB
const miio = require('miio'); const util = require('util'); console.log('Miio path: ', require.resolve('miio')); var allDevices = { instance: Math.random() }; var optionDevices = []; var propertyCallbacks = {}; var actionCallbacks = {}; var disableEventListener = true; const devices = miio.devices({ cacheTime: 1800 // 30 minutes }); /* ======================================================================================================= */ devices.on('available', reg => { const device = reg.device; if(!device) { console.log(reg.id, 'could not be connected to'); return; } registerDevice(reg.id, device); }); /* ======================================================================================================= */ var registerDevicesManually = function(devicesWithTokens) { devicesWithTokens.forEach(deviceWithToken => { console.log('Register device manually: ', deviceWithToken); miio.device({ address: deviceWithToken.address, token: deviceWithToken.token }) .then(device => registerDevice(device.id.replace('miio:', '') ,device)) .catch(err => console.log('Device not found ', device, err)); }); console.log('Scan again for miio devices...'); miio.devices({cacheTime: 1800}).on('available', reg => { var device = reg.device; if(!device) { console.log(reg.id, 'could not be connected to'); return; } registerDevice(reg.id, device); }); } /* ======================================================================================================= */ var registerDevice = function(deviceId, device) { // make sure device id is string always deviceId = deviceId + ''; console.log('Detected device wit+h identifier ', deviceId, ' types ', device.metadata.types, ' model ', device.miioModel); console.log(device.properties) setupLegacySupport(deviceId, device); if (typeof allDevices[deviceId] === 'undefined') { optionDevices.push({ id: deviceId, model: device.miioModel, type: device.type }); allDevices[deviceId] = device; switch (device.type) { case 'gateway': handleGateway(deviceId, device); break; case 'air-purifier': handleAirPurifier(deviceId, device); break; case 'controller': //console.log('sub-device!' + util.inspect(device, { showHidden: true, depth: 5 }) ); handleController(deviceId, device); break; case 'magnet': handleMagnet(deviceId, device); break; case 'sensor': handleSensor(deviceId, device); break; case 'motion': handleMotion(deviceId, device); break; default: console.log('Skip device: ' + deviceId + ' type:' + device.type); } } else { console.log('Device already initialized.'); } } /* ======================================================================================================= */ devices.on('unavailable', device => { // Device is no longer available and is destroyed console.log('Device unavailable', device); }); /* ======================================================================================================= */ var setupLegacySupport = function(id, device) { // Fix the issue with miio library if (device.miioModel.indexOf('magnet') !== -1) { device.contact().then(contact => device.magnetContact = contact); setTimeout(function() { disableEventListener = false; console.log('Enabled event listeners'); }, 10000); } // backward compatibility stuff device.type = mapMiioModelToType(device.miioModel); device.model = device.miioModel; device.getAllProperties = function(resultCallback) { if (device.miioModel.indexOf('magnet') !== -1) { // Fix the issue with miio library: https://github.com/aholstenson/miio/issues/170 device.contact().then( val => resultCallback({ open: device.magnetContact === false, contact: device.magnetContact }) ); } else { resultCallback(device.properties); } } } /* ======================================================================================================= */ var mapMiioModelToType = function(model) { if (model.indexOf('airpurifier') !== -1) { return 'air-purifier'; } else if (model.indexOf('cube') !== -1) { return 'controller'; } else if (model.indexOf('magnet') !== -1) { return 'magnet'; } else if (model.indexOf('motion') !== -1) { return 'motion'; } else if (model.indexOf('sensor_ht') !== -1) { return 'sensor'; } else if (model.indexOf('switch') !== -1) { return 'controller'; } else if (model.indexOf('gateway') !== -1) { return 'gateway'; } else if (model.indexOf('repeater') !== -1) { return 'repeater'; } else { console.warn('Unknown model ', model); return 'unknown'; } } /* ======================================================================================================= */ function triggerCallback(id, deviceCallbacks, event, device) { // console.log('event', ' ', id, ' ', arg1, ' ', arg2, ' ', arg3); var callbacks = deviceCallbacks[id]; if (callbacks) { for (var i = 0, len = callbacks.length; i < len; i++) { var callback = callbacks[i]; if (typeof callback === "function") { event = (typeof event === 'undefined') ? {} : event; // event.device = device; callback(event); } } } } /* ======================================================================================================= */ function hookGenericStateAndAction(id, device) { device.on('stateChanged', e => { if (disableEventListener) { return; } var event = { deviceId: id, key: e.key, value: e.value, model: device.model }; // Fix the issue with miio library: https://github.com/aholstenson/miio/issues/170 if ( (device.miioModel.indexOf('magnet') !== -1) && (e.key == 'contact') ) { device.magnetContact = e.value; } triggerCallback(id, propertyCallbacks, event, device); }); device.on('action', e => { if (disableEventListener) { return; } var event = { deviceId: id, action: e.action, model: device.model, amount: e.data.amount }; triggerCallback(id, actionCallbacks, event, device); }); } /* ======================================================================================================= */ function handleGateway(id, device) { // Handlers for Gateway events hookGenericStateAndAction(id, device); } /* ======================================================================================================= */ function handleController(id, device) { hookGenericStateAndAction(id, device); } /* ======================================================================================================= */ function handleMagnet(id, device) { hookGenericStateAndAction(id, device); } /* ======================================================================================================= */ function handleSensor(id, device) { hookGenericStateAndAction(id, device); } /* ======================================================================================================= */ function handleAirPurifier(id, device) { hookGenericStateAndAction(id, device); } /* ======================================================================================================= */ function handleMotion(id, device) { hookGenericStateAndAction(id, device); } /* ======================================================================================================= */ function addCallbacks(deviceId, deviceCallbacks, callback) { if (!deviceCallbacks[deviceId]) { deviceCallbacks[deviceId] = []; } deviceCallbacks[deviceId].push(callback); } /* ======================================================================================================= */ module.exports = { devices: allDevices, optionDevices: optionDevices, registerPropertyListener: function(deviceId, callback) { addCallbacks(deviceId, propertyCallbacks, callback); }, registerActionListener: function(deviceId, callback) { addCallbacks(deviceId, actionCallbacks, callback); }, registerDevicesManually: registerDevicesManually }