docs: modernize docstrings across the repository (#1682)

Comprehensive docstring cleanup across the repository — fix Sphinx
cross-references, Python REPL continuation syntax, stale class names,
and modernize the IotBulb docstring to use the
`Module.Light`/`Module.LightPreset` interface.
This commit is contained in:
ZeliardM
2026-07-05 12:30:01 -04:00
committed by GitHub
parent 7f10a39655
commit bb27a43027
18 changed files with 136 additions and 120 deletions

View File

@@ -6,10 +6,10 @@ Once you have a device via :ref:`Discovery <discover_target>` or
>>> from kasa import Discover
>>>
>>> dev = await Discover.discover_single(
>>> "127.0.0.2",
>>> username="user@example.com",
>>> password="great_password"
>>> )
... "127.0.0.2",
... username="user@example.com",
... password="great_password"
... )
>>>
Most devices can be turned on and off
@@ -46,7 +46,7 @@ Devices support different functionality that are exposed via
:ref:`modules <module_target>` that you can access via :attr:`~kasa.Device.modules`:
>>> for module_name in dev.modules:
>>> print(module_name)
... print(module_name)
homekit
Energy
schedule
@@ -81,7 +81,7 @@ They are useful if you want write code that dynamically adapts as new features a
added to the API.
>>> for feature_name in dev.features:
>>> print(feature_name)
... print(feature_name)
state
rssi
on_since

View File

@@ -7,10 +7,10 @@ Discovery returns a list of discovered devices:
>>> from kasa import Discover, Device
>>> device = await Discover.discover_single(
>>> "127.0.0.3",
>>> username="user@example.com",
>>> password="great_password",
>>> )
... "127.0.0.3",
... username="user@example.com",
... password="great_password",
... )
>>> print(device.alias) # Alias is None because update() has not been called
None

View File

@@ -1,11 +1,11 @@
"""Discover TPLink Smart Home devices.
The main entry point for this library is :func:`Discover.discover()`,
The main entry point for this library is :meth:`Discover.discover()`,
which returns a dictionary of the found devices. The key is the IP address
of the device and the value contains ready-to-use, SmartDevice-derived
device object.
:func:`discover_single()` can be used to initialize a single device given its
:meth:`discover_single()` can be used to initialize a single device given its
IP address. If the :class:`DeviceConfig` of the device is already known,
you can initialize the corresponding device class directly without discovery.
@@ -27,9 +27,9 @@ Discovery returns a dict of {ip: discovered devices}:
You can pass username and password for devices requiring authentication
>>> devices = await Discover.discover(
>>> username="user@example.com",
>>> password="great_password",
>>> )
... username="user@example.com",
... password="great_password",
... )
>>> print(len(devices))
6
@@ -61,8 +61,8 @@ None
It is also possible to pass a coroutine to be executed for each found device:
>>> async def print_dev_info(dev):
>>> await dev.update()
>>> print(f"Discovered {dev.alias} (model: {dev.model})")
... await dev.update()
... print(f"Discovered {dev.alias} (model: {dev.model})")
>>>
>>> devices = await Discover.discover(on_discovered=print_dev_info, credentials=creds)
Discovered Bedroom Power Strip (model: KP303)

View File

@@ -6,10 +6,10 @@ state, time, firmware.
>>> from kasa import Discover, Module
>>>
>>> dev = await Discover.discover_single(
>>> "127.0.0.3",
>>> username="user@example.com",
>>> password="great_password"
>>> )
... "127.0.0.3",
... username="user@example.com",
... password="great_password"
... )
>>> await dev.update()
>>> print(dev.alias)
Living Room Bulb
@@ -18,7 +18,7 @@ Features allow for instrospection and can be interacted with as new features are
to the API:
>>> for feature_id, feature in dev.features.items():
>>> print(f"{feature.name} ({feature_id}): {feature.value}")
... print(f"{feature.name} ({feature_id}): {feature.value}")
Device ID (device_id): 0000000000000000000000000000000000000000
State (state): True
Signal Level (signal_level): 2
@@ -44,7 +44,7 @@ Overheated (overheated): False
To see whether a device supports a feature, check for the existence of it:
>>> if feature := dev.features.get("brightness"):
>>> print(feature.value)
... print(feature.value)
100
You can update the value of a feature

View File

@@ -6,10 +6,10 @@ hubs.
>>> from kasa import Discover, Module, LightState
>>>
>>> dev = await Discover.discover_single(
>>> "127.0.0.6",
>>> username="user@example.com",
>>> password="great_password"
>>> )
... "127.0.0.6",
... username="user@example.com",
... password="great_password"
... )
>>> await dev.update()
>>> print(dev.alias)
Tapo Hub
@@ -27,7 +27,7 @@ The hub will pair with all supported devices in pairing mode:
'device_model': 'S200B', 'name': 'I01BU0tFRF9OQU1FIw===='}]
>>> for child in dev.children:
>>> print(f"{child.device_id} - {child.model}")
... print(f"{child.device_id} - {child.model}")
SCRUBBED_CHILD_DEVICE_ID_1 - T310
SCRUBBED_CHILD_DEVICE_ID_2 - T315
SCRUBBED_CHILD_DEVICE_ID_3 - T110
@@ -38,7 +38,7 @@ Unpair with the child `device_id`:
>>> await childsetup.unpair("SCRUBBED_CHILD_DEVICE_ID_4")
>>> for child in dev.children:
>>> print(f"{child.device_id} - {child.model}")
... print(f"{child.device_id} - {child.model}")
SCRUBBED_CHILD_DEVICE_ID_1 - T310
SCRUBBED_CHILD_DEVICE_ID_2 - T315
SCRUBBED_CHILD_DEVICE_ID_3 - T110

View File

@@ -3,10 +3,10 @@
>>> from kasa import Discover, Module
>>>
>>> dev = await Discover.discover_single(
>>> "127.0.0.3",
>>> username="user@example.com",
>>> password="great_password"
>>> )
... "127.0.0.3",
... username="user@example.com",
... password="great_password"
... )
>>> await dev.update()
>>> print(dev.alias)
Living Room Bulb
@@ -44,7 +44,7 @@ All known bulbs support changing the brightness:
Bulbs supporting color temperature can be queried for the supported range:
>>> if color_temp_feature := light.get_feature("color_temp"):
>>> print(f"{color_temp_feature.minimum_value}, {color_temp_feature.maximum_value}")
... print(f"{color_temp_feature.minimum_value}, {color_temp_feature.maximum_value}")
2500, 6500
>>> await light.set_color_temp(3000)
>>> await dev.update()

View File

@@ -3,10 +3,10 @@
>>> from kasa import Discover, Module, LightState
>>>
>>> dev = await Discover.discover_single(
>>> "127.0.0.3",
>>> username="user@example.com",
>>> password="great_password"
>>> )
... "127.0.0.3",
... username="user@example.com",
... password="great_password"
... )
>>> await dev.update()
>>> print(dev.alias)
Living Room Bulb
@@ -32,8 +32,8 @@ Party
If the device supports it you can set custom effects:
>>> if light_effect.has_custom_effects:
>>> effect_list = { "brightness", 50 }
>>> await light_effect.set_custom_effect(effect_list)
... effect_list = {"brightness": 50}
... await light_effect.set_custom_effect(effect_list)
>>> light_effect.has_custom_effects # The device in this examples does not support \
custom effects
False

View File

@@ -3,10 +3,10 @@
>>> from kasa import Discover, Module, LightState
>>>
>>> dev = await Discover.discover_single(
>>> "127.0.0.3",
>>> username="user@example.com",
>>> password="great_password"
>>> )
... "127.0.0.3",
... username="user@example.com",
... password="great_password"
... )
>>> await dev.update()
>>> print(dev.alias)
Living Room Bulb
@@ -48,9 +48,9 @@ LightState(light_on=True, brightness=50, hue=0,\
You can save a new preset state if the device supports it:
>>> if light_preset.has_save_preset:
>>> new_preset_state = LightState(light_on=True, brightness=75, hue=0,\
... new_preset_state = LightState(light_on=True, brightness=75, hue=0,\
saturation=100, color_temp=2700, transition=None)
>>> await light_preset.save_preset("Light preset 1", new_preset_state)
... await light_preset.save_preset("Light preset 1", new_preset_state)
>>> await dev.update()
>>> light_preset.preset # Saving updates the preset state for the preset, it does not \
set the preset

View File

@@ -102,12 +102,12 @@ _LOGGER = logging.getLogger(__name__)
class IotBulb(IotDevice):
r"""Representation of a TP-Link Smart Bulb.
To initialize, you have to await :func:`update()` at least once.
To initialize, you have to await :meth:`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,
so you must await :func:`update()` to fetch updates values from the device.
so you must await :meth:`update()` to fetch updated values from the device.
Errors reported by the device are raised as
:class:`KasaException <kasa.exceptions.KasaException>`,
@@ -118,7 +118,7 @@ class IotBulb(IotDevice):
>>> bulb = IotBulb("127.0.0.1")
>>> asyncio.run(bulb.update())
>>> print(bulb.alias)
Bulb2
Bedroom Bulb
Bulbs, like any other supported devices, can be turned on and off:
@@ -128,72 +128,82 @@ class IotBulb(IotDevice):
>>> print(bulb.is_on)
True
You can use the ``is_``-prefixed properties to check for supported features:
Get the light module to interact with light-specific features:
>>> bulb.is_dimmable
>>> light = bulb.modules[Module.Light]
You can use the :meth:`~kasa.module.Module.has_feature` method to check for supported light features:
>>> light.has_feature("brightness")
True
>>> bulb.is_color
>>> light.has_feature("hsv")
True
>>> bulb.is_variable_color_temp
>>> light.has_feature("color_temp")
True
All known bulbs support changing the brightness:
>>> bulb.brightness
>>> light.brightness
30
>>> asyncio.run(bulb.set_brightness(50))
>>> asyncio.run(light.set_brightness(50))
>>> asyncio.run(bulb.update())
>>> bulb.brightness
>>> light.brightness
50
Bulbs supporting color temperature can be queried for the supported range:
>>> bulb.valid_temperature_range
ColorTempRange(min=2500, max=9000)
>>> asyncio.run(bulb.set_color_temp(3000))
>>> if color_temp_feature := light.get_feature("color_temp"):
... print(
... f"{color_temp_feature.minimum_value}, "
... f"{color_temp_feature.maximum_value}"
... )
2500, 9000
>>> asyncio.run(light.set_color_temp(3000))
>>> asyncio.run(bulb.update())
>>> bulb.color_temp
>>> light.color_temp
3000
Color bulbs can be adjusted by passing hue, saturation and value:
>>> asyncio.run(bulb.set_hsv(180, 100, 80))
>>> asyncio.run(light.set_hsv(180, 100, 80))
>>> asyncio.run(bulb.update())
>>> bulb.hsv
>>> light.hsv
HSV(hue=180, saturation=100, value=80)
If you don't want to use the default transitions,
you can pass `transition` in milliseconds.
All methods changing the state of the device support this parameter:
All methods changing the light state support this parameter:
* :func:`turn_on`
* :func:`turn_off`
* :func:`set_hsv`
* :func:`set_color_temp`
* :func:`set_brightness`
* :meth:`turn_on`
* :meth:`turn_off`
* :meth:`set_hsv`
* :meth:`set_color_temp`
* :meth:`set_brightness`
Light strips (e.g., KL420L5) do not support this feature,
but silently ignore the parameter.
The following changes the brightness over a period of 10 seconds:
>>> asyncio.run(bulb.set_brightness(100, transition=10_000))
>>> asyncio.run(light.set_brightness(100, transition=10_000))
Bulb configuration presets can be accessed using the :func:`presets` property:
Bulb configuration presets can be accessed using the light preset module:
>>> [ preset.to_dict() for preset in bulb.presets }
[{'brightness': 50, 'hue': 0, 'saturation': 0, 'color_temp': 2700, 'index': 0}, {'brightness': 100, 'hue': 0, 'saturation': 75, 'color_temp': 0, 'index': 1}, {'brightness': 100, 'hue': 120, 'saturation': 75, 'color_temp': 0, 'index': 2}, {'brightness': 100, 'hue': 240, 'saturation': 75, 'color_temp': 0, 'index': 3}]
>>> light_preset = bulb.modules[Module.LightPreset]
>>> light_preset.preset_states_list[0]
IotLightPreset(light_on=None, brightness=50, hue=0, saturation=0, color_temp=2700, transition=None)
To modify an existing preset, pass :class:`~kasa.interfaces.light.LightPreset`
instance to :func:`save_preset` method:
To modify an existing preset, update one of the entries in
``preset_states_list`` and pass it to
:meth:`~kasa.interfaces.lightpreset.LightPreset.save_preset`:
>>> preset = bulb.presets[0]
>>> preset = light_preset.preset_states_list[0]
>>> preset.brightness
50
>>> preset.brightness = 100
>>> asyncio.run(bulb.save_preset(preset))
>>> asyncio.run(light_preset.save_preset("Light preset 1", preset))
>>> asyncio.run(bulb.update())
>>> bulb.presets[0].brightness
100
>>> light_preset.preset_states_list[0]
IotLightPreset(light_on=None, brightness=100, hue=0, saturation=0, color_temp=2700, transition=None)
""" # noqa: E501

View File

@@ -92,7 +92,7 @@ class IotDevice(Device):
* :class:`IotDimmer`
* :class:`IotLightStrip`
To initialize, you have to await :func:`update()` at least once.
To initialize, you have to await :meth:`update()` at least once.
This will allow accessing the properties using the exposed properties.
All changes to the device are done using awaitable methods,

View File

@@ -41,14 +41,14 @@ class IotDimmer(IotPlug):
r"""Representation of a TP-Link Smart Dimmer.
Dimmers work similarly to plugs, but provide also support for
adjusting the brightness. This class extends :class:`SmartPlug` interface.
adjusting the brightness. This class extends :class:`IotPlug` interface.
To initialize, you have to await :func:`update()` at least once.
To initialize, you have to await :meth:`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 :func:`update()` separately.
but you must await :meth:`update()` separately.
Errors reported by the device are raised as :class:`KasaException`\s,
and should be handled by the user of the library.
@@ -65,7 +65,7 @@ class IotDimmer(IotPlug):
>>> dimmer.brightness
50
Refer to :class:`SmartPlug` for the full API.
Refer to :class:`IotPlug` for the full API.
"""
DIMMER_SERVICE = "smartlife.iot.dimmer"

View File

@@ -16,7 +16,7 @@ class IotLightStrip(IotBulb):
Light strips work similarly to bulbs, but use a different service for controlling,
and expose some extra information (such as length and active effect).
This class extends :class:`SmartBulb` interface.
This class extends :class:`IotBulb` interface.
Examples:
>>> import asyncio
@@ -41,7 +41,7 @@ class IotLightStrip(IotBulb):
feel free to find out how to control them and create a PR!
See :class:`SmartBulb` for more examples.
See :class:`IotBulb` for more examples.
"""
LIGHT_SERVICE = "smartlife.iot.lightStrip"

View File

@@ -18,12 +18,12 @@ _LOGGER = logging.getLogger(__name__)
class IotPlug(IotDevice):
r"""Representation of a TP-Link Smart Plug.
To initialize, you have to await :func:`update()` at least once.
To initialize, you have to await :meth:`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 :func:`update()` separately.
but you must await :meth:`update()` separately.
Errors reported by the device are raised as :class:`KasaException`\s,
and should be handled by the user of the library.

View File

@@ -53,14 +53,14 @@ class IotStrip(IotDevice):
A strip consists of the parent device and its children.
All methods of the parent act on all children, while the child devices
share the common API with the :class:`SmartPlug` class.
share the common API with the :class:`IotPlug` class.
To initialize, you have to await :func:`update()` at least once.
To initialize, you have to await :meth:`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 :func:`update()` separately.
but you must await :meth:`update()` separately.
Errors reported by the device are raised as :class:`KasaException`\s,
and should be handled by the user of the library.
@@ -75,7 +75,7 @@ class IotStrip(IotDevice):
All methods act on the whole strip:
>>> for plug in strip.children:
>>> print(f"{plug.alias}: {plug.is_on}")
... print(f"{plug.alias}: {plug.is_on}")
Plug 1: True
Plug 2: False
Plug 3: False
@@ -89,7 +89,7 @@ class IotStrip(IotDevice):
>>> len(strip.children)
3
>>> for plug in strip.children:
>>> print(f"{plug.alias}: {plug.is_on}")
... print(f"{plug.alias}: {plug.is_on}")
Plug 1: False
Plug 2: False
Plug 3: False

View File

@@ -6,10 +6,10 @@ Light, AutoOff, Firmware etc.
>>> from kasa import Discover, Module
>>>
>>> dev = await Discover.discover_single(
>>> "127.0.0.3",
>>> username="user@example.com",
>>> password="great_password"
>>> )
... "127.0.0.3",
... username="user@example.com",
... password="great_password"
... )
>>> await dev.update()
>>> print(dev.alias)
Living Room Bulb
@@ -18,7 +18,7 @@ To see whether a device supports a group of functionality
check for the existence of the module:
>>> if light := dev.modules.get("Light"):
>>> print(light.brightness)
... print(light.brightness)
100
.. include:: ../featureattributes.md
@@ -28,7 +28,7 @@ To see whether a device supports specific functionality, you can check whether t
module has that feature:
>>> if light.has_feature("hsv"):
>>> print(light.hsv)
... print(light.hsv)
HSV(hue=0, saturation=100, value=100)
If you know or expect the module to exist you can access by index:
@@ -44,8 +44,8 @@ Modules support typing via the Module names in Module:
>>> light_effect = dev.modules.get("LightEffect")
>>> light_effect_typed = dev.modules.get(Module.LightEffect)
>>> if TYPE_CHECKING:
>>> reveal_type(light_effect) # Static checker will reveal: str
>>> reveal_type(light_effect_typed) # Static checker will reveal: LightEffect
... reveal_type(light_effect) # Static checker will reveal: str
... reveal_type(light_effect_typed) # Static checker will reveal: LightEffect
"""

View File

@@ -3,10 +3,10 @@
>>> from kasa import Discover
>>>
>>> dev = await Discover.discover_single(
>>> "127.0.0.1",
>>> username="user@example.com",
>>> password="great_password"
>>> )
... "127.0.0.1",
... username="user@example.com",
... password="great_password"
... )
>>> await dev.update()
>>> print(dev.alias)
Bedroom Power Strip
@@ -14,7 +14,7 @@ Bedroom Power Strip
All methods act on the whole strip:
>>> for plug in dev.children:
>>> print(f"{plug.alias}: {plug.is_on}")
... print(f"{plug.alias}: {plug.is_on}")
Plug 1: True
Plug 2: False
Plug 3: False
@@ -28,7 +28,7 @@ Accessing individual plugs can be done using the `children` property:
>>> len(dev.children)
3
>>> for plug in dev.children:
>>> print(f"{plug.alias}: {plug.is_on}")
... print(f"{plug.alias}: {plug.is_on}")
Plug 1: False
Plug 2: False
Plug 3: False