HarmonyTuya 0.1.0-alpha.3
dotnet add package HarmonyTuya --version 0.1.0-alpha.3
NuGet\Install-Package HarmonyTuya -Version 0.1.0-alpha.3
<PackageReference Include="HarmonyTuya" Version="0.1.0-alpha.3" />
<PackageVersion Include="HarmonyTuya" Version="0.1.0-alpha.3" />
<PackageReference Include="HarmonyTuya" />
paket add HarmonyTuya --version 0.1.0-alpha.3
#r "nuget: HarmonyTuya, 0.1.0-alpha.3"
#:package HarmonyTuya@0.1.0-alpha.3
#addin nuget:?package=HarmonyTuya&version=0.1.0-alpha.3&prerelease
#tool nuget:?package=HarmonyTuya&version=0.1.0-alpha.3&prerelease
HarmonyTuya
Lightweight, extensible .NET client for the Tuya Cloud API.
Install
HarmonyTuya is published as a NuGet package.
- Package ID: HarmonyTuya
- Targets: net8.0, net6.0
Example install command:
dotnet add package HarmonyTuya
Quick start
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp", // optional, per-tenant
Region = "EU", // EU, US, CN, IN. Or set BaseUrl directly.
UseRegionPathPrefix = true // set if your tenant requires region in path
};
using ITuyaClient client = new TuyaClient(options);
var state = await client.GetDeviceStateAsync("vdevo16*********15");
Console.WriteLine($"Frozen: {state.IsFrozen} (state={state.State})");
The client authenticates automatically (fetches and caches an access token) and signs each request.
Note: The client uses a default Tuya V2 HMAC-SHA256 request signer (TuyaV2RequestSigner
). If you need custom signing rules, inject your own ITuyaRequestSigner
. Keep your AccessSecret
safe—use environment variables or a secret store.
Supported frameworks
- .NET 8.0
- .NET 6.0
Changelog
See CHANGELOG.md
for release notes and version history.
Running tests
This repo includes a small xUnit test suite that validates authentication flow and the serialization/deserialization of selected endpoint models.
- Prerequisites: .NET SDK 8.0 or newer installed locally.
- Run tests from the repository root with your preferred tooling (for example, via your IDE's Test Explorer or
dotnet test
).
Notes:
- The library targets .NET 6.0 and .NET 8.0. The test project currently targets .NET 8.0.
- In CI, tests can run as part of your build pipeline before packaging/publishing.
Authentication
HarmonyTuya authenticates automatically using Tuya OAuth "simple mode" tokens.
- On the first API call, the client fetches an access token from
v1.0/token?grant_type=1
(request is signed with your AccessKey/AccessSecret). - The token is cached in-memory and refreshed just before expiry. You don't need to call anything manually.
- Every request still uses HMAC signing and also includes the
access_token
header as required by Tuya Cloud.
Configuration notes:
- Set
AccessKey
andAccessSecret
inTuyaClientOptions
. - Optional:
ProjectCode
if your tenant requires it;Region
orBaseUrl
for your Tuya environment. - Optional:
UseRegionPathPrefix = true
to include a region segment likeEU/
in the path when required by your setup.
Security: never commit real secrets. Use environment variables, Secret Manager, or your CI/CD secret store.
Configuration (TuyaClientOptions)
These options control how the client connects and authenticates:
- AccessKey: Tuya Cloud API Client ID
- AccessSecret: Tuya Cloud API Client Secret
- Region: Region code like EU, US, CN, IN (used for base URL mapping)
- BaseUrl: Optional. Override full base URL (skips Region mapping)
- HttpClient: Optional. Provide a custom HttpClient you manage
- UseRegionPathPrefix: When true, includes the region (e.g., EU/) as the first path segment
- ProjectCode: Optional. Required by some Tuya tenants; sent as project_code header
Client and interface split by category
To keep things maintainable, the monolithic client and interface were split into partials organized by domain. Public API remains the same; only the file layout changed.
Client implementation (
HarmonyTuya.Tuya
):TuyaClient.cs
— core shell (constructor, options, BaseUrl, Dispose)TuyaClient.DeviceState.cs
— device stateTuyaClient.DeviceLogs.cs
— device report logs (v2.0/v2.1)TuyaClient.DeviceCore.cs
— device CRUD, action, rename/transfer/freeze/resetTuyaClient.DeviceShadow.cs
— shadow and desired propertiesTuyaClient.DeviceSpecs.cs
— model, specification, functions, categories, latest statusTuyaClient.FirmwareAndLogs.cs
— firmware progress/info and event logsTuyaClient.Groups.cs
— groups management and propertiesTuyaClient.Spaces.cs
— spaces and project devicesTuyaClient.ScenesAndActivation.cs
— scenes (linkage rules), QR activation, signal
Interface (
HarmonyTuya.Tuya.Abstractions
):ITuyaClient.cs
— partial interface shellITuyaClient.DeviceState.cs
,ITuyaClient.DeviceLogs.cs
ITuyaClient.DeviceCore.cs
,ITuyaClient.DeviceShadow.cs
,ITuyaClient.DeviceSpecs.cs
ITuyaClient.Firmware.cs
,ITuyaClient.Groups.cs
,ITuyaClient.Spaces.cs
ITuyaClient.ScenesAndActivation.cs
Tip: Navigate by category to find related methods fast. Examples below continue to work unchanged.
Error handling
- Non-success responses produce HttpRequestException, or InvalidOperationException when the API response is unexpectedly empty.
- Methods are async and respect CancellationToken where available.
- Authentication and retry: the client retries once automatically on 401 Unauthorized (likely expired access token). It invalidates the token, refreshes it, and re-sends the request. Other transient retry policies are not enabled by default.
Report logs example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
// Note: TuyaClient uses a built-in TuyaV2RequestSigner by default which computes HMAC-SHA256 signatures.
// If your project uses different signing rules, inject a custom ITuyaRequestSigner into the TuyaClient constructor.
// SECURITY: Keep AccessSecret safe. Do NOT commit real secrets to source control. Use environment variables or a secret store in production.
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var hourAgo = now - 60_000 * 60;
var logs = await client.GetDeviceReportLogsAsync(
deviceId: "vdevo16*********15",
codes: "switch_led_1",
startTime: hourAgo,
endTime: now,
size: 20,
lastRowKey: null
);
Console.WriteLine($"DeviceId: {logs.DeviceId}, Total: {logs.Total}, HasMore: {logs.HasMore}, LastRowKey: {logs.LastRowKey}");
foreach (var item in logs.Logs)
{
Console.WriteLine($"{item.EventTime}: {item.Code} -> {item.Value}");
}
Report logs v2.1 example (recommended)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var hourAgo = now - 60_000 * 60;
// First page
var v21 = await client.GetDeviceReportLogsV21Async(
deviceId: "vdevo16*********15",
codes: "switch_led_1",
startTime: hourAgo,
endTime: now,
size: 20
);
Console.WriteLine($"DeviceId: {v21.DeviceId}, Total: {v21.Total}, HasMore: {v21.HasMore}, LastRowKey: {v21.LastRowKey}");
foreach (var item in v21.Logs)
{
Console.WriteLine($"{item.EventTime}: {item.Code} -> {item.Value}");
}
// Next page (when HasMore is true):
// var next = await client.GetDeviceReportLogsV21Async(
// deviceId: "vdevo16*********15",
// codes: "switch_led_1",
// startTime: hourAgo,
// endTime: now,
// size: 20,
// lastRowKey: v21.LastRowKey);
Get group details example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var group = await client.GetGroupAsync("1285***7");
Console.WriteLine($"Group {group.Id}: {group.Name} (type={group.Type}, pv={group.Pv})");
Add devices to group example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var request = new AddDevicesToGroupRequest
{
SpaceId = "2512****",
DeviceIds = "vdevo163815*****,529739391******"
};
var ok = await client.AddDevicesToGroupAsync("12856***", request);
Console.WriteLine($"Add devices result: {ok}");
Query devices in a group
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var devices = await client.GetGroupDevicesAsync("12856***", pageSize: 1, pageNo: 1);
if (devices != null)
{
Console.WriteLine($"Total: {devices.Count}, Page: {devices.PageNumber}/{devices.PageSize}");
foreach (var d in devices.DataList)
{
Console.WriteLine($"Dev: {d.DevId} Status: {d.Status}");
}
}
Delete devices from group example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var delReq = new DeleteDevicesFromGroupRequest
{
DeviceIds = "vdevo16381*****,5529739***"
};
var removed = await client.DeleteDevicesFromGroupAsync("12856***", delReq);
Console.WriteLine($"Delete devices result: {removed}");
Get groups by device example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var groups = await client.GetGroupsByDeviceAsync("vdevo16*********15");
foreach (var g in groups)
{
Console.WriteLine($"Group {g.Id}: {g.Name} (product={g.ProductId})");
}
Get group properties example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var props = await client.GetGroupPropertiesAsync("12856***");
foreach (var p in props.Properties)
{
Console.WriteLine($"{p.Code} (group={p.GId}): {p.Value}");
}
Delete group example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var deleted = await client.DeleteGroupAsync("1285*****");
Console.WriteLine($"Delete group result: {deleted}");
Rename group example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var renamed = await client.RenameGroupAsync("123***2", "gro*****ame");
Console.WriteLine($"Rename group result: {renamed}");
Create group example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var createReq = new CreateGroupRequest
{
SpaceId = "25129******",
Name = "Test Group**",
ProductId = "dsadhsjkh2***",
DeviceIds = "dshajhdkhaj***,hsdkajhkk***"
};
var created = await client.CreateGroupAsync(createReq);
Console.WriteLine($"Created group {created.Id} (name={created.Name}, pv={created.Pv})");
List groups in a space example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var result = await client.GetGroupsInSpaceAsync(spaceId: "158170***", pageSize: 10, pageNo: 1);
if (result != null)
{
Console.WriteLine($"Total: {result.Count}, Page: {result.PageNumber}, Size: {result.PageSize}");
foreach (var g in result.DataList)
{
Console.WriteLine($"Group {g.Id}: {g.Name} (pv={g.Pv}, product={g.ProductId})");
}
}
Send device action example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var action = new DeviceActionRequest
{
Code = "action",
InputParams = "{\"color\":\"red\",\"name\":\"text\"}"
};
var ok = await client.SendDeviceActionAsync("2132131*****", action);
Console.WriteLine($"Send device action result: {ok}");
Get device shadow properties example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
// Without codes filter
var allProps = await client.GetDeviceShadowPropertiesAsync("372817193*****");
if (allProps != null)
{
foreach (var p in allProps.Properties)
{
Console.WriteLine($"{p.Code} (dp:{p.DpId}) at {p.Time} = {p.Value}");
}
}
// With codes filter
var filtered = await client.GetDeviceShadowPropertiesAsync("372817193*****", codes: "switch_led,brightness");
if (filtered != null)
{
foreach (var p in filtered.Properties)
{
Console.WriteLine($"Filtered {p.Code}: {p.Value}");
}
}
Set device shadow properties example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var setReq = new DeviceShadowPropertiesSetRequest
{
Properties =
{
new DeviceShadowSetPropertyItem { Code = "switch_led", CustomName = "switch_led1" }
}
};
var updated = await client.SetDeviceShadowPropertiesAsync("372817193*****", setReq);
Console.WriteLine($"Set device shadow properties: {updated}");
Get desired device properties example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var desired = await client.GetDeviceDesiredPropertiesAsync("372817193*****");
if (desired != null)
{
foreach (var p in desired.Properties)
{
Console.WriteLine($"Desired {p.Code} (dp:{p.DpId}) value={p.Value} status={p.Status} exp={p.ExpireTime}");
}
}
Set desired device properties example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var desiredSet = new DeviceDesiredPropertiesSetRequest
{
Properties = "{\"switch_1\":true}",
Duration = 1000
};
var okDesired = await client.SetDeviceDesiredPropertiesAsync("vdevo1638******", desiredSet);
Console.WriteLine($"Set desired device properties result: {okDesired}");
Get device model example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var model = await client.GetDeviceModelAsync("372817193*****");
Console.WriteLine(model?.Model);
Get devices in batch example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var list = await client.GetDevicesBatchAsync("vdevo165942257398***,vdevo165abcd98***");
foreach (var d in list)
{
Console.WriteLine($"{d.Id} Online={d.IsOnline} Name={d.Name} Product={d.ProductName}");
}
Get firmware update progress example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var progress = await client.GetFirmwareUpdateProgressAsync("aab21***", channel: 0);
if (progress != null)
{
Console.WriteLine($"Progress: {progress.Progress}%, Status: {progress.UpgradeStatus}");
}
Start firmware update example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var started = await client.StartFirmwareUpdateAsync("vdev1243432***", channel: 0);
Console.WriteLine($"Start firmware update: {started}");
Get firmware update info example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var info = await client.GetFirmwareUpdateInfoAsync("vdev1243432***");
Console.WriteLine($"Status={info?.UpgradeStatus}, Current={info?.CurrentVersion}, Target={info?.Version}, Desc={info?.Desc}");
Get single device details example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var device = await client.GetDeviceAsync("vdevo165abcd98***");
Console.WriteLine($"{device?.Id} Online={device?.IsOnline} Name={device?.Name} Product={device?.ProductName}");
Get device event logs example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var dayAgo = now - 24L*60*60*1000;
var logs = await client.GetDeviceEventLogsAsync(
deviceId: "vdevo167115422036***",
type: "1,2,3,4,5,6,7,8,9,10",
startTime: dayAgo,
endTime: now,
queryType: 1,
size: 20,
codes: "switch_led_1");
if (logs != null)
{
Console.WriteLine($"HasNext: {logs.HasNext}, CurrentRowKey: {logs.CurrentRowKey}");
foreach (var ev in logs.Logs)
{
Console.WriteLine($"{ev.EventTime} {ev.Code} from {ev.EventFrom} id={ev.EventId} status={ev.Status} value={ev.Value}");
}
}
Modify device attribute example (rename)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var renameReq = new ModifyDeviceAttributeRequest { Type = 1, Data = "Main Bedroom Light" };
var renamed = await client.ModifyDeviceAttributeAsync("6c19224514c02f7490****", renameReq);
Console.WriteLine($"Rename device result: {renamed}");
Freeze or unfreeze device example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
// Freeze
var freezeReq = new DeviceFreezeRequest { State = 1 };
var froze = await client.SetDeviceFreezeStateAsync("6c19224514c02f7490****", freezeReq);
Console.WriteLine($"Freeze device result: {froze}");
// Unfreeze
var unfreezeReq = new DeviceFreezeRequest { State = 0 };
var unfroze = await client.SetDeviceFreezeStateAsync("6c19224514c02f7490****", unfreezeReq);
Console.WriteLine($"Unfreeze device result: {unfroze}");
Transfer device to another space example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var transferReq = new DeviceTransferRequest { SpaceId = "12345***" };
var transferred = await client.TransferDeviceAsync("vdevo16*********15", transferReq);
Console.WriteLine($"Transfer device result: {transferred}");
Reset (factory default) device example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var reset = await client.ResetDeviceAsync("6c19224514c02f7490****");
Console.WriteLine($"Reset device result: {reset}");
Send group properties example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var propsReq = new SendGroupPropertiesRequest
{
GroupId = "89218****",
Properties = "{ \"switch1\": true }"
};
var sent = await client.SendGroupPropertiesAsync(propsReq);
Console.WriteLine($"Send group properties result: {sent}");
Delete device example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var deleted = await client.DeleteDeviceAsync("vdevo16*********15");
Console.WriteLine($"Delete device result: {deleted}");
Query devices in spaces (paged) example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
// First page
var devices = await client.GetSpaceDevicesAsync(spaceIds: "146323341***", pageSize: 20, isRecursion: true);
foreach (var d in devices)
{
Console.WriteLine($"{d.Id} Online={d.IsOnline} Name={d.Name} Product={d.ProductName} Cat={d.Category}");
}
// For the next page, pass lastId as the last device id you received previously
// var nextPage = await client.GetSpaceDevicesAsync("146323341***", pageSize: 20, isRecursion: true, lastId: devices.LastOrDefault()?.Id);
Query project devices (paged) example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var page = await client.GetProjectDevicesAsync(pageSize: 20, productIds: "h9sabcugftb***");
foreach (var d in page)
{
Console.WriteLine($"{d.Id} Online={d.IsOnline} Name={d.Name} Product={d.ProductName} Cat={d.Category}");
}
// Next page example:
// var next = await client.GetProjectDevicesAsync(20, productIds: "h9sabcugftb***", lastId: page.LastOrDefault()?.Id);
Activate device with QR code example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var req = new QrCodeActivateRequest
{
QrCode = "https://m.smart321.com/********",
SpaceId = 1500_1234,
TimeZoneId = "Asia/Shanghai"
};
var result = await client.ActivateDeviceWithQrCodeAsync(req);
Console.WriteLine($"Token={result?.Token} DeviceId={result?.DeviceId}");
Query QR activation status example
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var status = await client.GetQrCodeActivationStatusAsync(token: "94IB****");
if (status != null)
{
foreach (var s in status.SuccessDevices)
{
Console.WriteLine($"PAIRED: {s.Id} {s.Name} Online={s.Online} Product={s.ProductId}");
}
foreach (var e in status.ErrorDevices)
{
Console.WriteLine($"FAILED: {e.Id} {e.Name} Code={e.ErrorCode} Msg={e.ErrorMsg}");
}
}
Issue signal detection to a device
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var issueReq = new SignalDetectionIssueRequest
{
DeviceId = "vdevo163815529739***",
DeviceType = "WiFi" // or "zigbee"
};
var issued = await client.IssueSignalDetectionAsync(issueReq);
Console.WriteLine($"Signal detection issued: {issued}");
Query device signal (Wi-Fi/Zigbee)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
// Wi-Fi (last 10 minutes)
var wifi = await client.GetDeviceSignalAsync("vdevo163815529739***", deviceType: "WiFi");
Console.WriteLine($"Wi-Fi RSSI={wifi?.Signal} level={wifi?.SignalLevel} type={wifi?.IndicatorType} at {wifi?.EventTime}");
// Zigbee (last 2 minutes)
var zigbee = await client.GetDeviceSignalAsync("vdevo163815529739***", deviceType: "zigbee");
Console.WriteLine($"Zigbee {zigbee?.IndicatorType}={zigbee?.Signal} level={zigbee?.SignalLevel} at {zigbee?.EventTime}");
Delete a space (and subspaces)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var removed = await client.DeleteSpaceAsync("70735***");
Console.WriteLine($"Delete space result: {removed}");
Query resources in a space (paged)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
// First page
var res = await client.GetSpaceResourcesAsync(spaceId: "15000***", onlySub: true, lastRowKey: 0, pageSize: 100);
if (res != null)
{
foreach (var r in res.Data)
{
Console.WriteLine($"ResId={r.ResId} Type={r.ResType}");
}
Console.WriteLine($"Next cursor: {res.LastRowKey} PageSize: {res.PageSize}");
}
// Next page example:
// var next = await client.GetSpaceResourcesAsync("15000***", onlySub: true, lastRowKey: res?.LastRowKey, pageSize: 100);
Create a space (optional parent)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var createReq = new CreateSpaceRequest
{
Name = "Test Space",
ParentId = 150000002,
Description = "Description of the space"
};
var newSpaceId = await client.CreateSpaceAsync(createReq);
Console.WriteLine($"Created space id: {newSpaceId}");
Get space info by ID
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var info = await client.GetSpaceAsync("1500****");
Console.WriteLine($"Space {info?.Id}: {info?.Name} (parent={info?.ParentId}, root={info?.RootId})");
Query child spaces (root or by space)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
// Root-level spaces
var root = await client.GetSpaceChildrenAsync(pageSize: 100);
Console.WriteLine($"Root next cursor: {root?.LastRowKey} size={root?.PageSize}");
foreach (var id in root?.Data ?? [])
{
Console.WriteLine($"Space ID: {id}");
}
// Children under a specific space (direct children only)
var childs = await client.GetSpaceChildrenAsync(spaceId: "15000002", onlySub: true, pageSize: 100);
Console.WriteLine($"Next cursor: {childs?.LastRowKey}");
foreach (var id in childs?.Data ?? [])
{
Console.WriteLine($"Child Space ID: {id}");
}
Update a space (name/description)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var upd = new UpdateSpaceRequest
{
Name = "test space",
Description = "This is a test space."
};
var ok = await client.UpdateSpaceAsync("7073****", upd);
Console.WriteLine($"Update space result: {ok}");
Check parent-child relation between spaces
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var isChild = await client.IsChildSpaceAsync(parentId: "15000001", childId: "15000002");
Console.WriteLine($"Is child: {isChild}");
Get linkage (scene) rule details
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var rule = await client.GetSceneRuleAsync("***");
Console.WriteLine($"Rule {rule?.Id}: {rule?.Name} type={rule?.Type} status={rule?.Status} mode={rule?.RunningMode}");
foreach (var c in rule?.Conditions ?? [])
{
Console.WriteLine($"COND[{c.Code}] {c.EntityType} on {c.EntityId} {c.Expr?.Comparator} {c.Expr?.StatusCode} = {c.Expr?.StatusValue}");
}
foreach (var a in rule?.Actions ?? [])
{
Console.WriteLine($"ACT {a.ActionExecutor} on {a.EntityId} code={a.ExecutorProperty?.FunctionCode} value={a.ExecutorProperty?.FunctionValue} delay={a.ExecutorProperty?.DelaySeconds}");
}
List linkage rules in a space (paged)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var list = await client.GetSceneRulesAsync(spaceId: "150***", pageSize: 2, pageNo: 1, type: "automation");
Console.WriteLine($"Total: {list?.Total} HasMore: {list?.HasMore}");
foreach (var r in list?.List ?? [])
{
Console.WriteLine($"Rule {r.Id}: {r.Name} type={r.Type} status={r.Status} mode={r.RunningMode}");
}
Delete multiple linkage rules
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var deleted = await client.DeleteSceneRulesAsync(spaceId: "53IYS**", ids: "abc123,def456");
Console.WriteLine($"Delete rules result: {deleted}");
Trigger a tap-to-run scene by rule ID
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "etjgr3pru3pt4m55m8vn",
AccessSecret = "<REDACTED_CLIENT_SECRET>",
ProjectCode = "p17579564043307sgqxp",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var triggered = await client.TriggerSceneRuleAsync(ruleId: "***");
Console.WriteLine($"Triggered: {triggered}");
Modify a linkage rule
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var update = new UpdateSceneRuleRequest
{
SpaceId = "150***",
Name = "Test Scene",
DecisionExpr = "or",
EffectiveTime = new SceneRuleEffectiveTime
{
Start = "00:00",
End = "23:00",
Loops = "1111111",
TimeZoneId = "Asia/Shanghai"
},
Conditions = new()
{
new SceneRuleCondition
{
Code = 1,
EntityId = "****",
EntityType = "device_report",
Expr = new SceneRuleConditionExpr
{
StatusCode = "switch_1",
Comparator = "==",
StatusValue = true
}
}
},
Actions = new()
{
new SceneRuleAction
{
EntityId = "****",
ActionExecutor = "device_issue",
ExecutorProperty = new SceneRuleExecutorProperty
{
FunctionCode = "switch_1",
FunctionValue = true
}
}
}
};
var ok = await client.UpdateSceneRuleAsync(ruleId: "***", update);
Console.WriteLine($"Updated: {ok}");
Enable or disable automation rules
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
// Enable two rules by IDs, optionally scoped to a space
var enabled = await client.SetSceneRuleStateAsync(ids: "abc1,abc2", isEnable: true, spaceId: "53IYS**");
Console.WriteLine($"Enabled: {enabled}");
// Disable the same rules
var disabled = await client.SetSceneRuleStateAsync(ids: "abc1,abc2", isEnable: false, spaceId: "53IYS**");
Console.WriteLine($"Disabled: {disabled}");
Create a linkage rule
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var create = new CreateSceneRuleRequest
{
SpaceId = "150***",
Name = "Test Scene",
Type = "automation", // or "scene"
DecisionExpr = "or",
EffectiveTime = new SceneRuleEffectiveTime
{
Start = "00:00",
End = "23:00",
Loops = "1111111",
TimeZoneId = "Asia/Shanghai"
},
Conditions = new()
{
new SceneRuleCondition
{
Code = 1,
EntityId = "****",
EntityType = "device_report",
Expr = new SceneRuleConditionExpr
{
StatusCode = "switch_1",
Comparator = "==",
StatusValue = true
}
}
},
Actions = new()
{
new SceneRuleAction
{
EntityId = "****",
ActionExecutor = "device_issue",
ExecutorProperty = new SceneRuleExecutorProperty
{
FunctionCode = "switch_1",
FunctionValue = true
}
}
}
};
var newRuleId = await client.CreateSceneRuleAsync(create);
Console.WriteLine($"Created rule id: {newRuleId}");
Get device specification (functions and status)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var spec = await client.GetDeviceSpecificationAsync(deviceId: "xxxid");
Console.WriteLine($"Category: {spec?.Category}");
foreach (var f in spec?.Functions ?? Array.Empty<DeviceFunctionItem>())
{
Console.WriteLine($"Function: {f.Code} ({f.Type}) values={f.Values}");
}
foreach (var s in spec?.Status ?? Array.Empty<DeviceStatusItem>())
{
Console.WriteLine($"Status: {s.Code} ({s.Type}) values={s.Values}");
}
Get device functions (instruction set)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var funcs = await client.GetDeviceFunctionsAsync(deviceId: "0123xxxxxx");
Console.WriteLine($"Category: {funcs?.Category}");
foreach (var f in funcs?.Functions ?? Array.Empty<DeviceFunctionItem>())
{
Console.WriteLine($"Function: {f.Code} ({f.Type}) desc={f.Desc} values={f.Values}");
}
Get category functions (instruction set)
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var catFuncs = await client.GetCategoryFunctionsAsync(category: "kg");
Console.WriteLine($"Category: {catFuncs?.Category}");
foreach (var f in catFuncs?.Functions ?? Array.Empty<DeviceFunctionItem>())
{
Console.WriteLine($"Function: {f.Code} ({f.Type}) desc={f.Desc} values={f.Values}");
}
Send device commands
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
using HarmonyTuya.Tuya.Models;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var cmd = new DeviceCommandsRequest
{
Commands = new()
{
new DeviceCommandItem { Code = "switch_led", Value = true }
}
};
var ok = await client.SendDeviceCommandsAsync(deviceId: "xxxid", cmd);
Console.WriteLine($"Command accepted: {ok}");
List device categories
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var categories = await client.GetDeviceCategoriesAsync();
foreach (var c in categories)
{
Console.WriteLine($"{c.Code}: {c.Name}");
}
Get latest device status
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var latest = await client.GetDeviceLatestStatusAsync(deviceId: "xxxxId");
foreach (var s in latest)
{
Console.WriteLine($"{s.Code}: {s.Value}");
}
Get latest status for multiple devices
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var batch = await client.GetDevicesLatestStatusAsync(deviceIds: "xxxid1,xxxxid2");
foreach (var item in batch)
{
Console.WriteLine($"Device {item.Id}:");
foreach (var s in item.Status)
{
Console.WriteLine($" {s.Code}: {s.Value}");
}
}
Get category status set
using HarmonyTuya.Tuya;
using HarmonyTuya.Tuya.Abstractions;
var options = new TuyaClientOptions
{
AccessKey = "<KEY>",
AccessSecret = "<SECRET>",
ProjectCode = "<PROJECT_CODE>",
Region = "EU",
UseRegionPathPrefix = true
};
using ITuyaClient client = new TuyaClient(options);
var catStatus = await client.GetCategoryStatusAsync("kg");
Console.WriteLine($"Category: {catStatus?.Category}");
foreach (var s in catStatus?.Statuses ?? new List<DeviceStatusItem>())
{
Console.WriteLine($"Status: {s.Code} ({s.Type}) values={s.Values}");
}
Roadmap
- Token auto-refresh (done) and add one-time retry on token-expired responses
- Concurrency guard for token refresh under parallel load
- More device/project/group endpoints as needed by users
- Error handling improvements and domain-specific exceptions
- Optional transient retry strategy (e.g., Polly)
Publishing to NuGet (CI/CD)
This repo includes a GitHub Actions workflow that builds, packs, and publishes the package to nuget.org when you push a tag.
- File:
.github/workflows/publish-nuget.yml
- Trigger: pushing a tag like
v1.2.3
orv1.2.3-beta.1
- Version: derived from the tag name (the leading
v
is stripped)
Before first run:
- Create a NuGet API key in your nuget.org account
- Add it to the repository secrets as
NUGET_API_KEY
Release:
- Create and push a tag, e.g.
v0.1.0-alpha.2
- The workflow will pack the project for net8.0 and net6.0 and publish to nuget.org (skipping duplicates)
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
0.1.0-alpha.3 | 243 | 9/17/2025 |
0.1.0-alpha.2 | 252 | 9/16/2025 |