{"id":21917456,"url":"https://github.com/clusterm/tuyanet","last_synced_at":"2025-08-20T18:33:27.285Z","repository":{"id":41038446,"uuid":"421855490","full_name":"ClusterM/tuyanet","owner":"ClusterM","description":".NET library to interface with Tuya WiFi smart devices over LAN.","archived":false,"fork":false,"pushed_at":"2024-02-20T18:58:18.000Z","size":96,"stargazers_count":66,"open_issues_count":7,"forks_count":12,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-12-13T23:55:31.888Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ClusterM.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":["ClusterM"],"custom":["https://www.buymeacoffee.com/cluster","https://boosty.to/cluster"]}},"created_at":"2021-10-27T14:39:57.000Z","updated_at":"2024-12-12T06:17:05.000Z","dependencies_parsed_at":"2024-02-20T19:55:22.399Z","dependency_job_id":"187e3451-624d-4d6e-a194-a1b1ec04f3fd","html_url":"https://github.com/ClusterM/tuyanet","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClusterM%2Ftuyanet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClusterM%2Ftuyanet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClusterM%2Ftuyanet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClusterM%2Ftuyanet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ClusterM","download_url":"https://codeload.github.com/ClusterM/tuyanet/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230445926,"owners_count":18227060,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-28T19:32:35.551Z","updated_at":"2024-12-19T14:07:42.969Z","avatar_url":"https://github.com/ClusterM.png","language":"C#","funding_links":["https://github.com/sponsors/ClusterM","https://www.buymeacoffee.com/cluster","https://boosty.to/cluster"],"categories":[],"sub_categories":[],"readme":"# Tuya.Net\r\n\r\n.NET Standard 2.0 library to interface with Tuya WiFi smart devices over LAN, without using the cloud.\r\n\r\nFull documentation: https://clusterm.github.io/tuyanet/\r\n\r\n## Description\r\n\r\nThis library controls and monitors [Tuya](https://en.tuya.com/) compatible WiFi Smart Devices (Plugs, Switches, Lights, Window Covers, etc.) using the local area network (LAN). [Tuya](https://en.tuya.com/) devices are designed to communicate with the Tuya Cloud but most also expose a local area network API, allowing us to directly control the devices without using the cloud.\r\n\r\n## Installation\r\n\r\nYou can use NuGet:\r\n```\r\nPM\u003e Install-Package TuyaNet\r\n```\r\n\r\n### Dependencies\r\n\r\n* .NET Standard 2.0\r\n* Newtonsoft.Json (\u003e= 13.0.1)\r\n\r\n## How communication with Tuya devices works at all\r\n\r\nEvery Tuya device broadcasts to local network UDP packets with short info about itself. This data is encrypted with AES but the encryption key is the same for every device and it can be easily decrypted. This packet is sent every 5 seconds and contains data with a unique **device ID**, device type ID and protocol version.\r\n\r\nAlso, every device can accept TCP connection and proceed requests. Every request contains command code and JSON string. JSON string is encrypted with AES using **local key**. This key is assigned by the Tuya Cloud server and is unique for each device. So you need to create a Tuya developer account and request this key from the server.\r\n\r\nMost requests must contain base JSON values:\r\n```JSON\r\n{\r\n    \"gwId\": \"DEVICE_ID\",\r\n    \"devId\": \"DEVICE_ID\",\r\n    \"uid\":  \"DEVICE_ID\",\r\n    \"t\": \"CURRENT_TIME\"\r\n}\r\n```\r\nWhere `DEVICE_ID` is a **device ID** and `CURRENT_TIME` is a Unix timestamp as a string.\r\n\r\nEvery response contains command code, JSON and return code (0 = success).\r\n\r\nMost devices are controlled via data points (DPs). Every data point has a number and a value. Usually you can send `DP_QUERY` (0x0A) command and receive ra esponse like\r\n```JSON\r\n{\r\n    \"devId\": \"DEVICE_ID\",\r\n    \"dps\":{\r\n        \"1\": true,\r\n        \"2\": false,\r\n        \"9\": 0,\r\n        \"10\": 0\r\n    }\r\n}\r\n```\r\n\r\n`dps` is a dictionary with data points and current values. This is the response from the \"2 in 1\" smart switch. `1` is a state of switch #1, `2` is a state of switch #2, `9` and `10` are timer values. Please note that DPs numbers are strings. DPs values can be of any type.\r\n\r\nUsually you can change DPs values using `CONTROL` (0x07) command and JSON like:\r\n```JSON\r\n{\r\n    \"gwId\": \"DEVICE_ID\",\r\n    \"devId\": \"DEVICE_ID\",\r\n    \"uid\":  \"DEVICE_ID\",\r\n    \"t\": \"CURRENT_TIME\",\r\n    \"dps\":{\r\n        \"1\": false\r\n    }    \r\n}\r\n```\r\nThis request will turn off switch #1.\r\n\r\nDon't worry, this library will help you to create requests automatically.\r\n\r\n## How to obtain local key\r\n\r\n* Download Smart Life mobile app: [for Android](https://play.google.com/store/apps/details?id=com.tuya.smartlife) or [for iOS](https://apps.apple.com/us/app/smart-life-smart-living/id1115101477). \r\n* Register your device using this app.\r\n* Open [iot.tuya.com](https://iot.tuya.com/), create developer account and log in.\r\n* Click on `Cloud`\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139099858-ad859219-ae39-411d-8b6f-7edd39684c90.png)\r\n\r\n* Click on the `Create Cloud Project` button\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139100737-7d8f5784-9e2f-492e-a867-b8f6765b3397.png)\r\n\r\n* Enter any name for your project, select \"Smart Home\" for industry and development method. You can select any data center but you **must** remember which one you chose.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139101390-2fb4e88f-235c-4872-91a1-3e78ee6217f8.png)\r\n\r\n* Skip Configuration Wizard.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139154750-690cf86a-98ac-4428-8aa8-467ef8b96d32.png)\r\n\r\n* Copy and save your **Access ID** and **Access Secret**.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139103527-0a048527-ddc2-40c3-aa99-29db0d3cb94c.png)\r\n\r\n* Select `Devices`.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139103834-927c6c02-5860-40d6-829d-5a5dfc9091b6.png)\r\n\r\n* Select `Link Tuya App Account`.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139103967-45cf78f0-375b-49db-a111-7c8509abc5c0.png)\r\n\r\n* Click on `Add App Account` and it will display a QR code.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139104100-e9b25366-2feb-489b-9044-322ca1dad9c6.png)\r\n\r\n* Scan the QR code using your mobile phone and Smart Life app by going to the \"Me\" tab and clicking on the QR code button [..] in the upper right hand corner of the app. Your account should appear on the list.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139104842-b93b5285-bf76-4eb2-b01b-8f6aa54fdcd9.png)\r\n\r\n* Now open the `Devices` tab.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139104946-2e4279a5-028f-4f9e-beb0-9cfb5bae5285.png)\r\n\r\n* You should see list of your devices. Copy and save at least one device ID.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139105306-5d37de66-a64a-4d5d-88e4-bf3a43f08f0e.png)\r\n\r\n* Click on the `Service API` tab.\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139105534-0b20a651-b72a-44c3-9531-8165d0be5f3e.png)\r\n\r\n* Click on `Go to Authorize`\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139105727-fcd3f3d0-349a-40ce-a5c3-c534556762ae.png)\r\n\r\n* Add `IoT Core` API (subscribe to it first).\r\n\r\n  ![image](https://user-images.githubusercontent.com/4236181/139105956-573be361-95ae-4a9d-bf5b-2e848b54547f.png)\r\n\r\n* Now you can retrieve **local keys** of your devices using `TuyaApi` class:\r\n\r\n  ```C#\r\n  var api = new TuyaApi(region: TuyaApi.Region.CentralEurope, accessId: ACCESS_ID, apiSecret: API_SECRET);\r\n  var devices = await api.GetAllDevicesInfoAsync(anyDeviceId: DEVICE_ID);\r\n  foreach(var device in devices)\r\n  {\r\n      Console.WriteLine($\"Device: {device.Name}, device ID: {device.Id}, local key: {device.LocalKey}\");\r\n  }\r\n  ```\r\n  `region` - the region of the data center that you have selected on [iot.tuya.com](https://iot.tuya.com/)\r\n  `accessID` and `apiSecret` - `Access ID` and `Access Secret` from [iot.tuya.com](https://iot.tuya.com/)\r\n  `anyDeviceId` - ID of any of your smart devices (to fetch user ID).\r\n\r\n## Network scanner for Tuya devices\r\nYou can use `TuyaScanner` class to catch and decode broadcast UDP packets from devices:\r\n```C#\r\nstatic void Main(string[] args)\r\n{\r\n    var scanner = new TuyaScanner();\r\n    scanner.OnNewDeviceInfoReceived += Scanner_OnNewDeviceInfoReceived;\r\n    Console.WriteLine(\"Scanning local network for Tuya devices, press any key to stop.\");\r\n    scanner.Start();\r\n    Console.ReadKey();\r\n    scanner.Stop();\r\n}\r\n\r\nprivate static void Scanner_OnNewDeviceInfoReceived(object sender, TuyaDeviceScanInfo e)\r\n{\r\n    Console.WriteLine($\"New device found! IP: {e.IP}, ID: {e.GwId}, version: {e.Version}\");\r\n}\r\n```\r\nYou can use it to retrieve the device's **IP address**, **ID**, and **protocol version**. Remember that your computer must be on the same network as your devices to receive broadcasts.\r\n\r\n## How to communicate with devices\r\n\r\nYou should now have:\r\n* Device IP address - from scanner or from your router\r\n* Device local encryption key - retrieved via `TuyaApi`\r\n* Device ID - from scanner or from [iot.tuya.com](https://iot.tuya.com/)\r\n\r\nThere is `TuyaDevice` class, you need create instance of it:\r\n```C#\r\nvar dev = new TuyaDevice(ip: DEVICE_IP, localKey: DEVICE_KEY, deviceId: DEVICE_ID);\r\n```\r\nIt uses protocol version 3.3 by default but you can specify version 3.1 as well:\r\n```C#\r\nTuyaDevice dev = new TuyaDevice(ip: DEVICE_IP, localKey: DEVICE_KEY, deviceId: DEVICE_ID, protocolVersion: TuyaProtocolVersion.V31);\r\n```\r\n\r\nNow you can encode requests:\r\n```C#\r\nbyte[] request = device.EncodeRequest(TuyaCommand.DP_QUERY, \"{\\\"gwId\\\":\\\"DEVICE_ID\\\",\\\"devId\\\":\\\"DEVICE_ID\\\",\\\"uid\\\":\\\"DEVICE_ID\\\",\\\"t\\\":\\\"CURRENT_TIME\\\"}\");\r\n```\r\nSend it:\r\n```C#\r\nbyte[] encryptedResponse = await device.SendAsync(request);\r\n```\r\nAnd decode response:\r\n```C#\r\nTuyaLocalResponse response = device.DecodeResponse(encryptedResponse);\r\nConsole.WriteLine($\"Response JSON: {response.JSON}\");\r\n```\r\n\r\nHow to set DPs:\r\n```C#\r\nbyte[] request = device.EncodeRequest(TuyaCommand.CONTROL, \"{\\\"gwId\\\":\\\"DEVICE_ID\\\",\\\"devId\\\":\\\"DEVICE_ID\\\",\\\"uid\\\":\\\"DEVICE_ID\\\",\\\"t\\\":\\\"CURRENT_TIME\\\"},\\\"dps\\\":{\\\"1\\\":false}}\");\r\nbyte[] encryptedResponse = await device.SendAsync(request);\r\nTuyaLocalResponse response = device.DecodeResponse(encryptedResponse);\r\nConsole.WriteLine($\"Response JSON: {response.JSON}\");\r\n```\r\n\r\nToo complicated, isn't it? There is more simple way. You can use `FillJson()` method to fill standard fields automatically:\r\n```C#\r\nbyte[] request = device.EncodeRequest(TuyaCommand.CONTROL, device.FillJson(\"{\\\"dps\\\":{\\\"1\\\":false}}\"));\r\nbyte[] encryptedResponse = await device.SendAsync(request);\r\nTuyaLocalResponse response = device.DecodeResponse(encryptedResponse);\r\nConsole.WriteLine($\"Response JSON: {response.JSON}\");\r\n```\r\n\r\nAlso, there is `SendAsync()` overload that accepts command ID with JSON, encodes it, and returns decoded data:\r\n```C#\r\nTuyaLocalResponse response = await device.SendAsync(TuyaCommand.CONTROL, device.FillJson(\"{\\\"dps\\\":{\\\"1\\\":false}}\"));\r\n```\r\n\r\nFinally, there are `GetDps()` and `SetDps()` methods:\r\n```C#\r\nDictionary\u003cint, object\u003e dps = await device.GetDpsAsync();\r\n// Change multiple values at once\r\nDictionary\u003cint, object\u003e newDps = await device.SetDpsAsync(new Dictionary\u003cint, object\u003e { { 1, false }, { 2, true } });\r\n// Change single value\r\nnewDps = await device.SetDpAsync(1, true);\r\n```\r\n\r\nSome devices may have dynamically changed values, such as timers. They need to be updated before reading:\r\n```C#\r\nnewDps = await device.UpdateDpsAsync(new int[] { 9, 10 });\r\n```\r\nOr just:\r\n```C#\r\nnewDps = await device.UpdateDpsAsync(9, 10);\r\n```\r\n### IR Remote Control\r\n\r\nSince version 1.0.3 you can control infrared remote controls. There is TuyaIRControl class for it. You can learn button from real remote control using `GetButtonCodeAsync` method (it returns Base64-encoded pulses sequences) and simulate button press using `SendButtonCodeAsync` method.\r\nExample:\r\n```C#\r\nstring codeBase64 = await ir.GetButtonCodeAsync(10000, retries: 1);\r\nConsole.WriteLine($\"Button code learned: {codeBase64}\");\r\nwhile (true)\r\n{\r\n    await ir.SendButtonCodeAsync(codeBase64);\r\n    Console.WriteLine(\"Button press simulated\");\r\n    await Task.Delay(1000);\r\n}\r\n```\r\n\r\n### Automatic local key refresh\r\n\r\nYou can create `TuyaDevice` object without local key but with API key/secret like this:\r\n```C#\r\nvar dev = new TuyaDevice(ip: DEVICE_IP, region: API_REGION, accessId: ACCESS_ID, apiSecret: API_SECRET, deviceId: DEVICE_ID);\r\n```\r\nThen obtain local key:\r\n```C#\r\nawait dev.RefreshLocalKeyAsync()\r\n```\r\n\r\n## Using the Cloud API\r\nYou can use official [Tuya Cloud API](https://developer.tuya.com/en/docs/cloud/). \r\n\r\nExample of [device specifications](https://developer.tuya.com/en/docs/cloud/device-control?id=K95zu01ksols7#title-27-Get%20the%20specifications%20and%20properties%20of%20the%20device%2C%20including%20the%20instruction%20set%20and%20status%20set) request:\r\n```C#\r\nvar api = new TuyaApi(region: TuyaApi.Region.CentralEurope, accessId: ACCESS_ID, apiSecret: API_SECRET);\r\nvar devspec = await api.RequestAsync(TuyaApi.Method.GET, $\"/v1.1/devices/{DEVICE_ID}/specifications\");\r\n```\r\n\r\n`devspec` will contain JSON with all device data points with descriptions and possible values.\r\n\r\n## Credits\r\n  * TinyTuya https://github.com/jasonacox/tinytuya by Jason Cox\r\n    For Python version of the library inspired me\r\n  * Tuya Smart Plug API https://github.com/Marcus-L/m4rcus.TuyaCore by Marcus Lum\r\n    For some ideas and algorithms\r\n  * TuyAPI https://github.com/codetheweb/tuyapi by codetheweb and blackrozes\r\n    For protocol reverse engineering, additional protocol reverse engineering from jepsonrob and clach04\r\n\r\n## Contacts\r\n* My site (Russian): https://clusterrr.com\r\n* Email: clusterrr@clusterrr.com\r\n* Telegram: https://t.me/Cluster_M\r\n* Donations: https://www.donationalerts.com/r/clustermeerkat\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclusterm%2Ftuyanet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclusterm%2Ftuyanet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclusterm%2Ftuyanet/lists"}