Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save smdn/1e06022628513a0345cb26a9a5527567 to your computer and use it in GitHub Desktop.
Save smdn/1e06022628513a0345cb26a9a5527567 to your computer and use it in GitHub Desktop.
Smdn.TPSmartHomeDevices 1.0.0-rc1 Release Notes

main/Smdn.TPSmartHomeDevices-1.0.0-rc1

diff --git a/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-net6.0.apilist.cs b/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-net6.0.apilist.cs
deleted file mode 100644
index 57cc447..0000000
--- a/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-net6.0.apilist.cs
+++ /dev/null
@@ -1,807 +0,0 @@
-// Smdn.TPSmartHomeDevices.dll (Smdn.TPSmartHomeDevices-1.0.0-preview3)
-// Name: Smdn.TPSmartHomeDevices
-// AssemblyVersion: 1.0.0.0
-// InformationalVersion: 1.0.0-preview3+ae327055c2651d84fffc372b481a72257af2a9d8
-// TargetFramework: .NETCoreApp,Version=v6.0
-// Configuration: Release
-// Referenced assemblies:
-// Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
-// Microsoft.Extensions.Http, 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=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// Smdn.Fundamental.PrintableEncoding.Hexadecimal, Version=3.0.1.0, Culture=neutral
-// Smdn.Net.AddressResolution, Version=1.0.0.0, Culture=neutral
-// System.Collections, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.ComponentModel, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Linq, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Memory, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// System.Net.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Net.Http.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// System.Net.NetworkInformation, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Net.Primitives, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Net.Sockets, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Security.Cryptography.Algorithms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Security.Cryptography.Encoding, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Security.Cryptography.Primitives, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Text.Encodings.Web, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// System.Text.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-#nullable enable annotations
-
-using System;
-using System.Buffers;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Net;
-using System.Net.Http;
-using System.Net.NetworkInformation;
-using System.Security.Cryptography;
-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.Net.AddressResolution;
-using Smdn.TPSmartHomeDevices;
-using Smdn.TPSmartHomeDevices.Kasa;
-using Smdn.TPSmartHomeDevices.Kasa.Protocol;
-using Smdn.TPSmartHomeDevices.Tapo;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-using Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-namespace Smdn.TPSmartHomeDevices {
- public interface IDeviceEndPointFactory<TAddress> {
- IDeviceEndPointProvider Create(TAddress address, int port = 0);
- }
-
- public interface IDeviceEndPointProvider {
- ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken = default);
- }
-
- public interface IDynamicDeviceEndPointProvider : IDeviceEndPointProvider {
- void InvalidateEndPoint();
- }
-
- public static class DeviceEndPointFactoryServiceCollectionExtensions {
- public static IServiceCollection AddDeviceEndPointFactory<TAddress>(this IServiceCollection services, IDeviceEndPointFactory<TAddress> endPointFactory) {}
- }
-
- public class DeviceEndPointResolutionException : Exception {
- public DeviceEndPointResolutionException(IDeviceEndPointProvider deviceEndPointProvider) {}
- public DeviceEndPointResolutionException(IDeviceEndPointProvider deviceEndPointProvider, string message, Exception? innerException) {}
-
- public IDeviceEndPointProvider EndPointProvider { get; }
- }
-
- public class MacAddressDeviceEndPointFactory :
- IDeviceEndPointFactory<PhysicalAddress>,
- IDisposable
- {
- protected class MacAddressDeviceEndPointProvider : IDynamicDeviceEndPointProvider {
- public MacAddressDeviceEndPointProvider(IAddressResolver<PhysicalAddress, IPAddress> resolver, PhysicalAddress address, int port) {}
-
- public async ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken) {}
- public void InvalidateEndPoint() {}
- public override string ToString() {}
- }
-
- protected MacAddressDeviceEndPointFactory(IAddressResolver<PhysicalAddress, IPAddress> resolver, IServiceProvider? serviceProvider = null) {}
- public MacAddressDeviceEndPointFactory(MacAddressResolver resolver, IServiceProvider? serviceProvider = null) {}
- public MacAddressDeviceEndPointFactory(MacAddressResolverOptions? options = null, IServiceProvider? serviceProvider = null) {}
-
- public virtual IDeviceEndPointProvider Create(PhysicalAddress address, int port = 0) {}
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- protected void ThrowIfDisposed() {}
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Kasa {
- public class HS105 : KasaDevice {
- public HS105(IDeviceEndPointProvider deviceEndPointProvider, 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) {}
- public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
- }
-
- public class KL130 : KasaDevice {
- public KL130(IDeviceEndPointProvider deviceEndPointProvider, 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 SetColorAsync(int hue, int saturation, int? brightness = null, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorTemperatureAsync(int colorTemperature, int? brightness = null, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetOnOffStateAsync(bool newOnOffState, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOffAsync(TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(TimeSpan? transitionPeriod = null, 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(IDeviceEndPointProvider deviceEndPointProvider, 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) {}
-
- protected KasaDevice(IDeviceEndPointProvider deviceEndPointProvider, IServiceProvider? serviceProvider = null) {}
- protected KasaDevice(IPAddress ipAddress, IServiceProvider? serviceProvider = null) {}
- protected KasaDevice(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- protected KasaDevice(string host, IServiceProvider? serviceProvider = null) {}
-
- public bool IsConnected { get; }
- protected bool IsDisposed { get; }
-
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- 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 static class KasaDeviceEndPointProvider {
- public static IDeviceEndPointProvider Create(IPAddress ipAddress) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IDeviceEndPointFactory<PhysicalAddress> endPointFactory) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static IDeviceEndPointProvider Create(string host) {}
- }
-
- public class KasaDisconnectedException : KasaProtocolException {
- public KasaDisconnectedException(string message, EndPoint deviceEndPoint, Exception? innerException) {}
- }
-
- public class KasaErrorResponseException : KasaUnexpectedResponseException {
- public KasaErrorResponseException(EndPoint deviceEndPoint, string requestModule, string requestMethod, ErrorCode errorCode) {}
-
- public ErrorCode ErrorCode { 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; }
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol {
- public enum ErrorCode : int {
- Success = 0,
- }
-
- 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 abstract class KasaClientExceptionHandler {
- internal protected static readonly KasaClientExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Kasa.Protocol.KasaClientDefaultExceptionHandler"
-
- protected KasaClientExceptionHandler() {}
-
- public abstract KasaClientExceptionHandling DetermineHandling(KasaDevice device, Exception exception, int attempt, ILogger? logger);
- }
-
- 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) {}
- }
-
- public readonly struct KasaClientExceptionHandling {
- public static readonly KasaClientExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=True}"
- public static readonly KasaClientExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=True}"
- public static readonly KasaClientExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=False}"
- public static readonly KasaClientExceptionHandling RetryAfterReconnect; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=True, ShouldInvalidateEndPoint=False}"
- public static readonly KasaClientExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=False}"
-
- public static KasaClientExceptionHandling 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.Tapo {
- public class L530 : TapoDevice {
- public L530(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public L530(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public L530(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public L530(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public L530(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public L530(string host, IServiceProvider serviceProvider) {}
- public L530(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- public ValueTask SetBrightnessAsync(int brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorAsync(int hue, int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorHueAsync(int hue, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorSaturationAsync(int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorTemperatureAsync(int colorTemperature, int? brightness = null, CancellationToken cancellationToken = default) {}
- }
-
- public class L900 : TapoDevice {
- public L900(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public L900(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public L900(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public L900(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public L900(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public L900(string host, IServiceProvider serviceProvider) {}
- public L900(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- public ValueTask SetBrightnessAsync(int brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorAsync(int hue, int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorHueAsync(int hue, int? brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorSaturationAsync(int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- }
-
- public class P105 : TapoDevice {
- public P105(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public P105(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public P105(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public P105(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public P105(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public P105(string host, IServiceProvider serviceProvider) {}
- public P105(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
- }
-
- public class TapoAuthenticationException : TapoProtocolException {
- public TapoAuthenticationException(string message, Uri endPoint, Exception? innerException = null) {}
- }
-
- public static class TapoCredentailProviderServiceCollectionExtensions {
- public static IServiceCollection AddTapoBase64EncodedCredential(this IServiceCollection services, string base64UserNameSHA1Digest, string base64Password) {}
- public static IServiceCollection AddTapoCredential(this IServiceCollection services, string email, string password) {}
- public static IServiceCollection AddTapoCredentialProvider(this IServiceCollection services, ITapoCredentialProvider credentialProvider) {}
- }
-
- public class TapoDevice :
- IDisposable,
- ITapoCredentialIdentity
- {
- public static TapoDevice Create(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public static TapoDevice Create(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public static TapoDevice Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(string host, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- protected TapoDevice(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, TapoClientExceptionHandler? exceptionHandler = null, IServiceProvider? serviceProvider = null) {}
- protected TapoDevice(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- protected TapoDevice(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- protected TapoDevice(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- protected TapoDevice(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- protected TapoDevice(string host, IServiceProvider serviceProvider) {}
- protected TapoDevice(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- protected bool IsDisposed { get; }
- public TapoSession? Session { get; }
- string ITapoCredentialIdentity.Name { get; }
- public string TerminalUuidString { get; }
- public TimeSpan? Timeout { get; set; }
-
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- protected ValueTask EnsureSessionEstablishedAsync(CancellationToken cancellationToken = default) {}
- public ValueTask<TapoDeviceInfo> GetDeviceInfoAsync(CancellationToken cancellationToken = default) {}
- public ValueTask<EndPoint> ResolveEndPointAsync(CancellationToken cancellationToken = default) {}
- protected ValueTask SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- protected ValueTask<TResult> SendRequestAsync<TRequest, TResponse, TResult>(TRequest request, Func<TResponse, TResult> composeResult, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- public ValueTask SetDeviceInfoAsync<TParameters>(TParameters parameters, CancellationToken cancellationToken = default) {}
- public ValueTask SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
- }
-
- public static class TapoDeviceEndPointProvider {
- public static IDeviceEndPointProvider Create(IPAddress ipAddress) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IDeviceEndPointFactory<PhysicalAddress> endPointFactory) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static IDeviceEndPointProvider Create(string host) {}
- }
-
- public class TapoDeviceInfo {
- public TapoDeviceInfo() {}
-
- [JsonPropertyName("avatar")]
- public string? Avatar { get; init; }
- [JsonPropertyName("fw_id")]
- public string? FirmwareId { get; init; }
- [JsonPropertyName("fw_ver")]
- public string? FirmwareVersion { get; init; }
- [JsonConverter(typeof(GeolocationInDecimalDegreesJsonConverter))]
- [JsonPropertyName("latitude")]
- public decimal? GeolocationLatitude { get; init; }
- [JsonConverter(typeof(GeolocationInDecimalDegreesJsonConverter))]
- [JsonPropertyName("longitude")]
- public decimal? GeolocationLongitude { get; init; }
- [JsonPropertyName("hw_id")]
- public string? HardwareId { get; init; }
- [JsonPropertyName("specs")]
- public string? HardwareSpecifications { get; init; }
- [JsonPropertyName("hw_ver")]
- public string? HardwareVersion { get; init; }
- [JsonPropertyName("has_set_location_info")]
- public bool HasGeolocationInfoSet { get; init; }
- [JsonConverter(typeof(TapoIPAddressJsonConverter))]
- [JsonPropertyName("ip")]
- public IPAddress? IPAddress { get; init; }
- [JsonPropertyName("device_id")]
- public string? Id { get; init; }
- [JsonPropertyName("device_on")]
- public bool IsOn { get; init; }
- [JsonPropertyName("overheated")]
- public bool IsOverheated { get; init; }
- [JsonPropertyName("lang")]
- public string? Language { get; init; }
- [JsonConverter(typeof(MacAddressJsonConverter))]
- [JsonPropertyName("mac")]
- public PhysicalAddress? MacAddress { get; init; }
- [JsonPropertyName("model")]
- public string? ModelName { get; init; }
- [JsonPropertyName("rssi")]
- public decimal? NetworkRssi { get; init; }
- [JsonPropertyName("signal_level")]
- public int? NetworkSignalLevel { get; init; }
- [JsonConverter(typeof(TapoBase64StringJsonConverter))]
- [JsonPropertyName("ssid")]
- public string? NetworkSsid { get; init; }
- [JsonConverter(typeof(TapoBase64StringJsonConverter))]
- [JsonPropertyName("nickname")]
- public string? NickName { get; init; }
- [JsonPropertyName("oem_id")]
- public string? OemId { get; init; }
- [JsonConverter(typeof(TimeSpanInSecondsJsonConverter))]
- [JsonPropertyName("on_time")]
- public TimeSpan? OnTimeDuration { get; init; }
- [JsonIgnore]
- public DateTimeOffset TimeStamp { get; }
- [JsonConverter(typeof(TimeSpanInMinutesJsonConverter))]
- [JsonPropertyName("time_diff")]
- public TimeSpan? TimeZoneOffset { get; init; }
- [JsonPropertyName("region")]
- public string? TimeZoneRegion { get; init; }
- [JsonPropertyName("type")]
- public string? TypeName { get; init; }
- }
-
- public class TapoErrorResponseException : TapoProtocolException {
- public TapoErrorResponseException(Uri requestEndPoint, string requestMethod, ErrorCode errorCode) {}
-
- public ErrorCode ErrorCode { get; }
- public string RequestMethod { get; }
- }
-
- public static class TapoHttpClientFactoryServiceCollectionExtensions {
- public static IServiceCollection AddTapoHttpClient(this IServiceCollection services, Action<HttpClient>? configureClient = null) {}
- }
-
- public class TapoProtocolException : InvalidOperationException {
- internal protected TapoProtocolException(string message, Uri endPoint, Exception? innerException) {}
-
- public Uri EndPoint { get; }
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Credentials {
- public interface ITapoCredential : IDisposable {
- void WritePasswordPropertyValue(Utf8JsonWriter writer);
- void WriteUsernamePropertyValue(Utf8JsonWriter writer);
- }
-
- public interface ITapoCredentialIdentity {
- string Name { get; }
- }
-
- public interface ITapoCredentialProvider {
- ITapoCredential GetCredential(ITapoCredentialIdentity? identity);
- }
-
- public static class TapoCredentialUtils {
- public const int HexSHA1HashSizeInBytes = 40;
-
- public static string ToBase64EncodedSHA1DigestString(ReadOnlySpan<char> str) {}
- public static string ToBase64EncodedString(ReadOnlySpan<char> str) {}
- public static bool TryConvertToHexSHA1Hash(ReadOnlySpan<byte> input, Span<byte> destination, out int bytesWritten) {}
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol {
- public interface ITapoPassThroughRequest : ITapoRequest {
- }
-
- public interface ITapoPassThroughResponse : ITapoResponse {
- }
-
- public interface ITapoRequest {
- string Method { get; }
- }
-
- public interface ITapoResponse {
- ErrorCode ErrorCode { get; init; }
- }
-
- public enum ErrorCode : int {
- Success = 0,
- }
-
- public class SecurePassThroughInvalidPaddingException : SystemException {
- public SecurePassThroughInvalidPaddingException(string message, Exception? innerException) {}
- }
-
- public sealed class SecurePassThroughJsonConverterFactory :
- JsonConverterFactory,
- IDisposable
- {
- public SecurePassThroughJsonConverterFactory(ITapoCredentialIdentity? identity, ICryptoTransform? encryptorForPassThroughRequest, ICryptoTransform? decryptorForPassThroughResponse, JsonSerializerOptions? baseJsonSerializerOptionsForPassThroughMessage, ILogger? logger = null) {}
-
- public override bool CanConvert(Type typeToConvert) {}
- public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) {}
- public void Dispose() {}
- }
-
- public sealed class TapoClient : IDisposable {
- public const int DefaultPort = 80;
-
- public TapoClient(EndPoint endPoint, IHttpClientFactory? httpClientFactory = null, ILogger? logger = null) {}
-
- public Uri EndPointUri { get; }
- public TapoSession? Session { get; }
- public TimeSpan? Timeout { get; set; }
-
- public ValueTask AuthenticateAsync(ITapoCredentialIdentity? identity, ITapoCredentialProvider credential, CancellationToken cancellationToken = default) {}
- public void Dispose() {}
- public ValueTask<TResponse> SendRequestAsync<TRequest, TResponse>(CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest, new() where TResponse : ITapoPassThroughResponse {}
- public ValueTask<TResponse> SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- }
-
- public abstract class TapoClientExceptionHandler {
- internal protected static readonly TapoClientExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Tapo.Protocol.TapoClientDefaultExceptionHandler"
-
- protected TapoClientExceptionHandler() {}
-
- public abstract TapoClientExceptionHandling DetermineHandling(TapoDevice device, Exception exception, int attempt, ILogger? logger);
- }
-
- public sealed class TapoSession : IDisposable {
- public DateTime ExpiresOn { get; }
- public bool HasExpired { get; }
- public Uri RequestPathAndQuery { get; }
- public string? SessionId { get; }
- public string? Token { get; }
-
- public void Dispose() {}
- }
-
- public static class TapoSessionCookieUtils {
- public static bool TryGetCookie(HttpResponseMessage response, out string? sessionId, out int? sessionTimeout) {}
- public static bool TryGetCookie(IEnumerable<string>? cookieValues, out string? sessionId, out int? sessionTimeout) {}
- public static bool TryParseCookie(ReadOnlySpan<char> cookie, out string? id, out int? timeout) {}
- }
-
- public readonly struct GetDeviceInfoRequest : ITapoPassThroughRequest {
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- }
-
- public readonly struct GetDeviceInfoResponse : ITapoPassThroughResponse {
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public TapoDeviceInfo Result { get; init; }
- }
-
- public readonly struct HandshakeRequest : ITapoRequest {
- public readonly struct RequestParameters : IEquatable<RequestParameters> {
- [CompilerGenerated]
- public static bool operator == (HandshakeRequest.RequestParameters left, HandshakeRequest.RequestParameters right) {}
- [CompilerGenerated]
- public static bool operator != (HandshakeRequest.RequestParameters left, HandshakeRequest.RequestParameters right) {}
-
- public RequestParameters(string Key) {}
-
- [JsonPropertyName("key")]
- public string Key { get; init; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
-
- [CompilerGenerated]
- public void Deconstruct(out string Key) {}
- [CompilerGenerated]
- public bool Equals(HandshakeRequest.RequestParameters other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public HandshakeRequest(string key) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public HandshakeRequest.RequestParameters Parameters { get; }
- }
-
- public readonly struct HandshakeResponse : ITapoResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (HandshakeResponse.ResponseResult left, HandshakeResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (HandshakeResponse.ResponseResult left, HandshakeResponse.ResponseResult right) {}
-
- public ResponseResult(string? Key) {}
-
- [JsonPropertyName("key")]
- public string? Key { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out string? Key) {}
- [CompilerGenerated]
- public bool Equals(HandshakeResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public HandshakeResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct LoginDeviceRequest : ITapoPassThroughRequest {
- public LoginDeviceRequest(ITapoCredentialProvider credential) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public ITapoCredentialProvider Parameters { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- }
-
- public readonly struct LoginDeviceResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (LoginDeviceResponse.ResponseResult left, LoginDeviceResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (LoginDeviceResponse.ResponseResult left, LoginDeviceResponse.ResponseResult right) {}
-
- public ResponseResult(string Token) {}
-
- [JsonPropertyName("token")]
- public string Token { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out string Token) {}
- [CompilerGenerated]
- public bool Equals(LoginDeviceResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public LoginDeviceResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct SecurePassThroughRequest<TPassThroughRequest> : ITapoRequest where TPassThroughRequest : ITapoPassThroughRequest {
- public readonly struct RequestParams : IEquatable<RequestParams> where TPassThroughRequest : ITapoPassThroughRequest {
- [CompilerGenerated]
- public static bool operator == (SecurePassThroughRequest<TPassThroughRequest>.RequestParams left, SecurePassThroughRequest<TPassThroughRequest>.RequestParams right) {}
- [CompilerGenerated]
- public static bool operator != (SecurePassThroughRequest<TPassThroughRequest>.RequestParams left, SecurePassThroughRequest<TPassThroughRequest>.RequestParams right) {}
-
- public RequestParams(TPassThroughRequest PassThroughRequest) {}
-
- [JsonPropertyName("request")]
- public TPassThroughRequest PassThroughRequest { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out TPassThroughRequest PassThroughRequest) {}
- [CompilerGenerated]
- public bool Equals(SecurePassThroughRequest<TPassThroughRequest>.RequestParams other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public SecurePassThroughRequest(TPassThroughRequest passThroughRequest) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public SecurePassThroughRequest<TPassThroughRequest>.RequestParams Params { get; }
- }
-
- public readonly struct SecurePassThroughResponse<TPassThroughResponse> : ITapoResponse where TPassThroughResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> where TPassThroughResponse : ITapoPassThroughResponse {
- [CompilerGenerated]
- public static bool operator == (SecurePassThroughResponse<TPassThroughResponse>.ResponseResult left, SecurePassThroughResponse<TPassThroughResponse>.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (SecurePassThroughResponse<TPassThroughResponse>.ResponseResult left, SecurePassThroughResponse<TPassThroughResponse>.ResponseResult right) {}
-
- public ResponseResult(TPassThroughResponse PassThroughResponse) {}
-
- [JsonPropertyName("response")]
- public TPassThroughResponse PassThroughResponse { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out TPassThroughResponse PassThroughResponse) {}
- [CompilerGenerated]
- public bool Equals(SecurePassThroughResponse<TPassThroughResponse>.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public SecurePassThroughResponse(ErrorCode errorCode, TPassThroughResponse passThroughResponse) {}
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public SecurePassThroughResponse<TPassThroughResponse>.ResponseResult Result { get; init; }
- }
-
- public readonly struct SetDeviceInfoRequest<TParameters> : ITapoPassThroughRequest {
- public SetDeviceInfoRequest(string terminalUuid, TParameters parameters) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public TParameters Parameters { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- [JsonPropertyName("terminalUUID")]
- public string TerminalUuid { get; }
- }
-
- public readonly struct SetDeviceInfoResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (SetDeviceInfoResponse.ResponseResult left, SetDeviceInfoResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (SetDeviceInfoResponse.ResponseResult left, SetDeviceInfoResponse.ResponseResult right) {}
-
- public ResponseResult(IDictionary<string, object>? ExtraData) {}
-
- [JsonExtensionData]
- public IDictionary<string, object>? ExtraData { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out IDictionary<string, object>? ExtraData) {}
- [CompilerGenerated]
- public bool Equals(SetDeviceInfoResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public SetDeviceInfoResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct TapoClientExceptionHandling {
- public static readonly TapoClientExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
- public static readonly TapoClientExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
- public static readonly TapoClientExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling RetryAfterReconnect; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=True, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling ThrowAsTapoProtocolException; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=True, ShouldInvalidateEndPoint=False}"
-
- public static TapoClientExceptionHandling 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 bool ShouldWrapIntoTapoProtocolException { get; init; }
-
- public override string ToString() {}
- }
-}
-// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.2.0.
-// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-net7.0.apilist.cs b/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-net7.0.apilist.cs
deleted file mode 100644
index 6eb1832..0000000
--- a/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-net7.0.apilist.cs
+++ /dev/null
@@ -1,805 +0,0 @@
-// Smdn.TPSmartHomeDevices.dll (Smdn.TPSmartHomeDevices-1.0.0-preview3)
-// Name: Smdn.TPSmartHomeDevices
-// AssemblyVersion: 1.0.0.0
-// InformationalVersion: 1.0.0-preview3+ae327055c2651d84fffc372b481a72257af2a9d8
-// TargetFramework: .NETCoreApp,Version=v7.0
-// Configuration: Release
-// Referenced assemblies:
-// Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
-// Microsoft.Extensions.Http, 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=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// Smdn.Fundamental.PrintableEncoding.Hexadecimal, Version=3.0.1.0, Culture=neutral
-// Smdn.Net.AddressResolution, Version=1.0.0.0, Culture=neutral
-// System.Collections, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.ComponentModel, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Linq, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Memory, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// System.Net.Http, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Net.Http.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// System.Net.NetworkInformation, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Net.Primitives, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Net.Sockets, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Security.Cryptography, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-// System.Text.Encodings.Web, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// System.Text.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-#nullable enable annotations
-
-using System;
-using System.Buffers;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Net;
-using System.Net.Http;
-using System.Net.NetworkInformation;
-using System.Security.Cryptography;
-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.Net.AddressResolution;
-using Smdn.TPSmartHomeDevices;
-using Smdn.TPSmartHomeDevices.Kasa;
-using Smdn.TPSmartHomeDevices.Kasa.Protocol;
-using Smdn.TPSmartHomeDevices.Tapo;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-using Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-namespace Smdn.TPSmartHomeDevices {
- public interface IDeviceEndPointFactory<TAddress> {
- IDeviceEndPointProvider Create(TAddress address, int port = 0);
- }
-
- public interface IDeviceEndPointProvider {
- ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken = default);
- }
-
- public interface IDynamicDeviceEndPointProvider : IDeviceEndPointProvider {
- void InvalidateEndPoint();
- }
-
- public static class DeviceEndPointFactoryServiceCollectionExtensions {
- public static IServiceCollection AddDeviceEndPointFactory<TAddress>(this IServiceCollection services, IDeviceEndPointFactory<TAddress> endPointFactory) {}
- }
-
- public class DeviceEndPointResolutionException : Exception {
- public DeviceEndPointResolutionException(IDeviceEndPointProvider deviceEndPointProvider) {}
- public DeviceEndPointResolutionException(IDeviceEndPointProvider deviceEndPointProvider, string message, Exception? innerException) {}
-
- public IDeviceEndPointProvider EndPointProvider { get; }
- }
-
- public class MacAddressDeviceEndPointFactory :
- IDeviceEndPointFactory<PhysicalAddress>,
- IDisposable
- {
- protected class MacAddressDeviceEndPointProvider : IDynamicDeviceEndPointProvider {
- public MacAddressDeviceEndPointProvider(IAddressResolver<PhysicalAddress, IPAddress> resolver, PhysicalAddress address, int port) {}
-
- public async ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken) {}
- public void InvalidateEndPoint() {}
- public override string ToString() {}
- }
-
- protected MacAddressDeviceEndPointFactory(IAddressResolver<PhysicalAddress, IPAddress> resolver, IServiceProvider? serviceProvider = null) {}
- public MacAddressDeviceEndPointFactory(MacAddressResolver resolver, IServiceProvider? serviceProvider = null) {}
- public MacAddressDeviceEndPointFactory(MacAddressResolverOptions? options = null, IServiceProvider? serviceProvider = null) {}
-
- public virtual IDeviceEndPointProvider Create(PhysicalAddress address, int port = 0) {}
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- protected void ThrowIfDisposed() {}
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Kasa {
- public class HS105 : KasaDevice {
- public HS105(IDeviceEndPointProvider deviceEndPointProvider, 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) {}
- public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
- }
-
- public class KL130 : KasaDevice {
- public KL130(IDeviceEndPointProvider deviceEndPointProvider, 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 SetColorAsync(int hue, int saturation, int? brightness = null, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorTemperatureAsync(int colorTemperature, int? brightness = null, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetOnOffStateAsync(bool newOnOffState, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOffAsync(TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(TimeSpan? transitionPeriod = null, 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(IDeviceEndPointProvider deviceEndPointProvider, 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) {}
-
- protected KasaDevice(IDeviceEndPointProvider deviceEndPointProvider, IServiceProvider? serviceProvider = null) {}
- protected KasaDevice(IPAddress ipAddress, IServiceProvider? serviceProvider = null) {}
- protected KasaDevice(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- protected KasaDevice(string host, IServiceProvider? serviceProvider = null) {}
-
- public bool IsConnected { get; }
- protected bool IsDisposed { get; }
-
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- 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 static class KasaDeviceEndPointProvider {
- public static IDeviceEndPointProvider Create(IPAddress ipAddress) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IDeviceEndPointFactory<PhysicalAddress> endPointFactory) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static IDeviceEndPointProvider Create(string host) {}
- }
-
- public class KasaDisconnectedException : KasaProtocolException {
- public KasaDisconnectedException(string message, EndPoint deviceEndPoint, Exception? innerException) {}
- }
-
- public class KasaErrorResponseException : KasaUnexpectedResponseException {
- public KasaErrorResponseException(EndPoint deviceEndPoint, string requestModule, string requestMethod, ErrorCode errorCode) {}
-
- public ErrorCode ErrorCode { 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; }
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol {
- public enum ErrorCode : int {
- Success = 0,
- }
-
- 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 abstract class KasaClientExceptionHandler {
- internal protected static readonly KasaClientExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Kasa.Protocol.KasaClientDefaultExceptionHandler"
-
- protected KasaClientExceptionHandler() {}
-
- public abstract KasaClientExceptionHandling DetermineHandling(KasaDevice device, Exception exception, int attempt, ILogger? logger);
- }
-
- 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) {}
- }
-
- public readonly struct KasaClientExceptionHandling {
- public static readonly KasaClientExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=True}"
- public static readonly KasaClientExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=True}"
- public static readonly KasaClientExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=False}"
- public static readonly KasaClientExceptionHandling RetryAfterReconnect; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=True, ShouldInvalidateEndPoint=False}"
- public static readonly KasaClientExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=False}"
-
- public static KasaClientExceptionHandling 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.Tapo {
- public class L530 : TapoDevice {
- public L530(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public L530(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public L530(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public L530(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public L530(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public L530(string host, IServiceProvider serviceProvider) {}
- public L530(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- public ValueTask SetBrightnessAsync(int brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorAsync(int hue, int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorHueAsync(int hue, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorSaturationAsync(int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorTemperatureAsync(int colorTemperature, int? brightness = null, CancellationToken cancellationToken = default) {}
- }
-
- public class L900 : TapoDevice {
- public L900(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public L900(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public L900(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public L900(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public L900(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public L900(string host, IServiceProvider serviceProvider) {}
- public L900(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- public ValueTask SetBrightnessAsync(int brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorAsync(int hue, int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorHueAsync(int hue, int? brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorSaturationAsync(int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- }
-
- public class P105 : TapoDevice {
- public P105(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public P105(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public P105(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public P105(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public P105(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public P105(string host, IServiceProvider serviceProvider) {}
- public P105(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
- }
-
- public class TapoAuthenticationException : TapoProtocolException {
- public TapoAuthenticationException(string message, Uri endPoint, Exception? innerException = null) {}
- }
-
- public static class TapoCredentailProviderServiceCollectionExtensions {
- public static IServiceCollection AddTapoBase64EncodedCredential(this IServiceCollection services, string base64UserNameSHA1Digest, string base64Password) {}
- public static IServiceCollection AddTapoCredential(this IServiceCollection services, string email, string password) {}
- public static IServiceCollection AddTapoCredentialProvider(this IServiceCollection services, ITapoCredentialProvider credentialProvider) {}
- }
-
- public class TapoDevice :
- IDisposable,
- ITapoCredentialIdentity
- {
- public static TapoDevice Create(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public static TapoDevice Create(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public static TapoDevice Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(string host, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- protected TapoDevice(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, TapoClientExceptionHandler? exceptionHandler = null, IServiceProvider? serviceProvider = null) {}
- protected TapoDevice(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- protected TapoDevice(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- protected TapoDevice(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- protected TapoDevice(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- protected TapoDevice(string host, IServiceProvider serviceProvider) {}
- protected TapoDevice(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- protected bool IsDisposed { get; }
- public TapoSession? Session { get; }
- string ITapoCredentialIdentity.Name { get; }
- public string TerminalUuidString { get; }
- public TimeSpan? Timeout { get; set; }
-
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- protected ValueTask EnsureSessionEstablishedAsync(CancellationToken cancellationToken = default) {}
- public ValueTask<TapoDeviceInfo> GetDeviceInfoAsync(CancellationToken cancellationToken = default) {}
- public ValueTask<EndPoint> ResolveEndPointAsync(CancellationToken cancellationToken = default) {}
- protected ValueTask SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- protected ValueTask<TResult> SendRequestAsync<TRequest, TResponse, TResult>(TRequest request, Func<TResponse, TResult> composeResult, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- public ValueTask SetDeviceInfoAsync<TParameters>(TParameters parameters, CancellationToken cancellationToken = default) {}
- public ValueTask SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
- }
-
- public static class TapoDeviceEndPointProvider {
- public static IDeviceEndPointProvider Create(IPAddress ipAddress) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IDeviceEndPointFactory<PhysicalAddress> endPointFactory) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static IDeviceEndPointProvider Create(string host) {}
- }
-
- public class TapoDeviceInfo {
- public TapoDeviceInfo() {}
-
- [JsonPropertyName("avatar")]
- public string? Avatar { get; init; }
- [JsonPropertyName("fw_id")]
- public string? FirmwareId { get; init; }
- [JsonPropertyName("fw_ver")]
- public string? FirmwareVersion { get; init; }
- [JsonConverter(typeof(GeolocationInDecimalDegreesJsonConverter))]
- [JsonPropertyName("latitude")]
- public decimal? GeolocationLatitude { get; init; }
- [JsonConverter(typeof(GeolocationInDecimalDegreesJsonConverter))]
- [JsonPropertyName("longitude")]
- public decimal? GeolocationLongitude { get; init; }
- [JsonPropertyName("hw_id")]
- public string? HardwareId { get; init; }
- [JsonPropertyName("specs")]
- public string? HardwareSpecifications { get; init; }
- [JsonPropertyName("hw_ver")]
- public string? HardwareVersion { get; init; }
- [JsonPropertyName("has_set_location_info")]
- public bool HasGeolocationInfoSet { get; init; }
- [JsonConverter(typeof(TapoIPAddressJsonConverter))]
- [JsonPropertyName("ip")]
- public IPAddress? IPAddress { get; init; }
- [JsonPropertyName("device_id")]
- public string? Id { get; init; }
- [JsonPropertyName("device_on")]
- public bool IsOn { get; init; }
- [JsonPropertyName("overheated")]
- public bool IsOverheated { get; init; }
- [JsonPropertyName("lang")]
- public string? Language { get; init; }
- [JsonConverter(typeof(MacAddressJsonConverter))]
- [JsonPropertyName("mac")]
- public PhysicalAddress? MacAddress { get; init; }
- [JsonPropertyName("model")]
- public string? ModelName { get; init; }
- [JsonPropertyName("rssi")]
- public decimal? NetworkRssi { get; init; }
- [JsonPropertyName("signal_level")]
- public int? NetworkSignalLevel { get; init; }
- [JsonConverter(typeof(TapoBase64StringJsonConverter))]
- [JsonPropertyName("ssid")]
- public string? NetworkSsid { get; init; }
- [JsonConverter(typeof(TapoBase64StringJsonConverter))]
- [JsonPropertyName("nickname")]
- public string? NickName { get; init; }
- [JsonPropertyName("oem_id")]
- public string? OemId { get; init; }
- [JsonConverter(typeof(TimeSpanInSecondsJsonConverter))]
- [JsonPropertyName("on_time")]
- public TimeSpan? OnTimeDuration { get; init; }
- [JsonIgnore]
- public DateTimeOffset TimeStamp { get; }
- [JsonConverter(typeof(TimeSpanInMinutesJsonConverter))]
- [JsonPropertyName("time_diff")]
- public TimeSpan? TimeZoneOffset { get; init; }
- [JsonPropertyName("region")]
- public string? TimeZoneRegion { get; init; }
- [JsonPropertyName("type")]
- public string? TypeName { get; init; }
- }
-
- public class TapoErrorResponseException : TapoProtocolException {
- public TapoErrorResponseException(Uri requestEndPoint, string requestMethod, ErrorCode errorCode) {}
-
- public ErrorCode ErrorCode { get; }
- public string RequestMethod { get; }
- }
-
- public static class TapoHttpClientFactoryServiceCollectionExtensions {
- public static IServiceCollection AddTapoHttpClient(this IServiceCollection services, Action<HttpClient>? configureClient = null) {}
- }
-
- public class TapoProtocolException : InvalidOperationException {
- internal protected TapoProtocolException(string message, Uri endPoint, Exception? innerException) {}
-
- public Uri EndPoint { get; }
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Credentials {
- public interface ITapoCredential : IDisposable {
- void WritePasswordPropertyValue(Utf8JsonWriter writer);
- void WriteUsernamePropertyValue(Utf8JsonWriter writer);
- }
-
- public interface ITapoCredentialIdentity {
- string Name { get; }
- }
-
- public interface ITapoCredentialProvider {
- ITapoCredential GetCredential(ITapoCredentialIdentity? identity);
- }
-
- public static class TapoCredentialUtils {
- public const int HexSHA1HashSizeInBytes = 40;
-
- public static string ToBase64EncodedSHA1DigestString(ReadOnlySpan<char> str) {}
- public static string ToBase64EncodedString(ReadOnlySpan<char> str) {}
- public static bool TryConvertToHexSHA1Hash(ReadOnlySpan<byte> input, Span<byte> destination, out int bytesWritten) {}
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol {
- public interface ITapoPassThroughRequest : ITapoRequest {
- }
-
- public interface ITapoPassThroughResponse : ITapoResponse {
- }
-
- public interface ITapoRequest {
- string Method { get; }
- }
-
- public interface ITapoResponse {
- ErrorCode ErrorCode { get; init; }
- }
-
- public enum ErrorCode : int {
- Success = 0,
- }
-
- public class SecurePassThroughInvalidPaddingException : SystemException {
- public SecurePassThroughInvalidPaddingException(string message, Exception? innerException) {}
- }
-
- public sealed class SecurePassThroughJsonConverterFactory :
- JsonConverterFactory,
- IDisposable
- {
- public SecurePassThroughJsonConverterFactory(ITapoCredentialIdentity? identity, ICryptoTransform? encryptorForPassThroughRequest, ICryptoTransform? decryptorForPassThroughResponse, JsonSerializerOptions? baseJsonSerializerOptionsForPassThroughMessage, ILogger? logger = null) {}
-
- public override bool CanConvert(Type typeToConvert) {}
- public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) {}
- public void Dispose() {}
- }
-
- public sealed class TapoClient : IDisposable {
- public const int DefaultPort = 80;
-
- public TapoClient(EndPoint endPoint, IHttpClientFactory? httpClientFactory = null, ILogger? logger = null) {}
-
- public Uri EndPointUri { get; }
- public TapoSession? Session { get; }
- public TimeSpan? Timeout { get; set; }
-
- public ValueTask AuthenticateAsync(ITapoCredentialIdentity? identity, ITapoCredentialProvider credential, CancellationToken cancellationToken = default) {}
- public void Dispose() {}
- public ValueTask<TResponse> SendRequestAsync<TRequest, TResponse>(CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest, new() where TResponse : ITapoPassThroughResponse {}
- public ValueTask<TResponse> SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- }
-
- public abstract class TapoClientExceptionHandler {
- internal protected static readonly TapoClientExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Tapo.Protocol.TapoClientDefaultExceptionHandler"
-
- protected TapoClientExceptionHandler() {}
-
- public abstract TapoClientExceptionHandling DetermineHandling(TapoDevice device, Exception exception, int attempt, ILogger? logger);
- }
-
- public sealed class TapoSession : IDisposable {
- public DateTime ExpiresOn { get; }
- public bool HasExpired { get; }
- public Uri RequestPathAndQuery { get; }
- public string? SessionId { get; }
- public string? Token { get; }
-
- public void Dispose() {}
- }
-
- public static class TapoSessionCookieUtils {
- public static bool TryGetCookie(HttpResponseMessage response, out string? sessionId, out int? sessionTimeout) {}
- public static bool TryGetCookie(IEnumerable<string>? cookieValues, out string? sessionId, out int? sessionTimeout) {}
- public static bool TryParseCookie(ReadOnlySpan<char> cookie, out string? id, out int? timeout) {}
- }
-
- public readonly struct GetDeviceInfoRequest : ITapoPassThroughRequest {
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- }
-
- public readonly struct GetDeviceInfoResponse : ITapoPassThroughResponse {
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public TapoDeviceInfo Result { get; init; }
- }
-
- public readonly struct HandshakeRequest : ITapoRequest {
- public readonly struct RequestParameters : IEquatable<RequestParameters> {
- [CompilerGenerated]
- public static bool operator == (HandshakeRequest.RequestParameters left, HandshakeRequest.RequestParameters right) {}
- [CompilerGenerated]
- public static bool operator != (HandshakeRequest.RequestParameters left, HandshakeRequest.RequestParameters right) {}
-
- public RequestParameters(string Key) {}
-
- [JsonPropertyName("key")]
- public string Key { get; init; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
-
- [CompilerGenerated]
- public void Deconstruct(out string Key) {}
- [CompilerGenerated]
- public bool Equals(HandshakeRequest.RequestParameters other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public HandshakeRequest(string key) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public HandshakeRequest.RequestParameters Parameters { get; }
- }
-
- public readonly struct HandshakeResponse : ITapoResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (HandshakeResponse.ResponseResult left, HandshakeResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (HandshakeResponse.ResponseResult left, HandshakeResponse.ResponseResult right) {}
-
- public ResponseResult(string? Key) {}
-
- [JsonPropertyName("key")]
- public string? Key { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out string? Key) {}
- [CompilerGenerated]
- public bool Equals(HandshakeResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public HandshakeResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct LoginDeviceRequest : ITapoPassThroughRequest {
- public LoginDeviceRequest(ITapoCredentialProvider credential) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public ITapoCredentialProvider Parameters { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- }
-
- public readonly struct LoginDeviceResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (LoginDeviceResponse.ResponseResult left, LoginDeviceResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (LoginDeviceResponse.ResponseResult left, LoginDeviceResponse.ResponseResult right) {}
-
- public ResponseResult(string Token) {}
-
- [JsonPropertyName("token")]
- public string Token { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out string Token) {}
- [CompilerGenerated]
- public bool Equals(LoginDeviceResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public LoginDeviceResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct SecurePassThroughRequest<TPassThroughRequest> : ITapoRequest where TPassThroughRequest : ITapoPassThroughRequest {
- public readonly struct RequestParams : IEquatable<RequestParams> where TPassThroughRequest : ITapoPassThroughRequest {
- [CompilerGenerated]
- public static bool operator == (SecurePassThroughRequest<TPassThroughRequest>.RequestParams left, SecurePassThroughRequest<TPassThroughRequest>.RequestParams right) {}
- [CompilerGenerated]
- public static bool operator != (SecurePassThroughRequest<TPassThroughRequest>.RequestParams left, SecurePassThroughRequest<TPassThroughRequest>.RequestParams right) {}
-
- public RequestParams(TPassThroughRequest PassThroughRequest) {}
-
- [JsonPropertyName("request")]
- public TPassThroughRequest PassThroughRequest { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out TPassThroughRequest PassThroughRequest) {}
- [CompilerGenerated]
- public bool Equals(SecurePassThroughRequest<TPassThroughRequest>.RequestParams other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public SecurePassThroughRequest(TPassThroughRequest passThroughRequest) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public SecurePassThroughRequest<TPassThroughRequest>.RequestParams Params { get; }
- }
-
- public readonly struct SecurePassThroughResponse<TPassThroughResponse> : ITapoResponse where TPassThroughResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> where TPassThroughResponse : ITapoPassThroughResponse {
- [CompilerGenerated]
- public static bool operator == (SecurePassThroughResponse<TPassThroughResponse>.ResponseResult left, SecurePassThroughResponse<TPassThroughResponse>.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (SecurePassThroughResponse<TPassThroughResponse>.ResponseResult left, SecurePassThroughResponse<TPassThroughResponse>.ResponseResult right) {}
-
- public ResponseResult(TPassThroughResponse PassThroughResponse) {}
-
- [JsonPropertyName("response")]
- public TPassThroughResponse PassThroughResponse { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out TPassThroughResponse PassThroughResponse) {}
- [CompilerGenerated]
- public bool Equals(SecurePassThroughResponse<TPassThroughResponse>.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public SecurePassThroughResponse(ErrorCode errorCode, TPassThroughResponse passThroughResponse) {}
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public SecurePassThroughResponse<TPassThroughResponse>.ResponseResult Result { get; init; }
- }
-
- public readonly struct SetDeviceInfoRequest<TParameters> : ITapoPassThroughRequest {
- public SetDeviceInfoRequest(string terminalUuid, TParameters parameters) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public TParameters Parameters { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- [JsonPropertyName("terminalUUID")]
- public string TerminalUuid { get; }
- }
-
- public readonly struct SetDeviceInfoResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (SetDeviceInfoResponse.ResponseResult left, SetDeviceInfoResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (SetDeviceInfoResponse.ResponseResult left, SetDeviceInfoResponse.ResponseResult right) {}
-
- public ResponseResult(IDictionary<string, object>? ExtraData) {}
-
- [JsonExtensionData]
- public IDictionary<string, object>? ExtraData { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out IDictionary<string, object>? ExtraData) {}
- [CompilerGenerated]
- public bool Equals(SetDeviceInfoResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public SetDeviceInfoResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct TapoClientExceptionHandling {
- public static readonly TapoClientExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
- public static readonly TapoClientExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
- public static readonly TapoClientExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling RetryAfterReconnect; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=True, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling ThrowAsTapoProtocolException; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=True, ShouldInvalidateEndPoint=False}"
-
- public static TapoClientExceptionHandling 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 bool ShouldWrapIntoTapoProtocolException { get; init; }
-
- public override string ToString() {}
- }
-}
-// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.2.0.
-// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-netstandard2.1.apilist.cs b/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-netstandard2.1.apilist.cs
deleted file mode 100644
index aaed417..0000000
--- a/doc/api-list/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices-netstandard2.1.apilist.cs
+++ /dev/null
@@ -1,789 +0,0 @@
-// Smdn.TPSmartHomeDevices.dll (Smdn.TPSmartHomeDevices-1.0.0-preview3)
-// Name: Smdn.TPSmartHomeDevices
-// AssemblyVersion: 1.0.0.0
-// InformationalVersion: 1.0.0-preview3+ae327055c2651d84fffc372b481a72257af2a9d8
-// TargetFramework: .NETStandard,Version=v2.1
-// Configuration: Release
-// Referenced assemblies:
-// Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
-// Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
-// Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
-// Smdn.Fundamental.PrintableEncoding.Hexadecimal, Version=3.0.1.0, Culture=neutral
-// Smdn.Net.AddressResolution, Version=1.0.0.0, Culture=neutral
-// System.Net.Http.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// System.Text.Encodings.Web, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// System.Text.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-// netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
-#nullable enable annotations
-
-using System;
-using System.Buffers;
-using System.Collections.Generic;
-using System.Net;
-using System.Net.Http;
-using System.Net.NetworkInformation;
-using System.Security.Cryptography;
-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.Net.AddressResolution;
-using Smdn.TPSmartHomeDevices;
-using Smdn.TPSmartHomeDevices.Kasa;
-using Smdn.TPSmartHomeDevices.Kasa.Protocol;
-using Smdn.TPSmartHomeDevices.Tapo;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-using Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-namespace Smdn.TPSmartHomeDevices {
- public interface IDeviceEndPointFactory<TAddress> {
- IDeviceEndPointProvider Create(TAddress address, int port = 0);
- }
-
- public interface IDeviceEndPointProvider {
- ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken = default);
- }
-
- public interface IDynamicDeviceEndPointProvider : IDeviceEndPointProvider {
- void InvalidateEndPoint();
- }
-
- public static class DeviceEndPointFactoryServiceCollectionExtensions {
- public static IServiceCollection AddDeviceEndPointFactory<TAddress>(this IServiceCollection services, IDeviceEndPointFactory<TAddress> endPointFactory) {}
- }
-
- public class DeviceEndPointResolutionException : Exception {
- public DeviceEndPointResolutionException(IDeviceEndPointProvider deviceEndPointProvider) {}
- public DeviceEndPointResolutionException(IDeviceEndPointProvider deviceEndPointProvider, string message, Exception? innerException) {}
-
- public IDeviceEndPointProvider EndPointProvider { get; }
- }
-
- public class MacAddressDeviceEndPointFactory :
- IDeviceEndPointFactory<PhysicalAddress>,
- IDisposable
- {
- protected class MacAddressDeviceEndPointProvider : IDynamicDeviceEndPointProvider {
- public MacAddressDeviceEndPointProvider(IAddressResolver<PhysicalAddress, IPAddress> resolver, PhysicalAddress address, int port) {}
-
- public async ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken) {}
- public void InvalidateEndPoint() {}
- public override string ToString() {}
- }
-
- protected MacAddressDeviceEndPointFactory(IAddressResolver<PhysicalAddress, IPAddress> resolver, IServiceProvider? serviceProvider = null) {}
- public MacAddressDeviceEndPointFactory(MacAddressResolver resolver, IServiceProvider? serviceProvider = null) {}
- public MacAddressDeviceEndPointFactory(MacAddressResolverOptions? options = null, IServiceProvider? serviceProvider = null) {}
-
- public virtual IDeviceEndPointProvider Create(PhysicalAddress address, int port = 0) {}
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- protected void ThrowIfDisposed() {}
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Kasa {
- public class HS105 : KasaDevice {
- public HS105(IDeviceEndPointProvider deviceEndPointProvider, 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) {}
- public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
- }
-
- public class KL130 : KasaDevice {
- public KL130(IDeviceEndPointProvider deviceEndPointProvider, 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 SetColorAsync(int hue, int saturation, int? brightness = null, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorTemperatureAsync(int colorTemperature, int? brightness = null, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetOnOffStateAsync(bool newOnOffState, TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOffAsync(TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(TimeSpan? transitionPeriod = null, CancellationToken cancellationToken = default) {}
- }
-
- public class KasaDevice : IDisposable {
- protected readonly struct NullParameter {
- }
-
- protected static readonly JsonEncodedText MethodTextGetSysInfo;
- protected static readonly JsonEncodedText ModuleTextSystem;
-
- public static KasaDevice Create(IDeviceEndPointProvider deviceEndPointProvider, 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) {}
-
- protected KasaDevice(IDeviceEndPointProvider deviceEndPointProvider, IServiceProvider? serviceProvider = null) {}
- protected KasaDevice(IPAddress ipAddress, IServiceProvider? serviceProvider = null) {}
- protected KasaDevice(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- protected KasaDevice(string host, IServiceProvider? serviceProvider = null) {}
-
- public bool IsConnected { get; }
- protected bool IsDisposed { get; }
-
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- 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 static class KasaDeviceEndPointProvider {
- public static IDeviceEndPointProvider Create(IPAddress ipAddress) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IDeviceEndPointFactory<PhysicalAddress> endPointFactory) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static IDeviceEndPointProvider Create(string host) {}
- }
-
- public class KasaDisconnectedException : KasaProtocolException {
- public KasaDisconnectedException(string message, EndPoint deviceEndPoint, Exception? innerException) {}
- }
-
- public class KasaErrorResponseException : KasaUnexpectedResponseException {
- public KasaErrorResponseException(EndPoint deviceEndPoint, string requestModule, string requestMethod, ErrorCode errorCode) {}
-
- public ErrorCode ErrorCode { 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 {
- [JsonPropertyName("brightness")]
- public int? Brightness { get; init; }
- [JsonPropertyName("color_temp")]
- public int? ColorTemperature { get; init; }
- [JsonPropertyName("hue")]
- public int? Hue { get; init; }
- [JsonConverter(typeof(KasaNumericalBooleanJsonConverter))]
- [JsonPropertyName("on_off")]
- public bool IsOn { get; init; }
- [JsonPropertyName("mode")]
- public string? Mode { get; init; }
- [JsonPropertyName("saturation")]
- public int? Saturation { get; init; }
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol {
- public enum ErrorCode : int {
- Success = 0,
- }
-
- 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 abstract class KasaClientExceptionHandler {
- internal protected static readonly KasaClientExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Kasa.Protocol.KasaClientDefaultExceptionHandler"
-
- protected KasaClientExceptionHandler() {}
-
- public abstract KasaClientExceptionHandling DetermineHandling(KasaDevice device, Exception exception, int attempt, ILogger? logger);
- }
-
- 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) {}
- }
-
- public readonly struct KasaClientExceptionHandling {
- public static readonly KasaClientExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=True}"
- public static readonly KasaClientExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=True}"
- public static readonly KasaClientExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=False}"
- public static readonly KasaClientExceptionHandling RetryAfterReconnect; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=True, ShouldInvalidateEndPoint=False}"
- public static readonly KasaClientExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldInvalidateEndPoint=False}"
-
- public static KasaClientExceptionHandling 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.Tapo {
- public class L530 : TapoDevice {
- public L530(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public L530(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public L530(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public L530(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public L530(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public L530(string host, IServiceProvider serviceProvider) {}
- public L530(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- public ValueTask SetBrightnessAsync(int brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorAsync(int hue, int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorHueAsync(int hue, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorSaturationAsync(int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorTemperatureAsync(int colorTemperature, int? brightness = null, CancellationToken cancellationToken = default) {}
- }
-
- public class L900 : TapoDevice {
- public L900(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public L900(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public L900(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public L900(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public L900(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public L900(string host, IServiceProvider serviceProvider) {}
- public L900(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- public ValueTask SetBrightnessAsync(int brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorAsync(int hue, int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorHueAsync(int hue, int? brightness, CancellationToken cancellationToken = default) {}
- public ValueTask SetColorSaturationAsync(int saturation, int? brightness = null, CancellationToken cancellationToken = default) {}
- }
-
- public class P105 : TapoDevice {
- public P105(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public P105(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public P105(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public P105(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public P105(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public P105(string host, IServiceProvider serviceProvider) {}
- public P105(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
- }
-
- public class TapoAuthenticationException : TapoProtocolException {
- public TapoAuthenticationException(string message, Uri endPoint, Exception? innerException = null) {}
- }
-
- public static class TapoCredentailProviderServiceCollectionExtensions {
- public static IServiceCollection AddTapoBase64EncodedCredential(this IServiceCollection services, string base64UserNameSHA1Digest, string base64Password) {}
- public static IServiceCollection AddTapoCredential(this IServiceCollection services, string email, string password) {}
- public static IServiceCollection AddTapoCredentialProvider(this IServiceCollection services, ITapoCredentialProvider credentialProvider) {}
- }
-
- public class TapoDevice :
- IDisposable,
- ITapoCredentialIdentity
- {
- public static TapoDevice Create(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, IServiceProvider? serviceProvider = null) {}
- public static TapoDevice Create(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- public static TapoDevice Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(string host, IServiceProvider serviceProvider) {}
- public static TapoDevice Create(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- protected TapoDevice(IDeviceEndPointProvider deviceEndPointProvider, ITapoCredentialProvider? credential = null, TapoClientExceptionHandler? exceptionHandler = null, IServiceProvider? serviceProvider = null) {}
- protected TapoDevice(IPAddress ipAddress, IServiceProvider serviceProvider) {}
- protected TapoDevice(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider = null) {}
- protected TapoDevice(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- protected TapoDevice(PhysicalAddress macAddress, string email, string password, IServiceProvider serviceProvider) {}
- protected TapoDevice(string host, IServiceProvider serviceProvider) {}
- protected TapoDevice(string host, string email, string password, IServiceProvider? serviceProvider = null) {}
-
- protected bool IsDisposed { get; }
- public TapoSession? Session { get; }
- string ITapoCredentialIdentity.Name { get; }
- public string TerminalUuidString { get; }
- public TimeSpan? Timeout { get; set; }
-
- protected virtual void Dispose(bool disposing) {}
- public void Dispose() {}
- protected ValueTask EnsureSessionEstablishedAsync(CancellationToken cancellationToken = default) {}
- public ValueTask<TapoDeviceInfo> GetDeviceInfoAsync(CancellationToken cancellationToken = default) {}
- public ValueTask<EndPoint> ResolveEndPointAsync(CancellationToken cancellationToken = default) {}
- protected ValueTask SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- protected ValueTask<TResult> SendRequestAsync<TRequest, TResponse, TResult>(TRequest request, Func<TResponse, TResult> composeResult, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- public ValueTask SetDeviceInfoAsync<TParameters>(TParameters parameters, CancellationToken cancellationToken = default) {}
- public ValueTask SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken = default) {}
- public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
- public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
- }
-
- public static class TapoDeviceEndPointProvider {
- public static IDeviceEndPointProvider Create(IPAddress ipAddress) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IDeviceEndPointFactory<PhysicalAddress> endPointFactory) {}
- public static IDeviceEndPointProvider Create(PhysicalAddress macAddress, IServiceProvider serviceProvider) {}
- public static IDeviceEndPointProvider Create(string host) {}
- }
-
- public class TapoDeviceInfo {
- public TapoDeviceInfo() {}
-
- [JsonPropertyName("avatar")]
- public string? Avatar { get; init; }
- [JsonPropertyName("fw_id")]
- public string? FirmwareId { get; init; }
- [JsonPropertyName("fw_ver")]
- public string? FirmwareVersion { get; init; }
- [JsonConverter(typeof(GeolocationInDecimalDegreesJsonConverter))]
- [JsonPropertyName("latitude")]
- public decimal? GeolocationLatitude { get; init; }
- [JsonConverter(typeof(GeolocationInDecimalDegreesJsonConverter))]
- [JsonPropertyName("longitude")]
- public decimal? GeolocationLongitude { get; init; }
- [JsonPropertyName("hw_id")]
- public string? HardwareId { get; init; }
- [JsonPropertyName("specs")]
- public string? HardwareSpecifications { get; init; }
- [JsonPropertyName("hw_ver")]
- public string? HardwareVersion { get; init; }
- [JsonPropertyName("has_set_location_info")]
- public bool HasGeolocationInfoSet { get; init; }
- [JsonConverter(typeof(TapoIPAddressJsonConverter))]
- [JsonPropertyName("ip")]
- public IPAddress? IPAddress { get; init; }
- [JsonPropertyName("device_id")]
- public string? Id { get; init; }
- [JsonPropertyName("device_on")]
- public bool IsOn { get; init; }
- [JsonPropertyName("overheated")]
- public bool IsOverheated { get; init; }
- [JsonPropertyName("lang")]
- public string? Language { get; init; }
- [JsonConverter(typeof(MacAddressJsonConverter))]
- [JsonPropertyName("mac")]
- public PhysicalAddress? MacAddress { get; init; }
- [JsonPropertyName("model")]
- public string? ModelName { get; init; }
- [JsonPropertyName("rssi")]
- public decimal? NetworkRssi { get; init; }
- [JsonPropertyName("signal_level")]
- public int? NetworkSignalLevel { get; init; }
- [JsonConverter(typeof(TapoBase64StringJsonConverter))]
- [JsonPropertyName("ssid")]
- public string? NetworkSsid { get; init; }
- [JsonConverter(typeof(TapoBase64StringJsonConverter))]
- [JsonPropertyName("nickname")]
- public string? NickName { get; init; }
- [JsonPropertyName("oem_id")]
- public string? OemId { get; init; }
- [JsonConverter(typeof(TimeSpanInSecondsJsonConverter))]
- [JsonPropertyName("on_time")]
- public TimeSpan? OnTimeDuration { get; init; }
- [JsonIgnore]
- public DateTimeOffset TimeStamp { get; }
- [JsonConverter(typeof(TimeSpanInMinutesJsonConverter))]
- [JsonPropertyName("time_diff")]
- public TimeSpan? TimeZoneOffset { get; init; }
- [JsonPropertyName("region")]
- public string? TimeZoneRegion { get; init; }
- [JsonPropertyName("type")]
- public string? TypeName { get; init; }
- }
-
- public class TapoErrorResponseException : TapoProtocolException {
- public TapoErrorResponseException(Uri requestEndPoint, string requestMethod, ErrorCode errorCode) {}
-
- public ErrorCode ErrorCode { get; }
- public string RequestMethod { get; }
- }
-
- public static class TapoHttpClientFactoryServiceCollectionExtensions {
- public static IServiceCollection AddTapoHttpClient(this IServiceCollection services, Action<HttpClient>? configureClient = null) {}
- }
-
- public class TapoProtocolException : InvalidOperationException {
- internal protected TapoProtocolException(string message, Uri endPoint, Exception? innerException) {}
-
- public Uri EndPoint { get; }
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Credentials {
- public interface ITapoCredential : IDisposable {
- [...] <unknown> WritePasswordPropertyValue(...);
- [...] <unknown> WriteUsernamePropertyValue(...);
- }
-
- public interface ITapoCredentialIdentity {
- string Name { get; }
- }
-
- public interface ITapoCredentialProvider {
- ITapoCredential GetCredential(ITapoCredentialIdentity? identity);
- }
-
- public static class TapoCredentialUtils {
- public const int HexSHA1HashSizeInBytes = 40;
-
- public static string ToBase64EncodedSHA1DigestString(ReadOnlySpan<char> str) {}
- public static string ToBase64EncodedString(ReadOnlySpan<char> str) {}
- public static bool TryConvertToHexSHA1Hash(ReadOnlySpan<byte> input, Span<byte> destination, out int bytesWritten) {}
- }
-}
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol {
- public interface ITapoPassThroughRequest : ITapoRequest {
- }
-
- public interface ITapoPassThroughResponse : ITapoResponse {
- }
-
- public interface ITapoRequest {
- string Method { get; }
- }
-
- public interface ITapoResponse {
- ErrorCode ErrorCode { get; init; }
- }
-
- public enum ErrorCode : int {
- Success = 0,
- }
-
- public class SecurePassThroughInvalidPaddingException : SystemException {
- public SecurePassThroughInvalidPaddingException(string message, Exception? innerException) {}
- }
-
- public sealed class SecurePassThroughJsonConverterFactory :
- JsonConverterFactory,
- IDisposable
- {
- public SecurePassThroughJsonConverterFactory(ITapoCredentialIdentity? identity, ICryptoTransform? encryptorForPassThroughRequest, ICryptoTransform? decryptorForPassThroughResponse, JsonSerializerOptions? baseJsonSerializerOptionsForPassThroughMessage, ILogger? logger = null) {}
-
- public override bool CanConvert(Type typeToConvert) {}
- public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) {}
- public void Dispose() {}
- }
-
- public sealed class TapoClient : IDisposable {
- public const int DefaultPort = 80;
-
- public TapoClient(EndPoint endPoint, IHttpClientFactory? httpClientFactory = null, ILogger? logger = null) {}
-
- public Uri EndPointUri { get; }
- public TapoSession? Session { get; }
- public TimeSpan? Timeout { get; set; }
-
- public ValueTask AuthenticateAsync(ITapoCredentialIdentity? identity, ITapoCredentialProvider credential, CancellationToken cancellationToken = default) {}
- public void Dispose() {}
- public ValueTask<TResponse> SendRequestAsync<TRequest, TResponse>(CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest, new() where TResponse : ITapoPassThroughResponse {}
- public ValueTask<TResponse> SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
- }
-
- public abstract class TapoClientExceptionHandler {
- internal protected static readonly TapoClientExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Tapo.Protocol.TapoClientDefaultExceptionHandler"
-
- protected TapoClientExceptionHandler() {}
-
- public abstract TapoClientExceptionHandling DetermineHandling(TapoDevice device, Exception exception, int attempt, ILogger? logger);
- }
-
- public sealed class TapoSession : IDisposable {
- public DateTime ExpiresOn { get; }
- public bool HasExpired { get; }
- public Uri RequestPathAndQuery { get; }
- public string? SessionId { get; }
- public string? Token { get; }
-
- public void Dispose() {}
- }
-
- public static class TapoSessionCookieUtils {
- public static bool TryGetCookie(HttpResponseMessage response, out string? sessionId, out int? sessionTimeout) {}
- public static bool TryGetCookie(IEnumerable<string>? cookieValues, out string? sessionId, out int? sessionTimeout) {}
- public static bool TryParseCookie(ReadOnlySpan<char> cookie, out string? id, out int? timeout) {}
- }
-
- public readonly struct GetDeviceInfoRequest : ITapoPassThroughRequest {
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- }
-
- public readonly struct GetDeviceInfoResponse : ITapoPassThroughResponse {
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public TapoDeviceInfo Result { get; init; }
- }
-
- public readonly struct HandshakeRequest : ITapoRequest {
- public readonly struct RequestParameters : IEquatable<RequestParameters> {
- [CompilerGenerated]
- public static bool operator == (HandshakeRequest.RequestParameters left, HandshakeRequest.RequestParameters right) {}
- [CompilerGenerated]
- public static bool operator != (HandshakeRequest.RequestParameters left, HandshakeRequest.RequestParameters right) {}
-
- public RequestParameters(string Key) {}
-
- [JsonPropertyName("key")]
- public string Key { get; init; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
-
- [CompilerGenerated]
- public void Deconstruct(out string Key) {}
- [CompilerGenerated]
- public bool Equals(HandshakeRequest.RequestParameters other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public HandshakeRequest(string key) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public HandshakeRequest.RequestParameters Parameters { get; }
- }
-
- public readonly struct HandshakeResponse : ITapoResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (HandshakeResponse.ResponseResult left, HandshakeResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (HandshakeResponse.ResponseResult left, HandshakeResponse.ResponseResult right) {}
-
- public ResponseResult(string? Key) {}
-
- [JsonPropertyName("key")]
- public string? Key { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out string? Key) {}
- [CompilerGenerated]
- public bool Equals(HandshakeResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public HandshakeResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct LoginDeviceRequest : ITapoPassThroughRequest {
- public LoginDeviceRequest(ITapoCredentialProvider credential) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public ITapoCredentialProvider Parameters { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- }
-
- public readonly struct LoginDeviceResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (LoginDeviceResponse.ResponseResult left, LoginDeviceResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (LoginDeviceResponse.ResponseResult left, LoginDeviceResponse.ResponseResult right) {}
-
- public ResponseResult(string Token) {}
-
- [JsonPropertyName("token")]
- public string Token { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out string Token) {}
- [CompilerGenerated]
- public bool Equals(LoginDeviceResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public LoginDeviceResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct SecurePassThroughRequest<TPassThroughRequest> : ITapoRequest where TPassThroughRequest : ITapoPassThroughRequest {
- public readonly struct RequestParams : IEquatable<RequestParams> where TPassThroughRequest : ITapoPassThroughRequest {
- [CompilerGenerated]
- public static bool operator == (SecurePassThroughRequest<TPassThroughRequest>.RequestParams left, SecurePassThroughRequest<TPassThroughRequest>.RequestParams right) {}
- [CompilerGenerated]
- public static bool operator != (SecurePassThroughRequest<TPassThroughRequest>.RequestParams left, SecurePassThroughRequest<TPassThroughRequest>.RequestParams right) {}
-
- public RequestParams(TPassThroughRequest PassThroughRequest) {}
-
- [JsonPropertyName("request")]
- public TPassThroughRequest PassThroughRequest { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out TPassThroughRequest PassThroughRequest) {}
- [CompilerGenerated]
- public bool Equals(SecurePassThroughRequest<TPassThroughRequest>.RequestParams other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public SecurePassThroughRequest(TPassThroughRequest passThroughRequest) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public SecurePassThroughRequest<TPassThroughRequest>.RequestParams Params { get; }
- }
-
- public readonly struct SecurePassThroughResponse<TPassThroughResponse> : ITapoResponse where TPassThroughResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> where TPassThroughResponse : ITapoPassThroughResponse {
- [CompilerGenerated]
- public static bool operator == (SecurePassThroughResponse<TPassThroughResponse>.ResponseResult left, SecurePassThroughResponse<TPassThroughResponse>.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (SecurePassThroughResponse<TPassThroughResponse>.ResponseResult left, SecurePassThroughResponse<TPassThroughResponse>.ResponseResult right) {}
-
- public ResponseResult(TPassThroughResponse PassThroughResponse) {}
-
- [JsonPropertyName("response")]
- public TPassThroughResponse PassThroughResponse { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out TPassThroughResponse PassThroughResponse) {}
- [CompilerGenerated]
- public bool Equals(SecurePassThroughResponse<TPassThroughResponse>.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- public SecurePassThroughResponse(ErrorCode errorCode, TPassThroughResponse passThroughResponse) {}
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public SecurePassThroughResponse<TPassThroughResponse>.ResponseResult Result { get; init; }
- }
-
- public readonly struct SetDeviceInfoRequest<TParameters> : ITapoPassThroughRequest {
- public SetDeviceInfoRequest(string terminalUuid, TParameters parameters) {}
-
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method { get; }
- [JsonPropertyName("params")]
- public TParameters Parameters { get; }
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds { get; }
- [JsonPropertyName("terminalUUID")]
- public string TerminalUuid { get; }
- }
-
- public readonly struct SetDeviceInfoResponse : ITapoPassThroughResponse {
- public readonly struct ResponseResult : IEquatable<ResponseResult> {
- [CompilerGenerated]
- public static bool operator == (SetDeviceInfoResponse.ResponseResult left, SetDeviceInfoResponse.ResponseResult right) {}
- [CompilerGenerated]
- public static bool operator != (SetDeviceInfoResponse.ResponseResult left, SetDeviceInfoResponse.ResponseResult right) {}
-
- public ResponseResult(IDictionary<string, object>? ExtraData) {}
-
- [JsonExtensionData]
- public IDictionary<string, object>? ExtraData { get; init; }
-
- [CompilerGenerated]
- public void Deconstruct(out IDictionary<string, object>? ExtraData) {}
- [CompilerGenerated]
- public bool Equals(SetDeviceInfoResponse.ResponseResult other) {}
- [CompilerGenerated]
- public override bool Equals(object obj) {}
- [CompilerGenerated]
- public override int GetHashCode() {}
- [CompilerGenerated]
- public override string ToString() {}
- }
-
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
- [JsonPropertyName("result")]
- public SetDeviceInfoResponse.ResponseResult Result { get; init; }
- }
-
- public readonly struct TapoClientExceptionHandling {
- public static readonly TapoClientExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
- public static readonly TapoClientExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
- public static readonly TapoClientExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling RetryAfterReconnect; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReconnect=True, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
- public static readonly TapoClientExceptionHandling ThrowAsTapoProtocolException; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReconnect=False, ShouldWrapIntoTapoProtocolException=True, ShouldInvalidateEndPoint=False}"
-
- public static TapoClientExceptionHandling 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 bool ShouldWrapIntoTapoProtocolException { get; init; }
-
- public override string ToString() {}
- }
-}
-// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.2.0.
-// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
--- Smdn.TPSmartHomeDevices.latest.nuspec
+++ Smdn.TPSmartHomeDevices.1.0.0-rc1.nuspec
@@ -1,41 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
-<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
+<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Smdn.TPSmartHomeDevices</id>
- <version>1.0.0-preview3</version>
+ <version>1.0.0-rc1</version>
<title>Smdn.TPSmartHomeDevices</title>
<authors>smdn</authors>
<license type="expression">MIT</license>
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
<icon>Smdn.TPSmartHomeDevices.png</icon>
<readme>README.md</readme>
<projectUrl>https://github.com/smdn/Smdn.TPSmartHomeDevices/</projectUrl>
- <description>.NET implementations for Kasa and Tapo, the TP-Link smart home devices.</description>
+ <description>A meta package that addes the dependency of Smdn.TPSmartHomeDevices.Kasa, Smdn.TPSmartHomeDevices.Tapo and Smdn.TPSmartHomeDevices.MacAddressEndPoint. This package is deprecated, use the individual packages instead.</description>
<copyright>Copyright © 2022 smdn</copyright>
- <tags>smdn.jp tplink-kasa,tplink-tapo,smarthome,homeautomation,smartdevice</tags>
- <repository type="git" url="https://github.com/smdn/Smdn.TPSmartHomeDevices" branch="main" commit="ae327055c2651d84fffc372b481a72257af2a9d8" />
+ <tags>smdn.jp metapackage,tplink-kasa,tplink-tapo,smarthome,homeautomation,smartdevice</tags>
+ <repository type="git" url="https://github.com/smdn/Smdn.TPSmartHomeDevices" commit="18af508b38d77fa369978c80b2088c6b506a74e7" />
<dependencies>
<group targetFramework="net6.0">
- <dependency id="Microsoft.Extensions.DependencyInjection" version="6.0.0" exclude="Build,Analyzers" />
- <dependency id="Microsoft.Extensions.Http" version="6.0.0" exclude="Build,Analyzers" />
- <dependency id="Smdn.Fundamental.PrintableEncoding.Hexadecimal" version="3.0.1" exclude="Build,Analyzers" />
- <dependency id="Smdn.Net.AddressResolution" version="1.0.0-preview5" exclude="Build,Analyzers" />
- <dependency id="System.Net.Http.Json" version="6.0.0" exclude="Build,Analyzers" />
- </group>
- <group targetFramework="net7.0">
- <dependency id="Microsoft.Extensions.DependencyInjection" version="6.0.0" exclude="Build,Analyzers" />
- <dependency id="Microsoft.Extensions.Http" version="6.0.0" exclude="Build,Analyzers" />
- <dependency id="Smdn.Fundamental.PrintableEncoding.Hexadecimal" version="3.0.1" exclude="Build,Analyzers" />
- <dependency id="Smdn.Net.AddressResolution" version="1.0.0-preview5" exclude="Build,Analyzers" />
- <dependency id="System.Net.Http.Json" version="6.0.0" exclude="Build,Analyzers" />
+ <dependency id="Smdn.TPSmartHomeDevices.Kasa" version="[1.0.0-rc1, 1.0.0)" exclude="Build,Analyzers" />
+ <dependency id="Smdn.TPSmartHomeDevices.MacAddressEndPoint" version="[1.0.0-rc1, 1.0.0)" exclude="Build,Analyzers" />
+ <dependency id="Smdn.TPSmartHomeDevices.Tapo" version="[1.0.0-rc1, 1.0.0)" exclude="Build,Analyzers" />
</group>
<group targetFramework=".NETStandard2.1">
- <dependency id="Microsoft.Extensions.DependencyInjection" version="6.0.0" exclude="Build,Analyzers" />
- <dependency id="Microsoft.Extensions.Http" version="6.0.0" exclude="Build,Analyzers" />
- <dependency id="Smdn.Fundamental.PrintableEncoding.Hexadecimal" version="3.0.1" exclude="Build,Analyzers" />
- <dependency id="Smdn.Net.AddressResolution" version="1.0.0-preview5" exclude="Build,Analyzers" />
- <dependency id="System.Net.Http.Json" version="6.0.0" exclude="Build,Analyzers" />
+ <dependency id="Smdn.TPSmartHomeDevices.Kasa" version="[1.0.0-rc1, 1.0.0)" exclude="Build,Analyzers" />
+ <dependency id="Smdn.TPSmartHomeDevices.MacAddressEndPoint" version="[1.0.0-rc1, 1.0.0)" exclude="Build,Analyzers" />
+ <dependency id="Smdn.TPSmartHomeDevices.Tapo" version="[1.0.0-rc1, 1.0.0)" exclude="Build,Analyzers" />
</group>
</dependencies>
</metadata>
</package>
\ No newline at end of file
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/GeolocationInDecimalDegreesJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/GeolocationInDecimalDegreesJsonConverter.cs
deleted file mode 100644
index 7445a17..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/GeolocationInDecimalDegreesJsonConverter.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Json;
-
-internal sealed class GeolocationInDecimalDegreesJsonConverter : JsonConverter<decimal?> {
- private const decimal Scaler = 10000m;
-
- public override decimal? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => reader.TryGetDecimal(out var scaledDecimalDegrees)
- ? scaledDecimalDegrees / Scaler
- : null;
-
- public override void Write(
- Utf8JsonWriter writer,
- decimal? value,
- JsonSerializerOptions options
- )
- => throw new NotImplementedException();
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/MacAddressJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/MacAddressJsonConverter.cs
deleted file mode 100644
index 989c75a..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/MacAddressJsonConverter.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net.NetworkInformation;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Json;
-
-internal sealed class MacAddressJsonConverter : JsonConverter<PhysicalAddress> {
- public override PhysicalAddress? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- {
- var str = reader.GetString();
-
- if (str is null)
- return null;
-
-#if SYSTEM_NET_NETWORKINFORMATION_PHYSICALADDRESS_TRYPARSE
- return PhysicalAddress.TryParse(str, out var ret)
- ? ret
- : null;
-#else
- try {
- return PhysicalAddress.Parse(str);
- }
- catch (FormatException) {
- return null;
- }
-#endif
- }
-
- public override void Write(
- Utf8JsonWriter writer,
- PhysicalAddress value,
- JsonSerializerOptions options
- )
- => throw new NotImplementedException();
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/TimeSpanInMinutesJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/TimeSpanInMinutesJsonConverter.cs
deleted file mode 100644
index d8cf379..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/TimeSpanInMinutesJsonConverter.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Json;
-
-internal sealed class TimeSpanInMinutesJsonConverter : JsonConverter<TimeSpan?> {
- public override TimeSpan? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => reader.TryGetInt32(out var timeDiff)
- ? TimeSpan.FromMinutes(timeDiff)
- : null;
-
- public override void Write(
- Utf8JsonWriter writer,
- TimeSpan? value,
- JsonSerializerOptions options
- )
- => throw new NotImplementedException();
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/TimeSpanInSecondsJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/TimeSpanInSecondsJsonConverter.cs
deleted file mode 100644
index 35f6793..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Json/TimeSpanInSecondsJsonConverter.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Json;
-
-internal sealed class TimeSpanInSecondsJsonConverter : JsonConverter<TimeSpan?> {
- public override TimeSpan? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => reader.TryGetInt32(out var onTime)
- ? TimeSpan.FromSeconds(onTime)
- : null;
-
- public override void Write(
- Utf8JsonWriter writer,
- TimeSpan? value,
- JsonSerializerOptions options
- )
- => throw new NotImplementedException();
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Json/KasaNumericalBooleanJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Json/KasaNumericalBooleanJsonConverter.cs
deleted file mode 100644
index c53f246..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Json/KasaNumericalBooleanJsonConverter.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Json;
-
-internal sealed class KasaNumericalBooleanJsonConverter : JsonConverter<bool> {
- public override bool Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => reader.TryGetInt32(out var numericalBool) && numericalBool != 0;
-
- public override void Write(
- Utf8JsonWriter writer,
- bool value,
- JsonSerializerOptions options
- )
- => writer.WriteNumberValue(value ? 1 : 0);
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/ErrorCode.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/ErrorCode.cs
deleted file mode 100644
index 66b11c5..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/ErrorCode.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-public enum ErrorCode : int {
- Success = 0,
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClient.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClient.cs
deleted file mode 100644
index e6c7476..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClient.cs
+++ /dev/null
@@ -1,314 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Buffers;
-using System.Net;
-using System.Net.Sockets;
-using System.Text;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-/// <remarks>
-/// This implementation is based on and ported from the following implementation: <see href="https://github.com/plasticrake/tplink-smarthome-api">plasticrake/tplink-smarthome-api</see>.
-/// </remarks>
-public sealed partial class KasaClient : IDisposable {
- public const int DefaultPort = 9999;
- internal const int DefaultBufferCapacity = 1536; // 1.5 [kB]
- private static readonly JsonEncodedText PropertyNameForErrorCode = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "err_code"u8
-#else
- "err_code"
-#endif
- );
-
- private bool IsDisposed => endPoint is null;
-
- private EndPoint? endPoint; // if null, it indicates a 'disposed' state.
- private Socket? socket;
- private readonly ILogger? logger;
- private readonly ArrayBufferWriter<byte> buffer;
-
- public bool IsConnected {
- get {
- ThrowIfDisposed();
- return socket is not null && socket.Connected;
- }
- }
-
- public EndPoint EndPoint {
- get {
- ThrowIfDisposed();
- return endPoint;
- }
- }
-
- internal ILogger? Logger {
- get {
- ThrowIfDisposed();
- return logger;
- }
- }
-
- public KasaClient(
- EndPoint endPoint,
- ILogger? logger = null
- )
- : this(
- endPoint: endPoint ?? throw new ArgumentNullException(nameof(endPoint)),
- buffer: new(initialCapacity: DefaultBufferCapacity),
- logger: logger
- )
- {
- }
-
- internal KasaClient(
- EndPoint endPoint,
- ArrayBufferWriter<byte> buffer,
- ILogger? logger
- )
- {
- this.endPoint = endPoint switch {
- null => throw new ArgumentNullException(nameof(endPoint)),
- DnsEndPoint dnsEndPoint => dnsEndPoint.Port == 0
- ? new DnsEndPoint(
- host: dnsEndPoint.Host,
- port: DefaultPort
- )
- : dnsEndPoint,
- IPEndPoint ipEndPoint => ipEndPoint.Port == 0
- ? new IPEndPoint(
- address: ipEndPoint.Address,
- port: DefaultPort
- )
- : ipEndPoint,
- EndPoint ep => ep, // TODO: should throw exception?
- };
-
- this.buffer = buffer;
- this.logger = logger;
- this.logger?.LogTrace("Device end point: {DeviceEndPoint} ({DeviceEndPointAddressFamily})", endPoint, endPoint.AddressFamily);
- }
-
- private void Dispose(bool disposing)
- {
- if (!disposing)
- return;
-
- endPoint = null;
-
- socket?.Dispose();
- socket = null;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void ThrowIfDisposed()
- {
- if (IsDisposed)
- throw new ObjectDisposedException(GetType().FullName);
- }
-
- private async ValueTask<Socket> ConnectAsync(
- CancellationToken cancellationToken
- )
- {
- var s = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-
- try {
- logger?.LogDebug("Connecting");
-
- await s.ConnectAsync(
- remoteEP: endPoint
- // TODO: cancellationToken
- ).ConfigureAwait(false);
-
- logger?.LogDebug("Connected");
-
- return s;
- }
- catch (Exception ex) {
- s.Dispose();
-
- if (ex is SocketException exSocket)
- logger?.LogError(ex, "Connection failed (NativeErrorCode = {NativeErrorCode})", exSocket.NativeErrorCode);
- else
- logger?.LogCritical(ex, "Connection failed with unexpected exception");
- throw;
- }
- }
-
- public ValueTask<TMethodResult> SendAsync<TMethodParameter, TMethodResult>(
- JsonEncodedText module,
- JsonEncodedText method,
- TMethodParameter parameter,
- Func<JsonElement, TMethodResult> composeResult,
- CancellationToken cancellationToken = default
- )
- {
- if (parameter is null)
- throw new ArgumentNullException(nameof(parameter));
- if (composeResult is null)
- throw new ArgumentNullException(nameof(composeResult));
-
- ThrowIfDisposed();
-
- return SendAsyncCore(
- module: module,
- method: method,
- parameter: parameter,
- composeResult: composeResult,
- cancellationToken: cancellationToken
- );
- }
-
- private async ValueTask<TMethodResult> SendAsyncCore<TMethodParameter, TMethodResult>(
- JsonEncodedText module,
- JsonEncodedText method,
- TMethodParameter parameter,
- Func<JsonElement, TMethodResult> composeResult,
- CancellationToken cancellationToken = default
- )
- {
- if (socket is null) {
- // ensure socket created and connected
- socket = await ConnectAsync(cancellationToken);
-
- // clear buffer for the initial use
- buffer.Clear();
- }
-
- /*
- * send
- */
- try {
- const SocketFlags sendSocketFlags = default;
-
- KasaJsonSerializer.Serialize(buffer, module, method, parameter, logger);
-
- logger?.LogTrace("Sending request {RequestSize} bytes", buffer.WrittenCount);
-
- try {
- await socket.SendAsync(
- buffer.WrittenMemory,
- sendSocketFlags,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
- }
- catch (SocketException ex) when (
- ex.SocketErrorCode is
- SocketError.Shutdown or // EPIPE
- SocketError.ConnectionReset or // ECONNRESET
- SocketError.ConnectionAborted // WSAECONNABORTED
- ) {
- throw new KasaDisconnectedException(ex.Message, endPoint, ex);
- }
- }
- finally {
- buffer.Clear(); // clear buffer state for next use
- }
-
- /*
- * receive
- */
- try {
- const SocketFlags receiveSocketFlags = default;
- const int receiveBlockSize = 0x100;
-
- for (; ;) {
- var buf = buffer.GetMemory(receiveBlockSize);
-
- int len = default;
-
- try {
- len = await socket.ReceiveAsync(
- buf,
- receiveSocketFlags,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
- }
- catch (SocketException ex) when (
- ex.SocketErrorCode is
- SocketError.ConnectionReset // ECONNRESET
- ) {
- throw new KasaDisconnectedException(ex.Message, endPoint, ex);
- }
-
- if (len <= 0)
- break;
-
- buffer.Advance(len);
-
- if (len < buf.Length)
- break;
- }
-
- if (buffer.WrittenCount == 0)
- throw new KasaDisconnectedException("The peer may have dropped connection", endPoint, innerException: null);
-
- logger?.LogTrace("Received response {ResponseSize} bytes", buffer.WrittenCount);
- logger?.LogTrace("Buffer capacity: {Capacity} bytes", buffer.Capacity);
-
- JsonElement result = default;
-
- try {
- result = KasaJsonSerializer.Deserialize(buffer, module, method, logger);
- }
- catch (KasaMessageBodyTooShortException ex) {
- throw new KasaIncompleteResponseException(
- message: "Received incomplete response: " + ex.Message,
- deviceEndPoint: endPoint,
- requestModule: Encoding.UTF8.GetString(module.EncodedUtf8Bytes),
- requestMethod: Encoding.UTF8.GetString(method.EncodedUtf8Bytes),
- innerException: ex
- );
- }
- catch (KasaMessageException ex) {
- throw new KasaUnexpectedResponseException(
- message: "Received unexpected or invalid response",
- deviceEndPoint: endPoint,
- requestModule: Encoding.UTF8.GetString(module.EncodedUtf8Bytes),
- requestMethod: Encoding.UTF8.GetString(method.EncodedUtf8Bytes),
- innerException: ex
- );
- }
-
- if (
- result.TryGetProperty(PropertyNameForErrorCode.EncodedUtf8Bytes, out var propErrorCode) &&
- propErrorCode.TryGetInt32(out var errorCode) &&
- errorCode != 0
- ) {
- throw new KasaErrorResponseException(
- deviceEndPoint: endPoint,
- requestModule: Encoding.UTF8.GetString(module.EncodedUtf8Bytes),
- requestMethod: Encoding.UTF8.GetString(method.EncodedUtf8Bytes),
- errorCode: (ErrorCode)errorCode
- );
- }
-
- try {
- return composeResult(result);
- }
- catch (Exception ex) {
- throw new KasaUnexpectedResponseException(
- message: "Unexpected method result: " + JsonSerializer.Serialize(result),
- deviceEndPoint: endPoint,
- requestModule: Encoding.UTF8.GetString(module.EncodedUtf8Bytes),
- requestMethod: Encoding.UTF8.GetString(method.EncodedUtf8Bytes),
- ex
- );
- }
- }
- finally {
- buffer.Clear(); // clear buffer state for next use
- }
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientDefaultExceptionHandler.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientDefaultExceptionHandler.cs
deleted file mode 100644
index 0e3ee1b..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientDefaultExceptionHandler.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System;
-using System.Net.Sockets;
-using Microsoft.Extensions.Logging;
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-internal class KasaClientDefaultExceptionHandler : KasaClientExceptionHandler {
- public override KasaClientExceptionHandling DetermineHandling(
- KasaDevice device,
- Exception exception,
- int attempt,
- ILogger? logger
- )
- {
- switch (exception) {
- case SocketException socketException: {
- var socketErrorCode = socketException.SocketErrorCode;
-
- if (
- socketErrorCode is
- SocketError.ConnectionRefused or // ECONNREFUSED
- SocketError.HostUnreachable or // EHOSTUNREACH
- SocketError.NetworkUnreachable // ENETUNREACH
- ) {
- if (attempt == 0 /* retry just once */) {
- // The end point may have changed.
- logger?.LogInformation($"Endpoint may have changed ({nameof(SocketError)}: {(int)socketErrorCode} {socketErrorCode})");
-
- return KasaClientExceptionHandling.InvalidateEndPointAndRetry;
- }
- else {
- logger?.LogError($"Endpoint unreachable ({nameof(SocketError)}: {(int)socketErrorCode} {socketErrorCode})");
-
- return KasaClientExceptionHandling.InvalidateEndPointAndThrow;
- }
- }
-
- // The client may have been invalid due to an exception at the transport layer.
- logger?.LogError(socketException, $"Unexpected socket exception ({nameof(SocketError)}: {(int)socketErrorCode} {socketErrorCode})");
-
- return KasaClientExceptionHandling.Throw;
- }
-
- case KasaDisconnectedException disconnectedException:
- if (attempt == 0) { // retry just once
- // The peer device disconnected the connection, or may have dropped the connection.
- if (disconnectedException.InnerException is SocketException innerSocketException)
- logger?.LogDebug($"Disconnected ({nameof(SocketError)}: {(int)innerSocketException.SocketErrorCode} {innerSocketException.SocketErrorCode})");
- else
- logger?.LogDebug($"Disconnected ({disconnectedException.Message})");
-
- return KasaClientExceptionHandling.RetryAfterReconnect;
- }
-
- return KasaClientExceptionHandling.Throw;
-
- case KasaIncompleteResponseException ex:
- // The peer has been in invalid state(?) and returnd incomplete response.
- const int maxRetryIncompleteResponse = 3;
- var nextAttempt = attempt + 1;
-
- if (nextAttempt < maxRetryIncompleteResponse) { // retry up to max attempts
- logger?.LogWarning(ex.Message);
- return KasaClientExceptionHandling.CreateRetry(
- retryAfter: TimeSpan.FromSeconds(2.0),
- shouldReconnect: true
- );
- }
-
- return KasaClientExceptionHandling.Throw;
-
- default:
- logger?.LogError(exception, $"Unhandled exception ({exception.GetType().FullName})");
- return KasaClientExceptionHandling.Throw;
- }
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientExceptionHandler.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientExceptionHandler.cs
deleted file mode 100644
index 274f0d6..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientExceptionHandler.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using Microsoft.Extensions.Logging;
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-public abstract class KasaClientExceptionHandler {
- protected internal static readonly KasaClientExceptionHandler Default = new KasaClientDefaultExceptionHandler();
-
- public abstract KasaClientExceptionHandling DetermineHandling(
- KasaDevice device,
- Exception exception,
- int attempt,
- ILogger? logger
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientExceptionHandling.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientExceptionHandling.cs
deleted file mode 100644
index 2415c4f..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaClientExceptionHandling.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-public readonly struct KasaClientExceptionHandling {
- public static readonly KasaClientExceptionHandling Throw = default;
- public static readonly KasaClientExceptionHandling InvalidateEndPointAndThrow = new() { ShouldInvalidateEndPoint = true };
-
- public static readonly KasaClientExceptionHandling Retry = new() { ShouldRetry = true };
- public static readonly KasaClientExceptionHandling RetryAfterReconnect = new() { ShouldRetry = true, ShouldReconnect = true };
- public static readonly KasaClientExceptionHandling InvalidateEndPointAndRetry = new() { ShouldRetry = true, ShouldInvalidateEndPoint = true, };
-
- public static KasaClientExceptionHandling CreateRetry(
- TimeSpan retryAfter,
- bool shouldReconnect = false
- )
- => new() {
- ShouldRetry = true,
- RetryAfter = retryAfter,
- ShouldReconnect = shouldReconnect,
- };
-
- public bool ShouldRetry { get; init; }
- public TimeSpan RetryAfter { get; init; }
- public bool ShouldReconnect { get; init; }
- public bool ShouldInvalidateEndPoint { get; init; }
-
- public override string ToString() => $"{{{nameof(ShouldRetry)}={ShouldRetry}, {nameof(RetryAfter)}={RetryAfter}, {nameof(ShouldReconnect)}={ShouldReconnect}, {nameof(ShouldInvalidateEndPoint)}={ShouldInvalidateEndPoint}}}";
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaJsonSerializer.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaJsonSerializer.cs
deleted file mode 100644
index ffd4c5a..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaJsonSerializer.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Buffers;
-using System.Buffers.Binary;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using Microsoft.Extensions.Logging;
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-/// <remarks>
-/// This implementation is based on and ported from the following implementation: <see href="https://github.com/plasticrake/tplink-smarthome-api">plasticrake/tplink-smarthome-api</see>.
-/// </remarks>
-public static class KasaJsonSerializer {
- public const byte InitialKey = 0xAB;
- private const int SizeOfHeaderInBytes = 4;
-
- private static readonly JsonSerializerOptions serializerOptions = new() {
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- };
-
- public static void Serialize<TMethodParameter>(
- ArrayBufferWriter<byte> buffer,
- JsonEncodedText module,
- JsonEncodedText method,
- TMethodParameter? parameter,
- ILogger? logger = null
- )
- {
- if (buffer is null)
- throw new ArgumentNullException(nameof(buffer));
-
- // reserve placeholder for header
- buffer.GetSpan(SizeOfHeaderInBytes);
- buffer.Advance(SizeOfHeaderInBytes);
-
- // write json body after header placeholder
- using var writer = new Utf8JsonWriter(buffer);
-
- writer.WriteStartObject();
- writer.WriteStartObject(module);
- writer.WritePropertyName(method);
-
- JsonSerializer.Serialize(
- writer,
- parameter,
- options: serializerOptions
- );
-
- writer.WriteEndObject(); // method
- writer.WriteEndObject(); // module
- writer.Flush();
-
- if (!MemoryMarshal.TryGetArray(buffer.WrittenMemory, out var writtenArraySegment))
- throw new NotSupportedException("cannot get underlying array segment");
-
- // write header
- BinaryPrimitives.WriteInt32BigEndian(
- writtenArraySegment.AsSpan(0, SizeOfHeaderInBytes),
- (int)writer.BytesCommitted
- );
-
- logger?.LogTrace(
- "Request: {Request}",
- Encoding.UTF8.GetString(writtenArraySegment.AsSpan(SizeOfHeaderInBytes))
- );
-
- // encrypt body
- EncryptInPlace(writtenArraySegment.AsSpan(SizeOfHeaderInBytes));
- }
-
- public static void EncryptInPlace(Span<byte> body)
- {
- var key = InitialKey;
-
- for (var i = 0; i < body.Length; i++) {
- key = body[i] = (byte)(body[i] ^ key);
- }
- }
-
- public static JsonElement Deserialize(
- ArrayBufferWriter<byte> buffer,
- JsonEncodedText module,
- JsonEncodedText method,
- ILogger? logger = null
- )
- {
- if (buffer is null)
- throw new ArgumentNullException(nameof(buffer));
-
- var buf = buffer.WrittenMemory;
-
- if (buf.Length < 4)
- throw new KasaMessageHeaderTooShortException($"input too short (expects at least 4 bytes of header but was {buf.Length})");
-
- var length = BinaryPrimitives.ReadInt32BigEndian(buf.Slice(0, 4).Span);
- var body = buf.Slice(4);
-
- if (body.Length < length)
- throw new KasaMessageBodyTooShortException(indicatedLength: length, actualLength: body.Length);
-
- body = body.Slice(0, length);
-
- if (!MemoryMarshal.TryGetArray(body, out var bodyArraySegment))
- throw new NotSupportedException("cannot get underlying array segment");
-
- DecryptInPlace(bodyArraySegment.AsSpan());
-
- logger?.LogTrace(
- "Response: {Response}",
- Encoding.UTF8.GetString(bodyArraySegment.AsSpan())
- );
-
- var doc = JsonDocument.Parse(bodyArraySegment.AsMemory());
-
- if (!doc.RootElement.TryGetProperty(module.EncodedUtf8Bytes, out var propModule))
- throw new KasaMessageException($"The response JSON does not contain the expected property for the module '{module}'.");
-
- if (!propModule.TryGetProperty(method.EncodedUtf8Bytes, out var propMethod))
- throw new KasaMessageException($"The response JSON does not contain the expected property for the method '{method}'.");
-
- return propMethod;
- }
-
- public static void DecryptInPlace(Span<byte> body)
- {
- var key = InitialKey;
-
- for (var i = 0; i < body.Length; i++) {
- (key, body[i]) = (body[i], (byte)(body[i] ^ key));
- }
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageBodyTooShortException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageBodyTooShortException.cs
deleted file mode 100644
index 880307d..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageBodyTooShortException.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-public class KasaMessageBodyTooShortException : KasaMessageException {
- public int IndicatedLength { get; }
- public int ActualLength { get; }
-
- public KasaMessageBodyTooShortException(
- int indicatedLength,
- int actualLength
- )
- : base(
- message: $"length of body is {actualLength} bytes but falls short of {indicatedLength} bytes which is indicated in the header."
- )
- {
- IndicatedLength = indicatedLength;
- ActualLength = actualLength;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageException.cs
deleted file mode 100644
index 405ff02..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageException.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-public class KasaMessageException : SystemException {
- public KasaMessageException(string message)
- : base(message: message)
- {
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageHeaderTooShortException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageHeaderTooShortException.cs
deleted file mode 100644
index 4b2ea5d..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa.Protocol/KasaMessageHeaderTooShortException.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-public class KasaMessageHeaderTooShortException : KasaMessageException {
- public KasaMessageHeaderTooShortException(string message)
- : base(message: message)
- {
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/HS105.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/HS105.cs
deleted file mode 100644
index 48f11cb..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/HS105.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using Smdn.TPSmartHomeDevices.Kasa.Json;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public class HS105 : KasaDevice {
- private static readonly JsonEncodedText MethodTextSetRelayState = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "set_relay_state"u8
-#else
- "set_relay_state"
-#endif
- );
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HS105"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <inheritdoc cref="KasaDevice(string, IServiceProvider?)" />
- public HS105(
- string host,
- IServiceProvider? serviceProvider = null
- )
- : base(
- host: host,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HS105"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="KasaDevice(IPAddress, IServiceProvider?)" />
- public HS105(
- IPAddress ipAddress,
- IServiceProvider? serviceProvider = null
- )
- : base(
- ipAddress: ipAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HS105"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <inheritdoc cref="KasaDevice(PhysicalAddress, IServiceProvider)" />
- public HS105(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- : base(
- macAddress: macAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HS105"/> class.
- /// </summary>
- /// <inheritdoc cref="KasaDevice(IDeviceEndPointProvider, IServiceProvider?)" />
- public HS105(
- IDeviceEndPointProvider deviceEndPointProvider,
- IServiceProvider? serviceProvider = null
- )
- : base(
- deviceEndPointProvider: deviceEndPointProvider,
- serviceProvider: serviceProvider
- )
- {
- }
-
- private readonly struct SetRelayStateParameter {
- public static readonly SetRelayStateParameter SetOff = new(false);
- public static readonly SetRelayStateParameter SetOn = new(true);
-
- [JsonPropertyName("state")]
- [JsonConverter(typeof(KasaNumericalBooleanJsonConverter))]
- public bool State { get; }
-
- public SetRelayStateParameter(bool state)
- {
- State = state;
- }
- }
-
- public ValueTask TurnOnAsync(CancellationToken cancellationToken = default)
- => SendRequestAsync(
- module: ModuleTextSystem,
- method: MethodTextSetRelayState,
- parameters: SetRelayStateParameter.SetOn,
- cancellationToken: cancellationToken
- );
-
- public ValueTask TurnOffAsync(CancellationToken cancellationToken = default)
- => SendRequestAsync(
- module: ModuleTextSystem,
- method: MethodTextSetRelayState,
- parameters: SetRelayStateParameter.SetOff,
- cancellationToken: cancellationToken
- );
-
- /// <summary>
- /// Sets the on/off state of the device according to the parameter <paramref name="newOnOffState" />.
- /// </summary>
- /// <param name="newOnOffState">
- /// The value that indicates new on/off state to be set. <see langword="true"/> for on, otherwise off.
- /// </param>
- public ValueTask SetOnOffStateAsync(
- bool newOnOffState,
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextSystem,
- method: MethodTextSetRelayState,
- parameters: newOnOffState ? SetRelayStateParameter.SetOn : SetRelayStateParameter.SetOff,
- cancellationToken: cancellationToken
- );
-
- private readonly struct GetSysInfoRelayStateParameter {
- [JsonPropertyName("relay_state")]
- [JsonConverter(typeof(KasaNumericalBooleanJsonConverter))]
- public bool RelayState { get; init; }
- }
-
- public ValueTask<bool> GetOnOffStateAsync(
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextSystem,
- method: MethodTextGetSysInfo,
- composeResult: static result => JsonSerializer.Deserialize<GetSysInfoRelayStateParameter>(result).RelayState,
- cancellationToken: cancellationToken
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KL130.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KL130.cs
deleted file mode 100644
index 949f691..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KL130.cs
+++ /dev/null
@@ -1,273 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using Smdn.TPSmartHomeDevices.Kasa.Json;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public class KL130 : KasaDevice {
- private static readonly JsonEncodedText ModuleTextLightingService = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "smartlife.iot.smartbulb.lightingservice"u8
-#else
- "smartlife.iot.smartbulb.lightingservice"
-#endif
- );
- private static readonly JsonEncodedText MethodTextTransitionLightState = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "transition_light_state"u8
-#else
- "transition_light_state"
-#endif
- );
-
- /// <summary>
- /// Initializes a new instance of the <see cref="KL130"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <inheritdoc cref="KasaDevice(string, IServiceProvider?)" />
- public KL130(
- string host,
- IServiceProvider? serviceProvider = null
- )
- : base(
- host: host,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="KL130"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="KasaDevice(IPAddress, IServiceProvider?)" />
- public KL130(
- IPAddress ipAddress,
- IServiceProvider? serviceProvider = null
- )
- : base(
- ipAddress: ipAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="KL130"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <inheritdoc cref="KasaDevice(PhysicalAddress, IServiceProvider)" />
- public KL130(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- : base(
- macAddress: macAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="KL130"/> class.
- /// </summary>
- /// <inheritdoc cref="KasaDevice(IDeviceEndPointProvider, IServiceProvider?)" />
- public KL130(
- IDeviceEndPointProvider deviceEndPointProvider,
- IServiceProvider? serviceProvider = null
- )
- : base(
- deviceEndPointProvider: deviceEndPointProvider,
- serviceProvider: serviceProvider
- )
- {
- }
-
- private static TimeSpan ValidateTransitionPeriod(TimeSpan? value, string paramName)
- {
- if (value == null)
- return TimeSpan.Zero;
-
- if (value.Value < TimeSpan.Zero)
- throw new ArgumentOutOfRangeException(paramName: paramName, actualValue: value, message: "The value for transition period must be zero or positive value.");
-
- return value.Value;
- }
-
- private readonly record struct SetOnOffStateParameter(
- [property: JsonPropertyName("on_off")]
- [property: JsonConverter(typeof(KasaNumericalBooleanJsonConverter))]
- bool OnOff,
- [property: JsonPropertyName("transition_period")]
- int TransitionPeriodInMilliseconds
- ) {
- [JsonPropertyName("ignore_default")]
- public int IgnoreDefault => 0; // turn on/off with the default configuration
- }
-
- public ValueTask TurnOnAsync(
- TimeSpan? transitionPeriod = null,
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextLightingService,
- method: MethodTextTransitionLightState,
- parameters: new SetOnOffStateParameter(
- OnOff: true, // on
- TransitionPeriodInMilliseconds: (int)ValidateTransitionPeriod(transitionPeriod, nameof(transitionPeriod)).TotalMilliseconds
- ),
- cancellationToken: cancellationToken
- );
-
- public ValueTask TurnOffAsync(
- TimeSpan? transitionPeriod = null,
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextLightingService,
- method: MethodTextTransitionLightState,
- parameters: new SetOnOffStateParameter(
- OnOff: false, // off
- TransitionPeriodInMilliseconds: (int)ValidateTransitionPeriod(transitionPeriod, nameof(transitionPeriod)).TotalMilliseconds
- ),
- cancellationToken: cancellationToken
- );
-
- /// <summary>
- /// Sets the on/off state of the device according to the parameter <paramref name="newOnOffState" />.
- /// </summary>
- /// <param name="newOnOffState">
- /// The value that indicates new on/off state to be set. <see langword="true"/> for on, otherwise off.
- /// </param>
- public ValueTask SetOnOffStateAsync(
- bool newOnOffState,
- TimeSpan? transitionPeriod = null,
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextLightingService,
- method: MethodTextTransitionLightState,
- parameters: new SetOnOffStateParameter(
- OnOff: newOnOffState,
- TransitionPeriodInMilliseconds: (int)ValidateTransitionPeriod(transitionPeriod, nameof(transitionPeriod)).TotalMilliseconds
- ),
- cancellationToken: cancellationToken
- );
-
- private readonly struct GetSysInfoLightStateParameter {
- [JsonPropertyName("light_state")]
- public KL130LightState LightState { get; init; }
- }
-
- public ValueTask<KL130LightState> GetLightStateAsync(
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextSystem,
- method: MethodTextGetSysInfo,
- composeResult: static result => JsonSerializer.Deserialize<GetSysInfoLightStateParameter>(result).LightState,
- cancellationToken: cancellationToken
- );
-
- public ValueTask<bool> GetOnOffStateAsync(
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextSystem,
- method: MethodTextGetSysInfo,
- composeResult: static result => JsonSerializer.Deserialize<GetSysInfoLightStateParameter>(result).LightState.IsOn,
- cancellationToken: cancellationToken
- );
-
- private readonly record struct SetColorTemperatureParameter(
- [property: JsonPropertyName("color_temp")]
- int ColorTemperature,
- [property: JsonPropertyName("brightness")]
- int? Brightness,
- [property: JsonPropertyName("transition_period")]
- int TransitionPeriodInMilliseconds
- ) {
- [JsonPropertyName("on_off")]
- [JsonConverter(typeof(KasaNumericalBooleanJsonConverter))]
- public bool OnOff => true; // on
-
- [JsonPropertyName("ignore_default")]
- public int IgnoreDefault => 1; // ignore the default configuration
- }
-
- /// <param name="colorTemperature">
- /// The color temperature in kelvin, in range of 2500~9000[K].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorTemperatureAsync(
- int colorTemperature,
- int? brightness = null,
- TimeSpan? transitionPeriod = null,
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextLightingService,
- method: MethodTextTransitionLightState,
- new SetColorTemperatureParameter(
- ColorTemperature: colorTemperature, // TODO: validation
- Brightness: brightness, // TODO: validation
- TransitionPeriodInMilliseconds: (int)ValidateTransitionPeriod(transitionPeriod, nameof(transitionPeriod)).TotalMilliseconds
- ),
- cancellationToken
- );
-
- private readonly record struct SetColorParameter(
- [property: JsonPropertyName("hue")]
- int? Hue,
- [property: JsonPropertyName("saturation")]
- int? Saturation,
- [property: JsonPropertyName("brightness")]
- int? Brightness,
- [property: JsonPropertyName("transition_period")]
- int TransitionPeriodInMilliseconds
- ) {
- [JsonPropertyName("color_temp")]
- public int ColorTemperature => 0; // set color with hue and saturation
-
- [JsonPropertyName("on_off")]
- [JsonConverter(typeof(KasaNumericalBooleanJsonConverter))]
- public bool OnOff => true; // on
-
- [JsonPropertyName("ignore_default")]
- public int IgnoreDefault => 1; // ignore the default configuration
- }
-
- /// <param name="hue">
- /// The hue of the color in degree, in range of 0~360[°].
- /// </param>
- /// <param name="saturation">
- /// The saturation of the color in percent value, in range of 0~100[%].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorAsync(
- int hue,
- int saturation,
- int? brightness = null,
- TimeSpan? transitionPeriod = null,
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync(
- module: ModuleTextLightingService,
- method: MethodTextTransitionLightState,
- new SetColorParameter(
- Hue: hue, // TODO: validation
- Saturation: saturation, // TODO: validation
- Brightness: brightness, // TODO: validation
- TransitionPeriodInMilliseconds: (int)ValidateTransitionPeriod(transitionPeriod, nameof(transitionPeriod)).TotalMilliseconds
- ),
- cancellationToken
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KL130LightState.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KL130LightState.cs
deleted file mode 100644
index d7cc5f6..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KL130LightState.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
-using System.Diagnostics.CodeAnalysis;
-#endif
-using System.Text.Json.Serialization;
-using Smdn.TPSmartHomeDevices.Kasa.Json;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public readonly struct KL130LightState {
- [JsonPropertyName("on_off")]
- [JsonConverter(typeof(KasaNumericalBooleanJsonConverter))]
- public bool IsOn { get; init; }
-
- /// <summary>Gets the current mode string of the light bulb.</summary>
- /// <value>If light is off, the value will be <see langword="null"/>.</value>
-#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
- [MemberNotNullWhenAttribute(true, nameof(IsOn))]
-#endif
- [JsonPropertyName("mode")]
- public string? Mode { get; init; }
-
- /// <summary>Gets the current hue value of the light bulb.</summary>
- /// <value>If light is off, the value will be <see langword="null"/>.</value>
-#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
- [MemberNotNullWhenAttribute(true, nameof(IsOn))]
-#endif
- [JsonPropertyName("hue")]
- public int? Hue { get; init; }
-
- /// <summary>Gets the current saturation value of the light bulb.</summary>
- /// <value>If light is off, the value will be <see langword="null"/>.</value>
-#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
- [MemberNotNullWhenAttribute(true, nameof(IsOn))]
-#endif
- [JsonPropertyName("saturation")]
- public int? Saturation { get; init; }
-
- /// <summary>Gets the current color temperature value of the light bulb.</summary>
- /// <value>If light is off, the value will be <see langword="null"/>.</value>
-#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
- [MemberNotNullWhenAttribute(true, nameof(IsOn))]
-#endif
- [JsonPropertyName("color_temp")]
- public int? ColorTemperature { get; init; }
-
- /// <summary>Gets the current brightness value of the light bulb.</summary>
- /// <value>If light is off, the value will be <see langword="null"/>.</value>
-#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
- [MemberNotNullWhenAttribute(true, nameof(IsOn))]
-#endif
- [JsonPropertyName("brightness")]
- public int? Brightness { get; init; }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDevice.Create.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDevice.Create.cs
deleted file mode 100644
index e7e5f2e..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDevice.Create.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-#pragma warning disable IDE0040
-partial class KasaDevice {
-#pragma warning restore IDE0040
- /// <inheritdoc cref="KasaDevice(string, IServiceProvider?)" />
- public static KasaDevice Create(
- string host,
- IServiceProvider? serviceProvider = null
- )
- => new(
- host: host,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc cref="KasaDevice(IPAddress, IServiceProvider?)" />
- public static KasaDevice Create(
- IPAddress ipAddress,
- IServiceProvider? serviceProvider = null
- )
- => new(
- ipAddress: ipAddress,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc cref="KasaDevice(PhysicalAddress, IServiceProvider)" />
- public static KasaDevice Create(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- => new(
- macAddress: macAddress,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc cref="KasaDevice(IDeviceEndPointProvider, IServiceProvider?)" />
- public static KasaDevice Create(
- IDeviceEndPointProvider deviceEndPointProvider,
- IServiceProvider? serviceProvider = null
- )
- => new(
- deviceEndPointProvider: deviceEndPointProvider,
- serviceProvider: serviceProvider
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDevice.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDevice.cs
deleted file mode 100644
index 854662c..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDevice.cs
+++ /dev/null
@@ -1,396 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Buffers;
-#if SYSTEM_DIAGNOSTICS_UNREACHABLEEXCEPTION
-using System.Diagnostics;
-#endif
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public partial class KasaDevice : IDisposable {
- protected static readonly JsonEncodedText ModuleTextSystem = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "system"u8
-#else
- "system"
-#endif
- );
- protected static readonly JsonEncodedText MethodTextGetSysInfo = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "get_sysinfo"u8
-#else
- "get_sysinfo"
-#endif
- );
-
- protected readonly struct NullParameter { }
-
- private IDeviceEndPointProvider? deviceEndPointProvider; // if null, it indicates a 'disposed' state.
- protected bool IsDisposed => deviceEndPointProvider is null;
-
- private readonly KasaClientExceptionHandler exceptionHandler;
- private readonly ArrayBufferWriter<byte> buffer = new(initialCapacity: KasaClient.DefaultBufferCapacity);
- private readonly IServiceProvider? serviceProvider;
-
- private KasaClient? client;
-
- public bool IsConnected {
- get {
- ThrowIfDisposed();
- return client is not null && client.IsConnected;
- }
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="KasaDevice"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <param name="host">
- /// A <see cref="string"/> that holds the host name or IP address string, representing the device endpoint.
- /// </param>
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// </param>
- protected KasaDevice(
- string host,
- IServiceProvider? serviceProvider = null
- )
- : this(
- deviceEndPointProvider: KasaDeviceEndPointProvider.Create(host),
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="KasaDevice"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <param name="ipAddress">
- /// A <see cref="IPAddress"/> that holds the IP address representing the device end point.
- /// </param>
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// </param>
- protected KasaDevice(
- IPAddress ipAddress,
- IServiceProvider? serviceProvider = null
- )
- : this(
- deviceEndPointProvider: KasaDeviceEndPointProvider.Create(ipAddress),
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="KasaDevice"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <param name="macAddress">
- /// A <see cref="PhysicalAddress"/> that holds the MAC address representing the device end point.
- /// </param>
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// <see cref="IDeviceEndPointFactory{PhysicalAddress}"/> must be registered to create an end point from the <paramref name="macAddress"/>.
- /// </param>
- /// <exception cref="InvalidOperationException">No service for type <see cref="IDeviceEndPointFactory{PhysicalAddress}"/> has been registered for <paramref name="serviceProvider"/>.</exception>
- protected KasaDevice(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- : this(
- deviceEndPointProvider: KasaDeviceEndPointProvider.Create(macAddress, serviceProvider),
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="KasaDevice"/> class.
- /// </summary>
- /// <param name="deviceEndPointProvider">
- /// A <see cref="IDeviceEndPointProvider"/> that provides the device end point.
- /// </param>
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// </param>
- /// <exception cref="InvalidOperationException">No service for type <see cref="IDeviceEndPointFactory{PhysicalAddress}"/> has been registered for <paramref name="serviceProvider"/>.</exception>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="deviceEndPointProvider"/> is <see langword="null"/>.
- /// </exception>
- protected KasaDevice(
- IDeviceEndPointProvider deviceEndPointProvider,
- IServiceProvider? serviceProvider = null
- )
- {
- this.deviceEndPointProvider = deviceEndPointProvider ?? throw new ArgumentNullException(nameof(deviceEndPointProvider));
- this.serviceProvider = serviceProvider;
-
- exceptionHandler = serviceProvider?.GetService<KasaClientExceptionHandler>() ?? KasaClientExceptionHandler.Default;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (!disposing)
- return;
-
- deviceEndPointProvider = null; // mark as disposed
-
- client?.Dispose();
- client = null;
- }
-
- private void ThrowIfDisposed()
- {
- if (IsDisposed)
- throw new ObjectDisposedException(GetType().FullName);
- }
-
- public ValueTask<EndPoint> ResolveEndPointAsync(
- CancellationToken cancellationToken = default
- )
- {
- ThrowIfDisposed();
-
- return cancellationToken.IsCancellationRequested
- ?
-#if SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
- ValueTask.FromCanceled<EndPoint>(cancellationToken)
-#else
- ValueTaskShim.FromCanceled<EndPoint>(cancellationToken)
-#endif
- : deviceEndPointProvider.ResolveOrThrowAsync(
- defaultPort: KasaClient.DefaultPort,
- cancellationToken: cancellationToken
- );
- }
-
- protected ValueTask SendRequestAsync<TMethodParameter>(
- JsonEncodedText module,
- JsonEncodedText method,
- TMethodParameter parameters,
- CancellationToken cancellationToken
- )
- {
- if (parameters is null)
- throw new ArgumentNullException(nameof(parameters));
-
- ThrowIfDisposed();
-
- return cancellationToken.IsCancellationRequested
- ?
-#if SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
- ValueTask.FromCanceled(cancellationToken)
-#else
- ValueTaskShim.FromCanceled(cancellationToken)
-#endif
- : SendRequestAsyncCore(
- module: module,
- method: method,
- parameters: parameters,
- cancellationToken: cancellationToken
- );
- }
-
- protected ValueTask<TMethodResult> SendRequestAsync<TMethodResult>(
- JsonEncodedText module,
- JsonEncodedText method,
- Func<JsonElement, TMethodResult> composeResult,
- CancellationToken cancellationToken
- )
- {
- if (composeResult is null)
- throw new ArgumentNullException(nameof(composeResult));
-
- ThrowIfDisposed();
-
- return cancellationToken.IsCancellationRequested
- ?
-#if SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
- ValueTask.FromCanceled<TMethodResult>(cancellationToken)
-#else
- ValueTaskShim.FromCanceled<TMethodResult>(cancellationToken)
-#endif
- : SendRequestAsyncCore<NullParameter, TMethodResult>(
- module: module,
- method: method,
- parameters: default,
- composeResult: composeResult,
- cancellationToken: cancellationToken
- );
- }
-
- protected ValueTask<TMethodResult> SendRequestAsync<TMethodParameter, TMethodResult>(
- JsonEncodedText module,
- JsonEncodedText method,
- TMethodParameter parameters,
- Func<JsonElement, TMethodResult> composeResult,
- CancellationToken cancellationToken
- )
- {
- if (parameters is null)
- throw new ArgumentNullException(nameof(parameters));
- if (composeResult is null)
- throw new ArgumentNullException(nameof(composeResult));
-
- ThrowIfDisposed();
-
- return cancellationToken.IsCancellationRequested
- ?
-#if SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
- ValueTask.FromCanceled<TMethodResult>(cancellationToken)
-#else
- ValueTaskShim.FromCanceled<TMethodResult>(cancellationToken)
-#endif
- : SendRequestAsyncCore(
- module: module,
- method: method,
- parameters: parameters,
- composeResult: composeResult,
- cancellationToken: cancellationToken
- );
- }
-
- private async ValueTask SendRequestAsyncCore<TMethodParameter>(
- JsonEncodedText module,
- JsonEncodedText method,
- TMethodParameter parameters,
- CancellationToken cancellationToken
- )
- => await SendRequestAsyncCore<TMethodParameter, None /* as an alternative to System.Void */>(
- module: module,
- method: method,
- parameters: parameters,
- composeResult: static _ => default,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
-
- private async ValueTask<TMethodResult> SendRequestAsyncCore<TMethodParameter, TMethodResult>(
- JsonEncodedText module,
- JsonEncodedText method,
- TMethodParameter parameters,
- Func<JsonElement, TMethodResult> composeResult,
- CancellationToken cancellationToken
- )
- {
- const int maxAttempts = 5;
- var delay = TimeSpan.Zero;
- EndPoint? endPoint = null;
-
- for (var attempt = 0; attempt < maxAttempts; attempt++) {
- if (TimeSpan.Zero < delay)
- await Task.Delay(delay, cancellationToken).ConfigureAwait(false);
-
- if (endPoint is null) {
- endPoint = await ResolveEndPointAsync(cancellationToken).ConfigureAwait(false);
-
- if (client is not null && !client.EndPoint.Equals(endPoint)) {
- // endpoint has changed, recreate client with new endpoint
- client.Logger?.LogInformation($"Endpoint has changed: {client.EndPoint} -> {endPoint}");
- client.Dispose();
- client = null;
- }
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- client ??= new KasaClient(
- endPoint: endPoint,
- buffer: buffer,
- logger: serviceProvider?.GetService<ILoggerFactory>()?.CreateLogger(GenerateLoggerCategoryName())
- );
-
- string GenerateLoggerCategoryName()
- => deviceEndPointProvider is IDynamicDeviceEndPointProvider
- ? $"{nameof(KasaClient)}({endPoint}, {deviceEndPointProvider})"
- : $"{nameof(KasaClient)}({endPoint})";
-
- try {
- return await client.SendAsync(
- module: module,
- method: method,
- parameter: parameters,
- composeResult: composeResult,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
- }
- catch (Exception ex) {
- // OperationCanceledException and TaskCanceledException due to a cancel
- // request triggered by a given CancellationToken must not be handled
- // by exception handler, just rethrow instead.
- var handling = (
- ex is OperationCanceledException exOperationCanceled &&
- exOperationCanceled.CancellationToken.Equals(cancellationToken)
- )
- ? KasaClientExceptionHandling.Throw
- : exceptionHandler.DetermineHandling(this, ex, attempt, client.Logger);
-
- static void LogRequest(ILogger logger, JsonEncodedText mod, JsonEncodedText meth, TMethodParameter param)
- => logger.LogError($"{{{mod}:{{{meth}:{{{JsonSerializer.Serialize(param)}}}}}}}");
-
- client.Logger?.LogTrace(
- "Exception handling for {TypeOfException}: {ExceptionHandling}",
- ex.GetType().FullName,
- handling
- );
-
- if (client.Logger is not null && !handling.ShouldRetry)
- LogRequest(client.Logger, module, method, parameters);
-
- if (handling.ShouldInvalidateEndPoint) {
- if (deviceEndPointProvider is IDynamicDeviceEndPointProvider dynamicEndPoint) {
- // mark end point as invalid to have the end point refreshed or rescanned
- dynamicEndPoint.InvalidateEndPoint();
-
- endPoint = null; // should resolve end point
- }
- else {
- // disallow retry
- handling = handling with { ShouldRetry = false };
- }
- }
-
- if (handling.ShouldRetry) {
- /*
- * retry
- */
- delay = handling.RetryAfter;
-
- if (handling.ShouldReconnect) {
- client.Dispose();
- client = null;
- }
-
- continue;
- }
-
- /*
- * rethrow
- */
- client.Dispose();
- client = null;
-
- throw;
- } // try
- } // for
-
-#if SYSTEM_DIAGNOSTICS_UNREACHABLEEXCEPTION
- throw new UnreachableException();
-#else
- throw new NotImplementedException("unreachable");
-#endif
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDeviceEndPointProvider.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDeviceEndPointProvider.cs
deleted file mode 100644
index 1c029da..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDeviceEndPointProvider.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public static class KasaDeviceEndPointProvider {
- public static IDeviceEndPointProvider Create(string host)
- => DeviceEndPointProvider.Create(host, KasaClient.DefaultPort);
-
- public static IDeviceEndPointProvider Create(IPAddress ipAddress)
- => DeviceEndPointProvider.Create(ipAddress, KasaClient.DefaultPort);
-
- public static IDeviceEndPointProvider Create(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- => DeviceEndPointProvider.Create(macAddress, KasaClient.DefaultPort, serviceProvider);
-
- public static IDeviceEndPointProvider Create(
- PhysicalAddress macAddress,
- IDeviceEndPointFactory<PhysicalAddress> endPointFactory
- )
- => DeviceEndPointProvider.Create(macAddress, KasaClient.DefaultPort, endPointFactory);
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDisconnectedException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDisconnectedException.cs
deleted file mode 100644
index ebd8161..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaDisconnectedException.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public class KasaDisconnectedException : KasaProtocolException {
- public KasaDisconnectedException(
- string message,
- EndPoint deviceEndPoint,
- Exception? innerException
- )
- : base(
- message: message,
- deviceEndPoint: deviceEndPoint,
- innerException: innerException
- )
- {
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaErrorResponseException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaErrorResponseException.cs
deleted file mode 100644
index 0ca6785..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaErrorResponseException.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Net;
-using Smdn.TPSmartHomeDevices.Kasa.Protocol;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public class KasaErrorResponseException : KasaUnexpectedResponseException {
- public ErrorCode ErrorCode { get; }
-
- public KasaErrorResponseException(
- EndPoint deviceEndPoint,
- string requestModule,
- string requestMethod,
- ErrorCode errorCode
- )
- : base(
- message: $"Request '{requestModule}:{requestMethod}' failed with error code {(int)errorCode}. (Device end point: {deviceEndPoint})",
- deviceEndPoint: deviceEndPoint,
- requestModule: requestModule,
- requestMethod: requestMethod,
- innerException: null
- )
- {
- ErrorCode = errorCode;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaIncompleteResponseException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaIncompleteResponseException.cs
deleted file mode 100644
index 954aa05..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaIncompleteResponseException.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public class KasaIncompleteResponseException : KasaUnexpectedResponseException {
- public KasaIncompleteResponseException(
- string message,
- EndPoint deviceEndPoint,
- string requestModule,
- string requestMethod,
- Exception? innerException
- )
- : base(
- message: message,
- deviceEndPoint: deviceEndPoint,
- requestModule: requestModule,
- requestMethod: requestMethod,
- innerException: innerException
- )
- {
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaProtocolException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaProtocolException.cs
deleted file mode 100644
index a0e4b91..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaProtocolException.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public abstract class KasaProtocolException : InvalidOperationException {
- public EndPoint DeviceEndPoint { get; }
-
- protected KasaProtocolException(
- string message,
- EndPoint deviceEndPoint,
- Exception? innerException
- )
- : base(
- message: message,
- innerException: innerException
- )
- {
- DeviceEndPoint = deviceEndPoint;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaUnexpectedResponseException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaUnexpectedResponseException.cs
deleted file mode 100644
index c3d54f4..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Kasa/KasaUnexpectedResponseException.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-
-namespace Smdn.TPSmartHomeDevices.Kasa;
-
-public class KasaUnexpectedResponseException : KasaProtocolException {
- public string RequestModule { get; }
- public string RequestMethod { get; }
-
- public KasaUnexpectedResponseException(
- string message,
- EndPoint deviceEndPoint,
- string requestModule,
- string requestMethod,
- Exception? innerException
- )
- : base(
- message: message,
- deviceEndPoint: deviceEndPoint,
- innerException: innerException
- )
- {
- RequestModule = requestModule;
- RequestMethod = requestMethod;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredential.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredential.cs
deleted file mode 100644
index cb398bd..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredential.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text.Json;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-public interface ITapoCredential : IDisposable {
- void WritePasswordPropertyValue(Utf8JsonWriter writer);
- void WriteUsernamePropertyValue(Utf8JsonWriter writer);
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredentialIdentity.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredentialIdentity.cs
deleted file mode 100644
index 224a5fd..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredentialIdentity.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-public interface ITapoCredentialIdentity {
- string Name { get; }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredentialProvider.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredentialProvider.cs
deleted file mode 100644
index d4707cf..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/ITapoCredentialProvider.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-public interface ITapoCredentialProvider {
- ITapoCredential GetCredential(ITapoCredentialIdentity? identity);
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/TapoCredentialUtils.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/TapoCredentialUtils.cs
deleted file mode 100644
index 9f42fbf..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Credentials/TapoCredentialUtils.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Buffers;
-using System.Security.Cryptography;
-using System.Text;
-using Smdn.Formats;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-/// <remarks>
-/// This implementation is based on and ported from the following implementation: <see href="https://github.com/fishbigger/TapoP100">fishbigger/TapoP100</see>.
-/// </remarks>
-public static class TapoCredentialUtils {
-#if SYSTEM_SECURITY_CRYPTOGRAPHY_SHA1_HASHSIZEINBYTES
- private const int SHA1HashSizeInBytes = SHA1.HashSizeInBytes;
-#else
- private const int SHA1HashSizeInBytes = 160/*bits*/ / 8;
-#endif
- public const int HexSHA1HashSizeInBytes = SHA1HashSizeInBytes * 2; // byte array -> hex byte array
-
- /// <summary>
- /// Hash the string passed by the <paramref name="str"/> with SHA-1 algorithm and converts to the base64 format used for authentication of Tapo devices.
- /// </summary>
- /// <param name="str">The string to convert.</param>
- /// <returns>The <see cref="string"/> containing the result of the conversion.</returns>
- public static string ToBase64EncodedSHA1DigestString(ReadOnlySpan<char> str)
- {
- byte[]? bytes = null;
-
- try {
- // string -> UTF-8 byte array
- var length = Encoding.UTF8.GetByteCount(str);
-
- bytes = ArrayPool<byte>.Shared.Rent(length);
-
- Encoding.UTF8.GetBytes(str, bytes);
-
- // UTF-8 byte array -> hex SHA-1 hash byte array
- Span<byte> hexSHA1Hash = stackalloc byte[HexSHA1HashSizeInBytes];
-
- if (!TryConvertToHexSHA1Hash(bytes.AsSpan(0, length), hexSHA1Hash, out _))
- throw new InvalidOperationException("failed to convert hex SHA-1 hash");
-
- // hex SHA-1 hash byte array -> base64 string
- return Convert.ToBase64String(hexSHA1Hash, Base64FormattingOptions.None);
- }
- finally {
- if (bytes is not null)
- ArrayPool<byte>.Shared.Return(bytes, clearArray: true);
- }
- }
-
- /// <summary>
- /// Attempts to convert the UTF-8 string passed by the <paramref name="input"/> to the SHA-1 hash represented in the hexadecimal format (base16).
- /// </summary>
- /// <param name="input">The UTF-8 string to convert.</param>
- /// <param name="destination">The buffer to receive the converted value.</param>
- /// <param name="bytesWritten">When this method returns, the total number of bytes written into destination.</param>
- /// <returns><see langword="false"/> if <paramref name="destination"/> is too small to hold the calculated hash, <see langword="true"/> otherwise.</returns>
- public static bool TryConvertToHexSHA1Hash(
- ReadOnlySpan<byte> input,
- Span<byte> destination,
- out int bytesWritten
- )
- {
- bytesWritten = 0;
-
- if (destination.Length < HexSHA1HashSizeInBytes)
- return false;
-
- Span<byte> sha1hash = stackalloc byte[SHA1HashSizeInBytes];
-
- try {
-#if SYSTEM_SECURITY_CRYPTOGRAPHY_SHA1_TRYHASHDATA
- if (!SHA1.TryHashData(input, sha1hash, out var bytesWrittenSHA1))
- return false; // destination too short
-#else
-#pragma warning disable CA5350
- using var sha1 = SHA1.Create();
-#pragma warning restore CA5350
-
- if (!sha1.TryComputeHash(input, sha1hash, out var bytesWrittenSHA1))
- return false; // destination too short
-#endif
-
- if (bytesWrittenSHA1 != SHA1HashSizeInBytes)
- return false; // unexpected state
-
- /*
- * SHA-1 hash byte array -> hex byte array
- */
- if (!Hexadecimal.TryEncodeLowerCase(sha1hash, destination, out bytesWritten))
- return false; // destination too short
-
- return true;
- }
- finally {
- sha1hash.Clear();
- }
- }
-
- public static string ToBase64EncodedString(ReadOnlySpan<char> str)
- {
- byte[]? bytes = null;
-
- try {
- var length = Encoding.UTF8.GetByteCount(str);
-
- bytes = ArrayPool<byte>.Shared.Rent(length);
-
- var bytesWritten = Encoding.UTF8.GetBytes(str, bytes);
-
- return Convert.ToBase64String(bytes.AsSpan(0, bytesWritten), Base64FormattingOptions.None);
- }
- finally {
- if (bytes is not null)
- ArrayPool<byte>.Shared.Return(bytes, clearArray: true);
- }
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Json/TapoBase64StringJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Json/TapoBase64StringJsonConverter.cs
deleted file mode 100644
index 6bce9ec..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Json/TapoBase64StringJsonConverter.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Json;
-
-internal sealed class TapoBase64StringJsonConverter : JsonConverter<string?> {
- public override string? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => reader.TryGetBytesFromBase64(out var base64) && base64 is not null
- ? Encoding.UTF8.GetString(base64)
- : null;
-
- public override void Write(
- Utf8JsonWriter writer,
- string? value,
- JsonSerializerOptions options
- )
- => throw new NotImplementedException();
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Json/TapoIPAddressJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Json/TapoIPAddressJsonConverter.cs
deleted file mode 100644
index efed510..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Json/TapoIPAddressJsonConverter.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Json;
-
-internal sealed class TapoIPAddressJsonConverter : JsonConverter<IPAddress> {
- public override IPAddress? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- {
- var str = reader.GetString();
-
- if (str is null)
- return null;
-
- return IPAddress.TryParse(str, out var ret)
- ? ret
- : null;
- }
-
- public override void Write(
- Utf8JsonWriter writer,
- IPAddress value,
- JsonSerializerOptions options
- )
- => throw new NotImplementedException();
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ErrorCode.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ErrorCode.cs
deleted file mode 100644
index d2f8e0a..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ErrorCode.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public enum ErrorCode {
- Success = 0,
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/GetDeviceInfoRequest.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/GetDeviceInfoRequest.cs
deleted file mode 100644
index bbc8544..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/GetDeviceInfoRequest.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct GetDeviceInfoRequest : ITapoPassThroughRequest {
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method => "get_device_info";
-
-#pragma warning disable CA1822
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds => 0L; // DateTimeOffset.Now.ToUnixTimeMilliseconds();
-#pragma warning restore CA1822
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/GetDeviceInfoResponse.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/GetDeviceInfoResponse.cs
deleted file mode 100644
index ecb2f39..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/GetDeviceInfoResponse.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-#pragma warning disable SA1313
-
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct GetDeviceInfoResponse : ITapoPassThroughResponse {
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
-
- [JsonPropertyName("result")]
- public TapoDeviceInfo Result { get; init; }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/HandshakeRequest.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/HandshakeRequest.cs
deleted file mode 100644
index 90fe240..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/HandshakeRequest.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct HandshakeRequest : ITapoRequest {
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method => "handshake";
-
- [JsonPropertyName("params")]
- public RequestParameters Parameters { get; }
-
- public HandshakeRequest(string key)
- {
- Parameters = new(key);
- }
-
- public readonly record struct RequestParameters(
-#pragma warning disable SA1313
- [property: JsonPropertyName("key")] string Key
-#pragma warning restore SA1313
- ) {
-#pragma warning disable CA1822
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds => 0L; // DateTimeOffset.Now.ToUnixTimeMilliseconds();
-#pragma warning restore CA1822
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/HandshakeResponse.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/HandshakeResponse.cs
deleted file mode 100644
index 8939a70..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/HandshakeResponse.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-#pragma warning disable SA1313
-
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct HandshakeResponse : ITapoResponse {
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
-
- [JsonPropertyName("result")]
- public ResponseResult Result { get; init; }
-
- public readonly record struct ResponseResult(
- [property: JsonPropertyName("key")] string? Key
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoPassThroughRequest.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoPassThroughRequest.cs
deleted file mode 100644
index 598543d..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoPassThroughRequest.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public interface ITapoPassThroughRequest : ITapoRequest {
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoPassThroughResponse.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoPassThroughResponse.cs
deleted file mode 100644
index 93705a5..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoPassThroughResponse.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public interface ITapoPassThroughResponse : ITapoResponse {
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoRequest.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoRequest.cs
deleted file mode 100644
index ed9ffda..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoRequest.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public interface ITapoRequest {
-/*
- [JsonPropertyName("method")]
- [JsonPropertyOrder(int.MinValue)]
-*/
- string Method { get; }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoResponse.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoResponse.cs
deleted file mode 100644
index 22eee02..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/ITapoResponse.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public interface ITapoResponse {
-/*
- [JsonPropertyName("error_code")]
-*/
- public ErrorCode ErrorCode { get; init; }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.TapoCredentialJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.TapoCredentialJsonConverter.cs
deleted file mode 100644
index a26abeb..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.TapoCredentialJsonConverter.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-#pragma warning disable IDE0040
-partial struct LoginDeviceRequest {
-#pragma warning restore IDE0040
- private static readonly JsonEncodedText PropertyNamePassword = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "password"u8
-#else
- "password"
-#endif
- );
- private static readonly JsonEncodedText PropertyNameUsername = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "username"u8
-#else
- "username"
-#endif
- );
-
- internal sealed class TapoCredentialJsonConverter : JsonConverter<ITapoCredentialProvider> {
- private readonly ITapoCredentialIdentity? identity;
-
- internal TapoCredentialJsonConverter(ITapoCredentialIdentity? identity)
- {
- this.identity = identity;
- }
-
- public override ITapoCredentialProvider? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => throw new NotSupportedException();
-
- public override void Write(
- Utf8JsonWriter writer,
- ITapoCredentialProvider value,
- JsonSerializerOptions options
- )
- {
- using var credential = value.GetCredential(identity);
-
- if (credential is null)
- throw new InvalidOperationException($"Could not get a credential for an identity '{identity?.Name ?? "(null)"}'");
-
- writer.WriteStartObject();
-
- writer.WritePropertyName(PropertyNamePassword);
-
- credential.WritePasswordPropertyValue(writer);
-
- writer.WritePropertyName(PropertyNameUsername);
-
- credential.WriteUsernamePropertyValue(writer);
-
- writer.WriteEndObject();
- }
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.TapoCredentialMaskingJsonConverter.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.TapoCredentialMaskingJsonConverter.cs
deleted file mode 100644
index a94e827..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.TapoCredentialMaskingJsonConverter.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-#pragma warning disable IDE0040
-partial struct LoginDeviceRequest {
-#pragma warning restore IDE0040
- internal sealed class TapoCredentialMaskingJsonConverter : JsonConverter<ITapoCredentialProvider> {
- private static readonly JsonEncodedText PropertyValueMaskedCredential = JsonEncodedText.Encode(
-#if LANG_VERSION_11_OR_GREATER
- "****"u8
-#else
- "****"
-#endif
- );
-
- public static readonly JsonConverter<ITapoCredentialProvider> Instance = new TapoCredentialMaskingJsonConverter();
-
- private TapoCredentialMaskingJsonConverter()
- {
- }
-
- public override ITapoCredentialProvider? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => throw new NotSupportedException();
-
- public override void Write(
- Utf8JsonWriter writer,
- ITapoCredentialProvider value,
- JsonSerializerOptions options
- )
- {
- writer.WriteStartObject();
- writer.WriteString(PropertyNamePassword, PropertyValueMaskedCredential);
- writer.WriteString(PropertyNameUsername, PropertyValueMaskedCredential);
- writer.WriteEndObject();
- }
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.cs
deleted file mode 100644
index 56ff224..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceRequest.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Text.Json.Serialization;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly partial struct LoginDeviceRequest : ITapoPassThroughRequest {
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method => "login_device";
-
- [JsonPropertyName("params")]
- public ITapoCredentialProvider Parameters { get; }
-
-#pragma warning disable CA1822
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds => 0L; // DateTimeOffset.Now.ToUnixTimeMilliseconds();
-#pragma warning restore CA1822
-
- public LoginDeviceRequest(ITapoCredentialProvider credential)
- {
- Parameters = credential;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceResponse.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceResponse.cs
deleted file mode 100644
index ec5c298..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/LoginDeviceResponse.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-#pragma warning disable SA1313
-
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct LoginDeviceResponse : ITapoPassThroughResponse {
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
-
- [JsonPropertyName("result")]
- public ResponseResult Result { get; init; }
-
- public readonly record struct ResponseResult(
- [property: JsonPropertyName("token")] string Token
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughInvalidPaddingException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughInvalidPaddingException.cs
deleted file mode 100644
index a851592..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughInvalidPaddingException.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public class SecurePassThroughInvalidPaddingException : SystemException {
- public SecurePassThroughInvalidPaddingException(
- string message,
- Exception? innerException
- )
- : base(
- message: message,
- innerException: innerException
- )
- {
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughJsonConverterFactory.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughJsonConverterFactory.cs
deleted file mode 100644
index a393794..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughJsonConverterFactory.cs
+++ /dev/null
@@ -1,233 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.IO;
-using System.Reflection;
-using System.Security.Cryptography;
-using System.Text;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-using Microsoft.Extensions.Logging;
-
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-/// <remarks>
-/// This implementation is based on and ported from the following implementation: <see href="https://github.com/fishbigger/TapoP100">fishbigger/TapoP100</see>.
-/// </remarks>
-public sealed class SecurePassThroughJsonConverterFactory :
- JsonConverterFactory,
- IDisposable,
- SecurePassThroughJsonConverterFactory.IPassThroughObjectJsonConverter
-{
- private interface IPassThroughObjectJsonConverter {
- void WriteEncryptedValue<TValue>(
- Utf8JsonWriter writer,
- TValue value
- );
- TValue ReadDecryptedValue<TValue>(
- ref Utf8JsonReader reader,
- Type typeToConvert
- );
- }
-
- private ICryptoTransform? encryptorForPassThroughRequest;
- private ICryptoTransform? decryptorForPassThroughResponse;
-
- // used for avoiding JsonSerializer to (de)serialize recursively
- private readonly JsonSerializerOptions jsonSerializerOptionsForPassThroughMessage;
- private readonly JsonSerializerOptions? jsonSerializerOptionsForPassThroughMessageLogger;
-
- private readonly ILogger? logger;
- private bool disposed;
-
- public SecurePassThroughJsonConverterFactory(
- ITapoCredentialIdentity? identity,
- ICryptoTransform? encryptorForPassThroughRequest,
- ICryptoTransform? decryptorForPassThroughResponse,
- JsonSerializerOptions? baseJsonSerializerOptionsForPassThroughMessage,
- ILogger? logger = null
- )
- {
- this.encryptorForPassThroughRequest = encryptorForPassThroughRequest;
- this.decryptorForPassThroughResponse = decryptorForPassThroughResponse;
- this.logger = logger;
-
- jsonSerializerOptionsForPassThroughMessage = baseJsonSerializerOptionsForPassThroughMessage is null
- ? new()
- : new(baseJsonSerializerOptionsForPassThroughMessage);
- jsonSerializerOptionsForPassThroughMessage.Converters.Add(
- new LoginDeviceRequest.TapoCredentialJsonConverter(identity: identity)
- );
-
- if (logger is not null) {
- jsonSerializerOptionsForPassThroughMessageLogger = baseJsonSerializerOptionsForPassThroughMessage is null
- ? new()
- : new(baseJsonSerializerOptionsForPassThroughMessage);
- jsonSerializerOptionsForPassThroughMessageLogger.Converters.Add(
- LoginDeviceRequest.TapoCredentialMaskingJsonConverter.Instance // mask credentials for logger output
- );
- }
- }
-
- public void Dispose()
- {
- encryptorForPassThroughRequest?.Dispose();
- encryptorForPassThroughRequest = null;
-
- decryptorForPassThroughResponse?.Dispose();
- decryptorForPassThroughResponse = null;
-
- disposed = true;
- }
-
- private Stream CreateEncryptingStream(Stream stream)
- => disposed
- ? throw new ObjectDisposedException(GetType().FullName)
- : new CryptoStream(
- stream: stream ?? throw new ArgumentNullException(nameof(stream)),
- transform: encryptorForPassThroughRequest ?? throw new NotSupportedException("encryption not supported"),
- mode: CryptoStreamMode.Write,
- leaveOpen: true
- );
-
- private Stream CreateDecryptingStream(Stream stream)
- => disposed
- ? throw new ObjectDisposedException(GetType().FullName)
- : new CryptoStream(
- stream: stream ?? throw new ArgumentNullException(nameof(stream)),
- transform: decryptorForPassThroughResponse ?? throw new NotSupportedException("decryption not supported"),
- mode: CryptoStreamMode.Read,
- leaveOpen: true
- );
-
- public override bool CanConvert(Type typeToConvert)
- =>
- typeof(ITapoPassThroughRequest).IsAssignableFrom(typeToConvert) ||
- typeof(ITapoPassThroughResponse).IsAssignableFrom(typeToConvert);
-
- public override JsonConverter? CreateConverter(
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => CanConvert(typeToConvert)
- ? (JsonConverter)Activator.CreateInstance(
- type: typeof(PassThroughObjectJsonConverter<>).MakeGenericType(typeToConvert),
- bindingAttr: BindingFlags.Instance | BindingFlags.Public,
- binder: null,
- args: new object[] { this },
- culture: null
- )!
- // should throw exception?
- // throw new NotSupportedException($"unexpected type to convert: {typeToConvert.FullName}");
- : null;
-
- void IPassThroughObjectJsonConverter.WriteEncryptedValue<TValue>(
- Utf8JsonWriter writer,
- TValue value
- )
- {
- logger?.LogTrace(
- "PassThroughRequest: {RawJson} ({TypeFullName})",
- JsonSerializer.Serialize(value: value, options: jsonSerializerOptionsForPassThroughMessageLogger),
- typeof(TValue).FullName
- );
-
- var stream = new MemoryStream(capacity: 256); // TODO: IMemoryAllocator
-
- using var encryptingStream = CreateEncryptingStream(stream);
-
- JsonSerializer.Serialize(
- utf8Json: encryptingStream,
- value: value,
- options: jsonSerializerOptionsForPassThroughMessage
- );
-
- encryptingStream.Close();
-
- if (!stream.TryGetBuffer(out var buffer))
- throw new InvalidOperationException("cannot get buffer from MemoryStream");
-
- writer.WriteBase64StringValue(buffer.AsSpan());
- }
-
- TValue IPassThroughObjectJsonConverter.ReadDecryptedValue<TValue>(
- ref Utf8JsonReader reader,
- Type typeToConvert
- )
- {
- if (!reader.TryGetBytesFromBase64(out var base64))
- throw new JsonException("could not read base64 string");
-
- var cryptographicExceptionThrown = false;
-
- try {
- using var stream = new MemoryStream(base64, writable: false);
- using var decryptingStream = CreateDecryptingStream(stream);
-
- try {
- return (TValue?)JsonSerializer.Deserialize(
- utf8Json: decryptingStream,
- returnType: typeToConvert,
- options: jsonSerializerOptionsForPassThroughMessage
- );
- }
- catch (CryptographicException ex) when (IsInvalidPaddingException(ex)) {
- cryptographicExceptionThrown = true;
- throw new SecurePassThroughInvalidPaddingException("Invalid padding detected in encrypted JSON", ex);
- }
- catch (CryptographicException ex) {
- cryptographicExceptionThrown = true;
- throw;
- }
-
- static bool IsInvalidPaddingException(CryptographicException ex)
- {
- // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx
- const string SR_Cryptography_InvalidPadding = "Padding is invalid and cannot be removed.";
-
- return SR_Cryptography_InvalidPadding.AsSpan().SequenceEqual(ex.Message); // TODO: consider localized message strings
- }
- }
- finally {
- if (!cryptographicExceptionThrown && logger is not null) {
- using var stream = new MemoryStream(base64, writable: false);
- using var decryptingStream = CreateDecryptingStream(stream);
-
- logger.LogTrace(
- "PassThroughResponse: {RawJson} ({TypeFullName})",
- new StreamReader(decryptingStream, Encoding.UTF8).ReadToEnd(),
- typeof(TValue).FullName
- );
- }
- }
- }
-
- private class PassThroughObjectJsonConverter<TPassThroughObject>
- : JsonConverter<TPassThroughObject>
- // where TPassThroughObject : ITapoPassThroughRequest or ITapoPassThroughResponse
- {
- private readonly IPassThroughObjectJsonConverter converter;
-
- public PassThroughObjectJsonConverter(IPassThroughObjectJsonConverter converter)
- {
- this.converter = converter;
- }
-
- public override void Write(
- Utf8JsonWriter writer,
- TPassThroughObject value,
- JsonSerializerOptions options
- )
- => converter.WriteEncryptedValue(writer, value);
-
- public override TPassThroughObject? Read(
- ref Utf8JsonReader reader,
- Type typeToConvert,
- JsonSerializerOptions options
- )
- => converter.ReadDecryptedValue<TPassThroughObject>(ref reader, typeToConvert);
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughRequest.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughRequest.cs
deleted file mode 100644
index 22451b4..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughRequest.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct SecurePassThroughRequest<TPassThroughRequest> :
- ITapoRequest
- where TPassThroughRequest : ITapoPassThroughRequest
-{
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method => "securePassthrough";
-
- [JsonPropertyName("params")]
- public RequestParams Params { get; }
-
- public readonly record struct RequestParams(
-#pragma warning disable SA1313
- [property: JsonPropertyName("request")]
- TPassThroughRequest PassThroughRequest
-#pragma warning restore SA1313
- );
-
- public SecurePassThroughRequest(TPassThroughRequest passThroughRequest)
- {
- Params = new(passThroughRequest);
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughResponse.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughResponse.cs
deleted file mode 100644
index 697c0a4..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughResponse.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct SecurePassThroughResponse<TPassThroughResponse> :
- ITapoResponse
- where TPassThroughResponse : ITapoPassThroughResponse
-{
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
-
- [JsonPropertyName("result")]
- public ResponseResult Result { get; init; }
-
- public SecurePassThroughResponse(
- ErrorCode errorCode,
- TPassThroughResponse passThroughResponse
- )
- {
- ErrorCode = errorCode;
- Result = new(passThroughResponse);
- }
-
- public readonly record struct ResponseResult(
-#pragma warning disable SA1313
- [property: JsonPropertyName("response")]
- TPassThroughResponse PassThroughResponse
-#pragma warning restore SA1313
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SetDeviceInfoRequest.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SetDeviceInfoRequest.cs
deleted file mode 100644
index f89a90d..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SetDeviceInfoRequest.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct SetDeviceInfoRequest<TParameters> : ITapoPassThroughRequest {
- [JsonPropertyName("method")]
- [JsonPropertyOrder(0)]
- public string Method => "set_device_info";
-
-#pragma warning disable CA1822
- [JsonPropertyName("requestTimeMils")]
- public long RequestTimeMilliseconds => 0L; // DateTimeOffset.Now.ToUnixTimeMilliseconds();
-#pragma warning restore CA1822
-
- [JsonPropertyName("terminalUUID")]
- public string TerminalUuid { get; }
-
- [JsonPropertyName("params")]
- public TParameters Parameters { get; }
-
- public SetDeviceInfoRequest(
- string terminalUuid,
- TParameters parameters
- )
- {
- TerminalUuid = terminalUuid;
- Parameters = parameters;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SetDeviceInfoResponse.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SetDeviceInfoResponse.cs
deleted file mode 100644
index 7cced70..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/SetDeviceInfoResponse.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-#pragma warning disable SA1313
-using System.Collections.Generic;
-using System.Text.Json.Serialization;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct SetDeviceInfoResponse : ITapoPassThroughResponse {
- [JsonPropertyName("error_code")]
- public ErrorCode ErrorCode { get; init; }
-
- [JsonPropertyName("result")]
- public ResponseResult Result { get; init; }
-
- public readonly record struct ResponseResult(
- [property: JsonExtensionData] IDictionary<string, object>? ExtraData
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.Authentication.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.Authentication.cs
deleted file mode 100644
index fe60a62..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.Authentication.cs
+++ /dev/null
@@ -1,237 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net.Http;
-using System.Security.Cryptography;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-#pragma warning disable IDE0040
-partial class TapoClient {
-#pragma warning restore IDE0040
- private const int KeyExchangeAlgorithmKeySizeInBytes = 128;
-
- private async ValueTask AuthenticateAsyncCore(
- ITapoCredentialIdentity identity,
- ITapoCredentialProvider credential,
- CancellationToken cancellationToken
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var prevSession = session;
-
- try {
- /*
- * handshake
- */
- logger?.LogDebug("Handshake starting: {EndPointUri}", endPointUri);
-
- session = await HandshakeAsync(identity, cancellationToken).ConfigureAwait(false);
-
- logger?.LogDebug("Handshake completed.");
-
- /*
- * login_device
- */
- logger?.LogDebug("Login starting: {EndPointUri}", endPointUri);
-
- var token = await LoginDeviceAsync(credential, cancellationToken).ConfigureAwait(false);
-
- logger?.LogDebug("Login completed, access token has issued: {Token}", token);
-
- /*
- * session established
- */
- session.SetToken(token);
-
- logger?.LogDebug("Request path and query for the session: '{PathAndQuery}'", session.RequestPathAndQuery);
-
- logger?.LogInformation(
- "Session established: {SessionRequestPathAndQuery}; {SessionIDPrefix}{SessionID}; expires on {ExpiresOn}",
- session.RequestPathAndQuery,
- TapoSessionCookieUtils.HttpCookiePrefixForSessionId,
- session.SessionId,
- session.ExpiresOn.ToString("o", provider: null)
- );
-
- prevSession?.Dispose();
- }
- catch {
- session?.Dispose();
- session = null;
-
- throw;
- }
- }
-
- private async ValueTask<TapoSession> HandshakeAsync(
- ITapoCredentialIdentity? identity,
- CancellationToken cancellationToken
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using var keyExchangeAlgorithm = RSA.Create(keySizeInBits: KeyExchangeAlgorithmKeySizeInBytes * 8);
-
- var publicKeyInfoPem =
-#if SYSTEM_SECURITY_CRYPTOGRAPHY_ASYMMETRICALGORITHM_EXPORTSUBJECTPUBLICKEYINFOPEM
- keyExchangeAlgorithm.ExportSubjectPublicKeyInfoPem();
-#else
- AsymmetricAlgorithmShim.ExportSubjectPublicKeyInfoPem(keyExchangeAlgorithm!);
-#endif
-
- logger?.LogTrace("[Handshake] Public key: {PublicKeyInfoPem}", publicKeyInfoPem);
-
- var baseTimeForExpiration = DateTime.Now;
- string base64EncryptedKey;
- string? sessionId;
- int? sessionTimeout;
-
- try {
- (var response, (sessionId, sessionTimeout)) = await PostPlainTextRequestAsync<
- HandshakeRequest,
- HandshakeResponse,
- (string?, int?)
- >(
- request: new(key: publicKeyInfoPem),
- jsonSerializerOptions: CommonJsonSerializerOptions,
- processHttpResponse: ParseSessionCookie,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
-
- if (response.Result.Key is null) {
- logger?.LogCritical("Could not exchange the key during handshaking.");
- throw new TapoAuthenticationException(
- message: $"Could not exchange the key during handshaking with the device at '{endPointUri}'.",
- endPoint: endPointUri
- );
- }
-
- base64EncryptedKey = response.Result.Key;
- }
- catch (TapoErrorResponseException ex) {
- logger?.LogCritical("Failed to handshake with error code {ErrorCode}.", (int)ex.ErrorCode);
- throw new TapoAuthenticationException(
- message: $"Failed to handshake with the device at '{endPointUri}' with error code {(int)ex.ErrorCode}.",
- endPoint: endPointUri,
- innerException: ex
- );
- }
- catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException exTimeout) {
- logger?.LogCritical("Failed to handshake due to timeout. ({ExceptionMessage})", ex.Message);
- throw new TapoAuthenticationException(
- message: $"Failed to handshake with the device at '{endPointUri}' due to timeout. ({ex.Message})",
- endPoint: endPointUri,
- innerException: exTimeout
- );
- }
-
- logger?.LogTrace("[Handshake] Exchanged key: {Key}", base64EncryptedKey);
- logger?.LogTrace("[Handshake] Session ID: {SessionId}", sessionId);
- logger?.LogTrace("[Handshake] Session timeout: {SessionTimeout}", sessionTimeout);
-
- var encryptedKey = Convert.FromBase64String(base64EncryptedKey);
-
- if (encryptedKey.Length != KeyExchangeAlgorithmKeySizeInBytes) {
- logger?.LogCritical("Exchanged unexpecting length of key");
- throw new TapoAuthenticationException(
- message: $"Exchanged unexpecting length of key from the device at '{endPointUri}'.",
- endPoint: endPointUri
- );
- }
-
- var keyBytes = keyExchangeAlgorithm.Decrypt(
- data: encryptedKey,
- padding: RSAEncryptionPadding.Pkcs1
- );
- var expiresOn = sessionTimeout.HasValue
- ? baseTimeForExpiration + TimeSpan.FromMinutes(sessionTimeout.Value)
- : DateTime.MaxValue;
-
- logger?.LogTrace(
- "[Handshake] Session expires on: {ExpiresOn}",
- expiresOn.ToString("o", provider: null)
- );
- logger?.LogTrace(
- "[Handshake] Key: {Key}",
-#if SYSTEM_CONVERT_TOHEXSTRING
- Convert.ToHexString(keyBytes.AsSpan(0, 16))
-#else
- Smdn.Formats.Hexadecimal.ToUpperCaseString(keyBytes.AsSpan(0, 16))
-#endif
- );
- logger?.LogTrace(
- "[Handshake] IV: {IV}",
-#if SYSTEM_CONVERT_TOHEXSTRING
- Convert.ToHexString(keyBytes.AsSpan(16, 16))
-#else
- Smdn.Formats.Hexadecimal.ToUpperCaseString(keyBytes.AsSpan(16, 16))
-#endif
- );
-
- return new(
- identity: identity,
- sessionId: sessionId,
- expiresOn: expiresOn,
- key: keyBytes.AsSpan(0, 16),
- iv: keyBytes.AsSpan(16, 16),
- baseJsonSerializerOptions: CommonJsonSerializerOptions,
- logger: logger
- );
-
- // TODO: logger
- static (string? SessionId, int? SessionTimeout) ParseSessionCookie(HttpResponseMessage response)
- => TapoSessionCookieUtils.TryGetCookie(response, out var sessionId, out var sessionTimeout)
- ? (sessionId, sessionTimeout)
- : default;
- }
-
- private async ValueTask<string> LoginDeviceAsync(
- ITapoCredentialProvider credential,
- CancellationToken cancellationToken
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- string token;
-
- try {
- var loginDeviceResponse = await SendRequestAsync<LoginDeviceRequest, LoginDeviceResponse>(
- request: new(credential: credential),
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
-
- token = loginDeviceResponse.Result.Token;
- }
- catch (TapoErrorResponseException ex) {
- throw new TapoAuthenticationException(
- message: $"Denied to initiate authorized session with the device at '{endPointUri}'. (error code: {(int)ex.ErrorCode})",
- endPoint: endPointUri,
- innerException: ex
- );
- }
- catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException exTimeout) {
- logger?.LogCritical("Failed to initiate authorized session due to timeout. ({ExceptionMessage})", ex.Message);
- throw new TapoAuthenticationException(
- message: $"Failed to initiate authorized session with the device at '{endPointUri}' due to timeout. ({ex.Message})",
- endPoint: endPointUri,
- innerException: exTimeout
- );
- }
-
- if (string.IsNullOrEmpty(token)) {
- logger?.LogError("Access token has not been issued.");
- throw new TapoAuthenticationException(
- message: $"An access token was not issued from the device at '{endPointUri}'.",
- endPoint: endPointUri
- );
- }
-
- return token;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.Http.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.Http.cs
deleted file mode 100644
index 4643604..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.Http.cs
+++ /dev/null
@@ -1,156 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Linq;
-using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Net.Http.Json;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-#pragma warning disable IDE0040
-partial class TapoClient {
-#pragma warning restore IDE0040
- private static readonly MediaTypeHeaderValue mediaTypeJson = new(mediaType: "application/json");
-
- public TimeSpan? Timeout { get; set; }
-
- private async ValueTask<TResponse> PostSecurePassThroughRequestAsync<TRequest, TResponse>(
- TRequest request,
- JsonSerializerOptions jsonSerializerOptions,
- CancellationToken cancellationToken
- )
- where TRequest : ITapoPassThroughRequest
- where TResponse : ITapoPassThroughResponse
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- logger?.LogDebug("Request: {Request}", JsonSerializer.Serialize(request, jsonSerializerOptions));
-
- var (securePassThroughResponse, requestUri) = await PostPlainTextRequestAsync<
- SecurePassThroughRequest<TRequest>,
- SecurePassThroughResponse<TResponse>,
- Uri
- >(
- request: new(passThroughRequest: request),
- jsonSerializerOptions: jsonSerializerOptions,
- processHttpResponse: static httpResponse => httpResponse.RequestMessage.RequestUri,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
-
- var response = securePassThroughResponse.Result.PassThroughResponse;
-
- logger?.LogDebug("Respose error code: {ErrorCode}", (int)response.ErrorCode);
-
- TapoErrorResponseException.ThrowIfError(
- requestUri,
- request.Method,
- response.ErrorCode
- );
-
- return response;
- }
-
- private async ValueTask<(
- TResponse? Response,
- THttpResult? HttpResult
- )>
- PostPlainTextRequestAsync<TRequest, TResponse, THttpResult>(
- TRequest request,
- JsonSerializerOptions jsonSerializerOptions,
- Func<HttpResponseMessage, THttpResult?>? processHttpResponse,
- CancellationToken cancellationToken
- )
- where TRequest : ITapoRequest
- where TResponse : ITapoResponse
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- logger?.LogDebug("HTTP Transaction: Session={SessionId}, Token={Token}", session?.SessionId, session?.Token);
-
- using var requestContent = JsonContent.Create(
- inputValue: request,
- mediaType: mediaTypeJson,
- options: jsonSerializerOptions
- );
-
- if (session?.SessionId is not null) {
- requestContent.Headers.Add(
- "Cookie",
- string.Concat(TapoSessionCookieUtils.HttpCookiePrefixForSessionId, session.SessionId)
- );
- }
-
- // Disables 'chunked' transfer encoding
- // The HTTP server inside of the Tapo devices does not seem to support 'chunked' transfer encoding.
- // To prevent content from being transferred by 'chunked', serialize the content onto a memory buffer
- // and ensure the HttpClient to calculate Content-Length before transferring.
- //
- // ref:
- // https://github.com/dotnet/runtime/issues/49357
- // https://github.com/dotnet/runtime/issues/70793
- await requestContent.LoadIntoBufferAsync().ConfigureAwait(false);
-
- var requestUri = session is null ? TapoSession.RequestPath : session.RequestPathAndQuery;
-
- logger?.LogTrace("HTTP Request URI: {RequestUri}", requestUri);
- logger?.LogTrace(
- "HTTP Request headers: {RequestHeaders}",
- string.Join(" ", requestContent.Headers.Select(static header => string.Concat(header.Key, ": ", string.Join("; ", header.Value))))
- );
- logger?.LogTrace(
- "HTTP Request content: {RequestContent}",
- await requestContent.ReadAsStringAsync().ConfigureAwait(false)
- );
-
- using var httpClient = httpClientFactory.CreateClient(
- name: string.Concat(nameof(TapoClient), " (", endPointUri, ")")
- );
-
- httpClient.BaseAddress = endPointUri;
-
- if (Timeout.HasValue)
- // override timeout value configured by IHttpClientFactory
- httpClient.Timeout = Timeout.Value;
-
- using var httpResponse = await httpClient.PostAsync(
- requestUri: requestUri,
- content: requestContent,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
-
- var httpResult = processHttpResponse is null
- ? default
- : processHttpResponse(httpResponse);
-
- logger?.LogTrace(
- "HTTP Response status: {ResponseStatusCode} {ResponseReasonPhrase}",
- (int)httpResponse.StatusCode,
- httpResponse.ReasonPhrase
- );
- logger?.LogTrace(
- "HTTP Response headers: {ResponseHeaders}",
- string.Join(" ", httpResponse.Content.Headers.Concat(httpResponse.Headers).Select(static header => string.Concat(header.Key, ": ", string.Join("; ", header.Value))))
- );
- logger?.LogTrace("HTTP Response content: {ResponseContent}", await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false));
-
- httpResponse.EnsureSuccessStatusCode();
-
- var response = await httpResponse.Content.ReadFromJsonAsync<TResponse>(
- cancellationToken: cancellationToken,
- options: jsonSerializerOptions
- ).ConfigureAwait(false);
-
- TapoErrorResponseException.ThrowIfError(
- httpResponse.RequestMessage.RequestUri,
- request.Method,
- response.ErrorCode
- );
-
- return (response, httpResult);
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.cs
deleted file mode 100644
index 0bb2d04..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClient.cs
+++ /dev/null
@@ -1,184 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.Http;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-/// <remarks>
-/// This implementation is based on and ported from the following implementation: <see href="https://github.com/fishbigger/TapoP100">fishbigger/TapoP100</see>.
-/// </remarks>
-public sealed partial class TapoClient : IDisposable {
- public const int DefaultPort = 80; // HTTP
-
- internal static Uri GetEndPointUri(EndPoint endPoint)
- {
- // 'http://<endPoint.Host>:<endPoint.Port>/'
- var uriBuilder = endPoint switch {
- null => throw new ArgumentNullException(nameof(endPoint)),
- DnsEndPoint dnsEndPoint => new UriBuilder() {
- Host = dnsEndPoint.Host,
- Port = dnsEndPoint.Port == 0 ? -1 : dnsEndPoint.Port,
- },
- IPEndPoint ipEndPoint => new UriBuilder() {
- Host = ipEndPoint.Address.ToString(),
- Port = ipEndPoint.Port == 0 ? -1 : ipEndPoint.Port,
- },
- _ => new UriBuilder() {
- Host = endPoint.ToString(), // XXX
- Port = -1,
- },
- };
-
- uriBuilder.Scheme = Uri.UriSchemeHttp;
-
- return uriBuilder.Uri;
- }
-
- private static readonly JsonSerializerOptions CommonJsonSerializerOptions = new() {
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
-
-#if false
- // disable encoding '+' in base64 strings
- // ref: https://github.com/dotnet/runtime/issues/35281
- // Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
-#endif
- };
-
- /*
- * instance members
- */
- private bool IsDisposed => httpClientFactory is null;
-
- private IHttpClientFactory? httpClientFactory; // if null, it indicates a 'disposed' state.
- private readonly Uri endPointUri;
- private readonly EndPoint endPoint;
- private readonly ILogger? logger;
- private TapoSession? session;
-
- public TapoSession? Session {
- get {
- ThrowIfDisposed();
- return session;
- }
- }
-
- public Uri EndPointUri {
- get {
- ThrowIfDisposed();
- return endPointUri;
- }
- }
-
- internal EndPoint EndPoint {
- get {
- ThrowIfDisposed();
- return endPoint;
- }
- }
-
- internal ILogger? Logger {
- get {
- ThrowIfDisposed();
- return logger;
- }
- }
-
- public TapoClient(
- EndPoint endPoint,
- IHttpClientFactory? httpClientFactory = null,
- ILogger? logger = null
- )
- {
- this.endPoint = endPoint ?? throw new ArgumentNullException(nameof(endPoint));
- this.logger = logger;
-
- endPointUri = GetEndPointUri(endPoint);
-
- logger?.LogTrace("Device end point: {DeviceEndPointUri}", endPointUri);
-
- this.httpClientFactory = httpClientFactory ?? TapoHttpClientFactory.Default;
-
- logger?.LogTrace("IHttpClientFactory: {IHttpClientFactory}", this.httpClientFactory.GetType().FullName);
- }
-
- private void Dispose(bool disposing)
- {
- if (!disposing)
- return;
-
- session?.Dispose();
- session = null;
-
- httpClientFactory = null;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void ThrowIfDisposed()
- {
- if (IsDisposed)
- throw new ObjectDisposedException(GetType().FullName);
- }
-
- public ValueTask AuthenticateAsync(
- ITapoCredentialIdentity? identity,
- ITapoCredentialProvider credential,
- CancellationToken cancellationToken = default
- )
- {
- if (credential is null)
- throw new ArgumentNullException(nameof(credential));
-
- ThrowIfDisposed();
-
- return AuthenticateAsyncCore(
- identity,
- credential,
- cancellationToken
- );
- }
-
- public ValueTask<TResponse> SendRequestAsync<TRequest, TResponse>(
- CancellationToken cancellationToken = default
- )
- where TRequest : ITapoPassThroughRequest, new()
- where TResponse : ITapoPassThroughResponse
- => SendRequestAsync<TRequest, TResponse>(
- request: new(),
- cancellationToken: cancellationToken
- );
-
- public ValueTask<TResponse> SendRequestAsync<TRequest, TResponse>(
- TRequest request,
- CancellationToken cancellationToken = default
- )
- where TRequest : ITapoPassThroughRequest
- where TResponse : ITapoPassThroughResponse
- {
- if (request is null)
- throw new ArgumentNullException(nameof(request));
-
- ThrowIfDisposed();
-
- if (session is null)
- throw new InvalidOperationException("The session for this instance has not been established.");
-
- return PostSecurePassThroughRequestAsync<TRequest, TResponse>(
- request: request,
- jsonSerializerOptions: session.SecurePassThroughJsonSerializerOptions,
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientDefaultExceptionHandler.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientDefaultExceptionHandler.cs
deleted file mode 100644
index 9b4999c..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientDefaultExceptionHandler.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Net.Sockets;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-internal class TapoClientDefaultExceptionHandler : TapoClientExceptionHandler {
- public override TapoClientExceptionHandling DetermineHandling(
- TapoDevice device,
- Exception exception,
- int attempt,
- ILogger? logger
- )
- {
- switch (exception) {
- case HttpRequestException httpRequestException:
- if (httpRequestException.InnerException is SocketException innerSocketException) {
- var socketErrorCode = innerSocketException.SocketErrorCode;
-
- if (
- socketErrorCode is
- SocketError.ConnectionRefused or // ECONNREFUSED
- SocketError.HostUnreachable or // EHOSTUNREACH
- SocketError.NetworkUnreachable // ENETUNREACH
- ) {
- if (attempt == 0 /* retry just once */) {
- // The end point may have changed.
- logger?.LogInformation($"Endpoint may have changed ({nameof(SocketError)}: {(int)socketErrorCode} {socketErrorCode})");
-
- return TapoClientExceptionHandling.InvalidateEndPointAndRetry;
- }
- else {
- logger?.LogError($"Endpoint unreachable ({nameof(SocketError)}: {(int)socketErrorCode} {socketErrorCode})");
-
- return TapoClientExceptionHandling.InvalidateEndPointAndThrow;
- }
- }
-
- // The HTTP client may have been invalid due to an exception at the transport layer.
- logger?.LogError(innerSocketException, $"Unexpected socket exception ({nameof(SocketError)}: {(int)socketErrorCode} {socketErrorCode})");
-
- return TapoClientExceptionHandling.Throw;
- }
-
- return TapoClientExceptionHandling.Throw;
-
- case SecurePassThroughInvalidPaddingException securePassThroughInvalidPaddingException:
- if (attempt == 0 /* retry just once */) {
- logger?.LogWarning(securePassThroughInvalidPaddingException.Message);
-
- // The session might have been in invalid state(?)
- return TapoClientExceptionHandling.RetryAfterReconnect;
- }
-
- return TapoClientExceptionHandling.ThrowAsTapoProtocolException;
-
- case TapoErrorResponseException errorResponseException:
- if (attempt == 0 /* retry just once */) {
- switch (errorResponseException.ErrorCode) {
- case TapoErrorCodes.DeviceBusy:
- logger?.LogWarning(errorResponseException.Message);
-
- // The session might have been in invalid state(?)
- return TapoClientExceptionHandling.CreateRetry(
- retryAfter: TimeSpan.FromSeconds(2.0),
- shouldReconnect: true
- );
-
- case TapoErrorCodes.RequestParameterError:
- logger?.LogWarning(errorResponseException.Message);
- return TapoClientExceptionHandling.Throw;
-
- default:
- logger?.LogWarning($"Unexpected error ({nameof(errorResponseException.ErrorCode)}: {(int)errorResponseException.ErrorCode})");
-
- // The session might have been in invalid state(?)
- return TapoClientExceptionHandling.RetryAfterReconnect;
- }
- }
-
- return TapoClientExceptionHandling.Throw;
-
- case TaskCanceledException taskCanceledException:
- if (taskCanceledException.InnerException is TimeoutException) {
- if (attempt < 2 /* retry up to 3 times */) {
- logger?.LogWarning("Request timed out; {ExceptionMessage}", taskCanceledException.Message);
- return TapoClientExceptionHandling.Retry;
- }
-
- logger?.LogError(taskCanceledException, "Request timed out");
-
- return TapoClientExceptionHandling.ThrowAsTapoProtocolException;
- }
-
- return TapoClientExceptionHandling.Throw;
-
- default:
- logger?.LogError(exception, $"Unhandled exception ({exception.GetType().FullName})");
- return TapoClientExceptionHandling.Throw;
- }
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientExceptionHandler.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientExceptionHandler.cs
deleted file mode 100644
index e74f149..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientExceptionHandler.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using Microsoft.Extensions.Logging;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public abstract class TapoClientExceptionHandler {
- protected internal static readonly TapoClientExceptionHandler Default = new TapoClientDefaultExceptionHandler();
-
- public abstract TapoClientExceptionHandling DetermineHandling(
- TapoDevice device,
- Exception exception,
- int attempt,
- ILogger? logger
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientExceptionHandling.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientExceptionHandling.cs
deleted file mode 100644
index 03b40a4..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoClientExceptionHandling.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public readonly struct TapoClientExceptionHandling {
- public static readonly TapoClientExceptionHandling Throw = default;
- public static readonly TapoClientExceptionHandling ThrowAsTapoProtocolException = new() { ShouldWrapIntoTapoProtocolException = true };
- public static readonly TapoClientExceptionHandling InvalidateEndPointAndThrow = new() { ShouldInvalidateEndPoint = true };
-
- public static readonly TapoClientExceptionHandling Retry = new() { ShouldRetry = true };
- public static readonly TapoClientExceptionHandling RetryAfterReconnect = new() { ShouldRetry = true, ShouldReconnect = true };
- public static readonly TapoClientExceptionHandling InvalidateEndPointAndRetry = new() { ShouldRetry = true, ShouldInvalidateEndPoint = true, };
-
- public static TapoClientExceptionHandling CreateRetry(
- TimeSpan retryAfter,
- bool shouldReconnect = false
- )
- => new() {
- ShouldRetry = true,
- RetryAfter = retryAfter,
- ShouldReconnect = shouldReconnect,
- };
-
- public bool ShouldRetry { get; init; }
- public TimeSpan RetryAfter { get; init; }
- public bool ShouldReconnect { get; init; }
- public bool ShouldWrapIntoTapoProtocolException { get; init; }
- public bool ShouldInvalidateEndPoint { get; init; }
-
- public override string ToString() => $"{{{nameof(ShouldRetry)}={ShouldRetry}, {nameof(RetryAfter)}={RetryAfter}, {nameof(ShouldReconnect)}={ShouldReconnect}, {nameof(ShouldWrapIntoTapoProtocolException)}={ShouldWrapIntoTapoProtocolException}, {nameof(ShouldInvalidateEndPoint)}={ShouldInvalidateEndPoint}}}";
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoErrorCodes.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoErrorCodes.cs
deleted file mode 100644
index fac7838..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoErrorCodes.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-// The situation represented by the following error codes are not well-confirmed.
-// Therefore, these error codes cannot be made public APIs.
-internal static class TapoErrorCodes {
- public const ErrorCode DeviceBusy = (ErrorCode)(-1301);
- public const ErrorCode RequestParameterError = (ErrorCode)(-1008);
- // -1012: ???
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoHttpClientFactory.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoHttpClientFactory.cs
deleted file mode 100644
index 5d3682d..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoHttpClientFactory.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.Http;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-internal class TapoHttpClientFactory : IHttpClientFactory {
- private static HttpMessageHandler CreateHandler()
- =>
-#if NETCOREAPP2_1_OR_GREATER || NET5_0_OR_GREATER
- new SocketsHttpHandler()
-#else
- new HttpClientHandler()
-#endif
- {
- AllowAutoRedirect = false,
- AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
- MaxConnectionsPerServer = 1,
- UseCookies = false,
- };
-
- private static HttpMessageHandler DefaultHandler { get; } = CreateHandler();
-
- public static IHttpClientFactory Default { get; } = new TapoHttpClientFactory(
- configureClient: null
- );
-
- /*
- * instance members
- */
- private readonly Action<HttpClient>? configureClient;
-
- internal TapoHttpClientFactory(
- Action<HttpClient>? configureClient
- )
- {
- this.configureClient = configureClient;
- }
-
- public HttpClient CreateClient(string name)
- {
- var client = new HttpClient(
- handler: DefaultHandler,
- disposeHandler: false
- ) {
- Timeout = TimeSpan.FromSeconds(20),
- };
-
- configureClient?.Invoke(client);
-
- return client;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSession.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSession.cs
deleted file mode 100644
index 85fb0e8..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSession.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Security.Cryptography;
-using System.Text.Json;
-using Microsoft.Extensions.Logging;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public sealed class TapoSession : IDisposable {
- internal static readonly Uri RequestPath = new("/app", UriKind.Relative);
-
- public Uri RequestPathAndQuery { get; private set; } = RequestPath;
- public string? Token { get; private set; }
- public string? SessionId { get; }
- public DateTime ExpiresOn { get; }
- public bool HasExpired => ExpiresOn <= DateTime.Now;
-
- private Aes? aes;
- private SecurePassThroughJsonConverterFactory? securePassThroughJsonConverterFactory;
-
- internal JsonSerializerOptions SecurePassThroughJsonSerializerOptions { get; }
-
- internal TapoSession(
- ITapoCredentialIdentity? identity,
- string? sessionId,
- DateTime expiresOn,
- ReadOnlySpan<byte> key,
- ReadOnlySpan<byte> iv,
- JsonSerializerOptions baseJsonSerializerOptions,
- ILogger? logger
- )
- {
- SessionId = sessionId;
- ExpiresOn = expiresOn;
-
- aes = Aes.Create();
- aes.Mode = CipherMode.CBC;
- aes.Padding = PaddingMode.PKCS7;
- aes.Key = key.ToArray();
- aes.IV = iv.ToArray();
-
- securePassThroughJsonConverterFactory = new(
- identity: identity,
- encryptorForPassThroughRequest: aes.CreateEncryptor(),
- decryptorForPassThroughResponse: aes.CreateDecryptor(),
- baseJsonSerializerOptionsForPassThroughMessage: baseJsonSerializerOptions,
- logger: logger
- );
-
- SecurePassThroughJsonSerializerOptions = new(baseJsonSerializerOptions);
- SecurePassThroughJsonSerializerOptions.Converters.Add(securePassThroughJsonConverterFactory);
- }
-
- public void Dispose()
- {
- securePassThroughJsonConverterFactory?.Dispose();
- securePassThroughJsonConverterFactory = null;
-
- aes?.Dispose();
- aes = null;
- }
-
- internal void SetToken(string token)
- {
- Token = token;
-
- // append issued token to the request path query
- RequestPathAndQuery = new Uri(
- string.Concat(
- RequestPath.ToString(), // only path
- "?token=",
- token
- ),
- UriKind.Relative
- );
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSessionCookieUtils.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSessionCookieUtils.cs
deleted file mode 100644
index 279ae37..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSessionCookieUtils.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Net.Http;
-
-namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-public static class TapoSessionCookieUtils {
- private const string HeaderNameSetCookie = "Set-Cookie";
-
- // Format and example of Set-Cookie header sent back by the Tapo HTTP server.
- // Set-Cookie: TP_SESSIONID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;TIMEOUT=1440
- internal static readonly string HttpCookiePrefixForSessionId = "TP_SESSIONID="; // TP_SESSIONID=XXXXXX
- internal static readonly string HttpCookieAttributePrefixForTimeout = "TIMEOUT="; // TIMEOUT=xxxx
-
- public static bool TryGetCookie(
- HttpResponseMessage response,
- out string? sessionId,
- out int? sessionTimeout
- )
- {
- sessionId = default;
- sessionTimeout = default;
-
- return
- response.Headers.TryGetValues(HeaderNameSetCookie, out var setCookieValues) &&
- TryGetCookie(setCookieValues, out sessionId, out sessionTimeout);
- }
-
- public static bool TryGetCookie(
- IEnumerable<string>? cookieValues,
- out string? sessionId,
- out int? sessionTimeout
- )
- {
- sessionId = default;
- sessionTimeout = default;
-
- var cookieValueOfTPSessionId = cookieValues?.FirstOrDefault(
- static val => val.StartsWith(HttpCookiePrefixForSessionId, StringComparison.Ordinal)
- );
-
- return
- cookieValueOfTPSessionId is not null &&
- TryParseCookie(
- cookieValueOfTPSessionId,
- id: out sessionId,
- timeout: out sessionTimeout
- );
- }
-
- // We cannot use System.Net.Cookie and its relevant collection classes to handle cookies
- // since the Tapo HTTP server uses non-standard cookie attribute 'TIMEOUT'.
- public static bool TryParseCookie(
- ReadOnlySpan<char> cookie,
- out string? id,
- out int? timeout
- )
- {
- id = default;
- timeout = default;
-
- var indexOfSessionIdPrefix = cookie.IndexOf(HttpCookiePrefixForSessionId, StringComparison.Ordinal);
-
- if (indexOfSessionIdPrefix < 0)
- return false;
-
- var cookieName = cookie.Slice(indexOfSessionIdPrefix + HttpCookiePrefixForSessionId.Length);
- var indexOfCookieNameTerminator = cookieName.IndexOf(';');
-
- ReadOnlySpan<char> cookieAttributes;
-
- if (0 <= indexOfCookieNameTerminator) {
- id = cookieName.Slice(0, indexOfCookieNameTerminator).TrimEnd().ToString(); // for the case of "TP_SESSIONID=XXXXXXXXXX ;"
-
- if (id.Length == 0)
- return false;
-
- cookieAttributes = cookie.Slice(indexOfSessionIdPrefix + HttpCookiePrefixForSessionId.Length + indexOfCookieNameTerminator);
- }
- else {
- id = cookieName.TrimEnd().ToString(); // for the case of "TP_SESSIONID=XXXXXXXXXX"
- return id.Length != 0; // no attributes
- }
-
- var indexOfTimeoutPrefix = cookieAttributes.IndexOf(HttpCookieAttributePrefixForTimeout, StringComparison.Ordinal);
-
- if (0 <= indexOfTimeoutPrefix) {
- var timeoutAttributeValue = cookieAttributes.Slice(indexOfTimeoutPrefix + HttpCookieAttributePrefixForTimeout.Length);
- var indexOfTimeoutTerminator = timeoutAttributeValue.IndexOf(';');
-
- if (0 <= indexOfTimeoutTerminator)
- // for the case of "TIMEOUT=XXXXXXXXXX ;"
- // no need to trim whitespaces here since NumberStyles.Integer trims leading and trailing white spaces
- timeoutAttributeValue = timeoutAttributeValue.Slice(0, indexOfTimeoutTerminator);
- else
- timeoutAttributeValue = timeoutAttributeValue.TrimEnd(); // for the case of "TIMEOUT=XXXXXXXXXX"
-
- const NumberStyles timeoutNumberStyles = NumberStyles.AllowTrailingWhite;
-
- if (int.TryParse(timeoutAttributeValue, timeoutNumberStyles, provider: null, out var to))
- timeout = to;
- }
-
- return true;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/L530.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/L530.cs
deleted file mode 100644
index c949754..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/L530.cs
+++ /dev/null
@@ -1,258 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public class L530 : TapoDevice {
- /// <summary>
- /// Initializes a new instance of the <see cref="L530"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" />
- public L530(
- string host,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- : base(
- host: host,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L530"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, IServiceProvider)" />
- public L530(
- string host,
- IServiceProvider serviceProvider
- )
- : base(
- host: host,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L530"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" />
- public L530(
- IPAddress ipAddress,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- : base(
- ipAddress: ipAddress,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L530"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(IPAddress, IServiceProvider)" />
- public L530(
- IPAddress ipAddress,
- IServiceProvider serviceProvider
- )
- : base(
- ipAddress: ipAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L530"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, string, string, IServiceProvider?)" />
- public L530(
- PhysicalAddress macAddress,
- string email,
- string password,
- IServiceProvider serviceProvider
- )
- : base(
- macAddress: macAddress,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L530"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, IServiceProvider)" />
- public L530(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- : base(
- macAddress: macAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L530"/> class.
- /// </summary>
- /// <inheritdoc
- /// cref="TapoDevice(IDeviceEndPointProvider, ITapoCredentialProvider?, Protocol.TapoClientExceptionHandler?, IServiceProvider?)"
- /// path="/exception | /param[@name='deviceEndPointProvider' or @name='credential' or @name='serviceProvider']"
- /// />
- public L530(
- IDeviceEndPointProvider deviceEndPointProvider,
- ITapoCredentialProvider? credential = null,
- IServiceProvider? serviceProvider = null
- )
- : base(
- deviceEndPointProvider: deviceEndPointProvider,
- credential: credential,
- serviceProvider: serviceProvider
- )
- {
- }
-
-#pragma warning disable SA1313, CA1822
- private readonly record struct SetBrightnessParameter(
- [property: JsonPropertyName("brightness")] int Brightness
- ) {
- [JsonPropertyName("device_on")]
- public bool DeviceOn => true;
- }
-
- private readonly record struct SetColorTemperatureParameter(
- [property: JsonPropertyName("color_temp")] int Temperature,
- [property: JsonPropertyName("brightness")] int? Brightness
- ) {
- [JsonPropertyName("device_on")]
- public bool DeviceOn => true;
- }
-
- private readonly record struct SetColorParameter(
- [property: JsonPropertyName("hue")] int? Hue,
- [property: JsonPropertyName("saturation")] int? Saturation,
- [property: JsonPropertyName("brightness")] int? Brightness
- ) {
- [JsonPropertyName("device_on")]
- public bool DeviceOn => true;
- }
-#pragma warning restore SA1313, CA1822
-
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%].
- /// </param>
- public ValueTask SetBrightnessAsync(
- int brightness,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetBrightnessParameter(
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness)).Value
- ),
- cancellationToken
- );
-
- /// <param name="colorTemperature">
- /// The color temperature in kelvin, in range of 2500~6500[K].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorTemperatureAsync(
- int colorTemperature,
- int? brightness = null,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetColorTemperatureParameter(
- Temperature: MulticolorLightUtils.ValidateColorTemperatureValue(colorTemperature, nameof(colorTemperature)),
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness))
- ),
- cancellationToken
- );
-
- /// <param name="hue">
- /// The hue of the color in degree, in range of 0~360[°].
- /// </param>
- /// <param name="saturation">
- /// The saturation of the color in percent value, in range of 0~100[%].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorAsync(
- int hue,
- int saturation,
- int? brightness = null,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetColorParameter(
- Hue: MulticolorLightUtils.ValidateHueValue(hue, nameof(hue)),
- Saturation: MulticolorLightUtils.ValidateSaturationValue(saturation, nameof(saturation)),
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness))
- ),
- cancellationToken
- );
-
- /// <param name="hue">
- /// The hue of the color in degree, in range of 0~360[°].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorHueAsync(
- int hue,
- int? brightness = null,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetColorParameter(
- Hue: MulticolorLightUtils.ValidateHueValue(hue, nameof(hue)),
- Saturation: null,
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness))
- ),
- cancellationToken
- );
-
- /// <param name="saturation">
- /// The saturation of the color in percent value, in range of 0~100[%].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorSaturationAsync(
- int saturation,
- int? brightness = null,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetColorParameter(
- Hue: null,
- Saturation: MulticolorLightUtils.ValidateSaturationValue(saturation, nameof(saturation)),
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness))
- ),
- cancellationToken
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/L900.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/L900.cs
deleted file mode 100644
index 501b03e..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/L900.cs
+++ /dev/null
@@ -1,243 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public class L900 : TapoDevice {
- /// <summary>
- /// Initializes a new instance of the <see cref="L900"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" />
- public L900(
- string host,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- : base(
- host: host,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L900"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, IServiceProvider)" />
- public L900(
- string host,
- IServiceProvider serviceProvider
- )
- : base(
- host: host,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L900"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" />
- public L900(
- IPAddress ipAddress,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- : base(
- ipAddress: ipAddress,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L900"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(IPAddress, IServiceProvider)" />
- public L900(
- IPAddress ipAddress,
- IServiceProvider serviceProvider
- )
- : base(
- ipAddress: ipAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L900"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, string, string, IServiceProvider?)" />
- public L900(
- PhysicalAddress macAddress,
- string email,
- string password,
- IServiceProvider serviceProvider
- )
- : base(
- macAddress: macAddress,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L900"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, IServiceProvider)" />
- public L900(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- : base(
- macAddress: macAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="L900"/> class.
- /// </summary>
- /// <inheritdoc
- /// cref="TapoDevice(IDeviceEndPointProvider, ITapoCredentialProvider?, Protocol.TapoClientExceptionHandler?, IServiceProvider?)"
- /// path="/exception | /param[@name='deviceEndPointProvider' or @name='credential' or @name='serviceProvider']"
- /// />
- public L900(
- IDeviceEndPointProvider deviceEndPointProvider,
- ITapoCredentialProvider? credential = null,
- IServiceProvider? serviceProvider = null
- )
- : base(
- deviceEndPointProvider: deviceEndPointProvider,
- credential: credential,
- serviceProvider: serviceProvider
- )
- {
- }
-
-#pragma warning disable SA1313, CA1822
- internal readonly record struct LightingEffectParameter(
- [property: JsonPropertyName("enable")] int Enable
- ) {
- public static readonly LightingEffectParameter Disabled = new(0);
- }
-
- private readonly record struct SetBrightnessParameter(
- [property: JsonPropertyName("brightness")] int Brightness
- ) {
- [JsonPropertyName("device_on")]
- public bool DeviceOn => true;
-
- [JsonPropertyName("lighting_effect")]
- public LightingEffectParameter LightingEffect => LightingEffectParameter.Disabled;
- }
-
- private readonly record struct SetColorParameter(
- [property: JsonPropertyName("hue")] int? Hue,
- [property: JsonPropertyName("saturation")] int? Saturation,
- [property: JsonPropertyName("brightness")] int? Brightness
- ) {
- [JsonPropertyName("device_on")]
- public bool DeviceOn => true;
-
- [JsonPropertyName("lighting_effect")]
- public LightingEffectParameter LightingEffect => LightingEffectParameter.Disabled;
- }
-#pragma warning restore SA1313, CA1822
-
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%].
- /// </param>
- public ValueTask SetBrightnessAsync(
- int brightness,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetBrightnessParameter(
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness)).Value
- ),
- cancellationToken
- );
-
- /// <param name="hue">
- /// The hue of the color in degree, in range of 0~360[°].
- /// </param>
- /// <param name="saturation">
- /// The saturation of the color in percent value, in range of 0~100[%].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorAsync(
- int hue,
- int saturation,
- int? brightness = null,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetColorParameter(
- Hue: MulticolorLightUtils.ValidateHueValue(hue, nameof(hue)),
- Saturation: MulticolorLightUtils.ValidateSaturationValue(saturation, nameof(saturation)),
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness))
- ),
- cancellationToken
- );
-
- /// <param name="hue">
- /// The hue of the color in degree, in range of 0~360[°].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorHueAsync(
- int hue,
- int? brightness,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetColorParameter(
- Hue: MulticolorLightUtils.ValidateHueValue(hue, nameof(hue)),
- Saturation: null,
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness))
- ),
- cancellationToken
- );
-
- /// <param name="saturation">
- /// The saturation of the color in percent value, in range of 0~100[%].
- /// </param>
- /// <param name="brightness">
- /// The brightness in percent value, in range of 1~100[%]. If <see langword="null"/>, the current brightness will be kept.
- /// </param>
- public ValueTask SetColorSaturationAsync(
- int saturation,
- int? brightness = null,
- CancellationToken cancellationToken = default
- )
- => SetDeviceInfoAsync(
- new SetColorParameter(
- Hue: null,
- Saturation: MulticolorLightUtils.ValidateSaturationValue(saturation, nameof(saturation)),
- Brightness: MulticolorLightUtils.ValidateBrightnessValue(brightness, nameof(brightness))
- ),
- cancellationToken
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/MulticolorLightUtils.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/MulticolorLightUtils.cs
deleted file mode 100644
index 6a720e7..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/MulticolorLightUtils.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-#if NULL_STATE_STATIC_ANALYSIS_ATTRIBUTES
-using System.Diagnostics.CodeAnalysis;
-#endif
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-internal static class MulticolorLightUtils {
- private const int BrightnessMinValue = 1;
- private const int BrightnessMaxValue = 100;
-
- private const int HueMinValue = 0;
- private const int HueMaxValue = 360;
-
- private const int SaturationMinValue = 0;
- private const int SaturationMaxValue = 100;
-
- private static readonly string BrightnessVallueOutOfRangeExceptionMessage =
- $"The value for brightness must be in range of {BrightnessMinValue}~{BrightnessMaxValue}";
-
- private static readonly string HueValueOutOfRangeExceptionMessage =
- $"The value for hue must be in range of {HueMinValue}~{HueMaxValue}";
-
- private static readonly string SaturationValueOutOfRangeExceptionMessage =
- $"The value for saturation must be in range of {SaturationMinValue}~{SaturationMaxValue}";
-
-#if false // TODO
- private static readonly string ColorTemperatureValueOutOfRangeExceptionMessage =
- "The value for saturation must be in range of {ColorTemperatureMinValue}~{ColorTemperatureMaxValue}";
-#endif
-
-#if LANG_VERSION_11_OR_GREATER && NULL_STATE_STATIC_ANALYSIS_ATTRIBUTES
- [return: NotNullIfNotNull(nameof(newValue))]
-#endif
- public static int? ValidateBrightnessValue(int? newValue, string paramName)
- {
- if (newValue == null)
- return null;
-
- if (newValue.Value is < BrightnessMinValue or > BrightnessMaxValue) {
- throw new ArgumentOutOfRangeException(
- paramName: paramName,
- actualValue: newValue.Value,
- message: BrightnessVallueOutOfRangeExceptionMessage
- );
- }
-
- return newValue;
- }
-
-#if LANG_VERSION_11_OR_GREATER && NULL_STATE_STATIC_ANALYSIS_ATTRIBUTES
- [return: NotNullIfNotNull(nameof(newValue))]
-#endif
- public static int? ValidateHueValue(int? newValue, string paramName)
- {
- if (newValue == null)
- return null;
-
- if (newValue.Value is < HueMinValue or > HueMaxValue) {
- throw new ArgumentOutOfRangeException(
- paramName: paramName,
- actualValue: newValue.Value,
- message: HueValueOutOfRangeExceptionMessage
- );
- }
-
- return newValue;
- }
-
-#if LANG_VERSION_11_OR_GREATER && NULL_STATE_STATIC_ANALYSIS_ATTRIBUTES
- [return: NotNullIfNotNull(nameof(newValue))]
-#endif
- public static int? ValidateSaturationValue(int? newValue, string paramName)
- {
- if (newValue == null)
- return null;
-
- if (newValue.Value is < SaturationMinValue or > SaturationMaxValue) {
- throw new ArgumentOutOfRangeException(
- paramName: paramName,
- actualValue: newValue.Value,
- message: SaturationValueOutOfRangeExceptionMessage
- );
- }
-
- return newValue;
- }
-
-#if LANG_VERSION_11_OR_GREATER && NULL_STATE_STATIC_ANALYSIS_ATTRIBUTES
- [return: NotNullIfNotNull(nameof(newValue))]
-#endif
- public static int ValidateColorTemperatureValue(int newValue, string paramName)
- {
-#if false // TODO
- if (newValue is < ColorTemperatureMinValue or > ColorTemperatureMaxValue) {
- throw new ArgumentOutOfRangeException(
- paramName: paramName,
- actualValue: newValue,
- message: ColorTemperatureValueOutOfRangeExceptionMessage
- );
- }
-#endif
-
- return newValue;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/P105.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/P105.cs
deleted file mode 100644
index 80b86b7..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/P105.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public class P105 : TapoDevice {
- /// <summary>
- /// Initializes a new instance of the <see cref="P105"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" />
- public P105(
- string host,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- : base(
- host: host,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="P105"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, IServiceProvider)" />
- public P105(
- string host,
- IServiceProvider serviceProvider
- )
- : base(
- host: host,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="P105"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" />
- public P105(
- IPAddress ipAddress,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- : base(
- ipAddress: ipAddress,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="P105"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(IPAddress, IServiceProvider)" />
- public P105(
- IPAddress ipAddress,
- IServiceProvider serviceProvider
- )
- : base(
- ipAddress: ipAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="P105"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, string, string, IServiceProvider?)" />
- public P105(
- PhysicalAddress macAddress,
- string email,
- string password,
- IServiceProvider serviceProvider
- )
- : base(
- macAddress: macAddress,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="P105"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, IServiceProvider)" />
- public P105(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- : base(
- macAddress: macAddress,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="P105"/> class.
- /// </summary>
- /// <inheritdoc
- /// cref="TapoDevice(IDeviceEndPointProvider, ITapoCredentialProvider?, Protocol.TapoClientExceptionHandler?, IServiceProvider?)"
- /// path="/exception | /param[@name='deviceEndPointProvider' or @name='credential' or @name='serviceProvider']"
- /// />
- public P105(
- IDeviceEndPointProvider deviceEndPointProvider,
- ITapoCredentialProvider? credential = null,
- IServiceProvider? serviceProvider = null
- )
- : base(
- deviceEndPointProvider: deviceEndPointProvider,
- credential: credential,
- serviceProvider: serviceProvider
- )
- {
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoAuthenticationException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoAuthenticationException.cs
deleted file mode 100644
index 6a6b968..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoAuthenticationException.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public class TapoAuthenticationException : TapoProtocolException {
- public TapoAuthenticationException(
- string message,
- Uri endPoint,
- Exception? innerException = null
- )
- : base(
- message: message,
- endPoint: endPoint,
- innerException: innerException
- )
- {
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoCredentailProviderServiceCollectionExtensions.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoCredentailProviderServiceCollectionExtensions.cs
deleted file mode 100644
index 161d777..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoCredentailProviderServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public static class TapoCredentailProviderServiceCollectionExtensions {
- public static IServiceCollection AddTapoCredential(
- this IServiceCollection services,
- string email,
- string password
- )
- {
- if (services is null)
- throw new ArgumentNullException(nameof(services));
-
- services.TryAdd(
- ServiceDescriptor.Singleton(
- typeof(ITapoCredentialProvider),
- TapoCredentialProviderFactory.CreateFromPlainText(email, password)
- )
- );
-
- return services;
- }
-
- public static IServiceCollection AddTapoBase64EncodedCredential(
- this IServiceCollection services,
- string base64UserNameSHA1Digest,
- string base64Password
- )
- {
- if (services is null)
- throw new ArgumentNullException(nameof(services));
-
- services.TryAdd(
- ServiceDescriptor.Singleton(
- typeof(ITapoCredentialProvider),
- TapoCredentialProviderFactory.CreateFromBase64EncodedText(base64UserNameSHA1Digest, base64Password)
- )
- );
-
- return services;
- }
-
- public static IServiceCollection AddTapoCredentialProvider(
- this IServiceCollection services,
- ITapoCredentialProvider credentialProvider
- )
- {
- if (services is null)
- throw new ArgumentNullException(nameof(services));
- if (credentialProvider is null)
- throw new ArgumentNullException(nameof(credentialProvider));
-
- services.TryAdd(
- ServiceDescriptor.Singleton(typeof(ITapoCredentialProvider), credentialProvider)
- );
-
- return services;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoCredentialProviderFactory.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoCredentialProviderFactory.cs
deleted file mode 100644
index 65ef672..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoCredentialProviderFactory.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Text;
-using System.Text.Json;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-internal static class TapoCredentialProviderFactory {
- public static ITapoCredentialProvider CreateFromPlainText(string email, string password)
- => new SingleIdentityStringCredentialProvider(
- username: email ?? throw new ArgumentNullException(nameof(email)),
- password: password ?? throw new ArgumentNullException(nameof(password)),
- isPlainText: true
- );
-
- public static ITapoCredentialProvider CreateFromBase64EncodedText(
- string base64UserNameSHA1Digest,
- string base64Password
- )
- => new SingleIdentityStringCredentialProvider(
- username: base64UserNameSHA1Digest ?? throw new ArgumentNullException(nameof(base64UserNameSHA1Digest)),
- password: base64Password ?? throw new ArgumentNullException(nameof(base64Password)),
- isPlainText: false
- );
-
- private sealed class SingleIdentityStringCredentialProvider : ITapoCredentialProvider, ITapoCredential {
- private readonly byte[] utf8Username;
- private readonly byte[] utf8Password;
- private readonly bool isPlainText;
-
- public SingleIdentityStringCredentialProvider(
- string username,
- string password,
- bool isPlainText
- )
- {
- utf8Username = Encoding.UTF8.GetBytes(username);
- utf8Password = Encoding.UTF8.GetBytes(password);
- this.isPlainText = isPlainText;
- }
-
- ITapoCredential ITapoCredentialProvider.GetCredential(ITapoCredentialIdentity? identity) => this;
-
- void IDisposable.Dispose() { /* nothing to do */ }
-
- void ITapoCredential.WritePasswordPropertyValue(Utf8JsonWriter writer)
- {
- if (isPlainText)
- writer.WriteBase64StringValue(utf8Password);
- else
- writer.WriteStringValue(utf8Password);
- }
-
- void ITapoCredential.WriteUsernamePropertyValue(Utf8JsonWriter writer)
- {
- if (isPlainText) {
- Span<byte> buffer = stackalloc byte[TapoCredentialUtils.HexSHA1HashSizeInBytes];
-
- try {
- if (!TapoCredentialUtils.TryConvertToHexSHA1Hash(utf8Username, buffer, out _))
- throw new InvalidOperationException("failed to encode username property");
-
- writer.WriteBase64StringValue(buffer);
- }
- finally {
- buffer.Clear();
- }
- }
- else {
- writer.WriteStringValue(utf8Username);
- }
- }
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.Create.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.Create.cs
deleted file mode 100644
index e6e13e1..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.Create.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-#pragma warning disable IDE0040
-partial class TapoDevice {
-#pragma warning restore IDE0040
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)"/>
- public static TapoDevice Create(
- string host,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- => new(
- host: host,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc cref="TapoDevice(string, IServiceProvider)"/>
- public static TapoDevice Create(
- string host,
- IServiceProvider serviceProvider
- )
- => new(
- host: host,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc cref="TapoDevice(IPAddress, string, string, IServiceProvider?)"/>
- public static TapoDevice Create(
- IPAddress ipAddress,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- => new(
- ipAddress: ipAddress,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc cref="TapoDevice(IPAddress, IServiceProvider)"/>
- public static TapoDevice Create(
- IPAddress ipAddress,
- IServiceProvider serviceProvider
- )
- => new(
- ipAddress: ipAddress,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, string, string, IServiceProvider)"/>
- public static TapoDevice Create(
- PhysicalAddress macAddress,
- string email,
- string password,
- IServiceProvider serviceProvider
- )
- => new(
- macAddress: macAddress,
- email: email,
- password: password,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, IServiceProvider)"/>
- public static TapoDevice Create(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- => new(
- macAddress: macAddress,
- serviceProvider: serviceProvider
- );
-
- /// <inheritdoc
- /// cref="TapoDevice(IDeviceEndPointProvider, ITapoCredentialProvider?, Protocol.TapoClientExceptionHandler?, IServiceProvider?)"
- /// path="/summary | /exception | /param[@name='deviceEndPointProvider' or @name='credential' or @name='serviceProvider']"
- /// />
- public static TapoDevice Create(
- IDeviceEndPointProvider deviceEndPointProvider,
- ITapoCredentialProvider? credential = null,
- IServiceProvider? serviceProvider = null
- )
- => new(
- deviceEndPointProvider: deviceEndPointProvider,
- credential: credential,
- serviceProvider: serviceProvider
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.SetDeviceInfo.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.SetDeviceInfo.cs
deleted file mode 100644
index 0524214..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.SetDeviceInfo.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-#pragma warning disable IDE0040
-partial class TapoDevice {
-#pragma warning restore IDE0040
-#pragma warning disable CA1822
- private readonly struct TurnOnParameter {
- [JsonPropertyName("device_on")]
- public bool DeviceOn => true;
- }
-
- private readonly struct TurnOffParameter {
- [JsonPropertyName("device_on")]
- public bool DeviceOn => false;
- }
-#pragma warning restore CA1822
-
- public ValueTask TurnOnAsync(CancellationToken cancellationToken = default)
- => SetDeviceInfoAsync(
- default(TurnOnParameter),
- cancellationToken
- );
-
- public ValueTask TurnOffAsync(CancellationToken cancellationToken = default)
- => SetDeviceInfoAsync(
- default(TurnOffParameter),
- cancellationToken
- );
-
- /// <summary>
- /// Sets the on/off state of the device according to the parameter <paramref name="newOnOffState" />.
- /// </summary>
- /// <param name="newOnOffState">
- /// The value that indicates new on/off state to be set. <see langword="true"/> for on, otherwise off.
- /// </param>
- public ValueTask SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken = default)
- => newOnOffState
- ? TurnOnAsync(cancellationToken)
- : TurnOffAsync(cancellationToken);
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.cs
deleted file mode 100644
index 96fed05..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDevice.cs
+++ /dev/null
@@ -1,509 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-#if SYSTEM_DIAGNOSTICS_UNREACHABLEEXCEPTION
-using System.Diagnostics;
-#endif
-using System.Net;
-using System.Net.Http;
-using System.Net.NetworkInformation;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Smdn.TPSmartHomeDevices.Tapo.Credentials;
-using Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public partial class TapoDevice : ITapoCredentialIdentity, IDisposable {
- private IDeviceEndPointProvider? deviceEndPointProvider; // if null, it indicates a 'disposed' state.
- protected bool IsDisposed => deviceEndPointProvider is null;
-
- private readonly ITapoCredentialProvider? credential;
- private readonly TapoClientExceptionHandler exceptionHandler;
- private readonly IServiceProvider? serviceProvider;
- public string TerminalUuidString { get; } // must be in the format of 00000000-0000-0000-0000-000000000000
-
- private TapoClient? client;
-
- /// <summary>
- /// Gets a current session information represented by <see cref="TapoSession" />.
- /// </summary>
- /// <value>
- /// The <see cref="TapoSession" />. <see langword="null"/> if the session for this instance has not been established or been disposed.
- /// </value>
- public TapoSession? Session => client?.Session;
-
- string ITapoCredentialIdentity.Name {
- get {
- ThrowIfDisposed();
- return $"{GetType().FullName} ({deviceEndPointProvider})";
- }
- }
-
- /// <summary>
- /// Gets or sets the timeout value for HTTP requests.
- /// </summary>
- /// <value>
- /// <para>If the value is <see langword="null"/>, the timeout value configured by the <see cref="IHttpClientFactory"/> is used. Otherwise, the specified <see cref="TimeSpan"/> is used for the timeout value for this instance.</para>
- /// <para>Therefore, even if the value is <see langword="null"/>, a timeout may still occur. If you do not want to timeout for this instance, specify <see cref="Timeout.InfiniteTimeSpan"/> explicitly.</para>
- /// </value>
- public TimeSpan? Timeout { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TapoDevice"/> class with specifying the device endpoint by host name.
- /// </summary>
- /// <param name="host">
- /// A <see cref="string"/> that holds the host name or IP address string, representing the device endpoint.
- /// </param>
- /// <param name="email">
- /// A <see cref="string"/> that holds the e-mail address of the Tapo account used for authentication.
- /// </param>
- /// <param name="password">
- /// A <see cref="string"/> that holds the password of the Tapo account used for authentication.
- /// </param>
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// </param>
- protected TapoDevice(
- string host,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- : this(
- deviceEndPointProvider: TapoDeviceEndPointProvider.Create(host),
- credential: TapoCredentialProviderFactory.CreateFromPlainText(email, password),
- exceptionHandler: null,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <param name="host">
- /// A <see cref="string"/> that holds the host name or IP address string, representing the device endpoint.
- /// </param>
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// <see cref="ITapoCredentialProvider"/> must be registered in order to retrieve the credentials required for authentication.
- /// </param>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" path="/summary"/>
- protected TapoDevice(
- string host,
- IServiceProvider serviceProvider
- )
- : this(
- deviceEndPointProvider: TapoDeviceEndPointProvider.Create(host),
- credential: null,
- exceptionHandler: null,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TapoDevice"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <param name="ipAddress">
- /// A <see cref="IPAddress"/> that holds the IP address representing the device end point.
- /// </param>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" path="/param[@name='email' or @name='password' or @name='serviceProvider']"/>
- protected TapoDevice(
- IPAddress ipAddress,
- string email,
- string password,
- IServiceProvider? serviceProvider = null
- )
- : this(
- deviceEndPointProvider: TapoDeviceEndPointProvider.Create(ipAddress),
- credential: TapoCredentialProviderFactory.CreateFromPlainText(email, password),
- exceptionHandler: null,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TapoDevice"/> class with specifying the device endpoint by IP address.
- /// </summary>
- /// <inheritdoc cref="TapoDevice(IPAddress, string, string, IServiceProvider?)" path="/summary | /param[@name='ipAddress']"/>
- /// <inheritdoc cref="TapoDevice(string, IServiceProvider)" path="/param[@name='serviceProvider']"/>
- protected TapoDevice(
- IPAddress ipAddress,
- IServiceProvider serviceProvider
- )
- : this(
- deviceEndPointProvider: TapoDeviceEndPointProvider.Create(ipAddress),
- credential: null,
- exceptionHandler: null,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TapoDevice"/> class with specifying the device endpoint by MAC address.
- /// </summary>
- /// <param name="macAddress">
- /// A <see cref="PhysicalAddress"/> that holds the MAC address representing the device end point.
- /// </param>
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// <see cref="IDeviceEndPointFactory{PhysicalAddress}"/> must be registered to create an end point from the <paramref name="macAddress"/>.
- /// </param>
- /// <exception cref="InvalidOperationException">No service for type <see cref="IDeviceEndPointFactory{PhysicalAddress}"/> has been registered for <paramref name="serviceProvider"/>.</exception>
- /// <inheritdoc cref="TapoDevice(string, string, string, IServiceProvider?)" path="/param[@name='email' or @name='password']"/>
- protected TapoDevice(
- PhysicalAddress macAddress,
- string email,
- string password,
- IServiceProvider serviceProvider
- )
- : this(
- deviceEndPointProvider: TapoDeviceEndPointProvider.Create(macAddress, serviceProvider),
- credential: TapoCredentialProviderFactory.CreateFromPlainText(email, password),
- exceptionHandler: null,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// <see cref="ITapoCredentialProvider"/> must be registered in order to retrieve the credentials required for authentication.
- /// <see cref="IDeviceEndPointFactory&lt;PhysicalAddress&gt;"/> must also be registered to create an <see cref="IDeviceEndPointProvider" />, corresponding to the <paramref name="macAddress"/>.
- /// </param>
- /// <exception cref="InvalidOperationException">No service for type <see cref="ITapoCredentialProvider"/> and/or <see cref="IDeviceEndPointFactory{PhysicalAddress}"/> has been registered for <paramref name="serviceProvider"/>.</exception>
- /// <inheritdoc cref="TapoDevice(PhysicalAddress, string, string, IServiceProvider?)" path="/summary | /param[@name='macAddress']"/>
- protected TapoDevice(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- : this(
- deviceEndPointProvider: TapoDeviceEndPointProvider.Create(macAddress, serviceProvider),
- credential: null,
- exceptionHandler: null,
- serviceProvider: serviceProvider
- )
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TapoDevice"/> class.
- /// </summary>
- /// <param name="deviceEndPointProvider">
- /// A <see cref="IDeviceEndPointProvider"/> that provides the device end point.
- /// </param>
- /// <param name="credential">
- /// A <see cref="ITapoCredentialProvider"/> that provides the credentials required for authentication.
- /// </param>
- /// <param name="exceptionHandler">
- /// A <see cref="TapoClientExceptionHandler"/> that determines the handling of the exception thrown by the <see cref="TapoClient"/>.
- /// </param>
- /// <param name="serviceProvider">
- /// A <see cref="IServiceProvider"/>.
- /// </param>
- /// <exception cref="InvalidOperationException">No service for type <see cref="ITapoCredentialProvider"/> and/or <see cref="IDeviceEndPointFactory{PhysicalAddress}"/> has been registered for <paramref name="serviceProvider"/>.</exception>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="deviceEndPointProvider"/> is <see langword="null"/>, or both <paramref name="credential"/> and <paramref name="serviceProvider"/> are <see langword="null"/>.
- /// </exception>
- protected TapoDevice(
- IDeviceEndPointProvider deviceEndPointProvider,
- ITapoCredentialProvider? credential = null,
- TapoClientExceptionHandler? exceptionHandler = null,
- IServiceProvider? serviceProvider = null
- )
- {
- this.deviceEndPointProvider = deviceEndPointProvider ?? throw new ArgumentNullException(nameof(deviceEndPointProvider));
- this.credential = credential
- ?? (serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider))).GetRequiredService<ITapoCredentialProvider>();
- this.exceptionHandler = exceptionHandler
- ?? serviceProvider?.GetService<TapoClientExceptionHandler>()
- ?? TapoClientExceptionHandler.Default;
- this.serviceProvider = serviceProvider;
-
- TerminalUuidString = Guid.NewGuid().ToString("D", provider: null); // TODO: support DI
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (!disposing)
- return;
-
- deviceEndPointProvider = null; // mark as disposed
-
- client?.Dispose();
- client = null;
- }
-
- private void ThrowIfDisposed()
- {
- if (IsDisposed)
- throw new ObjectDisposedException(GetType().FullName);
- }
-
- public ValueTask<EndPoint> ResolveEndPointAsync(
- CancellationToken cancellationToken = default
- )
- {
- ThrowIfDisposed();
-
- return cancellationToken.IsCancellationRequested
- ?
-#if SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
- ValueTask.FromCanceled<EndPoint>(cancellationToken)
-#else
- ValueTaskShim.FromCanceled<EndPoint>(cancellationToken)
-#endif
- : deviceEndPointProvider.ResolveOrThrowAsync(
- defaultPort: TapoClient.DefaultPort,
- cancellationToken: cancellationToken
- );
- }
-
- protected ValueTask EnsureSessionEstablishedAsync(
- CancellationToken cancellationToken = default
- )
- {
- ThrowIfDisposed();
-
- return EnsureSessionEstablishedAsyncCore(
- cancellationToken: cancellationToken
- );
- }
-
- private async ValueTask EnsureSessionEstablishedAsyncCore(
- CancellationToken cancellationToken
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var endPoint = await ResolveEndPointAsync(cancellationToken).ConfigureAwait(false);
-
- if (client is not null && !client.EndPoint.Equals(endPoint)) {
- // endpoint has changed, recreate client with new endpoint
- client.Logger?.LogInformation($"Endpoint has changed: {client.EndPoint} -> {endPoint}");
- client.Dispose();
- client = null;
- }
-
- client ??= new TapoClient(
- endPoint: endPoint,
- httpClientFactory: serviceProvider?.GetService<IHttpClientFactory>(),
- logger: serviceProvider?.GetService<ILoggerFactory>()?.CreateLogger(GenerateLoggerCategoryName())
- );
-
- string GenerateLoggerCategoryName()
- => deviceEndPointProvider is IDynamicDeviceEndPointProvider
- ? $"{nameof(TapoClient)}({endPoint}, {deviceEndPointProvider})"
- : $"{nameof(TapoClient)}({endPoint})";
-
- if (client.Session is not null)
- return;
-
- cancellationToken.ThrowIfCancellationRequested();
-
- try {
- await client.AuthenticateAsync(
- identity: this,
- credential: credential,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
- }
- catch {
- client.Dispose();
- client = null;
-
- throw;
- }
- }
-
- protected ValueTask SendRequestAsync<TRequest, TResponse>(
- TRequest request,
- CancellationToken cancellationToken = default
- )
- where TRequest : ITapoPassThroughRequest
- where TResponse : ITapoPassThroughResponse
- {
- if (request is null)
- throw new ArgumentNullException(nameof(request));
-
- ThrowIfDisposed();
-
- return SendRequestAsyncCore(request, cancellationToken);
-
- async ValueTask SendRequestAsyncCore(TRequest req, CancellationToken ct)
- => await SendRequestAsync<TRequest, TResponse, None /* as an alternative to System.Void */>(
- request: req,
- composeResult: static _ => default,
- cancellationToken: ct
- ).ConfigureAwait(false);
- }
-
- protected ValueTask<TResult> SendRequestAsync<TRequest, TResponse, TResult>(
- TRequest request,
- Func<TResponse, TResult> composeResult,
- CancellationToken cancellationToken = default
- )
- where TRequest : ITapoPassThroughRequest
- where TResponse : ITapoPassThroughResponse
- {
- if (request is null)
- throw new ArgumentNullException(nameof(request));
- if (composeResult is null)
- throw new ArgumentNullException(nameof(composeResult));
-
- ThrowIfDisposed();
-
- return SendRequestAsyncCore(
- request: request,
- composeResult: composeResult,
- cancellationToken: cancellationToken
- );
- }
-
- private async ValueTask<TResult> SendRequestAsyncCore<TRequest, TResponse, TResult>(
- TRequest request,
- Func<TResponse, TResult> composeResult,
- CancellationToken cancellationToken = default
- )
- where TRequest : ITapoPassThroughRequest
- where TResponse : ITapoPassThroughResponse
- {
- const int maxAttempts = 5;
- var delay = TimeSpan.Zero;
-
- for (var attempt = 0; attempt < maxAttempts; attempt++) {
- if (TimeSpan.Zero < delay)
- await Task.Delay(delay, cancellationToken).ConfigureAwait(false);
-
- await EnsureSessionEstablishedAsync(cancellationToken).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- client.Timeout = Timeout;
-
- try {
- var response = await client.SendRequestAsync<TRequest, TResponse>(
- request: request,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
-
- return composeResult(response);
- }
- catch (Exception ex) {
- // OperationCanceledException and TaskCanceledException due to a cancel
- // request triggered by a given CancellationToken must not be handled
- // by exception handler, just rethrow instead.
- var handling = (
- ex is OperationCanceledException exOperationCanceled &&
- exOperationCanceled.CancellationToken.Equals(cancellationToken)
- )
- ? TapoClientExceptionHandling.Throw
- : exceptionHandler.DetermineHandling(this, ex, attempt, client.Logger);
-
- static void LogRequest(ILogger logger, TRequest req)
- => logger.LogError(JsonSerializer.Serialize(req));
-
- client.Logger?.LogTrace(
- "Exception handling for {TypeOfException}: {ExceptionHandling}",
- ex.GetType().FullName,
- handling
- );
-
- if (client.Logger is not null && !handling.ShouldRetry)
- LogRequest(client.Logger, request);
-
- if (handling.ShouldInvalidateEndPoint) {
- if (deviceEndPointProvider is IDynamicDeviceEndPointProvider dynamicEndPoint) {
- // mark end point as invalid to have the end point refreshed or rescanned
- dynamicEndPoint.InvalidateEndPoint();
- }
- else {
- // disallow retry
- handling = handling with { ShouldRetry = false };
- }
- }
-
- if (handling.ShouldRetry) {
- /*
- * retry
- */
- delay = handling.RetryAfter;
-
- if (handling.ShouldReconnect) {
- client.Dispose();
- client = null;
- }
-
- continue;
- }
-
- /*
- * rethrow
- */
- var endPointUri = client.EndPointUri;
-
- client.Dispose();
- client = null;
-
- if (handling.ShouldWrapIntoTapoProtocolException) {
- if (
- ex is TaskCanceledException exTaskCanceled &&
- exTaskCanceled.InnerException is TimeoutException exInnerTimeout
- ) {
- throw new TapoProtocolException(
- message: $"Request timed out; {ex.Message}",
- endPoint: endPointUri,
- innerException: exInnerTimeout
- );
- }
- else {
- throw new TapoProtocolException(
- message: "Unhandled exception",
- endPoint: endPointUri,
- innerException: ex
- );
- }
- }
-
- throw;
- } // try
- } // for
-
-#if SYSTEM_DIAGNOSTICS_UNREACHABLEEXCEPTION
- throw new UnreachableException();
-#else
- throw new NotImplementedException("unreachable");
-#endif
- }
-
- public ValueTask<TapoDeviceInfo> GetDeviceInfoAsync(
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync<GetDeviceInfoRequest, GetDeviceInfoResponse, TapoDeviceInfo>(
- request: default,
- composeResult: static resp => resp.Result,
- cancellationToken: cancellationToken
- );
-
- public ValueTask SetDeviceInfoAsync<TParameters>(
- TParameters parameters,
- CancellationToken cancellationToken = default
- )
- => SendRequestAsync<SetDeviceInfoRequest<TParameters>, SetDeviceInfoResponse>(
- request: new(
- terminalUuid: TerminalUuidString,
- parameters: parameters
- ),
- cancellationToken: cancellationToken
- );
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceEndPointProvider.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceEndPointProvider.cs
deleted file mode 100644
index 876e6e5..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceEndPointProvider.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public static class TapoDeviceEndPointProvider {
- public static IDeviceEndPointProvider Create(string host)
- => DeviceEndPointProvider.Create(host, TapoClient.DefaultPort);
-
- public static IDeviceEndPointProvider Create(IPAddress ipAddress)
- => DeviceEndPointProvider.Create(ipAddress, TapoClient.DefaultPort);
-
- public static IDeviceEndPointProvider Create(
- PhysicalAddress macAddress,
- IServiceProvider serviceProvider
- )
- => DeviceEndPointProvider.Create(macAddress, TapoClient.DefaultPort, serviceProvider);
-
- public static IDeviceEndPointProvider Create(
- PhysicalAddress macAddress,
- IDeviceEndPointFactory<PhysicalAddress> endPointFactory
- )
- => DeviceEndPointProvider.Create(macAddress, TapoClient.DefaultPort, endPointFactory);
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceInfo.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceInfo.cs
deleted file mode 100644
index a6a8110..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceInfo.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Text.Json.Serialization;
-using Smdn.TPSmartHomeDevices.Json;
-using Smdn.TPSmartHomeDevices.Tapo.Json;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public class TapoDeviceInfo {
- [JsonIgnore]
- public DateTimeOffset TimeStamp { get; } = DateTimeOffset.Now;
-
- /*
- * storage for the device-specific informations
- */
- [JsonExtensionData]
- internal IDictionary<string, object>? ExtraData { get; init; }
-
- /*
- * properties for the informations common to the devices
- */
- [JsonPropertyName("device_id")]
- public string? Id { get; init; }
-
- [JsonPropertyName("type")]
- public string? TypeName { get; init; }
-
- [JsonPropertyName("model")]
- public string? ModelName { get; init; }
-
- [JsonPropertyName("fw_id")]
- public string? FirmwareId { get; init; }
-
- [JsonPropertyName("fw_ver")]
- public string? FirmwareVersion { get; init; }
-
- [JsonPropertyName("hw_id")]
- public string? HardwareId { get; init; }
-
- [JsonPropertyName("hw_ver")]
- public string? HardwareVersion { get; init; }
-
- [JsonPropertyName("oem_id")]
- public string? OemId { get; init; }
-
- [JsonPropertyName("mac")]
- [JsonConverter(typeof(MacAddressJsonConverter))]
- public PhysicalAddress? MacAddress { get; init; }
-
- [JsonPropertyName("specs")]
- public string? HardwareSpecifications { get; init; }
-
- [JsonPropertyName("lang")]
- public string? Language { get; init; }
-
- [JsonPropertyName("device_on")]
- public bool IsOn { get; init; }
-
- [JsonPropertyName("on_time")]
- [JsonConverter(typeof(TimeSpanInSecondsJsonConverter))]
- public TimeSpan? OnTimeDuration { get; init; }
-
- [JsonPropertyName("overheated")]
- public bool IsOverheated { get; init; }
-
- [JsonPropertyName("nickname")]
- [JsonConverter(typeof(TapoBase64StringJsonConverter))]
- public string? NickName { get; init; }
-
- [JsonPropertyName("avatar")]
- public string? Avatar { get; init; }
-
- [JsonPropertyName("time_diff")]
- [JsonConverter(typeof(TimeSpanInMinutesJsonConverter))]
- public TimeSpan? TimeZoneOffset { get; init; }
-
- [JsonPropertyName("region")]
- public string? TimeZoneRegion { get; init; }
-
- /// <summary>Gets a value that indicates a longitude in decimal degrees.</summary>
- [JsonPropertyName("longitude")]
- [JsonConverter(typeof(GeolocationInDecimalDegreesJsonConverter))]
- public decimal? GeolocationLongitude { get; init; }
-
- /// <summary>Gets a value that indicates a latitude in decimal degrees.</summary>
- [JsonPropertyName("latitude")]
- [JsonConverter(typeof(GeolocationInDecimalDegreesJsonConverter))]
- public decimal? GeolocationLatitude { get; init; }
-
- [JsonPropertyName("has_set_location_info")]
- public bool HasGeolocationInfoSet { get; init; }
-
- [JsonPropertyName("ip")]
- [JsonConverter(typeof(TapoIPAddressJsonConverter))]
- public IPAddress? IPAddress { get; init; }
-
- [JsonPropertyName("ssid")]
- [JsonConverter(typeof(TapoBase64StringJsonConverter))]
- public string? NetworkSsid { get; init; }
-
- [JsonPropertyName("signal_level")]
- public int? NetworkSignalLevel { get; init; }
-
- [JsonPropertyName("rssi")]
- public decimal? NetworkRssi { get; init; }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoErrorResponseException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoErrorResponseException.cs
deleted file mode 100644
index 34d207c..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoErrorResponseException.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public class TapoErrorResponseException : TapoProtocolException {
- internal static void ThrowIfError(Uri requestUri, string requestMethod, ErrorCode errorCode)
- {
- if (errorCode != ErrorCode.Success)
- throw new TapoErrorResponseException(requestUri, requestMethod, errorCode);
- }
-
- public string RequestMethod { get; }
- public ErrorCode ErrorCode { get; }
-
- public TapoErrorResponseException(
- Uri requestEndPoint,
- string requestMethod,
- ErrorCode errorCode
- )
- : base(
- message: $"Request '{requestMethod}' failed with error code {(int)errorCode}. (Request URI: {requestEndPoint})",
- endPoint: requestEndPoint,
- innerException: null
- )
- {
- RequestMethod = requestMethod;
- ErrorCode = errorCode;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoHttpClientFactoryServiceCollectionExtensions.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoHttpClientFactoryServiceCollectionExtensions.cs
deleted file mode 100644
index b07944c..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoHttpClientFactoryServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net.Http;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Smdn.TPSmartHomeDevices.Tapo.Protocol;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public static class TapoHttpClientFactoryServiceCollectionExtensions {
- public static IServiceCollection AddTapoHttpClient(
- this IServiceCollection services,
- Action<HttpClient>? configureClient = null
- )
- {
- if (services is null)
- throw new ArgumentNullException(nameof(services));
-
- services.TryAdd(
- ServiceDescriptor.Singleton(
- typeof(IHttpClientFactory),
- new TapoHttpClientFactory(
- configureClient: configureClient
- )
- )
- );
-
- return services;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoProtocolException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoProtocolException.cs
deleted file mode 100644
index e9f9924..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.Tapo/TapoProtocolException.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-
-namespace Smdn.TPSmartHomeDevices.Tapo;
-
-public class TapoProtocolException : InvalidOperationException {
- public Uri EndPoint { get; }
-
- protected internal TapoProtocolException(
- string message,
- Uri endPoint,
- Exception? innerException
- )
- : base(
- message: message,
- innerException: innerException
- )
- {
- EndPoint = endPoint;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.csproj b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.csproj
index ab416f3..926c695 100644
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.csproj
+++ b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices.csproj
@@ -5,40 +5,40 @@ SPDX-License-Identifier: MIT
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.1;net6.0</TargetFrameworks>
- <TargetFrameworks Condition="$([MSBuild]::VersionGreaterThanOrEquals('$(NETCoreSdkVersion)', '7.0.0'))">net7.0;$(TargetFrameworks)</TargetFrameworks>
<VersionPrefix>1.0.0</VersionPrefix>
- <VersionSuffix>preview3</VersionSuffix>
- <!-- <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion> -->
- <Nullable>enable</Nullable>
- <DefineConstants
- Condition="$([MSBuild]::VersionGreaterThanOrEquals('$(NETCoreSdkVersion)', '7.0.0'))"
- >$(DefineConstants);LANG_VERSION_11_OR_GREATER</DefineConstants> <!-- required to use the UTF-8 string literals in C# 11 -->
- <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <VersionSuffix>rc1</VersionSuffix>
+ <!-- exclude build output assembly from packing -->
+ <IncludeBuildOutput>false</IncludeBuildOutput>
+ <!-- ignore warning NU5128 -->
+ <NoWarn>NU5128;$(NoWarn)</NoWarn>
</PropertyGroup>
<PropertyGroup Label="assembly attributes">
<Description>
-<![CDATA[.NET implementations for Kasa and Tapo, the TP-Link smart home devices.
+<![CDATA[A meta package that addes the dependency of Smdn.TPSmartHomeDevices.Kasa, Smdn.TPSmartHomeDevices.Tapo and Smdn.TPSmartHomeDevices.MacAddressEndPoint.
+This package is deprecated, use the individual packages instead.
]]>
</Description>
<CopyrightYear>2022</CopyrightYear>
</PropertyGroup>
<PropertyGroup Label="package properties">
- <PackageTags>tplink-kasa,tplink-tapo,smarthome,homeautomation,smartdevice</PackageTags>
+ <PackageTags>metapackage,tplink-kasa,tplink-tapo,$(PackageCommonTags)</PackageTags>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
- <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
- <PackageReference Include="Smdn.Fundamental.PrintableEncoding.Hexadecimal" Version="3.0.1" />
- <PackageReference Include="Smdn.Net.AddressResolution" Version="1.0.0-preview5" />
+ <PackageReference Version="[1.0.0-*,1.0.0)" Include="Smdn.TPSmartHomeDevices.Kasa" />
+ <PackageReference Version="[1.0.0-*,1.0.0)" Include="Smdn.TPSmartHomeDevices.Tapo" />
+ <PackageReference Version="[1.0.0-*,1.0.0)" Include="Smdn.TPSmartHomeDevices.MacAddressEndPoint" />
</ItemGroup>
- <PropertyGroup>
- <NoWarn>CS1573;CS1591;$(NoWarn)</NoWarn> <!-- XML docs-->
- <NoWarn>CS8618;SA1027;SA1507;SA1005;$(NoWarn)</NoWarn>
- </PropertyGroup>
-
+ <ItemGroup>
+ <!-- Third party notice -->
+ <None
+ Include="$(MSBuildThisFileDirectory)..\..\ThirdPartyNotices.md"
+ Pack="true"
+ PackagePath="ThirdPartyNotices.md"
+ CopyToOutputDirectory="None"
+ />
+ </ItemGroup>
</Project>
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointFactoryServiceCollectionExtensions.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointFactoryServiceCollectionExtensions.cs
deleted file mode 100644
index 648e9b6..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointFactoryServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-
-namespace Smdn.TPSmartHomeDevices;
-
-public static class DeviceEndPointFactoryServiceCollectionExtensions {
- public static IServiceCollection AddDeviceEndPointFactory<TAddress>(
- this IServiceCollection services,
- IDeviceEndPointFactory<TAddress> endPointFactory
- )
- {
- if (services is null)
- throw new ArgumentNullException(nameof(services));
- if (endPointFactory is null)
- throw new ArgumentNullException(nameof(endPointFactory));
-
- services.TryAdd(ServiceDescriptor.Singleton(typeof(IDeviceEndPointFactory<TAddress>), endPointFactory));
-
- return services;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointProvider.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointProvider.cs
deleted file mode 100644
index 3270e8f..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointProvider.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Smdn.TPSmartHomeDevices;
-
-internal static class DeviceEndPointProvider {
- private sealed class StaticDeviceEndPointProvider : IDeviceEndPointProvider {
- private readonly ValueTask<EndPoint?> staticEndPointValueTaskResult;
-
- public StaticDeviceEndPointProvider(EndPoint endPoint)
- {
- staticEndPointValueTaskResult = new ValueTask<EndPoint?>(endPoint);
- }
-
- public ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken)
- => cancellationToken.IsCancellationRequested
- ?
-#if SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
- ValueTask.FromCanceled<EndPoint?>(cancellationToken)
-#else
- ValueTaskShim.FromCanceled<EndPoint?>(cancellationToken)
-#endif
- : staticEndPointValueTaskResult;
-
- public override string ToString()
- => staticEndPointValueTaskResult.Result!.ToString();
- }
-
- public static IDeviceEndPointProvider Create(string host, int port)
- => Create(
- new DnsEndPoint(
- host: host ?? throw new ArgumentNullException(nameof(host)),
- port: port
- )
- );
-
- public static IDeviceEndPointProvider Create(IPAddress ipAddress, int port)
- => Create(
- new IPEndPoint(
- address: ipAddress ?? throw new ArgumentNullException(nameof(ipAddress)),
- port: port
- )
- );
-
- public static IDeviceEndPointProvider Create(EndPoint endPoint)
- => new StaticDeviceEndPointProvider(
- endPoint ?? throw new ArgumentNullException(nameof(endPoint))
- );
-
- public static IDeviceEndPointProvider Create(
- PhysicalAddress macAddress,
- int port,
- IServiceProvider serviceProvider
- )
- => Create(
- macAddress: macAddress ?? throw new ArgumentNullException(nameof(macAddress)),
- port: port,
- endPointFactory: (serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)))
- .GetRequiredService<IDeviceEndPointFactory<PhysicalAddress>>()
- );
-
- public static IDeviceEndPointProvider Create(
- PhysicalAddress macAddress,
- int port,
- IDeviceEndPointFactory<PhysicalAddress> endPointFactory
- )
- => (endPointFactory ?? throw new ArgumentNullException(nameof(endPointFactory)))
- .Create(
- address: macAddress ?? throw new ArgumentNullException(nameof(macAddress)),
- port: port
- );
-
- internal static async ValueTask<EndPoint> ResolveOrThrowAsync(
- this IDeviceEndPointProvider provider,
- int defaultPort,
- CancellationToken cancellationToken
- )
- {
- var endPoint = await provider.GetEndPointAsync(cancellationToken);
-
- if (endPoint is null && provider is IDynamicDeviceEndPointProvider dynamicEndPoint)
- dynamicEndPoint.InvalidateEndPoint();
-
- return endPoint switch {
- IPEndPoint ipEndPoint => ipEndPoint.Port == 0
- ? new IPEndPoint(ipEndPoint.Address, defaultPort)
- : ipEndPoint,
-
- DnsEndPoint dnsEndPoint => dnsEndPoint.Port == 0
- ? new DnsEndPoint(dnsEndPoint.Host, defaultPort)
- : dnsEndPoint,
-
- EndPoint ep => ep,
-
- null => throw new DeviceEndPointResolutionException(provider),
- };
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointResolutionException.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointResolutionException.cs
deleted file mode 100644
index baba5a3..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/DeviceEndPointResolutionException.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-
-namespace Smdn.TPSmartHomeDevices;
-
-public class DeviceEndPointResolutionException : Exception {
- public IDeviceEndPointProvider EndPointProvider { get; }
-
- public DeviceEndPointResolutionException(
- IDeviceEndPointProvider deviceEndPointProvider
- )
- : this(
- deviceEndPointProvider: deviceEndPointProvider,
- message: "Could not get or resolve the device endpoint.",
- innerException: null
- )
- {
- }
-
- public DeviceEndPointResolutionException(
- IDeviceEndPointProvider deviceEndPointProvider,
- string message,
- Exception? innerException
- )
- : base(
- message: message,
- innerException: innerException
- )
- {
- EndPointProvider = deviceEndPointProvider;
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDeviceEndPointFactory.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDeviceEndPointFactory.cs
deleted file mode 100644
index 8a4daf9..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDeviceEndPointFactory.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices;
-
-public interface IDeviceEndPointFactory<TAddress> {
- IDeviceEndPointProvider Create(TAddress address, int port = 0);
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDeviceEndPointProvider.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDeviceEndPointProvider.cs
deleted file mode 100644
index d2d77af..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDeviceEndPointProvider.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Smdn.TPSmartHomeDevices;
-
-public interface IDeviceEndPointProvider {
- ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDynamicDeviceEndPointProvider.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDynamicDeviceEndPointProvider.cs
deleted file mode 100644
index 1f38438..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/IDynamicDeviceEndPointProvider.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices;
-
-public interface IDynamicDeviceEndPointProvider : IDeviceEndPointProvider {
- void InvalidateEndPoint();
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/MacAddressDeviceEndPointFactory.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/MacAddressDeviceEndPointFactory.cs
deleted file mode 100644
index 27196b2..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/MacAddressDeviceEndPointFactory.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Threading;
-using System.Threading.Tasks;
-using Smdn.Net;
-using Smdn.Net.AddressResolution;
-
-namespace Smdn.TPSmartHomeDevices;
-
-public class MacAddressDeviceEndPointFactory : IDeviceEndPointFactory<PhysicalAddress>, IDisposable {
- protected class MacAddressDeviceEndPointProvider : IDynamicDeviceEndPointProvider {
- private readonly IAddressResolver<PhysicalAddress, IPAddress> resolver;
- private readonly PhysicalAddress address;
- private readonly int port;
-
- public MacAddressDeviceEndPointProvider(
- IAddressResolver<PhysicalAddress, IPAddress> resolver,
- PhysicalAddress address,
- int port
- )
- {
- this.resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
- this.address = address ?? throw new ArgumentNullException(nameof(address));
- this.port = port;
- }
-
- public async ValueTask<EndPoint?> GetEndPointAsync(CancellationToken cancellationToken)
- {
- var resolvedAddress = await resolver.ResolveAsync(
- address: address,
- cancellationToken: cancellationToken
- ).ConfigureAwait(false);
-
- return resolvedAddress is null
- ? null
- : new IPEndPoint(
- address: resolvedAddress,
- port: port
- );
- }
-
- public void InvalidateEndPoint()
- => resolver.Invalidate(address);
-
- public override string ToString()
- => address.ToMacAddressString();
- }
-
- /*
- * instance members
- */
- private IAddressResolver<PhysicalAddress, IPAddress>? resolver; // if null, it indicates a 'disposed' state.
-
- public MacAddressDeviceEndPointFactory(
- MacAddressResolverOptions? options = null,
- IServiceProvider? serviceProvider = null
- )
- : this(
- resolver: (IAddressResolver<PhysicalAddress, IPAddress>)MacAddressResolver.Create(
- options: options,
- serviceProvider: serviceProvider
- ),
- serviceProvider: serviceProvider
- )
- {
- }
-
- public MacAddressDeviceEndPointFactory(
- MacAddressResolver resolver,
- IServiceProvider? serviceProvider = null
- )
- : this(
- resolver: (IAddressResolver<PhysicalAddress, IPAddress>)(resolver ?? throw new ArgumentNullException(nameof(resolver))),
- serviceProvider: serviceProvider
- )
- {
- }
-
-#pragma warning disable IDE0060
- protected MacAddressDeviceEndPointFactory(
- IAddressResolver<PhysicalAddress, IPAddress> resolver,
- IServiceProvider? serviceProvider = null
- )
-#pragma warning restore IDE0060
- {
- this.resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (!disposing)
- return;
-
- (resolver as IDisposable)?.Dispose();
- resolver = null; // mark as disposed
- }
-
- protected void ThrowIfDisposed()
- {
- if (resolver is null)
- throw new ObjectDisposedException(GetType().FullName);
- }
-
- public virtual IDeviceEndPointProvider Create(
- PhysicalAddress address,
- int port = 0
- )
- {
- ThrowIfDisposed();
-
- return new MacAddressDeviceEndPointProvider(
- resolver: resolver,
- address: address ?? throw new ArgumentNullException(nameof(address)),
- port: port
- );
- }
-}
diff --git a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/None.cs b/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/None.cs
deleted file mode 100644
index e7d21e2..0000000
--- a/src/Smdn.TPSmartHomeDevices/Smdn.TPSmartHomeDevices/None.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace Smdn.TPSmartHomeDevices;
-
-internal readonly struct None { }
diff --git a/src/Smdn.TPSmartHomeDevices/System.Runtime.CompilerServices/IsExternalInit.cs b/src/Smdn.TPSmartHomeDevices/System.Runtime.CompilerServices/IsExternalInit.cs
deleted file mode 100644
index 5971d2c..0000000
--- a/src/Smdn.TPSmartHomeDevices/System.Runtime.CompilerServices/IsExternalInit.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-// SPDX-FileCopyrightText: 2022 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-
-#if !SYSTEM_RUNTIME_COMPILERSERVICES_ISEXTERNALINIT
-namespace System.Runtime.CompilerServices;
-
-internal sealed class IsExternalInit { }
-#endif
diff --git a/src/Smdn.TPSmartHomeDevices/System.Security.Cryptography/AsymmetricAlgorithmShim.cs b/src/Smdn.TPSmartHomeDevices/System.Security.Cryptography/AsymmetricAlgorithmShim.cs
deleted file mode 100644
index 743537c..0000000
--- a/src/Smdn.TPSmartHomeDevices/System.Security.Cryptography/AsymmetricAlgorithmShim.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-using System.IO;
-using System.Text;
-
-namespace System.Security.Cryptography;
-
-internal static class AsymmetricAlgorithmShim {
-#if !SYSTEM_SECURITY_CRYPTOGRAPHY_ASYMMETRICALGORITHM_EXPORTSUBJECTPUBLICKEYINFOPEM
- public static string ExportSubjectPublicKeyInfoPem(AsymmetricAlgorithm algorithm)
- => ExportPem(
- header: "-----BEGIN PUBLIC KEY-----",
- footer: "-----END PUBLIC KEY-----",
- key: (algorithm ?? throw new ArgumentNullException(nameof(algorithm))).ExportSubjectPublicKeyInfo()
- );
-
- private static string ExportPem(string header, string footer, byte[] key)
- {
- using var stream = new MemoryStream(capacity: header.Length + footer.Length + (key.Length * 2));
- var writer = new StreamWriter(stream: stream, encoding: Encoding.ASCII, leaveOpen: true, bufferSize: 1024) {
- NewLine = "\n",
- };
-
- writer.WriteLine(header);
- writer.Flush();
-
- using var base64Stream = new CryptoStream(
- stream,
- new ToBase64Transform(),
- CryptoStreamMode.Write,
- leaveOpen: true
- );
-
- // TODO: write newline per 48 bytes
- base64Stream.Write(key, 0, key.Length);
- base64Stream.Close();
-
- writer.WriteLine();
-
- writer.WriteLine(footer);
- writer.Close();
-
- stream.Position = 0L;
-
- using var reader = new StreamReader(stream, Encoding.ASCII);
-
- return reader.ReadToEnd();
- }
-#endif
-}
diff --git a/src/Smdn.TPSmartHomeDevices/System.Threading.Tasks/ValueTaskShim.cs b/src/Smdn.TPSmartHomeDevices/System.Threading.Tasks/ValueTaskShim.cs
deleted file mode 100644
index 36d6b7a..0000000
--- a/src/Smdn.TPSmartHomeDevices/System.Threading.Tasks/ValueTaskShim.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-FileCopyrightText: 2022 smdn <smdn@smdn.jp>
-// SPDX-License-Identifier: MIT
-namespace System.Threading.Tasks;
-
-internal static class ValueTaskShim {
-#if !SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
- public static ValueTask FromCanceled(CancellationToken cancellationToken)
- => new(Task.FromCanceled(cancellationToken));
-
- public static ValueTask<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)
- => new(Task.FromCanceled<TResult>(cancellationToken));
-#endif
-
-#if !SYSTEM_THREADING_TASKS_VALUETASK_FROMRESULT
- public static ValueTask<TResult> FromResult<TResult>(TResult result) => new(result);
-#endif
-}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment