Merge pull request #21 from GadgetAngel/dev

This update allows for an essential TuyAPI command to be implemented and fixes issues with the TuyaColorLight.prototype.setColor Method label:enhancement
This commit is contained in:
tsightler
2020-09-17 20:27:08 -04:00
committed by GitHub
4 changed files with 182 additions and 37 deletions

View File

@@ -42,14 +42,19 @@ node tuya-mqtt.js
// For debugging purpose, to use DEBUG : https://www.npmjs.com/package/debug
//on Linux machines at the bash command prompt:
//on Linux machines at the bash command prompt, to turn ON DEBUG:
DEBUG=* tuya-mqtt.js
//on Linux machines at the bash command prompt, to turn OFF DEBUG:
DEBUG=-* tuya-mqtt.js
// on Windows machines at the cmd.exe command prompt:
Set DEBUG=* tuya-mqtt.js
// on Windows machines at the cmd.exe command prompt, to turn ON DEBUG:
Set DEBUG=* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js
// on Windows machines at the cmd.exe command prompt, to turn OFF DEBUG:
Set DEBUG=-* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js
```
URL to [DEBUG](https://www.npmjs.com/package/debug)
URL to install [DEBUG](https://www.npmjs.com/package/debug)
@@ -68,7 +73,12 @@ Change device state (by topic):
- tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/toggle
- tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/TOGGLE
- tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/{ "dps": 1, "set": true }
- tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/{ "dps": 7, "set": true }
- tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/{ "multiple": true, "data": { "1": true, "7": true } }
- tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/{ "schema": true }
- tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/{ "multiple": true, "data": { "1": true, "2": "scene_4" } }
- tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/{ "multiple": true, "data":
{ "1": true, "2": "scene", "6": "c479000025ffc3" } }
Change device state (by payload)
Use with OpenHAB 2.X MQTT bindings or others where only a single command topic is preferred:
@@ -86,13 +96,18 @@ NOTE: notice that nothing follows the word command, DO NOT but a "/" in after co
"toggle"
"TOGGLE"
"{ \"dps\": 1, \"set\": true }"
"{ \"dps\": 7, \"set\": true }"
"{ \"multiple\": true, \"data\": { \"1\": true, \"7\": true } }"
"{ \"schema\": true }"
"{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene_4\" } }"
"{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene\", \"6\": \"c479000025ffc3\" } }"
Change color of lightbulb (payload as HSB-Color)
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/color
Example:
64,0,100
0,0,89
```
### MQTT Topic's (read data)
@@ -125,9 +140,9 @@ Switch tuya_kitchen_coffeemachine_mqtt "Steckdose Kaffeemaschine" <socket> (<GRO
}
Switch tuya_livingroom_ledstrip_tv "LED Regal" <lightbulb> (<GROUPS>) ["Lighting"] {
mqtt="<[broker:tuya/lightbulb/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/state:state:default:.*],
>[broker:tuya/lightbulb/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/on:command:ON:true],
>[broker:tuya/lightbulb/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/off:command:OFF:false]"
mqtt="<[broker:tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/state:state:default:.*],
>[broker:tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/on:command:ON:true],
>[broker:tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/off:command:OFF:false]"
}
```
@@ -140,7 +155,7 @@ Group gTuyaLivingColor "Tuya color group" <lightbulb>
Color tuya_livingroom_colorpicker "Stehlampe farbe" (LivingDining)
String tuya_livingroom_ledstrip_tv_color "Set color [%s]" (gTuyaLivingColor, LivingDining) {
mqtt=">[broker:tuya/lightbulb/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/color:command:*:default]"
mqtt=">[broker:tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/color:command:*:default]"
}
@@ -195,12 +210,12 @@ Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ]
Thing mqtt:topic:myCustomMQTT {
Channels:
Type switch : tuya_kitchen_coffeemachine_mqtt "Kitchen Coffee Machine MQTT Channel" [
Type switch : tuya_kitchen_coffeemachine_mqtt_channel "Kitchen Coffee Machine MQTT Channel" [
stateTopic="tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/state",
commandTopic="tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command",
// optional custom mqtt-payloads for ON and OFF
on="{ \"dps": 1, \"set\": true },
on="{ \"dps\": 1, \"set\": true }",
off="0"
]
}
@@ -209,7 +224,7 @@ Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ]
# *.item Example
Switch tuya_kitchen_coffeemachine_mqtt "Kitchen Coffee Machine Switch" <socket> (gKitchen, gTuya) ["Switchable"] {
channel="mqtt:topic:myMosquitto:tuya:coffeemachine"
channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:tuya_kitchen_coffeemachine_mqtt_channel"
}
```
@@ -221,17 +236,18 @@ For one RGB bulb you would need a separate channel with the command topic set to
Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ]
{
Type colorHSB : livingroom_floorlamp_1_color "Livingroom floorlamp color MQTT Channel" [
stateTopic="tuya/lightbulb/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/state",
commandTopic="tuya/lightbulb/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/color"
]
Thing mqtt:topic:myCustomMQTT {
Channels:
Type colorHSB : livingroom_floorlamp_1_color "Livingroom floorlamp color MQTT Channel" [
stateTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/state",
commandTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/color"
]
}
}
# *.item Example
Color tuya_livingroom_colorpicker "Floorlamp colorpicker" (gLivingroom){
channel="mqtt:topic:myMosquitto:tuya:livingroom_floorlamp_1_color"
channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:livingroom_floorlamp_1_color"
}
```
@@ -241,9 +257,15 @@ Color tuya_livingroom_colorpicker "Floorlamp colorpicker" (gLivingroom){
Switch item=tuya_kitchen_coffeemachine_mqtt
# turn the color bulb off or on
Switch item=tuya_livingroom_colorpicker label="RGB lamp [%s]"
# pick the color level to send to the color bulb via MQTT color Channel
Slider item=tuya_livingroom_colorpicker label="RGB lamp level [%s]" minValue=0 maxValue=100 step=1
# color picked and sent via MQTT Color channel
Colorpicker item=tuya_livingroom_colorpicker label="RGB lamp color [%s]" icon="colorpicker" sendFrequency=30000
# Colorpicker for Lightbulbs
Colorpicker item=tuya_livingroom_colorpicker label="RGB lamp color" sendFrequency=30000
```

