Hello community, here is the log from the commit of package python-phue for openSUSE:Factory checked in at 2019-03-26 15:44:56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-phue (Old) and /work/SRC/openSUSE:Factory/.python-phue.new.25356 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-phue" Tue Mar 26 15:44:56 2019 rev:3 rq:688289 version:1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-phue/python-phue.changes 2018-12-24 11:41:07.713466149 +0100 +++ /work/SRC/openSUSE:Factory/.python-phue.new.25356/python-phue.changes 2019-03-26 15:45:01.616107297 +0100 @@ -1,0 +2,10 @@ +Mon Mar 25 13:46:59 UTC 2019 - [email protected] + +- version update to 1.1 + * Add support for deleting scenes + * Various bug fixes +- use github tarball to: + * package LICENSE and *.md + * run tests + +------------------------------------------------------------------- Old: ---- phue-1.0.tar.gz New: ---- phue-1.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-phue.spec ++++++ --- /var/tmp/diff_new_pack.59HzYe/_old 2019-03-26 15:45:03.112105543 +0100 +++ /var/tmp/diff_new_pack.59HzYe/_new 2019-03-26 15:45:03.152105497 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-phue # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,16 +18,22 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-phue -Version: 1.0 +Version: 1.1 Release: 0 Summary: Philips Hue Python library License: MIT Group: Development/Languages/Python Url: https://github.com/studioimaginaire/phue -Source: https://files.pythonhosted.org/packages/source/p/phue/phue-%{version}.tar.gz +# https://github.com/studioimaginaire/phue/issues/152 +Source0: https://github.com/studioimaginaire/phue/archive/1.1.tar.gz#/phue-%{version}.tar.gz BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros +# SECTION test requirements +BuildRequires: %{python_module fixtures} +BuildRequires: %{python_module mock} +BuildRequires: %{python_module pytest} +# /SECTION BuildArch: noarch %python_subpackages @@ -41,6 +47,9 @@ %build %python_build +%check +%pytest + %install %python_install %{python_expand chmod a+x %{buildroot}%{$python_sitelib}/phue.py @@ -52,6 +61,8 @@ %files %{python_files} %defattr(-,root,root,-) +%doc *.md +%license LICENSE %{python_sitelib}/phue.py* %{python_sitelib}/phue-%{version}-py*.egg-info %pycache_only %{python_sitelib}/__pycache__/phue*.py* ++++++ phue-1.0.tar.gz -> phue-1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/.gitignore new/phue-1.1/.gitignore --- old/phue-1.0/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/.gitignore 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,8 @@ +*.pyc +*~ +*.swp +test.py +*.DS_Store +*.sublime-workspace +dist +MANIFEST diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/.travis.yml new/phue-1.1/.travis.yml --- old/phue-1.0/.travis.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/.travis.yml 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,12 @@ +language: python +matrix: + fast_finish: true + include: + - python: "2.7" + env: TOXENV=py27 + - python: "3.5" + env: TOXENV=py35 + - python: "2.7" + env: TOXENV=pep8 +install: pip install tox +script: tox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/CHANGELOG.md new/phue-1.1/CHANGELOG.md --- old/phue-1.0/CHANGELOG.md 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/CHANGELOG.md 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,57 @@ +# phue changelog + +## r11 +- Add support for deleting scenes +- Various bug fixes + +## r10 +- Misc bug fixes +- Better support for schedules + +## r9 +- Added unit tests (sdague) +- Added scene support (sdague) +- Added sensor support (eldstal) +- Added reachable and type attributes to the Light object (carlosperate) +- Changed License to MIT + +## r8 +- iOS compatibility (Nathanaël Lécaudé) +- Logging fixes +- Added effect changing options (bradykent) +- Several unicode fixes (Nathanaël Lécaudé) +- Misc bug fixes + +## r7 +- Added to pypi +- Added support for Python 3 (Nathanaël Lécaudé) +- Logging level can be set with b.set_logging() (Nathanaël Lécaudé) +- Logging level can be set at init: b = Bridge(logging = 'debug') (Nathanaël Lécaudé) +- Added docstrings to Light properties (Nathanaël Lécaudé) +- Added colormode property to Light class (Nathanaël Lécaudé) +- IP is now optional if present in config file (Nathanaël Lécaudé) +- Implemented groups (Nathanaël Lécaudé) +- Implemented schedules (Nathanaël Lécaudé) +- Renamed get_info to get_api (Nathanaël Lécaudé) +- Renamed get_lights to get_light_objects (Nathanaël Lécaudé) +- Renamed set_state and get_state to set_light and get_light (Nathanaël Lécaudé) +- Fixed important bug when using set_state with a list of lights (Nathanaël Lécaudé) +- Add access to Light objects via direct indexing of the Bridge object via __getitem__ (Marshall Perrin) +- Implement real logging using Python's logging module, including error checking and display of responses from the server. (Marshall Perrin) +- Add function colortemp_k for color temperatures in Kelvin. (Marshall Perrin) +- Some additional error checking for invalid or missing parameters (Marshall Perrin) +- More details in docstrings. (Marshall Perrin) + + +## r6 +- Light objects are now obtained using the get_lights method +- Added the alert method to the Light object +- All requests now use httplib for consistency +- Moved all source to github +- Renamed the module to phue + +## r5 + - Renamed the Bulb() object to Light() so it reflects the official API better + - You can now pass the username as argument to the Bridge class if you don't want to read/store to file + - You can now get the bridge name with brdige.name or set it with bridge.name = 'newname' + - The set_state method can now use a dictionary as first argument to send more complex messages diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/LICENSE new/phue-1.1/LICENSE --- old/phue-1.0/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/LICENSE 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Nathanaël Lécaudé +https://github.com/studioimaginaire/phue + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/PKG-INFO new/phue-1.1/PKG-INFO --- old/phue-1.0/PKG-INFO 2017-07-02 19:30:40.000000000 +0200 +++ new/phue-1.1/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,10 +0,0 @@ -Metadata-Version: 1.0 -Name: phue -Version: 1.0 -Summary: A Philips Hue Python library -Home-page: https://github.com/studioimaginaire/phue -Author: Nathanaël Lécaudé -Author-email: UNKNOWN -License: MIT -Description: UNKNOWN -Platform: UNKNOWN diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/README.md new/phue-1.1/README.md --- old/phue-1.0/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/README.md 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,259 @@ +# phue: A Python library for Philips Hue + +Full featured Python library to control the Philips Hue lighting system. + +## Features + +- Compliant with the Philips Hue API 1.0 +- Support for Lights +- Support for Groups +- Support for Schedules +- Support for Scenes +- Support for Sensors +- Compatible with Python 2.6.x and upwards +- Compatible with Python 3 +- No dependencies +- Simple structure, single phue.py file +- Work in a procedural way or object oriented way + +## Installation + +### Using distutils + +``` +sudo easy_install phue +``` +or +``` +pip install phue +``` + +### Manually + +phue consists of a single file (phue.py) that you can put in your python search path or in site-packages (or dist-packages depending on the platform) +You can also simply run it by putting it in the same directory as you main script file or start a python interpreter in the same directory. +phue works with Python 2.6.x, 2.7.x and 3.x + +## Examples + +### Basic usage + +Using the set_light and get_light methods you can control pretty much all the parameters : + +```python +#!/usr/bin/python + +from phue import Bridge + +b = Bridge('ip_of_your_bridge') + +# If the app is not registered and the button is not pressed, press the button and call connect() (this only needs to be run a single time) +b.connect() + +# Get the bridge state (This returns the full dictionary that you can explore) +b.get_api() + +# Prints if light 1 is on or not +b.get_light(1, 'on') + +# Set brightness of lamp 1 to max +b.set_light(1, 'bri', 254) + +# Set brightness of lamp 2 to 50% +b.set_light(2, 'bri', 127) + +# Turn lamp 2 on +b.set_light(2,'on', True) + +# You can also control multiple lamps by sending a list as lamp_id +b.set_light( [1,2], 'on', True) + +# Get the name of a lamp +b.get_light(1, 'name') + +# You can also use light names instead of the id +b.get_light('Kitchen') +b.set_light('Kitchen', 'bri', 254) + +# Also works with lists +b.set_light(['Bathroom', 'Garage'], 'on', False) + +# The set_light method can also take a dictionary as the second argument to do more fancy stuff +# This will turn light 1 on with a transition time of 30 seconds +command = {'transitiontime' : 300, 'on' : True, 'bri' : 254} +b.set_light(1, command) +``` + +### Light Objects + +If you want to work in a more object-oriented way, there are several ways you can get Light objects. + +#### Get a flat list of light objects +```python + +lights = b.lights + +# Print light names +for l in lights: + print(l.name) + +# Set brightness of each light to 127 +for l in lights: + l.brightness = 127 + +``` + +#### Get Light objects as dictionaries + +```python +# Get a dictionary with the light id as the key +lights = b.get_light_objects('id') + +# Get the name of light 1, set the brightness to 127 +lights[1].name +lights[1].brightness = 127 + +# Get a dictionary with the light name as the key +light_names = b.get_light_objects('name') + +# Set the birghtness of the bulb named "Kitchen" +light_names["Kitchen"].brightness = 254 + +# Set lights using name as key +for light in ['Kitchen', 'Bedroom', 'Garage'] + light_names[light].on = True + light_names[light].hue = 15000 + light_names[light].saturation = 120 + +# Get a flat list of the light objects (same as calling b.lights) +lights_list = b.get_light_objects('list') + +for light in lights_list: + light.on = True + light.brightness = 127 + +``` + +### Setting Transition Times + +In the Hue API, transition times are specified in deciseconds (tenths +of a second). This +is not tracked as a device setting, but rather needs to be applied on +each individual transition command you want to control the time of. + +This can be done by specifying a transitiontime keyword when calling +set_light on the bridge: + + +```python +# Set brightness of lamp 1 to max, rapidly +b.set_light(1, 'bri', 254, transitiontime=1) +``` + +As a convenience, the Light class implements a wrapper that remembers +a specified transition time for that light, and applies it +automatically to every transition: + +```python +light = light_names['Kitchen'] +light.transitiontime = 2 +# this next transition will happen rapidly +light.brightness = 20 +``` + +Note that there is a known bug where turning a light off with the +transitiontime specified can cause the brightness level to behave +erratically when the light is turned back on. See [this +discussion](http://www.everyhue.com/vanilla/discussion/204/bug-with-brightness-when-requesting-ontrue-transitiontime5) +This package attempts to work around this issue by automatically +resetting the brightness when necessary, but this may not work in all +cases. + +Transition times from 0-300 deciseconds (i.e. 0 - 30 seconds) have +been tested to work. + +### Groups + +You can also work with the groups functionality of the Bridge. If groups aren't working, try re-setting the bridge by unpluging it and plugging it back again. + +```python + +# List groups +b.get_group() + +# List group 1 +b.get_group(1) + +# Get name of group 1 +b.get_group(1, 'name') + +# Get lights in group 1 +b.get_group(1,'lights') + +# Create a group with lights 1 and 3 +b.create_group('Kitchen', [1,3]) + +# Rename group with id 1 +b.set_group(1, 'name', 'New Group Name') + +# Change lights within group 1 +b.set_group(1, 'lights', [3,4]) + +# Turn group 1 off +b.set_group(1, 'on', False) + +# Delete group 2 +b.delete_group(1) + +``` + +### Schedules + +You can view, create and delete schedules using the following methods. Note that updates to the Hue API now use local time instead of UTC. If you have issues with schedules not triggering correctly, double check that the time zone is set correctly on your Hue Bridge and that your time in your code is not in UTC by default. + +```python + +# Get the list of different schedules +b.get_schedule() + +# Get the data of a particular schedules +b.get_schedule(1) + +# Create a schedule for a light, arguments are name, time, light_id, data (as a dictionary) and optional description +data = {'on': False, 'transitiontime': 600} +b.create_schedule('My schedule', '2012-11-12T22:34:00', 1, data, 'Bedtime' ) + +# Create a schedule for a group, same as above but with a group_id instead of light_id +data = {'on': False, 'transitiontime': 600} +b.create_group_schedule('My schedule', '2012-11-12T22:34:00', 0, data, 'Bedtime' ) + +# Delete a schedule +b.delete_schedule(1) + +``` + +## Using phue with Max/MSP via Jython + +You can use the phue library within [Max/MSP](http://www.cycling74.com) by using [Nick Rothwell's](http://www.cassiel.com) Jython objects. He recently updated the version to support Jython 2.7 which is required for phue to work. + +Download it here: https://github.com/cassiel/net.loadbang.jython + +## Using phue on iOS via Pythonista + +You can use phue on your iOS device via the [Pythonista](http://omz-software.com/pythonista) app. +This is a great way to build quick prototypes on iOS as you don't need to compile anything, you can code directly from the device itself. + +See this little example: + +http://www.youtube.com/embed/6K-fxWG6JSs + +## Acknowledgments + +Huge thanks to http://rsmck.co.uk/hue for hacking the protocol ! + +## License + +MIT - http://opensource.org/licenses/MIT + +"Hue Personal Wireless Lighting" is a trademark owned by Koninklijke Philips Electronics N.V., see www.meethue.com for more information. +I am in no way affiliated with the Philips organization. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/TODO.md new/phue-1.1/TODO.md --- old/phue-1.0/TODO.md 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/TODO.md 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,13 @@ +#phue TODO + + * Find a more elegant way to deal with transitiontime + * Add a Group class + * Cleanup the connect and register_app methods + * ~~Replace prints with proper logging mechanism~~ + * ~~Read/Write config file from cwd if home is not writable~~ + * ~~Store IP in config file~~ + * ~~Add support for groups~~ + * ~~Make username passable as argument and config file optional (so it can run when no write access is present like on iphone)~~ + * ~~Rename Bulb to Light to conform to API~~ (done) + * ~~Add the ability to get and set the bridge name~~ (done) + * ~~Update Bulbs dictionary when lights are renamed~~ (done) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/examples/random_colors.py new/phue-1.1/examples/random_colors.py --- old/phue-1.0/examples/random_colors.py 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/examples/random_colors.py 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,16 @@ +#!/usr/bin/python +from phue import Bridge +import random + +b = Bridge() # Enter bridge IP here. + +#If running for the first time, press button on bridge and run with b.connect() uncommented +#b.connect() + +lights = b.get_light_objects() + +for light in lights: + light.brightness = 254 + light.xy = [random.random(),random.random()] + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/examples/tk_gui_complex.py new/phue-1.1/examples/tk_gui_complex.py --- old/phue-1.0/examples/tk_gui_complex.py 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/examples/tk_gui_complex.py 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,42 @@ +#!/usr/bin/python +from Tkinter import * +from phue import Bridge + +''' +This example creates 3 sliders for the first 3 lights +and shows the name of the light under each slider. +There is also a checkbox to toggle the light. +''' + +b = Bridge() # Enter bridge IP here. + +#If running for the first time, press button on bridge and run with b.connect() uncommented +#b.connect() + +root = Tk() + +horizontal_frame = Frame(root) +horizontal_frame.pack() + +lights = b.get_light_objects('id') + +for light_id in lights: + channel_frame = Frame(horizontal_frame) + channel_frame.pack(side = LEFT) + + scale_command = lambda x, light_id=light_id: b.set_light(light_id,{'bri': int(x), 'transitiontime': 1}) + scale = Scale(channel_frame, from_ = 254, to = 0, command = scale_command, length = 200, showvalue = 0) + scale.set(b.get_light(light_id,'bri')) + scale.pack() + + button_var = BooleanVar() + button_var.set(b.get_light(light_id, 'on')) + button_command = lambda button_var=button_var, light_id=light_id: b.set_light(light_id, 'on', button_var.get()) + button = Checkbutton(channel_frame, variable = button_var, command = button_command) + button.pack() + + label = Label(channel_frame) + label.config(text = b.get_light(light_id,'name')) + label.pack() + +root.mainloop() \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/examples/tk_gui_hsb.py new/phue-1.1/examples/tk_gui_hsb.py --- old/phue-1.0/examples/tk_gui_hsb.py 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/examples/tk_gui_hsb.py 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,96 @@ +#!/usr/bin/python +from Tkinter import * +from phue import Bridge + +''' +This example creates 3 sliders for the first 3 lights +and shows the name of the light under each slider. +There is also a checkbox to toggle the light. +''' + +b = Bridge() # Enter bridge IP here. + +#If running for the first time, press button on bridge and run with b.connect() uncommented +#b.connect() + +root = Tk() + +lights = b.get_light_objects('id') +light_selection = [] + + +def curry(fn, *cargs, **ckwargs): + def call_fn(*fargs, **fkwargs): + d = ckwargs.copy() + d.update(fkwargs) + return fn(*(cargs + fargs), **d) + return call_fn + +def hue_command(x): + if len(light_selection) > 0: + b.set_light(light_selection, 'hue', int(x)) +def sat_command(x): + if len(light_selection) > 0: + b.set_light(light_selection, 'sat', int(x)) +def bri_command(x): + if len(light_selection) > 0: + b.set_light(light_selection, 'bri', int(x)) + +def select_button_command(light, button_state): + global light_selection + if button_state.get(): + light_selection.append(light) + else: + light_selection.remove(light) + print light_selection + +slider_frame = Frame(root) +slider_frame.pack(pady = 10) + +channels_frame = Frame(root) +channels_frame.pack() + +label_frame = Frame(channels_frame) +label_frame.pack(side=LEFT, padx = 10) + +label_state = Label(label_frame) +label_state.config(text = 'State') +label_state.pack() + +label_select = Label(label_frame) +label_select.config(text = 'Select') +label_select.pack() + +label_name = Label(label_frame) +label_name.config(text = 'Name') +label_name.pack() + +hue_slider = Scale(slider_frame, from_ = 65535, to = 0, command = hue_command) +sat_slider = Scale(slider_frame, from_ = 254, to = 0, command = sat_command) +bri_slider = Scale(slider_frame, from_ = 254, to = 0, command = bri_command) +hue_slider.pack(side=LEFT) +sat_slider.pack(side=LEFT) +bri_slider.pack(side=LEFT) + + +for light_id in lights: + channel_frame = Frame(channels_frame) + channel_frame.pack(side = LEFT, padx = 10) + + button_var = BooleanVar() + button_var.set(b.get_light(light_id, 'on')) + button_command = lambda button_var=button_var, light_id=light_id: b.set_light(light_id, 'on', button_var.get()) + button = Checkbutton(channel_frame, variable = button_var, command = button_command) + button.pack() + + select_button_var = BooleanVar() + #select_button_var.set(b.get_light(light_id, 'on')) + select_button_callback = curry(select_button_command, light_id, select_button_var) + select_button = Checkbutton(channel_frame, variable = select_button_var, command = select_button_callback) + select_button.pack() + + label = Label(channel_frame) + label.config(text = b.get_light(light_id,'name')) + label.pack() + +root.mainloop() \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/examples/tk_gui_simple.py new/phue-1.1/examples/tk_gui_simple.py --- old/phue-1.0/examples/tk_gui_simple.py 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/examples/tk_gui_simple.py 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,25 @@ +#!/usr/bin/python +from Tkinter import * +from phue import Bridge + +''' +This example creates a slider that controls the +brightness of the first 3 lights. +''' + +b = Bridge() # Enter bridge IP here. + +#If running for the first time, press button on bridge and run with b.connect() uncommented +#b.connect() + +b.set_light([1,2,3], 'on', True) + +def sel(data): + b.set_light([1,2,3],{'bri':int(data), 'transitiontime': 1}) + +root = Tk() +scale = Scale( root, from_ = 254, to = 0, command= sel, length = 200 ) +scale.set(b.get_light(1,'bri')) +scale.pack(anchor=CENTER) + +root.mainloop() \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/phue.py new/phue-1.1/phue.py --- old/phue-1.0/phue.py 2017-07-01 19:36:13.000000000 +0200 +++ new/phue-1.1/phue.py 2019-01-28 00:01:03.000000000 +0100 @@ -38,7 +38,7 @@ else: USER_HOME = 'HOME' -__version__ = '1.0' +__version__ = '1.1' def is_string(data): @@ -48,6 +48,19 @@ else: return isinstance(data, str) or isinstance(data, unicode) # noqa +def encodeString(string): + """Utility method to encode strings as utf-8.""" + if PY3K: + return string + else: + return string.encode('utf-8') + +def decodeString(string): + """Utility method to decode strings as utf-8.""" + if PY3K: + return string + else: + return string.decode('utf-8') class PhueException(Exception): @@ -118,11 +131,7 @@ @property def name(self): '''Get or set the name of the light [string]''' - if PY3K: - self._name = self._get('name') - else: - self._name = self._get('name').encode('utf-8') - return self._name + return encodeString(self._get('name')) @name.setter def name(self, value): @@ -360,11 +369,7 @@ @property def name(self): '''Get or set the name of the sensor [string]''' - if PY3K: - self._name = self._get('name') - else: - self._name = self._get('name').encode('utf-8') - return self._name + return encodeString(self._get('name')) @name.setter def name(self, value): @@ -467,14 +472,9 @@ name = group_id groups = bridge.get_group() for idnumber, info in groups.items(): - if PY3K: - if info['name'] == name: - self.group_id = int(idnumber) - break - else: - if info['name'] == name.decode('utf-8'): - self.group_id = int(idnumber) - break + if info['name'] == decodeString(name): + self.group_id = int(idnumber) + break else: raise LookupError("Could not find a group by that name.") @@ -499,11 +499,7 @@ @property def name(self): '''Get or set the name of the light group [string]''' - if PY3K: - self._name = self._get('name') - else: - self._name = self._get('name').encode('utf-8') - return self._name + return encodeString(self._get('name')) @name.setter def name(self, value): @@ -549,7 +545,8 @@ def __init__(self, sid, appdata=None, lastupdated=None, lights=None, locked=False, name="", owner="", - picture="", recycle=False, version=0): + picture="", recycle=False, version=0, type="", group="", + *args, **kwargs): self.scene_id = sid self.appdata = appdata or {} self.lastupdated = lastupdated @@ -558,14 +555,16 @@ else: self.lights = [] self.locked = locked - self.name = name + self.name = encodeString(name) self.owner = owner self.picture = picture self.recycle = recycle self.version = version + self.type = type + self.group = group def __repr__(self): - # like default python repr function, but add sensor name + # like default python repr function, but add scene name return '<{0}.{1} id="{2}" name="{3}" lights={4}>'.format( self.__class__.__module__, self.__class__.__name__, @@ -664,10 +663,10 @@ response = result.read() connection.close() if PY3K: - return json.loads(response.decode('utf-8')) - else: - logger.debug(response) - return json.loads(response) + response = response.decode('utf-8') + + logger.debug(response) + return json.loads(response) def get_ip_address(self, set_result=False): @@ -755,12 +754,8 @@ """ Lookup a light id based on string name. Case-sensitive. """ lights = self.get_light() for light_id in lights: - if PY3K: - if name == lights[light_id]['name']: - return light_id - else: - if name.decode('utf-8') == lights[light_id]['name']: - return light_id + if decodeString(name) == lights[light_id]['name']: + return light_id return False def get_light_objects(self, mode='list'): @@ -785,12 +780,8 @@ """ Lookup a sensor id based on string name. Case-sensitive. """ sensors = self.get_sensor() for sensor_id in sensors: - if PY3K: - if name == sensors[sensor_id]['name']: - return sensor_id - else: - if name.decode('utf-8') == sensors[sensor_id]['name']: - return sensor_id + if decodeString(name) == sensors[sensor_id]['name']: + return sensor_id return False def get_sensor_objects(self, mode='list'): @@ -820,10 +811,7 @@ return self.lights_by_id[key] except: try: - if PY3K: - return self.lights_by_name[key] - else: - return self.lights_by_name[key.decode('utf-8')] + return self.lights_by_name[decodeString(key)] except: raise KeyError( 'Not a valid key (integer index starting with 1, or light name): ' + str(key)) @@ -1026,6 +1014,12 @@ logger.debug(result) return result + def delete_scene(self, scene_id): + try: + return self.request('DELETE', '/api/' + self.username + '/scenes/' + str(scene_id)) + except: + logger.debug("Unable to delete scene with ID {0}".format(scene_id)) + def delete_sensor(self, sensor_id): try: name = self.sensors_by_id[sensor_id].name @@ -1045,12 +1039,8 @@ """ Lookup a group id based on string name. Case-sensitive. """ groups = self.get_group() for group_id in groups: - if PY3K: - if name == groups[group_id]['name']: - return group_id - else: - if name.decode('utf-8') == groups[group_id]['name']: - return group_id + if decodeString(name) == groups[group_id]['name']: + return int(group_id) return False def get_group(self, group_id=None, parameter=None): @@ -1140,12 +1130,15 @@ def get_scene(self): return self.request('GET', '/api/' + self.username + '/scenes') - def activate_scene(self, group_id, scene_id): + def activate_scene(self, group_id, scene_id, transition_time=4): return self.request('PUT', '/api/' + self.username + '/groups/' + str(group_id) + '/action', - {"scene": scene_id}) + { + "scene": scene_id, + "transitiontime": transition_time + }) - def run_scene(self, group_name, scene_name): + def run_scene(self, group_name, scene_name, transition_time=4): """Run a scene by group and scene name. As of 1.11 of the Hue API the scenes are accessable in the @@ -1161,30 +1154,33 @@ perfect, but is convenient for setting lights symbolically (and can be improved later). + :param transition_time: The duration of the transition from the + light’s current state to the new state in a multiple of 100ms + :returns True if a scene was run, False otherwise + """ groups = [x for x in self.groups if x.name == group_name] scenes = [x for x in self.scenes if x.name == scene_name] if len(groups) != 1: - logger.warn("run_scene: More than 1 group found by name %s", - group_name) - return + logger.warn("run_scene: More than 1 group found by name {}".format(group_name)) + return False group = groups[0] if len(scenes) == 0: - logger.warn("run_scene: No scene found %s", scene_name) - return + logger.warn("run_scene: No scene found {}".format(scene_name)) + return False if len(scenes) == 1: - self.activate_scene(group.group_id, scenes[0].scene_id) - return + self.activate_scene(group.group_id, scenes[0].scene_id, transition_time) + return True # otherwise, lets figure out if one of the named scenes uses # all the lights of the group group_lights = sorted([x.light_id for x in group.lights]) for scene in scenes: if group_lights == scene.lights: - self.activate_scene(group.group_id, scene.scene_id) - return - logger.warn("run_scene: did not find a scene: %s " - "that shared lights with group %s", - (scene_name, group)) + self.activate_scene(group.group_id, scene.scene_id, transition_time) + return True + logger.warn("run_scene: did not find a scene: {} " + "that shared lights with group {}".format(scene_name, group_name)) + return False # Schedules ##### def get_schedule(self, schedule_id=None, parameter=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/phue.sublime-project new/phue-1.1/phue.sublime-project --- old/phue-1.0/phue.sublime-project 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/phue.sublime-project 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,9 @@ +{ + "folders": + [ + { + "follow_symlinks": true, + "path": "../phue" + } + ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/test-requirements.txt new/phue-1.1/test-requirements.txt --- old/phue-1.0/test-requirements.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/test-requirements.txt 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,5 @@ +mock +pytest>=2.9.2 +pytest-cov>=2.3.1 +pytest-timeout>=1.0.0 +testtools diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/tests/fakes.py new/phue-1.1/tests/fakes.py --- old/phue-1.0/tests/fakes.py 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/tests/fakes.py 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,36 @@ +import json +import sys +import samples + +if sys.version_info[0] > 2: + from io import BytesIO as StringIO + def dump(data): + return json.dumps(data).encode('utf-8') +else: + from StringIO import StringIO + def dump(data): + return json.dumps(data) + + +class Request(object): + def __init__(self, mode, addr, data): + self.mode = mode + self.addr = addr + self.data = data + + +class FakeHTTP(object): + + def __init__(self, *args, **kwargs): + super(FakeHTTP, self).__init__() + self.call = None + + def request(self, mode, addr, data=None): + self.call = Request(mode, addr, data) + + def getresponse(self): + data = samples.RESP[self.call.mode][self.call.addr] + return StringIO(dump(data)) + + def close(self): + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/tests/samples.py new/phue-1.1/tests/samples.py --- old/phue-1.0/tests/samples.py 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/tests/samples.py 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,211 @@ +LIGHTS1 = { + u'1': {u'manufacturername': u'Philips', + u'modelid': u'LCT001', + u'name': u'Living Room Bulb', + u'state': {u'alert': u'none', + u'bri': 254, + u'colormode': u'xy', + u'ct': 382, + u'effect': u'none', + u'hue': 14665, + u'on': True, + u'reachable': True, + u'sat': 156, + u'xy': [0.4677, 0.4121]}, + u'swversion': u'5.23.1.13452', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:00:d1:fd:53-0b'}, + u'10': {u'manufacturername': u'Philips', + u'modelid': u'LCT001', + u'name': u'Porch 4', + u'state': {u'alert': u'none', + u'bri': 117, + u'colormode': u'xy', + u'ct': 500, + u'effect': u'none', + u'hue': 11004, + u'on': False, + u'reachable': True, + u'sat': 252, + u'xy': [0.5593, 0.406]}, + u'swversion': u'5.23.1.13452', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:00:ec:74:ab-0b'}, + u'11': {u'manufacturername': u'Philips', + u'modelid': u'LST002', + u'name': u'Living Room light strips', + u'state': {u'alert': u'none', + u'bri': 254, + u'colormode': u'xy', + u'ct': 389, + u'effect': u'none', + u'hue': 64967, + u'on': True, + u'reachable': True, + u'sat': 79, + u'xy': [0.472, 0.353]}, + u'swversion': u'5.50.2.19072', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:01:1a:22:cd-0b'}, + u'12': {u'manufacturername': u'Philips', + u'modelid': u'LCT007', + u'name': u'Arwen Dresser', + u'state': {u'alert': u'none', + u'bri': 251, + u'colormode': u'xy', + u'ct': 403, + u'effect': u'none', + u'hue': 14314, + u'on': False, + u'reachable': True, + u'sat': 172, + u'xy': [0.4791, 0.4139]}, + u'swversion': u'5.50.1.19085', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:10:47:9a:fe-0b'}, + u'13': {u'manufacturername': u'Philips', + u'modelid': u'LCT007', + u'name': u'Arwen Changing Table', + u'state': {u'alert': u'none', + u'bri': 117, + u'colormode': u'xy', + u'ct': 488, + u'effect': u'none', + u'hue': 12713, + u'on': False, + u'reachable': True, + u'sat': 222, + u'xy': [0.5224, 0.414]}, + u'swversion': u'5.50.1.19085', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:10:31:ee:f0-0b'}, + u'2': {u'manufacturername': u'Philips', + u'modelid': u'LCT001', + u'name': u'Bedroom Susan', + u'state': {u'alert': u'none', + u'bri': 117, + u'colormode': u'xy', + u'ct': 153, + u'effect': u'none', + u'hue': 52067, + u'on': False, + u'reachable': True, + u'sat': 237, + u'xy': [0.3089, 0.1334]}, + u'swversion': u'5.23.1.13452', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:00:f9:02:ae-0b'}, + u'3': {u'manufacturername': u'Philips', + u'modelid': u'LCT001', + u'name': u'Bedroom Sean', + u'state': {u'alert': u'none', + u'bri': 117, + u'colormode': u'xy', + u'ct': 153, + u'effect': u'none', + u'hue': 52067, + u'on': False, + u'reachable': True, + u'sat': 237, + u'xy': [0.3089, 0.1334]}, + u'swversion': u'5.23.1.13452', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:00:fe:00:e3-0b'}, + u'4': {u'manufacturername': u'Philips', + u'modelid': u'LLC010', + u'name': u'Living Room Iris', + u'state': {u'alert': u'none', + u'bri': 254, + u'colormode': u'xy', + u'effect': u'none', + u'hue': 58619, + u'on': True, + u'reachable': True, + u'sat': 81, + u'xy': [0.4715, 0.3499]}, + u'swversion': u'5.23.1.13452', + u'type': u'Color light', + u'uniqueid': u'00:17:88:01:00:12:01:83-0b'}, + u'5': {u'manufacturername': u'Philips', + u'modelid': u'LST001', + u'name': u'Living Room Dim Strip', + u'state': {u'alert': u'none', + u'bri': 254, + u'colormode': u'xy', + u'effect': u'none', + u'hue': 58619, + u'on': True, + u'reachable': True, + u'sat': 81, + u'xy': [0.4715, 0.3499]}, + u'swversion': u'5.23.1.13452', + u'type': u'Color light', + u'uniqueid': u'00:17:88:01:00:cd:b2:03-0b'}, + u'6': {u'manufacturername': u'Philips', + u'modelid': u'LLC011', + u'name': u'Arwen Flower', + u'state': {u'alert': u'none', + u'bri': 3, + u'colormode': u'xy', + u'effect': u'none', + u'hue': 64654, + u'on': True, + u'reachable': True, + u'sat': 249, + u'xy': [0.6855, 0.2927]}, + u'swversion': u'5.23.1.13452', + u'type': u'Color light', + u'uniqueid': u'00:17:88:01:00:c5:23:7e-0b'}, + u'7': {u'manufacturername': u'Philips', + u'modelid': u'LCT001', + u'name': u'Porch Fan 1', + u'state': {u'alert': u'none', + u'bri': 117, + u'colormode': u'xy', + u'ct': 500, + u'effect': u'none', + u'hue': 11004, + u'on': False, + u'reachable': True, + u'sat': 252, + u'xy': [0.5593, 0.406]}, + u'swversion': u'5.23.1.13452', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:00:fe:8a:df-0b'}, + u'8': {u'manufacturername': u'Philips', + u'modelid': u'LCT001', + u'name': u'Porch Fan 2', + u'state': {u'alert': u'none', + u'bri': 117, + u'colormode': u'xy', + u'ct': 500, + u'effect': u'none', + u'hue': 11004, + u'on': False, + u'reachable': True, + u'sat': 252, + u'xy': [0.5593, 0.406]}, + u'swversion': u'5.23.1.13452', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:00:f9:88:37-0b'}, + u'9': {u'manufacturername': u'Philips', + u'modelid': u'LCT001', + u'name': u'Porch 3', + u'state': {u'alert': u'none', + u'bri': 117, + u'colormode': u'xy', + u'ct': 500, + u'effect': u'none', + u'hue': 11004, + u'on': False, + u'reachable': True, + u'sat': 252, + u'xy': [0.5593, 0.406]}, + u'swversion': u'5.23.1.13452', + u'type': u'Extended color light', + u'uniqueid': u'00:17:88:01:00:fe:88:17-0b'}} + +RESP = dict(GET=dict(), POST=dict(), PUT=dict(), DELETE=dict()) +RESP['GET']['/api/username/lights/'] = LIGHTS1 +for key, value in LIGHTS1.items(): + RESP['GET']['/api/username/lights/%s' % key] = LIGHTS1[key] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/tests/test_basic_import.py new/phue-1.1/tests/test_basic_import.py --- old/phue-1.0/tests/test_basic_import.py 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/tests/test_basic_import.py 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,14 @@ +# Published under the MIT license - See LICENSE file for more detail +# +# This is a basic test file which just tests that things import, which +# means that this is even vaguely python code. + +import testtools + +import phue # noqa + + +class TestImport(testtools.TestCase): + + def test_import_works(self): + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/tests/test_request.py new/phue-1.1/tests/test_request.py --- old/phue-1.0/tests/test_request.py 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/tests/test_request.py 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,74 @@ +# Published under the MIT license - See LICENSE file for more detail +# +# This is a basic test file which just tests that things import, which +# means that this is even vaguely python code. + +import fixtures +import mock +import os +import sys +import testtools + +import phue +import fakes + +if sys.version_info[0] > 2: + httplib = 'http.client.HTTPConnection' +else: + httplib = 'httplib.HTTPConnection' + + +class TestRequest(testtools.TestCase): + + def setUp(self): + super(TestRequest, self).setUp() + self.home = fixtures.TempHomeDir() + self.useFixture(self.home) + + def test_register(self): + """test that registration happens automatically during setup.""" + confname = os.path.join(self.home.path, '.python_hue') + with mock.patch("phue.Bridge.request") as req: + req.return_value = [{'success': {'username': 'fooo'}}] + bridge = phue.Bridge(ip="10.0.0.0") + self.assertEqual(bridge.config_file_path, confname) + + # check contents of file + with open(confname) as f: + contents = f.read() + self.assertEqual(contents, '{"10.0.0.0": {"username": "fooo"}}') + + # make sure we can open under a different file + bridge2 = phue.Bridge(ip="10.0.0.0") + self.assertEqual(bridge2.username, "fooo") + + # and that we can even open without an ip address + bridge3 = phue.Bridge() + self.assertEqual(bridge3.username, "fooo") + self.assertEqual(bridge3.ip, "10.0.0.0") + + def test_register_fail(self): + """Test that registration fails in the expected way for timeout""" + with mock.patch("phue.Bridge.request") as req: + req.return_value = [{'error': {'type': 101}}] + self.assertRaises(phue.PhueRegistrationException, + phue.Bridge, ip="10.0.0.0") + + def test_register_unknown_user(self): + """Test that registration for unknown user works.""" + with mock.patch("phue.Bridge.request") as req: + req.return_value = [{'error': {'type': 7}}] + self.assertRaises(phue.PhueException, + phue.Bridge, ip="10.0.0.0") + + +class TestLights(testtools.TestCase): + + def setUp(self): + super(TestLights, self).setUp() + self.useFixture(fixtures.MonkeyPatch(httplib, fakes.FakeHTTP)) + self.bridge = phue.Bridge(ip="10.0.0.0", username="username") + + def test_get_lights(self): + lights = self.bridge.get_light_objects('id') + self.assertEqual(lights[1].name, "Living Room Bulb") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/phue-1.0/tox.ini new/phue-1.1/tox.ini --- old/phue-1.0/tox.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/phue-1.1/tox.ini 2019-01-28 00:01:03.000000000 +0100 @@ -0,0 +1,22 @@ +[tox] +envlist = pep8, py27, py35 +skip_missing_interpreters = True + +[testenv] +setenv = + LANG=en_US.UTF-8 + PYTHONPATH = {toxinidir} +commands = + py.test -v --timeout=30 --duration=10 --cov=phue --cov-report html {posargs} +deps = + -r{toxinidir}/test-requirements.txt + +[testenv:pep8] +deps = flake8 +basepython = python3 +commands = + flake8 phue.py + +[flake8] +ignore = E501 +exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build
