UNPKG

node-red-contrib-ewelink-cube

Version:

Node-RED integration with eWeLink Cube

389 lines (363 loc) 15.2 kB
const _ = require('lodash'); const { convertHexToRgb } = require('../utils/tools'); const { getDeviceInfo } = require('../utils/device'); const { v2CapabilityToV1Capability } = require('../utils/capabilitiesTransform'); function v2StateDataToV1StateData(v2Data) { try { return _v2StateDataToV1StateData(v2Data); } catch (error) { console.log('v2StateDataToV1StateData-----------error', error); return null; } } function frontEndDataToV2StateData(frontEndData) { try { return _frontEndDataToV2StateData(frontEndData); } catch (error) { console.log('frontEndDataToV2StateData-----------error', error); return null; } } /** power和toggle反转和保持的处理 */ function v2StateDataToRealStateData(v2Data, deviceId, serverId) { try { return _v2StateDataToRealStateData(v2Data, deviceId, serverId); } catch (error) { console.log('v2StateDataToRealStateData-----------error', error); } } function _frontEndDataToV2StateData(frontEndData) { const v2StateData = {}; const v2Capabilities = []; const ihost = {}; switch (frontEndData.type) { case 'single': // {"type":"single","single":"reverse"} const power = { powerState: frontEndData.single }; _.merge(v2StateData, { power }); break; case 'multi': // {"multi":{"1":"keep","2":"off","3":"on","4":"on"},"type":"multi"} const multiObj = frontEndData.multi; for (const toggleIndex in multiObj) { const toggle = { [toggleIndex]: { toggleState: multiObj[toggleIndex] } }; _.merge(v2StateData, { toggle }); } break; case 'light': //{"type":"light","light":{"power":"on","brightness":"20","type":"color-temperature","colorOrTemp":"43","hslStr":""}} // {"type":"light","light":{"power":"on","brightness":"20","type":"color-rgb","colorOrTemp":"#4769ff","hslStr":"rgb(71, 105, 255)"}} for (const item in frontEndData.light) { if (frontEndData.light[item]) { if ([item] == 'power') { const power = { powerState: frontEndData.light[item] }; _.merge(v2StateData, { power }); } else if ([item] == 'brightness') { const brightness = { brightness: Number(frontEndData.light[item]) }; _.merge(v2StateData, { brightness }); } else if ([item] == 'colorOrTemp') { if (frontEndData.light.type === 'color-temperature') { const colorTemperatureObj = { colorTemperature: Number(frontEndData.light[item]), }; _.merge(v2StateData, { 'color-temperature': colorTemperatureObj }); } else if (frontEndData.light.type === 'color-rgb') { if (frontEndData.light.hslStr) { const [red, green, blue] = frontEndData.light.hslStr.match(/\d+/g).map(Number); const rgbObj = { red, green, blue, }; _.merge(v2StateData, { 'color-rgb': rgbObj }); } } } } } break; case 'curtain': // {"type":"curtain","curtain":"open"} // {"type":"curtain","curtain":34} let curtain = {}; if (typeof frontEndData.curtain === 'string') { curtain = { 'motor-control': { motorControl: frontEndData.curtain, }, }; } else { curtain = { percentage: { percentage: frontEndData.curtain } }; } _.merge(v2StateData, curtain); break; case 'fanLight': //{"type":"fanLight","lightVal":"on","fanVal":"low"} if (frontEndData.lightVal) { _.merge(v2StateData, { toggle: { 1: { toggleState: frontEndData.lightVal, }, }, }); } if (frontEndData.fanVal) { if (['off','keep'].includes(frontEndData.fanVal)) { _.merge(v2StateData, { toggle: { 2: { toggleState: frontEndData.fanVal, }, }, }); } else { _.merge(v2StateData, { mode: { fanLevel: { modeValue: frontEndData.fanVal, }, }, }); } } break; case 'lightStrip': // {"type":"lightStrip","lightStripParams":{"power":"on","stripMode":"colorTemperature","brightness":"23","colorTemp":"34","hasModeCapabilty":true}} // 开关 const powerState = frontEndData.lightStripParams.power const powerObj = { powerState }; _.merge(v2StateData, { power: powerObj }); // 当开关是开的时候才允许操作亮度色温颜色模式 if (powerState === 'on') { // 亮度 if (frontEndData.lightStripParams.brightness) { const brightness = { brightness: Number(frontEndData.lightStripParams.brightness) }; _.merge(v2StateData, { brightness }); } const lightStripMode = frontEndData.lightStripParams.stripMode; // 色温 if (lightStripMode === 'colorTemperature' && frontEndData.lightStripParams.colorTemp) { const colorTemperatureObj = { 'color-temperature': { colorTemperature: Number(frontEndData.lightStripParams.colorTemp), }, }; _.merge(v2StateData, colorTemperatureObj); } // 颜色 if (lightStripMode === 'color' && frontEndData.lightStripParams.hslStr) { const rgb = convertHexToRgb(frontEndData.lightStripParams.hslStr); const [red, green, blue] = rgb.match(/\d+/g).map(Number); const rgbObj = { red, green, blue, }; _.merge(v2StateData, { 'color-rgb': rgbObj }); } // 模式 const hasModeCapabilty = frontEndData.lightStripParams.hasModeCapabilty; if (hasModeCapabilty) { _.merge(v2StateData, { mode: { lightMode: { modeValue: lightStripMode, }, }, }); } } break; case 'thermostat': _.merge({ state: v2StateData, capabilities: v2Capabilities }, _thermostatChange(frontEndData)); break; case 'ihost': _.merge({ ihost }, { ihost: frontEndData.ihostParams }); break; default: break; } return { state: v2StateData, capabilities: v2Capabilities, ihost, }; } function _thermostatChange(frontEndData) { const v2StateData = {}; const v2Capabilities = []; if (frontEndData.thermostatParams['checkTemp']) { const obj = { capability: 'temperature', permission: '1010', settings: { temperatureCalibration: { value: Number(frontEndData.thermostatParams['checkTemp']), }, }, }; v2Capabilities.push(obj); } if (frontEndData.thermostatParams['isTargetOrMode'] === 'targetTemperature' && frontEndData.thermostatParams['targetTemp']) { // set target temperature const obj = { 'thermostat-target-setpoint': { 'manual-mode': { targetSetpoint: Number(frontEndData.thermostatParams['targetTemp']), }, }, }; _.merge(v2StateData, obj); } else if ( frontEndData.thermostatParams['isTargetOrMode'] === 'thermostatMode' && frontEndData.thermostatParams['thermostatMode'] && frontEndData.thermostatParams['thermostatMode'] !== 'keep' ) { // set mode const thermostatObj = { thermostat: { 'thermostat-mode': { thermostatMode: frontEndData.thermostatParams['thermostatMode'], }, }, }; _.merge(v2StateData, thermostatObj); } const frostProtectionTemp = frontEndData.thermostatParams['frostProtectionTemp']; if (frostProtectionTemp) { const obj = { 'thermostat-target-setpoint': { 'eco-mode': { targetSetpoint: Number(frostProtectionTemp), }, }, }; _.merge(v2StateData, obj); } const childLock = frontEndData.thermostatParams['childLock']; if (childLock && childLock !== 'keep') { const obj = { 'child-lock': { powerState: childLock, }, }; _.merge(v2StateData, obj); } const openWindowsDetect = frontEndData.thermostatParams['openWindowsDetect']; if (openWindowsDetect && openWindowsDetect !== 'keep') { const obj = { 'window-detection': { powerState: openWindowsDetect, }, }; _.merge(v2StateData, obj); } return { state: v2StateData, capabilities: v2Capabilities, }; } // power和toggle能力的state是否存在反转或者保持的状态 function _isExistReverseOrKeep(v2Data) { const powerObj = _.get(v2Data.state, ['power'], {}); const toggleObj = _.get(v2Data.state, ['toggle'], {}); const powerAndToggleString = JSON.stringify(powerObj) + JSON.stringify(toggleObj); if (powerAndToggleString.indexOf('reverse') > -1 || powerAndToggleString.indexOf('keep') > -1) { return true; } return false; } function _v2StateDataToV1StateData(v2Data) { let v1StateData = _.cloneDeep(v2Data.state); const specialNewCapabilities = ['window-detection', 'child-lock', 'horizontal-swing', 'vertical-swing', 'eco', 'anti-direct-blow']; const newCapabilities = ['smoke', 'contact', 'motion', 'water-leak', 'lqi']; const newCapability2ToggleName = { 'window-detection': 'open-window-detect', }; const deviceState = v2Data.state ?? {}; const stateKeys = Object.keys(deviceState); const specialToggleStates = []; let toggleState; for (const stateKey of stateKeys) { if (newCapabilities.includes(stateKey)) continue; if (stateKey === 'toggle') { toggleState = deviceState[stateKey]; } else if (specialNewCapabilities.includes(stateKey)) { const restoreStateKey = newCapability2ToggleName[stateKey] ?? stateKey; specialToggleStates.push({ [restoreStateKey]: { toggleState: deviceState[stateKey].powerState, updated_at: deviceState[stateKey].updated_at, }, }); delete deviceState[stateKey]; } else if (stateKey === 'thermostat') { v1StateData[stateKey] = { 'thermostat-mode': { thermostatMode: deviceState[stateKey]['thermostat-mode'] } }; } else { v1StateData[stateKey] = deviceState[stateKey]; } } if (specialToggleStates.length > 0) { if (!toggleState) toggleState = {}; for (const specialToggleState of specialToggleStates) { toggleState = Object.assign({}, toggleState, specialToggleState); } } if (toggleState) { v1StateData['toggle'] = toggleState; } const v1Capabilities = v2CapabilityToV1Capability(v2Data.capabilities); return { state: v1StateData, capabilities: v1Capabilities, ihost: v2Data.ihost, }; } async function _v2StateDataToRealStateData(v2Data, deviceId, serverId) { // 不用处理反转和保持的state if (!_isExistReverseOrKeep(v2Data)) { return v2Data; } const deviceData = await getDeviceInfo(deviceId, serverId); const originState = deviceData.state; const { state, capabilities } = v2Data; for (capaKey in state) { if (capaKey === 'power') { const nowPowerState = _.get(state, ['power', 'powerState'], 'off'); if (nowPowerState === 'reverse') { const originPowerState = _.get(originState, ['power', 'powerState'], 'off'); state['power']['powerState'] = originPowerState === 'on' ? 'off' : 'on'; } } if (capaKey === 'toggle') { let toggleObj = _.get(state, ['toggle']); const originToggleObj = _.get(originState, ['toggle'], {}); let isAllKeepState = Object.values(toggleObj).every((item) => item.toggleState === 'keep'); if (isAllKeepState) { toggleObj = 'allKeepStateError'; } else { for (const toggleIndex in toggleObj) { const originToggleState = _.get(originToggleObj, [toggleIndex, 'toggleState']); const toggleState = _.get(toggleObj, [toggleIndex, 'toggleState']); if (toggleState === 'reverse') { toggleObj[toggleIndex].toggleState = originToggleState === 'on' ? 'off' : 'on'; } if (toggleState === 'keep') { toggleObj[toggleIndex].toggleState = originToggleState; } } } state['toggle'] = toggleObj; } } return { state, capabilities, }; } module.exports = { frontEndDataToV2StateData, v2StateDataToV1StateData, v2StateDataToRealStateData, };