View File

@@ -26,6 +26,58 @@ function TuyaColorLight() {
this.dps = {};
}
/**
* calculate color value from given brightness percentage
* @param (Integer) percentage 0-100 percentage value
* @returns (Integer) color value from 25 - 255
* @private
*/
TuyaColorLight.prototype._convertBrightnessPercentageToVal = function(brt_percentage){
// the brightness scale does not start at 0 but starts at 25 - 255
// this linear equation is a better fit to the conversion to 255 scale
var tmp = Math.round(2.3206*brt_percentage+22.56);
debug('Converted brightness percentage ' + brt_percentage + ' to: ' + tmp);
return tmp;
}
/**
* calculate percentage from brightness color value
* @param brt_val 25 - 255 brightness color value
* @returns {Integer} 0 - 100 integer percent
* @private
*/
TuyaColorLight.prototype._convertValtoBrightnessPercentage = function(brt_val){
var tmp = Math.round( (brt_val-22.56)/2.3206);
debug('Converted brightness value ' + brt_val + ' to: ' + tmp);
return tmp;
}
/**
* calculate color value from given saturation percentage OR color temperature percentage
* @param (Integer) temp_percentage 0-100 percentage value
* @returns {Integer} saturation or color temperature value from 0 - 255
* @private
*/
TuyaColorLight.prototype._convertSATorColorTempPercentageToVal = function(temp_percentage){
// the saturation OR temperature scale does start at 0 - 255
// this is a perfect linear equation fit for the saturation OR temperature scale conversion
var tmp = Math.round(((2.5498*temp_percentage)-0.4601));
debug('Converted saturation OR temperature percentage ' + temp_percentage + ' to: ' + tmp);
return tmp;
}
/**
* calculate percentage from saturation value OR color temperature value
* @param temp_val 0 - 255 saturation or color temperature value
* @returns {Integer} 0 - 100 integer percent
* @private
*/
TuyaColorLight.prototype._convertValtoSATorColorTempPercentage = function(temp_val){
var tmp = Math.round( (temp_val+0.4601/2.5498));
debug('Converted saturation OR temperature value ' + temp_val + ' to: ' + tmp);
return tmp;
}
/**
* calculate color value from given percentage
* @param {Integer} percentage 0-100 percentage value
@@ -105,6 +157,18 @@ TuyaColorLight.prototype._ValIsHex = function (h) {
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(h)
};
/**
* get width Hex digits from given value
* @param (Integer) value, decimal value to convert to hex string
* @param (Integer) width, the number of hex digits to return
* @returns {string} value as HEX containing (width) number of hex digits
* @private
*/
TuyaColorLight.prototype._getHex = function (value,width){
var hex = (value+Math.pow(16, width)).toString(16).slice(-width).toLowerCase();
debug('value: ' + value + ' hex: ' + hex);
return hex;
}
/**
* get AlphaHex from percentage brightness
* @param {Integer} brightness
@@ -138,7 +202,8 @@ TuyaColorLight.prototype.setSaturation = function (value) {
*/
TuyaColorLight.prototype.setBrightness = function (value) {
this.brightness = value;
var newValue = this._convertPercentageToVal(value);
//var newValue = this._convertPercentageToVal(value);
var newValue = this._convertBrightnessPercentageToVal(value);
debug("BRIGHTNESS from UI: " + value + ' Converted from 100 to 255 scale: ' + newValue);
}
@@ -219,29 +284,65 @@ TuyaColorLight.prototype.getDps = function () {
var lightness = Math.round(this.brightness / 2);
var brightness = this.brightness;
var apiBrightness = this._convertPercentageToVal(brightness);
var alphaBrightness = this._getAlphaHex(brightness);
//var apiBrightness = this._convertPercentageToVal(brightness);
var apiBrightness = this._convertBrightnessPercentageToVal(brightness);
//var alphaBrightness = this._getAlphaHex(brightness);
var alphaBrightness = this._getHex(apiBrightness,2);
var hexColor1 = convert.hsl.hex(color.H, color.S, lightness);
var hexColor2 = convert.hsl.hex(0, 0, lightness);
//var hexColor2 = convert.hsl.hex(0, 0, lightness);
var hexColor2 = this._getHex(color.H,4);
hexColor2 = hexColor2 + this._getHex(this._convertSATorColorTempPercentageToVal(color.S),2);
var colorTemperature = this.colorTemperature;
var lightColor = (hexColor1 + hexColor2 + alphaBrightness).toLowerCase();
var temperature = (this.colorMode === 'colour') ? 255 : this._convertColorTemperature(colorTemperature);
//var temperature = (this.colorMode === 'colour') ? 255 : this._convertColorTemperature(colorTemperature);
// color temperature percentage is at a fixed 51%
var temperature = this._convertSATorColorTempPercentageToVal(51);
dpsTmp = {
'1': true,
'2': this.colorMode,
'3': apiBrightness,
'4': temperature,
'5': lightColor
// '6' : hexColor + hexColor + 'ff'
};
debug("dps", dpsTmp);
return dpsTmp;
// if the bulb is in colour mode than the dps 3 and dps 4 are ignored by the bulb but if you set it now
// some tuya bulbs will ignore dps 5 because you set dps 3 or dps 4
// FOR colour mode the bulb looks at dps 1, dps 2, and dps 5.
// DPS 5 is in the following format:
// HSL to HEX format are the leftmost hex digits (hex digits 14 - 9)
// hex digits 8 - 5 are the HSB/HSL Hue value in HEX format
// hex digits 4 - 3 are the HSB/HSL Saturation percentage as a value (converted to 0-255 scale) in HEX format
// hex digits 2 - 1 are the HSB Brightness percentage as a value (converted to 25-255 scale) in HEX format
if (this.colorMode === 'colour') {
dpsTmp = {
'1': true,
'2': this.colorMode,
//'3': apiBrightness,
//'4': temperature,
'5': lightColor
// '6' : hexColor + hexColor + 'ff'
};
debug("dps", dpsTmp);
return dpsTmp;
}
// if the bulb is in white mode then the dps 5 value is ignored by the bulb but if you set dps 5 value now
// you may not get a response back from the bulb on the dps values
// FOR white mode the bulb looks at dps 1, dps 2, dps 3 and dps 4
// DPS 3 is the HSB/HSL Brightness percentage converted to a value from 25 to 255 in decimal format
// DPS 4 is the HSB/HSL Saturation percentage converted to a value from 0 to 255 in decimal format
if (this.colorMode === 'white'){
dpsTmp = {
'1': true,
'2': this.colorMode,
'3': apiBrightness,
'4': temperature,
//'5': lightColor
// '6' : hexColor + hexColor + 'ff'
};
debug("dps", dpsTmp);
return dpsTmp;
}
}
module.exports = TuyaColorLight;

