diff --git a/converters/fromZigbee.js b/converters/fromZigbee.js index cb7929d73eb5c..9aec8b0feb9d6 100644 --- a/converters/fromZigbee.js +++ b/converters/fromZigbee.js @@ -493,7 +493,8 @@ const converters = { } if (msg.data.hasOwnProperty('colorMode')) { - result.color_mode = msg.data['colorMode']; + result.color_mode = constants.colorMode.hasOwnProperty(msg.data['colorMode']) ? + constants.colorMode[msg.data['colorMode']] : msg.data['colorMode']; } if ( diff --git a/converters/toZigbee.js b/converters/toZigbee.js index d463170a191aa..ae99e8698a41f 100644 --- a/converters/toZigbee.js +++ b/converters/toZigbee.js @@ -63,6 +63,12 @@ const converters = { await entity.read('genOnOff', ['startUpOnOff']); }, }, + light_color_mode: { + key: ['color_mode'], + convertGet: async (entity, key, meta) => { + await entity.read('lightingColorCtrl', ['colorMode']); + }, + }, light_color_options: { key: ['color_options'], convertSet: async (entity, key, value, meta) => { @@ -514,12 +520,13 @@ const converters = { await entity.command('lightingColorCtrl', 'moveColorTemp', payload, utils.getOptions(meta.mapped, entity)); - // As we cannot determine the new brightness state, we read it from the device + // We cannot determine the color temperaturefrom the current state so we read it, because + // - Color mode could have been swithed (x/y or colortemp) if (value === 'stop' || value === 0) { const entityToRead = utils.getEntityOrFirstGroupMember(entity); if (entityToRead) { await utils.sleep(100); - await entityToRead.read('lightingColorCtrl', ['colorTemperature']); + await entityToRead.read('lightingColorCtrl', ['colorTemperature', 'colorMode']); } } } else { @@ -559,7 +566,7 @@ const converters = { const entityToRead = utils.getEntityOrFirstGroupMember(entity); if (entityToRead) { await utils.sleep(100 + (transition * 100)); - await entityToRead.read('lightingColorCtrl', [attribute]); + await entityToRead.read('lightingColorCtrl', [attribute, 'colorMode']); } }, }, @@ -585,12 +592,13 @@ const converters = { await entity.command('lightingColorCtrl', command, payload, utils.getOptions(meta.mapped, entity)); - // As we cannot determine the new brightness state, we read it from the device + // We cannot determine the hue/saturation from the current state so we read it, because + // - Color mode could have been swithed (x/y or colortemp) if (value === 'stop' || value === 0) { const entityToRead = utils.getEntityOrFirstGroupMember(entity); if (entityToRead) { await utils.sleep(100); - await entityToRead.read('lightingColorCtrl', [attribute]); + await entityToRead.read('lightingColorCtrl', [attribute, 'colorMode']); } } }, @@ -731,7 +739,7 @@ const converters = { const payload = {colortemp: value, transtime: utils.getTransition(entity, key, meta).time}; await entity.command('lightingColorCtrl', 'moveToColorTemp', payload, utils.getOptions(meta.mapped, entity)); - return {state: {color_temp: value}, readAfterWriteTime: payload.transtime * 100}; + return {state: {color_temp: value, color_mode: constants.colorMode[2]}, readAfterWriteTime: payload.transtime * 100}; }, convertGet: async (entity, key, meta) => { await entity.read('lightingColorCtrl', ['colorTemperature']); @@ -928,6 +936,7 @@ const converters = { switch (command) { case 'enhancedMoveToHueAndSaturationAndBrightness': + newState.color_mode = constants.colorMode[0]; await entity.command( 'genLevelCtrl', 'moveToLevelWithOnOff', @@ -940,15 +949,18 @@ const converters = { command = 'enhancedMoveToHueAndSaturation'; break; case 'enhancedMoveToHueAndSaturation': + newState.color_mode = constants.colorMode[0]; zclData.enhancehue = value.hue; zclData.saturation = value.saturation; zclData.direction = value.direction || 0; break; case 'enhancedMoveToHue': + newState.color_mode = constants.colorMode[0]; zclData.enhancehue = value.hue; zclData.direction = value.direction || 0; break; case 'moveToHueAndSaturationAndBrightness': + newState.color_mode = constants.colorMode[0]; await entity.command( 'genLevelCtrl', 'moveToLevelWithOnOff', @@ -961,15 +973,18 @@ const converters = { command = 'moveToHueAndSaturation'; break; case 'moveToHueAndSaturation': + newState.color_mode = constants.colorMode[0]; zclData.hue = value.hue; zclData.saturation = value.saturation; zclData.direction = value.direction || 0; break; case 'moveToHue': + newState.color_mode = constants.colorMode[0]; zclData.hue = value.hue; zclData.direction = value.direction || 0; break; case 'moveToSaturation': + newState.color_mode = constants.colorMode[0]; zclData.saturation = value.saturation; break; @@ -986,6 +1001,7 @@ const converters = { } newState.color = {x: value.x, y: value.y}; + newState.color_mode = constants.colorMode[1]; zclData.colorx = Math.round(value.x * 65535); zclData.colory = Math.round(value.y * 65535); } @@ -3820,23 +3836,36 @@ const converters = { const sceneid = value; await entity.command('genScenes', 'recall', {groupid, sceneid}, utils.getOptions(meta.mapped)); + const addColorMode = (newState) => { + if (newState.hasOwnProperty('color_temp')) { + newState.color_mode = constants.colorMode[2]; + } else if (newState.hasOwnProperty('color')) { + if (newState.color.hasOwnProperty('x')) { + newState.color_mode = constants.colorMode[1]; + } else { + newState.color_mode = constants.colorMode[0]; + } + } + + return newState; + }; + const isGroup = entity.constructor.name === 'Group'; const metaKey = `${sceneid}_${groupid}`; if (isGroup) { const membersState = {}; for (const member of entity.members) { if (member.meta.hasOwnProperty('scenes') && member.meta.scenes.hasOwnProperty(metaKey)) { - membersState[member.getDevice().ieeeAddr] = member.meta.scenes[metaKey].state; + membersState[member.getDevice().ieeeAddr] = addColorMode(member.meta.scenes[metaKey].state); } else { meta.logger.warn(`Unknown scene was recalled for ${member.getDevice().ieeeAddr}, can't restore state.`); membersState[member.getDevice().ieeeAddr] = {}; } } - return {membersState}; } else { if (entity.meta.scenes.hasOwnProperty(metaKey)) { - return {state: entity.meta.scenes[metaKey].state}; + return {state: addColorMode(entity.meta.scenes[metaKey].state)}; } else { meta.logger.warn(`Unknown scene was recalled for ${entity.deviceIeeeAddress}, can't restore state.`); return {state: {}}; diff --git a/devices.js b/devices.js index ee63d93688eb2..68a8baff58e7f 100755 --- a/devices.js +++ b/devices.js @@ -67,7 +67,7 @@ const preset = { const exposes = [e.light_brightness_colortemp(options.colorTempRange), ...(!options.disableEffect ? [e.effect()] : [])]; const toZigbee = [tz.light_onoff_brightness, tz.light_colortemp, tz.ignore_transition, tz.ignore_rate, tz.light_brightness_move, tz.light_colortemp_move, tz.light_brightness_step, tz.light_colortemp_step, tz.light_colortemp_startup, tz.level_config, - tz.power_on_behavior, tz.light_color_options, ...(!options.disableEffect ? [tz.effect] : [])]; + tz.power_on_behavior, tz.light_color_options, tz.light_color_mode, ...(!options.disableEffect ? [tz.effect] : [])]; const fromZigbee = [fz.color_colortemp, fz.on_off, fz.brightness, fz.level_config, fz.power_on_behavior, fz.ignore_basic_report]; if (options.disableColorTempStartup) { @@ -94,7 +94,7 @@ const preset = { const fromZigbee = [fz.color_colortemp, fz.on_off, fz.brightness, fz.level_config, fz.power_on_behavior, fz.ignore_basic_report]; const toZigbee = [tz.light_onoff_brightness, tz.light_color, tz.ignore_transition, tz.ignore_rate, tz.light_brightness_move, tz.light_brightness_step, tz.level_config, tz.power_on_behavior, tz.light_hue_saturation_move, - tz.light_hue_saturation_step, tz.light_color_options, ...(!options.disableEffect ? [tz.effect] : [])]; + tz.light_hue_saturation_step, tz.light_color_options, tz.light_color_mode, ...(!options.disableEffect ? [tz.effect] : [])]; return { exposes, fromZigbee, toZigbee, meta: {configureKey: 2}, @@ -116,7 +116,7 @@ const preset = { tz.light_onoff_brightness, tz.light_color_colortemp, tz.ignore_transition, tz.ignore_rate, tz.light_brightness_move, tz.light_colortemp_move, tz.light_brightness_step, tz.light_colortemp_step, tz.light_hue_saturation_move, tz.light_hue_saturation_step, tz.light_colortemp_startup, tz.level_config, tz.power_on_behavior, tz.light_color_options, - ...(!options.disableEffect ? [tz.effect] : [])]; + tz.light_color_mode, ...(!options.disableEffect ? [tz.effect] : [])]; if (options.disableColorTempStartup) { exposes[0].removeFeature('color_temp_startup'); diff --git a/lib/constants.js b/lib/constants.js index a1b2255229598..64387670a4cf2 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -112,6 +112,14 @@ const armMode = { 10: 'arming_away', }; +// ID's from ZCL mapped to ha names where appropriate +// https://github.com/home-assistant/core/pull/47720 +const colorMode = { + 0: 'hs', + 1: 'xy', + 2: 'color_temp', +}; + module.exports = { OneJanuary2000, repInterval, @@ -126,4 +134,5 @@ module.exports = { keypadLockoutMode, lockSourceName, armMode, + colorMode, };