Common API

SmartDevice class

The basic functionalities of all supported devices are accessible using the common SmartDevice base class.

The property accesses use the data obtained before by awaiting SmartDevice.update(). The values are cached until the next update call. In practice this means that property accesses do no I/O and are dependent, while I/O producing methods need to be awaited. See Library Design & Modules for more detailed information.

Note

The device instances share the communication socket in background to optimize I/O accesses. This means that you need to use the same event loop for subsequent requests. The library gives a warning (“Detected protocol reuse between different event loop”) to hint if you are accessing the device incorrectly.

Methods changing the state of the device do not invalidate the cache (i.e., there is no implicit SmartDevice.update() call made by the library). You can assume that the operation has succeeded if no exception is raised. These methods will return the device response, which can be useful for some use cases.

Errors are raised as SmartDeviceException instances for the library user to handle.

Simple example script showing some functionality for legacy devices:

import asyncio
from kasa import SmartPlug

async def main():
    p = SmartPlug("127.0.0.1")

    await p.update()  # Request the update
    print(p.alias)  # Print out the alias
    print(p.emeter_realtime)  # Print out current emeter status

    await p.turn_off()  # Turn the device off

if __name__ == "__main__":
    asyncio.run(main())

If you are connecting to a newer KASA or TAPO device you can get the device via discovery or connect directly with DeviceConfig:

import asyncio
from kasa import Discover, Credentials

async def main():
    device = await Discover.discover_single(
        "127.0.0.1",
        credentials=Credentials("myusername", "mypassword"),
        discovery_timeout=10
    )

    config = device.config # DeviceConfig.to_dict() can be used to store for later

    # To connect directly later without discovery

    later_device = await SmartDevice.connect(config=config)

    await later_device.update()

    print(later_device.alias)  # Print out the alias

If you want to perform updates in a loop, you need to make sure that the device accesses are done in the same event loop:

import asyncio
from kasa import SmartPlug

async def main():
    dev = SmartPlug("127.0.0.1")  # We create the instance inside the main loop
    while True:
        await dev.update()  # Request an update
        print(dev.emeter_realtime)
        await asyncio.sleep(0.5)  # Sleep some time between updates

if __name__ == "__main__":
    asyncio.run(main())

Refer to device type specific classes for more examples: SmartPlug, SmartBulb, SmartStrip, SmartDimmer, SmartLightStrip.

DeviceConfig class

The DeviceConfig class can be used to initialise devices with parameters to allow them to be connected to without using discovery. This is required for newer KASA and TAPO devices that use different protocols for communication and will not respond on port 9999 but instead use different encryption protocols over http port 80. Currently there are three known types of encryption for TP-Link devices and two different protocols. Devices with automatic firmware updates enabled may update to newer versions of the encryption without separate notice, so discovery can be helpful to determine the correct config.

To connect directly pass a DeviceConfig object to SmartDevice.connect().

A DeviceConfig can be constucted manually if you know the DeviceConfig.connection_type values for the device or alternatively the config can be retrieved from SmartDevice.config post discovery and then re-used.

Energy Consumption and Usage Statistics

Note

In order to use the helper methods to calculate the statistics correctly, your devices need to have correct time set. The devices use NTP and public servers from NTP Pool Project to synchronize their time.

Energy Consumption

The availability of energy consumption sensors depend on the device. While most of the bulbs support it, only specific switches (e.g., HS110) or strips (e.g., HS300) support it. You can use has_emeter to check for the availability.

Usage statistics

You can use on_since to query for the time the device has been turned on. Some devices also support reporting the usage statistics on daily or monthly basis. You can access this information using through the usage module (kasa.modules.Usage):

dev = SmartPlug("127.0.0.1")
usage = dev.modules["usage"]
print(f"Minutes on this month: {usage.usage_this_month}")
print(f"Minutes on today: {usage.usage_today}")

API documentation

class kasa.SmartDevice(host: str, *, config: Optional[DeviceConfig] = None, protocol: Optional[BaseProtocol] = None)[source]

Base class for all supported device types.

You don’t usually want to initialize this class manually, but either use Discover class, or use one of the subclasses:

To initialize, you have to await update() at least once. This will allow accessing the properties using the exposed properties.

All changes to the device are done using awaitable methods, which will not change the cached values, but you must await update() separately.

Errors reported by the device are raised as SmartDeviceExceptions, and should be handled by the user of the library.

Examples:
>>> import asyncio
>>> dev = SmartDevice("127.0.0.1")
>>> asyncio.run(dev.update())

All devices provide several informational properties:

>>> dev.alias
Kitchen
>>> dev.model
HS110(EU)
>>> dev.rssi
-71
>>> dev.mac
50:C7:BF:00:00:00

Some information can also be changed programmatically:

