Tuya WiFi Smart Plug

From Leo's Notes
Revision as of 03:10, 26 August 2019 by Leo (talk | contribs) (→Self Hosted)
This page was last edited on 26 August 2019, at 03:10.

To talk to the device, you need 3 things:

  1. The device IP address
  2. UUID / Device ID
  3. localKey

You can get this by sniffing the traffic the Android app makes with a SSL Capture app. When adding a new plug, some of the requests should contain something about a 'localKey'. One of the requests should also return all devices as well as their corresponding localKey from their server.

Self Hosted

These Tuya smart plugs are hard coded to these servers:

  • mq.gw.tuyaus.com via MQTT
  • a.tuyaus.com via HTTP

To self host these services, you will need to hijack the DNS records to your self hosted service.

MQTT communication is encrypted with 128-bit AES ECB using the localKey as the encryption key. The topic is smart/device/out/device_id. Because it is using ECB, similar messages will have similar cipher texts as seen below.

smart/device/out/02200194dc4f22135606 2.1f9d6aaa8c028751dtahaVgb6sZEC2uRNfToAVHb+qE94qh+hk1R7VkGt0djgWetQX1e4IxSvtQYLfwBhOA9eLsEfLfBdgpfZV3z0kkeUENQM7x/OcUTnhv31fBPFcxdihiAfGBTUgtG1OUjL
smart/device/out/02200194dc4f22135606 2.1468d26b81e3787aatahaVgb6sZEC2uRNfToAVHb+qE94qh+hk1R7VkGt0djgWetQX1e4IxSvtQYLfwBhOA9eLsEfLfBdgpfZV3z0kkeUENQM7x/OcUTnhv31fBPK039bcLeTGse/67AOz6Lm
smart/device/out/02200194dc4f22135606 2.1c320cd2dfa2d20e1tahaVgb6sZEC2uRNfToAVHb+qE94qh+hk1R7VkGt0djgWetQX1e4IxSvtQYLfwBhOA9eLsEfLfBdgpfZV3z0kkqeR4eguTTZfmoDnLO6Gsv8UBqGAblVCndgzJmUDrlO
smart/device/out/02200194dc4f22135606 2.168c2d8f05211a108tahaVgb6sZEC2uRNfToAVMhq/cediwGM0WFFnEJ4kvvgWetQX1e4IxSvtQYLfwBhOA9eLsEfLfBdgpfZV3z0khHz8nKhR2hdXlCbUYtj/GjX0qFqGa/6s3uSB36TRSSE
smart/device/out/02200194dc4f22135606 2.15447b84b25063944tahaVgb6sZEC2uRNfToAVOiDIlXh48IBRomThtqXzoDnn+1z5MCcHEIrwfVuPfvgpu5AYqA77Ys1ADM0zpHXV6YJLnpWJXghRTcedNpbhfpkK5IMk+UvEQNenmF0sduA
smart/device/out/02200194dc4f22135606 2.1f5542a52dabfcf73tahaVgb6sZEC2uRNfToAVOiDIlXh48IBRomThtqXzoDnn+1z5MCcHEIrwfVuPfvgpu5AYqA77Ys1ADM0zpHXV0RQlDrQca4knVjDakm02aatECxAMdcGsMmLjUtCk0mr
smart/device/out/02200194dc4f22135606 2.15ef756451e1db8aatahaVgb6sZEC2uRNfToAVGtwt9PXVjhurww9gAAsVUXnn+1z5MCcHEIrwfVuPfvgpu5AYqA77Ys1ADM0zpHXV6YJLnpWJXghRTcedNpbhfqVhbDB6i1efHyNUnWaDsr/
smart/device/out/02200194dc4f22135606 2.15c4fe47ade0801e7tahaVgb6sZEC2uRNfToAVGtwt9PXVjhurww9gAAsVUXnn+1z5MCcHEIrwfVuPfvgpu5AYqA77Ys1ADM0zpHXV0RQlDrQca4knVjDakm02abAea+OhxGULiAXXFiilhMA

The payload in plain text looks like this:

{"protocol":4,"t":27,"data":{"devId":"02200194dc4f22135606","dps":{"1":true,"2":0}},"s":9}

The device also phones home via HTTP. It appears that blocking HTTP completely will render the device somewhat crippled. Certain features such as voltage / wattage are unavailable.


A few captured requests are shown below.

POST /gw.json?a=atop.online.debug.log&gwId=0220xxxxxxxxxxxxxx606
GET
Array
(
    [a] => atop.online.debug.log
    [gwId] => 0220xxxxxxxxxxxxxx606
    [t] => 1317
    [sign] => 0214c93ac7dc7d6f3834d6dfda475e3a
)

POST
Array
(
    [data] => D37735B04A...
)

Headers:
Content-Type: application/json;charset=UTF-8'
Content-Length: 56'
Content-Language: zh-CN'
Server: Tuya-Sec'
User-Agent: Go-http-client/1.1

Response from Server:
{"result":true,"t":1566787618,"e":false,"success":true}


POST /gw.json?a=s.gw.dev.timer.count&gwId=0220xxxxxxxxxxxxxx606
GET
Array
(
    [a] => s.gw.dev.timer.count
    [gwId] => 0220xxxxxxxxxxxxxx606
    [t] => 7614
    [sign] => 2d9833c929f652d72a905d1459ea6b7a

)

POST
Array
(
    [data] => D377...
)

Headers:
Content-Type: application/json;charset=UTF-8'
Content-Length: 56'
Content-Language: zh-CN'
Server: Tuya-Sec'
User-Agent: Go-http-client/1.1

Response from Server:
{"result":{"devId":"0220xxxxxxxxxxxxxx606","count":0,"lastFetchTime":0},"t":1566787629,"e":false,"success":true}

tuya python

The status output from the AWP04L smart plug looks like this. The plug has monitoring features and should return the voltage, amps, and watts.

{u'devId': u'02200194dc4f22137d7f', u'dps': {
u'1': True,        outlet status
u'2': 0,           timer, decrements by 5, in seconds
u'5': 56,          watts (5.6)
u'4': 73,          amps (0.073 amps)
u'6': 1198         volts
}}

{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0, u'5': 187, u'4': 176, u'6': 1190}}

I don't understand why amps * volts != watts...

You will probably still need to allow it to connect to the cloud once. If you don't let it completely connect, some features like voltage/wattage are disabled from the output (?). MQTT must be working, or else these features are disabled:


{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0}}
>>> x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0}}
>>> x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0}}
>>> x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0, u'5': 0, u'4': 0, u'6': 1184}}
>>> x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0, u'5': 0, u'4': 0, u'6': 1184}}
>>> x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0, u'5': 0, u'4': 0, u'6': 1184}}

See Also