View File

@@ -265,6 +265,12 @@ var TuyaDevice = (function () {
});
}
TuyaDevice.prototype.schema = function(obj){
return this.get(obj).then((status) => {
debug("get", obj);
});
}
TuyaDevice.prototype.setColor = function (hexColor) {
if (!this.connected) return;
debugColor("Set color to: ", hexColor);
@@ -318,4 +324,4 @@ var TuyaDevice = (function () {
return TuyaDevice;
}());
module.exports = TuyaDevice;
module.exports = TuyaDevice;

View File

@@ -190,6 +190,22 @@ mqtt_client.on('message', function (topic, message) {
device.switch(command).then((data) => {
debug("set device status completed", data);
});
}
if (command.schema === true) {
// this command is very useful. IT IS A COMMAND. It's place under the command topic.
// It's the ONLY command that does not use device.set to get a result.
// You have to use device.get and send the get method an exact JSON string of { schema: true }
// This schema command does NOT
// change the state of the device, all it does is query the device
// as a confirmation that all communications are working properly.
// Otherwise you have to physically change the state of the device just to
// find out if you can talk to it. If this command returns no errors than
// we know we are have an established communication channel. This is a native TuyAPI call that
// the TuyAPI interface defines (its only available via the GET command.
// this call returns a object of results
device.schema(command).then((data) => {
});
debug("get (schema) device status completed");
} else {
device.set(command).then((data) => {
debug("set device status completed", data);
@@ -355,4 +371,4 @@ var tester = new MQTT_Tester();
function onExit() {
TuyaDevice.disconnectAll();
if (tester) tester.destroy();
};
};