mirror of
https://github.com/lehanspb/tuya-mqtt.git
synced 2025-12-19 08:28:45 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
390d81c893 | ||
|
|
b0549bf392 | ||
|
|
989834821a | ||
|
|
9944f326c5 | ||
|
|
df08c6fc8a | ||
|
|
d05ac857d8 | ||
|
|
868b0977b0 | ||
|
|
82faeff78e | ||
|
|
89a7bfd04d | ||
|
|
1bc87b513c | ||
|
|
01fd5abcff | ||
|
|
9091398810 | ||
|
|
65a9922a7f | ||
|
|
14fc702041 | ||
|
|
4a605f4536 | ||
|
|
ec8d2c62a5 | ||
|
|
a554cd3f58 | ||
|
|
fb79927020 | ||
|
|
279590eb71 | ||
|
|
6f0e03d3f2 | ||
|
|
c6e88ac08b | ||
|
|
a747e122c6 | ||
|
|
604256709a | ||
|
|
3b72a000c8 | ||
|
|
fa228074b3 | ||
|
|
54745c4f8b | ||
|
|
b5f1f1fd31 | ||
|
|
4ea15db772 | ||
|
|
61433e74a9 | ||
|
|
825b9f97ce | ||
|
|
cfefbabe90 | ||
|
|
59acf6d2a4 | ||
|
|
1485ae0689 | ||
|
|
7a9dde716c | ||
|
|
e9e46a7b77 | ||
|
|
998028fa1c | ||
|
|
18ebcbdd74 | ||
|
|
86e602d6cf | ||
|
|
7637a372f7 | ||
|
|
dd2385f6e2 |
51
CHANGELOG.md
Normal file
51
CHANGELOG.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## [2.1.0]
|
||||||
|
### Added
|
||||||
|
- Added ability to update validate communicaton with device and update state topic by issuing { "schema": true } command
|
||||||
|
- Added support for protocol 3.3 either via automatic device discovery or manual specification when using IP address
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Can specify "discover" instead of IP address to automatically find device (only works if device on same IP subnet as system running this script). This mode will also automatically detect 3.1 and 3.3 protocol devices
|
||||||
|
- Can manually specific protocol via ver3.1/ver3.3 in topic line after tuya/
|
||||||
|
- Bump Tuyapi version to v5.3.x
|
||||||
|
- Bump MQTT version to v4.x.x
|
||||||
|
- Moved openHAB config to it's own document since many users use this with other tools
|
||||||
|
- Verious other fixes and cleanups
|
||||||
|
|
||||||
|
## [2.0.1]
|
||||||
|
### Added
|
||||||
|
- Added capability to set multiple dps values over MQTT-Command
|
||||||
|
- Custom Set-Function for TuyAPI-Class (added error handling for "index [1] not found" error)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- MQTT-Topic no longer requires a device type
|
||||||
|
- Updated TuyAPI to v4.x.x
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- remove device type from topic
|
||||||
|
|
||||||
|
## [2.0.0]
|
||||||
|
### Added
|
||||||
|
- support for OH MQTT-Binding 2.4
|
||||||
|
- default QoS of 2, if not set through config.json
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated TuyAPI to v3.x.x
|
||||||
|
- Constant off states after start #11
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- custom set function for TuyAPI
|
||||||
|
|
||||||
|
## [1.0.0]
|
||||||
|
### Added
|
||||||
|
- Add ability to connect to protected MQTT server
|
||||||
|
- Added seperate configuration file
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- TuyAPI repository not found
|
||||||
|
|
||||||
|
### Removed
|
||||||
304
README.md
304
README.md
@@ -1,169 +1,135 @@
|
|||||||
# TuyaAPI-MQTT Client
|
# tuyAPI-MQTT Client
|
||||||
MQTT interface for Tuya home automation devices sold under various names.
|
MQTT interface for Tuya home automation devices sold under various names.
|
||||||
This is a wrapper script for the Project codetheweb/tuyapi. https://github.com/codetheweb/tuyapi
|
This is a wrapper script for the Project codetheweb/tuyapi. https://github.com/codetheweb/tuyapi
|
||||||
|
|
||||||
This project provides an MQTT client for communication with the home automation devices.
|
This project provides an MQTT gateway for locally controlling home automation devices made by Tuya Inc. To use this script you will need to obtain the device ID and local keys for each of your devices after they are configured via the Tuya/Smart Life or other Tuya compatible app (there are many). With this information it is possible to communicate locally with Tuya devices using protocol 3.1 and 3.3, without using the Tuya Cloud service, however, getting the keys requires signing up for a Tuya IOT developer account or using one of several other alternative methods (such as dumping the memory of a Tuya based app running on Andriod). Acquiring keys is not part of this project, please see the instructions at the TuyAPI project (on which this script is based) available at the TuyAPI project site:
|
||||||
|
|
||||||
:exclamation: There is a greate Step-By-Step guide from user HolgiHab at openhab community ([Step-By-Step Guide](
|
https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md.
|
||||||
https://community.openhab.org/t/step-by-step-guide-for-adding-tuya-bulbs-smart-life-to-oh2-using-tuya-mqtt-js-by-agentk/59371)). This guide is not only for light bulbs, but also applies to sockets. :exclamation:
|
|
||||||
|
## Instructions:
|
||||||
## Instructions:
|
Download this project to your system into any directory (example below uses /opt/tuya-mqtt) and install tuyapi from the same folder that the tuya-mqtt.js is in
|
||||||
|
```
|
||||||
Download this project to your openhab2-script-folder "/etc/openhab2/scripts" and install tuyapi from the same folder that the tuya-mqtt.js is in
|
// switch to opt directory
|
||||||
```
|
cd /opt
|
||||||
cd /etc/openhab2/scripts
|
|
||||||
git clone git@github.com:TheAgentK/tuyaapi_mqtt.git // this project
|
// clone this project
|
||||||
cd tuyaapi_mqtt
|
git clone https://github.com/TheAgentK/tuya-mqtt
|
||||||
npm install //downloads codetheweb/tuyapi
|
|
||||||
```
|
// change directory to the project directory
|
||||||
|
cd tuya-mqtt
|
||||||
This involves MIM of the connection. Instructions can be found here: https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md
|
|
||||||
|
//installs this project along with codetheweb/tuyapi project
|
||||||
Create your configuration file:
|
npm install
|
||||||
```
|
```
|
||||||
cp config.json.sample config.json
|
|
||||||
nano config.json // edit the configuration file
|
|
||||||
```
|
## Basic Usage
|
||||||
|
### Create your configuration file:
|
||||||
Start command
|
```
|
||||||
```
|
cp config.json.sample config.json
|
||||||
node tuya-mqtt.js
|
|
||||||
|
// edit the configuration file
|
||||||
// For debugging purpose
|
nano config.json
|
||||||
DEBUG=* tuya-mqtt.js
|
```
|
||||||
```
|
|
||||||
|
### Start command
|
||||||
MQTT Topic
|
```
|
||||||
```
|
node tuya-mqtt.js
|
||||||
Current device state:
|
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/state
|
// For debugging purpose, to use DEBUG : https://www.npmjs.com/package/debug
|
||||||
|
|
||||||
Change device state (by topic):
|
//on Linux machines at the bash command prompt, to turn ON DEBUG:
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command/<STATE>
|
DEBUG=* tuya-mqtt.js
|
||||||
|
|
||||||
Example:
|
//on Linux machines at the bash command prompt, to turn OFF DEBUG:
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command/on
|
DEBUG=-* tuya-mqtt.js
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command/off
|
|
||||||
|
// on Windows machines at the cmd.exe command prompt, to turn ON DEBUG:
|
||||||
Change device state (by payload)
|
Set DEBUG=* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js
|
||||||
Use with OpenHAB 2.X MQTT bindings or others where only a single command topic is preferred
|
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command // State as Payload (on,off)
|
// on Windows machines at the cmd.exe command prompt, to turn OFF DEBUG:
|
||||||
|
Set DEBUG=-* & node c:/openhab2/userdata/etc/scripts/tuya-mqtt.js
|
||||||
Color for lightbulb:
|
```
|
||||||
|
|
||||||
Example:
|
### MQTT Topic's (send data)
|
||||||
tuya/lightbulb/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/color // Color as Payload as hexColor
|
**It's possible to replace the device IP address \<tuyAPI-ip\> with the word "discover" to have the API attempt to automatically discover the device IP address. This allows support for 3.3 protocol devices transparently, without additional configuraiton, but does require the system running this script to be on the same IP subnet as the Tuya device since the discovery protocol relies on UDP broadcast packets from the devices.**
|
||||||
|
```
|
||||||
Read data from device:
|
tuya/<tuyAPI-id>/<tuyAPI-key>/discover/state
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/dps // returns JSON.stringify(dps) values, use with care, does not always contain all dps values
|
tuya/<tuyAPI-id>/<tuyAPI-key>/discover/command
|
||||||
|
```
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/dps/<tuya-dps-id> // return single dps data value
|
**If discovery will not work for your case you can still use the IP address, but, to use protocol 3.3 you must specify it in the topic explicitly**
|
||||||
```
|
```
|
||||||
|
tuya/ver3.3/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip/state
|
||||||
#### Issues
|
tuya/ver3.3/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command
|
||||||
There are some reliability issues with tuyapi. Latest changes changed the syntax but still getting error maybe at an even higher rate.
|
```
|
||||||
|
### Example command topic to set the device state:
|
||||||
All questions regarding the TuyaAPI please ask in the project https://github.com/codetheweb/tuyapi .
|
```
|
||||||
|
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command
|
||||||
|
```
|
||||||
## Example items for OpenHAB 1.x Bindings (still works with >2.4 but only if legacy 1.x MQTT bindings are enabled)
|
### Example MQTT message payload for basic commands (default controls DPS[1] value, assumes true/false state control):
|
||||||
#### simple switch on/off
|
```
|
||||||
```
|
"ON"
|
||||||
Switch tuya_kitchen_coffeemachine_mqtt "Steckdose Kaffeemaschine" <socket> (<GROUPS>) ["Switchable"] {
|
"OFF"
|
||||||
mqtt="<[broker:tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/state:state:default:.*],
|
"on"
|
||||||
>[broker:tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command/on:command:ON:true],
|
"off"
|
||||||
>[broker:tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command/off:command:OFF:false]"
|
"1"
|
||||||
}
|
"0"
|
||||||
|
"toggle"
|
||||||
Switch tuya_livingroom_ledstrip_tv "LED Regal" <lightbulb> (<GROUPS>) ["Lighting"] {
|
"TOGGLE"
|
||||||
mqtt="<[broker:tuya/lightbulb/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/state:state:default:.*],
|
```
|
||||||
>[broker:tuya/lightbulb/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command/on:command:ON:true],
|
### Example MQTT message payload for advanced commands (set any DPS value):
|
||||||
>[broker:tuya/lightbulb/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command/off:command:OFF:false]"
|
```
|
||||||
}
|
"{ \"dps\": 1, \"set\": true }"
|
||||||
```
|
"{ \"dps\": 7, \"set\": true }"
|
||||||
#### change color of lightbulb
|
"{ \"multiple\": true, \"data\": { \"1\": true, \"7\": true } }"
|
||||||
```
|
"{ \"schema\": true }"
|
||||||
# .items
|
"{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene_4\" } }"
|
||||||
Group gTuyaLivingColor "Tuya color group" <lightbulb>
|
"{ \"multiple\": true, \"data\": { \"1\": true, \"2\": \"scene\", \"6\": \"c479000025ffc3\" } }"
|
||||||
Color tuya_livingroom_colorpicker "Stehlampe farbe" (LivingDining, Wohnzimmer)
|
```
|
||||||
|
### Example command topic for color change of lightbulb
|
||||||
String tuya_livingroom_ledstrip_tv_color "Set color [%s]" (gTuyaLivingColor, LivingDining, Wohnzimmer) {
|
```
|
||||||
mqtt=">[broker:tuya/lightbulb/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/color:command:*:default]"
|
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/color
|
||||||
}
|
|
||||||
|
Example MQTT message payload:
|
||||||
# .rules
|
64,0,100
|
||||||
import org.openhab.core.library.types.HSBType;
|
0,0,89
|
||||||
|
```
|
||||||
rule "Set HSB value of item RGBLed to RGB color value"
|
|
||||||
when
|
### Example state topics (get device data)
|
||||||
Item tuya_livingroom_colorpicker received command
|
### Get current device state (always DPS[1] value):
|
||||||
then
|
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/state
|
||||||
var appName = "Colorpicker.livingroom"
|
|
||||||
var color = receivedCommand.toString;
|
### Get all available device DPS values
|
||||||
|
Returns JSON.stringify(dps) values, use with care, does not always contain all dps values
|
||||||
// get all colors and send it via mqtt if light ist enabled
|
```
|
||||||
gTuyaLivingColor.members.forEach[ i |
|
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/dps
|
||||||
var name = i.name;
|
```
|
||||||
var stateName = name.toString.split("_color").get(0);
|
|
||||||
var stateItem = gTuyaLights.allMembers.filter [ conf | conf.name.contains(stateName.toString) ].head;
|
### Get any single DPS data value
|
||||||
|
```
|
||||||
if(stateItem.state == ON){
|
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/dps/<tuya-dps-id>
|
||||||
logInfo(appName, name + " change to color: " + color);
|
```
|
||||||
i.sendCommand(color);
|
|
||||||
Thread::sleep(400);
|
## Issues
|
||||||
}
|
Not all Tuya protocols are supported. For example, some devices use protocol 3.2 which currently remains unsupported by the TuyAPI project due to lack of enough information to reverse engineer the protcol. If you are unable to control your devices with tuya-mqtt please verify that you can query and control them with tuya-cli first. If tuya-cli works, then this script should also work, if it doesn't then this script will not work either.
|
||||||
]
|
|
||||||
end
|
## Integration with other Home Automation tools
|
||||||
|
openHAB examples are [here](docs/openHAB.md).
|
||||||
```
|
|
||||||
## Example items for OpenHAB 2.4 Bindings
|
## Contributors
|
||||||
#### simple switch on/off
|
- [TheAgentK](https://github.com/TheAgentK)
|
||||||
|
- [tsightler](https://github.com/tsightler)
|
||||||
With OpenHAB 2.X MQTT bindings you can add devices using a generic MQTT Thing via PaperUI or
|
- [Tycale](https://github.com/Tycale)
|
||||||
configuration files. For PaperUI simply at the generic MQTT Thing and set the state and
|
- [crashdummymch](https://github.com/crashdummymch)
|
||||||
command topics as follows:
|
- [GadgetAngel](https://github.com/GadgetAngel)
|
||||||
```
|
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/state
|
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command
|
## Related Projects:
|
||||||
```
|
- https://github.com/codetheweb/tuyapi
|
||||||
If you prefer using configuration files vs PaperUI, it should look something like this:
|
- https://github.com/unparagoned/njsTuya
|
||||||
|
- https://github.com/clach04/python-tuya
|
||||||
```
|
- https://github.com/Marcus-L/m4rcus.TuyaCore
|
||||||
Bridge mqtt:broker:myUnsecureBroker [ host="192.168.0.42", secure=false ]
|
- Specs: https://docs.tuya.com/en/cloudapi/cloud_access.html
|
||||||
{
|
|
||||||
Thing mqtt:topic:mything {
|
[](https://forthebadge.com)
|
||||||
Channels:
|
[](https://forthebadge.com)
|
||||||
Type switch : tuya_kitchen_coffeemachine_mqtt "Steckdose Kaffeemaschine" [ stateTopic="tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/state", commandTopic="tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/command" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
For a light with color you would need a separate channel with the command topic set to
|
|
||||||
tuya/<tuyaAPI-type>/<tuyaAPI-id>/<tuyaAPI-key>/<tuyaAPI-ip>/color and link that to your
|
|
||||||
color item.
|
|
||||||
|
|
||||||
#### Basic UI sitemap
|
|
||||||
```
|
|
||||||
Switch item=tuya_kitchen_coffeemachine_mqtt mappings=[ON="On", OFF="Off"]
|
|
||||||
Switch item=tuya_livingroom_ledstrip_tv mappings=[ON="On", OFF="Off"]
|
|
||||||
|
|
||||||
|
|
||||||
# Colorpicker for Lightbulbs
|
|
||||||
Colorpicker item=tuya_livingroom_colorpicker label="RGB Lampenfarbe" icon="slider" sendFrequency=30000
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
- [TheAgentK](https://github.com/TheAgentK)
|
|
||||||
- [tsightler](https://github.com/tsightler)
|
|
||||||
- [Tycale](https://github.com/Tycale)
|
|
||||||
- [crashdummymch](https://github.com/crashdummymch)
|
|
||||||
|
|
||||||
|
|
||||||
## Related Projects:
|
|
||||||
- https://github.com/codetheweb/tuyapi
|
|
||||||
- https://github.com/unparagoned/njsTuya
|
|
||||||
- https://github.com/clach04/python-tuya
|
|
||||||
- https://github.com/Marcus-L/m4rcus.TuyaCore
|
|
||||||
- Specs: https://docs.tuya.com/en/cloudapi/cloud_access.html
|
|
||||||
|
|
||||||
[](https://forthebadge.com)
|
|
||||||
[](https://forthebadge.com)
|
|
||||||
|
|||||||
142
docs/openHAB.md
Normal file
142
docs/openHAB.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
:exclamation: There is a greate Step-By-Step guide from user HolgiHab at openhab community ([Step-By-Step Guide](
|
||||||
|
https://community.openhab.org/t/step-by-step-guide-for-adding-tuya-bulbs-smart-life-to-oh2-using-tuya-mqtt-js-by-agentk/59371)). This guide is not only for light bulbs, but also applies to sockets. :exclamation:
|
||||||
|
|
||||||
|
## Example items for OpenHAB 1.x Bindings (still works with OH > 2.4 but only if legacy 1.x MQTT bindings are enabled)
|
||||||
|
### simple switch on/off
|
||||||
|
```
|
||||||
|
|
||||||
|
Switch tuya_kitchen_coffeemachine_mqtt "Steckdose Kaffeemaschine" <socket> (<GROUPS>) ["Switchable"] {
|
||||||
|
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]"
|
||||||
|
}
|
||||||
|
|
||||||
|
Switch tuya_livingroom_ledstrip_tv "LED Regal" <lightbulb> (<GROUPS>) ["Lighting"] {
|
||||||
|
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]"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### change color of lightbulb
|
||||||
|
```
|
||||||
|
|
||||||
|
# .items
|
||||||
|
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/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/color:command:*:default]"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# .rules
|
||||||
|
import org.openhab.core.library.types.HSBType;
|
||||||
|
|
||||||
|
rule "Set HSB value of item RGBLed to RGB color value"
|
||||||
|
when
|
||||||
|
Item tuya_livingroom_colorpicker received command
|
||||||
|
then
|
||||||
|
var appName = "Colorpicker.livingroom"
|
||||||
|
var color = receivedCommand.toString;
|
||||||
|
|
||||||
|
// get all colors and send it via mqtt if light ist enabled
|
||||||
|
gTuyaLivingColor.members.forEach[ i |
|
||||||
|
var name = i.name;
|
||||||
|
var stateName = name.toString.split("_color").get(0);
|
||||||
|
var stateItem = gTuyaLights.allMembers.filter [ conf | conf.name.contains(stateName.toString) ].head;
|
||||||
|
|
||||||
|
if(stateItem.state == ON){
|
||||||
|
logInfo(appName, name + " change to color: " + color);
|
||||||
|
i.sendCommand(color);
|
||||||
|
Thread::sleep(400);
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example items for OpenHAB 2.4 Bindings
|
||||||
|
### simple switch on/off
|
||||||
|
|
||||||
|
With OpenHAB 2.X MQTT bindings you can add devices using a generic MQTT Thing via PaperUI or
|
||||||
|
configuration files. For PaperUI simply at the generic MQTT Thing and set the state and
|
||||||
|
command topics as follows:
|
||||||
|
```
|
||||||
|
|
||||||
|
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/state
|
||||||
|
|
||||||
|
tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/command
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
If you prefer using configuration files vs PaperUI, it should look something like this:
|
||||||
|
See also OpenHAB 2.X MQTT binding [documentation](https://www.openhab.org/v2.4/addons/bindings/mqtt.generic/)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ]
|
||||||
|
{
|
||||||
|
|
||||||
|
Thing mqtt:topic:myCustomMQTT {
|
||||||
|
Channels:
|
||||||
|
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 }",
|
||||||
|
off="0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# *.item Example
|
||||||
|
Switch tuya_kitchen_coffeemachine_mqtt "Kitchen Coffee Machine Switch" <socket> (gKitchen, gTuya) ["Switchable"] {
|
||||||
|
channel="mqtt:topic:myUnsecureBroker:myCustomMQTT:tuya_kitchen_coffeemachine_mqtt_channel"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
For one RGB bulb you would need a separate channel with the command topic set to
|
||||||
|
`tuya/<tuyAPI-id>/<tuyAPI-key>/<tuyAPI-ip>/color` and link that to your color item.
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Bridge mqtt:broker:myUnsecureBroker [ host="localhost", secure=false ]
|
||||||
|
{
|
||||||
|
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:myUnsecureBroker:myCustomMQTT:livingroom_floorlamp_1_color"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Basic UI sitemap
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
381
package-lock.json
generated
381
package-lock.json
generated
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "tuya-api",
|
"name": "tuya-mqtt",
|
||||||
"version": "1.0.0",
|
"version": "2.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async-limiter": {
|
"@types/retry": {
|
||||||
"version": "1.0.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
||||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
|
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
|
||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -15,17 +15,30 @@
|
|||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||||
},
|
},
|
||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||||
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
|
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||||
},
|
},
|
||||||
"bl": {
|
"bl": {
|
||||||
"version": "1.2.2",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||||
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
|
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"readable-stream": "^2.3.5",
|
"buffer": "^5.5.0",
|
||||||
"safe-buffer": "^5.1.1"
|
"inherits": "^2.0.4",
|
||||||
|
"readable-stream": "^3.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
@@ -38,9 +51,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"buffer": {
|
"buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||||
"integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
|
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"base64-js": "^1.0.2",
|
"base64-js": "^1.0.2",
|
||||||
"ieee754": "^1.1.4"
|
"ieee754": "^1.1.4"
|
||||||
@@ -61,24 +74,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"color-convert": {
|
"color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"color-name": "1.1.3"
|
"color-name": "~1.1.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"color-name": {
|
"color-name": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
},
|
},
|
||||||
"commist": {
|
"commist": {
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/commist/-/commist-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz",
|
||||||
"integrity": "sha1-wMNSUBz29S6RJOPvicmAbiAi6+8=",
|
"integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"leven": "^1.0.0",
|
"leven": "^2.1.0",
|
||||||
"minimist": "^1.1.0"
|
"minimist": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -103,34 +116,27 @@
|
|||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||||
},
|
},
|
||||||
"crc": {
|
|
||||||
"version": "3.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
|
|
||||||
"integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
|
|
||||||
"requires": {
|
|
||||||
"buffer": "^5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"d": {
|
"d": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||||
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
|
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"es5-ext": "^0.10.9"
|
"es5-ext": "^0.10.50",
|
||||||
|
"type": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "3.2.6",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duplexify": {
|
"duplexify": {
|
||||||
"version": "3.6.0",
|
"version": "3.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
|
||||||
"integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==",
|
"integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"end-of-stream": "^1.0.0",
|
"end-of-stream": "^1.0.0",
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
@@ -139,21 +145,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"end-of-stream": {
|
"end-of-stream": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
|
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"once": "^1.4.0"
|
"once": "^1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es5-ext": {
|
"es5-ext": {
|
||||||
"version": "0.10.46",
|
"version": "0.10.53",
|
||||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
|
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
|
||||||
"integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==",
|
"integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"es6-iterator": "~2.0.3",
|
"es6-iterator": "~2.0.3",
|
||||||
"es6-symbol": "~3.1.1",
|
"es6-symbol": "~3.1.3",
|
||||||
"next-tick": "1"
|
"next-tick": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es6-iterator": {
|
"es6-iterator": {
|
||||||
@@ -189,15 +195,26 @@
|
|||||||
"es6-iterator": "~2.0.1",
|
"es6-iterator": "~2.0.1",
|
||||||
"es6-symbol": "3.1.1",
|
"es6-symbol": "3.1.1",
|
||||||
"event-emitter": "~0.3.5"
|
"event-emitter": "~0.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"es6-symbol": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
|
||||||
|
"integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
|
||||||
|
"requires": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "~0.10.14"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es6-symbol": {
|
"es6-symbol": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||||
"integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
|
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"d": "1",
|
"d": "^1.0.1",
|
||||||
"es5-ext": "~0.10.14"
|
"ext": "^1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event-emitter": {
|
"event-emitter": {
|
||||||
@@ -209,6 +226,21 @@
|
|||||||
"es5-ext": "~0.10.14"
|
"es5-ext": "~0.10.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ext": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
|
||||||
|
"requires": {
|
||||||
|
"type": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"type": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||||
@@ -220,9 +252,9 @@
|
|||||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||||
},
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
@@ -270,9 +302,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ieee754": {
|
"ieee754": {
|
||||||
"version": "1.1.12",
|
"version": "1.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||||
"integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA=="
|
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||||
},
|
},
|
||||||
"inflight": {
|
"inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
@@ -284,9 +316,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
"is-absolute": {
|
"is-absolute": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -341,23 +373,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||||
},
|
},
|
||||||
"json-stable-stringify": {
|
"json-stable-stringify-without-jsonify": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
|
||||||
"integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
|
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
|
||||||
"requires": {
|
|
||||||
"jsonify": "~0.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jsonify": {
|
|
||||||
"version": "0.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
|
|
||||||
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
|
|
||||||
},
|
},
|
||||||
"leven": {
|
"leven": {
|
||||||
"version": "1.0.2",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/leven/-/leven-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
|
||||||
"integrity": "sha1-kUS27ryl8dBoAWnxpncNzqYLdcM="
|
"integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA="
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
@@ -368,57 +392,53 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||||
},
|
},
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
"version": "2.18.8",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.18.8.tgz",
|
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.1.tgz",
|
||||||
"integrity": "sha512-3h6oHlPY/yWwtC2J3geraYRtVVoRM6wdI+uchF4nvSSafXPZnaKqF8xnX+S22SU/FcgEAgockVIlOaAX3fkMpA==",
|
"integrity": "sha512-Iv893r+jWlo5GkNcPOfCGwW8M49IixwHiKLFFYTociEymSibUVCORVEjPXWPGzSxhn7BdlUeHicbRmWiv0Crkg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"base64-js": "^1.3.0",
|
||||||
"commist": "^1.0.0",
|
"commist": "^1.0.0",
|
||||||
"concat-stream": "^1.6.2",
|
"concat-stream": "^1.6.2",
|
||||||
|
"debug": "^4.1.1",
|
||||||
"end-of-stream": "^1.4.1",
|
"end-of-stream": "^1.4.1",
|
||||||
"es6-map": "^0.1.5",
|
"es6-map": "^0.1.5",
|
||||||
"help-me": "^1.0.1",
|
"help-me": "^1.0.1",
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.5",
|
||||||
"mqtt-packet": "^5.6.0",
|
"mqtt-packet": "^6.3.2",
|
||||||
"pump": "^3.0.0",
|
"pump": "^3.0.0",
|
||||||
"readable-stream": "^2.3.6",
|
"readable-stream": "^2.3.6",
|
||||||
"reinterval": "^1.1.0",
|
"reinterval": "^1.1.0",
|
||||||
"split2": "^2.1.1",
|
"split2": "^3.1.0",
|
||||||
"websocket-stream": "^5.1.2",
|
"ws": "^7.3.1",
|
||||||
"xtend": "^4.0.1"
|
"xtend": "^4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mqtt-packet": {
|
"mqtt-packet": {
|
||||||
"version": "5.6.0",
|
"version": "6.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.6.0.tgz",
|
||||||
"integrity": "sha512-QECe2ivqcR1LRsPobRsjenEKAC3i1a5gmm+jNKJLrsiq9PaSQ18LlKFuxvhGxWkvGEPadWv6rKd31O4ICqS1Xw==",
|
"integrity": "sha512-LvghnKMFC70hKWMVykmhJarlO5e7lT3t9s9A2qPCUx+lazL3Mq55U+eCV0eLi7/nRRQYvEUWo/2tTo89EjnCJQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bl": "^1.2.1",
|
"bl": "^4.0.2",
|
||||||
"inherits": "^2.0.3",
|
"debug": "^4.1.1",
|
||||||
"process-nextick-args": "^2.0.0",
|
"process-nextick-args": "^2.0.1"
|
||||||
"safe-buffer": "^5.1.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"next-tick": {
|
"next-tick": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
||||||
},
|
},
|
||||||
"node-forge": {
|
|
||||||
"version": "0.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.0.tgz",
|
|
||||||
"integrity": "sha512-DVrvVeXwnSSX0Bgi9jy8p4IXQKLhGRQltG+UTR3Oci3Wb/zIROMoxw9im/K5s6KnNMheSWgG8K4qz8Njccdj3g=="
|
|
||||||
},
|
|
||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@@ -440,10 +460,19 @@
|
|||||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||||
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
|
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
|
||||||
},
|
},
|
||||||
|
"p-retry": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/retry": "^0.12.0",
|
||||||
|
"retry": "^0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"p-timeout": {
|
"p-timeout": {
|
||||||
"version": "2.0.1",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
|
||||||
"integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
|
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"p-finally": "^1.0.0"
|
"p-finally": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -459,9 +488,9 @@
|
|||||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||||
},
|
},
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||||
},
|
},
|
||||||
"pump": {
|
"pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@@ -494,9 +523,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
"inherits": "~2.0.3",
|
"inherits": "~2.0.3",
|
||||||
@@ -518,9 +547,9 @@
|
|||||||
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
|
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
|
||||||
},
|
},
|
||||||
"retry": {
|
"retry": {
|
||||||
"version": "0.10.1",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||||
"integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q="
|
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
|
||||||
},
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
@@ -528,17 +557,29 @@
|
|||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
},
|
},
|
||||||
"split2": {
|
"split2": {
|
||||||
"version": "2.2.0",
|
"version": "3.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
|
||||||
"integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==",
|
"integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"through2": "^2.0.2"
|
"readable-stream": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stream-shift": {
|
"stream-shift": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
|
||||||
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
|
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
|
||||||
},
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
@@ -549,18 +590,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"through2": {
|
"through2": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||||
"integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
|
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"readable-stream": "^2.1.5",
|
"readable-stream": "~2.3.6",
|
||||||
"xtend": "~4.0.1"
|
"xtend": "~4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"through2-filter": {
|
"through2-filter": {
|
||||||
"version": "2.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
|
||||||
"integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=",
|
"integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"through2": "~2.0.0",
|
"through2": "~2.0.0",
|
||||||
"xtend": "~4.0.0"
|
"xtend": "~4.0.0"
|
||||||
@@ -576,49 +617,37 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tuyapi": {
|
"tuyapi": {
|
||||||
"version": "3.2.3",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tuyapi/-/tuyapi-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/tuyapi/-/tuyapi-5.3.1.tgz",
|
||||||
"integrity": "sha512-3KJKPbjLz5UaSpZaIPgcZ5nGGE71S/S2mp2tzVrM0zHbMCeSNAzt3YIUv6rHXv1XTnCCjBQCXDGE46zMePZSng==",
|
"integrity": "sha512-l0bbWxe4L8J7/bAQn0bJtBVbVDAEglC1T3a/YKYM3UvDXaKgFQUDVKhfQfHFAt0bzXVq1TeqU0zG4WIrxgiTHg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"crc": "^3.5.0",
|
"debug": "4.1.1",
|
||||||
"debug": "^4.0.0",
|
"p-retry": "4.2.0",
|
||||||
"node-forge": "^0.8.0",
|
"p-timeout": "3.2.0"
|
||||||
"p-timeout": "^2.0.1",
|
|
||||||
"retry": "^0.10.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"debug": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
|
||||||
"requires": {
|
|
||||||
"ms": "^2.1.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"type": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
|
||||||
|
},
|
||||||
"typedarray": {
|
"typedarray": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||||
},
|
},
|
||||||
"ultron": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
|
|
||||||
},
|
|
||||||
"unc-path-regex": {
|
"unc-path-regex": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
||||||
"integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
|
"integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
|
||||||
},
|
},
|
||||||
"unique-stream": {
|
"unique-stream": {
|
||||||
"version": "2.2.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
|
||||||
"integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=",
|
"integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"json-stable-stringify": "^1.0.0",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"through2-filter": "^2.0.0"
|
"through2-filter": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"util-deprecate": {
|
"util-deprecate": {
|
||||||
@@ -626,38 +655,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
},
|
},
|
||||||
"websocket-stream": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-lchLOk435iDWs0jNuL+hiU14i3ERSrMA0IKSiJh7z6X/i4XNsutBZrtqu2CPOZuA4G/zabiqVAos0vW+S7GEVw==",
|
|
||||||
"requires": {
|
|
||||||
"duplexify": "^3.5.1",
|
|
||||||
"inherits": "^2.0.1",
|
|
||||||
"readable-stream": "^2.3.3",
|
|
||||||
"safe-buffer": "^5.1.1",
|
|
||||||
"ws": "^3.2.0",
|
|
||||||
"xtend": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "3.3.3",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||||
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
|
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||||
"requires": {
|
|
||||||
"async-limiter": "~1.0.0",
|
|
||||||
"safe-buffer": "~5.1.0",
|
|
||||||
"ultron": "~1.1.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
package.json
22
package.json
@@ -1,18 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "tuya-api",
|
"name": "tuya-mqtt",
|
||||||
"version": "1.0.0",
|
"version": "2.1.0",
|
||||||
"description": "",
|
"description": "Control Tuya devices locally via MQTT",
|
||||||
"main": "tuya.js",
|
"homepage": "https://github.com/TheAgentK/tuya-mqtt#readme",
|
||||||
|
"main": "tuya-mqtt.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": {
|
||||||
|
"name": "TheAgentK",
|
||||||
|
"email": "lulattsch22@googlemail.com"
|
||||||
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^1.9.3",
|
"color-convert": "^2.0.1",
|
||||||
"debug": "^3.2.6",
|
"debug": "^4.1.1",
|
||||||
"mqtt": "^2.18.8",
|
"mqtt": "^4.2.1",
|
||||||
"tuyapi": "^3.2.3"
|
"tuyapi": "^5.3.1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
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;
|
||||||
225
tuya-device.js
225
tuya-device.js
@@ -1,8 +1,8 @@
|
|||||||
const TuyAPI = require('tuyapi');
|
const TuyAPI = require('tuyapi');
|
||||||
const TuyColor = require('./tuya-color');
|
const TuyColor = require('./tuya-color');
|
||||||
const debug = require('debug')('TuyAPI-device');
|
const debug = require('debug')('TuyAPI:device');
|
||||||
const debugColor = require('debug')('TuyAPI-device-color');
|
const debugError = require('debug')('TuyAPI:device:error');
|
||||||
const debugTimer = require('debug')('TuyAPI-device-timer');
|
const debugColor = require('debug')('TuyAPI:device:color');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -10,7 +10,7 @@ const debugTimer = require('debug')('TuyAPI-device-timer');
|
|||||||
id: '03200240600194781244',
|
id: '03200240600194781244',
|
||||||
key: 'b8bdebab418f5b55',
|
key: 'b8bdebab418f5b55',
|
||||||
ip: '192.168.178.45',
|
ip: '192.168.178.45',
|
||||||
type: "socket"
|
type: "ver33"
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -18,8 +18,6 @@ var TuyaDevice = (function () {
|
|||||||
var devices = [];
|
var devices = [];
|
||||||
var events = {};
|
var events = {};
|
||||||
|
|
||||||
var autoTimeout = undefined;
|
|
||||||
|
|
||||||
function checkExisiting(id) {
|
function checkExisiting(id) {
|
||||||
var existing = false;
|
var existing = false;
|
||||||
// Check for existing instance
|
// Check for existing instance
|
||||||
@@ -33,65 +31,101 @@ var TuyaDevice = (function () {
|
|||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetTimer() {
|
function deleteDevice(id) {
|
||||||
return;
|
devices.forEach((device, key) => {
|
||||||
debugTimer("Reset timer for auto disconnect all devices");
|
if (device.hasOwnProperty("options")) {
|
||||||
clearTimeout(autoTimeout);
|
if (id === device.options.id) {
|
||||||
autoTimeout = setTimeout(() => {
|
debug("delete Device", devices[key].toString());
|
||||||
debugTimer("Auto disconnect all devices");
|
delete devices[key];
|
||||||
TuyaDevice.disconnectAll();
|
}
|
||||||
}, 10000);
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function TuyaDevice(options) {
|
function TuyaDevice(options, callback) {
|
||||||
var device = this;
|
var device = this;
|
||||||
// Check for existing instance
|
// Check for existing instance
|
||||||
if (existing = checkExisiting(options.id)) {
|
if (existing = checkExisiting(options.id)) {
|
||||||
return existing;
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve({
|
||||||
|
status: "connected",
|
||||||
|
device: existing
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(this instanceof TuyaDevice)) {
|
if (!(this instanceof TuyaDevice)) {
|
||||||
return new TuyaDevice(options);
|
return new TuyaDevice(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
options.type = options.type || "socket";
|
options.type = options.type || undefined;
|
||||||
options.persistentConnection = true;
|
|
||||||
|
|
||||||
Object.defineProperty(this, 'type', {
|
this.type = options.type;
|
||||||
value: options.type
|
this.options = options;
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(this, 'options', {
|
|
||||||
value: options
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(this, 'device', {
|
Object.defineProperty(this, 'device', {
|
||||||
value: new TuyAPI(this.options)
|
value: new TuyAPI(JSON.parse(JSON.stringify(this.options)))
|
||||||
});
|
|
||||||
|
|
||||||
this.device.on('connected', () => {
|
|
||||||
debug('Connected to device.');
|
|
||||||
device.triggerAll('connected');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.device.on('disconnected', () => {
|
|
||||||
debug('Disconnected from device.');
|
|
||||||
device.triggerAll('disconnected');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.device.on('data', data => {
|
this.device.on('data', data => {
|
||||||
debug('Data from device:', data);
|
if (typeof data == "string") {
|
||||||
device.triggerAll('data', data);
|
debugError('Data from device not encrypted:', data.replace(/[^a-zA-Z0-9 ]/g, ""));
|
||||||
|
} else {
|
||||||
|
debug('Data from device:', data);
|
||||||
|
device.triggerAll('data', data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.device.on('error', (err) => {
|
|
||||||
debug('Error: ' + err);
|
|
||||||
device.triggerAll('error', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.device.connect();
|
|
||||||
devices.push(this);
|
devices.push(this);
|
||||||
resetTimer();
|
// Find device on network
|
||||||
|
debug("Search device in network");
|
||||||
|
this.find().then(() => {
|
||||||
|
debug("Device found in network");
|
||||||
|
// Connect to device
|
||||||
|
this.device.connect();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return promis to wait for connection
|
||||||
|
*/
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.device.on('connected', () => {
|
||||||
|
device.triggerAll('connected');
|
||||||
|
device.connected = true;
|
||||||
|
debug('Connected to device.', device.toString());
|
||||||
|
resolve({
|
||||||
|
status: "connected",
|
||||||
|
device: this
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.device.on('disconnected', () => {
|
||||||
|
device.triggerAll('disconnected');
|
||||||
|
device.connected = false;
|
||||||
|
debug('Disconnected from device.', device.toString());
|
||||||
|
deleteDevice(options.id);
|
||||||
|
return reject({
|
||||||
|
status: "disconnect",
|
||||||
|
device: null
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.device.on('error', (err) => {
|
||||||
|
debugError(err);
|
||||||
|
device.triggerAll('error', err);
|
||||||
|
return reject({
|
||||||
|
error: err,
|
||||||
|
device: this
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TuyaDevice.prototype.toString = function () {
|
||||||
|
if (typeof this.type != "undefined") {
|
||||||
|
return this.type + " (" + this.options.ip + ", " + this.options.id + ", " + this.options.key + ")";
|
||||||
|
} else {
|
||||||
|
return " (" + this.options.ip + ", " + this.options.id + ", " + this.options.key + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.triggerAll = function (name, argument) {
|
TuyaDevice.prototype.triggerAll = function (name, argument) {
|
||||||
@@ -103,98 +137,105 @@ var TuyaDevice = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.on = function (name, callback) {
|
TuyaDevice.prototype.on = function (name, callback) {
|
||||||
|
if (!this.connected) return;
|
||||||
var device = this;
|
var device = this;
|
||||||
this.device.on(name, function () {
|
this.device.on(name, function () {
|
||||||
callback.apply(device, arguments);
|
callback.apply(device, arguments);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.get = function (options) {
|
TuyaDevice.prototype.find = function () {
|
||||||
resetTimer();
|
return this.device.find();
|
||||||
return this.device.get(options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.set = function (options, callback) {
|
TuyaDevice.prototype.get = function () {
|
||||||
var device = this;
|
return this.device.get();
|
||||||
debug('Setting status:', options);
|
}
|
||||||
return this.device.set(options).then(result => {
|
|
||||||
device.get().then(status => {
|
TuyaDevice.prototype.set = function (options) {
|
||||||
debug('Result of setting status to', status);
|
debug('set:', options);
|
||||||
if (callback != undefined) {
|
return new Promise((resolve, reject) => {
|
||||||
callback.call(device, status);
|
this.device.set(options).then((result) => {
|
||||||
}
|
this.get().then(() => {
|
||||||
return;
|
debug("set completed ");
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
resetTimer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.switch = function (newStatus, callback) {
|
TuyaDevice.prototype.switch = function (newStatus, callback) {
|
||||||
|
if (!this.connected) return;
|
||||||
newStatus = newStatus.toLowerCase();
|
newStatus = newStatus.toLowerCase();
|
||||||
debug("switch: " + newStatus);
|
|
||||||
if (newStatus == "on") {
|
if (newStatus == "on") {
|
||||||
this.switchOn(callback);
|
return this.switchOn(callback);
|
||||||
}
|
}
|
||||||
if (newStatus == "off") {
|
if (newStatus == "off") {
|
||||||
this.switchOff(callback);
|
return this.switchOff(callback);
|
||||||
}
|
}
|
||||||
if (newStatus == "toggle") {
|
if (newStatus == "toggle") {
|
||||||
this.toggle(callback);
|
return this.toggle(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.switchOn = function (callback) {
|
TuyaDevice.prototype.switchOn = function () {
|
||||||
var device = this;
|
if (!this.connected) return;
|
||||||
debug("switch -> ON");
|
debug("switch -> ON");
|
||||||
device.get().then(status => {
|
|
||||||
device.set({
|
return this.set({
|
||||||
set: true
|
set: true
|
||||||
}, callback);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.switchOff = function (callback) {
|
TuyaDevice.prototype.switchOff = function () {
|
||||||
var device = this;
|
if (!this.connected) return;
|
||||||
debug("switch -> OFF");
|
debug("switch -> OFF");
|
||||||
device.get().then(status => {
|
|
||||||
device.set({
|
return this.set({
|
||||||
set: false
|
set: false
|
||||||
}, callback);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.toggle = function (callback) {
|
TuyaDevice.prototype.toggle = function () {
|
||||||
var device = this;
|
if (!this.connected) return;
|
||||||
device.get().then(status => {
|
return new Promise((resolve, reject) => {
|
||||||
device.set({
|
this.get().then((status) => {
|
||||||
set: !status
|
debug("toogle state", status);
|
||||||
}, callback);
|
this.set({
|
||||||
|
set: !status
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.setColor = function (hexColor, callback) {
|
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);
|
debugColor("Set color to: ", hexColor);
|
||||||
var device = this;
|
|
||||||
var tuya = this.device;
|
var tuya = this.device;
|
||||||
var color = new TuyColor(tuya);
|
var color = new TuyColor(tuya);
|
||||||
var dps = color.setColor(hexColor);
|
var dps = color.setColor(hexColor);
|
||||||
debugColor("dps values:", dps);
|
debugColor("dps values:", dps);
|
||||||
|
|
||||||
device.get().then(status => {
|
return this.set({
|
||||||
device.set({
|
multiple: true,
|
||||||
multiple: true,
|
data: dps
|
||||||
data: dps
|
|
||||||
}, callback);
|
|
||||||
});
|
});
|
||||||
resetTimer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.connect = function (callback) {
|
TuyaDevice.prototype.connect = function (callback) {
|
||||||
this.device.connect(callback);
|
debug("Connect to TuyAPI Device");
|
||||||
|
return this.device.connect(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
TuyaDevice.prototype.disconnect = function (callback) {
|
TuyaDevice.prototype.disconnect = function (callback) {
|
||||||
this.device.disconnect(callback);
|
debug("Disconnect from TuyAPI Device");
|
||||||
|
return this.device.disconnect(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(TuyaDevice, 'devices', {
|
Object.defineProperty(TuyaDevice, 'devices', {
|
||||||
@@ -226,4 +267,4 @@ var TuyaDevice = (function () {
|
|||||||
return TuyaDevice;
|
return TuyaDevice;
|
||||||
}());
|
}());
|
||||||
|
|
||||||
module.exports = TuyaDevice;
|
module.exports = TuyaDevice;
|
||||||
|
|||||||
623
tuya-mqtt.js
623
tuya-mqtt.js
@@ -1,248 +1,375 @@
|
|||||||
const mqtt = require('mqtt');
|
const mqtt = require('mqtt');
|
||||||
const TuyaDevice = require('./tuya-device');
|
const TuyaDevice = require('./tuya-device');
|
||||||
const debug = require('debug')('tuya-mqtt');
|
const debug = require('debug')('TuyAPI:mqtt');
|
||||||
const debugColor = require('debug')('color');
|
const debugColor = require('debug')('TuyAPI:mqtt:color');
|
||||||
const debugMqtt = require('debug')('mqtt');
|
const debugTuya = require('debug')('TuyAPI:mqtt:device');
|
||||||
const debugTuya = require('debug')('tuyAPI-Events');
|
const debugError = require('debug')('TuyAPI:mqtt:error');
|
||||||
const debugError = require('debug')('error');
|
var cleanup = require('./cleanup').Cleanup(onExit);
|
||||||
var cleanup = require('./cleanup').Cleanup(onExit);
|
|
||||||
|
var CONFIG = undefined;
|
||||||
function bmap(istate) {
|
var mqtt_client = undefined;
|
||||||
return istate ? 'ON' : "OFF";
|
|
||||||
}
|
function bmap(istate) {
|
||||||
|
return istate ? 'ON' : "OFF";
|
||||||
function boolToString(istate) {
|
}
|
||||||
return istate ? 'true' : "false";
|
|
||||||
}
|
function boolToString(istate) {
|
||||||
|
return istate ? 'true' : "false";
|
||||||
var connected = undefined;
|
}
|
||||||
var CONFIG = undefined;
|
|
||||||
|
/*
|
||||||
try {
|
* execute function on topic message
|
||||||
CONFIG = require("./config");
|
*/
|
||||||
} catch (e) {
|
|
||||||
console.error("Configuration file not found")
|
function IsJsonString(text) {
|
||||||
debugError(e)
|
if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||||
process.exit(1)
|
//the json is ok
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
if (typeof CONFIG.qos == "undefined") {
|
return false;
|
||||||
CONFIG.qos = 2;
|
}
|
||||||
}
|
|
||||||
if (typeof CONFIG.retain == "undefined") {
|
/**
|
||||||
CONFIG.retain = false;
|
* check mqtt-topic string for old notation with included device type
|
||||||
}
|
* @param {String} topic
|
||||||
|
*/
|
||||||
const mqtt_client = mqtt.connect({
|
function checkTopicNotation(_topic) {
|
||||||
host: CONFIG.host,
|
var topic = _topic.split("/");
|
||||||
port: CONFIG.port,
|
var type = topic[1];
|
||||||
username: CONFIG.mqtt_user,
|
var result = (type == "socket" || type == "lightbulb" || type == "ver3.1" || type == "ver3.3");
|
||||||
password: CONFIG.mqtt_pass,
|
return result;
|
||||||
});
|
}
|
||||||
|
|
||||||
mqtt_client.on('connect', function (err) {
|
/**
|
||||||
debugMqtt("Verbindung mit MQTT-Server hergestellt");
|
* get action from mqtt-topic string
|
||||||
connected = true;
|
* @param {String} topic
|
||||||
var topic = CONFIG.topic + '#';
|
* @returns {String} action type
|
||||||
mqtt_client.subscribe(topic, {
|
*/
|
||||||
retain: CONFIG.retain,
|
function getActionFromTopic(_topic) {
|
||||||
qos: CONFIG.qos
|
var topic = _topic.split("/");
|
||||||
});
|
|
||||||
});
|
if (checkTopicNotation(_topic)) {
|
||||||
|
return topic[5];
|
||||||
mqtt_client.on("reconnect", function (error) {
|
} else {
|
||||||
if (connected) {
|
return topic[4];
|
||||||
debugMqtt("Verbindung mit MQTT-Server wurde unterbrochen. Erneuter Verbindungsversuch!");
|
}
|
||||||
} else {
|
}
|
||||||
debugMqtt("Verbindung mit MQTT-Server konnte nicht herrgestellt werden.");
|
|
||||||
}
|
/**
|
||||||
connected = false;
|
* get device informations from mqtt-topic string
|
||||||
});
|
* @param {String} topic
|
||||||
|
* @returns {String} object.id
|
||||||
mqtt_client.on("error", function (error) {
|
* @returns {String} object.key
|
||||||
debugMqtt("Verbindung mit MQTT-Server konnte nicht herrgestellt werden.", error);
|
* @returns {String} object.ip
|
||||||
connected = false;
|
*/
|
||||||
});
|
function getDeviceFromTopic(_topic) {
|
||||||
|
var topic = _topic.split("/");
|
||||||
/**
|
|
||||||
* execute function on topic message
|
if (checkTopicNotation(_topic)) {
|
||||||
*/
|
// When there are 5 topic levels
|
||||||
function boolToString(istate) {
|
// topic 2 is id, and topic 3 is key
|
||||||
return istate == 1 ? 'on' : "off";
|
var options = {
|
||||||
}
|
id: topic[2],
|
||||||
|
key: topic[3]
|
||||||
function convertMessage(message) {
|
};
|
||||||
var status = message.toString();
|
|
||||||
status = boolToString(status);
|
// 4th topic is IP address or "discover" keyword
|
||||||
status = status.toLowerCase();
|
if (topic[4] !== "discover") {
|
||||||
return status;
|
options.ip = topic[4]
|
||||||
}
|
// If IP is manually specified check if topic 1
|
||||||
|
// is protocol version and set accordingly
|
||||||
mqtt_client.on('message', function (topic, message) {
|
if (topic[1] == "ver3.3") {
|
||||||
try {
|
options.version = "3.3"
|
||||||
var cMessage = convertMessage(message);
|
} else if (topic[1] == "ver3.1") {
|
||||||
var topic = topic.split("/");
|
options.version = "3.1"
|
||||||
var options = {
|
} else {
|
||||||
type: topic[1],
|
// If topic is not version then it's device type
|
||||||
id: topic[2],
|
// Not used anymore but still supported for legacy setups
|
||||||
key: topic[3],
|
options.type = topic[1]
|
||||||
ip: topic[4],
|
};
|
||||||
};
|
};
|
||||||
var exec = topic[5];
|
|
||||||
|
return options;
|
||||||
if (options.type == "socket" || options.type == "lightbulb") {
|
} else {
|
||||||
debug("device", options);
|
// When there are 4 topic levels
|
||||||
debug("message", cMessage);
|
// topic 1 is id, topic 2 is key
|
||||||
var device = new TuyaDevice(options);
|
var options = {
|
||||||
|
id: topic[1],
|
||||||
if (exec == "command") {
|
key: topic[2]
|
||||||
var status = topic[6];
|
};
|
||||||
if (status == null) {
|
|
||||||
device.switch(cMessage);
|
// If topic 3 is not discover assume it is IP address
|
||||||
} else {
|
// Todo: Validate it is an IP address
|
||||||
device.switch(status);
|
if (topic[3] !== "discover") {
|
||||||
}
|
options.ip = topic[3]
|
||||||
}
|
};
|
||||||
if (exec == "color") {
|
|
||||||
var color = message.toString();
|
return options;
|
||||||
color = color.toLowerCase();
|
}
|
||||||
debugColor("topic: ", topic);
|
}
|
||||||
debugColor("onColor: ", color);
|
|
||||||
device.setColor(color);
|
/**
|
||||||
}
|
* get command from mqtt - topic string
|
||||||
}
|
* converts simple commands to TuyAPI JSON commands
|
||||||
} catch (e) {
|
* @param {String} topic
|
||||||
debugError(e);
|
* @returns {Object}
|
||||||
}
|
*/
|
||||||
});
|
function getCommandFromTopic(_topic, _message) {
|
||||||
|
var topic = _topic.split("/");
|
||||||
/**
|
var command = null;
|
||||||
* Publish current TuyaDevice state to MQTT-Topic
|
|
||||||
* @param {TuyaDevice} device
|
if (checkTopicNotation(_topic)) {
|
||||||
* @param {boolean} status
|
command = topic[6];
|
||||||
*/
|
} else {
|
||||||
function publishStatus(device, status) {
|
command = topic[5];
|
||||||
if (mqtt_client.connected == true) {
|
}
|
||||||
try {
|
|
||||||
var type = device.type;
|
if (command == null) {
|
||||||
var tuyaID = device.options.id;
|
command = _message;
|
||||||
var tuyaKey = device.options.key;
|
}
|
||||||
var tuyaIP = device.options.ip;
|
|
||||||
|
if (command != "1" && command != "0" && IsJsonString(command)) {
|
||||||
if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined" && typeof tuyaIP != "undefined") {
|
debug("command is JSON");
|
||||||
var topic = CONFIG.topic + type + "/" + tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state";
|
command = JSON.parse(command);
|
||||||
mqtt_client.publish(topic, status, {
|
} else {
|
||||||
retain: CONFIG.retain,
|
if (command.toLowerCase() != "toggle") {
|
||||||
qos: CONFIG.qos
|
// convert simple commands (on, off, 1, 0) to TuyAPI-Commands
|
||||||
});
|
var convertString = command.toLowerCase() == "on" || command == "1" || command == 1 ? true : false;
|
||||||
debugTuya("mqtt status updated to:" + topic + " -> " + status);
|
command = {
|
||||||
} else {
|
set: convertString
|
||||||
debugTuya("mqtt status not updated");
|
}
|
||||||
}
|
} else {
|
||||||
} catch (e) {
|
command = command.toLowerCase();
|
||||||
debugError(e);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
return command;
|
||||||
|
}
|
||||||
function publishColorState(device, state) {
|
|
||||||
|
/**
|
||||||
}
|
* Publish current TuyaDevice state to MQTT-Topic
|
||||||
|
* @param {TuyaDevice} device
|
||||||
/**
|
* @param {boolean} status
|
||||||
* publish all dps-values to topic
|
*/
|
||||||
* @param {TuyaDevice} device
|
function publishStatus(device, status) {
|
||||||
* @param {Object} dps
|
if (mqtt_client.connected == true) {
|
||||||
*/
|
try {
|
||||||
function publishDPS(device, dps) {
|
var type = device.type;
|
||||||
if (mqtt_client.connected == true) {
|
var tuyaID = device.options.id;
|
||||||
try {
|
var tuyaKey = device.options.key;
|
||||||
var type = device.type;
|
var tuyaIP = device.options.ip;
|
||||||
var tuyaID = device.options.id;
|
|
||||||
var tuyaKey = device.options.key;
|
if (typeof tuyaIP == "undefined") {
|
||||||
var tuyaIP = device.options.ip;
|
tuyaIP = "discover"
|
||||||
|
}
|
||||||
if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined" && typeof tuyaIP != "undefined") {
|
|
||||||
var topic = CONFIG.topic + type + "/" + tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/dps";
|
if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined") {
|
||||||
var data = JSON.stringify(dps);
|
var topic = CONFIG.topic;
|
||||||
debugTuya("mqtt dps updated to:" + topic + " -> ", data);
|
if (typeof type != "undefined") {
|
||||||
mqtt_client.publish(topic, data, {
|
topic += type + "/";
|
||||||
retain: CONFIG.retain,
|
}
|
||||||
qos: CONFIG.qos
|
topic += tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/state";
|
||||||
});
|
|
||||||
|
mqtt_client.publish(topic, status, {
|
||||||
Object.keys(dps).forEach(function (key) {
|
retain: CONFIG.retain,
|
||||||
var topic = CONFIG.topic + type + "/" + tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/dps/" + key;
|
qos: CONFIG.qos
|
||||||
var data = JSON.stringify(dps[key]);
|
});
|
||||||
debugTuya("mqtt dps updated to:" + topic + " -> dps[" + key + "]", data);
|
debugTuya("mqtt status updated to:" + topic + " -> " + status);
|
||||||
mqtt_client.publish(topic, data, {
|
} else {
|
||||||
retain: CONFIG.retain,
|
debugTuya("mqtt status not updated");
|
||||||
qos: CONFIG.qos
|
}
|
||||||
});
|
} catch (e) {
|
||||||
});
|
debugError(e);
|
||||||
} else {
|
}
|
||||||
debugTuya("mqtt dps not updated");
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
debugError(e);
|
function publishColorState(device, state) {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* publish all dps-values to topic
|
||||||
* event fires if TuyaDevice sends data
|
* @param {TuyaDevice} device
|
||||||
* @see TuyAPI (https://github.com/codetheweb/tuyapi)
|
* @param {Object} dps
|
||||||
*/
|
*/
|
||||||
TuyaDevice.onAll('data', function (data) {
|
function publishDPS(device, dps) {
|
||||||
try {
|
if (mqtt_client.connected == true) {
|
||||||
debugTuya('Data from device ' + this.type + ' :', data);
|
try {
|
||||||
var status = data.dps['1'];
|
var type = device.type;
|
||||||
if (typeof status != "undefined") {
|
var tuyaID = device.options.id;
|
||||||
publishStatus(this, bmap(status));
|
var tuyaKey = device.options.key;
|
||||||
}
|
var tuyaIP = device.options.ip;
|
||||||
publishDPS(this, data.dps);
|
|
||||||
} catch (e) {
|
if (typeof tuyaIP == "undefined") {
|
||||||
debugError(e);
|
tuyaIP = "discover"
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (typeof tuyaID != "undefined" && typeof tuyaKey != "undefined") {
|
||||||
/**
|
var baseTopic = CONFIG.topic;
|
||||||
* MQTT connection tester
|
if (typeof type != "undefined") {
|
||||||
*/
|
baseTopic += type + "/";
|
||||||
function MQTT_Tester() {
|
}
|
||||||
this.interval = null;
|
baseTopic += tuyaID + "/" + tuyaKey + "/" + tuyaIP + "/dps";
|
||||||
|
|
||||||
function mqttConnectionTest() {
|
var topic = baseTopic;
|
||||||
if (mqtt_client.connected != connected) {
|
var data = JSON.stringify(dps);
|
||||||
connected = mqtt_client.connected;
|
debugTuya("mqtt dps updated to:" + topic + " -> ", data);
|
||||||
if (connected) {
|
mqtt_client.publish(topic, data, {
|
||||||
debugMqtt('MQTT-Server verbunden.');
|
retain: CONFIG.retain,
|
||||||
} else {
|
qos: CONFIG.qos
|
||||||
debugMqtt('MQTT-Server nicht verbunden.');
|
});
|
||||||
}
|
|
||||||
}
|
Object.keys(dps).forEach(function (key) {
|
||||||
}
|
var topic = baseTopic + "/" + key;
|
||||||
|
var data = JSON.stringify(dps[key]);
|
||||||
this.destroy = function () {
|
debugTuya("mqtt dps updated to:" + topic + " -> dps[" + key + "]", data);
|
||||||
clearInterval(this.interval);
|
mqtt_client.publish(topic, data, {
|
||||||
this.interval = undefined;
|
retain: CONFIG.retain,
|
||||||
}
|
qos: CONFIG.qos
|
||||||
|
});
|
||||||
this.connect = function () {
|
});
|
||||||
this.interval = setInterval(mqttConnectionTest, 1500);
|
} else {
|
||||||
mqttConnectionTest();
|
debugTuya("mqtt dps not updated");
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
var constructor = (function (that) {
|
debugError(e);
|
||||||
that.connect.call(that);
|
}
|
||||||
})(this);
|
}
|
||||||
}
|
}
|
||||||
var tester = new MQTT_Tester();
|
|
||||||
|
/**
|
||||||
/**
|
* event fires if TuyaDevice sends data
|
||||||
* Function call on script exit
|
* @see TuyAPI (https://github.com/codetheweb/tuyapi)
|
||||||
*/
|
*/
|
||||||
function onExit() {
|
TuyaDevice.onAll('data', function (data) {
|
||||||
TuyaDevice.disconnectAll();
|
try {
|
||||||
if (tester) tester.destroy();
|
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));
|
||||||
|
}
|
||||||
|
publishDPS(this, data.dps);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function call on script exit
|
||||||
|
*/
|
||||||
|
function onExit() {
|
||||||
|
TuyaDevice.disconnectAll();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple sleep to pause in async functions
|
||||||
|
function sleep(sec) {
|
||||||
|
return new Promise(res => setTimeout(res, sec*1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main code function
|
||||||
|
const main = async() => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
CONFIG = require("./config");
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Configuration file not found")
|
||||||
|
debugError(e)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof CONFIG.qos == "undefined") {
|
||||||
|
CONFIG.qos = 2;
|
||||||
|
}
|
||||||
|
if (typeof CONFIG.retain == "undefined") {
|
||||||
|
CONFIG.retain = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mqtt_client = mqtt.connect({
|
||||||
|
host: CONFIG.host,
|
||||||
|
port: CONFIG.port,
|
||||||
|
username: CONFIG.mqtt_user,
|
||||||
|
password: CONFIG.mqtt_pass,
|
||||||
|
});
|
||||||
|
|
||||||
|
mqtt_client.on('connect', function (err) {
|
||||||
|
debug("Connection established to MQTT server");
|
||||||
|
var topic = CONFIG.topic + '#';
|
||||||
|
mqtt_client.subscribe(topic, {
|
||||||
|
retain: CONFIG.retain,
|
||||||
|
qos: CONFIG.qos
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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("Received command: ", command);
|
||||||
|
if (command == "toggle") {
|
||||||
|
device.switch(command).then((data) => {
|
||||||
|
debug("Set device status completed: ", data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (command.schema === true) {
|
||||||
|
// Trigger device schema update to update state
|
||||||
|
device.schema(command).then((data) => {
|
||||||
|
});
|
||||||
|
debug("Get schema status command complete");
|
||||||
|
} 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()
|
||||||
Reference in New Issue
Block a user