>>> asyncio.run(dev.set_alias("new alias"))
>>> asyncio.run(dev.set_mac("01:23:45:67:89:ab"))
>>> asyncio.run(dev.update())
>>> dev.alias
new alias
>>> dev.mac
01:23:45:67:89:ab

When initialized using discovery or using a subclass, you can check the type of the device:

>>> dev.is_bulb
False
>>> dev.is_strip
False
>>> dev.is_plug
True

You can also get the hardware and software as a dict, or access the full device response:

>>> dev.hw_info
{'sw_ver': '1.2.5 Build 171213 Rel.101523',
 'hw_ver': '1.0',
 'mac': '01:23:45:67:89:ab',
 'type': 'IOT.SMARTPLUGSWITCH',
 'hwId': '00000000000000000000000000000000',
 'fwId': '00000000000000000000000000000000',
 'oemId': '00000000000000000000000000000000',
 'dev_name': 'Wi-Fi Smart Plug With Energy Monitoring'}
>>> dev.sys_info

All devices can be turned on and off:

>>> asyncio.run(dev.turn_off())
>>> asyncio.run(dev.turn_on())
>>> asyncio.run(dev.update())
>>> dev.is_on
True

Some devices provide energy consumption meter, and regular update will already fetch some information:

>>> dev.has_emeter
True
>>> dev.emeter_realtime
<EmeterStatus power=0.928511 voltage=231.067823 current=0.014937 total=55.139>
>>> dev.emeter_today
>>> dev.emeter_this_month

You can also query the historical data (note that these needs to be awaited), keyed with month/day:

>>> asyncio.run(dev.get_emeter_monthly(year=2016))
{11: 1.089, 12: 1.582}
>>> asyncio.run(dev.get_emeter_daily(year=2016, month=11))
{24: 0.026, 25: 0.109}
add_module(name: str, module: Module)[source]

Register a module.

property alias: Optional[str]

Return device name (alias).

property config: DeviceConfig

Return the device configuration.

async static connect(*, host: Optional[str] = None, config: Optional[DeviceConfig] = None) SmartDevice[source]

Connect to a single device by the given hostname or device configuration.

This method avoids the UDP based discovery process and will connect directly to the device.

It is generally preferred to avoid discover_single() and use this function instead as it should perform better when the WiFi network is congested or the device is not responding to discovery requests.

Parameters:
  • host – Hostname of device to query

  • config – Connection parameters to ensure the correct protocol and connection options are used.

Return type:

SmartDevice

Returns:

Object for querying/controlling found device.

property credentials: Optional[Credentials]

The device credentials.

property credentials_hash: Optional[str]

The protocol specific hash of the credentials the device is using.

async current_consumption() float[source]

Get the current power consumption in Watt.

property device_id: str

Return unique ID for the device.

If not overridden, this is the MAC address of the device. Individual sockets on strips will override this.

property device_type: DeviceType

Return the device type.

async disconnect()[source]

Disconnect and close any underlying connection resources.

property emeter_realtime: EmeterStatus

Return current energy readings.

property emeter_this_month: Optional[float]

Return this month’s energy consumption in kWh.

property emeter_today: Optional[float]

Return today’s energy consumption in kWh.

emeter_type = 'emeter'
async erase_emeter_stats() Dict[source]

Erase energy meter statistics.

property features: Set[str]

Return a set of features that the device supports.

async get_emeter_daily(year: Optional[int] = None, month: Optional[int] = None, kwh: bool = True) Dict[source]

Retrieve daily statistics for a given month.

Parameters:
  • year – year for which to retrieve statistics (default: this year)

  • month – month for which to retrieve statistics (default: this month)

  • kwh – return usage in kWh (default: True)

Returns:

mapping of day of month to value

async get_emeter_monthly(year: Optional[int] = None, kwh: bool = True) Dict[source]

Retrieve monthly statistics for a given year.

Parameters:
  • year – year for which to retrieve statistics (default: this year)

  • kwh – return usage in kWh (default: True)

Returns:

dict: mapping of month to value

async get_emeter_realtime() EmeterStatus[source]

Retrieve current energy readings.

get_plug_by_index(index: int) SmartDevice[source]

Return child device for the given index.

get_plug_by_name(name: str) SmartDevice[source]

Return child device for the given name.

async get_sys_info() Dict[str, Any][source]

Retrieve system information.

async get_time() Optional[datetime][source]

Return current time from the device, if available.

async get_timezone() Dict[source]

Return timezone information.

property has_children: bool

Return true if the device has children devices.

property has_emeter: bool

Return True if device has an energy meter.

property host: str

The device host.

property hw_info: Dict

Return hardware information.

This returns just a selection of sysinfo keys that are related to hardware.

