mirror of
https://github.com/lehanspb/tuya-mqtt.git
synced 2025-12-16 17:54:36 +00:00
Merge branch 'dev' into master
This commit is contained in:
62
README.md
62
README.md
@@ -52,14 +52,19 @@ node tuya-mqtt.js
|
|||||||
|
|
||||||
// For debugging purpose, to use DEBUG : https://www.npmjs.com/package/debug
|
// 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
|
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:
|
// on Windows machines at the cmd.exe command prompt, to turn ON DEBUG:
|
||||||
Set DEBUG=* tuya-mqtt.js
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -83,7 +88,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/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": 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/{ "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)
|
Change device state (by payload)
|
||||||
Use with OpenHAB 2.X MQTT bindings or others where only a single command topic is preferred:
|
Use with OpenHAB 2.X MQTT bindings or others where only a single command topic is preferred:
|
||||||
@@ -101,13 +111,18 @@ NOTE: notice that nothing follows the word command, DO NOT but a "/" in after co
|
|||||||
"toggle"
|
"toggle"
|
||||||
"TOGGLE"
|
"TOGGLE"
|
||||||
"{ \"dps\": 1, \"set\": true }"
|
"{ \"dps\": 1, \"set\": true }"
|
||||||
|
"{ \"dps\": 7, \"set\": true }"
|
||||||
"{ \"multiple\": true, \"data\": { \"1\": true, \"7\": 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)
|
Change color of lightbulb (payload as HSB-Color)
|
||||||
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/color
|
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/color
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
64,0,100
|
64,0,100
|
||||||
|
0,0,89
|
||||||
```
|
```
|
||||||
|
|
||||||
### MQTT Topic's (read data)
|
### MQTT Topic's (read data)
|
||||||
@@ -140,9 +155,9 @@ Switch tuya_kitchen_coffeemachine_mqtt "Steckdose Kaffeemaschine" <socket> (<GRO
|
|||||||
}
|
}
|
||||||
|
|
||||||
Switch tuya_livingroom_ledstrip_tv "LED Regal" <lightbulb> (<GROUPS>) ["Lighting"] {
|
Switch tuya_livingroom_ledstrip_tv "LED Regal" <lightbulb> (<GROUPS>) ["Lighting"] {
|
||||||
mqtt="<[broker:tuya/lightbulb/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/state:state:default:.*],
|
mqtt="<[broker:tuya/<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/<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]"
|
>[broker:tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command/off:command:OFF:false]"
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -155,7 +170,7 @@ Group gTuyaLivingColor "Tuya color group" <lightbulb>
|
|||||||
Color tuya_livingroom_colorpicker "Stehlampe farbe" (LivingDining)
|
Color tuya_livingroom_colorpicker "Stehlampe farbe" (LivingDining)
|
||||||
|
|
||||||
String tuya_livingroom_ledstrip_tv_color "Set color [%s]" (gTuyaLivingColor, 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]"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -210,12 +225,12 @@ Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ]
|
|||||||
|
|
||||||
Thing mqtt:topic:myCustomMQTT {
|
Thing mqtt:topic:myCustomMQTT {
|
||||||
Channels:
|
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",
|
stateTopic="tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/state",
|
||||||
commandTopic="tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command",
|
commandTopic="tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command",
|
||||||
|
|
||||||
// optional custom mqtt-payloads for ON and OFF
|
// optional custom mqtt-payloads for ON and OFF
|
||||||
on="{ \"dps": 1, \"set\": true },
|
on="{ \"dps\": 1, \"set\": true }",
|
||||||
off="0"
|
off="0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -224,7 +239,7 @@ Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ]
|
|||||||
|
|
||||||
# *.item Example
|
# *.item Example
|
||||||
Switch tuya_kitchen_coffeemachine_mqtt "Kitchen Coffee Machine Switch" <socket> (gKitchen, gTuya) ["Switchable"] {
|
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -236,17 +251,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 ]
|
Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ]
|
||||||
{
|
{
|
||||||
|
Thing mqtt:topic:myCustomMQTT {
|
||||||
Type colorHSB : livingroom_floorlamp_1_color "Livingroom floorlamp color MQTT Channel" [
|
Channels:
|
||||||
stateTopic="tuya/lightbulb/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/state",
|
Type colorHSB : livingroom_floorlamp_1_color "Livingroom floorlamp color MQTT Channel" [
|
||||||
commandTopic="tuya/lightbulb/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/color"
|
stateTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/state",
|
||||||
]
|
commandTopic="tuya/05200399bcddc2e02ec9/b58cf92e8bc5c899/192.168.178.49/color"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# *.item Example
|
# *.item Example
|
||||||
Color tuya_livingroom_colorpicker "Floorlamp colorpicker" (gLivingroom){
|
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -256,9 +272,15 @@ Color tuya_livingroom_colorpicker "Floorlamp colorpicker" (gLivingroom){
|
|||||||
|
|
||||||
Switch item=tuya_kitchen_coffeemachine_mqtt
|
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
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
131
tuya-color.js
131
tuya-color.js
@@ -26,6 +26,58 @@ function TuyaColorLight() {
|
|||||||
this.dps = {};
|
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
|
* calculate color value from given percentage
|
||||||
* @param {Integer} percentage 0-100 percentage value
|
* @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)
|
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
|
* get AlphaHex from percentage brightness
|
||||||
* @param {Integer} brightness
|
* @param {Integer} brightness
|
||||||
@@ -138,7 +202,8 @@ TuyaColorLight.prototype.setSaturation = function (value) {
|
|||||||
*/
|
*/
|
||||||
TuyaColorLight.prototype.setBrightness = function (value) {
|
TuyaColorLight.prototype.setBrightness = function (value) {
|
||||||
this.brightness = 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);
|
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 lightness = Math.round(this.brightness / 2);
|
||||||
var brightness = this.brightness;
|
var brightness = this.brightness;
|
||||||
var apiBrightness = this._convertPercentageToVal(brightness);
|
//var apiBrightness = this._convertPercentageToVal(brightness);
|
||||||
var alphaBrightness = this._getAlphaHex(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 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 colorTemperature = this.colorTemperature;
|
||||||
|
|
||||||
var lightColor = (hexColor1 + hexColor2 + alphaBrightness).toLowerCase();
|
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 = {
|
// 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
|
||||||
'1': true,
|
// some tuya bulbs will ignore dps 5 because you set dps 3 or dps 4
|
||||||
'2': this.colorMode,
|
// FOR colour mode the bulb looks at dps 1, dps 2, and dps 5.
|
||||||
'3': apiBrightness,
|
// DPS 5 is in the following format:
|
||||||
'4': temperature,
|
// HSL to HEX format are the leftmost hex digits (hex digits 14 - 9)
|
||||||
'5': lightColor
|
// hex digits 8 - 5 are the HSB/HSL Hue value in HEX format
|
||||||
// '6' : hexColor + hexColor + 'ff'
|
// 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
|
||||||
debug("dps", dpsTmp);
|
|
||||||
return dpsTmp;
|
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;
|
module.exports = TuyaColorLight;
|
||||||
@@ -208,6 +208,12 @@ var TuyaDevice = (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TuyaDevice.prototype.schema = function(obj){
|
||||||
|
return this.get(obj).then((status) => {
|
||||||
|
debug("get", obj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.setColor = function (hexColor) {
|
TuyaDevice.prototype.setColor = function (hexColor) {
|
||||||
if (!this.connected) return;
|
if (!this.connected) return;
|
||||||
debugColor("Set color to: ", hexColor);
|
debugColor("Set color to: ", hexColor);
|
||||||
|
|||||||
814
tuya-mqtt.js
814
tuya-mqtt.js
@@ -1,371 +1,443 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
const mqtt = require('mqtt');
|
const mqtt = require('mqtt');
|
||||||
const TuyaDevice = require('./tuya-device');
|
const TuyaDevice = require('./tuya-device');
|
||||||
const debug = require('debug')('TuyAPI:mqtt');
|
const debug = require('debug')('TuyAPI:mqtt');
|
||||||
const debugColor = require('debug')('TuyAPI:mqtt:color');
|
const debugColor = require('debug')('TuyAPI:mqtt:color');
|
||||||
const debugTuya = require('debug')('TuyAPI:mqtt:device');
|
const debugTuya = require('debug')('TuyAPI:mqtt:device');
|
||||||
const debugError = require('debug')('TuyAPI:mqtt:error');
|
const debugError = require('debug')('TuyAPI:mqtt:error');
|
||||||
var cleanup = require('./cleanup').Cleanup(onExit);
|
var cleanup = require('./cleanup').Cleanup(onExit);
|
||||||
|
|
||||||
var CONFIG = undefined;
|
var CONFIG = undefined;
|
||||||
var mqtt_client = undefined;
|
var mqtt_client = undefined;
|
||||||
|
|
||||||
function bmap(istate) {
|
function bmap(istate) {
|
||||||
return istate ? 'ON' : "OFF";
|
return istate ? 'ON' : "OFF";
|
||||||
}
|
}
|
||||||
|
|
||||||
function boolToString(istate) {
|
function boolToString(istate) {
|
||||||
return istate ? 'true' : "false";
|
return istate ? 'true' : "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* execute function on topic message
|
* execute function on topic message
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function IsJsonString(text) {
|
function IsJsonString(text) {
|
||||||
if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||||
//the json is ok
|
//the json is ok
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check mqtt-topic string for old notation with included device type
|
* check mqtt-topic string for old notation with included device type
|
||||||
* @param {String} topic
|
* @param {String} topic
|
||||||
*/
|
*/
|
||||||
function checkTopicNotation(_topic) {
|
function checkTopicNotation(_topic) {
|
||||||
var topic = _topic.split("/");
|
var topic = _topic.split("/");
|
||||||
var type = topic[1];
|
var type = topic[1];
|
||||||
var result = (type == "socket" || type == "lightbulb" || type == "ver3.1" || type == "ver3.3");
|
var result = (type == "socket" || type == "lightbulb" || type == "ver3.1" || type == "ver3.3");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get action from mqtt-topic string
|
* get action from mqtt-topic string
|
||||||
* @param {String} topic
|
* @param {String} topic
|
||||||
* @returns {String} action type
|
* @returns {String} action type
|
||||||
*/
|
*/
|
||||||
function getActionFromTopic(_topic) {
|
function getActionFromTopic(_topic) {
|
||||||
var topic = _topic.split("/");
|
var topic = _topic.split("/");
|
||||||
|
|
||||||
if (checkTopicNotation(_topic)) {
|
if (checkTopicNotation(_topic)) {
|
||||||
return topic[5];
|
return topic[5];
|
||||||
} else {
|
} else {
|
||||||
return topic[4];
|
return topic[4];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get device informations from mqtt-topic string
|
* get device informations from mqtt-topic string
|
||||||
* @param {String} topic
|
* @param {String} topic
|
||||||
* @returns {String} object.id
|
* @returns {String} object.id
|
||||||
* @returns {String} object.key
|
* @returns {String} object.key
|
||||||
* @returns {String} object.ip
|
* @returns {String} object.ip
|
||||||
*/
|
*/
|
||||||
function getDeviceFromTopic(_topic) {
|
function getDeviceFromTopic(_topic) {
|
||||||
var topic = _topic.split("/");
|
var topic = _topic.split("/");
|
||||||
|
|
||||||
if (checkTopicNotation(_topic)) {
|
if (checkTopicNotation(_topic)) {
|
||||||
// When there are 5 topic levels
|
// When there are 5 topic levels
|
||||||
// topic 2 is id, and topic 3 is key
|
// topic 2 is id, and topic 3 is key
|
||||||
var options = {
|
var options = {
|
||||||
id: topic[2],
|
id: topic[2],
|
||||||
key: topic[3]
|
key: topic[3]
|
||||||
};
|
};
|
||||||
|
|
||||||
// 4th topic is IP address or "discover" keyword
|
// 4th topic is IP address or "discover" keyword
|
||||||
if (topic[4] !== "discover") {
|
if (topic[4] !== "discover") {
|
||||||
options.ip = topic[4]
|
options.ip = topic[4]
|
||||||
// If IP is manually specified check if topic 1
|
// If IP is manually specified check if topic 1
|
||||||
// is protocol version and set accordingly
|
// is protocol version and set accordingly
|
||||||
if (topic[1] == "ver3.3") {
|
if (topic[1] == "ver3.3") {
|
||||||
options.version = "3.3"
|
options.version = "3.3"
|
||||||
} else if (topic[1] == "ver3.1") {
|
} else if (topic[1] == "ver3.1") {
|
||||||
options.version = "3.1"
|
options.version = "3.1"
|
||||||
} else {
|
} else {
|
||||||
// If topic is not version then it's device type
|
// If topic is not version then it's device type
|
||||||
// Not used anymore but still supported for legacy setups
|
// Not used anymore but still supported for legacy setups
|
||||||
options.type = topic[1]
|
options.type = topic[1]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
} else {
|
} else {
|
||||||
// When there are 4 topic levels
|
// When there are 4 topic levels
|
||||||
// topic 1 is id, topic 2 is key
|
// topic 1 is id, topic 2 is key
|
||||||
var options = {
|
var options = {
|
||||||
id: topic[1],
|
id: topic[1],
|
||||||
key: topic[2]
|
key: topic[2]
|
||||||
};
|
};
|
||||||
|
|
||||||
// If topic 3 is not discover assume it is IP address
|
// If topic 3 is not discover assume it is IP address
|
||||||
// Todo: Validate it is an IP address
|
// Todo: Validate it is an IP address
|
||||||
if (topic[3] !== "discover") {
|
if (topic[3] !== "discover") {
|
||||||
options.ip = topic[3]
|
options.ip = topic[3]
|
||||||
};
|
};
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get command from mqtt - topic string
|
* get command from mqtt - topic string
|
||||||
* converts simple commands to TuyAPI JSON commands
|
* converts simple commands to TuyAPI JSON commands
|
||||||
* @param {String} topic
|
* @param {String} topic
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
function getCommandFromTopic(_topic, _message) {
|
function getCommandFromTopic(_topic, _message) {
|
||||||
var topic = _topic.split("/");
|
var topic = _topic.split("/");
|
||||||
var command = null;
|
var command = null;
|
||||||
|
|
||||||
if (checkTopicNotation(_topic)) {
|
if (checkTopicNotation(_topic)) {
|
||||||
command = topic[6];
|
command = topic[6];
|
||||||
} else {
|
} else {
|
||||||
command = topic[5];
|
command = topic[5];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command == null) {
|
if (command == null) {
|
||||||
command = _message;
|
command = _message;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command != "1" && command != "0" && IsJsonString(command)) {
|
if (command != "1" && command != "0" && IsJsonString(command)) {
|
||||||
debug("command is JSON");
|
debug("command is JSON");
|
||||||
command = JSON.parse(command);
|
command = JSON.parse(command);
|
||||||
} else {
|
} else {
|
||||||
if (command.toLowerCase() != "toggle") {
|
if (command.toLowerCase() != "toggle") {
|
||||||
// convert simple commands (on, off, 1, 0) to TuyAPI-Commands
|
// convert simple commands (on, off, 1, 0) to TuyAPI-Commands
|
||||||
var convertString = command.toLowerCase() == "on" || command == "1" || command == 1 ? true : false;
|
var convertString = command.toLowerCase() == "on" || command == "1" || command == 1 ? true : false;
|
||||||
command = {
|
command = {
|
||||||
set: convertString
|
set: convertString
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
command = command.toLowerCase();
|
command = command.toLowerCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
mqtt_client.on('message', function (topic, message) {
|
||||||
* Publish current TuyaDevice state to MQTT-Topic
|
try {
|
||||||
* @param {TuyaDevice} device
|
message = message.toString();
|
||||||
* @param {boolean} status
|
var action = getActionFromTopic(topic);
|
||||||
*/
|
var options = getDeviceFromTopic(topic);
|
||||||
function publishStatus(device, status) {
|
|
||||||
if (mqtt_client.connected == true) {
|
debug("receive settings", JSON.stringify({
|
||||||
try {
|
topic: topic,
|
||||||
var type = device.type;
|
action: action,
|
||||||
var tuyaID = device.options.id;
|
message: message,
|
||||||
var tuyaKey = device.options.key;
|
options: options
|
||||||
var tuyaIP = device.options.ip;
|
}));
|
||||||
|
|
||||||
if (typeof tuyaIP == "undefined") {
|
var device = new TuyaDevice(options);
|
||||||
tuyaIP = "discover"
|
device.then(function (params) {
|
||||||
}
|
var device = params.device;
|
||||||
|
|
||||||
if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined") {
|
switch (action) {
|
||||||
var topic = CONFIG.topic;
|
case "command":
|
||||||
if (typeof type != "undefined") {
|
var command = getCommandFromTopic(topic, message);
|
||||||
topic += type + "/";
|
debug("receive command", command);
|
||||||
}
|
if (command == "toggle") {
|
||||||
topic += tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state";
|
device.switch(command).then((data) => {
|
||||||
|
debug("set device status completed", data);
|
||||||
mqtt_client.publish(topic, status, {
|
});
|
||||||
retain: CONFIG.retain,
|
}
|
||||||
qos: CONFIG.qos
|
if (command.schema === true) {
|
||||||
});
|
// this command is very useful. IT IS A COMMAND. It's place under the command topic.
|
||||||
debugTuya("mqtt status updated to:" + topic + " -> " + status);
|
// It's the ONLY command that does not use device.set to get a result.
|
||||||
} else {
|
// You have to use device.get and send the get method an exact JSON string of { schema: true }
|
||||||
debugTuya("mqtt status not updated");
|
// This schema command does NOT
|
||||||
}
|
// change the state of the device, all it does is query the device
|
||||||
} catch (e) {
|
// as a confirmation that all communications are working properly.
|
||||||
debugError(e);
|
// 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
|
||||||
function publishColorState(device, state) {
|
device.schema(command).then((data) => {
|
||||||
|
});
|
||||||
}
|
debug("get (schema) device status completed");
|
||||||
|
} else {
|
||||||
/**
|
device.set(command).then((data) => {
|
||||||
* publish all dps-values to topic
|
debug("set device status completed", data);
|
||||||
* @param {TuyaDevice} device
|
});
|
||||||
* @param {Object} dps
|
}
|
||||||
*/
|
break;
|
||||||
function publishDPS(device, dps) {
|
case "color":
|
||||||
if (mqtt_client.connected == true) {
|
var color = message.toLowerCase();
|
||||||
try {
|
debugColor("set color: ", color);
|
||||||
var type = device.type;
|
device.setColor(color).then((data) => {
|
||||||
var tuyaID = device.options.id;
|
debug("set device color completed", data);
|
||||||
var tuyaKey = device.options.key;
|
});
|
||||||
var tuyaIP = device.options.ip;
|
break;
|
||||||
|
}
|
||||||
if (typeof tuyaIP == "undefined") {
|
|
||||||
tuyaIP = "discover"
|
}).catch((err) => {
|
||||||
}
|
debugError(err);
|
||||||
|
});
|
||||||
if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined") {
|
} catch (e) {
|
||||||
var baseTopic = CONFIG.topic;
|
debugError(e);
|
||||||
if (typeof type != "undefined") {
|
}
|
||||||
baseTopic += type + "/";
|
});
|
||||||
}
|
|
||||||
baseTopic += tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/dps";
|
/**
|
||||||
|
* Publish current TuyaDevice state to MQTT-Topic
|
||||||
var topic = baseTopic;
|
* @param {TuyaDevice} device
|
||||||
var data = JSON.stringify(dps);
|
* @param {boolean} status
|
||||||
debugTuya("mqtt dps updated to:" + topic + " -> ", data);
|
*/
|
||||||
mqtt_client.publish(topic, data, {
|
function publishStatus(device, status) {
|
||||||
retain: CONFIG.retain,
|
if (mqtt_client.connected == true) {
|
||||||
qos: CONFIG.qos
|
try {
|
||||||
});
|
var type = device.type;
|
||||||
|
var tuyaID = device.options.id;
|
||||||
Object.keys(dps).forEach(function (key) {
|
var tuyaKey = device.options.key;
|
||||||
var topic = baseTopic + "/" + key;
|
var tuyaIP = device.options.ip;
|
||||||
var data = JSON.stringify(dps[key]);
|
|
||||||
debugTuya("mqtt dps updated to:" + topic + " -> dps[" + key + "]", data);
|
if (typeof tuyaIP == "undefined") {
|
||||||
mqtt_client.publish(topic, data, {
|
tuyaIP = "discover"
|
||||||
retain: CONFIG.retain,
|
}
|
||||||
qos: CONFIG.qos
|
|
||||||
});
|
if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined") {
|
||||||
});
|
var topic = CONFIG.topic;
|
||||||
} else {
|
if (typeof type != "undefined") {
|
||||||
debugTuya("mqtt dps not updated");
|
topic += type + "/";
|
||||||
}
|
}
|
||||||
} catch (e) {
|
topic += tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state";
|
||||||
debugError(e);
|
|
||||||
}
|
mqtt_client.publish(topic, status, {
|
||||||
}
|
retain: CONFIG.retain,
|
||||||
}
|
qos: CONFIG.qos
|
||||||
|
});
|
||||||
/**
|
debugTuya("mqtt status updated to:" + topic + " -> " + status);
|
||||||
* event fires if TuyaDevice sends data
|
} else {
|
||||||
* @see TuyAPI (https://github.com/codetheweb/tuyapi)
|
debugTuya("mqtt status not updated");
|
||||||
*/
|
}
|
||||||
TuyaDevice.onAll('data', function (data) {
|
} catch (e) {
|
||||||
try {
|
debugError(e);
|
||||||
if (typeof data.dps != "undefined") {
|
}
|
||||||
debugTuya('Data from device ' + this.tuyID + ' :', data);
|
}
|
||||||
var status = data.dps['1'];
|
}
|
||||||
if (typeof status != "undefined") {
|
|
||||||
publishStatus(this, bmap(status));
|
function publishColorState(device, state) {
|
||||||
}
|
|
||||||
publishDPS(this, data.dps);
|
}
|
||||||
}
|
|
||||||
} catch (e) {
|
/**
|
||||||
debugError(e);
|
* publish all dps-values to topic
|
||||||
}
|
* @param {TuyaDevice} device
|
||||||
});
|
* @param {Object} dps
|
||||||
|
*/
|
||||||
/**
|
function publishDPS(device, dps) {
|
||||||
* Function call on script exit
|
if (mqtt_client.connected == true) {
|
||||||
*/
|
try {
|
||||||
function onExit() {
|
var type = device.type;
|
||||||
TuyaDevice.disconnectAll();
|
var tuyaID = device.options.id;
|
||||||
};
|
var tuyaKey = device.options.key;
|
||||||
|
var tuyaIP = device.options.ip;
|
||||||
// Simple sleep to pause in async functions
|
|
||||||
function sleep(sec) {
|
if (typeof tuyaIP == "undefined") {
|
||||||
return new Promise(res => setTimeout(res, sec*1000));
|
tuyaIP = "discover"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main code loop
|
if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined") {
|
||||||
const main = async() => {
|
var baseTopic = CONFIG.topic;
|
||||||
|
if (typeof type != "undefined") {
|
||||||
try {
|
baseTopic += type + "/";
|
||||||
CONFIG = require("./config");
|
}
|
||||||
} catch (e) {
|
baseTopic += tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/dps";
|
||||||
console.error("Configuration file not found")
|
|
||||||
debugError(e)
|
var topic = baseTopic;
|
||||||
process.exit(1)
|
var data = JSON.stringify(dps);
|
||||||
}
|
debugTuya("mqtt dps updated to:" + topic + " -> ", data);
|
||||||
|
mqtt_client.publish(topic, data, {
|
||||||
if (typeof CONFIG.qos == "undefined") {
|
retain: CONFIG.retain,
|
||||||
CONFIG.qos = 2;
|
qos: CONFIG.qos
|
||||||
}
|
});
|
||||||
if (typeof CONFIG.retain == "undefined") {
|
|
||||||
CONFIG.retain = false;
|
Object.keys(dps).forEach(function (key) {
|
||||||
}
|
var topic = baseTopic + "/" + key;
|
||||||
|
var data = JSON.stringify(dps[key]);
|
||||||
mqtt_client = mqtt.connect({
|
debugTuya("mqtt dps updated to:" + topic + " -> dps[" + key + "]", data);
|
||||||
host: CONFIG.host,
|
mqtt_client.publish(topic, data, {
|
||||||
port: CONFIG.port,
|
retain: CONFIG.retain,
|
||||||
username: CONFIG.mqtt_user,
|
qos: CONFIG.qos
|
||||||
password: CONFIG.mqtt_pass,
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
mqtt_client.on('connect', function (err) {
|
debugTuya("mqtt dps not updated");
|
||||||
debug("Connection established to MQTT server");
|
}
|
||||||
var topic = CONFIG.topic + '#';
|
} catch (e) {
|
||||||
mqtt_client.subscribe(topic, {
|
debugError(e);
|
||||||
retain: CONFIG.retain,
|
}
|
||||||
qos: CONFIG.qos
|
}
|
||||||
});
|
}
|
||||||
});
|
|
||||||
|
/**
|
||||||
mqtt_client.on("reconnect", function (error) {
|
* event fires if TuyaDevice sends data
|
||||||
if (mqtt_client.connected) {
|
* @see TuyAPI (https://github.com/codetheweb/tuyapi)
|
||||||
debug("Connection to MQTT server lost. Attempting to reconnect...");
|
*/
|
||||||
} else {
|
TuyaDevice.onAll('data', function (data) {
|
||||||
debug("Unable to connect to MQTT server");
|
try {
|
||||||
}
|
if (typeof data.dps != "undefined") {
|
||||||
});
|
debugTuya('Data from device ' + this.tuyID + ' :', data);
|
||||||
|
var status = data.dps['1'];
|
||||||
mqtt_client.on("error", function (error) {
|
if (typeof status != "undefined") {
|
||||||
debug("Unable to connect to MQTT server", error);
|
publishStatus(this, bmap(status));
|
||||||
});
|
}
|
||||||
|
publishDPS(this, data.dps);
|
||||||
mqtt_client.on('message', function (topic, message) {
|
}
|
||||||
try {
|
} catch (e) {
|
||||||
message = message.toString();
|
debugError(e);
|
||||||
var action = getActionFromTopic(topic);
|
}
|
||||||
var options = getDeviceFromTopic(topic);
|
});
|
||||||
|
|
||||||
debug("receive settings", JSON.stringify({
|
/**
|
||||||
topic: topic,
|
* Function call on script exit
|
||||||
action: action,
|
*/
|
||||||
message: message,
|
function onExit() {
|
||||||
options: options
|
TuyaDevice.disconnectAll();
|
||||||
}));
|
};
|
||||||
|
|
||||||
var device = new TuyaDevice(options);
|
// Simple sleep to pause in async functions
|
||||||
|
function sleep(sec) {
|
||||||
device.then(function (params) {
|
return new Promise(res => setTimeout(res, sec*1000));
|
||||||
var device = params.device;
|
}
|
||||||
|
|
||||||
switch (action) {
|
// Main code loop
|
||||||
case "command":
|
const main = async() => {
|
||||||
var command = getCommandFromTopic(topic, message);
|
|
||||||
debug("receive command", command);
|
try {
|
||||||
if (command == "toggle") {
|
CONFIG = require("./config");
|
||||||
device.switch(command).then((data) => {
|
} catch (e) {
|
||||||
debug("set device status completed", data);
|
console.error("Configuration file not found")
|
||||||
});
|
debugError(e)
|
||||||
} else {
|
process.exit(1)
|
||||||
device.set(command).then((data) => {
|
}
|
||||||
debug("set device status completed", data);
|
|
||||||
});
|
if (typeof CONFIG.qos == "undefined") {
|
||||||
}
|
CONFIG.qos = 2;
|
||||||
break;
|
}
|
||||||
case "color":
|
if (typeof CONFIG.retain == "undefined") {
|
||||||
var color = message.toLowerCase();
|
CONFIG.retain = false;
|
||||||
debugColor("set color: ", color);
|
}
|
||||||
device.setColor(color).then((data) => {
|
|
||||||
debug("set device color completed", data);
|
mqtt_client = mqtt.connect({
|
||||||
});
|
host: CONFIG.host,
|
||||||
break;
|
port: CONFIG.port,
|
||||||
}
|
username: CONFIG.mqtt_user,
|
||||||
|
password: CONFIG.mqtt_pass,
|
||||||
}).catch((err) => {
|
});
|
||||||
debugError(err);
|
|
||||||
});
|
mqtt_client.on('connect', function (err) {
|
||||||
} catch (e) {
|
debug("Connection established to MQTT server");
|
||||||
debugError(e);
|
var topic = CONFIG.topic + '#';
|
||||||
}
|
mqtt_client.subscribe(topic, {
|
||||||
});
|
retain: CONFIG.retain,
|
||||||
|
qos: CONFIG.qos
|
||||||
}
|
});
|
||||||
|
});
|
||||||
// Call the main code
|
|
||||||
main()
|
mqtt_client.on("reconnect", function (error) {
|
||||||
|
if (mqtt_client.connected) {
|
||||||
|
debug("Connection to MQTT server lost. Attempting to reconnect...");
|
||||||
|
} else {
|
||||||
|
debug("Unable to connect to MQTT server");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mqtt_client.on("error", function (error) {
|
||||||
|
debug("Unable to connect to MQTT server", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
mqtt_client.on('message', function (topic, message) {
|
||||||
|
try {
|
||||||
|
message = message.toString();
|
||||||
|
var action = getActionFromTopic(topic);
|
||||||
|
var options = getDeviceFromTopic(topic);
|
||||||
|
|
||||||
|
debug("receive settings", JSON.stringify({
|
||||||
|
topic: topic,
|
||||||
|
action: action,
|
||||||
|
message: message,
|
||||||
|
options: options
|
||||||
|
}));
|
||||||
|
|
||||||
|
var device = new TuyaDevice(options);
|
||||||
|
|
||||||
|
device.then(function (params) {
|
||||||
|
var device = params.device;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "command":
|
||||||
|
var command = getCommandFromTopic(topic, message);
|
||||||
|
debug("receive command", command);
|
||||||
|
if (command == "toggle") {
|
||||||
|
device.switch(command).then((data) => {
|
||||||
|
debug("set device status completed", data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
device.set(command).then((data) => {
|
||||||
|
debug("set device status completed", data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "color":
|
||||||
|
var color = message.toLowerCase();
|
||||||
|
debugColor("set color: ", color);
|
||||||
|
device.setColor(color).then((data) => {
|
||||||
|
debug("set device color completed", data);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}).catch((err) => {
|
||||||
|
debugError(err);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
debugError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the main code
|
||||||
|
main()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function call on script exit
|
||||||
|
*/
|
||||||
|
function onExit() {
|
||||||
|
TuyaDevice.disconnectAll();
|
||||||
|
if (tester) tester.destroy();
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user