Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RGBTW Color Temp Range incorrectly reported to HomeKit as 0-600 despite configuration. #357

Open
ekobres opened this issue Feb 3, 2022 · 4 comments
Assignees
Labels
bug Something isn't working

Comments

@ekobres
Copy link

ekobres commented Feb 3, 2022

Checklist

  • [ X] I have read the common issues wiki page
  • [ X] I have checked to make sure the plugin is up to date

Describe the bug
HomeKit thinks the range of Tuya RGBTW bulbs is 0-600 mired, regardless of the configured min/max, which is incorrect. The configured min/max in the accessory definition should be passed to HomeKit. For example, Wiz-LAN plugin does this properly. Also, HK certified bulbs such as Hue, Meross and Nanoleaf have a color temp range that matches the actual characteristics of the bulb. The bulbs I am using are Feit WiFi Color with Tunable White and their range is 154-370 (6500K-2700K.) These bulbs are HSB with white temperature scale of 1000.

Although you can reach the min and max colors of the bulb (0-1000 for example with an HSB bulb with 1000 scale) with the temperature picker, the reported mired level HomeKit sees is way off - and ranges from 71 to 600. This is a problem if you want to try to match other bulbs in the room - or to use any of the "White" color temperatures Siri understands, like Topaz, Sandy Brown, etc.

To Reproduce
Set up a Tuya LAN bulb with HSB color values and a 1000 color temperature scale.
Set up the Minimum and Maximum white temperature values to be correct for the bulb - or use the defaults of 140 & 400.
Set the bulb to the warmest possible color using the Tuya app.
Use an app such as Controller for HomeKit that can read the properties of accessories and examine the min/max color temperature properties of the Tuya plugin bulb. The minimum value will be 0, and the Max will be 600.
Likewise, if you set the bulb through the Tuya or other app to the highest mired value - HomeKit will show that the bulb is set to 600 mired, even though the actual mired value is probably in the 370-400 range. (Some, like Hue bulbs go all the way to 2000K or 500 mired - but no real bulb goes to 600 that I have seen.)

Expected behavior
Homekit minimum, maximum and current mired values for RGBTW HSB bulbs should be scaled based on the configured color range of the bulb, not based on a hard-coded range of 0-600 - which is the maximum range of HomeKit - not the bulb.

For example for an RGBTW light with HSB color commands, min color of 153, max color of 370, and color temperature scale of 1000, when the accessory Color Temperature property in HomeKit is set to the configured maximum white color (e.g. 370 mired), the command to the bulb should be the minimum scale value (e.g. 1). When the accessory Color Temperature property in HomeKit is set to the configured minimum white color (e.g. 153 mired), the command to the bulb should be the maximum scale value (e.g. 1000.)

Screenshots
The screen shots are from the HomeKit Controller app - the bulb shown is Tuya RGBTW configured with a white range of 154-370
IMG_6609

This shot shows the manual input field for the color temp - and the app validates the possible range reported by HomeKit - which you see is 0-600. (Actually any value below 71 is invalid - but HomeKit seems to just pass through whatever the accessory properties are.)
IMG_6610

This is a corresponding shot from a Hue Color Ambience bulb with a range of 153-500 mired. Notice that it correctly shows 369 mired while the bulb is set to 2700K - standard "Warm White" color.
IMG_6611

And here you can see HomeKit is reporting the correct range for the Hue bulb:
IMG_6612

Environment (please complete the following information):

  • OS: Raspbian
  • iOS version: 15.3
  • Homehubs: HPMx6, ATV4Kx6
  • Node Version: 16.3.1
  • Plugin Version: 2.0.1
  • Accessory Type: RGBTWLight (HSB Color, 1000 Scale Brightness and Color Temp)

Additional context
Add any other context about the problem here.

@ekobres ekobres added the bug Something isn't working label Feb 3, 2022
@ekobres
Copy link
Author

ekobres commented Feb 3, 2022

To add a bit - I think I see what's going on - but I don't know enough about the framework to figure out where to go with it. Basically there should be a handler for min_value() and max_value() on the public.hap.characteristic.color-temperature characteristic - and those value should be the min/max from the accessory configuration.

This means that the implementation of the HomeKit<->Tuya conversion functions can be simplified to something like:

convertColorTemperatureFromHomeKitToTuya(value) {
        const min = this.device.context.minWhiteColor || 140;
        const max = this.device.context.maxWhiteColor || 400;
        const scale = this.device.context.scaleWhiteColor || 255;
        const result = Math.round(Math.max(Math.min(scale-(scale-1)/(max-min)*(value-min),scale),1));
        return result;
    }

and

convertColorTemperatureFromTuyaToHomeKit(value) {
        const min = this.device.context.minWhiteColor || 140;
        const max = this.device.context.maxWhiteColor || 400;
        const scale = this.device.context.scaleWhiteColor || 255;
        const result = Math.round(Math.max(Math.min((scale-value)/((scale-1)/(max-min))+min,max),min));
        return Math.min(600, Math.max(71, result));
    }

