const TuyaDevice = require('./tuya-device') const debug = require('debug')('tuya-mqtt:device-detect') const debugDiscovery = require('debug')('tuya-mqtt:discovery') const utils = require('../lib/utils') class RGBTWLight extends TuyaDevice { async init() { // If no manual config try to detect device settings if (!this.config.dpsPower) { await this.guessLightInfo() } // If detection failed and no manual config return without initializing if (!this.guess.dpsPower && !this.config.dpsPower) { debug('Automatic discovery of Tuya bulb settings failed and no manual configuration') return } // Set device specific variables this.config.dpsPower = this.config.dpsPower ? this.config.dpsPower : this.guess.dpsPower this.config.dpsMode = this.config.dpsMode ? this.config.dpsMode : this.guess.dpsMode this.config.dpsWhiteValue = this.config.dpsWhiteValue ? this.config.dpsWhiteValue : this.guess.dpsWhiteValue this.config.whiteValueScale = this.config.whiteValueScale ? this.config.whiteValueScale : this.guess.whiteValueScale this.config.dpsColorTemp = this.config.dpsColorTemp ? this.config.dpsColorTemp : this.guess.dpsColorTemp this.config.minColorTemp = this.config.minColorTemp ? this.config.minColorTemp : 154 // ~6500K this.config.maxColorTemp = this.config.maxColorTemp ? this.config.maxColorTemp : 400 // ~2500K this.config.colorTempScale = this.config.colorTempScale ? this.config.colorTempScale : this.guess.colorTempScale this.config.dpsColor = this.config.dpsColor ? this.config.dpsColor : this.guess.dpsColor this.config.colorType = this.config.colorType ? this.config.colorType : this.guess.colorType this.deviceData.mdl = 'RGBTW Light' this.isRgbtwLight = true // Map generic DPS topics to device specific topic names this.deviceTopics = { state: { key: this.config.dpsPower, type: 'bool' }, white_brightness_state: { key: this.config.dpsWhiteValue, type: 'int', min: 1, max: 100, scale: this.config.whiteValueScale, stateMath: '/('+this.config.whiteValueScale+'/100)', commandMath: '*('+this.config.whiteValueScale+'/100)' }, hs_state: { key: this.config.dpsColor, type: this.config.colorType, components: 'h,s' }, color_brightness_state: { key: this.config.dpsColor, type: this.config.colorType, components: 'b' }, hsb_state: { key: this.config.dpsColor, type: this.config.colorType, components: 'h,s,b' }, mode_state: { key: this.config.dpsMode, type: 'str' } } // If device supports Color Temperature add color temp device topic if (this.config.dpsColorTemp) { // Values used for tranforming from 1-255 scale to mireds range const rangeFactor = (this.config.maxColorTemp-this.config.minColorTemp)/100 const scaleFactor = this.config.colorTempScale/100 const tuyaMaxColorTemp = this.config.maxColorTemp/rangeFactor*scaleFactor this.deviceTopics.color_temp_state = { key: this.config.dpsColorTemp, type: 'int', min: this.config.minColorTemp, max: this.config.maxColorTemp, stateMath: '/'+scaleFactor+'*-'+rangeFactor+'+'+this.config.maxColorTemp, commandMath: '/'+rangeFactor+'*-'+scaleFactor+'+'+tuyaMaxColorTemp } } // Send home assistant discovery data and give it a second before sending state updates this.initDiscovery() await utils.sleep(1) // Get initial states and start publishing topics this.getStates() } initDiscovery() { const configTopic = 'homeassistant/light/'+this.config.id+'/config' const discoveryData = { name: (this.config.name) ? this.config.name : this.config.id, state_topic: this.baseTopic+'state', command_topic: this.baseTopic+'command', brightness_state_topic: this.baseTopic+'color_brightness_state', brightness_command_topic: this.baseTopic+'color_brightness_command', brightness_scale: 100, hs_state_topic: this.baseTopic+'hs_state', hs_command_topic: this.baseTopic+'hs_command', white_value_state_topic: this.baseTopic+'white_brightness_state', white_value_command_topic: this.baseTopic+'white_brightness_command', white_value_scale: 100, availability_topic: this.baseTopic+'status', payload_available: 'online', payload_not_available: 'offline', unique_id: this.config.id, device: this.deviceData } if (this.config.dpsColorTemp) { discoveryData.color_temp_state_topic = this.baseTopic+'color_temp_state' discoveryData.color_temp_command_topic = this.baseTopic+'color_temp_command' discoveryData.min_mireds = this.config.minColorTemp discoveryData.max_mireds = this.config.maxColorTemp } debugDiscovery('Home Assistant config topic: '+configTopic) debugDiscovery(discoveryData) this.publishMqtt(configTopic, JSON.stringify(discoveryData)) } async guessLightInfo() { this.guess = new Object() debug('Attempting to detect light capabilites and DPS values...') debug('Querying DPS 2 for white/color mode setting...') // Check if DPS 2 contains typical values for RGBTW light const mode2 = await this.device.get({"dps": 2}) const mode21 = await this.device.get({"dps": 21}) if (mode2 && (mode2 === 'white' || mode2 === 'colour' || mode2.toString().includes('scene'))) { debug('Detected probably Tuya color bulb at DPS 1-5, checking more details...') this.guess = {'dpsPower': 1, 'dpsMode': 2, 'dpsWhiteValue': 3, 'whiteValueScale': 255, 'dpsColorTemp': 4, 'colorTempScale': 255, 'dpsColor': 5} } else if (mode21 && (mode21 === 'white' || mode21 === 'colour' || mode21.toString().includes('scene'))) { debug('Detected likely Tuya color bulb at DPS 20-24, checking more details...') this.guess = {'dpsPower': 20, 'dpsMode': 21, 'dpsWhiteValue': 22, 'whiteValueScale': 1000, 'dpsColorTemp': 23, 'colorTempScale': 1000, 'dpsColor': 24} } if (this.guess.dpsPower) { debug('Attempting to detect if bulb supports color temperature...') const colorTemp = await this.device.get({"dps": this.guess.dpsColorTemp}) if (colorTemp !== '' && colorTemp >= 0 && colorTemp <= this.guess.colorTempScale) { debug('Detected likely color temerature support') } else { debug('No color temperature support detected') this.guess.dpsColorTemp = 0 } debug('Attempting to detect Tuya color format used by device...') const color = await this.device.get({"dps": this.guess.dpsColor}) this.guess.colorType = (color && color.length === 12) ? 'hsb' : 'hsbhex' debug ('Detected Tuya color format '+this.guess.colorType.toUpperCase()) } } } module.exports = RGBTWLight