Skip to content

Smdn.TPSmartHomeDevices.Tapo version 1.0.1

Compare
Choose a tag to compare
@smdn smdn released this 14 May 12:39
· 141 commits to main since this release
0d6bee5

Released package

Release notes

The full release notes are available at gist.

Change log

Change log in this release:

API changes

API changes in this release:
diff --git a/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-net6.0.apilist.cs b/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-net6.0.apilist.cs
index 637b0f0..37197ea 100644
--- a/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-net6.0.apilist.cs
+++ b/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-net6.0.apilist.cs
@@ -1,495 +1,495 @@
-// Smdn.TPSmartHomeDevices.Tapo.dll (Smdn.TPSmartHomeDevices.Tapo-1.0.0)
+// Smdn.TPSmartHomeDevices.Tapo.dll (Smdn.TPSmartHomeDevices.Tapo-1.0.1)
 //   Name: Smdn.TPSmartHomeDevices.Tapo
-//   AssemblyVersion: 1.0.0.0
-//   InformationalVersion: 1.0.0+4dd7eda1e01a411bacbd6593ca050a45b3c57c37
+//   AssemblyVersion: 1.0.1.0
+//   InformationalVersion: 1.0.1+26b3994b9e663ddd0b4c39b0a86948a876d03dad
 //   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
 //     Smdn.Fundamental.PrintableEncoding.Hexadecimal, Version=3.0.1.0, Culture=neutral
 //     Smdn.TPSmartHomeDevices.Primitives, 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.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.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.TPSmartHomeDevices;
 using Smdn.TPSmartHomeDevices.Tapo;
 using Smdn.TPSmartHomeDevices.Tapo.Credentials;
 using Smdn.TPSmartHomeDevices.Tapo.Protocol;
 
 namespace Smdn.TPSmartHomeDevices.Tapo {
   public class L530 : TapoDevice {
     public static L530 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public L530(IDeviceEndPoint deviceEndPoint, 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 static L900 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public L900(IDeviceEndPoint deviceEndPoint, 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 static P105 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public P105(IDeviceEndPoint deviceEndPoint, 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(IDeviceEndPoint deviceEndPoint, 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) {}
     public static TapoDevice Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     protected TapoDevice(IDeviceEndPoint deviceEndPoint, ITapoCredentialProvider? credential, IServiceProvider? serviceProvider) {}
     protected TapoDevice(IDeviceEndPoint deviceEndPoint, ITapoCredentialProvider? credential, TapoDeviceExceptionHandler? exceptionHandler, IServiceProvider? serviceProvider) {}
     protected TapoDevice(IPAddress ipAddress, IServiceProvider serviceProvider) {}
     protected TapoDevice(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider) {}
     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) {}
 
     [MemberNotNullWhen(false, "deviceEndPoint")]
     protected bool IsDisposed { [MemberNotNullWhen(false, "deviceEndPoint")] get; }
     public TapoSession? Session { 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<TDeviceInfo> GetDeviceInfoAsync<TDeviceInfo>(CancellationToken cancellationToken = default) {}
     public ValueTask<TResult> GetDeviceInfoAsync<TDeviceInfo, TResult>(Func<TDeviceInfo, TResult> composeResult, CancellationToken cancellationToken = default) {}
     public ValueTask<TapoDeviceInfo> GetDeviceInfoAsync(CancellationToken cancellationToken = default) {}
     public ValueTask<bool> GetOnOffStateAsync(CancellationToken cancellationToken = default) {}
     public ValueTask<EndPoint> ResolveEndPointAsync(CancellationToken cancellationToken = default) {}
     protected ValueTask SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
     protected ValueTask<TResult> SendRequestAsync<TRequest, TResponse, TResult>(TRequest request, Func<TResponse, TResult> composeResult, CancellationToken cancellationToken = default) where TRequest : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
     public ValueTask SetDeviceInfoAsync<TDeviceInfo>(TDeviceInfo deviceInfo, CancellationToken cancellationToken = default) {}
     public ValueTask SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken = default) {}
     public override string? ToString() {}
     public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
     public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
   }
 
   public abstract class TapoDeviceExceptionHandler {
     internal protected static readonly TapoDeviceExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Tapo.TapoDeviceDefaultExceptionHandler"
 
     protected TapoDeviceExceptionHandler() {}
 
     public abstract TapoDeviceExceptionHandling DetermineHandling(TapoDevice device, Exception exception, int attempt, ILogger? logger);
   }
 
   public static class TapoDeviceExceptionHandlerServiceCollectionExtensions {
     public static IServiceCollection AddTapoDeviceExceptionHandler(this IServiceCollection services, TapoDeviceExceptionHandler exceptionHandler) {}
   }
 
   public class TapoDeviceInfo {
     public TapoDeviceInfo() {}
 
     [JsonPropertyName("avatar")]
     public string? Avatar { get; init; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("fw_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("hw_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("device_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("oem_id")]
     public byte[]? 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, int rawErrorCode) {}
 
     public int RawErrorCode { 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; }
   }
 
   public readonly struct TapoDeviceExceptionHandling {
     public static readonly TapoDeviceExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
     public static readonly TapoDeviceExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
     public static readonly TapoDeviceExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling RetryAfterReestablishSession; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=True, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling ThrowAsTapoProtocolException; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=True, ShouldInvalidateEndPoint=False}"
 
     public static TapoDeviceExceptionHandling CreateRetry(TimeSpan retryAfter, bool shouldReestablishSession = false) {}
 
     public TimeSpan RetryAfter { get; init; }
     public bool ShouldInvalidateEndPoint { get; init; }
     public bool ShouldReestablishSession { get; init; }
     public bool ShouldRetry { get; init; }
     public bool ShouldWrapIntoTapoProtocolException { get; init; }
 
     public override string ToString() {}
   }
 }
 
 namespace Smdn.TPSmartHomeDevices.Tapo.Credentials {
   public interface ITapoCredential : IDisposable {
     void WritePasswordPropertyValue(Utf8JsonWriter writer);
     void WriteUsernamePropertyValue(Utf8JsonWriter writer);
   }
 
   public interface ITapoCredentialIdentity {
   }
 
   public interface ITapoCredentialProvider {
     ITapoCredential GetCredential(ITapoCredentialIdentity? identity);
   }
 
   public static class TapoCredentials {
     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.Json {
   public sealed class TapoBase16ByteArrayJsonConverter : JsonConverter<byte[]> {
     public TapoBase16ByteArrayJsonConverter() {}
 
     public override byte[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     public override void Write(Utf8JsonWriter writer, byte[]? @value, JsonSerializerOptions options) {}
   }
 
   public sealed class TapoBase64StringJsonConverter : JsonConverter<string> {
     public TapoBase64StringJsonConverter() {}
 
     public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     public override void Write(Utf8JsonWriter writer, string? @value, JsonSerializerOptions options) {}
   }
 
   public sealed class TapoIPAddressJsonConverter : JsonConverter<IPAddress> {
     public TapoIPAddressJsonConverter() {}
 
     public override IPAddress? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     public override void Write(Utf8JsonWriter writer, IPAddress @value, JsonSerializerOptions options) {}
   }
 }
 
 namespace Smdn.TPSmartHomeDevices.Tapo.Protocol {
   public interface ITapoPassThroughRequest : ITapoRequest {
   }
 
   public interface ITapoPassThroughResponse : ITapoResponse {
   }
 
   public interface ITapoRequest {
     string Method { get; }
   }
 
   public interface ITapoResponse {
     int ErrorCode { get; }
   }
 
   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 static IHttpClientFactory DefaultHttpClientFactory { get; }
 
     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 : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
   }
 
   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<TResult> : ITapoPassThroughResponse {
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public TResult Result { get; init; }
   }
 
   public readonly struct HandshakeRequest : ITapoRequest {
     public readonly struct RequestParameters {
       [JsonPropertyName("key")]
       public string Key { get; init; }
       [JsonPropertyName("requestTimeMils")]
       public long RequestTimeMilliseconds { get; }
     }
 
     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 {
       [JsonPropertyName("key")]
       public string? Key { get; init; }
     }
 
     [JsonPropertyName("error_code")]
     public int 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 {
       [JsonPropertyName("token")]
       public string Token { get; init; }
     }
 
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public LoginDeviceResponse.ResponseResult Result { get; init; }
   }
 
   public readonly struct SecurePassThroughRequest<TPassThroughRequest> : ITapoRequest where TPassThroughRequest : notnull, ITapoPassThroughRequest {
     public readonly struct RequestParams where TPassThroughRequest : notnull, ITapoPassThroughRequest {
       [JsonPropertyName("request")]
       public TPassThroughRequest PassThroughRequest { get; init; }
     }
 
     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 where TPassThroughResponse : notnull, ITapoPassThroughResponse {
       [JsonPropertyName("response")]
       public TPassThroughResponse PassThroughResponse { get; init; }
     }
 
     public SecurePassThroughResponse(int errorCode, TPassThroughResponse passThroughResponse) {}
 
     [JsonPropertyName("error_code")]
     public int 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<TResult> : ITapoPassThroughResponse {
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public TResult Result { get; init; }
   }
 }
 // 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.Tapo/Smdn.TPSmartHomeDevices.Tapo-net7.0.apilist.cs b/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-net7.0.apilist.cs
index 07326d0..41c2640 100644
--- a/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-net7.0.apilist.cs
+++ b/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-net7.0.apilist.cs
@@ -1,493 +1,493 @@
-// Smdn.TPSmartHomeDevices.Tapo.dll (Smdn.TPSmartHomeDevices.Tapo-1.0.0)
+// Smdn.TPSmartHomeDevices.Tapo.dll (Smdn.TPSmartHomeDevices.Tapo-1.0.1)
 //   Name: Smdn.TPSmartHomeDevices.Tapo
-//   AssemblyVersion: 1.0.0.0
-//   InformationalVersion: 1.0.0+4dd7eda1e01a411bacbd6593ca050a45b3c57c37
+//   AssemblyVersion: 1.0.1.0
+//   InformationalVersion: 1.0.1+26b3994b9e663ddd0b4c39b0a86948a876d03dad
 //   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
 //     Smdn.Fundamental.PrintableEncoding.Hexadecimal, Version=3.0.1.0, Culture=neutral
 //     Smdn.TPSmartHomeDevices.Primitives, 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.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.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.TPSmartHomeDevices;
 using Smdn.TPSmartHomeDevices.Tapo;
 using Smdn.TPSmartHomeDevices.Tapo.Credentials;
 using Smdn.TPSmartHomeDevices.Tapo.Protocol;
 
 namespace Smdn.TPSmartHomeDevices.Tapo {
   public class L530 : TapoDevice {
     public static L530 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public L530(IDeviceEndPoint deviceEndPoint, 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 static L900 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public L900(IDeviceEndPoint deviceEndPoint, 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 static P105 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public P105(IDeviceEndPoint deviceEndPoint, 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(IDeviceEndPoint deviceEndPoint, 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) {}
     public static TapoDevice Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     protected TapoDevice(IDeviceEndPoint deviceEndPoint, ITapoCredentialProvider? credential, IServiceProvider? serviceProvider) {}
     protected TapoDevice(IDeviceEndPoint deviceEndPoint, ITapoCredentialProvider? credential, TapoDeviceExceptionHandler? exceptionHandler, IServiceProvider? serviceProvider) {}
     protected TapoDevice(IPAddress ipAddress, IServiceProvider serviceProvider) {}
     protected TapoDevice(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider) {}
     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) {}
 
     [MemberNotNullWhen(false, "deviceEndPoint")]
     protected bool IsDisposed { [MemberNotNullWhen(false, "deviceEndPoint")] get; }
     public TapoSession? Session { 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<TDeviceInfo> GetDeviceInfoAsync<TDeviceInfo>(CancellationToken cancellationToken = default) {}
     public ValueTask<TResult> GetDeviceInfoAsync<TDeviceInfo, TResult>(Func<TDeviceInfo, TResult> composeResult, CancellationToken cancellationToken = default) {}
     public ValueTask<TapoDeviceInfo> GetDeviceInfoAsync(CancellationToken cancellationToken = default) {}
     public ValueTask<bool> GetOnOffStateAsync(CancellationToken cancellationToken = default) {}
     public ValueTask<EndPoint> ResolveEndPointAsync(CancellationToken cancellationToken = default) {}
     protected ValueTask SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
     protected ValueTask<TResult> SendRequestAsync<TRequest, TResponse, TResult>(TRequest request, Func<TResponse, TResult> composeResult, CancellationToken cancellationToken = default) where TRequest : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
     public ValueTask SetDeviceInfoAsync<TDeviceInfo>(TDeviceInfo deviceInfo, CancellationToken cancellationToken = default) {}
     public ValueTask SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken = default) {}
     public override string? ToString() {}
     public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
     public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
   }
 
   public abstract class TapoDeviceExceptionHandler {
     internal protected static readonly TapoDeviceExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Tapo.TapoDeviceDefaultExceptionHandler"
 
     protected TapoDeviceExceptionHandler() {}
 
     public abstract TapoDeviceExceptionHandling DetermineHandling(TapoDevice device, Exception exception, int attempt, ILogger? logger);
   }
 
   public static class TapoDeviceExceptionHandlerServiceCollectionExtensions {
     public static IServiceCollection AddTapoDeviceExceptionHandler(this IServiceCollection services, TapoDeviceExceptionHandler exceptionHandler) {}
   }
 
   public class TapoDeviceInfo {
     public TapoDeviceInfo() {}
 
     [JsonPropertyName("avatar")]
     public string? Avatar { get; init; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("fw_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("hw_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("device_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("oem_id")]
     public byte[]? 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, int rawErrorCode) {}
 
     public int RawErrorCode { 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; }
   }
 
   public readonly struct TapoDeviceExceptionHandling {
     public static readonly TapoDeviceExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
     public static readonly TapoDeviceExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
     public static readonly TapoDeviceExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling RetryAfterReestablishSession; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=True, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling ThrowAsTapoProtocolException; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=True, ShouldInvalidateEndPoint=False}"
 
     public static TapoDeviceExceptionHandling CreateRetry(TimeSpan retryAfter, bool shouldReestablishSession = false) {}
 
     public TimeSpan RetryAfter { get; init; }
     public bool ShouldInvalidateEndPoint { get; init; }
     public bool ShouldReestablishSession { get; init; }
     public bool ShouldRetry { get; init; }
     public bool ShouldWrapIntoTapoProtocolException { get; init; }
 
     public override string ToString() {}
   }
 }
 
 namespace Smdn.TPSmartHomeDevices.Tapo.Credentials {
   public interface ITapoCredential : IDisposable {
     void WritePasswordPropertyValue(Utf8JsonWriter writer);
     void WriteUsernamePropertyValue(Utf8JsonWriter writer);
   }
 
   public interface ITapoCredentialIdentity {
   }
 
   public interface ITapoCredentialProvider {
     ITapoCredential GetCredential(ITapoCredentialIdentity? identity);
   }
 
   public static class TapoCredentials {
     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.Json {
   public sealed class TapoBase16ByteArrayJsonConverter : JsonConverter<byte[]> {
     public TapoBase16ByteArrayJsonConverter() {}
 
     public override byte[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     public override void Write(Utf8JsonWriter writer, byte[]? @value, JsonSerializerOptions options) {}
   }
 
   public sealed class TapoBase64StringJsonConverter : JsonConverter<string> {
     public TapoBase64StringJsonConverter() {}
 
     public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     public override void Write(Utf8JsonWriter writer, string? @value, JsonSerializerOptions options) {}
   }
 
   public sealed class TapoIPAddressJsonConverter : JsonConverter<IPAddress> {
     public TapoIPAddressJsonConverter() {}
 
     public override IPAddress? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     public override void Write(Utf8JsonWriter writer, IPAddress @value, JsonSerializerOptions options) {}
   }
 }
 
 namespace Smdn.TPSmartHomeDevices.Tapo.Protocol {
   public interface ITapoPassThroughRequest : ITapoRequest {
   }
 
   public interface ITapoPassThroughResponse : ITapoResponse {
   }
 
   public interface ITapoRequest {
     string Method { get; }
   }
 
   public interface ITapoResponse {
     int ErrorCode { get; }
   }
 
   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 static IHttpClientFactory DefaultHttpClientFactory { get; }
 
     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 : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
   }
 
   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<TResult> : ITapoPassThroughResponse {
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public TResult Result { get; init; }
   }
 
   public readonly struct HandshakeRequest : ITapoRequest {
     public readonly struct RequestParameters {
       [JsonPropertyName("key")]
       public string Key { get; init; }
       [JsonPropertyName("requestTimeMils")]
       public long RequestTimeMilliseconds { get; }
     }
 
     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 {
       [JsonPropertyName("key")]
       public string? Key { get; init; }
     }
 
     [JsonPropertyName("error_code")]
     public int 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 {
       [JsonPropertyName("token")]
       public string Token { get; init; }
     }
 
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public LoginDeviceResponse.ResponseResult Result { get; init; }
   }
 
   public readonly struct SecurePassThroughRequest<TPassThroughRequest> : ITapoRequest where TPassThroughRequest : notnull, ITapoPassThroughRequest {
     public readonly struct RequestParams where TPassThroughRequest : notnull, ITapoPassThroughRequest {
       [JsonPropertyName("request")]
       public TPassThroughRequest PassThroughRequest { get; init; }
     }
 
     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 where TPassThroughResponse : notnull, ITapoPassThroughResponse {
       [JsonPropertyName("response")]
       public TPassThroughResponse PassThroughResponse { get; init; }
     }
 
     public SecurePassThroughResponse(int errorCode, TPassThroughResponse passThroughResponse) {}
 
     [JsonPropertyName("error_code")]
     public int 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<TResult> : ITapoPassThroughResponse {
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public TResult Result { get; init; }
   }
 }
 // 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.Tapo/Smdn.TPSmartHomeDevices.Tapo-netstandard2.1.apilist.cs b/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-netstandard2.1.apilist.cs
index 903e548..c121b5b 100644
--- a/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-netstandard2.1.apilist.cs
+++ b/doc/api-list/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo-netstandard2.1.apilist.cs
@@ -1,483 +1,483 @@
-// Smdn.TPSmartHomeDevices.Tapo.dll (Smdn.TPSmartHomeDevices.Tapo-1.0.0)
+// Smdn.TPSmartHomeDevices.Tapo.dll (Smdn.TPSmartHomeDevices.Tapo-1.0.1)
 //   Name: Smdn.TPSmartHomeDevices.Tapo
-//   AssemblyVersion: 1.0.0.0
-//   InformationalVersion: 1.0.0+4dd7eda1e01a411bacbd6593ca050a45b3c57c37
+//   AssemblyVersion: 1.0.1.0
+//   InformationalVersion: 1.0.1+26b3994b9e663ddd0b4c39b0a86948a876d03dad
 //   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.TPSmartHomeDevices.Primitives, 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.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.TPSmartHomeDevices;
 using Smdn.TPSmartHomeDevices.Tapo;
 using Smdn.TPSmartHomeDevices.Tapo.Credentials;
 using Smdn.TPSmartHomeDevices.Tapo.Protocol;
 
 namespace Smdn.TPSmartHomeDevices.Tapo {
   public class L530 : TapoDevice {
     public static L530 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public L530(IDeviceEndPoint deviceEndPoint, 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 static L900 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public L900(IDeviceEndPoint deviceEndPoint, 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 static P105 Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     public P105(IDeviceEndPoint deviceEndPoint, 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(IDeviceEndPoint deviceEndPoint, 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) {}
     public static TapoDevice Create<TAddress>(TAddress deviceAddress, IServiceProvider serviceProvider, ITapoCredentialProvider? credential = null) where TAddress : notnull {}
 
     protected TapoDevice(IDeviceEndPoint deviceEndPoint, ITapoCredentialProvider? credential, IServiceProvider? serviceProvider) {}
     protected TapoDevice(IDeviceEndPoint deviceEndPoint, ITapoCredentialProvider? credential, TapoDeviceExceptionHandler? exceptionHandler, IServiceProvider? serviceProvider) {}
     protected TapoDevice(IPAddress ipAddress, IServiceProvider serviceProvider) {}
     protected TapoDevice(IPAddress ipAddress, string email, string password, IServiceProvider? serviceProvider) {}
     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) {}
 
     protected bool IsDisposed { get; }
     public TapoSession? Session { 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<TDeviceInfo> GetDeviceInfoAsync<TDeviceInfo>(CancellationToken cancellationToken = default) {}
     public ValueTask<TResult> GetDeviceInfoAsync<TDeviceInfo, TResult>(Func<TDeviceInfo, TResult> composeResult, CancellationToken cancellationToken = default) {}
     public ValueTask<TapoDeviceInfo> GetDeviceInfoAsync(CancellationToken cancellationToken = default) {}
     public ValueTask<bool> GetOnOffStateAsync(CancellationToken cancellationToken = default) {}
     public ValueTask<EndPoint> ResolveEndPointAsync(CancellationToken cancellationToken = default) {}
     protected ValueTask SendRequestAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
     protected ValueTask<TResult> SendRequestAsync<TRequest, TResponse, TResult>(TRequest request, Func<TResponse, TResult> composeResult, CancellationToken cancellationToken = default) where TRequest : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
     public ValueTask SetDeviceInfoAsync<TDeviceInfo>(TDeviceInfo deviceInfo, CancellationToken cancellationToken = default) {}
     public ValueTask SetOnOffStateAsync(bool newOnOffState, CancellationToken cancellationToken = default) {}
     public override string? ToString() {}
     public ValueTask TurnOffAsync(CancellationToken cancellationToken = default) {}
     public ValueTask TurnOnAsync(CancellationToken cancellationToken = default) {}
   }
 
   public abstract class TapoDeviceExceptionHandler {
     internal protected static readonly TapoDeviceExceptionHandler Default; // = "Smdn.TPSmartHomeDevices.Tapo.TapoDeviceDefaultExceptionHandler"
 
     protected TapoDeviceExceptionHandler() {}
 
     public abstract TapoDeviceExceptionHandling DetermineHandling(TapoDevice device, Exception exception, int attempt, ILogger? logger);
   }
 
   public static class TapoDeviceExceptionHandlerServiceCollectionExtensions {
     public static IServiceCollection AddTapoDeviceExceptionHandler(this IServiceCollection services, TapoDeviceExceptionHandler exceptionHandler) {}
   }
 
   public class TapoDeviceInfo {
     public TapoDeviceInfo() {}
 
     [JsonPropertyName("avatar")]
     public string? Avatar { get; init; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("fw_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("hw_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("device_id")]
     public byte[]? 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; }
     [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
     [JsonPropertyName("oem_id")]
     public byte[]? 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, int rawErrorCode) {}
 
     public int RawErrorCode { 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; }
   }
 
   public readonly struct TapoDeviceExceptionHandling {
     public static readonly TapoDeviceExceptionHandling InvalidateEndPointAndRetry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
     public static readonly TapoDeviceExceptionHandling InvalidateEndPointAndThrow; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=True}"
     public static readonly TapoDeviceExceptionHandling Retry; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling RetryAfterReestablishSession; // = "{ShouldRetry=True, RetryAfter=00:00:00, ShouldReestablishSession=True, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling Throw; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=False, ShouldInvalidateEndPoint=False}"
     public static readonly TapoDeviceExceptionHandling ThrowAsTapoProtocolException; // = "{ShouldRetry=False, RetryAfter=00:00:00, ShouldReestablishSession=False, ShouldWrapIntoTapoProtocolException=True, ShouldInvalidateEndPoint=False}"
 
     public static TapoDeviceExceptionHandling CreateRetry(TimeSpan retryAfter, bool shouldReestablishSession = false) {}
 
     public TimeSpan RetryAfter { get; init; }
     public bool ShouldInvalidateEndPoint { get; init; }
     public bool ShouldReestablishSession { get; init; }
     public bool ShouldRetry { get; init; }
     public bool ShouldWrapIntoTapoProtocolException { get; init; }
 
     public override string ToString() {}
   }
 }
 
 namespace Smdn.TPSmartHomeDevices.Tapo.Credentials {
   public interface ITapoCredential : IDisposable {
     [...] <unknown> WritePasswordPropertyValue(...);
     [...] <unknown> WriteUsernamePropertyValue(...);
   }
 
   public interface ITapoCredentialIdentity {
   }
 
   public interface ITapoCredentialProvider {
     ITapoCredential GetCredential(ITapoCredentialIdentity? identity);
   }
 
   public static class TapoCredentials {
     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.Json {
   public sealed class TapoBase16ByteArrayJsonConverter : JsonConverter<byte[]> {
     public TapoBase16ByteArrayJsonConverter() {}
 
     public override byte[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     [...] public override <unknown> Write(...) {}
   }
 
   public sealed class TapoBase64StringJsonConverter : JsonConverter<string> {
     public TapoBase64StringJsonConverter() {}
 
     public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     [...] public override <unknown> Write(...) {}
   }
 
   public sealed class TapoIPAddressJsonConverter : JsonConverter<IPAddress> {
     public TapoIPAddressJsonConverter() {}
 
     public override IPAddress? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {}
     [...] public override <unknown> Write(...) {}
   }
 }
 
 namespace Smdn.TPSmartHomeDevices.Tapo.Protocol {
   public interface ITapoPassThroughRequest : ITapoRequest {
   }
 
   public interface ITapoPassThroughResponse : ITapoResponse {
   }
 
   public interface ITapoRequest {
     string Method { get; }
   }
 
   public interface ITapoResponse {
     int ErrorCode { get; }
   }
 
   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 static IHttpClientFactory DefaultHttpClientFactory { get; }
 
     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 : notnull, ITapoPassThroughRequest where TResponse : ITapoPassThroughResponse {}
   }
 
   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<TResult> : ITapoPassThroughResponse {
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public TResult Result { get; init; }
   }
 
   public readonly struct HandshakeRequest : ITapoRequest {
     public readonly struct RequestParameters {
       [JsonPropertyName("key")]
       public string Key { get; init; }
       [JsonPropertyName("requestTimeMils")]
       public long RequestTimeMilliseconds { get; }
     }
 
     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 {
       [JsonPropertyName("key")]
       public string? Key { get; init; }
     }
 
     [JsonPropertyName("error_code")]
     public int 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 {
       [JsonPropertyName("token")]
       public string Token { get; init; }
     }
 
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public LoginDeviceResponse.ResponseResult Result { get; init; }
   }
 
   public readonly struct SecurePassThroughRequest<TPassThroughRequest> : ITapoRequest where TPassThroughRequest : notnull, ITapoPassThroughRequest {
     public readonly struct RequestParams where TPassThroughRequest : notnull, ITapoPassThroughRequest {
       [JsonPropertyName("request")]
       public TPassThroughRequest PassThroughRequest { get; init; }
     }
 
     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 where TPassThroughResponse : notnull, ITapoPassThroughResponse {
       [JsonPropertyName("response")]
       public TPassThroughResponse PassThroughResponse { get; init; }
     }
 
     public SecurePassThroughResponse(int errorCode, TPassThroughResponse passThroughResponse) {}
 
     [JsonPropertyName("error_code")]
     public int 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<TResult> : ITapoPassThroughResponse {
     [JsonPropertyName("error_code")]
     public int ErrorCode { get; init; }
     [JsonPropertyName("result")]
     public TResult Result { get; init; }
   }
 }
 // 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)

Full changes

Full changes in this release:
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughInvalidPaddingException.cs b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughInvalidPaddingException.cs
index 3b8e087..3868a3f 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughInvalidPaddingException.cs
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughInvalidPaddingException.cs
@@ -1,5 +1,7 @@
 // SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
 // SPDX-License-Identifier: MIT
+#pragma warning disable CA1032
+
 using System;
 
 namespace Smdn.TPSmartHomeDevices.Tapo.Protocol;
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughJsonConverterFactory.cs b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughJsonConverterFactory.cs
index d504ec1..42b9c6f 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughJsonConverterFactory.cs
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/SecurePassThroughJsonConverterFactory.cs
@@ -206,20 +206,23 @@ public sealed class SecurePassThroughJsonConverterFactory :
       if (!cryptographicExceptionThrown && logger is not null) {
         using var stream = new MemoryStream(base64, writable: false);
         using var decryptingStream = CreateDecryptingStream(stream);
+        using var decryptingReader = new StreamReader(decryptingStream, Encoding.UTF8);
 
         logger.LogTrace(
           "PassThroughResponse: {RawJson} ({TypeFullName})",
-          new StreamReader(decryptingStream, Encoding.UTF8).ReadToEnd(),
+          decryptingReader.ReadToEnd(),
           typeof(TValue).FullName
         );
       }
     }
   }
 
+#pragma warning disable CA1812
   private sealed class PassThroughObjectJsonConverter<TPassThroughObject>
     : JsonConverter<TPassThroughObject>
     // where TPassThroughObject : ITapoPassThroughRequest or ITapoPassThroughResponse
   {
+#pragma warning restore CA1812
     private readonly IPassThroughObjectJsonConverter converter;
 
     public PassThroughObjectJsonConverter(IPassThroughObjectJsonConverter converter)
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSessionCookieUtils.cs b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSessionCookieUtils.cs
index 279ae37..7939400 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSessionCookieUtils.cs
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.Protocol/TapoSessionCookieUtils.cs
@@ -13,8 +13,8 @@ public static class TapoSessionCookieUtils {
 
   // 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
+  internal const string HttpCookiePrefixForSessionId = "TP_SESSIONID="; // TP_SESSIONID=XXXXXX
+  internal const string HttpCookieAttributePrefixForTimeout = "TIMEOUT="; // TIMEOUT=xxxx
 
   public static bool TryGetCookie(
     HttpResponseMessage response,
@@ -25,6 +25,9 @@ public static class TapoSessionCookieUtils {
     sessionId = default;
     sessionTimeout = default;
 
+    if (response is null)
+      return false;
+
     return
       response.Headers.TryGetValues(HeaderNameSetCookie, out var setCookieValues) &&
       TryGetCookie(setCookieValues, out sessionId, out sessionTimeout);
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.csproj b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.csproj
index 06146cd..d8c4eec 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.csproj
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo.csproj
@@ -6,9 +6,10 @@ SPDX-License-Identifier: MIT
   <PropertyGroup>
     <TargetFrameworks>netstandard2.1;net6.0</TargetFrameworks>
     <TargetFrameworks Condition="$([MSBuild]::VersionGreaterThanOrEquals('$(NETCoreSdkVersion)', '7.0.0'))">net7.0;$(TargetFrameworks)</TargetFrameworks>
-    <VersionPrefix>1.0.0</VersionPrefix>
+    <VersionPrefix>1.0.1</VersionPrefix>
     <VersionSuffix></VersionSuffix>
-    <!-- <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion> -->
+    <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion>
+    <RootNamespace/> <!-- empty the root namespace so that the namespace is determined only by the directory name, for code style rule IDE0030 -->
     <Nullable>enable</Nullable>
     <DefineConstants
       Condition="$([MSBuild]::VersionGreaterThanOrEquals('$(NETCoreSdkVersion)', '7.0.0'))"
@@ -36,10 +37,6 @@ SPDX-License-Identifier: MIT
     <StyleCopAnalyzersConfigurationFile>..\stylecop.json</StyleCopAnalyzersConfigurationFile>
   </PropertyGroup>
 
-  <ItemGroup>
-    <GlobalAnalyzerConfigFiles Include="..\CodeAnalysis.globalconfig" />
-  </ItemGroup>
-
   <ItemGroup>
     <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
     <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoAuthenticationException.cs b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoAuthenticationException.cs
index 3c7d779..cfdf194 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoAuthenticationException.cs
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoAuthenticationException.cs
@@ -1,5 +1,7 @@
 // SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
 // SPDX-License-Identifier: MIT
+#pragma warning disable CA1032
+
 using System;
 
 namespace Smdn.TPSmartHomeDevices.Tapo;
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceDefaultExceptionHandler.cs b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceDefaultExceptionHandler.cs
index dcb8438..dc4f065 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceDefaultExceptionHandler.cs
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceDefaultExceptionHandler.cs
@@ -12,17 +12,13 @@ using Smdn.TPSmartHomeDevices.Tapo.Protocol;
 namespace Smdn.TPSmartHomeDevices.Tapo;
 
 internal sealed class TapoDeviceDefaultExceptionHandler : TapoDeviceExceptionHandler {
-  public override TapoDeviceExceptionHandling DetermineHandling(
-    TapoDevice device,
-    Exception exception,
+  private static TapoDeviceExceptionHandling DetermineHandling(
+    SocketException socketException,
     int attempt,
     ILogger? logger
   )
   {
-    switch (exception) {
-      case HttpRequestException httpRequestException:
-        if (httpRequestException.InnerException is SocketException innerSocketException) {
-          var socketErrorCode = innerSocketException.SocketErrorCode;
+    var socketErrorCode = socketException.SocketErrorCode;
 
     if (
       socketErrorCode is
@@ -40,7 +36,7 @@ internal sealed class TapoDeviceDefaultExceptionHandler : TapoDeviceExceptionHan
 
         return TapoDeviceExceptionHandling.InvalidateEndPointAndRetry;
       }
-            else {
+
       logger?.LogError(
         "Endpoint unreachable (SocketError: {SocketErrorCodeNumeric} {SocketErrorCode})",
         (int)socketErrorCode,
@@ -49,11 +45,10 @@ internal sealed class TapoDeviceDefaultExceptionHandler : TapoDeviceExceptionHan
 
       return TapoDeviceExceptionHandling.InvalidateEndPointAndThrow;
     }
-          }
 
     // The HTTP client may have been invalid due to an exception at the transport layer.
     logger?.LogError(
-            innerSocketException,
+      socketException,
       "Unexpected socket exception (SocketError: {SocketErrorCodeNumeric} {SocketErrorCode})",
       (int)socketErrorCode,
       socketErrorCode
@@ -62,20 +57,16 @@ internal sealed class TapoDeviceDefaultExceptionHandler : TapoDeviceExceptionHan
     return TapoDeviceExceptionHandling.Throw;
   }
 
+  private static TapoDeviceExceptionHandling DetermineHandling(
+    TapoErrorResponseException errorResponseException,
+    int attempt,
+    ILogger? logger
+  )
+  {
+    if (0 < attempt)
+      // retry just once
       return TapoDeviceExceptionHandling.Throw;
 
-      case SecurePassThroughInvalidPaddingException securePassThroughInvalidPaddingException:
-        if (attempt == 0 /* retry just once */) {
-          logger?.LogWarning("{Message}", securePassThroughInvalidPaddingException.Message);
-
-          // The session might have been in invalid state(?)
-          return TapoDeviceExceptionHandling.RetryAfterReestablishSession;
-        }
-
-        return TapoDeviceExceptionHandling.ThrowAsTapoProtocolException;
-
-      case TapoErrorResponseException errorResponseException:
-        if (attempt == 0 /* retry just once */) {
     switch (errorResponseException.RawErrorCode) {
       case TapoErrorCodes.DeviceBusy:
         logger?.LogWarning("{Message}", errorResponseException.Message);
@@ -101,10 +92,37 @@ internal sealed class TapoDeviceDefaultExceptionHandler : TapoDeviceExceptionHan
     }
   }
 
+  public override TapoDeviceExceptionHandling DetermineHandling(
+    TapoDevice device,
+    Exception exception,
+    int attempt,
+    ILogger? logger
+  )
+  {
+    switch (exception) {
+      case HttpRequestException httpRequestException:
+        if (httpRequestException.InnerException is not SocketException innerSocketException)
           return TapoDeviceExceptionHandling.Throw;
 
+        return DetermineHandling(innerSocketException, attempt, logger);
+
+      case SecurePassThroughInvalidPaddingException securePassThroughInvalidPaddingException:
+        if (attempt == 0 /* retry just once */) {
+          logger?.LogWarning("{Message}", securePassThroughInvalidPaddingException.Message);
+
+          // The session might have been in invalid state(?)
+          return TapoDeviceExceptionHandling.RetryAfterReestablishSession;
+        }
+
+        return TapoDeviceExceptionHandling.ThrowAsTapoProtocolException;
+
+      case TapoErrorResponseException errorResponseException:
+        return DetermineHandling(errorResponseException, attempt, logger);
+
       case TaskCanceledException taskCanceledException:
-        if (taskCanceledException.InnerException is TimeoutException) {
+        if (taskCanceledException.InnerException is not TimeoutException)
+          return TapoDeviceExceptionHandling.Throw;
+
         if (attempt < 2 /* retry up to 3 times */) {
           logger?.LogWarning("Request timed out; {ExceptionMessage}", taskCanceledException.Message);
           return TapoDeviceExceptionHandling.Retry;
@@ -113,9 +131,6 @@ internal sealed class TapoDeviceDefaultExceptionHandler : TapoDeviceExceptionHan
         logger?.LogError(taskCanceledException, "Request timed out");
 
         return TapoDeviceExceptionHandling.ThrowAsTapoProtocolException;
-        }
-
-        return TapoDeviceExceptionHandling.Throw;
 
       default:
         logger?.LogError(
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceInfo.cs b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceInfo.cs
index 096077e..d32492a 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceInfo.cs
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoDeviceInfo.cs
@@ -24,11 +25,13 @@ public class TapoDeviceInfo {
    * properties for the informations common to the devices
    */
 
+#pragma warning disable CA1819
   /// <summary>Gets the Tapo device's ID.</summary>
   /// <remarks>The value will be a <see cref="string"/> representing a HEX-encoded(Base16) byte array.</remarks>
   [JsonPropertyName("device_id")]
   [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
   public byte[]? Id { get; init; }
+#pragma warning restore CA1819
 
   /// <summary>Gets the Tapo device's type name.</summary>
   /// <remarks>The value would be like <c>SMART.TAPOBULB</c> for Tapo L530, as an example.</remarks>
@@ -40,33 +43,39 @@ public class TapoDeviceInfo {
   [JsonPropertyName("model")]
   public string? ModelName { get; init; }
 
+#pragma warning disable CA1819
   /// <summary>Gets the Tapo device's current firmware ID.</summary>
   /// <remarks>The value will be a <see cref="string"/> representing a HEX-encoded(Base16) byte array.</remarks>
   [JsonPropertyName("fw_id")]
   [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
   public byte[]? FirmwareId { get; init; }
+#pragma warning restore CA1819
 
   /// <summary>Gets the Tapo device's current firmware version string.</summary>
   /// <remarks>The value would be like <c>x.y.z Build yyyyMMdd Rel. XXXXX</c>, as an example.</remarks>
   [JsonPropertyName("fw_ver")]
   public string? FirmwareVersion { get; init; }
 
+#pragma warning disable CA1819
   /// <summary>Gets the Tapo device's hardware ID.</summary>
   /// <remarks>The value will be a <see cref="string"/> representing a HEX-encoded(Base16) byte array.</remarks>
   [JsonPropertyName("hw_id")]
   [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
   public byte[]? HardwareId { get; init; }
+#pragma warning restore CA1819
 
   /// <summary>Gets the Tapo device's hardware version string.</summary>
   /// <remarks>The value would be like <c>x.y.z</c>, as an example.</remarks>
   [JsonPropertyName("hw_ver")]
   public string? HardwareVersion { get; init; }
 
+#pragma warning disable CA1819
   /// <summary>Gets the Tapo device's OEM ID.</summary>
   /// <remarks>The value will be a <see cref="string"/> representing a HEX-encoded(Base16) byte array.</remarks>
   [JsonPropertyName("oem_id")]
   [JsonConverter(typeof(TapoBase16ByteArrayJsonConverter))]
   public byte[]? OemId { get; init; }
+#pragma warning restore CA1819
 
   /// <summary>Gets the <see cref="PhysicalAddress"/> that represents the Tapo device's MAC address.</summary>
   [JsonPropertyName("mac")]
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoErrorResponseException.cs b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoErrorResponseException.cs
index 2cff067..79adbaa 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoErrorResponseException.cs
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoErrorResponseException.cs
@@ -1,6 +1,9 @@
 // SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
 // SPDX-License-Identifier: MIT
+#pragma warning disable CA1032
+
 using System;
+
 using Smdn.TPSmartHomeDevices.Tapo.Protocol;
 
 namespace Smdn.TPSmartHomeDevices.Tapo;
diff --git a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoProtocolException.cs b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoProtocolException.cs
index 004243d..3cfe842 100644
--- a/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoProtocolException.cs
+++ b/src/Smdn.TPSmartHomeDevices.Tapo/Smdn.TPSmartHomeDevices.Tapo/TapoProtocolException.cs
@@ -1,5 +1,7 @@
 // SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
 // SPDX-License-Identifier: MIT
+#pragma warning disable CA1032
+
 using System;
 
 namespace Smdn.TPSmartHomeDevices.Tapo;

Notes

Full Changelog: releases/Smdn.TPSmartHomeDevices.Tapo-1.0.0...releases/Smdn.TPSmartHomeDevices.Tapo-1.0.1