Smdn.TPSmartHomeDevices.Kasa 2.0.0-preview3

Prefix Reserved
This is a prerelease version of Smdn.TPSmartHomeDevices.Kasa.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Smdn.TPSmartHomeDevices.Kasa --version 2.0.0-preview3                
NuGet\Install-Package Smdn.TPSmartHomeDevices.Kasa -Version 2.0.0-preview3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Smdn.TPSmartHomeDevices.Kasa" Version="2.0.0-preview3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Smdn.TPSmartHomeDevices.Kasa --version 2.0.0-preview3                
#r "nuget: Smdn.TPSmartHomeDevices.Kasa, 2.0.0-preview3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Smdn.TPSmartHomeDevices.Kasa as a Cake Addin
#addin nuget:?package=Smdn.TPSmartHomeDevices.Kasa&version=2.0.0-preview3&prerelease

// Install Smdn.TPSmartHomeDevices.Kasa as a Cake Tool
#tool nuget:?package=Smdn.TPSmartHomeDevices.Kasa&version=2.0.0-preview3&prerelease                

Smdn.TPSmartHomeDevices.Kasa 2.0.0-preview3

Smdn.TPSmartHomeDevices.Kasa is a library that provides APIs for operating Kasa, the TP-Link smart home devices. This library performs operations by communicating directly with Kasa devices in the same network.

This library also provides following features:

  • Automatic connection management, including reconnection.
  • Built-in/customizable error handling for typical errors and retries (like device busy, request timeout).
  • Using MAC address and following IP address change in DHCP networks (requires Smdn.TPSmartHomeDevices.MacAddressEndPoint).
  • async operation and cancellation.

Getting started

First, add package Smdn.TPSmartHomeDevices.Kasa to the project file.

dotnet add package Smdn.TPSmartHomeDevices.Kasa

To operate the Kasa device, write code like the following. Replace the Kasa device's IP address with yours.

using Smdn.TPSmartHomeDevices.Kasa;

// Creates device controller for Kasa KL130 multicolor light bulb.
using var bulb = new KL130(
  "192.0.2.255" // IP address currently associated to KL130
);

// Turns on the bulb, and set the color temperature and brightness.
await bulb.SetColorTemperatureAsync(colorTemperature: 5500, brightness: 80);

// Turns off the bulb
await bulb.TurnOffAsync();

More examples can be found on the GitHub repository, including examples of using other Kasa devices and their functionalities, and examples of using library features.

Contributing

This project welcomes contributions, feedbacks and suggestions. You can contribute to this project by submitting Issues or Pull Requests on the GitHub repository.

Notice

License

This project is licensed under the GNU GPL version 3 or later.

This project includes source code licensed under MIT or GPLv3 as described below and produces artifacts licensed in accordance therewith.

Smdn.TPSmartHomeDevices.Tapo (GPLv3)

The some source files in the directory under the src/Smdn.TPSmartHomeDevices.Tapo/ include codes that has been ported from codes which licensed under the GPLv3, and are licensed under the GNU GPL version 3 or later.

Therefore, artifacts from this directory, including NuGet package Smdn.TPSmartHomeDevices.Tapo, are released under the GNU GPL version 3 or later.

For the license of individual files, refer to SPDX-License-Identifier at the top of each file header.

Smdn.TPSmartHomeDevices.Kasa, Smdn.TPSmartHomeDevices.MacAddressEndPoint, Smdn.TPSmartHomeDevices.Primitives (MIT)

The source files and generated artifacts from the directory under the src/, excluding Smdn.TPSmartHomeDevices.Tapo, are licensed and released under the terms of the MIT License.

Disclaimer

(An English translation for the reference follows the text written in Japanese.)

本プロジェクトは、TP-Linkとは無関係の非公式なものです。

This is an unofficial project that has no affiliation with TP-Link.

本プロジェクトが提供するソフトウェアは、デバイスの設定の取得・変更等、製品仕様の範囲内での操作のみを行うものであり、ファームウェアの改変・修正および製品の改造や製品仕様の変更を引き起こさないものの、製品使用上の許諾事項に抵触する可能性は否定できないため、使用の際はその点にご留意ください。

The software provided by this project is intended only for operations within the scope of the product specifications, such as acquiring and changing device settings, and while it does not cause altering the product itself, product's firmware or operating specifications. Nevertheless, please note that the possibility of violating terms of use of the product cannot be dismissed when using the software provided by this project.

TapoKasa、および各製品名の著作権はTP-Linkに帰属します。

Tapo, Kasa and all respective product names are copyright of TP-Link.

Credit

This project incorporates implementations partially ported from the following projects. See also ThirdPartyNotices.md for detail.

API List

List of APIs exposed by assembly Smdn.TPSmartHomeDevices.Kasa-2.0.0-preview3 (net8.0)

// Smdn.TPSmartHomeDevices.Kasa.dll (Smdn.TPSmartHomeDevices.Kasa-2.0.0-preview3)
//   Name: Smdn.TPSmartHomeDevices.Kasa
//   AssemblyVersion: 2.0.0.0
//   InformationalVersion: 2.0.0-preview3+8f11224df269f7ecb743bef2e7c9b994a578c8dd
//   TargetFramework: .NETCoreApp,Version=v8.0
//   Configuration: Release
//   Referenced assemblies:
//     Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
//     Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
//     Microsoft.Win32.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
//     Smdn.TPSmartHomeDevices.Primitives, Version=1.1.0.0, Culture=neutral
//     System.Collections, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
//     System.ComponentModel, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
//     System.Memory, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
//     System.Net.NetworkInformation, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
//     System.Net.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
//     System.Net.Sockets, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
//     System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
//     System.Text.Encodings.Web, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
//     System.Text.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
#nullable enable annotations

using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.NetworkInformation;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Smdn.TPSmartHomeDevices;
using Smdn.TPSmartHomeDevices.Kasa;
using Smdn.TPSmartHomeDevices.Kasa.Protocol;

namespace Smdn.TPSmartHomeDevices.Kasa {
  public class HS105 :
    KasaDevice,
    ISmartDevice
  {
    public static HS105 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider) where TAddress : notnull {}

    public HS105(IDeviceEndPoint deviceEndPoint, IServiceProvider? serviceProvider = null) {}
    public HS105(IPAddress ipAddress, IServiceProvider? serviceProvider = null) {}
    public HS105(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
    public HS105(string host, IServiceProvider? serviceProvider = null) {}

    public ValueTask<bool> GetOnOffStateAsync(CancellationToken cancellationToken = default) {}
    public ValueTask SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken = default) {}
    async ValueTask<IDeviceInfo> ISmartDevice.GetDeviceInfoAsync(CancellationToken cancellationToken) {}
    public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
    public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
  }

  public class KL130 :
    KasaDevice,
    IMulticolorSmartLight
  {
    public static KL130 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider) where TAddress : notnull {}

    public KL130(IDeviceEndPoint deviceEndPoint, IServiceProvider? serviceProvider = null) {}
    public KL130(IPAddress ipAddress, IServiceProvider? serviceProvider = null) {}
    public KL130(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
    public KL130(string host, IServiceProvider? serviceProvider = null) {}

    public ValueTask<KL130LightState> GetLightStateAsync(CancellationToken cancellationToken = default) {}
    public ValueTask<bool> GetOnOffStateAsync(CancellationToken cancellationToken = default) {}
    public ValueTask SetBrightnessAsync(int brightness, TimeSpan transitionPeriod = default, CancellationToken cancellationToken = default) {}
    public ValueTask SetColorAsync(int hue, int saturation, int? brightness = null, TimeSpan transitionPeriod = default, CancellationToken cancellationToken = default) {}
    public ValueTask SetColorTemperatureAsync(int colorTemperature, int? brightness = null, TimeSpan transitionPeriod = default, CancellationToken cancellationToken = default) {}
    public ValueTask SetOnOffStateAsync(bool newOnOffState, TimeSpan transitionPeriod = default, CancellationToken cancellationToken = default) {}
    async ValueTask<IDeviceInfo> ISmartDevice.GetDeviceInfoAsync(CancellationToken cancellationToken) {}
    ValueTask ISmartDevice.SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken) {}
    public ValueTask TurnOffAsync(TimeSpan transitionPeriod = default, CancellationToken cancellationToken = default) {}
    public ValueTask TurnOnAsync(TimeSpan transitionPeriod = default, CancellationToken cancellationToken = default) {}
  }

  public class KasaDevice : IDisposable {
    protected readonly struct NullParameter {
    }

    protected static readonly JsonEncodedText MethodTextGetSysInfo; // = "get_sysinfo"
    protected static readonly JsonEncodedText ModuleTextSystem; // = "system"

    public static KasaDevice Create(IDeviceEndPoint deviceEndPoint, IServiceProvider? serviceProvider = null) {}
    public static KasaDevice Create(IPAddress ipAddress, IServiceProvider? serviceProvider = null) {}
    public static KasaDevice Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
    public static KasaDevice Create(string host, IServiceProvider? serviceProvider = null) {}
    public static KasaDevice Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider) where TAddress : notnull {}

    protected KasaDevice(IDeviceEndPoint deviceEndPoint, IServiceProvider? serviceProvider) {}
    protected KasaDevice(IPAddress ipAddress, IServiceProvider? serviceProvider) {}
    protected KasaDevice(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
    protected KasaDevice(string host, IServiceProvider? serviceProvider) {}

    public IDeviceEndPoint EndPoint { get; }
    public bool IsConnected { get; }
    [MemberNotNullWhen(false, "deviceEndPoint")]
    protected bool IsDisposed { [MemberNotNullWhen(false, "deviceEndPoint")] get; }

    protected virtual void Dispose(bool disposing) {}
    public void Dispose() {}
    public ValueTask<KasaDeviceInfo?> GetDeviceInfoAsync(CancellationToken cancellationToken = default) {}
    protected ValueTask<TSysInfo> GetSysInfoAsync<TSysInfo>(Func<JsonElement, TSysInfo> composeResult, CancellationToken cancellationToken = default) {}
    public ValueTask<EndPoint> ResolveEndPointAsync(CancellationToken cancellationToken = default) {}
    protected ValueTask SendRequestAsync<TMethodParameter>(JsonEncodedText module, JsonEncodedText method, TMethodParameter parameters, CancellationToken cancellationToken) {}
    protected ValueTask<TMethodResult> SendRequestAsync<TMethodParameter, TMethodResult>(JsonEncodedText module, JsonEncodedText method, TMethodParameter parameters, Func<JsonElement, TMethodResult> composeResult, CancellationToken cancellationToken) {}
    protected ValueTask<TMethodResult> SendRequestAsync<TMethodResult>(JsonEncodedText module, JsonEncodedText method, Func<JsonElement, TMethodResult> composeResult, CancellationToken cancellationToken) {}
    public override string? ToString() {}
  }

  public abstract class KasaDeviceExceptionHandler {
    internal protected static readonly KasaDeviceExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Kasa.KasaDeviceDefaultExceptionHandler"

    protected KasaDeviceExceptionHandler() {}

    public abstract KasaDeviceExceptionHandling DetermineHandling(KasaDevice device, Exception exception, int attempt, ILogger? logger);
  }

  public static class KasaDeviceExceptionHandlerServiceCollectionExtensions {
    public static IServiceCollection AddKasaDeviceExceptionHandler(this IServiceCollection services, KasaDeviceExceptionHandler exceptionHandler) {}
  }

  public class KasaDeviceInfo : IDeviceInfo {
    public KasaDeviceInfo() {}

    public string? Description { get; }
    [JsonConverter(typeof(Base16ByteArrayJsonConverter))]
    [JsonPropertyName("fwId")]
    public byte[]? FirmwareId { get; init; }
    [JsonPropertyName("sw_ver")]
    public string? FirmwareVersion { get; init; }
    [JsonConverter(typeof(Base16ByteArrayJsonConverter))]
    [JsonPropertyName("hwId")]
    public byte[]? HardwareId { get; init; }
    [JsonPropertyName("hw_ver")]
    public string? HardwareVersion { get; init; }
    [JsonConverter(typeof(Base16ByteArrayJsonConverter))]
    [JsonPropertyName("deviceId")]
    public byte[]? Id { get; init; }
    public PhysicalAddress? MacAddress { get; }
    [JsonPropertyName("model")]
    public string? ModelName { get; init; }
    [JsonPropertyName("rssi")]
    public decimal? NetworkRssi { get; init; }
    [JsonConverter(typeof(Base16ByteArrayJsonConverter))]
    [JsonPropertyName("oemId")]
    public byte[]? OemId { get; init; }
    ReadOnlySpan<byte> IDeviceInfo.Id { get; }
    [JsonIgnore]
    public DateTimeOffset TimeStamp { get; }
    public string? TypeName { get; }
  }

  public class KasaDisconnectedException : KasaProtocolException {
    public KasaDisconnectedException(string message, EndPoint deviceEndPoint, Exception? innerException) {}
  }

  public class KasaErrorResponseException : KasaUnexpectedResponseException {
    public KasaErrorResponseException(EndPoint deviceEndPoint, string requestModule, string requestMethod, int rawErrorCode) {}

    public int RawErrorCode { get; }
  }

  public class KasaIncompleteResponseException : KasaUnexpectedResponseException {
    public KasaIncompleteResponseException(string message, EndPoint deviceEndPoint, string requestModule, string requestMethod, Exception? innerException) {}
  }

  public abstract class KasaProtocolException : InvalidOperationException {
    protected KasaProtocolException(string message, EndPoint deviceEndPoint, Exception? innerException) {}

    public EndPoint DeviceEndPoint { get; }
  }

  public class KasaUnexpectedResponseException : KasaProtocolException {
    public KasaUnexpectedResponseException(string message, EndPoint deviceEndPoint, string requestModule, string requestMethod, Exception? innerException) {}

    public string RequestMethod { get; }
    public string RequestModule { get; }
  }

  public readonly struct KL130LightState {
    [MemberNotNullWhen(true, "IsOn")]
    [JsonPropertyName("brightness")]
    public int? Brightness { [MemberNotNullWhen(true, "IsOn")] get; [MemberNotNullWhen(true, "IsOn")] init; }
    [MemberNotNullWhen(true, "IsOn")]
    [JsonPropertyName("color_temp")]
    public int? ColorTemperature { [MemberNotNullWhen(true, "IsOn")] get; [MemberNotNullWhen(true, "IsOn")] init; }
    [MemberNotNullWhen(true, "IsOn")]
    [JsonPropertyName("hue")]
    public int? Hue { [MemberNotNullWhen(true, "IsOn")] get; [MemberNotNullWhen(true, "IsOn")] init; }
    [JsonConverter(typeof(KasaNumericalBooleanJsonConverter))]
    [JsonPropertyName("on_off")]
    public bool IsOn { get; init; }
    [MemberNotNullWhen(true, "IsOn")]
    [JsonPropertyName("mode")]
    public string? Mode { [MemberNotNullWhen(true, "IsOn")] get; [MemberNotNullWhen(true, "IsOn")] init; }
    [MemberNotNullWhen(true, "IsOn")]
    [JsonPropertyName("saturation")]
    public int? Saturation { [MemberNotNullWhen(true, "IsOn")] get; [MemberNotNullWhen(true, "IsOn")] init; }
  }

  public readonly struct KasaDeviceExceptionHandling {
    public static readonly KasaDeviceExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=True}"
    public static readonly KasaDeviceExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=True}"
    public static readonly KasaDeviceExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=False}"
    public static readonly KasaDeviceExceptionHandling RetryAfterReconnect; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=True, ShouldInvalidateEndPoint=False}"
    public static readonly KasaDeviceExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=False}"

    public static KasaDeviceExceptionHandling CreateRetry(TimeSpan retryAfter, bool shouldReconnect = false) {}

    public TimeSpan RetryAfter { get; init; }
    public bool ShouldInvalidateEndPoint { get; init; }
    public bool ShouldReconnect { get; init; }
    public bool ShouldRetry { get; init; }

    public override string ToString() {}
  }
}

namespace Smdn.TPSmartHomeDevices.Kasa.Json {
  public sealed class KasaNumericalBooleanJsonConverter : JsonConverter<bool> {
    public KasaNumericalBooleanJsonConverter() {}

    public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
    public override void Write(Utf8JsonWriter writer, bool @value, JsonSerializerOptions options) {}
  }
}

namespace Smdn.TPSmartHomeDevices.Kasa.Protocol {
  public sealed class KasaClient : IDisposable {
    public const int DefaultPort = 9999;

    public KasaClient(EndPoint endPoint, ILogger? logger = null) {}

    public EndPoint EndPoint { get; }
    public bool IsConnected { get; }

    public void Dispose() {}
    public ValueTask<TMethodResult> SendAsync<TMethodParameter, TMethodResult>(JsonEncodedText module, JsonEncodedText method, TMethodParameter parameter, Func<JsonElement, TMethodResult> composeResult, CancellationToken cancellationToken = default) {}
  }

  public static class KasaJsonSerializer {
    public const byte InitialKey = 171;

    public static void DecryptInPlace(Span<byte> body) {}
    public static JsonElement Deserialize(ArrayBufferWriter<byte> buffer, JsonEncodedText module, JsonEncodedText method, ILogger? logger = null) {}
    public static void EncryptInPlace(Span<byte> body) {}
    public static void Serialize<TMethodParameter>(ArrayBufferWriter<byte> buffer, JsonEncodedText module, JsonEncodedText method, TMethodParameter parameter, ILogger? logger = null) {}
  }

  public class KasaMessageBodyTooShortException : KasaMessageException {
    public KasaMessageBodyTooShortException(int indicatedLength, int actualLength) {}

    public int ActualLength { get; }
    public int IndicatedLength { get; }
  }

  public class KasaMessageException : SystemException {
    public KasaMessageException(string message) {}
  }

  public class KasaMessageHeaderTooShortException : KasaMessageException {
    public KasaMessageHeaderTooShortException(string message) {}
  }
}
// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.3.2.0.
// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  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. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Smdn.TPSmartHomeDevices.Kasa:

Package Downloads
Smdn.TPSmartHomeDevices

A meta package that addes the dependency of Smdn.TPSmartHomeDevices.Kasa, Smdn.TPSmartHomeDevices.Tapo and Smdn.TPSmartHomeDevices.MacAddressEndPoint.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.0.0 282 1/23/2024
2.0.0-preview3 118 1/18/2024
2.0.0-preview2 118 1/16/2024
2.0.0-preview1 122 1/8/2024
1.0.1 221 5/14/2023
1.0.0 371 5/2/2023
1.0.0-rc1 275 4/27/2023