With this code my light colors actually match the color temperature value now - but the color picker is still stuck in the 0-600 range. That's not a huge deal because at least now every part of the system has the correct mired values for the lights - and the conversion functions ensure the values stay within the light's actual range.

So, if anyone can point me in the direction of how to implement the min_value() and max_value() callbacks - I could probably fix it myself from there.

Thanks!

@iRayanKhan iRayanKhan pinned this issue Feb 11, 2022
@iRayanKhan
Copy link
Owner

Thanks for the well documented issue, and code snippet. Did your code change resolve homebridge characteristic warnings? Hoping to get this patched in 2.1.0/3.0.0.

@ekobres
Copy link
Author

ekobres commented Feb 11, 2022 via email

@Vvorcun
Copy link

Vvorcun commented Jun 18, 2023

@ekobres, you're just a magician! I've been trying to work around this myself and your code really me saved a lot of time! About hard-coded values - in RGBTWLightAccessory.js (judging by the time passed you've probably already figured this yourself, but still):

const characteristicColorTemperature = service.getCharacteristic(Characteristic.ColorTemperature)
    .setProps({
        minValue: this.device.context.minWhiteColor,
        maxValue: this.device.context.maxWhiteColor
    })

And now color picker works perfectly. However, there's a few other hard-coded functions. For example, this needs to be changed in the same RGBTWLightAccessory.js:

getColorTemperature(callback) {
    if (this.device.state[this.dpMode] !== this.cmdWhite) return callback(null, this.device.context.minWhiteColor);
    callback(null, this.convertColorTemperatureFromTuyaToHomeKit(this.device.state[this.dpColorTemperature]));
}

_setHueSaturation(prop, callback) {
    ...
    const callbacks = this._pendingHueSaturation.callbacks;
    const callEachBack = err => {
        async.eachSeries(callbacks, (callback, next) => {
            try {
                callback(err);
            } catch (ex) {}
            next();
        }, () => {
            this.characteristicColorTemperature.updateValue(this.device.context.minWhiteColor);
        });
    };
    ...
}

_registerCharacteristics(dps) {
    ...
    const characteristicColorTemperature = service.getCharacteristic(Characteristic.ColorTemperature)
        .setProps({
            minValue: 0,
            maxValue: 600
        })
        .updateValue(dps[this.dpMode] === this.cmdWhite ? this.convertColorTemperatureFromTuyaToHomeKit(dps[this.dpColorTemperature]) : this.device.context.minWhiteColor)
        .on('get', this.getColorTemperature.bind(this))
        .on('set', this.setColorTemperature.bind(this));
    ...
    default:
        if (changes.hasOwnProperty(this.dpColor)) {
            const oldColor = this.convertColorFromTuyaToHomeKit(this.convertColorFromHomeKitToTuya({
                h: characteristicHue.value,
                s: characteristicSaturation.value,
                b: characteristicBrightness.value
            }));
            const newColor = this.convertColorFromTuyaToHomeKit(changes[this.dpColor]);

            if (oldColor.b !== newColor.b) characteristicBrightness.updateValue(newColor.b);
            if (oldColor.h !== newColor.h) characteristicHue.updateValue(newColor.h);

            if (oldColor.s !== newColor.s) characteristicSaturation.updateValue(newColor.h);

            if (characteristicColorTemperature.value !== this.device.context.minWhiteColor) characteristicColorTemperature.updateValue(this.device.context.minWhiteColor);

        } else if (changes[this.dpMode]) {
            if (characteristicColorTemperature.value !== this.device.context.minWhiteColor) characteristicColorTemperature.updateValue(this.device.context.minWhiteColor);
        }
    ...
}

I also don't think that it's good practice to report 0 hue and 0 saturation when in white mode - this causes color temperature picker to not work properly (picker works, but as soon as homekit grabs new bulb state it shows that bulb is white no matter what it's actual color is - even though color temperature is being reported correctly), here's new code for the same RGBTWLightAccessory.js to fix this behaviour:

getHue(callback) {
        if (this.device.state[this.dpMode] === this.cmdWhite) {
                const prepareHue = this.convertHomeKitColorTemperatureToHomeKitColor(this.convertColorTemperatureFromTuyaToHomeKit(this.device.state[this.dpColorTemperature]));
                callback(null, prepareHue.h);
        } else {
                callback(null, this.convertColorFromTuyaToHomeKit(this.device.state[this.dpColor]).h);
        }
}

getSaturation(callback) {
        if (this.device.state[this.dpMode] === this.cmdWhite) {
                const prepareSaturation = this.convertHomeKitColorTemperatureToHomeKitColor(this.convertColorTemperatureFromTuyaToHomeKit(this.device.state[this.dpColorTemperature]));
                callback(null, prepareSaturation.s);
        } else {
                callback(null, this.convertColorFromTuyaToHomeKit(this.device.state[this.dpColor]).s);
        }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants