Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No bulbs seen #3

Open
frakman1 opened this issue Jun 2, 2015 · 13 comments
Open

No bulbs seen #3

frakman1 opened this issue Jun 2, 2015 · 13 comments

Comments

@frakman1
Copy link

frakman1 commented Jun 2, 2015

Hi there,

I tried using the test code in the readme but it returns 0 bulbs even though I have one LIFX bulb up and running and pingable from the prompt. I confirmed that I can control it from the app as well.
This is the code I used ( I printed bulbs to make sure it really is empty in this case):

import lazylights
import time
bulbs = lazylights.find_bulbs(expected_bulbs=1)
lazylights.set_power(bulbs, True)
time.sleep(1)
lazylights.set_power(bulbs, False)
print bulbs

running it on my windows dos prompt:

>python fraktest.py
set([])

What am I missing?

I also tried "python test_lazylights.py" but nothing happened there either.

Thank you
-I am using Python 2.7.5 on windows 7 64bit with a LIFX bulb running latest 2.0 firmware
-I installed lazylights2.0 by downloading the zip file and running "python setup.py install" . The github way didn't work for some reason and gave me a " error: invalid command 'egg_info'" error.

@mpapi
Copy link
Owner

mpapi commented Jun 4, 2015

Hey, thanks for filing an issue! I'll see what I can do to help.

Everything in your code snippet looks good to me.

One thing you could try, that's hopefully quick, is to call find_bulbs with the timeout parameter. The default timeout is 1 second; I've noticed with mine that once in a while, for whatever reason, I don't always get a response within that time. Can you try something like the following?

import lazylights
print lazylights.find_bulbs(expected_bubs=1, timeout=60)

If it still doesn't work with the exaggerated timeout, we can dig in a little deeper.

I will say I don't currently have access to a Windows machine, and I've done no real testing on Windows. It might be the case that the some of the socket options in use don't work quite the same way in Windows -- that's something I could look into.

I also tried "python test_lazylights.py" but nothing happened there either.

As an aside on this, the test suite is meant to be run through Nose -- so you'd pip install nose and then run nosetests, rather than running test_lazylights.py directly.

I installed lazylights2.0 by downloading the zip file and running "python setup.py install"

Just to be super extra sure, you got the zip file from https://github.com/mpapi/lazylights/archive/2.0.zip (and not master.zip), right?

The github way didn't work for some reason and gave me a " error: invalid command 'egg_info'" error.

What do you get when you run pip --version? I may not have set it up to work with older (or much newer) versions than what I have on my machine.

@frakman1
Copy link
Author

frakman1 commented Jun 4, 2015

Hi Matt,

Thank you for replying and offering to help.

1- I tried the extended timeout and there was no change. I made sure I could ping and control the pulb first via my iphone app and http API. See output:

C:\Python27>python
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import lazylights
>>> print lazylights.find_bulbs(expected_bulbs=1, timeout=60)
set([])

2- I had already installed nosetests since the tesT_lazylights wouldn't work without it. I ran nosetests and it just sat there with a "EE" in the output until I cancelled it with a Control-C. It looks like it was running some other python scripts that I wrote in the past. I have no idea what it is supposed to be used for. I would have thought that a test python script would be the thing to run when attempting a test. Maybe I am misunderstanding something. See output below:

C:\Python27>nosetests
EE
======================================================================
ERROR: docsis_signal_testv23.run_full_test
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\Python27\lib\site-packages\nose\case.py", line 197, in runTest
    self.test(*self.arg)
  File "C:\Python27\docsis_signal_testv23.py", line 448, in run_full_test
    load_settop_objects()
  File "C:\Python27\docsis_signal_testv23.py", line 286, in load_settop_objects
    sys.exit()
SystemExit:
-------------------- >> begin captured stdout << ---------------------
<dst_started nosetests 2.3 Thu Jun 04 13:18:50 2015>
<dst_cannot_open_ip_file docsis_signal_list_of_ip_or_mac.txt>
<dst_ip_file_error_end_of_test at Thu Jun 04 13:18:50 2015 elapsed 0 seconds>



--------------------- >> end captured stdout << ----------------------

======================================================================
ERROR: Failure: SyntaxError (EOL while scanning string literal (testip.py, line 22))
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\Python27\lib\site-packages\nose\loader.py", line 418, in loadTestsFromName
    addr.filename, addr.module)
  File "c:\Python27\lib\site-packages\nose\importer.py", line 47, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "c:\Python27\lib\site-packages\nose\importer.py", line 94, in importFromDir
    mod = load_module(part_fqname, fh, filename, desc)
  File "C:\Python27\testip.py", line 22
    os.system("cmd2000.exe " + HOST + " osdiag SilentDiag" ")
                                                            ^
SyntaxError: EOL while scanning string literal

----------------------------------------------------------------------
Ran 2 tests in 15.627s

FAILED (errors=2)

3- I didn't use the link you provided. I did the only thing I thought I could do and that was go to the repository at: https://github.com/mpapi/lazylights and then click on "Download zip" and used that to do the setup from. How else would I do it? Where would I get your link from?

4-

C:\Python27>pip --version
pip 6.0.2 from c:\Python27\lib\site-packages (python 2.7)

@frakman1
Copy link
Author

frakman1 commented Jun 5, 2015

Are you sure your code supports the new v2.0 firmware? In your credits, you reference the "lifxjs Protocol wiki page " project and I see that it is based on the v1.x firmware. Could this be the issue?

@mpapi
Copy link
Owner

mpapi commented Jun 5, 2015

Okay, I think I understand what might be happening.

The support for the 2.0 firmware is only on the 2.0 branch, and it sounds like you're actually running code from the master branch. To get 2.0, you'd either have to use the pip install command in the readme (which you said didn't work for you, possibly because I messed up the setup.py), or select the 2.0 branch from the branch selection dropdown (to get to this page) before you hit "Download ZIP" -- that's how I got the link to 2.0.zip that I referenced above. So, try using that link, and see if that works for you.

The master branch still only has 1.x support, mostly because I hadn't done much testing of the 2.0 code. But, yes, the 2.0 branch supports the 2.0 protocol, which I ended up reverse engineering using tcpdump the week the new firmware came out (so it's not in fact using what was on that wiki page anymore). I've been running that code with my 2.0 bulbs since the update, on an application that I use daily, without any major issues.

Now that 2.0 has been out for a while and I've done quite a bit of real-world testing, I can merge the 2.0 branch into master the next time I have a chance, to make things clearer going forward. The repository is definitely in need of some love. :)

Thanks again for these reports, by the way -- it's super useful for me to see what's confusing to users, and I'm always glad to see that other people are interested in the project!

@frakman1
Copy link
Author

frakman1 commented Jun 5, 2015

Thanks for clearing that up. I didn't even know about the other branch.

So I tried downloading the new zip and running the test script also failed. Thinking it had something to do with having installed the original/master scripts, I deleted all references to lazylights on my computer and started again. This time, I upgraded pip and tried the github method. That didn't work either unfortunately. See output below:

C:\Python27>pip install git+https://github.com/mpapi/lazylights
Collecting git+https://github.com/mpapi/lazylights
  Cloning https://github.com/mpapi/lazylights to c:\temp\pip-8aged5-build
    c:\Python27\lib\distutils\dist.py:267: UserWarning: Unknown distribution option: 'install_requires'
      warnings.warn(msg)
    usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
       or: -c --help [cmd1 cmd2 ...]
       or: -c --help-commands
       or: -c cmd --help
    error: invalid command 'egg_info'
    Complete output from command python setup.py egg_info:
    c:\Python27\lib\distutils\dist.py:267: UserWarning: Unknown distribution option: 'install_requires'

      warnings.warn(msg)

    usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]

       or: -c --help [cmd1 cmd2 ...]

       or: -c --help-commands

       or: -c cmd --help



    error: invalid command 'egg_info'

    ----------------------------------------
    Command "python setup.py egg_info" failed with error code 1 in c:\temp\pip-8aged5-build

C:\Python27>pip --version
pip 6.0.2 from c:\Python27\lib\site-packages (python 2.7)

C:\Python27>python get-pip.py
c:\temp\tmp6ilko2\pip.zip\pip\_vendor\requests\packages\urllib3\util\ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
c:\temp\tmp6ilko2\pip.zip\pip\_vendor\requests\packages\urllib3\util\ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
Collecting pip
  Downloading pip-7.0.3-py2.py3-none-any.whl (1.1MB)
    100% |################################| 1.1MB 401kB/s
Collecting wheel
  Downloading wheel-0.24.0-py2.py3-none-any.whl (63kB)
    100% |################################| 65kB 2.7MB/s
Installing collected packages: pip, wheel
  Found existing installation: pip 6.0.2
    Uninstalling pip-6.0.2:
      Successfully uninstalled pip-6.0.2
Successfully installed pip-7.0.3 wheel-0.24.0

C:\Python27>pip --version
pip 7.0.3 from C:\Python27\lib\site-packages (python 2.7)

C:\Python27>pip install git+https://github.com/mpapi/lazylights
Collecting git+https://github.com/mpapi/lazylights
  Cloning https://github.com/mpapi/lazylights to c:\temp\pip-scwem8-build
    Complete output from command python setup.py egg_info:
    C:\Python27\lib\distutils\dist.py:267: UserWarning: Unknown distribution option: 'install_requires'
      warnings.warn(msg)
    usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
       or: -c --help [cmd1 cmd2 ...]
       or: -c --help-commands
       or: -c cmd --help

    error: invalid command 'egg_info'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in c:\temp\pip-scwem8-build

So then I just manually downloaded the 2.0 branch's zip file and tested with that instead. I ran "python setup.py install" and ran the test script with the longer timeout and still got the empty set...

C:\Python27\lazylights-2.0 (1)\lazylights-2.0>python setup.py install
c:\python27\lib\distutils\dist.py:267: UserWarning: Unknown distribution option: 'install_requires'
  warnings.warn(msg)
running install
running build
running build_py
creating build
creating build\lib
copying lazylights.py -> build\lib
running install_lib
copying build\lib\lazylights.py -> c:\python27\Lib\site-packages
byte-compiling c:\python27\Lib\site-packages\lazylights.py to lazylights.pyc
running install_egg_info
Writing c:\python27\Lib\site-packages\lazylights-2.0.0-py2.7.egg-info

C:\Python27\lazylights-2.0 (1)\lazylights-2.0>python fraktest.py
set([])

@frakman1
Copy link
Author

frakman1 commented Jun 7, 2015

If I know the IP and MAC addresses of the bulbs, can't I leverage that to make it work better? I know I can ping the bulbs so maybe the code can be improved to allow that to be specified then it wouldn't have to try (and in my case fail) to find the bulb(s).

@VinzzB
Copy link

VinzzB commented Aug 22, 2015