property internal_state: Any

Return the internal state of the instance.

The returned object contains the raw results from the last update call. This should only be used for debugging purposes.

property is_bulb: bool

Return True if the device is a bulb.

property is_color: bool

Return True if the device supports color changes.

property is_dimmable: bool

Return True if the device is dimmable.

property is_dimmer: bool

Return True if the device is a dimmer.

property is_light_strip: bool

Return True if the device is a led strip.

property is_off: bool

Return True if device is off.

property is_on: bool

Return True if the device is on.

property is_plug: bool

Return True if the device is a plug.

property is_strip: bool

Return True if the device is a strip.

property is_strip_socket: bool

Return True if the device is a strip socket.

property is_variable_color_temp: bool

Return True if the device supports color temperature.

property location: Dict

Return geographical location.

property mac: str

Return mac address.

Returns:

mac address in hexadecimal with colons, e.g. 01:23:45:67:89:ab

property max_device_response_size: int

Returns the maximum response size the device can safely construct.

property model: str

Return device model.

property on_since: Optional[datetime]

Return pretty-printed on-time, or None if not available.

property port: int

The device port.

async reboot(delay: int = 1) None[source]

Reboot the device.

Note that giving a delay of zero causes this to block, as the device reboots immediately without responding to the call.

property rssi: Optional[int]

Return WiFi signal strength (rssi).

async set_alias(alias: str) None[source]

Set the device name (alias).

async set_mac(mac)[source]

Set the mac address.

Parameters:

mac (str) – mac in hexadecimal with colons, e.g. 01:23:45:67:89:ab

property state_information: Dict[str, Any]

Return device-type specific, end-user friendly state information.

property supported_modules: List[str]

Return a set of modules supported by the device.

property sys_info: Dict[str, Any]

Return system information.

Do not call this function from within the SmartDevice class itself as @requires_update will be affected for other properties.

property time: datetime

Return current time from the device.

property timezone: Dict

Return the current timezone.

async turn_off(**kwargs) Dict[source]

Turn off the device.

async turn_on(**kwargs) Dict[source]

Turn device on.

async update(update_children: bool = True)[source]

Query the device to update the data.

Needed for properties that are decorated with requires_update.

update_from_discover_info(info: Dict[str, Any]) None[source]

Update state from info from the discover call.

async wifi_join(ssid: str, password: str, keytype: str = '3')[source]

Join the given wifi network.

If joining the network fails, the device will return to AP mode after a while.

async wifi_scan() List[WifiNetwork][source]

Scan for available wifi networks.

class kasa.DeviceConfig(host: str, timeout: ~typing.Optional[int] = 5, port_override: ~typing.Optional[int] = None, credentials: ~typing.Optional[~kasa.credentials.Credentials] = None, credentials_hash: ~typing.Optional[str] = None, batch_size: ~typing.Optional[int] = None, connection_type: ~kasa.deviceconfig.ConnectionType = <factory>, uses_http: bool = False, http_client: ~typing.Optional[ClientSession] = None)[source]

Class to represent paramaters that determine how to connect to devices.

DEFAULT_TIMEOUT = 5
host: str

IP address or hostname

timeout: Optional[int] = 5

Timeout for querying the device

port_override: Optional[int] = None

Override the default 9999 port to support port forwarding

credentials: Optional[Credentials] = None

Credentials for devices requiring authentication

credentials_hash: Optional[str] = None

Credentials hash for devices requiring authentication. If credentials are also supplied they take precendence over credentials_hash. Credentials hash can be retrieved from SmartDevice.credentials_hash

batch_size: Optional[int] = None

The protocol specific type of connection. Defaults to the legacy type.

connection_type: ConnectionType

The batch size for protoools supporting multiple request batches.

uses_http: bool = False

True if the device uses http. Consumers should retrieve rather than set this in order to determine whether they should pass a custom http client if desired.

http_client: Optional[ClientSession] = None

Set a custom http_client for the device to use.

to_dict(*, credentials_hash: Optional[str] = None, exclude_credentials: bool = False) Dict[str, Dict[str, str]][source]

Convert device config to dict.

static from_dict(config_dict: Dict[str, Dict[str, str]]) DeviceConfig[source]

Return device config from dict.

class kasa.Credentials(username: str = '', password: str = '')[source]

Credentials for authentication.

password: str = ''

Password of the cloud account

username: str = ''

Username (email address) of the cloud account

class kasa.SmartDeviceException(*args: Any, **kwargs: Any)[source]

Base exception for device errors.

class kasa.AuthenticationException(*args: Any, **kwargs: Any)[source]

Base exception for device authentication errors.

class kasa.UnsupportedDeviceException(*args: Any, **kwargs: Any)[source]

Exception for trying to connect to unsupported devices.