UNPKG

zigbee-herdsman-converters

Version:

Collection of device converters to be used with zigbee-herdsman

1,344 lines (1,296 loc) • 86.8 kB
'use strict'; const {isLegacyEnabled, precisionRound} = require('./utils'); const fromZigbeeConverters = require('../converters/fromZigbee'); const fromZigbeeStore = {}; const tuya = require('./tuya'); const constants = require('./constants'); // get object property name (key) by it's value const getKey = (object, value) => { for (const key in object) { if (object[key]==value) return key; } }; const numberWithinRange = (number, min, max) => { if (number > max) { return max; } else if (number < min) { return min; } else { return number; } }; const holdUpdateBrightness324131092621 = (deviceID) => { if (fromZigbeeStore[deviceID] && fromZigbeeStore[deviceID].brightnessSince && fromZigbeeStore[deviceID].brightnessDirection) { const duration = Date.now() - fromZigbeeStore[deviceID].brightnessSince; const delta = (duration / 10) * (fromZigbeeStore[deviceID].brightnessDirection === 'up' ? 1 : -1); const newValue = fromZigbeeStore[deviceID].brightnessValue + delta; fromZigbeeStore[deviceID].brightnessValue = numberWithinRange(newValue, 1, 255); } }; function getMetaValue(entity, definition, key, groupStrategy='first') { if (entity.constructor.name === 'Group' && entity.members.length > 0) { const values = []; for (const memberMeta of definition) { if (memberMeta.meta && memberMeta.meta.hasOwnProperty(key)) { if (groupStrategy === 'first') { return memberMeta.meta[key]; } values.push(memberMeta.meta[key]); } else { values.push(undefined); } } if (groupStrategy === 'allEqual' && (new Set(values)).size === 1) { return values[0]; } } else if (definition && definition.meta && definition.meta.hasOwnProperty(key)) { return definition.meta[key]; } return undefined; } const convertMultiByteNumberPayloadToSingleDecimalNumber = (chunks) => { // Destructuring "chunks" is needed because it's a Buffer // and we need a simple array. let value = 0; for (let i = 0; i < chunks.length; i++) { value = value << 8; value += chunks[i]; } return value; }; const tuyaGetDataValue = (dataType, data) => { switch (dataType) { case tuya.dataTypes.raw: return data; case tuya.dataTypes.bool: return data[0] === 1; case tuya.dataTypes.value: return convertMultiByteNumberPayloadToSingleDecimalNumber(data); case tuya.dataTypes.string: // eslint-disable-next-line let dataString = ''; // Don't use .map here, doesn't work: https://github.com/Koenkk/zigbee-herdsman-converters/pull/1799/files#r530377091 for (let i = 0; i < data.length; ++i) { dataString += String.fromCharCode(data[i]); } return dataString; case tuya.dataTypes.enum: return data[0]; case tuya.dataTypes.bitmap: return convertMultiByteNumberPayloadToSingleDecimalNumber(data); } }; const toPercentage = (value, min, max) => { if (value > max) { value = max; } else if (value < min) { value = min; } const normalised = (value - min) / (max - min); return Math.round(normalised * 100); }; const postfixWithEndpointName = (name, msg, definition) => { if (definition.meta && definition.meta.multiEndpoint) { const endpointName = definition.hasOwnProperty('endpoint') ? getKey(definition.endpoint(msg.device), msg.endpoint.ID) : msg.endpoint.ID; return `${name}_${endpointName}`; } else { return name; } }; const transactionStore = {}; const hasAlreadyProcessedMessage = (msg, transaction=null, key=null) => { const current = transaction !== null ? transaction : msg.meta.zclTransactionSequenceNumber; key = key || msg.device.ieeeAddr; if (transactionStore[key] === current) return true; transactionStore[key] = current; return false; }; const addActionGroup = (payload, msg, definition) => { const disableActionGroup = definition.meta && definition.meta.disableActionGroup; if (!disableActionGroup && msg.groupID) { payload.action_group = msg.groupID; } }; const ratelimitedDimmer = (model, msg, publish, options, meta) => { const deviceID = msg.device.ieeeAddr; const payload = {}; let duration = 0; if (!fromZigbeeStore[deviceID]) { fromZigbeeStore[deviceID] = {lastmsg: false}; } const s = fromZigbeeStore[deviceID]; if (s.lastmsg) { duration = Date.now() - s.lastmsg; } else { s.lastmsg = Date.now(); } if (duration > 500) { s.lastmsg = Date.now(); payload.action = 'brightness'; payload.brightness = msg.data.level; publish(payload); } }; const ictcg1 = (model, msg, publish, options, action) => { const deviceID = msg.device.ieeeAddr; const payload = {}; if (!fromZigbeeStore[deviceID]) { fromZigbeeStore[deviceID] = {since: false, direction: false, value: 255, publish: publish}; } const s = fromZigbeeStore[deviceID]; // if rate == 70 so we rotate slowly const rate = (msg.data.rate == 70) ? 0.3 : 1; if (action === 'move') { s.since = Date.now(); const direction = msg.data.movemode === 1 ? 'left' : 'right'; s.direction = direction; payload.action = `rotate_${direction}`; } else if (action === 'level') { s.value = msg.data.level; const direction = s.value === 0 ? 'left' : 'right'; payload.action = `rotate_${direction}_quick`; payload.brightness = s.value; } else if (action === 'stop') { if (s.direction) { const duration = Date.now() - s.since; const delta = Math.round(rate * (duration / 10) * (s.direction === 'left' ? -1 : 1)); const newValue = s.value + delta; if (newValue >= 0 && newValue <= 255) { s.value = newValue; } } payload.action = 'rotate_stop'; payload.brightness = s.value; s.direction = false; } if (s.timerId) { clearInterval(s.timerId); s.timerId = false; } if (action === 'move') { s.timerId = setInterval(() => { const duration = Date.now() - s.since; const delta = Math.round(rate * (duration / 10) * (s.direction === 'left' ? -1 : 1)); const newValue = s.value + delta; if (newValue >= 0 && newValue <= 255) { s.value = newValue; } payload.brightness = s.value; s.since = Date.now(); s.publish(payload); }, 200); } return payload.brightness; }; const fromZigbee = { WXKG11LM_click: { cluster: 'genOnOff', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const data = msg.data; let clicks; if (data.onOff) { clicks = 1; } else if (data['32768']) { clicks = data['32768']; } const actionLookup = {1: 'single', 2: 'double', 3: 'triple', 4: 'quadruple'}; if (actionLookup[clicks]) { return {click: actionLookup[clicks]}; } } }, }, SmartButton_skip: { cluster: 'genLevelCtrl', type: 'commandStep', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const direction = msg.data.stepmode === 1 ? 'backward' : 'forward'; return { action: `skip_${direction}`, step_size: msg.data.stepsize, transition_time: msg.data.transtime, }; } else { return fromZigbeeConverters.command_step.convert(model, msg, publish, options, meta); } }, }, konke_click: { cluster: 'genOnOff', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const value = msg.data['onOff']; const lookup = { 128: {click: 'single'}, // single click 129: {click: 'double'}, // double and many click 130: {click: 'long'}, // hold }; return lookup[value] ? lookup[value] : null; } }, }, xiaomi_action_click_multistate: { cluster: 'genMultistateInput', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const value = msg.data['presentValue']; const lookup = { 1: {click: 'single'}, // single click 2: {click: 'double'}, // double click }; return lookup[value] ? lookup[value] : null; } }, }, WXKG12LM_action_click_multistate: { cluster: 'genMultistateInput', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const value = msg.data['presentValue']; const lookup = { 1: {click: 'single'}, // single click 2: {click: 'double'}, // double click }; return lookup[value] ? lookup[value] : null; } }, }, terncy_raw: { cluster: 'manuSpecificClusterAduroSmart', type: 'raw', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { // 13,40,18,104, 0,8,1 - click // 13,40,18,22, 0,17,1 // 13,40,18,32, 0,18,1 // 13,40,18,6, 0,16,1 // 13,40,18,111, 0,4,2 - double click // 13,40,18,58, 0,7,2 // 13,40,18,6, 0,2,3 - triple click // motion messages: // 13,40,18,105, 4,167,0,7 - motion on right side // 13,40,18,96, 4,27,0,5 // 13,40,18,101, 4,27,0,7 // 13,40,18,125, 4,28,0,5 // 13,40,18,85, 4,28,0,7 // 13,40,18,3, 4,24,0,5 // 13,40,18,81, 4,10,1,7 // 13,40,18,72, 4,30,1,5 // 13,40,18,24, 4,25,0,40 - motion on left side // 13,40,18,47, 4,28,0,56 // 13,40,18,8, 4,32,0,40 let value = {}; if (msg.data[4] == 0) { value = msg.data[6]; if (1 <= value && value <= 3) { const actionLookup = {1: 'single', 2: 'double', 3: 'triple', 4: 'quadruple'}; return {click: actionLookup[value]}; } } } }, }, CCTSwitch_D0001_on_off: { cluster: 'genOnOff', type: ['commandOn', 'commandOff'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'power'}; } }, }, ptvo_switch_buttons: { cluster: 'genMultistateInput', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = getKey(model.endpoint(msg.device), msg.endpoint.ID); const value = msg.data['presentValue']; const actionLookup = { 0: 'release', 1: 'single', 2: 'double', 3: 'tripple', 4: 'hold', }; const action = actionLookup[value]; if (button) { return {click: button + (action ? `_${action}` : '')}; } } }, }, ZGRC013_brightness_onoff: { cluster: 'genLevelCtrl', type: 'commandMoveWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = msg.endpoint.ID; const direction = msg.data.movemode == 0 ? 'up' : 'down'; if (button) { return {click: `${button}_${direction}`}; } } }, }, ZGRC013_brightness_stop: { cluster: 'genLevelCtrl', type: 'commandStopWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = msg.endpoint.ID; if (button) { return {click: `${button}_stop`}; } } }, }, ZGRC013_scene: { cluster: 'genScenes', type: 'commandRecall', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: `scene_${msg.data.groupid}_${msg.data.sceneid}`}; } }, }, ZGRC013_cmdOn: { cluster: 'genOnOff', type: 'commandOn', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = msg.endpoint.ID; if (button) { return {click: `${button}_on`}; } } }, }, ZGRC013_cmdOff: { cluster: 'genOnOff', type: 'commandOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = msg.endpoint.ID; if (button) { return {click: `${button}_off`}; } } }, }, ZGRC013_brightness: { cluster: 'genLevelCtrl', type: 'commandMove', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = msg.endpoint.ID; const direction = msg.data.movemode == 0 ? 'up' : 'down'; if (button) { return {click: `${button}_${direction}`}; } } }, }, CTR_U_scene: { cluster: 'genScenes', type: 'commandRecall', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: `scene_${msg.data.groupid}_${msg.data.sceneid}`}; } }, }, st_button_state: { cluster: 'ssIasZone', type: 'commandStatusChangeNotification', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const buttonStates = { 0: 'off', 1: 'single', 2: 'double', 3: 'hold', }; if (msg.data.hasOwnProperty('data')) { const zoneStatus = msg.data.zonestatus; return {click: buttonStates[zoneStatus]}; } else { const zoneStatus = msg.data.zonestatus; return {click: buttonStates[zoneStatus]}; } } }, }, QBKG11LM_click: { cluster: 'genMultistateInput', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { if ([1, 2].includes(msg.data.presentValue)) { const times = {1: 'single', 2: 'double'}; return {click: times[msg.data.presentValue]}; } } }, }, QBKG12LM_click: { cluster: 'genMultistateInput', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { if ([1, 2].includes(msg.data.presentValue)) { const mapping = {5: 'left', 6: 'right', 7: 'both'}; const times = {1: 'single', 2: 'double'}; const button = mapping[msg.endpoint.ID]; return {click: `${button}_${times[msg.data.presentValue]}`}; } } }, }, QBKG03LM_QBKG12LM_click: { cluster: 'genOnOff', type: ['attributeReport'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { if (!msg.data['61440']) { const mapping = {4: 'left', 5: 'right', 6: 'both'}; const button = mapping[msg.endpoint.ID]; return {click: button}; } } }, }, QBKG04LM_QBKG11LM_click: { cluster: 'genOnOff', type: ['attributeReport'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { if (!msg.data['61440']) { return {click: 'single'}; } } }, }, cover_stop: { cluster: 'closuresWindowCovering', type: 'commandStop', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'release'}; } }, }, cover_open: { cluster: 'closuresWindowCovering', type: 'commandUpOpen', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'open'}; } }, }, cover_close: { cluster: 'closuresWindowCovering', type: 'commandDownClose', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'close'}; } }, }, WXKG03LM_click: { cluster: 'genOnOff', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'single'}; } }, }, TS0218_click: { cluster: 'ssIasAce', type: 'commandEmergency', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'click'}; } else { return fromZigbeeConverters.command_emergency.convert(model, msg, publish, options, meta); } }, }, xiaomi_on_off_action: { cluster: 'genOnOff', type: ['attributeReport'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: getKey(model.endpoint(msg.device), msg.endpoint.ID)}; } else { return fromZigbeeConverters.xiaomi_on_off_action.convert(model, msg, publish, options, meta); } }, }, WXKG02LM_click: { cluster: 'genOnOff', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const lookup = {1: 'left', 2: 'right', 3: 'both'}; return {click: lookup[msg.endpoint.ID]}; } }, }, WXKG02LM_click_multistate: { cluster: 'genMultistateInput', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { // Somestime WXKG02LM sends multiple messages on a single click, this prevents handling // of a message with the same transaction sequence number twice. const current = msg.meta.zclTransactionSequenceNumber; if (fromZigbeeStore[msg.device.ieeeAddr + 'legacy'] === current) return; fromZigbeeStore[msg.device.ieeeAddr + 'legacy'] = current; const buttonLookup = {1: 'left', 2: 'right', 3: 'both'}; const button = buttonLookup[msg.endpoint.ID]; const value = msg.data['presentValue']; const actionLookup = { 0: 'long', 1: null, 2: 'double', }; const action = actionLookup[value]; if (button) { if (isLegacyEnabled(options)) { return {click: button + (action ? `_${action}` : '')}; } } }, }, WXKG01LM_click: { cluster: 'genOnOff', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const deviceID = msg.device.ieeeAddr; const state = msg.data['onOff']; const key = `${deviceID}_legacy`; if (!fromZigbeeStore[key]) { fromZigbeeStore[key] = {}; } const current = msg.meta.zclTransactionSequenceNumber; if (fromZigbeeStore[key].transaction === current) return; fromZigbeeStore[key].transaction = current; // 0 = click down, 1 = click up, else = multiple clicks if (state === 0) { fromZigbeeStore[key].timer = setTimeout(() => { publish({click: 'long'}); fromZigbeeStore[key].timer = null; fromZigbeeStore[key].long = Date.now(); fromZigbeeStore[key].long_timer = setTimeout(() => { fromZigbeeStore[key].long = false; }, 4000); // After 4000 milliseconds of not reciving long_release we assume it will not happen. }, options.long_timeout || 1000); // After 1000 milliseconds of not releasing we assume long click. } else if (state === 1) { if (fromZigbeeStore[key].long) { const duration = Date.now() - fromZigbeeStore[key].long; publish({click: 'long_release', duration: duration}); fromZigbeeStore[key].long = false; } if (fromZigbeeStore[key].timer) { clearTimeout(fromZigbeeStore[key].timer); fromZigbeeStore[key].timer = null; publish({click: 'single'}); } } else { const clicks = msg.data['32768']; const actionLookup = {1: 'single', 2: 'double', 3: 'triple', 4: 'quadruple'}; const payload = actionLookup[clicks] ? actionLookup[clicks] : 'many'; publish({click: payload}); } } }, }, scenes_recall_click: { cluster: 'genScenes', type: 'commandRecall', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: msg.data.sceneid}; } }, }, AV2010_34_click: { cluster: 'genScenes', type: 'commandRecall', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: msg.data.groupid}; } }, }, E1743_brightness_down: { cluster: 'genLevelCtrl', type: 'commandMove', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'brightness_down'}; } }, }, E1743_brightness_up: { cluster: 'genLevelCtrl', type: 'commandMoveWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'brightness_up'}; } }, }, E1743_brightness_stop: { cluster: 'genLevelCtrl', type: 'commandStopWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'brightness_stop'}; } }, }, genOnOff_cmdOn: { cluster: 'genOnOff', type: 'commandOn', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'on'}; } }, }, genOnOff_cmdOff: { cluster: 'genOnOff', type: 'commandOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {click: 'off'}; } }, }, RM01_on_click: { cluster: 'genOnOff', type: 'commandOn', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = getKey(model.endpoint(msg.device), msg.endpoint.ID); return {action: `${button}_on`}; } else { return fromZigbeeConverters.command_on.convert(model, msg, publish, options, meta); } }, }, RM01_off_click: { cluster: 'genOnOff', type: 'commandOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = getKey(model.endpoint(msg.device), msg.endpoint.ID); return {action: `${button}_off`}; } else { return fromZigbeeConverters.command_off.convert(model, msg, publish, options, meta); } }, }, RM01_down_hold: { cluster: 'genLevelCtrl', type: 'commandStep', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = getKey(model.endpoint(msg.device), msg.endpoint.ID); return { action: `${button}_down`, step_mode: msg.data.stepmode, step_size: msg.data.stepsize, transition_time: msg.data.transtime, }; } else { return fromZigbeeConverters.command_step.convert(model, msg, publish, options, meta); } }, }, RM01_up_hold: { cluster: 'genLevelCtrl', type: 'commandStepWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = getKey(model.endpoint(msg.device), msg.endpoint.ID); return { action: `${button}_up`, step_mode: msg.data.stepmode, step_size: msg.data.stepsize, transition_time: msg.data.transtime, }; } else { return fromZigbeeConverters.command_step.convert(model, msg, publish, options, meta); } }, }, RM01_stop: { cluster: 'genLevelCtrl', type: 'commandStop', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const button = getKey(model.endpoint(msg.device), msg.endpoint.ID); return {action: `${button}_stop`}; } else { return fromZigbeeConverters.command_stop.convert(model, msg, publish, options, meta); } }, }, xiaomi_multistate_action: { cluster: 'genMultistateInput', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { // refactor to xiaomi_multistate_action] if (isLegacyEnabled(options)) { const button = getKey(model.endpoint(msg.device), msg.endpoint.ID); const value = msg.data['presentValue']; const actionLookup = {0: 'long', 1: null, 2: 'double'}; const action = actionLookup[value]; if (button) { return {action: `${button}${(action ? `_${action}` : '')}`}; } } else { return fromZigbeeConverters.xiaomi_multistate_action.convert(model, msg, publish, options, meta); } }, }, E1744_play_pause: { cluster: 'genOnOff', type: 'commandToggle', convert: (model, msg, publish, options, meta) => { if (hasAlreadyProcessedMessage(msg)) return; if (isLegacyEnabled(options)) { return {action: 'play_pause'}; } else { return fromZigbeeConverters.command_toggle.convert(model, msg, publish, options, meta); } }, }, E1744_skip: { cluster: 'genLevelCtrl', type: 'commandStep', convert: (model, msg, publish, options, meta) => { if (hasAlreadyProcessedMessage(msg)) return; if (isLegacyEnabled(options)) { const direction = msg.data.stepmode === 1 ? 'backward' : 'forward'; return { action: `skip_${direction}`, step_size: msg.data.stepsize, transition_time: msg.data.transtime, }; } else { return fromZigbeeConverters.command_step.convert(model, msg, publish, options, meta); } }, }, cmd_move: { cluster: 'genLevelCtrl', type: 'commandMove', convert: (model, msg, publish, options, meta) => { if (hasAlreadyProcessedMessage(msg)) return; if (isLegacyEnabled(options)) { ictcg1(model, msg, publish, options, 'move'); const direction = msg.data.movemode === 1 ? 'left' : 'right'; return {action: `rotate_${direction}`, rate: msg.data.rate}; } else { return fromZigbeeConverters.command_move.convert(model, msg, publish, options, meta); } }, }, cmd_move_with_onoff: { cluster: 'genLevelCtrl', type: 'commandMoveWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { ictcg1(model, msg, publish, options, 'move'); const direction = msg.data.movemode === 1 ? 'left' : 'right'; return {action: `rotate_${direction}`, rate: msg.data.rate}; } else { return fromZigbeeConverters.command_move.convert(model, msg, publish, options, meta); } }, }, cmd_stop: { cluster: 'genLevelCtrl', type: 'commandStop', convert: (model, msg, publish, options, meta) => { if (hasAlreadyProcessedMessage(msg)) return; if (isLegacyEnabled(options)) { const value = ictcg1(model, msg, publish, options, 'stop'); return {action: `rotate_stop`, brightness: value}; } else { return fromZigbeeConverters.command_stop.convert(model, msg, publish, options, meta); } }, }, cmd_stop_with_onoff: { cluster: 'genLevelCtrl', type: 'commandStopWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const value = ictcg1(model, msg, publish, options, 'stop'); return {action: `rotate_stop`, brightness: value}; } else { return fromZigbeeConverters.command_stop.convert(model, msg, publish, options, meta); } }, }, cmd_move_to_level_with_onoff: { cluster: 'genLevelCtrl', type: 'commandMoveToLevelWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const value = ictcg1(model, msg, publish, options, 'level'); const direction = msg.data.level === 0 ? 'left' : 'right'; return {action: `rotate_${direction}_quick`, level: msg.data.level, brightness: value}; } else { return fromZigbeeConverters.command_move_to_level.convert(model, msg, publish, options, meta); } }, }, immax_07046L_arm: { cluster: 'ssIasAce', type: 'commandArm', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const action = msg.data['armmode']; delete msg.data['armmode']; const modeLookup = { 0: 'disarm', 1: 'arm_stay', 3: 'arm_away', }; return {action: modeLookup[action]}; } else { return fromZigbeeConverters.command_arm.convert(model, msg, publish, options, meta); } }, }, KEF1PA_arm: { cluster: 'ssIasAce', type: 'commandArm', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const action = msg.data['armmode']; delete msg.data['armmode']; const modeLookup = { 0: 'home', 2: 'sleep', 3: 'away', }; return {action: modeLookup[action]}; } else { return fromZigbeeConverters.command_arm.convert(model, msg, publish, options, meta); } }, }, QBKG25LM_click: { cluster: 'genMultistateInput', type: ['attributeReport', 'readResponse'], convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { if ([1, 2, 3, 0, 255].includes(msg.data.presentValue)) { const mapping = {41: 'left', 42: 'center', 43: 'right'}; const times = {1: 'single', 2: 'double', 3: 'triple', 0: 'hold', 255: 'release'}; const button = mapping[msg.endpoint.ID]; return {action: `${button}_${times[msg.data.presentValue]}`}; } } else { return fromZigbeeConverters.xiaomi_multistate_action.convert(model, msg, publish, options, meta); } }, }, QBKG03LM_buttons: { cluster: 'genOnOff', type: ['attributeReport'], convert: (model, msg, publish, options, meta) => { const mapping = {4: 'left', 5: 'right'}; const button = mapping[msg.endpoint.ID]; if (button) { const payload = {}; payload[`button_${button}`] = msg.data['onOff'] === 1 ? 'release' : 'hold'; return payload; } }, }, CTR_U_brightness_updown_click: { cluster: 'genLevelCtrl', type: 'commandStep', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const deviceID = msg.device.ieeeAddr; const direction = msg.data.stepmode === 1 ? 'down' : 'up'; // Save last direction for release event if (!fromZigbeeStore[deviceID]) { fromZigbeeStore[deviceID] = {}; } fromZigbeeStore[deviceID].direction = direction; return { action: `brightness_${direction}_click`, step_size: msg.data.stepsize, transition_time: msg.data.transtime, }; } else { return fromZigbeeConverters.command_step.convert(model, msg, publish, options, meta); } }, }, CTR_U_brightness_updown_hold: { cluster: 'genLevelCtrl', type: 'commandMove', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const deviceID = msg.device.ieeeAddr; const direction = msg.data.movemode === 1 ? 'down' : 'up'; // Save last direction for release event if (!fromZigbeeStore[deviceID]) { fromZigbeeStore[deviceID] = {}; } fromZigbeeStore[deviceID].direction = direction; return { action: `brightness_${direction}_hold`, rate: msg.data.rate, }; } else { return fromZigbeeConverters.command_move.convert(model, msg, publish, options, meta); } }, }, CTR_U_brightness_updown_release: { cluster: 'genLevelCtrl', type: 'commandStop', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const deviceID = msg.device.ieeeAddr; if (!fromZigbeeStore[deviceID]) { return null; } const direction = fromZigbeeStore[deviceID].direction; return { action: `brightness_${direction}_release`, }; } else { return fromZigbeeConverters.command_stop.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_cmdOn: { cluster: 'genOnOff', type: 'commandOn', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'up'}; } else { return fromZigbeeConverters.command_on.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_cmdOff: { cluster: 'genOnOff', type: 'commandOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'down'}; } else { return fromZigbeeConverters.command_off.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_cmdMoveWithOnOff: { cluster: 'genLevelCtrl', type: 'commandMoveWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const deviceID = msg.device.ieeeAddr; if (!fromZigbeeStore[deviceID]) { fromZigbeeStore[deviceID] = {direction: null}; } fromZigbeeStore[deviceID].direction = 'up'; return {action: 'up_hold'}; } else { return fromZigbeeConverters.command_move.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AC0251100NJ_cmdStop: { cluster: 'genLevelCtrl', type: 'commandStop', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const map = { 1: 'up_release', 2: 'down_release', }; return {action: map[msg.endpoint.ID]}; } else { return fromZigbeeConverters.command_stop.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_cmdMove: { cluster: 'genLevelCtrl', type: 'commandMove', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const deviceID = msg.device.ieeeAddr; if (!fromZigbeeStore[deviceID]) { fromZigbeeStore[deviceID] = {direction: null}; } fromZigbeeStore[deviceID].direction = 'down'; return {action: 'down_hold'}; } else { return fromZigbeeConverters.command_move.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_cmdMoveHue: { cluster: 'lightingColorCtrl', type: 'commandMoveHue', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { if (msg.data.movemode === 0) { return {action: 'circle_release'}; } } else { return fromZigbeeConverters.command_move_hue.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_cmdMoveToSaturation: { cluster: 'lightingColorCtrl', type: 'commandMoveToSaturation', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'circle_hold'}; } else { return fromZigbeeConverters.command_move_to_saturation.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_cmdMoveToLevelWithOnOff: { cluster: 'genLevelCtrl', type: 'commandMoveToLevelWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'circle_click'}; } else { return fromZigbeeConverters.command_move_to_level.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_cmdMoveToColorTemp: { cluster: 'lightingColorCtrl', type: 'commandMoveToColorTemp', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return null; } else { return fromZigbeeConverters.command_move_to_color_temp.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_73743_cmdStop: { cluster: 'genLevelCtrl', type: 'commandStop', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const deviceID = msg.device.ieeeAddr; if (!fromZigbeeStore[deviceID]) { fromZigbeeStore[deviceID] = {direction: null}; } let direction; if (fromZigbeeStore[deviceID].direction) { direction = `${fromZigbeeStore[deviceID].direction}_`; } return {action: `${direction}release`}; } else { return fromZigbeeConverters.command_stop.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AB371860355_cmdOn: { cluster: 'genOnOff', type: 'commandOn', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'left_top_click'}; } else { return fromZigbeeConverters.command_on.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AB371860355_cmdOff: { cluster: 'genOnOff', type: 'commandOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'left_bottom_click'}; } else { return fromZigbeeConverters.command_off.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AB371860355_cmdStepColorTemp: { cluster: 'lightingColorCtrl', type: 'commandStepColorTemp', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const pos = (msg.data.stepmode === 1) ? 'top' : 'bottom'; return {action: `right_${pos}_click`}; } else { return fromZigbeeConverters.command_step_color_temperature.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AB371860355_cmdMoveWithOnOff: { cluster: 'genLevelCtrl', type: 'commandMoveWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'left_top_hold'}; } else { return fromZigbeeConverters.command_move.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AB371860355_cmdMove: { cluster: 'genLevelCtrl', type: 'commandMove', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return {action: 'left_bottom_hold'}; } else { return fromZigbeeConverters.command_move.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AB371860355_cmdStop: { cluster: 'genLevelCtrl', type: 'commandStop', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const pos = (msg.endpoint.ID === 1) ? 'top' : 'bottom'; return {action: `left_${pos}_release`}; } else { return fromZigbeeConverters.command_stop.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AB371860355_cmdMoveHue: { cluster: 'lightingColorCtrl', type: 'commandMoveHue', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const pos = (msg.endpoint.ID === 2) ? 'top' : 'bottom'; const action = (msg.data.movemode === 0) ? 'release' : 'hold'; return {action: `right_${pos}_${action}`}; } else { return fromZigbeeConverters.command_move_hue.convert(model, msg, publish, options, meta); } }, }, osram_lightify_switch_AB371860355_cmdMoveSat: { cluster: 'lightingColorCtrl', type: 'commandMoveToSaturation', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const pos = (msg.endpoint.ID === 2) ? 'top' : 'bottom'; return {action: `right_${pos}_hold`}; } else { return fromZigbeeConverters.command_move_to_saturation.convert(model, msg, publish, options, meta); } }, }, insta_scene_click: { cluster: 'genScenes', type: 'commandRecall', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return { action: `select_${msg.data.sceneid}`, }; } else { return fromZigbeeConverters.command_recall.convert(model, msg, publish, options, meta); } }, }, insta_down_hold: { cluster: 'genLevelCtrl', type: 'commandStep', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return { action: 'down', step_mode: msg.data.stepmode, step_size: msg.data.stepsize, transition_time: msg.data.transtime, }; } else { return fromZigbeeConverters.command_step.convert(model, msg, publish, options, meta); } }, }, insta_up_hold: { cluster: 'genLevelCtrl', type: 'commandStepWithOnOff', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return { action: 'up', step_mode: msg.data.stepmode, step_size: msg.data.stepsize, transition_time: msg.data.transtime, }; } else { return fromZigbeeConverters.command_step.convert(model, msg, publish, options, meta); } }, }, insta_stop: { cluster: 'genLevelCtrl', type: 'commandStop', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { return { action: 'stop', }; } else { return fromZigbeeConverters.command_stop.convert(model, msg, publish, options, meta); } }, }, tint404011_brightness_updown_click: { cluster: 'genLevelCtrl', type: 'commandStep', convert: (model, msg, publish, options, meta) => { if (isLegacyEnabled(options)) { const direction = msg.data.stepmode === 1 ? 'down' : 'up'; const payload = { action: `brightness_${direction}_click`, step_size: msg.data.stepsize, transition_time: msg.data.transtime, }; addActionGroup(payload, msg, model); return payload; } else { return fromZigbeeConverters.command_step.convert(model, msg, publish, options, meta); } }, }, tint404011_brightness_updown_hold: { cluster: 'genLevelCtrl', type: 'commandMove', convert: (model, msg, publish, options, meta) => { if (isL