mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Various documentation updates (#333)
* Add a note about socket sharing * Show inherited members for apidocs * Remove outdated note of emeters not being supported on smartstrips * Describe emeter and usage modules, add note about NTP for time sync * Describe lib design and modules * Bump sphinx version, ignore d001 (line-length) for doc8 * demote energy & usage to 3rd level, promote api for 2nd
This commit is contained in:
parent
68038c93df
commit
d848117384
@ -37,3 +37,9 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
additional_dependencies: [types-click]
|
additional_dependencies: [types-click]
|
||||||
|
|
||||||
|
- repo: https://github.com/PyCQA/doc8
|
||||||
|
rev: '0.11.1'
|
||||||
|
hooks:
|
||||||
|
- id: doc8
|
||||||
|
additional_dependencies: [tomli]
|
||||||
|
50
docs/source/design.rst
Normal file
50
docs/source/design.rst
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
.. py:module:: kasa.modules
|
||||||
|
|
||||||
|
.. _library_design:
|
||||||
|
|
||||||
|
Library Design & Modules
|
||||||
|
========================
|
||||||
|
|
||||||
|
This page aims to provide some details on the design and internals of this library.
|
||||||
|
You might be interested in this if you want to improve this library,
|
||||||
|
or if you are just looking to access some information that is not currently exposed.
|
||||||
|
|
||||||
|
.. _update_cycle:
|
||||||
|
|
||||||
|
Update Cycle
|
||||||
|
************
|
||||||
|
|
||||||
|
When :meth:`~kasa.SmartDevice.update()` is called,
|
||||||
|
the library constructs a query to send to the device based on :ref:`supported modules <modules>`.
|
||||||
|
Internally, each module defines :meth:`~kasa.modules.Module.query()` to describe what they want query during the update.
|
||||||
|
|
||||||
|
The returned data is cached internally to avoid I/O on property accesses.
|
||||||
|
All properties defined both in the device class and in the module classes follow this principle.
|
||||||
|
|
||||||
|
While the properties are designed to provide a nice API to use for common use cases,
|
||||||
|
you may sometimes want to access the raw, cached data as returned by the device.
|
||||||
|
This can be done using the :attr:`~kasa.SmartDevice.internal_state` property.
|
||||||
|
|
||||||
|
.. _modules:
|
||||||
|
|
||||||
|
Modules
|
||||||
|
*******
|
||||||
|
|
||||||
|
The functionality provided by all :class:`~kasa.SmartDevice` instances is (mostly) done inside separate modules.
|
||||||
|
While the individual device-type specific classes provide an easy access for the most import features,
|
||||||
|
you can also access individual modules through :attr:`kasa.SmartDevice.modules`.
|
||||||
|
You can get the list of supported modules for a given device instance using :attr:`~kasa.SmartDevice.supported_modules`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you only need some module-specific information,
|
||||||
|
you can call the wanted method on the module to avoid using :meth:`~kasa.SmartDevice.update`.
|
||||||
|
|
||||||
|
|
||||||
|
API documentation for modules
|
||||||
|
*****************************
|
||||||
|
|
||||||
|
.. automodule:: kasa.modules
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
:undoc-members:
|
@ -8,6 +8,7 @@
|
|||||||
cli
|
cli
|
||||||
discover
|
discover
|
||||||
smartdevice
|
smartdevice
|
||||||
|
design
|
||||||
smartbulb
|
smartbulb
|
||||||
smartplug
|
smartplug
|
||||||
smartdimmer
|
smartdimmer
|
||||||
|
@ -56,4 +56,5 @@ API documentation
|
|||||||
|
|
||||||
.. autoclass:: kasa.SmartBulb
|
.. autoclass:: kasa.SmartBulb
|
||||||
:members:
|
:members:
|
||||||
|
:inherited-members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
|
.. py:module:: kasa
|
||||||
|
|
||||||
Common API
|
Common API
|
||||||
======================
|
==========
|
||||||
|
|
||||||
The basic functionalities of all supported devices are accessible using the common :class:`SmartDevice` base class.
|
The basic functionalities of all supported devices are accessible using the common :class:`SmartDevice` base class.
|
||||||
|
|
||||||
The property accesses use the data obtained before by awaiting :func:`update()`.
|
The property accesses use the data obtained before by awaiting :func:`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.
|
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 :ref:`library_design` for more detailed information.
|
||||||
|
|
||||||
Methods changing the state of the device do not invalidate the cache (i.e., there is no implicit `update()`).
|
.. 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 :func:`SmartDevice.update()` call made by the library).
|
||||||
You can assume that the operation has succeeded if no exception is raised.
|
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.
|
These methods will return the device response, which can be useful for some use cases.
|
||||||
|
|
||||||
@ -22,28 +30,70 @@ Simple example script showing some functionality:
|
|||||||
async def main():
|
async def main():
|
||||||
p = SmartPlug("127.0.0.1")
|
p = SmartPlug("127.0.0.1")
|
||||||
|
|
||||||
await p.update()
|
await p.update() # Request the update
|
||||||
print(p.alias)
|
print(p.alias) # Print out the alias
|
||||||
|
print(p.emeter_realtime) # Print out current emeter status
|
||||||
|
|
||||||
await p.turn_off()
|
await p.turn_off() # Turn the device off
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
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__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|
||||||
|
|
||||||
Refer to device type specific classes for more examples:
|
Refer to device type specific classes for more examples:
|
||||||
|
:class:`SmartPlug`, :class:`SmartBulb`, :class:`SmartStrip`,
|
||||||
|
:class:`SmartDimmer`, :class:`SmartLightStrip`.
|
||||||
|
|
||||||
* :class:`SmartPlug`
|
Energy Consumption and Usage Statistics
|
||||||
* :class:`SmartBulb`
|
***************************************
|
||||||
* :class:`SmartStrip`
|
|
||||||
* :class:`SmartDimmer`
|
.. note::
|
||||||
* :class:`SmartLightStrip`
|
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 <https://www.ntppool.org/>`_ 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 :attr:`~SmartDevice.has_emeter` to check for the availability.
|
||||||
|
|
||||||
|
|
||||||
|
Usage statistics
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
You can use :attr:`~SmartDevice.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 (:class:`kasa.modules.Usage`):
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
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
|
API documentation
|
||||||
~~~~~~~~~~~~~~~~~
|
*****************
|
||||||
|
|
||||||
.. autoclass:: kasa.SmartDevice
|
.. autoclass:: SmartDevice
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
@ -10,4 +10,5 @@ API documentation
|
|||||||
|
|
||||||
.. autoclass:: kasa.SmartDimmer
|
.. autoclass:: kasa.SmartDimmer
|
||||||
:members:
|
:members:
|
||||||
|
:inherited-members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
@ -10,4 +10,5 @@ API documentation
|
|||||||
|
|
||||||
.. autoclass:: kasa.SmartLightStrip
|
.. autoclass:: kasa.SmartLightStrip
|
||||||
:members:
|
:members:
|
||||||
|
:inherited-members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
@ -11,4 +11,5 @@ API documentation
|
|||||||
|
|
||||||
.. autoclass:: kasa.SmartPlug
|
.. autoclass:: kasa.SmartPlug
|
||||||
:members:
|
:members:
|
||||||
|
:inherited-members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
Smart strips
|
Smart strips
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The emeter feature is currently not implemented for smart strips. See https://github.com/python-kasa/python-kasa/issues/64 for details.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Feel free to open a pull request to improve the documentation!
|
Feel free to open a pull request to improve the documentation!
|
||||||
@ -34,4 +29,5 @@ API documentation
|
|||||||
|
|
||||||
.. autoclass:: kasa.SmartStrip
|
.. autoclass:: kasa.SmartStrip
|
||||||
:members:
|
:members:
|
||||||
|
:inherited-members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
19
poetry.lock
generated
19
poetry.lock
generated
@ -497,18 +497,19 @@ python-versions = "*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sphinx"
|
name = "sphinx"
|
||||||
version = "3.5.4"
|
version = "4.5.0"
|
||||||
description = "Python documentation generator"
|
description = "Python documentation generator"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = true
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
alabaster = ">=0.7,<0.8"
|
alabaster = ">=0.7,<0.8"
|
||||||
babel = ">=1.3"
|
babel = ">=1.3"
|
||||||
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
|
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
|
||||||
docutils = ">=0.12,<0.17"
|
docutils = ">=0.14,<0.18"
|
||||||
imagesize = "*"
|
imagesize = "*"
|
||||||
|
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
|
||||||
Jinja2 = ">=2.3"
|
Jinja2 = ">=2.3"
|
||||||
packaging = "*"
|
packaging = "*"
|
||||||
Pygments = ">=2.0"
|
Pygments = ">=2.0"
|
||||||
@ -516,14 +517,14 @@ requests = ">=2.5.0"
|
|||||||
snowballstemmer = ">=1.1"
|
snowballstemmer = ">=1.1"
|
||||||
sphinxcontrib-applehelp = "*"
|
sphinxcontrib-applehelp = "*"
|
||||||
sphinxcontrib-devhelp = "*"
|
sphinxcontrib-devhelp = "*"
|
||||||
sphinxcontrib-htmlhelp = "*"
|
sphinxcontrib-htmlhelp = ">=2.0.0"
|
||||||
sphinxcontrib-jsmath = "*"
|
sphinxcontrib-jsmath = "*"
|
||||||
sphinxcontrib-qthelp = "*"
|
sphinxcontrib-qthelp = "*"
|
||||||
sphinxcontrib-serializinghtml = "*"
|
sphinxcontrib-serializinghtml = ">=1.1.5"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["sphinxcontrib-websupport"]
|
docs = ["sphinxcontrib-websupport"]
|
||||||
lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.800)", "docutils-stubs"]
|
lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "docutils-stubs", "types-typed-ast", "types-requests"]
|
||||||
test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"]
|
test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -754,7 +755,7 @@ docs = ["sphinx", "sphinx_rtd_theme", "m2r", "mistune", "sphinxcontrib-programou
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.7"
|
||||||
content-hash = "cbc8eb721e3b498c25eef73c95b2aa309419fa075b878c18cac0b148113c25f9"
|
content-hash = "6577513a016c329bc825369761eae9971cb6a18a13c96ac0669c1f51ab3de87d"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
alabaster = [
|
alabaster = [
|
||||||
@ -1074,8 +1075,8 @@ snowballstemmer = [
|
|||||||
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
|
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
|
||||||
]
|
]
|
||||||
sphinx = [
|
sphinx = [
|
||||||
{file = "Sphinx-3.5.4-py3-none-any.whl", hash = "sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8"},
|
{file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"},
|
||||||
{file = "Sphinx-3.5.4.tar.gz", hash = "sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1"},
|
{file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"},
|
||||||
]
|
]
|
||||||
sphinx-rtd-theme = [
|
sphinx-rtd-theme = [
|
||||||
{file = "sphinx_rtd_theme-0.5.2-py2.py3-none-any.whl", hash = "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f"},
|
{file = "sphinx_rtd_theme-0.5.2-py2.py3-none-any.whl", hash = "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f"},
|
||||||
|
@ -22,7 +22,7 @@ asyncclick = ">=8"
|
|||||||
pydantic = "^1"
|
pydantic = "^1"
|
||||||
|
|
||||||
# required only for docs
|
# required only for docs
|
||||||
sphinx = { version = "^3", optional = true }
|
sphinx = { version = "^4", optional = true }
|
||||||
m2r = { version = "^0", optional = true }
|
m2r = { version = "^0", optional = true }
|
||||||
mistune = { version = "<2.0.0", optional = true }
|
mistune = { version = "<2.0.0", optional = true }
|
||||||
sphinx_rtd_theme = { version = "^0", optional = true }
|
sphinx_rtd_theme = { version = "^0", optional = true }
|
||||||
@ -81,6 +81,10 @@ markers = [
|
|||||||
"requires_dummy: test requires dummy data to pass, skipped on real devices",
|
"requires_dummy: test requires dummy data to pass, skipped on real devices",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.doc8]
|
||||||
|
paths = ["docs"]
|
||||||
|
ignore = ["D001"]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
Loading…
Reference in New Issue
Block a user