diff --git a/CHANGELOG b/CHANGELOG index efc01620..530cc4ab 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,156 @@ Changelog ========= +0.3.0 (2017-09-14) +------------------ + +This is the first release after a while and aims to improve the robustness all-around. +To make this happen we have decided to break the API and drop the support for Python 2. + +API break: + * Python2 support has been dropped. + * pyHS100/pyHS100.py has been splitted to smartdevice.py, smartplug.py and smartbulb.py, no one should have ever accessed these directly though. + * SmartPlugException is no more, SmartDeviceException is used by both SmartPlug and SmartBulb + * Discovery has been moved from TPLinkSmartHomeProtocol into its own class for easier 3rd party use. + * SmartDevice's identify() and `features` will emit a warning when used. These will likely be dropped or revised in the future and their use should be avoided. + +Other changes: + + * CLI tool supports device discovery and is usable without specifying device type or IP for testing + * CLI tool supports changing bulb-specific settings + * Library support & unit tests are extended to cover more devices. + - Supported plugs: HS100, HS105, HS110 + - Supported switches: HS200 + - Supported bulbs: LB100, LB110, LB120, LB130 + +- Bump the version. [Teemu Rytilahti] + +- Revise README, fixes #86. [Teemu Rytilahti] + +- Update the changelog. [Teemu Rytilahti] + +- Local test clean (#96) [Sean Gollschewsky] + + * Add ignores for working coverage/tox/IDE files. + + * Allow tox not to fail if python version is not available. + +- Move SmartDeviceException to SmartDevice, and remove types.py complet… + (#95) [Teemu R] + + * move SmartDeviceException to SmartDevice, and remove types.py completely. fixes #94 + + * do not import skipIf anymore + +- Move has_emeter implementation from SmartDevice to SmartPlug, avoid + using features() internally (#93) [Teemu R] + + * move has_emeter implementation from SmartDevice to SmartPlug, avoid using features() internally + + * add stacklevel to deprecation warnings to see where they are really called + + * make tests pass on a real device. if PLUG_IP is not None, the tests will be run on a device at the defined IP address + +- Add typing hints to make it easier for 3rd party developers to use the + library (#90) [Teemu R] + + * add typing hints to make it easier for 3rd party developers to use the library + + * remove unused devicetype enum to support python3.3 + + * add python 3.3 to travis and tox, install typing module in setup.py +- Execute coveralls only on travis, fixes #84 (#91) [Teemu R] + +- Make flake8 pass by some rewording. [Teemu Rytilahti] + +- Make hound a bit more happier. [Teemu Rytilahti] + +- Deprecate features and identify, use state_information in __repr__ instead of identify. [Teemu Rytilahti] + +- Fix smartbulb hsv documentation, values are degrees and percentages instead of 0-255. [Teemu Rytilahti] + +- Another try, just with module name. [Teemu Rytilahti] + +- Make tox run pytest-cov, add coveralls. [Teemu Rytilahti] + +- Prevent failure if device's sysinfo does not have a "feature" attribute. (#77) [Sean Gollschewsky] + +- Allow None for rssi, add a missing newline to fakes.py. [Teemu Rytilahti] + +- Add hs100 tests. [Teemu Rytilahti] + +- Make tests to test against all known device variants. [Teemu Rytilahti] + +- Remove unused tplinksmarthomeprotocol import. [Teemu Rytilahti] + +- Fix hs105 mac to pass the test, wrap sysinfo_lb110 properly inside 'system' [Teemu Rytilahti] + +- Return None instead of False for emeter related actions. [Teemu Rytilahti] + +- Wrap sysinfo to defaultdict to return None for keys which do not exist, makes unsupported keys not to fail hard (#72) [Teemu R] + +- Add hs100 example to fakes.py, thanks to Semant1ka on #67 (#74) [Teemu R] + +- Discover refactoring, enhancements to the cli tool (#71) [Teemu R] + + * Discover refactoring, enhancements to the cli tool + + * Discover tries to detect the type of the device from sysinfo response + * Discover.discover() returns an IP address keyed dictionary, + values are initialized instances of the automatically detected device type. + + * When no IP is given, autodetect all supported devices and print out their states + * When only IP but no type is given, autodetect type and make a call based on that information. + * One can define --bulb or --plug to skip the detection. + + * renamed pyHS100.py -> smartdevice.py + + * SmartPlugException -> SmartDeviceException in comments + + * fix mic_type check + + * make time() return None on failure as we don't know which devices support getting the time and it's used in the cli tool + + * hw_info: check if key exists before accessing it, add mic_mac and mic_type + + * Check for mic_mac on mac, based on work by kdschloesser on issue #59 + + * make hound happy, __init__ on SmartDevice cannot error out so removing 'raises' documentation + +- Add LB110 sysinfo (#75) [Sean Gollschewsky] + + * Add LB110 sysinfo + + * Linting. + +- Add @pass_dev to hsv, adjust ranges (#70) [Teemu R] + + * add @pass_dev to hsv command, it was always broken + + * Hue goes up to 360, saturation and value are up to 100(%) + +- Extract shared types (exceptions, enums), add module level doc, rename exception to be generic. [Teemu Rytilahti] + +- Add check to ensure devices with lat/lon with `_i` suffix are supported (#54) (#56) [Matt LeBrun] + + * Add check to ensure devices with lat/lon with `_i` suffix are supported (#54) + + * Add .gitignore for posterity + +- Generalize smartdevice class and add bulb support for the cli tool (#50) [Teemu R] + + Fixes #48 and #51. The basic functionality should work on all types of supported devices, for bulb specific commands it is currently necessary to specify ```--bulb```. + +- Refactor and drop py2 support (#49) [Teemu R] + + * move is_off property to SmartDevice, implement is_on for bulb and use it + + * refactor by moving smartbulb and smartplug to their own classes + + * drop python2 compatibility, make flake8 happy + + * travis: remove 2.7, add 3.6 + 0.2.4.2 (2017-04-08) -------------------- - Add installation requirement for future package. [Teemu Rytilahti] diff --git a/README.md b/README.md index b72a4dd9..615545a9 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,157 @@ # pyHS100 -Python Library to control TPLink smart plugs (HS100, HS105, HS110, HS200) and TPLink smart bulbs (LB1xx). +[![PyPI version](https://badge.fury.io/py/pyHS100.svg)](https://badge.fury.io/py/pyHS100) +[![Build Status](https://travis-ci.org/GadgetReactor/pyHS100.svg?branch=master)](https://travis-ci.org/GadgetReactor/pyHS100) +[![Coverage Status](https://coveralls.io/repos/github/GadgetReactor/pyHS100/badge.svg?branch=master)](https://coveralls.io/github/GadgetReactor/pyHS100?branch=master) + +Python Library to control TPLink smart plugs/switches and smart bulbs. + +**Supported devices** + +* Plugs + * HS100 + * HS105 + * HS110 +* Wall switches + * HS200 +* Bulbs + * LB100 + * LB110 + * LB120 + * LB130 # Usage The package is shipped with a console tool named pyhs100, please refer to ```pyhs100 --help``` for detailed usage. -Note: The tool does not currently support bulb-specific commands, please feel free to prepare a pull request! +The device to which the commands are sent is chosen by `PYHS100_IP` environment variable or passing `--ip
` as an option. +To see what is being sent to and received from the device, specify option `--debug`. + +To avoid discovering the devices when executing commands its type can be passed by specifying either `--plug` or `--bulb`, +if no type is given its type will be discovered automatically with a small delay. +Some commands (such as reading energy meter values and setting color of bulbs) additional parameters are required, +which you can find by adding `--help` after the command, e.g. `pyhs100 emeter --help` or `pyhs100 hsv --help`. + +If no command is given, the `state` command will be executed to query the device state. + ## Discovering devices -``` -$ pyhs100 discover +The devices can be discovered either by using `pyhs100 discover` or by calling `pyhs100` without any parameters. +In both cases supported devices are discovered from the same broadcast domain, and their current state will be queried and printed out. -Discovering devices for 5 seconds -Found device: {'ip': '192.168.250.186', - 'port': 9999, - 'sys_info': {'emeter': {'get_realtime': {'current': 0.013309, - ``` - -## Querying the state -``` -$ pyhs100 --ip 192.168.250.186 - +$ pyhs100 +No --bulb nor --plug given, discovering.. +Discovering devices for 3 seconds == My Smart Plug - HS110(EU) == -Device state: OFF -LED state: False -Time: 1970-01-01 01:52:35 -On since: 2017-03-19 17:09:16.408657 +Device state: ON +IP address: 192.168.x.x +LED state: False +On since: 2017-03-26 18:29:17.242219 +== Generic information == +Time: 1970-06-22 02:39:41 Hardware: 1.0 Software: 1.0.8 Build 151101 Rel.24452 -MAC (rssi): 50:C7:BF:XX:XX:XX (-61) -Location: {'longitude': XXXX, 'latitude': XXXX} +MAC (rssi): 50:C7:BF:XX:XX:XX (-77) +Location: {'latitude': XXXX, 'longitude': XXXX} == Emeter == -Current state: {'power': 0, 'total': 0.001, 'current': 0.013552, 'voltage': 223.394238} +Current state: {'total': 133.082, 'power': 100.418681, 'current': 0.510967, 'voltage': 225.600477} ``` +## Basic controls + +All devices support a variety of common commands, including: + * `state` which returns state information + * `on` and `off` for turning the device on or off + * `emeter` (where applicable) to return energy consumption information + * `sysinfo` to return raw system information which is used by e.g. `state`, useful for debugging and when adding support for new device types + +## Energy meter + +Passing no options to `emeter` command will return the current consumption. +Possible options include `--year` and `--month` for retrieving historical state, +and reseting the counters is done with `--erase`. + +``` +$ pyhs100 emeter +== Emeter == +Current state: {'total': 133.105, 'power': 108.223577, 'current': 0.54463, 'voltage': 225.296283} +``` + +## Plug-specific commands + +At the moment only switching the state of the LED is implemented. +**Feel free to submit patches as pull requests for further features!** +### Controlling the LED + +`led` command can be used to control whether the LED light on front of the plug is on or off. + +``` +$ pyhs100 --plug led +LED state: False +$ pyhs100 --plug led 1 +Turning led to True +``` + +## Bulb-specific commands + +At the moment setting brightness, color temperature and color (in HSV) is supported. +The commands are straightforward, so feel free to check `--help` for instructions how to use them. + +**Feel free to submit patches as pull requests to add more functionality (e.g. scenes)!** + # Library usage +The public API is well documented, but here are some examples to get you started. For all available API functions run ```help(SmartPlug)``` or ```help(SmartBulb)```. +## Discovering devices + +`Discover` class' `discover()` can be used to discover supported devices, +which returns a dictionary keyed with the IP address whose value hold a ready-to-use instance of the detected device type. + +Example: +```python +from pyHS100 import Discover + +for dev in Discover.discover().values(): + print(dev) +``` +``` +$ python3 example.py + +``` + +## Querying basic information + +*Please note that most property getters do I/O (e.g. fetching the system information) on each call. +If you want to avoid unnecessary communication with the device please use `get_sysinfo` and handle parsing of information by yourself.* + ```python from pyHS100 import SmartPlug, SmartBulb from pprint import pformat as pf -plug = SmartPlug("192.168.250.186") -print("Alias, type and supported features: %s" % (plug.identify(),)) +plug = SmartPlug("192.168.XXX.XXX") print("Hardware: %s" % pf(plug.hw_info)) print("Full sysinfo: %s" % pf(plug.get_sysinfo())) # this prints lots of information about the device ``` +## State & switching + +Devices can be turned on and off by either calling appropriate methods on the device object, +or by assigning a new state to `state` property. + +```python +print("Current state: %s" % plug.state) +plug.turn_off() +plug.turn_on() +``` + +```python +plug.state = "ON" +plug.state = "OFF" +``` + ## Time information ```python print("Current time: %s" % plug.time) @@ -62,29 +164,50 @@ print("Alias: %s" % plug.alias) plug.alias = "My New Smartplug" ``` -## State & switching -```python -print("Current state: %s" % plug.state) -plug.turn_off() -plug.turn_on() -``` -or -```python -plug.state = "ON" -plug.state = "OFF" -``` - -## Getting emeter status (on HS110) +## Getting emeter status (if applicable) ```python print("Current consumption: %s" % plug.get_emeter_realtime()) print("Per day: %s" % plug.get_emeter_daily(year=2016, month=12)) print("Per month: %s" % plug.get_emeter_monthly(year=2016)) ``` -## Switching the led +## Plug-specific + +### Switching the led (plugs only) ```python print("Current LED state: %s" % plug.led) plug.led = False # turn off led print("New LED state: %s" % plug.led) - ``` + +## Bulb-specific API + +The bulb API is likewise straightforward, so please refer to its API documentation. +Information about supported features can be queried by using properties prefixed with `is_`, e.g. `is_dimmable`. + +### Setting the brightness + +The `brightness` property works in percentages. + +```python +print(bulb.brightness) +if bulb.is_dimmable: + bulb.brightness = 100 +``` + +### Setting the color temperature +```python +print(bulb.color_temp) +if bulb.is_variable_color_temp: + bulb.color_temp = 3000 +``` + +### Setting the color + +Hue is given in degrees (0-360) and saturation and value in percentage. + +```python +print(bulb.hsv) +if bulb.is_color: + bulb.hsv = (180, 100, 100) # set to cyan +``` \ No newline at end of file diff --git a/setup.py b/setup.py index 05d1576b..a559fed5 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup setup(name='pyHS100', - version='0.2.4.2', + version='0.3.0', description='Interface for TPLink HS100 Smart Plugs.', url='https://github.com/GadgetReactor/pyHS100', author='Sean Seah (GadgetReactor)',