{"id":15678962,"url":"https://github.com/aldaviva/kasa","last_synced_at":"2025-05-07T09:08:24.197Z","repository":{"id":40803003,"uuid":"499057463","full_name":"Aldaviva/Kasa","owner":"Aldaviva","description":"🔌 Control TP-Link Kasa smart outlets/plugs.","archived":false,"fork":false,"pushed_at":"2025-02-17T07:14:14.000Z","size":1751,"stargazers_count":14,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-07T09:08:16.218Z","etag":null,"topics":["home-automation","kasa","kasa-api","kasa-smart","smart-outlets","smart-plug","smarthome","smartplug","tp-link","tplink","tplink-kasa"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/Kasa/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Aldaviva.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":".github/funding.yml","license":"License.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":["https://paypal.me/aldaviva"]}},"created_at":"2022-06-02T08:39:35.000Z","updated_at":"2025-02-17T06:55:42.000Z","dependencies_parsed_at":"2024-10-28T03:49:13.501Z","dependency_job_id":null,"html_url":"https://github.com/Aldaviva/Kasa","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aldaviva%2FKasa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aldaviva%2FKasa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aldaviva%2FKasa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aldaviva%2FKasa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Aldaviva","download_url":"https://codeload.github.com/Aldaviva/Kasa/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252847494,"owners_count":21813454,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["home-automation","kasa","kasa-api","kasa-smart","smart-outlets","smart-plug","smarthome","smartplug","tp-link","tplink","tplink-kasa"],"created_at":"2024-10-03T16:25:37.277Z","updated_at":"2025-05-07T09:08:24.173Z","avatar_url":"https://github.com/Aldaviva.png","language":"C#","funding_links":["https://paypal.me/aldaviva"],"categories":[],"sub_categories":[],"readme":"Kasa\n===\n\n[![Nuget](https://img.shields.io/nuget/v/Kasa?logo=nuget)](https://www.nuget.org/packages/Kasa/) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Aldaviva/Kasa/dotnetpackage.yml?branch=master\u0026logo=github)](https://github.com/Aldaviva/Kasa/actions/workflows/dotnetpackage.yml) [![Testspace](https://img.shields.io/testspace/tests/Aldaviva/Aldaviva:Kasa/master?passed_label=passing\u0026failed_label=failing\u0026logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA4NTkgODYxIj48cGF0aCBkPSJtNTk4IDUxMy05NCA5NCAyOCAyNyA5NC05NC0yOC0yN3pNMzA2IDIyNmwtOTQgOTQgMjggMjggOTQtOTQtMjgtMjh6bS00NiAyODctMjcgMjcgOTQgOTQgMjctMjctOTQtOTR6bTI5My0yODctMjcgMjggOTQgOTQgMjctMjgtOTQtOTR6TTQzMiA4NjFjNDEuMzMgMCA3Ni44My0xNC42NyAxMDYuNS00NFM1ODMgNzUyIDU4MyA3MTBjMC00MS4zMy0xNC44My03Ni44My00NC41LTEwNi41UzQ3My4zMyA1NTkgNDMyIDU1OWMtNDIgMC03Ny42NyAxNC44My0xMDcgNDQuNXMtNDQgNjUuMTctNDQgMTA2LjVjMCA0MiAxNC42NyA3Ny42NyA0NCAxMDdzNjUgNDQgMTA3IDQ0em0wLTU1OWM0MS4zMyAwIDc2LjgzLTE0LjgzIDEwNi41LTQ0LjVTNTgzIDE5Mi4zMyA1ODMgMTUxYzAtNDItMTQuODMtNzcuNjctNDQuNS0xMDdTNDczLjMzIDAgNDMyIDBjLTQyIDAtNzcuNjcgMTQuNjctMTA3IDQ0cy00NCA2NS00NCAxMDdjMCA0MS4zMyAxNC42NyA3Ni44MyA0NCAxMDYuNVMzOTAgMzAyIDQzMiAzMDJ6bTI3NiAyODJjNDIgMCA3Ny42Ny0xNC44MyAxMDctNDQuNXM0NC02NS4xNyA0NC0xMDYuNWMwLTQyLTE0LjY3LTc3LjY3LTQ0LTEwN3MtNjUtNDQtMTA3LTQ0Yy00MS4zMyAwLTc2LjY3IDE0LjY3LTEwNiA0NHMtNDQgNjUtNDQgMTA3YzAgNDEuMzMgMTQuNjcgNzYuODMgNDQgMTA2LjVTNjY2LjY3IDU4NCA3MDggNTg0em0tNTU3IDBjNDIgMCA3Ny42Ny0xNC44MyAxMDctNDQuNXM0NC02NS4xNyA0NC0xMDYuNWMwLTQyLTE0LjY3LTc3LjY3LTQ0LTEwN3MtNjUtNDQtMTA3LTQ0Yy00MS4zMyAwLTc2LjgzIDE0LjY3LTEwNi41IDQ0UzAgMzkxIDAgNDMzYzAgNDEuMzMgMTQuODMgNzYuODMgNDQuNSAxMDYuNVMxMDkuNjcgNTg0IDE1MSA1ODR6IiBmaWxsPSIjZmZmIi8%2BPC9zdmc%2B)](https://aldaviva.testspace.com/spaces/191996) [![Coveralls](https://img.shields.io/coveralls/github/Aldaviva/Kasa?logo=coveralls)](https://coveralls.io/github/Aldaviva/Kasa?branch=master)\n\n*Control TP-Link Kasa smart outlets/plugs*\n\n![Kasa EP10](https://raw.githubusercontent.com/Aldaviva/Kasa/master/.github/images/readme-header.jpg)\n\n\u003c!-- MarkdownTOC autolink=\"true\" bracket=\"round\" autoanchor=\"false\" levels=\"1,2,3\" bullets=\"1.,-,-,-\" --\u003e\n\n1. [Quick Start](#quick-start)\n1. [Prerequisites](#prerequisites)\n1. [Terminology](#terminology)\n1. [Installation](#installation)\n1. [Configuration](#configuration)\n    - [Connections](#connections)\n    - [Options](#options)\n1. [Commands](#commands)\n    - [System](#system)\n    - [Time](#time)\n    - [Timer](#timer)\n    - [Schedule](#schedule)\n    - [Energy Meter](#energy-meter)\n1. [Exceptions](#exceptions)\n1. [Supporting additional devices](#supporting-additional-devices)\n1. [Acknowledgments](#acknowledgments)\n\n\u003c!-- /MarkdownTOC --\u003e\n\n## Quick Start\n```cs\nusing Kasa;\n\nusing IKasaOutlet kasa = new KasaOutlet(\"192.168.1.100\");\nif (!await kasa.System.IsSocketOn()) {\n    await kasa.System.SetSocketOn(true);\n}\n```\n\n## Prerequisites\n\n- A [Kasa smart outlet](https://www.kasasmart.com/us/products/smart-plugs)\n    - Verified devices:\n        - [EP10](https://www.kasasmart.com/us/products/smart-plugs/kasa-smart-plug-mini-ep10)\n        - [EP40](https://www.kasasmart.com/us/products/smart-plugs/kasa-smart-wi-fi-outdoor-plug)\n        - [KP125](https://www.kasasmart.com/us/products/smart-plugs/kasa-smart-plug-slim-energy-monitoring-kp125)\n    - Other similar devices may also work if they have the same API, such as the HS103, HS105, KP100, KP115, EP25, or KP400\n    - See [Supporting additional devices](#supporting-additional-devices) if your device is not supported\n- Any .NET runtime that supports [.NET Standard 2.0 or later](https://docs.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0#net-standard-versions):\n    - [.NET 5.0 or later](https://dotnet.microsoft.com/en-us/download/dotnet)\n    - [.NET Core 2.0 or later](https://dotnet.microsoft.com/en-us/download/dotnet)\n    - [.NET Framework 4.6.1 or later](https://dotnet.microsoft.com/en-us/download/dotnet-framework)\n\n## Terminology\n\n_**outlet**_\n\nThe entire Kasa device that supplies AC power, regardless of whether it takes the form of a plug, dongle, strip, or wall plate. Has one or more electrical sockets on it.\n\n_**socket**_\n\nA three-prong 15A AC NEMA 5-15-R receptacle located on an outlet. Unrelated to TCP/IP socket connections used for network communication.\n\nFor example, the EP10 is an outlet that has one socket, while the EP40 is an outlet that has two sockets.\n\n## Installation\n\nYou can install this library into your project from [NuGet Gallery](https://www.nuget.org/packages/Kasa):\n- `dotnet add package Kasa`\n- `Install-Package Kasa`\n- Go to Project › Manage NuGet Packages in Visual Studio and search for `Kasa`\n\n## Configuration\n\n1. Connect your Kasa device to your wi-fi network.\n    - You can do this with the [Kasa Smart app for Android](https://play.google.com/store/apps/details?id=com.tplink.kasa_android) or [iOS](https://apps.apple.com/us/app/kasa-smart/id1034035493).\n1. Get your device's hostname.\n    - You can find its IP address in your router's client or DHCP lists. The MAC address will match the one printed on the device and shown in Device Info in the Kasa Smart app.\n    - You can scan for servers exposing TCP port 9999.\n        ```sh\n        nmap --open -pT:9999 192.168.1.0/24\n        ```\n    - You can use its FQDN if you assigned one using a DNS A record.\n1. Construct a new instance of a class below, passing the device's hostname as the `hostname` constructor parameter.\n    - If your device has exactly one AC socket (like the EP10), the class to construct is **`KasaOutlet`**.\n        ```cs\n        using IKasaOutlet kasa = new KasaOutlet(hostname: \"192.168.1.100\");\n        ```\n    - If your device has multiple AC sockets (like the EP40), the class to construct is **`MultiSocketKasaOutlet`**.\n        ```cs\n        using IMultiSocketKasaOutlet kasa = new MultiSocketKasaOutlet(hostname: \"192.168.1.100\");\n        ```\n        Commands that are related to one specific socket, rather than the overall outlet device, require a socket identifier as the first argument. This ID is the 0-indexed position of the socket on the device. For example, pass `0` as the `socketId` argument to control \"plug 1\" on an EP40, and pass `1` to control \"plug 2.\"\n    - If you don't know how many AC sockets the outlet has and can't set it through configuration or parameters, you can construct a `KasaOutlet` and check its [`System.CountSockets()`](#countsockets) result to determine if you should instead construct a `MultiSocketKasaOutlet`. This requires the outlet to be online, and is slower and asynchronous because it sends it a request.\n        ```cs\n        KasaOutlet singleSocketOutlet = new(hostname: \"192.168.1.100\");\n        if (await singleSocketOutlet.System.CountSockets() == 1) {\n            return singleSocketOutlet;\n        } else {\n            singleSocketOutlet.Dispose();\n            return new MultiSocketKasaOutlet(hostname: \"192.168.1.100\");\n        }\n        ```\n        This logic is provided in **`KasaOutletFactory.CreateOutlet`**:\n        ```cs\n        using IKasaOutletBase kasa = await KasaOutletFactory.CreateOutlet(hostname: \"192.168.1.100\");\n        ```\n\nInstances can be reused to send multiple commands over the lifetime of your application. You can add one to your dependency injection context and retain it for as long as you like. Remember to `Dispose` it when you're done using it, so that it can tear down the TCP socket.\n\n### Connections\n\nThe `KasaOutlet` instance will try to transparently handle the TCP connection for you. It will automatically lazily ensure the TCP socket is connected to the Kasa device's server before sending any commands.\n\nIf the connection drops, for example if the device reboots, it will automatically attempt to reconnect multiple times before sending the next command, with a delay between attempts. The number of attempts and delay duration can be adjusted using the [options](#options) below. If all reconnections fail, it will throw a `NetworkException` (see [Exceptions](#exceptions) below).\n\nOptionally, you may manually eagerly connect before sending any commands, if you want to connect early to test the connection or ensure lower latency for the first command:\n\n```cs\nawait kasa.Connect();\n```\n\n### Options\n\nYou can customize the `KasaOutlet` instance by setting optional properties to control logging, timeouts, and retries.\n\n```cs\nusing IKasaOutlet kasa = new KasaOutlet(hostname: \"192.168.1.100\", new Options {\n    LoggerFactory = loggerFactory,\n    MaxAttempts = 20,\n    RetryDelay = TimeSpan.FromSeconds(1),\n    SendTimeout = TimeSpan.FromSeconds(2),\n    ReceiveTimeout = TimeSpan.FromSeconds(2)\n});\n```\n\n#### Logging\n\nThis library will emit log messages at the `Debug` level when it connects and disconnects from the Kasa device's TCP server. It will also emit `Trace` messages with the raw, stringified JSON objects sent and received on the TCP socket.\n\n[`Microsoft.Extensions.Logging.ILoggerFactory`](https://docs.microsoft.com/en-us/dotnet/core/extensions/logging) instances can be injected from an [ASP.NET Core Builder](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0) or [.NET Generic Host Builder](https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host), or [created manually](https://docs.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line#non-host-console-app).\n\n##### ASP.NET Core\n```cs\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddSingleton\u003cIKasaOutlet\u003e(services =\u003e new KasaOutlet(\"192.168.1.100\", new Options {\n    LoggerFactory = services.GetRequiredService\u003cILoggerFactory\u003e()\n}));\n```\n\n```js\n// appsettings.json\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Kasa\": \"Trace\"\n    }\n  }\n}\n```\n\n##### .NET Generic Host\n```cs\nHost.CreateDefaultBuilder(args).ConfigureServices(services =\u003e {\n    services.AddSingleton\u003cIKasaOutlet\u003e(s =\u003e new KasaOutlet(\"192.168.1.100\", new Options {\n        LoggerFactory = s.GetRequiredService\u003cILoggerFactory\u003e()\n    }));\n});\n```\n\n```js\n// appsettings.json\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Kasa\": \"Trace\"\n    }\n  }\n}\n```\n\n##### Manual\n```c\nILoggerFactory loggerFactory = LoggerFactory.Create(builder =\u003e builder\n    .AddFilter(\"Kasa\", LogLevel.Trace)\n    .AddConsole());\n\nusing IKasaOutlet kasa = new KasaOutlet(\"192.168.1.100\", new Options {\n    LoggerFactory = loggerFactory\n});\n```\n\n##### Third-party logging providers\nYou can also [adapt other logging providers](https://docs.microsoft.com/en-us/dotnet/core/extensions/logging-providers#third-party-logging-providers), such as [NLog](https://github.com/NLog/NLog/wiki/Getting-started-with-.NET-Core-2---Console-application), to consume the logs produced by `Kasa.KasaOutlet`.\n\n```cs\nConsoleTarget consoleLog = new();\nLoggingConfiguration nlogConfig = new();\nnlogConfig.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, consoleLog);\nLogManager.Configuration = nlogConfig;\n\nILoggerFactory loggerFactory = LoggerFactory.Create(builder =\u003e builder\n    .ClearProviders()\n    .AddNLog());\n\nusing IKasaOutlet kasa = new KasaOutlet(\"192.168.1.100\", new Options {\n    LoggerFactory = loggerFactory\n});\n```\n\n## Commands\n\nAll commands are asynchronous, so you should `await` the returned `Task` inside an `async` method to get the result.\n\nMore information about each command, including the parameters accepted and data returned, is available in their class and method comments.\n\n### System\n\nCommands that get or set system properties, like status, name, and whether the socket is on or off.\n\n#### IsSocketOn\nGet whether the socket on the device is energized and can supply power to any connected electrical consumers or not.\n\nThis is unrelated to whether the entire Kasa outlet device is booted up and running. If you can connect to it, it's running.\n```cs\nbool isOn = await kasa.System.IsSocketOn();\nConsole.WriteLine($\"Socket is {(isOn ? \"on\" : \"off\")}\");\n```\n```text\nSocket is on\n```\n\nFor devices with multiple sockets like the EP40, construct a new `MultiSocketKasaOutlet` instead of `KasaOutlet`, and pass the 0-indexed socket ID to `IsSocketOn` that specifies which socket to query.\n```cs\nusing IMultiSocketKasaOutlet ep40 = new MultiSocketKasaOutlet(\"192.168.1.100\");\nfor (int socketId = 0; socketId \u003c await ep40.System.CountSockets(); socketId++) {\n    bool isOn = await ep40.System.IsSocketOn(socketId);\n    Console.WriteLine($\"Socket {socketId} is {(isOn ? \"on\" : \"off\")}\");\n}\n```\n```text\nSocket 0 is on\nSocket 1 is off\n```\n\n#### SetSocketOn\nTurn on or off the device's socket so it can supply power to any connected electrical consumers or not.\n\nYou can also toggle the socket by pressing the physical button on the device.\n\nThis call is idempotent: if you try to turn the socket on and it's already on, the call will have no effect.\n\nThe state is persisted across restarts. If the device loses power, it will restore the previous socket power state when it turns on again.\n\nThis call is unrelated to turning the entire Kasa outlet device on or off. To reboot the device, use [`Reboot`](#reboot).\n```cs\nawait kasa.System.SetSocketOn(true);\n```\n\nFor devices with multiple sockets like the EP40, construct a new `MultiSocketKasaOutlet` instead of `KasaOutlet`, and pass the 0-indexed socket ID to `SetSocketOn` that specifies which socket to control.\n```cs\nusing IMultiSocketKasaOutlet ep40 = new MultiSocketKasaOutlet(\"192.168.1.100\");\nawait ep40.System.SetSocketOn(0, true);\n```\n\n#### GetName\nThe name or alias of the device that you chose during setup.\n```cs\nstring name = await kasa.System.GetName();\nConsole.WriteLine($\"Name: {name}\");\n```\n```text\nName: Washing Machine\n```\n\nFor devices with multiple sockets like the EP40, each individual socket also has its own name, in addition to the outlet device's overall name. You can fetch the individual socket name by passing the 0-indexed socket ID to `GetName`.\n```cs\nawait kasa.System.GetName(0);\n```\n\n#### SetName\nChange the alias of this device. This will appear in the Kasa mobile app.\n```cs\nawait kasa.System.SetName(\"My Outlet\");\n```\n\nFor devices with multiple sockets like the EP40, each individual socket also has its own name, in addition to the outlet device's overall name. You can change the individual socket name by passing the 0-indexed socket ID to `SetName`.\n```cs\nawait kasa.System.SetName(0, \"Socket 1\");\n```\n\n#### CountSockets\nSome outlets have multiple sockets, like the EP40. This method returns the number of physical AC sockets actually on the device, regardless of whether the class is `KasaOutlet` or `MultiSocketKasaOutlet`, so you can use this to dynamically decide which class to construct. This count excludes USB ports.\n\n```cs\nint socketCount = await kasa.System.CountSockets();\nConsole.WriteLine($\"Device has {socketCount} sockets.\");\n```\n```text\nDevice has 2 sockets.\n```\n\n#### GetInfo\nGet data about the device, including hardware, software, configuration, and current state.\n```cs\nSystemInfo systemInfo = await kasa.System.GetInfo();\nConsole.WriteLine($\"Operating mode: {systemInfo.OperatingMode}\");\nConsole.WriteLine($\"Model name: {systemInfo.ModelName}\");\nConsole.WriteLine($\"Model family: {systemInfo.ModelFamily}\");\nConsole.WriteLine($\"Signal strength: {systemInfo.SignalStrength} dBm\");\nConsole.WriteLine($\"Features: {string.Join(\", \", systemInfo.Features)}\");\nConsole.WriteLine($\"MAC address: {systemInfo.MacAddress}\");\nConsole.WriteLine($\"Device ID: {systemInfo.DeviceId}\");\nConsole.WriteLine($\"Updating: {systemInfo.Updating}\");\nConsole.WriteLine($\"Software version: {systemInfo.SoftwareVersion}\");\nConsole.WriteLine($\"Hardware version: {systemInfo.HardwareVersion}\");\nConsole.WriteLine($\"Hardware ID: {systemInfo.HardwareId}\");\nConsole.WriteLine($\"OEM ID: {systemInfo.OemId}\");\n```\n```text\nOperating mode: Schedule\nModel name: EP10(US)\nModel family: Smart Wi-Fi Plug Mini\nSignal strength: -49 dBm\nFeatures: Timer\nMAC address: 5CA6E64EF3EF\nDevice ID: 8006C153CFEBDE93CD3572549B5A47611F49F0D2\nUpdating: False\nSoftware version: 1.0.2 Build 200915 Rel.085940\nHardware version: 1.0\nHardware ID: AE6865C67F6A54B756C0B5812472C825\nOEM ID: 41372DE62C896B2C0E93C20D70B62DDB\n```\n\n#### IsIndicatorLightOn\nSockets have a physical status light (usually a blue or green LED) that shows whether they are supplying power to consumers or not.\n\nThis light can be disabled even when the socket is on, for example if it's annoyingly bright in a room where you're trying to watch a movie or go to sleep.\n\nBy default, this returns `true`, and the light will turn on if and only if the socket is supplying power.\n```cs\nbool isIndicatorLightOn = await kasa.System.IsIndicatorLightOn();\nConsole.WriteLine($\"Is indicator light on: {isIndicatorLightOn}\");\n```\n```text\nIs indicator light on: true\n```\n\n#### SetIndicatorLightOn\nSockets have a physical status light that shows whether they are supplying power to consumers or not.\n\nThis light can be disabled even when the socket is on, for example if it's annoyingly bright in a room where you're trying to watch a movie or go to sleep.\n\nWhen you set this to `true` (the default value), the light will turn on if and only if the socket is supplying power. When you set this to `false`, the light will never turn on, regardless of whether the socket is supplying power. This setting is persistent, so if you want the light to always be off, you don't need to call `SetIndicatorLightOn(false)` every time you call `SetSocketOn(true)`.\n\nEven when you set this to `false`, the light will still blink for a few seconds while the outlet is booting up before turning the light off.\n```cs\nawait kasa.System.SetIndicatorLightOn(true);\n```\n\n#### Reboot\nRestart the device.\n\nRebooting will interrupt power to any connected consumers for roughly 108 milliseconds.\nIt takes about 8 seconds for a KP125 to completely reboot and resume responding to API requests, and about 14 seconds for an EP10.\n\nThe existing socket power state will be retained after rebooting, so if it was on before rebooting, it will turn on again after rebooting, and there is no need to explicitly call [`SetSocketOn`](#setsocketon) to reestablish the previous state.\n\nBy default, this client will automatically reconnect to the outlet after it reboots, which can be tuned using the [`MaxAttempts`](#options) and [`RetryDelay`](#options) properties.\n```cs\nawait kasa.System.Reboot(TimeSpan.FromSeconds(5));\n```\n\n### Time\n\nCommands that deal with the device's internal clock that keeps track of the current wall clock date and time.\n\nThis is unrelated to schedules and timers that control when the socket turns on or off, see [Timer](#timer).\n\n#### GetTime\nGet the current local date and time from the device's internal clock.\n```cs\nDateTime time = await kasa.Time.GetTime();\nConsole.WriteLine($\"Device time: {time:F}\");\n```\n```text\nDevice time: Saturday, June 11, 2022 3:48:21 am\n```\n\n#### GetTimeWithZoneOffset\nGet the current date, time, and time zone from the device's internal clock.\n```cs\nDateTimeOffset dateTime = await kasa.Time.GetTimeWithZoneOffset();\nConsole.WriteLine($\"Device time: {dateTime:O}\");\n```\n```text\nDevice time: 2022-06-11T03:56:03.0000000-07:00\n```\n\n#### GetTimeZones\nGet a list of possible time zones that the device is in.\n\nThis may return multiple possibilities instead of one time zone because, unfortunately, Kasa devices internally represent multiple time zones with non-unique identifiers.\n\nFor example, `Central Standard Time` is unambiguously stored as `13` on the Kasa device, so this method will only return that time zone. However, `Eastern Standard Time` is stored as `18` on the Kasa device, which collides with `18` that it also uses to represent `Eastern Standard Time (Mexico)`, `Turks and Caicos Standard Time`, `Haiti Standard Time`, and `Easter Island Standard Time`, so this method will return all five possibilities since they cannot be distinguished based on the information provided by the device.\n```cs\nIEnumerable\u003cTimeZoneInfo\u003e timeZones = await kasa.Time.GetTimeZones();\nConsole.WriteLine($\"Device time zone may be {string.Join(\" or \", timeZones.Select(zone =\u003e zone.Id))}\");\n```\n```text\nDevice time zone may be Yukon Standard Time or Pacific Standard Time\n```\n\n#### SetTimeZone\nConfigure the device to use a specific time zone.\n```cs\nawait kasa.Time.SetTimeZone(TimeZoneInfo.Local);\n```\n\n### Timer\nCountdown timers allow you to schedule the socket to turn on or off once after a delay of configurable duration.\n\nSockets can handle at most one timer at once.\n\nThis is unrelated to the current time of the device's internal clock, see [Time](#time).\n\n#### Get\nGet the currently running countdown timer rule on the device, along with its updated `RemainingDuration`.\n\nThere can be either 0 or 1 timers on the device at once; multiple timers are not possible.\n\nIf no timer has ever been created, it already elapsed, or you deleted it with [Clear](#clear), this method will return `null`.\n\n```cs\nif(await outlet.Timer.Get() is { } timer){\n    Console.WriteLine($\"Socket will turn {(timer.SetSocketOnWhenComplete ? \"on\" : \"off\")} in {timer.RemainingDuration.TotalSeconds:N1} seconds.\");\n} else {\n    Console.WriteLine(\"No timer running.\");\n}\n```\n```text\nSocket will turn on in 9.3 seconds.\n```\n\nFor devices like the EP40 with multiple sockets, pass the 0-indexed socket ID as the first argument.\n\n#### Start\nSave a new, enabled countdown timer to the device.\n\nThere can be at most one timer on the device at once, so any existing timers will first be deleted, even if they had not elapsed yet.\n\nThe created timer will be returned, which is useful if you want to inspect the newly-populated `RemainingDuration` property.\n\n```cs\nTimer timer = await outlet.Timer.Start(TimeSpan.FromMinutes(30), true);\nConsole.WriteLine($\"Socket will turn {(timer.SetSocketOnWhenComplete ? \"on\" : \"off\")} in {timer.RemainingDuration.TotalSeconds:N1} seconds.\");\n```\n```text\nSocket will turn on in 1,800.0 seconds.\n```\n\nFor devices like the EP40 with multiple sockets, pass the 0-indexed socket ID as the first argument.\n\n#### Clear\nDelete any existing timer rule from the device, cancelling its countdown.\n\nThis will cause [Get](#get) to return `null` until you [Start](#start) a new timer.\n\nIdempotent: this will succeed even if there are no timers to delete.\n\n```cs\nawait outlet.Timers.Clear();\n```\n\nFor devices like the EP40 with multiple sockets, pass the 0-indexed socket ID as the first argument.\n\n### Schedule\nCommands that deal with schedules.\n\nSchedules allow you to set the socket to turn on or off once on a specific date and time, or on multiple days with a weekly recurrence pattern. Times can be relative to the start of the day, sunrise, or sunset.\n\n#### GetAll\nFetch all of the existing schedules from the socket.\n```cs\nIEnumerable\u003cSchedule\u003e schedules = await outlet.Schedule.GetAll();\nforeach (Schedule s in schedules) {\n    string time = s.TimeBasis switch {\n        Schedule.Basis.StartOfDay =\u003e TimeOnly.FromTimeSpan(s.Time).ToString(),\n        Schedule.Basis.Sunrise    =\u003e $\"{s.Time:%m} min {(s.Time \u003c TimeSpan.Zero ? \"before\" : \"after\")} sunrise\",\n        Schedule.Basis.Sunset     =\u003e $\"{s.Time:%m} min {(s.Time \u003c TimeSpan.Zero ? \"before\" : \"after\")} sunset\"\n    };\n    Console.WriteLine($\"Turn {(s.WillSetSocketOn ? \"on \" : \"off\")} at {time} on {(s.IsRecurring ? string.Join(\", \", s.DaysOfWeek) : s.Date)}{(s.IsEnabled ? \"\" : \" (disabled)\")}\");\n}\n```\n```text\nTurn on  at 19:45 on Tuesday, Wednesday, Thursday\nTurn off at 23:45 on Monday, Tuesday, Wednesday, Thursday\nTurn on  at 18:45 on Monday\n```\n\n#### Save\nPersist a schedule to the socket.\n\nTo insert a new schedule, construct a new `Schedule` instance, leaving its `Schedule.Id` property `null`. After saving it with this method, the returned instance will be a copy with the `Schedule.Id` value populated.\n\nTo update an existing schedule, retrieve it using `GetAll`, make any changes you like, then save it with this method.\n\n```cs\nSchedule schedule = new(true, new[] { DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday }, new TimeOnly(12 + 7, 45));\nschedule = await outlet.Schedule.Save(schedule);\nConsole.WriteLine($\"Created schedule with ID {schedule.Id}\");\n```\n```text\nCreated schedule with ID 0D3D1C778103039FEBCAB23C261AD821\n```\n\n#### Delete\nRemove an existing schedule from the socket.\n\nYou can pass an existing Schedule instance or just its `Id`.\n\n```cs\nSchedule schedule = (await outlet.Schedule.GetAll()).First();\nawait outlet.Schedule.Delete(schedule);\n```\n```cs\nawait outlet.Schedule.Delete(\"0D3D1C778103039FEBCAB23C261AD821\");\n```\n\n#### DeleteAll\nClear all existing schedules from the socket.\n\n```cs\nawait outlet.Schedule.DeleteAll();\n```\n\n### Energy Meter\nCommands that deal with the energy meter present in some Kasa devices, such as the EP25, KP125, and KP115.\n\nTo determine if your device has an energy meter, you can call:\n```cs\nbool hasEnergyMeter = (await kasaOutlet.System.GetInfo()).Features.Contains(Feature.EnergyMeter);\n```\n\n#### GetInstantaneousPowerUsage\nFetch a point-in-time measurement of the instantaneous electrical usage of the socket.\n\nThe returned `PowerUsage` struct has `Current` (mA), `Voltage` (mV), `Power` (mW) fields. There is also a `CumulativeEnergySinceBoot` (W⋅h) field that shows how much total energy has been used since the Kasa device last booted, and which resets to `0` when the device [reboots](#reboot), loses power, or has [`DeleteHistoricalUsage`](#deletehistoricalusage) called on it.\n```cs\nPowerUsage usage = await kasa.EnergyMeter.GetInstantaneousPowerUsage();\nConsole.WriteLine($\"Attached consumer is currently using {usage.Current:N0} mA, {usage.Voltage / 1000.0:N3} V, and {usage.Power:N0} mW.\");\nConsole.WriteLine($\"It has used {usage.CumulativeEnergySinceBoot / 1000.0:N3} kWh since the Kasa device last booted.\");\n```\n```text\nAttached consumer is currently using 0 mA, 123.069 V, and 0 mW.\nIt has used 0.433 kWh since the Kasa device last booted.\n```\n\n#### GetDailyEnergyUsage\nFor a given month and year, fetch a historical report of cumulative energy usage, grouped by day and aligned to the start of the day (midnight).\n\nThe returned array has one entry for each day in the given month, and the days are 0-indexed (the first day of the month is located at index `0`). The array values are in watt-hours.\n\nThese data persist on the device across power loss and [reboots](#reboot); to reset them, call [`DeleteHistoricalUsage`](#deletehistoricalusage).\n```cs\nDateTimeOffset now = DateTimeOffset.Now;\nif (await kasa.EnergyMeter.GetDailyEnergyUsage(now.Year, now.Month) is { } days) {\n    int monthStart = (int) new DateTime(now.Year, now.Month, 1).DayOfWeek;\n    Console.Write(string.Join(null, Enumerable.Repeat(\"           | \", monthStart)));\n    for (int day = 0; day \u003c days.Count; day++) {\n        int      usage = days[day];\n        DateTime date  = new(now.Year, now.Month, day + 1);\n        Console.Write($\"{date,2:%d}: {usage,3:N0} Wh{(day % 7 == monthStart ? \"\\n\" : \" | \")}\");\n    }\n\n    Console.WriteLine();\n} else {\n    Console.WriteLine(\"No energy data for the given month and year\");\n}\n```\n```text\n           |            |            |  1:  23 Wh |  2:  24 Wh |  3:  23 Wh |  4: 188 Wh\n 5: 481 Wh |  6:   8 Wh |  7:   7 Wh |  8:   8 Wh |  9:   8 Wh | 10:  12 Wh | 11:  22 Wh\n12:  22 Wh | 13:  23 Wh | 14:  24 Wh | 15:  23 Wh | 16:  24 Wh | 17:  23 Wh | 18:  24 Wh\n19: 181 Wh | 20:   7 Wh | 21:   8 Wh | 22:   7 Wh | 23:   2 Wh | 24:   0 Wh | 25:   0 Wh\n26:   0 Wh | 27:   0 Wh | 28:   0 Wh | 29:   0 Wh | 30:   0 Wh | 31:   0 Wh |\n```\n\n#### GetMonthlyEnergyUsage\nFor a given year, fetch a historical report of cumulative energy usage, grouped by month.\n\nThe returned array has 12 entries for each month in the given year, and the months are 0-indexed (January is located at index `0`). The array values are in watt-hours.\n\nThese data persist on the device across power loss and [reboots](#reboot); to reset them, call [`DeleteHistoricalUsage`](#deletehistoricalusage).\n```cs\nDateTimeOffset now = DateTimeOffset.Now;\nif (await kasa.EnergyMeter.GetMonthlyEnergyUsage(now.Year) is { } months) {\n    for (int month = 0; month \u003c 12; month++) {\n        int      usage = months[month];\n        DateTime date  = new(now.Year, month + 1, 1);\n        Console.Write($\"{date:MMM}: {usage,5:N0} Wh{(month % 3 == 2 ? \"\\n\" : \" | \")}\");\n    }\n} else {\n    Console.WriteLine(\"No energy data for the given year\");\n}\n```\n```text\nJan:   797 Wh | Feb: 1,020 Wh | Mar: 1,172 Wh\nApr:     0 Wh | May:     0 Wh | Jun:     0 Wh\nJul:     0 Wh | Aug:     0 Wh | Sep:     0 Wh\nOct:     0 Wh | Nov:     0 Wh | Dec:     0 Wh\n```\n\n#### DeleteHistoricalUsage\nClear all energy usage data for all days, months, and years, and begin gathering new data from a fresh start.\n\nAfter calling this method, subsequent calls to [`GetDailyEnergyUsage`](#getdailyenergyusage) will return `null` for past months, and the current month's data will be reset to `0`. Subsequent calls to [`GetMonthlyEnergyUsage`](#getmonthlyenergyusage) will similarly return `null` for past years, and the current year's data will be reset to `0`. Since these are not reset on power loss or reboot, this `DeleteHistoricalUsage` method is the only way to reset these.\n\nIn addition, subsequent calls to [`GetInstantaneousPowerUsage`](#getinstantaneouspowerusage) will return `0` for `CumulativeEnergySinceBoot`, just like a [reboot](#reboot), although it will not affect the point-in-time, non-historical measurements `Current`, `Voltage`, and `Power`.\n```cs\nawait kasa.EnergyMeter.DeleteHistoricalUsage();\n```\n\n## Exceptions\n\nAll known exceptions thrown by this library are documented in the comments of each method.\n\nEach command can throw two main exceptions:\n- **`NetworkException`** if the TCP socket connection failed and could not be automatically recovered. Check the inner `SocketException` or `IOException` for the cause.\n- **`ResponseParsingException`** if the TCP server returned JSON that could not be deserialized, possibly because the API changed or the device is unsupported.\n\nIf you try to run a command on an outlet that doesn't support it, it will throw a **`FeatureUnavailable`** exception, for example, if you try to retrieve the energy usage of an EP10 outlet which doesn't have an energy meter. You can check the `RequiredFeature` property of the exception to see which `Feature` was required, and you can call `IKasaOutlet.System.GetInfo()` and check the contents of the returned `SystemInfo` struct's `Features` set to see which features your outlet offers.\n\nSome methods also throw other exceptions in specific cases, such as `ArgumentOutOfRangeException` or `TimeZoneNotFoundException`. Check the `\u003cexception\u003e` XML documentation comments for each method, or use an exception checker tool like [Exception Analyzers 2022](https://marketplace.visualstudio.com/items?itemName=carlreinke.ExceptionAnalyzers2022).\n\n## Supporting additional devices\n\nIf you want this library to support [more Kasa smart outlets](https://www.kasasmart.com/us/products/smart-plugs), then you may help me buy the hardware to develop, test, and document those integrations with [Amazon wishlist gifts](https://www.amazon.com/hz/wishlist/ls/19QN9PU1W8CRE?ref_=wl_share) or [PayPal donations](https://paypal.me/aldaviva) (you can specify a funding goal in the donation description).\n\n#### Funding goals\n\n|Image|Name|Sockets|Weatherproofing|Form factor|Reason to develop \u0026 test|Cost|\n|-|-|-:|-|-|-|-:|\n|![KP405](https://raw.githubusercontent.com/Aldaviva/Kasa/master/.github/images/kp405.png)|[**KP405**](https://www.kasasmart.com/us/products/smart-plugs/product-kp405)|1|Outdoor|Dongle|Dimmer|$27 USD|\n|![HS300](https://raw.githubusercontent.com/Aldaviva/Kasa/master/.github/images/hs300.png)|[**HS300**](https://www.kasasmart.com/us/products/smart-plugs/kasa-smart-wi-fi-power-strip-hs300)|6|Indoor|Strip|Multiple individual socket energy monitoring|$52 USD|\n\n## Acknowledgments\n- [tplink-smarthome-commands.txt](https://github.com/softScheck/tplink-smartplug/blob/master/tplink-smarthome-commands.txt) — *Lubomir Stroetmann, Tobias Esser*\n- [Reverse Engineering the TP-Link HS110](https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/) — *Lubomir Stroetmann, Tobias Esser*\n- [Controlling the TP-LINK HS100 Wi-Fi smart plug](https://blog.georgovassilis.com/2016/05/07/controlling-the-tp-link-hs100-wi-fi-smart-plug/) — *George Georgovassilis, Thomas Baust*\n- [python-kasa](https://github.com/python-kasa/python-kasa)\n- [Pi Projects](https://morepablo.com/2022/04/household-pi-projects.html) — *Pablo Meier*\n- [THLaundry](http://thlaundry.techhouse.org) — *Robert Mustacchi*\n- [homebridge-tplink-smarthome#202](https://github.com/plasticrake/homebridge-tplink-smarthome/issues/202) — *the1maximus*\n- [KasaLink](https://github.com/mguinness/KasaLink/blob/main/KasaLink/Program.cs) — *mguinness*\n- [kasasock](https://github.com/english299/Smart-Home-Without-the-Cloud/blob/main/kasaHS1xx/csharp/kasasock.cs) — *Brian English*\n- [Home Assistant](https://www.home-assistant.io/integrations/tplink)\n- EP40 hardware donation — *John M. McKeon*\n- [Wireshark TP-Link Smart Home Protocol dissector](https://www.wireshark.org/docs/dfref/t/tplink-smarthome.html) — *Fulko Hew*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faldaviva%2Fkasa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faldaviva%2Fkasa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faldaviva%2Fkasa/lists"}