Tuya WiFi Smart Plug: Difference between revisions

From Leo's Notes
This page was last edited on 26 August 2019, at 03:10.
Line 9: Line 9:
These Tuya smart plugs are hard coded to these servers:
These Tuya smart plugs are hard coded to these servers:
* mq.gw.tuyaus.com via MQTT
* mq.gw.tuyaus.com via MQTT
* a1.tuyaus.com via HTTP
* a.tuyaus.com via HTTP


To self host these services, you will need to hijack the DNS records to your self hosted service.
To self host these services, you will need to hijack the DNS records to your self hosted service.
Line 28: Line 28:
{{highlight|lang=text|code=
{{highlight|lang=text|code=
<nowiki>{"protocol":4,"t":27,"data":{"devId":"02200194dc4f22135606","dps":{"1":true,"2":0}},"s":9}</nowiki>
<nowiki>{"protocol":4,"t":27,"data":{"devId":"02200194dc4f22135606","dps":{"1":true,"2":0}},"s":9}</nowiki>
}}
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.
{{highlight|lang=text|code=
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}
}}
{{highlight|lang=text|code=
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}
}}
}}



Revision as of 03:10, 26 August 2019

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}}
&gt;&gt;&gt; x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0}}
&gt;&gt;&gt; x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0}}
&gt;&gt;&gt; x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0, u'5': 0, u'4': 0, u'6': 1184}}
&gt;&gt;&gt; x.status()
{u'devId': u'02200194dc4f22135606', u'dps': {u'1': True, u'2': 0, u'5': 0, u'4': 0, u'6': 1184}}
&gt;&gt;&gt; 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