@frakman1 Try changing the broadcast address in lazylights.py to your personal network broadcast address. If your network is 192.168.1.0 with netmask 255.255.255.0 then set it to 192.168.1.255. I tested this on my Raspberry Pi (with Python 2.7) and it works. Also tested on Windows (with Python 3.4 (via 2to3 script) but then i get socket errors.

btw, sending state (or power) commands are not reliable with this script. You have to check if the given command were really executed on the bulb by checking the get_state against the executed command.

After playing around a bit i created a (dirty) function that does just that: (only power on/off)

def setBulbPower(bulbs, power):
    for bulb in bulbs:
        x = 0
        while True:     
            print(("Re-sending" if x>0 else "Sending") + " power command to " + bulb.addr[0])   
            lazylights.set_power([bulb], power)         
            if x>0: time.sleep(1)

            print('Retrieving bulbstate from ' + bulb.addr[0])
            bulbState = lazylights.get_state([bulb])
            if len(bulbState) > 0:
                if bulbState[0].power==0 and not power:
                    print(bulbState[0].label + ' is powered OFF!')
                    break
                elif bulbState[0].power>0 and power:
                    print(bulbState[0].label + ' is powered ON!')
                    break;  
            else:
                print('The bulb did not respond yet...')
            x = x+1
    return

I've read somewhere that Lifx Firmware 2 also works with TCP instead of UDP. packets for controlling the bulbs. that would result in a reliable connection...

edit: cleaned-up code a bit.

@VinzzB
Copy link

VinzzB commented Aug 23, 2015

okay, tcp is only for cloud, which i don't want. So we stick with the UDP protocol.

Just saw a python 3 script forked from Lazylights2.0. (by georgwaechter). Tested on windows and has the same, non reliable, functionality.
Played a whole day with this script figuring out why powering off (+Status check) a bulb is taking so long.

Powering ON 2 bulbs takes 0.02 seconds. (almost every time)
Powering OFF 2 bulbs takes 0.3 seconds and can take up to 3 seconds!
After executing the set_power function. The get_state still receives a powered ON bulb for a while...
Is this a bug in the lazylights script or in the Lifx bulb firmware? Can we send a transition time with a power command?

@frakman1
You can easily create your own bulbs if you know the mac, IP address and port number. If i'm not mistaken, portnumber is always 56700. Have a look at my testscript, more specifically the function createBulb()

my testscript:

"""
Created by VinzzB on 2015/08/23
-------------------------------
Argument:
1 = power all bulbs
0 = set all bulbs off
-------------------------------
"""
import lazylights
import time
import struct
import sys, string
import binascii

if sys.version_info[0] > 2:
    PY3K = True
else:
    PY3K = False

def setBulbPower(bulbs, power, timeout=5):  
    try:
        starttime = time.time()
        for x in range(timeout): #iterate for timeout seconds max. Stop executing if not all bulbs were found within this time
            print("Sending power command to " + str(len(bulbs)) + " bulbs")
            lazylights.set_power(bulbs, power)
            print("Retrieving State from " + str(len(bulbs)) + " bulbs")
            for y in range(20): # ~1sec max
                bulbState = lazylights.get_state(bulbs) #Get state from all bulbs given in the args.
                if len(bulbState) == len(bulbs): #Did we find all the bulbs? If not, check again...
                    if powerState(bulbState) == power: #What prowerState do we have? Did the command exceute on all bulbs?
                        print(getLabels(bulbState) +  ' bulbs powered '+ ("ON" if power else "OFF")  + '!')
                        return #exit function
                else:           
                    print('Not all bulbs responded yet... Found {0} bulb(s) - Retry {1}'.format(len(bulbState), str(y+x+1)))
                time.sleep(0.05)
        print("The command timed-out. (" + str(time.time() - starttime) + " seconds)" )
    finally:
        print("setBulbPower() was executed in %1.2f seconds" % (time.time() - starttime))
    return

#Check if all bulbs are powered on or off   
def powerState(bulbs):
    power = False #Off by default.
    for bulb in bulbs:
        power |= bulb.power>0 #once True, always True!
        print("bulb " + getLabels([bulb]) + " is " + ("ON" if bulb.power>0 else "OFF"))
    return power

def getLabels(bulbs):
    r =""
    for bulb in bulbs:
        if PY3K:
            r += ("" if r=="" else ", ") + str(bulb.label,'ASCII').split('\x00')[0]
        else:
            r += ("" if r=="" else ", ") + str(bulb.label).split('\x00')[0]
    return r

def createBulb(ip, macString, port = 56700):        
    return lazylights.Bulb(b'LIFXV2', binascii.unhexlify(macString.replace(':', '')), (ip,port))

if __name__ == "__main__":  
    #Create our own Bulbs
    myBulb1 = createBulb('192.168.0.1','00:00:00:00:00:00') #FILL IN
    myBulb2 = createBulb('192.168.0.2','00:00:00:00:00:00')  #FILL IN
    print('MyBulb1: ' + str(myBulb1))
    print('MyBulb2: ' + str(myBulb2))
    #append to bulbs list
    bulbs = [myBulb1, myBulb2] 

    #Or discover bulbs
    # bulbs = lazylights.find_bulbs(2,0.075,5)  
    #print('done searching. Found ' + str(len(bulbs)) + ' bulbs')
    #for bulb in bulbs: print(bulb)     

    setBulbPower(bulbs,sys.argv[1] == '1')

Edit:

and now is see that the power value is going down from 65535 (max) to zero in steps when powering down. (same with On) Now i understand why powering off is taking longer. my script waits until power (brightness) is zero.

So my question: can we power down immediately?

@frakman1
Copy link
Author

Thank you for the insight and sample code. I will try them out and let you know.
Two quick comments:

1- I ran this script on my jail broken iPhone's command line/Python prompt and althoug it works sometimes, it was unreliable. I've had varying degrees of success with windows and Mac laptops (Win7 and 10)

2- Regarding powering on and off. I doubt it I the firmware because I wrote an iOS program that uses their/LIFX SDK and it works perfectly and is very responsive. I imagine they use the same functions at the lower level.

3- Did you know that they have release the documentation for their bulbs? Full IP packet protocol so no need to reverse engineer everything.

@VinzzB
Copy link

VinzzB commented Aug 24, 2015

  1. If you create your bulbs yourself then this script is actually pretty reliable (even without checking the state). But discovering the bulbs isn't working perfectly. That's why i was also looking for a way to create the bulbs manually (and to store / re-create the bulb list)
  2. I saw the API documentation yesterday. Seems that the power command also has a transition time. But it is not implemented in lazylights. This would fix my problem. I have to dig deeper in the lazylights code to add this feature.
  3. jep, i know / i saw / i've rapidly read it :-)

What i'm looking for now is a way to see changes in the lightbulbs. If i dim the lights with my phone, my script should see these changes almost immediately. The Lifx app handles that quite well. Just have to figure out how they do it (Do they use polling or just listen to broadcasts/multicasts messages)

@mpapi
Copy link
Owner

mpapi commented Aug 24, 2015

  • @VinzzB and @frakman1: thanks for the updates!

    Can you both recap/summarize which specific things are working for you reliably at this point and which aren't? Is it just bulb discovery, or other things as well?

    The best next step seems to be updating lazylights according to what's in the protocol docs, and I'd love to make sure I understand which things are not reliable to figure out what to focus on with the limited time I have. Trying to reproduce these kinds of issues in my local environment (where everything's working great) tends to be a lot of work for little benefit.

  • @VinzzB: for power transition, one thing you could do today is set the bulb state (not power) to change brightness to 0 with a transition/fade time, and then set power to off when that's done. That's pretty much what I do now without support for a transition time for power.

    Seeing changes is a little harder: I believe the app listens to the broadcast messages, and that's what the _recv function does as well. I've used _recv directly to listen to state changes or other message types as well -- I didn't make that first class in the library, though, because in many cases you'd need threading or greenlets or some other form of concurrency for it to be useful, and those come with a bunch of complexity and tradeoffs for different use cases. I'm not opposed to adding it eventually, though.

  • Also, please feel free to add some comments on reflect official 2.0 API #4 with anything else you find in the protocol docs that you'd like to see (transition time for powering on/off being a great example).

@VinzzB
Copy link

VinzzB commented Aug 29, 2015

@mpapi,

I figured out that the your discovery implementation isn't working well. Once you manually add the bulbs (like i did in my testscript) your script actually is reliable. Although UDP isn't reliable by nature, i did not noticed any flaws yet. Luckily i have a stable home network. :-)

I just tried an other python script form smarthall. (https://github.com/smarthall/python-lifx-sdk) He actually has a good discovery implementation and his script also listens to incoming messages for bulb changes. (+ it has transition time argument in power method)
What i'm missing there is a method to re-create the bulbs from a db store. as i did in my testscript.
Still have to figure out what script i'm going to use. Both has there pro / cons...

About the power transition:
I tried setting the brightness to 0 first. But the power state remains on. And when i power it down after setting brightness to zero, the power state is still going down in steps.

@frakman1
Copy link
Author

frakman1 commented Oct 2, 2015

@VinzzB
Thank you for your test script. I tried it with two manually defined bulbs and although the power turned on and off correctly, it never heard back from the bulbs and was stuck in a neverending loop of these messages:
Not all bulbs responded yet... Found 0 bulb(s) - Retry 1

By the way, I've used smarthall's library too and was able to get it to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants